windows-development-sandbox 1.18.2 → 1.18.3
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/config.example.json +1 -1
- package/dist/app/features/defs.d.ts +8 -0
- package/dist/app/features/sandbox/sandbox.def.d.ts +12 -0
- package/dist/app/features/sandbox/sandbox.def.js +17 -2
- package/dist/app/features/sandbox/sandbox.run.d.ts +4 -0
- package/dist/app/features/sandbox/sandbox.run.js +4 -0
- package/dist/bin/agent-sandbox.js +2 -2
- package/dist/bin/{chunk-IIHL7W6B.js → chunk-3G53NNZP.js} +1 -1
- package/dist/bin/{chunk-MQIA7GNB.js → chunk-FQWYCF6L.js} +981 -866
- package/dist/bin/{chunk-M4OM632I.js → chunk-M6XUQK53.js} +1 -1
- package/dist/bin/claude-host.js +2 -2
- package/dist/bin/claude-sbx.js +2 -2
- package/dist/bin/codex-sbx.js +2 -2
- package/dist/bin/gemini-sbx.js +2 -2
- package/dist/bin/sandbox.js +2 -2
- package/dist/bin/sbx-hide.js +2 -2
- package/dist/bin/sbx-show.js +2 -2
- package/dist/cli/commands/repair/checks/sandbox.d.ts +3 -1
- package/dist/cli/commands/repair/checks/sandbox.js +24 -0
- package/dist/core/config/defaults.js +1 -1
- package/dist/core/schema/sandbox.js +1 -1
- package/dist/core/schema/toolchains.js +2 -2
- package/dist/guest/daemon/daemon.js +1 -1
- package/dist/guest/daemon/heartbeat.d.ts +6 -0
- package/dist/guest/daemon/heartbeat.js +40 -0
- package/dist/guest/entries/daemon.js +2 -0
- package/dist/guest/governor/core.d.ts +19 -1
- package/dist/guest/governor/core.js +23 -1
- package/dist/guest/governor/decide.d.ts +14 -0
- package/dist/guest/governor/decide.js +52 -7
- package/dist/guest/governor/governor.d.ts +4 -1
- package/dist/guest/governor/governor.js +9 -2
- package/dist/guest/toolchains/native/rust-extras.d.ts +7 -0
- package/dist/guest/toolchains/native/rust-extras.js +26 -3
- package/dist/guest/toolchains/plan.js +6 -4
- package/dist/host/channel/channel.d.ts +1 -1
- package/dist/host/channel/channel.js +10 -2
- package/dist/host/launch/wsb-file.d.ts +6 -1
- package/dist/host/launch/wsb-file.js +9 -5
- package/dist/host/life/crash-report.d.ts +2 -0
- package/dist/host/life/crash-report.js +4 -0
- package/dist/host/life/index.d.ts +1 -0
- package/dist/host/life/index.js +1 -0
- package/dist/host/life/last-end.d.ts +21 -0
- package/dist/host/life/last-end.js +70 -0
- package/dist/host/life/watchdog.d.ts +2 -0
- package/dist/host/life/watchdog.js +8 -2
- package/dist/runtime/agent.js +26 -3
- package/dist/runtime/daemon.js +367 -234
- package/dist/runtime/db.js +26 -3
- package/dist/runtime/main.js +57 -11
- package/dist/runtime/netproxy.js +26 -3
- package/dist/runtime/tool.js +26 -3
- package/dist/runtime/view.js +26 -3
- package/dist/selftest/heavy-load.test.js +95 -14
- package/dist/selftest/live-terminal.test.js +4 -0
- package/dist/shared/channel/run-dir.d.ts +4 -1
- package/dist/shared/channel/run-dir.js +7 -1
- package/dist/shared/channel/schema.d.ts +23 -0
- package/dist/shared/channel/schema.js +21 -0
- package/dist/shared/memory/policy.d.ts +10 -0
- package/dist/shared/memory/policy.js +17 -1
- package/dist/sync/watcher.js +1 -1
- package/dist/web/index.html +1 -1
- package/dist/web/{main-CWIF4VQI.js → main-6XYO5FZC.js} +43 -43
- package/native/csharp/HcsHost/AssemblyInfo.cs +3 -3
- package/native/csharp/HostTools/AssemblyInfo.cs +3 -3
- package/native/csharp/StorageHelper/AssemblyInfo.cs +3 -3
- package/package.json +1 -1
- package/sandbox/csharp/MemGov.cs +58 -0
- package/schemas/config.schema.json +3 -3
- package/schemas/sandbox-config.schema.json +3 -3
|
@@ -25997,7 +25997,7 @@ var DEFAULTS = {
|
|
|
25997
25997
|
components: [],
|
|
25998
25998
|
targets: [],
|
|
25999
25999
|
crates: [],
|
|
26000
|
-
targetDir: "
|
|
26000
|
+
targetDir: "auto",
|
|
26001
26001
|
linker: "lld",
|
|
26002
26002
|
linkSlots: "auto"
|
|
26003
26003
|
},
|
|
@@ -46130,7 +46130,7 @@ var SandboxSchema = external_exports.strictObject({
|
|
|
46130
46130
|
'(alpha) What runs the sandbox. "windows-sandbox" = Windows Sandbox (the default: desktop, Windows Terminal, GPU, clipboard, audio, printers). "hcs" = a headless Hyper-V isolated Server Core container for agents (no desktop, GPU or clipboard); it needs Windows features and an image on this PC (sandbox engine check hcs lists what is missing), and starting it is not enabled yet in this alpha. sandbox engine shows what each engine can do. Yours only: a project file (.sandbox-config.json) cannot set it.'
|
|
46131
46131
|
),
|
|
46132
46132
|
memoryMb: external_exports.union([external_exports.literal("auto"), external_exports.number().int().min(2048)]).describe(
|
|
46133
|
-
'Sandbox RAM in MB (>= 2048), or "auto": free RAM minus reserveHostMb, capped at maxMemoryMb. Never more than this PC can keep in RAM for it (its RAM minus the larger of reserveHostMb and 25 %,
|
|
46133
|
+
'Sandbox RAM in MB (>= 2048), or "auto": free RAM minus reserveHostMb, capped at maxMemoryMb. Never more than this PC can keep in RAM for it (its RAM minus the larger of reserveHostMb and 25 %, 4 GB more with vGPU): a bigger value is lowered.'
|
|
46134
46134
|
),
|
|
46135
46135
|
maxMemoryMb: external_exports.number().int().positive().describe('Upper limit for memoryMb "auto".'),
|
|
46136
46136
|
maxMemoryPercent: external_exports.number().int().min(10).max(100).describe(`memoryMb "auto" never takes more than this share of this PC's total RAM (percent).`),
|
|
@@ -46292,13 +46292,13 @@ var ToolchainsSchema = external_exports.strictObject({
|
|
|
46292
46292
|
'["sccache", "cargo-nextest", "cargo-deny"]'
|
|
46293
46293
|
),
|
|
46294
46294
|
targetDir: external_exports.string().min(1).describe(
|
|
46295
|
-
`"shared": cargo's default target\\ (shared with builds on this PC, so builds on either side invalidate the other), "separate": <workspace>\\target\\sandbox (kept, no cross-talk), "local": on the sandbox's own disk (fastest, lost every boot), or a guest path.`
|
|
46295
|
+
`"auto" (default): on the sandbox's own disk when C: has 25 GB free at boot, else "separate" (builds then write through the mapped workspace, which can overload this PC's disk). "shared": cargo's default target\\ (shared with builds on this PC, so builds on either side invalidate the other), "separate": <workspace>\\target\\sandbox (kept, no cross-talk), "local": on the sandbox's own disk (fastest, lost every boot), or a guest path.`
|
|
46296
46296
|
),
|
|
46297
46297
|
linker: external_exports.enum(["lld", "gnu"]).describe(
|
|
46298
46298
|
`GNU host: "lld" links with Rust's own lld through MinGW's gcc (much less memory than GNU ld), "gnu" with GNU ld.`
|
|
46299
46299
|
),
|
|
46300
46300
|
linkSlots: external_exports.union([external_exports.literal("auto"), external_exports.number().int().min(1).max(32)]).describe(
|
|
46301
|
-
'GNU host: how many programs may link at the same time in the whole sandbox (other links wait). "auto" = one per
|
|
46301
|
+
'GNU host: how many programs may link at the same time in the whole sandbox (other links wait). "auto" = one per 6 GB of sandbox memory, 1 to 4 (the memory governor slows links down too when memory runs short).'
|
|
46302
46302
|
)
|
|
46303
46303
|
}),
|
|
46304
46304
|
msvc: external_exports.strictObject({
|
|
@@ -48552,7 +48552,7 @@ function hostClaude(storageRoot, personal, args) {
|
|
|
48552
48552
|
|
|
48553
48553
|
// src/host/sandbox/doctor.ts
|
|
48554
48554
|
import os6 from "node:os";
|
|
48555
|
-
import
|
|
48555
|
+
import path36 from "node:path";
|
|
48556
48556
|
|
|
48557
48557
|
// src/host/storage/cleanup.ts
|
|
48558
48558
|
var import_lodash4 = __toESM(require_lodash(), 1);
|
|
@@ -52293,8 +52293,8 @@ function stopWarning(life, find, crashReport = () => null) {
|
|
|
52293
52293
|
}
|
|
52294
52294
|
|
|
52295
52295
|
// src/host/life/crash-report.ts
|
|
52296
|
-
import
|
|
52297
|
-
import
|
|
52296
|
+
import fs26 from "node:fs";
|
|
52297
|
+
import path33 from "node:path";
|
|
52298
52298
|
|
|
52299
52299
|
// src/host/launch/risk-notes.ts
|
|
52300
52300
|
function loadProfileOf(config3) {
|
|
@@ -52445,12 +52445,542 @@ function takePageFileHint(storageRoot, now2 = /* @__PURE__ */ new Date()) {
|
|
|
52445
52445
|
return true;
|
|
52446
52446
|
}
|
|
52447
52447
|
|
|
52448
|
+
// src/shared/channel/schema.ts
|
|
52449
|
+
var IdSchema = external_exports.string().regex(/^[A-Za-z0-9_-]{1,64}$/, "invalid id");
|
|
52450
|
+
var Text = (max = 4096) => external_exports.string().max(max);
|
|
52451
|
+
var TimeSchema = Text(64);
|
|
52452
|
+
var AttachEndpointSchema = external_exports.strictObject({
|
|
52453
|
+
addresses: external_exports.array(Text(64)).max(16),
|
|
52454
|
+
port: external_exports.number().int().min(1024).max(65535),
|
|
52455
|
+
token: Text(256)
|
|
52456
|
+
});
|
|
52457
|
+
var BootProgressSchema = external_exports.strictObject({
|
|
52458
|
+
step: external_exports.number().int().min(1).max(32),
|
|
52459
|
+
steps: external_exports.number().int().min(1).max(32),
|
|
52460
|
+
title: Text(120),
|
|
52461
|
+
/** Being set up now; percent while it downloads (when the size is known). */
|
|
52462
|
+
active: external_exports.array(external_exports.strictObject({ name: Text(64), percent: external_exports.number().int().min(0).max(100).optional() })).max(8),
|
|
52463
|
+
done: external_exports.number().int().min(0).max(1e3),
|
|
52464
|
+
total: external_exports.number().int().min(0).max(1e3)
|
|
52465
|
+
});
|
|
52466
|
+
var SandboxStatusSchema = external_exports.object({
|
|
52467
|
+
launchId: Text(64),
|
|
52468
|
+
phase: external_exports.enum(["booting", "provisioning", "ready", "failed"]),
|
|
52469
|
+
message: Text(),
|
|
52470
|
+
updatedAt: TimeSchema,
|
|
52471
|
+
workspaceHost: Text(),
|
|
52472
|
+
guestWorkspace: Text(),
|
|
52473
|
+
headless: external_exports.boolean(),
|
|
52474
|
+
/** The sandbox's IPv4 addresses (for network.forwardPorts; the host only uses ones on its Hyper-V network). */
|
|
52475
|
+
addresses: external_exports.array(external_exports.string().max(45)).max(8).optional(),
|
|
52476
|
+
/** A malformed progress is dropped, never the status (the phase still counts). */
|
|
52477
|
+
progress: BootProgressSchema.optional().catch(void 0)
|
|
52478
|
+
});
|
|
52479
|
+
var VersionSchema = external_exports.strictObject({ spec: Text(128), source: Text(256) });
|
|
52480
|
+
var DatabasePlanSchema = external_exports.strictObject({
|
|
52481
|
+
database: external_exports.enum(["postgres", "mysql", "sqlserver", "mongodb", "redis", "dynamodb"]),
|
|
52482
|
+
reasons: external_exports.array(Text(1024)).max(32),
|
|
52483
|
+
version: Text(64),
|
|
52484
|
+
port: external_exports.number().int().min(1024).max(65535),
|
|
52485
|
+
adminPort: external_exports.number().int().min(1024).max(65535).optional()
|
|
52486
|
+
});
|
|
52487
|
+
var ProjectSpecSchema = external_exports.strictObject({
|
|
52488
|
+
name: IdSchema,
|
|
52489
|
+
/** Inside the sandbox: C:\Projects\<name>. */
|
|
52490
|
+
workspace: Text(1024),
|
|
52491
|
+
workspaceHost: Text(1024),
|
|
52492
|
+
toolchains: ToolchainsSchema,
|
|
52493
|
+
versions: external_exports.strictObject({ node: VersionSchema, nodeExtra: external_exports.array(Text(128)).max(16), python: VersionSchema }),
|
|
52494
|
+
databases: external_exports.array(DatabasePlanSchema).max(8),
|
|
52495
|
+
/** Agents this project may start that the sandbox did not boot with (its .sandbox-config.json "harness"). */
|
|
52496
|
+
harnesses: external_exports.record(Text(64), HarnessSchema).refine((h) => Object.keys(h).length <= 8, "at most 8 agents"),
|
|
52497
|
+
/** Its hooks were staged in C:\AgentLaunch\projects\<name>\hooks. */
|
|
52498
|
+
hooks: external_exports.boolean()
|
|
52499
|
+
});
|
|
52500
|
+
var ShellChoiceSchema = external_exports.enum(["auto", "pwsh", "powershell", "cmd"]);
|
|
52501
|
+
var GuestRequestSchema = external_exports.discriminatedUnion("kind", [
|
|
52502
|
+
external_exports.strictObject({
|
|
52503
|
+
kind: external_exports.literal("session"),
|
|
52504
|
+
id: IdSchema,
|
|
52505
|
+
harness: Text(64),
|
|
52506
|
+
args: external_exports.array(Text()).max(256),
|
|
52507
|
+
cwd: Text(),
|
|
52508
|
+
project: ProjectSpecSchema.optional(),
|
|
52509
|
+
harnessSpec: HarnessSchema.optional(),
|
|
52510
|
+
/** A plain shell instead of an agent (harness is then "shell" and args are ignored). */
|
|
52511
|
+
shell: ShellChoiceSchema.optional(),
|
|
52512
|
+
createdAt: TimeSchema
|
|
52513
|
+
}),
|
|
52514
|
+
/** End a running session (an agent or a shell) and everything it started. */
|
|
52515
|
+
external_exports.strictObject({ kind: external_exports.literal("close"), id: IdSchema, session: IdSchema, createdAt: TimeSchema }),
|
|
52516
|
+
/** Provision a registered project without starting an agent (sandbox start / exec / db from its folder). */
|
|
52517
|
+
external_exports.strictObject({ kind: external_exports.literal("project"), id: IdSchema, project: ProjectSpecSchema, createdAt: TimeSchema }),
|
|
52518
|
+
external_exports.strictObject({
|
|
52519
|
+
kind: external_exports.literal("exec"),
|
|
52520
|
+
id: IdSchema,
|
|
52521
|
+
command: external_exports.array(Text()).min(1).max(256),
|
|
52522
|
+
cwd: Text(),
|
|
52523
|
+
timeoutSeconds: external_exports.number().positive().max(86400),
|
|
52524
|
+
createdAt: TimeSchema
|
|
52525
|
+
}),
|
|
52526
|
+
/** cwd: only the sessions in this folder or below (one project's); omitted = every session. */
|
|
52527
|
+
external_exports.strictObject({
|
|
52528
|
+
kind: external_exports.literal("restart"),
|
|
52529
|
+
id: IdSchema,
|
|
52530
|
+
harness: Text(64).nullable(),
|
|
52531
|
+
cwd: Text().optional(),
|
|
52532
|
+
createdAt: TimeSchema
|
|
52533
|
+
}),
|
|
52534
|
+
external_exports.strictObject({ kind: external_exports.literal("ping"), id: IdSchema, createdAt: TimeSchema }),
|
|
52535
|
+
/**
|
|
52536
|
+
* The PC's own memory or CPU is running low (the host watchdog): the sandbox slows its heaviest builds down for
|
|
52537
|
+
* `seconds` (it never stops them) so Windows does not have to close it. Repeated while the PC stays low.
|
|
52538
|
+
*/
|
|
52539
|
+
external_exports.strictObject({
|
|
52540
|
+
kind: external_exports.literal("pressure"),
|
|
52541
|
+
id: IdSchema,
|
|
52542
|
+
reason: Text(300),
|
|
52543
|
+
seconds: external_exports.number().int().min(10).max(300),
|
|
52544
|
+
/** "severe": the PC is nearly out of memory; builds go to the governor's last step at once. */
|
|
52545
|
+
level: external_exports.enum(["slow", "severe"]).optional(),
|
|
52546
|
+
/** The disk queue the PC reported (shown in the heartbeat). */
|
|
52547
|
+
diskQueue: external_exports.number().min(0).max(1e5).optional(),
|
|
52548
|
+
createdAt: TimeSchema
|
|
52549
|
+
})
|
|
52550
|
+
]);
|
|
52551
|
+
var PressureStatusSchema = external_exports.object({
|
|
52552
|
+
level: external_exports.enum(["ok", "throttled", "suspending"]),
|
|
52553
|
+
/** The governor's dial, 0 (nothing wrong) to 4 (one compiler at a time). */
|
|
52554
|
+
step: external_exports.number().int().min(0).max(4).optional(),
|
|
52555
|
+
profile: external_exports.enum(["normal", "gentle"]),
|
|
52556
|
+
/** Share of the sandbox's RAM still free and of its commit limit in use (percent). */
|
|
52557
|
+
ramFreePercent: external_exports.number().int().min(0).max(100),
|
|
52558
|
+
commitPercent: external_exports.number().int().min(0).max(100),
|
|
52559
|
+
/** Programs running at lower priority because of it. */
|
|
52560
|
+
lowered: external_exports.number().int().min(0).max(1024),
|
|
52561
|
+
/** Compilers waiting their turn right now (name, process id, seconds until they may go on). */
|
|
52562
|
+
paused: external_exports.array(external_exports.object({ name: Text(64), pid: external_exports.number().int(), seconds: external_exports.number().int().min(0).max(60) })).max(8),
|
|
52563
|
+
/** The CPU share the compilers get together (percent; 100 = no cap). */
|
|
52564
|
+
cpuPercent: external_exports.number().int().min(0).max(100).optional(),
|
|
52565
|
+
/** The PC itself asked for slowing down (its memory or CPU is low). */
|
|
52566
|
+
pcLow: external_exports.boolean(),
|
|
52567
|
+
lastAction: Text(300),
|
|
52568
|
+
updatedAt: TimeSchema
|
|
52569
|
+
});
|
|
52570
|
+
var HeartbeatSchema = external_exports.object({
|
|
52571
|
+
at: TimeSchema,
|
|
52572
|
+
/** Seconds since the sandbox booted. */
|
|
52573
|
+
uptimeS: external_exports.number().int().min(0).max(1e9),
|
|
52574
|
+
ramFreePct: external_exports.number().min(0).max(100),
|
|
52575
|
+
commitPct: external_exports.number().min(0).max(100),
|
|
52576
|
+
/** The governor's dial now, and the step this PC asked for. */
|
|
52577
|
+
step: external_exports.number().int().min(0).max(4),
|
|
52578
|
+
hostStep: external_exports.number().int().min(0).max(4),
|
|
52579
|
+
/** The biggest programs in the agents' job. */
|
|
52580
|
+
topProcesses: external_exports.array(external_exports.object({ name: Text(64), privateMb: external_exports.number().int().min(0).max(1e7) })).max(5),
|
|
52581
|
+
/** The disk queue this PC last reported while asking the sandbox to slow down. */
|
|
52582
|
+
diskQueueReported: external_exports.number().min(0).max(1e5).optional(),
|
|
52583
|
+
phase: external_exports.enum(["ready", "working"])
|
|
52584
|
+
});
|
|
52585
|
+
var GuestResponseSchema = external_exports.object({
|
|
52586
|
+
id: IdSchema,
|
|
52587
|
+
ok: external_exports.boolean(),
|
|
52588
|
+
message: Text(65536),
|
|
52589
|
+
exitCode: external_exports.number().int().optional(),
|
|
52590
|
+
stdout: Text(4 * 1024 * 1024).optional(),
|
|
52591
|
+
stderr: Text(4 * 1024 * 1024).optional(),
|
|
52592
|
+
finishedAt: TimeSchema
|
|
52593
|
+
});
|
|
52594
|
+
var SessionStatusSchema = external_exports.object({
|
|
52595
|
+
id: IdSchema,
|
|
52596
|
+
launchId: Text(64),
|
|
52597
|
+
harness: Text(64),
|
|
52598
|
+
/** A plain shell (harness is then the shell: pwsh, powershell or cmd); absent: an agent. */
|
|
52599
|
+
kind: external_exports.enum(["agent", "shell"]).optional(),
|
|
52600
|
+
cwd: Text(),
|
|
52601
|
+
/** The project it runs in: its hub name, or the launch folder's for the first project. */
|
|
52602
|
+
project: Text(64).optional(),
|
|
52603
|
+
phase: external_exports.enum(["starting", "running", "exited", "failed"]),
|
|
52604
|
+
message: Text(),
|
|
52605
|
+
updatedAt: TimeSchema,
|
|
52606
|
+
startedAt: TimeSchema,
|
|
52607
|
+
/** The agent's own conversation this session is in (Claude: the file name of its .jsonl), once known. */
|
|
52608
|
+
conversation: Text(128).optional(),
|
|
52609
|
+
exitCode: external_exports.number().int().optional(),
|
|
52610
|
+
attach: AttachEndpointSchema.optional()
|
|
52611
|
+
});
|
|
52612
|
+
var ProjectStatusSchema = external_exports.object({
|
|
52613
|
+
name: IdSchema,
|
|
52614
|
+
workspace: Text(1024),
|
|
52615
|
+
phase: external_exports.enum(["provisioning", "ready", "failed"]),
|
|
52616
|
+
message: Text(),
|
|
52617
|
+
updatedAt: TimeSchema,
|
|
52618
|
+
databases: external_exports.array(external_exports.object({ database: Text(32), port: external_exports.number().int() })).max(16),
|
|
52619
|
+
summary: external_exports.array(Text(512)).max(64)
|
|
52620
|
+
});
|
|
52621
|
+
var VpnStatusSchema = external_exports.object({
|
|
52622
|
+
state: external_exports.enum(["off", "starting", "up", "failed"]),
|
|
52623
|
+
message: Text(2e3),
|
|
52624
|
+
killSwitch: external_exports.boolean(),
|
|
52625
|
+
/** A direct connection was tried after the kill switch was applied: false = it still got out. */
|
|
52626
|
+
killSwitchHeld: external_exports.boolean().optional(),
|
|
52627
|
+
/** The server's name, e.g. uk2201.nordvpn.com. */
|
|
52628
|
+
server: Text(128).optional(),
|
|
52629
|
+
/** What the sandbox's traffic looks like from outside, once the tunnel is up. */
|
|
52630
|
+
publicIp: Text(64).optional(),
|
|
52631
|
+
updatedAt: TimeSchema
|
|
52632
|
+
});
|
|
52633
|
+
|
|
52634
|
+
// src/shared/channel/run-dir.ts
|
|
52635
|
+
import fs25 from "node:fs";
|
|
52636
|
+
import path32 from "node:path";
|
|
52637
|
+
function safeId(id) {
|
|
52638
|
+
return IdSchema.parse(id);
|
|
52639
|
+
}
|
|
52640
|
+
function now() {
|
|
52641
|
+
return (/* @__PURE__ */ new Date()).toISOString();
|
|
52642
|
+
}
|
|
52643
|
+
var scan = (schema, dir) => {
|
|
52644
|
+
if (!fs25.existsSync(dir)) return { valid: [], invalid: [] };
|
|
52645
|
+
const valid = [];
|
|
52646
|
+
const invalid = [];
|
|
52647
|
+
for (const name2 of fs25.readdirSync(dir)) {
|
|
52648
|
+
if (name2.endsWith(".tmp")) continue;
|
|
52649
|
+
const message = /^[A-Za-z0-9_-]{1,64}\.json$/.test(name2) ? readValid(schema, path32.join(dir, name2)) : null;
|
|
52650
|
+
if (message) valid.push(message);
|
|
52651
|
+
else invalid.push(path32.join(dir, name2));
|
|
52652
|
+
}
|
|
52653
|
+
return { valid, invalid };
|
|
52654
|
+
};
|
|
52655
|
+
var RunDir = class {
|
|
52656
|
+
root;
|
|
52657
|
+
sandboxFile;
|
|
52658
|
+
vpnFile;
|
|
52659
|
+
/** What the memory governor in the sandbox is doing (pressure.json). */
|
|
52660
|
+
pressureFile;
|
|
52661
|
+
/** The daemon's heartbeat every 10 seconds (heartbeat.json): what the sandbox did last. */
|
|
52662
|
+
heartbeatFile;
|
|
52663
|
+
/** What the sandbox's programs may really use (budget.json): processors, memory, build jobs, link slots. */
|
|
52664
|
+
budgetFile;
|
|
52665
|
+
requestsDir;
|
|
52666
|
+
responsesDir;
|
|
52667
|
+
sessionsDir;
|
|
52668
|
+
projectsDir;
|
|
52669
|
+
/** audit.processes: programs the sandbox started, in small .jsonl files the host takes in and deletes. */
|
|
52670
|
+
auditDir;
|
|
52671
|
+
/**
|
|
52672
|
+
* Each running session's process tree, recorded by its ptyhost (guest only: the daemon ends what a ptyhost that was
|
|
52673
|
+
* itself ended left running).
|
|
52674
|
+
*/
|
|
52675
|
+
treesDir;
|
|
52676
|
+
/** One-shot secrets the host hands over (see secrets.ts). Next to run, not in it: shared-use users can change run. */
|
|
52677
|
+
secretsDir;
|
|
52678
|
+
/** Live folders (live.ts): signed host requests, and the daemon's replies. */
|
|
52679
|
+
liveDir;
|
|
52680
|
+
liveRepliesDir;
|
|
52681
|
+
constructor(dataDir) {
|
|
52682
|
+
this.root = path32.join(dataDir, "run");
|
|
52683
|
+
this.sandboxFile = path32.join(this.root, "sandbox.json");
|
|
52684
|
+
this.vpnFile = path32.join(this.root, "vpn.json");
|
|
52685
|
+
this.pressureFile = path32.join(this.root, "pressure.json");
|
|
52686
|
+
this.heartbeatFile = path32.join(this.root, "heartbeat.json");
|
|
52687
|
+
this.budgetFile = path32.join(this.root, "budget.json");
|
|
52688
|
+
this.requestsDir = path32.join(this.root, "requests");
|
|
52689
|
+
this.responsesDir = path32.join(this.root, "responses");
|
|
52690
|
+
this.sessionsDir = path32.join(this.root, "sessions");
|
|
52691
|
+
this.projectsDir = path32.join(this.root, "projects");
|
|
52692
|
+
this.auditDir = path32.join(this.root, "audit");
|
|
52693
|
+
this.treesDir = path32.join(this.root, "trees");
|
|
52694
|
+
this.secretsDir = path32.join(dataDir, "secrets");
|
|
52695
|
+
this.liveDir = path32.join(this.root, "live");
|
|
52696
|
+
this.liveRepliesDir = path32.join(this.root, "live-replies");
|
|
52697
|
+
}
|
|
52698
|
+
liveFile(id) {
|
|
52699
|
+
return path32.join(this.liveDir, safeId(id) + ".json");
|
|
52700
|
+
}
|
|
52701
|
+
liveReplyFile(id) {
|
|
52702
|
+
return path32.join(this.liveRepliesDir, safeId(id) + ".json");
|
|
52703
|
+
}
|
|
52704
|
+
requestFile(id) {
|
|
52705
|
+
return path32.join(this.requestsDir, safeId(id) + ".json");
|
|
52706
|
+
}
|
|
52707
|
+
responseFile(id) {
|
|
52708
|
+
return path32.join(this.responsesDir, safeId(id) + ".json");
|
|
52709
|
+
}
|
|
52710
|
+
sessionFile(id) {
|
|
52711
|
+
return path32.join(this.sessionsDir, safeId(id) + ".json");
|
|
52712
|
+
}
|
|
52713
|
+
treeFile(id) {
|
|
52714
|
+
return path32.join(this.treesDir, safeId(id) + ".json");
|
|
52715
|
+
}
|
|
52716
|
+
projectFile(name2) {
|
|
52717
|
+
return path32.join(this.projectsDir, safeId(name2) + ".json");
|
|
52718
|
+
}
|
|
52719
|
+
secretFile(id) {
|
|
52720
|
+
return path32.join(this.secretsDir, safeId(id) + ".json");
|
|
52721
|
+
}
|
|
52722
|
+
readSandboxStatus() {
|
|
52723
|
+
return readValid(SandboxStatusSchema, this.sandboxFile);
|
|
52724
|
+
}
|
|
52725
|
+
readVpnStatus() {
|
|
52726
|
+
return readValid(VpnStatusSchema, this.vpnFile);
|
|
52727
|
+
}
|
|
52728
|
+
readPressure() {
|
|
52729
|
+
return readValid(PressureStatusSchema, this.pressureFile);
|
|
52730
|
+
}
|
|
52731
|
+
readHeartbeat() {
|
|
52732
|
+
return readValid(HeartbeatSchema, this.heartbeatFile);
|
|
52733
|
+
}
|
|
52734
|
+
readSession(id) {
|
|
52735
|
+
return readValid(SessionStatusSchema, this.sessionFile(id));
|
|
52736
|
+
}
|
|
52737
|
+
readResponse(id) {
|
|
52738
|
+
return readValid(GuestResponseSchema, this.responseFile(id));
|
|
52739
|
+
}
|
|
52740
|
+
listSessions() {
|
|
52741
|
+
return scan(SessionStatusSchema, this.sessionsDir).valid.sort((a, b) => a.startedAt.localeCompare(b.startedAt));
|
|
52742
|
+
}
|
|
52743
|
+
readProjectStatus(name2) {
|
|
52744
|
+
return readValid(ProjectStatusSchema, this.projectFile(name2));
|
|
52745
|
+
}
|
|
52746
|
+
listProjectStatus() {
|
|
52747
|
+
return scan(ProjectStatusSchema, this.projectsDir).valid.slice(0, 64);
|
|
52748
|
+
}
|
|
52749
|
+
scanRequests() {
|
|
52750
|
+
return scan(GuestRequestSchema, this.requestsDir);
|
|
52751
|
+
}
|
|
52752
|
+
};
|
|
52753
|
+
|
|
52754
|
+
// src/shared/accounts/payloads.ts
|
|
52755
|
+
var KB = 1024;
|
|
52756
|
+
var base643 = (maxBytes) => external_exports.string().min(4).max(maxBytes).regex(/^[A-Za-z0-9+/]+={0,2}$/);
|
|
52757
|
+
var secretText = (max) => external_exports.string().min(1).max(max).regex(/^[^\r\n]+$/);
|
|
52758
|
+
var EpicPayloadSchema = external_exports.strictObject({
|
|
52759
|
+
data: external_exports.string().min(16).max(16384).regex(/^[A-Za-z0-9+/=_-]+$/)
|
|
52760
|
+
});
|
|
52761
|
+
var SteamPayloadSchema = external_exports.strictObject({
|
|
52762
|
+
account: external_exports.string().regex(/^[A-Za-z0-9_]{1,64}$/),
|
|
52763
|
+
configVdf: external_exports.string().min(1).max(256 * KB),
|
|
52764
|
+
sentries: external_exports.array(external_exports.strictObject({ name: external_exports.string().regex(/^ssfn\d{1,40}$/), base64: base643(64 * KB) })).max(4),
|
|
52765
|
+
/** Reserved for the Steam client's own login (very likely not portable between Windows users). */
|
|
52766
|
+
client: base643(256 * KB).optional()
|
|
52767
|
+
});
|
|
52768
|
+
var UnityPayloadSchema = external_exports.strictObject({
|
|
52769
|
+
ulf: external_exports.string().min(1).max(128 * KB).optional(),
|
|
52770
|
+
email: external_exports.string().email().max(256).optional(),
|
|
52771
|
+
password: secretText(1024).optional(),
|
|
52772
|
+
serial: secretText(64).optional()
|
|
52773
|
+
});
|
|
52774
|
+
var CfxPayloadSchema = external_exports.strictObject({
|
|
52775
|
+
licenseKey: external_exports.string().max(256).regex(/^cfxk_[A-Za-z0-9_]+$/),
|
|
52776
|
+
steamWebApiKey: external_exports.string().regex(/^[A-F0-9]{32}$/).optional()
|
|
52777
|
+
});
|
|
52778
|
+
var KeystorePayloadSchema = external_exports.strictObject({
|
|
52779
|
+
fileName: external_exports.string().regex(/^[A-Za-z0-9._-]{1,100}\.(jks|keystore|p12)$/i),
|
|
52780
|
+
jksBase64: base643(256 * KB),
|
|
52781
|
+
storePassword: secretText(1024),
|
|
52782
|
+
keyAlias: secretText(256),
|
|
52783
|
+
keyPassword: secretText(1024)
|
|
52784
|
+
});
|
|
52785
|
+
var PlayPayloadSchema = external_exports.strictObject({
|
|
52786
|
+
serviceAccountJson: external_exports.string().min(2).max(64 * KB).refine((text) => {
|
|
52787
|
+
try {
|
|
52788
|
+
const parsed = JSON.parse(text);
|
|
52789
|
+
return typeof parsed === "object" && parsed !== null && parsed.type === "service_account";
|
|
52790
|
+
} catch {
|
|
52791
|
+
return false;
|
|
52792
|
+
}
|
|
52793
|
+
}, 'not a Google service account key (its "type" is not "service_account")')
|
|
52794
|
+
});
|
|
52795
|
+
var ExpoPayloadSchema = external_exports.strictObject({
|
|
52796
|
+
token: external_exports.string().regex(/^[A-Za-z0-9_-]{8,512}$/)
|
|
52797
|
+
});
|
|
52798
|
+
var AppstorePayloadSchema = external_exports.strictObject({
|
|
52799
|
+
issuerId: external_exports.string().uuid(),
|
|
52800
|
+
keyId: external_exports.string().regex(/^[A-Z0-9]{10}$/),
|
|
52801
|
+
p8: external_exports.string().max(8 * KB).regex(/^-----BEGIN PRIVATE KEY-----\r?\n[A-Za-z0-9+/=\r\n]+-----END PRIVATE KEY-----\s*$/)
|
|
52802
|
+
});
|
|
52803
|
+
var PAYLOADS = {
|
|
52804
|
+
epic: EpicPayloadSchema,
|
|
52805
|
+
steam: SteamPayloadSchema,
|
|
52806
|
+
unity: UnityPayloadSchema,
|
|
52807
|
+
cfx: CfxPayloadSchema,
|
|
52808
|
+
keystore: KeystorePayloadSchema,
|
|
52809
|
+
play: PlayPayloadSchema,
|
|
52810
|
+
expo: ExpoPayloadSchema,
|
|
52811
|
+
appstore: AppstorePayloadSchema
|
|
52812
|
+
};
|
|
52813
|
+
var ServiceAccountsSchema = external_exports.strictObject({
|
|
52814
|
+
epic: EpicPayloadSchema.optional(),
|
|
52815
|
+
steam: SteamPayloadSchema.optional(),
|
|
52816
|
+
unity: UnityPayloadSchema.optional(),
|
|
52817
|
+
cfx: CfxPayloadSchema.optional(),
|
|
52818
|
+
keystore: KeystorePayloadSchema.optional(),
|
|
52819
|
+
play: PlayPayloadSchema.optional(),
|
|
52820
|
+
expo: ExpoPayloadSchema.optional(),
|
|
52821
|
+
appstore: AppstorePayloadSchema.optional()
|
|
52822
|
+
});
|
|
52823
|
+
var LaunchIdSchema = external_exports.string().regex(/^[A-Za-z0-9-]{1,64}$/);
|
|
52824
|
+
function handoffSchema(kind) {
|
|
52825
|
+
return external_exports.strictObject({
|
|
52826
|
+
kind: external_exports.literal(kind),
|
|
52827
|
+
launchId: LaunchIdSchema,
|
|
52828
|
+
payload: PAYLOADS[kind]
|
|
52829
|
+
});
|
|
52830
|
+
}
|
|
52831
|
+
|
|
52832
|
+
// src/shared/github/token.ts
|
|
52833
|
+
var GITHUB_TOKEN_KINDS = ["fine-grained", "classic"];
|
|
52834
|
+
var MIN_LENGTH = 20;
|
|
52835
|
+
var MAX_LENGTH = 255;
|
|
52836
|
+
var PRINTABLE = /^[\x21-\x7e]+$/;
|
|
52837
|
+
var GithubTokenSchema = external_exports.string().min(MIN_LENGTH).max(MAX_LENGTH).regex(PRINTABLE);
|
|
52838
|
+
var OTHER_TOKENS = {
|
|
52839
|
+
gho_: "This is an OAuth token (from gh auth login or gh auth token), which can reach everything your account can.",
|
|
52840
|
+
ghu_: "This is a GitHub App user token, not a personal access token.",
|
|
52841
|
+
ghs_: "This is a GitHub App installation token, not a personal access token.",
|
|
52842
|
+
ghr_: "This is a refresh token, not a personal access token."
|
|
52843
|
+
};
|
|
52844
|
+
var CREATE_FINE_GRAINED = "Create a fine-grained token at https://github.com/settings/personal-access-tokens/new with only the repositories and permissions the agent needs, then run: sandbox github login";
|
|
52845
|
+
function githubTokenKind(token) {
|
|
52846
|
+
if (token.startsWith("github_pat_")) return "fine-grained";
|
|
52847
|
+
if (token.startsWith("ghp_")) return "classic";
|
|
52848
|
+
return null;
|
|
52849
|
+
}
|
|
52850
|
+
function githubTokenProblem(token) {
|
|
52851
|
+
if (!token) return { message: "No token was typed.", fix: CREATE_FINE_GRAINED };
|
|
52852
|
+
if (!PRINTABLE.test(token))
|
|
52853
|
+
return {
|
|
52854
|
+
message: "The token may not contain spaces, line breaks or other special characters.",
|
|
52855
|
+
fix: "Copy the token again from GitHub (only the token itself) and run: sandbox github login"
|
|
52856
|
+
};
|
|
52857
|
+
if (token.length < MIN_LENGTH || token.length > MAX_LENGTH)
|
|
52858
|
+
return {
|
|
52859
|
+
message: "That is not the length of a GitHub token.",
|
|
52860
|
+
fix: "Copy the whole token again from GitHub and run: sandbox github login"
|
|
52861
|
+
};
|
|
52862
|
+
const other = Object.keys(OTHER_TOKENS).find((p) => token.startsWith(p));
|
|
52863
|
+
if (other) return { message: OTHER_TOKENS[other], fix: CREATE_FINE_GRAINED };
|
|
52864
|
+
if (!githubTokenKind(token))
|
|
52865
|
+
return {
|
|
52866
|
+
message: "That is not a GitHub personal access token (they start with github_pat_ or ghp_).",
|
|
52867
|
+
fix: CREATE_FINE_GRAINED
|
|
52868
|
+
};
|
|
52869
|
+
return null;
|
|
52870
|
+
}
|
|
52871
|
+
|
|
52872
|
+
// src/shared/channel/secrets.ts
|
|
52873
|
+
var Environment = external_exports.record(external_exports.string().max(256), external_exports.string().max(65536));
|
|
52874
|
+
var SecretsFileSchema = external_exports.strictObject({
|
|
52875
|
+
/** The agent the sandbox booted with: its harness environment. */
|
|
52876
|
+
harnessEnvironment: Environment.optional(),
|
|
52877
|
+
/** harnessSpec of a session request (an agent the sandbox did not boot with). */
|
|
52878
|
+
harnessSpecEnvironment: Environment.optional(),
|
|
52879
|
+
/** project.harnesses of a request, by agent name. */
|
|
52880
|
+
projectHarnessEnvironment: external_exports.record(external_exports.string().max(64), Environment).optional(),
|
|
52881
|
+
/** The login of this PC's Claude (claudeProfile.copyCredentials): the text of .credentials.json. */
|
|
52882
|
+
claudeCredentials: external_exports.string().max(1024 * 1024).optional(),
|
|
52883
|
+
/** VPN service credentials (vpn.provider's login), only for the launch's own sandbox. */
|
|
52884
|
+
vpn: external_exports.strictObject({ username: external_exports.string().min(1).max(256), password: external_exports.string().min(1).max(1024) }).optional(),
|
|
52885
|
+
/** The checked WireGuard config of vpn.provider wireguard-file (holds the private key). */
|
|
52886
|
+
wireguardConfig: external_exports.string().min(1).max(32 * 1024).optional(),
|
|
52887
|
+
/** The GitHub token you saved for sandboxes (github.shareToken), only with git access "full". */
|
|
52888
|
+
githubToken: GithubTokenSchema.optional(),
|
|
52889
|
+
/** Your saved service sign-ins (accounts.<kind>.share): each goes to its tool's own files at boot. */
|
|
52890
|
+
serviceAccounts: ServiceAccountsSchema.optional(),
|
|
52891
|
+
/** projects.liveFolders "hcs": this launch's key for signed live-folder requests (live.ts). */
|
|
52892
|
+
liveFoldersKey: external_exports.string().regex(/^[0-9a-f]{64}$/).optional()
|
|
52893
|
+
});
|
|
52894
|
+
|
|
52895
|
+
// src/shared/channel/live.ts
|
|
52896
|
+
import crypto3 from "node:crypto";
|
|
52897
|
+
var LIVE_SHARE_NAME = /^agentsbx-[0-9a-f]{8,32}$/;
|
|
52898
|
+
var LIVE_FOLDER_NAME = /^[a-z0-9][a-z0-9_-]{0,63}$/;
|
|
52899
|
+
var LIVE_KEY = /^[0-9a-f]{64}$/;
|
|
52900
|
+
var LIVE_TOKEN = /^[0-9a-f]{32}$/;
|
|
52901
|
+
var LIVE_MAX_AGE_MS = 2 * 60 * 1e3;
|
|
52902
|
+
var MAX_PAYLOAD = 4096;
|
|
52903
|
+
var LivePayloadSchema = external_exports.strictObject({
|
|
52904
|
+
v: external_exports.literal(1),
|
|
52905
|
+
/** canary: show the share, read token.txt, hide it again and reply with the token; map / unmap: the real folder. */
|
|
52906
|
+
kind: external_exports.enum(["canary", "map", "unmap"]),
|
|
52907
|
+
id: IdSchema,
|
|
52908
|
+
launchId: external_exports.string().min(1).max(64),
|
|
52909
|
+
share: external_exports.string().regex(LIVE_SHARE_NAME).optional(),
|
|
52910
|
+
folder: external_exports.string().regex(LIVE_FOLDER_NAME),
|
|
52911
|
+
readOnly: external_exports.boolean(),
|
|
52912
|
+
createdAt: external_exports.string().max(64)
|
|
52913
|
+
});
|
|
52914
|
+
var LiveEnvelopeSchema = external_exports.strictObject({
|
|
52915
|
+
payload: external_exports.string().min(2).max(MAX_PAYLOAD),
|
|
52916
|
+
mac: external_exports.string().regex(/^[0-9a-f]{64}$/)
|
|
52917
|
+
});
|
|
52918
|
+
var LiveReplySchema = external_exports.strictObject({
|
|
52919
|
+
id: IdSchema,
|
|
52920
|
+
ok: external_exports.boolean(),
|
|
52921
|
+
message: external_exports.string().max(1e3),
|
|
52922
|
+
token: external_exports.string().regex(LIVE_TOKEN).optional(),
|
|
52923
|
+
finishedAt: external_exports.string().max(64)
|
|
52924
|
+
});
|
|
52925
|
+
function mac3(key3, payload) {
|
|
52926
|
+
return crypto3.createHmac("sha256", Buffer.from(key3, "hex")).update(payload, "utf8").digest("hex");
|
|
52927
|
+
}
|
|
52928
|
+
function signLive(key3, payload) {
|
|
52929
|
+
if (!LIVE_KEY.test(key3)) throw new Error("not a live-folder key");
|
|
52930
|
+
const text = JSON.stringify(LivePayloadSchema.parse(payload));
|
|
52931
|
+
return { payload: text, mac: mac3(key3, text) };
|
|
52932
|
+
}
|
|
52933
|
+
function newShareName() {
|
|
52934
|
+
return "agentsbx-" + crypto3.randomBytes(8).toString("hex");
|
|
52935
|
+
}
|
|
52936
|
+
function newLiveKey() {
|
|
52937
|
+
return crypto3.randomBytes(32).toString("hex");
|
|
52938
|
+
}
|
|
52939
|
+
|
|
52940
|
+
// src/host/life/last-end.ts
|
|
52941
|
+
function readHeartbeat(dataPath) {
|
|
52942
|
+
try {
|
|
52943
|
+
const root = hostDataRoot(dataPath);
|
|
52944
|
+
const file2 = new RunDir(root).heartbeatFile;
|
|
52945
|
+
assertNoLinks(root, file2);
|
|
52946
|
+
return readValid(HeartbeatSchema, file2);
|
|
52947
|
+
} catch {
|
|
52948
|
+
return null;
|
|
52949
|
+
}
|
|
52950
|
+
}
|
|
52951
|
+
var clock = (iso) => {
|
|
52952
|
+
const at = new Date(iso);
|
|
52953
|
+
return Number.isNaN(at.getTime()) ? iso : at.toLocaleTimeString(void 0, { hour: "2-digit", minute: "2-digit", hour12: false });
|
|
52954
|
+
};
|
|
52955
|
+
function heartbeatLine(hb) {
|
|
52956
|
+
const top = hb.topProcesses.map((p) => safeText(p.name) + " " + p.privateMb + " MB").join(", ");
|
|
52957
|
+
return "last seen inside at " + clock(hb.at) + ": up " + Math.round(hb.uptimeS / 60) + " min, " + hb.ramFreePct + "% RAM free, " + hb.commitPct + "% commit used, builds at step " + hb.step + (hb.hostStep > 0 ? " (this PC asked for step " + hb.hostStep + ")" : "") + (hb.diskQueueReported !== void 0 ? ", this PC disk queue " + hb.diskQueueReported : "") + (top ? "; biggest: " + top : "");
|
|
52958
|
+
}
|
|
52959
|
+
function guessReason(hb) {
|
|
52960
|
+
if (hb && hb.diskQueueReported !== void 0 && hb.hostStep > 0)
|
|
52961
|
+
return "this PC's disks were overloaded (queue " + hb.diskQueueReported + ")";
|
|
52962
|
+
if (hb && (hb.ramFreePct < 8 || hb.commitPct > 92)) return "the sandbox was running out of memory";
|
|
52963
|
+
return "Windows stopped it (no reason was logged)";
|
|
52964
|
+
}
|
|
52965
|
+
function lastEndOf(life, heartbeat, running) {
|
|
52966
|
+
if (!life || running) return null;
|
|
52967
|
+
if (life.ended && !life.ended.cause) return null;
|
|
52968
|
+
const hb = heartbeat && Date.parse(heartbeat.at) >= Date.parse(life.started) - 6e4 ? heartbeat : null;
|
|
52969
|
+
const at = life.ended?.at ?? life.lastSeen;
|
|
52970
|
+
const why = life.ended?.cause ? safeText(life.ended.cause.text) : guessReason(hb);
|
|
52971
|
+
return { at, text: "ended unexpectedly " + clock(at) + ": " + why };
|
|
52972
|
+
}
|
|
52973
|
+
function readLastEnd(lifeRead, dataPath, running) {
|
|
52974
|
+
const life = lifeRead();
|
|
52975
|
+
return lastEndOf(life, life && !running ? readHeartbeat(dataPath) : null, running);
|
|
52976
|
+
}
|
|
52977
|
+
|
|
52448
52978
|
// src/host/life/crash-report.ts
|
|
52449
52979
|
var KEEP_REPORTS = 20;
|
|
52450
52980
|
var MEMORY_TAIL_BYTES = 16 * 1024;
|
|
52451
52981
|
var FREEZE_GAP_MS = 5 * 60 * 1e3;
|
|
52452
52982
|
function crashDir(storageRoot) {
|
|
52453
|
-
return
|
|
52983
|
+
return path33.join(storageRoot, "logs", "crashes");
|
|
52454
52984
|
}
|
|
52455
52985
|
function freezeLine(cause, lastSeen) {
|
|
52456
52986
|
if (cause.kind !== "crash") return null;
|
|
@@ -52493,11 +53023,11 @@ function findDumps(dirs, since) {
|
|
|
52493
53023
|
const found = [];
|
|
52494
53024
|
for (const dir of dirs) {
|
|
52495
53025
|
try {
|
|
52496
|
-
for (const name2 of
|
|
53026
|
+
for (const name2 of fs26.readdirSync(dir)) {
|
|
52497
53027
|
if (!/\.dmp$/i.test(name2)) continue;
|
|
52498
|
-
const stat =
|
|
53028
|
+
const stat = fs26.lstatSync(path33.join(dir, name2));
|
|
52499
53029
|
if (stat.isFile() && stat.mtime >= since)
|
|
52500
|
-
found.push({ name:
|
|
53030
|
+
found.push({ name: path33.join(dir, name2), at: stat.mtime.toISOString() });
|
|
52501
53031
|
}
|
|
52502
53032
|
} catch {
|
|
52503
53033
|
}
|
|
@@ -52506,30 +53036,30 @@ function findDumps(dirs, since) {
|
|
|
52506
53036
|
}
|
|
52507
53037
|
function launchHadVgpu(storageRoot, lastSeen) {
|
|
52508
53038
|
try {
|
|
52509
|
-
const root =
|
|
53039
|
+
const root = path33.join(storageRoot, "launches");
|
|
52510
53040
|
const limit = Date.parse(lastSeen) + 60 * 1e3;
|
|
52511
|
-
const newest =
|
|
53041
|
+
const newest = fs26.readdirSync(root).map((name2) => ({ name: name2, at: fs26.lstatSync(path33.join(root, name2)).mtimeMs })).filter((l) => l.at <= limit).sort((a, b) => b.at - a.at)[0];
|
|
52512
53042
|
if (!newest) return false;
|
|
52513
|
-
const dir =
|
|
52514
|
-
const wsb =
|
|
52515
|
-
return wsb ? /<VGpu>\s*Enable\s*<\/VGpu>/i.test(
|
|
53043
|
+
const dir = path33.join(root, newest.name);
|
|
53044
|
+
const wsb = fs26.readdirSync(dir).find((f) => f.endsWith(".wsb"));
|
|
53045
|
+
return wsb ? /<VGpu>\s*Enable\s*<\/VGpu>/i.test(fs26.readFileSync(path33.join(dir, wsb), "utf8").slice(0, 65536)) : false;
|
|
52516
53046
|
} catch {
|
|
52517
53047
|
return false;
|
|
52518
53048
|
}
|
|
52519
53049
|
}
|
|
52520
53050
|
function hostMemoryTail(storageRoot, dataPath) {
|
|
52521
53051
|
try {
|
|
52522
|
-
const file2 =
|
|
52523
|
-
const stat =
|
|
53052
|
+
const file2 = path33.join(storageRoot, "logs", "host-memory-" + path33.basename(dataPath) + ".log");
|
|
53053
|
+
const stat = fs26.lstatSync(file2);
|
|
52524
53054
|
if (!stat.isFile()) return "";
|
|
52525
53055
|
const length2 = Math.min(stat.size, MEMORY_TAIL_BYTES);
|
|
52526
|
-
const fd =
|
|
53056
|
+
const fd = fs26.openSync(file2, "r");
|
|
52527
53057
|
try {
|
|
52528
53058
|
const buffer = Buffer.alloc(length2);
|
|
52529
|
-
|
|
53059
|
+
fs26.readSync(fd, buffer, 0, length2, stat.size - length2);
|
|
52530
53060
|
return buffer.toString("utf8");
|
|
52531
53061
|
} finally {
|
|
52532
|
-
|
|
53062
|
+
fs26.closeSync(fd);
|
|
52533
53063
|
}
|
|
52534
53064
|
} catch {
|
|
52535
53065
|
return "";
|
|
@@ -52571,6 +53101,7 @@ function crashReportText(cause, events, memoryLog, lastSeen, extra = {}) {
|
|
|
52571
53101
|
...hostLines.length ? hostLines : [" (none: the background sync was not running)"],
|
|
52572
53102
|
...extra.hostMemoryNow ? [" after the restart: " + extra.hostMemoryNow] : [],
|
|
52573
53103
|
"",
|
|
53104
|
+
...extra.lastInside ? [safeText(extra.lastInside), ""] : [],
|
|
52574
53105
|
"The sandbox's memory log, last lines (C:\\AgentData\\logs\\memory-*.log inside it):",
|
|
52575
53106
|
...memoryLines,
|
|
52576
53107
|
""
|
|
@@ -52579,10 +53110,10 @@ function crashReportText(cause, events, memoryLog, lastSeen, extra = {}) {
|
|
|
52579
53110
|
function newestCrashReport(storageRoot) {
|
|
52580
53111
|
try {
|
|
52581
53112
|
const dir = crashDir(storageRoot);
|
|
52582
|
-
const newest =
|
|
53113
|
+
const newest = fs26.readdirSync(dir).filter((f) => /^crash-.*\.txt$/.test(f)).sort().pop();
|
|
52583
53114
|
if (!newest) return null;
|
|
52584
|
-
const file2 =
|
|
52585
|
-
return { file: file2, text: safeText(
|
|
53115
|
+
const file2 = path33.join(dir, newest);
|
|
53116
|
+
return { file: file2, text: safeText(fs26.readFileSync(file2, "utf8").slice(0, 64 * 1024)) };
|
|
52586
53117
|
} catch {
|
|
52587
53118
|
return null;
|
|
52588
53119
|
}
|
|
@@ -52591,19 +53122,19 @@ function memoryLogTail(dataPath) {
|
|
|
52591
53122
|
try {
|
|
52592
53123
|
const logs = hostDataFolder(dataPath, "logs");
|
|
52593
53124
|
assertNoLinks(hostDataRoot(dataPath), logs);
|
|
52594
|
-
const newest =
|
|
53125
|
+
const newest = fs26.readdirSync(logs).filter((f) => /^memory-\d{8}-\d{6}\.log$/.test(f)).sort().pop();
|
|
52595
53126
|
if (!newest) return "";
|
|
52596
|
-
const file2 =
|
|
52597
|
-
if (!
|
|
52598
|
-
const size =
|
|
52599
|
-
const fd =
|
|
53127
|
+
const file2 = path33.join(logs, newest);
|
|
53128
|
+
if (!fs26.lstatSync(file2).isFile()) return "";
|
|
53129
|
+
const size = fs26.statSync(file2).size;
|
|
53130
|
+
const fd = fs26.openSync(file2, "r");
|
|
52600
53131
|
try {
|
|
52601
53132
|
const length2 = Math.min(size, MEMORY_TAIL_BYTES);
|
|
52602
53133
|
const buffer = Buffer.alloc(length2);
|
|
52603
|
-
|
|
53134
|
+
fs26.readSync(fd, buffer, 0, length2, size - length2);
|
|
52604
53135
|
return buffer.toString("utf8");
|
|
52605
53136
|
} finally {
|
|
52606
|
-
|
|
53137
|
+
fs26.closeSync(fd);
|
|
52607
53138
|
}
|
|
52608
53139
|
} catch {
|
|
52609
53140
|
return "";
|
|
@@ -52614,22 +53145,24 @@ function saveCrashReport(storageRoot, dataPath, lastSeen) {
|
|
|
52614
53145
|
const cause = explainStop(events);
|
|
52615
53146
|
if (!cause || !["sandboxCrash", "crash", "memory"].includes(cause.kind)) return null;
|
|
52616
53147
|
const dir = crashDir(storageRoot);
|
|
52617
|
-
|
|
52618
|
-
const file2 =
|
|
53148
|
+
fs26.mkdirSync(dir, { recursive: true });
|
|
53149
|
+
const file2 = path33.join(dir, "crash-" + cause.at.replace(/[:.]/g, "-") + ".txt");
|
|
52619
53150
|
const since = new Date(Date.parse(lastSeen) - 10 * 60 * 1e3);
|
|
52620
53151
|
const windir = process.env.SystemRoot ?? "C:\\Windows";
|
|
52621
53152
|
const memoryNow = readHostMemory(storageRoot);
|
|
52622
|
-
|
|
53153
|
+
const heartbeat = readHeartbeat(dataPath);
|
|
53154
|
+
fs26.writeFileSync(
|
|
52623
53155
|
file2,
|
|
52624
53156
|
crashReportText(cause, events, memoryLogTail(dataPath), lastSeen, {
|
|
52625
53157
|
hostMemoryLog: hostMemoryTail(storageRoot, dataPath),
|
|
52626
53158
|
vgpu: launchHadVgpu(storageRoot, lastSeen),
|
|
52627
|
-
|
|
53159
|
+
...heartbeat ? { lastInside: heartbeatLine(heartbeat) } : {},
|
|
53160
|
+
dumps: findDumps([path33.join(windir, "Minidump"), windir], since),
|
|
52628
53161
|
...memoryNow ? { hostMemoryNow: hostMemoryLine(memoryNow) } : {}
|
|
52629
53162
|
})
|
|
52630
53163
|
);
|
|
52631
|
-
for (const old of
|
|
52632
|
-
|
|
53164
|
+
for (const old of fs26.readdirSync(dir).filter((f) => f.startsWith("crash-")).sort().slice(0, -KEEP_REPORTS))
|
|
53165
|
+
fs26.rmSync(path33.join(dir, old), { force: true });
|
|
52633
53166
|
return file2;
|
|
52634
53167
|
}
|
|
52635
53168
|
|
|
@@ -52728,25 +53261,25 @@ function updateRestartPending() {
|
|
|
52728
53261
|
var UPDATE_RESTART_WARNING = "Windows is waiting to restart for updates and may do so on its own (often overnight), which closes the sandbox. Restart first, or pause updates: Settings > Windows Update > Pause updates.";
|
|
52729
53262
|
|
|
52730
53263
|
// src/host/life/session-ledger.ts
|
|
52731
|
-
import
|
|
53264
|
+
import path34 from "node:path";
|
|
52732
53265
|
var MAX_SESSIONS = 50;
|
|
52733
53266
|
var MAX_TEXT = 1024;
|
|
52734
53267
|
var AUTO_RESTARTS = 3;
|
|
52735
53268
|
var AUTO_RESTART_WINDOW_MS = 6 * 60 * 60 * 1e3;
|
|
52736
|
-
var
|
|
53269
|
+
var Text2 = external_exports.string().max(MAX_TEXT);
|
|
52737
53270
|
var LedgerSchema = external_exports.object({
|
|
52738
53271
|
savedAt: external_exports.string().max(40),
|
|
52739
53272
|
launch: external_exports.object({
|
|
52740
|
-
primary:
|
|
52741
|
-
harness:
|
|
52742
|
-
projects: external_exports.array(
|
|
53273
|
+
primary: Text2,
|
|
53274
|
+
harness: Text2.nullable(),
|
|
53275
|
+
projects: external_exports.array(Text2).max(MAX_SESSIONS),
|
|
52743
53276
|
headless: external_exports.boolean(),
|
|
52744
|
-
configPath:
|
|
53277
|
+
configPath: Text2.nullable()
|
|
52745
53278
|
}),
|
|
52746
53279
|
sessions: external_exports.array(
|
|
52747
53280
|
external_exports.object({
|
|
52748
|
-
harness:
|
|
52749
|
-
project:
|
|
53281
|
+
harness: Text2,
|
|
53282
|
+
project: Text2,
|
|
52750
53283
|
conversation: external_exports.string().max(128).optional(),
|
|
52751
53284
|
startedAt: external_exports.string().max(40)
|
|
52752
53285
|
})
|
|
@@ -52778,7 +53311,7 @@ function recent(restarts, now2) {
|
|
|
52778
53311
|
});
|
|
52779
53312
|
}
|
|
52780
53313
|
function describeLast(sessions) {
|
|
52781
|
-
const names = sessions.map((s) => s.harness + " in " + (
|
|
53314
|
+
const names = sessions.map((s) => s.harness + " in " + (path34.win32.basename(s.project) || s.project));
|
|
52782
53315
|
return names.join(", ") + " (" + sessions.length + " session" + (sessions.length === 1 ? "" : "s") + ")";
|
|
52783
53316
|
}
|
|
52784
53317
|
var SessionLedger = class {
|
|
@@ -52862,8 +53395,8 @@ var SessionLedger = class {
|
|
|
52862
53395
|
var key = (s) => [s.harness, s.project.toLowerCase(), s.startedAt].join("|");
|
|
52863
53396
|
|
|
52864
53397
|
// src/host/life/watchdog.ts
|
|
52865
|
-
import
|
|
52866
|
-
import
|
|
53398
|
+
import fs27 from "node:fs";
|
|
53399
|
+
import path35 from "node:path";
|
|
52867
53400
|
|
|
52868
53401
|
// src/shared/memory/policy.ts
|
|
52869
53402
|
var HOST_THRESHOLDS = {
|
|
@@ -52895,6 +53428,7 @@ function effectiveCpuLimit(cpuLimit, profile) {
|
|
|
52895
53428
|
if (profile === "gentle") return Math.min(gentleCpuLimit(cpuLimit), cpuLimit === "none" ? 100 : cpuLimit);
|
|
52896
53429
|
return cpuLimit === "none" ? null : cpuLimit;
|
|
52897
53430
|
}
|
|
53431
|
+
var LOCAL_TARGET_MIN_BYTES = 25 * 1024 ** 3;
|
|
52898
53432
|
|
|
52899
53433
|
// src/host/life/watchdog.ts
|
|
52900
53434
|
var WATCHDOG_START = {
|
|
@@ -52903,6 +53437,7 @@ var WATCHDOG_START = {
|
|
|
52903
53437
|
calm: 0,
|
|
52904
53438
|
cpuHighSeconds: 0,
|
|
52905
53439
|
diskHighSeconds: 0,
|
|
53440
|
+
diskSevereSeconds: 0,
|
|
52906
53441
|
lastSentAt: 0
|
|
52907
53442
|
};
|
|
52908
53443
|
var RESEND_MS = 3e4;
|
|
@@ -52930,8 +53465,10 @@ function watchdogStep(prev, input2) {
|
|
|
52930
53465
|
reason = "this PC's CPU has been above " + t.cpuPercent + "% for " + cpuHigh + " seconds";
|
|
52931
53466
|
const diskQueue = input2.memory?.diskQueue;
|
|
52932
53467
|
const diskHigh = diskQueue !== void 0 && diskQueue >= t.diskQueue ? prev.diskHighSeconds + input2.intervalSeconds : 0;
|
|
53468
|
+
const diskDouble = diskQueue !== void 0 && diskQueue >= 2 * t.diskQueue ? prev.diskSevereSeconds + input2.intervalSeconds : 0;
|
|
53469
|
+
const diskSevere = diskDouble >= t.diskSeconds || diskHigh >= 3 * t.diskSeconds;
|
|
52933
53470
|
if (!reason && diskHigh >= t.diskSeconds)
|
|
52934
|
-
reason = "this PC's disks have had a queue of " + diskQueue + " for " + diskHigh + " seconds";
|
|
53471
|
+
reason = diskSevere ? "this PC's disks are overloaded (queue " + diskQueue + " for " + diskHigh + " seconds)" : "this PC's disks have had a queue of " + diskQueue + " for " + diskHigh + " seconds";
|
|
52935
53472
|
const calmNow = input2.memory ? lowReason(input2.memory, input2.reserveHostMb, t, 1.25) === "" : true;
|
|
52936
53473
|
let low = prev.low;
|
|
52937
53474
|
let calm = prev.calm;
|
|
@@ -52945,7 +53482,7 @@ function watchdogStep(prev, input2) {
|
|
|
52945
53482
|
calm = 0;
|
|
52946
53483
|
}
|
|
52947
53484
|
}
|
|
52948
|
-
const severe = low && input2.memory !== null && severeHost(input2.memory);
|
|
53485
|
+
const severe = low && (input2.memory !== null && severeHost(input2.memory) || diskSevere);
|
|
52949
53486
|
const send = low && (input2.nowMs - prev.lastSentAt >= RESEND_MS || severe && !prev.severe);
|
|
52950
53487
|
return {
|
|
52951
53488
|
state: {
|
|
@@ -52954,6 +53491,7 @@ function watchdogStep(prev, input2) {
|
|
|
52954
53491
|
calm,
|
|
52955
53492
|
cpuHighSeconds: cpuHigh,
|
|
52956
53493
|
diskHighSeconds: diskHigh,
|
|
53494
|
+
diskSevereSeconds: diskDouble,
|
|
52957
53495
|
lastSentAt: send ? input2.nowMs : prev.lastSentAt
|
|
52958
53496
|
},
|
|
52959
53497
|
send,
|
|
@@ -52978,12 +53516,12 @@ function cpuPercentBetween(a, b) {
|
|
|
52978
53516
|
var MAX_LOG_BYTES = 512 * 1024;
|
|
52979
53517
|
function appendHostMemory(file2, memory, cpuPercent) {
|
|
52980
53518
|
try {
|
|
52981
|
-
|
|
53519
|
+
fs27.mkdirSync(path35.dirname(file2), { recursive: true });
|
|
52982
53520
|
try {
|
|
52983
|
-
if (
|
|
53521
|
+
if (fs27.statSync(file2).size > MAX_LOG_BYTES) fs27.renameSync(file2, file2 + ".old");
|
|
52984
53522
|
} catch {
|
|
52985
53523
|
}
|
|
52986
|
-
|
|
53524
|
+
fs27.appendFileSync(file2, hostMemoryLine(memory) + (cpuPercent === null ? "" : " | CPU " + cpuPercent + "%") + "\n");
|
|
52987
53525
|
} catch {
|
|
52988
53526
|
}
|
|
52989
53527
|
}
|
|
@@ -52992,16 +53530,16 @@ var LoadSettingsSchema = external_exports.object({
|
|
|
52992
53530
|
reserveHostMb: external_exports.number().int().nonnegative()
|
|
52993
53531
|
});
|
|
52994
53532
|
function loadFile(storageRoot, dataPath) {
|
|
52995
|
-
return
|
|
53533
|
+
return path35.join(storageRoot, "run", "load-" + path35.basename(dataPath) + ".json");
|
|
52996
53534
|
}
|
|
52997
53535
|
function writeLoadSettings(storageRoot, dataPath, settings) {
|
|
52998
53536
|
const file2 = loadFile(storageRoot, dataPath);
|
|
52999
|
-
|
|
53000
|
-
|
|
53537
|
+
fs27.mkdirSync(path35.dirname(file2), { recursive: true });
|
|
53538
|
+
fs27.writeFileSync(file2, JSON.stringify(settings));
|
|
53001
53539
|
}
|
|
53002
53540
|
function readLoadSettings(storageRoot, dataPath) {
|
|
53003
53541
|
try {
|
|
53004
|
-
return LoadSettingsSchema.parse(JSON.parse(
|
|
53542
|
+
return LoadSettingsSchema.parse(JSON.parse(fs27.readFileSync(loadFile(storageRoot, dataPath), "utf8")));
|
|
53005
53543
|
} catch {
|
|
53006
53544
|
return { profile: "normal", reserveHostMb: 8192 };
|
|
53007
53545
|
}
|
|
@@ -53032,7 +53570,7 @@ function doctorRows(config3, harnessName, storageRoot, configPath) {
|
|
|
53032
53570
|
["Host nvm versions", probe.nvmDir ?? "(no NVM_HOME; versions will be downloaded)"],
|
|
53033
53571
|
[
|
|
53034
53572
|
"Host " + harnessName,
|
|
53035
|
-
probe.harnessExe ?
|
|
53573
|
+
probe.harnessExe ? path36.join(probe.harnessExe.dir, probe.harnessExe.file) : "(none; will npm install)"
|
|
53036
53574
|
],
|
|
53037
53575
|
["Host Git for Windows", probe.gitDir ?? "(none with bash; PortableGit will be downloaded)"],
|
|
53038
53576
|
["Storage root", storageRoot + (dirExists(storageRoot) ? "" : " (will be created)")]
|
|
@@ -53040,10 +53578,10 @@ function doctorRows(config3, harnessName, storageRoot, configPath) {
|
|
|
53040
53578
|
}
|
|
53041
53579
|
|
|
53042
53580
|
// src/host/sandbox/window.ts
|
|
53043
|
-
import
|
|
53581
|
+
import path37 from "node:path";
|
|
53044
53582
|
import { spawn as spawn7, spawnSync as spawnSync11 } from "node:child_process";
|
|
53045
53583
|
function stateFile(storageRoot) {
|
|
53046
|
-
return
|
|
53584
|
+
return path37.join(storageRoot, "run", "hidden-windows.json");
|
|
53047
53585
|
}
|
|
53048
53586
|
function run(file2, args) {
|
|
53049
53587
|
return new Promise((resolve, reject) => {
|
|
@@ -53081,19 +53619,19 @@ function showSandboxWindow(storageRoot) {
|
|
|
53081
53619
|
}
|
|
53082
53620
|
|
|
53083
53621
|
// src/host/sandbox/wsb-cli.ts
|
|
53084
|
-
import
|
|
53085
|
-
import
|
|
53622
|
+
import fs28 from "node:fs";
|
|
53623
|
+
import path38 from "node:path";
|
|
53086
53624
|
import { spawnSync as spawnSync12 } from "node:child_process";
|
|
53087
53625
|
var GUID = /\b[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}\b/i;
|
|
53088
53626
|
var MAX_OUTPUT = 1024 * 1024;
|
|
53089
53627
|
function wsbCandidates(env = process.env) {
|
|
53090
53628
|
return [
|
|
53091
|
-
env.LOCALAPPDATA ?
|
|
53092
|
-
|
|
53629
|
+
env.LOCALAPPDATA ? path38.join(env.LOCALAPPDATA, "Microsoft", "WindowsApps", "wsb.exe") : "",
|
|
53630
|
+
path38.join(env.SystemRoot ?? env.WINDIR ?? "C:\\Windows", "System32", "wsb.exe")
|
|
53093
53631
|
].filter(Boolean);
|
|
53094
53632
|
}
|
|
53095
|
-
function findWsb(env = process.env, exists2 =
|
|
53096
|
-
return process.platform === "win32" || exists2 !==
|
|
53633
|
+
function findWsb(env = process.env, exists2 = fs28.existsSync) {
|
|
53634
|
+
return process.platform === "win32" || exists2 !== fs28.existsSync ? wsbCandidates(env).find((p) => exists2(p)) ?? null : null;
|
|
53097
53635
|
}
|
|
53098
53636
|
function parseVersion(out) {
|
|
53099
53637
|
return /\d+\.\d+(?:\.\d+){0,2}/.exec(out)?.[0] ?? null;
|
|
@@ -53177,17 +53715,17 @@ function shareFolder(wsb, id, hostPath, sandboxPath, readOnly = false) {
|
|
|
53177
53715
|
}
|
|
53178
53716
|
|
|
53179
53717
|
// src/host/sharing/machine-paths.ts
|
|
53180
|
-
import
|
|
53718
|
+
import path39 from "node:path";
|
|
53181
53719
|
function usersDir(env = process.env) {
|
|
53182
|
-
return
|
|
53720
|
+
return path39.join(machineRoot(env), "users");
|
|
53183
53721
|
}
|
|
53184
53722
|
function activeDir(env = process.env) {
|
|
53185
|
-
return
|
|
53723
|
+
return path39.join(machineRoot(env), "active");
|
|
53186
53724
|
}
|
|
53187
53725
|
|
|
53188
53726
|
// src/host/sharing/active-record.ts
|
|
53189
|
-
import
|
|
53190
|
-
import
|
|
53727
|
+
import fs29 from "node:fs";
|
|
53728
|
+
import path40 from "node:path";
|
|
53191
53729
|
var SID = /^S-1-[0-9-]{3,180}$/;
|
|
53192
53730
|
var TEXT = external_exports.string().max(1024);
|
|
53193
53731
|
var MAX_RECORD_BYTES = 64 * 1024;
|
|
@@ -53215,17 +53753,17 @@ var ActiveRecords = class {
|
|
|
53215
53753
|
readAll() {
|
|
53216
53754
|
let names;
|
|
53217
53755
|
try {
|
|
53218
|
-
names =
|
|
53756
|
+
names = fs29.readdirSync(this.dir);
|
|
53219
53757
|
} catch {
|
|
53220
53758
|
return [];
|
|
53221
53759
|
}
|
|
53222
53760
|
const records2 = [];
|
|
53223
53761
|
for (const name2 of names) {
|
|
53224
53762
|
if (!name2.toLowerCase().endsWith(".json")) continue;
|
|
53225
|
-
const file2 =
|
|
53763
|
+
const file2 = path40.join(this.dir, name2);
|
|
53226
53764
|
let stat;
|
|
53227
53765
|
try {
|
|
53228
|
-
stat =
|
|
53766
|
+
stat = fs29.lstatSync(file2);
|
|
53229
53767
|
} catch {
|
|
53230
53768
|
continue;
|
|
53231
53769
|
}
|
|
@@ -53241,7 +53779,7 @@ var ActiveRecords = class {
|
|
|
53241
53779
|
}
|
|
53242
53780
|
fileOf(sid) {
|
|
53243
53781
|
if (!SID.test(sid)) throw new Error("Not a Windows SID: " + sid);
|
|
53244
|
-
return
|
|
53782
|
+
return path40.join(this.dir, sid + ".json");
|
|
53245
53783
|
}
|
|
53246
53784
|
};
|
|
53247
53785
|
|
|
@@ -53328,13 +53866,13 @@ function runningOwner(records2 = new ActiveRecords(activeDir())) {
|
|
|
53328
53866
|
}
|
|
53329
53867
|
|
|
53330
53868
|
// src/host/sharing/borrow.ts
|
|
53331
|
-
import
|
|
53869
|
+
import path41 from "node:path";
|
|
53332
53870
|
function sharedDataPath(record2, machine3 = machineRoot()) {
|
|
53333
|
-
const users =
|
|
53334
|
-
const dataPath =
|
|
53335
|
-
const parts =
|
|
53871
|
+
const users = path41.join(machine3, "users");
|
|
53872
|
+
const dataPath = path41.resolve(record2.dataPath);
|
|
53873
|
+
const parts = path41.relative(users, dataPath).split(path41.sep);
|
|
53336
53874
|
const inside = parts.length === 3 && parts[1].toLowerCase() === "data" && parts.every((p) => p && p !== "..");
|
|
53337
|
-
if (!inside ||
|
|
53875
|
+
if (!inside || path41.isAbsolute(path41.relative(users, dataPath)))
|
|
53338
53876
|
fail(
|
|
53339
53877
|
"The record of " + ownerName(record2.user) + "'s sandbox points outside the shared folder, so it is not used.",
|
|
53340
53878
|
"Ask them to restart their sandbox."
|
|
@@ -53347,475 +53885,6 @@ function sharedDataPath(record2, machine3 = machineRoot()) {
|
|
|
53347
53885
|
import fs33 from "node:fs";
|
|
53348
53886
|
import path49 from "node:path";
|
|
53349
53887
|
|
|
53350
|
-
// src/shared/channel/schema.ts
|
|
53351
|
-
var IdSchema = external_exports.string().regex(/^[A-Za-z0-9_-]{1,64}$/, "invalid id");
|
|
53352
|
-
var Text2 = (max = 4096) => external_exports.string().max(max);
|
|
53353
|
-
var TimeSchema = Text2(64);
|
|
53354
|
-
var AttachEndpointSchema = external_exports.strictObject({
|
|
53355
|
-
addresses: external_exports.array(Text2(64)).max(16),
|
|
53356
|
-
port: external_exports.number().int().min(1024).max(65535),
|
|
53357
|
-
token: Text2(256)
|
|
53358
|
-
});
|
|
53359
|
-
var BootProgressSchema = external_exports.strictObject({
|
|
53360
|
-
step: external_exports.number().int().min(1).max(32),
|
|
53361
|
-
steps: external_exports.number().int().min(1).max(32),
|
|
53362
|
-
title: Text2(120),
|
|
53363
|
-
/** Being set up now; percent while it downloads (when the size is known). */
|
|
53364
|
-
active: external_exports.array(external_exports.strictObject({ name: Text2(64), percent: external_exports.number().int().min(0).max(100).optional() })).max(8),
|
|
53365
|
-
done: external_exports.number().int().min(0).max(1e3),
|
|
53366
|
-
total: external_exports.number().int().min(0).max(1e3)
|
|
53367
|
-
});
|
|
53368
|
-
var SandboxStatusSchema = external_exports.object({
|
|
53369
|
-
launchId: Text2(64),
|
|
53370
|
-
phase: external_exports.enum(["booting", "provisioning", "ready", "failed"]),
|
|
53371
|
-
message: Text2(),
|
|
53372
|
-
updatedAt: TimeSchema,
|
|
53373
|
-
workspaceHost: Text2(),
|
|
53374
|
-
guestWorkspace: Text2(),
|
|
53375
|
-
headless: external_exports.boolean(),
|
|
53376
|
-
/** The sandbox's IPv4 addresses (for network.forwardPorts; the host only uses ones on its Hyper-V network). */
|
|
53377
|
-
addresses: external_exports.array(external_exports.string().max(45)).max(8).optional(),
|
|
53378
|
-
/** A malformed progress is dropped, never the status (the phase still counts). */
|
|
53379
|
-
progress: BootProgressSchema.optional().catch(void 0)
|
|
53380
|
-
});
|
|
53381
|
-
var VersionSchema = external_exports.strictObject({ spec: Text2(128), source: Text2(256) });
|
|
53382
|
-
var DatabasePlanSchema = external_exports.strictObject({
|
|
53383
|
-
database: external_exports.enum(["postgres", "mysql", "sqlserver", "mongodb", "redis", "dynamodb"]),
|
|
53384
|
-
reasons: external_exports.array(Text2(1024)).max(32),
|
|
53385
|
-
version: Text2(64),
|
|
53386
|
-
port: external_exports.number().int().min(1024).max(65535),
|
|
53387
|
-
adminPort: external_exports.number().int().min(1024).max(65535).optional()
|
|
53388
|
-
});
|
|
53389
|
-
var ProjectSpecSchema = external_exports.strictObject({
|
|
53390
|
-
name: IdSchema,
|
|
53391
|
-
/** Inside the sandbox: C:\Projects\<name>. */
|
|
53392
|
-
workspace: Text2(1024),
|
|
53393
|
-
workspaceHost: Text2(1024),
|
|
53394
|
-
toolchains: ToolchainsSchema,
|
|
53395
|
-
versions: external_exports.strictObject({ node: VersionSchema, nodeExtra: external_exports.array(Text2(128)).max(16), python: VersionSchema }),
|
|
53396
|
-
databases: external_exports.array(DatabasePlanSchema).max(8),
|
|
53397
|
-
/** Agents this project may start that the sandbox did not boot with (its .sandbox-config.json "harness"). */
|
|
53398
|
-
harnesses: external_exports.record(Text2(64), HarnessSchema).refine((h) => Object.keys(h).length <= 8, "at most 8 agents"),
|
|
53399
|
-
/** Its hooks were staged in C:\AgentLaunch\projects\<name>\hooks. */
|
|
53400
|
-
hooks: external_exports.boolean()
|
|
53401
|
-
});
|
|
53402
|
-
var ShellChoiceSchema = external_exports.enum(["auto", "pwsh", "powershell", "cmd"]);
|
|
53403
|
-
var GuestRequestSchema = external_exports.discriminatedUnion("kind", [
|
|
53404
|
-
external_exports.strictObject({
|
|
53405
|
-
kind: external_exports.literal("session"),
|
|
53406
|
-
id: IdSchema,
|
|
53407
|
-
harness: Text2(64),
|
|
53408
|
-
args: external_exports.array(Text2()).max(256),
|
|
53409
|
-
cwd: Text2(),
|
|
53410
|
-
project: ProjectSpecSchema.optional(),
|
|
53411
|
-
harnessSpec: HarnessSchema.optional(),
|
|
53412
|
-
/** A plain shell instead of an agent (harness is then "shell" and args are ignored). */
|
|
53413
|
-
shell: ShellChoiceSchema.optional(),
|
|
53414
|
-
createdAt: TimeSchema
|
|
53415
|
-
}),
|
|
53416
|
-
/** End a running session (an agent or a shell) and everything it started. */
|
|
53417
|
-
external_exports.strictObject({ kind: external_exports.literal("close"), id: IdSchema, session: IdSchema, createdAt: TimeSchema }),
|
|
53418
|
-
/** Provision a registered project without starting an agent (sandbox start / exec / db from its folder). */
|
|
53419
|
-
external_exports.strictObject({ kind: external_exports.literal("project"), id: IdSchema, project: ProjectSpecSchema, createdAt: TimeSchema }),
|
|
53420
|
-
external_exports.strictObject({
|
|
53421
|
-
kind: external_exports.literal("exec"),
|
|
53422
|
-
id: IdSchema,
|
|
53423
|
-
command: external_exports.array(Text2()).min(1).max(256),
|
|
53424
|
-
cwd: Text2(),
|
|
53425
|
-
timeoutSeconds: external_exports.number().positive().max(86400),
|
|
53426
|
-
createdAt: TimeSchema
|
|
53427
|
-
}),
|
|
53428
|
-
/** cwd: only the sessions in this folder or below (one project's); omitted = every session. */
|
|
53429
|
-
external_exports.strictObject({
|
|
53430
|
-
kind: external_exports.literal("restart"),
|
|
53431
|
-
id: IdSchema,
|
|
53432
|
-
harness: Text2(64).nullable(),
|
|
53433
|
-
cwd: Text2().optional(),
|
|
53434
|
-
createdAt: TimeSchema
|
|
53435
|
-
}),
|
|
53436
|
-
external_exports.strictObject({ kind: external_exports.literal("ping"), id: IdSchema, createdAt: TimeSchema }),
|
|
53437
|
-
/**
|
|
53438
|
-
* The PC's own memory or CPU is running low (the host watchdog): the sandbox slows its heaviest builds down for
|
|
53439
|
-
* `seconds` (it never stops them) so Windows does not have to close it. Repeated while the PC stays low.
|
|
53440
|
-
*/
|
|
53441
|
-
external_exports.strictObject({
|
|
53442
|
-
kind: external_exports.literal("pressure"),
|
|
53443
|
-
id: IdSchema,
|
|
53444
|
-
reason: Text2(300),
|
|
53445
|
-
seconds: external_exports.number().int().min(10).max(300),
|
|
53446
|
-
/** "severe": the PC is nearly out of memory; builds go to the governor's last step at once. */
|
|
53447
|
-
level: external_exports.enum(["slow", "severe"]).optional(),
|
|
53448
|
-
createdAt: TimeSchema
|
|
53449
|
-
})
|
|
53450
|
-
]);
|
|
53451
|
-
var PressureStatusSchema = external_exports.object({
|
|
53452
|
-
level: external_exports.enum(["ok", "throttled", "suspending"]),
|
|
53453
|
-
/** The governor's dial, 0 (nothing wrong) to 4 (one compiler at a time). */
|
|
53454
|
-
step: external_exports.number().int().min(0).max(4).optional(),
|
|
53455
|
-
profile: external_exports.enum(["normal", "gentle"]),
|
|
53456
|
-
/** Share of the sandbox's RAM still free and of its commit limit in use (percent). */
|
|
53457
|
-
ramFreePercent: external_exports.number().int().min(0).max(100),
|
|
53458
|
-
commitPercent: external_exports.number().int().min(0).max(100),
|
|
53459
|
-
/** Programs running at lower priority because of it. */
|
|
53460
|
-
lowered: external_exports.number().int().min(0).max(1024),
|
|
53461
|
-
/** Compilers waiting their turn right now (name, process id, seconds until they may go on). */
|
|
53462
|
-
paused: external_exports.array(external_exports.object({ name: Text2(64), pid: external_exports.number().int(), seconds: external_exports.number().int().min(0).max(60) })).max(8),
|
|
53463
|
-
/** The CPU share the compilers get together (percent; 100 = no cap). */
|
|
53464
|
-
cpuPercent: external_exports.number().int().min(0).max(100).optional(),
|
|
53465
|
-
/** The PC itself asked for slowing down (its memory or CPU is low). */
|
|
53466
|
-
pcLow: external_exports.boolean(),
|
|
53467
|
-
lastAction: Text2(300),
|
|
53468
|
-
updatedAt: TimeSchema
|
|
53469
|
-
});
|
|
53470
|
-
var GuestResponseSchema = external_exports.object({
|
|
53471
|
-
id: IdSchema,
|
|
53472
|
-
ok: external_exports.boolean(),
|
|
53473
|
-
message: Text2(65536),
|
|
53474
|
-
exitCode: external_exports.number().int().optional(),
|
|
53475
|
-
stdout: Text2(4 * 1024 * 1024).optional(),
|
|
53476
|
-
stderr: Text2(4 * 1024 * 1024).optional(),
|
|
53477
|
-
finishedAt: TimeSchema
|
|
53478
|
-
});
|
|
53479
|
-
var SessionStatusSchema = external_exports.object({
|
|
53480
|
-
id: IdSchema,
|
|
53481
|
-
launchId: Text2(64),
|
|
53482
|
-
harness: Text2(64),
|
|
53483
|
-
/** A plain shell (harness is then the shell: pwsh, powershell or cmd); absent: an agent. */
|
|
53484
|
-
kind: external_exports.enum(["agent", "shell"]).optional(),
|
|
53485
|
-
cwd: Text2(),
|
|
53486
|
-
/** The project it runs in: its hub name, or the launch folder's for the first project. */
|
|
53487
|
-
project: Text2(64).optional(),
|
|
53488
|
-
phase: external_exports.enum(["starting", "running", "exited", "failed"]),
|
|
53489
|
-
message: Text2(),
|
|
53490
|
-
updatedAt: TimeSchema,
|
|
53491
|
-
startedAt: TimeSchema,
|
|
53492
|
-
/** The agent's own conversation this session is in (Claude: the file name of its .jsonl), once known. */
|
|
53493
|
-
conversation: Text2(128).optional(),
|
|
53494
|
-
exitCode: external_exports.number().int().optional(),
|
|
53495
|
-
attach: AttachEndpointSchema.optional()
|
|
53496
|
-
});
|
|
53497
|
-
var ProjectStatusSchema = external_exports.object({
|
|
53498
|
-
name: IdSchema,
|
|
53499
|
-
workspace: Text2(1024),
|
|
53500
|
-
phase: external_exports.enum(["provisioning", "ready", "failed"]),
|
|
53501
|
-
message: Text2(),
|
|
53502
|
-
updatedAt: TimeSchema,
|
|
53503
|
-
databases: external_exports.array(external_exports.object({ database: Text2(32), port: external_exports.number().int() })).max(16),
|
|
53504
|
-
summary: external_exports.array(Text2(512)).max(64)
|
|
53505
|
-
});
|
|
53506
|
-
var VpnStatusSchema = external_exports.object({
|
|
53507
|
-
state: external_exports.enum(["off", "starting", "up", "failed"]),
|
|
53508
|
-
message: Text2(2e3),
|
|
53509
|
-
killSwitch: external_exports.boolean(),
|
|
53510
|
-
/** A direct connection was tried after the kill switch was applied: false = it still got out. */
|
|
53511
|
-
killSwitchHeld: external_exports.boolean().optional(),
|
|
53512
|
-
/** The server's name, e.g. uk2201.nordvpn.com. */
|
|
53513
|
-
server: Text2(128).optional(),
|
|
53514
|
-
/** What the sandbox's traffic looks like from outside, once the tunnel is up. */
|
|
53515
|
-
publicIp: Text2(64).optional(),
|
|
53516
|
-
updatedAt: TimeSchema
|
|
53517
|
-
});
|
|
53518
|
-
|
|
53519
|
-
// src/shared/channel/run-dir.ts
|
|
53520
|
-
import fs29 from "node:fs";
|
|
53521
|
-
import path41 from "node:path";
|
|
53522
|
-
function safeId(id) {
|
|
53523
|
-
return IdSchema.parse(id);
|
|
53524
|
-
}
|
|
53525
|
-
function now() {
|
|
53526
|
-
return (/* @__PURE__ */ new Date()).toISOString();
|
|
53527
|
-
}
|
|
53528
|
-
var scan = (schema, dir) => {
|
|
53529
|
-
if (!fs29.existsSync(dir)) return { valid: [], invalid: [] };
|
|
53530
|
-
const valid = [];
|
|
53531
|
-
const invalid = [];
|
|
53532
|
-
for (const name2 of fs29.readdirSync(dir)) {
|
|
53533
|
-
if (name2.endsWith(".tmp")) continue;
|
|
53534
|
-
const message = /^[A-Za-z0-9_-]{1,64}\.json$/.test(name2) ? readValid(schema, path41.join(dir, name2)) : null;
|
|
53535
|
-
if (message) valid.push(message);
|
|
53536
|
-
else invalid.push(path41.join(dir, name2));
|
|
53537
|
-
}
|
|
53538
|
-
return { valid, invalid };
|
|
53539
|
-
};
|
|
53540
|
-
var RunDir = class {
|
|
53541
|
-
root;
|
|
53542
|
-
sandboxFile;
|
|
53543
|
-
vpnFile;
|
|
53544
|
-
/** What the memory governor in the sandbox is doing (pressure.json). */
|
|
53545
|
-
pressureFile;
|
|
53546
|
-
/** What the sandbox's programs may really use (budget.json): processors, memory, build jobs, link slots. */
|
|
53547
|
-
budgetFile;
|
|
53548
|
-
requestsDir;
|
|
53549
|
-
responsesDir;
|
|
53550
|
-
sessionsDir;
|
|
53551
|
-
projectsDir;
|
|
53552
|
-
/** audit.processes: programs the sandbox started, in small .jsonl files the host takes in and deletes. */
|
|
53553
|
-
auditDir;
|
|
53554
|
-
/**
|
|
53555
|
-
* Each running session's process tree, recorded by its ptyhost (guest only: the daemon ends what a ptyhost that was
|
|
53556
|
-
* itself ended left running).
|
|
53557
|
-
*/
|
|
53558
|
-
treesDir;
|
|
53559
|
-
/** One-shot secrets the host hands over (see secrets.ts). Next to run, not in it: shared-use users can change run. */
|
|
53560
|
-
secretsDir;
|
|
53561
|
-
/** Live folders (live.ts): signed host requests, and the daemon's replies. */
|
|
53562
|
-
liveDir;
|
|
53563
|
-
liveRepliesDir;
|
|
53564
|
-
constructor(dataDir) {
|
|
53565
|
-
this.root = path41.join(dataDir, "run");
|
|
53566
|
-
this.sandboxFile = path41.join(this.root, "sandbox.json");
|
|
53567
|
-
this.vpnFile = path41.join(this.root, "vpn.json");
|
|
53568
|
-
this.pressureFile = path41.join(this.root, "pressure.json");
|
|
53569
|
-
this.budgetFile = path41.join(this.root, "budget.json");
|
|
53570
|
-
this.requestsDir = path41.join(this.root, "requests");
|
|
53571
|
-
this.responsesDir = path41.join(this.root, "responses");
|
|
53572
|
-
this.sessionsDir = path41.join(this.root, "sessions");
|
|
53573
|
-
this.projectsDir = path41.join(this.root, "projects");
|
|
53574
|
-
this.auditDir = path41.join(this.root, "audit");
|
|
53575
|
-
this.treesDir = path41.join(this.root, "trees");
|
|
53576
|
-
this.secretsDir = path41.join(dataDir, "secrets");
|
|
53577
|
-
this.liveDir = path41.join(this.root, "live");
|
|
53578
|
-
this.liveRepliesDir = path41.join(this.root, "live-replies");
|
|
53579
|
-
}
|
|
53580
|
-
liveFile(id) {
|
|
53581
|
-
return path41.join(this.liveDir, safeId(id) + ".json");
|
|
53582
|
-
}
|
|
53583
|
-
liveReplyFile(id) {
|
|
53584
|
-
return path41.join(this.liveRepliesDir, safeId(id) + ".json");
|
|
53585
|
-
}
|
|
53586
|
-
requestFile(id) {
|
|
53587
|
-
return path41.join(this.requestsDir, safeId(id) + ".json");
|
|
53588
|
-
}
|
|
53589
|
-
responseFile(id) {
|
|
53590
|
-
return path41.join(this.responsesDir, safeId(id) + ".json");
|
|
53591
|
-
}
|
|
53592
|
-
sessionFile(id) {
|
|
53593
|
-
return path41.join(this.sessionsDir, safeId(id) + ".json");
|
|
53594
|
-
}
|
|
53595
|
-
treeFile(id) {
|
|
53596
|
-
return path41.join(this.treesDir, safeId(id) + ".json");
|
|
53597
|
-
}
|
|
53598
|
-
projectFile(name2) {
|
|
53599
|
-
return path41.join(this.projectsDir, safeId(name2) + ".json");
|
|
53600
|
-
}
|
|
53601
|
-
secretFile(id) {
|
|
53602
|
-
return path41.join(this.secretsDir, safeId(id) + ".json");
|
|
53603
|
-
}
|
|
53604
|
-
readSandboxStatus() {
|
|
53605
|
-
return readValid(SandboxStatusSchema, this.sandboxFile);
|
|
53606
|
-
}
|
|
53607
|
-
readVpnStatus() {
|
|
53608
|
-
return readValid(VpnStatusSchema, this.vpnFile);
|
|
53609
|
-
}
|
|
53610
|
-
readPressure() {
|
|
53611
|
-
return readValid(PressureStatusSchema, this.pressureFile);
|
|
53612
|
-
}
|
|
53613
|
-
readSession(id) {
|
|
53614
|
-
return readValid(SessionStatusSchema, this.sessionFile(id));
|
|
53615
|
-
}
|
|
53616
|
-
readResponse(id) {
|
|
53617
|
-
return readValid(GuestResponseSchema, this.responseFile(id));
|
|
53618
|
-
}
|
|
53619
|
-
listSessions() {
|
|
53620
|
-
return scan(SessionStatusSchema, this.sessionsDir).valid.sort((a, b) => a.startedAt.localeCompare(b.startedAt));
|
|
53621
|
-
}
|
|
53622
|
-
readProjectStatus(name2) {
|
|
53623
|
-
return readValid(ProjectStatusSchema, this.projectFile(name2));
|
|
53624
|
-
}
|
|
53625
|
-
listProjectStatus() {
|
|
53626
|
-
return scan(ProjectStatusSchema, this.projectsDir).valid.slice(0, 64);
|
|
53627
|
-
}
|
|
53628
|
-
scanRequests() {
|
|
53629
|
-
return scan(GuestRequestSchema, this.requestsDir);
|
|
53630
|
-
}
|
|
53631
|
-
};
|
|
53632
|
-
|
|
53633
|
-
// src/shared/accounts/payloads.ts
|
|
53634
|
-
var KB = 1024;
|
|
53635
|
-
var base643 = (maxBytes) => external_exports.string().min(4).max(maxBytes).regex(/^[A-Za-z0-9+/]+={0,2}$/);
|
|
53636
|
-
var secretText = (max) => external_exports.string().min(1).max(max).regex(/^[^\r\n]+$/);
|
|
53637
|
-
var EpicPayloadSchema = external_exports.strictObject({
|
|
53638
|
-
data: external_exports.string().min(16).max(16384).regex(/^[A-Za-z0-9+/=_-]+$/)
|
|
53639
|
-
});
|
|
53640
|
-
var SteamPayloadSchema = external_exports.strictObject({
|
|
53641
|
-
account: external_exports.string().regex(/^[A-Za-z0-9_]{1,64}$/),
|
|
53642
|
-
configVdf: external_exports.string().min(1).max(256 * KB),
|
|
53643
|
-
sentries: external_exports.array(external_exports.strictObject({ name: external_exports.string().regex(/^ssfn\d{1,40}$/), base64: base643(64 * KB) })).max(4),
|
|
53644
|
-
/** Reserved for the Steam client's own login (very likely not portable between Windows users). */
|
|
53645
|
-
client: base643(256 * KB).optional()
|
|
53646
|
-
});
|
|
53647
|
-
var UnityPayloadSchema = external_exports.strictObject({
|
|
53648
|
-
ulf: external_exports.string().min(1).max(128 * KB).optional(),
|
|
53649
|
-
email: external_exports.string().email().max(256).optional(),
|
|
53650
|
-
password: secretText(1024).optional(),
|
|
53651
|
-
serial: secretText(64).optional()
|
|
53652
|
-
});
|
|
53653
|
-
var CfxPayloadSchema = external_exports.strictObject({
|
|
53654
|
-
licenseKey: external_exports.string().max(256).regex(/^cfxk_[A-Za-z0-9_]+$/),
|
|
53655
|
-
steamWebApiKey: external_exports.string().regex(/^[A-F0-9]{32}$/).optional()
|
|
53656
|
-
});
|
|
53657
|
-
var KeystorePayloadSchema = external_exports.strictObject({
|
|
53658
|
-
fileName: external_exports.string().regex(/^[A-Za-z0-9._-]{1,100}\.(jks|keystore|p12)$/i),
|
|
53659
|
-
jksBase64: base643(256 * KB),
|
|
53660
|
-
storePassword: secretText(1024),
|
|
53661
|
-
keyAlias: secretText(256),
|
|
53662
|
-
keyPassword: secretText(1024)
|
|
53663
|
-
});
|
|
53664
|
-
var PlayPayloadSchema = external_exports.strictObject({
|
|
53665
|
-
serviceAccountJson: external_exports.string().min(2).max(64 * KB).refine((text) => {
|
|
53666
|
-
try {
|
|
53667
|
-
const parsed = JSON.parse(text);
|
|
53668
|
-
return typeof parsed === "object" && parsed !== null && parsed.type === "service_account";
|
|
53669
|
-
} catch {
|
|
53670
|
-
return false;
|
|
53671
|
-
}
|
|
53672
|
-
}, 'not a Google service account key (its "type" is not "service_account")')
|
|
53673
|
-
});
|
|
53674
|
-
var ExpoPayloadSchema = external_exports.strictObject({
|
|
53675
|
-
token: external_exports.string().regex(/^[A-Za-z0-9_-]{8,512}$/)
|
|
53676
|
-
});
|
|
53677
|
-
var AppstorePayloadSchema = external_exports.strictObject({
|
|
53678
|
-
issuerId: external_exports.string().uuid(),
|
|
53679
|
-
keyId: external_exports.string().regex(/^[A-Z0-9]{10}$/),
|
|
53680
|
-
p8: external_exports.string().max(8 * KB).regex(/^-----BEGIN PRIVATE KEY-----\r?\n[A-Za-z0-9+/=\r\n]+-----END PRIVATE KEY-----\s*$/)
|
|
53681
|
-
});
|
|
53682
|
-
var PAYLOADS = {
|
|
53683
|
-
epic: EpicPayloadSchema,
|
|
53684
|
-
steam: SteamPayloadSchema,
|
|
53685
|
-
unity: UnityPayloadSchema,
|
|
53686
|
-
cfx: CfxPayloadSchema,
|
|
53687
|
-
keystore: KeystorePayloadSchema,
|
|
53688
|
-
play: PlayPayloadSchema,
|
|
53689
|
-
expo: ExpoPayloadSchema,
|
|
53690
|
-
appstore: AppstorePayloadSchema
|
|
53691
|
-
};
|
|
53692
|
-
var ServiceAccountsSchema = external_exports.strictObject({
|
|
53693
|
-
epic: EpicPayloadSchema.optional(),
|
|
53694
|
-
steam: SteamPayloadSchema.optional(),
|
|
53695
|
-
unity: UnityPayloadSchema.optional(),
|
|
53696
|
-
cfx: CfxPayloadSchema.optional(),
|
|
53697
|
-
keystore: KeystorePayloadSchema.optional(),
|
|
53698
|
-
play: PlayPayloadSchema.optional(),
|
|
53699
|
-
expo: ExpoPayloadSchema.optional(),
|
|
53700
|
-
appstore: AppstorePayloadSchema.optional()
|
|
53701
|
-
});
|
|
53702
|
-
var LaunchIdSchema = external_exports.string().regex(/^[A-Za-z0-9-]{1,64}$/);
|
|
53703
|
-
function handoffSchema(kind) {
|
|
53704
|
-
return external_exports.strictObject({
|
|
53705
|
-
kind: external_exports.literal(kind),
|
|
53706
|
-
launchId: LaunchIdSchema,
|
|
53707
|
-
payload: PAYLOADS[kind]
|
|
53708
|
-
});
|
|
53709
|
-
}
|
|
53710
|
-
|
|
53711
|
-
// src/shared/github/token.ts
|
|
53712
|
-
var GITHUB_TOKEN_KINDS = ["fine-grained", "classic"];
|
|
53713
|
-
var MIN_LENGTH = 20;
|
|
53714
|
-
var MAX_LENGTH = 255;
|
|
53715
|
-
var PRINTABLE = /^[\x21-\x7e]+$/;
|
|
53716
|
-
var GithubTokenSchema = external_exports.string().min(MIN_LENGTH).max(MAX_LENGTH).regex(PRINTABLE);
|
|
53717
|
-
var OTHER_TOKENS = {
|
|
53718
|
-
gho_: "This is an OAuth token (from gh auth login or gh auth token), which can reach everything your account can.",
|
|
53719
|
-
ghu_: "This is a GitHub App user token, not a personal access token.",
|
|
53720
|
-
ghs_: "This is a GitHub App installation token, not a personal access token.",
|
|
53721
|
-
ghr_: "This is a refresh token, not a personal access token."
|
|
53722
|
-
};
|
|
53723
|
-
var CREATE_FINE_GRAINED = "Create a fine-grained token at https://github.com/settings/personal-access-tokens/new with only the repositories and permissions the agent needs, then run: sandbox github login";
|
|
53724
|
-
function githubTokenKind(token) {
|
|
53725
|
-
if (token.startsWith("github_pat_")) return "fine-grained";
|
|
53726
|
-
if (token.startsWith("ghp_")) return "classic";
|
|
53727
|
-
return null;
|
|
53728
|
-
}
|
|
53729
|
-
function githubTokenProblem(token) {
|
|
53730
|
-
if (!token) return { message: "No token was typed.", fix: CREATE_FINE_GRAINED };
|
|
53731
|
-
if (!PRINTABLE.test(token))
|
|
53732
|
-
return {
|
|
53733
|
-
message: "The token may not contain spaces, line breaks or other special characters.",
|
|
53734
|
-
fix: "Copy the token again from GitHub (only the token itself) and run: sandbox github login"
|
|
53735
|
-
};
|
|
53736
|
-
if (token.length < MIN_LENGTH || token.length > MAX_LENGTH)
|
|
53737
|
-
return {
|
|
53738
|
-
message: "That is not the length of a GitHub token.",
|
|
53739
|
-
fix: "Copy the whole token again from GitHub and run: sandbox github login"
|
|
53740
|
-
};
|
|
53741
|
-
const other = Object.keys(OTHER_TOKENS).find((p) => token.startsWith(p));
|
|
53742
|
-
if (other) return { message: OTHER_TOKENS[other], fix: CREATE_FINE_GRAINED };
|
|
53743
|
-
if (!githubTokenKind(token))
|
|
53744
|
-
return {
|
|
53745
|
-
message: "That is not a GitHub personal access token (they start with github_pat_ or ghp_).",
|
|
53746
|
-
fix: CREATE_FINE_GRAINED
|
|
53747
|
-
};
|
|
53748
|
-
return null;
|
|
53749
|
-
}
|
|
53750
|
-
|
|
53751
|
-
// src/shared/channel/secrets.ts
|
|
53752
|
-
var Environment = external_exports.record(external_exports.string().max(256), external_exports.string().max(65536));
|
|
53753
|
-
var SecretsFileSchema = external_exports.strictObject({
|
|
53754
|
-
/** The agent the sandbox booted with: its harness environment. */
|
|
53755
|
-
harnessEnvironment: Environment.optional(),
|
|
53756
|
-
/** harnessSpec of a session request (an agent the sandbox did not boot with). */
|
|
53757
|
-
harnessSpecEnvironment: Environment.optional(),
|
|
53758
|
-
/** project.harnesses of a request, by agent name. */
|
|
53759
|
-
projectHarnessEnvironment: external_exports.record(external_exports.string().max(64), Environment).optional(),
|
|
53760
|
-
/** The login of this PC's Claude (claudeProfile.copyCredentials): the text of .credentials.json. */
|
|
53761
|
-
claudeCredentials: external_exports.string().max(1024 * 1024).optional(),
|
|
53762
|
-
/** VPN service credentials (vpn.provider's login), only for the launch's own sandbox. */
|
|
53763
|
-
vpn: external_exports.strictObject({ username: external_exports.string().min(1).max(256), password: external_exports.string().min(1).max(1024) }).optional(),
|
|
53764
|
-
/** The checked WireGuard config of vpn.provider wireguard-file (holds the private key). */
|
|
53765
|
-
wireguardConfig: external_exports.string().min(1).max(32 * 1024).optional(),
|
|
53766
|
-
/** The GitHub token you saved for sandboxes (github.shareToken), only with git access "full". */
|
|
53767
|
-
githubToken: GithubTokenSchema.optional(),
|
|
53768
|
-
/** Your saved service sign-ins (accounts.<kind>.share): each goes to its tool's own files at boot. */
|
|
53769
|
-
serviceAccounts: ServiceAccountsSchema.optional(),
|
|
53770
|
-
/** projects.liveFolders "hcs": this launch's key for signed live-folder requests (live.ts). */
|
|
53771
|
-
liveFoldersKey: external_exports.string().regex(/^[0-9a-f]{64}$/).optional()
|
|
53772
|
-
});
|
|
53773
|
-
|
|
53774
|
-
// src/shared/channel/live.ts
|
|
53775
|
-
import crypto3 from "node:crypto";
|
|
53776
|
-
var LIVE_SHARE_NAME = /^agentsbx-[0-9a-f]{8,32}$/;
|
|
53777
|
-
var LIVE_FOLDER_NAME = /^[a-z0-9][a-z0-9_-]{0,63}$/;
|
|
53778
|
-
var LIVE_KEY = /^[0-9a-f]{64}$/;
|
|
53779
|
-
var LIVE_TOKEN = /^[0-9a-f]{32}$/;
|
|
53780
|
-
var LIVE_MAX_AGE_MS = 2 * 60 * 1e3;
|
|
53781
|
-
var MAX_PAYLOAD = 4096;
|
|
53782
|
-
var LivePayloadSchema = external_exports.strictObject({
|
|
53783
|
-
v: external_exports.literal(1),
|
|
53784
|
-
/** canary: show the share, read token.txt, hide it again and reply with the token; map / unmap: the real folder. */
|
|
53785
|
-
kind: external_exports.enum(["canary", "map", "unmap"]),
|
|
53786
|
-
id: IdSchema,
|
|
53787
|
-
launchId: external_exports.string().min(1).max(64),
|
|
53788
|
-
share: external_exports.string().regex(LIVE_SHARE_NAME).optional(),
|
|
53789
|
-
folder: external_exports.string().regex(LIVE_FOLDER_NAME),
|
|
53790
|
-
readOnly: external_exports.boolean(),
|
|
53791
|
-
createdAt: external_exports.string().max(64)
|
|
53792
|
-
});
|
|
53793
|
-
var LiveEnvelopeSchema = external_exports.strictObject({
|
|
53794
|
-
payload: external_exports.string().min(2).max(MAX_PAYLOAD),
|
|
53795
|
-
mac: external_exports.string().regex(/^[0-9a-f]{64}$/)
|
|
53796
|
-
});
|
|
53797
|
-
var LiveReplySchema = external_exports.strictObject({
|
|
53798
|
-
id: IdSchema,
|
|
53799
|
-
ok: external_exports.boolean(),
|
|
53800
|
-
message: external_exports.string().max(1e3),
|
|
53801
|
-
token: external_exports.string().regex(LIVE_TOKEN).optional(),
|
|
53802
|
-
finishedAt: external_exports.string().max(64)
|
|
53803
|
-
});
|
|
53804
|
-
function mac3(key3, payload) {
|
|
53805
|
-
return crypto3.createHmac("sha256", Buffer.from(key3, "hex")).update(payload, "utf8").digest("hex");
|
|
53806
|
-
}
|
|
53807
|
-
function signLive(key3, payload) {
|
|
53808
|
-
if (!LIVE_KEY.test(key3)) throw new Error("not a live-folder key");
|
|
53809
|
-
const text = JSON.stringify(LivePayloadSchema.parse(payload));
|
|
53810
|
-
return { payload: text, mac: mac3(key3, text) };
|
|
53811
|
-
}
|
|
53812
|
-
function newShareName() {
|
|
53813
|
-
return "agentsbx-" + crypto3.randomBytes(8).toString("hex");
|
|
53814
|
-
}
|
|
53815
|
-
function newLiveKey() {
|
|
53816
|
-
return crypto3.randomBytes(32).toString("hex");
|
|
53817
|
-
}
|
|
53818
|
-
|
|
53819
53888
|
// src/host/install/layout.ts
|
|
53820
53889
|
import fs30 from "node:fs";
|
|
53821
53890
|
import path42 from "node:path";
|
|
@@ -58473,7 +58542,7 @@ var GUEST = {
|
|
|
58473
58542
|
var MB = 1048576;
|
|
58474
58543
|
var MIN_AUTO_MEMORY_MB = 4096;
|
|
58475
58544
|
var roundDown = (mb) => Math.floor(mb / 1024) * 1024;
|
|
58476
|
-
var VGPU_RESERVE_MB =
|
|
58545
|
+
var VGPU_RESERVE_MB = 4096;
|
|
58477
58546
|
function residentCapMb(sandbox, totalMb) {
|
|
58478
58547
|
const keep = Math.max(sandbox.reserveHostMb, Math.floor(totalMb / 4)) + (sandbox.vGpu ? VGPU_RESERVE_MB : 0);
|
|
58479
58548
|
return roundDown(totalMb - keep);
|
|
@@ -58488,8 +58557,9 @@ function residentNote(asked, given) {
|
|
|
58488
58557
|
}
|
|
58489
58558
|
function decideMemory(sandbox, freeMb, totalMb, commitAvailableMb) {
|
|
58490
58559
|
const shareMb = Math.floor(totalMb * sandbox.maxMemoryPercent / 100);
|
|
58491
|
-
const
|
|
58492
|
-
const
|
|
58560
|
+
const reserveMb = sandbox.reserveHostMb + (sandbox.vGpu ? VGPU_RESERVE_MB : 0);
|
|
58561
|
+
const byFree = freeMb - reserveMb;
|
|
58562
|
+
const byCommit = commitAvailableMb === null ? Infinity : commitAvailableMb - reserveMb;
|
|
58493
58563
|
const candidates = [
|
|
58494
58564
|
["max", sandbox.maxMemoryMb],
|
|
58495
58565
|
["share", shareMb],
|
|
@@ -62343,8 +62413,16 @@ var SandboxChannel = class {
|
|
|
62343
62413
|
return this.waitForResponse(requestId, 3e4);
|
|
62344
62414
|
}
|
|
62345
62415
|
/** The PC is low on memory or CPU: the sandbox slows its builds down for `seconds`. Never waits for an answer. */
|
|
62346
|
-
pressure(reason, seconds, level = "slow") {
|
|
62347
|
-
this.send({
|
|
62416
|
+
pressure(reason, seconds, level = "slow", diskQueue) {
|
|
62417
|
+
this.send({
|
|
62418
|
+
kind: "pressure",
|
|
62419
|
+
id: newId(),
|
|
62420
|
+
reason,
|
|
62421
|
+
seconds,
|
|
62422
|
+
level,
|
|
62423
|
+
...diskQueue === void 0 ? {} : { diskQueue },
|
|
62424
|
+
createdAt: now()
|
|
62425
|
+
});
|
|
62348
62426
|
}
|
|
62349
62427
|
async ping() {
|
|
62350
62428
|
const requestId = newId();
|
|
@@ -64083,7 +64161,12 @@ var Watcher = class {
|
|
|
64083
64161
|
step.state.low ? "this PC is low (" + step.reason + "): asking the sandbox to slow its builds down" : "this PC has recovered: no longer asking the sandbox to slow down"
|
|
64084
64162
|
);
|
|
64085
64163
|
if (step.send)
|
|
64086
|
-
new SandboxChannel(this.dataPath).pressure(
|
|
64164
|
+
new SandboxChannel(this.dataPath).pressure(
|
|
64165
|
+
step.reason,
|
|
64166
|
+
REQUEST_SECONDS,
|
|
64167
|
+
step.severe ? "severe" : "slow",
|
|
64168
|
+
memory?.diskQueue
|
|
64169
|
+
);
|
|
64087
64170
|
} catch (e) {
|
|
64088
64171
|
this.note("host watchdog: " + errMsg(e));
|
|
64089
64172
|
} finally {
|
|
@@ -76784,6 +76867,16 @@ async function ensureConpty(dataPath, refresh) {
|
|
|
76784
76867
|
|
|
76785
76868
|
// src/cli/commands/repair/checks/sandbox.ts
|
|
76786
76869
|
var LAUNCH_DAYS = 7;
|
|
76870
|
+
var PAGE_FILE_MAX_SHARE = 0.4;
|
|
76871
|
+
function pageFileFinding(pageFileMb, driveBytes) {
|
|
76872
|
+
if (pageFileMb <= 0 || driveBytes <= 0 || pageFileMb * 1024 * 1024 <= PAGE_FILE_MAX_SHARE * driveBytes) return null;
|
|
76873
|
+
return {
|
|
76874
|
+
status: "warn",
|
|
76875
|
+
detail: "the paging file is " + pageFileMb + " MB on a " + humanBytes(driveBytes) + " drive",
|
|
76876
|
+
means: "sandbox.pageFileMb takes more than 40% of the drive, so builds and installs have little room left and the disk fills up (a full disk can freeze the sandbox).",
|
|
76877
|
+
fix: "Run: sandbox config set sandbox.pageFileMb 8192 (or 0 to let Windows decide)."
|
|
76878
|
+
};
|
|
76879
|
+
}
|
|
76787
76880
|
var hiddenWindowsFile = (env) => path169.join(env.storageRoot, "run", "hidden-windows.json");
|
|
76788
76881
|
var SANDBOX_CHECKS = [
|
|
76789
76882
|
{
|
|
@@ -76797,6 +76890,16 @@ var SANDBOX_CHECKS = [
|
|
|
76797
76890
|
fix: "Run: sandbox config set sandbox.vGpu false"
|
|
76798
76891
|
} : ok("off")
|
|
76799
76892
|
},
|
|
76893
|
+
{
|
|
76894
|
+
id: "sandbox-pagefile",
|
|
76895
|
+
group: "Sandbox",
|
|
76896
|
+
name: "Paging file size",
|
|
76897
|
+
run: (env) => {
|
|
76898
|
+
const mb = env.ctx.loaded.config.sandbox.pageFileMb;
|
|
76899
|
+
const drive = diskUsage(env.storageRoot).find((d) => d.drive.toUpperCase() === "C:\\");
|
|
76900
|
+
return pageFileFinding(mb, drive?.totalBytes ?? 0) ?? ok(mb > 0 ? mb + " MB" : "Windows' own");
|
|
76901
|
+
}
|
|
76902
|
+
},
|
|
76800
76903
|
{
|
|
76801
76904
|
id: "sandbox-exe",
|
|
76802
76905
|
group: "Sandbox",
|
|
@@ -77679,311 +77782,6 @@ var enableSandboxRun = implement(enableSandbox, async (ctx, _input, io) => {
|
|
|
77679
77782
|
});
|
|
77680
77783
|
var lastHealthRun = implement(lastHealth, (ctx) => ({ last: readLast(ctx.storageRoot) }));
|
|
77681
77784
|
|
|
77682
|
-
// src/app/features/sandbox/sandbox.def.ts
|
|
77683
|
-
var ProjectSchema = external_exports.object({
|
|
77684
|
-
name: external_exports.string().describe("The project"),
|
|
77685
|
-
host: external_exports.string().describe("Its folder on this PC"),
|
|
77686
|
-
guest: external_exports.string().describe("Its folder in the sandbox"),
|
|
77687
|
-
mechanism: external_exports.string().describe("How it got into the sandbox (launch, root, boot, wsb or hcs)"),
|
|
77688
|
-
here: external_exports.boolean().describe("The project of the current folder"),
|
|
77689
|
-
setup: external_exports.object({ phase: external_exports.string(), message: external_exports.string() }).describe("How far its setup is"),
|
|
77690
|
-
databases: external_exports.array(external_exports.object({ database: external_exports.string(), port: external_exports.number(), state: external_exports.string() })),
|
|
77691
|
-
sessions: external_exports.array(external_exports.object({ id: external_exports.string(), harness: external_exports.string(), viewers: external_exports.string(), started: external_exports.string() })),
|
|
77692
|
-
repoGuard: external_exports.array(external_exports.string()).describe("Repo guard findings in its folder")
|
|
77693
|
-
});
|
|
77694
|
-
var StateSchema2 = external_exports.object({
|
|
77695
|
-
running: external_exports.boolean().describe("A Windows Sandbox is running"),
|
|
77696
|
-
phase: external_exports.string().describe("What it is doing (ready, provisioning ...)"),
|
|
77697
|
-
message: external_exports.string().optional().describe("Its last message"),
|
|
77698
|
-
headless: external_exports.boolean().optional().describe("Its window is hidden"),
|
|
77699
|
-
startedAt: external_exports.number().nullable().describe("When it started (ms), if known"),
|
|
77700
|
-
daemon: external_exports.string().describe("The sandbox helper's answer"),
|
|
77701
|
-
account: external_exports.string().nullable().describe("The Claude login it uses"),
|
|
77702
|
-
syncWatcher: external_exports.boolean().describe("The background history sync runs")
|
|
77703
|
-
});
|
|
77704
|
-
var SandboxStatusSchema2 = external_exports.object({
|
|
77705
|
-
/** Present when the running sandbox is another user's. */
|
|
77706
|
-
shared: external_exports.object({
|
|
77707
|
-
line: external_exports.string().describe('"Running, started by alice (shared with you)"'),
|
|
77708
|
-
limited: external_exports.boolean().describe("Not shared with you: nothing more is shown"),
|
|
77709
|
-
startedBy: external_exports.string().nullable().describe("Whose it is")
|
|
77710
|
-
}).optional(),
|
|
77711
|
-
sandbox: StateSchema2,
|
|
77712
|
-
here: external_exports.object({ folder: external_exports.string(), project: external_exports.string().nullable(), guest: external_exports.string().nullable() }),
|
|
77713
|
-
roots: external_exports.array(external_exports.object({ host: external_exports.string(), guest: external_exports.string() })),
|
|
77714
|
-
projects: external_exports.array(ProjectSchema),
|
|
77715
|
-
otherSessions: external_exports.array(external_exports.object({ id: external_exports.string(), harness: external_exports.string(), cwd: external_exports.string() })),
|
|
77716
|
-
repoGuard: external_exports.object({ guarded: external_exports.number(), findings: external_exports.array(external_exports.string()) })
|
|
77717
|
-
});
|
|
77718
|
-
var MessageSchema5 = external_exports.object({ message: external_exports.string().describe("What happened") });
|
|
77719
|
-
function uptime(s) {
|
|
77720
|
-
return s.running && s.startedAt ? ago(s.startedAt).replace(" ago", "") : null;
|
|
77721
|
-
}
|
|
77722
|
-
var sandboxStatus = defineQuery({
|
|
77723
|
-
id: "sandbox.status",
|
|
77724
|
-
title: "Sandbox status",
|
|
77725
|
-
summary: "show what the sandbox is doing",
|
|
77726
|
-
input: external_exports.object({}),
|
|
77727
|
-
output: SandboxStatusSchema2,
|
|
77728
|
-
live: { everyMs: 2500 },
|
|
77729
|
-
cli: {
|
|
77730
|
-
name: "status",
|
|
77731
|
-
group: "Sessions",
|
|
77732
|
-
json: true,
|
|
77733
|
-
// The --json shape `sandbox status` has always printed.
|
|
77734
|
-
shapeJson: (o) => {
|
|
77735
|
-
if (o.shared?.limited) return { sandbox: { running: true, shared: false, startedBy: o.shared.startedBy } };
|
|
77736
|
-
const s = o.sandbox;
|
|
77737
|
-
const borrowed = o.shared?.startedBy ? { startedBy: o.shared.startedBy, shared: true } : {};
|
|
77738
|
-
return {
|
|
77739
|
-
sandbox: {
|
|
77740
|
-
...borrowed,
|
|
77741
|
-
running: s.running,
|
|
77742
|
-
phase: s.phase,
|
|
77743
|
-
message: s.message,
|
|
77744
|
-
headless: s.headless,
|
|
77745
|
-
uptime: uptime(s),
|
|
77746
|
-
daemon: s.daemon,
|
|
77747
|
-
account: s.account,
|
|
77748
|
-
syncWatcher: s.syncWatcher
|
|
77749
|
-
},
|
|
77750
|
-
here: o.here,
|
|
77751
|
-
roots: o.roots,
|
|
77752
|
-
projects: o.projects,
|
|
77753
|
-
otherSessions: o.otherSessions,
|
|
77754
|
-
repoGuard: o.repoGuard
|
|
77755
|
-
};
|
|
77756
|
-
}
|
|
77757
|
-
}
|
|
77758
|
-
});
|
|
77759
|
-
var showWindow = defineOperation({
|
|
77760
|
-
id: "sandbox.showWindow",
|
|
77761
|
-
access: "control",
|
|
77762
|
-
title: "Show the window",
|
|
77763
|
-
summary: "show the sandbox window",
|
|
77764
|
-
input: external_exports.object({}),
|
|
77765
|
-
output: MessageSchema5,
|
|
77766
|
-
text: (o) => o.message,
|
|
77767
|
-
cli: { name: "show", group: "Sessions" }
|
|
77768
|
-
});
|
|
77769
|
-
var hideWindow = defineOperation({
|
|
77770
|
-
id: "sandbox.hideWindow",
|
|
77771
|
-
access: "control",
|
|
77772
|
-
title: "Hide the window",
|
|
77773
|
-
summary: "hide the sandbox window (it keeps running)",
|
|
77774
|
-
input: external_exports.object({}),
|
|
77775
|
-
output: MessageSchema5,
|
|
77776
|
-
text: (o) => o.message,
|
|
77777
|
-
cli: { name: "hide", group: "Sessions" }
|
|
77778
|
-
});
|
|
77779
|
-
var stopSandbox = defineOperation({
|
|
77780
|
-
id: "sandbox.stop",
|
|
77781
|
-
access: "control",
|
|
77782
|
-
title: "Stop the sandbox\u2026",
|
|
77783
|
-
summary: "shut the sandbox down (every project's sessions end)",
|
|
77784
|
-
input: external_exports.object({}),
|
|
77785
|
-
output: external_exports.object({ stopped: external_exports.boolean().describe("It was asked to shut down"), message: external_exports.string() }),
|
|
77786
|
-
danger: true,
|
|
77787
|
-
text: (o) => o.message,
|
|
77788
|
-
cli: { name: "stop", group: "Agents", yes: "do not ask when other projects' sessions would end" }
|
|
77789
|
-
});
|
|
77790
|
-
var MECHANISM_TEXT = {
|
|
77791
|
-
launch: "started from here",
|
|
77792
|
-
root: "in a project root",
|
|
77793
|
-
boot: "mapped at start",
|
|
77794
|
-
wsb: "wsb share",
|
|
77795
|
-
hcs: "live folder through HCS, alpha"
|
|
77796
|
-
};
|
|
77797
|
-
function projectItem2(p) {
|
|
77798
|
-
return {
|
|
77799
|
-
id: "project:" + p.name,
|
|
77800
|
-
label: p.name,
|
|
77801
|
-
icon: "project",
|
|
77802
|
-
hint: p.host + " -> " + p.guest + " (" + (MECHANISM_TEXT[p.mechanism] ?? "wsb share") + ")",
|
|
77803
|
-
marked: p.here,
|
|
77804
|
-
details: [
|
|
77805
|
-
"Setup: " + p.setup.phase + (p.setup.phase === "ready" ? "" : " - " + p.setup.message),
|
|
77806
|
-
...p.databases.length ? ["Databases: " + p.databases.map((d) => d.database + " :" + d.port + " " + d.state).join(", ")] : [],
|
|
77807
|
-
...p.sessions.map((s) => "Session: " + s.harness + " " + s.id + ", " + s.viewers + ", started " + ago(s.started)),
|
|
77808
|
-
...p.repoGuard.map((f) => "Guard: " + f)
|
|
77809
|
-
]
|
|
77810
|
-
};
|
|
77811
|
-
}
|
|
77812
|
-
function facts(o) {
|
|
77813
|
-
const s = o.sandbox;
|
|
77814
|
-
const sessions = o.projects.reduce((n, p) => n + p.sessions.length, 0) + o.otherSessions.length;
|
|
77815
|
-
const fact = (id, label2, value, extra = {}) => ({
|
|
77816
|
-
id,
|
|
77817
|
-
label: label2,
|
|
77818
|
-
value,
|
|
77819
|
-
...extra
|
|
77820
|
-
});
|
|
77821
|
-
return [
|
|
77822
|
-
...o.shared?.startedBy ? [fact("owner", "Started by", o.shared.startedBy)] : [],
|
|
77823
|
-
// One line for what it is doing: "Ready", "Installing Rust ...", or "not running".
|
|
77824
|
-
fact("state", "State", s.running ? s.message || s.phase : "not running", {
|
|
77825
|
-
tone: !s.running ? "muted" : s.phase === "ready" ? "ok" : "warn"
|
|
77826
|
-
}),
|
|
77827
|
-
...s.running ? [
|
|
77828
|
-
fact("window", "Window", s.headless ? "hidden (sandbox show opens it)" : "shown"),
|
|
77829
|
-
fact("uptime", "Uptime", uptime(s) ?? "-"),
|
|
77830
|
-
// The session count is its own fact: the helper only says whether it answers.
|
|
77831
|
-
fact("helper", "Helper", s.daemon === "-" ? "-" : s.daemon.startsWith("pong") ? "Responding" : s.daemon),
|
|
77832
|
-
fact("sessions", "Sessions", String(sessions))
|
|
77833
|
-
] : [],
|
|
77834
|
-
fact("account", "Account", s.account ?? "none selected (sandbox account use)"),
|
|
77835
|
-
fact("sync", "Background sync", s.syncWatcher ? "on" : "off")
|
|
77836
|
-
];
|
|
77837
|
-
}
|
|
77838
|
-
function projectsSection(o) {
|
|
77839
|
-
const roots = o.roots.map((r) => ({
|
|
77840
|
-
id: "root:" + r.host,
|
|
77841
|
-
label: "root",
|
|
77842
|
-
icon: "folder",
|
|
77843
|
-
hint: r.host + " -> " + r.guest + " (every project in it opens without a restart)"
|
|
77844
|
-
}));
|
|
77845
|
-
const here = o.projects.some((p) => p.here);
|
|
77846
|
-
return {
|
|
77847
|
-
id: "projects",
|
|
77848
|
-
title: "Projects",
|
|
77849
|
-
items: [...o.projects.map(projectItem2), ...roots],
|
|
77850
|
-
notes: [
|
|
77851
|
-
here ? { text: "* = the project of this folder", tone: "muted" } : { text: o.here.folder + " is not in this sandbox (start it here: sandbox claude)", tone: "muted" }
|
|
77852
|
-
]
|
|
77853
|
-
};
|
|
77854
|
-
}
|
|
77855
|
-
function guardSection(o) {
|
|
77856
|
-
const findings = o.repoGuard.findings;
|
|
77857
|
-
return {
|
|
77858
|
-
id: "guard",
|
|
77859
|
-
title: "Repo guard",
|
|
77860
|
-
summary: "Watching " + plural2(o.repoGuard.guarded, "folder") + ". " + (findings.length ? plural2(findings.length, "change") + " from the sandbox were blocked:" : "Nothing from the sandbox needed blocking."),
|
|
77861
|
-
items: findings.slice(-10).map((f, i) => ({ id: "guard:" + i, label: f, tone: "warn" }))
|
|
77862
|
-
};
|
|
77863
|
-
}
|
|
77864
|
-
function sandboxPage(o) {
|
|
77865
|
-
const notice = o.shared ? {
|
|
77866
|
-
text: o.shared.line + (o.shared.limited ? "" : ". Its window is on their desktop; use its agents here."),
|
|
77867
|
-
tone: "accent"
|
|
77868
|
-
} : void 0;
|
|
77869
|
-
if (o.shared?.limited) return { title: "Sandbox", notice, sections: [] };
|
|
77870
|
-
const running = o.sandbox.running;
|
|
77871
|
-
const status = {
|
|
77872
|
-
id: "status",
|
|
77873
|
-
title: "Status",
|
|
77874
|
-
facts: facts(o),
|
|
77875
|
-
// Only the window action that changes something: show a hidden window, hide a shown one.
|
|
77876
|
-
actions: running ? [
|
|
77877
|
-
o.sandbox.headless ? action(showWindow, {}, { key: "w", verb: "show", importance: 3 }) : action(hideWindow, {}, { key: "h", verb: "hide", importance: 3 }),
|
|
77878
|
-
action(stopSandbox, {}, { key: "x", verb: "stop", importance: 1 })
|
|
77879
|
-
] : []
|
|
77880
|
-
};
|
|
77881
|
-
const others = {
|
|
77882
|
-
id: "other-sessions",
|
|
77883
|
-
title: "Other sessions",
|
|
77884
|
-
items: o.otherSessions.map((s) => ({
|
|
77885
|
-
id: "session:" + s.id,
|
|
77886
|
-
label: "session " + s.id,
|
|
77887
|
-
hint: s.harness + " " + s.cwd
|
|
77888
|
-
}))
|
|
77889
|
-
};
|
|
77890
|
-
return {
|
|
77891
|
-
title: "Sandbox",
|
|
77892
|
-
notice,
|
|
77893
|
-
sections: [
|
|
77894
|
-
status,
|
|
77895
|
-
...running ? [projectsSection(o)] : [],
|
|
77896
|
-
...others.items?.length ? [others] : [],
|
|
77897
|
-
guardSection(o)
|
|
77898
|
-
]
|
|
77899
|
-
};
|
|
77900
|
-
}
|
|
77901
|
-
var sandboxView = defineView({
|
|
77902
|
-
id: "sandbox",
|
|
77903
|
-
title: "Sandbox",
|
|
77904
|
-
summary: "Status, projects and controls of the running sandbox.",
|
|
77905
|
-
queries: { status: sandboxStatus },
|
|
77906
|
-
operations: [showWindow, hideWindow, stopSandbox],
|
|
77907
|
-
build: ({ status }) => sandboxPage(status)
|
|
77908
|
-
});
|
|
77909
|
-
|
|
77910
|
-
// src/app/features/sandbox/sandbox.run.ts
|
|
77911
|
-
import path173 from "node:path";
|
|
77912
|
-
function limitedStatus(view, folder) {
|
|
77913
|
-
const owner = view.owner;
|
|
77914
|
-
return {
|
|
77915
|
-
shared: { line: view.line ?? "", limited: true, startedBy: owner?.kind === "other" ? owner.user : null },
|
|
77916
|
-
sandbox: {
|
|
77917
|
-
running: true,
|
|
77918
|
-
phase: "unknown",
|
|
77919
|
-
startedAt: null,
|
|
77920
|
-
daemon: "-",
|
|
77921
|
-
account: null,
|
|
77922
|
-
syncWatcher: false
|
|
77923
|
-
},
|
|
77924
|
-
here: { folder, project: null, guest: null },
|
|
77925
|
-
roots: [],
|
|
77926
|
-
projects: [],
|
|
77927
|
-
otherSessions: [],
|
|
77928
|
-
repoGuard: { guarded: 0, findings: [] }
|
|
77929
|
-
};
|
|
77930
|
-
}
|
|
77931
|
-
var sandboxStatusRun = implement(sandboxStatus, async (asked) => {
|
|
77932
|
-
const view = lookAtSandbox(asked);
|
|
77933
|
-
if (view.limited) return limitedStatus(view, hereDir(asked));
|
|
77934
|
-
const report = await new SandboxService(view.ctx).status();
|
|
77935
|
-
return {
|
|
77936
|
-
...view.line ? { shared: { line: view.line, limited: false, startedBy: view.ctx.borrowed?.user ?? null } } : {},
|
|
77937
|
-
...report,
|
|
77938
|
-
sandbox: { ...report.sandbox, daemon: safeText(report.sandbox.daemon) }
|
|
77939
|
-
};
|
|
77940
|
-
});
|
|
77941
|
-
var showWindowRun = implement(showWindow, async (ctx, _input, io) => {
|
|
77942
|
-
requireOnPc("show");
|
|
77943
|
-
requireRunning();
|
|
77944
|
-
await useSharedSandbox(ctx, { kind: "owner-only", what: "show the window", window: true }, typedConfirm(io));
|
|
77945
|
-
new SandboxService(ctx).showWindow();
|
|
77946
|
-
return { message: "Sandbox window shown." };
|
|
77947
|
-
});
|
|
77948
|
-
var hideWindowRun = implement(hideWindow, async (ctx, _input, io) => {
|
|
77949
|
-
requireOnPc("hide");
|
|
77950
|
-
requireRunning();
|
|
77951
|
-
await useSharedSandbox(ctx, { kind: "owner-only", what: "hide the window", window: true }, typedConfirm(io));
|
|
77952
|
-
io.progress("Hiding the window");
|
|
77953
|
-
await new SandboxService(ctx).hideWindow();
|
|
77954
|
-
return { message: "Sandbox window hidden. Bring it back with: sbx-show" };
|
|
77955
|
-
});
|
|
77956
|
-
var stopSandboxRun = implement(stopSandbox, async (asked, _input, io) => {
|
|
77957
|
-
requireOnPc("stop");
|
|
77958
|
-
if (!new SandboxService(asked).isRunning()) return { stopped: false, message: "No Windows Sandbox is running." };
|
|
77959
|
-
const ctx = runningOwner().kind === "mine" ? asked : await useSharedSandbox(
|
|
77960
|
-
asked,
|
|
77961
|
-
(shared) => ({ kind: "stop", sessions: new SandboxChannel(shared.dataPath).currentSessions().length }),
|
|
77962
|
-
typedConfirm(io)
|
|
77963
|
-
);
|
|
77964
|
-
const service = new SandboxService(ctx);
|
|
77965
|
-
const { sandbox, running, others } = service.stopPlan();
|
|
77966
|
-
if (running.length)
|
|
77967
|
-
io.log(
|
|
77968
|
-
"Stopping ends " + running.length + " session(s): " + running.map((s) => safeText(s.harness) + " in " + safeText(s.project ?? path173.win32.basename(s.cwd))).join(", ") + "."
|
|
77969
|
-
);
|
|
77970
|
-
const yes = await io.confirm({
|
|
77971
|
-
title: others.length ? "Stop the whole sandbox?" : "Stop the sandbox?",
|
|
77972
|
-
lines: others.length ? [others.length + " of them belong to other projects."] : [],
|
|
77973
|
-
yes: "Stop",
|
|
77974
|
-
danger: true,
|
|
77975
|
-
implied: others.length === 0,
|
|
77976
|
-
noTerminal: {
|
|
77977
|
-
message: others.length + " of these sessions belong to other projects, so the sandbox was not stopped.",
|
|
77978
|
-
fix: "Stop it anyway with: sandbox stop --yes"
|
|
77979
|
-
}
|
|
77980
|
-
});
|
|
77981
|
-
if (!yes) cancelled();
|
|
77982
|
-
io.progress("Asking the sandbox to shut down");
|
|
77983
|
-
await service.shutdown(sandbox);
|
|
77984
|
-
return { stopped: true, message: "Sandbox is shutting down; history and claude-mem sync back when it closes." };
|
|
77985
|
-
});
|
|
77986
|
-
|
|
77987
77785
|
// src/app/features/logs/logs.def.ts
|
|
77988
77786
|
var CRASH = "@crash";
|
|
77989
77787
|
var MAX_SHOWN = 1e3;
|
|
@@ -78083,6 +77881,323 @@ var logsView = defineView({
|
|
|
78083
77881
|
build: ({ files, tail }) => logsPage(files, tail)
|
|
78084
77882
|
});
|
|
78085
77883
|
|
|
77884
|
+
// src/app/features/sandbox/sandbox.def.ts
|
|
77885
|
+
var ProjectSchema = external_exports.object({
|
|
77886
|
+
name: external_exports.string().describe("The project"),
|
|
77887
|
+
host: external_exports.string().describe("Its folder on this PC"),
|
|
77888
|
+
guest: external_exports.string().describe("Its folder in the sandbox"),
|
|
77889
|
+
mechanism: external_exports.string().describe("How it got into the sandbox (launch, root, boot, wsb or hcs)"),
|
|
77890
|
+
here: external_exports.boolean().describe("The project of the current folder"),
|
|
77891
|
+
setup: external_exports.object({ phase: external_exports.string(), message: external_exports.string() }).describe("How far its setup is"),
|
|
77892
|
+
databases: external_exports.array(external_exports.object({ database: external_exports.string(), port: external_exports.number(), state: external_exports.string() })),
|
|
77893
|
+
sessions: external_exports.array(external_exports.object({ id: external_exports.string(), harness: external_exports.string(), viewers: external_exports.string(), started: external_exports.string() })),
|
|
77894
|
+
repoGuard: external_exports.array(external_exports.string()).describe("Repo guard findings in its folder")
|
|
77895
|
+
});
|
|
77896
|
+
var StateSchema2 = external_exports.object({
|
|
77897
|
+
running: external_exports.boolean().describe("A Windows Sandbox is running"),
|
|
77898
|
+
phase: external_exports.string().describe("What it is doing (ready, provisioning ...)"),
|
|
77899
|
+
message: external_exports.string().optional().describe("Its last message"),
|
|
77900
|
+
headless: external_exports.boolean().optional().describe("Its window is hidden"),
|
|
77901
|
+
startedAt: external_exports.number().nullable().describe("When it started (ms), if known"),
|
|
77902
|
+
daemon: external_exports.string().describe("The sandbox helper's answer"),
|
|
77903
|
+
account: external_exports.string().nullable().describe("The Claude login it uses"),
|
|
77904
|
+
syncWatcher: external_exports.boolean().describe("The background history sync runs")
|
|
77905
|
+
});
|
|
77906
|
+
var SandboxStatusSchema2 = external_exports.object({
|
|
77907
|
+
/** Present when the running sandbox is another user's. */
|
|
77908
|
+
shared: external_exports.object({
|
|
77909
|
+
line: external_exports.string().describe('"Running, started by alice (shared with you)"'),
|
|
77910
|
+
limited: external_exports.boolean().describe("Not shared with you: nothing more is shown"),
|
|
77911
|
+
startedBy: external_exports.string().nullable().describe("Whose it is")
|
|
77912
|
+
}).optional(),
|
|
77913
|
+
sandbox: StateSchema2,
|
|
77914
|
+
here: external_exports.object({ folder: external_exports.string(), project: external_exports.string().nullable(), guest: external_exports.string().nullable() }),
|
|
77915
|
+
roots: external_exports.array(external_exports.object({ host: external_exports.string(), guest: external_exports.string() })),
|
|
77916
|
+
projects: external_exports.array(ProjectSchema),
|
|
77917
|
+
otherSessions: external_exports.array(external_exports.object({ id: external_exports.string(), harness: external_exports.string(), cwd: external_exports.string() })),
|
|
77918
|
+
repoGuard: external_exports.object({ guarded: external_exports.number(), findings: external_exports.array(external_exports.string()) }),
|
|
77919
|
+
/** How the last sandbox ended when nobody closed it (it is not running now). */
|
|
77920
|
+
lastEnd: external_exports.object({ text: external_exports.string().describe("What happened"), at: external_exports.string().describe("When (ISO time)") }).optional().describe("The last sandbox ended unexpectedly")
|
|
77921
|
+
});
|
|
77922
|
+
var MessageSchema5 = external_exports.object({ message: external_exports.string().describe("What happened") });
|
|
77923
|
+
function uptime(s) {
|
|
77924
|
+
return s.running && s.startedAt ? ago(s.startedAt).replace(" ago", "") : null;
|
|
77925
|
+
}
|
|
77926
|
+
var sandboxStatus = defineQuery({
|
|
77927
|
+
id: "sandbox.status",
|
|
77928
|
+
title: "Sandbox status",
|
|
77929
|
+
summary: "show what the sandbox is doing",
|
|
77930
|
+
input: external_exports.object({}),
|
|
77931
|
+
output: SandboxStatusSchema2,
|
|
77932
|
+
live: { everyMs: 2500 },
|
|
77933
|
+
cli: {
|
|
77934
|
+
name: "status",
|
|
77935
|
+
group: "Sessions",
|
|
77936
|
+
json: true,
|
|
77937
|
+
// The --json shape `sandbox status` has always printed.
|
|
77938
|
+
shapeJson: (o) => {
|
|
77939
|
+
if (o.shared?.limited) return { sandbox: { running: true, shared: false, startedBy: o.shared.startedBy } };
|
|
77940
|
+
const s = o.sandbox;
|
|
77941
|
+
const borrowed = o.shared?.startedBy ? { startedBy: o.shared.startedBy, shared: true } : {};
|
|
77942
|
+
return {
|
|
77943
|
+
sandbox: {
|
|
77944
|
+
...borrowed,
|
|
77945
|
+
running: s.running,
|
|
77946
|
+
phase: s.phase,
|
|
77947
|
+
message: s.message,
|
|
77948
|
+
headless: s.headless,
|
|
77949
|
+
uptime: uptime(s),
|
|
77950
|
+
daemon: s.daemon,
|
|
77951
|
+
account: s.account,
|
|
77952
|
+
syncWatcher: s.syncWatcher
|
|
77953
|
+
},
|
|
77954
|
+
here: o.here,
|
|
77955
|
+
roots: o.roots,
|
|
77956
|
+
projects: o.projects,
|
|
77957
|
+
otherSessions: o.otherSessions,
|
|
77958
|
+
repoGuard: o.repoGuard,
|
|
77959
|
+
...o.lastEnd ? { lastEnd: o.lastEnd } : {}
|
|
77960
|
+
};
|
|
77961
|
+
}
|
|
77962
|
+
}
|
|
77963
|
+
});
|
|
77964
|
+
var showWindow = defineOperation({
|
|
77965
|
+
id: "sandbox.showWindow",
|
|
77966
|
+
access: "control",
|
|
77967
|
+
title: "Show the window",
|
|
77968
|
+
summary: "show the sandbox window",
|
|
77969
|
+
input: external_exports.object({}),
|
|
77970
|
+
output: MessageSchema5,
|
|
77971
|
+
text: (o) => o.message,
|
|
77972
|
+
cli: { name: "show", group: "Sessions" }
|
|
77973
|
+
});
|
|
77974
|
+
var hideWindow = defineOperation({
|
|
77975
|
+
id: "sandbox.hideWindow",
|
|
77976
|
+
access: "control",
|
|
77977
|
+
title: "Hide the window",
|
|
77978
|
+
summary: "hide the sandbox window (it keeps running)",
|
|
77979
|
+
input: external_exports.object({}),
|
|
77980
|
+
output: MessageSchema5,
|
|
77981
|
+
text: (o) => o.message,
|
|
77982
|
+
cli: { name: "hide", group: "Sessions" }
|
|
77983
|
+
});
|
|
77984
|
+
var stopSandbox = defineOperation({
|
|
77985
|
+
id: "sandbox.stop",
|
|
77986
|
+
access: "control",
|
|
77987
|
+
title: "Stop the sandbox\u2026",
|
|
77988
|
+
summary: "shut the sandbox down (every project's sessions end)",
|
|
77989
|
+
input: external_exports.object({}),
|
|
77990
|
+
output: external_exports.object({ stopped: external_exports.boolean().describe("It was asked to shut down"), message: external_exports.string() }),
|
|
77991
|
+
danger: true,
|
|
77992
|
+
text: (o) => o.message,
|
|
77993
|
+
cli: { name: "stop", group: "Agents", yes: "do not ask when other projects' sessions would end" }
|
|
77994
|
+
});
|
|
77995
|
+
var MECHANISM_TEXT = {
|
|
77996
|
+
launch: "started from here",
|
|
77997
|
+
root: "in a project root",
|
|
77998
|
+
boot: "mapped at start",
|
|
77999
|
+
wsb: "wsb share",
|
|
78000
|
+
hcs: "live folder through HCS, alpha"
|
|
78001
|
+
};
|
|
78002
|
+
function projectItem2(p) {
|
|
78003
|
+
return {
|
|
78004
|
+
id: "project:" + p.name,
|
|
78005
|
+
label: p.name,
|
|
78006
|
+
icon: "project",
|
|
78007
|
+
hint: p.host + " -> " + p.guest + " (" + (MECHANISM_TEXT[p.mechanism] ?? "wsb share") + ")",
|
|
78008
|
+
marked: p.here,
|
|
78009
|
+
details: [
|
|
78010
|
+
"Setup: " + p.setup.phase + (p.setup.phase === "ready" ? "" : " - " + p.setup.message),
|
|
78011
|
+
...p.databases.length ? ["Databases: " + p.databases.map((d) => d.database + " :" + d.port + " " + d.state).join(", ")] : [],
|
|
78012
|
+
...p.sessions.map((s) => "Session: " + s.harness + " " + s.id + ", " + s.viewers + ", started " + ago(s.started)),
|
|
78013
|
+
...p.repoGuard.map((f) => "Guard: " + f)
|
|
78014
|
+
]
|
|
78015
|
+
};
|
|
78016
|
+
}
|
|
78017
|
+
function facts(o) {
|
|
78018
|
+
const s = o.sandbox;
|
|
78019
|
+
const sessions = o.projects.reduce((n, p) => n + p.sessions.length, 0) + o.otherSessions.length;
|
|
78020
|
+
const fact = (id, label2, value, extra = {}) => ({
|
|
78021
|
+
id,
|
|
78022
|
+
label: label2,
|
|
78023
|
+
value,
|
|
78024
|
+
...extra
|
|
78025
|
+
});
|
|
78026
|
+
return [
|
|
78027
|
+
...o.shared?.startedBy ? [fact("owner", "Started by", o.shared.startedBy)] : [],
|
|
78028
|
+
// One line for what it is doing: "Ready", "Installing Rust ...", or "not running".
|
|
78029
|
+
fact("state", "State", s.running ? s.message || s.phase : "not running", {
|
|
78030
|
+
tone: !s.running ? "muted" : s.phase === "ready" ? "ok" : "warn"
|
|
78031
|
+
}),
|
|
78032
|
+
...s.running ? [
|
|
78033
|
+
fact("window", "Window", s.headless ? "hidden (sandbox show opens it)" : "shown"),
|
|
78034
|
+
fact("uptime", "Uptime", uptime(s) ?? "-"),
|
|
78035
|
+
// The session count is its own fact: the helper only says whether it answers.
|
|
78036
|
+
fact("helper", "Helper", s.daemon === "-" ? "-" : s.daemon.startsWith("pong") ? "Responding" : s.daemon),
|
|
78037
|
+
fact("sessions", "Sessions", String(sessions))
|
|
78038
|
+
] : [],
|
|
78039
|
+
...o.lastEnd ? [fact("lastEnd", "Last sandbox", o.lastEnd.text, { tone: "warn" })] : [],
|
|
78040
|
+
fact("account", "Account", s.account ?? "none selected (sandbox account use)"),
|
|
78041
|
+
fact("sync", "Background sync", s.syncWatcher ? "on" : "off")
|
|
78042
|
+
];
|
|
78043
|
+
}
|
|
78044
|
+
function projectsSection(o) {
|
|
78045
|
+
const roots = o.roots.map((r) => ({
|
|
78046
|
+
id: "root:" + r.host,
|
|
78047
|
+
label: "root",
|
|
78048
|
+
icon: "folder",
|
|
78049
|
+
hint: r.host + " -> " + r.guest + " (every project in it opens without a restart)"
|
|
78050
|
+
}));
|
|
78051
|
+
const here = o.projects.some((p) => p.here);
|
|
78052
|
+
return {
|
|
78053
|
+
id: "projects",
|
|
78054
|
+
title: "Projects",
|
|
78055
|
+
items: [...o.projects.map(projectItem2), ...roots],
|
|
78056
|
+
notes: [
|
|
78057
|
+
here ? { text: "* = the project of this folder", tone: "muted" } : { text: o.here.folder + " is not in this sandbox (start it here: sandbox claude)", tone: "muted" }
|
|
78058
|
+
]
|
|
78059
|
+
};
|
|
78060
|
+
}
|
|
78061
|
+
function guardSection(o) {
|
|
78062
|
+
const findings = o.repoGuard.findings;
|
|
78063
|
+
return {
|
|
78064
|
+
id: "guard",
|
|
78065
|
+
title: "Repo guard",
|
|
78066
|
+
summary: "Watching " + plural2(o.repoGuard.guarded, "folder") + ". " + (findings.length ? plural2(findings.length, "change") + " from the sandbox were blocked:" : "Nothing from the sandbox needed blocking."),
|
|
78067
|
+
items: findings.slice(-10).map((f, i) => ({ id: "guard:" + i, label: f, tone: "warn" }))
|
|
78068
|
+
};
|
|
78069
|
+
}
|
|
78070
|
+
function sandboxPage(o) {
|
|
78071
|
+
const notice = o.shared ? {
|
|
78072
|
+
text: o.shared.line + (o.shared.limited ? "" : ". Its window is on their desktop; use its agents here."),
|
|
78073
|
+
tone: "accent"
|
|
78074
|
+
} : void 0;
|
|
78075
|
+
if (o.shared?.limited) return { title: "Sandbox", notice, sections: [] };
|
|
78076
|
+
const running = o.sandbox.running;
|
|
78077
|
+
const status = {
|
|
78078
|
+
id: "status",
|
|
78079
|
+
title: "Status",
|
|
78080
|
+
facts: facts(o),
|
|
78081
|
+
// Only the window action that changes something: show a hidden window, hide a shown one.
|
|
78082
|
+
actions: running ? [
|
|
78083
|
+
o.sandbox.headless ? action(showWindow, {}, { key: "w", verb: "show", importance: 3 }) : action(hideWindow, {}, { key: "h", verb: "hide", importance: 3 }),
|
|
78084
|
+
action(stopSandbox, {}, { key: "x", verb: "stop", importance: 1 })
|
|
78085
|
+
] : o.lastEnd ? [
|
|
78086
|
+
link({ kind: "view", view: "logs", params: { name: CRASH } }, "Show crash report", {
|
|
78087
|
+
verb: "show",
|
|
78088
|
+
importance: 2
|
|
78089
|
+
})
|
|
78090
|
+
] : []
|
|
78091
|
+
};
|
|
78092
|
+
const others = {
|
|
78093
|
+
id: "other-sessions",
|
|
78094
|
+
title: "Other sessions",
|
|
78095
|
+
items: o.otherSessions.map((s) => ({
|
|
78096
|
+
id: "session:" + s.id,
|
|
78097
|
+
label: "session " + s.id,
|
|
78098
|
+
hint: s.harness + " " + s.cwd
|
|
78099
|
+
}))
|
|
78100
|
+
};
|
|
78101
|
+
return {
|
|
78102
|
+
title: "Sandbox",
|
|
78103
|
+
notice,
|
|
78104
|
+
sections: [
|
|
78105
|
+
status,
|
|
78106
|
+
...running ? [projectsSection(o)] : [],
|
|
78107
|
+
...others.items?.length ? [others] : [],
|
|
78108
|
+
guardSection(o)
|
|
78109
|
+
]
|
|
78110
|
+
};
|
|
78111
|
+
}
|
|
78112
|
+
var sandboxView = defineView({
|
|
78113
|
+
id: "sandbox",
|
|
78114
|
+
title: "Sandbox",
|
|
78115
|
+
summary: "Status, projects and controls of the running sandbox.",
|
|
78116
|
+
queries: { status: sandboxStatus },
|
|
78117
|
+
operations: [showWindow, hideWindow, stopSandbox],
|
|
78118
|
+
build: ({ status }) => sandboxPage(status)
|
|
78119
|
+
});
|
|
78120
|
+
|
|
78121
|
+
// src/app/features/sandbox/sandbox.run.ts
|
|
78122
|
+
import path173 from "node:path";
|
|
78123
|
+
function limitedStatus(view, folder) {
|
|
78124
|
+
const owner = view.owner;
|
|
78125
|
+
return {
|
|
78126
|
+
shared: { line: view.line ?? "", limited: true, startedBy: owner?.kind === "other" ? owner.user : null },
|
|
78127
|
+
sandbox: {
|
|
78128
|
+
running: true,
|
|
78129
|
+
phase: "unknown",
|
|
78130
|
+
startedAt: null,
|
|
78131
|
+
daemon: "-",
|
|
78132
|
+
account: null,
|
|
78133
|
+
syncWatcher: false
|
|
78134
|
+
},
|
|
78135
|
+
here: { folder, project: null, guest: null },
|
|
78136
|
+
roots: [],
|
|
78137
|
+
projects: [],
|
|
78138
|
+
otherSessions: [],
|
|
78139
|
+
repoGuard: { guarded: 0, findings: [] }
|
|
78140
|
+
};
|
|
78141
|
+
}
|
|
78142
|
+
var sandboxStatusRun = implement(sandboxStatus, async (asked) => {
|
|
78143
|
+
const view = lookAtSandbox(asked);
|
|
78144
|
+
if (view.limited) return limitedStatus(view, hereDir(asked));
|
|
78145
|
+
const report = await new SandboxService(view.ctx).status();
|
|
78146
|
+
const { storageRoot, dataPath } = view.ctx;
|
|
78147
|
+
const lastEnd = readLastEnd(() => new LifeRecord(storageRoot, dataPath).read(), dataPath, report.sandbox.running);
|
|
78148
|
+
return {
|
|
78149
|
+
...lastEnd ? { lastEnd } : {},
|
|
78150
|
+
...view.line ? { shared: { line: view.line, limited: false, startedBy: view.ctx.borrowed?.user ?? null } } : {},
|
|
78151
|
+
...report,
|
|
78152
|
+
sandbox: { ...report.sandbox, daemon: safeText(report.sandbox.daemon) }
|
|
78153
|
+
};
|
|
78154
|
+
});
|
|
78155
|
+
var showWindowRun = implement(showWindow, async (ctx, _input, io) => {
|
|
78156
|
+
requireOnPc("show");
|
|
78157
|
+
requireRunning();
|
|
78158
|
+
await useSharedSandbox(ctx, { kind: "owner-only", what: "show the window", window: true }, typedConfirm(io));
|
|
78159
|
+
new SandboxService(ctx).showWindow();
|
|
78160
|
+
return { message: "Sandbox window shown." };
|
|
78161
|
+
});
|
|
78162
|
+
var hideWindowRun = implement(hideWindow, async (ctx, _input, io) => {
|
|
78163
|
+
requireOnPc("hide");
|
|
78164
|
+
requireRunning();
|
|
78165
|
+
await useSharedSandbox(ctx, { kind: "owner-only", what: "hide the window", window: true }, typedConfirm(io));
|
|
78166
|
+
io.progress("Hiding the window");
|
|
78167
|
+
await new SandboxService(ctx).hideWindow();
|
|
78168
|
+
return { message: "Sandbox window hidden. Bring it back with: sbx-show" };
|
|
78169
|
+
});
|
|
78170
|
+
var stopSandboxRun = implement(stopSandbox, async (asked, _input, io) => {
|
|
78171
|
+
requireOnPc("stop");
|
|
78172
|
+
if (!new SandboxService(asked).isRunning()) return { stopped: false, message: "No Windows Sandbox is running." };
|
|
78173
|
+
const ctx = runningOwner().kind === "mine" ? asked : await useSharedSandbox(
|
|
78174
|
+
asked,
|
|
78175
|
+
(shared) => ({ kind: "stop", sessions: new SandboxChannel(shared.dataPath).currentSessions().length }),
|
|
78176
|
+
typedConfirm(io)
|
|
78177
|
+
);
|
|
78178
|
+
const service = new SandboxService(ctx);
|
|
78179
|
+
const { sandbox, running, others } = service.stopPlan();
|
|
78180
|
+
if (running.length)
|
|
78181
|
+
io.log(
|
|
78182
|
+
"Stopping ends " + running.length + " session(s): " + running.map((s) => safeText(s.harness) + " in " + safeText(s.project ?? path173.win32.basename(s.cwd))).join(", ") + "."
|
|
78183
|
+
);
|
|
78184
|
+
const yes = await io.confirm({
|
|
78185
|
+
title: others.length ? "Stop the whole sandbox?" : "Stop the sandbox?",
|
|
78186
|
+
lines: others.length ? [others.length + " of them belong to other projects."] : [],
|
|
78187
|
+
yes: "Stop",
|
|
78188
|
+
danger: true,
|
|
78189
|
+
implied: others.length === 0,
|
|
78190
|
+
noTerminal: {
|
|
78191
|
+
message: others.length + " of these sessions belong to other projects, so the sandbox was not stopped.",
|
|
78192
|
+
fix: "Stop it anyway with: sandbox stop --yes"
|
|
78193
|
+
}
|
|
78194
|
+
});
|
|
78195
|
+
if (!yes) cancelled();
|
|
78196
|
+
io.progress("Asking the sandbox to shut down");
|
|
78197
|
+
await service.shutdown(sandbox);
|
|
78198
|
+
return { stopped: true, message: "Sandbox is shutting down; history and claude-mem sync back when it closes." };
|
|
78199
|
+
});
|
|
78200
|
+
|
|
78086
78201
|
// src/app/features/logs/logs.run.ts
|
|
78087
78202
|
function lastLines(text, n) {
|
|
78088
78203
|
return text.split("\n").slice(-n).join("\n");
|
|
@@ -80442,7 +80557,7 @@ async function attachOpened(ctx, session) {
|
|
|
80442
80557
|
nextStep("sandbox attach " + session);
|
|
80443
80558
|
return 0;
|
|
80444
80559
|
}
|
|
80445
|
-
const { attach: attach2 } = await import("./chunk-
|
|
80560
|
+
const { attach: attach2 } = await import("./chunk-M6XUQK53.js");
|
|
80446
80561
|
return attach2({ ...ctx, options: { ...ctx.options, positionals: [session] } });
|
|
80447
80562
|
}
|
|
80448
80563
|
function definitionHandler(def) {
|
|
@@ -81292,13 +81407,13 @@ export {
|
|
|
81292
81407
|
StorageHistory,
|
|
81293
81408
|
planOffer,
|
|
81294
81409
|
staleMountNote,
|
|
81410
|
+
RunDir,
|
|
81295
81411
|
LifeRecord,
|
|
81296
81412
|
shouldReopen,
|
|
81297
81413
|
describeLast,
|
|
81298
81414
|
SessionLedger,
|
|
81299
81415
|
currentUserName,
|
|
81300
81416
|
runningOwner,
|
|
81301
|
-
RunDir,
|
|
81302
81417
|
describeCopy,
|
|
81303
81418
|
takeHiddenInstallNotice,
|
|
81304
81419
|
protectPrivateFolder,
|
|
@@ -81328,14 +81443,14 @@ export {
|
|
|
81328
81443
|
showData,
|
|
81329
81444
|
cleanData,
|
|
81330
81445
|
dataPage,
|
|
81331
|
-
sandboxStatus,
|
|
81332
|
-
sandboxPage,
|
|
81333
|
-
listProjects,
|
|
81334
|
-
projectsPage,
|
|
81335
81446
|
CRASH,
|
|
81336
81447
|
listLogs,
|
|
81337
81448
|
tailLog,
|
|
81338
81449
|
logsPage,
|
|
81450
|
+
sandboxStatus,
|
|
81451
|
+
sandboxPage,
|
|
81452
|
+
listProjects,
|
|
81453
|
+
projectsPage,
|
|
81339
81454
|
listAudit,
|
|
81340
81455
|
auditPage,
|
|
81341
81456
|
auditArgv,
|