@synap-core/cli 1.12.2 → 1.13.1-next.20261102.246dc73
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 +74 -44
- 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 +42 -5
- package/dist/commands/knowledge.js +94 -29
- 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 +89 -31
- package/dist/commands/market.js +215 -222
- 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/template-lifecycle.d.ts +0 -31
- package/dist/lib/template-lifecycle.js +0 -74
- package/dist/lib/template-lifecycle.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 +188 -94
- 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/hand-off.md +10 -6
- 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,63 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The worker's GitHub identity: a GitHub App installation, never a person.
|
|
3
|
+
*
|
|
4
|
+
* An unattended run has Bash, so whatever `gh` and `git push` authenticate as
|
|
5
|
+
* is what a prompt injection can use. The founder's login is an org admin (it
|
|
6
|
+
* can `gh pr merge --admin` past the ruleset); the "Synap Agent" App has no
|
|
7
|
+
* administration, members or bypass permission (see docs/synap-agent-app.md).
|
|
8
|
+
*
|
|
9
|
+
* The App's id and private key live in an owner-only env file
|
|
10
|
+
* (default ~/.config/synap-agent-app.env):
|
|
11
|
+
*
|
|
12
|
+
* GITHUB_APP_ID=123456
|
|
13
|
+
* GITHUB_APP_PRIVATE_KEY_FILE=/home/me/.config/synap-agent-app.pem (0600)
|
|
14
|
+
* GITHUB_APP_INSTALLATION_ID=7890123 # optional: the org's installation
|
|
15
|
+
*
|
|
16
|
+
* Each launch mints an installation token (valid 1 h, cached until 5 min
|
|
17
|
+
* before it ends) and puts it in the run's GH_TOKEN. Neither the key nor a
|
|
18
|
+
* token is ever logged or written to state.
|
|
19
|
+
*/
|
|
20
|
+
export interface GithubAppConfig {
|
|
21
|
+
appId: string;
|
|
22
|
+
privateKey: string;
|
|
23
|
+
installationId?: string;
|
|
24
|
+
}
|
|
25
|
+
export declare function defaultGithubAppFile(): string;
|
|
26
|
+
/** `KEY=value` lines, `#` comments, optional quotes. */
|
|
27
|
+
export declare function parseEnvFile(text: string): Record<string, string>;
|
|
28
|
+
/**
|
|
29
|
+
* `undefined`: no env file (the caller warns, loudly). Throws when the file is
|
|
30
|
+
* there but unusable, so a half-set-up App never silently falls back to a
|
|
31
|
+
* person's login.
|
|
32
|
+
*/
|
|
33
|
+
export declare function loadGithubAppConfig(file?: string): GithubAppConfig | undefined;
|
|
34
|
+
/** The files a person must keep owner-only: any that others can read. */
|
|
35
|
+
export declare function githubAppFilesTooOpen(file?: string): string[];
|
|
36
|
+
/** The App JWT (RS256): 10 minutes at most, issued 60 s in the past for clock skew. */
|
|
37
|
+
export declare function signAppJwt(appId: string, privateKey: string, nowMs: number): string;
|
|
38
|
+
export interface InstallationTokenSource {
|
|
39
|
+
/** A token valid now; mints a new one when the cached one is near its end. Throws on failure. */
|
|
40
|
+
get(): Promise<string>;
|
|
41
|
+
}
|
|
42
|
+
export declare function createInstallationTokenSource(config: GithubAppConfig, o?: {
|
|
43
|
+
fetchImpl?: typeof fetch;
|
|
44
|
+
now?: () => number;
|
|
45
|
+
}): InstallationTokenSource;
|
|
46
|
+
export interface GithubRunEnv {
|
|
47
|
+
/** A token valid at launch: what a caller that skips the helpers (an absolute /usr/bin/gh) gets for its first hour. */
|
|
48
|
+
token: string;
|
|
49
|
+
/** The worker's token broker socket (worker-github-broker.ts). */
|
|
50
|
+
sock: string;
|
|
51
|
+
/** Holds the `gh` wrapper and the git credential helper; first on the run's PATH. */
|
|
52
|
+
binDir: string;
|
|
53
|
+
gitHelper: string;
|
|
54
|
+
/** An EMPTY folder: `gh` never reads the person's hosts.yml. */
|
|
55
|
+
ghConfigDir: string;
|
|
56
|
+
}
|
|
57
|
+
/**
|
|
58
|
+
* The environment that makes `gh` AND `git push` (https) act as the App for
|
|
59
|
+
* the whole run: both ask the broker for a fresh token each time, and the
|
|
60
|
+
* credential-helper list is reset first, so a helper holding a person's login
|
|
61
|
+
* is never asked.
|
|
62
|
+
*/
|
|
63
|
+
export declare function githubRunEnv(g: GithubRunEnv, basePath: string | undefined): NodeJS.ProcessEnv;
|
|
@@ -0,0 +1,161 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The worker's GitHub identity: a GitHub App installation, never a person.
|
|
3
|
+
*
|
|
4
|
+
* An unattended run has Bash, so whatever `gh` and `git push` authenticate as
|
|
5
|
+
* is what a prompt injection can use. The founder's login is an org admin (it
|
|
6
|
+
* can `gh pr merge --admin` past the ruleset); the "Synap Agent" App has no
|
|
7
|
+
* administration, members or bypass permission (see docs/synap-agent-app.md).
|
|
8
|
+
*
|
|
9
|
+
* The App's id and private key live in an owner-only env file
|
|
10
|
+
* (default ~/.config/synap-agent-app.env):
|
|
11
|
+
*
|
|
12
|
+
* GITHUB_APP_ID=123456
|
|
13
|
+
* GITHUB_APP_PRIVATE_KEY_FILE=/home/me/.config/synap-agent-app.pem (0600)
|
|
14
|
+
* GITHUB_APP_INSTALLATION_ID=7890123 # optional: the org's installation
|
|
15
|
+
*
|
|
16
|
+
* Each launch mints an installation token (valid 1 h, cached until 5 min
|
|
17
|
+
* before it ends) and puts it in the run's GH_TOKEN. Neither the key nor a
|
|
18
|
+
* token is ever logged or written to state.
|
|
19
|
+
*/
|
|
20
|
+
import { createSign } from "node:crypto";
|
|
21
|
+
import fs from "node:fs";
|
|
22
|
+
import os from "node:os";
|
|
23
|
+
import path from "node:path";
|
|
24
|
+
export function defaultGithubAppFile() {
|
|
25
|
+
return path.join(os.homedir(), ".config", "synap-agent-app.env");
|
|
26
|
+
}
|
|
27
|
+
/** `KEY=value` lines, `#` comments, optional quotes. */
|
|
28
|
+
export function parseEnvFile(text) {
|
|
29
|
+
const out = {};
|
|
30
|
+
for (const line of text.split("\n")) {
|
|
31
|
+
const m = /^\s*(?:export\s+)?([A-Z0-9_]+)\s*=\s*(.*?)\s*$/.exec(line);
|
|
32
|
+
if (!m || line.trimStart().startsWith("#"))
|
|
33
|
+
continue;
|
|
34
|
+
out[m[1]] = m[2].replace(/^(["'])(.*)\1$/, "$2");
|
|
35
|
+
}
|
|
36
|
+
return out;
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* `undefined`: no env file (the caller warns, loudly). Throws when the file is
|
|
40
|
+
* there but unusable, so a half-set-up App never silently falls back to a
|
|
41
|
+
* person's login.
|
|
42
|
+
*/
|
|
43
|
+
export function loadGithubAppConfig(file = defaultGithubAppFile()) {
|
|
44
|
+
let text;
|
|
45
|
+
try {
|
|
46
|
+
text = fs.readFileSync(file, "utf8");
|
|
47
|
+
}
|
|
48
|
+
catch (err) {
|
|
49
|
+
if (err.code === "ENOENT")
|
|
50
|
+
return undefined;
|
|
51
|
+
throw new Error(`GitHub App file ${file} could not be read: ${err.message}`);
|
|
52
|
+
}
|
|
53
|
+
const env = parseEnvFile(text);
|
|
54
|
+
const appId = env.GITHUB_APP_ID;
|
|
55
|
+
const keyFile = env.GITHUB_APP_PRIVATE_KEY_FILE;
|
|
56
|
+
if (!appId || !keyFile)
|
|
57
|
+
throw new Error(`GitHub App file ${file} needs GITHUB_APP_ID and GITHUB_APP_PRIVATE_KEY_FILE`);
|
|
58
|
+
const keyPath = keyFile.startsWith("~/") ? path.join(os.homedir(), keyFile.slice(2)) : keyFile;
|
|
59
|
+
let privateKey;
|
|
60
|
+
try {
|
|
61
|
+
privateKey = fs.readFileSync(keyPath, "utf8");
|
|
62
|
+
}
|
|
63
|
+
catch (err) {
|
|
64
|
+
throw new Error(`GitHub App private key ${keyPath} could not be read: ${err.message}`);
|
|
65
|
+
}
|
|
66
|
+
return { appId, privateKey, installationId: env.GITHUB_APP_INSTALLATION_ID || undefined };
|
|
67
|
+
}
|
|
68
|
+
/** The files a person must keep owner-only: any that others can read. */
|
|
69
|
+
export function githubAppFilesTooOpen(file = defaultGithubAppFile()) {
|
|
70
|
+
const open = [];
|
|
71
|
+
const check = (f) => {
|
|
72
|
+
try {
|
|
73
|
+
if ((fs.statSync(f).mode & 0o077) !== 0)
|
|
74
|
+
open.push(f);
|
|
75
|
+
}
|
|
76
|
+
catch {
|
|
77
|
+
/* missing: loadGithubAppConfig reports it */
|
|
78
|
+
}
|
|
79
|
+
};
|
|
80
|
+
check(file);
|
|
81
|
+
try {
|
|
82
|
+
const keyFile = parseEnvFile(fs.readFileSync(file, "utf8")).GITHUB_APP_PRIVATE_KEY_FILE;
|
|
83
|
+
if (keyFile)
|
|
84
|
+
check(keyFile.startsWith("~/") ? path.join(os.homedir(), keyFile.slice(2)) : keyFile);
|
|
85
|
+
}
|
|
86
|
+
catch {
|
|
87
|
+
/* unreadable: loadGithubAppConfig reports it */
|
|
88
|
+
}
|
|
89
|
+
return open;
|
|
90
|
+
}
|
|
91
|
+
const b64url = (b) => Buffer.from(b).toString("base64url");
|
|
92
|
+
/** The App JWT (RS256): 10 minutes at most, issued 60 s in the past for clock skew. */
|
|
93
|
+
export function signAppJwt(appId, privateKey, nowMs) {
|
|
94
|
+
const iat = Math.floor(nowMs / 1000) - 60;
|
|
95
|
+
const head = b64url(JSON.stringify({ alg: "RS256", typ: "JWT" }));
|
|
96
|
+
const body = b64url(JSON.stringify({ iat, exp: iat + 9 * 60, iss: appId }));
|
|
97
|
+
const sig = createSign("RSA-SHA256").update(`${head}.${body}`).sign(privateKey);
|
|
98
|
+
return `${head}.${body}.${b64url(sig)}`;
|
|
99
|
+
}
|
|
100
|
+
const API = "https://api.github.com";
|
|
101
|
+
const REFRESH_BEFORE_MS = 5 * 60_000;
|
|
102
|
+
export function createInstallationTokenSource(config, o = {}) {
|
|
103
|
+
const doFetch = o.fetchImpl ?? fetch;
|
|
104
|
+
const now = o.now ?? Date.now;
|
|
105
|
+
let cached;
|
|
106
|
+
let installationId = config.installationId;
|
|
107
|
+
const call = async (method, url) => {
|
|
108
|
+
const res = await doFetch(`${API}${url}`, {
|
|
109
|
+
method,
|
|
110
|
+
headers: {
|
|
111
|
+
Authorization: `Bearer ${signAppJwt(config.appId, config.privateKey, now())}`,
|
|
112
|
+
Accept: "application/vnd.github+json",
|
|
113
|
+
"X-GitHub-Api-Version": "2022-11-28",
|
|
114
|
+
"User-Agent": "synap-worker",
|
|
115
|
+
},
|
|
116
|
+
});
|
|
117
|
+
if (!res.ok)
|
|
118
|
+
throw new Error(`GitHub ${method} ${url} answered ${res.status}`);
|
|
119
|
+
return res.json();
|
|
120
|
+
};
|
|
121
|
+
return {
|
|
122
|
+
async get() {
|
|
123
|
+
if (cached && cached.expiresAt - now() > REFRESH_BEFORE_MS)
|
|
124
|
+
return cached.token;
|
|
125
|
+
if (!installationId) {
|
|
126
|
+
const list = (await call("GET", "/app/installations"));
|
|
127
|
+
if (!Array.isArray(list) || list.length !== 1) {
|
|
128
|
+
throw new Error(`the App has ${Array.isArray(list) ? list.length : "an unreadable number of"} installations — set GITHUB_APP_INSTALLATION_ID`);
|
|
129
|
+
}
|
|
130
|
+
installationId = String(list[0].id);
|
|
131
|
+
}
|
|
132
|
+
const r = (await call("POST", `/app/installations/${installationId}/access_tokens`));
|
|
133
|
+
if (!r.token || !r.expires_at)
|
|
134
|
+
throw new Error("GitHub answered no installation token");
|
|
135
|
+
cached = { token: r.token, expiresAt: Date.parse(r.expires_at) };
|
|
136
|
+
return r.token;
|
|
137
|
+
},
|
|
138
|
+
};
|
|
139
|
+
}
|
|
140
|
+
/**
|
|
141
|
+
* The environment that makes `gh` AND `git push` (https) act as the App for
|
|
142
|
+
* the whole run: both ask the broker for a fresh token each time, and the
|
|
143
|
+
* credential-helper list is reset first, so a helper holding a person's login
|
|
144
|
+
* is never asked.
|
|
145
|
+
*/
|
|
146
|
+
export function githubRunEnv(g, basePath) {
|
|
147
|
+
return {
|
|
148
|
+
PATH: [g.binDir, basePath].filter(Boolean).join(path.delimiter),
|
|
149
|
+
SYNAP_GH_SOCK: g.sock,
|
|
150
|
+
GH_CONFIG_DIR: g.ghConfigDir,
|
|
151
|
+
GH_TOKEN: g.token,
|
|
152
|
+
GITHUB_TOKEN: g.token,
|
|
153
|
+
GIT_TERMINAL_PROMPT: "0",
|
|
154
|
+
GIT_CONFIG_COUNT: "2",
|
|
155
|
+
GIT_CONFIG_KEY_0: "credential.helper",
|
|
156
|
+
GIT_CONFIG_VALUE_0: "",
|
|
157
|
+
GIT_CONFIG_KEY_1: "credential.helper",
|
|
158
|
+
GIT_CONFIG_VALUE_1: `!${g.gitHelper}`,
|
|
159
|
+
};
|
|
160
|
+
}
|
|
161
|
+
//# sourceMappingURL=worker-github-app.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"worker-github-app.js","sourceRoot":"","sources":["../../src/commands/worker-github-app.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
import { createVerify, generateKeyPairSync } from "node:crypto";
|
|
2
|
+
import fs from "node:fs";
|
|
3
|
+
import os from "node:os";
|
|
4
|
+
import path from "node:path";
|
|
5
|
+
import { describe, expect, it } from "vitest";
|
|
6
|
+
import { createInstallationTokenSource, githubAppFilesTooOpen, githubRunEnv, loadGithubAppConfig, parseEnvFile, signAppJwt, } from "./worker-github-app.js";
|
|
7
|
+
const { privateKey, publicKey } = generateKeyPairSync("rsa", {
|
|
8
|
+
modulusLength: 2048,
|
|
9
|
+
privateKeyEncoding: { type: "pkcs8", format: "pem" },
|
|
10
|
+
publicKeyEncoding: { type: "spki", format: "pem" },
|
|
11
|
+
});
|
|
12
|
+
const tmp = () => fs.mkdtempSync(path.join(os.tmpdir(), "gh-app-"));
|
|
13
|
+
describe("signAppJwt", () => {
|
|
14
|
+
it("is a RS256 JWT for the app id that its public key verifies, valid under 10 minutes", () => {
|
|
15
|
+
const now = Date.parse("2026-10-28T12:00:00Z");
|
|
16
|
+
const [h, b, s] = signAppJwt("123", privateKey, now).split(".");
|
|
17
|
+
expect(JSON.parse(Buffer.from(h, "base64url").toString())).toEqual({ alg: "RS256", typ: "JWT" });
|
|
18
|
+
const body = JSON.parse(Buffer.from(b, "base64url").toString());
|
|
19
|
+
expect(body.iss).toBe("123");
|
|
20
|
+
expect(body.exp - body.iat).toBeLessThanOrEqual(600);
|
|
21
|
+
expect(createVerify("RSA-SHA256").update(`${h}.${b}`).verify(publicKey, Buffer.from(s, "base64url"))).toBe(true);
|
|
22
|
+
});
|
|
23
|
+
});
|
|
24
|
+
describe("loadGithubAppConfig", () => {
|
|
25
|
+
it("no file is undefined (the caller warns), never a throw", () => {
|
|
26
|
+
expect(loadGithubAppConfig(path.join(tmp(), "none.env"))).toBeUndefined();
|
|
27
|
+
});
|
|
28
|
+
it("reads the id, the key by path and the installation id", () => {
|
|
29
|
+
const d = tmp();
|
|
30
|
+
fs.writeFileSync(path.join(d, "k.pem"), privateKey, { mode: 0o600 });
|
|
31
|
+
fs.writeFileSync(path.join(d, "a.env"), `# the Synap Agent App\nGITHUB_APP_ID=42\nGITHUB_APP_PRIVATE_KEY_FILE="${path.join(d, "k.pem")}"\nGITHUB_APP_INSTALLATION_ID=7\n`, { mode: 0o600 });
|
|
32
|
+
const cfg = loadGithubAppConfig(path.join(d, "a.env"));
|
|
33
|
+
expect(cfg).toMatchObject({ appId: "42", installationId: "7" });
|
|
34
|
+
expect(cfg?.privateKey).toContain("BEGIN PRIVATE KEY");
|
|
35
|
+
expect(githubAppFilesTooOpen(path.join(d, "a.env"))).toEqual([]);
|
|
36
|
+
});
|
|
37
|
+
it("a file that is there but unusable THROWS — never a silent fall back to a person's login", () => {
|
|
38
|
+
const d = tmp();
|
|
39
|
+
fs.writeFileSync(path.join(d, "a.env"), "GITHUB_APP_ID=42\n");
|
|
40
|
+
expect(() => loadGithubAppConfig(path.join(d, "a.env"))).toThrow(/GITHUB_APP_PRIVATE_KEY_FILE/);
|
|
41
|
+
fs.writeFileSync(path.join(d, "b.env"), `GITHUB_APP_ID=42\nGITHUB_APP_PRIVATE_KEY_FILE=${path.join(d, "missing.pem")}\n`);
|
|
42
|
+
expect(() => loadGithubAppConfig(path.join(d, "b.env"))).toThrow(/could not be read/);
|
|
43
|
+
});
|
|
44
|
+
it("names a key or env file that others can read", () => {
|
|
45
|
+
const d = tmp();
|
|
46
|
+
fs.writeFileSync(path.join(d, "k.pem"), privateKey, { mode: 0o644 });
|
|
47
|
+
fs.writeFileSync(path.join(d, "a.env"), `GITHUB_APP_ID=1\nGITHUB_APP_PRIVATE_KEY_FILE=${path.join(d, "k.pem")}\n`, { mode: 0o600 });
|
|
48
|
+
expect(githubAppFilesTooOpen(path.join(d, "a.env"))).toEqual([path.join(d, "k.pem")]);
|
|
49
|
+
});
|
|
50
|
+
it("parseEnvFile ignores comments and strips quotes", () => {
|
|
51
|
+
expect(parseEnvFile("# A=1\nB='x y'\nexport C=3")).toEqual({ B: "x y", C: "3" });
|
|
52
|
+
});
|
|
53
|
+
});
|
|
54
|
+
describe("createInstallationTokenSource", () => {
|
|
55
|
+
const T0 = Date.parse("2026-10-28T12:00:00Z");
|
|
56
|
+
const fakeFetch = (log, opts = {}) => (async (url, init) => {
|
|
57
|
+
log.push(`${init.method} ${url.replace("https://api.github.com", "")}`);
|
|
58
|
+
expect(init.headers.Authorization).toMatch(/^Bearer [\w-]+\.[\w-]+\.[\w-]+$/);
|
|
59
|
+
const ok = (opts.status ?? 200) < 400;
|
|
60
|
+
if (url.endsWith("/app/installations"))
|
|
61
|
+
return { ok, status: opts.status ?? 200, json: async () => opts.installations ?? [{ id: 9 }] };
|
|
62
|
+
return {
|
|
63
|
+
ok,
|
|
64
|
+
status: opts.status ?? 200,
|
|
65
|
+
json: async () => ({ token: `ghs_${log.length}`, expires_at: new Date(T0 + 3600_000).toISOString() }),
|
|
66
|
+
};
|
|
67
|
+
});
|
|
68
|
+
it("finds the single installation, mints a token, and reuses it until 5 min before it ends", async () => {
|
|
69
|
+
const log = [];
|
|
70
|
+
let t = T0;
|
|
71
|
+
const src = createInstallationTokenSource({ appId: "1", privateKey }, { fetchImpl: fakeFetch(log), now: () => t });
|
|
72
|
+
const a = await src.get();
|
|
73
|
+
expect(log).toEqual(["GET /app/installations", "POST /app/installations/9/access_tokens"]);
|
|
74
|
+
t = T0 + 50 * 60_000;
|
|
75
|
+
expect(await src.get()).toBe(a);
|
|
76
|
+
expect(log).toHaveLength(2);
|
|
77
|
+
t = T0 + 56 * 60_000;
|
|
78
|
+
expect(await src.get()).not.toBe(a);
|
|
79
|
+
expect(log).toHaveLength(3);
|
|
80
|
+
});
|
|
81
|
+
it("two installations and no id is an error, not a guess", async () => {
|
|
82
|
+
const src = createInstallationTokenSource({ appId: "1", privateKey }, { fetchImpl: fakeFetch([], { installations: [{ id: 1 }, { id: 2 }] }), now: () => T0 });
|
|
83
|
+
await expect(src.get()).rejects.toThrow(/GITHUB_APP_INSTALLATION_ID/);
|
|
84
|
+
});
|
|
85
|
+
it("a refused call throws (the worker then does not launch)", async () => {
|
|
86
|
+
const src = createInstallationTokenSource({ appId: "1", privateKey, installationId: "9" }, { fetchImpl: fakeFetch([], { status: 401 }), now: () => T0 });
|
|
87
|
+
await expect(src.get()).rejects.toThrow(/401/);
|
|
88
|
+
});
|
|
89
|
+
});
|
|
90
|
+
describe("githubRunEnv", () => {
|
|
91
|
+
const g = { token: "ghs_x", sock: "/s", binDir: "/b", gitHelper: "/b/git-credential-synap", ghConfigDir: "/c" };
|
|
92
|
+
it("puts the helpers first on PATH, gh on an empty config dir, and resets the credential helpers first", () => {
|
|
93
|
+
const e = githubRunEnv(g, "/usr/bin:/bin");
|
|
94
|
+
expect(e.PATH).toBe("/b:/usr/bin:/bin");
|
|
95
|
+
expect(e.GH_CONFIG_DIR).toBe("/c");
|
|
96
|
+
expect(e.SYNAP_GH_SOCK).toBe("/s");
|
|
97
|
+
expect(e.GH_TOKEN).toBe("ghs_x");
|
|
98
|
+
expect([e.GIT_CONFIG_KEY_0, e.GIT_CONFIG_VALUE_0]).toEqual(["credential.helper", ""]);
|
|
99
|
+
expect(e.GIT_CONFIG_VALUE_1).toBe("!/b/git-credential-synap");
|
|
100
|
+
});
|
|
101
|
+
});
|
|
102
|
+
//# sourceMappingURL=worker-github-app.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"worker-github-app.test.js","sourceRoot":"","sources":["../../src/commands/worker-github-app.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Keeps a long run on its GitHub identity.
|
|
3
|
+
*
|
|
4
|
+
* An installation token lasts 1 h; a static GH_TOKEN would stop working under
|
|
5
|
+
* a longer run. So a run's `gh` and `git` ask the WORKER for a token each time
|
|
6
|
+
* they need one, over an owner-only unix socket, and the worker mints or
|
|
7
|
+
* reuses one from the App key (worker-github-app.ts). The key stays in the
|
|
8
|
+
* worker process; a run is handed tokens, never the key, and its environment
|
|
9
|
+
* carries neither the key file's path nor a long-lived secret.
|
|
10
|
+
*
|
|
11
|
+
* <binDir>/gh `gh` with GH_TOKEN set to a fresh token
|
|
12
|
+
* <binDir>/git-credential-synap git credential helper (https pushes)
|
|
13
|
+
* <binDir>/synap-gh-token asks the broker, prints the token
|
|
14
|
+
*
|
|
15
|
+
* The runs' PATH starts with <binDir>. An absolute /usr/bin/gh still finds no
|
|
16
|
+
* login: the run's GH_CONFIG_DIR is an empty folder (the person's hosts.yml is
|
|
17
|
+
* never read).
|
|
18
|
+
*/
|
|
19
|
+
import type { InstallationTokenSource } from "./worker-github-app.js";
|
|
20
|
+
export interface TokenBroker {
|
|
21
|
+
/** The unix socket a run's helpers connect to (SYNAP_GH_SOCK). */
|
|
22
|
+
sock: string;
|
|
23
|
+
ready: Promise<void>;
|
|
24
|
+
close(): void;
|
|
25
|
+
}
|
|
26
|
+
/** One request = one connection: the answer is `OK <token>\n` or `ERR <message>\n`. */
|
|
27
|
+
export declare function startTokenBroker(source: InstallationTokenSource): TokenBroker;
|
|
28
|
+
/** The first `name` on PATH that is not inside `exclude`. */
|
|
29
|
+
export declare function findOnPath(name: string, searchPath: string | undefined, exclude: string): string | undefined;
|
|
30
|
+
/**
|
|
31
|
+
* Writes the three helpers. `realGh`: the `gh` to call (undefined: no `gh`
|
|
32
|
+
* wrapper, the box has none). Returns the helper paths.
|
|
33
|
+
*/
|
|
34
|
+
export declare function writeGithubHelpers(binDir: string, o?: {
|
|
35
|
+
realGh?: string;
|
|
36
|
+
nodeBin?: string;
|
|
37
|
+
}): {
|
|
38
|
+
gitHelper: string;
|
|
39
|
+
};
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Keeps a long run on its GitHub identity.
|
|
3
|
+
*
|
|
4
|
+
* An installation token lasts 1 h; a static GH_TOKEN would stop working under
|
|
5
|
+
* a longer run. So a run's `gh` and `git` ask the WORKER for a token each time
|
|
6
|
+
* they need one, over an owner-only unix socket, and the worker mints or
|
|
7
|
+
* reuses one from the App key (worker-github-app.ts). The key stays in the
|
|
8
|
+
* worker process; a run is handed tokens, never the key, and its environment
|
|
9
|
+
* carries neither the key file's path nor a long-lived secret.
|
|
10
|
+
*
|
|
11
|
+
* <binDir>/gh `gh` with GH_TOKEN set to a fresh token
|
|
12
|
+
* <binDir>/git-credential-synap git credential helper (https pushes)
|
|
13
|
+
* <binDir>/synap-gh-token asks the broker, prints the token
|
|
14
|
+
*
|
|
15
|
+
* The runs' PATH starts with <binDir>. An absolute /usr/bin/gh still finds no
|
|
16
|
+
* login: the run's GH_CONFIG_DIR is an empty folder (the person's hosts.yml is
|
|
17
|
+
* never read).
|
|
18
|
+
*/
|
|
19
|
+
import fs from "node:fs";
|
|
20
|
+
import net from "node:net";
|
|
21
|
+
import os from "node:os";
|
|
22
|
+
import path from "node:path";
|
|
23
|
+
import { writeSecretFile } from "../lib/atomic-file.js";
|
|
24
|
+
/** One request = one connection: the answer is `OK <token>\n` or `ERR <message>\n`. */
|
|
25
|
+
export function startTokenBroker(source) {
|
|
26
|
+
// Under the tmp dir, not the state dir: a unix socket path is limited to ~104 bytes.
|
|
27
|
+
const dir = fs.mkdtempSync(path.join(os.tmpdir(), "synap-gh-"));
|
|
28
|
+
fs.chmodSync(dir, 0o700);
|
|
29
|
+
const sock = path.join(dir, "s");
|
|
30
|
+
const server = net.createServer((conn) => {
|
|
31
|
+
source.get().then((t) => conn.end(`OK ${t}\n`), (err) => conn.end(`ERR ${err.message.replace(/\s+/g, " ")}\n`));
|
|
32
|
+
conn.on("error", () => conn.destroy());
|
|
33
|
+
});
|
|
34
|
+
server.unref(); // never what keeps the worker process alive
|
|
35
|
+
const ready = new Promise((resolve, reject) => {
|
|
36
|
+
server.once("error", reject);
|
|
37
|
+
server.listen(sock, () => {
|
|
38
|
+
fs.chmodSync(sock, 0o600);
|
|
39
|
+
resolve();
|
|
40
|
+
});
|
|
41
|
+
});
|
|
42
|
+
return {
|
|
43
|
+
sock,
|
|
44
|
+
ready,
|
|
45
|
+
close: () => {
|
|
46
|
+
server.close();
|
|
47
|
+
fs.rmSync(dir, { recursive: true, force: true });
|
|
48
|
+
},
|
|
49
|
+
};
|
|
50
|
+
}
|
|
51
|
+
const sh = (s) => `'${s.replace(/'/g, `'\\''`)}'`;
|
|
52
|
+
/** The first `name` on PATH that is not inside `exclude`. */
|
|
53
|
+
export function findOnPath(name, searchPath, exclude) {
|
|
54
|
+
for (const d of (searchPath ?? "").split(path.delimiter)) {
|
|
55
|
+
if (!d || path.resolve(d) === path.resolve(exclude))
|
|
56
|
+
continue;
|
|
57
|
+
const f = path.join(d, name);
|
|
58
|
+
try {
|
|
59
|
+
fs.accessSync(f, fs.constants.X_OK);
|
|
60
|
+
if (fs.statSync(f).isFile())
|
|
61
|
+
return f;
|
|
62
|
+
}
|
|
63
|
+
catch {
|
|
64
|
+
/* not here */
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
return undefined;
|
|
68
|
+
}
|
|
69
|
+
/**
|
|
70
|
+
* Writes the three helpers. `realGh`: the `gh` to call (undefined: no `gh`
|
|
71
|
+
* wrapper, the box has none). Returns the helper paths.
|
|
72
|
+
*/
|
|
73
|
+
export function writeGithubHelpers(binDir, o = {}) {
|
|
74
|
+
fs.mkdirSync(binDir, { recursive: true, mode: 0o700 });
|
|
75
|
+
const node = o.nodeBin ?? process.execPath;
|
|
76
|
+
const tokenBin = path.join(binDir, "synap-gh-token");
|
|
77
|
+
// writeSecretFile chmods after writing: a helper left by an earlier run is tightened too.
|
|
78
|
+
const put = (name, body) => writeSecretFile(path.join(binDir, name), body, { mode: 0o700 });
|
|
79
|
+
put("synap-gh-token", `#!${node}
|
|
80
|
+
const net = require("node:net");
|
|
81
|
+
const c = net.connect(process.env.SYNAP_GH_SOCK || "");
|
|
82
|
+
let buf = "";
|
|
83
|
+
c.on("data", (d) => (buf += d));
|
|
84
|
+
c.on("error", (e) => { console.error("synap-gh-token: " + e.message); process.exit(1); });
|
|
85
|
+
c.on("end", () => {
|
|
86
|
+
const m = /^OK (.+)\\n$/.exec(buf);
|
|
87
|
+
if (!m) { console.error("synap-gh-token: " + (buf.trim() || "no answer")); process.exit(1); }
|
|
88
|
+
process.stdout.write(m[1]);
|
|
89
|
+
});
|
|
90
|
+
`);
|
|
91
|
+
put("git-credential-synap", `#!/bin/sh
|
|
92
|
+
# git credential helper: only "get" answers; the token is asked for now, never stored.
|
|
93
|
+
[ "$1" = get ] || exit 0
|
|
94
|
+
cat >/dev/null
|
|
95
|
+
t=$(${sh(tokenBin)}) || exit 1
|
|
96
|
+
printf 'username=x-access-token\\npassword=%s\\n' "$t"
|
|
97
|
+
`);
|
|
98
|
+
if (o.realGh) {
|
|
99
|
+
put("gh", `#!/bin/sh
|
|
100
|
+
t=$(${sh(tokenBin)}) || exit 1
|
|
101
|
+
GH_TOKEN="$t" GITHUB_TOKEN="$t" exec ${sh(o.realGh)} "$@"
|
|
102
|
+
`);
|
|
103
|
+
}
|
|
104
|
+
return { gitHelper: path.join(binDir, "git-credential-synap") };
|
|
105
|
+
}
|
|
106
|
+
//# sourceMappingURL=worker-github-broker.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"worker-github-broker.js","sourceRoot":"","sources":["../../src/commands/worker-github-broker.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
import { execFile } from "node:child_process";
|
|
2
|
+
import fs from "node:fs";
|
|
3
|
+
import os from "node:os";
|
|
4
|
+
import path from "node:path";
|
|
5
|
+
import { generateKeyPairSync } from "node:crypto";
|
|
6
|
+
import { afterEach, describe, expect, it } from "vitest";
|
|
7
|
+
import { createInstallationTokenSource } from "./worker-github-app.js";
|
|
8
|
+
import { findOnPath, startTokenBroker, writeGithubHelpers } from "./worker-github-broker.js";
|
|
9
|
+
const { privateKey } = generateKeyPairSync("rsa", { modulusLength: 2048, privateKeyEncoding: { type: "pkcs8", format: "pem" }, publicKeyEncoding: { type: "spki", format: "pem" } });
|
|
10
|
+
const T0 = Date.parse("2026-10-28T12:00:00Z");
|
|
11
|
+
let t = T0;
|
|
12
|
+
let minted = 0;
|
|
13
|
+
const fakeFetch = (async (url) => {
|
|
14
|
+
if (url.endsWith("/app/installations"))
|
|
15
|
+
return { ok: true, status: 200, json: async () => [{ id: 9 }] };
|
|
16
|
+
minted++;
|
|
17
|
+
return { ok: true, status: 200, json: async () => ({ token: `ghs_${minted}`, expires_at: new Date(t + 3600_000).toISOString() }) };
|
|
18
|
+
});
|
|
19
|
+
const run = (bin, args, env, input) => new Promise((resolve) => {
|
|
20
|
+
const c = execFile(bin, args, { env }, (err, stdout, stderr) => resolve({ code: err ? (err.code ?? 1) : 0, stdout, stderr }));
|
|
21
|
+
// A helper may exit without reading its stdin (the git helper on `store`):
|
|
22
|
+
// the write then fails with EPIPE, which is the child's choice, not a test failure.
|
|
23
|
+
c.stdin?.on("error", () => { });
|
|
24
|
+
c.stdin?.end(input ?? "");
|
|
25
|
+
});
|
|
26
|
+
const closers = [];
|
|
27
|
+
afterEach(() => closers.splice(0).forEach((c) => c()));
|
|
28
|
+
const setup = async (source = createInstallationTokenSource({ appId: "1", privateKey }, { fetchImpl: fakeFetch, now: () => t })) => {
|
|
29
|
+
t = T0;
|
|
30
|
+
minted = 0;
|
|
31
|
+
const dir = fs.mkdtempSync(path.join(os.tmpdir(), "bk-"));
|
|
32
|
+
const realGh = path.join(dir, "real-gh");
|
|
33
|
+
fs.writeFileSync(realGh, '#!/bin/sh\necho "gh-as:$GH_TOKEN args:$*"\n', { mode: 0o755 });
|
|
34
|
+
const binDir = path.join(dir, "bin");
|
|
35
|
+
const { gitHelper } = writeGithubHelpers(binDir, { realGh });
|
|
36
|
+
const broker = startTokenBroker(source);
|
|
37
|
+
closers.push(() => broker.close());
|
|
38
|
+
await broker.ready;
|
|
39
|
+
const env = { SYNAP_GH_SOCK: broker.sock, PATH: `${binDir}:/usr/bin:/bin` };
|
|
40
|
+
return { binDir, gitHelper, env, broker };
|
|
41
|
+
};
|
|
42
|
+
describe("the GitHub helpers a run uses", () => {
|
|
43
|
+
it("gh runs with a token that is still valid after the first hour (the clock is stubbed)", async () => {
|
|
44
|
+
const { binDir, env } = await setup();
|
|
45
|
+
const first = await run(path.join(binDir, "gh"), ["pr", "list"], env);
|
|
46
|
+
expect(first.stdout.trim()).toBe("gh-as:ghs_1 args:pr list");
|
|
47
|
+
t = T0 + 30 * 60_000;
|
|
48
|
+
expect((await run(path.join(binDir, "gh"), [], env)).stdout).toContain("ghs_1"); // reused, not re-minted
|
|
49
|
+
t = T0 + 61 * 60_000; // past the first token's end
|
|
50
|
+
const later = await run(path.join(binDir, "gh"), ["pr", "list"], env);
|
|
51
|
+
expect(later.stdout.trim()).toBe("gh-as:ghs_2 args:pr list");
|
|
52
|
+
});
|
|
53
|
+
it("the git credential helper answers `get` with a fresh token and nothing for other operations", async () => {
|
|
54
|
+
const { gitHelper, env } = await setup();
|
|
55
|
+
const get = await run(gitHelper, ["get"], env, "protocol=https\nhost=github.com\n\n");
|
|
56
|
+
expect(get.stdout).toBe("username=x-access-token\npassword=ghs_1\n");
|
|
57
|
+
t = T0 + 61 * 60_000;
|
|
58
|
+
expect((await run(gitHelper, ["get"], env, "host=github.com\n\n")).stdout).toContain("password=ghs_2");
|
|
59
|
+
expect((await run(gitHelper, ["store"], env, "x\n")).stdout).toBe("");
|
|
60
|
+
});
|
|
61
|
+
it("a broker that cannot mint makes gh fail without calling the real gh (no fall back to another login)", async () => {
|
|
62
|
+
const { binDir, env } = await setup({
|
|
63
|
+
get: async () => {
|
|
64
|
+
throw new Error("GitHub answered 401");
|
|
65
|
+
},
|
|
66
|
+
});
|
|
67
|
+
const r = await run(path.join(binDir, "gh"), ["pr", "list"], env);
|
|
68
|
+
expect(r.code).not.toBe(0);
|
|
69
|
+
expect(r.stdout).not.toContain("gh-as");
|
|
70
|
+
expect(r.stderr).toContain("401");
|
|
71
|
+
});
|
|
72
|
+
it("the key is never handed over: neither helper nor answer carries it", async () => {
|
|
73
|
+
const { binDir, env } = await setup();
|
|
74
|
+
const out = (await run(path.join(binDir, "gh"), [], env)).stdout;
|
|
75
|
+
expect(out).not.toContain("PRIVATE KEY");
|
|
76
|
+
for (const f of fs.readdirSync(binDir))
|
|
77
|
+
expect(fs.readFileSync(path.join(binDir, f), "utf8")).not.toContain("PRIVATE KEY");
|
|
78
|
+
});
|
|
79
|
+
});
|
|
80
|
+
describe("findOnPath", () => {
|
|
81
|
+
it("skips the helpers' own folder, so the wrapper never calls itself", () => {
|
|
82
|
+
const a = fs.mkdtempSync(path.join(os.tmpdir(), "fp-"));
|
|
83
|
+
const b = fs.mkdtempSync(path.join(os.tmpdir(), "fp-"));
|
|
84
|
+
for (const d of [a, b])
|
|
85
|
+
fs.writeFileSync(path.join(d, "gh"), "#!/bin/sh\n", { mode: 0o755 });
|
|
86
|
+
expect(findOnPath("gh", `${a}:${b}`, a)).toBe(path.join(b, "gh"));
|
|
87
|
+
expect(findOnPath("nope", `${a}:${b}`, a)).toBeUndefined();
|
|
88
|
+
});
|
|
89
|
+
});
|
|
90
|
+
//# sourceMappingURL=worker-github-broker.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"worker-github-broker.test.js","sourceRoot":"","sources":["../../src/commands/worker-github-broker.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|