@bendyline/gezel 0.1.0 → 1.0.1

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.
@@ -23,10 +23,12 @@ export { D as DEVICE_HARD_TEMPERATURE_C } from '../device-safety-DezzpNyR.js';
23
23
  * const bin = resolveNativeBinaryPath('llama-server', import.meta.url, probe.backend);
24
24
  *
25
25
  * Caching: the result is memoized at
26
- * `<home>/engines/llama-cpp/backend.json` so subsequent launches
27
- * skip the probe. Cache invalidates whenever `engineVersion`
28
- * changes bumping the llama.cpp pin may prefer a different
29
- * backend.
26
+ * `<home>/engines/llama-cpp/backend.json`. Subsequent launches reuse
27
+ * it only while the cheap driver-library check still resolves to the
28
+ * same backend. That matters when a user installs or removes a GPU
29
+ * driver between launches. The cache also invalidates whenever
30
+ * `engineVersion` changes — bumping the llama.cpp pin may prefer a
31
+ * different backend.
30
32
  */
31
33
  type LlamaBackend = 'cuda' | 'vulkan' | 'metal' | 'cpu';
32
34
  /**
@@ -376,44 +378,36 @@ declare function resolveNativeBinaryUnder(root: string, name: NativeBinaryName,
376
378
  declare function discoverNativeBinaries(input: DiscoverInput): DiscoverResult;
377
379
 
378
380
  /**
379
- * Windows console-allocation policy for spawned native children.
380
- *
381
- * Console-subsystem executables (llama-server, ds4-server, bundled Node) get
382
- * a console allocated by the loader unless the creator says otherwise. Under
383
- * the machine-wide service there is nothing to allocate one from: the daemon
384
- * runs in non-interactive Session 0, where as
385
- * `native/helpers/service-host/src/main.cpp` records `AllocConsole` fails
386
- * with error 317. `DETACHED_PROCESS`, which Node exposes as `detached: true`,
387
- * asks for no console at all.
388
- *
389
- * `windowsHide` is NOT that flag. It maps to `CREATE_NO_WINDOW`, which still
390
- * allocates a console and only withholds the window.
391
- *
392
- * `detached` is deliberately scoped to win32. On POSIX the same option means
393
- * `setsid()`, which changes process-group and signal semantics that callers
394
- * may depend on; there is no console problem to solve there.
395
- *
396
- * ## What this does not fix
397
- *
398
- * This helper was introduced in v1.26215.31 believing it fixed the
399
- * `spawn EPERM` that killed every native-engine launch under the machine
400
- * service. It did not. That release shipped the flag and the failure
401
- * continued, on two machines, with the daemon's own log showing the engine,
402
- * the bundled device-health helper, `nvidia-smi`, `amd-smi` and `rocm-smi`
403
- * all denied at once. The cause was the service token: `sc sidtype ...
404
- * restricted` write-restricts it, and libuv creates a named pipe per piped
405
- * stdio handle before every `CreateProcess`, which that token cannot do. The
406
- * installer now assigns `unrestricted` (see
407
- * `packages/app/installer/nsis-hooks.nsh`), and `probeChildProcessSpawn` in
408
- * the service catches a recurrence at boot.
409
- *
410
- * The flag is still correct and still used — a console the service cannot
411
- * allocate is one Windows should not be asked for — but it is a tidiness
412
- * measure, not a fix for a permission error. If `spawn EPERM` appears again,
413
- * do not reach for spawn flags: look at the token.
381
+ * Windows console-window policy for owned child processes.
382
+ *
383
+ * Node documents `detached: true` on Windows as giving the child its own
384
+ * console window. That is the opposite of what short-lived helpers and
385
+ * supervised engines want: it is what caused Windows Terminal to flash a
386
+ * fresh command prompt for every native signature check. `windowsHide` is
387
+ * the dedicated Node/libuv option for suppressing a subprocess console
388
+ * window, and it leaves the child's lifetime and process-group ownership
389
+ * unchanged.
390
+ *
391
+ * Keep this win32-only. The option is ignored on POSIX, but omitting it there
392
+ * makes the platform contract explicit and keeps captured spawn options
393
+ * deterministic in tests.
394
+ */
395
+ declare function windowsHeadlessSpawnOptions(platform?: NodeJS.Platform): {
396
+ windowsHide: true;
397
+ } | Record<string, never>;
398
+ /**
399
+ * Windows options for a genuine fire-and-forget child that must outlive its
400
+ * parent. Detachment is a lifetime decision, not a headless-launch strategy;
401
+ * pair it with `windowsHide` so Windows does not surface the child's new
402
+ * console while it starts.
403
+ *
404
+ * Most callers should use {@link windowsHeadlessSpawnOptions}. In particular,
405
+ * awaited probes, package installers, and supervised engines are owned
406
+ * children and must not be detached.
414
407
  */
415
408
  declare function windowsDetachedSpawnOptions(platform?: NodeJS.Platform): {
416
409
  detached: true;
410
+ windowsHide: true;
417
411
  } | Record<string, never>;
418
412
 
419
413
  /**
@@ -647,4 +641,4 @@ interface FindGpuPanicOptions {
647
641
  */
648
642
  declare function findRecentGpuPanics(opts?: FindGpuPanicOptions): GpuPanicRecord[];
649
643
 
650
- export { type CommandResult, DEFAULT_DEVICE_SAFETY_POLICY, type DetectInput, type DetectResult, type DeviceHealthCommandRunner, type DeviceHealthDecision, DeviceHealthGate, type DeviceHealthGateOptions, type DeviceHealthProbe, type DeviceHealthReading, type DeviceHealthSample, type DeviceHealthState, type DeviceHealthStatusSnapshot, type DeviceSafetyMode, type DeviceSafetyPolicyInput, type DeviceTelemetryFailurePolicy, type DeviceVendor, type DiscoverInput, type DiscoverResult, type FindGpuPanicOptions, GPU_PANIC_RE, type GpuPanicRecord, type GpuVendorHint, LLAMA_ENGINE_VERSION, type LlamaBackend, type LlamaQuarantineEntry, type NativeBinaryName, type QuarantineIo, type ResolvedDeviceSafetyPolicy, type ResolvedLlamaBinary, type SystemDeviceHealthProbeOptions, binaryFingerprint, createSystemDeviceHealthProbe, detectLlamaBackend, discoverNativeBinaries, evaluateDeviceHealth, findRecentGpuPanics, isBinaryQuarantined, llamaQuarantinePath, parseAmdSmiJson, parseNvidiaSmiCsv, readLlamaQuarantine, recordLlamaQuarantine, resolveAvailableLlamaBinary, resolveDeviceSafetyPolicy, resolveNativeBinaryUnder, resolvePlatformKey, windowsDetachedSpawnOptions };
644
+ export { type CommandResult, DEFAULT_DEVICE_SAFETY_POLICY, type DetectInput, type DetectResult, type DeviceHealthCommandRunner, type DeviceHealthDecision, DeviceHealthGate, type DeviceHealthGateOptions, type DeviceHealthProbe, type DeviceHealthReading, type DeviceHealthSample, type DeviceHealthState, type DeviceHealthStatusSnapshot, type DeviceSafetyMode, type DeviceSafetyPolicyInput, type DeviceTelemetryFailurePolicy, type DeviceVendor, type DiscoverInput, type DiscoverResult, type FindGpuPanicOptions, GPU_PANIC_RE, type GpuPanicRecord, type GpuVendorHint, LLAMA_ENGINE_VERSION, type LlamaBackend, type LlamaQuarantineEntry, type NativeBinaryName, type QuarantineIo, type ResolvedDeviceSafetyPolicy, type ResolvedLlamaBinary, type SystemDeviceHealthProbeOptions, binaryFingerprint, createSystemDeviceHealthProbe, detectLlamaBackend, discoverNativeBinaries, evaluateDeviceHealth, findRecentGpuPanics, isBinaryQuarantined, llamaQuarantinePath, parseAmdSmiJson, parseNvidiaSmiCsv, readLlamaQuarantine, recordLlamaQuarantine, resolveAvailableLlamaBinary, resolveDeviceSafetyPolicy, resolveNativeBinaryUnder, resolvePlatformKey, windowsDetachedSpawnOptions, windowsHeadlessSpawnOptions };
@@ -10,6 +10,16 @@ import {
10
10
  } from "fs";
11
11
  import { dirname, join } from "path";
12
12
  import { arch as nodeArch, platform as nodePlatform } from "process";
13
+
14
+ // src/native/console-detach.ts
15
+ function windowsHeadlessSpawnOptions(platform = process.platform) {
16
+ return platform === "win32" ? { windowsHide: true } : {};
17
+ }
18
+ function windowsDetachedSpawnOptions(platform = process.platform) {
19
+ return platform === "win32" ? { detached: true, windowsHide: true } : {};
20
+ }
21
+
22
+ // src/native/llama-backend.ts
13
23
  function resolveAvailableLlamaBinary(preferredBackend, resolveBinary, allowFallbacks, isUsable) {
14
24
  const fallbackOrder = {
15
25
  cuda: ["cuda", "vulkan", "cpu"],
@@ -50,9 +60,41 @@ function anyExists(paths, probe) {
50
60
  }
51
61
  return null;
52
62
  }
63
+ var LINUX_CUDA_DRIVER_PATHS = [
64
+ "/usr/lib/x86_64-linux-gnu/libcuda.so.1",
65
+ "/usr/lib/aarch64-linux-gnu/libcuda.so.1",
66
+ "/usr/lib64/libcuda.so.1",
67
+ "/usr/lib/libcuda.so.1",
68
+ "/lib/x86_64-linux-gnu/libcuda.so.1",
69
+ "/lib/aarch64-linux-gnu/libcuda.so.1"
70
+ ];
71
+ var LINUX_VULKAN_LOADER_PATHS = [
72
+ "/usr/lib/x86_64-linux-gnu/libvulkan.so.1",
73
+ "/usr/lib/aarch64-linux-gnu/libvulkan.so.1",
74
+ "/usr/lib64/libvulkan.so.1",
75
+ "/usr/lib/libvulkan.so.1"
76
+ ];
77
+ function windowsSystem32() {
78
+ return process.env.SYSTEMROOT ? join(process.env.SYSTEMROOT, "System32") : "C:\\Windows\\System32";
79
+ }
80
+ function detectDriverBackend(os, probeFile) {
81
+ if (os === "linux") {
82
+ const libcuda = anyExists(LINUX_CUDA_DRIVER_PATHS, probeFile);
83
+ if (libcuda) return { backend: "cuda", path: libcuda };
84
+ const libvulkan = anyExists(LINUX_VULKAN_LOADER_PATHS, probeFile);
85
+ if (libvulkan) return { backend: "vulkan", path: libvulkan };
86
+ return { backend: "cpu" };
87
+ }
88
+ const sys32 = windowsSystem32();
89
+ const nvcuda = anyExists([join(sys32, "nvcuda.dll"), join(sys32, "nvml.dll")], probeFile);
90
+ if (nvcuda) return { backend: "cuda", path: nvcuda };
91
+ const vulkan = anyExists([join(sys32, "vulkan-1.dll")], probeFile);
92
+ if (vulkan) return { backend: "vulkan", path: vulkan };
93
+ return { backend: "cpu" };
94
+ }
53
95
  function commandOkDefault(cmd) {
54
96
  try {
55
- execSync(cmd, { stdio: "ignore" });
97
+ execSync(cmd, { stdio: "ignore", ...windowsHeadlessSpawnOptions() });
56
98
  return true;
57
99
  } catch {
58
100
  return false;
@@ -87,7 +129,7 @@ function detectVendor(os, probeFile, readFileText, readDir) {
87
129
  if (found.has("intel")) return "intel";
88
130
  return void 0;
89
131
  }
90
- const sys32 = process.env.SYSTEMROOT ? join(process.env.SYSTEMROOT, "System32") : "C:\\Windows\\System32";
132
+ const sys32 = windowsSystem32();
91
133
  if (probeFile(join(sys32, "nvcuda.dll")) || probeFile(join(sys32, "nvapi64.dll"))) {
92
134
  return "nvidia";
93
135
  }
@@ -101,52 +143,20 @@ function detectVendor(os, probeFile, readFileText, readDir) {
101
143
  }
102
144
  function detectLinuxOrWin(os, probeFile, probeCmd, readFileText, readDir) {
103
145
  const vendorHint = detectVendor(os, probeFile, readFileText, readDir);
104
- if (os === "linux") {
105
- const libcuda = anyExists(
106
- [
107
- "/usr/lib/x86_64-linux-gnu/libcuda.so.1",
108
- "/usr/lib/aarch64-linux-gnu/libcuda.so.1",
109
- "/usr/lib64/libcuda.so.1",
110
- "/usr/lib/libcuda.so.1",
111
- "/lib/x86_64-linux-gnu/libcuda.so.1",
112
- "/lib/aarch64-linux-gnu/libcuda.so.1"
113
- ],
114
- probeFile
115
- );
116
- if (libcuda) {
146
+ const driver = detectDriverBackend(os, probeFile);
147
+ if (driver.backend === "cuda" && driver.path) {
148
+ if (os === "linux") {
117
149
  const smiOk = probeCmd("nvidia-smi -L");
118
150
  return {
119
151
  backend: "cuda",
120
- reason: `found ${libcuda}${smiOk ? ", nvidia-smi ok" : ", nvidia-smi absent or failing (proceeding anyway)"}`,
152
+ reason: `found ${driver.path}${smiOk ? ", nvidia-smi ok" : ", nvidia-smi absent or failing (proceeding anyway)"}`,
121
153
  vendorHint
122
154
  };
123
155
  }
124
- } else {
125
- const sys32 = process.env.SYSTEMROOT ? join(process.env.SYSTEMROOT, "System32") : "C:\\Windows\\System32";
126
- const nvcuda = anyExists([join(sys32, "nvcuda.dll"), join(sys32, "nvml.dll")], probeFile);
127
- if (nvcuda) {
128
- return { backend: "cuda", reason: `found ${nvcuda}`, vendorHint };
129
- }
156
+ return { backend: "cuda", reason: `found ${driver.path}`, vendorHint };
130
157
  }
131
- if (os === "linux") {
132
- const libvulkan = anyExists(
133
- [
134
- "/usr/lib/x86_64-linux-gnu/libvulkan.so.1",
135
- "/usr/lib/aarch64-linux-gnu/libvulkan.so.1",
136
- "/usr/lib64/libvulkan.so.1",
137
- "/usr/lib/libvulkan.so.1"
138
- ],
139
- probeFile
140
- );
141
- if (libvulkan) {
142
- return { backend: "vulkan", reason: `found ${libvulkan}`, vendorHint };
143
- }
144
- } else {
145
- const sys32 = process.env.SYSTEMROOT ? join(process.env.SYSTEMROOT, "System32") : "C:\\Windows\\System32";
146
- const vk = anyExists([join(sys32, "vulkan-1.dll")], probeFile);
147
- if (vk) {
148
- return { backend: "vulkan", reason: `found ${vk}`, vendorHint };
149
- }
158
+ if (driver.backend === "vulkan" && driver.path) {
159
+ return { backend: "vulkan", reason: `found ${driver.path}`, vendorHint };
150
160
  }
151
161
  return { backend: "cpu", reason: "no CUDA driver and no Vulkan loader found", vendorHint };
152
162
  }
@@ -177,11 +187,15 @@ function probeOrCached(input) {
177
187
  const probeReadFile = input.probe?.readFileText ?? readFileTextDefault;
178
188
  const probeReadDir = input.probe?.readDir ?? readDirDefault;
179
189
  const cachePath = join(input.home, "engines", "llama-cpp", "backend.json");
190
+ const plat = input.probe?.platform ?? nodePlatform;
191
+ const ar = input.probe?.arch ?? nodeArch;
180
192
  if (fileExistsDefault(cachePath)) {
181
193
  try {
182
194
  const cached = JSON.parse(readFileSync(cachePath, "utf8"));
183
195
  const schemaOk = (cached.probeSchemaVersion ?? 0) >= PROBE_SCHEMA_VERSION;
184
- if (cached.engineVersion === input.engineVersion && schemaOk) {
196
+ const currentDriverBackend = plat === "linux" && (ar === "x64" || ar === "arm64") || plat === "win32" && ar === "x64" ? detectDriverBackend(plat, probeFile).backend : void 0;
197
+ const driverStateUnchanged = currentDriverBackend === void 0 || currentDriverBackend === cached.backend;
198
+ if (cached.engineVersion === input.engineVersion && schemaOk && driverStateUnchanged) {
185
199
  return {
186
200
  backend: cached.backend,
187
201
  cached: true,
@@ -193,8 +207,6 @@ function probeOrCached(input) {
193
207
  } catch {
194
208
  }
195
209
  }
196
- const plat = input.probe?.platform ?? nodePlatform;
197
- const ar = input.probe?.arch ?? nodeArch;
198
210
  let result;
199
211
  if (plat === "darwin") {
200
212
  if (ar === "arm64") {
@@ -455,11 +467,6 @@ function discoverNativeBinaries(input) {
455
467
  return result;
456
468
  }
457
469
 
458
- // src/native/console-detach.ts
459
- function windowsDetachedSpawnOptions(platform = process.platform) {
460
- return platform === "win32" ? { detached: true } : {};
461
- }
462
-
463
470
  // src/native/device-health.ts
464
471
  import { execFile as nodeExecFile } from "child_process";
465
472
 
@@ -730,13 +737,10 @@ function defaultCommandRunner(command, args, timeoutMs) {
730
737
  nodeExecFile(
731
738
  command,
732
739
  args,
733
- // nvidia-smi / amd-smi are console-subsystem, and the Session 0 service
734
- // has no console for the loader to allocate from, so they start with
735
- // DETACHED_PROCESS; `windowsHide` (CREATE_NO_WINDOW) still allocates
736
- // one. When these probes fail the daemon sees no GPU at all and plans
737
- // capacity against system RAM — the symptom that made a token-level
738
- // spawn denial look like a GPU-detection bug.
739
- { timeout: timeoutMs, ...windowsDetachedSpawnOptions() },
740
+ // Hardware CLIs are short-lived, owned children. Hide their Windows
741
+ // console instead of detaching them; detachment gives each probe its
742
+ // own console window and produces a visible terminal flash.
743
+ { timeout: timeoutMs, ...windowsHeadlessSpawnOptions() },
740
744
  (error, stdout, stderr) => {
741
745
  if (error) {
742
746
  reject(error);
@@ -1117,5 +1121,6 @@ export {
1117
1121
  resolveDeviceSafetyPolicy,
1118
1122
  resolveNativeBinaryUnder,
1119
1123
  resolvePlatformKey,
1120
- windowsDetachedSpawnOptions
1124
+ windowsDetachedSpawnOptions,
1125
+ windowsHeadlessSpawnOptions
1121
1126
  };
package/dist/paths.d.ts CHANGED
@@ -207,6 +207,8 @@ declare function projectLocalDir(root: string, projectId: string): string;
207
207
  declare function projectMetaFile(root: string, projectId: string): string;
208
208
  /** Durable user/worker lifecycle for indexed code findings. Account-private. */
209
209
  declare function projectFindingLifecycleFile(root: string, projectId: string): string;
210
+ /** Durable Boekwachter issue identity + lifecycle. Account-private. */
211
+ declare function projectBoekwachterIssuesFile(root: string, projectId: string): string;
210
212
  /** Durable per-project code-review records for this account. */
211
213
  declare function projectCodeReviewsFile(root: string, projectId: string): string;
212
214
  /** Durable per-account report-action lifecycle records. */
@@ -215,6 +217,16 @@ declare function projectReportActionsFile(root: string, projectId: string): stri
215
217
  declare function projectDocsDir(root: string, projectId: string, external?: ExternalFolders): string;
216
218
  /** Per-project artifacts folder — agent-generated outputs. */
217
219
  declare function projectArtifactsDir(root: string, projectId: string, external?: ExternalFolders): string;
220
+ /**
221
+ * Reserved artifacts subtree holding gezel-generated shadow files: markdown
222
+ * representations of workspace content (converted office documents, image
223
+ * descriptions, audio transcripts). Lives under artifacts — never the
224
+ * workspace, which may be read-only — and is a regenerable cache: write-denied
225
+ * to gezels/users, safe to delete, rebuilt by indexing.
226
+ */
227
+ declare const PROJECT_SHADOW_DIR_NAME = "shadow";
228
+ /** Per-project `artifacts/shadow/` root. */
229
+ declare function projectShadowDir(root: string, projectId: string, external?: ExternalFolders): string;
218
230
  /** Per-project memories folder (daily markdown + summary + vectra index). */
219
231
  declare function projectMemoriesDir(root: string, projectId: string, external?: ExternalFolders): string;
220
232
  /**
@@ -394,8 +406,11 @@ declare function projectLocalRoot(workspaceDir: string): string;
394
406
  declare function projectLocalIndexDir(workspaceDir: string): string;
395
407
  declare function projectLocalIndexDbFile(workspaceDir: string): string;
396
408
  /**
397
- * Converted-document artifacts (squisq-flavored markdown + media + CSV),
398
- * mirroring the source tree under `.gezel/files/<mirror>/<name>_files/`.
409
+ * Legacy converted-document location under the workspace's own
410
+ * `.gezel/files/<mirror>/<name>_files/`. Workspace conversions now live in
411
+ * the project's `artifacts/shadow/` tree ({@link projectShadowDir}) so a
412
+ * read-only workspace never loses them; this helper remains only so the
413
+ * indexer can clean the old tree up.
399
414
  */
400
415
  declare function projectLocalFilesDir(workspaceDir: string): string;
401
416
  /**
@@ -418,6 +433,14 @@ declare function fallbackProjectIndexDir(root: string, projectId: string): strin
418
433
  * always use the account-private fallback to prevent cross-daemon SQLite use.
419
434
  */
420
435
  declare function projectContentIndexDbFile(root: string, projectId: string, workspaceDir: string): string;
436
+ /**
437
+ * Derived FTS index over a project's artifact corpora (connector records under
438
+ * `artifacts/data/**`). Always in the account-private sidecar, never inside
439
+ * the artifacts tree itself — the database must not surface in the corpus
440
+ * browser, and mutable SQLite must not ride an externalized artifacts folder.
441
+ * Rebuildable cache, safe to delete.
442
+ */
443
+ declare function projectArtifactsIndexDbFile(root: string, projectId: string): string;
421
444
  /**
422
445
  * The committable code-map "city file": placement anchors, user overrides, and
423
446
  * the layout journal. Deliberately OUTSIDE the self-gitignored `.gezel/index/`
@@ -575,4 +598,4 @@ declare function keurmeesterDigestStatePath(root: string): string;
575
598
  */
576
599
  declare function readConfigRaw(root: string): Promise<Record<string, unknown>>;
577
600
 
578
- export { type ExternalFolders, type GezelPaths, MACHINE_SHARED_MARKER, type MachineStorageScope, activeMachineSharedHome, backupsDir, channelsDir, craftbookShardPrefix, craftbookTemplateDir, craftbookTemplateManifestFile, craftbookTemplateScriptFile, craftbookTemplateScriptsDir, craftbookTemplateVersionDir, craftbookTemplateVersionManifestFile, craftbookTemplatesRoot, daemonTransactionsRoot, deviceIdentityFile, fallbackProjectIndexDir, fallbackProjectVillageFile, foldersStateDir, gezelDir, gezelGrowthPath, gezelHome, gezelLocalDir, gezelMemoriesDir, gezelPaths, gezelPoppetjePath, gezelSessionFile, gezelSessionsDir, gezelStorageScope, gezelToolsPath, gezelToolsetsFile, gezelToolsetsInstallDir, gildeLiveRoot, gildeLiveStateFile, gildeLiveVersionDir, gildeLiveVersionsDir, globalHistoryFile, globalIndexDbFile, globalIndexDir, keurmeesterCasesDir, keurmeesterDigestStatePath, keurmeesterDigestsDir, keurmeesterDir, machineSharedGezelDir, machineSharedHome, machineSharedMarkerFile, machineSharedProjectDir, meesterStatusDir, meesterStatusFile, meesterStatusStateFile, pendingGrantsFile, playwrightBrowsersDir, projectActivityFile, projectArtifactsDir, projectCodeReviewsFile, projectContentIndexDbFile, projectCreateTransactionsRoot, projectDir, projectDocsDir, projectFindingLifecycleFile, projectHistoryFile, projectIndexDir, projectInternalGithubDir, projectLocalConfigFile, projectLocalCraftbookDir, projectLocalCraftbooksRoot, projectLocalDir, projectLocalFilesDir, projectLocalGezelDir, projectLocalGezelsRoot, projectLocalImportsFile, projectLocalIndexDbFile, projectLocalIndexDir, projectLocalPendingImportsFile, projectLocalQuarantineDir, projectLocalRoot, projectLocalVillageFile, projectMemoriesDir, projectMemoryIndexDir, projectMetaFile, projectPrivateDir, projectQuestionsFile, projectReportActionsFile, projectScriptFile, projectScriptRunFile, projectScriptRunsDir, projectScriptsDir, projectStorageDir, projectStorageScope, projectTaskAboutFile, projectTaskDir, projectTaskFile, projectTaskNextIdFile, projectTaskNotesFile, projectTasksDir, projectTerminalFile, projectTerminalsDir, projectToolsetsFile, projectToolsetsInstallDir, projectTypeDir, projectTypeManifestFile, projectTypeShardPrefix, projectTypeVersionDir, projectTypeVersionManifestFile, projectTypesRoot, readConfigRaw, remotesFile, secretsFile, secretsKeyFile, sharedCloneDir, sharedClonesRoot, sharedToolsetsFile, sharedToolsetsInstallDir, systemInstalledToolsetsFile, systemToolsetsFile, systemToolsetsInstallDir, tokensFile, toolsetConfigFile, toolsetConfigsDir, userGezelDir, userProjectDir, userScriptFile, userScriptsDir };
601
+ export { type ExternalFolders, type GezelPaths, MACHINE_SHARED_MARKER, type MachineStorageScope, PROJECT_SHADOW_DIR_NAME, activeMachineSharedHome, backupsDir, channelsDir, craftbookShardPrefix, craftbookTemplateDir, craftbookTemplateManifestFile, craftbookTemplateScriptFile, craftbookTemplateScriptsDir, craftbookTemplateVersionDir, craftbookTemplateVersionManifestFile, craftbookTemplatesRoot, daemonTransactionsRoot, deviceIdentityFile, fallbackProjectIndexDir, fallbackProjectVillageFile, foldersStateDir, gezelDir, gezelGrowthPath, gezelHome, gezelLocalDir, gezelMemoriesDir, gezelPaths, gezelPoppetjePath, gezelSessionFile, gezelSessionsDir, gezelStorageScope, gezelToolsPath, gezelToolsetsFile, gezelToolsetsInstallDir, gildeLiveRoot, gildeLiveStateFile, gildeLiveVersionDir, gildeLiveVersionsDir, globalHistoryFile, globalIndexDbFile, globalIndexDir, keurmeesterCasesDir, keurmeesterDigestStatePath, keurmeesterDigestsDir, keurmeesterDir, machineSharedGezelDir, machineSharedHome, machineSharedMarkerFile, machineSharedProjectDir, meesterStatusDir, meesterStatusFile, meesterStatusStateFile, pendingGrantsFile, playwrightBrowsersDir, projectActivityFile, projectArtifactsDir, projectArtifactsIndexDbFile, projectBoekwachterIssuesFile, projectCodeReviewsFile, projectContentIndexDbFile, projectCreateTransactionsRoot, projectDir, projectDocsDir, projectFindingLifecycleFile, projectHistoryFile, projectIndexDir, projectInternalGithubDir, projectLocalConfigFile, projectLocalCraftbookDir, projectLocalCraftbooksRoot, projectLocalDir, projectLocalFilesDir, projectLocalGezelDir, projectLocalGezelsRoot, projectLocalImportsFile, projectLocalIndexDbFile, projectLocalIndexDir, projectLocalPendingImportsFile, projectLocalQuarantineDir, projectLocalRoot, projectLocalVillageFile, projectMemoriesDir, projectMemoryIndexDir, projectMetaFile, projectPrivateDir, projectQuestionsFile, projectReportActionsFile, projectScriptFile, projectScriptRunFile, projectScriptRunsDir, projectScriptsDir, projectShadowDir, projectStorageDir, projectStorageScope, projectTaskAboutFile, projectTaskDir, projectTaskFile, projectTaskNextIdFile, projectTaskNotesFile, projectTasksDir, projectTerminalFile, projectTerminalsDir, projectToolsetsFile, projectToolsetsInstallDir, projectTypeDir, projectTypeManifestFile, projectTypeShardPrefix, projectTypeVersionDir, projectTypeVersionManifestFile, projectTypesRoot, readConfigRaw, remotesFile, secretsFile, secretsKeyFile, sharedCloneDir, sharedClonesRoot, sharedToolsetsFile, sharedToolsetsInstallDir, systemInstalledToolsetsFile, systemToolsetsFile, systemToolsetsInstallDir, tokensFile, toolsetConfigFile, toolsetConfigsDir, userGezelDir, userProjectDir, userScriptFile, userScriptsDir };
package/dist/paths.js CHANGED
@@ -2,6 +2,47 @@
2
2
  import { existsSync, lstatSync, readFileSync } from "fs";
3
3
  import { homedir } from "os";
4
4
  import { join, posix, win32 } from "path";
5
+
6
+ // src/entity-id.ts
7
+ var SAFE_ENTITY_ID = /^[A-Za-z0-9@][A-Za-z0-9@._-]{0,199}$/;
8
+ var RESERVED_WINDOWS_IDS = /* @__PURE__ */ new Set([
9
+ "CON",
10
+ "PRN",
11
+ "AUX",
12
+ "NUL",
13
+ "COM1",
14
+ "COM2",
15
+ "COM3",
16
+ "COM4",
17
+ "COM5",
18
+ "COM6",
19
+ "COM7",
20
+ "COM8",
21
+ "COM9",
22
+ "LPT1",
23
+ "LPT2",
24
+ "LPT3",
25
+ "LPT4",
26
+ "LPT5",
27
+ "LPT6",
28
+ "LPT7",
29
+ "LPT8",
30
+ "LPT9"
31
+ ]);
32
+ function isSafeEntityId(value) {
33
+ if (typeof value !== "string" || !SAFE_ENTITY_ID.test(value)) return false;
34
+ const windowsStem = (value.split(".")[0] ?? "").toUpperCase();
35
+ return !RESERVED_WINDOWS_IDS.has(windowsStem);
36
+ }
37
+ function assertSafeEntityId(value, label = "entity id") {
38
+ if (!isSafeEntityId(value)) {
39
+ throw new TypeError(
40
+ `${label} must be a portable single-segment id (letters, numbers, @, ., _, or -)`
41
+ );
42
+ }
43
+ }
44
+
45
+ // src/paths.ts
5
46
  var MACHINE_SHARED_MARKER = ".gezel-machine-shared-v1.json";
6
47
  function machineSharedHome(platform = process.platform, env = process.env) {
7
48
  const override = env.GEZEL_MACHINE_SHARED_HOME;
@@ -42,19 +83,23 @@ function activeMachineSharedHome(env = process.env) {
42
83
  }
43
84
  }
44
85
  function userGezelDir(root, gezelId, external) {
86
+ assertSafeEntityId(gezelId, "gezel id");
45
87
  return join(external?.gezels ?? join(root, "gezels"), gezelId);
46
88
  }
47
89
  function userProjectDir(root, projectId) {
90
+ assertSafeEntityId(projectId, "project id");
48
91
  return join(root, "projects", projectId);
49
92
  }
50
93
  function projectPrivateDir(root, projectId) {
51
94
  return userProjectDir(root, projectId);
52
95
  }
53
96
  function machineSharedGezelDir(gezelId) {
97
+ assertSafeEntityId(gezelId, "gezel id");
54
98
  const shared = activeMachineSharedHome();
55
99
  return shared ? join(shared, "gezels", gezelId) : null;
56
100
  }
57
101
  function machineSharedProjectDir(projectId) {
102
+ assertSafeEntityId(projectId, "project id");
58
103
  const shared = activeMachineSharedHome();
59
104
  return shared ? join(shared, "projects", projectId) : null;
60
105
  }
@@ -113,6 +158,7 @@ function gezelDir(root, gezelId, external) {
113
158
  return userGezelDir(root, gezelId, external);
114
159
  }
115
160
  function gezelLocalDir(root, gezelId) {
161
+ assertSafeEntityId(gezelId, "gezel id");
116
162
  return join(root, "gezels", gezelId);
117
163
  }
118
164
  function gezelSessionsDir(root, gezelId, external) {
@@ -149,6 +195,7 @@ function gezelPoppetjePath(root, gezelId, external) {
149
195
  return join(gezelDir(root, gezelId, external), "poppetje.json");
150
196
  }
151
197
  function projectDir(root, projectId, external) {
198
+ assertSafeEntityId(projectId, "project id");
152
199
  if (projectStorageScope(root, projectId) === "machine-shared") {
153
200
  return machineSharedProjectDir(projectId);
154
201
  }
@@ -169,6 +216,9 @@ function projectMetaFile(root, projectId) {
169
216
  function projectFindingLifecycleFile(root, projectId) {
170
217
  return join(projectPrivateDir(root, projectId), "finding-lifecycle.json");
171
218
  }
219
+ function projectBoekwachterIssuesFile(root, projectId) {
220
+ return join(projectPrivateDir(root, projectId), "boekwachter-issues.json");
221
+ }
172
222
  function projectCodeReviewsFile(root, projectId) {
173
223
  return join(projectPrivateDir(root, projectId), "code-reviews.json");
174
224
  }
@@ -181,6 +231,10 @@ function projectDocsDir(root, projectId, external) {
181
231
  function projectArtifactsDir(root, projectId, external) {
182
232
  return join(projectDir(root, projectId, external), "artifacts");
183
233
  }
234
+ var PROJECT_SHADOW_DIR_NAME = "shadow";
235
+ function projectShadowDir(root, projectId, external) {
236
+ return join(projectArtifactsDir(root, projectId, external), PROJECT_SHADOW_DIR_NAME);
237
+ }
184
238
  function projectMemoriesDir(root, projectId, external) {
185
239
  return join(projectDir(root, projectId, external), "memories");
186
240
  }
@@ -331,6 +385,9 @@ function fallbackProjectIndexDir(root, projectId) {
331
385
  function projectContentIndexDbFile(root, projectId, workspaceDir) {
332
386
  return projectStorageScope(root, projectId) === "machine-shared" ? join(fallbackProjectIndexDir(root, projectId), "index.db") : projectLocalIndexDbFile(workspaceDir);
333
387
  }
388
+ function projectArtifactsIndexDbFile(root, projectId) {
389
+ return join(fallbackProjectIndexDir(root, projectId), "artifacts.db");
390
+ }
334
391
  function projectLocalVillageFile(workspaceDir) {
335
392
  return join(projectLocalRoot(workspaceDir), "village.json");
336
393
  }
@@ -350,6 +407,7 @@ function projectLocalGezelsRoot(workspaceDir) {
350
407
  return join(projectLocalRoot(workspaceDir), "gezels");
351
408
  }
352
409
  function projectLocalGezelDir(workspaceDir, localId) {
410
+ assertSafeEntityId(localId, "project-local gezel id");
353
411
  return join(projectLocalGezelsRoot(workspaceDir), localId);
354
412
  }
355
413
  function projectLocalCraftbooksRoot(workspaceDir) {
@@ -450,6 +508,7 @@ async function readConfigRaw(root) {
450
508
  }
451
509
  export {
452
510
  MACHINE_SHARED_MARKER,
511
+ PROJECT_SHADOW_DIR_NAME,
453
512
  activeMachineSharedHome,
454
513
  backupsDir,
455
514
  channelsDir,
@@ -501,6 +560,8 @@ export {
501
560
  playwrightBrowsersDir,
502
561
  projectActivityFile,
503
562
  projectArtifactsDir,
563
+ projectArtifactsIndexDbFile,
564
+ projectBoekwachterIssuesFile,
504
565
  projectCodeReviewsFile,
505
566
  projectContentIndexDbFile,
506
567
  projectCreateTransactionsRoot,
@@ -534,6 +595,7 @@ export {
534
595
  projectScriptRunFile,
535
596
  projectScriptRunsDir,
536
597
  projectScriptsDir,
598
+ projectShadowDir,
537
599
  projectStorageDir,
538
600
  projectStorageScope,
539
601
  projectTaskAboutFile,