@nowcrew/daemon 0.5.18 → 0.5.19
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/README.md +23 -0
- package/dist/attachments.js +161 -0
- package/dist/execution-event-limit.js +1 -1
- package/dist/execution-protocol.js +19 -1
- package/dist/execution-runner.js +38 -5
- package/dist/external-output.js +86 -0
- package/dist/list-models.js +41 -5
- package/dist/local-executor.js +43 -5
- package/dist/machine-info.js +5 -1
- package/dist/origin-decision.js +3 -1
- package/dist/prompt.js +4 -1
- package/dist/runner.js +3 -3
- package/dist/runtime-capabilities.js +38 -0
- package/dist/runtime-path.js +56 -0
- package/dist/runtimes/claude.js +1 -0
- package/dist/runtimes/codex-app-server-runner.js +340 -0
- package/dist/runtimes/codex.js +2 -0
- package/dist/runtimes/kimi-acp-runner.js +117 -17
- package/dist/runtimes/kimi.js +2 -0
- package/dist/runtimes/progress-watchdog.js +26 -0
- package/dist/serve.js +38 -6
- package/dist/session.js +1 -1
- package/package.json +3 -3
|
@@ -4,6 +4,9 @@ import { Readable, Writable } from "node:stream";
|
|
|
4
4
|
import { pathToFileURL } from "node:url";
|
|
5
5
|
import spawn from "cross-spawn";
|
|
6
6
|
import { PROTOCOL_VERSION, client, methods, ndJsonStream, } from "@agentclientprotocol/sdk";
|
|
7
|
+
import { augmentedPath } from "../runtime-path.js";
|
|
8
|
+
import { startFirstProgressWatchdog } from "./progress-watchdog.js";
|
|
9
|
+
import { assertKimiLegacyPromptFits, buildKimiArgs } from "./kimi.js";
|
|
7
10
|
const ERROR_MESSAGE_CAP = 2_000;
|
|
8
11
|
const PROBE_TIMEOUT_MS = 5_000;
|
|
9
12
|
function jsonLine(event) {
|
|
@@ -60,6 +63,17 @@ function safeErrorMessage(error, prompt) {
|
|
|
60
63
|
const redacted = prompt && raw.includes(prompt) ? raw.replaceAll(prompt, "[prompt redacted]") : raw;
|
|
61
64
|
return redacted.slice(0, ERROR_MESSAGE_CAP);
|
|
62
65
|
}
|
|
66
|
+
export function isKimiAuthenticationRequired(error) {
|
|
67
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
68
|
+
return /\bauthentication required\b/i.test(message);
|
|
69
|
+
}
|
|
70
|
+
export function kimiResumeMethod(capabilities) {
|
|
71
|
+
if (capabilities?.sessionCapabilities?.resume != null)
|
|
72
|
+
return "resume";
|
|
73
|
+
if (capabilities?.loadSession)
|
|
74
|
+
return "load";
|
|
75
|
+
return null;
|
|
76
|
+
}
|
|
63
77
|
/** Full access may approve an operation, but it must never fabricate an answer to an agent question. */
|
|
64
78
|
export function selectKimiPermission(params) {
|
|
65
79
|
const allowOnce = params.options.filter((option) => option.kind === "allow_once");
|
|
@@ -100,11 +114,12 @@ async function stopChild(child) {
|
|
|
100
114
|
await closed;
|
|
101
115
|
}
|
|
102
116
|
}
|
|
103
|
-
/**
|
|
117
|
+
/** Probe the ACP transport without starting a session or forcing an optional interactive login flow. */
|
|
104
118
|
export async function probeKimiAcp(options, spawnProcess = spawn) {
|
|
105
119
|
const child = spawnProcess(options.bin, ["acp"], {
|
|
106
120
|
cwd: process.cwd(),
|
|
107
|
-
|
|
121
|
+
// probe 在 daemon 自身 PATH 下运行,补上用户级 CLI 目录,与 which 探测保持一致。
|
|
122
|
+
env: { ...process.env, PATH: augmentedPath() },
|
|
108
123
|
stdio: ["pipe", "pipe", "pipe"],
|
|
109
124
|
});
|
|
110
125
|
if (child.stdin === null || child.stdout === null || child.stderr === null)
|
|
@@ -119,15 +134,11 @@ export async function probeKimiAcp(options, spawnProcess = spawn) {
|
|
|
119
134
|
try {
|
|
120
135
|
const stream = ndJsonStream(Writable.toWeb(child.stdin), Readable.toWeb(child.stdout));
|
|
121
136
|
const connected = app.connectWith(stream, async (context) => {
|
|
122
|
-
|
|
137
|
+
await context.request(methods.agent.initialize, {
|
|
123
138
|
protocolVersion: PROTOCOL_VERSION,
|
|
124
139
|
clientCapabilities: {},
|
|
125
140
|
clientInfo: { name: "nowcrew-daemon", version: "1" },
|
|
126
141
|
});
|
|
127
|
-
const authMethod = initialized.authMethods?.[0];
|
|
128
|
-
if (authMethod !== undefined) {
|
|
129
|
-
await context.request(methods.agent.authenticate, { methodId: authMethod.id });
|
|
130
|
-
}
|
|
131
142
|
return true;
|
|
132
143
|
});
|
|
133
144
|
const result = await Promise.race([
|
|
@@ -157,12 +168,15 @@ export async function runKimiAcp(options) {
|
|
|
157
168
|
env: process.env,
|
|
158
169
|
stdio: ["pipe", "pipe", "pipe"],
|
|
159
170
|
});
|
|
171
|
+
let runtimeChild = child;
|
|
160
172
|
if (child.stdin === null || child.stdout === null || child.stderr === null) {
|
|
161
173
|
throw new Error("Kimi ACP process did not expose stdio");
|
|
162
174
|
}
|
|
163
175
|
child.stderr.pipe(process.stderr, { end: false });
|
|
164
176
|
let context = null;
|
|
165
177
|
let sessionId = null;
|
|
178
|
+
let acpSemanticProgress = false;
|
|
179
|
+
let progressTimedOut = false;
|
|
166
180
|
let cancelling = false;
|
|
167
181
|
const cancel = async () => {
|
|
168
182
|
if (cancelling)
|
|
@@ -171,7 +185,7 @@ export async function runKimiAcp(options) {
|
|
|
171
185
|
if (context !== null && sessionId !== null) {
|
|
172
186
|
await context.notify(methods.agent.session.cancel, { sessionId }).catch(() => undefined);
|
|
173
187
|
}
|
|
174
|
-
await stopChild(
|
|
188
|
+
await stopChild(runtimeChild);
|
|
175
189
|
};
|
|
176
190
|
const onSignal = () => {
|
|
177
191
|
void cancel().finally(() => process.exit(130));
|
|
@@ -181,9 +195,13 @@ export async function runKimiAcp(options) {
|
|
|
181
195
|
const app = client({ name: "nowcrew-daemon-kimi" })
|
|
182
196
|
.onRequest(methods.client.session.requestPermission, ({ params }) => selectKimiPermission(params))
|
|
183
197
|
.onNotification(methods.client.session.update, async ({ params }) => {
|
|
198
|
+
acpSemanticProgress = true;
|
|
199
|
+
firstProgress.observe();
|
|
184
200
|
for (const event of mapKimiAcpUpdate(params.update))
|
|
185
201
|
await jsonLine(event);
|
|
186
202
|
});
|
|
203
|
+
let firstProgress = startFirstProgressWatchdog(() => undefined);
|
|
204
|
+
firstProgress.stop();
|
|
187
205
|
try {
|
|
188
206
|
const stream = ndJsonStream(Writable.toWeb(child.stdin), Readable.toWeb(child.stdout));
|
|
189
207
|
const result = await app.connectWith(stream, async (nextContext) => {
|
|
@@ -196,15 +214,35 @@ export async function runKimiAcp(options) {
|
|
|
196
214
|
if (process.env.CREW_KIMI_ACP_DEBUG === "1") {
|
|
197
215
|
process.stderr.write(`Kimi ACP auth methods: ${JSON.stringify(initialized.authMethods ?? [])}\n`);
|
|
198
216
|
}
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
217
|
+
if (options.resume && options.sessionId) {
|
|
218
|
+
const resumeMethod = kimiResumeMethod(initialized.agentCapabilities);
|
|
219
|
+
if (resumeMethod === "resume") {
|
|
220
|
+
await nextContext.request(methods.agent.session.resume, {
|
|
221
|
+
sessionId: options.sessionId,
|
|
222
|
+
cwd: process.cwd(),
|
|
223
|
+
mcpServers: [],
|
|
224
|
+
});
|
|
225
|
+
}
|
|
226
|
+
else if (resumeMethod === "load") {
|
|
227
|
+
await nextContext.request(methods.agent.session.load, {
|
|
228
|
+
sessionId: options.sessionId,
|
|
229
|
+
cwd: process.cwd(),
|
|
230
|
+
mcpServers: [],
|
|
231
|
+
});
|
|
232
|
+
}
|
|
233
|
+
else {
|
|
234
|
+
throw new Error("Kimi ACP does not advertise session resume support");
|
|
235
|
+
}
|
|
236
|
+
sessionId = options.sessionId;
|
|
202
237
|
}
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
238
|
+
else {
|
|
239
|
+
const session = await nextContext.request(methods.agent.session.new, {
|
|
240
|
+
cwd: process.cwd(),
|
|
241
|
+
mcpServers: [],
|
|
242
|
+
});
|
|
243
|
+
sessionId = session.sessionId;
|
|
244
|
+
}
|
|
245
|
+
await jsonLine({ type: "thread.started", thread_id: sessionId });
|
|
208
246
|
if (options.model) {
|
|
209
247
|
await nextContext.request(methods.agent.session.setConfigOption, {
|
|
210
248
|
sessionId,
|
|
@@ -212,11 +250,20 @@ export async function runKimiAcp(options) {
|
|
|
212
250
|
value: options.model,
|
|
213
251
|
});
|
|
214
252
|
}
|
|
253
|
+
firstProgress = startFirstProgressWatchdog(() => {
|
|
254
|
+
progressTimedOut = true;
|
|
255
|
+
void cancel();
|
|
256
|
+
});
|
|
215
257
|
return nextContext.request(methods.agent.session.prompt, {
|
|
216
258
|
sessionId,
|
|
217
259
|
prompt: [{ type: "text", text: prompt }],
|
|
218
260
|
});
|
|
219
261
|
});
|
|
262
|
+
firstProgress.stop();
|
|
263
|
+
if (progressTimedOut) {
|
|
264
|
+
process.stderr.write("Kimi produced no semantic progress within the startup window\n");
|
|
265
|
+
return 1;
|
|
266
|
+
}
|
|
220
267
|
const usage = result.usage;
|
|
221
268
|
await jsonLine({
|
|
222
269
|
type: "turn.completed",
|
|
@@ -233,10 +280,54 @@ export async function runKimiAcp(options) {
|
|
|
233
280
|
return result.stopReason === "end_turn" ? 0 : result.stopReason === "cancelled" ? 130 : 1;
|
|
234
281
|
}
|
|
235
282
|
catch (error) {
|
|
283
|
+
if (progressTimedOut) {
|
|
284
|
+
process.stderr.write("Kimi produced no semantic progress within the startup window\n");
|
|
285
|
+
return 1;
|
|
286
|
+
}
|
|
287
|
+
if (!acpSemanticProgress && isKimiAuthenticationRequired(error)) {
|
|
288
|
+
firstProgress.stop();
|
|
289
|
+
await stopChild(child);
|
|
290
|
+
process.stderr.write("Kimi ACP requires account login; falling back to configured CLI provider transport\n");
|
|
291
|
+
assertKimiLegacyPromptFits(prompt);
|
|
292
|
+
const fallback = spawn(options.bin, buildKimiArgs({
|
|
293
|
+
wakePrompt: prompt,
|
|
294
|
+
effectivePermission: "full_access",
|
|
295
|
+
...(options.model === undefined ? {} : { model: options.model }),
|
|
296
|
+
...(options.resume && options.sessionId ? { sessionId: options.sessionId } : {}),
|
|
297
|
+
}), {
|
|
298
|
+
cwd: process.cwd(),
|
|
299
|
+
env: process.env,
|
|
300
|
+
stdio: ["ignore", "pipe", "pipe"],
|
|
301
|
+
});
|
|
302
|
+
runtimeChild = fallback;
|
|
303
|
+
if (fallback.stdout === null || fallback.stderr === null) {
|
|
304
|
+
throw new Error("Kimi CLI fallback did not expose output streams");
|
|
305
|
+
}
|
|
306
|
+
firstProgress = startFirstProgressWatchdog(() => {
|
|
307
|
+
progressTimedOut = true;
|
|
308
|
+
void stopChild(fallback);
|
|
309
|
+
});
|
|
310
|
+
fallback.stdout.on("data", () => firstProgress.observe());
|
|
311
|
+
fallback.stdout.pipe(process.stdout, { end: false });
|
|
312
|
+
fallback.stderr.pipe(process.stderr, { end: false });
|
|
313
|
+
const code = await new Promise((resolve, reject) => {
|
|
314
|
+
fallback.once("error", reject);
|
|
315
|
+
fallback.once("close", (exitCode, signal) => {
|
|
316
|
+
resolve(exitCode ?? (signal === null ? 1 : 128));
|
|
317
|
+
});
|
|
318
|
+
});
|
|
319
|
+
firstProgress.stop();
|
|
320
|
+
if (progressTimedOut) {
|
|
321
|
+
process.stderr.write("Kimi CLI fallback produced no output within the startup window\n");
|
|
322
|
+
return 1;
|
|
323
|
+
}
|
|
324
|
+
return code;
|
|
325
|
+
}
|
|
236
326
|
process.stderr.write(`Kimi ACP execution failed: ${safeErrorMessage(error, prompt)}\n`);
|
|
237
327
|
return 1;
|
|
238
328
|
}
|
|
239
329
|
finally {
|
|
330
|
+
firstProgress.stop();
|
|
240
331
|
process.off("SIGTERM", onSignal);
|
|
241
332
|
process.off("SIGINT", onSignal);
|
|
242
333
|
await stopChild(child);
|
|
@@ -248,11 +339,20 @@ function optionsFromArgv(argv) {
|
|
|
248
339
|
options: {
|
|
249
340
|
bin: { type: "string" },
|
|
250
341
|
model: { type: "string" },
|
|
342
|
+
session: { type: "string" },
|
|
343
|
+
resume: { type: "boolean", default: false },
|
|
251
344
|
},
|
|
252
345
|
});
|
|
253
346
|
if (!values.bin)
|
|
254
347
|
throw new Error("--bin is required");
|
|
255
|
-
|
|
348
|
+
if (values.resume && !values.session)
|
|
349
|
+
throw new Error("--resume requires --session");
|
|
350
|
+
return {
|
|
351
|
+
bin: values.bin,
|
|
352
|
+
...(values.model ? { model: values.model } : {}),
|
|
353
|
+
...(values.session ? { sessionId: values.session } : {}),
|
|
354
|
+
...(values.resume ? { resume: true } : {}),
|
|
355
|
+
};
|
|
256
356
|
}
|
|
257
357
|
if (process.argv[1] !== undefined && import.meta.url === pathToFileURL(process.argv[1]).href) {
|
|
258
358
|
runKimiAcp(optionsFromArgv(process.argv.slice(2)))
|
package/dist/runtimes/kimi.js
CHANGED
|
@@ -29,6 +29,8 @@ export function buildKimiArgs(input) {
|
|
|
29
29
|
const args = ["--output-format", "stream-json"];
|
|
30
30
|
if (input.model)
|
|
31
31
|
args.push("--model", input.model);
|
|
32
|
+
if (input.sessionId)
|
|
33
|
+
args.push("--session", input.sessionId);
|
|
32
34
|
args.push("--prompt", input.wakePrompt);
|
|
33
35
|
return args;
|
|
34
36
|
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
export const DEFAULT_FIRST_PROGRESS_TIMEOUT_MS = 120_000;
|
|
2
|
+
/**
|
|
3
|
+
* Bound the silent gap after a protocol turn starts. Once any semantic notification arrives,
|
|
4
|
+
* the runtime's configured total timeout remains authoritative; long-running tools are not killed
|
|
5
|
+
* merely because they produce no output.
|
|
6
|
+
*/
|
|
7
|
+
export function startFirstProgressWatchdog(onTimeout, timeoutMs = DEFAULT_FIRST_PROGRESS_TIMEOUT_MS) {
|
|
8
|
+
if (!Number.isFinite(timeoutMs) || timeoutMs <= 0) {
|
|
9
|
+
throw new RangeError("First-progress timeout must be a positive finite number");
|
|
10
|
+
}
|
|
11
|
+
let active = true;
|
|
12
|
+
const timer = setTimeout(() => {
|
|
13
|
+
if (!active)
|
|
14
|
+
return;
|
|
15
|
+
active = false;
|
|
16
|
+
onTimeout();
|
|
17
|
+
}, timeoutMs);
|
|
18
|
+
timer.unref?.();
|
|
19
|
+
const stop = () => {
|
|
20
|
+
if (!active)
|
|
21
|
+
return;
|
|
22
|
+
active = false;
|
|
23
|
+
clearTimeout(timer);
|
|
24
|
+
};
|
|
25
|
+
return { observe: stop, stop };
|
|
26
|
+
}
|
package/dist/serve.js
CHANGED
|
@@ -237,9 +237,10 @@ export function serve(config, opts = {}) {
|
|
|
237
237
|
initSlog(config.serverUrl, config.machineToken);
|
|
238
238
|
dslog("daemon.start", "daemon 常驻模式启动", { server_url: config.serverUrl, runtime: config.runtimeBin });
|
|
239
239
|
// 并行调度:同一 agent 可并行处理多个【不同任务】(线程/频道),每任务隔离 cwd+work-log。
|
|
240
|
-
//
|
|
241
|
-
//
|
|
240
|
+
// scheduled 重复 run 仍由 running 去重;普通同线程 wake 用 legacyTaskTails 串成 FIFO。
|
|
241
|
+
// 每 agent 超过并行上限的任务继续进入 sharedQueues(不丢)。
|
|
242
242
|
const running = new Set();
|
|
243
|
+
const legacyTaskTails = new Map();
|
|
243
244
|
const acquireSlot = async (handle) => {
|
|
244
245
|
await reserveSharedSlot(handle, "legacy").ready;
|
|
245
246
|
};
|
|
@@ -436,8 +437,23 @@ export function serve(config, opts = {}) {
|
|
|
436
437
|
&& "type" in decoded && decoded.type === "agent:start"
|
|
437
438
|
? LegacyAgentStartSchema.safeParse(decoded)
|
|
438
439
|
: null;
|
|
439
|
-
if (legacy !== null && !legacy.success)
|
|
440
|
+
if (legacy !== null && !legacy.success) {
|
|
441
|
+
// 帧结构不合法(如 server 端已升级到 execution v1 但本 daemon 仍按 legacy 校验)。
|
|
442
|
+
// 曾经静默 return——只表现为"@了 agent 没反应",且完全没有排查线索。记录字段路径和
|
|
443
|
+
// zod 错误码,不记录消息正文/附件等可能敏感的字段值。
|
|
444
|
+
const raw = decoded;
|
|
445
|
+
dslog("run.legacy_frame_rejected", "agent:start 帧未通过 legacy schema 校验,已丢弃", {
|
|
446
|
+
level: "WARN",
|
|
447
|
+
agent_handle: typeof raw.agentHandle === "string" ? raw.agentHandle : undefined,
|
|
448
|
+
channel_id: typeof raw.channelId === "string" ? raw.channelId : undefined,
|
|
449
|
+
reason: typeof raw.reason === "string" ? raw.reason : undefined,
|
|
450
|
+
issue_count: legacy.error.issues.length,
|
|
451
|
+
issue_paths: legacy.error.issues
|
|
452
|
+
.map((issue) => `${issue.path.join(".")}:${issue.code}`)
|
|
453
|
+
.join(","),
|
|
454
|
+
});
|
|
440
455
|
return;
|
|
456
|
+
}
|
|
441
457
|
const msg = (legacy?.success ? legacy.data : decoded);
|
|
442
458
|
// 控制面鉴权拒绝:server 端 resolveToken 未命中有效的 machine 凭证(失效/被吊销/
|
|
443
459
|
// 库已重置)。不能静默丢弃这帧——否则只表现为神秘的「每 1s 重连」循环。打印可执行
|
|
@@ -534,16 +550,28 @@ export function serve(config, opts = {}) {
|
|
|
534
550
|
wake_origin: msg.wake?.origin ?? null,
|
|
535
551
|
content_preview: (msg.wake?.content ?? "").replace(/\s+/g, " ").slice(0, 120),
|
|
536
552
|
});
|
|
537
|
-
if (running.has(key)) {
|
|
553
|
+
if (scheduled && running.has(key)) {
|
|
538
554
|
log(`↩︎ 跳过(该任务已在运行): ${key}`);
|
|
539
555
|
dslog("run.dedupe_skip", "跳过唤醒:该任务已在运行", { level: "WARN", ...runKeys });
|
|
540
556
|
return;
|
|
541
557
|
}
|
|
558
|
+
const queueWaitStart = Date.now();
|
|
559
|
+
let legacyQueueDone = null;
|
|
560
|
+
let finishLegacyQueue = () => { };
|
|
561
|
+
if (!scheduled) {
|
|
562
|
+
const previous = legacyTaskTails.get(key);
|
|
563
|
+
legacyQueueDone = new Promise((resolve) => { finishLegacyQueue = resolve; });
|
|
564
|
+
legacyTaskTails.set(key, legacyQueueDone);
|
|
565
|
+
if (previous) {
|
|
566
|
+
log(`⏳ 排队(该任务已在运行): ${key}`);
|
|
567
|
+
dslog("run.wake_queued", "唤醒已排队:该任务正在运行", { ...runKeys });
|
|
568
|
+
await previous;
|
|
569
|
+
}
|
|
570
|
+
}
|
|
542
571
|
running.add(key);
|
|
543
572
|
// 并行槽:同 agent 超过配置上限的任务在此排队(不丢),有空位再跑。
|
|
544
|
-
const slotWaitStart = Date.now();
|
|
545
573
|
await acquireSlot(msg.agentHandle);
|
|
546
|
-
const queueMs = Date.now() -
|
|
574
|
+
const queueMs = Date.now() - queueWaitStart;
|
|
547
575
|
const threadLabel = threadId ?? null;
|
|
548
576
|
const from = msg.wake?.senderHandle ?? "?";
|
|
549
577
|
const incoming = msg.wake?.content ?? "";
|
|
@@ -769,6 +797,10 @@ export function serve(config, opts = {}) {
|
|
|
769
797
|
finally {
|
|
770
798
|
running.delete(key);
|
|
771
799
|
releaseSlot(msg.agentHandle);
|
|
800
|
+
finishLegacyQueue();
|
|
801
|
+
if (legacyQueueDone && legacyTaskTails.get(key) === legacyQueueDone) {
|
|
802
|
+
legacyTaskTails.delete(key);
|
|
803
|
+
}
|
|
772
804
|
void flushSlog(); // 每轮收尾冲一次,保证 run.end 尽快可查
|
|
773
805
|
}
|
|
774
806
|
});
|
package/dist/session.js
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@nowcrew/daemon",
|
|
3
|
-
"version": "0.5.
|
|
3
|
+
"version": "0.5.19",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "crew daemon — 运行在用户机器:拉起/管理 agent 进程,注入 crew CLI,归一化 runtime 事件",
|
|
6
6
|
"license": "Apache-2.0",
|
|
@@ -21,7 +21,7 @@
|
|
|
21
21
|
"cross-spawn": "^7.0.6",
|
|
22
22
|
"ws": "^8",
|
|
23
23
|
"zod": "^3.23.0",
|
|
24
|
-
"@nowcrew/cli": "^0.4.
|
|
24
|
+
"@nowcrew/cli": "^0.4.11"
|
|
25
25
|
},
|
|
26
26
|
"devDependencies": {
|
|
27
27
|
"@types/cross-spawn": "^6.0.6",
|
|
@@ -32,7 +32,7 @@
|
|
|
32
32
|
"vitest": "^2.1.0"
|
|
33
33
|
},
|
|
34
34
|
"scripts": {
|
|
35
|
-
"daemon": "tsx src/main.ts",
|
|
35
|
+
"daemon": "pnpm --filter @nowcrew/cli build && tsx src/main.ts",
|
|
36
36
|
"build": "tsc -p tsconfig.json",
|
|
37
37
|
"test": "vitest run",
|
|
38
38
|
"typecheck": "tsc --noEmit"
|