@synap-core/cli 1.12.2 → 1.13.1-next.20261102.ec3d166
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +38 -11
- package/dist/build-info.json +2 -3
- package/dist/commands/agents.d.ts +0 -1
- package/dist/commands/agents.js +13 -3
- package/dist/commands/agents.js.map +1 -1
- package/dist/commands/app.js +2 -1
- package/dist/commands/app.js.map +1 -1
- package/dist/commands/automation.js +46 -41
- package/dist/commands/automation.js.map +1 -1
- package/dist/commands/box.d.ts +42 -0
- package/dist/commands/box.js +89 -0
- package/dist/commands/box.js.map +1 -0
- package/dist/commands/bridge-setup.js +3 -1
- package/dist/commands/bridge-setup.js.map +1 -1
- package/dist/commands/capability.d.ts +87 -1
- package/dist/commands/capability.js +174 -64
- package/dist/commands/capability.js.map +1 -1
- package/dist/commands/close-questions.d.ts +27 -0
- package/dist/commands/close-questions.js +49 -0
- package/dist/commands/close-questions.js.map +1 -0
- package/dist/commands/close-questions.test.d.ts +1 -0
- package/dist/commands/close-questions.test.js +52 -0
- package/dist/commands/close-questions.test.js.map +1 -0
- package/dist/commands/connect.d.ts +1 -3
- package/dist/commands/connect.js +2 -2
- package/dist/commands/connect.js.map +1 -1
- package/dist/commands/context.js +72 -43
- package/dist/commands/context.js.map +1 -1
- package/dist/commands/data-extra.d.ts +13 -0
- package/dist/commands/data-extra.js +35 -6
- package/dist/commands/data-extra.js.map +1 -1
- package/dist/commands/data.js +2 -1
- package/dist/commands/data.js.map +1 -1
- package/dist/commands/doctor.d.ts +39 -0
- package/dist/commands/doctor.js +136 -14
- package/dist/commands/doctor.js.map +1 -1
- package/dist/commands/hooks.d.ts +19 -0
- package/dist/commands/hooks.js +70 -0
- package/dist/commands/hooks.js.map +1 -0
- package/dist/commands/import.d.ts +25 -0
- package/dist/commands/import.js +98 -12
- package/dist/commands/import.js.map +1 -1
- package/dist/commands/init.d.ts +14 -0
- package/dist/commands/init.js +63 -21
- package/dist/commands/init.js.map +1 -1
- package/dist/commands/keys-request.d.ts +48 -0
- package/dist/commands/keys-request.js +236 -0
- package/dist/commands/keys-request.js.map +1 -0
- package/dist/commands/keys-request.test.d.ts +1 -0
- package/dist/commands/keys-request.test.js +176 -0
- package/dist/commands/keys-request.test.js.map +1 -0
- package/dist/commands/knowledge.d.ts +6 -0
- package/dist/commands/knowledge.js +13 -3
- package/dist/commands/knowledge.js.map +1 -1
- package/dist/commands/lens.js +2 -1
- package/dist/commands/lens.js.map +1 -1
- package/dist/commands/market-authoring.js +1 -1
- package/dist/commands/market-authoring.js.map +1 -1
- package/dist/commands/market.d.ts +61 -4
- package/dist/commands/market.js +106 -48
- package/dist/commands/market.js.map +1 -1
- package/dist/commands/mcp.d.ts +1 -1
- package/dist/commands/mcp.js +14 -9
- package/dist/commands/mcp.js.map +1 -1
- package/dist/commands/observe.category.test.d.ts +1 -0
- package/dist/commands/observe.category.test.js +59 -0
- package/dist/commands/observe.category.test.js.map +1 -0
- package/dist/commands/observe.d.ts +3 -0
- package/dist/commands/observe.js +7 -5
- package/dist/commands/observe.js.map +1 -1
- package/dist/commands/observe.title.test.d.ts +1 -0
- package/dist/commands/observe.title.test.js +37 -0
- package/dist/commands/observe.title.test.js.map +1 -0
- package/dist/commands/playbook.js +9 -0
- package/dist/commands/playbook.js.map +1 -1
- package/dist/commands/pods.d.ts +6 -1
- package/dist/commands/pods.js +38 -17
- package/dist/commands/pods.js.map +1 -1
- package/dist/commands/proposals-actions.js +7 -1
- package/dist/commands/proposals-actions.js.map +1 -1
- package/dist/commands/proton-setup.js +3 -1
- package/dist/commands/proton-setup.js.map +1 -1
- package/dist/commands/providers.d.ts +22 -6
- package/dist/commands/providers.js +39 -32
- package/dist/commands/providers.js.map +1 -1
- package/dist/commands/raycast.d.ts +15 -0
- package/dist/commands/raycast.js +36 -10
- package/dist/commands/raycast.js.map +1 -1
- package/dist/commands/session-feed-wait.d.ts +48 -0
- package/dist/commands/session-feed-wait.js +261 -0
- package/dist/commands/session-feed-wait.js.map +1 -0
- package/dist/commands/session-post.d.ts +40 -0
- package/dist/commands/session-post.js +96 -0
- package/dist/commands/session-post.js.map +1 -0
- package/dist/commands/session-wait.d.ts +9 -4
- package/dist/commands/session-wait.js +20 -5
- package/dist/commands/session-wait.js.map +1 -1
- package/dist/commands/sessions.d.ts +2 -0
- package/dist/commands/sessions.js +23 -0
- package/dist/commands/sessions.js.map +1 -1
- package/dist/commands/skills-commands.d.ts +2 -0
- package/dist/commands/skills-commands.js +9 -1
- package/dist/commands/skills-commands.js.map +1 -1
- package/dist/commands/skills-commands.test.d.ts +1 -0
- package/dist/commands/skills-commands.test.js +57 -0
- package/dist/commands/skills-commands.test.js.map +1 -0
- package/dist/commands/status.js +13 -7
- package/dist/commands/status.js.map +1 -1
- package/dist/commands/statusline.backoff.test.js +13 -3
- package/dist/commands/statusline.backoff.test.js.map +1 -1
- package/dist/commands/statusline.d.ts +104 -9
- package/dist/commands/statusline.js +392 -148
- package/dist/commands/statusline.js.map +1 -1
- package/dist/commands/statusline.line2.test.d.ts +15 -0
- package/dist/commands/statusline.line2.test.js +226 -0
- package/dist/commands/statusline.line2.test.js.map +1 -0
- package/dist/commands/statusline.needs-you.test.d.ts +1 -0
- package/dist/commands/statusline.needs-you.test.js +17 -0
- package/dist/commands/statusline.needs-you.test.js.map +1 -0
- package/dist/commands/statusline.now.test.d.ts +10 -0
- package/dist/commands/statusline.now.test.js +250 -0
- package/dist/commands/statusline.now.test.js.map +1 -0
- package/dist/commands/tracks.d.ts +68 -6
- package/dist/commands/tracks.js +272 -16
- package/dist/commands/tracks.js.map +1 -1
- package/dist/commands/upload.js +12 -0
- package/dist/commands/upload.js.map +1 -1
- package/dist/commands/wait.d.ts +124 -0
- package/dist/commands/wait.js +384 -0
- package/dist/commands/wait.js.map +1 -0
- package/dist/commands/wait.test.d.ts +8 -0
- package/dist/commands/wait.test.js +308 -0
- package/dist/commands/wait.test.js.map +1 -0
- package/dist/commands/whoami.d.ts +7 -0
- package/dist/commands/whoami.js +17 -0
- package/dist/commands/whoami.js.map +1 -1
- package/dist/commands/worker-accounts.d.ts +149 -0
- package/dist/commands/worker-accounts.js +339 -0
- package/dist/commands/worker-accounts.js.map +1 -0
- package/dist/commands/worker-accounts.test.d.ts +1 -0
- package/dist/commands/worker-accounts.test.js +224 -0
- package/dist/commands/worker-accounts.test.js.map +1 -0
- package/dist/commands/worker-github-app.d.ts +63 -0
- package/dist/commands/worker-github-app.js +161 -0
- package/dist/commands/worker-github-app.js.map +1 -0
- package/dist/commands/worker-github-app.test.d.ts +1 -0
- package/dist/commands/worker-github-app.test.js +102 -0
- package/dist/commands/worker-github-app.test.js.map +1 -0
- package/dist/commands/worker-github-broker.d.ts +39 -0
- package/dist/commands/worker-github-broker.js +106 -0
- package/dist/commands/worker-github-broker.js.map +1 -0
- package/dist/commands/worker-github-broker.test.d.ts +1 -0
- package/dist/commands/worker-github-broker.test.js +90 -0
- package/dist/commands/worker-github-broker.test.js.map +1 -0
- package/dist/commands/worker-runs.test.d.ts +1 -0
- package/dist/commands/worker-runs.test.js +150 -0
- package/dist/commands/worker-runs.test.js.map +1 -0
- package/dist/commands/worker-worktrees.d.ts +34 -0
- package/dist/commands/worker-worktrees.js +87 -0
- package/dist/commands/worker-worktrees.js.map +1 -0
- package/dist/commands/worker.d.ts +406 -10
- package/dist/commands/worker.js +1180 -126
- package/dist/commands/worker.js.map +1 -1
- package/dist/commands/worker.test.js +588 -11
- package/dist/commands/worker.test.js.map +1 -1
- package/dist/index.js +220 -28
- package/dist/index.js.map +1 -1
- package/dist/lib/agent-door.d.ts +1 -3
- package/dist/lib/agent-door.js +10 -7
- package/dist/lib/agent-door.js.map +1 -1
- package/dist/lib/agents-config.d.ts +9 -0
- package/dist/lib/agents-config.js +20 -0
- package/dist/lib/agents-config.js.map +1 -1
- package/dist/lib/atomic-file.d.ts +20 -1
- package/dist/lib/atomic-file.js +100 -2
- package/dist/lib/atomic-file.js.map +1 -1
- package/dist/lib/auth.d.ts +28 -2
- package/dist/lib/auth.js +52 -12
- package/dist/lib/auth.js.map +1 -1
- package/dist/lib/box/egress.d.ts +82 -0
- package/dist/lib/box/egress.js +264 -0
- package/dist/lib/box/egress.js.map +1 -0
- package/dist/lib/box/evidence.d.ts +86 -0
- package/dist/lib/box/evidence.js +121 -0
- package/dist/lib/box/evidence.js.map +1 -0
- package/dist/lib/box/job-server.d.ts +72 -0
- package/dist/lib/box/job-server.js +250 -0
- package/dist/lib/box/job-server.js.map +1 -0
- package/dist/lib/box/provisioner.d.ts +83 -0
- package/dist/lib/box/provisioner.js +312 -0
- package/dist/lib/box/provisioner.js.map +1 -0
- package/dist/lib/box/proxmox.d.ts +112 -0
- package/dist/lib/box/proxmox.js +211 -0
- package/dist/lib/box/proxmox.js.map +1 -0
- package/dist/lib/box/wire.d.ts +98 -0
- package/dist/lib/box/wire.js +70 -0
- package/dist/lib/box/wire.js.map +1 -0
- package/dist/lib/bridge-invocation.test.d.ts +1 -0
- package/dist/lib/bridge-invocation.test.js +75 -0
- package/dist/lib/bridge-invocation.test.js.map +1 -0
- package/dist/lib/bridge-status.d.ts +144 -0
- package/dist/lib/bridge-status.js +227 -0
- package/dist/lib/bridge-status.js.map +1 -0
- package/dist/lib/bridge-status.test.d.ts +6 -0
- package/dist/lib/bridge-status.test.js +197 -0
- package/dist/lib/bridge-status.test.js.map +1 -0
- package/dist/lib/client-header.test.d.ts +1 -0
- package/dist/lib/client-header.test.js +103 -0
- package/dist/lib/client-header.test.js.map +1 -0
- package/dist/lib/conversation-headers.d.ts +89 -9
- package/dist/lib/conversation-headers.js +156 -3
- package/dist/lib/conversation-headers.js.map +1 -1
- package/dist/lib/conversation-headers.test.d.ts +1 -0
- package/dist/lib/conversation-headers.test.js +78 -0
- package/dist/lib/conversation-headers.test.js.map +1 -0
- package/dist/lib/credential-class.d.ts +40 -2
- package/dist/lib/credential-class.js +48 -6
- package/dist/lib/credential-class.js.map +1 -1
- package/dist/lib/device-connect.d.ts +45 -0
- package/dist/lib/device-connect.js +60 -0
- package/dist/lib/device-connect.js.map +1 -1
- package/dist/lib/exporter-coverage.d.ts +1 -1
- package/dist/lib/exporter-coverage.js +1 -1
- package/dist/lib/goal-words.d.ts +38 -0
- package/dist/lib/goal-words.js +115 -0
- package/dist/lib/goal-words.js.map +1 -0
- package/dist/lib/hub-client.d.ts +87 -8
- package/dist/lib/hub-client.js +205 -70
- package/dist/lib/hub-client.js.map +1 -1
- package/dist/lib/init-handoff.d.ts +8 -0
- package/dist/lib/init-handoff.js +9 -2
- package/dist/lib/init-handoff.js.map +1 -1
- package/dist/lib/key-locations.d.ts +3 -3
- package/dist/lib/key-locations.js +3 -39
- package/dist/lib/key-locations.js.map +1 -1
- package/dist/lib/key-source.d.ts +3 -1
- package/dist/lib/key-source.js +7 -4
- package/dist/lib/key-source.js.map +1 -1
- package/dist/lib/knowledge-title.d.ts +3 -2
- package/dist/lib/knowledge-title.js +3 -2
- package/dist/lib/knowledge-title.js.map +1 -1
- package/dist/lib/machine-instance.d.ts +7 -5
- package/dist/lib/machine-instance.js +7 -5
- package/dist/lib/machine-instance.js.map +1 -1
- package/dist/lib/mcp-bridge.d.ts +50 -0
- package/dist/lib/mcp-bridge.js +232 -11
- package/dist/lib/mcp-bridge.js.map +1 -1
- package/dist/lib/mcp-bridge.test.js +224 -4
- package/dist/lib/mcp-bridge.test.js.map +1 -1
- package/dist/lib/mcp-call.d.ts +5 -1
- package/dist/lib/mcp-call.js +7 -6
- package/dist/lib/mcp-call.js.map +1 -1
- package/dist/lib/mcp-connector-url.test.d.ts +1 -0
- package/dist/lib/mcp-connector-url.test.js +13 -0
- package/dist/lib/mcp-connector-url.test.js.map +1 -0
- package/dist/lib/mcp-snippets.d.ts +16 -0
- package/dist/lib/mcp-snippets.js +20 -2
- package/dist/lib/mcp-snippets.js.map +1 -1
- package/dist/lib/now-line.d.ts +66 -0
- package/dist/lib/now-line.js +193 -0
- package/dist/lib/now-line.js.map +1 -0
- package/dist/lib/now-line.test.d.ts +5 -0
- package/dist/lib/now-line.test.js +177 -0
- package/dist/lib/now-line.test.js.map +1 -0
- package/dist/lib/package-updates.d.ts +66 -0
- package/dist/lib/package-updates.js +76 -0
- package/dist/lib/package-updates.js.map +1 -0
- package/dist/lib/pod-moved.d.ts +70 -0
- package/dist/lib/pod-moved.js +146 -0
- package/dist/lib/pod-moved.js.map +1 -0
- package/dist/lib/pod-reach.d.ts +39 -0
- package/dist/lib/pod-reach.js +117 -0
- package/dist/lib/pod-reach.js.map +1 -0
- package/dist/lib/pod-reach.test.d.ts +1 -0
- package/dist/lib/pod-reach.test.js +67 -0
- package/dist/lib/pod-reach.test.js.map +1 -0
- package/dist/lib/pod.d.ts +8 -4
- package/dist/lib/pod.js +12 -10
- package/dist/lib/pod.js.map +1 -1
- package/dist/lib/register-claude-code-mcp.test.d.ts +1 -0
- package/dist/lib/register-claude-code-mcp.test.js +97 -0
- package/dist/lib/register-claude-code-mcp.test.js.map +1 -0
- package/dist/lib/review-credential.js +3 -1
- package/dist/lib/review-credential.js.map +1 -1
- package/dist/lib/safe-open.d.ts +5 -3
- package/dist/lib/safe-open.js +9 -8
- package/dist/lib/safe-open.js.map +1 -1
- package/dist/lib/self-host.d.ts +17 -0
- package/dist/lib/self-host.js +24 -0
- package/dist/lib/self-host.js.map +1 -0
- package/dist/lib/session-feed.d.ts +185 -0
- package/dist/lib/session-feed.js +257 -0
- package/dist/lib/session-feed.js.map +1 -0
- package/dist/lib/targets.d.ts +52 -3
- package/dist/lib/targets.js +150 -209
- package/dist/lib/targets.js.map +1 -1
- package/dist/lib/term.d.ts +24 -0
- package/dist/lib/term.js +52 -0
- package/dist/lib/term.js.map +1 -0
- package/dist/lib/term.test.d.ts +1 -0
- package/dist/lib/term.test.js +29 -0
- package/dist/lib/term.test.js.map +1 -0
- package/dist/lib/transcript-index.d.ts +94 -0
- package/dist/lib/transcript-index.js +321 -0
- package/dist/lib/transcript-index.js.map +1 -0
- package/dist/lib/transcript-index.test.d.ts +10 -0
- package/dist/lib/transcript-index.test.js +200 -0
- package/dist/lib/transcript-index.test.js.map +1 -0
- package/dist/lib/wait-help.d.ts +11 -0
- package/dist/lib/wait-help.js +25 -0
- package/dist/lib/wait-help.js.map +1 -0
- package/dist/lib/wait-state.d.ts +57 -0
- package/dist/lib/wait-state.js +114 -0
- package/dist/lib/wait-state.js.map +1 -0
- package/dist/lib/wake-hook-settings.d.ts +57 -0
- package/dist/lib/wake-hook-settings.js +153 -0
- package/dist/lib/wake-hook-settings.js.map +1 -0
- package/dist/lib/wake-state.d.ts +65 -0
- package/dist/lib/wake-state.js +122 -0
- package/dist/lib/wake-state.js.map +1 -0
- package/package.json +5 -2
- package/skills/README.md +1 -1
- package/skills/agent-os/SKILL.md +8 -8
- package/skills/synap/SKILL.md +178 -88
- package/skills/synap/code-skills.md +84 -0
- package/skills/synap/concepts.md +38 -20
- package/skills/synap/creative-loop.md +1 -1
- package/skills/synap/document-embeds.md +17 -1
- package/skills/synap/focus-sessions.md +17 -8
- package/skills/synap/from-intent.md +33 -17
- package/skills/synap/governance-rules.md +3 -1
- package/skills/synap/lenses.md +4 -4
- package/skills/synap/linking.md +2 -2
- package/skills/synap/mental-model.md +1 -1
- package/skills/synap/operating-mode-rules.md +1 -1
- package/skills/synap/quick-reference.md +2 -1
- package/skills/synap/reflexes.md +8 -8
- package/skills/synap/viewframe-cells.md +1 -1
- package/skills/synap/work-model.md +24 -10
- package/skills/synap/workspace-design.md +5 -5
- package/skills/synap/writes.md +16 -9
- package/skills/synap/writing-titles.md +7 -0
- package/skills/synap-app/SKILL.md +148 -66
- package/skills/synap-app/creating-the-base-app.md +12 -7
- package/skills/synap-app/governance.md +27 -16
- package/skills/synap-app/install-commands.md +18 -9
- package/skills/synap-app/overview.md +16 -9
- package/skills/synap-app/using-the-sdk.md +75 -25
- package/skills/synap-market/SKILL.md +1 -1
- package/skills/synap-market/governance-and-catalog.md +1 -1
- package/skills/synap-schema/SKILL.md +24 -21
- package/skills/synap-schema/intent-vocabulary-ssot.md +24 -21
- package/templates/box/runner.py +206 -0
|
@@ -0,0 +1,206 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""The synap box runner: the only program the box template starts on boot.
|
|
3
|
+
|
|
4
|
+
It holds no credential. It fetches its job from the provisioner by the
|
|
5
|
+
provisioner's address on the isolated bridge, installs the declared packages
|
|
6
|
+
(setup phase: the egress proxy lets only the package registries through),
|
|
7
|
+
tells the provisioner it is done installing (run phase: only the declared
|
|
8
|
+
hosts), runs the script as the unprivileged user `box` with /in and /out,
|
|
9
|
+
streams the log back, uploads /out and reports the exit code.
|
|
10
|
+
|
|
11
|
+
The provisioner, not this program, enforces the wall clock: it destroys the box.
|
|
12
|
+
Python stdlib only (Debian 12's python3), so the template needs nothing else to
|
|
13
|
+
run it. Contract with the provisioner: synap-cli src/lib/box/job-server.ts.
|
|
14
|
+
"""
|
|
15
|
+
|
|
16
|
+
import json
|
|
17
|
+
import os
|
|
18
|
+
import pwd
|
|
19
|
+
import subprocess
|
|
20
|
+
import sys
|
|
21
|
+
import threading
|
|
22
|
+
import time
|
|
23
|
+
import urllib.parse
|
|
24
|
+
import urllib.request
|
|
25
|
+
|
|
26
|
+
BASE = os.environ.get("SYNAP_BOX_PROVISIONER", "http://10.20.0.1:8790")
|
|
27
|
+
PROXY = os.environ.get("SYNAP_BOX_PROXY", "http://10.20.0.1:3128")
|
|
28
|
+
WORK, IN, OUT = "/work", "/in", "/out"
|
|
29
|
+
USER = "box"
|
|
30
|
+
MAX_OUT_FILES = 20
|
|
31
|
+
|
|
32
|
+
# The job server is on the bridge: never through the proxy.
|
|
33
|
+
_direct = urllib.request.build_opener(urllib.request.ProxyHandler({}))
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
def call(method, path, body=None, ctype="application/json", timeout=60):
|
|
37
|
+
data = body if isinstance(body, (bytes, type(None))) else json.dumps(body).encode()
|
|
38
|
+
req = urllib.request.Request(BASE + path, data=data, method=method)
|
|
39
|
+
if data is not None:
|
|
40
|
+
req.add_header("Content-Type", ctype)
|
|
41
|
+
with _direct.open(req, timeout=timeout) as res:
|
|
42
|
+
raw = res.read()
|
|
43
|
+
return json.loads(raw) if raw and res.headers.get("content-type", "").startswith("application/json") else None
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
def fetch_job():
|
|
47
|
+
# The box may boot before the provisioner registers its address: retry.
|
|
48
|
+
deadline = time.time() + 180
|
|
49
|
+
while True:
|
|
50
|
+
try:
|
|
51
|
+
return call("GET", "/job")
|
|
52
|
+
except Exception as err: # noqa: BLE001
|
|
53
|
+
if time.time() > deadline:
|
|
54
|
+
raise SystemExit(f"no job from {BASE}: {err}")
|
|
55
|
+
time.sleep(2)
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
class LogPump:
|
|
59
|
+
"""Batches output and POSTs it to /log about twice a second."""
|
|
60
|
+
|
|
61
|
+
def __init__(self):
|
|
62
|
+
self.buf = bytearray()
|
|
63
|
+
self.lock = threading.Lock()
|
|
64
|
+
self.stop = threading.Event()
|
|
65
|
+
self.thread = threading.Thread(target=self._loop, daemon=True)
|
|
66
|
+
self.thread.start()
|
|
67
|
+
|
|
68
|
+
def write(self, data):
|
|
69
|
+
with self.lock:
|
|
70
|
+
self.buf += data
|
|
71
|
+
|
|
72
|
+
def _flush(self):
|
|
73
|
+
with self.lock:
|
|
74
|
+
chunk, self.buf = bytes(self.buf), bytearray()
|
|
75
|
+
if chunk:
|
|
76
|
+
try:
|
|
77
|
+
call("POST", "/log", chunk, "application/octet-stream")
|
|
78
|
+
except Exception: # noqa: BLE001 - a lost log chunk must not kill the run
|
|
79
|
+
pass
|
|
80
|
+
|
|
81
|
+
def _loop(self):
|
|
82
|
+
while not self.stop.wait(0.5):
|
|
83
|
+
self._flush()
|
|
84
|
+
|
|
85
|
+
def close(self):
|
|
86
|
+
self.stop.set()
|
|
87
|
+
self.thread.join()
|
|
88
|
+
self._flush()
|
|
89
|
+
|
|
90
|
+
|
|
91
|
+
def demote(user):
|
|
92
|
+
pw = pwd.getpwnam(user)
|
|
93
|
+
|
|
94
|
+
def run_as():
|
|
95
|
+
os.setgroups([])
|
|
96
|
+
os.setgid(pw.pw_gid)
|
|
97
|
+
os.setuid(pw.pw_uid)
|
|
98
|
+
|
|
99
|
+
return run_as, pw
|
|
100
|
+
|
|
101
|
+
|
|
102
|
+
def run(cmd, env, pump):
|
|
103
|
+
"""Run cmd as the box user; its stdout+stderr go to the log. Returns the exit code."""
|
|
104
|
+
pre, pw = demote(USER)
|
|
105
|
+
pump.write(("$ " + " ".join(cmd) + "\n").encode())
|
|
106
|
+
proc = subprocess.Popen(
|
|
107
|
+
cmd, cwd=WORK, env={**env, "HOME": pw.pw_dir, "USER": USER},
|
|
108
|
+
stdout=subprocess.PIPE, stderr=subprocess.STDOUT, preexec_fn=pre,
|
|
109
|
+
)
|
|
110
|
+
for chunk in iter(lambda: proc.stdout.read1(65536), b""):
|
|
111
|
+
pump.write(chunk)
|
|
112
|
+
return proc.wait()
|
|
113
|
+
|
|
114
|
+
|
|
115
|
+
def installed(job, env):
|
|
116
|
+
try:
|
|
117
|
+
if job["language"] == "python":
|
|
118
|
+
out = subprocess.run(
|
|
119
|
+
["uv", "pip", "list", "--python", f"{WORK}/.venv/bin/python", "--format", "json"],
|
|
120
|
+
cwd=WORK, env=env, capture_output=True, timeout=60,
|
|
121
|
+
).stdout
|
|
122
|
+
return [{"name": p["name"], "version": p["version"], "manager": "pip"} for p in json.loads(out or b"[]")]
|
|
123
|
+
out = subprocess.run(
|
|
124
|
+
["pnpm", "list", "--json", "--depth", "0"], cwd=WORK, env=env, capture_output=True, timeout=60,
|
|
125
|
+
).stdout
|
|
126
|
+
deps = (json.loads(out or b"[{}]") or [{}])[0].get("dependencies", {}) or {}
|
|
127
|
+
return [{"name": n, "version": d.get("version", ""), "manager": "npm"} for n, d in deps.items()]
|
|
128
|
+
except Exception: # noqa: BLE001 - the list is evidence, not the verdict
|
|
129
|
+
return []
|
|
130
|
+
|
|
131
|
+
|
|
132
|
+
def upload_out(pump):
|
|
133
|
+
sent = 0
|
|
134
|
+
for root, _dirs, files in os.walk(OUT):
|
|
135
|
+
for name in sorted(files):
|
|
136
|
+
full = os.path.join(root, name)
|
|
137
|
+
if os.path.islink(full) or not os.path.isfile(full):
|
|
138
|
+
continue
|
|
139
|
+
rel = os.path.relpath(full, OUT)
|
|
140
|
+
if sent >= MAX_OUT_FILES:
|
|
141
|
+
pump.write(f"\n[synap box] /out/{rel} skipped: at most {MAX_OUT_FILES} files\n".encode())
|
|
142
|
+
continue
|
|
143
|
+
try:
|
|
144
|
+
with open(full, "rb") as fh:
|
|
145
|
+
call("PUT", "/out/" + urllib.parse.quote(rel), fh.read(), "application/octet-stream", timeout=600)
|
|
146
|
+
sent += 1
|
|
147
|
+
except Exception as err: # noqa: BLE001
|
|
148
|
+
pump.write(f"\n[synap box] /out/{rel} not uploaded: {err}\n".encode())
|
|
149
|
+
|
|
150
|
+
|
|
151
|
+
def main():
|
|
152
|
+
job = fetch_job()
|
|
153
|
+
pump = LogPump()
|
|
154
|
+
for d in (WORK, IN, OUT):
|
|
155
|
+
os.makedirs(d, exist_ok=True)
|
|
156
|
+
pw = pwd.getpwnam(USER)
|
|
157
|
+
for d in (WORK, OUT):
|
|
158
|
+
os.chown(d, pw.pw_uid, pw.pw_gid)
|
|
159
|
+
|
|
160
|
+
entry = os.path.normpath(job["entrypoint"])
|
|
161
|
+
if entry.startswith("..") or os.path.isabs(entry):
|
|
162
|
+
raise SystemExit(f"bad entrypoint {job['entrypoint']!r}")
|
|
163
|
+
os.makedirs(os.path.dirname(os.path.join(WORK, entry)), exist_ok=True)
|
|
164
|
+
with open(os.path.join(WORK, entry), "w") as fh:
|
|
165
|
+
fh.write(job["code"])
|
|
166
|
+
os.chown(os.path.join(WORK, entry), pw.pw_uid, pw.pw_gid)
|
|
167
|
+
with open(os.path.join(IN, "parameters.json"), "w") as fh:
|
|
168
|
+
json.dump(job.get("parameters"), fh)
|
|
169
|
+
|
|
170
|
+
env = {
|
|
171
|
+
"PATH": "/usr/local/bin:/usr/bin:/bin",
|
|
172
|
+
"LANG": "C.UTF-8",
|
|
173
|
+
"HTTP_PROXY": PROXY, "HTTPS_PROXY": PROXY, "http_proxy": PROXY, "https_proxy": PROXY,
|
|
174
|
+
"npm_config_proxy": PROXY, "npm_config_https_proxy": PROXY,
|
|
175
|
+
# Node's own fetch ignores HTTP(S)_PROXY unless told (Node >= 22.21 / 24).
|
|
176
|
+
"NODE_USE_ENV_PROXY": "1",
|
|
177
|
+
"UV_CACHE_DIR": f"{WORK}/.cache/uv", "npm_config_cache": f"{WORK}/.cache/npm",
|
|
178
|
+
"SYNAP_PARAMETERS_FILE": f"{IN}/parameters.json", "SYNAP_OUT_DIR": OUT,
|
|
179
|
+
}
|
|
180
|
+
pkgs = job.get("packages") or []
|
|
181
|
+
if job["language"] == "python":
|
|
182
|
+
code = run(["uv", "venv", f"{WORK}/.venv"], env, pump)
|
|
183
|
+
if code == 0 and pkgs:
|
|
184
|
+
code = run(["uv", "pip", "install", "--python", f"{WORK}/.venv/bin/python",
|
|
185
|
+
*[f"{p['name']}=={p['version']}" for p in pkgs]], env, pump)
|
|
186
|
+
cmd = [f"{WORK}/.venv/bin/python", entry]
|
|
187
|
+
else:
|
|
188
|
+
with open(os.path.join(WORK, "package.json"), "w") as fh:
|
|
189
|
+
json.dump({"private": True}, fh)
|
|
190
|
+
os.chown(os.path.join(WORK, "package.json"), pw.pw_uid, pw.pw_gid)
|
|
191
|
+
code = run(["pnpm", "add", *[f"{p['name']}@{p['version']}" for p in pkgs]], env, pump) if pkgs else 0
|
|
192
|
+
cmd = ["node", entry]
|
|
193
|
+
|
|
194
|
+
call("POST", "/phase", {"phase": "run"})
|
|
195
|
+
if code == 0:
|
|
196
|
+
code = run(cmd, env, pump)
|
|
197
|
+
else:
|
|
198
|
+
pump.write(f"\n[synap box] packages did not install (exit {code}); the script was not run\n".encode())
|
|
199
|
+
|
|
200
|
+
upload_out(pump)
|
|
201
|
+
pump.close()
|
|
202
|
+
call("POST", "/done", {"exitCode": code, "installedPackages": installed(job, env)})
|
|
203
|
+
|
|
204
|
+
|
|
205
|
+
if __name__ == "__main__":
|
|
206
|
+
sys.exit(main())
|