@nowcrew/daemon 0.5.27 → 0.5.28

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (77) hide show
  1. package/package.json +2 -2
  2. package/dist/attachments.js +0 -196
  3. package/dist/bound-im-decision.js +0 -22
  4. package/dist/completion-retransmitter.js +0 -77
  5. package/dist/computer-cli.js +0 -274
  6. package/dist/computer-profile-lock.js +0 -395
  7. package/dist/computer-profile.js +0 -364
  8. package/dist/computer-service.js +0 -358
  9. package/dist/config.js +0 -82
  10. package/dist/console-collapse.js +0 -13
  11. package/dist/console-formatter.js +0 -77
  12. package/dist/console-payload.js +0 -73
  13. package/dist/console.js +0 -329
  14. package/dist/daemon-startup-error.js +0 -30
  15. package/dist/execution-backend.js +0 -44
  16. package/dist/execution-event-limit.js +0 -64
  17. package/dist/execution-journal-lock.js +0 -421
  18. package/dist/execution-journal.js +0 -716
  19. package/dist/execution-protocol.js +0 -342
  20. package/dist/execution-recovery.js +0 -95
  21. package/dist/execution-runner.js +0 -659
  22. package/dist/execution-supervisor-child.js +0 -236
  23. package/dist/execution-supervisor.js +0 -316
  24. package/dist/execution-telemetry-journal.js +0 -71
  25. package/dist/external-output.js +0 -114
  26. package/dist/i18n.js +0 -64
  27. package/dist/json-result.js +0 -27
  28. package/dist/list-models.js +0 -92
  29. package/dist/local-executor.js +0 -439
  30. package/dist/log-format.js +0 -10
  31. package/dist/machine-info.js +0 -124
  32. package/dist/main.js +0 -118
  33. package/dist/normalize.js +0 -170
  34. package/dist/origin-decision.js +0 -44
  35. package/dist/platform.js +0 -8
  36. package/dist/prompt.js +0 -307
  37. package/dist/provider-env.js +0 -90
  38. package/dist/remote/claude-bridge.js +0 -402
  39. package/dist/remote/claude-channel.js +0 -164
  40. package/dist/remote/codex-client.js +0 -408
  41. package/dist/remote/codex-runtime.js +0 -77
  42. package/dist/remote/config.js +0 -83
  43. package/dist/remote/gateway.js +0 -572
  44. package/dist/remote/protocol.js +0 -178
  45. package/dist/remote/remote-cli.js +0 -233
  46. package/dist/remote/session-discovery.js +0 -249
  47. package/dist/remote/wrapper.js +0 -40
  48. package/dist/runner.js +0 -234
  49. package/dist/runtime-cancellation.js +0 -74
  50. package/dist/runtime-capabilities.js +0 -43
  51. package/dist/runtime-path.js +0 -60
  52. package/dist/runtimes/claude.js +0 -51
  53. package/dist/runtimes/codex-app-server-runner.js +0 -344
  54. package/dist/runtimes/codex-deepseek-catalog.js +0 -7
  55. package/dist/runtimes/codex-deepseek-config.js +0 -50
  56. package/dist/runtimes/codex.js +0 -53
  57. package/dist/runtimes/kimi-acp-runner.js +0 -364
  58. package/dist/runtimes/kimi.js +0 -45
  59. package/dist/runtimes/progress-watchdog.js +0 -26
  60. package/dist/scheduled-report.js +0 -51
  61. package/dist/scheduled-run-report.js +0 -57
  62. package/dist/serve-lifecycle.js +0 -82
  63. package/dist/serve.js +0 -868
  64. package/dist/session.js +0 -82
  65. package/dist/shared-execution-slots.js +0 -68
  66. package/dist/shutdown-deadline.js +0 -32
  67. package/dist/skill-preview.js +0 -21
  68. package/dist/skills.js +0 -56
  69. package/dist/slog.js +0 -228
  70. package/dist/supervised-runtime.js +0 -104
  71. package/dist/token.js +0 -24
  72. package/dist/unified-diff.js +0 -84
  73. package/dist/websocket-shutdown.js +0 -53
  74. package/dist/win32-job-object.js +0 -193
  75. package/dist/workspace-fs.js +0 -80
  76. package/dist/workspace-import.js +0 -127
  77. package/dist/workspace.js +0 -148
@@ -1,364 +0,0 @@
1
- import { access, chmod, mkdir, readFile, readdir, rename, rm, stat, writeFile } from "node:fs/promises";
2
- import { constants } from "node:fs";
3
- import { realpathSync } from "node:fs";
4
- import { homedir } from "node:os";
5
- import { dirname, posix, resolve, win32 } from "node:path";
6
- import { randomUUID } from "node:crypto";
7
- import { spawn } from "node:child_process";
8
- import { z } from "zod";
9
- import { acquireProfileSaveLock, } from "./computer-profile-lock.js";
10
- import { formatDaemonText } from "./i18n.js";
11
- const PROFILE_NAME = /^[a-z0-9][a-z0-9_-]{0,47}$/;
12
- const ServerUrlSchema = z.string().url().max(2048).refine((value) => {
13
- const url = new URL(value);
14
- return (url.protocol === "http:" || url.protocol === "https:") && !url.username && !url.password;
15
- }, "serverUrl must be an HTTP(S) URL without embedded credentials");
16
- const ProfileSchema = z.object({
17
- version: z.literal(1),
18
- name: z.string().regex(PROFILE_NAME),
19
- serverUrl: ServerUrlSchema,
20
- machineToken: z.string().startsWith("sk_machine_").max(4096),
21
- agentsRoot: z.string().min(1).optional(),
22
- runtimePath: z.string().min(1).max(32768).refine((value) => !value.includes("\0") && !value.includes("\n") && !value.includes("\r"), "runtimePath must be a single line").optional(),
23
- }).strict();
24
- const ProfileAgentsRootCandidateSchema = ProfileSchema.pick({
25
- name: true,
26
- serverUrl: true,
27
- agentsRoot: true,
28
- }).strict();
29
- const StoredPlainProfileSchema = ProfileSchema;
30
- const StoredProtectedProfileSchema = ProfileSchema.omit({ machineToken: true }).extend({
31
- machineTokenProtected: z.object({
32
- scheme: z.literal("dpapi-current-user"),
33
- ciphertext: z.string().min(1).max(16384),
34
- }).strict(),
35
- }).strict();
36
- const StoredProfileSchema = z.union([StoredPlainProfileSchema, StoredProtectedProfileSchema]);
37
- export const PROFILE_AGENTS_ROOT_CONFLICT_MESSAGE = "Profile '{{profile}}' conflicts with profile '{{conflict}}': both resolve to agents root '{{agentsRoot}}'. Save it with a unique root, for example: {{command}}";
38
- export class ProfileAgentsRootConflictError extends Error {
39
- profile;
40
- conflict;
41
- agentsRoot;
42
- command;
43
- constructor(input) {
44
- const quote = input.platform === "win32" ? quotePowerShellArgument : quotePosixArgument;
45
- const pathApi = input.platform === "win32" ? win32 : posix;
46
- const suggestedRoot = resolveAgentsRoot(pathApi.join(input.userHome, ".crew", `agents-${input.profile}`), input.userHome, input.platform);
47
- const command = [
48
- "crew-daemon profile save",
49
- quote(input.profile),
50
- "--server-url",
51
- quote(input.serverUrl),
52
- "--agents-root",
53
- quote(suggestedRoot),
54
- "--token-stdin",
55
- ].join(" ");
56
- super(formatDaemonText("en", PROFILE_AGENTS_ROOT_CONFLICT_MESSAGE, {
57
- profile: input.profile,
58
- conflict: input.conflict,
59
- agentsRoot: input.agentsRoot,
60
- command,
61
- }));
62
- this.name = "ProfileAgentsRootConflictError";
63
- this.profile = input.profile;
64
- this.conflict = input.conflict;
65
- this.agentsRoot = input.agentsRoot;
66
- this.command = command;
67
- }
68
- }
69
- const quotePosixArgument = (value) => `'${value.replaceAll("'", `'"'"'`)}'`;
70
- const quotePowerShellArgument = (value) => `'${value.replaceAll("'", "''")}'`;
71
- export function daemonHome(env = process.env) {
72
- return resolve(env.CREW_DAEMON_HOME ?? resolve(homedir(), ".crew/daemon"));
73
- }
74
- export function resolveAgentsRoot(configured, userHome = homedir(), platform = process.platform) {
75
- const pathApi = platform === "win32" ? win32 : posix;
76
- const normalizedHome = pathApi.resolve(userHome);
77
- if (configured === undefined)
78
- return canonicalizeRoot(pathApi.resolve(normalizedHome, ".crew", "agents"), platform);
79
- const expanded = configured === "~"
80
- ? normalizedHome
81
- : configured.startsWith("~/") || (platform === "win32" && configured.startsWith("~\\"))
82
- ? pathApi.resolve(normalizedHome, configured.slice(2))
83
- : configured;
84
- return canonicalizeRoot(pathApi.resolve(expanded), platform);
85
- }
86
- function canonicalizeRoot(path, platform) {
87
- const compatibleHost = (platform === "win32") === (process.platform === "win32");
88
- if (!compatibleHost)
89
- return path;
90
- try {
91
- return realpathSync.native(path);
92
- }
93
- catch (error) {
94
- const code = error.code;
95
- if (code === "ENOTDIR")
96
- return path;
97
- if (code !== "ENOENT")
98
- throw error;
99
- }
100
- const pathApi = platform === "win32" ? win32 : posix;
101
- const missingSegments = [];
102
- let ancestor = path;
103
- for (;;) {
104
- const parent = pathApi.dirname(ancestor);
105
- if (parent === ancestor)
106
- return path;
107
- missingSegments.unshift(pathApi.basename(ancestor));
108
- ancestor = parent;
109
- try {
110
- return pathApi.resolve(realpathSync.native(ancestor), ...missingSegments);
111
- }
112
- catch (error) {
113
- const code = error.code;
114
- if (code === "ENOTDIR")
115
- return path;
116
- if (code !== "ENOENT")
117
- throw error;
118
- }
119
- }
120
- }
121
- function comparableAgentsRoot(path, platform) {
122
- return platform === "win32" ? win32.normalize(path).toLocaleLowerCase("en-US") : path;
123
- }
124
- export function validateProfileName(name) {
125
- if (!PROFILE_NAME.test(name)) {
126
- throw new Error("profile name must match [a-z0-9][a-z0-9_-]{0,47}");
127
- }
128
- return name;
129
- }
130
- export function profilePath(name, home = daemonHome()) {
131
- return resolve(home, "profiles", `${validateProfileName(name)}.json`);
132
- }
133
- async function atomicPrivateWrite(path, content, beforeCommit) {
134
- await mkdir(dirname(path), { recursive: true, mode: 0o700 });
135
- await chmod(dirname(path), 0o700);
136
- const temporary = `${path}.${randomUUID()}.tmp`;
137
- try {
138
- await writeFile(temporary, content, { encoding: "utf8", mode: 0o600, flag: "wx" });
139
- await beforeCommit?.(temporary);
140
- await rename(temporary, path);
141
- await chmod(path, 0o600);
142
- }
143
- finally {
144
- await rm(temporary, { force: true });
145
- }
146
- }
147
- const DPAPI_PROTECT = "$p=[Console]::In.ReadToEnd();$b=[Text.Encoding]::UTF8.GetBytes($p);$e=[Security.Cryptography.ProtectedData]::Protect($b,$null,[Security.Cryptography.DataProtectionScope]::CurrentUser);[Convert]::ToBase64String($e)";
148
- const DPAPI_UNPROTECT = "$p=[Console]::In.ReadToEnd();$b=[Convert]::FromBase64String($p);$d=[Security.Cryptography.ProtectedData]::Unprotect($b,$null,[Security.Cryptography.DataProtectionScope]::CurrentUser);[Console]::Out.Write([Text.Encoding]::UTF8.GetString($d))";
149
- async function powershellStdin(script, input) {
150
- return new Promise((resolvePromise, reject) => {
151
- const child = spawn("powershell.exe", ["-NoProfile", "-NonInteractive", "-Command", script], {
152
- stdio: ["pipe", "pipe", "pipe"],
153
- windowsHide: true,
154
- });
155
- const stdout = [];
156
- const stderr = [];
157
- child.stdout.on("data", (chunk) => stdout.push(chunk));
158
- child.stderr.on("data", (chunk) => stderr.push(chunk));
159
- child.once("error", reject);
160
- child.once("close", (code) => {
161
- if (code !== 0) {
162
- reject(new Error(`Windows DPAPI operation failed: ${Buffer.concat(stderr).toString("utf8").trim() || `exit ${code}`}`));
163
- return;
164
- }
165
- resolvePromise(Buffer.concat(stdout).toString("utf8").trim());
166
- });
167
- child.stdin.end(input);
168
- });
169
- }
170
- export const windowsDpapiProtector = {
171
- protect: (plaintext) => powershellStdin(DPAPI_PROTECT, plaintext),
172
- unprotect: (ciphertext) => powershellStdin(DPAPI_UNPROTECT, ciphertext),
173
- };
174
- function storageOptions(options) {
175
- const platform = options.platform ?? process.platform;
176
- return {
177
- platform,
178
- ...(options.protector ? { protector: options.protector } : platform === "win32" ? { protector: windowsDpapiProtector } : {}),
179
- ...(options.harden ? { harden: options.harden } : {}),
180
- };
181
- }
182
- function attachCleanupError(primaryError, cleanupError) {
183
- try {
184
- if (!(primaryError instanceof Error)
185
- || !Object.isExtensible(primaryError)
186
- || Object.prototype.hasOwnProperty.call(primaryError, "cleanupError"))
187
- return;
188
- Object.defineProperty(primaryError, "cleanupError", {
189
- configurable: true,
190
- value: cleanupError,
191
- });
192
- }
193
- catch {
194
- // Preserve the primary error even when it cannot accept diagnostics.
195
- }
196
- }
197
- export async function saveProfile(input, home = daemonHome(), options = {}) {
198
- const profile = ProfileSchema.parse({ version: 1, ...input });
199
- const configured = storageOptions(options);
200
- const release = await acquireProfileSaveLock(resolve(home, "profiles"), {
201
- ...(options.lockTimeoutMs === undefined ? {} : { timeoutMs: options.lockTimeoutMs }),
202
- ...(options.lockRetryMs === undefined ? {} : { retryMs: options.lockRetryMs }),
203
- ...(options.lockNow === undefined ? {} : { now: options.lockNow }),
204
- ...(options.lockWait === undefined ? {} : { wait: options.lockWait }),
205
- ...(options.lockProcessController === undefined
206
- ? {}
207
- : { processController: options.lockProcessController }),
208
- ...(options.lockHooks === undefined ? {} : { hooks: options.lockHooks }),
209
- });
210
- let bodyFailed = false;
211
- let bodyError;
212
- try {
213
- await assertProfileAgentsRootUnique(profile, home, options.userHome ?? homedir(), options);
214
- if (configured.platform === "win32") {
215
- if (!configured.protector)
216
- throw new Error("Windows profile storage requires CurrentUser DPAPI");
217
- if (!configured.harden)
218
- throw new Error("Windows profile storage requires ACL hardening");
219
- const ciphertext = await configured.protector.protect(profile.machineToken);
220
- if (!ciphertext)
221
- throw new Error("Windows DPAPI returned an empty ciphertext");
222
- const stored = StoredProtectedProfileSchema.parse({
223
- version: 1,
224
- name: profile.name,
225
- serverUrl: profile.serverUrl,
226
- ...(profile.agentsRoot ? { agentsRoot: profile.agentsRoot } : {}),
227
- ...(profile.runtimePath ? { runtimePath: profile.runtimePath } : {}),
228
- machineTokenProtected: { scheme: "dpapi-current-user", ciphertext },
229
- });
230
- await atomicPrivateWrite(profilePath(profile.name, home), `${JSON.stringify(stored, null, 2)}\n`, configured.harden);
231
- }
232
- else {
233
- await atomicPrivateWrite(profilePath(profile.name, home), `${JSON.stringify(profile, null, 2)}\n`);
234
- }
235
- }
236
- catch (error) {
237
- bodyFailed = true;
238
- bodyError = error;
239
- throw error;
240
- }
241
- finally {
242
- try {
243
- await release();
244
- }
245
- catch (cleanupError) {
246
- if (!bodyFailed)
247
- throw cleanupError;
248
- attachCleanupError(bodyError, cleanupError);
249
- }
250
- }
251
- return profile;
252
- }
253
- export async function loadProfile(name, home = daemonHome(), options = {}) {
254
- const path = profilePath(name, home);
255
- try {
256
- const stored = StoredProfileSchema.parse(JSON.parse(await readFile(path, "utf8")));
257
- const configured = storageOptions(options);
258
- if (configured.platform === "win32") {
259
- if (!("machineTokenProtected" in stored)) {
260
- throw new Error(`Computer profile '${name}' contains a plaintext token and is refused on Windows`);
261
- }
262
- if (!configured.protector)
263
- throw new Error("Windows profile storage requires CurrentUser DPAPI");
264
- return ProfileSchema.parse({
265
- version: stored.version,
266
- name: stored.name,
267
- serverUrl: stored.serverUrl,
268
- ...(stored.agentsRoot ? { agentsRoot: stored.agentsRoot } : {}),
269
- ...(stored.runtimePath ? { runtimePath: stored.runtimePath } : {}),
270
- machineToken: await configured.protector.unprotect(stored.machineTokenProtected.ciphertext),
271
- });
272
- }
273
- if (!("machineToken" in stored)) {
274
- throw new Error(`Computer profile '${name}' is DPAPI-protected and can only be loaded by its Windows user`);
275
- }
276
- return stored;
277
- }
278
- catch (error) {
279
- if (error.code === "ENOENT") {
280
- throw new Error(`Computer profile '${name}' does not exist`);
281
- }
282
- throw error;
283
- }
284
- }
285
- export async function listProfiles(home = daemonHome()) {
286
- const root = resolve(home, "profiles");
287
- try {
288
- const entries = await readdir(root, { withFileTypes: true });
289
- return entries
290
- .filter((entry) => entry.isFile() && entry.name.endsWith(".json"))
291
- .map((entry) => entry.name.slice(0, -5))
292
- .filter((name) => PROFILE_NAME.test(name))
293
- .sort();
294
- }
295
- catch (error) {
296
- if (error.code === "ENOENT")
297
- return [];
298
- throw error;
299
- }
300
- }
301
- export async function inspectProfileAgentsRoot(profile, home = daemonHome(), userHome = homedir(), options = {}) {
302
- const platform = storageOptions(options).platform;
303
- const agentsRoot = resolveAgentsRoot(profile.agentsRoot, userHome, platform);
304
- const comparableRoot = comparableAgentsRoot(agentsRoot, platform);
305
- const names = (await listProfiles(home)).filter((name) => name !== profile.name);
306
- const profiles = await Promise.all(names.map((name) => loadProfile(name, home, options)));
307
- return {
308
- agentsRoot,
309
- duplicateProfiles: profiles
310
- .filter((candidate) => comparableAgentsRoot(resolveAgentsRoot(candidate.agentsRoot, userHome, platform), platform) === comparableRoot)
311
- .map((candidate) => candidate.name),
312
- };
313
- }
314
- export async function assertProfileAgentsRootUnique(profile, home = daemonHome(), userHome = homedir(), options = {}) {
315
- const candidate = ProfileAgentsRootCandidateSchema.parse({
316
- name: profile.name,
317
- serverUrl: profile.serverUrl,
318
- ...(profile.agentsRoot === undefined ? {} : { agentsRoot: profile.agentsRoot }),
319
- });
320
- const inspection = await inspectProfileAgentsRoot(candidate, home, userHome, options);
321
- const conflict = inspection.duplicateProfiles[0];
322
- if (conflict === undefined)
323
- return;
324
- throw new ProfileAgentsRootConflictError({
325
- profile: candidate.name,
326
- conflict,
327
- agentsRoot: inspection.agentsRoot,
328
- serverUrl: candidate.serverUrl,
329
- userHome,
330
- platform: storageOptions(options).platform,
331
- });
332
- }
333
- export async function removeProfile(name, home = daemonHome()) {
334
- await rm(profilePath(name, home), { force: true });
335
- }
336
- export async function profileIsPrivate(name, home = daemonHome(), platform = process.platform) {
337
- const path = profilePath(name, home);
338
- await access(path, constants.R_OK);
339
- if (platform === "win32") {
340
- const stored = StoredProfileSchema.parse(JSON.parse(await readFile(path, "utf8")));
341
- return "machineTokenProtected" in stored && stored.machineTokenProtected.scheme === "dpapi-current-user";
342
- }
343
- const { mode } = await stat(path);
344
- return (mode & 0o077) === 0;
345
- }
346
- export function publicProfile(profile) {
347
- return {
348
- version: profile.version,
349
- name: profile.name,
350
- serverUrl: profile.serverUrl,
351
- machineTokenConfigured: true,
352
- ...(profile.agentsRoot ? { agentsRoot: profile.agentsRoot } : {}),
353
- };
354
- }
355
- export function applyProfileToEnv(profile, env) {
356
- env.CREW_SERVER_URL = profile.serverUrl;
357
- env.CREW_MACHINE_TOKEN = profile.machineToken;
358
- if (profile.agentsRoot)
359
- env.CREW_AGENTS_ROOT = profile.agentsRoot;
360
- else
361
- delete env.CREW_AGENTS_ROOT;
362
- if (profile.runtimePath)
363
- env.PATH = profile.runtimePath;
364
- }