@multiplayer-app/agent 0.1.19 → 0.1.21
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 +14 -7
- package/dist/agent/definition.d.ts +13 -0
- package/dist/agent/definition.d.ts.map +1 -1
- package/dist/agent/definition.test.js +8 -3
- package/dist/agent/definition.test.js.map +1 -1
- package/dist/agent/profiles/coding.d.ts +1 -1
- package/dist/agent/profiles/coding.d.ts.map +1 -1
- package/dist/agent/profiles/coding.js +21 -4
- package/dist/agent/profiles/coding.js.map +1 -1
- package/dist/agent/profiles/index.js +1 -1
- package/dist/agent/profiles/index.js.map +1 -1
- package/dist/agent/profiles/research.d.ts.map +1 -1
- package/dist/agent/profiles/research.js +23 -1
- package/dist/agent/profiles/research.js.map +1 -1
- package/dist/agent/turn-history.d.ts +40 -0
- package/dist/agent/turn-history.d.ts.map +1 -0
- package/dist/agent/turn-history.js +167 -0
- package/dist/agent/turn-history.js.map +1 -0
- package/dist/agent/turn-runner.d.ts +22 -1
- package/dist/agent/turn-runner.d.ts.map +1 -1
- package/dist/agent/turn-runner.js +162 -79
- package/dist/agent/turn-runner.js.map +1 -1
- package/dist/app/app-host.d.ts +57 -0
- package/dist/app/app-host.d.ts.map +1 -0
- package/dist/app/app-host.js +252 -0
- package/dist/app/app-host.js.map +1 -0
- package/dist/app/app-host.test.d.ts +2 -0
- package/dist/app/app-host.test.d.ts.map +1 -0
- package/dist/app/app-host.test.js +639 -0
- package/dist/app/app-host.test.js.map +1 -0
- package/dist/app/config.d.ts +196 -0
- package/dist/app/config.d.ts.map +1 -0
- package/dist/app/config.js +362 -0
- package/dist/app/config.js.map +1 -0
- package/dist/app/config.test.d.ts +2 -0
- package/dist/app/config.test.d.ts.map +1 -0
- package/dist/app/config.test.js +224 -0
- package/dist/app/config.test.js.map +1 -0
- package/dist/app/detect.d.ts +68 -0
- package/dist/app/detect.d.ts.map +1 -0
- package/dist/app/detect.js +449 -0
- package/dist/app/detect.js.map +1 -0
- package/dist/app/detect.test.d.ts +2 -0
- package/dist/app/detect.test.d.ts.map +1 -0
- package/dist/app/detect.test.js +267 -0
- package/dist/app/detect.test.js.map +1 -0
- package/dist/app/env.d.ts +5 -0
- package/dist/app/env.d.ts.map +1 -0
- package/dist/app/env.js +22 -0
- package/dist/app/env.js.map +1 -0
- package/dist/app/functions-cli.d.ts +3 -0
- package/dist/app/functions-cli.d.ts.map +1 -0
- package/dist/app/functions-cli.js +27 -0
- package/dist/app/functions-cli.js.map +1 -0
- package/dist/app/functions-host.d.ts +15 -0
- package/dist/app/functions-host.d.ts.map +1 -0
- package/dist/app/functions-host.js +276 -0
- package/dist/app/functions-host.js.map +1 -0
- package/dist/app/functions-host.test.d.ts +2 -0
- package/dist/app/functions-host.test.d.ts.map +1 -0
- package/dist/app/functions-host.test.js +117 -0
- package/dist/app/functions-host.test.js.map +1 -0
- package/dist/app/functions-routes.d.ts +23 -0
- package/dist/app/functions-routes.d.ts.map +1 -0
- package/dist/app/functions-routes.js +85 -0
- package/dist/app/functions-routes.js.map +1 -0
- package/dist/app/functions-routes.test.d.ts +2 -0
- package/dist/app/functions-routes.test.d.ts.map +1 -0
- package/dist/app/functions-routes.test.js +40 -0
- package/dist/app/functions-routes.test.js.map +1 -0
- package/dist/app/manifest.d.ts +17 -0
- package/dist/app/manifest.d.ts.map +1 -0
- package/dist/app/manifest.js +64 -0
- package/dist/app/manifest.js.map +1 -0
- package/dist/app/manifest.test.d.ts +2 -0
- package/dist/app/manifest.test.d.ts.map +1 -0
- package/dist/app/manifest.test.js +173 -0
- package/dist/app/manifest.test.js.map +1 -0
- package/dist/app/release-artifacts.d.ts +39 -0
- package/dist/app/release-artifacts.d.ts.map +1 -0
- package/dist/app/release-artifacts.js +229 -0
- package/dist/app/release-artifacts.js.map +1 -0
- package/dist/app/release-artifacts.test.d.ts +2 -0
- package/dist/app/release-artifacts.test.d.ts.map +1 -0
- package/dist/app/release-artifacts.test.js +295 -0
- package/dist/app/release-artifacts.test.js.map +1 -0
- package/dist/app/route-pattern.d.ts +23 -0
- package/dist/app/route-pattern.d.ts.map +1 -0
- package/dist/app/route-pattern.js +339 -0
- package/dist/app/route-pattern.js.map +1 -0
- package/dist/app/route-pattern.test.d.ts +2 -0
- package/dist/app/route-pattern.test.d.ts.map +1 -0
- package/dist/app/route-pattern.test.js +187 -0
- package/dist/app/route-pattern.test.js.map +1 -0
- package/dist/app/rules.d.ts +61 -0
- package/dist/app/rules.d.ts.map +1 -0
- package/dist/app/rules.js +90 -0
- package/dist/app/rules.js.map +1 -0
- package/dist/app/rules.test.d.ts +2 -0
- package/dist/app/rules.test.d.ts.map +1 -0
- package/dist/app/rules.test.js +69 -0
- package/dist/app/rules.test.js.map +1 -0
- package/dist/app/runner.d.ts +109 -0
- package/dist/app/runner.d.ts.map +1 -0
- package/dist/app/runner.js +429 -0
- package/dist/app/runner.js.map +1 -0
- package/dist/app/site.d.ts +73 -0
- package/dist/app/site.d.ts.map +1 -0
- package/dist/app/site.js +161 -0
- package/dist/app/site.js.map +1 -0
- package/dist/app/site.test.d.ts +2 -0
- package/dist/app/site.test.d.ts.map +1 -0
- package/dist/app/site.test.js +291 -0
- package/dist/app/site.test.js.map +1 -0
- package/dist/app/static-server.d.ts +20 -0
- package/dist/app/static-server.d.ts.map +1 -0
- package/dist/app/static-server.js +145 -0
- package/dist/app/static-server.js.map +1 -0
- package/dist/context-pruning.d.ts +2 -0
- package/dist/context-pruning.d.ts.map +1 -1
- package/dist/context-pruning.js +3 -2
- package/dist/context-pruning.js.map +1 -1
- package/dist/models/usage.d.ts +6 -0
- package/dist/models/usage.d.ts.map +1 -1
- package/dist/models/usage.js +13 -0
- package/dist/models/usage.js.map +1 -1
- package/dist/persistence/conversation-store.d.ts +21 -0
- package/dist/persistence/conversation-store.d.ts.map +1 -0
- package/dist/persistence/conversation-store.js +59 -0
- package/dist/persistence/conversation-store.js.map +1 -0
- package/dist/persistence/conversation-store.test.d.ts +2 -0
- package/dist/persistence/conversation-store.test.d.ts.map +1 -0
- package/dist/persistence/conversation-store.test.js +98 -0
- package/dist/persistence/conversation-store.test.js.map +1 -0
- package/dist/persistence/git-sync.d.ts +14 -1
- package/dist/persistence/git-sync.d.ts.map +1 -1
- package/dist/persistence/git-sync.js +73 -17
- package/dist/persistence/git-sync.js.map +1 -1
- package/dist/persistence/git-sync.test.js +155 -6
- package/dist/persistence/git-sync.test.js.map +1 -1
- package/dist/persistence/snapshot-sync.d.ts +19 -2
- package/dist/persistence/snapshot-sync.d.ts.map +1 -1
- package/dist/persistence/snapshot-sync.js +194 -83
- package/dist/persistence/snapshot-sync.js.map +1 -1
- package/dist/persistence/snapshot-sync.test.js +145 -27
- package/dist/persistence/snapshot-sync.test.js.map +1 -1
- package/dist/persistence/workspace.d.ts +3 -0
- package/dist/persistence/workspace.d.ts.map +1 -1
- package/dist/persistence/workspace.js +1 -0
- package/dist/persistence/workspace.js.map +1 -1
- package/dist/research/extract-texts.d.ts.map +1 -1
- package/dist/research/extract-texts.js +34 -16
- package/dist/research/extract-texts.js.map +1 -1
- package/dist/research/filingramp/attachment-tools.d.ts.map +1 -1
- package/dist/research/filingramp/attachment-tools.js +7 -4
- package/dist/research/filingramp/attachment-tools.js.map +1 -1
- package/dist/research/filingramp/shared.d.ts.map +1 -1
- package/dist/research/filingramp/shared.js +5 -5
- package/dist/research/filingramp/shared.js.map +1 -1
- package/dist/research/prompt.d.ts.map +1 -1
- package/dist/research/prompt.js +2 -1
- package/dist/research/prompt.js.map +1 -1
- package/dist/runtime/app-mode.d.ts +10 -0
- package/dist/runtime/app-mode.d.ts.map +1 -0
- package/dist/runtime/app-mode.js +172 -0
- package/dist/runtime/app-mode.js.map +1 -0
- package/dist/runtime/app-mode.test.d.ts +2 -0
- package/dist/runtime/app-mode.test.d.ts.map +1 -0
- package/dist/runtime/app-mode.test.js +130 -0
- package/dist/runtime/app-mode.test.js.map +1 -0
- package/dist/runtime/app-rehearsal.d.ts +2 -0
- package/dist/runtime/app-rehearsal.d.ts.map +1 -0
- package/dist/runtime/app-rehearsal.js +56 -0
- package/dist/runtime/app-rehearsal.js.map +1 -0
- package/dist/runtime/build-mode.d.ts +12 -0
- package/dist/runtime/build-mode.d.ts.map +1 -0
- package/dist/runtime/build-mode.js +116 -0
- package/dist/runtime/build-mode.js.map +1 -0
- package/dist/runtime/build-mode.test.d.ts +2 -0
- package/dist/runtime/build-mode.test.d.ts.map +1 -0
- package/dist/runtime/build-mode.test.js +121 -0
- package/dist/runtime/build-mode.test.js.map +1 -0
- package/dist/runtime/cli.js +7 -2
- package/dist/runtime/cli.js.map +1 -1
- package/dist/runtime/config.d.ts +4 -0
- package/dist/runtime/config.d.ts.map +1 -1
- package/dist/runtime/config.js +13 -2
- package/dist/runtime/config.js.map +1 -1
- package/dist/runtime/guest-server.d.ts +3 -0
- package/dist/runtime/guest-server.d.ts.map +1 -0
- package/dist/runtime/{lambda-microvm.js → guest-server.js} +49 -18
- package/dist/runtime/guest-server.js.map +1 -0
- package/dist/runtime/image-hooks.d.ts +5 -0
- package/dist/runtime/image-hooks.d.ts.map +1 -0
- package/dist/runtime/image-hooks.js +68 -0
- package/dist/runtime/image-hooks.js.map +1 -0
- package/dist/runtime/image-hooks.test.d.ts +2 -0
- package/dist/runtime/image-hooks.test.d.ts.map +1 -0
- package/dist/runtime/image-hooks.test.js +74 -0
- package/dist/runtime/image-hooks.test.js.map +1 -0
- package/dist/runtime/index.d.ts +8 -3
- package/dist/runtime/index.d.ts.map +1 -1
- package/dist/runtime/index.js +53 -35
- package/dist/runtime/index.js.map +1 -1
- package/dist/runtime/listening-ports.d.ts +13 -0
- package/dist/runtime/listening-ports.d.ts.map +1 -0
- package/dist/runtime/listening-ports.js +78 -0
- package/dist/runtime/listening-ports.js.map +1 -0
- package/dist/runtime/listening-ports.test.d.ts +2 -0
- package/dist/runtime/listening-ports.test.d.ts.map +1 -0
- package/dist/runtime/listening-ports.test.js +39 -0
- package/dist/runtime/listening-ports.test.js.map +1 -0
- package/dist/runtime/preview-proxy.d.ts +56 -0
- package/dist/runtime/preview-proxy.d.ts.map +1 -0
- package/dist/runtime/preview-proxy.js +302 -0
- package/dist/runtime/preview-proxy.js.map +1 -0
- package/dist/runtime/preview-proxy.test.d.ts +2 -0
- package/dist/runtime/preview-proxy.test.d.ts.map +1 -0
- package/dist/runtime/preview-proxy.test.js +122 -0
- package/dist/runtime/preview-proxy.test.js.map +1 -0
- package/dist/runtime/session-mode.d.ts +3 -0
- package/dist/runtime/session-mode.d.ts.map +1 -0
- package/dist/runtime/session-mode.js +60 -0
- package/dist/runtime/session-mode.js.map +1 -0
- package/dist/runtime/workload.d.ts +44 -0
- package/dist/runtime/workload.d.ts.map +1 -0
- package/dist/runtime/workload.js +222 -0
- package/dist/runtime/workload.js.map +1 -0
- package/dist/runtime/workload.test.d.ts +2 -0
- package/dist/runtime/workload.test.d.ts.map +1 -0
- package/dist/runtime/workload.test.js +235 -0
- package/dist/runtime/workload.test.js.map +1 -0
- package/dist/session/conversation.d.ts +10 -2
- package/dist/session/conversation.d.ts.map +1 -1
- package/dist/session/conversation.js +40 -12
- package/dist/session/conversation.js.map +1 -1
- package/dist/session/conversation.test.js +2 -3
- package/dist/session/conversation.test.js.map +1 -1
- package/dist/session/index.d.ts +1 -1
- package/dist/session/index.d.ts.map +1 -1
- package/dist/session/index.js +2 -1
- package/dist/session/index.js.map +1 -1
- package/dist/session/runtime.d.ts +20 -3
- package/dist/session/runtime.d.ts.map +1 -1
- package/dist/session/runtime.js +97 -16
- package/dist/session/runtime.js.map +1 -1
- package/dist/session/runtime.test.js +18 -7
- package/dist/session/runtime.test.js.map +1 -1
- package/dist/session/tools.d.ts.map +1 -1
- package/dist/session/tools.js +5 -4
- package/dist/session/tools.js.map +1 -1
- package/dist/task/loop.d.ts.map +1 -1
- package/dist/task/loop.js +6 -4
- package/dist/task/loop.js.map +1 -1
- package/dist/tools/exec-tools.d.ts +12 -0
- package/dist/tools/exec-tools.d.ts.map +1 -1
- package/dist/tools/exec-tools.js +46 -3
- package/dist/tools/exec-tools.js.map +1 -1
- package/dist/tools/exec-tools.test.d.ts +2 -0
- package/dist/tools/exec-tools.test.d.ts.map +1 -0
- package/dist/tools/exec-tools.test.js +48 -0
- package/dist/tools/exec-tools.test.js.map +1 -0
- package/dist/tools/fs-tools.d.ts.map +1 -1
- package/dist/tools/fs-tools.js +5 -5
- package/dist/tools/fs-tools.js.map +1 -1
- package/dist/transport/link.d.ts.map +1 -1
- package/dist/transport/link.js +16 -5
- package/dist/transport/link.js.map +1 -1
- package/dist/transport/link.test.js +24 -0
- package/dist/transport/link.test.js.map +1 -1
- package/dist/transport/protocol.d.ts +190 -3
- package/dist/transport/protocol.d.ts.map +1 -1
- package/dist/transport/protocol.js +147 -7
- package/dist/transport/protocol.js.map +1 -1
- package/dist/transport/protocol.test.js +41 -0
- package/dist/transport/protocol.test.js.map +1 -1
- package/dist/workspace-sync/eligibility.d.ts +14 -0
- package/dist/workspace-sync/eligibility.d.ts.map +1 -0
- package/dist/workspace-sync/eligibility.js +53 -0
- package/dist/workspace-sync/eligibility.js.map +1 -0
- package/dist/workspace-sync/merge.d.ts +15 -0
- package/dist/workspace-sync/merge.d.ts.map +1 -0
- package/dist/workspace-sync/merge.js +66 -0
- package/dist/workspace-sync/merge.js.map +1 -0
- package/dist/workspace-sync/merge.test.d.ts +2 -0
- package/dist/workspace-sync/merge.test.d.ts.map +1 -0
- package/dist/workspace-sync/merge.test.js +28 -0
- package/dist/workspace-sync/merge.test.js.map +1 -0
- package/dist/workspace-sync/workspace-sync.d.ts +48 -0
- package/dist/workspace-sync/workspace-sync.d.ts.map +1 -0
- package/dist/workspace-sync/workspace-sync.js +308 -0
- package/dist/workspace-sync/workspace-sync.js.map +1 -0
- package/dist/workspace-sync/workspace-sync.test.d.ts +2 -0
- package/dist/workspace-sync/workspace-sync.test.d.ts.map +1 -0
- package/dist/workspace-sync/workspace-sync.test.js +111 -0
- package/dist/workspace-sync/workspace-sync.test.js.map +1 -0
- package/dist/workspace-sync/write-drafts.d.ts +24 -0
- package/dist/workspace-sync/write-drafts.d.ts.map +1 -0
- package/dist/workspace-sync/write-drafts.js +87 -0
- package/dist/workspace-sync/write-drafts.js.map +1 -0
- package/dist/workspace-sync/write-drafts.test.d.ts +2 -0
- package/dist/workspace-sync/write-drafts.test.d.ts.map +1 -0
- package/dist/workspace-sync/write-drafts.test.js +36 -0
- package/dist/workspace-sync/write-drafts.test.js.map +1 -0
- package/package.json +12 -2
- package/dist/runtime/lambda-microvm.d.ts +0 -3
- package/dist/runtime/lambda-microvm.d.ts.map +0 -1
- package/dist/runtime/lambda-microvm.js.map +0 -1
|
@@ -0,0 +1,222 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.WORKLOAD_HOME = exports.WORKLOAD_GID = exports.WORKLOAD_UID = exports.WORKLOAD_USER = void 0;
|
|
7
|
+
exports.workloadIdentity = workloadIdentity;
|
|
8
|
+
exports.workloadEnv = workloadEnv;
|
|
9
|
+
exports.writeWorkloadFile = writeWorkloadFile;
|
|
10
|
+
exports.appendWorkloadFile = appendWorkloadFile;
|
|
11
|
+
exports.removeWorkloadFile = removeWorkloadFile;
|
|
12
|
+
exports.makeWorkloadDir = makeWorkloadDir;
|
|
13
|
+
exports.workloadAppendStream = workloadAppendStream;
|
|
14
|
+
exports.handOverToWorkload = handOverToWorkload;
|
|
15
|
+
exports.packAsWorkload = packAsWorkload;
|
|
16
|
+
exports.unpackAsWorkload = unpackAsWorkload;
|
|
17
|
+
exports.isInsideDir = isInsideDir;
|
|
18
|
+
exports.openInside = openInside;
|
|
19
|
+
exports.readInside = readInside;
|
|
20
|
+
exports.realDirInside = realDirInside;
|
|
21
|
+
// Who runs what in a VM. The agent runtime is root and holds the VM's secrets; everything the model
|
|
22
|
+
// or an app controls (its commands, its app's processes, a build's steps) runs as an unprivileged
|
|
23
|
+
// user that can't read the runtime's environment or memory. That user owns the workspace, so the
|
|
24
|
+
// runtime must never trust what's in it: a link there can lead anywhere and root would follow it.
|
|
25
|
+
// So the runtime writes workspace files as that user, and reads them only through openInside.
|
|
26
|
+
//
|
|
27
|
+
// Not root (a test, or a dev's machine) there is no one to protect the runtime from: these are
|
|
28
|
+
// plain file calls and commands run as whoever ran the agent.
|
|
29
|
+
const node_child_process_1 = require("node:child_process");
|
|
30
|
+
const node_fs_1 = require("node:fs");
|
|
31
|
+
const node_path_1 = __importDefault(require("node:path"));
|
|
32
|
+
const promises_1 = require("node:stream/promises");
|
|
33
|
+
const node_zlib_1 = require("node:zlib");
|
|
34
|
+
exports.WORKLOAD_USER = 'agent';
|
|
35
|
+
exports.WORKLOAD_UID = 1001;
|
|
36
|
+
exports.WORKLOAD_GID = 1001;
|
|
37
|
+
exports.WORKLOAD_HOME = '/home/agent';
|
|
38
|
+
/** The identity to start a workload process with, for spawn's options. */
|
|
39
|
+
function workloadIdentity() {
|
|
40
|
+
var _a;
|
|
41
|
+
return ((_a = process.getuid) === null || _a === void 0 ? void 0 : _a.call(process)) === 0 ? { uid: exports.WORKLOAD_UID, gid: exports.WORKLOAD_GID } : undefined;
|
|
42
|
+
}
|
|
43
|
+
/** A workload's env: its own home, since root's isn't readable to it (npm's and pip's caches live there). */
|
|
44
|
+
function workloadEnv(env) {
|
|
45
|
+
return workloadIdentity() ? { ...env, HOME: exports.WORKLOAD_HOME, USER: exports.WORKLOAD_USER, LOGNAME: exports.WORKLOAD_USER } : env;
|
|
46
|
+
}
|
|
47
|
+
function runAsWorkload(script, target, input) {
|
|
48
|
+
return new Promise((resolve, reject) => {
|
|
49
|
+
const child = (0, node_child_process_1.spawn)('sh', ['-c', script, 'sh', target], {
|
|
50
|
+
...workloadIdentity(),
|
|
51
|
+
cwd: '/',
|
|
52
|
+
env: { PATH: process.env.PATH },
|
|
53
|
+
stdio: ['pipe', 'ignore', 'pipe'],
|
|
54
|
+
});
|
|
55
|
+
let stderr = '';
|
|
56
|
+
child.stderr.on('data', (chunk) => {
|
|
57
|
+
stderr += chunk.toString('utf8');
|
|
58
|
+
});
|
|
59
|
+
child.on('error', reject);
|
|
60
|
+
// It failed before reading what it was given: its exit says why, this only repeats it.
|
|
61
|
+
child.stdin.on('error', () => undefined);
|
|
62
|
+
child.on('close', (code) => {
|
|
63
|
+
if (code === 0)
|
|
64
|
+
resolve();
|
|
65
|
+
else
|
|
66
|
+
reject(new Error(`Could not change ${target}: ${stderr.trim() || `exit ${code}`}`));
|
|
67
|
+
});
|
|
68
|
+
child.stdin.end(input);
|
|
69
|
+
});
|
|
70
|
+
}
|
|
71
|
+
const WRITE = 'mkdir -p -- "$(dirname -- "$1")" && cat > "$1"';
|
|
72
|
+
const APPEND = 'mkdir -p -- "$(dirname -- "$1")" && cat >> "$1"';
|
|
73
|
+
const APPEND_STREAM = 'mkdir -p -- "$(dirname -- "$1")" && exec cat >> "$1"';
|
|
74
|
+
/** Writes a workspace file, and any folder it needs. */
|
|
75
|
+
async function writeWorkloadFile(target, content) {
|
|
76
|
+
if (workloadIdentity())
|
|
77
|
+
return runAsWorkload(WRITE, target, content);
|
|
78
|
+
await node_fs_1.promises.mkdir(node_path_1.default.dirname(target), { recursive: true });
|
|
79
|
+
await node_fs_1.promises.writeFile(target, content);
|
|
80
|
+
}
|
|
81
|
+
async function appendWorkloadFile(target, content) {
|
|
82
|
+
if (workloadIdentity())
|
|
83
|
+
return runAsWorkload(APPEND, target, content);
|
|
84
|
+
await node_fs_1.promises.mkdir(node_path_1.default.dirname(target), { recursive: true });
|
|
85
|
+
await node_fs_1.promises.appendFile(target, content);
|
|
86
|
+
}
|
|
87
|
+
async function removeWorkloadFile(target) {
|
|
88
|
+
if (workloadIdentity())
|
|
89
|
+
return runAsWorkload('rm -f -- "$1"', target);
|
|
90
|
+
await node_fs_1.promises.rm(target, { force: true });
|
|
91
|
+
}
|
|
92
|
+
async function makeWorkloadDir(dir) {
|
|
93
|
+
if (workloadIdentity())
|
|
94
|
+
return runAsWorkload('mkdir -p -- "$1"', dir);
|
|
95
|
+
await node_fs_1.promises.mkdir(dir, { recursive: true });
|
|
96
|
+
}
|
|
97
|
+
/** A file an app's output keeps being appended to, for as long as it runs. */
|
|
98
|
+
function workloadAppendStream(target) {
|
|
99
|
+
if (!workloadIdentity())
|
|
100
|
+
return (0, node_fs_1.createWriteStream)(target, { flags: 'a' });
|
|
101
|
+
const child = (0, node_child_process_1.spawn)('sh', ['-c', APPEND_STREAM, 'sh', target], {
|
|
102
|
+
...workloadIdentity(),
|
|
103
|
+
cwd: '/',
|
|
104
|
+
env: { PATH: process.env.PATH },
|
|
105
|
+
stdio: ['pipe', 'ignore', 'ignore'],
|
|
106
|
+
});
|
|
107
|
+
child.on('error', (error) => child.stdin.destroy(error));
|
|
108
|
+
return child.stdin;
|
|
109
|
+
}
|
|
110
|
+
/**
|
|
111
|
+
* Gives a tree the runtime fetched to the workload. Only before any workload runs: nothing can
|
|
112
|
+
* change it meanwhile, and -h with -R never follows a link left there by an earlier run on this disk.
|
|
113
|
+
*/
|
|
114
|
+
async function handOverToWorkload(dir) {
|
|
115
|
+
const identity = workloadIdentity();
|
|
116
|
+
if (!identity) {
|
|
117
|
+
console.warn('[workload] not running as root: commands and apps run as this user, with no separation');
|
|
118
|
+
return;
|
|
119
|
+
}
|
|
120
|
+
await node_fs_1.promises.mkdir(exports.WORKLOAD_HOME, { recursive: true });
|
|
121
|
+
await node_fs_1.promises.chown(exports.WORKLOAD_HOME, identity.uid, identity.gid);
|
|
122
|
+
await new Promise((resolve, reject) => {
|
|
123
|
+
const child = (0, node_child_process_1.spawn)('chown', ['-hR', `${identity.uid}:${identity.gid}`, '--', dir], { stdio: 'ignore' });
|
|
124
|
+
child.on('error', reject);
|
|
125
|
+
child.on('close', (code) => (code === 0 ? resolve() : reject(new Error(`Could not hand over ${dir}: exit ${code}`))));
|
|
126
|
+
});
|
|
127
|
+
}
|
|
128
|
+
/**
|
|
129
|
+
* Runs tar as the workload, feeding it through `flow`. What's in a folder a build wrote, or an
|
|
130
|
+
* archive of one, is what the app's own code left there: root would follow its links and write where
|
|
131
|
+
* its paths lead, the workload can only reach what it could anyway.
|
|
132
|
+
*/
|
|
133
|
+
async function tarAsWorkload(args, cwd, flow) {
|
|
134
|
+
const child = (0, node_child_process_1.spawn)('tar', args, { ...workloadIdentity(), cwd, env: { PATH: process.env.PATH }, stdio: ['pipe', 'pipe', 'pipe'] });
|
|
135
|
+
let stderr = '';
|
|
136
|
+
child.stderr.on('data', (chunk) => {
|
|
137
|
+
stderr = (stderr + chunk.toString('utf8')).slice(-2000);
|
|
138
|
+
});
|
|
139
|
+
const exited = new Promise((resolve, reject) => {
|
|
140
|
+
child.on('error', reject);
|
|
141
|
+
child.on('close', (code) => (code === 0 ? resolve() : reject(new Error(`tar failed: ${stderr.trim() || `exit ${code}`}`))));
|
|
142
|
+
});
|
|
143
|
+
const [fed, ended] = await Promise.allSettled([flow(child), exited]);
|
|
144
|
+
// When tar gives up, its pipe breaks too: only its own exit says why.
|
|
145
|
+
if (fed.status === 'rejected' && fed.reason.code !== 'EPIPE')
|
|
146
|
+
throw fed.reason;
|
|
147
|
+
if (ended.status === 'rejected')
|
|
148
|
+
throw ended.reason;
|
|
149
|
+
}
|
|
150
|
+
/** Writes `folders` of `dir` to `target` as a compressed archive, leaving out what `exclude` names at any depth. */
|
|
151
|
+
function packAsWorkload(dir, folders, exclude, target) {
|
|
152
|
+
const args = ['-c', '-f', '-', ...exclude.map((name) => `--exclude=${name}`), ...folders.map((folder) => (folder === '.' ? '.' : `./${folder}`))];
|
|
153
|
+
return tarAsWorkload(args, dir, async ({ stdin, stdout }) => {
|
|
154
|
+
stdin.end();
|
|
155
|
+
await (0, promises_1.pipeline)(stdout, (0, node_zlib_1.createZstdCompress)({ params: { [node_zlib_1.constants.ZSTD_c_checksumFlag]: 1 } }), target);
|
|
156
|
+
});
|
|
157
|
+
}
|
|
158
|
+
/** Unpacks what packAsWorkload wrote into `dir`, which the workload already owns. */
|
|
159
|
+
function unpackAsWorkload(dir, source) {
|
|
160
|
+
return tarAsWorkload(['-x', '-f', '-'], dir, async ({ stdin, stdout }) => {
|
|
161
|
+
stdout.resume();
|
|
162
|
+
await (0, promises_1.pipeline)(source, (0, node_zlib_1.createZstdDecompress)(), stdin);
|
|
163
|
+
});
|
|
164
|
+
}
|
|
165
|
+
function notFound(file) {
|
|
166
|
+
return Object.assign(new Error(`ENOENT: no such file, open '${file}'`), { code: 'ENOENT' });
|
|
167
|
+
}
|
|
168
|
+
/**
|
|
169
|
+
* Whether `target` is `root` or somewhere under it, by path alone: a neighbour that only shares its
|
|
170
|
+
* prefix isn't. Both absolute and normalized, and real paths where a link could lead out.
|
|
171
|
+
*/
|
|
172
|
+
function isInsideDir(root, target) {
|
|
173
|
+
return target === root || target.startsWith(`${root}${node_path_1.default.sep}`);
|
|
174
|
+
}
|
|
175
|
+
/**
|
|
176
|
+
* Opens a file to read, for the runtime: only if what actually opened is a regular file under
|
|
177
|
+
* `root`. Checking first and opening after would leave a gap to swap a link in, so it asks the
|
|
178
|
+
* kernel what this open reached. Anything else reads as missing, which callers already handle.
|
|
179
|
+
*/
|
|
180
|
+
async function openInside(root, file, flags = 0) {
|
|
181
|
+
if (!workloadIdentity())
|
|
182
|
+
return node_fs_1.promises.open(file, node_fs_1.constants.O_RDONLY | flags);
|
|
183
|
+
// Non-blocking: a FIFO left there must not hang the runtime on an open with no writer.
|
|
184
|
+
const handle = await node_fs_1.promises.open(file, node_fs_1.constants.O_RDONLY | node_fs_1.constants.O_NONBLOCK | flags);
|
|
185
|
+
try {
|
|
186
|
+
const [realRoot, opened, stat] = await Promise.all([
|
|
187
|
+
node_fs_1.promises.realpath(root),
|
|
188
|
+
node_fs_1.promises.readlink(`/proc/self/fd/${handle.fd}`),
|
|
189
|
+
handle.stat(),
|
|
190
|
+
]);
|
|
191
|
+
const inside = isInsideDir(realRoot, opened);
|
|
192
|
+
if (inside && stat.isDirectory()) {
|
|
193
|
+
throw Object.assign(new Error(`EISDIR: illegal operation on a directory, read '${file}'`), { code: 'EISDIR' });
|
|
194
|
+
}
|
|
195
|
+
if (inside && stat.isFile())
|
|
196
|
+
return handle;
|
|
197
|
+
throw notFound(file);
|
|
198
|
+
}
|
|
199
|
+
catch (error) {
|
|
200
|
+
await handle.close();
|
|
201
|
+
throw error;
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
async function readInside(root, file, flags = 0) {
|
|
205
|
+
const handle = await openInside(root, file, flags);
|
|
206
|
+
try {
|
|
207
|
+
return await handle.readFile();
|
|
208
|
+
}
|
|
209
|
+
finally {
|
|
210
|
+
await handle.close();
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
/** A directory's real path, if it's really under `root` (listing one follows links too). */
|
|
214
|
+
async function realDirInside(root, dir) {
|
|
215
|
+
if (!workloadIdentity())
|
|
216
|
+
return dir;
|
|
217
|
+
const [realRoot, real] = await Promise.all([node_fs_1.promises.realpath(root), node_fs_1.promises.realpath(dir)]);
|
|
218
|
+
if (!isInsideDir(realRoot, real))
|
|
219
|
+
throw notFound(dir);
|
|
220
|
+
return real;
|
|
221
|
+
}
|
|
222
|
+
//# sourceMappingURL=workload.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"workload.js","sourceRoot":"","sources":["../../src/runtime/workload.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"workload.test.d.ts","sourceRoot":"","sources":["../../src/runtime/workload.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,235 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
var _a;
|
|
6
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
7
|
+
const strict_1 = __importDefault(require("node:assert/strict"));
|
|
8
|
+
const node_child_process_1 = require("node:child_process");
|
|
9
|
+
const node_fs_1 = require("node:fs");
|
|
10
|
+
const node_os_1 = __importDefault(require("node:os"));
|
|
11
|
+
const node_path_1 = __importDefault(require("node:path"));
|
|
12
|
+
const node_stream_1 = require("node:stream");
|
|
13
|
+
const node_util_1 = require("node:util");
|
|
14
|
+
const node_zlib_1 = require("node:zlib");
|
|
15
|
+
const node_test_1 = require("node:test");
|
|
16
|
+
const workload_1 = require("./workload");
|
|
17
|
+
const exec = (0, node_util_1.promisify)(node_child_process_1.execFile);
|
|
18
|
+
const isRoot = ((_a = process.getuid) === null || _a === void 0 ? void 0 : _a.call(process)) === 0;
|
|
19
|
+
/** A tar entry as a hostile build could write one, which no tar on purpose would. */
|
|
20
|
+
function tarEntry(name, options = {}) {
|
|
21
|
+
var _a, _b, _c, _d;
|
|
22
|
+
const content = Buffer.from((_a = options.content) !== null && _a !== void 0 ? _a : '');
|
|
23
|
+
const header = Buffer.alloc(512);
|
|
24
|
+
const put = (value, at, length) => header.write(value.slice(0, length), at, 'latin1');
|
|
25
|
+
const octal = (value, length) => value.toString(8).padStart(length - 1, '0');
|
|
26
|
+
put(name, 0, 100);
|
|
27
|
+
put(octal((_b = options.mode) !== null && _b !== void 0 ? _b : 0o644, 8), 100, 8);
|
|
28
|
+
put(octal(0, 8), 108, 8);
|
|
29
|
+
put(octal(0, 8), 116, 8);
|
|
30
|
+
put(octal(content.length, 12), 124, 12);
|
|
31
|
+
put(octal(0, 12), 136, 12);
|
|
32
|
+
header.fill(' ', 148, 156);
|
|
33
|
+
put((_c = options.type) !== null && _c !== void 0 ? _c : '0', 156, 1);
|
|
34
|
+
put((_d = options.link) !== null && _d !== void 0 ? _d : '', 157, 100);
|
|
35
|
+
put('ustar ', 257, 8);
|
|
36
|
+
put(`${octal(header.reduce((sum, byte) => sum + byte, 0), 7)}\0 `, 148, 8);
|
|
37
|
+
return Buffer.concat([header, content, Buffer.alloc((512 - (content.length % 512)) % 512)]);
|
|
38
|
+
}
|
|
39
|
+
const hostileArchive = (...entries) => node_stream_1.Readable.from([(0, node_zlib_1.zstdCompressSync)(Buffer.concat([...entries, Buffer.alloc(1024)]))]);
|
|
40
|
+
(0, node_test_1.describe)('a path inside a directory', () => {
|
|
41
|
+
(0, node_test_1.it)('is the directory or under it, and not a neighbour that shares its prefix', () => {
|
|
42
|
+
strict_1.default.equal((0, workload_1.isInsideDir)('/workspace/app', '/workspace/app'), true);
|
|
43
|
+
strict_1.default.equal((0, workload_1.isInsideDir)('/workspace/app', '/workspace/app/src/a.js'), true);
|
|
44
|
+
strict_1.default.equal((0, workload_1.isInsideDir)('/workspace/app', '/workspace/app-secrets/a.js'), false);
|
|
45
|
+
strict_1.default.equal((0, workload_1.isInsideDir)('/workspace/app', '/workspace'), false);
|
|
46
|
+
});
|
|
47
|
+
});
|
|
48
|
+
(0, node_test_1.describe)('workspace files', () => {
|
|
49
|
+
let root;
|
|
50
|
+
let workspace;
|
|
51
|
+
(0, node_test_1.beforeEach)(async () => {
|
|
52
|
+
root = await node_fs_1.promises.mkdtemp(node_path_1.default.join(node_os_1.default.tmpdir(), 'workload-'));
|
|
53
|
+
await node_fs_1.promises.chmod(root, 0o755);
|
|
54
|
+
workspace = node_path_1.default.join(root, 'workspace');
|
|
55
|
+
await node_fs_1.promises.mkdir(workspace);
|
|
56
|
+
await (0, workload_1.handOverToWorkload)(workspace);
|
|
57
|
+
});
|
|
58
|
+
(0, node_test_1.afterEach)(() => node_fs_1.promises.rm(root, { recursive: true, force: true }));
|
|
59
|
+
(0, node_test_1.it)('writes, appends and removes, making the folders a file needs', async () => {
|
|
60
|
+
const file = node_path_1.default.join(workspace, 'deep/er/notes.txt');
|
|
61
|
+
await (0, workload_1.writeWorkloadFile)(file, 'one\n');
|
|
62
|
+
await (0, workload_1.appendWorkloadFile)(file, 'two\n');
|
|
63
|
+
strict_1.default.equal(await node_fs_1.promises.readFile(file, 'utf8'), 'one\ntwo\n');
|
|
64
|
+
await (0, workload_1.writeWorkloadFile)(file, Buffer.from([0, 1, 2, 255]));
|
|
65
|
+
strict_1.default.deepEqual([...(await node_fs_1.promises.readFile(file))], [0, 1, 2, 255]);
|
|
66
|
+
await (0, workload_1.removeWorkloadFile)(file);
|
|
67
|
+
await strict_1.default.rejects(node_fs_1.promises.access(file));
|
|
68
|
+
await (0, workload_1.removeWorkloadFile)(file);
|
|
69
|
+
await (0, workload_1.makeWorkloadDir)(node_path_1.default.join(workspace, 'made/here'));
|
|
70
|
+
strict_1.default.ok((await node_fs_1.promises.stat(node_path_1.default.join(workspace, 'made/here'))).isDirectory());
|
|
71
|
+
});
|
|
72
|
+
(0, node_test_1.it)('handles names a shell would trip on', async () => {
|
|
73
|
+
const file = node_path_1.default.join(workspace, 'a b/$(touch pwned)"; rm -rf x\'.txt');
|
|
74
|
+
await (0, workload_1.writeWorkloadFile)(file, 'kept');
|
|
75
|
+
strict_1.default.equal(await node_fs_1.promises.readFile(file, 'utf8'), 'kept');
|
|
76
|
+
await strict_1.default.rejects(node_fs_1.promises.access(node_path_1.default.join(workspace, 'pwned')));
|
|
77
|
+
await strict_1.default.rejects(node_fs_1.promises.access('/pwned'));
|
|
78
|
+
});
|
|
79
|
+
(0, node_test_1.it)('keeps appending to a log stream until it ends', async () => {
|
|
80
|
+
const file = node_path_1.default.join(workspace, '.builder/logs/app.log');
|
|
81
|
+
await (0, workload_1.makeWorkloadDir)(node_path_1.default.dirname(file));
|
|
82
|
+
const stream = (0, workload_1.workloadAppendStream)(file);
|
|
83
|
+
await new Promise((resolve, reject) => {
|
|
84
|
+
stream.on('error', reject);
|
|
85
|
+
stream.write('line one\n');
|
|
86
|
+
stream.write(Buffer.from('line two\n'));
|
|
87
|
+
stream.end(resolve);
|
|
88
|
+
});
|
|
89
|
+
for (let tries = 0; tries < 50 && !(await node_fs_1.promises.readFile(file, 'utf8').catch(() => '')).includes('two'); tries += 1) {
|
|
90
|
+
await new Promise((resolve) => setTimeout(resolve, 20));
|
|
91
|
+
}
|
|
92
|
+
strict_1.default.equal(await node_fs_1.promises.readFile(file, 'utf8'), 'line one\nline two\n');
|
|
93
|
+
});
|
|
94
|
+
(0, node_test_1.it)('reads a file inside, and says a missing one is missing', async () => {
|
|
95
|
+
await (0, workload_1.writeWorkloadFile)(node_path_1.default.join(workspace, 'a.txt'), 'hello');
|
|
96
|
+
strict_1.default.equal((await (0, workload_1.readInside)(workspace, node_path_1.default.join(workspace, 'a.txt'))).toString(), 'hello');
|
|
97
|
+
await strict_1.default.rejects((0, workload_1.readInside)(workspace, node_path_1.default.join(workspace, 'none.txt')), { code: 'ENOENT' });
|
|
98
|
+
strict_1.default.equal(await (0, workload_1.realDirInside)(workspace, workspace).then(() => true), true);
|
|
99
|
+
});
|
|
100
|
+
(0, node_test_1.it)('only changes the env when there is someone to run as', () => {
|
|
101
|
+
var _a;
|
|
102
|
+
const env = { PATH: '/bin', HOME: '/root' };
|
|
103
|
+
strict_1.default.deepEqual((0, workload_1.workloadEnv)(env), isRoot ? { ...env, HOME: workload_1.WORKLOAD_HOME, USER: 'agent', LOGNAME: 'agent' } : env);
|
|
104
|
+
strict_1.default.equal((_a = (0, workload_1.workloadIdentity)()) === null || _a === void 0 ? void 0 : _a.uid, isRoot ? workload_1.WORKLOAD_UID : undefined);
|
|
105
|
+
});
|
|
106
|
+
});
|
|
107
|
+
// What the separation is for. They need root and Linux's /proc: run in the VM image as root, see
|
|
108
|
+
// docs/architecture/deployments.md. Skipped anywhere else, where there's no privilege to protect.
|
|
109
|
+
(0, node_test_1.describe)('a workspace a workload controls, read by a root runtime', { skip: !isRoot }, () => {
|
|
110
|
+
let root;
|
|
111
|
+
let workspace;
|
|
112
|
+
let secret;
|
|
113
|
+
const as = (command) => exec('sh', ['-c', command], { cwd: workspace, uid: workload_1.WORKLOAD_UID, gid: workload_1.WORKLOAD_UID, env: { PATH: process.env.PATH } });
|
|
114
|
+
(0, node_test_1.beforeEach)(async () => {
|
|
115
|
+
root = await node_fs_1.promises.mkdtemp(node_path_1.default.join(node_os_1.default.tmpdir(), 'workload-'));
|
|
116
|
+
await node_fs_1.promises.chmod(root, 0o755);
|
|
117
|
+
workspace = node_path_1.default.join(root, 'workspace');
|
|
118
|
+
await node_fs_1.promises.mkdir(workspace);
|
|
119
|
+
secret = node_path_1.default.join(root, 'root-only.txt');
|
|
120
|
+
await node_fs_1.promises.writeFile(secret, 'SECRET', { mode: 0o600 });
|
|
121
|
+
await (0, workload_1.handOverToWorkload)(workspace);
|
|
122
|
+
});
|
|
123
|
+
(0, node_test_1.afterEach)(() => node_fs_1.promises.rm(root, { recursive: true, force: true }));
|
|
124
|
+
(0, node_test_1.it)('gives the workspace to the workload, without following a link out of it', async () => {
|
|
125
|
+
await node_fs_1.promises.symlink(secret, node_path_1.default.join(workspace, 'link'));
|
|
126
|
+
await (0, workload_1.handOverToWorkload)(workspace);
|
|
127
|
+
strict_1.default.equal((await node_fs_1.promises.stat(secret)).uid, 0, 'the link itself changed owner, not what it leads to');
|
|
128
|
+
strict_1.default.equal((await node_fs_1.promises.lstat(node_path_1.default.join(workspace, 'link'))).uid, workload_1.WORKLOAD_UID);
|
|
129
|
+
});
|
|
130
|
+
(0, node_test_1.it)('stops the workload from reading what root can', async () => {
|
|
131
|
+
await strict_1.default.rejects(as(`cat ${secret}`));
|
|
132
|
+
await strict_1.default.rejects(as('cat /proc/1/environ'));
|
|
133
|
+
});
|
|
134
|
+
(0, node_test_1.it)('never reads through a link leading out, to a file or to the runtime itself', async () => {
|
|
135
|
+
await as(`ln -s ${secret} leak.txt && ln -s /proc/self/environ env.txt && ln -s /proc/1/environ one.txt`);
|
|
136
|
+
for (const name of ['leak.txt', 'env.txt', 'one.txt']) {
|
|
137
|
+
await strict_1.default.rejects((0, workload_1.readInside)(workspace, node_path_1.default.join(workspace, name)), { code: 'ENOENT' }, name);
|
|
138
|
+
}
|
|
139
|
+
});
|
|
140
|
+
(0, node_test_1.it)('never reads through a folder swapped for a link', async () => {
|
|
141
|
+
await as(`ln -s ${root} dir`);
|
|
142
|
+
await strict_1.default.rejects((0, workload_1.readInside)(workspace, node_path_1.default.join(workspace, 'dir/root-only.txt')), { code: 'ENOENT' });
|
|
143
|
+
await strict_1.default.rejects((0, workload_1.realDirInside)(workspace, node_path_1.default.join(workspace, 'dir')), { code: 'ENOENT' });
|
|
144
|
+
});
|
|
145
|
+
(0, node_test_1.it)('still reads a link that stays inside', async () => {
|
|
146
|
+
await as('echo -n inside > real.txt && ln -s real.txt alias.txt');
|
|
147
|
+
strict_1.default.equal((await (0, workload_1.readInside)(workspace, node_path_1.default.join(workspace, 'alias.txt'))).toString(), 'inside');
|
|
148
|
+
});
|
|
149
|
+
(0, node_test_1.it)('does not hang on a FIFO, or read a directory as a file', async () => {
|
|
150
|
+
await as('mkfifo pipe && mkdir folder');
|
|
151
|
+
await strict_1.default.rejects((0, workload_1.readInside)(workspace, node_path_1.default.join(workspace, 'pipe')), { code: 'ENOENT' });
|
|
152
|
+
await strict_1.default.rejects((0, workload_1.readInside)(workspace, node_path_1.default.join(workspace, 'folder')), { code: 'EISDIR' });
|
|
153
|
+
});
|
|
154
|
+
(0, node_test_1.it)('never writes, appends or removes through a link as root', async () => {
|
|
155
|
+
await as(`ln -s ${secret} target.txt && ln -s ${root} parent`);
|
|
156
|
+
await strict_1.default.rejects((0, workload_1.writeWorkloadFile)(node_path_1.default.join(workspace, 'target.txt'), 'OVERWRITTEN'));
|
|
157
|
+
await strict_1.default.rejects((0, workload_1.appendWorkloadFile)(node_path_1.default.join(workspace, 'target.txt'), 'OVERWRITTEN'));
|
|
158
|
+
await strict_1.default.rejects((0, workload_1.writeWorkloadFile)(node_path_1.default.join(workspace, 'parent/planted.txt'), 'x'));
|
|
159
|
+
await (0, workload_1.removeWorkloadFile)(node_path_1.default.join(workspace, 'parent/root-only.txt')).catch(() => undefined);
|
|
160
|
+
strict_1.default.equal(await node_fs_1.promises.readFile(secret, 'utf8'), 'SECRET');
|
|
161
|
+
await strict_1.default.rejects(node_fs_1.promises.access(node_path_1.default.join(root, 'planted.txt')));
|
|
162
|
+
});
|
|
163
|
+
(0, node_test_1.describe)('an archive of what a build left', () => {
|
|
164
|
+
const packed = async (folders = ['.']) => {
|
|
165
|
+
const chunks = [];
|
|
166
|
+
const sink = new node_stream_1.PassThrough();
|
|
167
|
+
sink.on('data', (chunk) => chunks.push(chunk));
|
|
168
|
+
await (0, workload_1.packAsWorkload)(workspace, folders, [], sink);
|
|
169
|
+
return Buffer.concat(chunks);
|
|
170
|
+
};
|
|
171
|
+
(0, node_test_1.it)('holds a link leading out as a link, and never what it leads to', async () => {
|
|
172
|
+
await as(`echo -n mine > own.txt && ln -s ${secret} leak.txt`);
|
|
173
|
+
const tar = (0, node_zlib_1.zstdDecompressSync)(await packed()).toString('latin1');
|
|
174
|
+
strict_1.default.ok(tar.includes('mine'));
|
|
175
|
+
strict_1.default.ok(tar.includes(secret), 'the link, by where it leads');
|
|
176
|
+
strict_1.default.ok(!tar.includes('SECRET'));
|
|
177
|
+
});
|
|
178
|
+
(0, node_test_1.it)('can\'t pack what the workload couldn\'t read', async () => {
|
|
179
|
+
await node_fs_1.promises.copyFile(secret, node_path_1.default.join(workspace, 'root-only.txt'));
|
|
180
|
+
await node_fs_1.promises.chmod(node_path_1.default.join(workspace, 'root-only.txt'), 0o600);
|
|
181
|
+
await strict_1.default.rejects(packed(), /tar failed/);
|
|
182
|
+
});
|
|
183
|
+
(0, node_test_1.it)('unpacks as the workload, whatever it holds', async () => {
|
|
184
|
+
await as('echo -n mine > own.txt');
|
|
185
|
+
const archive = await packed();
|
|
186
|
+
await as('rm own.txt');
|
|
187
|
+
await (0, workload_1.unpackAsWorkload)(workspace, node_stream_1.Readable.from([archive]));
|
|
188
|
+
strict_1.default.equal((await node_fs_1.promises.stat(node_path_1.default.join(workspace, 'own.txt'))).uid, workload_1.WORKLOAD_UID);
|
|
189
|
+
});
|
|
190
|
+
(0, node_test_1.it)('never writes outside through a link it brought', async () => {
|
|
191
|
+
const archive = hostileArchive(tarEntry('out', { type: '2', link: root }), tarEntry('out/planted.txt', { content: 'x' }));
|
|
192
|
+
await (0, workload_1.unpackAsWorkload)(workspace, archive).catch(() => undefined);
|
|
193
|
+
await strict_1.default.rejects(node_fs_1.promises.access(node_path_1.default.join(root, 'planted.txt')));
|
|
194
|
+
});
|
|
195
|
+
(0, node_test_1.it)('never writes where an absolute path or a parent leads', async () => {
|
|
196
|
+
await (0, workload_1.unpackAsWorkload)(workspace, hostileArchive(tarEntry(node_path_1.default.join(root, 'absolute.txt'), { content: 'x' }))).catch(() => undefined);
|
|
197
|
+
await strict_1.default.rejects(node_fs_1.promises.access(node_path_1.default.join(root, 'absolute.txt')));
|
|
198
|
+
await (0, workload_1.unpackAsWorkload)(workspace, hostileArchive(tarEntry('../parent.txt', { content: 'x' }))).catch(() => undefined);
|
|
199
|
+
await strict_1.default.rejects(node_fs_1.promises.access(node_path_1.default.join(root, 'parent.txt')));
|
|
200
|
+
});
|
|
201
|
+
(0, node_test_1.it)('never overwrites a file of root\'s through a link, or takes root\'s through a hard link', async () => {
|
|
202
|
+
await (0, workload_1.unpackAsWorkload)(workspace, hostileArchive(tarEntry('over', { type: '2', link: secret }), tarEntry('over', { content: 'OVERWRITTEN' }))).catch(() => undefined);
|
|
203
|
+
strict_1.default.equal(await node_fs_1.promises.readFile(secret, 'utf8'), 'SECRET');
|
|
204
|
+
strict_1.default.equal((await node_fs_1.promises.stat(secret)).uid, 0);
|
|
205
|
+
await (0, workload_1.unpackAsWorkload)(workspace, hostileArchive(tarEntry('hard', { type: '1', link: secret }))).catch(() => undefined);
|
|
206
|
+
strict_1.default.equal((await node_fs_1.promises.stat(secret)).uid, 0);
|
|
207
|
+
await strict_1.default.rejects(as('cat hard'));
|
|
208
|
+
});
|
|
209
|
+
(0, node_test_1.it)('gives root nothing to run: no file it unpacks belongs to root, setuid or not', async () => {
|
|
210
|
+
await (0, workload_1.unpackAsWorkload)(workspace, hostileArchive(tarEntry('suid', { mode: 0o4755, content: '#!/bin/sh\n' })));
|
|
211
|
+
const stats = await node_fs_1.promises.stat(node_path_1.default.join(workspace, 'suid'));
|
|
212
|
+
strict_1.default.equal(stats.uid, workload_1.WORKLOAD_UID);
|
|
213
|
+
strict_1.default.notEqual(stats.mode & 0o100, 0);
|
|
214
|
+
});
|
|
215
|
+
(0, node_test_1.it)('stops at an archive that isn\'t one', async () => {
|
|
216
|
+
await strict_1.default.rejects((0, workload_1.unpackAsWorkload)(workspace, node_stream_1.Readable.from([Buffer.from('not an archive')])));
|
|
217
|
+
});
|
|
218
|
+
(0, node_test_1.it)('never leaves what it was given in the environment of tar', async () => {
|
|
219
|
+
process.env.TEST_SECRET_ENV = 'SECRET_IN_ENV';
|
|
220
|
+
try {
|
|
221
|
+
await (0, workload_1.unpackAsWorkload)(workspace, hostileArchive(tarEntry('a.txt', { content: 'x' })));
|
|
222
|
+
strict_1.default.equal((await node_fs_1.promises.readFile(node_path_1.default.join(workspace, 'a.txt'), 'utf8')), 'x');
|
|
223
|
+
}
|
|
224
|
+
finally {
|
|
225
|
+
delete process.env.TEST_SECRET_ENV;
|
|
226
|
+
}
|
|
227
|
+
});
|
|
228
|
+
});
|
|
229
|
+
(0, node_test_1.it)('writes what the workload can then change', async () => {
|
|
230
|
+
await (0, workload_1.writeWorkloadFile)(node_path_1.default.join(workspace, 'new/file.txt'), 'x');
|
|
231
|
+
strict_1.default.equal((await node_fs_1.promises.stat(node_path_1.default.join(workspace, 'new/file.txt'))).uid, workload_1.WORKLOAD_UID);
|
|
232
|
+
await as('echo more >> new/file.txt && rm new/file.txt && rmdir new');
|
|
233
|
+
});
|
|
234
|
+
});
|
|
235
|
+
//# sourceMappingURL=workload.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"workload.test.js","sourceRoot":"","sources":["../../src/runtime/workload.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -8,8 +8,16 @@ export interface PersistedConversation {
|
|
|
8
8
|
}
|
|
9
9
|
/** Drops the oldest turns (user message + everything up to the next user message) past the cap, keeping the first one. */
|
|
10
10
|
export declare function limitConversation(messages: ModelMessage[], charBudget?: number): ModelMessage[];
|
|
11
|
+
/**
|
|
12
|
+
* Every earlier turn as one assistant message (its final text plus what it did), then the character
|
|
13
|
+
* cap. Tool calls and results of finished turns never re-enter the prompt: the workspace holds what they
|
|
14
|
+
* produced, and the account names the files. An interrupted turn (a lost VM mid-work) collapses the same
|
|
15
|
+
* way, which is how a restored session knows what was already done.
|
|
16
|
+
*/
|
|
17
|
+
export declare function prepareHistory(messages: ModelMessage[], charBudget?: number): ModelMessage[];
|
|
18
|
+
export declare function conversationFile(stateDir: string): string;
|
|
11
19
|
/** Missing conversation means a new session; corrupt state must fail startup. */
|
|
12
|
-
export declare function readConversation(
|
|
20
|
+
export declare function readConversation(stateDir: string): Promise<PersistedConversation | undefined>;
|
|
13
21
|
/** Replace atomically so recovery never reads half of a conversation. The caller serializes writes. */
|
|
14
|
-
export declare function writeConversation(
|
|
22
|
+
export declare function writeConversation(stateDir: string, getConversation: () => PersistedConversation): Promise<void>;
|
|
15
23
|
//# sourceMappingURL=conversation.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"conversation.d.ts","sourceRoot":"","sources":["../../src/session/conversation.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,IAAI,CAAA;
|
|
1
|
+
{"version":3,"file":"conversation.d.ts","sourceRoot":"","sources":["../../src/session/conversation.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,IAAI,CAAA;AAOtC,MAAM,WAAW,qBAAqB;IACpC,OAAO,EAAE,CAAC,CAAA;IACV,QAAQ,EAAE,YAAY,EAAE,CAAA;IACxB,UAAU,EAAE,MAAM,CAAA;IAClB,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,YAAY,CAAC,EAAE,OAAO,CAAA;CACvB;AAED,0HAA0H;AAC1H,wBAAgB,iBAAiB,CAAC,QAAQ,EAAE,YAAY,EAAE,EAAE,UAAU,SAAsB,GAAG,YAAY,EAAE,CAa5G;AAED;;;;;GAKG;AACH,wBAAgB,cAAc,CAAC,QAAQ,EAAE,YAAY,EAAE,EAAE,UAAU,SAAsB,GAAG,YAAY,EAAE,CAgBzG;AAED,wBAAgB,gBAAgB,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CAEzD;AAED,iFAAiF;AACjF,wBAAsB,gBAAgB,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,qBAAqB,GAAG,SAAS,CAAC,CAanG;AAED,uGAAuG;AACvG,wBAAsB,iBAAiB,CAAC,QAAQ,EAAE,MAAM,EAAE,eAAe,EAAE,MAAM,qBAAqB,GAAG,OAAO,CAAC,IAAI,CAAC,CAOrH"}
|
|
@@ -4,20 +4,20 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
4
4
|
};
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
6
|
exports.limitConversation = limitConversation;
|
|
7
|
+
exports.prepareHistory = prepareHistory;
|
|
8
|
+
exports.conversationFile = conversationFile;
|
|
7
9
|
exports.readConversation = readConversation;
|
|
8
10
|
exports.writeConversation = writeConversation;
|
|
9
11
|
const node_fs_1 = require("node:fs");
|
|
10
12
|
const node_path_1 = __importDefault(require("node:path"));
|
|
11
|
-
const
|
|
13
|
+
const turn_history_1 = require("../agent/turn-history");
|
|
14
|
+
const CONVERSATION_FILE = 'conversation.json';
|
|
12
15
|
/** Rough context cap (chars ≈ tokens × 4): older turns are dropped, the first question is kept. */
|
|
13
16
|
const CONTEXT_CHAR_BUDGET = 480000;
|
|
14
|
-
function approxChars(message) {
|
|
15
|
-
return JSON.stringify(message).length;
|
|
16
|
-
}
|
|
17
17
|
/** Drops the oldest turns (user message + everything up to the next user message) past the cap, keeping the first one. */
|
|
18
18
|
function limitConversation(messages, charBudget = CONTEXT_CHAR_BUDGET) {
|
|
19
19
|
var _a;
|
|
20
|
-
let total = messages.reduce((sum, message) => sum + approxChars(message), 0);
|
|
20
|
+
let total = messages.reduce((sum, message) => sum + (0, turn_history_1.approxChars)(message), 0);
|
|
21
21
|
if (total <= charBudget)
|
|
22
22
|
return messages;
|
|
23
23
|
const result = [...messages];
|
|
@@ -28,15 +28,43 @@ function limitConversation(messages, charBudget = CONTEXT_CHAR_BUDGET) {
|
|
|
28
28
|
const start = userIndexes[1];
|
|
29
29
|
const end = (_a = userIndexes[2]) !== null && _a !== void 0 ? _a : result.length;
|
|
30
30
|
const removed = result.splice(start, end - start);
|
|
31
|
-
total -= removed.reduce((sum, message) => sum + approxChars(message), 0);
|
|
31
|
+
total -= removed.reduce((sum, message) => sum + (0, turn_history_1.approxChars)(message), 0);
|
|
32
32
|
}
|
|
33
33
|
return result;
|
|
34
34
|
}
|
|
35
|
+
/**
|
|
36
|
+
* Every earlier turn as one assistant message (its final text plus what it did), then the character
|
|
37
|
+
* cap. Tool calls and results of finished turns never re-enter the prompt: the workspace holds what they
|
|
38
|
+
* produced, and the account names the files. An interrupted turn (a lost VM mid-work) collapses the same
|
|
39
|
+
* way, which is how a restored session knows what was already done.
|
|
40
|
+
*/
|
|
41
|
+
function prepareHistory(messages, charBudget = CONTEXT_CHAR_BUDGET) {
|
|
42
|
+
const prepared = [];
|
|
43
|
+
let run = [];
|
|
44
|
+
const flush = () => {
|
|
45
|
+
if (run.length)
|
|
46
|
+
prepared.push((0, turn_history_1.collapseTurn)(run));
|
|
47
|
+
run = [];
|
|
48
|
+
};
|
|
49
|
+
for (const message of messages) {
|
|
50
|
+
if (message.role === 'assistant' || message.role === 'tool')
|
|
51
|
+
run.push(message);
|
|
52
|
+
else {
|
|
53
|
+
flush();
|
|
54
|
+
prepared.push(message);
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
flush();
|
|
58
|
+
return limitConversation(prepared, charBudget);
|
|
59
|
+
}
|
|
60
|
+
function conversationFile(stateDir) {
|
|
61
|
+
return node_path_1.default.join(stateDir, CONVERSATION_FILE);
|
|
62
|
+
}
|
|
35
63
|
/** Missing conversation means a new session; corrupt state must fail startup. */
|
|
36
|
-
async function readConversation(
|
|
64
|
+
async function readConversation(stateDir) {
|
|
37
65
|
let raw;
|
|
38
66
|
try {
|
|
39
|
-
raw = await node_fs_1.promises.readFile(
|
|
67
|
+
raw = await node_fs_1.promises.readFile(conversationFile(stateDir), 'utf8');
|
|
40
68
|
}
|
|
41
69
|
catch (error) {
|
|
42
70
|
if (error.code === 'ENOENT')
|
|
@@ -50,12 +78,12 @@ async function readConversation(workspaceDir) {
|
|
|
50
78
|
return parsed;
|
|
51
79
|
}
|
|
52
80
|
/** Replace atomically so recovery never reads half of a conversation. The caller serializes writes. */
|
|
53
|
-
async function writeConversation(
|
|
54
|
-
const target =
|
|
55
|
-
await node_fs_1.promises.mkdir(
|
|
81
|
+
async function writeConversation(stateDir, getConversation) {
|
|
82
|
+
const target = conversationFile(stateDir);
|
|
83
|
+
await node_fs_1.promises.mkdir(stateDir, { recursive: true, mode: 0o700 });
|
|
56
84
|
// Read messages and token counters together after the directory is ready. A model step
|
|
57
85
|
// may finish during mkdir; capturing counters earlier would pair them with newer messages.
|
|
58
|
-
await node_fs_1.promises.writeFile(`${target}.tmp`, JSON.stringify(getConversation()));
|
|
86
|
+
await node_fs_1.promises.writeFile(`${target}.tmp`, JSON.stringify(getConversation()), { mode: 0o600 });
|
|
59
87
|
await node_fs_1.promises.rename(`${target}.tmp`, target);
|
|
60
88
|
}
|
|
61
89
|
//# sourceMappingURL=conversation.js.map
|