windows-development-sandbox 1.0.5 → 1.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +22 -6
- package/config.example.json +17 -0
- package/dist/api.js +2 -1
- package/dist/cli/args.d.ts +20 -0
- package/dist/cli/args.js +35 -3
- package/dist/cli/colors.d.ts +24 -0
- package/dist/cli/colors.js +42 -0
- package/dist/cli/commands/agent-commands.d.ts +6 -0
- package/dist/cli/commands/agent-commands.js +61 -0
- package/dist/cli/commands/context.d.ts +2 -0
- package/dist/cli/commands/context.js +3 -0
- package/dist/cli/commands/data/clean.d.ts +15 -0
- package/dist/cli/commands/data/clean.js +101 -0
- package/dist/cli/commands/data/collect.d.ts +11 -0
- package/dist/cli/commands/data/collect.js +54 -0
- package/dist/cli/commands/data/index.d.ts +4 -0
- package/dist/cli/commands/data/index.js +45 -0
- package/dist/cli/commands/data/remove.d.ts +2 -0
- package/dist/cli/commands/data/remove.js +30 -0
- package/dist/cli/commands/index.d.ts +5 -3
- package/dist/cli/commands/index.js +5 -3
- package/dist/cli/commands/launch.d.ts +26 -2
- package/dist/cli/commands/launch.js +220 -39
- package/dist/cli/commands/maintenance.js +33 -4
- package/dist/cli/commands/project.d.ts +29 -0
- package/dist/cli/commands/project.js +63 -0
- package/dist/cli/commands/projects.d.ts +14 -0
- package/dist/cli/commands/projects.js +146 -0
- package/dist/cli/commands/repair/checks.d.ts +2 -0
- package/dist/cli/commands/{repair.js → repair/checks.js} +19 -181
- package/dist/cli/commands/{repair.d.ts → repair/finders.d.ts} +0 -29
- package/dist/cli/commands/repair/finders.js +108 -0
- package/dist/cli/commands/repair/index.d.ts +12 -0
- package/dist/cli/commands/repair/index.js +61 -0
- package/dist/cli/commands/repair/types.d.ts +26 -0
- package/dist/cli/commands/repair/types.js +3 -0
- package/dist/cli/commands/restart.d.ts +6 -0
- package/dist/cli/commands/restart.js +85 -0
- package/dist/cli/commands/sessions.d.ts +2 -1
- package/dist/cli/commands/sessions.js +189 -45
- package/dist/cli/commands/setup/checks.d.ts +13 -0
- package/dist/cli/commands/setup/checks.js +71 -0
- package/dist/cli/commands/{setup.d.ts → setup/facts.d.ts} +1 -15
- package/dist/cli/commands/setup/facts.js +60 -0
- package/dist/cli/commands/setup/index.d.ts +2 -0
- package/dist/cli/commands/setup/index.js +43 -0
- package/dist/cli/commands/user-config.d.ts +8 -0
- package/dist/cli/commands/user-config.js +41 -0
- package/dist/cli/history.d.ts +11 -0
- package/dist/cli/history.js +21 -0
- package/dist/cli/index.d.ts +7 -1
- package/dist/cli/index.js +38 -13
- package/dist/cli/manifest.d.ts +8 -0
- package/dist/cli/manifest.js +35 -15
- package/dist/cli/menu-multi.d.ts +12 -0
- package/dist/cli/menu-multi.js +82 -0
- package/dist/cli/menu.d.ts +5 -13
- package/dist/cli/menu.js +149 -46
- package/dist/cli/progress.d.ts +9 -0
- package/dist/cli/progress.js +49 -0
- package/dist/cli/settings/edit.d.ts +13 -0
- package/dist/cli/settings/edit.js +19 -0
- package/dist/cli/settings/index.d.ts +8 -0
- package/dist/cli/settings/index.js +131 -0
- package/dist/cli/settings/prompts.d.ts +7 -0
- package/dist/cli/settings/prompts.js +48 -0
- package/dist/cli/settings/schema.d.ts +33 -0
- package/dist/cli/settings/schema.js +29 -0
- package/dist/cli/validate.d.ts +17 -0
- package/dist/cli/validate.js +89 -0
- package/dist/core/config.js +4 -1
- package/dist/core/disk.d.ts +16 -0
- package/dist/core/disk.js +55 -0
- package/dist/core/local-config.d.ts +8 -0
- package/dist/core/local-config.js +18 -5
- package/dist/core/project-policy.d.ts +15 -0
- package/dist/core/project-policy.js +35 -0
- package/dist/core/prompt.d.ts +4 -0
- package/dist/core/prompt.js +15 -0
- package/dist/core/safe-paths.d.ts +4 -0
- package/dist/core/safe-paths.js +37 -0
- package/dist/core/schema/common.d.ts +17 -0
- package/dist/core/schema/common.js +15 -0
- package/dist/core/schema/config.d.ts +594 -0
- package/dist/core/schema/config.js +63 -0
- package/dist/core/schema/databases.d.ts +33 -0
- package/dist/core/schema/databases.js +35 -0
- package/dist/core/schema/harness.d.ts +12 -0
- package/dist/core/schema/harness.js +17 -0
- package/dist/core/schema/json-schema.d.ts +5 -0
- package/dist/core/schema/json-schema.js +25 -0
- package/dist/core/schema/profile.d.ts +27 -0
- package/dist/core/schema/profile.js +29 -0
- package/dist/core/schema/projects.d.ts +9 -0
- package/dist/core/schema/projects.js +11 -0
- package/dist/core/schema/sandbox.d.ts +50 -0
- package/dist/core/schema/sandbox.js +36 -0
- package/dist/core/schema/toolchains.d.ts +93 -0
- package/dist/core/schema/toolchains.js +83 -0
- package/dist/core/schema.d.ts +6 -604
- package/dist/core/schema.js +8 -264
- package/dist/core/types.d.ts +29 -1
- package/dist/core/util.d.ts +7 -0
- package/dist/core/util.js +12 -3
- package/dist/core/workspace.d.ts +0 -5
- package/dist/core/workspace.js +12 -46
- package/dist/guest/boot-env.d.ts +4 -0
- package/dist/guest/boot-env.js +25 -0
- package/dist/guest/cmdline.d.ts +12 -0
- package/dist/guest/cmdline.js +23 -0
- package/dist/guest/daemon/exec.d.ts +5 -0
- package/dist/guest/daemon/exec.js +32 -0
- package/dist/guest/daemon/project.d.ts +14 -0
- package/dist/guest/daemon/project.js +132 -0
- package/dist/guest/daemon/session.d.ts +13 -0
- package/dist/guest/daemon/session.js +139 -0
- package/dist/guest/daemon/shutdown.d.ts +1 -0
- package/dist/guest/daemon/shutdown.js +21 -0
- package/dist/guest/daemon/state.d.ts +17 -0
- package/dist/guest/daemon/state.js +31 -0
- package/dist/guest/daemon.js +8 -141
- package/dist/guest/db.js +18 -3
- package/dist/guest/env.d.ts +2 -0
- package/dist/guest/env.js +4 -2
- package/dist/guest/guide.d.ts +6 -2
- package/dist/guest/guide.js +5 -4
- package/dist/guest/helpers.d.ts +3 -0
- package/dist/guest/helpers.js +26 -0
- package/dist/guest/main.js +17 -7
- package/dist/guest/ports.d.ts +6 -0
- package/dist/guest/ports.js +22 -0
- package/dist/guest/project-context.d.ts +7 -0
- package/dist/guest/project-context.js +10 -0
- package/dist/guest/project-env.d.ts +20 -0
- package/dist/guest/project-env.js +20 -0
- package/dist/guest/project-status.d.ts +5 -0
- package/dist/guest/project-status.js +17 -0
- package/dist/guest/ptyhost.d.ts +0 -18
- package/dist/guest/ptyhost.js +5 -63
- package/dist/guest/setup/agents.d.ts +5 -2
- package/dist/guest/setup/agents.js +22 -8
- package/dist/guest/setup/claude.js +11 -3
- package/dist/guest/setup/environment.d.ts +13 -3
- package/dist/guest/setup/environment.js +14 -10
- package/dist/guest/setup/hooks.d.ts +9 -1
- package/dist/guest/setup/hooks.js +28 -14
- package/dist/guest/toolchains/catalogue.d.ts +4 -0
- package/dist/guest/toolchains/catalogue.js +2 -2
- package/dist/guest/toolchains/cli.js +25 -17
- package/dist/guest/toolchains/common.d.ts +18 -1
- package/dist/guest/toolchains/common.js +34 -11
- package/dist/guest/toolchains/databases/dynamodb.d.ts +8 -0
- package/dist/guest/toolchains/databases/dynamodb.js +52 -0
- package/dist/guest/toolchains/{databases.d.ts → databases/index.d.ts} +2 -3
- package/dist/guest/toolchains/databases/index.js +62 -0
- package/dist/guest/toolchains/databases/mongodb.d.ts +3 -0
- package/dist/guest/toolchains/databases/mongodb.js +31 -0
- package/dist/guest/toolchains/databases/mysql.d.ts +3 -0
- package/dist/guest/toolchains/databases/mysql.js +38 -0
- package/dist/guest/toolchains/databases/postgres.d.ts +5 -0
- package/dist/guest/toolchains/databases/postgres.js +38 -0
- package/dist/guest/toolchains/databases/redis.d.ts +3 -0
- package/dist/guest/toolchains/databases/redis.js +16 -0
- package/dist/guest/toolchains/databases/server.d.ts +14 -0
- package/dist/guest/toolchains/databases/server.js +52 -0
- package/dist/guest/toolchains/git.js +25 -16
- package/dist/guest/toolchains/go.js +1 -1
- package/dist/guest/toolchains/index.d.ts +13 -9
- package/dist/guest/toolchains/index.js +40 -82
- package/dist/guest/toolchains/java.js +5 -5
- package/dist/guest/toolchains/leftovers.d.ts +2 -0
- package/dist/guest/toolchains/leftovers.js +26 -0
- package/dist/guest/toolchains/managers.js +12 -10
- package/dist/guest/toolchains/{binstall.js → native/binstall.js} +6 -9
- package/dist/guest/toolchains/native/cpp.d.ts +4 -0
- package/dist/guest/toolchains/native/cpp.js +38 -0
- package/dist/guest/toolchains/native/dotnet.d.ts +4 -0
- package/dist/guest/toolchains/native/dotnet.js +103 -0
- package/dist/guest/toolchains/native/mesa.d.ts +7 -0
- package/dist/guest/toolchains/native/mesa.js +46 -0
- package/dist/guest/toolchains/native/msvc/environment.d.ts +16 -0
- package/dist/guest/toolchains/native/msvc/environment.js +57 -0
- package/dist/guest/toolchains/native/msvc/index.d.ts +6 -0
- package/dist/guest/toolchains/native/msvc/index.js +48 -0
- package/dist/guest/toolchains/native/msvc/packages.d.ts +6 -0
- package/dist/guest/toolchains/native/msvc/packages.js +156 -0
- package/dist/guest/toolchains/native/msvc/tree.d.ts +15 -0
- package/dist/guest/toolchains/native/msvc/tree.js +21 -0
- package/dist/guest/toolchains/native/rust.d.ts +3 -0
- package/dist/guest/toolchains/native/rust.js +45 -0
- package/dist/guest/toolchains/node.js +26 -6
- package/dist/guest/toolchains/plan.d.ts +22 -0
- package/dist/guest/toolchains/plan.js +96 -0
- package/dist/guest/toolchains/pwsh.js +1 -1
- package/dist/guest/toolchains/python.js +17 -10
- package/dist/guest/toolchains/runtimes.d.ts +2 -0
- package/dist/guest/toolchains/runtimes.js +6 -8
- package/dist/guest/toolchains/scripting/php.d.ts +3 -0
- package/dist/guest/toolchains/scripting/php.js +71 -0
- package/dist/guest/toolchains/scripting/ruby.d.ts +3 -0
- package/dist/guest/toolchains/scripting/ruby.js +47 -0
- package/dist/guest/toolchains/scripting/vcruntime.d.ts +7 -0
- package/dist/guest/toolchains/scripting/vcruntime.js +45 -0
- package/dist/guest/view.js +21 -1
- package/dist/host/accounts.js +14 -5
- package/dist/host/agent-commands.d.ts +25 -0
- package/dist/host/agent-commands.js +145 -0
- package/dist/host/attach.d.ts +17 -0
- package/dist/host/attach.js +69 -0
- package/dist/host/channel.d.ts +23 -22
- package/dist/host/channel.js +66 -84
- package/dist/host/claude-profile.js +7 -3
- package/dist/host/cleanup.js +2 -11
- package/dist/host/database-plan.d.ts +12 -0
- package/dist/host/database-plan.js +52 -0
- package/dist/host/generate.d.ts +4 -12
- package/dist/host/generate.js +48 -135
- package/dist/host/join.d.ts +28 -0
- package/dist/host/join.js +76 -0
- package/dist/host/launch/mappings.d.ts +3 -0
- package/dist/host/launch/mappings.js +50 -0
- package/dist/host/launch/projects.d.ts +15 -0
- package/dist/host/launch/projects.js +56 -0
- package/dist/host/launch/stage.d.ts +11 -0
- package/dist/host/launch/stage.js +66 -0
- package/dist/host/launch/wsb.d.ts +10 -0
- package/dist/host/launch/wsb.js +34 -0
- package/dist/host/pc-scan.d.ts +6 -0
- package/dist/host/pc-scan.js +37 -0
- package/dist/host/platform.d.ts +2 -0
- package/dist/host/platform.js +13 -1
- package/dist/host/project-scan.d.ts +1 -17
- package/dist/host/project-scan.js +2 -86
- package/dist/host/projects.d.ts +100 -0
- package/dist/host/projects.js +147 -0
- package/dist/host/repo-guard.d.ts +15 -2
- package/dist/host/repo-guard.js +103 -12
- package/dist/host/terminal.js +3 -0
- package/dist/host/test-guard.d.ts +6 -0
- package/dist/host/test-guard.js +15 -0
- package/dist/host/window.js +7 -3
- package/dist/host/wsb.d.ts +37 -0
- package/dist/host/wsb.js +101 -0
- package/dist/runtime/daemon.js +10997 -2794
- package/dist/runtime/db.js +6428 -464
- package/dist/runtime/main.js +7359 -1135
- package/dist/runtime/view.js +5922 -47
- package/dist/selftest/accounts.test.d.ts +2 -0
- package/dist/selftest/accounts.test.js +44 -0
- package/dist/selftest/agent-commands.test.d.ts +2 -0
- package/dist/selftest/agent-commands.test.js +47 -0
- package/dist/selftest/channel.test.d.ts +2 -0
- package/dist/selftest/channel.test.js +37 -0
- package/dist/selftest/cli.test.d.ts +2 -2
- package/dist/selftest/cli.test.js +115 -27
- package/dist/selftest/cmdline.test.d.ts +2 -0
- package/dist/selftest/cmdline.test.js +45 -0
- package/dist/selftest/config.test.d.ts +2 -3
- package/dist/selftest/config.test.js +36 -72
- package/dist/selftest/data.test.d.ts +2 -2
- package/dist/selftest/data.test.js +77 -70
- package/dist/selftest/detect.test.d.ts +2 -2
- package/dist/selftest/detect.test.js +54 -38
- package/dist/selftest/fixtures.d.ts +44 -4
- package/dist/selftest/fixtures.js +40 -23
- package/dist/selftest/generate.test.d.ts +2 -0
- package/dist/selftest/generate.test.js +63 -0
- package/dist/selftest/git-access.test.d.ts +2 -0
- package/dist/selftest/git-access.test.js +26 -0
- package/dist/selftest/guest-boot.test.d.ts +2 -0
- package/dist/selftest/guest-boot.test.js +76 -0
- package/dist/selftest/harness.d.ts +44 -7
- package/dist/selftest/harness.js +62 -21
- package/dist/selftest/host-sim.test.d.ts +2 -0
- package/dist/selftest/host-sim.test.js +93 -0
- package/dist/selftest/hostile-input.test.d.ts +2 -0
- package/dist/selftest/hostile-input.test.js +54 -0
- package/dist/selftest/index.d.ts +8 -1
- package/dist/selftest/index.js +134 -35
- package/dist/selftest/links.test.d.ts +2 -0
- package/dist/selftest/links.test.js +50 -0
- package/dist/selftest/live-daemon.test.d.ts +2 -0
- package/dist/selftest/live-daemon.test.js +91 -0
- package/dist/selftest/live-git.test.d.ts +2 -0
- package/dist/selftest/live-git.test.js +60 -0
- package/dist/selftest/live-projects.test.d.ts +2 -0
- package/dist/selftest/live-projects.test.js +127 -0
- package/dist/selftest/live.d.ts +65 -0
- package/dist/selftest/live.js +109 -0
- package/dist/selftest/local-config.test.d.ts +2 -0
- package/dist/selftest/local-config.test.js +107 -0
- package/dist/selftest/mirror.test.d.ts +2 -3
- package/dist/selftest/mirror.test.js +92 -65
- package/dist/selftest/network.test.d.ts +2 -0
- package/dist/selftest/network.test.js +28 -0
- package/dist/selftest/personalization.test.d.ts +2 -0
- package/dist/selftest/personalization.test.js +53 -0
- package/dist/selftest/projects.test.d.ts +2 -0
- package/dist/selftest/projects.test.js +254 -0
- package/dist/selftest/repair.test.d.ts +2 -0
- package/dist/selftest/repair.test.js +42 -0
- package/dist/selftest/repo-guard.test.d.ts +2 -0
- package/dist/selftest/repo-guard.test.js +63 -0
- package/dist/selftest/secrets.test.d.ts +2 -0
- package/dist/selftest/secrets.test.js +71 -0
- package/dist/selftest/setup.test.d.ts +2 -2
- package/dist/selftest/setup.test.js +45 -22
- package/dist/selftest/suite.d.ts +13 -0
- package/dist/selftest/suite.js +1 -0
- package/dist/selftest/sync.test.d.ts +2 -3
- package/dist/selftest/sync.test.js +90 -78
- package/dist/selftest/test-guard.test.d.ts +2 -0
- package/dist/selftest/test-guard.test.js +49 -0
- package/dist/selftest/toolchains.test.d.ts +2 -0
- package/dist/selftest/toolchains.test.js +103 -0
- package/dist/selftest/workspace.test.d.ts +2 -0
- package/dist/selftest/workspace.test.js +53 -0
- package/dist/shared/download.d.ts +3 -1
- package/dist/shared/download.js +19 -4
- package/dist/shared/paths.d.ts +5 -0
- package/dist/shared/paths.js +6 -0
- package/dist/shared/projects.d.ts +53 -0
- package/dist/shared/projects.js +67 -0
- package/dist/shared/rundir.d.ts +10 -1
- package/dist/shared/rundir.js +12 -4
- package/dist/shared/safe-remove.js +11 -0
- package/dist/shared/schema.d.ts +443 -0
- package/dist/shared/schema.js +41 -2
- package/dist/shared/test-guard.d.ts +4 -0
- package/dist/shared/test-guard.js +37 -0
- package/dist/sync/claude-mem.js +3 -1
- package/dist/sync/history.d.ts +23 -12
- package/dist/sync/history.js +52 -20
- package/dist/sync/watcher.d.ts +8 -0
- package/dist/sync/watcher.js +23 -4
- package/package.json +6 -6
- package/sandbox/bootstrap.ps1 +258 -258
- package/sandbox/cpucap.ps1 +51 -0
- package/sandbox/logon.cmd +18 -16
- package/sandbox/openssl.cs +42 -0
- package/sandbox/ptyhost.cs +15 -4
- package/sandbox/trust.cmd +35 -35
- package/schemas/config.schema.json +100 -1
- package/schemas/sandbox-config.schema.json +17 -1
- package/dist/cli/commands/data.d.ts +0 -46
- package/dist/cli/commands/data.js +0 -249
- package/dist/cli/commands/setup.js +0 -152
- package/dist/cli/settings.d.ts +0 -11
- package/dist/cli/settings.js +0 -156
- package/dist/guest/toolchains/databases.js +0 -226
- package/dist/guest/toolchains/msvc.d.ts +0 -10
- package/dist/guest/toolchains/msvc.js +0 -216
- package/dist/guest/toolchains/native.d.ts +0 -12
- package/dist/guest/toolchains/native.js +0 -220
- package/dist/guest/toolchains/scripting.d.ts +0 -10
- package/dist/guest/toolchains/scripting.js +0 -137
- package/dist/selftest/guest.test.d.ts +0 -3
- package/dist/selftest/guest.test.js +0 -61
- package/dist/selftest/helpers.test.d.ts +0 -2
- package/dist/selftest/helpers.test.js +0 -125
- package/dist/selftest/host.test.d.ts +0 -2
- package/dist/selftest/host.test.js +0 -71
- package/dist/selftest/live.test.d.ts +0 -7
- package/dist/selftest/live.test.js +0 -127
- package/dist/selftest/security.test.d.ts +0 -3
- package/dist/selftest/security.test.js +0 -143
- /package/dist/guest/toolchains/{binstall.d.ts → native/binstall.d.ts} +0 -0
package/dist/sync/history.js
CHANGED
|
@@ -12,12 +12,25 @@
|
|
|
12
12
|
import fs from 'node:fs';
|
|
13
13
|
import path from 'node:path';
|
|
14
14
|
import _ from 'lodash';
|
|
15
|
-
import {
|
|
16
|
-
import {
|
|
15
|
+
import { z } from 'zod';
|
|
16
|
+
import { guestWorkspaceAliases, isSafeFileName, projectFolderName, translatePaths } from '../shared/paths.js';
|
|
17
|
+
import { readValid, writeJsonAtomic } from '../shared/rundir.js';
|
|
18
|
+
import { assertTestWritable } from '../host/test-guard.js';
|
|
19
|
+
import { dirExists, ensureDir, fileExists, isLink, removeLink } from '../core/util.js';
|
|
17
20
|
export { projectFolderName, translatePaths };
|
|
18
21
|
const SESSION_DIRS = ['todos', 'plans', 'file-history'];
|
|
19
22
|
const DATA_FILE = /\.(jsonl?|txt|md)$/i;
|
|
20
23
|
const MAX_FILE_BYTES = 100 * 1024 * 1024;
|
|
24
|
+
/** Host-only record of the last launch: which folders map to each other and what syncs (from the effective config). */
|
|
25
|
+
const AbsolutePath = z.string().max(1024).refine((p) => path.isAbsolute(p), 'must be an absolute path');
|
|
26
|
+
const SyncStateSchema = z.object({
|
|
27
|
+
hostClaudeDir: AbsolutePath, hostWorkspace: AbsolutePath, guestWorkspace: z.string().max(1024),
|
|
28
|
+
syncHistory: z.boolean().optional(), syncClaudeMem: z.boolean().optional(),
|
|
29
|
+
/** Newest claude-mem database time on each side right after the last sync (detects edits on both sides). */
|
|
30
|
+
memHostStamp: z.number().optional(), memGuestStamp: z.number().optional(),
|
|
31
|
+
/** Projects registered while the sandbox ran (their history goes back to their own folders). */
|
|
32
|
+
projects: z.array(z.object({ hostWorkspace: AbsolutePath, guestWorkspace: z.string().max(1024) })).max(256).optional(),
|
|
33
|
+
});
|
|
21
34
|
const anyFile = () => true;
|
|
22
35
|
const transcript = (name) => name.endsWith('.jsonl');
|
|
23
36
|
const json = (name) => name.endsWith('.json');
|
|
@@ -43,6 +56,8 @@ const copyNewer = (from, to, options = {}) => {
|
|
|
43
56
|
return 0;
|
|
44
57
|
const accept = options.accept ?? anyFile;
|
|
45
58
|
return _.sumBy(fs.readdirSync(from, { withFileTypes: true }), (entry) => {
|
|
59
|
+
if (!isSafeFileName(entry.name))
|
|
60
|
+
return 0;
|
|
46
61
|
const source = path.join(from, entry.name);
|
|
47
62
|
const destination = path.join(to, entry.name);
|
|
48
63
|
if (entry.isDirectory())
|
|
@@ -69,23 +84,37 @@ const copyNewer = (from, to, options = {}) => {
|
|
|
69
84
|
});
|
|
70
85
|
};
|
|
71
86
|
const readLines = (file) => fileExists(file) && !isLink(file) && fs.statSync(file).size <= MAX_FILE_BYTES ? fs.readFileSync(file, 'utf8').split(/\r?\n/).filter((l) => l.trim() !== '') : [];
|
|
87
|
+
const isJsonObject = (line) => { try {
|
|
88
|
+
const v = JSON.parse(line);
|
|
89
|
+
return typeof v === 'object' && v !== null && !Array.isArray(v);
|
|
90
|
+
}
|
|
91
|
+
catch {
|
|
92
|
+
return false;
|
|
93
|
+
} };
|
|
72
94
|
const lineTime = (line) => { try {
|
|
73
95
|
return Number(JSON.parse(line).timestamp) || 0;
|
|
74
96
|
}
|
|
75
97
|
catch {
|
|
76
98
|
return 0;
|
|
77
99
|
} };
|
|
100
|
+
/** Every workspace pair of the sandbox: the launch folder's, then the projects registered later (most specific first). */
|
|
101
|
+
const pairsOf = (p) => _.sortBy([{ hostWorkspace: p.hostWorkspace, guestWorkspace: p.guestWorkspace }, ...(p.extra ?? [])], (x) => -x.hostWorkspace.length);
|
|
102
|
+
const translateAll = (text, pairs) => pairs.reduce((out, [from, to]) => translatePaths(out, from, to), text);
|
|
78
103
|
/** Brings this PC's history into the sandbox state (run before the sandbox starts). Returns files copied. */
|
|
79
|
-
export const syncHistoryIn = (p) => {
|
|
80
|
-
|
|
81
|
-
const
|
|
82
|
-
const
|
|
104
|
+
export const syncHistoryIn = (p, options = {}) => {
|
|
105
|
+
assertTestWritable(p.guestClaudeDir);
|
|
106
|
+
const pairs = pairsOf(p);
|
|
107
|
+
const byHost = new Map(pairs.map((x) => [projectFolderName(x.hostWorkspace), projectFolderName(x.guestWorkspace)]));
|
|
108
|
+
const guestNames = new Set(pairs.map((x) => projectFolderName(x.guestWorkspace)));
|
|
109
|
+
const transform = (t) => translateAll(t, pairs.map((x) => [x.hostWorkspace, x.guestWorkspace]));
|
|
83
110
|
const hostProjects = path.join(p.hostClaudeDir, 'projects');
|
|
84
|
-
const names = dirExists(hostProjects) ? fs.readdirSync(hostProjects).filter((name) => name
|
|
85
|
-
const projects = _.sumBy(names, (name) => name
|
|
86
|
-
? copyNewer(path.join(hostProjects, name), path.join(p.guestClaudeDir, 'projects',
|
|
111
|
+
const names = dirExists(hostProjects) ? fs.readdirSync(hostProjects).filter((name) => isSafeFileName(name) && (!guestNames.has(name) || byHost.has(name))) : [];
|
|
112
|
+
const projects = _.sumBy(names, (name) => byHost.has(name)
|
|
113
|
+
? copyNewer(path.join(hostProjects, name), path.join(p.guestClaudeDir, 'projects', byHost.get(name)), { transform })
|
|
87
114
|
: copyNewer(path.join(hostProjects, name), path.join(p.guestClaudeDir, 'projects', name)));
|
|
88
115
|
const sessions = _.sumBy(SESSION_DIRS, (dir) => copyNewer(path.join(p.hostClaudeDir, dir), path.join(p.guestClaudeDir, dir)));
|
|
116
|
+
if (options.promptHistory === false)
|
|
117
|
+
return projects + sessions;
|
|
89
118
|
// Prompt history: rebuild the sandbox copy from this PC's file plus anything only the sandbox has.
|
|
90
119
|
const target = path.join(p.guestClaudeDir, 'history.jsonl');
|
|
91
120
|
const hostLines = readLines(path.join(p.hostClaudeDir, 'history.jsonl')).map(transform);
|
|
@@ -99,21 +128,23 @@ export const syncHistoryIn = (p) => {
|
|
|
99
128
|
};
|
|
100
129
|
/** Copies new sandbox history back to this PC (safe while the sandbox runs: transcripts are append-only). */
|
|
101
130
|
export const syncHistoryOut = (p) => {
|
|
102
|
-
|
|
103
|
-
// A mirrored workspace on F: really lives at C:\Mirror\F\...; tools that resolve real paths record that one.
|
|
104
|
-
|
|
105
|
-
const
|
|
106
|
-
const
|
|
131
|
+
assertTestWritable(p.hostClaudeDir);
|
|
132
|
+
// A mirrored workspace on F: really lives at C:\Mirror\F\...; tools that resolve real paths record that one. A
|
|
133
|
+
// project registered later is in the hub (C:\Projects\<name>) and goes back to its real folder on this PC.
|
|
134
|
+
const pairs = pairsOf(p).flatMap((x) => guestWorkspaceAliases(x.guestWorkspace).map((guest) => [guest, x.hostWorkspace]));
|
|
135
|
+
const byGuest = new Map(pairs.map(([guest, host]) => [projectFolderName(guest), projectFolderName(host)]));
|
|
136
|
+
const transform = (t) => translateAll(t, pairs);
|
|
107
137
|
const guestProjectsDir = path.join(p.guestClaudeDir, 'projects');
|
|
108
|
-
const names = dirExists(guestProjectsDir) && !isLink(guestProjectsDir) ? fs.readdirSync(guestProjectsDir) : [];
|
|
109
|
-
const projects = _.sumBy(names, (name) =>
|
|
110
|
-
? copyNewer(path.join(guestProjectsDir, name), path.join(p.hostClaudeDir, 'projects',
|
|
138
|
+
const names = dirExists(guestProjectsDir) && !isLink(guestProjectsDir) ? fs.readdirSync(guestProjectsDir).filter(isSafeFileName) : [];
|
|
139
|
+
const projects = _.sumBy(names, (name) => byGuest.has(name)
|
|
140
|
+
? copyNewer(path.join(guestProjectsDir, name), path.join(p.hostClaudeDir, 'projects', byGuest.get(name)), { accept: transcript, skipDirs: NO_MEMORY, transform })
|
|
111
141
|
: copyNewer(path.join(guestProjectsDir, name), path.join(p.hostClaudeDir, 'projects', name), { accept: transcript, skipDirs: NO_MEMORY }));
|
|
112
142
|
// Only to-do lists come back besides transcripts; plans and file backups stay in the sandbox.
|
|
113
143
|
const sessions = copyNewer(path.join(p.guestClaudeDir, 'todos'), path.join(p.hostClaudeDir, 'todos'), { accept: json });
|
|
114
144
|
const hostFile = path.join(p.hostClaudeDir, 'history.jsonl');
|
|
115
145
|
const known = new Set(readLines(hostFile));
|
|
116
|
-
|
|
146
|
+
// Only prompt-history entries (one JSON object per line) come back, whatever else the sandbox wrote in that file.
|
|
147
|
+
const missing = readLines(path.join(p.guestClaudeDir, 'history.jsonl')).filter(isJsonObject).map(transform).filter((l) => !known.has(l));
|
|
117
148
|
if (missing.length) {
|
|
118
149
|
ensureDir(p.hostClaudeDir);
|
|
119
150
|
fs.appendFileSync(hostFile, missing.join('\n') + '\n', 'utf8');
|
|
@@ -125,5 +156,6 @@ export const syncHistoryOut = (p) => {
|
|
|
125
156
|
* never live in the data folder the sandbox can write (a forged copy could redirect writes anywhere on this PC).
|
|
126
157
|
*/
|
|
127
158
|
export const syncStatePath = (dataPath) => path.join(path.dirname(path.dirname(dataPath)), 'sync', path.basename(dataPath) + '.json');
|
|
128
|
-
|
|
129
|
-
export const
|
|
159
|
+
/** A state file that is missing, oversized or not in this shape counts as no state (nothing syncs back until the next launch). */
|
|
160
|
+
export const readSyncState = (dataPath) => readValid(SyncStateSchema, syncStatePath(dataPath));
|
|
161
|
+
export const writeSyncState = (dataPath, state) => writeJsonAtomic(syncStatePath(dataPath), state);
|
package/dist/sync/watcher.d.ts
CHANGED
|
@@ -1,5 +1,13 @@
|
|
|
1
1
|
import type { Config } from '../core/types.js';
|
|
2
2
|
import { type MemResult } from './claude-mem.js';
|
|
3
|
+
/**
|
|
4
|
+
* A project registered while the sandbox runs: its history comes in now (the sandbox's copy only gains files), and
|
|
5
|
+
* from now on syncs back to its own folder on this PC.
|
|
6
|
+
*/
|
|
7
|
+
export declare const syncProjectIn: (dataPath: string, project: {
|
|
8
|
+
hostWorkspace: string;
|
|
9
|
+
guestWorkspace: string;
|
|
10
|
+
}) => number;
|
|
3
11
|
export interface SyncSummary {
|
|
4
12
|
history: number;
|
|
5
13
|
claudeMem: MemResult | 'disabled';
|
package/dist/sync/watcher.js
CHANGED
|
@@ -3,6 +3,8 @@
|
|
|
3
3
|
// Its pid file and log live in host-only storage, never in the data folder the sandbox can write.
|
|
4
4
|
import fs from 'node:fs';
|
|
5
5
|
import path from 'node:path';
|
|
6
|
+
import { writeFileAtomic } from '../shared/rundir.js';
|
|
7
|
+
import { refuseInSelfTest } from '../host/test-guard.js';
|
|
6
8
|
import { assertNoLinks, errMsg, sleep } from '../core/util.js';
|
|
7
9
|
import { hostClaudeDir } from '../host/claude-profile.js';
|
|
8
10
|
import { checkRepo } from '../host/repo-guard.js';
|
|
@@ -16,7 +18,21 @@ const guestMem = (dataPath) => path.join(dataPath, 'state', 'claude-mem');
|
|
|
16
18
|
/** Sync paths for the sandbox side, after checking nothing on the way there is a link planted by the sandbox. */
|
|
17
19
|
const toPaths = (state, dataPath) => {
|
|
18
20
|
assertNoLinks(dataPath, guestClaude(dataPath));
|
|
19
|
-
return { hostClaudeDir: state.hostClaudeDir, guestClaudeDir: guestClaude(dataPath), hostWorkspace: state.hostWorkspace, guestWorkspace: state.guestWorkspace };
|
|
21
|
+
return { hostClaudeDir: state.hostClaudeDir, guestClaudeDir: guestClaude(dataPath), hostWorkspace: state.hostWorkspace, guestWorkspace: state.guestWorkspace, extra: state.projects ?? [] };
|
|
22
|
+
};
|
|
23
|
+
/**
|
|
24
|
+
* A project registered while the sandbox runs: its history comes in now (the sandbox's copy only gains files), and
|
|
25
|
+
* from now on syncs back to its own folder on this PC.
|
|
26
|
+
*/
|
|
27
|
+
export const syncProjectIn = (dataPath, project) => {
|
|
28
|
+
const state = readSyncState(dataPath);
|
|
29
|
+
if (!state)
|
|
30
|
+
return 0;
|
|
31
|
+
const projects = [...(state.projects ?? []).filter((p) => p.hostWorkspace.toLowerCase() !== project.hostWorkspace.toLowerCase()), project];
|
|
32
|
+
const next = { ...state, projects };
|
|
33
|
+
writeSyncState(dataPath, next);
|
|
34
|
+
// Prompt history is left alone: sessions are appending to the sandbox's copy right now.
|
|
35
|
+
return state.syncHistory ? syncHistoryIn(toPaths(next, dataPath), { promptHistory: false }) : 0;
|
|
20
36
|
};
|
|
21
37
|
const memSync = (dataPath, state, direction) => {
|
|
22
38
|
assertNoLinks(dataPath, guestMem(dataPath));
|
|
@@ -31,7 +47,7 @@ export const syncBeforeLaunch = (config, dataPath, hostWorkspace) => {
|
|
|
31
47
|
const previous = readSyncState(dataPath);
|
|
32
48
|
if (previous?.syncHistory)
|
|
33
49
|
syncHistoryOut(toPaths(previous, dataPath));
|
|
34
|
-
const state = { ...previous, hostClaudeDir: hostClaudeDir(profile), hostWorkspace, guestWorkspace: config.guestWorkspace, syncHistory: profile.syncHistory, syncClaudeMem: profile.syncClaudeMem };
|
|
50
|
+
const state = { ...previous, hostClaudeDir: hostClaudeDir(profile), hostWorkspace, guestWorkspace: config.guestWorkspace, syncHistory: profile.syncHistory, syncClaudeMem: profile.syncClaudeMem, projects: [] };
|
|
35
51
|
if (profile.syncHistory)
|
|
36
52
|
summary.history = syncHistoryIn(toPaths(state, dataPath));
|
|
37
53
|
if (profile.syncClaudeMem) {
|
|
@@ -58,7 +74,9 @@ export const syncBack = (dataPath, final) => {
|
|
|
58
74
|
const hostRunDir = (dataPath) => path.join(storageOf(dataPath), 'run');
|
|
59
75
|
const watcherPidFile = (dataPath) => path.join(hostRunDir(dataPath), 'watcher-' + path.basename(dataPath) + '.pid');
|
|
60
76
|
const watcherLog = (dataPath) => path.join(storageOf(dataPath), 'logs', 'host-sync-' + path.basename(dataPath) + '.log');
|
|
61
|
-
|
|
77
|
+
// Signal 0 only asks whether the process exists. pid 0 would mean "my own process group", so it never counts.
|
|
78
|
+
const alive = (pid) => { if (!Number.isInteger(pid) || pid <= 0)
|
|
79
|
+
return false; try {
|
|
62
80
|
process.kill(pid, 0);
|
|
63
81
|
return true;
|
|
64
82
|
}
|
|
@@ -75,11 +93,12 @@ export const watcherRunning = (dataPath) => {
|
|
|
75
93
|
};
|
|
76
94
|
/** Body of the detached watcher process (`agent-sandbox _watch`). */
|
|
77
95
|
export const watch = async (dataPath) => {
|
|
96
|
+
refuseInSelfTest('start the background sync');
|
|
78
97
|
if (watcherRunning(dataPath))
|
|
79
98
|
return;
|
|
80
99
|
fs.mkdirSync(hostRunDir(dataPath), { recursive: true });
|
|
81
100
|
fs.mkdirSync(path.dirname(watcherLog(dataPath)), { recursive: true });
|
|
82
|
-
|
|
101
|
+
writeFileAtomic(watcherPidFile(dataPath), String(process.pid));
|
|
83
102
|
const note = (message) => { try {
|
|
84
103
|
fs.appendFileSync(watcherLog(dataPath), new Date().toISOString() + ' ' + message + '\n');
|
|
85
104
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "windows-development-sandbox",
|
|
3
|
-
"version": "1.0
|
|
3
|
+
"version": "1.1.0",
|
|
4
4
|
"description": "A disposable Windows Sandbox for development, with or without an AI agent (Claude Code, Codex, Gemini, opencode, ...): the folder you run it from is the workspace, toolchains and databases for your project install themselves, and nothing else on your PC is reachable.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
@@ -37,14 +37,14 @@
|
|
|
37
37
|
"hooks",
|
|
38
38
|
"config.example.json",
|
|
39
39
|
".sandbox-config.example.json",
|
|
40
|
-
"README.md"
|
|
40
|
+
"README.md",
|
|
41
|
+
"LICENSE"
|
|
41
42
|
],
|
|
42
43
|
"scripts": {
|
|
43
44
|
"build": "tsc -p tsconfig.json && node scripts/bundle.mjs && node scripts/schemas.mjs",
|
|
44
45
|
"clean": "node -e \"require('fs').rmSync('dist',{recursive:true,force:true})\"",
|
|
45
46
|
"rebuild": "npm run clean && npm run build",
|
|
46
47
|
"test": "npm run build && node dist/bin/agent-sandbox.js self-test",
|
|
47
|
-
"prepare": "npm run build",
|
|
48
48
|
"prepack": "npm run rebuild",
|
|
49
49
|
"release:patch": "npm version patch && git push --follow-tags",
|
|
50
50
|
"release:minor": "npm version minor && git push --follow-tags",
|
|
@@ -84,10 +84,10 @@
|
|
|
84
84
|
},
|
|
85
85
|
"repository": {
|
|
86
86
|
"type": "git",
|
|
87
|
-
"url": "git+https://github.com/JoshDiDuca/
|
|
87
|
+
"url": "git+https://github.com/JoshDiDuca/windows-development-sandbox.git"
|
|
88
88
|
},
|
|
89
|
-
"homepage": "https://github.com/JoshDiDuca/
|
|
89
|
+
"homepage": "https://github.com/JoshDiDuca/windows-development-sandbox#readme",
|
|
90
90
|
"bugs": {
|
|
91
|
-
"url": "https://github.com/JoshDiDuca/
|
|
91
|
+
"url": "https://github.com/JoshDiDuca/windows-development-sandbox/issues"
|
|
92
92
|
}
|
|
93
93
|
}
|