Add reusable multi-session skill and five-page project workbench

This commit is contained in:
aiagent
2026-10-19 03:03:29 -07:00
parent 939c14de77
commit 579dece104
75 changed files with 13096 additions and 0 deletions
+94
View File
@@ -0,0 +1,94 @@
#!/usr/bin/env python3
"""Authenticated control client. No credentials are accepted on the command line."""
import argparse
import json
import os
from pathlib import Path
import shlex
import subprocess
import sys
import urllib.error
import urllib.request
class APIError(RuntimeError):
def __init__(self,status):
self.status=status
super().__init__('Control API returned HTTP %s' % status)
class NoRedirect(urllib.request.HTTPRedirectHandler):
def redirect_request(self,req,fp,code,msg,headers,newurl):
return None
class Client:
def __init__(self, config):
self.config = config
def call(self, path, data=None, method=None):
if not path.startswith('/api/') or '\n' in path:
raise ValueError('Only control API paths are accepted')
if self.config.get('transport') == 'ssh':
cfg = self.config
command = ['ssh', '-i', str(Path(cfg['identity_file']).expanduser()),
'-o', 'IdentitiesOnly=yes', '-o', 'BatchMode=yes',
'-o', 'StrictHostKeyChecking=yes', '-o', 'ConnectTimeout=10',
cfg['ssh_host'], 'python3 -X utf8 ' + shlex.quote(cfg['remote_client']) +
' --config ' + shlex.quote(cfg['remote_config']) + ' rpc']
result = subprocess.run(command, input=json.dumps({'path': path, 'data': data, 'method': method}),
capture_output=True, text=True, encoding='utf-8', errors='strict', timeout=45)
if result.returncode:
try:
problem=json.loads(result.stdout)
except ValueError:
problem={}
if set(problem)=={'api_error'} and isinstance(problem['api_error'],int):
raise APIError(problem['api_error'])
raise RuntimeError('SSH control request failed; check connection and remote configuration')
return json.loads(result.stdout)
cfg = self.config
url = cfg.get('url') or ('http://%s:%s' % (cfg['bind'], cfg['port']))
token_path = Path(cfg['token_file']).expanduser()
if os.name != 'nt' and token_path.stat().st_mode & 0o077:
raise ValueError('Token file must not be readable by group or other users')
token = token_path.read_text(encoding='utf-8').strip()
if not token:
raise ValueError('Empty token file')
body = None if data is None else json.dumps(data).encode()
request = urllib.request.Request(url.rstrip('/') + path, data=body,
headers={'Authorization': 'Bearer ' + token, 'Content-Type': 'application/json',
'X-CSRF-Token': '1'}, method=method or ('GET' if data is None else 'POST'))
try:
opener=urllib.request.build_opener(NoRedirect(),urllib.request.ProxyHandler({}))
with opener.open(request, timeout=30) as response:
return json.load(response)
except urllib.error.HTTPError as exc:
status = exc.code
exc.close()
raise APIError(status) from None
def main():
parser = argparse.ArgumentParser(description=__doc__)
parser.add_argument('--config', required=True)
parser.add_argument('action', choices=['state', 'rpc'])
args = parser.parse_args()
client = Client(json.loads(Path(args.config).read_text(encoding='utf-8-sig')))
if args.action == 'state':
result = client.call('/api/state')
else:
payload = json.load(sys.stdin)
result = client.call(payload['path'], payload.get('data'), payload.get('method'))
print(json.dumps(result, ensure_ascii=True))
if __name__ == '__main__':
try:
main()
except APIError as exc:
print(json.dumps({'api_error':exc.status}))
sys.exit(2)
except Exception as exc:
print(str(exc), file=sys.stderr)
sys.exit(1)