@intentic/sandbox-contract 1.310.0 → 1.312.0
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/README.md +43 -159
- package/dist/chores/chores.js +6 -6
- package/dist/chores/chores.js.map +1 -1
- package/dist/chores/fix-deps.d.ts.map +1 -1
- package/dist/chores/fix-deps.js +3 -2
- package/dist/chores/fix-deps.js.map +1 -1
- package/dist/chores/prompt.d.ts.map +1 -1
- package/dist/chores/prompt.js +3 -2
- package/dist/chores/prompt.js.map +1 -1
- package/dist/contracts/accounts.contract.d.ts +15 -0
- package/dist/contracts/accounts.contract.d.ts.map +1 -1
- package/dist/contracts/agent.contract.d.ts +15 -0
- package/dist/contracts/agent.contract.d.ts.map +1 -1
- package/dist/contracts/agents.contract.d.ts +1228 -32
- package/dist/contracts/agents.contract.d.ts.map +1 -1
- package/dist/contracts/agents.contract.js +42 -3
- package/dist/contracts/agents.contract.js.map +1 -1
- package/dist/contracts/capabilities.contract.d.ts +2 -0
- package/dist/contracts/capabilities.contract.d.ts.map +1 -1
- package/dist/contracts/device.contract.d.ts +2 -0
- package/dist/contracts/device.contract.d.ts.map +1 -1
- package/dist/contracts/extensions.contract.d.ts +2 -0
- package/dist/contracts/extensions.contract.d.ts.map +1 -1
- package/dist/contracts/offload.contract.d.ts +107 -0
- package/dist/contracts/offload.contract.d.ts.map +1 -0
- package/dist/contracts/offload.contract.js +53 -0
- package/dist/contracts/offload.contract.js.map +1 -0
- package/dist/contracts/ports.contract.d.ts +5 -0
- package/dist/contracts/ports.contract.d.ts.map +1 -1
- package/dist/contracts/runner.contract.d.ts +111 -0
- package/dist/contracts/runner.contract.d.ts.map +1 -1
- package/dist/contracts/runner.contract.js +3 -1
- package/dist/contracts/runner.contract.js.map +1 -1
- package/dist/contracts/settings.contract.d.ts +16 -0
- package/dist/contracts/settings.contract.d.ts.map +1 -1
- package/dist/contracts/system.contract.d.ts +154 -0
- package/dist/contracts/system.contract.d.ts.map +1 -1
- package/dist/contracts/translator.contract.d.ts +16 -0
- package/dist/contracts/translator.contract.d.ts.map +1 -1
- package/dist/contracts/workspace.contract.d.ts +146 -0
- package/dist/contracts/workspace.contract.d.ts.map +1 -1
- package/dist/contracts/workspace.contract.js +40 -3
- package/dist/contracts/workspace.contract.js.map +1 -1
- package/dist/documents/conversions.d.ts +81 -0
- package/dist/documents/conversions.d.ts.map +1 -0
- package/dist/documents/conversions.js +213 -0
- package/dist/documents/conversions.js.map +1 -0
- package/dist/documents/index.d.ts +3 -0
- package/dist/documents/index.d.ts.map +1 -0
- package/dist/documents/index.js +3 -0
- package/dist/documents/index.js.map +1 -0
- package/dist/documents/passthrough.d.ts +13 -0
- package/dist/documents/passthrough.d.ts.map +1 -0
- package/dist/documents/passthrough.js +101 -0
- package/dist/documents/passthrough.js.map +1 -0
- package/dist/events/agent-events.d.ts +20 -0
- package/dist/events/agent-events.d.ts.map +1 -1
- package/dist/events/agent-events.js +6 -1
- package/dist/events/agent-events.js.map +1 -1
- package/dist/events/land-breakage.d.ts +3 -0
- package/dist/events/land-breakage.d.ts.map +1 -1
- package/dist/events/land-breakage.js +3 -0
- package/dist/events/land-breakage.js.map +1 -1
- package/dist/events/land-conflict.d.ts +3 -0
- package/dist/events/land-conflict.d.ts.map +1 -0
- package/dist/events/land-conflict.js +4 -0
- package/dist/events/land-conflict.js.map +1 -0
- package/dist/events/system-events.d.ts +114 -0
- package/dist/events/system-events.d.ts.map +1 -1
- package/dist/events/system-events.js +3 -1
- package/dist/events/system-events.js.map +1 -1
- package/dist/events/transcript.d.ts +5 -0
- package/dist/events/transcript.d.ts.map +1 -1
- package/dist/events/transcript.js +9 -1
- package/dist/events/transcript.js.map +1 -1
- package/dist/events/verify-nudge.d.ts.map +1 -1
- package/dist/events/verify-nudge.js.map +1 -1
- package/dist/front/generated/wire.d.ts +137 -0
- package/dist/front/generated/wire.d.ts.map +1 -0
- package/dist/front/generated/wire.js +2 -0
- package/dist/front/generated/wire.js.map +1 -0
- package/dist/front/terminal-frames.d.ts +17 -0
- package/dist/front/terminal-frames.d.ts.map +1 -0
- package/dist/front/terminal-frames.js +79 -0
- package/dist/front/terminal-frames.js.map +1 -0
- package/dist/ids/conversation-ids.d.ts +2 -0
- package/dist/ids/conversation-ids.d.ts.map +1 -1
- package/dist/ids/conversation-ids.js +2 -0
- package/dist/ids/conversation-ids.js.map +1 -1
- package/dist/index.d.ts +1773 -96
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +8 -1
- package/dist/index.js.map +1 -1
- package/dist/policy/command-run.d.ts +1 -1
- package/dist/policy/command-run.d.ts.map +1 -1
- package/dist/policy/command-run.js +7 -4
- package/dist/policy/command-run.js.map +1 -1
- package/dist/protocol/container-requirements.d.ts +0 -1
- package/dist/protocol/container-requirements.d.ts.map +1 -1
- package/dist/protocol/container-requirements.js +1 -2
- package/dist/protocol/container-requirements.js.map +1 -1
- package/dist/protocol/ingress-contract.d.ts +0 -11
- package/dist/protocol/ingress-contract.d.ts.map +1 -1
- package/dist/protocol/raw-routes.d.ts +36 -4
- package/dist/protocol/raw-routes.d.ts.map +1 -1
- package/dist/protocol/raw-routes.js +18 -13
- package/dist/protocol/raw-routes.js.map +1 -1
- package/dist/protocol/route-meta.d.ts +2 -0
- package/dist/protocol/route-meta.d.ts.map +1 -1
- package/dist/protocol/route-meta.js.map +1 -1
- package/dist/protocol/runner-protocol.d.ts +34 -0
- package/dist/protocol/runner-protocol.d.ts.map +1 -1
- package/dist/protocol/runner-protocol.js +33 -0
- package/dist/protocol/runner-protocol.js.map +1 -1
- package/dist/protocol/tunnel-lanes.d.ts +10 -0
- package/dist/protocol/tunnel-lanes.d.ts.map +1 -0
- package/dist/protocol/tunnel-lanes.js +16 -0
- package/dist/protocol/tunnel-lanes.js.map +1 -0
- package/dist/schemas/activity.d.ts.map +1 -1
- package/dist/schemas/activity.js.map +1 -1
- package/dist/schemas/agents.d.ts +145 -3
- package/dist/schemas/agents.d.ts.map +1 -1
- package/dist/schemas/agents.js +43 -9
- package/dist/schemas/agents.js.map +1 -1
- package/dist/schemas/automations.d.ts +43 -0
- package/dist/schemas/automations.d.ts.map +1 -1
- package/dist/schemas/capabilities.d.ts +3 -0
- package/dist/schemas/capabilities.d.ts.map +1 -1
- package/dist/schemas/capabilities.js +4 -0
- package/dist/schemas/capabilities.js.map +1 -1
- package/dist/schemas/devices.d.ts +34 -6
- package/dist/schemas/devices.d.ts.map +1 -1
- package/dist/schemas/devices.js +8 -6
- package/dist/schemas/devices.js.map +1 -1
- package/dist/schemas/extension-updates.d.ts +4 -0
- package/dist/schemas/extension-updates.d.ts.map +1 -1
- package/dist/schemas/git/git.d.ts +16 -0
- package/dist/schemas/git/git.d.ts.map +1 -1
- package/dist/schemas/git/git.js +1 -0
- package/dist/schemas/git/git.js.map +1 -1
- package/dist/schemas/hosts.d.ts +8 -0
- package/dist/schemas/hosts.d.ts.map +1 -1
- package/dist/schemas/hosts.js +3 -0
- package/dist/schemas/hosts.js.map +1 -1
- package/dist/schemas/keep-warm.d.ts +68 -0
- package/dist/schemas/keep-warm.d.ts.map +1 -0
- package/dist/schemas/keep-warm.js +74 -0
- package/dist/schemas/keep-warm.js.map +1 -0
- package/dist/schemas/metrics.d.ts +7 -0
- package/dist/schemas/metrics.d.ts.map +1 -1
- package/dist/schemas/metrics.js +1 -0
- package/dist/schemas/metrics.js.map +1 -1
- package/dist/schemas/offload.d.ts +62 -0
- package/dist/schemas/offload.d.ts.map +1 -0
- package/dist/schemas/offload.js +23 -0
- package/dist/schemas/offload.js.map +1 -0
- package/dist/schemas/personas.js +3 -3
- package/dist/schemas/personas.js.map +1 -1
- package/dist/schemas/ports.d.ts +10 -0
- package/dist/schemas/ports.d.ts.map +1 -1
- package/dist/schemas/ports.js +8 -0
- package/dist/schemas/ports.js.map +1 -1
- package/dist/schemas/providers/plan-limits.d.ts +29 -0
- package/dist/schemas/providers/plan-limits.d.ts.map +1 -1
- package/dist/schemas/providers/plan-limits.js +7 -0
- package/dist/schemas/providers/plan-limits.js.map +1 -1
- package/dist/schemas/providers/provider-oauth.d.ts +15 -0
- package/dist/schemas/providers/provider-oauth.d.ts.map +1 -1
- package/dist/schemas/providers/provider-oauth.js +4 -0
- package/dist/schemas/providers/provider-oauth.js.map +1 -1
- package/dist/schemas/providers/usage.d.ts +6 -0
- package/dist/schemas/providers/usage.d.ts.map +1 -1
- package/dist/schemas/providers/usage.js +4 -0
- package/dist/schemas/providers/usage.js.map +1 -1
- package/dist/schemas/settings-history.d.ts +28 -0
- package/dist/schemas/settings-history.d.ts.map +1 -0
- package/dist/schemas/settings-history.js +39 -0
- package/dist/schemas/settings-history.js.map +1 -0
- package/dist/schemas/settings.d.ts +8 -0
- package/dist/schemas/settings.d.ts.map +1 -1
- package/dist/schemas/settings.js +33 -3
- package/dist/schemas/settings.js.map +1 -1
- package/dist/schemas/system.d.ts +24 -0
- package/dist/schemas/system.d.ts.map +1 -1
- package/dist/schemas/system.js +15 -0
- package/dist/schemas/system.js.map +1 -1
- package/dist/schemas/workspace/mainline.d.ts +371 -0
- package/dist/schemas/workspace/mainline.d.ts.map +1 -0
- package/dist/schemas/workspace/mainline.js +134 -0
- package/dist/schemas/workspace/mainline.js.map +1 -0
- package/dist/schemas/workspace/workspace-tree.d.ts +23 -0
- package/dist/schemas/workspace/workspace-tree.d.ts.map +1 -1
- package/dist/schemas/workspace/workspace-tree.js +34 -0
- package/dist/schemas/workspace/workspace-tree.js.map +1 -1
- package/dist/state/definition.d.ts +36 -4
- package/dist/state/definition.d.ts.map +1 -1
- package/dist/state/definition.js.map +1 -1
- package/dist/state/fix-stance.d.ts.map +1 -1
- package/dist/state/fix-stance.js +1 -0
- package/dist/state/fix-stance.js.map +1 -1
- package/dist/state/history-state.d.ts.map +1 -1
- package/dist/state/history-state.js +5 -0
- package/dist/state/history-state.js.map +1 -1
- package/dist/state/runtime-state.d.ts +6 -0
- package/dist/state/runtime-state.d.ts.map +1 -1
- package/dist/state/runtime-state.js +2 -0
- package/dist/state/runtime-state.js.map +1 -1
- package/dist/state/workspace-state.d.ts +22 -1
- package/dist/state/workspace-state.d.ts.map +1 -1
- package/dist/state/workspace-state.js +27 -1
- package/dist/state/workspace-state.js.map +1 -1
- package/dist/text/transcript-fold.d.ts +3 -0
- package/dist/text/transcript-fold.d.ts.map +1 -1
- package/dist/text/transcript-fold.js +30 -8
- package/dist/text/transcript-fold.js.map +1 -1
- package/dist/time/zone.d.ts +1 -0
- package/dist/time/zone.d.ts.map +1 -1
- package/dist/time/zone.js +5 -0
- package/dist/time/zone.js.map +1 -1
- package/package.json +34 -22
- package/src/chores/chores.ts +6 -6
- package/src/chores/fix-deps.ts +3 -2
- package/src/chores/prompt.ts +3 -2
- package/src/contracts/agents.contract.ts +50 -3
- package/src/contracts/offload.contract.ts +60 -0
- package/src/contracts/runner.contract.ts +7 -1
- package/src/contracts/workspace.contract.ts +54 -2
- package/src/documents/conversions.ts +402 -0
- package/src/documents/index.ts +5 -0
- package/src/documents/passthrough.ts +139 -0
- package/src/events/agent-events.ts +9 -1
- package/src/events/land-breakage.ts +12 -0
- package/src/events/land-conflict.ts +12 -0
- package/src/events/system-events.ts +9 -3
- package/src/events/transcript.ts +14 -3
- package/src/events/verify-nudge.ts +4 -3
- package/src/front/generated/wire.ts +104 -0
- package/src/front/terminal-frames.fixture.json +55 -0
- package/src/front/terminal-frames.test.ts +63 -0
- package/src/front/terminal-frames.ts +102 -0
- package/src/ids/conversation-ids.ts +7 -0
- package/src/ids/hostnames.fixture.json +20 -0
- package/src/ids/hostnames.test.ts +20 -0
- package/src/index.ts +8 -1
- package/src/policy/command-run.test.ts +35 -0
- package/src/policy/command-run.ts +10 -5
- package/src/protocol/container-requirements.test.ts +2 -3
- package/src/protocol/container-requirements.ts +1 -7
- package/src/protocol/ingress-contract.fixture.json +77 -0
- package/src/protocol/ingress-contract.test.ts +18 -26
- package/src/protocol/ingress-contract.ts +3 -22
- package/src/protocol/raw-routes.ts +21 -14
- package/src/protocol/route-meta.ts +4 -0
- package/src/protocol/runner-protocol.ts +53 -0
- package/src/protocol/tunnel-lanes.json +136 -0
- package/src/protocol/tunnel-lanes.test.ts +30 -0
- package/src/protocol/tunnel-lanes.ts +32 -0
- package/src/schemas/activity.ts +3 -1
- package/src/schemas/agents.ts +66 -10
- package/src/schemas/capabilities.ts +6 -0
- package/src/schemas/devices.ts +16 -9
- package/src/schemas/git/git.ts +5 -0
- package/src/schemas/hosts.ts +8 -0
- package/src/schemas/keep-warm.test.ts +68 -0
- package/src/schemas/keep-warm.ts +124 -0
- package/src/schemas/metrics.ts +2 -0
- package/src/schemas/offload.ts +44 -0
- package/src/schemas/personas.ts +3 -3
- package/src/schemas/ports.ts +10 -0
- package/src/schemas/providers/plan-limits.ts +13 -0
- package/src/schemas/providers/provider-oauth.ts +6 -0
- package/src/schemas/providers/usage.ts +7 -0
- package/src/schemas/settings-history.ts +61 -0
- package/src/schemas/settings.ts +70 -19
- package/src/schemas/system.ts +17 -0
- package/src/schemas/workspace/mainline.ts +201 -0
- package/src/schemas/workspace/workspace-tree.ts +47 -0
- package/src/state/definition.ts +3 -2
- package/src/state/fix-stance.test.ts +14 -0
- package/src/state/fix-stance.ts +2 -0
- package/src/state/history-state.ts +15 -0
- package/src/state/runtime-state.ts +8 -0
- package/src/state/workspace-state.ts +35 -1
- package/src/text/transcript-fold.test.ts +53 -1
- package/src/text/transcript-fold.ts +53 -10
- package/src/time/zone.ts +7 -0
- package/src/webext/index.ts +1 -1
- package/dist/protocol/ingress-protocol.d.ts +0 -30
- package/dist/protocol/ingress-protocol.d.ts.map +0 -1
- package/dist/protocol/ingress-protocol.js +0 -285
- package/dist/protocol/ingress-protocol.js.map +0 -1
- package/dist/protocol/terminal-protocol.d.ts +0 -18
- package/dist/protocol/terminal-protocol.d.ts.map +0 -1
- package/dist/protocol/terminal-protocol.js +0 -2
- package/dist/protocol/terminal-protocol.js.map +0 -1
- package/dist/protocol/tunnel-heartbeat.d.ts +0 -19
- package/dist/protocol/tunnel-heartbeat.d.ts.map +0 -1
- package/dist/protocol/tunnel-heartbeat.js +0 -41
- package/dist/protocol/tunnel-heartbeat.js.map +0 -1
- package/src/protocol/ingress-protocol.test.ts +0 -535
- package/src/protocol/ingress-protocol.ts +0 -448
- package/src/protocol/terminal-protocol.ts +0 -13
- package/src/protocol/tunnel-heartbeat.test.ts +0 -75
- package/src/protocol/tunnel-heartbeat.ts +0 -67
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
{
|
|
2
|
+
"publicKey": "-----BEGIN PUBLIC KEY-----\nMCowBQYDK2VwAyEAfiMlxPB/8tnjh6TYS3rVde63OXB/wY77KWWtJ39XjBg=\n-----END PUBLIC KEY-----\n",
|
|
3
|
+
"grants": [
|
|
4
|
+
{ "token": "ig1.eyJzdWIiOiJhYmNkZWYwMTIzNDUiLCJpYXQiOjE3MDAwMDAwMDB9.wdpBpqmMl21mqAZvRSYi4WfDejreTKDxHfOQp_sBKOTQB0_N2trL6iFcRxFUDrGBnGHmYnv-x66FgU_j57ZECA", "claim": {"sandboxId": "abcdef012345", "issuedAt": 1700000000} },
|
|
5
|
+
{ "token": "ig1.eyJzdWIiOiIwMTIzNDU2Nzg5YWIiLCJpYXQiOjE3NTcwMDAwMDAsImV4dHJhIjoiaWdub3JlZCJ9.NK2BR2L_ycCACdmnbZgkg7sovw-Nhlt5vIL6ldArAx_jN9XOh-k1wisviROAgU40YGBgUASIgA6Wdn5SyYHHCA", "claim": {"sandboxId": "0123456789ab", "issuedAt": 1757000000} },
|
|
6
|
+
{ "token": "ig1.eyJpYXQiOjE3MDAwMDAwMDAsInN1YiI6ImFiY2RlZjAxMjM0NSJ9.AenzYQn3Opfc-Sg_vkBrMEIOnCFZ3a46oSzjABAWyoVe3GP0vXlIM8MFgddHDQc7yow4kdfn1kHcUcsOoKkLCg", "claim": {"sandboxId": "abcdef012345", "issuedAt": 1700000000} },
|
|
7
|
+
{ "token": "ig1.eyJzdWIiOiJhYmNkZWYwMTIzNDUiLCJpYXQiOjE3MDAwMDAwMDB9.wNpBpqmMl21mqAZvRSYi4WfDejreTKDxHfOQp_sBKOTQB0_N2trL6iFcRxFUDrGBnGHmYnv-x66FgU_j57ZECA", "claim": null },
|
|
8
|
+
{ "token": "ig1.eiJzdWIiOiJhYmNkZWYwMTIzNDUiLCJpYXQiOjE3MDAwMDAwMDB9.wdpBpqmMl21mqAZvRSYi4WfDejreTKDxHfOQp_sBKOTQB0_N2trL6iFcRxFUDrGBnGHmYnv-x66FgU_j57ZECA", "claim": null },
|
|
9
|
+
{ "token": "ig1.eyJzdWIiOiJhYmNkZWYwMTIzNDUiLCJpYXQiOjE3MDAwMDAwMDB9.Ac2ofn7IxD3M51pchXEycOPg4TP_kiTzu-aTVowDLTirjVzYrvfJ-ntrzPbQg-3RpBhmc53yP7s7iWyOgGWuAw", "claim": null },
|
|
10
|
+
{ "token": "ig2.eyJzdWIiOiJhYmNkZWYwMTIzNDUiLCJpYXQiOjE3MDAwMDAwMDB9.wdpBpqmMl21mqAZvRSYi4WfDejreTKDxHfOQp_sBKOTQB0_N2trL6iFcRxFUDrGBnGHmYnv-x66FgU_j57ZECA", "claim": null },
|
|
11
|
+
{ "token": "eyJzdWIiOiJhYmNkZWYwMTIzNDUiLCJpYXQiOjE3MDAwMDAwMDB9.wdpBpqmMl21mqAZvRSYi4WfDejreTKDxHfOQp_sBKOTQB0_N2trL6iFcRxFUDrGBnGHmYnv-x66FgU_j57ZECA", "claim": null },
|
|
12
|
+
{ "token": "ig1.eyJzdWIiOiJhYmNkZWYwMTIzNDUiLCJpYXQiOjE3MDAwMDAwMDB9.wdpBpqmMl21mqAZvRSYi4WfDejreTKDxHfOQp_sBKOTQB0_N2trL6iFcRxFUDrGBnGHmYnv-x66FgU_j57ZECA.wdpBpqmMl21mqAZvRSYi4WfDejreTKDxHfOQp_sBKOTQB0_N2trL6iFcRxFUDrGBnGHmYnv-x66FgU_j57ZECA", "claim": null },
|
|
13
|
+
{ "token": "ig1.eyJzdWIiOiJhYmNkZWYwMTIzNDUiLCJpYXQiOjE3MDAwMDAwMDB9.", "claim": null },
|
|
14
|
+
{ "token": "", "claim": null },
|
|
15
|
+
{ "token": "ig1", "claim": null },
|
|
16
|
+
{ "token": "ig1..", "claim": null },
|
|
17
|
+
{ "token": "ig1.%%%.%%%", "claim": null },
|
|
18
|
+
{ "token": "ig1.eyJzdWIiOiJBQkNERUYwMTIzNDUiLCJpYXQiOjE3MDAwMDAwMDB9.CbPEciFki-GXJnUcS5F9_9uXUOw5tWhv3iS69BjuQpSmsA72_xnvYUIjhAMiBPI98XBQ9-zKc9GSPJIFGQqaDA", "claim": null },
|
|
19
|
+
{ "token": "ig1.eyJzdWIiOiJhYmNkZWYwMTIzNCIsImlhdCI6MTcwMDAwMDAwMH0.qfJbxpVtbcymaweaCu9fUounkldkMy4HSdllEDlHs5Yq8b11XxiHLguDyvqubXcMGCuVVygCeg2MjU3i0vEuDA", "claim": null },
|
|
20
|
+
{ "token": "ig1.eyJzdWIiOiJhYmNkZWYwMTIzNDUiLCJpYXQiOiIxNzAwMDAwMDAwIn0.BI6UT1aTaN01bGZyI9SHc_qIf9S-JlpmNtiBd8by_giZCq8pQIJCBKIutYZW0mmqCF0YHf0NHc9icq1h9Wr7BA", "claim": null },
|
|
21
|
+
{ "token": "ig1.eyJzdWIiOiJhYmNkZWYwMTIzNDUifQ.Aa5FeLKazhsr2IvYZ2p5BJmLL1qvQWReXeCKt5gxCq9PqSaAnn8Pa6PJp0jiaXY5FIbc_S9MFrUCeEfQ6vnkBQ", "claim": null },
|
|
22
|
+
{ "token": "ig1.eyJzdWIiOjEyMzQ1Njc4OTAxMiwiaWF0IjoxNzAwMDAwMDAwfQ.kb_lMTBvFS4nzTmqLf-T7l9IF8PHaPPkP5HB1T0yd0IRWqYJnIZH2eoKexv7e6aA2PkqCpe-B1FL8Nbu4qPvDg", "claim": null },
|
|
23
|
+
{ "token": "ig1.bm90IGpzb24.Ffec27cYSbvKvjlxtSUIFFX7sGo3Zp4BxotW02rFwcJHfJikM51Zfzn3_0Rg8coR4y43iOUA2u-ZOcBJg4mmDA", "claim": null },
|
|
24
|
+
{ "token": "ig1.bnVsbA.lFa3FNSkMcf3WCyjdb87YRVdt8h3aSzt4cignx9Rkwq6drwl12m0rlrmzdPkLjsdrQdL1oWZ2E2MGuo2CMeSBw", "claim": null },
|
|
25
|
+
{ "token": "ig1.WyJhYmNkZWYwMTIzNDUiLDE3MDAwMDAwMDBd.BWFTD0bHKDBAEWwhYq6F-cWo_HXEC8UvDWkrTmafOycvTZUHvVOjWy_97OYl_pF-svCO1vbeumhF89ybhqv4Dw", "claim": null }
|
|
26
|
+
],
|
|
27
|
+
"owners": [
|
|
28
|
+
{ "host": "sandbox-abcdef012345.sbx.example.test", "owner": "abcdef012345" },
|
|
29
|
+
{ "host": "sandbox-abcdef012345.sbx.example.test:443", "owner": "abcdef012345" },
|
|
30
|
+
{ "host": "preview-web-app-abcdef012345.sbx.example.test:443", "owner": "abcdef012345" },
|
|
31
|
+
{ "host": "port-0f0f0f0f0f0f-abcdef012345.sbx.example.dev", "owner": "abcdef012345" },
|
|
32
|
+
{ "host": "public-1a2b3c4d5e6f-abcdef012345.sbx.example.dev", "owner": "abcdef012345" },
|
|
33
|
+
{ "host": "SANDBOX-abcdef012345.sbx.example.test", "owner": "abcdef012345" },
|
|
34
|
+
{ "host": "-abcdef012345.example.com", "owner": "abcdef012345" },
|
|
35
|
+
{ "host": "sandbox-abcdef012345", "owner": "abcdef012345" },
|
|
36
|
+
{ "host": "ingress.sbx.example.test", "owner": null },
|
|
37
|
+
{ "host": "sbx.example.test", "owner": null },
|
|
38
|
+
{ "host": "abcdef012345.local.sbx.example.dev", "owner": null },
|
|
39
|
+
{ "host": "sandbox-ABCDEF012345.sbx.example.test", "owner": null },
|
|
40
|
+
{ "host": "sandbox-abcdef01234.sbx.example.test", "owner": null },
|
|
41
|
+
{ "host": "sandbox-abcdef012345x.sbx.example.test", "owner": null },
|
|
42
|
+
{ "host": "sandbox-abcdef012345-.sbx.example.test", "owner": null },
|
|
43
|
+
{ "host": "[::1]:8788", "owner": null },
|
|
44
|
+
{ "host": "127.0.0.1:8788", "owner": null },
|
|
45
|
+
{ "host": "", "owner": null }
|
|
46
|
+
],
|
|
47
|
+
"lanes": [
|
|
48
|
+
{ "host": "sandbox-abcdef012345.sbx.example.test", "method": "GET", "path": "/workspace/media?path=a.mp4", "lane": "bulk" },
|
|
49
|
+
{ "host": "sandbox-abcdef012345.sbx.example.test", "method": "POST", "path": "/workspace/upload?path=a", "lane": "bulk" },
|
|
50
|
+
{ "host": "sandbox-abcdef012345.sbx.example.test:443", "method": "GET", "path": "/system/runners/git/app/info/refs?service=git-upload-pack", "lane": "bulk" },
|
|
51
|
+
{ "host": "sandbox-abcdef012345.sbx.example.test", "method": "POST", "path": "/system/runners/git/app/git-receive-pack", "lane": "bulk" },
|
|
52
|
+
{ "host": "sandbox-abcdef012345.sbx.example.test", "method": "GET", "path": "/system/sync/ssh", "lane": "bulk" },
|
|
53
|
+
{ "host": "sandbox-abcdef012345.sbx.example.test", "method": "GET", "path": "/extensions/abc/bundle", "lane": "bulk" },
|
|
54
|
+
{ "host": "sandbox-abcdef012345.sbx.example.test", "method": "GET", "path": "/diff/raw?a=1?b=2", "lane": "bulk" },
|
|
55
|
+
{ "host": "sandbox-abcdef012345.sbx.example.test", "method": "GET", "path": "/bundles/download?ticket=x", "lane": "bulk" },
|
|
56
|
+
{ "host": "sandbox-abcdef012345.sbx.example.test", "method": "POST", "path": "/workspace/upload-archive", "lane": "bulk" },
|
|
57
|
+
{ "host": "sandbox-abcdef012345.sbx.example.test", "method": "HEAD", "path": "/workspace/raw?path=a", "lane": "bulk" },
|
|
58
|
+
{ "host": "sandbox-abcdef012345.sbx.example.test", "method": "OPTIONS", "path": "/workspace/raw", "lane": "bulk" },
|
|
59
|
+
{ "host": "sandbox-abcdef012345.sbx.example.test", "method": "OPTIONS", "path": "/system/runners/git/app/git-upload-pack", "lane": "bulk" },
|
|
60
|
+
{ "host": "sandbox-abcdef012345.sbx.example.test", "method": "get", "path": "/workspace/raw?path=a", "lane": "bulk" },
|
|
61
|
+
{ "host": "sandbox-abcdef012345.sbx.example.test", "method": "GET", "path": "/workspace/thumb?path=a.png", "lane": "interactive" },
|
|
62
|
+
{ "host": "sandbox-abcdef012345.sbx.example.test", "method": "POST", "path": "/workspace/upload-diff", "lane": "interactive" },
|
|
63
|
+
{ "host": "sandbox-abcdef012345.sbx.example.test", "method": "GET", "path": "/agents", "lane": "interactive" },
|
|
64
|
+
{ "host": "sandbox-abcdef012345.sbx.example.test", "method": "GET", "path": "/system/terminal?ticket=t", "lane": "interactive" },
|
|
65
|
+
{ "host": "sandbox-abcdef012345.sbx.example.test", "method": "POST", "path": "/workspace/raw", "lane": "interactive" },
|
|
66
|
+
{ "host": "sandbox-abcdef012345.sbx.example.test", "method": "GET", "path": "/workspace/raw/", "lane": "interactive" },
|
|
67
|
+
{ "host": "sandbox-abcdef012345.sbx.example.test", "method": "GET", "path": "/system/runners/git//info/refs", "lane": "interactive" },
|
|
68
|
+
{ "host": "sandbox-abcdef012345.sbx.example.test", "method": "GET", "path": "/x/anything/at/all", "lane": "interactive" },
|
|
69
|
+
{ "host": "sandbox-abcdef012345.sbx.example.test", "method": "GET", "path": "/", "lane": "interactive" },
|
|
70
|
+
{ "host": "sandbox-abcdef012345.sbx.example.test", "method": "GET", "path": "", "lane": "interactive" },
|
|
71
|
+
{ "host": "preview-web-abcdef012345.sbx.example.test", "method": "GET", "path": "/agents", "lane": "bulk" },
|
|
72
|
+
{ "host": "port-1a2b3c4d5e6f-abcdef012345.sbx.example.test", "method": "GET", "path": "/", "lane": "bulk" },
|
|
73
|
+
{ "host": "Sandbox-abcdef012345.sbx.example.test", "method": "GET", "path": "/agents", "lane": "bulk" },
|
|
74
|
+
{ "host": "sbx.example.test", "method": "GET", "path": "/agents", "lane": "bulk" },
|
|
75
|
+
{ "host": "", "method": "GET", "path": "/agents", "lane": "bulk" }
|
|
76
|
+
]
|
|
77
|
+
}
|
|
@@ -1,5 +1,14 @@
|
|
|
1
1
|
import { generateKeyPairSync } from "node:crypto";
|
|
2
|
-
import {
|
|
2
|
+
import { readFileSync } from "node:fs";
|
|
3
|
+
import { hostOwnerId, type ReachabilityGrant, mintReachabilityGrant, verifyReachabilityGrant } from "./ingress-contract.js";
|
|
4
|
+
|
|
5
|
+
// The edge's rules as both implementations must answer them; the Rust edge reads the same file. Its grants were minted
|
|
6
|
+
// by a throwaway pair whose private half was never kept, so a new grant case takes a new pair and every token again.
|
|
7
|
+
const FIXTURE = JSON.parse(readFileSync(new URL("./ingress-contract.fixture.json", import.meta.url), "utf8")) as {
|
|
8
|
+
readonly publicKey: string;
|
|
9
|
+
readonly grants: readonly { readonly token: string; readonly claim: ReachabilityGrant | null }[];
|
|
10
|
+
readonly owners: readonly { readonly host: string; readonly owner: string | null }[];
|
|
11
|
+
};
|
|
3
12
|
|
|
4
13
|
const pemPair = (): { privateKey: string; publicKey: string } => {
|
|
5
14
|
const { privateKey, publicKey } = generateKeyPairSync("ed25519");
|
|
@@ -31,10 +40,11 @@ describe("reachability grant", () => {
|
|
|
31
40
|
expect(verifyReachabilityGrant(keys.publicKey, `${prefix}.${forged}.${signature}`)).toBeUndefined();
|
|
32
41
|
});
|
|
33
42
|
|
|
34
|
-
it("answers
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
43
|
+
it("answers every grant in the shared fixture as it states, and garbage with undefined rather than a throw", () => {
|
|
44
|
+
// Both verdicts, so the loop below can never pass by holding only one kind of case.
|
|
45
|
+
expect(new Set(FIXTURE.grants.map(({ claim }) => (claim === null ? "refused" : "accepted")))).toEqual(new Set(["accepted", "refused"]));
|
|
46
|
+
for (const { token, claim } of FIXTURE.grants) {
|
|
47
|
+
expect({ token, claim: verifyReachabilityGrant(FIXTURE.publicKey, token) ?? null }).toEqual({ token, claim });
|
|
38
48
|
}
|
|
39
49
|
});
|
|
40
50
|
|
|
@@ -44,27 +54,9 @@ describe("reachability grant", () => {
|
|
|
44
54
|
});
|
|
45
55
|
|
|
46
56
|
describe("hostOwnerId", () => {
|
|
47
|
-
it("owns the
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
expect(hostOwnerId(`port-0f0f0f0f0f0f-${SANDBOX_ID}.sbx.example.dev`)).toBe(SANDBOX_ID);
|
|
51
|
-
expect(hostOwnerId(`public-1a2b3c4d5e6f-${SANDBOX_ID}.sbx.example.dev`)).toBe(SANDBOX_ID);
|
|
52
|
-
});
|
|
53
|
-
|
|
54
|
-
it("ignores a port suffix", () => {
|
|
55
|
-
expect(hostOwnerId(`sandbox-${SANDBOX_ID}.sbx.example.dev:443`)).toBe(SANDBOX_ID);
|
|
56
|
-
});
|
|
57
|
-
|
|
58
|
-
it("owns nothing that does not end in a 12-hex tail", () => {
|
|
59
|
-
// The ingress's own door, the zone apex, the loopback name's bare-id label, and a near-miss tail.
|
|
60
|
-
for (const host of [
|
|
61
|
-
"ingress.sbx.example.dev",
|
|
62
|
-
"sbx.example.dev",
|
|
63
|
-
`${SANDBOX_ID}.local.sbx.example.dev`,
|
|
64
|
-
"sandbox-abc123def45.sbx.example.dev",
|
|
65
|
-
"",
|
|
66
|
-
]) {
|
|
67
|
-
expect(hostOwnerId(host)).toBeUndefined();
|
|
57
|
+
it("owns every host in the shared fixture as it states, port or not", () => {
|
|
58
|
+
for (const { host, owner } of FIXTURE.owners) {
|
|
59
|
+
expect({ host, owner: hostOwnerId(host) ?? null }).toEqual({ host, owner });
|
|
68
60
|
}
|
|
69
61
|
});
|
|
70
62
|
});
|
|
@@ -73,31 +73,12 @@ export const INGRESS_GRANT_HEADER = "x-intentic-grant";
|
|
|
73
73
|
export const ENV_INGRESS_URL = "INGRESS_URL";
|
|
74
74
|
export const ENV_SANDBOX_GRANT = "SANDBOX_GRANT";
|
|
75
75
|
|
|
76
|
-
//
|
|
77
|
-
|
|
78
|
-
// Daemon-side tunnel behavior this contract requires: dial, register, forward to the loopback listener, and reconnect
|
|
79
|
-
// forever with backoff, never giving up. `close()` is for shutdown and tests.
|
|
80
|
-
export interface IngressTunnelOptions {
|
|
81
|
-
// e.g. https://ingress.<zone>; the daemon derives the wss:// door itself via INGRESS_TUNNEL_PATH.
|
|
82
|
-
readonly url: string;
|
|
83
|
-
readonly grant: string;
|
|
84
|
-
// The daemon's own loopback listener; every h2 stream lands there as a plain HTTP/1.1 request or upgrade.
|
|
85
|
-
readonly targetPort: number;
|
|
86
|
-
readonly log: (message: string, error?: unknown) => void;
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
export interface IngressTunnelHandle {
|
|
90
|
-
readonly close: () => Promise<void>;
|
|
91
|
-
// For /health and the boot log: whether the tunnel currently holds a registered session.
|
|
92
|
-
readonly connected: () => boolean;
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
export type StartIngressTunnel = (options: IngressTunnelOptions) => IngressTunnelHandle;
|
|
96
|
-
|
|
97
|
-
// What the ingress side must also honor, enforced by ingress-protocol tests:
|
|
76
|
+
// What the edge (_platform/ingress, Rust) must also honor, held by its suites and by ingress-contract.fixture.json:
|
|
98
77
|
// - Register: verify the grant offline; if PLATFORM_URL is set, check the sandbox exists (GET /api/reachability/<id>)
|
|
99
78
|
// and cache the answer, failing open if the platform doesn't respond. A 404 refuses the tunnel.
|
|
100
79
|
// - Displacement: a new tunnel for an id closes the old session (code 4001) and takes the registration.
|
|
80
|
+
// - Lanes: a tunnel naming the bulk lane (tunnel-lanes.ts) is held beside the interactive one and displaces only its
|
|
81
|
+
// own kind; only the interactive lane is the sandbox's home in the cluster.
|
|
101
82
|
// - Liveness: WebSocket ping every 15s; a peer silent for 45s is unregistered.
|
|
102
83
|
// - Routing: host maps via hostOwnerId to a registered tunnel; no tunnel answers 502 naming the sandbox label, with
|
|
103
84
|
// the verdict header and CORS of edge-verdict.ts, and admits the preflight of the request it is about to refuse.
|
|
@@ -8,26 +8,26 @@ import { runnerTranslatorPath } from "./runner-protocol.js";
|
|
|
8
8
|
export const RAW_ROUTES = {
|
|
9
9
|
// The "is a daemon there" probe every flow uses; it names the sandbox and says whether it is still converging.
|
|
10
10
|
"GET /health": { auth: "door", beforeBoot: true },
|
|
11
|
-
"GET /diff/raw": {},
|
|
11
|
+
"GET /diff/raw": { lane: "bulk" },
|
|
12
12
|
// Arming the dictation model is a read any tier makes; dictating is writing a message, the collaborator's grant.
|
|
13
13
|
"GET /speech/status": { guest: true },
|
|
14
14
|
"POST /speech/transcribe": { floor: "collaborator", guest: true },
|
|
15
15
|
// The bytes behind the workspace reads; each applies the caller's fence itself.
|
|
16
|
-
"GET /workspace/raw": { guest: true },
|
|
16
|
+
"GET /workspace/raw": { guest: true, lane: "bulk" },
|
|
17
17
|
// No door, unlike media: the page fetches a thumbnail itself, so the request carries the header.
|
|
18
18
|
"GET /workspace/thumb": { guest: true },
|
|
19
19
|
// Fetched by a <video>/<audio> tag, which sends no header: its scoped ticket is checked in the handler.
|
|
20
|
-
"GET /workspace/media": { auth: "door", guest: true },
|
|
20
|
+
"GET /workspace/media": { auth: "door", guest: true, lane: "bulk" },
|
|
21
21
|
// An attachment is part of the message it rides with; any other target edits the shared tree.
|
|
22
|
-
"POST /workspace/upload": { floor: "writer", attachmentFloor: "collaborator" },
|
|
22
|
+
"POST /workspace/upload": { floor: "writer", attachmentFloor: "collaborator", lane: "bulk" },
|
|
23
23
|
"POST /workspace/upload-diff": {},
|
|
24
|
-
"POST /workspace/upload-archive": {},
|
|
24
|
+
"POST /workspace/upload-archive": { lane: "bulk" },
|
|
25
25
|
// Minting is cheap: each WebSocket upgrade floors its own redemption.
|
|
26
26
|
"POST /system/ws-ticket": { beforeBoot: true, floor: "collaborator", control: "never" },
|
|
27
27
|
// A WebSocket upgrade carries no Authorization header; the terminal and both browser wires check a query ticket.
|
|
28
|
-
"GET /system/terminal": { auth: "door", beforeBoot: true, control: "never" },
|
|
28
|
+
"GET /system/terminal": { auth: "door", beforeBoot: true, control: "never", front: true },
|
|
29
29
|
// Desktop sync's byte pipe, guarded again by sshd's own key check against the same enrollment.
|
|
30
|
-
"GET /system/sync/ssh": { sync: "pipe", control: "never" },
|
|
30
|
+
"GET /system/sync/ssh": { sync: "pipe", control: "never", lane: "bulk" },
|
|
31
31
|
"GET /system/browser-profile": { auth: "door", beforeBoot: true, control: "never" },
|
|
32
32
|
"GET /system/browser-view": { auth: "door", beforeBoot: true, control: "never" },
|
|
33
33
|
// Deploy-target enrollment, gated by the connect token.
|
|
@@ -68,7 +68,7 @@ export const RAW_ROUTES = {
|
|
|
68
68
|
"DELETE /bundles": { control: "never" },
|
|
69
69
|
"POST /bundles/ticket": { control: "never" },
|
|
70
70
|
// Navigated to by the browser, so no header: its ticket is checked in the handler.
|
|
71
|
-
"GET /bundles/download": { auth: "door", control: "never" },
|
|
71
|
+
"GET /bundles/download": { auth: "door", control: "never", lane: "bulk" },
|
|
72
72
|
"GET /definition": {},
|
|
73
73
|
"POST /definition/diff": {},
|
|
74
74
|
"GET /definition/workspace": {},
|
|
@@ -78,9 +78,14 @@ export const RAW_ROUTES = {
|
|
|
78
78
|
"POST /arrivals/scan": {},
|
|
79
79
|
"POST /arrivals/apply": {},
|
|
80
80
|
"DELETE /arrivals": {},
|
|
81
|
-
"GET /extensions/{id}/bundle": {},
|
|
81
|
+
"GET /extensions/{id}/bundle": { lane: "bulk" },
|
|
82
82
|
// An extension backend's own namespace, proxied verbatim.
|
|
83
83
|
"ALL /x/*": {},
|
|
84
|
+
// An extension card's MCP endpoint (its manifest's `mcp`), forwarded to the backend host; the per-boot token its
|
|
85
|
+
// handler checks is the one the turn's tool config carries.
|
|
86
|
+
"POST /mcp/extensions/{id}": { auth: "door" },
|
|
87
|
+
"GET /mcp/extensions/{id}": { auth: "door" },
|
|
88
|
+
"DELETE /mcp/extensions/{id}": { auth: "door" },
|
|
84
89
|
// The `capabilities` CLI: discovery by name, and the ask that parks on an owner-decided card.
|
|
85
90
|
"GET /capabilities/connectable": { floor: "maintainer", agent: true, control: "never" },
|
|
86
91
|
"POST /capabilities/ask": { floor: "maintainer", agent: true, control: "never" },
|
|
@@ -135,16 +140,18 @@ export const RAW_ROUTES = {
|
|
|
135
140
|
"GET /system/runners": { control: "never" },
|
|
136
141
|
"DELETE /system/runners/{id}": { control: "never" },
|
|
137
142
|
"GET /system/runners/connect": { auth: "door", beforeBoot: true, control: "never" },
|
|
138
|
-
// A turn's
|
|
139
|
-
"POST /
|
|
143
|
+
// A turn's browser routers, hosted in the daemon; each carries its own per-turn bearer, checked by the handler.
|
|
144
|
+
"POST /mcp/browser/{id}": { auth: "door", control: "never" },
|
|
145
|
+
"GET /mcp/browser/{id}": { auth: "door", control: "never" },
|
|
146
|
+
"DELETE /mcp/browser/{id}": { auth: "door", control: "never" },
|
|
140
147
|
// A connected browser's credential doors, on its durable token: a sign-in moves in (`session`) or out (`lend`).
|
|
141
148
|
"POST /system/webext/session": { auth: "door", control: "never" },
|
|
142
149
|
"POST /system/webext/lend": { auth: "door", control: "never" },
|
|
143
150
|
"POST /system/runners/{id}/definition/sync": { control: "never" },
|
|
144
151
|
// A runner's git door and credential doors, each on the runner's own bearer.
|
|
145
|
-
"GET /system/runners/git/{repo}/info/refs": { auth: "door", control: "never" },
|
|
146
|
-
"POST /system/runners/git/{repo}/git-upload-pack": { auth: "door", control: "never" },
|
|
147
|
-
"POST /system/runners/git/{repo}/git-receive-pack": { auth: "door", control: "never" },
|
|
152
|
+
"GET /system/runners/git/{repo}/info/refs": { auth: "door", control: "never", lane: "bulk" },
|
|
153
|
+
"POST /system/runners/git/{repo}/git-upload-pack": { auth: "door", control: "never", lane: "bulk" },
|
|
154
|
+
"POST /system/runners/git/{repo}/git-receive-pack": { auth: "door", control: "never", lane: "bulk" },
|
|
148
155
|
"POST /system/runners/credentials": { auth: "door", control: "never" },
|
|
149
156
|
"POST /system/runners/credentials/refresh": { auth: "door", control: "never" },
|
|
150
157
|
[`ALL ${runnerTranslatorPath}/*` as const]: { auth: "door", control: "never" },
|
|
@@ -33,6 +33,10 @@ export interface RouteMeta {
|
|
|
33
33
|
// Withheld from the panel token, which reaches every other route: it puts a stored credential in motion.
|
|
34
34
|
readonly panel?: false;
|
|
35
35
|
readonly control?: ControlReach;
|
|
36
|
+
// Rides the tunnel's bulk lane: a transfer, kept off the connection every keystroke and call waits on.
|
|
37
|
+
readonly lane?: "bulk";
|
|
38
|
+
// Served by intentic-front itself (_sandbox/front, term/): the daemon only answers the front's question about it.
|
|
39
|
+
readonly front?: true;
|
|
36
40
|
}
|
|
37
41
|
|
|
38
42
|
// The builder every sandbox procedure starts from, so each carries a RouteMeta and whatever it leaves out defaults.
|
|
@@ -188,6 +188,59 @@ export const RunnerTurnSchema = z.object({
|
|
|
188
188
|
});
|
|
189
189
|
export type RunnerTurn = z.infer<typeof RunnerTurnSchema>;
|
|
190
190
|
|
|
191
|
+
// OFFLOADED COMMANDS: one shell line the parent hands a runner instead of running it itself (settings `offload`). The
|
|
192
|
+
// code travels as a snapshot commit of the parent's working tree, uncommitted and untracked work included, parked under
|
|
193
|
+
// this ref in the repo's git dir for the runner to fetch through the git door, and deleted once the run ends.
|
|
194
|
+
export const OFFLOAD_REF_PREFIX = "refs/intentic-offload/";
|
|
195
|
+
export const offloadRef = (runId: string): string => `${OFFLOAD_REF_PREFIX}${runId}`;
|
|
196
|
+
const OffloadRunIdSchema = z.string().regex(/^[a-z0-9][a-z0-9-]{7,63}$/u);
|
|
197
|
+
const EnvNameSchema = z.string().regex(/^[A-Za-z_][A-Za-z0-9_]*$/u);
|
|
198
|
+
|
|
199
|
+
export const RunnerCommandSchema = z.object({
|
|
200
|
+
runId: OffloadRunIdSchema,
|
|
201
|
+
// The repo id the git door serves (a /history/gits entry: `root` for the workspace itself).
|
|
202
|
+
repo: z.string().min(1),
|
|
203
|
+
// The snapshot commit's ref in that repo, always under OFFLOAD_REF_PREFIX.
|
|
204
|
+
ref: z.string().startsWith(OFFLOAD_REF_PREFIX),
|
|
205
|
+
// Where in the repo the line runs, repo-relative; "" is the repo root.
|
|
206
|
+
cwd: z.string().refine((path) => !path.startsWith("/") && !path.split("/").includes(".."), "must stay inside the repo"),
|
|
207
|
+
command: z.string().min(1).max(64_000),
|
|
208
|
+
// Values set for the line, beside the runner's own environment. Never a secret: a line that needs one runs here.
|
|
209
|
+
env: z.record(EnvNameSchema, z.string()).default({}),
|
|
210
|
+
// Variables the line writes a file to (a report path): the runner points each at a scratch file and hands the file
|
|
211
|
+
// back with the exit.
|
|
212
|
+
exports: z.array(EnvNameSchema).max(8).default([]),
|
|
213
|
+
// The heavy-command rule it matched here, named in the runner's own output and queue.
|
|
214
|
+
label: z.string().min(1).max(80),
|
|
215
|
+
timeoutMs: z
|
|
216
|
+
.number()
|
|
217
|
+
.int()
|
|
218
|
+
.positive()
|
|
219
|
+
.max(6 * 60 * 60_000)
|
|
220
|
+
.optional(),
|
|
221
|
+
});
|
|
222
|
+
export type RunnerCommand = z.infer<typeof RunnerCommandSchema>;
|
|
223
|
+
|
|
224
|
+
// What a running command streams back: narration of the runner's own steps (fetch, install, queue), the line's output
|
|
225
|
+
// as it comes, and last its end: the exit, every file it changed as a binary git patch against the snapshot, and the
|
|
226
|
+
// exported files, base64.
|
|
227
|
+
export const RunnerCommandFrameSchema = z.union([
|
|
228
|
+
z.object({ kind: z.literal("status"), text: z.string() }),
|
|
229
|
+
z.object({ kind: z.literal("output"), stream: z.enum(["stdout", "stderr"]), text: z.string() }),
|
|
230
|
+
z.object({
|
|
231
|
+
kind: z.literal("exit"),
|
|
232
|
+
code: z.number().int(),
|
|
233
|
+
signal: z.string().optional(),
|
|
234
|
+
// Why the run did not end on its own: stopped, timed out, or never started (a fetch or install that failed).
|
|
235
|
+
failure: z.string().optional(),
|
|
236
|
+
// False when the line never started there, which sends it back to run on the parent.
|
|
237
|
+
ran: z.boolean().default(true),
|
|
238
|
+
patchBase64: z.string().optional(),
|
|
239
|
+
files: z.record(EnvNameSchema, z.string()).default({}),
|
|
240
|
+
}),
|
|
241
|
+
]);
|
|
242
|
+
export type RunnerCommandFrame = z.infer<typeof RunnerCommandFrameSchema>;
|
|
243
|
+
|
|
191
244
|
// Where a conversation runs, fixed on its first turn like `isolated` placement: later turns follow the registry, not
|
|
192
245
|
// the sender. `runner` implies isolation since a remote conversation is branch-anchored; absent means local.
|
|
193
246
|
export const AgentPlacementSchema = z.union([z.object({ kind: z.literal("local") }), z.object({ kind: z.literal("runner"), id: z.string().min(1) })]);
|
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
[
|
|
2
|
+
{"method":"GET","path":"/health","lane":"interactive"},
|
|
3
|
+
{"method":"GET","path":"/diff/raw","lane":"bulk"},
|
|
4
|
+
{"method":"GET","path":"/speech/status","lane":"interactive"},
|
|
5
|
+
{"method":"POST","path":"/speech/transcribe","lane":"interactive"},
|
|
6
|
+
{"method":"GET","path":"/workspace/raw","lane":"bulk"},
|
|
7
|
+
{"method":"GET","path":"/workspace/thumb","lane":"interactive"},
|
|
8
|
+
{"method":"GET","path":"/workspace/media","lane":"bulk"},
|
|
9
|
+
{"method":"POST","path":"/workspace/upload","lane":"bulk"},
|
|
10
|
+
{"method":"POST","path":"/workspace/upload-diff","lane":"interactive"},
|
|
11
|
+
{"method":"POST","path":"/workspace/upload-archive","lane":"bulk"},
|
|
12
|
+
{"method":"POST","path":"/system/ws-ticket","lane":"interactive"},
|
|
13
|
+
{"method":"GET","path":"/system/terminal","lane":"interactive"},
|
|
14
|
+
{"method":"GET","path":"/system/sync/ssh","lane":"bulk"},
|
|
15
|
+
{"method":"GET","path":"/system/browser-profile","lane":"interactive"},
|
|
16
|
+
{"method":"GET","path":"/system/browser-view","lane":"interactive"},
|
|
17
|
+
{"method":"POST","path":"/enroll","lane":"interactive"},
|
|
18
|
+
{"method":"POST","path":"/automations/{id}/fire","lane":"interactive"},
|
|
19
|
+
{"method":"POST","path":"/workflows/{id}/gate","lane":"interactive"},
|
|
20
|
+
{"method":"GET","path":"/webchat/widget.js","lane":"interactive"},
|
|
21
|
+
{"method":"GET","path":"/webchat/{id}/config","lane":"interactive"},
|
|
22
|
+
{"method":"GET","path":"/webchat/{id}/challenge","lane":"interactive"},
|
|
23
|
+
{"method":"POST","path":"/webchat/{id}/message","lane":"interactive"},
|
|
24
|
+
{"method":"GET","path":"/webchat/{id}/messages","lane":"interactive"},
|
|
25
|
+
{"method":"GET","path":"/webchat/{id}/installs","lane":"interactive"},
|
|
26
|
+
{"method":"GET","path":"/intake/sdk.js","lane":"interactive"},
|
|
27
|
+
{"method":"GET","path":"/intake/{id}/config","lane":"interactive"},
|
|
28
|
+
{"method":"GET","path":"/intake/{id}/challenge","lane":"interactive"},
|
|
29
|
+
{"method":"POST","path":"/intake/{id}/report","lane":"interactive"},
|
|
30
|
+
{"method":"GET","path":"/members","lane":"interactive"},
|
|
31
|
+
{"method":"POST","path":"/members","lane":"interactive"},
|
|
32
|
+
{"method":"DELETE","path":"/members","lane":"interactive"},
|
|
33
|
+
{"method":"DELETE","path":"/members/self","lane":"interactive"},
|
|
34
|
+
{"method":"GET","path":"/environment","lane":"interactive"},
|
|
35
|
+
{"method":"GET","path":"/environment/contents","lane":"interactive"},
|
|
36
|
+
{"method":"POST","path":"/environment/approve","lane":"interactive"},
|
|
37
|
+
{"method":"POST","path":"/environment/reject","lane":"interactive"},
|
|
38
|
+
{"method":"POST","path":"/environment/runtime-install","lane":"interactive"},
|
|
39
|
+
{"method":"GET","path":"/engines","lane":"interactive"},
|
|
40
|
+
{"method":"POST","path":"/engines/channel","lane":"interactive"},
|
|
41
|
+
{"method":"POST","path":"/engines/update","lane":"interactive"},
|
|
42
|
+
{"method":"POST","path":"/engines/revert","lane":"interactive"},
|
|
43
|
+
{"method":"GET","path":"/bundles","lane":"interactive"},
|
|
44
|
+
{"method":"POST","path":"/bundles","lane":"interactive"},
|
|
45
|
+
{"method":"DELETE","path":"/bundles","lane":"interactive"},
|
|
46
|
+
{"method":"POST","path":"/bundles/ticket","lane":"interactive"},
|
|
47
|
+
{"method":"GET","path":"/bundles/download","lane":"bulk"},
|
|
48
|
+
{"method":"GET","path":"/definition","lane":"interactive"},
|
|
49
|
+
{"method":"POST","path":"/definition/diff","lane":"interactive"},
|
|
50
|
+
{"method":"GET","path":"/definition/workspace","lane":"interactive"},
|
|
51
|
+
{"method":"POST","path":"/definition/workspace/publish","lane":"interactive"},
|
|
52
|
+
{"method":"POST","path":"/arrivals/plan","lane":"interactive"},
|
|
53
|
+
{"method":"GET","path":"/arrivals/hosts","lane":"interactive"},
|
|
54
|
+
{"method":"POST","path":"/arrivals/scan","lane":"interactive"},
|
|
55
|
+
{"method":"POST","path":"/arrivals/apply","lane":"interactive"},
|
|
56
|
+
{"method":"DELETE","path":"/arrivals","lane":"interactive"},
|
|
57
|
+
{"method":"GET","path":"/extensions/{id}/bundle","lane":"bulk"},
|
|
58
|
+
{"method":"ALL","path":"/x/*","lane":"interactive"},
|
|
59
|
+
{"method":"POST","path":"/mcp/extensions/{id}","lane":"interactive"},
|
|
60
|
+
{"method":"GET","path":"/mcp/extensions/{id}","lane":"interactive"},
|
|
61
|
+
{"method":"DELETE","path":"/mcp/extensions/{id}","lane":"interactive"},
|
|
62
|
+
{"method":"GET","path":"/capabilities/connectable","lane":"interactive"},
|
|
63
|
+
{"method":"POST","path":"/capabilities/ask","lane":"interactive"},
|
|
64
|
+
{"method":"GET","path":"/sandboxes","lane":"interactive"},
|
|
65
|
+
{"method":"POST","path":"/sandboxes","lane":"interactive"},
|
|
66
|
+
{"method":"GET","path":"/wallet/status","lane":"interactive"},
|
|
67
|
+
{"method":"POST","path":"/wallet/fetch","lane":"interactive"},
|
|
68
|
+
{"method":"GET","path":"/wallet/history","lane":"interactive"},
|
|
69
|
+
{"method":"POST","path":"/children/spawn","lane":"interactive"},
|
|
70
|
+
{"method":"GET","path":"/children/providers","lane":"interactive"},
|
|
71
|
+
{"method":"POST","path":"/children/wait","lane":"interactive"},
|
|
72
|
+
{"method":"POST","path":"/children/send","lane":"interactive"},
|
|
73
|
+
{"method":"POST","path":"/children/answer","lane":"interactive"},
|
|
74
|
+
{"method":"GET","path":"/children","lane":"interactive"},
|
|
75
|
+
{"method":"GET","path":"/fleet","lane":"interactive"},
|
|
76
|
+
{"method":"POST","path":"/fleet/message","lane":"interactive"},
|
|
77
|
+
{"method":"GET","path":"/fleet/{handle}","lane":"interactive"},
|
|
78
|
+
{"method":"GET","path":"/listeners/{provider}/state","lane":"interactive"},
|
|
79
|
+
{"method":"POST","path":"/listeners/{provider}/dispatch","lane":"interactive"},
|
|
80
|
+
{"method":"POST","path":"/listeners/{provider}/failure","lane":"interactive"},
|
|
81
|
+
{"method":"POST","path":"/listeners/{provider}/status","lane":"interactive"},
|
|
82
|
+
{"method":"POST","path":"/ci/webhook/{host}","lane":"interactive"},
|
|
83
|
+
{"method":"POST","path":"/system/sync/pair","lane":"interactive"},
|
|
84
|
+
{"method":"POST","path":"/system/hosts/pair","lane":"interactive"},
|
|
85
|
+
{"method":"POST","path":"/system/hosts/enroll","lane":"interactive"},
|
|
86
|
+
{"method":"GET","path":"/system/hosts","lane":"interactive"},
|
|
87
|
+
{"method":"DELETE","path":"/system/hosts/{id}","lane":"interactive"},
|
|
88
|
+
{"method":"GET","path":"/system/hosts/connect","lane":"interactive"},
|
|
89
|
+
{"method":"POST","path":"/mcp/hosts/{id}","lane":"interactive"},
|
|
90
|
+
{"method":"GET","path":"/mcp/hosts/{id}","lane":"interactive"},
|
|
91
|
+
{"method":"DELETE","path":"/mcp/hosts/{id}","lane":"interactive"},
|
|
92
|
+
{"method":"POST","path":"/system/webext/pair","lane":"interactive"},
|
|
93
|
+
{"method":"POST","path":"/system/webext/enroll","lane":"interactive"},
|
|
94
|
+
{"method":"GET","path":"/system/webext","lane":"interactive"},
|
|
95
|
+
{"method":"DELETE","path":"/system/webext/{id}","lane":"interactive"},
|
|
96
|
+
{"method":"GET","path":"/system/webext/connect","lane":"interactive"},
|
|
97
|
+
{"method":"POST","path":"/mcp/webext/{id}","lane":"interactive"},
|
|
98
|
+
{"method":"GET","path":"/mcp/webext/{id}","lane":"interactive"},
|
|
99
|
+
{"method":"DELETE","path":"/mcp/webext/{id}","lane":"interactive"},
|
|
100
|
+
{"method":"POST","path":"/system/runners/pair","lane":"interactive"},
|
|
101
|
+
{"method":"POST","path":"/system/runners/enroll","lane":"interactive"},
|
|
102
|
+
{"method":"GET","path":"/system/runners","lane":"interactive"},
|
|
103
|
+
{"method":"DELETE","path":"/system/runners/{id}","lane":"interactive"},
|
|
104
|
+
{"method":"GET","path":"/system/runners/connect","lane":"interactive"},
|
|
105
|
+
{"method":"POST","path":"/mcp/browser/{id}","lane":"interactive"},
|
|
106
|
+
{"method":"GET","path":"/mcp/browser/{id}","lane":"interactive"},
|
|
107
|
+
{"method":"DELETE","path":"/mcp/browser/{id}","lane":"interactive"},
|
|
108
|
+
{"method":"POST","path":"/system/webext/session","lane":"interactive"},
|
|
109
|
+
{"method":"POST","path":"/system/webext/lend","lane":"interactive"},
|
|
110
|
+
{"method":"POST","path":"/system/runners/{id}/definition/sync","lane":"interactive"},
|
|
111
|
+
{"method":"GET","path":"/system/runners/git/{repo}/info/refs","lane":"bulk"},
|
|
112
|
+
{"method":"POST","path":"/system/runners/git/{repo}/git-upload-pack","lane":"bulk"},
|
|
113
|
+
{"method":"POST","path":"/system/runners/git/{repo}/git-receive-pack","lane":"bulk"},
|
|
114
|
+
{"method":"POST","path":"/system/runners/credentials","lane":"interactive"},
|
|
115
|
+
{"method":"POST","path":"/system/runners/credentials/refresh","lane":"interactive"},
|
|
116
|
+
{"method":"ALL","path":"/system/runners/translator/*","lane":"interactive"},
|
|
117
|
+
{"method":"POST","path":"/system/control/tokens","lane":"interactive"},
|
|
118
|
+
{"method":"GET","path":"/system/control/tokens","lane":"interactive"},
|
|
119
|
+
{"method":"DELETE","path":"/system/control/tokens/{id}","lane":"interactive"},
|
|
120
|
+
{"method":"GET","path":"/system/passkeys","lane":"interactive"},
|
|
121
|
+
{"method":"POST","path":"/system/passkeys/register/options","lane":"interactive"},
|
|
122
|
+
{"method":"POST","path":"/system/passkeys/register","lane":"interactive"},
|
|
123
|
+
{"method":"POST","path":"/system/passkeys/assert/options","lane":"interactive"},
|
|
124
|
+
{"method":"POST","path":"/system/passkeys/assert","lane":"interactive"},
|
|
125
|
+
{"method":"POST","path":"/system/passkeys/policy","lane":"interactive"},
|
|
126
|
+
{"method":"POST","path":"/system/passkeys/recovery","lane":"interactive"},
|
|
127
|
+
{"method":"DELETE","path":"/system/passkeys/{id}","lane":"interactive"},
|
|
128
|
+
{"method":"POST","path":"/system/session/recover","lane":"interactive"},
|
|
129
|
+
{"method":"POST","path":"/system/sessions/revoke","lane":"interactive"},
|
|
130
|
+
{"method":"POST","path":"/system/access/disable","lane":"interactive"},
|
|
131
|
+
{"method":"POST","path":"/system/authorized-key","lane":"interactive"},
|
|
132
|
+
{"method":"GET","path":"/system/sync","lane":"interactive"},
|
|
133
|
+
{"method":"POST","path":"/system/sync/report","lane":"interactive"},
|
|
134
|
+
{"method":"DELETE","path":"/system/authorized-key","lane":"interactive"},
|
|
135
|
+
{"method":"DELETE","path":"/system/authorized-key/{machine}","lane":"interactive"}
|
|
136
|
+
]
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { readFileSync } from "node:fs";
|
|
2
|
+
import { type TunnelLane, tunnelLaneNamed, tunnelLaneOf, tunnelLaneTable } from "./tunnel-lanes.js";
|
|
3
|
+
|
|
4
|
+
interface LaneCase {
|
|
5
|
+
readonly host: string;
|
|
6
|
+
readonly method: string;
|
|
7
|
+
readonly path: string;
|
|
8
|
+
readonly lane: TunnelLane;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
const read = (name: string): unknown => JSON.parse(readFileSync(new URL(name, import.meta.url), "utf8"));
|
|
12
|
+
const CASES = (read("./ingress-contract.fixture.json") as { lanes: LaneCase[] }).lanes;
|
|
13
|
+
|
|
14
|
+
test("the table the Rust edge embeds is the raw routes' lanes as they stand (pnpm --filter @intentic/sandbox-contract lanes)", () => {
|
|
15
|
+
expect(read("./tunnel-lanes.json")).toEqual(tunnelLaneTable());
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
test("every request the shared fixture names rides the lane it states", () => {
|
|
19
|
+
expect(CASES.length).toBeGreaterThan(20);
|
|
20
|
+
for (const { host, method, path, lane } of CASES) {
|
|
21
|
+
expect({ host, method, path, lane: tunnelLaneOf(host, method, path) }).toEqual({ host, method, path, lane });
|
|
22
|
+
}
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
test("a tunnel is interactive unless its upgrade names the bulk lane", () => {
|
|
26
|
+
expect(tunnelLaneNamed("bulk")).toBe("bulk");
|
|
27
|
+
expect(tunnelLaneNamed(undefined)).toBe("interactive");
|
|
28
|
+
expect(tunnelLaneNamed("interactive")).toBe("interactive");
|
|
29
|
+
expect(tunnelLaneNamed(["bulk", "bulk"])).toBe("interactive");
|
|
30
|
+
});
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { sandboxSubdomain } from "../ids/hostnames.js";
|
|
2
|
+
import { hostOwnerId } from "./ingress-contract.js";
|
|
3
|
+
import { RAW_ROUTE_LIST } from "./raw-routes.js";
|
|
4
|
+
import { servedRoute } from "./routes.js";
|
|
5
|
+
|
|
6
|
+
// Each sandbox dials two tunnels, and each browser request rides one, so a transfer never queues a keystroke, a call or
|
|
7
|
+
// an event frame behind it on one TCP connection. The front dials both (_sandbox/front tunnel.rs); the edge
|
|
8
|
+
// (_platform/ingress lanes.rs) picks by the table this file emits, and falls back to the other lane while the one picked
|
|
9
|
+
// is not held.
|
|
10
|
+
|
|
11
|
+
export type TunnelLane = "interactive" | "bulk";
|
|
12
|
+
|
|
13
|
+
// Names a tunnel's lane on its upgrade; a tunnel that names none is the interactive one.
|
|
14
|
+
export const INGRESS_LANE_HEADER = "x-intentic-lane";
|
|
15
|
+
|
|
16
|
+
export const tunnelLaneNamed = (header: string | string[] | undefined): TunnelLane => (header === "bulk" ? "bulk" : "interactive");
|
|
17
|
+
|
|
18
|
+
// A preview's dev server (every label but the daemon's own) is bulk: a page load is hundreds of requests and its assets
|
|
19
|
+
// can be large. The daemon's routes are interactive unless their policy says otherwise (RouteMeta `lane`).
|
|
20
|
+
export const tunnelLaneOf = (host: string, method: string, path: string): TunnelLane => {
|
|
21
|
+
const id = hostOwnerId(host);
|
|
22
|
+
const label = host.split(":")[0]?.split(".")[0] ?? "";
|
|
23
|
+
if (id === undefined || label !== sandboxSubdomain(id)) {
|
|
24
|
+
return "bulk";
|
|
25
|
+
}
|
|
26
|
+
return servedRoute(RAW_ROUTE_LIST, [], method, path)?.meta.lane ?? "interactive";
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
// The raw routes in registration order with the lane each rides: what the Rust edge matches a daemon request against,
|
|
30
|
+
// written to tunnel-lanes.json by scripts/write-lanes.mjs since the edge cannot run this file.
|
|
31
|
+
export const tunnelLaneTable = (): { readonly method: string; readonly path: string; readonly lane: TunnelLane }[] =>
|
|
32
|
+
RAW_ROUTE_LIST.map(({ method, path, meta }) => ({ method, path, lane: meta.lane ?? "interactive" }));
|
package/src/schemas/activity.ts
CHANGED
|
@@ -20,7 +20,9 @@ export const ActivityEventSchema = z.object({
|
|
|
20
20
|
// in: message.received, voice_utterance.received, voice_transcript.received
|
|
21
21
|
// out: message.send, reaction.add, messages.read, api.call (unclassified endpoint)
|
|
22
22
|
// system: gateway.login_failed, dispatch.failed, voice.session_started, voice.session_ended, automation.run,
|
|
23
|
-
// turn.started, turn.plan, turn.error, turn.completed, rule.
|
|
23
|
+
// turn.started, turn.plan, turn.error, turn.completed, rule.held_work, deps.verify_green, deps.verify_red,
|
|
24
|
+
// deps.breakage_routed, deps.breakage_fixup, deps.breakage_waiting, deps.breakage_held, deps.breakage_resolved,
|
|
25
|
+
// deps.breakage_spent; rule.blocked_push and rule.continued_turn are retired and only appear in older logs
|
|
24
26
|
type: z
|
|
25
27
|
.string()
|
|
26
28
|
.describe(
|