@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,397 @@
|
|
|
1
|
+
import git from "isomorphic-git";
|
|
2
|
+
|
|
3
|
+
const DEFAULT_DIRECTORY = "repository";
|
|
4
|
+
const MARKER_NAME = "nanocodex-repository.json";
|
|
5
|
+
const MARKER_VERSION = 1;
|
|
6
|
+
const MAX_MARKER_BYTES = 16 * 1024;
|
|
7
|
+
const SHA1 = /^[a-f0-9]{40}$/;
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Materializes one immutable repository generation into a workspace child.
|
|
11
|
+
* A completed generation is never refreshed: matching reopens only validate
|
|
12
|
+
* the host marker and local HEAD, leaving retained worktree changes untouched.
|
|
13
|
+
*/
|
|
14
|
+
export async function materializeRepositoryWorkspace(options) {
|
|
15
|
+
const input = repositoryOptions(options);
|
|
16
|
+
const fs = workspaceFs(input.workspace);
|
|
17
|
+
const directory = `${input.workspace.root}/${input.directory}`;
|
|
18
|
+
const markerPath = `${directory}/.git/${MARKER_NAME}`;
|
|
19
|
+
const expectedMarker = marker(input);
|
|
20
|
+
const retained = await readMarker(input.workspace, markerPath);
|
|
21
|
+
|
|
22
|
+
if (retained !== undefined) {
|
|
23
|
+
if (!validMarker(retained)) {
|
|
24
|
+
throw new Error("repository workspace has an invalid host marker");
|
|
25
|
+
}
|
|
26
|
+
if (!sameMarker(retained, expectedMarker)) {
|
|
27
|
+
throw new Error("repository workspace is already materialized for a different generation");
|
|
28
|
+
}
|
|
29
|
+
const localHead = await resolveHead(fs, directory);
|
|
30
|
+
if (localHead !== input.head) {
|
|
31
|
+
throw new Error("repository workspace HEAD no longer matches its immutable generation");
|
|
32
|
+
}
|
|
33
|
+
return descriptor(input, directory, markerPath);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
// This named child is package-owned. Only an unmarked partial checkout at
|
|
37
|
+
// this exact, validated child path is eligible for recursive cleanup.
|
|
38
|
+
if (await workspaceEntry(input.workspace, directory)) {
|
|
39
|
+
await input.workspace.remove(directory, { recursive: true });
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
await git.clone({
|
|
43
|
+
fs,
|
|
44
|
+
http: gitHttp(input.fetch),
|
|
45
|
+
dir: directory,
|
|
46
|
+
url: input.seedUrl,
|
|
47
|
+
ref: input.branch,
|
|
48
|
+
remote: "seed",
|
|
49
|
+
singleBranch: true,
|
|
50
|
+
noTags: true,
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
const localHead = await resolveHead(fs, directory);
|
|
54
|
+
if (localHead !== input.head) {
|
|
55
|
+
throw new Error(`repository seed resolved ${localHead}, expected ${input.head}`);
|
|
56
|
+
}
|
|
57
|
+
if (input.writableRemote !== undefined) {
|
|
58
|
+
const remote = input.writableRemote;
|
|
59
|
+
await git.addRemote({ fs, dir: directory, remote: remote.name, url: remote.url, force: true });
|
|
60
|
+
await git.setConfig({
|
|
61
|
+
fs,
|
|
62
|
+
dir: directory,
|
|
63
|
+
path: `branch.${input.branch}.remote`,
|
|
64
|
+
value: remote.name,
|
|
65
|
+
});
|
|
66
|
+
await git.setConfig({
|
|
67
|
+
fs,
|
|
68
|
+
dir: directory,
|
|
69
|
+
path: `branch.${input.branch}.merge`,
|
|
70
|
+
value: `refs/heads/${remote.branch}`,
|
|
71
|
+
});
|
|
72
|
+
}
|
|
73
|
+
await input.workspace.writeFile(markerPath, `${JSON.stringify(expectedMarker)}\n`);
|
|
74
|
+
return descriptor(input, directory, markerPath);
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
function repositoryOptions(value) {
|
|
78
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) {
|
|
79
|
+
throw new TypeError("repository workspace options are required");
|
|
80
|
+
}
|
|
81
|
+
const workspace = value.workspace;
|
|
82
|
+
validateWorkspace(workspace);
|
|
83
|
+
if (typeof value.fetch !== "function") {
|
|
84
|
+
throw new TypeError("repository workspace fetch must be a function");
|
|
85
|
+
}
|
|
86
|
+
const head = sha1(value.head, "repository head");
|
|
87
|
+
const seedUrl = repositoryUrl(value.seedUrl, "repository seed URL");
|
|
88
|
+
if (!new URL(seedUrl).pathname.endsWith(`/${head}`)) {
|
|
89
|
+
throw new TypeError("repository seed URL must end in its immutable head");
|
|
90
|
+
}
|
|
91
|
+
const branch = gitRef(value.branch ?? "master", "repository branch");
|
|
92
|
+
const directory = childName(value.directory ?? DEFAULT_DIRECTORY, "repository directory");
|
|
93
|
+
const writableRemote = value.writableRemote === undefined
|
|
94
|
+
? undefined
|
|
95
|
+
: writableRemoteOptions(value.writableRemote, branch);
|
|
96
|
+
return { workspace, fetch: value.fetch, head, seedUrl, branch, directory, writableRemote };
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
function writableRemoteOptions(value, defaultBranch) {
|
|
100
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) {
|
|
101
|
+
throw new TypeError("writableRemote must be an object");
|
|
102
|
+
}
|
|
103
|
+
const name = childName(value.name ?? "origin", "writable remote name");
|
|
104
|
+
if (name === "seed") throw new TypeError("writable remote name is reserved");
|
|
105
|
+
return Object.freeze({
|
|
106
|
+
name,
|
|
107
|
+
url: repositoryUrl(value.url, "writable remote URL"),
|
|
108
|
+
branch: gitRef(value.branch ?? defaultBranch, "writable remote branch"),
|
|
109
|
+
});
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
function descriptor(input, directory, markerPath) {
|
|
113
|
+
return Object.freeze({
|
|
114
|
+
branch: input.branch,
|
|
115
|
+
directory,
|
|
116
|
+
gitDirectory: `${directory}/.git`,
|
|
117
|
+
head: input.head,
|
|
118
|
+
markerPath,
|
|
119
|
+
seedUrl: input.seedUrl,
|
|
120
|
+
writableRemote: input.writableRemote,
|
|
121
|
+
});
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
function marker(input) {
|
|
125
|
+
return {
|
|
126
|
+
version: MARKER_VERSION,
|
|
127
|
+
branch: input.branch,
|
|
128
|
+
directory: input.directory,
|
|
129
|
+
head: input.head,
|
|
130
|
+
seedUrl: input.seedUrl,
|
|
131
|
+
...(input.writableRemote === undefined ? {} : { writableRemote: input.writableRemote }),
|
|
132
|
+
};
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
async function readMarker(workspace, path) {
|
|
136
|
+
const entry = await workspaceEntry(workspace, path);
|
|
137
|
+
if (entry === undefined) return undefined;
|
|
138
|
+
if (entry.kind !== "file" || (entry.size !== undefined
|
|
139
|
+
&& (!Number.isSafeInteger(entry.size) || entry.size > MAX_MARKER_BYTES))) {
|
|
140
|
+
throw new Error("repository workspace has an invalid host marker");
|
|
141
|
+
}
|
|
142
|
+
try {
|
|
143
|
+
const contents = await workspace.readFile(path);
|
|
144
|
+
if (contents.byteLength > MAX_MARKER_BYTES) throw new Error("marker is too large");
|
|
145
|
+
return JSON.parse(new TextDecoder("utf-8", { fatal: true }).decode(contents));
|
|
146
|
+
} catch {
|
|
147
|
+
throw new Error("repository workspace has an invalid host marker");
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
function validMarker(value) {
|
|
152
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) return false;
|
|
153
|
+
const keys = Object.keys(value).sort();
|
|
154
|
+
const expectedKeys = ["branch", "directory", "head", "seedUrl", "version"];
|
|
155
|
+
if (value.writableRemote !== undefined) expectedKeys.push("writableRemote");
|
|
156
|
+
if (keys.join("\0") !== expectedKeys.sort().join("\0")) return false;
|
|
157
|
+
try {
|
|
158
|
+
sha1(value.head, "marker head");
|
|
159
|
+
childName(value.directory, "marker directory");
|
|
160
|
+
gitRef(value.branch, "marker branch");
|
|
161
|
+
const url = repositoryUrl(value.seedUrl, "marker seed URL");
|
|
162
|
+
if (!new URL(url).pathname.endsWith(`/${value.head}`)) return false;
|
|
163
|
+
if (value.writableRemote !== undefined) writableRemoteOptions(value.writableRemote, value.branch);
|
|
164
|
+
return value.version === MARKER_VERSION;
|
|
165
|
+
} catch {
|
|
166
|
+
return false;
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
function sameMarker(left, right) {
|
|
171
|
+
return JSON.stringify(left) === JSON.stringify(right);
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
async function resolveHead(fs, directory) {
|
|
175
|
+
try {
|
|
176
|
+
return await git.resolveRef({ fs, dir: directory, ref: "HEAD" });
|
|
177
|
+
} catch (error) {
|
|
178
|
+
throw new Error("repository workspace has no valid local HEAD", { cause: error });
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
function gitHttp(fetch) {
|
|
183
|
+
return {
|
|
184
|
+
async request(request) {
|
|
185
|
+
const body = request.body === undefined ? undefined : await collectBody(request.body);
|
|
186
|
+
const response = await fetch(request.url, {
|
|
187
|
+
...request.fetchOptions,
|
|
188
|
+
method: request.method ?? "GET",
|
|
189
|
+
headers: request.headers,
|
|
190
|
+
...(body === undefined ? {} : { body }),
|
|
191
|
+
...(typeof AbortSignal !== "undefined" && request.signal instanceof AbortSignal
|
|
192
|
+
? { signal: request.signal }
|
|
193
|
+
: {}),
|
|
194
|
+
});
|
|
195
|
+
if (!response || typeof response.status !== "number") {
|
|
196
|
+
throw new TypeError("repository fetch returned an invalid response");
|
|
197
|
+
}
|
|
198
|
+
return {
|
|
199
|
+
url: response.url || request.url,
|
|
200
|
+
statusCode: response.status,
|
|
201
|
+
statusMessage: response.statusText ?? "",
|
|
202
|
+
headers: responseHeaders(response.headers),
|
|
203
|
+
body: responseBody(response),
|
|
204
|
+
};
|
|
205
|
+
},
|
|
206
|
+
};
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
async function collectBody(body) {
|
|
210
|
+
const chunks = [];
|
|
211
|
+
let size = 0;
|
|
212
|
+
for await (const chunk of body) {
|
|
213
|
+
if (!(chunk instanceof Uint8Array)) throw new TypeError("git HTTP body must contain bytes");
|
|
214
|
+
size += chunk.byteLength;
|
|
215
|
+
chunks.push(chunk);
|
|
216
|
+
}
|
|
217
|
+
const joined = new Uint8Array(size);
|
|
218
|
+
let offset = 0;
|
|
219
|
+
for (const chunk of chunks) {
|
|
220
|
+
joined.set(chunk, offset);
|
|
221
|
+
offset += chunk.byteLength;
|
|
222
|
+
}
|
|
223
|
+
return joined;
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
function responseHeaders(headers) {
|
|
227
|
+
const output = Object.create(null);
|
|
228
|
+
if (headers && typeof headers.forEach === "function") {
|
|
229
|
+
headers.forEach((value, name) => { output[name] = value; });
|
|
230
|
+
} else if (headers && typeof headers === "object") {
|
|
231
|
+
for (const [name, value] of Object.entries(headers)) output[name] = String(value);
|
|
232
|
+
}
|
|
233
|
+
return output;
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
async function* responseBody(response) {
|
|
237
|
+
if (response.body instanceof Uint8Array) {
|
|
238
|
+
yield response.body;
|
|
239
|
+
return;
|
|
240
|
+
}
|
|
241
|
+
if (response.body?.[Symbol.asyncIterator]) {
|
|
242
|
+
for await (const chunk of response.body) yield chunk;
|
|
243
|
+
return;
|
|
244
|
+
}
|
|
245
|
+
if (typeof response.arrayBuffer !== "function") {
|
|
246
|
+
throw new TypeError("repository fetch response has no byte body");
|
|
247
|
+
}
|
|
248
|
+
yield new Uint8Array(await response.arrayBuffer());
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
function workspaceFs(workspace) {
|
|
252
|
+
const operation = (run) => async (...args) => {
|
|
253
|
+
try {
|
|
254
|
+
return await run(...args);
|
|
255
|
+
} catch (error) {
|
|
256
|
+
throw gitFsError(error);
|
|
257
|
+
}
|
|
258
|
+
};
|
|
259
|
+
const promises = {
|
|
260
|
+
readFile: operation(async (path, options) => {
|
|
261
|
+
const contents = await workspace.readFile(workspacePath(workspace, path));
|
|
262
|
+
const encoding = typeof options === "string" ? options : options?.encoding;
|
|
263
|
+
return encoding ? new TextDecoder(encoding).decode(contents) : contents;
|
|
264
|
+
}),
|
|
265
|
+
writeFile: operation((path, contents) => workspace.writeFile(workspacePath(workspace, path), contents)),
|
|
266
|
+
unlink: operation((path) => workspace.remove(workspacePath(workspace, path))),
|
|
267
|
+
readdir: operation(async (path) => (await workspace.list(workspacePath(workspace, path)))
|
|
268
|
+
.map((entry) => entry.path.slice(entry.path.lastIndexOf("/") + 1))),
|
|
269
|
+
mkdir: operation((path) => workspace.mkdir(workspacePath(workspace, path))),
|
|
270
|
+
rmdir: operation((path) => workspace.remove(workspacePath(workspace, path))),
|
|
271
|
+
stat: operation(async (path) => gitStat(await requiredWorkspaceEntry(workspace, workspacePath(workspace, path)))),
|
|
272
|
+
lstat: operation(async (path) => gitStat(await requiredWorkspaceEntry(workspace, workspacePath(workspace, path)))),
|
|
273
|
+
readlink: async () => { throw fsError("EINVAL", "symbolic links are unavailable"); },
|
|
274
|
+
symlink: async () => { throw fsError("ENOSYS", "symbolic links are unavailable"); },
|
|
275
|
+
chmod: operation(async (path) => { await requiredWorkspaceEntry(workspace, workspacePath(workspace, path)); }),
|
|
276
|
+
};
|
|
277
|
+
return { promises };
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
function workspacePath(workspace, value) {
|
|
281
|
+
if (typeof value !== "string" || value.includes("\0") || value.includes("\\")) {
|
|
282
|
+
throw fsError("EINVAL", "invalid repository filesystem path");
|
|
283
|
+
}
|
|
284
|
+
const source = value.startsWith("/") ? value : `${workspace.root}/${value}`;
|
|
285
|
+
const segments = [];
|
|
286
|
+
for (const segment of source.split("/")) {
|
|
287
|
+
if (!segment || segment === ".") continue;
|
|
288
|
+
if (segment === "..") segments.pop();
|
|
289
|
+
else segments.push(segment);
|
|
290
|
+
}
|
|
291
|
+
const resolved = `/${segments.join("/")}`;
|
|
292
|
+
if (resolved !== workspace.root && !resolved.startsWith(`${workspace.root}/`)) {
|
|
293
|
+
throw fsError("EPERM", `repository path escapes ${workspace.root}`);
|
|
294
|
+
}
|
|
295
|
+
return resolved;
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
async function workspaceEntry(workspace, path) {
|
|
299
|
+
if (path === workspace.root) return { kind: "directory", path };
|
|
300
|
+
const slash = path.lastIndexOf("/");
|
|
301
|
+
const parent = path.slice(0, slash) || workspace.root;
|
|
302
|
+
try {
|
|
303
|
+
return (await workspace.list(parent)).find((entry) => entry.path === path);
|
|
304
|
+
} catch (error) {
|
|
305
|
+
if (missingError(error)) return undefined;
|
|
306
|
+
throw error;
|
|
307
|
+
}
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
async function requiredWorkspaceEntry(workspace, path) {
|
|
311
|
+
const entry = await workspaceEntry(workspace, path);
|
|
312
|
+
if (entry !== undefined) return entry;
|
|
313
|
+
throw fsError("ENOENT", `${path} does not exist`);
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
function gitStat(entry) {
|
|
317
|
+
const modified = new Date(entry.modifiedAt ?? 0);
|
|
318
|
+
return {
|
|
319
|
+
isFile: () => entry.kind === "file",
|
|
320
|
+
isDirectory: () => entry.kind === "directory",
|
|
321
|
+
isSymbolicLink: () => false,
|
|
322
|
+
mode: entry.kind === "directory" ? 0o755 : 0o644,
|
|
323
|
+
size: entry.size ?? 0,
|
|
324
|
+
mtime: modified,
|
|
325
|
+
ctime: modified,
|
|
326
|
+
birthtime: modified,
|
|
327
|
+
dev: 0,
|
|
328
|
+
ino: 0,
|
|
329
|
+
uid: 0,
|
|
330
|
+
gid: 0,
|
|
331
|
+
};
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
function validateWorkspace(workspace) {
|
|
335
|
+
if (!workspace || typeof workspace !== "object" || typeof workspace.root !== "string") {
|
|
336
|
+
throw new TypeError("repository materialization requires a workspace handle");
|
|
337
|
+
}
|
|
338
|
+
if (!/^\/[^/]+(?:\/[^/]+)*$/.test(workspace.root)
|
|
339
|
+
|| workspace.root.includes("\\") || workspace.root.includes("\0")
|
|
340
|
+
|| workspace.root.split("/").some((segment) => segment === "." || segment === "..")) {
|
|
341
|
+
throw new TypeError("repository workspace root must be a bounded absolute path");
|
|
342
|
+
}
|
|
343
|
+
for (const method of ["list", "readFile", "writeFile", "remove", "mkdir"]) {
|
|
344
|
+
if (typeof workspace[method] !== "function") {
|
|
345
|
+
throw new TypeError(`repository workspace requires ${method}()`);
|
|
346
|
+
}
|
|
347
|
+
}
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
function repositoryUrl(value, name) {
|
|
351
|
+
if (typeof value !== "string" || !value) throw new TypeError(`${name} must be a URL`);
|
|
352
|
+
let url;
|
|
353
|
+
try { url = new URL(value); } catch { throw new TypeError(`${name} must be a URL`); }
|
|
354
|
+
if (!["http:", "https:"].includes(url.protocol) || url.username || url.password || url.search || url.hash) {
|
|
355
|
+
throw new TypeError(`${name} must be a credential-free HTTP URL`);
|
|
356
|
+
}
|
|
357
|
+
return url.href.replace(/\/$/, "");
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
function sha1(value, name) {
|
|
361
|
+
if (typeof value !== "string" || !SHA1.test(value)) {
|
|
362
|
+
throw new TypeError(`${name} must be a lowercase SHA-1 object ID`);
|
|
363
|
+
}
|
|
364
|
+
return value;
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
function childName(value, name) {
|
|
368
|
+
if (typeof value !== "string" || !/^[A-Za-z0-9][A-Za-z0-9._-]{0,127}$/.test(value)
|
|
369
|
+
|| value === "." || value === "..") {
|
|
370
|
+
throw new TypeError(`${name} must be one safe child name`);
|
|
371
|
+
}
|
|
372
|
+
return value;
|
|
373
|
+
}
|
|
374
|
+
|
|
375
|
+
function gitRef(value, name) {
|
|
376
|
+
if (typeof value !== "string" || !value || value.length > 255
|
|
377
|
+
|| value.startsWith(".") || value.endsWith(".") || value.endsWith("/")
|
|
378
|
+
|| value.includes("..") || value.includes("//") || value.includes("@{")
|
|
379
|
+
|| /[\x00-\x20~^:?*[\\]/.test(value)
|
|
380
|
+
|| value.split("/").some((part) => !part || part.endsWith(".lock"))) {
|
|
381
|
+
throw new TypeError(`${name} is invalid`);
|
|
382
|
+
}
|
|
383
|
+
return value;
|
|
384
|
+
}
|
|
385
|
+
|
|
386
|
+
function missingError(error) {
|
|
387
|
+
return error?.code === "ENOENT" || error?.code === "ENOTDIR" || error?.name === "NotFoundError";
|
|
388
|
+
}
|
|
389
|
+
|
|
390
|
+
function gitFsError(error) {
|
|
391
|
+
if (missingError(error) && !error?.code) error.code = "ENOENT";
|
|
392
|
+
return error;
|
|
393
|
+
}
|
|
394
|
+
|
|
395
|
+
function fsError(code, message) {
|
|
396
|
+
return Object.assign(new Error(message), { code });
|
|
397
|
+
}
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
export type SshStream = Readonly<{
|
|
2
|
+
closed(listener: (event: { error?: Error }) => void): Readonly<{ dispose(): void }>;
|
|
3
|
+
close(error?: Error): Promise<void>;
|
|
4
|
+
dispose(): void;
|
|
5
|
+
readonly isDisposed: boolean;
|
|
6
|
+
read(count: number, cancellation?: unknown): Promise<Uint8Array | null>;
|
|
7
|
+
write(data: Uint8Array, cancellation?: unknown): Promise<void>;
|
|
8
|
+
}>;
|
|
9
|
+
|
|
10
|
+
export type SshEndpoint = URL | Readonly<{ hostname: string; port: number }>;
|
|
11
|
+
export type SshCommandResult = Readonly<{ stdout: string; stderr: string; exitCode: number }>;
|
|
12
|
+
export type SshIdentityReferenceRequest = Readonly<{
|
|
13
|
+
identityReference: string;
|
|
14
|
+
endpoint: SshEndpoint;
|
|
15
|
+
username: string;
|
|
16
|
+
commandArgs: readonly string[];
|
|
17
|
+
stdin?: string;
|
|
18
|
+
}>;
|
|
19
|
+
|
|
20
|
+
export type SshCommandOptions = Readonly<{
|
|
21
|
+
transport: "tcp" | "websocket";
|
|
22
|
+
maxOutputBytes?: number;
|
|
23
|
+
openStream(endpoint: SshEndpoint, signal?: AbortSignal): Promise<SshStream>;
|
|
24
|
+
readIdentity?(
|
|
25
|
+
path: string,
|
|
26
|
+
context: Readonly<{ cwd: string; signal?: AbortSignal }>,
|
|
27
|
+
): Promise<string>;
|
|
28
|
+
resolvePassword?(reference: string): Promise<string>;
|
|
29
|
+
executeWithIdentityReference?(
|
|
30
|
+
request: SshIdentityReferenceRequest,
|
|
31
|
+
context: Readonly<{ signal?: AbortSignal }>,
|
|
32
|
+
): Promise<SshCommandResult>;
|
|
33
|
+
}>;
|
|
34
|
+
|
|
35
|
+
/** Creates a non-interactive SSH command over a host-owned byte stream. */
|
|
36
|
+
export function createSshCommand(options: SshCommandOptions): Readonly<{
|
|
37
|
+
name: "ssh";
|
|
38
|
+
trusted?: boolean;
|
|
39
|
+
execute(args: string[], context: unknown): Promise<SshCommandResult>;
|
|
40
|
+
}>;
|
|
41
|
+
|
|
42
|
+
/** Adapts a WHATWG byte-stream socket, including Cloudflare TCP sockets, to SSH. */
|
|
43
|
+
export function createWebStreamSshStream(
|
|
44
|
+
socket: Readonly<{
|
|
45
|
+
readable: ReadableStream<Uint8Array>;
|
|
46
|
+
writable: WritableStream<Uint8Array>;
|
|
47
|
+
closed?: Promise<void>;
|
|
48
|
+
close(): Promise<void>;
|
|
49
|
+
}>,
|
|
50
|
+
signal?: AbortSignal,
|
|
51
|
+
): SshStream;
|
|
52
|
+
|
|
53
|
+
/** Host-only credential generation; keep privateKey in the credential vault. */
|
|
54
|
+
export function createSshKeyPair(): Promise<Readonly<{ privateKey: string; publicKey: string }>>;
|
|
55
|
+
/** Derives an OpenSSH authorized_keys line from a PEM private key. */
|
|
56
|
+
export function sshPublicKey(privateKey: string): Promise<string>;
|