@agentsh/secure-sandbox 0.1.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.
Files changed (52) hide show
  1. package/README.md +198 -0
  2. package/dist/adapters/blaxel.d.ts +5 -0
  3. package/dist/adapters/blaxel.js +9 -0
  4. package/dist/adapters/blaxel.js.map +1 -0
  5. package/dist/adapters/cloudflare.d.ts +5 -0
  6. package/dist/adapters/cloudflare.js +9 -0
  7. package/dist/adapters/cloudflare.js.map +1 -0
  8. package/dist/adapters/daytona.d.ts +5 -0
  9. package/dist/adapters/daytona.js +9 -0
  10. package/dist/adapters/daytona.js.map +1 -0
  11. package/dist/adapters/e2b.d.ts +5 -0
  12. package/dist/adapters/e2b.js +9 -0
  13. package/dist/adapters/e2b.js.map +1 -0
  14. package/dist/adapters/index.d.ts +6 -0
  15. package/dist/adapters/index.js +26 -0
  16. package/dist/adapters/index.js.map +1 -0
  17. package/dist/adapters/vercel.d.ts +5 -0
  18. package/dist/adapters/vercel.js +8 -0
  19. package/dist/adapters/vercel.js.map +1 -0
  20. package/dist/chunk-2P37YGN7.js +52 -0
  21. package/dist/chunk-2P37YGN7.js.map +1 -0
  22. package/dist/chunk-45FKFVMC.js +55 -0
  23. package/dist/chunk-45FKFVMC.js.map +1 -0
  24. package/dist/chunk-JY5ERJTX.js +49 -0
  25. package/dist/chunk-JY5ERJTX.js.map +1 -0
  26. package/dist/chunk-L4KFLVNU.js +33 -0
  27. package/dist/chunk-L4KFLVNU.js.map +1 -0
  28. package/dist/chunk-LMN3KM53.js +48 -0
  29. package/dist/chunk-LMN3KM53.js.map +1 -0
  30. package/dist/chunk-NWHVZ3DG.js +599 -0
  31. package/dist/chunk-NWHVZ3DG.js.map +1 -0
  32. package/dist/chunk-OANLKSOD.js +28 -0
  33. package/dist/chunk-OANLKSOD.js.map +1 -0
  34. package/dist/chunk-PZ5AY32C.js +10 -0
  35. package/dist/chunk-PZ5AY32C.js.map +1 -0
  36. package/dist/chunk-UYEAO27E.js +65 -0
  37. package/dist/chunk-UYEAO27E.js.map +1 -0
  38. package/dist/esm-7TZRRYDK.js +1375 -0
  39. package/dist/esm-7TZRRYDK.js.map +1 -0
  40. package/dist/index-D0UvBOzr.d.ts +463 -0
  41. package/dist/index-aQ1TVPtG.d.ts +16 -0
  42. package/dist/index.d.ts +77 -0
  43. package/dist/index.js +774 -0
  44. package/dist/index.js.map +1 -0
  45. package/dist/policies/index.d.ts +2 -0
  46. package/dist/policies/index.js +26 -0
  47. package/dist/policies/index.js.map +1 -0
  48. package/dist/testing/index.d.ts +13 -0
  49. package/dist/testing/index.js +32 -0
  50. package/dist/testing/index.js.map +1 -0
  51. package/dist/types-BwEbraFo.d.ts +194 -0
  52. package/package.json +99 -0
package/dist/index.js ADDED
@@ -0,0 +1,774 @@
1
+ import {
2
+ adapters_exports
3
+ } from "./chunk-L4KFLVNU.js";
4
+ import "./chunk-UYEAO27E.js";
5
+ import "./chunk-LMN3KM53.js";
6
+ import "./chunk-45FKFVMC.js";
7
+ import "./chunk-2P37YGN7.js";
8
+ import "./chunk-OANLKSOD.js";
9
+ import "./chunk-JY5ERJTX.js";
10
+ import {
11
+ AgentSHError,
12
+ IncompatibleProviderVersionError,
13
+ IntegrityError,
14
+ MissingPeerDependencyError,
15
+ PolicyValidationError,
16
+ ProvisioningError,
17
+ RuntimeError,
18
+ agentDefault,
19
+ policies_exports,
20
+ serializePolicy,
21
+ systemPolicyYaml,
22
+ validatePolicy
23
+ } from "./chunk-NWHVZ3DG.js";
24
+ import "./chunk-PZ5AY32C.js";
25
+
26
+ // src/core/integrity.ts
27
+ var PINNED_VERSION = "0.15.0";
28
+ var CHECKSUMS = {
29
+ "0.15.0": {
30
+ linux_amd64: "89f7ebbfd75ffd961245ec62b2602fd0cc387740502ac858dbc39c367c5699c5",
31
+ linux_arm64: "3fabbd749f9e98fb9f96ddfc94c389a6868cda7ed3668daa8440c39ceec85f3b"
32
+ },
33
+ "0.14.0": {
34
+ linux_amd64: "2ab8ba0d6637fe1a5badf840c3db197161a6f9865d721ed216029d229b1b9bbc",
35
+ linux_arm64: "929d18dd9fe36e9b2fa830d7ae64b4fb481853e743ade8674fcfcdc73470ed53"
36
+ }
37
+ };
38
+ function getChecksum(version, arch, override) {
39
+ if (override) {
40
+ return override;
41
+ }
42
+ const versionChecksums = CHECKSUMS[version];
43
+ if (versionChecksums && versionChecksums[arch]) {
44
+ return versionChecksums[arch];
45
+ }
46
+ throw new IntegrityError({
47
+ expected: "",
48
+ actual: "",
49
+ message: `No pinned checksum for agentsh v${version}. Provide \`agentshChecksum\` explicitly or use \`skipIntegrityCheck: true\`.`
50
+ });
51
+ }
52
+ function buildVerifyCommand(filePath) {
53
+ return [
54
+ `sha256sum "${filePath}" | awk '{print $1}'`,
55
+ `shasum -a 256 "${filePath}" | awk '{print $1}'`,
56
+ `openssl dgst -sha256 "${filePath}" | awk '{print $NF}'`
57
+ ];
58
+ }
59
+ function binaryUrl(version, arch, overrideUrl) {
60
+ if (overrideUrl) {
61
+ return overrideUrl;
62
+ }
63
+ return `https://github.com/canyonroad/agentsh/releases/download/v${version}/agentsh_${version}_${arch}.tar.gz`;
64
+ }
65
+
66
+ // src/core/config.ts
67
+ import yaml from "js-yaml";
68
+ var defaultThreatFeeds = {
69
+ action: "deny",
70
+ feeds: [
71
+ {
72
+ name: "urlhaus",
73
+ url: "https://urlhaus.abuse.ch/downloads/hostfile/",
74
+ format: "hostfile",
75
+ refreshInterval: "6h"
76
+ },
77
+ {
78
+ name: "phishing",
79
+ url: "https://raw.githubusercontent.com/mitchellkrogza/Phishing.Database/master/phishing-domains-ACTIVE.txt",
80
+ format: "domain-list",
81
+ refreshInterval: "12h"
82
+ }
83
+ ],
84
+ allowlist: [
85
+ "github.com",
86
+ "*.github.com",
87
+ "registry.npmjs.org",
88
+ "registry.yarnpkg.com",
89
+ "pypi.org",
90
+ "files.pythonhosted.org",
91
+ "crates.io",
92
+ "static.crates.io",
93
+ "index.crates.io",
94
+ "proxy.golang.org",
95
+ "sum.golang.org"
96
+ ]
97
+ };
98
+ function generateServerConfig(opts) {
99
+ const config = {
100
+ server: {
101
+ http: {
102
+ addr: "127.0.0.1:18080"
103
+ }
104
+ },
105
+ auth: {
106
+ type: "none"
107
+ },
108
+ policies: {
109
+ system_dir: "/etc/agentsh/system",
110
+ dir: "/etc/agentsh",
111
+ default: "policy"
112
+ },
113
+ workspace: opts.workspace,
114
+ sandbox: {
115
+ enabled: true,
116
+ allow_degraded: true
117
+ }
118
+ };
119
+ if (opts.watchtower) config.watchtower = opts.watchtower;
120
+ if (opts.enforceRedirects) config.enforce_redirects = true;
121
+ if (opts.realPaths) config.real_paths = true;
122
+ const feeds = opts.threatFeeds === false ? void 0 : opts.threatFeeds ?? defaultThreatFeeds;
123
+ if (feeds) {
124
+ config.threat_feeds = {
125
+ enabled: true,
126
+ action: feeds.action ?? "deny",
127
+ feeds: feeds.feeds.map((f) => ({
128
+ name: f.name,
129
+ url: f.url,
130
+ format: f.format,
131
+ refresh_interval: f.refreshInterval ?? "6h"
132
+ })),
133
+ ...feeds.allowlist?.length ? { allowlist: feeds.allowlist } : {}
134
+ };
135
+ }
136
+ return yaml.dump(config, { lineWidth: -1 });
137
+ }
138
+
139
+ // src/core/traceparent.ts
140
+ async function getTraceparent() {
141
+ try {
142
+ const { trace } = await import("./esm-7TZRRYDK.js");
143
+ const span = trace.getActiveSpan();
144
+ const ctx = span?.spanContext();
145
+ if (!ctx?.traceId || ctx.traceId === "00000000000000000000000000000000") {
146
+ return void 0;
147
+ }
148
+ const flags = (ctx.traceFlags ?? 0).toString(16).padStart(2, "0");
149
+ return `00-${ctx.traceId}-${ctx.spanId}-${flags}`;
150
+ } catch {
151
+ return void 0;
152
+ }
153
+ }
154
+
155
+ // src/core/provision.ts
156
+ var SECURITY_MODE_RANK = {
157
+ full: 4,
158
+ landlock: 3,
159
+ "landlock-only": 2,
160
+ minimal: 1
161
+ };
162
+ function isWeakerThan(detected, required) {
163
+ return SECURITY_MODE_RANK[detected] < SECURITY_MODE_RANK[required];
164
+ }
165
+ function mapArch(uname) {
166
+ const trimmed = uname.trim();
167
+ if (trimmed === "x86_64") return "linux_amd64";
168
+ if (trimmed === "aarch64") return "linux_arm64";
169
+ throw new ProvisioningError({
170
+ phase: "install",
171
+ command: "uname -m",
172
+ stderr: `Unsupported architecture: ${trimmed}`
173
+ });
174
+ }
175
+ var AGENTSH_PATHS = ["/usr/local/bin/agentsh", "/usr/bin/agentsh"];
176
+ async function binaryExists(adapter) {
177
+ for (const path of AGENTSH_PATHS) {
178
+ const found = adapter.fileExists ? await adapter.fileExists(path) : (await adapter.exec("test", ["-f", path])).exitCode === 0;
179
+ if (found) return true;
180
+ }
181
+ return false;
182
+ }
183
+ function sleep(ms) {
184
+ return new Promise((resolve) => setTimeout(resolve, ms));
185
+ }
186
+ async function provision(adapter, config = {}) {
187
+ const {
188
+ policy: rawPolicy,
189
+ workspace = "/workspace",
190
+ watchtower,
191
+ installStrategy = "download",
192
+ agentshVersion = PINNED_VERSION,
193
+ agentshArch: archOverride,
194
+ agentshBinaryUrl,
195
+ agentshChecksum,
196
+ skipIntegrityCheck = false,
197
+ minimumSecurityMode,
198
+ realPaths: realPathsOverride,
199
+ enforceRedirects = false,
200
+ traceParent,
201
+ policyName = "policy",
202
+ threatFeeds
203
+ } = config;
204
+ const policy = rawPolicy ? validatePolicy(rawPolicy) : agentDefault();
205
+ let securityMode = "full";
206
+ if (installStrategy === "running") {
207
+ securityMode = await detectSecurityMode(adapter);
208
+ if (minimumSecurityMode && isWeakerThan(securityMode, minimumSecurityMode)) {
209
+ throw new ProvisioningError({
210
+ phase: "install",
211
+ command: "agentsh detect --output json",
212
+ stderr: `Detected security mode '${securityMode}' is weaker than required '${minimumSecurityMode}'`
213
+ });
214
+ }
215
+ await healthCheck(adapter);
216
+ const envResult = await adapter.exec("sh", ["-c", "echo $AGENTSH_SESSION_ID"]);
217
+ const sessionId2 = envResult.stdout.trim();
218
+ if (!sessionId2) {
219
+ throw new ProvisioningError({
220
+ phase: "session",
221
+ command: "echo $AGENTSH_SESSION_ID",
222
+ stderr: "AGENTSH_SESSION_ID not set \u2014 running strategy requires a pre-created session"
223
+ });
224
+ }
225
+ return { sessionId: sessionId2, securityMode, passthrough: true };
226
+ }
227
+ const exists = await binaryExists(adapter);
228
+ if (installStrategy === "preinstalled") {
229
+ if (!exists) {
230
+ throw new ProvisioningError({
231
+ phase: "install",
232
+ command: AGENTSH_PATHS.map((p) => `test -f ${p}`).join(" || "),
233
+ stderr: "Binary not found but installStrategy is preinstalled"
234
+ });
235
+ }
236
+ } else if (installStrategy === "download" || installStrategy === "upload") {
237
+ if (!exists) {
238
+ const arch = archOverride ?? await detectArch(adapter);
239
+ if (installStrategy === "download") {
240
+ await downloadBinary(adapter, agentshVersion, arch, agentshBinaryUrl);
241
+ } else {
242
+ await uploadBinary(adapter, agentshVersion, arch, agentshBinaryUrl);
243
+ }
244
+ if (!skipIntegrityCheck) {
245
+ await verifyChecksum(
246
+ adapter,
247
+ agentshVersion,
248
+ arch,
249
+ agentshChecksum,
250
+ "/tmp/agentsh.tar.gz"
251
+ );
252
+ }
253
+ const binaries = [
254
+ { src: "/tmp/agentsh", dest: "/usr/local/bin/agentsh" },
255
+ { src: "/tmp/agentsh-shell-shim", dest: "/usr/bin/agentsh-shell-shim" },
256
+ { src: "/tmp/agentsh-unixwrap", dest: "/usr/local/bin/agentsh-unixwrap" }
257
+ ];
258
+ for (const { src, dest } of binaries) {
259
+ const installResult = await adapter.exec(
260
+ "install",
261
+ ["-m", "0755", src, dest],
262
+ { sudo: true }
263
+ );
264
+ if (installResult.exitCode !== 0) {
265
+ throw new ProvisioningError({
266
+ phase: "install",
267
+ command: `install -m 0755 ${src} ${dest}`,
268
+ stderr: installResult.stderr
269
+ });
270
+ }
271
+ }
272
+ }
273
+ }
274
+ securityMode = await detectSecurityMode(adapter);
275
+ if (minimumSecurityMode && isWeakerThan(securityMode, minimumSecurityMode)) {
276
+ throw new ProvisioningError({
277
+ phase: "install",
278
+ command: "agentsh detect --json",
279
+ stderr: `Detected security mode '${securityMode}' is weaker than required '${minimumSecurityMode}'`
280
+ });
281
+ }
282
+ const hasFuse = securityMode === "full" || securityMode === "landlock";
283
+ const realPaths = realPathsOverride ?? hasFuse;
284
+ const shimResult = await adapter.exec(
285
+ "agentsh",
286
+ [
287
+ "shim",
288
+ "install-shell",
289
+ "--root",
290
+ "/",
291
+ "--shim",
292
+ "/usr/bin/agentsh-shell-shim",
293
+ "--bash",
294
+ "--i-understand-this-modifies-the-host"
295
+ ],
296
+ { sudo: true }
297
+ );
298
+ if (shimResult.exitCode !== 0) {
299
+ throw new ProvisioningError({
300
+ phase: "install",
301
+ command: "agentsh shim install-shell",
302
+ stderr: shimResult.stderr
303
+ });
304
+ }
305
+ const mkdirResult = await adapter.exec(
306
+ "mkdir",
307
+ ["-p", "/etc/agentsh/system"],
308
+ { sudo: true }
309
+ );
310
+ if (mkdirResult.exitCode !== 0) {
311
+ throw new ProvisioningError({
312
+ phase: "policy",
313
+ command: "mkdir -p /etc/agentsh/system",
314
+ stderr: mkdirResult.stderr
315
+ });
316
+ }
317
+ await adapter.exec("chmod", ["-R", "777", "/etc/agentsh/"], { sudo: true });
318
+ await adapter.writeFile(
319
+ "/etc/agentsh/system/policy.yml",
320
+ systemPolicyYaml(),
321
+ { sudo: true }
322
+ );
323
+ await adapter.writeFile(
324
+ "/etc/agentsh/policy.yml",
325
+ serializePolicy(policy),
326
+ { sudo: true }
327
+ );
328
+ const serverConfig = generateServerConfig({
329
+ workspace,
330
+ watchtower,
331
+ enforceRedirects,
332
+ realPaths,
333
+ threatFeeds
334
+ });
335
+ await adapter.writeFile("/etc/agentsh/config.yml", serverConfig, {
336
+ sudo: true
337
+ });
338
+ const chmodDirResult = await adapter.exec(
339
+ "find",
340
+ ["/etc/agentsh", "-type", "d", "-exec", "chmod", "555", "{}", "+"],
341
+ { sudo: true }
342
+ );
343
+ if (chmodDirResult.exitCode !== 0) {
344
+ throw new ProvisioningError({
345
+ phase: "policy",
346
+ command: "find /etc/agentsh -type d -exec chmod 555 {} +",
347
+ stderr: chmodDirResult.stderr
348
+ });
349
+ }
350
+ const chmodFileResult = await adapter.exec(
351
+ "find",
352
+ ["/etc/agentsh", "-type", "f", "-exec", "chmod", "444", "{}", "+"],
353
+ { sudo: true }
354
+ );
355
+ if (chmodFileResult.exitCode !== 0) {
356
+ throw new ProvisioningError({
357
+ phase: "policy",
358
+ command: "find /etc/agentsh -type f -exec chmod 444 {} +",
359
+ stderr: chmodFileResult.stderr
360
+ });
361
+ }
362
+ const chownResult = await adapter.exec(
363
+ "chown",
364
+ ["-R", "root:root", "/etc/agentsh/"],
365
+ { sudo: true }
366
+ );
367
+ if (chownResult.exitCode !== 0) {
368
+ throw new ProvisioningError({
369
+ phase: "policy",
370
+ command: "chown -R root:root /etc/agentsh/",
371
+ stderr: chownResult.stderr
372
+ });
373
+ }
374
+ await adapter.exec("mkdir", ["-p", workspace], { sudo: true });
375
+ const serverResult = await adapter.exec(
376
+ "agentsh",
377
+ ["server", "--config", "/etc/agentsh/config.yml"],
378
+ { detached: true, sudo: true }
379
+ );
380
+ if (serverResult.exitCode !== 0) {
381
+ throw new ProvisioningError({
382
+ phase: "startup",
383
+ command: "agentsh server --config /etc/agentsh/config.yml",
384
+ stderr: serverResult.stderr
385
+ });
386
+ }
387
+ await healthCheck(adapter);
388
+ const sessionResult = await adapter.exec("agentsh", [
389
+ "session",
390
+ "create",
391
+ "--workspace",
392
+ workspace,
393
+ "--policy",
394
+ "policy"
395
+ ]);
396
+ if (sessionResult.exitCode !== 0) {
397
+ throw new ProvisioningError({
398
+ phase: "session",
399
+ command: "agentsh session create",
400
+ stderr: sessionResult.stderr
401
+ });
402
+ }
403
+ let sessionId;
404
+ try {
405
+ const sessionData = JSON.parse(sessionResult.stdout);
406
+ sessionId = sessionData.session_id;
407
+ } catch {
408
+ const match = sessionResult.stdout.match(/Session\s+(session-[^\s]+)/);
409
+ if (match) {
410
+ sessionId = match[1];
411
+ } else {
412
+ throw new ProvisioningError({
413
+ phase: "session",
414
+ command: "agentsh session create",
415
+ stderr: `Failed to parse session output: ${sessionResult.stdout}`
416
+ });
417
+ }
418
+ }
419
+ const effectiveTraceParent = traceParent ?? await getTraceparent();
420
+ if (effectiveTraceParent) {
421
+ await adapter.exec("curl", [
422
+ "-X",
423
+ "PUT",
424
+ `http://127.0.0.1:18080/sessions/${sessionId}/trace-context`,
425
+ "-H",
426
+ "Content-Type: application/json",
427
+ "-d",
428
+ JSON.stringify({ traceparent: effectiveTraceParent })
429
+ ]);
430
+ }
431
+ return { sessionId, securityMode };
432
+ }
433
+ async function detectArch(adapter) {
434
+ const result = await adapter.exec("uname", ["-m"]);
435
+ if (result.exitCode !== 0) {
436
+ throw new ProvisioningError({
437
+ phase: "install",
438
+ command: "uname -m",
439
+ stderr: result.stderr
440
+ });
441
+ }
442
+ return mapArch(result.stdout);
443
+ }
444
+ async function downloadBinary(adapter, version, arch, overrideUrl) {
445
+ const url = binaryUrl(version, arch, overrideUrl);
446
+ const curlResult = await adapter.exec("curl", [
447
+ "-fsSL",
448
+ url,
449
+ "-o",
450
+ "/tmp/agentsh.tar.gz"
451
+ ]);
452
+ if (curlResult.exitCode !== 0) {
453
+ let wgetResult;
454
+ try {
455
+ wgetResult = await adapter.exec("wget", [
456
+ "-q",
457
+ url,
458
+ "-O",
459
+ "/tmp/agentsh.tar.gz"
460
+ ]);
461
+ } catch {
462
+ throw new ProvisioningError({
463
+ phase: "install",
464
+ command: `curl -fsSL ${url} -o /tmp/agentsh.tar.gz`,
465
+ stderr: curlResult.stderr || "Download failed (curl failed, wget not available)"
466
+ });
467
+ }
468
+ if (wgetResult.exitCode !== 0) {
469
+ throw new ProvisioningError({
470
+ phase: "install",
471
+ command: `wget -q ${url} -O /tmp/agentsh.tar.gz`,
472
+ stderr: wgetResult.stderr
473
+ });
474
+ }
475
+ }
476
+ const tarResult = await adapter.exec("tar", [
477
+ "xz",
478
+ "-C",
479
+ "/tmp/",
480
+ "-f",
481
+ "/tmp/agentsh.tar.gz"
482
+ ]);
483
+ if (tarResult.exitCode !== 0) {
484
+ throw new ProvisioningError({
485
+ phase: "install",
486
+ command: "tar xz -C /tmp/ -f /tmp/agentsh.tar.gz",
487
+ stderr: tarResult.stderr
488
+ });
489
+ }
490
+ }
491
+ async function uploadBinary(adapter, version, arch, overrideUrl) {
492
+ const url = binaryUrl(version, arch, overrideUrl);
493
+ const response = await fetch(url);
494
+ if (!response.ok) {
495
+ throw new ProvisioningError({
496
+ phase: "install",
497
+ command: `fetch ${url}`,
498
+ stderr: `HTTP ${response.status}: ${response.statusText}`
499
+ });
500
+ }
501
+ const buffer = Buffer.from(await response.arrayBuffer());
502
+ await adapter.writeFile("/tmp/agentsh.tar.gz", buffer);
503
+ const tarResult = await adapter.exec("tar", [
504
+ "xz",
505
+ "-C",
506
+ "/tmp/",
507
+ "-f",
508
+ "/tmp/agentsh.tar.gz"
509
+ ]);
510
+ if (tarResult.exitCode !== 0) {
511
+ throw new ProvisioningError({
512
+ phase: "install",
513
+ command: "tar xz -C /tmp/ -f /tmp/agentsh.tar.gz",
514
+ stderr: tarResult.stderr
515
+ });
516
+ }
517
+ }
518
+ async function verifyChecksum(adapter, version, arch, checksumOverride, filePath) {
519
+ const expected = getChecksum(version, arch, checksumOverride);
520
+ const commands = buildVerifyCommand(filePath);
521
+ let actual;
522
+ for (const cmd of commands) {
523
+ const result = await adapter.exec("sh", ["-c", cmd]);
524
+ if (result.exitCode === 0 && result.stdout.trim()) {
525
+ actual = result.stdout.trim();
526
+ break;
527
+ }
528
+ }
529
+ if (actual === void 0) {
530
+ throw new ProvisioningError({
531
+ phase: "install",
532
+ command: "sha256sum / shasum / openssl",
533
+ stderr: "No checksum tool available in sandbox"
534
+ });
535
+ }
536
+ if (actual !== expected) {
537
+ throw new IntegrityError({
538
+ expected,
539
+ actual,
540
+ message: `Checksum mismatch: expected ${expected}, got ${actual}`
541
+ });
542
+ }
543
+ }
544
+ async function detectSecurityMode(adapter) {
545
+ const result = await adapter.exec("agentsh", ["detect", "--output", "json"]);
546
+ if (result.exitCode !== 0) {
547
+ throw new ProvisioningError({
548
+ phase: "install",
549
+ command: "agentsh detect --output json",
550
+ stderr: result.stderr
551
+ });
552
+ }
553
+ const jsonOutput = result.stderr || result.stdout;
554
+ let parsed;
555
+ try {
556
+ parsed = JSON.parse(jsonOutput);
557
+ } catch {
558
+ throw new ProvisioningError({
559
+ phase: "install",
560
+ command: "agentsh detect --output json",
561
+ stderr: `Failed to parse detect JSON: ${jsonOutput.slice(0, 200)}`
562
+ });
563
+ }
564
+ const mode = parsed.security_mode;
565
+ const validModes = ["full", "landlock", "landlock-only", "minimal"];
566
+ if (!validModes.includes(mode)) {
567
+ throw new ProvisioningError({
568
+ phase: "install",
569
+ command: "agentsh detect --output json",
570
+ stderr: `Unknown security mode: '${mode}'`
571
+ });
572
+ }
573
+ return mode;
574
+ }
575
+ async function healthCheck(adapter) {
576
+ const maxRetries = 10;
577
+ const delayMs = 500;
578
+ for (let i = 0; i < maxRetries; i++) {
579
+ const result = await adapter.exec("curl", [
580
+ "-sf",
581
+ "http://127.0.0.1:18080/health"
582
+ ]);
583
+ if (result.exitCode === 0) {
584
+ return;
585
+ }
586
+ if (i < maxRetries - 1) {
587
+ await sleep(delayMs);
588
+ }
589
+ }
590
+ throw new ProvisioningError({
591
+ phase: "startup",
592
+ command: "curl http://127.0.0.1:18080/health",
593
+ stderr: "Health check failed after 10 attempts"
594
+ });
595
+ }
596
+
597
+ // src/core/runtime.ts
598
+ async function traceEnv() {
599
+ const tp = await getTraceparent();
600
+ return tp ? { TRACEPARENT: tp } : void 0;
601
+ }
602
+ function parseExecJson(raw) {
603
+ try {
604
+ const json = JSON.parse(raw.stdout);
605
+ const result = json.result ?? {};
606
+ return {
607
+ exitCode: result.exit_code ?? raw.exitCode,
608
+ stdout: result.stdout ?? "",
609
+ stderr: result.stderr ?? result.error?.message ?? ""
610
+ };
611
+ } catch {
612
+ return raw;
613
+ }
614
+ }
615
+ function createSecuredSandbox(adapter, sessionId, securityMode, options) {
616
+ if (options?.passthrough) {
617
+ return createPassthroughSandbox(adapter, sessionId, securityMode);
618
+ }
619
+ return createAgentshSandbox(adapter, sessionId, securityMode);
620
+ }
621
+ function createPassthroughSandbox(adapter, sessionId, securityMode) {
622
+ return {
623
+ sessionId,
624
+ securityMode,
625
+ async exec(command, opts) {
626
+ const result = await adapter.exec("bash", ["-c", command], {
627
+ cwd: opts?.cwd
628
+ });
629
+ return result;
630
+ },
631
+ async writeFile(path, content) {
632
+ const b64 = Buffer.from(content, "utf-8").toString("base64");
633
+ const result = await adapter.exec("sh", [
634
+ "-c",
635
+ 'printf "%s" "$1" | base64 -d > "$2"',
636
+ "_",
637
+ b64,
638
+ path
639
+ ]);
640
+ if (result.exitCode !== 0) {
641
+ return {
642
+ success: false,
643
+ path,
644
+ error: result.stderr || "writeFile failed"
645
+ };
646
+ }
647
+ return { success: true, path };
648
+ },
649
+ async readFile(path) {
650
+ const result = await adapter.exec("cat", [path]);
651
+ if (result.exitCode !== 0) {
652
+ return {
653
+ success: false,
654
+ path,
655
+ error: result.stderr || "readFile failed"
656
+ };
657
+ }
658
+ return { success: true, path, content: result.stdout };
659
+ },
660
+ async stop() {
661
+ await adapter.stop?.();
662
+ }
663
+ };
664
+ }
665
+ function createAgentshSandbox(adapter, sessionId, securityMode) {
666
+ return {
667
+ sessionId,
668
+ securityMode,
669
+ async exec(command, opts) {
670
+ const args = [
671
+ "exec",
672
+ "--output",
673
+ "json",
674
+ sessionId,
675
+ "--",
676
+ "bash",
677
+ "-c",
678
+ command
679
+ ];
680
+ const env = await traceEnv();
681
+ const execOpts = { cwd: opts?.cwd, env };
682
+ const result = await adapter.exec("agentsh", args, execOpts);
683
+ if (isTransportFailure(result)) {
684
+ throw new RuntimeError({
685
+ sessionId,
686
+ command,
687
+ stderr: result.stderr
688
+ });
689
+ }
690
+ return parseExecJson(result);
691
+ },
692
+ async writeFile(path, content) {
693
+ const b64 = Buffer.from(content, "utf-8").toString("base64");
694
+ const args = [
695
+ "exec",
696
+ sessionId,
697
+ "--",
698
+ "sh",
699
+ "-c",
700
+ 'printf "%s" "$1" | base64 -d > "$2"',
701
+ "_",
702
+ b64,
703
+ path
704
+ ];
705
+ const env = await traceEnv();
706
+ const result = await adapter.exec("agentsh", args, { env });
707
+ if (isTransportFailure(result)) {
708
+ throw new RuntimeError({
709
+ sessionId,
710
+ command: `writeFile ${path}`,
711
+ stderr: result.stderr
712
+ });
713
+ }
714
+ if (result.exitCode !== 0) {
715
+ return {
716
+ success: false,
717
+ path,
718
+ error: result.stderr || "writeFile failed"
719
+ };
720
+ }
721
+ return { success: true, path };
722
+ },
723
+ async readFile(path) {
724
+ const args = ["exec", sessionId, "--", "cat", path];
725
+ const env = await traceEnv();
726
+ const result = await adapter.exec("agentsh", args, { env });
727
+ if (isTransportFailure(result)) {
728
+ throw new RuntimeError({
729
+ sessionId,
730
+ command: `readFile ${path}`,
731
+ stderr: result.stderr
732
+ });
733
+ }
734
+ if (result.exitCode !== 0) {
735
+ return {
736
+ success: false,
737
+ path,
738
+ error: result.stderr || "readFile failed"
739
+ };
740
+ }
741
+ return { success: true, path, content: result.stdout };
742
+ },
743
+ async stop() {
744
+ await adapter.stop?.();
745
+ }
746
+ };
747
+ }
748
+ function isTransportFailure(result) {
749
+ return result.exitCode === 127 && result.stderr.includes("agentsh");
750
+ }
751
+
752
+ // src/api.ts
753
+ async function secureSandbox(adapter, config) {
754
+ const resolvedConfig = config ?? {};
755
+ const { sessionId, securityMode, passthrough } = await provision(adapter, {
756
+ workspace: "/workspace",
757
+ ...resolvedConfig
758
+ });
759
+ return createSecuredSandbox(adapter, sessionId, securityMode, { passthrough });
760
+ }
761
+ export {
762
+ AgentSHError,
763
+ IncompatibleProviderVersionError,
764
+ IntegrityError,
765
+ MissingPeerDependencyError,
766
+ PolicyValidationError,
767
+ ProvisioningError,
768
+ RuntimeError,
769
+ adapters_exports as adapters,
770
+ defaultThreatFeeds,
771
+ policies_exports as policies,
772
+ secureSandbox
773
+ };
774
+ //# sourceMappingURL=index.js.map