@lamplitisles/dsh-nanocodex 0.1.0-beta.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/LICENSE +65 -0
- package/NOTICE +2 -0
- package/README.md +182 -0
- package/THIRD_PARTY_NOTICES.md +29 -0
- package/cordis.patch.yml +52 -0
- package/dist/index.d.ts +435 -0
- package/dist/index.js +3405 -0
- package/dist/standard-preset.d.ts +7 -0
- package/dist/standard-preset.js +25 -0
- package/engine-release.json +14 -0
- package/package.json +119 -0
- package/presets/standard/agent.cordis.yml +6 -0
- package/presets/standard/preset.yml +2 -0
- package/vendor/nanocodex/README.md +1344 -0
- package/vendor/nanocodex/actions/events.d.mts +10 -0
- package/vendor/nanocodex/actions/events.mjs +163 -0
- package/vendor/nanocodex/actions/index.d.mts +13 -0
- package/vendor/nanocodex/actions/index.mjs +35 -0
- package/vendor/nanocodex/actions/session.d.mts +66 -0
- package/vendor/nanocodex/actions/session.mjs +59 -0
- package/vendor/nanocodex/actions/turn.d.mts +59 -0
- package/vendor/nanocodex/actions/turn.mjs +37 -0
- package/vendor/nanocodex/actions/voice.d.mts +22 -0
- package/vendor/nanocodex/actions/voice.mjs +50 -0
- package/vendor/nanocodex/browser/Agent.d.mts +55 -0
- package/vendor/nanocodex/browser/Agent.mjs +11 -0
- package/vendor/nanocodex/browser/ChatGptSubscription.d.mts +7 -0
- package/vendor/nanocodex/browser/ChatGptSubscription.mjs +19 -0
- package/vendor/nanocodex/browser/InlineAgent.mjs +266 -0
- package/vendor/nanocodex/browser/Transport.d.mts +74 -0
- package/vendor/nanocodex/browser/Transport.mjs +61 -0
- package/vendor/nanocodex/browser/Voice.d.mts +71 -0
- package/vendor/nanocodex/browser/Voice.mjs +291 -0
- package/vendor/nanocodex/browser/VoiceSession.mjs +688 -0
- package/vendor/nanocodex/browser/WorkerAgent.d.mts +65 -0
- package/vendor/nanocodex/browser/WorkerAgent.mjs +1472 -0
- package/vendor/nanocodex/browser/agent.worker.mjs +3 -0
- package/vendor/nanocodex/browser/config.d.mts +36 -0
- package/vendor/nanocodex/browser/config.mjs +439 -0
- package/vendor/nanocodex/browser/engine.mjs +13 -0
- package/vendor/nanocodex/browser/harness.mjs +55 -0
- package/vendor/nanocodex/browser/host.d.mts +83 -0
- package/vendor/nanocodex/browser/host.mjs +555 -0
- package/vendor/nanocodex/browser/hostManagedWebSocket.d.mts +14 -0
- package/vendor/nanocodex/browser/hostManagedWebSocket.mjs +110 -0
- package/vendor/nanocodex/browser/index.d.mts +92 -0
- package/vendor/nanocodex/browser/index.mjs +23 -0
- package/vendor/nanocodex/browser/indexeddb-durability-store.mjs +197 -0
- package/vendor/nanocodex/browser/workspace.d.mts +16 -0
- package/vendor/nanocodex/browser/workspace.mjs +107 -0
- package/vendor/nanocodex/cloud/Client.d.mts +83 -0
- package/vendor/nanocodex/cloud/Client.mjs +231 -0
- package/vendor/nanocodex/cloud/Decorator.d.mts +31 -0
- package/vendor/nanocodex/cloud/Decorator.mjs +37 -0
- package/vendor/nanocodex/cloud/Dialog.d.mts +113 -0
- package/vendor/nanocodex/cloud/Dialog.mjs +366 -0
- package/vendor/nanocodex/cloud/Errors.d.mts +22 -0
- package/vendor/nanocodex/cloud/Errors.mjs +39 -0
- package/vendor/nanocodex/cloud/Principal.d.mts +26 -0
- package/vendor/nanocodex/cloud/Principal.mjs +88 -0
- package/vendor/nanocodex/cloud/RemoteProvider.mjs +88 -0
- package/vendor/nanocodex/cloud/Transport.d.mts +47 -0
- package/vendor/nanocodex/cloud/Transport.mjs +454 -0
- package/vendor/nanocodex/cloud/actions/account.d.mts +9 -0
- package/vendor/nanocodex/cloud/actions/account.mjs +19 -0
- package/vendor/nanocodex/cloud/actions/agent.d.mts +18 -0
- package/vendor/nanocodex/cloud/actions/agent.mjs +428 -0
- package/vendor/nanocodex/cloud/actions/connection.d.mts +133 -0
- package/vendor/nanocodex/cloud/actions/connection.mjs +580 -0
- package/vendor/nanocodex/cloud/actions/grant.d.mts +10 -0
- package/vendor/nanocodex/cloud/actions/grant.mjs +12 -0
- package/vendor/nanocodex/cloud/actions/index.d.mts +7 -0
- package/vendor/nanocodex/cloud/actions/index.mjs +7 -0
- package/vendor/nanocodex/cloud/actions/machineUsd.d.mts +23 -0
- package/vendor/nanocodex/cloud/actions/machineUsd.mjs +37 -0
- package/vendor/nanocodex/cloud/actions/model.d.mts +12 -0
- package/vendor/nanocodex/cloud/actions/model.mjs +50 -0
- package/vendor/nanocodex/cloud/actions/mpp.d.mts +28 -0
- package/vendor/nanocodex/cloud/actions/mpp.mjs +39 -0
- package/vendor/nanocodex/cloud/index.d.mts +30 -0
- package/vendor/nanocodex/cloud/index.mjs +9 -0
- package/vendor/nanocodex/cloud/internal.mjs +533 -0
- package/vendor/nanocodex/cloud/mercator.mjs +108 -0
- package/vendor/nanocodex/cloud/server/HostPrincipal.d.mts +34 -0
- package/vendor/nanocodex/cloud/server/HostPrincipal.mjs +202 -0
- package/vendor/nanocodex/cloud/server/index.d.mts +1 -0
- package/vendor/nanocodex/cloud/server/index.mjs +1 -0
- package/vendor/nanocodex/cloud/types.d.mts +211 -0
- package/vendor/nanocodex/cloudflare/Agent.d.mts +133 -0
- package/vendor/nanocodex/cloudflare/Agent.mjs +811 -0
- package/vendor/nanocodex/cloudflare/egress-subject.mjs +19 -0
- package/vendor/nanocodex/cloudflare/egress.d.mts +41 -0
- package/vendor/nanocodex/cloudflare/egress.mjs +119 -0
- package/vendor/nanocodex/cloudflare/event-socket.mjs +316 -0
- package/vendor/nanocodex/cloudflare/index.d.mts +7 -0
- package/vendor/nanocodex/cloudflare/index.mjs +6 -0
- package/vendor/nanocodex/host/Agent.d.mts +55 -0
- package/vendor/nanocodex/host/Agent.mjs +1 -0
- package/vendor/nanocodex/host/index.d.mts +82 -0
- package/vendor/nanocodex/host/index.mjs +10 -0
- package/vendor/nanocodex/index.d.mts +81 -0
- package/vendor/nanocodex/index.mjs +12 -0
- package/vendor/nanocodex/internal.mjs +1275 -0
- package/vendor/nanocodex/managed/Agent.d.mts +380 -0
- package/vendor/nanocodex/managed/Agent.mjs +1694 -0
- package/vendor/nanocodex/managed/ManagedError.d.mts +9 -0
- package/vendor/nanocodex/managed/ManagedError.mjs +8 -0
- package/vendor/nanocodex/managed/README.md +241 -0
- package/vendor/nanocodex/managed/Voice.mjs +202 -0
- package/vendor/nanocodex/managed/index.d.mts +20 -0
- package/vendor/nanocodex/managed/index.mjs +2 -0
- package/vendor/nanocodex/managed/internal.mjs +119 -0
- package/vendor/nanocodex/node/Agent.d.mts +50 -0
- package/vendor/nanocodex/node/Agent.mjs +203 -0
- package/vendor/nanocodex/node/ChatGptSubscription.d.mts +9 -0
- package/vendor/nanocodex/node/ChatGptSubscription.mjs +17 -0
- package/vendor/nanocodex/node/Transport.d.mts +44 -0
- package/vendor/nanocodex/node/Transport.mjs +46 -0
- package/vendor/nanocodex/node/host.mjs +731 -0
- package/vendor/nanocodex/node/index.d.mts +73 -0
- package/vendor/nanocodex/node/index.mjs +17 -0
- package/vendor/nanocodex/node/workspace.d.mts +10 -0
- package/vendor/nanocodex/node/workspace.mjs +198 -0
- package/vendor/nanocodex/package.json +200 -0
- package/vendor/nanocodex/pkg-node/nanocodex.d.ts +675 -0
- package/vendor/nanocodex/pkg-node/nanocodex.js +2628 -0
- package/vendor/nanocodex/pkg-node/nanocodex_bg.wasm.d.ts +105 -0
- package/vendor/nanocodex/pkg-node/package.json +1 -0
- package/vendor/nanocodex/pkg-web/.nanocodex-bindgen-stamp +6 -0
- package/vendor/nanocodex/pkg-web/nanocodex-build.json +1 -0
- package/vendor/nanocodex/pkg-web/nanocodex.d.ts +805 -0
- package/vendor/nanocodex/pkg-web/nanocodex.js +2714 -0
- package/vendor/nanocodex/pkg-web/nanocodex_bg.js +2615 -0
- package/vendor/nanocodex/pkg-web/nanocodex_bg.wasm +0 -0
- package/vendor/nanocodex/pkg-web/nanocodex_bg.wasm.d.ts +105 -0
- package/vendor/nanocodex/pkg-web/nanocodex_worker.js +9 -0
- package/vendor/nanocodex/pkg-web/package.json +1 -0
- package/vendor/nanocodex/runtime/chatgpt-subscription.mjs +212 -0
- package/vendor/nanocodex/runtime/cloudflare-durability-store.d.mts +26 -0
- package/vendor/nanocodex/runtime/cloudflare-durability-store.mjs +168 -0
- package/vendor/nanocodex/runtime/code-evaluator.worker.mjs +137 -0
- package/vendor/nanocodex/runtime/code-runtime.mjs +1 -0
- package/vendor/nanocodex/runtime/durability-store.d.mts +85 -0
- package/vendor/nanocodex/runtime/durability-store.mjs +631 -0
- package/vendor/nanocodex/runtime/durability.mjs +153 -0
- package/vendor/nanocodex/runtime/managed-transport.mjs +460 -0
- package/vendor/nanocodex/runtime/mcp-runtime.mjs +534 -0
- package/vendor/nanocodex/runtime/postgres-durability-store.d.mts +64 -0
- package/vendor/nanocodex/runtime/postgres-durability-store.mjs +532 -0
- package/vendor/nanocodex/runtime/quickjs-evaluator.d.mts +17 -0
- package/vendor/nanocodex/runtime/quickjs-evaluator.mjs +236 -0
- package/vendor/nanocodex/runtime/responses-transport.mjs +22 -0
- package/vendor/nanocodex/runtime/subagents.d.mts +107 -0
- package/vendor/nanocodex/runtime/subagents.mjs +54 -0
- package/vendor/nanocodex/runtime/subscription-store.d.mts +8 -0
- package/vendor/nanocodex/runtime/subscription-store.mjs +42 -0
- package/vendor/nanocodex/runtime/tempo-provider.d.mts +125 -0
- package/vendor/nanocodex/runtime/tempo-provider.mjs +264 -0
- package/vendor/nanocodex/runtime/tool-configuration.mjs +1 -0
- package/vendor/nanocodex/runtime/tool-router.mjs +1 -0
- package/vendor/nanocodex/runtime/utf8.mjs +1 -0
- package/vendor/nanocodex/runtime/worker-evaluator.mjs +162 -0
- package/vendor/nanocodex/runtime/workspace.d.mts +1 -0
- package/vendor/nanocodex/runtime/workspace.mjs +1 -0
- package/vendor/nanocodex/tools/Tools.d.mts +56 -0
- package/vendor/nanocodex/tools/Tools.mjs +136 -0
- package/vendor/nanocodex/tools/artifact.d.mts +1 -0
- package/vendor/nanocodex/tools/artifact.mjs +1 -0
- package/vendor/nanocodex/tools/attachment.mjs +1 -0
- package/vendor/nanocodex/tools/bash.d.mts +1 -0
- package/vendor/nanocodex/tools/bash.mjs +1 -0
- package/vendor/nanocodex/tools/browser/accountInfo.mjs +795 -0
- package/vendor/nanocodex/tools/browser/browserBuffer.mjs +18 -0
- package/vendor/nanocodex/tools/browser/browserCompiler.mjs +155 -0
- package/vendor/nanocodex/tools/browser/browserEgress.mjs +161 -0
- package/vendor/nanocodex/tools/browser/browserPython.mjs +133 -0
- package/vendor/nanocodex/tools/browser/browserShell.mjs +1075 -0
- package/vendor/nanocodex/tools/browser/browserSprintf.mjs +4 -0
- package/vendor/nanocodex/tools/browser/browserSsh.mjs +201 -0
- package/vendor/nanocodex/tools/browser/browserZlib.mjs +136 -0
- package/vendor/nanocodex/tools/browser/compiler.worker.mjs +160 -0
- package/vendor/nanocodex/tools/browser/devTunnelsSshBrowser.mjs +13 -0
- package/vendor/nanocodex/tools/browser/index.d.mts +209 -0
- package/vendor/nanocodex/tools/browser/index.mjs +174 -0
- package/vendor/nanocodex/tools/browser/opfsGit.mjs +248 -0
- package/vendor/nanocodex/tools/browser/python.worker.mjs +114 -0
- package/vendor/nanocodex/tools/browser/threadGit.mjs +197 -0
- package/vendor/nanocodex/tools/browser/unsupportedNodeRsa.mjs +5 -0
- package/vendor/nanocodex/tools/browser/workspace.mjs +83 -0
- package/vendor/nanocodex/tools/dataset.d.mts +1 -0
- package/vendor/nanocodex/tools/dataset.mjs +1 -0
- package/vendor/nanocodex/tools/datasetContract.mjs +1 -0
- package/vendor/nanocodex/tools/datasetEngine.mjs +1 -0
- package/vendor/nanocodex/tools/hostedCatalog.d.mts +1 -0
- package/vendor/nanocodex/tools/hostedCatalog.mjs +1 -0
- package/vendor/nanocodex/tools/index.d.mts +49 -0
- package/vendor/nanocodex/tools/index.mjs +19 -0
- package/vendor/nanocodex/tools/namedTool.mjs +1 -0
- package/vendor/nanocodex/tools/repository-workspace.d.mts +1 -0
- package/vendor/nanocodex/tools/repository-workspace.mjs +1 -0
- package/vendor/nanocodex/tools/ssh.d.mts +1 -0
- package/vendor/nanocodex/tools/ssh.mjs +1 -0
- package/vendor/nanocodex/tools/standard.mjs +1 -0
- package/vendor/nanocodex/tools/standardDescriptions.mjs +1 -0
- package/vendor/nanocodex/types.d.mts +663 -0
- package/vendor/nanocodex/wasm.d.mts +4 -0
- package/vendor/nanocodex/worker/ChatGptSubscription.d.mts +9 -0
- package/vendor/nanocodex/worker/ChatGptSubscription.mjs +29 -0
- package/vendor/nanocodex/worker/index.d.mts +18 -0
- package/vendor/nanocodex/worker/index.mjs +5 -0
- package/vendor/nanocodex-tools/README.md +65 -0
- package/vendor/nanocodex-tools/dist/hosted/app-tool-catalog.d.ts +7 -0
- package/vendor/nanocodex-tools/dist/hosted/app-tool-catalog.js +14 -0
- package/vendor/nanocodex-tools/dist/hosted/broker-core.d.ts +172 -0
- package/vendor/nanocodex-tools/dist/hosted/broker-core.js +1278 -0
- package/vendor/nanocodex-tools/dist/hosted/index.d.ts +3 -0
- package/vendor/nanocodex-tools/dist/hosted/index.js +3 -0
- package/vendor/nanocodex-tools/dist/hosted/protocol.d.ts +120 -0
- package/vendor/nanocodex-tools/dist/hosted/protocol.js +415 -0
- package/vendor/nanocodex-tools/dist/index.d.ts +19 -0
- package/vendor/nanocodex-tools/dist/index.js +14 -0
- package/vendor/nanocodex-tools/dist/memory.d.ts +204 -0
- package/vendor/nanocodex-tools/dist/memory.js +411 -0
- package/vendor/nanocodex-tools/dist/namespace.d.ts +87 -0
- package/vendor/nanocodex-tools/dist/namespace.js +338 -0
- package/vendor/nanocodex-tools/dist/repository-archive.d.ts +4 -0
- package/vendor/nanocodex-tools/dist/repository-archive.js +104 -0
- package/vendor/nanocodex-tools/dist/runtime.d.ts +30 -0
- package/vendor/nanocodex-tools/dist/runtime.js +37 -0
- package/vendor/nanocodex-tools/dist/session.d.ts +167 -0
- package/vendor/nanocodex-tools/dist/session.js +286 -0
- package/vendor/nanocodex-tools/dist/shell.d.ts +51 -0
- package/vendor/nanocodex-tools/dist/shell.js +553 -0
- package/vendor/nanocodex-tools/dist/ssh.d.ts +29 -0
- package/vendor/nanocodex-tools/dist/ssh.js +66 -0
- package/vendor/nanocodex-tools/dist/workspace.d.ts +45 -0
- package/vendor/nanocodex-tools/dist/workspace.js +237 -0
- package/vendor/nanocodex-tools/package.json +122 -0
- package/vendor/nanocodex-tools/runtime/code-runtime.mjs +817 -0
- package/vendor/nanocodex-tools/runtime/tool-configuration.mjs +50 -0
- package/vendor/nanocodex-tools/runtime/tool-router.mjs +730 -0
- package/vendor/nanocodex-tools/runtime/utf8.d.mts +2 -0
- package/vendor/nanocodex-tools/runtime/utf8.mjs +25 -0
- package/vendor/nanocodex-tools/tools/artifact.d.mts +69 -0
- package/vendor/nanocodex-tools/tools/artifact.mjs +270 -0
- package/vendor/nanocodex-tools/tools/attachment.mjs +603 -0
- package/vendor/nanocodex-tools/tools/bash.d.mts +90 -0
- package/vendor/nanocodex-tools/tools/bash.mjs +742 -0
- package/vendor/nanocodex-tools/tools/dataset.d.mts +9 -0
- package/vendor/nanocodex-tools/tools/dataset.mjs +47 -0
- package/vendor/nanocodex-tools/tools/datasetContract.mjs +54 -0
- package/vendor/nanocodex-tools/tools/datasetEngine.mjs +1167 -0
- package/vendor/nanocodex-tools/tools/execution-contract.d.mts +5 -0
- package/vendor/nanocodex-tools/tools/execution-contract.mjs +63 -0
- package/vendor/nanocodex-tools/tools/hostedCatalog.d.mts +20 -0
- package/vendor/nanocodex-tools/tools/hostedCatalog.mjs +67 -0
- package/vendor/nanocodex-tools/tools/hostedMachine.d.mts +10 -0
- package/vendor/nanocodex-tools/tools/hostedMachine.mjs +56 -0
- package/vendor/nanocodex-tools/tools/just-bash-browser.d.mts +1 -0
- package/vendor/nanocodex-tools/tools/just-bash-browser.mjs +1 -0
- package/vendor/nanocodex-tools/tools/namedTool.d.mts +3 -0
- package/vendor/nanocodex-tools/tools/namedTool.mjs +3 -0
- package/vendor/nanocodex-tools/tools/nodeProcess.d.mts +10 -0
- package/vendor/nanocodex-tools/tools/nodeProcess.mjs +314 -0
- package/vendor/nanocodex-tools/tools/processOutput.mjs +56 -0
- package/vendor/nanocodex-tools/tools/repository-workspace.d.mts +55 -0
- package/vendor/nanocodex-tools/tools/repository-workspace.mjs +397 -0
- package/vendor/nanocodex-tools/tools/ssh.d.mts +56 -0
- package/vendor/nanocodex-tools/tools/ssh.mjs +503 -0
- package/vendor/nanocodex-tools/tools/standard.mjs +458 -0
- package/vendor/nanocodex-tools/tools/standardDescriptions.mjs +3 -0
- package/vendor/nanocodex-tools/tools/types.d.mts +83 -0
- package/vendor/nanocodex-tools/tools/workspace.d.mts +25 -0
- package/vendor/nanocodex-tools/tools/workspace.mjs +282 -0
- package/vendor/nanocodex-tools/tools/x.d.mts +30 -0
- package/vendor/nanocodex-tools/tools/x.mjs +91 -0
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
/** @deprecated Namespace size is governed by the selected providers. */
|
|
2
|
+
export declare const MAX_NAMESPACE_MOUNTS: number;
|
|
3
|
+
export declare const MAX_NAMESPACE_GRANTS = 128;
|
|
4
|
+
export declare const MAX_NAMESPACE_PATH_BYTES = 4096;
|
|
5
|
+
export declare const MAX_NAMESPACE_PATH_SEGMENTS = 256;
|
|
6
|
+
export declare const MAX_NAMESPACE_SEGMENT_BYTES = 255;
|
|
7
|
+
export declare const MAX_NAMESPACE_ID_BYTES = 256;
|
|
8
|
+
export declare const NAMESPACE_RIGHTS: readonly ["namespace.discover", "filesystem.read", "filesystem.write", "process.exec", "process.stdin", "network.preview"];
|
|
9
|
+
export type NamespaceRight = (typeof NAMESPACE_RIGHTS)[number];
|
|
10
|
+
declare const namespaceIdentityBrand: unique symbol;
|
|
11
|
+
export type NamespaceIdentity = string & {
|
|
12
|
+
readonly [namespaceIdentityBrand]: "NamespaceIdentity";
|
|
13
|
+
};
|
|
14
|
+
export type NamespaceMountInput = Readonly<{
|
|
15
|
+
root: string;
|
|
16
|
+
mountId: string;
|
|
17
|
+
handId: string;
|
|
18
|
+
exportId: string;
|
|
19
|
+
generation: string;
|
|
20
|
+
rights: readonly NamespaceRight[];
|
|
21
|
+
}>;
|
|
22
|
+
export type NamespaceManifestInput = Readonly<{
|
|
23
|
+
manifestId: string;
|
|
24
|
+
mounts: readonly NamespaceMountInput[];
|
|
25
|
+
}>;
|
|
26
|
+
export type NamespaceMount = Readonly<{
|
|
27
|
+
root: string;
|
|
28
|
+
mountId: NamespaceIdentity;
|
|
29
|
+
handId: NamespaceIdentity;
|
|
30
|
+
exportId: NamespaceIdentity;
|
|
31
|
+
generation: NamespaceIdentity;
|
|
32
|
+
rights: readonly NamespaceRight[];
|
|
33
|
+
}>;
|
|
34
|
+
export type NamespaceManifest = Readonly<{
|
|
35
|
+
manifestId: NamespaceIdentity;
|
|
36
|
+
mounts: readonly NamespaceMount[];
|
|
37
|
+
}>;
|
|
38
|
+
export type NamespaceGrant = Readonly<{
|
|
39
|
+
mountId: NamespaceIdentity;
|
|
40
|
+
path: string;
|
|
41
|
+
rights: readonly NamespaceRight[];
|
|
42
|
+
}>;
|
|
43
|
+
export type NamespaceScope = Readonly<{
|
|
44
|
+
manifest: NamespaceManifest;
|
|
45
|
+
defaultCwd: string;
|
|
46
|
+
grants: readonly NamespaceGrant[];
|
|
47
|
+
}>;
|
|
48
|
+
export type NamespaceAttenuation = Readonly<{
|
|
49
|
+
mountId: string;
|
|
50
|
+
path?: string;
|
|
51
|
+
rights?: readonly NamespaceRight[];
|
|
52
|
+
}>;
|
|
53
|
+
export type NamespaceRoute = Readonly<{
|
|
54
|
+
cwd: string;
|
|
55
|
+
relativePath: string;
|
|
56
|
+
mount: NamespaceMount;
|
|
57
|
+
rights: readonly NamespaceRight[];
|
|
58
|
+
}>;
|
|
59
|
+
export declare class NamespaceError extends Error {
|
|
60
|
+
readonly code: "invalid_manifest" | "invalid_path" | "invalid_scope" | "access_denied" | "no_execution_root";
|
|
61
|
+
constructor(code: "invalid_manifest" | "invalid_path" | "invalid_scope" | "access_denied" | "no_execution_root", message: string);
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* Admits and snapshots a mount table. Calling this function is an authority
|
|
65
|
+
* boundary: later APIs only preserve or attenuate mounts admitted here.
|
|
66
|
+
*/
|
|
67
|
+
export declare function createNamespaceManifest(input: NamespaceManifestInput): NamespaceManifest;
|
|
68
|
+
/** Derives a deterministic portable mount root from an opaque machine identity. */
|
|
69
|
+
export declare function namespaceMountRoot(machineId: string): string;
|
|
70
|
+
/** Resolves an absolute logical path without consulting the host filesystem. */
|
|
71
|
+
export declare function normalizeNamespacePath(path: string): string;
|
|
72
|
+
/** Resolves an optional workdir against a captured, absolute default cwd. */
|
|
73
|
+
export declare function resolveNamespaceCwd(defaultCwd: string, workdir?: string): string;
|
|
74
|
+
/** Component-aware containment; lexical siblings such as /host2 never match /host. */
|
|
75
|
+
export declare function isNamespacePathWithin(path: string, parent: string): boolean;
|
|
76
|
+
/** Returns the longest component-boundary mount match, if one exists. */
|
|
77
|
+
export declare function resolveNamespaceMount(manifest: NamespaceManifest, path: string): NamespaceMount | undefined;
|
|
78
|
+
/** Creates the root scope with every right present in the admitted manifest. */
|
|
79
|
+
export declare function createNamespaceScope(manifest: NamespaceManifest, defaultCwd: string): NamespaceScope;
|
|
80
|
+
/**
|
|
81
|
+
* Creates a child snapshot. Omitted attenuation inherits exactly; supplied
|
|
82
|
+
* entries may only remove mounts, narrow paths, or remove rights.
|
|
83
|
+
*/
|
|
84
|
+
export declare function deriveChildNamespaceScope(parent: NamespaceScope, attenuation?: readonly NamespaceAttenuation[]): NamespaceScope;
|
|
85
|
+
/** Resolves and authorizes a cwd for process routing on its owning hand. */
|
|
86
|
+
export declare function routeNamespaceCwd(scope: NamespaceScope, workdir?: string, requiredRight?: NamespaceRight): NamespaceRoute;
|
|
87
|
+
export {};
|
|
@@ -0,0 +1,338 @@
|
|
|
1
|
+
import { utf8ByteLength } from "../runtime/utf8.mjs";
|
|
2
|
+
/** @deprecated Namespace size is governed by the selected providers. */
|
|
3
|
+
export const MAX_NAMESPACE_MOUNTS = Infinity;
|
|
4
|
+
export const MAX_NAMESPACE_GRANTS = 128;
|
|
5
|
+
export const MAX_NAMESPACE_PATH_BYTES = 4_096;
|
|
6
|
+
export const MAX_NAMESPACE_PATH_SEGMENTS = 256;
|
|
7
|
+
export const MAX_NAMESPACE_SEGMENT_BYTES = 255;
|
|
8
|
+
export const MAX_NAMESPACE_ID_BYTES = 256;
|
|
9
|
+
export const NAMESPACE_RIGHTS = Object.freeze([
|
|
10
|
+
"namespace.discover",
|
|
11
|
+
"filesystem.read",
|
|
12
|
+
"filesystem.write",
|
|
13
|
+
"process.exec",
|
|
14
|
+
"process.stdin",
|
|
15
|
+
"network.preview",
|
|
16
|
+
]);
|
|
17
|
+
export class NamespaceError extends Error {
|
|
18
|
+
code;
|
|
19
|
+
constructor(code, message) {
|
|
20
|
+
super(message);
|
|
21
|
+
this.code = code;
|
|
22
|
+
this.name = "NamespaceError";
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
const PORTABLE_ROOT = /^[a-z0-9](?:[a-z0-9._-]{0,61}[a-z0-9])?$/;
|
|
26
|
+
const WINDOWS_DEVICE_ROOT = /^(?:con|prn|aux|nul|com[1-9]|lpt[1-9])(?:\.|$)/;
|
|
27
|
+
const CONTROL_CHARACTER = /[\u0000-\u001f\u007f]/;
|
|
28
|
+
const HASH_UTF8 = new TextEncoder();
|
|
29
|
+
const RESERVED_ROOTS = new Set([
|
|
30
|
+
".nanocodex",
|
|
31
|
+
"dev",
|
|
32
|
+
"proc",
|
|
33
|
+
"tmp",
|
|
34
|
+
]);
|
|
35
|
+
const RESERVED_HAND_ROOTS = new Set([
|
|
36
|
+
...RESERVED_ROOTS,
|
|
37
|
+
"brain",
|
|
38
|
+
"sandbox",
|
|
39
|
+
]);
|
|
40
|
+
const RIGHT_SET = new Set(NAMESPACE_RIGHTS);
|
|
41
|
+
const admittedManifests = new WeakSet();
|
|
42
|
+
const admittedScopes = new WeakSet();
|
|
43
|
+
/**
|
|
44
|
+
* Admits and snapshots a mount table. Calling this function is an authority
|
|
45
|
+
* boundary: later APIs only preserve or attenuate mounts admitted here.
|
|
46
|
+
*/
|
|
47
|
+
export function createNamespaceManifest(input) {
|
|
48
|
+
if (!isRecord(input) || !Array.isArray(input.mounts)) {
|
|
49
|
+
throw new NamespaceError("invalid_manifest", "namespace manifest must contain a mounts array");
|
|
50
|
+
}
|
|
51
|
+
if (input.mounts.length === 0) {
|
|
52
|
+
throw new NamespaceError("invalid_manifest", "namespace manifest must contain at least one mount");
|
|
53
|
+
}
|
|
54
|
+
const manifestId = parseIdentity(input.manifestId, "manifestId");
|
|
55
|
+
const roots = new Set();
|
|
56
|
+
const mountIds = new Set();
|
|
57
|
+
const mounts = input.mounts.map((candidate, index) => {
|
|
58
|
+
if (!isRecord(candidate)) {
|
|
59
|
+
throw new NamespaceError("invalid_manifest", `mount ${index} must be an object`);
|
|
60
|
+
}
|
|
61
|
+
const root = parseMountRoot(candidate.root, index);
|
|
62
|
+
if (roots.has(root)) {
|
|
63
|
+
throw new NamespaceError("invalid_manifest", `duplicate mount root ${root}`);
|
|
64
|
+
}
|
|
65
|
+
roots.add(root);
|
|
66
|
+
const mountId = parseIdentity(candidate.mountId, `mount ${root} mountId`);
|
|
67
|
+
if (mountIds.has(mountId)) {
|
|
68
|
+
throw new NamespaceError("invalid_manifest", `duplicate mount identity for ${root}`);
|
|
69
|
+
}
|
|
70
|
+
mountIds.add(mountId);
|
|
71
|
+
return Object.freeze({
|
|
72
|
+
root,
|
|
73
|
+
mountId,
|
|
74
|
+
handId: parseIdentity(candidate.handId, `mount ${root} handId`),
|
|
75
|
+
exportId: parseIdentity(candidate.exportId, `mount ${root} exportId`),
|
|
76
|
+
generation: parseIdentity(candidate.generation, `mount ${root} generation`),
|
|
77
|
+
rights: parseRights(candidate.rights, `mount ${root}`),
|
|
78
|
+
});
|
|
79
|
+
});
|
|
80
|
+
// Canonical ordering makes the same admitted mapping stable regardless of
|
|
81
|
+
// discovery order. Routing never consults display or host names.
|
|
82
|
+
mounts.sort((left, right) => left.root.localeCompare(right.root, "en"));
|
|
83
|
+
const manifest = Object.freeze({ manifestId, mounts: Object.freeze(mounts) });
|
|
84
|
+
admittedManifests.add(manifest);
|
|
85
|
+
return manifest;
|
|
86
|
+
}
|
|
87
|
+
/** Derives a deterministic portable mount root from an opaque machine identity. */
|
|
88
|
+
export function namespaceMountRoot(machineId) {
|
|
89
|
+
if (typeof machineId !== "string" || machineId.length === 0) {
|
|
90
|
+
throw new TypeError("machine identity must be a non-empty string");
|
|
91
|
+
}
|
|
92
|
+
const exact = machineId.toLowerCase();
|
|
93
|
+
if (exact === machineId
|
|
94
|
+
&& PORTABLE_ROOT.test(exact)
|
|
95
|
+
&& !WINDOWS_DEVICE_ROOT.test(exact)
|
|
96
|
+
&& !RESERVED_HAND_ROOTS.has(exact)) {
|
|
97
|
+
return `/${exact}`;
|
|
98
|
+
}
|
|
99
|
+
const slug = exact.replace(/[^a-z0-9._-]+/g, "-").replace(/^[^a-z0-9]+|[^a-z0-9]+$/g, "")
|
|
100
|
+
.slice(0, 48) || "hand";
|
|
101
|
+
return `/hand-${slug}-${stableHash(machineId).slice(0, 8)}`;
|
|
102
|
+
}
|
|
103
|
+
/** Resolves an absolute logical path without consulting the host filesystem. */
|
|
104
|
+
export function normalizeNamespacePath(path) {
|
|
105
|
+
return normalizePath(path, undefined);
|
|
106
|
+
}
|
|
107
|
+
/** Resolves an optional workdir against a captured, absolute default cwd. */
|
|
108
|
+
export function resolveNamespaceCwd(defaultCwd, workdir) {
|
|
109
|
+
const base = normalizeNamespacePath(defaultCwd);
|
|
110
|
+
if (workdir === undefined || workdir === "")
|
|
111
|
+
return base;
|
|
112
|
+
return normalizePath(workdir, base);
|
|
113
|
+
}
|
|
114
|
+
/** Component-aware containment; lexical siblings such as /host2 never match /host. */
|
|
115
|
+
export function isNamespacePathWithin(path, parent) {
|
|
116
|
+
const normalizedPath = normalizeNamespacePath(path);
|
|
117
|
+
const normalizedParent = normalizeNamespacePath(parent);
|
|
118
|
+
return pathWithinNormalized(normalizedPath, normalizedParent);
|
|
119
|
+
}
|
|
120
|
+
/** Returns the longest component-boundary mount match, if one exists. */
|
|
121
|
+
export function resolveNamespaceMount(manifest, path) {
|
|
122
|
+
assertManifest(manifest);
|
|
123
|
+
const normalized = normalizeNamespacePath(path);
|
|
124
|
+
let match;
|
|
125
|
+
for (const mount of manifest.mounts) {
|
|
126
|
+
if (pathWithinNormalized(normalized, mount.root)
|
|
127
|
+
&& (match === undefined || mount.root.length > match.root.length)) {
|
|
128
|
+
match = mount;
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
return match;
|
|
132
|
+
}
|
|
133
|
+
/** Creates the root scope with every right present in the admitted manifest. */
|
|
134
|
+
export function createNamespaceScope(manifest, defaultCwd) {
|
|
135
|
+
assertManifest(manifest);
|
|
136
|
+
const cwd = normalizeNamespacePath(defaultCwd);
|
|
137
|
+
const grants = manifest.mounts.map((mount) => Object.freeze({
|
|
138
|
+
mountId: mount.mountId,
|
|
139
|
+
path: mount.root,
|
|
140
|
+
rights: mount.rights,
|
|
141
|
+
}));
|
|
142
|
+
return admitScope(manifest, cwd, grants);
|
|
143
|
+
}
|
|
144
|
+
/**
|
|
145
|
+
* Creates a child snapshot. Omitted attenuation inherits exactly; supplied
|
|
146
|
+
* entries may only remove mounts, narrow paths, or remove rights.
|
|
147
|
+
*/
|
|
148
|
+
export function deriveChildNamespaceScope(parent, attenuation) {
|
|
149
|
+
assertScope(parent);
|
|
150
|
+
if (attenuation === undefined) {
|
|
151
|
+
return admitScope(parent.manifest, parent.defaultCwd, parent.grants);
|
|
152
|
+
}
|
|
153
|
+
if (!Array.isArray(attenuation) || attenuation.length > MAX_NAMESPACE_GRANTS) {
|
|
154
|
+
throw new NamespaceError("invalid_scope", `child namespace may contain at most ${MAX_NAMESPACE_GRANTS} grants`);
|
|
155
|
+
}
|
|
156
|
+
const grants = [];
|
|
157
|
+
const keys = new Set();
|
|
158
|
+
for (const [index, request] of attenuation.entries()) {
|
|
159
|
+
if (!isRecord(request)) {
|
|
160
|
+
throw new NamespaceError("invalid_scope", `attenuation ${index} must be an object`);
|
|
161
|
+
}
|
|
162
|
+
const mountId = parseIdentity(request.mountId, `attenuation ${index} mountId`);
|
|
163
|
+
const mount = parent.manifest.mounts.find((candidate) => candidate.mountId === mountId);
|
|
164
|
+
if (mount === undefined) {
|
|
165
|
+
throw new NamespaceError("access_denied", "attenuation references an inaccessible mount");
|
|
166
|
+
}
|
|
167
|
+
if (request.path !== undefined && typeof request.path !== "string") {
|
|
168
|
+
throw new NamespaceError("invalid_scope", `attenuation ${index} path must be a string`);
|
|
169
|
+
}
|
|
170
|
+
const path = request.path === undefined
|
|
171
|
+
? mount.root
|
|
172
|
+
: resolveNamespaceCwd(mount.root, request.path);
|
|
173
|
+
if (!pathWithinNormalized(path, mount.root)) {
|
|
174
|
+
throw new NamespaceError("access_denied", "attenuation path escapes its mount");
|
|
175
|
+
}
|
|
176
|
+
const available = effectiveRights(parent, mountId, path);
|
|
177
|
+
const rights = request.rights === undefined
|
|
178
|
+
? available
|
|
179
|
+
: parseRights(request.rights, `attenuation ${index}`);
|
|
180
|
+
if (rights.some((right) => !available.includes(right))) {
|
|
181
|
+
throw new NamespaceError("access_denied", "child namespace rights exceed its parent scope");
|
|
182
|
+
}
|
|
183
|
+
const key = `${mountId}\u0000${path}`;
|
|
184
|
+
if (keys.has(key)) {
|
|
185
|
+
throw new NamespaceError("invalid_scope", "duplicate child namespace grant");
|
|
186
|
+
}
|
|
187
|
+
keys.add(key);
|
|
188
|
+
grants.push(Object.freeze({ mountId, path, rights }));
|
|
189
|
+
}
|
|
190
|
+
// Scope construction happens only after every requested grant is validated.
|
|
191
|
+
// Therefore an inaccessible inherited cwd rejects the whole derivation.
|
|
192
|
+
return admitScope(parent.manifest, parent.defaultCwd, grants);
|
|
193
|
+
}
|
|
194
|
+
/** Resolves and authorizes a cwd for process routing on its owning hand. */
|
|
195
|
+
export function routeNamespaceCwd(scope, workdir, requiredRight = "process.exec") {
|
|
196
|
+
assertScope(scope);
|
|
197
|
+
assertRight(requiredRight, "required right");
|
|
198
|
+
const cwd = resolveNamespaceCwd(scope.defaultCwd, workdir);
|
|
199
|
+
const mount = resolveNamespaceMount(scope.manifest, cwd);
|
|
200
|
+
if (mount === undefined) {
|
|
201
|
+
throw new NamespaceError("no_execution_root", `no mount owns namespace cwd ${cwd}`);
|
|
202
|
+
}
|
|
203
|
+
const rights = effectiveRights(scope, mount.mountId, cwd);
|
|
204
|
+
if (!rights.includes(requiredRight)) {
|
|
205
|
+
throw new NamespaceError("access_denied", `namespace cwd ${cwd} lacks ${requiredRight}`);
|
|
206
|
+
}
|
|
207
|
+
const relativePath = cwd === mount.root ? "/" : cwd.slice(mount.root.length);
|
|
208
|
+
return Object.freeze({ cwd, relativePath, mount, rights });
|
|
209
|
+
}
|
|
210
|
+
function admitScope(manifest, defaultCwd, sourceGrants) {
|
|
211
|
+
const grants = Object.freeze(sourceGrants.map((grant) => Object.freeze({
|
|
212
|
+
mountId: grant.mountId,
|
|
213
|
+
path: grant.path,
|
|
214
|
+
rights: grant.rights,
|
|
215
|
+
})));
|
|
216
|
+
if (grants.length === 0 || !grants.some((grant) => pathWithinNormalized(defaultCwd, grant.path))) {
|
|
217
|
+
throw new NamespaceError("access_denied", "child namespace cannot access its inherited cwd");
|
|
218
|
+
}
|
|
219
|
+
const scope = Object.freeze({ manifest, defaultCwd, grants });
|
|
220
|
+
admittedScopes.add(scope);
|
|
221
|
+
return scope;
|
|
222
|
+
}
|
|
223
|
+
function effectiveRights(scope, mountId, path) {
|
|
224
|
+
const rights = new Set();
|
|
225
|
+
for (const grant of scope.grants) {
|
|
226
|
+
if (grant.mountId === mountId && pathWithinNormalized(path, grant.path)) {
|
|
227
|
+
for (const right of grant.rights)
|
|
228
|
+
rights.add(right);
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
return Object.freeze(NAMESPACE_RIGHTS.filter((right) => rights.has(right)));
|
|
232
|
+
}
|
|
233
|
+
function normalizePath(path, base) {
|
|
234
|
+
if (typeof path !== "string") {
|
|
235
|
+
throw new NamespaceError("invalid_path", "namespace path must be a string");
|
|
236
|
+
}
|
|
237
|
+
assertByteLimit(path, MAX_NAMESPACE_PATH_BYTES, "namespace path", "invalid_path");
|
|
238
|
+
if (CONTROL_CHARACTER.test(path) || path.includes("\\")) {
|
|
239
|
+
throw new NamespaceError("invalid_path", "namespace path contains a non-portable character");
|
|
240
|
+
}
|
|
241
|
+
const absolute = path.startsWith("/");
|
|
242
|
+
if (!absolute && base === undefined) {
|
|
243
|
+
throw new NamespaceError("invalid_path", "namespace path must be absolute");
|
|
244
|
+
}
|
|
245
|
+
const parts = absolute ? [] : splitNormalizedBase(base);
|
|
246
|
+
for (const part of path.split("/")) {
|
|
247
|
+
if (part === "" || part === ".")
|
|
248
|
+
continue;
|
|
249
|
+
if (part === "..") {
|
|
250
|
+
if (parts.length === 0) {
|
|
251
|
+
throw new NamespaceError("invalid_path", "namespace path escapes logical root");
|
|
252
|
+
}
|
|
253
|
+
parts.pop();
|
|
254
|
+
continue;
|
|
255
|
+
}
|
|
256
|
+
assertByteLimit(part, MAX_NAMESPACE_SEGMENT_BYTES, "namespace path segment", "invalid_path");
|
|
257
|
+
parts.push(part);
|
|
258
|
+
if (parts.length > MAX_NAMESPACE_PATH_SEGMENTS) {
|
|
259
|
+
throw new NamespaceError("invalid_path", `namespace path exceeds ${MAX_NAMESPACE_PATH_SEGMENTS} segments`);
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
const normalized = parts.length === 0 ? "/" : `/${parts.join("/")}`;
|
|
263
|
+
assertByteLimit(normalized, MAX_NAMESPACE_PATH_BYTES, "normalized namespace path", "invalid_path");
|
|
264
|
+
return normalized;
|
|
265
|
+
}
|
|
266
|
+
function splitNormalizedBase(base) {
|
|
267
|
+
return base === "/" ? [] : base.slice(1).split("/");
|
|
268
|
+
}
|
|
269
|
+
function pathWithinNormalized(path, parent) {
|
|
270
|
+
return parent === "/" || path === parent || path.startsWith(`${parent}/`);
|
|
271
|
+
}
|
|
272
|
+
function parseMountRoot(value, index) {
|
|
273
|
+
if (typeof value !== "string" || !value.startsWith("/")) {
|
|
274
|
+
throw new NamespaceError("invalid_manifest", `mount ${index} root must be absolute`);
|
|
275
|
+
}
|
|
276
|
+
const normalized = normalizeNamespacePath(value);
|
|
277
|
+
const name = normalized.slice(1);
|
|
278
|
+
if (RESERVED_ROOTS.has(name)) {
|
|
279
|
+
throw new NamespaceError("invalid_manifest", `mount root ${value} is reserved`);
|
|
280
|
+
}
|
|
281
|
+
if (!PORTABLE_ROOT.test(name) || normalized !== value || WINDOWS_DEVICE_ROOT.test(name)) {
|
|
282
|
+
throw new NamespaceError("invalid_manifest", `mount root ${value} is not a portable top-level root`);
|
|
283
|
+
}
|
|
284
|
+
return normalized;
|
|
285
|
+
}
|
|
286
|
+
function parseIdentity(value, label) {
|
|
287
|
+
if (typeof value !== "string" || value.length === 0 || value.trim() !== value
|
|
288
|
+
|| CONTROL_CHARACTER.test(value)) {
|
|
289
|
+
throw new NamespaceError("invalid_manifest", `${label} must be a non-empty opaque identity`);
|
|
290
|
+
}
|
|
291
|
+
assertByteLimit(value, MAX_NAMESPACE_ID_BYTES, label, "invalid_manifest");
|
|
292
|
+
return value;
|
|
293
|
+
}
|
|
294
|
+
function parseRights(value, label) {
|
|
295
|
+
if (!Array.isArray(value) || value.length === 0 || value.length > NAMESPACE_RIGHTS.length) {
|
|
296
|
+
throw new NamespaceError("invalid_manifest", `${label} must contain a bounded non-empty rights set`);
|
|
297
|
+
}
|
|
298
|
+
const rights = new Set();
|
|
299
|
+
for (const right of value) {
|
|
300
|
+
assertRight(right, label);
|
|
301
|
+
if (rights.has(right)) {
|
|
302
|
+
throw new NamespaceError("invalid_manifest", `${label} contains a duplicate right`);
|
|
303
|
+
}
|
|
304
|
+
rights.add(right);
|
|
305
|
+
}
|
|
306
|
+
return Object.freeze(NAMESPACE_RIGHTS.filter((right) => rights.has(right)));
|
|
307
|
+
}
|
|
308
|
+
function assertRight(value, label) {
|
|
309
|
+
if (typeof value !== "string" || !RIGHT_SET.has(value)) {
|
|
310
|
+
throw new NamespaceError("invalid_manifest", `${label} contains an unsupported namespace right`);
|
|
311
|
+
}
|
|
312
|
+
}
|
|
313
|
+
function assertManifest(value) {
|
|
314
|
+
if (!isRecord(value) || !admittedManifests.has(value)) {
|
|
315
|
+
throw new NamespaceError("invalid_manifest", "namespace manifest was not admitted by this runtime");
|
|
316
|
+
}
|
|
317
|
+
}
|
|
318
|
+
function assertScope(value) {
|
|
319
|
+
if (!isRecord(value) || !admittedScopes.has(value)) {
|
|
320
|
+
throw new NamespaceError("invalid_scope", "namespace scope was not admitted by this runtime");
|
|
321
|
+
}
|
|
322
|
+
}
|
|
323
|
+
function assertByteLimit(value, maximum, label, code) {
|
|
324
|
+
if (utf8ByteLength(value) > maximum) {
|
|
325
|
+
throw new NamespaceError(code, `${label} exceeds ${maximum} UTF-8 bytes`);
|
|
326
|
+
}
|
|
327
|
+
}
|
|
328
|
+
function stableHash(value) {
|
|
329
|
+
let hash = 0xcbf29ce484222325n;
|
|
330
|
+
for (const byte of HASH_UTF8.encode(value)) {
|
|
331
|
+
hash ^= BigInt(byte);
|
|
332
|
+
hash = BigInt.asUintN(64, hash * 0x100000001b3n);
|
|
333
|
+
}
|
|
334
|
+
return hash.toString(16).padStart(16, "0");
|
|
335
|
+
}
|
|
336
|
+
function isRecord(value) {
|
|
337
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
338
|
+
}
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import type { Workspace } from "../tools/types.mjs";
|
|
2
|
+
import type { ShellFetch } from "./shell.js";
|
|
3
|
+
/** Extract a GitHub source snapshot directly into the host-owned workspace. */
|
|
4
|
+
export declare function downloadRepositoryArchive(fetch: ShellFetch, workspace: Workspace, repository: string, ref: string, directory: string, signal?: AbortSignal): Promise<void>;
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
import { createGzipDecoder, createTarDecoder } from "modern-tar";
|
|
2
|
+
/** Extract a GitHub source snapshot directly into the host-owned workspace. */
|
|
3
|
+
export async function downloadRepositoryArchive(fetch, workspace, repository, ref, directory, signal) {
|
|
4
|
+
signal?.throwIfAborted();
|
|
5
|
+
const transfer = new AbortController();
|
|
6
|
+
const transferSignal = signal ? AbortSignal.any([signal, transfer.signal]) : transfer.signal;
|
|
7
|
+
const response = await (fetch.stream ?? fetch)(`https://api.github.com/repos/${repository}/tarball/${encodeURIComponent(ref)}`, { headers: { accept: "application/vnd.github+json" }, signal: transferSignal });
|
|
8
|
+
const iterator = (async function* () {
|
|
9
|
+
if (response.body instanceof Uint8Array)
|
|
10
|
+
yield response.body;
|
|
11
|
+
else
|
|
12
|
+
yield* response.body;
|
|
13
|
+
})()[Symbol.asyncIterator]();
|
|
14
|
+
if (response.status !== 200) {
|
|
15
|
+
transfer.abort();
|
|
16
|
+
await iterator.return(undefined);
|
|
17
|
+
throw new Error(`repository archive download failed (HTTP ${response.status})`);
|
|
18
|
+
}
|
|
19
|
+
const source = new ReadableStream({
|
|
20
|
+
async pull(controller) {
|
|
21
|
+
signal?.throwIfAborted();
|
|
22
|
+
const next = await iterator.next();
|
|
23
|
+
if (next.done)
|
|
24
|
+
controller.close();
|
|
25
|
+
else
|
|
26
|
+
controller.enqueue(next.value);
|
|
27
|
+
},
|
|
28
|
+
async cancel() { await iterator.return(undefined); },
|
|
29
|
+
});
|
|
30
|
+
const entries = source.pipeThrough(createGzipDecoder()).pipeThrough(createTarDecoder({ strict: true })).getReader();
|
|
31
|
+
const writes = new Set();
|
|
32
|
+
let writeFailure;
|
|
33
|
+
let archiveRoot;
|
|
34
|
+
let files = 0;
|
|
35
|
+
const paths = new Set();
|
|
36
|
+
try {
|
|
37
|
+
await workspace.mkdir(directory);
|
|
38
|
+
for (;;) {
|
|
39
|
+
signal?.throwIfAborted();
|
|
40
|
+
if (writeFailure !== undefined)
|
|
41
|
+
throw writeFailure;
|
|
42
|
+
const { done, value: entry } = await entries.read();
|
|
43
|
+
if (done)
|
|
44
|
+
break;
|
|
45
|
+
const name = entry.header.name.replace(/\/$/, "");
|
|
46
|
+
const parts = name.split("/");
|
|
47
|
+
if (parts.some((part) => !part || part === "." || part === ".." || part === ".git")
|
|
48
|
+
|| name.includes("\\") || name.includes("\0")) {
|
|
49
|
+
await entry.body.cancel();
|
|
50
|
+
throw new Error("repository archive contains an unsafe path");
|
|
51
|
+
}
|
|
52
|
+
archiveRoot ??= parts[0];
|
|
53
|
+
if (parts[0] !== archiveRoot || (parts.length === 1 && entry.header.type !== "directory")) {
|
|
54
|
+
await entry.body.cancel();
|
|
55
|
+
throw new Error("repository archive must contain one root directory");
|
|
56
|
+
}
|
|
57
|
+
if (entry.header.type !== "directory" && entry.header.type !== "file") {
|
|
58
|
+
await entry.body.cancel();
|
|
59
|
+
throw new Error(`repository archive entry type '${entry.header.type}' is unsupported`);
|
|
60
|
+
}
|
|
61
|
+
if (parts.length === 1) {
|
|
62
|
+
await entry.body.cancel();
|
|
63
|
+
continue;
|
|
64
|
+
}
|
|
65
|
+
const relative = parts.slice(1).join("/");
|
|
66
|
+
if (paths.has(relative)) {
|
|
67
|
+
await entry.body.cancel();
|
|
68
|
+
throw new Error("repository archive contains duplicate paths");
|
|
69
|
+
}
|
|
70
|
+
paths.add(relative);
|
|
71
|
+
const target = `${directory}/${relative}`;
|
|
72
|
+
if (entry.header.type === "directory") {
|
|
73
|
+
await entry.body.cancel();
|
|
74
|
+
await workspace.mkdir(target);
|
|
75
|
+
continue;
|
|
76
|
+
}
|
|
77
|
+
const contents = new Uint8Array(await new Response(entry.body).arrayBuffer());
|
|
78
|
+
signal?.throwIfAborted();
|
|
79
|
+
files += 1;
|
|
80
|
+
// Overlap durable writes without retaining the complete archive in memory.
|
|
81
|
+
const write = workspace.writeFile(target, contents)
|
|
82
|
+
.catch((error) => { writeFailure ??= error; })
|
|
83
|
+
.finally(() => { writes.delete(write); });
|
|
84
|
+
writes.add(write);
|
|
85
|
+
if (writes.size >= 8)
|
|
86
|
+
await Promise.race(writes);
|
|
87
|
+
}
|
|
88
|
+
await Promise.all(writes);
|
|
89
|
+
if (writeFailure !== undefined)
|
|
90
|
+
throw writeFailure;
|
|
91
|
+
if (!archiveRoot || files === 0)
|
|
92
|
+
throw new Error("repository archive contains no files");
|
|
93
|
+
signal?.throwIfAborted();
|
|
94
|
+
}
|
|
95
|
+
finally {
|
|
96
|
+
// Stop an upstream read before cancelling the decoder, including when
|
|
97
|
+
// validation or a durable write failed while the network was still active.
|
|
98
|
+
transfer.abort();
|
|
99
|
+
await entries.cancel().catch(() => { });
|
|
100
|
+
entries.releaseLock();
|
|
101
|
+
// The caller may clean up the destination only after every write has settled.
|
|
102
|
+
await Promise.all(writes);
|
|
103
|
+
}
|
|
104
|
+
}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import type { JustBashCustomCommand, JustBashDescriptor } from "../tools/bash.mjs";
|
|
2
|
+
import type { NamedTool, Workspace } from "../tools/types.mjs";
|
|
3
|
+
import { type ShellFetch } from "./shell.js";
|
|
4
|
+
export type ComputerCommandContext = Readonly<{
|
|
5
|
+
fetch: ShellFetch;
|
|
6
|
+
filesystem(): Workspace;
|
|
7
|
+
}>;
|
|
8
|
+
export type ComputerRuntimeOptions = Readonly<{
|
|
9
|
+
filesystem: Workspace;
|
|
10
|
+
fetch: ShellFetch;
|
|
11
|
+
refreshFilesystemBeforeExec?: boolean | undefined;
|
|
12
|
+
networkMode: string;
|
|
13
|
+
maxEntries?: number | undefined;
|
|
14
|
+
maxOutputTokens?: number | undefined;
|
|
15
|
+
commands?(context: ComputerCommandContext): readonly JustBashCustomCommand[];
|
|
16
|
+
}>;
|
|
17
|
+
export type ComputerRuntime = Readonly<{
|
|
18
|
+
commandNames: readonly string[];
|
|
19
|
+
descriptor: JustBashDescriptor;
|
|
20
|
+
fetch: ShellFetch;
|
|
21
|
+
filesystem: Workspace;
|
|
22
|
+
instructions: string;
|
|
23
|
+
tool: NamedTool;
|
|
24
|
+
}>;
|
|
25
|
+
/**
|
|
26
|
+
* Mounts persistent storage into the bounded JS shell and installs the generic
|
|
27
|
+
* Git/GitHub compatibility commands. Hosts inject all network and extra command
|
|
28
|
+
* capabilities; this package owns no credentials or provider policy.
|
|
29
|
+
*/
|
|
30
|
+
export declare function createComputerRuntime(options: ComputerRuntimeOptions): Promise<ComputerRuntime>;
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { justBash } from "../tools/bash.mjs";
|
|
2
|
+
import { createGhCommand, createGitCommand } from "./shell.js";
|
|
3
|
+
const DEFAULT_MAX_OUTPUT_TOKENS = 10_000;
|
|
4
|
+
/**
|
|
5
|
+
* Mounts persistent storage into the bounded JS shell and installs the generic
|
|
6
|
+
* Git/GitHub compatibility commands. Hosts inject all network and extra command
|
|
7
|
+
* capabilities; this package owns no credentials or provider policy.
|
|
8
|
+
*/
|
|
9
|
+
export async function createComputerRuntime(options) {
|
|
10
|
+
let mountedFilesystem;
|
|
11
|
+
const filesystem = () => {
|
|
12
|
+
if (!mountedFilesystem)
|
|
13
|
+
throw new Error("computer filesystem is not mounted");
|
|
14
|
+
return mountedFilesystem;
|
|
15
|
+
};
|
|
16
|
+
const git = createGitCommand(options.fetch, filesystem);
|
|
17
|
+
const gh = createGhCommand(options.fetch, (args, context) => git.execute(args, context));
|
|
18
|
+
const additional = options.commands?.({ fetch: options.fetch, filesystem }) ?? [];
|
|
19
|
+
const shell = await justBash({
|
|
20
|
+
filesystem: options.filesystem,
|
|
21
|
+
refreshFilesystemBeforeExec: options.refreshFilesystemBeforeExec,
|
|
22
|
+
maxEntries: options.maxEntries,
|
|
23
|
+
maxOutputTokens: options.maxOutputTokens ?? DEFAULT_MAX_OUTPUT_TOKENS,
|
|
24
|
+
fetch: options.fetch,
|
|
25
|
+
networkMode: options.networkMode,
|
|
26
|
+
customCommands: [git, gh, ...additional],
|
|
27
|
+
});
|
|
28
|
+
mountedFilesystem = shell.filesystem;
|
|
29
|
+
return Object.freeze({
|
|
30
|
+
commandNames: shell.descriptor.customCommands,
|
|
31
|
+
descriptor: shell.descriptor,
|
|
32
|
+
fetch: options.fetch,
|
|
33
|
+
filesystem: shell.filesystem,
|
|
34
|
+
instructions: shell.instructions,
|
|
35
|
+
tool: shell.tool,
|
|
36
|
+
});
|
|
37
|
+
}
|