@nowcrew/daemon 0.6.88-beta.15 → 0.6.88-beta.17
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 +15 -0
- package/dist/daemon-update-eligibility.js +1 -1
- package/dist/local-executor.js +4 -2
- package/dist/machine-info.js +48 -15
- package/dist/main.js +4 -69
- package/dist/project-skills/execution-adapter.js +3 -2
- package/dist/project-skills/runtime-launch.js +14 -0
- package/dist/provider-env.js +3 -4
- package/dist/runtime-probe.js +34 -5
- package/dist/runtimes/deepseek-harness-profile.js +92 -0
- package/dist/runtimes/deepseek-harness-runner.js +224 -0
- package/dist/runtimes/deepseek-harness.js +61 -10
- package/dist/runtimes/kimi-acp-runner.js +37 -6
- package/dist/serve.js +36 -68
- package/dist/storage-identity.js +0 -3
- package/dist/storage-serve-adapter.js +6 -72
- package/dist/supervised-runtime.js +17 -2
- package/package.json +1 -1
- package/dist/daemon-migration-controller.js +0 -211
- package/dist/daemon-migration-wiring.js +0 -22
- package/dist/storage-control.js +0 -212
- package/dist/storage-copy.js +0 -628
- package/dist/storage-credential-guard.js +0 -21
- package/dist/storage-doctor.js +0 -228
- package/dist/storage-manifest.js +0 -1027
- package/dist/storage-migration-controller.js +0 -440
- package/dist/storage-migration-manifest.js +0 -105
- package/dist/storage-migration-policy.js +0 -100
- package/dist/storage-runtime-controller.js +0 -381
- package/dist/storage-runtime-readiness.js +0 -240
- package/dist/storage-safe-fs.js +0 -302
- package/dist/storage-status-delivery.js +0 -224
- package/dist/storage-status-replay.js +0 -140
- package/dist/storage-target-confirmation.js +0 -129
- package/dist/storage-worker-command.js +0 -37
- package/dist/storage-worker-executor.js +0 -449
- package/dist/storage-worker-handoff.js +0 -151
- package/dist/storage-worker-operation.js +0 -294
- package/dist/storage-worker-status.js +0 -259
package/README.md
CHANGED
|
@@ -3,6 +3,21 @@
|
|
|
3
3
|
The daemon is NowWork's local execution adapter. It holds a machine credential, keeps one control-plane
|
|
4
4
|
WebSocket open, and launches coding runtimes on that machine. Product policy remains on the server.
|
|
5
5
|
|
|
6
|
+
## 已完成搬迁后的存储运行
|
|
7
|
+
|
|
8
|
+
NAS / `NOWWORK_HOME` 搬迁和旧 daemon 布局标准化入口已退役。daemon 不再提供内部
|
|
9
|
+
`storage-migration-worker` / `storage-migration-recover` 命令,不处理 `daemon:migrate`、
|
|
10
|
+
`daemon:storage-doctor`、`daemon:storage-migrate` 或迁移确认消息,也不再声明
|
|
11
|
+
`storage_migration_v1` / `storage_migration_status_v1` / `daemon_layout_migration_v1` 能力。
|
|
12
|
+
|
|
13
|
+
现有 `installations.json` 即使仍有历史 `storageMigrationId`,也按当前 installation 解析
|
|
14
|
+
Agent、仓库和报告目录;启动、hello 和身份绑定不再读取迁移 receipt、outbox 或备份。
|
|
15
|
+
hello 继续报告布局、环境、Workspace、路径指纹与 ownership,但不再发送迁移 ID。
|
|
16
|
+
每次连接仍须完成 Server 身份绑定后才能执行任务;Workspace 或 Machine 身份不匹配时拒绝执行。
|
|
17
|
+
|
|
18
|
+
旧布局读取兼容、日常安装/升级、NAS 所有权校验、执行 journal 与 ACK/replay 继续保留。
|
|
19
|
+
退役代码不会删除历史安装字段、迁移记录、receipt、备份或旧数据目录;这些数据的保留与清理另行处理。
|
|
20
|
+
|
|
6
21
|
## Run
|
|
7
22
|
|
|
8
23
|
For a temporary foreground connection, run:
|
|
@@ -14,7 +14,7 @@ import { assertRegistryCoversDescriptors, listManagedServiceDescriptorPaths, man
|
|
|
14
14
|
export async function managedDaemonCapabilities(eligibility) {
|
|
15
15
|
try {
|
|
16
16
|
return (await eligibility()).eligible
|
|
17
|
-
? ["daemon_update_v1", "daemon_restart_v1", "daemon_update_registry_v1"
|
|
17
|
+
? ["daemon_update_v1", "daemon_restart_v1", "daemon_update_registry_v1"]
|
|
18
18
|
: [];
|
|
19
19
|
}
|
|
20
20
|
catch {
|
package/dist/local-executor.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { deepSeekHarnessLauncher } from "./runtimes/deepseek-harness.js";
|
|
1
2
|
import { createInterface } from "node:readline";
|
|
2
3
|
import { readFile, rm, writeFile } from "node:fs/promises";
|
|
3
4
|
import { delimiter, join } from "node:path";
|
|
@@ -569,7 +570,7 @@ async function executeLocalUnlocked(input, callbacks, dependencies) {
|
|
|
569
570
|
});
|
|
570
571
|
}
|
|
571
572
|
const trainingRoots = input.trainingProjectContext?.projects.map((project) => project.root) ?? [];
|
|
572
|
-
const { codexSkillRoots, claudeAdditionalDirectories } = await projectSkillRuntimeDirectories({
|
|
573
|
+
const { codexSkillRoots, claudeAdditionalDirectories, harnessSkillRoots } = await projectSkillRuntimeDirectories({
|
|
573
574
|
runtime: runtime.name,
|
|
574
575
|
...(runtimeRoot === undefined ? {} : { runtimeRoot }),
|
|
575
576
|
agentRoot: workspace.dir,
|
|
@@ -607,7 +608,8 @@ async function executeLocalUnlocked(input, callbacks, dependencies) {
|
|
|
607
608
|
const workspaceRoots = [...legacyRoots, ...trainingRoots];
|
|
608
609
|
const launchRequest = {
|
|
609
610
|
runtime: runtime.name,
|
|
610
|
-
bin: runtime.name === "deepseek-harness" ?
|
|
611
|
+
bin: runtime.name === "deepseek-harness" ? deepSeekHarnessLauncher(runtimeEnv).bin : runtime.name,
|
|
612
|
+
...(runtime.name === "deepseek-harness" ? { harnessStateRoot: executionWorkspace.runDir, harnessSkillRoots: harnessSkillRoots ?? [] } : {}),
|
|
611
613
|
cwd: input.projectContext?.primary?.root ?? executionWorkspace.runDir,
|
|
612
614
|
...(runtimeRoot !== undefined
|
|
613
615
|
|| (input.projectSkills === undefined && input.abilityRelease === undefined)
|
package/dist/machine-info.js
CHANGED
|
@@ -2,6 +2,8 @@
|
|
|
2
2
|
* 采集本机信息上报给控制面 (machine:hello):hostname / os / daemon 版本 / 已装 runtimes。
|
|
3
3
|
* runtimes 探测靠 `which <bin>`(win32 用 `where`),只报真实可执行的 CLI(用于展示 Detected Runtimes)。
|
|
4
4
|
*/
|
|
5
|
+
import { randomUUID } from "node:crypto";
|
|
6
|
+
import { dslog } from "./slog.js";
|
|
5
7
|
import { hostname, arch, platform as osPlatform } from "node:os";
|
|
6
8
|
import { lookupCmd } from "./platform.js";
|
|
7
9
|
import { augmentedPath } from "./runtime-path.js";
|
|
@@ -18,7 +20,7 @@ import { probeKimiAcp } from "./runtimes/kimi-acp-runner.js";
|
|
|
18
20
|
import { probeHermesAcp } from "./runtimes/hermes.js";
|
|
19
21
|
import { probeOpenCodeRun } from "./runtimes/opencode.js";
|
|
20
22
|
import { RUNTIME_HEALTH_PROBE_CAPABILITY } from "./runtime-health.js";
|
|
21
|
-
import { probeDeepSeekHarnessAcp } from "./runtimes/deepseek-harness.js";
|
|
23
|
+
import { probeDeepSeekHarnessAcp, deepSeekHarnessLauncher } from "./runtimes/deepseek-harness.js";
|
|
22
24
|
import { daemonHome } from "./computer-profile.js";
|
|
23
25
|
import { daemonReleaseProvenance } from "./release-provenance.js";
|
|
24
26
|
export { executableRuntimes } from "./runtime-capabilities.js";
|
|
@@ -90,7 +92,7 @@ const RUNTIME_BINS = [
|
|
|
90
92
|
["gemini", "gemini"],
|
|
91
93
|
["hermes", "hermes"],
|
|
92
94
|
["opencode", "opencode"],
|
|
93
|
-
["deepseek-harness", "dsh
|
|
95
|
+
["deepseek-harness", "dsh"],
|
|
94
96
|
["copilot", "copilot"],
|
|
95
97
|
["kimi", "kimi"],
|
|
96
98
|
["pi", "pi"],
|
|
@@ -107,29 +109,60 @@ async function isInstalled(bin) {
|
|
|
107
109
|
}
|
|
108
110
|
/** 并发探测所有候选 runtime,返回已安装的展示名列表。 */
|
|
109
111
|
export async function detectRuntimes() {
|
|
110
|
-
const checks = await Promise.all(RUNTIME_BINS.map(async ([name, bin]) =>
|
|
112
|
+
const checks = await Promise.all(RUNTIME_BINS.map(async ([name, bin]) => {
|
|
113
|
+
try {
|
|
114
|
+
return await isInstalled(name === "deepseek-harness" ? deepSeekHarnessLauncher().bin : bin) ? name : null;
|
|
115
|
+
}
|
|
116
|
+
catch {
|
|
117
|
+
return null;
|
|
118
|
+
}
|
|
119
|
+
}));
|
|
111
120
|
return checks.filter((x) => x !== null);
|
|
112
121
|
}
|
|
113
|
-
async function supportsKimiAcp(signal) {
|
|
114
|
-
return probeKimiAcp({ bin: "kimi", ...(signal ? { signal } : {}) });
|
|
122
|
+
async function supportsKimiAcp(signal, probeId) {
|
|
123
|
+
return probeKimiAcp({ bin: "kimi", ...(probeId ? { probeId } : {}), ...(signal ? { signal } : {}) });
|
|
115
124
|
}
|
|
116
|
-
async function supportsHermesAcp(signal) {
|
|
117
|
-
return probeHermesAcp({ bin: "hermes", ...(signal ? { signal } : {}) });
|
|
125
|
+
async function supportsHermesAcp(signal, probeId) {
|
|
126
|
+
return probeHermesAcp({ bin: "hermes", ...(probeId ? { probeId } : {}), ...(signal ? { signal } : {}) });
|
|
118
127
|
}
|
|
119
128
|
async function supportsOpenCodeRun(signal) {
|
|
120
129
|
return probeOpenCodeRun(signal);
|
|
121
130
|
}
|
|
122
|
-
async function supportsDeepSeekHarnessAcp(signal) {
|
|
123
|
-
|
|
131
|
+
async function supportsDeepSeekHarnessAcp(signal, probeId) {
|
|
132
|
+
try {
|
|
133
|
+
return await probeDeepSeekHarnessAcp({ bin: deepSeekHarnessLauncher().bin, ...(probeId ? { probeId } : {}), ...(signal ? { signal } : {}) });
|
|
134
|
+
}
|
|
135
|
+
catch {
|
|
136
|
+
return false;
|
|
137
|
+
}
|
|
124
138
|
}
|
|
125
139
|
/** Runtime adapters that can satisfy the durable protocol-v1 process contract. */
|
|
126
|
-
export async function detectExecutionRuntimes(installed = detectRuntimes(), kimiAcpProbe = supportsKimiAcp, hermesAcpProbe = supportsHermesAcp, openCodeProbe = supportsOpenCodeRun, deepSeekHarnessProbe = supportsDeepSeekHarnessAcp, signal) {
|
|
140
|
+
export async function detectExecutionRuntimes(installed = detectRuntimes(), kimiAcpProbe = supportsKimiAcp, hermesAcpProbe = supportsHermesAcp, openCodeProbe = supportsOpenCodeRun, deepSeekHarnessProbe = supportsDeepSeekHarnessAcp, signal, probeId = randomUUID()) {
|
|
127
141
|
const present = await installed;
|
|
142
|
+
const probe = (runtime, detect) => {
|
|
143
|
+
const startedAt = Date.now();
|
|
144
|
+
dslog("runtime_probe.optional_start", "开始可选 runtime 探测", { probe_id: probeId, runtime, aborted: signal?.aborted ?? false });
|
|
145
|
+
const complete = (outcome, ready) => {
|
|
146
|
+
dslog("runtime_probe.optional_complete", "可选 runtime 探测结束", {
|
|
147
|
+
probe_id: probeId, runtime, outcome, duration_ms: Date.now() - startedAt,
|
|
148
|
+
aborted: signal?.aborted ?? false, ...(ready === undefined ? {} : { ready }),
|
|
149
|
+
});
|
|
150
|
+
};
|
|
151
|
+
let pending;
|
|
152
|
+
try {
|
|
153
|
+
pending = detect(signal, probeId);
|
|
154
|
+
}
|
|
155
|
+
catch (error) {
|
|
156
|
+
complete("threw");
|
|
157
|
+
throw error;
|
|
158
|
+
}
|
|
159
|
+
return pending.then((ready) => { complete("resolved", ready); return ready; }, (error) => { complete("threw"); throw error; });
|
|
160
|
+
};
|
|
128
161
|
const [kimiReady, hermesReady, openCodeReady, deepSeekHarnessReady] = await Promise.all([
|
|
129
|
-
present.includes("kimi") ? kimiAcpProbe
|
|
130
|
-
present.includes("hermes") ? hermesAcpProbe
|
|
131
|
-
present.includes("opencode") ? openCodeProbe
|
|
132
|
-
present.includes("deepseek-harness") ? deepSeekHarnessProbe
|
|
162
|
+
present.includes("kimi") ? probe("kimi", kimiAcpProbe) : false,
|
|
163
|
+
present.includes("hermes") ? probe("hermes", hermesAcpProbe) : false,
|
|
164
|
+
present.includes("opencode") ? probe("opencode", openCodeProbe) : false,
|
|
165
|
+
present.includes("deepseek-harness") ? probe("deepseek-harness", deepSeekHarnessProbe) : false,
|
|
133
166
|
]);
|
|
134
167
|
return executableRuntimes(present).filter((runtime) => (runtime === "kimi" ? kimiReady
|
|
135
168
|
: runtime === "hermes" ? hermesReady
|
|
@@ -137,7 +170,7 @@ export async function detectExecutionRuntimes(installed = detectRuntimes(), kimi
|
|
|
137
170
|
: runtime === "deepseek-harness" ? deepSeekHarnessReady
|
|
138
171
|
: true));
|
|
139
172
|
}
|
|
140
|
-
export const detectExecutionRuntimesWithSignal = (installed, signal) => detectExecutionRuntimes(installed, supportsKimiAcp, supportsHermesAcp, supportsOpenCodeRun, supportsDeepSeekHarnessAcp, signal);
|
|
173
|
+
export const detectExecutionRuntimesWithSignal = (installed, signal, probeId) => detectExecutionRuntimes(installed, supportsKimiAcp, supportsHermesAcp, supportsOpenCodeRun, supportsDeepSeekHarnessAcp, signal, probeId);
|
|
141
174
|
export function daemonVersion() {
|
|
142
175
|
try {
|
|
143
176
|
const here = dirname(fileURLToPath(import.meta.url));
|
package/dist/main.js
CHANGED
|
@@ -7,7 +7,6 @@
|
|
|
7
7
|
*/
|
|
8
8
|
import { parseArgs } from "node:util";
|
|
9
9
|
import { homedir } from "node:os";
|
|
10
|
-
import { join } from "node:path";
|
|
11
10
|
import { loadConfig, ConfigError } from "./config.js";
|
|
12
11
|
import { detectDaemonLang, formatDaemonText, translateDaemon } from "./i18n.js";
|
|
13
12
|
import { cliVersion, daemonVersion } from "./machine-info.js";
|
|
@@ -25,19 +24,9 @@ import { assertManagedServiceStartup } from "./managed-service-startup.js";
|
|
|
25
24
|
import { daemonGlobalInstallation } from "./daemon-installation.js";
|
|
26
25
|
import { acquireDaemonInstallationLease, runWithDaemonInstallationLease, } from "./daemon-installation-lease.js";
|
|
27
26
|
import { createStorageRuntimeOwnershipBinding, loadStorageRuntime } from "./storage-runtime.js";
|
|
28
|
-
import { readStorageMigrationReceipt } from "./storage-migration-controller.js";
|
|
29
|
-
import { storageMigrationStatusReplay } from "./storage-status-replay.js";
|
|
30
|
-
import { runStorageWorkerCommand } from "./storage-worker-command.js";
|
|
31
|
-
import { probeStorageMigrationRuntime } from "./storage-runtime-readiness.js";
|
|
32
|
-
import { createStorageTargetConfirmationHandler } from "./storage-target-confirmation.js";
|
|
33
27
|
import { resolveDaemonLocalStateRoot } from "./daemon-local-home.js";
|
|
34
28
|
import { storageServiceEnvironment } from "./computer-service.js";
|
|
35
29
|
async function main() {
|
|
36
|
-
const workerResult = await runStorageWorkerCommand(process.argv.slice(2));
|
|
37
|
-
if (workerResult !== null) {
|
|
38
|
-
process.exitCode = workerResult;
|
|
39
|
-
return;
|
|
40
|
-
}
|
|
41
30
|
const computerResult = await runComputerCommand(process.argv.slice(2));
|
|
42
31
|
if (computerResult !== null) {
|
|
43
32
|
process.exitCode = computerResult;
|
|
@@ -74,7 +63,6 @@ async function main() {
|
|
|
74
63
|
// 不把 machine token 放进 argv / plist / systemd unit / scheduled task。
|
|
75
64
|
if (values["daemon-home"])
|
|
76
65
|
process.env.CREW_DAEMON_HOME = values["daemon-home"];
|
|
77
|
-
let activeDaemonHome;
|
|
78
66
|
const localHome = resolveDaemonLocalStateRoot({
|
|
79
67
|
userHome: homedir(),
|
|
80
68
|
platform: process.platform,
|
|
@@ -85,7 +73,6 @@ async function main() {
|
|
|
85
73
|
});
|
|
86
74
|
if (values.profile) {
|
|
87
75
|
const home = daemonHome(process.env, values.profile);
|
|
88
|
-
activeDaemonHome = home;
|
|
89
76
|
const profile = await loadProfile(values.profile, home);
|
|
90
77
|
if (cmd === "serve") {
|
|
91
78
|
await assertProfileAgentsRootUnique(profile, home, homedir());
|
|
@@ -148,68 +135,16 @@ async function main() {
|
|
|
148
135
|
const installationLease = installation === null
|
|
149
136
|
? null
|
|
150
137
|
: await acquireDaemonInstallationLease(installation.npmPrefix, { localHome });
|
|
151
|
-
const
|
|
152
|
-
? { ready: false, reason: "not_legacy_source" }
|
|
153
|
-
: await probeStorageMigrationRuntime({
|
|
154
|
-
profile: values.profile,
|
|
155
|
-
serverUrl: config.serverUrl,
|
|
156
|
-
currentDaemonHome: activeDaemonHome,
|
|
157
|
-
currentAgentsRoot: config.agentsRoot,
|
|
158
|
-
userHome: homedir(),
|
|
159
|
-
platform: process.platform,
|
|
160
|
-
...(process.getuid === undefined ? {} : { uid: process.getuid() }),
|
|
161
|
-
environment: process.env,
|
|
162
|
-
});
|
|
163
|
-
const targetStorageBinding = storageRuntime === null
|
|
138
|
+
const storageBinding = storageRuntime === null
|
|
164
139
|
? null
|
|
165
140
|
: createStorageRuntimeOwnershipBinding(storageRuntime);
|
|
166
|
-
const targetConfirmation = storageRuntime?.identity.storageMigrationId === undefined
|
|
167
|
-
? null
|
|
168
|
-
: createStorageTargetConfirmationHandler({
|
|
169
|
-
backupsRoot: storageRuntime.local.backupsRoot,
|
|
170
|
-
identity: storageRuntime.identity,
|
|
171
|
-
profile: storageRuntime.installation.profile,
|
|
172
|
-
completionTimeoutMs: 180_000,
|
|
173
|
-
});
|
|
174
|
-
const storageMigration = storageRuntime !== null
|
|
175
|
-
? {
|
|
176
|
-
ready: false,
|
|
177
|
-
statusOutboxPath: join(storageRuntime.local.stateRoot, "storage-status-outbox.json"),
|
|
178
|
-
identity: targetStorageBinding.identity,
|
|
179
|
-
bindServerIdentity: targetStorageBinding.bind,
|
|
180
|
-
...(targetConfirmation === null ? {} : { confirm: targetConfirmation.handle }),
|
|
181
|
-
...(storageRuntime.identity.storageMigrationId === undefined ? {} : {
|
|
182
|
-
replayStatus: async (send) => {
|
|
183
|
-
const migrationId = storageRuntime.identity.storageMigrationId;
|
|
184
|
-
const receipt = await readStorageMigrationReceipt(storageRuntime.local.backupsRoot, migrationId);
|
|
185
|
-
if (receipt === null)
|
|
186
|
-
throw new Error(`storage migration receipt not found: ${migrationId}`);
|
|
187
|
-
for (const frame of storageMigrationStatusReplay(receipt, {
|
|
188
|
-
migrationId,
|
|
189
|
-
environment: storageRuntime.identity.storageEnvironment,
|
|
190
|
-
workspaceId: storageRuntime.identity.storageWorkspaceId,
|
|
191
|
-
profile: storageRuntime.installation.profile,
|
|
192
|
-
}))
|
|
193
|
-
send(frame);
|
|
194
|
-
},
|
|
195
|
-
}),
|
|
196
|
-
}
|
|
197
|
-
: migrationRuntime.ready
|
|
198
|
-
? {
|
|
199
|
-
ready: true,
|
|
200
|
-
statusOutboxPath: migrationRuntime.runtime.statusOutboxPath,
|
|
201
|
-
identity: migrationRuntime.runtime.identity,
|
|
202
|
-
controller: migrationRuntime.runtime.controller,
|
|
203
|
-
attach: migrationRuntime.runtime.attach,
|
|
204
|
-
bindServerIdentity: migrationRuntime.runtime.bindServerIdentity,
|
|
205
|
-
replayStatus: migrationRuntime.runtime.replayStatus,
|
|
206
|
-
}
|
|
207
|
-
: undefined;
|
|
208
141
|
await runWithDaemonInstallationLease(installationLease, async () => {
|
|
209
142
|
const service = serve(runtimeConfig, {
|
|
210
143
|
...(values.profile === undefined ? {} : { profileName: values.profile }),
|
|
211
144
|
localHome,
|
|
212
|
-
...(
|
|
145
|
+
...(storageBinding === null ? {} : {
|
|
146
|
+
storageRuntime: { identity: storageBinding.identity, bindServerIdentity: storageBinding.bind },
|
|
147
|
+
}),
|
|
213
148
|
});
|
|
214
149
|
await runServeLifecycle(service);
|
|
215
150
|
});
|
|
@@ -9,10 +9,11 @@ export function projectSkillExecutionProjection(bindings, generation) {
|
|
|
9
9
|
});
|
|
10
10
|
}
|
|
11
11
|
/**
|
|
12
|
-
* 投影门控与 ownership 判定的唯一谓词:只有绑定了 Project Skills
|
|
12
|
+
* 投影门控与 ownership 判定的唯一谓词:只有绑定了 Project Skills 且运行时支持受控目录的执行
|
|
13
13
|
* 才会有投影目录,也才需要在停止无法证明时保留它。两处判定分开写过一次,结果 kimi + projectSkills
|
|
14
14
|
* 会在取消路径上错抛 ownership 未确认。
|
|
15
15
|
*/
|
|
16
16
|
export const projectSkillProjectionApplies = (spec) => spec.agent.projectSkills !== undefined
|
|
17
|
-
&& (spec.runtime.name === "codex" || spec.runtime.name === "claude"
|
|
17
|
+
&& (spec.runtime.name === "codex" || spec.runtime.name === "claude"
|
|
18
|
+
|| spec.runtime.name === "deepseek-harness");
|
|
18
19
|
export const projectSkillProjectionErrorCode = (error) => error instanceof ProjectProjectionError ? error.code : null;
|
|
@@ -11,12 +11,26 @@ export function projectSkillRuntimeLaunchFacts(runtime, root) {
|
|
|
11
11
|
if (runtime === "claude") {
|
|
12
12
|
return Object.freeze({ claudeAdditionalDirectory: root.claudeAdditionalDirectory });
|
|
13
13
|
}
|
|
14
|
+
if (runtime === "deepseek-harness") {
|
|
15
|
+
return Object.freeze({ harnessSkillRoot: root.codexSkillRoot });
|
|
16
|
+
}
|
|
14
17
|
throw new ProjectProjectionError("skill_projection_failed");
|
|
15
18
|
}
|
|
16
19
|
/** Builds Runtime-native arrays while keeping v1 shared roots separate from v2 exact roots. */
|
|
17
20
|
export async function projectSkillRuntimeDirectories(input) {
|
|
18
21
|
const trainingProjectRoots = input.trainingProjectRoots ?? [];
|
|
19
22
|
const facts = projectSkillRuntimeLaunchFacts(input.runtime, input.runtimeRoot);
|
|
23
|
+
if (input.runtime === "deepseek-harness") {
|
|
24
|
+
// Harness 的 local provider 接受直接包含 Skill 子目录的受控 root,不扫描项目/用户目录。
|
|
25
|
+
return Object.freeze({
|
|
26
|
+
codexSkillRoots: Object.freeze([]),
|
|
27
|
+
claudeAdditionalDirectories: Object.freeze([]),
|
|
28
|
+
harnessSkillRoots: Object.freeze([
|
|
29
|
+
...(facts.harnessSkillRoot === undefined ? [] : [facts.harnessSkillRoot]),
|
|
30
|
+
...(input.abilitySkillRoot === undefined ? [] : [input.abilitySkillRoot]),
|
|
31
|
+
]),
|
|
32
|
+
});
|
|
33
|
+
}
|
|
20
34
|
const codexSkillRoots = input.runtime === "codex"
|
|
21
35
|
&& (input.runtimeRoot !== undefined || input.projectContext !== undefined)
|
|
22
36
|
? Object.freeze([
|
package/dist/provider-env.js
CHANGED
|
@@ -10,7 +10,6 @@
|
|
|
10
10
|
*/
|
|
11
11
|
import { join } from "node:path";
|
|
12
12
|
import { DEEPSEEK_CODEX_KEY_ENV, deepSeekCodexHome, } from "./runtimes/codex-deepseek-config.js";
|
|
13
|
-
import { DEEPSEEK_HARNESS_CONFIG_ENV } from "./runtimes/deepseek-harness.js";
|
|
14
13
|
const ENV_KEY_RE = /^[A-Za-z_][A-Za-z0-9_]*$/;
|
|
15
14
|
const RESERVED_ENV = new Set([
|
|
16
15
|
"PATH",
|
|
@@ -96,9 +95,9 @@ export function applyProviderEnv(base, runtime, cfg, homeDir, deepSeekHomeOverri
|
|
|
96
95
|
}
|
|
97
96
|
return env;
|
|
98
97
|
}
|
|
99
|
-
const deepSeekHarnessMachineOwnedKey = (key) => (key.startsWith("DEEPSEEK_")
|
|
100
|
-
|| key
|
|
101
|
-
|| key.startsWith("DSH_"));
|
|
98
|
+
const deepSeekHarnessMachineOwnedKey = (key) => (key.toUpperCase().startsWith("DEEPSEEK_")
|
|
99
|
+
|| key.toUpperCase().startsWith("NOWCREW_DEEPSEEK_HARNESS_")
|
|
100
|
+
|| key.toUpperCase().startsWith("DSH_"));
|
|
102
101
|
/** Agent env remains available to tools, but cannot replace Harness process policy or credentials. */
|
|
103
102
|
export function applyDeepSeekHarnessMachineEnv(base, machineEnv) {
|
|
104
103
|
const env = { ...base };
|
package/dist/runtime-probe.js
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { randomUUID } from "node:crypto";
|
|
2
|
+
import { dslog } from "./slog.js";
|
|
1
3
|
import { executableRuntimes } from "./runtime-capabilities.js";
|
|
2
4
|
export function conservativeExecutionRuntimes(installed) {
|
|
3
5
|
return executableRuntimes(installed)
|
|
@@ -7,16 +9,43 @@ export function createRuntimeProbeCoordinator() {
|
|
|
7
9
|
const abort = new AbortController();
|
|
8
10
|
const flights = new Map();
|
|
9
11
|
return {
|
|
10
|
-
detect(installed, detector) {
|
|
12
|
+
detect(installed, detector, connectionId) {
|
|
11
13
|
const key = [...installed].sort().join("\0");
|
|
12
14
|
const existing = flights.get(key);
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
15
|
+
const probeId = existing?.probeId ?? randomUUID();
|
|
16
|
+
const fields = { probe_id: probeId, connection_id: connectionId, installed_runtimes: JSON.stringify(installed) };
|
|
17
|
+
dslog("runtime_probe.request", "请求 runtime 执行能力探测", fields);
|
|
18
|
+
if (existing) {
|
|
19
|
+
dslog("runtime_probe.cache_hit", "复用 runtime 探测结果", { ...fields, cache_state: existing.state });
|
|
20
|
+
return existing.promise;
|
|
21
|
+
}
|
|
22
|
+
const startedAt = Date.now();
|
|
23
|
+
const complete = (outcome, executionRuntimes) => {
|
|
24
|
+
dslog("runtime_probe.complete", "runtime 探测聚合结束", {
|
|
25
|
+
...fields, outcome, duration_ms: Date.now() - startedAt, aborted: abort.signal.aborted,
|
|
26
|
+
...(executionRuntimes === undefined ? {} : { execution_runtimes: JSON.stringify(executionRuntimes) }),
|
|
27
|
+
});
|
|
28
|
+
};
|
|
29
|
+
let detection;
|
|
30
|
+
try {
|
|
31
|
+
detection = detector(installed, abort.signal, probeId);
|
|
32
|
+
}
|
|
33
|
+
catch (error) {
|
|
34
|
+
complete("threw");
|
|
35
|
+
throw error;
|
|
36
|
+
}
|
|
37
|
+
const pending = detection.then((result) => {
|
|
38
|
+
const flight = flights.get(key);
|
|
39
|
+
if (flight)
|
|
40
|
+
flights.set(key, { ...flight, state: "resolved" });
|
|
41
|
+
complete("resolved", result);
|
|
42
|
+
return result;
|
|
43
|
+
}, (error) => {
|
|
16
44
|
flights.delete(key);
|
|
45
|
+
complete("threw");
|
|
17
46
|
throw error;
|
|
18
47
|
});
|
|
19
|
-
flights.set(key, pending);
|
|
48
|
+
flights.set(key, { promise: pending, probeId, state: "pending" });
|
|
20
49
|
return pending;
|
|
21
50
|
},
|
|
22
51
|
stop() {
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
/** 短命执行配置:不读取全局 Harness home,不新增 daemon 状态。 */
|
|
2
|
+
import { mkdtemp, mkdir, writeFile, access } from "node:fs/promises";
|
|
3
|
+
import { join, isAbsolute } from "node:path";
|
|
4
|
+
import { stringify } from "yaml";
|
|
5
|
+
export const harnessPermissionMode = (permission) => permission === "sandboxed"
|
|
6
|
+
? "read-only"
|
|
7
|
+
: permission === "workspace_write"
|
|
8
|
+
? "workspace-write"
|
|
9
|
+
: "danger-full-access";
|
|
10
|
+
// A variable's returned value is interpolated once: literal {{...}} in NowWork
|
|
11
|
+
// instructions must not become unknown Harness template variables.
|
|
12
|
+
const instructionsPlugin = `export const name = 'nowwork-instructions';
|
|
13
|
+
export const inject = ['systemPrompt'];
|
|
14
|
+
export function apply(ctx, config) {
|
|
15
|
+
ctx.systemPrompt.variable('nowwork_instructions', () => config.text);
|
|
16
|
+
ctx.systemPrompt.section({name: 'nowwork-instructions', order: 100000, text: '{{nowwork_instructions}}'});
|
|
17
|
+
}
|
|
18
|
+
`;
|
|
19
|
+
export async function prepareHarnessProfile(input) {
|
|
20
|
+
if (![input.stateRoot, input.cwd, ...input.skillRoots].every(isAbsolute)) {
|
|
21
|
+
throw new Error("DeepSeek Harness execution paths must be absolute");
|
|
22
|
+
}
|
|
23
|
+
if (input.patchPath !== undefined) {
|
|
24
|
+
if (!isAbsolute(input.patchPath))
|
|
25
|
+
throw new Error("DeepSeek Harness patch must be absolute");
|
|
26
|
+
await access(input.patchPath);
|
|
27
|
+
}
|
|
28
|
+
await mkdir(input.stateRoot, { recursive: true, mode: 0o700 });
|
|
29
|
+
const home = await mkdtemp(join(input.stateRoot, ".deepseek-harness-"));
|
|
30
|
+
const pluginPath = join(home, "nowwork-instructions.mjs");
|
|
31
|
+
const policyPath = join(home, "nowwork-policy.yml");
|
|
32
|
+
const mode = harnessPermissionMode(input.effectivePermission);
|
|
33
|
+
await writeFile(pluginPath, instructionsPlugin, { mode: 0o600 });
|
|
34
|
+
await writeFile(policyPath, stringify([
|
|
35
|
+
{
|
|
36
|
+
id: "sandbox-policy",
|
|
37
|
+
disabled: false,
|
|
38
|
+
config: { mode, workspaceRoot: input.cwd },
|
|
39
|
+
},
|
|
40
|
+
{ id: "approval", disabled: false, config: { policy: "never" } },
|
|
41
|
+
{
|
|
42
|
+
id: "permission",
|
|
43
|
+
disabled: false,
|
|
44
|
+
config: {
|
|
45
|
+
defaultPreset: "nowwork",
|
|
46
|
+
presets: { nowwork: { sandbox: mode, approval: "never" } },
|
|
47
|
+
},
|
|
48
|
+
},
|
|
49
|
+
{
|
|
50
|
+
id: "skill-filesystem",
|
|
51
|
+
disabled: false,
|
|
52
|
+
config: {
|
|
53
|
+
includeDefaultRoots: false,
|
|
54
|
+
customSkillDirs: [...input.skillRoots],
|
|
55
|
+
watch: false,
|
|
56
|
+
},
|
|
57
|
+
},
|
|
58
|
+
{
|
|
59
|
+
id: "session-persistence-jsonl",
|
|
60
|
+
disabled: false,
|
|
61
|
+
config: { root: join(home, "sessions") },
|
|
62
|
+
},
|
|
63
|
+
{
|
|
64
|
+
insert: [
|
|
65
|
+
{
|
|
66
|
+
id: "nowwork-instructions",
|
|
67
|
+
name: pluginPath,
|
|
68
|
+
config: { text: input.systemPrompt },
|
|
69
|
+
},
|
|
70
|
+
],
|
|
71
|
+
},
|
|
72
|
+
]), { mode: 0o600 });
|
|
73
|
+
const env = Object.fromEntries(Object.entries(input.env).filter(([key]) => !key.toUpperCase().startsWith("DSH_")));
|
|
74
|
+
return {
|
|
75
|
+
home,
|
|
76
|
+
policyPath,
|
|
77
|
+
args: [
|
|
78
|
+
"--profile",
|
|
79
|
+
"acp",
|
|
80
|
+
...(input.patchPath ? ["--patch", input.patchPath] : []),
|
|
81
|
+
"--patch",
|
|
82
|
+
policyPath,
|
|
83
|
+
],
|
|
84
|
+
env: {
|
|
85
|
+
...env,
|
|
86
|
+
DSH_HOME: home,
|
|
87
|
+
DSH_AGENTS_HOME: join(home, "agents-home"),
|
|
88
|
+
DSH_PERMISSION_MODE: mode,
|
|
89
|
+
DSH_TELEMETRY_DISABLED: "1",
|
|
90
|
+
},
|
|
91
|
+
};
|
|
92
|
+
}
|