@nowcrew/daemon 0.6.77-beta.9 → 0.6.78
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/control-plane-url.js +3 -9
- package/dist/execution-runner.js +4 -5
- package/dist/local-executor.js +18 -17
- package/dist/local-memory/runtime.js +5 -3
- package/dist/machine-info.js +3 -0
- package/dist/project-skills/controller.js +6 -71
- package/dist/project-skills/execution-adapter.js +7 -0
- package/dist/project-skills/execution-projection.js +163 -0
- package/dist/project-skills/initialized-reconciler.js +10 -8
- package/dist/project-skills/legacy-cleanup.js +224 -0
- package/dist/project-skills/machine-hello-publisher.js +20 -0
- package/dist/project-skills/reconciler.js +114 -500
- package/dist/project-skills/resolution-records.js +63 -0
- package/dist/project-skills/runtime-launch.js +0 -42
- package/dist/project-skills/runtime-root-bootstrap.js +0 -6
- package/dist/project-skills/runtime-root-startup.js +1 -2
- package/dist/project-skills/runtime-warning.js +1 -3
- package/dist/project-skills/scanner.js +1 -17
- package/dist/project-skills/serve-startup.js +0 -23
- package/dist/project-skills/startup-cleanup.js +24 -0
- package/dist/serve.js +44 -45
- package/package.json +1 -1
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import { posix, win32 } from "node:path";
|
|
2
|
+
import { compareProjectSkillRefs, isProjectId, isProjectSkillName, MAX_AGENT_PROJECT_SKILL_BINDINGS, } from "./types.js";
|
|
3
|
+
const MAX_SOURCE_PATH_BYTES = 16 * 1024;
|
|
4
|
+
/** Thrown when resolution records fail the shape/normalization invariants below. */
|
|
5
|
+
export class ProjectSkillResolutionRecordsCorruptError extends Error {
|
|
6
|
+
constructor() {
|
|
7
|
+
super("skill_projection_state_corrupt");
|
|
8
|
+
this.name = "ProjectSkillResolutionRecordsCorruptError";
|
|
9
|
+
}
|
|
10
|
+
}
|
|
11
|
+
const corrupt = () => { throw new ProjectSkillResolutionRecordsCorruptError(); };
|
|
12
|
+
const normalizeWindowsPath = (sourcePath) => {
|
|
13
|
+
let value = sourcePath.replaceAll("/", "\\");
|
|
14
|
+
if (/^\\\\\?\\UNC\\/iu.test(value))
|
|
15
|
+
value = `\\\\${value.slice(8)}`;
|
|
16
|
+
else if (/^\\\\\?\\[A-Za-z]:\\/u.test(value))
|
|
17
|
+
value = value.slice(4);
|
|
18
|
+
if (!win32.isAbsolute(value))
|
|
19
|
+
return corrupt();
|
|
20
|
+
return win32.normalize(value).toLowerCase();
|
|
21
|
+
};
|
|
22
|
+
const normalizeSourcePath = (sourcePath, platform) => {
|
|
23
|
+
if (sourcePath.includes("\0") || Buffer.byteLength(sourcePath, "utf8") > MAX_SOURCE_PATH_BYTES) {
|
|
24
|
+
return corrupt();
|
|
25
|
+
}
|
|
26
|
+
if (platform === "win32")
|
|
27
|
+
return normalizeWindowsPath(sourcePath);
|
|
28
|
+
if (!posix.isAbsolute(sourcePath))
|
|
29
|
+
return corrupt();
|
|
30
|
+
return posix.normalize(sourcePath);
|
|
31
|
+
};
|
|
32
|
+
export function normalizeProjectSkillResolutionRecords(records, platform = process.platform) {
|
|
33
|
+
if (records.length > MAX_AGENT_PROJECT_SKILL_BINDINGS)
|
|
34
|
+
return corrupt();
|
|
35
|
+
const seen = new Set();
|
|
36
|
+
const normalized = records.map((record) => {
|
|
37
|
+
if (!isProjectId(record.projectId) || !isProjectSkillName(record.skillName))
|
|
38
|
+
return corrupt();
|
|
39
|
+
const key = `${record.projectId}\0${record.skillName}`;
|
|
40
|
+
if (seen.has(key))
|
|
41
|
+
return corrupt();
|
|
42
|
+
seen.add(key);
|
|
43
|
+
if (record.mode === "missing") {
|
|
44
|
+
if (record.sourcePath !== null)
|
|
45
|
+
return corrupt();
|
|
46
|
+
return Object.freeze({
|
|
47
|
+
projectId: record.projectId,
|
|
48
|
+
skillName: record.skillName,
|
|
49
|
+
sourcePath: null,
|
|
50
|
+
mode: "missing",
|
|
51
|
+
});
|
|
52
|
+
}
|
|
53
|
+
if (record.mode !== "resolved" || record.sourcePath === null)
|
|
54
|
+
return corrupt();
|
|
55
|
+
return Object.freeze({
|
|
56
|
+
projectId: record.projectId,
|
|
57
|
+
skillName: record.skillName,
|
|
58
|
+
sourcePath: normalizeSourcePath(record.sourcePath, platform),
|
|
59
|
+
mode: "resolved",
|
|
60
|
+
});
|
|
61
|
+
}).sort(compareProjectSkillRefs);
|
|
62
|
+
return Object.freeze(normalized);
|
|
63
|
+
}
|
|
@@ -1,48 +1,6 @@
|
|
|
1
1
|
import { join } from "node:path";
|
|
2
2
|
import { resolveOrderedUniqueClaudeDirectories } from "../runtimes/claude.js";
|
|
3
3
|
import { ProjectProjectionError, } from "./reconciler.js";
|
|
4
|
-
const runtimeRootSecrets = (root) => Object.freeze([...new Set([
|
|
5
|
-
root.rootDirectory,
|
|
6
|
-
root.codexSkillRoot,
|
|
7
|
-
root.claudeAdditionalDirectory,
|
|
8
|
-
root.rootId,
|
|
9
|
-
].flatMap((value) => [
|
|
10
|
-
value,
|
|
11
|
-
value.replaceAll("\\", "/"),
|
|
12
|
-
value.replaceAll("/", "\\"),
|
|
13
|
-
]))].sort((left, right) => right.length - left.length));
|
|
14
|
-
export function redactProjectSkillRuntimeRootText(text, root) {
|
|
15
|
-
if (root === undefined)
|
|
16
|
-
return text;
|
|
17
|
-
return runtimeRootSecrets(root)
|
|
18
|
-
.reduce((redacted, secret) => redacted.replaceAll(secret, "[project-skill-root]"), text);
|
|
19
|
-
}
|
|
20
|
-
export function redactProjectSkillRuntimeRootError(error, root) {
|
|
21
|
-
if (!(error instanceof Error)) {
|
|
22
|
-
return typeof error === "string" ? redactProjectSkillRuntimeRootText(error, root) : error;
|
|
23
|
-
}
|
|
24
|
-
const message = redactProjectSkillRuntimeRootText(error.message, root);
|
|
25
|
-
if (message === error.message)
|
|
26
|
-
return error;
|
|
27
|
-
const redacted = new Error(message);
|
|
28
|
-
redacted.name = error.name;
|
|
29
|
-
return redacted;
|
|
30
|
-
}
|
|
31
|
-
/** Recursively redacts exact v2 root identities before Runtime output crosses daemon boundaries. */
|
|
32
|
-
export function redactProjectSkillRuntimeRootValue(value, root) {
|
|
33
|
-
if (root === undefined)
|
|
34
|
-
return value;
|
|
35
|
-
if (typeof value === "string")
|
|
36
|
-
return redactProjectSkillRuntimeRootText(value, root);
|
|
37
|
-
if (Array.isArray(value)) {
|
|
38
|
-
return value.map((item) => redactProjectSkillRuntimeRootValue(item, root));
|
|
39
|
-
}
|
|
40
|
-
if (typeof value === "object" && value !== null) {
|
|
41
|
-
return Object.fromEntries(Object.entries(value)
|
|
42
|
-
.map(([key, item]) => [key, redactProjectSkillRuntimeRootValue(item, root)]));
|
|
43
|
-
}
|
|
44
|
-
return value;
|
|
45
|
-
}
|
|
46
4
|
/** Maps one captured daemon-local root to the exact native Runtime registration seam. */
|
|
47
5
|
export function projectSkillRuntimeLaunchFacts(runtime, root) {
|
|
48
6
|
if (root === undefined)
|
|
@@ -16,18 +16,12 @@ export async function initializeAfterProjectSkillLeaseProtection(input) {
|
|
|
16
16
|
/**
|
|
17
17
|
* Runs only after execution-journal restart reconciliation has settled. A lease without a terminal
|
|
18
18
|
* journal fact remains durable and prevents the caller from marking Project Skill v2 ready.
|
|
19
|
-
*
|
|
20
|
-
* 重连时本进程仍在跑的 execution 合法持有 lease(`liveExecutionIds`):既不释放,也不算 unresolved。
|
|
21
|
-
* 否则一次网络抖动就会让 daemon 在整条连接上摘掉 Project Skills 能力(incident: 2026-09-02,
|
|
22
|
-
* 绑定项目的 Agent 全部 execution_spec_incompatible)。
|
|
23
19
|
*/
|
|
24
20
|
export async function recoverProjectSkillRuntimeRoots(input) {
|
|
25
21
|
for (const agentRoot of input.agentRoots) {
|
|
26
22
|
await input.roots.recover(agentRoot);
|
|
27
23
|
let unresolvedLease = false;
|
|
28
24
|
await input.leases.recover(agentRoot, async (executionId) => {
|
|
29
|
-
if (input.liveExecutionIds?.has(executionId))
|
|
30
|
-
return false;
|
|
31
25
|
const entry = await input.journal.get(executionId);
|
|
32
26
|
const releasable = entry?.state === "completed" || entry?.state === "interrupted";
|
|
33
27
|
if (!releasable)
|
|
@@ -22,14 +22,13 @@ export function createProjectSkillRuntimeRootStartup() {
|
|
|
22
22
|
leases,
|
|
23
23
|
initialize,
|
|
24
24
|
}),
|
|
25
|
-
recover: async (agentsRoot, journal, protectedExecutionIds
|
|
25
|
+
recover: async (agentsRoot, journal, protectedExecutionIds) => {
|
|
26
26
|
await recoverProjectSkillRuntimeRoots({
|
|
27
27
|
agentRoots: await agentRoots(agentsRoot),
|
|
28
28
|
journal,
|
|
29
29
|
roots,
|
|
30
30
|
leases,
|
|
31
31
|
gc,
|
|
32
|
-
...(liveExecutionIds === undefined ? {} : { liveExecutionIds }),
|
|
33
32
|
currentRootId: async (agentRoot) => (await readAppliedProjectSkillManifest(agentRoot))?.rootId ?? null,
|
|
34
33
|
afterLeaseRecovery: async () => {
|
|
35
34
|
const remaining = await initializeAfterProjectSkillLeaseProtection({
|
|
@@ -1,6 +1,4 @@
|
|
|
1
1
|
/** Stable, path-free console text; only logical IDs from the server snapshot are included. */
|
|
2
2
|
export function formatProjectSkillRuntimeWarning(warning) {
|
|
3
|
-
return warning.
|
|
4
|
-
? "[runtime-warning] skill_projection_advanced"
|
|
5
|
-
: `[runtime-warning] project_skill_unavailable project=${warning.projectId} skill=${warning.skillName}`;
|
|
3
|
+
return `[runtime-warning] project_skill_unavailable project=${warning.projectId} skill=${warning.skillName}`;
|
|
6
4
|
}
|
|
@@ -2,7 +2,7 @@ import { readdir, readFile, stat } from "node:fs/promises";
|
|
|
2
2
|
import { join } from "node:path";
|
|
3
3
|
import { compareProjectSkillRefs, isProjectSkillName, MAX_MACHINE_PROJECT_SKILLS, MAX_PROJECT_SKILL_DESCRIPTION_LENGTH, MAX_PROJECT_SKILL_FILE_BYTES, MAX_PROJECT_SKILLS_PER_PROJECT, } from "./types.js";
|
|
4
4
|
import { parseSkillFrontmatter } from "../skill-frontmatter.js";
|
|
5
|
-
import {
|
|
5
|
+
import { normalizeProjectSkillResolutionRecords } from "./resolution-records.js";
|
|
6
6
|
const unavailable = (projectId, scannedAt, errorCode) => Object.freeze({
|
|
7
7
|
inventory: Object.freeze({
|
|
8
8
|
projectId,
|
|
@@ -143,19 +143,3 @@ export function projectSkillResolutionRecords(bindings, projects, platform = pro
|
|
|
143
143
|
});
|
|
144
144
|
return normalizeProjectSkillResolutionRecords(records, platform);
|
|
145
145
|
}
|
|
146
|
-
/** Daemon-local expectation derived from one immutable scanner snapshot; no path crosses the wire. */
|
|
147
|
-
export function projectSkillProjectionExpectation(snapshot, projects, platform = process.platform) {
|
|
148
|
-
const bindings = Object.freeze(snapshot.bindings
|
|
149
|
-
.map((binding) => Object.freeze({
|
|
150
|
-
projectId: binding.projectId,
|
|
151
|
-
skillName: binding.skillName,
|
|
152
|
-
}))
|
|
153
|
-
.sort(compareProjectSkillRefs));
|
|
154
|
-
const resolutions = projectSkillResolutionRecords(bindings, projects, platform);
|
|
155
|
-
return Object.freeze({
|
|
156
|
-
bindings,
|
|
157
|
-
generation: snapshot.generation,
|
|
158
|
-
bindingDigest: computeProjectSkillBindingDigest(bindings),
|
|
159
|
-
resolutionDigest: computeProjectSkillResolutionDigest(resolutions, platform),
|
|
160
|
-
});
|
|
161
|
-
}
|
|
@@ -1,29 +1,6 @@
|
|
|
1
1
|
import { createExecutionJournal } from "../execution-journal.js";
|
|
2
2
|
import { reconcileExecutionJournal } from "../execution-recovery.js";
|
|
3
3
|
import { dslog, flushSlog } from "../slog.js";
|
|
4
|
-
/**
|
|
5
|
-
* 每次连接 preflight 的 Runtime root / lease 恢复。重连时本进程仍在跑的 execution 合法持有 lease,
|
|
6
|
-
* 必须当作存活而非孤儿;失败也必须留痕,否则整条连接静默摘掉 Project Skills 能力
|
|
7
|
-
* (incident: 2026-09-02,绑定项目的 Agent 全部 execution_spec_incompatible)。
|
|
8
|
-
*/
|
|
9
|
-
export function createProjectSkillReconnectRecovery(startup, agentsRoot, journal, protectedExecutionIds, liveExecutionIds) {
|
|
10
|
-
return async () => {
|
|
11
|
-
const live = liveExecutionIds();
|
|
12
|
-
try {
|
|
13
|
-
await startup.recover(agentsRoot, journal(), protectedExecutionIds, live);
|
|
14
|
-
return true;
|
|
15
|
-
}
|
|
16
|
-
catch (error) {
|
|
17
|
-
dslog("project_skills.preflight_recovery_failed", "Project Skill runtime root 恢复失败,本连接不宣告 Project Skills 能力", {
|
|
18
|
-
level: "ERROR",
|
|
19
|
-
error_code: error.code ?? "unknown",
|
|
20
|
-
error_message: error.message,
|
|
21
|
-
live_execution_count: live.size,
|
|
22
|
-
});
|
|
23
|
-
return false;
|
|
24
|
-
}
|
|
25
|
-
};
|
|
26
|
-
}
|
|
27
4
|
export async function initializeProjectSkillProtectedExecutionJournal(input) {
|
|
28
5
|
return input.startup.initializeAfterLeaseProtection(input.config.agentsRoot, async (snapshot) => {
|
|
29
6
|
input.protectedExecutionIds.clear();
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { cleanupLegacyProjectSkillState, sweepAllExecutionProjections } from "./legacy-cleanup.js";
|
|
2
|
+
export async function runProjectSkillStartupCleanup(agentsRoot, journal, log) {
|
|
3
|
+
try {
|
|
4
|
+
const swept = await sweepAllExecutionProjections(agentsRoot, async (executionId) => {
|
|
5
|
+
const entry = await journal.get(executionId);
|
|
6
|
+
return entry !== null && entry.state !== "completed" && entry.state !== "interrupted";
|
|
7
|
+
});
|
|
8
|
+
const legacy = await cleanupLegacyProjectSkillState({ agentsRoot, journal });
|
|
9
|
+
const degraded = swept.failed > 0 || legacy.failedAgents.length > 0 || legacy.keptStores.length > 0;
|
|
10
|
+
log("project_skills.startup_cleanup", "启动清扫执行投影与旧运行时状态", {
|
|
11
|
+
removed_projections: swept.removed, kept_projections: swept.kept,
|
|
12
|
+
failed_projection_sweeps: swept.failed,
|
|
13
|
+
removed_legacy_stores: legacy.removedStores.length, kept_legacy_stores: legacy.keptStores.length,
|
|
14
|
+
failed_legacy_agents: legacy.failedAgents.length,
|
|
15
|
+
removed_shared_entries: legacy.removedSharedEntries,
|
|
16
|
+
...(degraded ? { level: "WARN" } : {}),
|
|
17
|
+
});
|
|
18
|
+
}
|
|
19
|
+
catch (error) {
|
|
20
|
+
log("project_skills.startup_cleanup_failed", "启动清扫失败,能力不受影响", {
|
|
21
|
+
level: "WARN", error_message: error.message,
|
|
22
|
+
});
|
|
23
|
+
}
|
|
24
|
+
}
|
package/dist/serve.js
CHANGED
|
@@ -8,7 +8,8 @@ import { randomUUID } from "node:crypto";
|
|
|
8
8
|
import { dslog, setSlogDefaults, drainSpool, flushSlog } from "./slog.js";
|
|
9
9
|
import { mergeRunAgentResults, reportScheduledStartFailure, runAgent } from "./runner.js";
|
|
10
10
|
import { buildOriginDecisionRetryPrompt, buildScheduledPrompt } from "./prompt.js";
|
|
11
|
-
import { collectMachineHello, daemonCapabilities, detectExecutionRuntimesWithSignal, } from "./machine-info.js";
|
|
11
|
+
import { collectMachineHello, daemonCapabilities, daemonCapabilityBindings, detectExecutionRuntimesWithSignal, } from "./machine-info.js";
|
|
12
|
+
import { reconcileExecutionJournal } from "./execution-recovery.js";
|
|
12
13
|
import { initReleaseSlog, reportDaemonConnectionClose, reportReleaseError } from "./release-channel-guard.js";
|
|
13
14
|
import { conservativeExecutionRuntimes, createRuntimeProbeCoordinator, } from "./runtime-probe.js";
|
|
14
15
|
import { listWorkspace, readWorkspaceFile } from "./workspace-fs.js";
|
|
@@ -19,6 +20,7 @@ import { normalizeScheduledContext } from "./scheduled-report.js";
|
|
|
19
20
|
import { formatDaemonLogLine } from "./log-format.js";
|
|
20
21
|
import { reportAgentRunComplete } from "./scheduled-run-report.js";
|
|
21
22
|
import { runWithOriginDecisionGuard } from "./origin-decision.js";
|
|
23
|
+
import { createExecutionJournal } from "./execution-journal.js";
|
|
22
24
|
import { createExecutionTelemetryJournal } from "./execution-telemetry-journal.js";
|
|
23
25
|
import { ExecutionRejectedSchema, ExecutionSnapshotSchema, LegacyAgentStartSchema, ServerToDaemonExecutionFrameSchema, } from "./execution-protocol.js";
|
|
24
26
|
import { hashExecutionSpec, projectWorkspaceCapabilityRejection, runExecution, } from "./execution-runner.js";
|
|
@@ -40,10 +42,8 @@ import { createProjectRegistryWiring } from "./project-registry-wiring.js";
|
|
|
40
42
|
import { createProjectSkillsController, } from "./project-skills/controller.js";
|
|
41
43
|
import { PROJECT_SKILLS_CAPABILITY } from "./project-skills/types.js";
|
|
42
44
|
import { createAgentProjectionCoordinator } from "./project-skills/agent-projection-coordinator.js";
|
|
43
|
-
import {
|
|
44
|
-
import {
|
|
45
|
-
import { createProjectSkillReconnectRecovery, initializeProjectSkillProtectedExecutionJournal } from "./project-skills/serve-startup.js";
|
|
46
|
-
import { createProjectSkillConnectionCapabilityProvider, createProjectSkillMachineHelloPublisher } from "./project-skills/serve-capability.js";
|
|
45
|
+
import { createMachineHelloPublisher } from "./project-skills/machine-hello-publisher.js";
|
|
46
|
+
import { runProjectSkillStartupCleanup } from "./project-skills/startup-cleanup.js";
|
|
47
47
|
import { createAgentAbilityRuntime } from "./agent-ability/runtime.js";
|
|
48
48
|
import { createProjectSkillsReconciler, } from "./project-skills/reconciler.js";
|
|
49
49
|
import { initializedProjectSkillsReconciler } from "./project-skills/initialized-reconciler.js";
|
|
@@ -76,7 +76,6 @@ export function serve(config, opts = {}) {
|
|
|
76
76
|
let stopPromise = null;
|
|
77
77
|
let executionJournal;
|
|
78
78
|
const injectedExecutionJournal = opts.execution?.journal;
|
|
79
|
-
const protectedExecutionIds = new Set();
|
|
80
79
|
const agentMemory = opts.execution?.dependencies?.agentMemory
|
|
81
80
|
?? (config.agentMemory === null ? undefined : createAgentMemoryBridge(config.agentMemory));
|
|
82
81
|
const executionTelemetry = createExecutionTelemetryJournal(config.agentsRoot);
|
|
@@ -154,9 +153,10 @@ export function serve(config, opts = {}) {
|
|
|
154
153
|
let projectSkillsController;
|
|
155
154
|
const projectSkillsReconciler = opts.projectSkills?.reconciler ?? createProjectSkillsReconciler({
|
|
156
155
|
agentsRoot: config.agentsRoot,
|
|
157
|
-
coordinator: projectionCoordinator,
|
|
158
156
|
scannedProjects: () => projectSkillsController.scannedProjects(),
|
|
159
|
-
|
|
157
|
+
onCleanupFailed: (executionId, error) => dslog("project_skills.cleanup_failed", "执行投影目录清理失败,留待下次启动清扫", {
|
|
158
|
+
level: "WARN", execution_id: executionId, error_code: error.code ?? "unknown",
|
|
159
|
+
}),
|
|
160
160
|
});
|
|
161
161
|
projectSkillsController = opts.projectSkills?.controller ?? createProjectSkillsController({
|
|
162
162
|
registry: projectRegistry,
|
|
@@ -171,30 +171,21 @@ export function serve(config, opts = {}) {
|
|
|
171
171
|
},
|
|
172
172
|
isAdmissionClosed: () => storageMigrationAdmissionClosed(),
|
|
173
173
|
reconcile: (handle, bindings) => projectSkillsReconciler.reconcile(handle, bindings),
|
|
174
|
-
ensureSnapshot: (handle, snapshot, options) => projectSkillsReconciler.ensureSnapshot(handle, snapshot, options),
|
|
175
174
|
});
|
|
176
|
-
const
|
|
177
|
-
??
|
|
178
|
-
const projectSkillControllerReadiness = createProjectSkillControllerReadiness({
|
|
179
|
-
initialize: () => projectSkillsController.initialize(),
|
|
180
|
-
onUnavailable: (error) => dslog("project_skills.registry_failed", "本机项目注册表读取失败", {
|
|
181
|
-
level: "ERROR", error_code: error.code ?? "project_registry_corrupt",
|
|
182
|
-
}),
|
|
183
|
-
onSettled: () => machineHelloPublisher.republish(),
|
|
175
|
+
const logRegistryFailure = (error) => dslog("project_skills.registry_failed", "本机项目注册表读取失败,本次不上报项目清单(能力不受影响)", {
|
|
176
|
+
level: "ERROR", error_code: error.code ?? "project_registry_corrupt",
|
|
184
177
|
});
|
|
185
|
-
const machineHelloPublisher =
|
|
186
|
-
|
|
187
|
-
effective: (hello, status) => {
|
|
188
|
-
const effective = withProjectSkillCapabilityStatus(hello, status);
|
|
178
|
+
const machineHelloPublisher = createMachineHelloPublisher({
|
|
179
|
+
effective: (hello) => {
|
|
189
180
|
const configuredIdentity = opts.storageMigration?.identity;
|
|
190
181
|
const currentIdentity = typeof configuredIdentity === "function"
|
|
191
182
|
? configuredIdentity()
|
|
192
183
|
: configuredIdentity;
|
|
193
|
-
return currentIdentity === undefined ?
|
|
184
|
+
return currentIdentity === undefined ? hello : { ...hello, ...currentIdentity };
|
|
194
185
|
},
|
|
195
186
|
onSent: (hello) => log(`📤 已上报机器信息: ${hello.hostname} · ${hello.os} · installed=[${hello.runtimes.join(",")}] · executable=[${hello.executionRuntimes.join(",")}] · agents=[${hello.agentHandles.join(",")}]`),
|
|
196
187
|
});
|
|
197
|
-
const initializedProjectSkills = initializedProjectSkillsReconciler(
|
|
188
|
+
const initializedProjectSkills = initializedProjectSkillsReconciler(() => projectSkillsController.ensureScanned(), projectSkillsReconciler);
|
|
198
189
|
const configuredCapabilities = opts.machineInfo?.capabilities ?? daemonCapabilities(process.platform);
|
|
199
190
|
const storageCapabilities = storageMigrationCapabilities(configuredCapabilities, opts.storageMigration);
|
|
200
191
|
const storageStatusDelivery = createServeStorageStatusDelivery(opts.storageMigration, {
|
|
@@ -212,15 +203,7 @@ export function serve(config, opts = {}) {
|
|
|
212
203
|
sendStatus: storageStatusDelivery.send,
|
|
213
204
|
});
|
|
214
205
|
storageMigrationAdmissionClosed = storageMigrationRuntime.isAdmissionClosed;
|
|
215
|
-
const
|
|
216
|
-
platform: process.platform,
|
|
217
|
-
baseCapabilities: storageCapabilities,
|
|
218
|
-
additionalCapabilities: () => managedDaemonCapabilities(updateEligibility),
|
|
219
|
-
probeRuntimeRootStorePrimitive: projectSkillRuntimeRootStartup.probeNativePublicationPrimitiveReadiness,
|
|
220
|
-
initializeController: projectSkillControllerReadiness.ensure,
|
|
221
|
-
recoverRuntimeRootsAndLeases: createProjectSkillReconnectRecovery(projectSkillRuntimeRootStartup, config.agentsRoot, () => executionJournal, protectedExecutionIds, () => new Set(executionRuns.keys())),
|
|
222
|
-
applyReadiness: projectSkillControllerReadiness.applyPreflight,
|
|
223
|
-
});
|
|
206
|
+
const prepareCapabilities = async () => daemonCapabilityBindings(process.platform, [...storageCapabilities, ...await managedDaemonCapabilities(updateEligibility)]);
|
|
224
207
|
const safeExecutionSend = (frame) => {
|
|
225
208
|
try {
|
|
226
209
|
if (ws?.readyState !== WebSocket.OPEN)
|
|
@@ -325,10 +308,10 @@ export function serve(config, opts = {}) {
|
|
|
325
308
|
async function connect() {
|
|
326
309
|
if (stopped)
|
|
327
310
|
return;
|
|
328
|
-
const capabilities = await
|
|
311
|
+
const capabilities = await prepareCapabilities();
|
|
329
312
|
if (stopped)
|
|
330
313
|
return;
|
|
331
|
-
const wsUrl = (opts.buildControlPlaneUrl ?? buildControlPlaneUrl)(config.serverUrl, config.machineToken, process.platform, undefined,
|
|
314
|
+
const wsUrl = (opts.buildControlPlaneUrl ?? buildControlPlaneUrl)(config.serverUrl, config.machineToken, process.platform, undefined, capabilities.controlPlaneUrl);
|
|
332
315
|
ws = createWebSocket(wsUrl);
|
|
333
316
|
let connectionEpochToken;
|
|
334
317
|
ws.on("open", () => {
|
|
@@ -844,8 +827,9 @@ export function serve(config, opts = {}) {
|
|
|
844
827
|
? r.serverCapabilities.filter((c) => typeof c === "string")
|
|
845
828
|
: []);
|
|
846
829
|
if (serverCapabilities.has(PROJECT_SKILLS_CAPABILITY)) {
|
|
847
|
-
|
|
848
|
-
|
|
830
|
+
void projectSkillsController.initialize()
|
|
831
|
+
.then(() => projectSkillsController.publishCurrent())
|
|
832
|
+
.catch(logRegistryFailure);
|
|
849
833
|
}
|
|
850
834
|
return;
|
|
851
835
|
}
|
|
@@ -870,10 +854,6 @@ export function serve(config, opts = {}) {
|
|
|
870
854
|
const result = serverCapabilities.has(PROJECT_SKILLS_CAPABILITY)
|
|
871
855
|
? await projectSkillsController.handle(msg, connectionEpochToken)
|
|
872
856
|
: { ok: false, error: "capability_unavailable" };
|
|
873
|
-
if (result.ok && (msg.type === "project:remove"
|
|
874
|
-
|| msg.type === "project:rescan")) {
|
|
875
|
-
void projectSkillControllerReadiness.ensure();
|
|
876
|
-
}
|
|
877
857
|
try {
|
|
878
858
|
ws?.send(JSON.stringify({ type: "fs:result", reqId: request.reqId, ...result }));
|
|
879
859
|
}
|
|
@@ -1336,13 +1316,32 @@ export function serve(config, opts = {}) {
|
|
|
1336
1316
|
});
|
|
1337
1317
|
}
|
|
1338
1318
|
const ready = (async () => {
|
|
1339
|
-
executionJournal =
|
|
1340
|
-
|
|
1319
|
+
executionJournal = injectedExecutionJournal ?? createExecutionJournal(config.agentsRoot, {
|
|
1320
|
+
onOperationTiming: (event) => {
|
|
1321
|
+
if (event.operation !== "mark_runtime_ready"
|
|
1322
|
+
&& event.queueWaitMs < 250
|
|
1323
|
+
&& event.operationMs < 250)
|
|
1324
|
+
return;
|
|
1325
|
+
dslog("execution.journal_operation", "execution journal 操作完成", {
|
|
1326
|
+
level: event.outcome === "succeeded" ? "INFO" : "ERROR",
|
|
1327
|
+
execution_id: event.executionId,
|
|
1328
|
+
operation: event.operation,
|
|
1329
|
+
outcome: event.outcome,
|
|
1330
|
+
queue_wait_ms: event.queueWaitMs,
|
|
1331
|
+
operation_ms: event.operationMs,
|
|
1332
|
+
total_ms: event.queueWaitMs + event.operationMs,
|
|
1333
|
+
});
|
|
1334
|
+
},
|
|
1335
|
+
});
|
|
1336
|
+
await reconcileExecutionJournal(executionJournal, {
|
|
1337
|
+
agentsRoot: config.agentsRoot,
|
|
1338
|
+
serverUrl: config.serverUrl,
|
|
1341
1339
|
...(opts.profileName === undefined ? {} : { profileName: opts.profileName }),
|
|
1342
|
-
|
|
1343
|
-
|
|
1344
|
-
|
|
1340
|
+
log: dslog,
|
|
1341
|
+
flush: flushSlog,
|
|
1342
|
+
writeStderr: (line) => process.stderr.write(line),
|
|
1345
1343
|
});
|
|
1344
|
+
await runProjectSkillStartupCleanup(config.agentsRoot, executionJournal, dslog);
|
|
1346
1345
|
await connect();
|
|
1347
1346
|
})();
|
|
1348
1347
|
const stop = () => {
|