@nowcrew/daemon 0.6.80-beta.1 → 0.6.80-beta.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/agent-ability/resolver.js +10 -7
- package/dist/computer-cli.js +3 -9
- package/dist/computer-service.js +14 -0
- package/dist/daemon-local-home.js +23 -0
- package/dist/daemon-update-eligibility.js +7 -4
- package/dist/execution-protocol.js +1 -0
- package/dist/execution-runner.js +9 -3
- package/dist/local-execution-prompt.js +3 -2
- package/dist/local-executor.js +49 -28
- package/dist/machine-info.js +3 -0
- package/dist/main.js +5 -9
- package/dist/managed-service-lifecycle.js +6 -2
- package/dist/project-skills/capability.js +109 -0
- package/dist/project-skills/controller-convergence.js +57 -0
- package/dist/project-skills/projection-set-switch.js +419 -0
- package/dist/project-skills/projection-state-domain.js +170 -0
- package/dist/project-skills/projection-state-store.js +842 -0
- package/dist/project-skills/projection-state-transaction.js +322 -0
- package/dist/project-skills/projection-state.js +3 -0
- package/dist/project-skills/runtime-root-bootstrap.js +47 -0
- package/dist/project-skills/runtime-root-domain.js +268 -0
- package/dist/project-skills/runtime-root-gc.js +293 -0
- package/dist/project-skills/runtime-root-lease-artifact.js +46 -0
- package/dist/project-skills/runtime-root-leases.js +487 -0
- package/dist/project-skills/runtime-root-source-identity.js +60 -0
- package/dist/project-skills/runtime-root-startup.js +49 -0
- package/dist/project-skills/runtime-root-state-artifact-domain.js +143 -0
- package/dist/project-skills/runtime-root-state-index.js +356 -0
- package/dist/project-skills/runtime-root-store.js +722 -0
- package/dist/project-skills/serve-capability.js +28 -0
- package/dist/project-skills/serve-startup.js +37 -0
- package/dist/provider-env.js +21 -3
- package/dist/runtimes/codex-app-server-runner.js +12 -4
- package/dist/runtimes/codex-deepseek-config.js +17 -7
- package/dist/runtimes/codex.js +3 -0
- package/dist/self-training-workspace.js +15 -0
- package/dist/serve.js +1 -1
- package/dist/supervised-runtime.js +1 -0
- package/dist/workspace.js +13 -10
- package/package.json +9 -10
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { WebSocket } from "ws";
|
|
2
|
+
import { daemonCapabilityBindings, } from "../machine-info.js";
|
|
3
|
+
import { prepareProjectSkillConnectionCapabilities, } from "./capability.js";
|
|
4
|
+
export function createProjectSkillConnectionCapabilityProvider(input) {
|
|
5
|
+
return async () => daemonCapabilityBindings(input.platform, await prepareProjectSkillConnectionCapabilities(input));
|
|
6
|
+
}
|
|
7
|
+
export function createProjectSkillMachineHelloPublisher(input) {
|
|
8
|
+
let latest = null;
|
|
9
|
+
let latestSocket = null;
|
|
10
|
+
const republish = () => {
|
|
11
|
+
if (latest === null || latestSocket?.readyState !== WebSocket.OPEN)
|
|
12
|
+
return;
|
|
13
|
+
const hello = input.effective(latest, input.status());
|
|
14
|
+
try {
|
|
15
|
+
latestSocket.send(JSON.stringify(hello));
|
|
16
|
+
input.onSent(hello);
|
|
17
|
+
}
|
|
18
|
+
catch { /* 非 OPEN,忽略 */ }
|
|
19
|
+
};
|
|
20
|
+
return Object.freeze({
|
|
21
|
+
publish: (hello, socket) => {
|
|
22
|
+
latest = hello;
|
|
23
|
+
latestSocket = socket;
|
|
24
|
+
republish();
|
|
25
|
+
},
|
|
26
|
+
republish,
|
|
27
|
+
});
|
|
28
|
+
}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { createExecutionJournal } from "../execution-journal.js";
|
|
2
|
+
import { reconcileExecutionJournal } from "../execution-recovery.js";
|
|
3
|
+
import { dslog, flushSlog } from "../slog.js";
|
|
4
|
+
export async function initializeProjectSkillProtectedExecutionJournal(input) {
|
|
5
|
+
return input.startup.initializeAfterLeaseProtection(input.config.agentsRoot, async (snapshot) => {
|
|
6
|
+
input.protectedExecutionIds.clear();
|
|
7
|
+
for (const executionId of snapshot)
|
|
8
|
+
input.protectedExecutionIds.add(executionId);
|
|
9
|
+
const journal = input.injectedJournal ?? createExecutionJournal(input.config.agentsRoot, {
|
|
10
|
+
protectedExecutionIds: () => input.protectedExecutionIds,
|
|
11
|
+
onOperationTiming: (event) => {
|
|
12
|
+
if (event.operation !== "mark_runtime_ready"
|
|
13
|
+
&& event.queueWaitMs < 250
|
|
14
|
+
&& event.operationMs < 250)
|
|
15
|
+
return;
|
|
16
|
+
dslog("execution.journal_operation", "execution journal 操作完成", {
|
|
17
|
+
level: event.outcome === "succeeded" ? "INFO" : "ERROR",
|
|
18
|
+
execution_id: event.executionId,
|
|
19
|
+
operation: event.operation,
|
|
20
|
+
outcome: event.outcome,
|
|
21
|
+
queue_wait_ms: event.queueWaitMs,
|
|
22
|
+
operation_ms: event.operationMs,
|
|
23
|
+
total_ms: event.queueWaitMs + event.operationMs,
|
|
24
|
+
});
|
|
25
|
+
},
|
|
26
|
+
});
|
|
27
|
+
await reconcileExecutionJournal(journal, {
|
|
28
|
+
agentsRoot: input.config.agentsRoot,
|
|
29
|
+
serverUrl: input.config.serverUrl,
|
|
30
|
+
...(input.profileName === undefined ? {} : { profileName: input.profileName }),
|
|
31
|
+
log: dslog,
|
|
32
|
+
flush: flushSlog,
|
|
33
|
+
writeStderr: (line) => process.stderr.write(line),
|
|
34
|
+
});
|
|
35
|
+
return journal;
|
|
36
|
+
});
|
|
37
|
+
}
|
package/dist/provider-env.js
CHANGED
|
@@ -11,6 +11,24 @@
|
|
|
11
11
|
import { join } from "node:path";
|
|
12
12
|
import { DEEPSEEK_CODEX_KEY_ENV, deepSeekCodexHome, } from "./runtimes/codex-deepseek-config.js";
|
|
13
13
|
import { DEEPSEEK_HARNESS_CONFIG_ENV } from "./runtimes/deepseek-harness.js";
|
|
14
|
+
const ENV_KEY_RE = /^[A-Za-z_][A-Za-z0-9_]*$/;
|
|
15
|
+
const RESERVED_ENV = new Set([
|
|
16
|
+
"PATH",
|
|
17
|
+
"XDG_CONFIG_HOME",
|
|
18
|
+
"XDG_DATA_HOME",
|
|
19
|
+
"XDG_CACHE_HOME",
|
|
20
|
+
"GH_CONFIG_DIR",
|
|
21
|
+
"CLOUDSDK_CONFIG",
|
|
22
|
+
]);
|
|
23
|
+
/** Keep user-provided variables away from daemon-owned runtime and credential fields. */
|
|
24
|
+
export function sanitizeEnvVars(raw) {
|
|
25
|
+
if (!raw)
|
|
26
|
+
return {};
|
|
27
|
+
return Object.fromEntries(Object.entries(raw).filter(([key, value]) => typeof value === "string"
|
|
28
|
+
&& ENV_KEY_RE.test(key)
|
|
29
|
+
&& !key.toUpperCase().startsWith("CREW_")
|
|
30
|
+
&& !RESERVED_ENV.has(key.toUpperCase())));
|
|
31
|
+
}
|
|
14
32
|
/** custom 模式下必须从继承 env 中剔除的全局路由/凭证变量(避免与注入值叠加或抢优先级)。 */
|
|
15
33
|
const CONFLICTING_ENV = [
|
|
16
34
|
"ANTHROPIC_BASE_URL",
|
|
@@ -39,12 +57,12 @@ const CONFLICTING_CODEX_ENV = [
|
|
|
39
57
|
DEEPSEEK_CODEX_KEY_ENV,
|
|
40
58
|
];
|
|
41
59
|
/** 返回叠加了 provider 隔离的新 env(不改入参);不支持的 runtime/provider 组合原样返回。 */
|
|
42
|
-
export function applyProviderEnv(base, runtime, cfg, homeDir) {
|
|
60
|
+
export function applyProviderEnv(base, runtime, cfg, homeDir, deepSeekHomeOverride) {
|
|
43
61
|
if (runtime === "codex" && cfg.provider === "deepseek") {
|
|
44
62
|
const env = { ...base };
|
|
45
63
|
for (const key of CONFLICTING_CODEX_ENV)
|
|
46
64
|
delete env[key];
|
|
47
|
-
env.CODEX_HOME = deepSeekCodexHome(homeDir);
|
|
65
|
+
env.CODEX_HOME = deepSeekHomeOverride ?? deepSeekCodexHome(homeDir);
|
|
48
66
|
if (cfg.providerApiKey)
|
|
49
67
|
env[DEEPSEEK_CODEX_KEY_ENV] = cfg.providerApiKey;
|
|
50
68
|
return env;
|
|
@@ -100,7 +118,7 @@ export function applyDeepSeekHarnessMachineEnv(base, machineEnv) {
|
|
|
100
118
|
*/
|
|
101
119
|
export function providerFingerprint(runtime, cfg) {
|
|
102
120
|
if (runtime === "codex" && cfg.provider === "deepseek") {
|
|
103
|
-
return `deepseek|${cfg.providerApiKey ? "key" : "nokey"}`;
|
|
121
|
+
return `deepseek|${cfg.providerBaseUrl ?? "default"}|${cfg.providerApiKey ? "key" : "nokey"}`;
|
|
104
122
|
}
|
|
105
123
|
if (runtime !== "claude" || cfg.provider !== "custom")
|
|
106
124
|
return null;
|
|
@@ -21,9 +21,14 @@ const RunnerInputSchema = z.object({
|
|
|
21
21
|
cwd: z.string().min(1).optional(),
|
|
22
22
|
workspaceRoots: z.array(z.string().min(1)).optional(),
|
|
23
23
|
skillRoots: z.array(z.string().min(1)).optional(),
|
|
24
|
+
networkAccess: z.boolean().optional(),
|
|
24
25
|
codexCollaborationTools: z.boolean().optional(),
|
|
25
26
|
resume: z.boolean(),
|
|
26
|
-
}).strict()
|
|
27
|
+
}).strict().superRefine((input, ctx) => {
|
|
28
|
+
if (input.networkAccess === true && input.effectivePermission !== "workspace_write") {
|
|
29
|
+
ctx.addIssue({ code: "custom", message: "network_access_requires_workspace_write" });
|
|
30
|
+
}
|
|
31
|
+
});
|
|
27
32
|
const RPC_TIMEOUT_MS = 30_000;
|
|
28
33
|
const INITIALIZE_RPC_TIMEOUT_MS = 60_000;
|
|
29
34
|
const ERROR_MESSAGE_CAP = 2_000;
|
|
@@ -565,18 +570,18 @@ async function runCodexAppServerAttempt(bin, input, attempt, initializeTimeoutMs
|
|
|
565
570
|
...(input.model === undefined ? {} : { model: input.model }),
|
|
566
571
|
};
|
|
567
572
|
let thread;
|
|
568
|
-
const
|
|
573
|
+
const workspaceContractRequired = input.workspaceRoots !== undefined;
|
|
569
574
|
try {
|
|
570
575
|
thread = input.resume && input.sessionId !== undefined
|
|
571
576
|
? await rpc.request("thread/resume", { threadId: input.sessionId, ...threadParams })
|
|
572
577
|
: await rpc.request("thread/start", threadParams);
|
|
573
578
|
}
|
|
574
579
|
catch (error) {
|
|
575
|
-
if (
|
|
580
|
+
if (workspaceContractRequired)
|
|
576
581
|
throw codexProjectWorkspaceUnsupported();
|
|
577
582
|
throw error;
|
|
578
583
|
}
|
|
579
|
-
if (
|
|
584
|
+
if (workspaceContractRequired) {
|
|
580
585
|
thread = assertCodexProjectWorkspaceResponse(thread, {
|
|
581
586
|
cwd: threadCwd,
|
|
582
587
|
workspaceRoots: input.workspaceRoots ?? [],
|
|
@@ -613,6 +618,9 @@ async function runCodexAppServerAttempt(bin, input, attempt, initializeTimeoutMs
|
|
|
613
618
|
...(input.reasoning === undefined || input.reasoning === "default"
|
|
614
619
|
? {}
|
|
615
620
|
: { effort: input.reasoning }),
|
|
621
|
+
...(input.networkAccess === true ? {
|
|
622
|
+
sandboxPolicy: { type: "workspaceWrite", networkAccess: true },
|
|
623
|
+
} : {}),
|
|
616
624
|
});
|
|
617
625
|
logStage(activeStage, "ok", attempt, stageStartedAt);
|
|
618
626
|
turnId = started.turn.id;
|
|
@@ -7,10 +7,20 @@ export const DEEPSEEK_CODEX_BASE_URL = "https://api.deepseek.com/";
|
|
|
7
7
|
export const DEEPSEEK_CODEX_KEY_ENV = "NOWCREW_DEEPSEEK_API_KEY";
|
|
8
8
|
export const DEEPSEEK_CODEX_REASONING_LEVELS = ["low", "high", "max"];
|
|
9
9
|
const PRIVATE_CODEX_HOME = ".codex-deepseek";
|
|
10
|
-
|
|
11
|
-
|
|
10
|
+
const SELF_TRAINING_PRIVATE_CODEX_HOME = ".codex-deepseek-self-training";
|
|
11
|
+
export function deepSeekCodexHome(homeDir, selfTraining = false) {
|
|
12
|
+
return resolve(homeDir, selfTraining ? SELF_TRAINING_PRIVATE_CODEX_HOME : PRIVATE_CODEX_HOME);
|
|
12
13
|
}
|
|
13
|
-
|
|
14
|
+
function normalizedBaseUrl(value) {
|
|
15
|
+
const url = new URL(value);
|
|
16
|
+
if (url.protocol !== "http:" && url.protocol !== "https:") {
|
|
17
|
+
throw new Error("DeepSeek provider base URL must use HTTP(S)");
|
|
18
|
+
}
|
|
19
|
+
if (!url.pathname.endsWith("/"))
|
|
20
|
+
url.pathname = `${url.pathname}/`;
|
|
21
|
+
return url.toString();
|
|
22
|
+
}
|
|
23
|
+
export function renderDeepSeekConfig(codexHome, baseUrl = DEEPSEEK_CODEX_BASE_URL) {
|
|
14
24
|
const catalogPath = join(codexHome, "models.json");
|
|
15
25
|
return [
|
|
16
26
|
`model = ${JSON.stringify(DEEPSEEK_CODEX_MODEL)}`,
|
|
@@ -20,7 +30,7 @@ export function renderDeepSeekConfig(codexHome) {
|
|
|
20
30
|
"",
|
|
21
31
|
"[model_providers.deepseek]",
|
|
22
32
|
'name = "deepseek"',
|
|
23
|
-
`base_url = ${JSON.stringify(
|
|
33
|
+
`base_url = ${JSON.stringify(normalizedBaseUrl(baseUrl))}`,
|
|
24
34
|
'wire_api = "responses"',
|
|
25
35
|
`env_key = ${JSON.stringify(DEEPSEEK_CODEX_KEY_ENV)}`,
|
|
26
36
|
"",
|
|
@@ -40,11 +50,11 @@ async function writePrivateAtomic(path, content) {
|
|
|
40
50
|
await rm(temporary, { force: true });
|
|
41
51
|
}
|
|
42
52
|
}
|
|
43
|
-
export async function materializeDeepSeekCodexHome(homeDir) {
|
|
44
|
-
const codexHome = deepSeekCodexHome(homeDir);
|
|
53
|
+
export async function materializeDeepSeekCodexHome(homeDir, options = {}) {
|
|
54
|
+
const codexHome = deepSeekCodexHome(homeDir, options.selfTraining === true);
|
|
45
55
|
await mkdir(codexHome, { recursive: true, mode: 0o700 });
|
|
46
56
|
await chmod(codexHome, 0o700);
|
|
47
57
|
await writePrivateAtomic(join(codexHome, "models.json"), DEEPSEEK_CODEX_CATALOG);
|
|
48
|
-
await writePrivateAtomic(join(codexHome, "config.toml"), renderDeepSeekConfig(codexHome));
|
|
58
|
+
await writePrivateAtomic(join(codexHome, "config.toml"), renderDeepSeekConfig(codexHome, options.baseUrl));
|
|
49
59
|
return codexHome;
|
|
50
60
|
}
|
package/dist/runtimes/codex.js
CHANGED
|
@@ -36,6 +36,9 @@ export function buildCodexArgs(input) {
|
|
|
36
36
|
else if (input.effectivePermission === "full_access" || (input.effectivePermission === undefined && input.dangerous)) {
|
|
37
37
|
args.push("--dangerously-bypass-approvals-and-sandbox");
|
|
38
38
|
}
|
|
39
|
+
if (input.effectivePermission === "workspace_write" && input.networkAccess === true) {
|
|
40
|
+
args.push("-c", "sandbox_workspace_write.network_access=true");
|
|
41
|
+
}
|
|
39
42
|
for (const imagePath of input.imagePaths ?? [])
|
|
40
43
|
args.push("--image", imagePath);
|
|
41
44
|
// `-` instructs codex exec to read the prompt from stdin. Keeping the complete prompt out of argv
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { rm } from "node:fs/promises";
|
|
2
|
+
import { isAbsolute, relative, sep } from "node:path";
|
|
3
|
+
/** Delete only a concrete child of this Agent's tasks root; never follow an ambiguous target. */
|
|
4
|
+
export async function cleanupSelfTrainingRunDir(tasksRoot, runDir) {
|
|
5
|
+
const child = relative(tasksRoot, runDir);
|
|
6
|
+
if (child === "" || isAbsolute(child) || child === ".." || child.startsWith(`..${sep}`))
|
|
7
|
+
return;
|
|
8
|
+
try {
|
|
9
|
+
await rm(runDir, { recursive: true, force: true });
|
|
10
|
+
}
|
|
11
|
+
catch (error) {
|
|
12
|
+
const detail = error instanceof Error ? error.message : String(error);
|
|
13
|
+
process.stderr.write(`[execution] failed to remove self-training run dir ${runDir}: ${detail}\n`);
|
|
14
|
+
}
|
|
15
|
+
}
|
package/dist/serve.js
CHANGED
|
@@ -106,7 +106,7 @@ export function serve(config, opts = {}) {
|
|
|
106
106
|
const cancellations = new Map();
|
|
107
107
|
const legacyRuns = new Map();
|
|
108
108
|
const updateEligibility = opts.update?.eligibility
|
|
109
|
-
?? (() => detectDaemonUpdateEligibility(opts.profileName));
|
|
109
|
+
?? (() => detectDaemonUpdateEligibility(opts.profileName, { localHome }));
|
|
110
110
|
const updateController = createDaemonUpdateController({
|
|
111
111
|
eligibility: updateEligibility,
|
|
112
112
|
install: opts.update?.install ?? ((input) => installExactDaemonUpdate({
|
|
@@ -46,6 +46,7 @@ export function supervisorLaunch(request) {
|
|
|
46
46
|
effectivePermission: request.effectivePermission,
|
|
47
47
|
...(request.model === undefined ? {} : { model: request.model }),
|
|
48
48
|
...(request.reasoning === undefined ? {} : { reasoning: request.reasoning }),
|
|
49
|
+
...(request.networkAccess === undefined ? {} : { networkAccess: request.networkAccess }),
|
|
49
50
|
...(request.sessionId === undefined ? {} : { sessionId: request.sessionId }),
|
|
50
51
|
...(request.imagePaths === undefined ? {} : { imagePaths: request.imagePaths }),
|
|
51
52
|
...(request.agentRoot === undefined ? {} : {
|
package/dist/workspace.js
CHANGED
|
@@ -59,12 +59,13 @@ export async function prepareWorkspace(input) {
|
|
|
59
59
|
const crewDir = join(dir, ".crew");
|
|
60
60
|
await mkdir(crewDir, { recursive: true });
|
|
61
61
|
// 预先创建明细目录,让收尾流程可以按功能创建 notes/<topic>.md。
|
|
62
|
-
|
|
62
|
+
if (!input.skipLocalMemory)
|
|
63
|
+
await mkdir(join(dir, "notes"), { recursive: true });
|
|
63
64
|
// MEMORY.md:首次创建"持久身份 + 知识索引"骨架,之后由 agent 自己维护
|
|
64
65
|
const memoryPath = join(dir, "MEMORY.md");
|
|
65
|
-
const memorySeedCreated = !(await exists(memoryPath));
|
|
66
|
+
const memorySeedCreated = !input.skipLocalMemory && !(await exists(memoryPath));
|
|
66
67
|
const localMemoryV2Initialized = await hasLocalMemoryV2Marker(dir);
|
|
67
|
-
if (memorySeedCreated) {
|
|
68
|
+
if (!input.skipLocalMemory && memorySeedCreated) {
|
|
68
69
|
try {
|
|
69
70
|
await writeFile(memoryPath, memorySeed(input.handle, input.description), "utf8");
|
|
70
71
|
}
|
|
@@ -73,13 +74,15 @@ export async function prepareWorkspace(input) {
|
|
|
73
74
|
throw error;
|
|
74
75
|
}
|
|
75
76
|
}
|
|
76
|
-
let memory;
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
77
|
+
let memory = "";
|
|
78
|
+
if (!input.skipLocalMemory) {
|
|
79
|
+
try {
|
|
80
|
+
memory = await readFile(memoryPath, "utf8");
|
|
81
|
+
}
|
|
82
|
+
catch (error) {
|
|
83
|
+
logMemoryPrepareFailure(input, "memory_read", error);
|
|
84
|
+
throw error;
|
|
85
|
+
}
|
|
83
86
|
}
|
|
84
87
|
// 系统提示词按 execution 隔离;省略 identity 的兼容调用每次使用随机路径。
|
|
85
88
|
const promptDir = join(crewDir, "prompts");
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@nowcrew/daemon",
|
|
3
|
-
"version": "0.6.80-beta.
|
|
3
|
+
"version": "0.6.80-beta.3",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "crew daemon — 运行在用户机器:拉起/管理 agent 进程,注入 crew CLI,归一化 runtime 事件",
|
|
6
6
|
"license": "Apache-2.0",
|
|
@@ -16,14 +16,6 @@
|
|
|
16
16
|
"publishConfig": {
|
|
17
17
|
"access": "public"
|
|
18
18
|
},
|
|
19
|
-
"scripts": {
|
|
20
|
-
"daemon": "pnpm --filter @nowcrew/cli build && tsx src/main.ts",
|
|
21
|
-
"build": "tsc -p tsconfig.json",
|
|
22
|
-
"codex-home:migrate": "tsx src/runtimes/codex-home-migration-cli.ts",
|
|
23
|
-
"prepublishOnly": "pnpm build && node ../scripts/daemon-release-artifact.mjs --strict-registry",
|
|
24
|
-
"test": "vitest run",
|
|
25
|
-
"typecheck": "tsc --noEmit"
|
|
26
|
-
},
|
|
27
19
|
"dependencies": {
|
|
28
20
|
"@agentclientprotocol/sdk": "1.2.1",
|
|
29
21
|
"@nowcrew/cli": "0.4.13",
|
|
@@ -44,5 +36,12 @@
|
|
|
44
36
|
"tsx": "^4.19.0",
|
|
45
37
|
"typescript": "^5.6.0",
|
|
46
38
|
"vitest": "^2.1.0"
|
|
39
|
+
},
|
|
40
|
+
"scripts": {
|
|
41
|
+
"daemon": "pnpm --filter @nowcrew/cli build && tsx src/main.ts",
|
|
42
|
+
"build": "tsc -p tsconfig.json",
|
|
43
|
+
"codex-home:migrate": "tsx src/runtimes/codex-home-migration-cli.ts",
|
|
44
|
+
"test": "vitest run",
|
|
45
|
+
"typecheck": "tsc --noEmit"
|
|
47
46
|
}
|
|
48
|
-
}
|
|
47
|
+
}
|