@nowcrew/daemon 0.6.89 → 0.6.90
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 +64 -3
- package/dist/agent-ability/materializer.js +21 -10
- package/dist/agent-environment-contract.js +55 -0
- package/dist/agent-environment-control.js +24 -0
- package/dist/agent-environment-execution.js +98 -0
- package/dist/agent-environments.js +275 -0
- package/dist/automatic-daemon-installer.js +49 -1
- package/dist/computer-main.js +18 -0
- package/dist/computer-profile.js +17 -4
- package/dist/computer-setup-client.js +318 -0
- package/dist/computer-setup-orchestration.js +203 -0
- package/dist/computer-setup.js +244 -0
- package/dist/config.js +30 -12
- package/dist/console-formatter.js +3 -3
- package/dist/console.js +15 -2
- package/dist/daemon-update-eligibility.js +1 -1
- package/dist/execution-journal.js +9 -6
- package/dist/execution-protocol.js +1 -0
- package/dist/execution-runner.js +13 -8
- package/dist/i18n.js +19 -0
- package/dist/list-models.js +12 -12
- package/dist/local-execution-lease.js +22 -0
- package/dist/local-executor.js +57 -84
- package/dist/local-memory/candidate.js +43 -0
- package/dist/local-memory/index-budget.js +8 -0
- package/dist/local-memory/inventory-cli.js +61 -0
- package/dist/local-memory/inventory-preview.js +159 -0
- package/dist/local-memory/inventory-source.js +159 -0
- package/dist/local-memory/pending-match.js +29 -0
- package/dist/local-memory/pending-query.js +100 -0
- package/dist/local-memory/pending-schema.js +29 -0
- package/dist/local-memory/pending-source.js +126 -0
- package/dist/local-memory/pending.js +34 -84
- package/dist/local-memory/prompt.js +19 -12
- package/dist/local-memory/read-cli.js +97 -12
- package/dist/local-memory/rollout.js +82 -0
- package/dist/local-memory/runtime.js +23 -17
- package/dist/local-memory/work-log-execution.js +31 -0
- package/dist/local-memory/work-log.js +58 -0
- package/dist/local-memory/write.js +27 -58
- package/dist/local-runtime-environment.js +50 -0
- package/dist/machine-environment-cache.js +147 -0
- package/dist/machine-environment-contract.js +10 -0
- package/dist/machine-environment-control.js +27 -0
- package/dist/machine-environment-policy.js +75 -0
- package/dist/machine-environment-process.js +110 -0
- package/dist/machine-info.js +70 -25
- package/dist/machine-runtime-environment.js +189 -0
- package/dist/machine-shell-environment-capture.js +135 -0
- package/dist/machine-windows-environment-capture.js +215 -0
- package/dist/machine-windows-environment-script.js +158 -0
- package/dist/main.js +4 -69
- package/dist/marketplace-command.js +21 -0
- package/dist/marketplace-controller.js +80 -0
- package/dist/marketplace-protocol.js +38 -0
- package/dist/marketplace-remote.js +48 -0
- package/dist/marketplace-runner.js +235 -0
- package/dist/marketplace-serve.js +17 -0
- package/dist/marketplace-source.js +103 -0
- package/dist/message-file-preview.js +246 -0
- package/dist/pre-admission-rejection.js +64 -0
- package/dist/project-skills/execution-adapter.js +3 -2
- package/dist/project-skills/runtime-launch.js +14 -0
- package/dist/provider-env.js +26 -15
- package/dist/runner.js +17 -4
- package/dist/runtime-environment-merge.js +16 -0
- package/dist/runtime-health-handler.js +10 -0
- package/dist/runtime-health.js +3 -1
- package/dist/runtime-path.js +22 -12
- package/dist/runtime-probe.js +35 -6
- package/dist/runtimes/acp-probe-transport.js +69 -0
- package/dist/runtimes/claude-models.js +20 -9
- package/dist/runtimes/deepseek-harness-launch.js +10 -0
- package/dist/runtimes/deepseek-harness-profile.js +92 -0
- package/dist/runtimes/deepseek-harness-runner.js +226 -0
- package/dist/runtimes/deepseek-harness.js +76 -15
- package/dist/runtimes/harness-probe-retry.js +42 -0
- package/dist/runtimes/hermes-models.js +2 -1
- package/dist/runtimes/kimi-acp-runner.js +78 -13
- package/dist/runtimes/opencode.js +2 -2
- package/dist/serve.js +88 -176
- package/dist/storage-identity.js +0 -3
- package/dist/storage-serve-adapter.js +6 -72
- package/dist/supervised-runtime.js +17 -2
- package/dist/token.js +0 -1
- package/dist/workspace-control-handler.js +27 -0
- package/dist/workspace-fs.js +66 -15
- package/dist/workspace.js +1 -1
- package/package.json +13 -12
- package/dist/codex-home-launch.js +0 -7
- package/dist/codex-home-mode-controller.js +0 -68
- package/dist/daemon-migration-controller.js +0 -211
- package/dist/daemon-migration-wiring.js +0 -22
- package/dist/runtimes/codex-home-migration-cli.js +0 -26
- package/dist/runtimes/codex-home-migration.js +0 -112
- package/dist/runtimes/codex-home.js +0 -236
- 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 -241
- 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 -447
- package/dist/storage-worker-handoff.js +0 -151
- package/dist/storage-worker-operation.js +0 -294
- package/dist/storage-worker-status.js +0 -259
|
@@ -1,381 +0,0 @@
|
|
|
1
|
-
import { createHash } from "node:crypto";
|
|
2
|
-
import { dirname, join } from "node:path";
|
|
3
|
-
import { z } from "zod";
|
|
4
|
-
import { buildServiceSpec } from "./computer-service.js";
|
|
5
|
-
import { buildManagedServiceRecord } from "./managed-service-lifecycle.js";
|
|
6
|
-
import { createStorageControlController } from "./storage-control.js";
|
|
7
|
-
import { runStorageDoctor } from "./storage-doctor.js";
|
|
8
|
-
import { storageHelloIdentity, storageWorkspaceRootFingerprint, } from "./storage-identity.js";
|
|
9
|
-
import { storageLayout, validateStorageEnvironment } from "./storage-layout.js";
|
|
10
|
-
import { StorageMigrationFailure } from "./storage-migration-controller.js";
|
|
11
|
-
import { launchStorageMigrationWorker, writeStorageWorkerHandoff } from "./storage-worker-handoff.js";
|
|
12
|
-
import { writeStorageWorkerOperation } from "./storage-worker-operation.js";
|
|
13
|
-
import { latestSourceStorageMigrationReceipt, monitorStorageMigrationStatus, storageMigrationStatusReplay, } from "./storage-status-replay.js";
|
|
14
|
-
import { resolveWorkspaceStorageOwnershipClaim, } from "./storage-ownership.js";
|
|
15
|
-
const UUID = z.string().uuid();
|
|
16
|
-
function fingerprint(value) {
|
|
17
|
-
return `sha256:${createHash("sha256").update(value).digest("hex")}`;
|
|
18
|
-
}
|
|
19
|
-
function deterministicUuid(value) {
|
|
20
|
-
const chars = createHash("sha256").update(value).digest("hex").slice(0, 32).split("");
|
|
21
|
-
chars[12] = "4";
|
|
22
|
-
chars[16] = ((Number.parseInt(chars[16], 16) & 0x3) | 0x8).toString(16);
|
|
23
|
-
const hex = chars.join("");
|
|
24
|
-
return `${hex.slice(0, 8)}-${hex.slice(8, 12)}-${hex.slice(12, 16)}-${hex.slice(16, 20)}-${hex.slice(20)}`;
|
|
25
|
-
}
|
|
26
|
-
function storageId(input, workspaceId, ownership) {
|
|
27
|
-
if (ownership.workspaceId !== workspaceId || ownership.environment !== input.environment) {
|
|
28
|
-
throw new Error("Storage ownership claim does not match the migration identity");
|
|
29
|
-
}
|
|
30
|
-
return `storage_${ownership.claimFingerprint.slice("sha256:".length)}`;
|
|
31
|
-
}
|
|
32
|
-
function installationBase(input, workspaceId, migrationId, createdAt, ownership) {
|
|
33
|
-
const layout = storageLayout({
|
|
34
|
-
nowworkHome: input.nowworkHome,
|
|
35
|
-
environment: input.environment,
|
|
36
|
-
workspaceId,
|
|
37
|
-
runtimeBase: input.local.runtimeBase,
|
|
38
|
-
platform: input.platform,
|
|
39
|
-
});
|
|
40
|
-
return {
|
|
41
|
-
version: 1,
|
|
42
|
-
installationId: deterministicUuid(`installation\0${input.serverUrl}\0${input.profile}\0${workspaceId}\0${input.nowworkHome}`),
|
|
43
|
-
profile: input.profile,
|
|
44
|
-
serverUrl: input.serverUrl,
|
|
45
|
-
environment: input.environment,
|
|
46
|
-
workspaceId,
|
|
47
|
-
storageId: storageId(input, workspaceId, ownership),
|
|
48
|
-
nowworkHome: input.nowworkHome,
|
|
49
|
-
runtimeRoot: layout.runtimeRoot,
|
|
50
|
-
credentialRef: `profile:${input.profile}`,
|
|
51
|
-
...(migrationId === undefined ? {} : { storageMigrationId: migrationId }),
|
|
52
|
-
createdAt,
|
|
53
|
-
updatedAt: createdAt,
|
|
54
|
-
};
|
|
55
|
-
}
|
|
56
|
-
function targetRecord(input, workspaceId, createdAt) {
|
|
57
|
-
const layout = storageLayout({
|
|
58
|
-
nowworkHome: input.nowworkHome,
|
|
59
|
-
environment: input.environment,
|
|
60
|
-
workspaceId,
|
|
61
|
-
runtimeBase: input.local.runtimeBase,
|
|
62
|
-
platform: input.platform,
|
|
63
|
-
});
|
|
64
|
-
const spec = buildServiceSpec({
|
|
65
|
-
platform: input.platform,
|
|
66
|
-
profile: input.profile,
|
|
67
|
-
userHome: input.userHome,
|
|
68
|
-
uid: input.uid,
|
|
69
|
-
nodePath: input.sourceRecord.nodePath,
|
|
70
|
-
entryPath: input.sourceRecord.entryPath,
|
|
71
|
-
profileHome: input.local.configRoot,
|
|
72
|
-
...(input.serviceEnvironment === undefined ? {} : { environment: input.serviceEnvironment }),
|
|
73
|
-
});
|
|
74
|
-
return buildManagedServiceRecord({
|
|
75
|
-
spec,
|
|
76
|
-
daemonHome: input.local.configRoot,
|
|
77
|
-
agentsRoot: layout.agentItemsRoot,
|
|
78
|
-
nodePath: input.sourceRecord.nodePath,
|
|
79
|
-
entryPath: input.sourceRecord.entryPath,
|
|
80
|
-
packageRoot: input.sourceRecord.packageRoot,
|
|
81
|
-
npmPrefix: input.sourceRecord.npmPrefix,
|
|
82
|
-
generation: () => deterministicUuid(`service\0${spec.id}\0${workspaceId}`),
|
|
83
|
-
now: () => new Date(createdAt),
|
|
84
|
-
});
|
|
85
|
-
}
|
|
86
|
-
export function createStorageMigrationRuntimeController(rawInput, injected = {}) {
|
|
87
|
-
const input = {
|
|
88
|
-
...rawInput,
|
|
89
|
-
profile: z.string().regex(/^[a-z0-9][a-z0-9_-]{0,47}$/u).parse(rawInput.profile),
|
|
90
|
-
serverUrl: z.string().url().parse(rawInput.serverUrl),
|
|
91
|
-
environment: validateStorageEnvironment(rawInput.environment),
|
|
92
|
-
};
|
|
93
|
-
if (input.sourceRecord.profile !== input.profile || input.sourceRecord.platform !== input.platform) {
|
|
94
|
-
throw new Error("Storage runtime source service identity does not match profile");
|
|
95
|
-
}
|
|
96
|
-
const dependencies = {
|
|
97
|
-
assertSourceIdentity: async () => undefined,
|
|
98
|
-
runDoctor: (doctorInput) => runStorageDoctor(doctorInput),
|
|
99
|
-
writeOperation: writeStorageWorkerOperation,
|
|
100
|
-
writeHandoff: writeStorageWorkerHandoff,
|
|
101
|
-
launchWorker: launchStorageMigrationWorker,
|
|
102
|
-
monitorWorker: monitorStorageMigrationStatus,
|
|
103
|
-
resolveOwnership: resolveWorkspaceStorageOwnershipClaim,
|
|
104
|
-
now: () => new Date(),
|
|
105
|
-
...injected,
|
|
106
|
-
};
|
|
107
|
-
let workspaceId = null;
|
|
108
|
-
let machineId = null;
|
|
109
|
-
let identity;
|
|
110
|
-
let gate = null;
|
|
111
|
-
let sendStatus = null;
|
|
112
|
-
let control = null;
|
|
113
|
-
let ownership = input.ownership;
|
|
114
|
-
let identityBindTail = Promise.resolve();
|
|
115
|
-
const pendingControlMessages = [];
|
|
116
|
-
let drainingPendingControlMessages = false;
|
|
117
|
-
const drainPendingControlMessages = () => {
|
|
118
|
-
if (control === null || drainingPendingControlMessages || pendingControlMessages.length === 0)
|
|
119
|
-
return;
|
|
120
|
-
drainingPendingControlMessages = true;
|
|
121
|
-
const pending = pendingControlMessages.splice(0);
|
|
122
|
-
void (async () => {
|
|
123
|
-
for (const message of pending)
|
|
124
|
-
await control?.handle(message);
|
|
125
|
-
})().catch(() => undefined).finally(() => {
|
|
126
|
-
drainingPendingControlMessages = false;
|
|
127
|
-
drainPendingControlMessages();
|
|
128
|
-
});
|
|
129
|
-
};
|
|
130
|
-
const doctorInput = async (migrationId) => {
|
|
131
|
-
if (workspaceId === null || gate === null)
|
|
132
|
-
throw new Error("Storage migration runtime is not attached");
|
|
133
|
-
let profileIdentityExact = true;
|
|
134
|
-
await dependencies.assertSourceIdentity().catch(() => { profileIdentityExact = false; });
|
|
135
|
-
const target = storageLayout({
|
|
136
|
-
nowworkHome: input.nowworkHome,
|
|
137
|
-
environment: input.environment,
|
|
138
|
-
workspaceId,
|
|
139
|
-
runtimeBase: input.local.runtimeBase,
|
|
140
|
-
platform: input.platform,
|
|
141
|
-
});
|
|
142
|
-
return {
|
|
143
|
-
migrationId,
|
|
144
|
-
generatedAt: dependencies.now().toISOString(),
|
|
145
|
-
environment: input.environment,
|
|
146
|
-
workspaceId,
|
|
147
|
-
profile: input.profile,
|
|
148
|
-
targetWorkspaceRoot: target.workspaceRoot,
|
|
149
|
-
sources: [
|
|
150
|
-
{ kind: "daemon-home", root: input.sourceRecord.daemonHome, logicalPrefix: "daemon" },
|
|
151
|
-
{ kind: "agents-root", root: input.sourceRecord.agentsRoot, logicalPrefix: "agent-items" },
|
|
152
|
-
],
|
|
153
|
-
policy: input.policy,
|
|
154
|
-
manifestStoreRoot: input.local.stateRoot,
|
|
155
|
-
state: {
|
|
156
|
-
profileIdentityExact,
|
|
157
|
-
duplicateWorkspaceMappings: input.duplicateWorkspaceMappings ?? 0,
|
|
158
|
-
activeAgentExecutions: gate.isBusy() ? 1 : 0,
|
|
159
|
-
activeScheduledRuns: 0,
|
|
160
|
-
},
|
|
161
|
-
freeSpaceMultiplier: 1.2,
|
|
162
|
-
};
|
|
163
|
-
};
|
|
164
|
-
const buildOperation = (doctor, freshInput) => {
|
|
165
|
-
if (workspaceId === null)
|
|
166
|
-
throw new Error("Storage migration workspace identity is missing");
|
|
167
|
-
const createdAt = freshInput.generatedAt;
|
|
168
|
-
const target = storageLayout({
|
|
169
|
-
nowworkHome: input.nowworkHome,
|
|
170
|
-
environment: input.environment,
|
|
171
|
-
workspaceId,
|
|
172
|
-
runtimeBase: input.local.runtimeBase,
|
|
173
|
-
platform: input.platform,
|
|
174
|
-
});
|
|
175
|
-
const { freeSpaceMultiplier, ...doctorBase } = freshInput;
|
|
176
|
-
const operationDoctorInput = {
|
|
177
|
-
...doctorBase,
|
|
178
|
-
sources: freshInput.sources.map((source) => ({ ...source })),
|
|
179
|
-
policy: {
|
|
180
|
-
exclusions: freshInput.policy.exclusions.map((exclusion) => ({ ...exclusion })),
|
|
181
|
-
transformations: freshInput.policy.transformations.map((transformation) => ({ ...transformation })),
|
|
182
|
-
},
|
|
183
|
-
...(freeSpaceMultiplier === undefined ? {} : { freeSpaceMultiplier }),
|
|
184
|
-
};
|
|
185
|
-
return {
|
|
186
|
-
version: 1,
|
|
187
|
-
migrationId: doctor.manifest.migrationId,
|
|
188
|
-
createdAt,
|
|
189
|
-
environment: input.environment,
|
|
190
|
-
workspaceId,
|
|
191
|
-
profile: input.profile,
|
|
192
|
-
expectedManifestDigest: doctor.manifestDigest,
|
|
193
|
-
doctorInput: operationDoctorInput,
|
|
194
|
-
migration: {
|
|
195
|
-
sourceRoots: {
|
|
196
|
-
"daemon-home": input.sourceRecord.daemonHome,
|
|
197
|
-
"agents-root": input.sourceRecord.agentsRoot,
|
|
198
|
-
},
|
|
199
|
-
stagingWorkspaceRoot: join(dirname(target.workspaceRoot), `.nowwork-staging-${doctor.manifest.migrationId}`),
|
|
200
|
-
targetWorkspaceRoot: target.workspaceRoot,
|
|
201
|
-
backupsRoot: input.local.backupsRoot,
|
|
202
|
-
backupArtifacts: input.backupArtifacts.map((artifact) => ({ ...artifact })),
|
|
203
|
-
ownership: {
|
|
204
|
-
ownerEpoch: ownership.ownerEpoch,
|
|
205
|
-
ownershipClaimFingerprint: ownership.claimFingerprint,
|
|
206
|
-
},
|
|
207
|
-
acceptedWarnings: [...input.policy.acceptedWarnings],
|
|
208
|
-
},
|
|
209
|
-
service: {
|
|
210
|
-
platform: input.platform,
|
|
211
|
-
userHome: input.userHome,
|
|
212
|
-
...(input.uid === undefined ? {} : { uid: input.uid }),
|
|
213
|
-
...(input.serviceEnvironment === undefined ? {} : { serviceEnvironment: input.serviceEnvironment }),
|
|
214
|
-
sourceWasRunning: true,
|
|
215
|
-
sourceRecord: input.sourceRecord,
|
|
216
|
-
targetRecord: targetRecord(input, workspaceId, createdAt),
|
|
217
|
-
},
|
|
218
|
-
installation: {
|
|
219
|
-
configRoot: input.local.configRoot,
|
|
220
|
-
target: {
|
|
221
|
-
...installationBase(input, workspaceId, doctor.manifest.migrationId, createdAt, ownership),
|
|
222
|
-
storageMigrationId: doctor.manifest.migrationId,
|
|
223
|
-
},
|
|
224
|
-
},
|
|
225
|
-
};
|
|
226
|
-
};
|
|
227
|
-
const ensureControl = () => {
|
|
228
|
-
if (control !== null || workspaceId === null || gate === null || sendStatus === null || identity === undefined)
|
|
229
|
-
return;
|
|
230
|
-
const sourceStorageFingerprint = fingerprint(JSON.stringify({
|
|
231
|
-
profile: input.profile,
|
|
232
|
-
daemonHome: input.sourceRecord.daemonHome,
|
|
233
|
-
agentsRoot: input.sourceRecord.agentsRoot,
|
|
234
|
-
serviceId: input.sourceRecord.serviceId,
|
|
235
|
-
}));
|
|
236
|
-
control = createStorageControlController({
|
|
237
|
-
environment: input.environment,
|
|
238
|
-
workspaceId,
|
|
239
|
-
profile: input.profile,
|
|
240
|
-
sourceStorageFingerprint,
|
|
241
|
-
targetStorageFingerprint: identity.storageIdFingerprint,
|
|
242
|
-
ownership: {
|
|
243
|
-
ownerEpoch: ownership.ownerEpoch,
|
|
244
|
-
ownershipClaimFingerprint: ownership.claimFingerprint,
|
|
245
|
-
},
|
|
246
|
-
runDoctor: async (migrationId) => dependencies.runDoctor(await doctorInput(migrationId)),
|
|
247
|
-
runMigration: async (doctor) => {
|
|
248
|
-
await gate.closeAndFlush();
|
|
249
|
-
let workerLaunched = false;
|
|
250
|
-
try {
|
|
251
|
-
const freshInput = await doctorInput(doctor.manifest.migrationId);
|
|
252
|
-
const freshDoctor = await dependencies.runDoctor(freshInput);
|
|
253
|
-
if (!freshDoctor.applyAllowed || freshDoctor.manifestDigest !== doctor.manifestDigest) {
|
|
254
|
-
throw new StorageMigrationFailure("source_changed_after_preflight", "source data or doctor gates changed after server preflight");
|
|
255
|
-
}
|
|
256
|
-
const operation = buildOperation(freshDoctor, freshInput);
|
|
257
|
-
const written = await dependencies.writeOperation(operation, { stateRoot: input.local.stateRoot });
|
|
258
|
-
const handoffPath = await dependencies.writeHandoff({
|
|
259
|
-
version: 1,
|
|
260
|
-
migrationId: operation.migrationId,
|
|
261
|
-
profile: operation.profile,
|
|
262
|
-
operationPath: written.path,
|
|
263
|
-
operationSha256: written.sha256,
|
|
264
|
-
}, { stateRoot: input.local.stateRoot });
|
|
265
|
-
await dependencies.launchWorker({
|
|
266
|
-
entryPath: input.sourceRecord.entryPath,
|
|
267
|
-
handoffPath,
|
|
268
|
-
migrationId: operation.migrationId,
|
|
269
|
-
platform: input.platform,
|
|
270
|
-
userHome: input.userHome,
|
|
271
|
-
...(input.uid === undefined ? {} : { uid: input.uid }),
|
|
272
|
-
});
|
|
273
|
-
workerLaunched = true;
|
|
274
|
-
const outcome = await dependencies.monitorWorker({
|
|
275
|
-
stateRoot: input.local.stateRoot,
|
|
276
|
-
backupsRoot: input.local.backupsRoot,
|
|
277
|
-
migrationId: operation.migrationId,
|
|
278
|
-
environment: operation.environment,
|
|
279
|
-
workspaceId: operation.workspaceId,
|
|
280
|
-
profile: operation.profile,
|
|
281
|
-
...(operation.migration.ownership === undefined ? {} : operation.migration.ownership),
|
|
282
|
-
}, sendStatus);
|
|
283
|
-
if (outcome.reopenAdmission)
|
|
284
|
-
gate.reopen();
|
|
285
|
-
}
|
|
286
|
-
catch (error) {
|
|
287
|
-
if (!workerLaunched) {
|
|
288
|
-
gate.reopen();
|
|
289
|
-
const failure = error instanceof Error ? error : new Error(String(error));
|
|
290
|
-
throw Object.assign(new Error(failure.message, { cause: failure }), {
|
|
291
|
-
code: error.code,
|
|
292
|
-
reopenAdmission: true,
|
|
293
|
-
});
|
|
294
|
-
}
|
|
295
|
-
throw error;
|
|
296
|
-
}
|
|
297
|
-
},
|
|
298
|
-
sendStatus,
|
|
299
|
-
});
|
|
300
|
-
};
|
|
301
|
-
const bindServerIdentityOnce = async (server) => {
|
|
302
|
-
const nextWorkspaceId = UUID.parse(server.workspaceId).toLowerCase();
|
|
303
|
-
const nextMachineId = UUID.parse(server.machineId).toLowerCase();
|
|
304
|
-
if (workspaceId !== null && workspaceId !== nextWorkspaceId) {
|
|
305
|
-
throw new Error("Storage migration server workspace identity changed");
|
|
306
|
-
}
|
|
307
|
-
if (machineId !== null && machineId !== nextMachineId) {
|
|
308
|
-
throw new Error("Storage migration server machine identity changed");
|
|
309
|
-
}
|
|
310
|
-
const layout = storageLayout({
|
|
311
|
-
nowworkHome: input.nowworkHome,
|
|
312
|
-
environment: input.environment,
|
|
313
|
-
workspaceId: nextWorkspaceId,
|
|
314
|
-
runtimeBase: input.local.runtimeBase,
|
|
315
|
-
platform: input.platform,
|
|
316
|
-
});
|
|
317
|
-
if (ownership.workspaceId !== nextWorkspaceId
|
|
318
|
-
|| ownership.machineId !== nextMachineId
|
|
319
|
-
|| ownership.environment !== input.environment) {
|
|
320
|
-
ownership = await dependencies.resolveOwnership({
|
|
321
|
-
nowworkHome: input.nowworkHome,
|
|
322
|
-
machineId: nextMachineId,
|
|
323
|
-
workspaceId: nextWorkspaceId,
|
|
324
|
-
environment: input.environment,
|
|
325
|
-
workspaceRootFingerprint: storageWorkspaceRootFingerprint(layout.workspaceRoot),
|
|
326
|
-
});
|
|
327
|
-
}
|
|
328
|
-
if (ownership.workspaceId !== nextWorkspaceId
|
|
329
|
-
|| ownership.machineId !== nextMachineId
|
|
330
|
-
|| ownership.environment !== input.environment) {
|
|
331
|
-
throw new Error("Storage ownership claim does not match authenticated server identity");
|
|
332
|
-
}
|
|
333
|
-
const createdAt = dependencies.now().toISOString();
|
|
334
|
-
const installation = installationBase(input, nextWorkspaceId, undefined, createdAt, ownership);
|
|
335
|
-
const nextIdentity = storageHelloIdentity({ installation, layout, ownership });
|
|
336
|
-
workspaceId = nextWorkspaceId;
|
|
337
|
-
machineId = nextMachineId;
|
|
338
|
-
identity = nextIdentity;
|
|
339
|
-
ensureControl();
|
|
340
|
-
drainPendingControlMessages();
|
|
341
|
-
};
|
|
342
|
-
const bindServerIdentity = (server) => {
|
|
343
|
-
const current = identityBindTail.then(() => bindServerIdentityOnce(server));
|
|
344
|
-
identityBindTail = current.catch(() => undefined);
|
|
345
|
-
return current;
|
|
346
|
-
};
|
|
347
|
-
const attach = (runtime) => {
|
|
348
|
-
gate = runtime.gate;
|
|
349
|
-
sendStatus = runtime.sendStatus;
|
|
350
|
-
ensureControl();
|
|
351
|
-
drainPendingControlMessages();
|
|
352
|
-
};
|
|
353
|
-
const replayStatus = async (send) => {
|
|
354
|
-
const receipt = await latestSourceStorageMigrationReceipt(input.local.backupsRoot, {
|
|
355
|
-
environment: input.environment,
|
|
356
|
-
profile: input.profile,
|
|
357
|
-
});
|
|
358
|
-
if (receipt === null)
|
|
359
|
-
return;
|
|
360
|
-
for (const frame of storageMigrationStatusReplay(receipt))
|
|
361
|
-
send(frame);
|
|
362
|
-
};
|
|
363
|
-
return Object.freeze({
|
|
364
|
-
statusOutboxPath: join(input.local.stateRoot, "storage-status-outbox.json"),
|
|
365
|
-
identity: () => identity,
|
|
366
|
-
bindServerIdentity,
|
|
367
|
-
attach,
|
|
368
|
-
replayStatus,
|
|
369
|
-
controller: Object.freeze({
|
|
370
|
-
handle: (raw) => {
|
|
371
|
-
if (control !== null && !drainingPendingControlMessages)
|
|
372
|
-
return control.handle(raw);
|
|
373
|
-
if (pendingControlMessages.length >= 16)
|
|
374
|
-
return Promise.resolve(false);
|
|
375
|
-
pendingControlMessages.push(raw);
|
|
376
|
-
drainPendingControlMessages();
|
|
377
|
-
return Promise.resolve(true);
|
|
378
|
-
},
|
|
379
|
-
}),
|
|
380
|
-
});
|
|
381
|
-
}
|
|
@@ -1,241 +0,0 @@
|
|
|
1
|
-
import { realpathSync } from "node:fs";
|
|
2
|
-
import { lstat } from "node:fs/promises";
|
|
3
|
-
import { isAbsolute, resolve } from "node:path";
|
|
4
|
-
import { z } from "zod";
|
|
5
|
-
import { buildServiceSpec } from "./computer-service.js";
|
|
6
|
-
import { profilePath } from "./computer-profile.js";
|
|
7
|
-
import { assertManagedServiceIdentity } from "./managed-service-lifecycle.js";
|
|
8
|
-
import { managedServiceRegistryPath, readManagedServiceRegistry, } from "./managed-service-registry.js";
|
|
9
|
-
import { installationsPath, resolveLocalStorageRoots } from "./storage-config.js";
|
|
10
|
-
import { readStorageMigrationPolicy } from "./storage-migration-policy.js";
|
|
11
|
-
import { createStorageMigrationRuntimeController, } from "./storage-runtime-controller.js";
|
|
12
|
-
import { resolveEnvTag, resolveNowworkHome } from "./storage-layout.js";
|
|
13
|
-
import { readStorageOwnershipClaim } from "./storage-ownership.js";
|
|
14
|
-
import { parseDaemonLocalHomeMode, resolveDaemonLocalStateRoot } from "./daemon-local-home.js";
|
|
15
|
-
import { isProductionServerHostname } from "./server-url.js";
|
|
16
|
-
const notReady = (reason) => Object.freeze({ ready: false, reason });
|
|
17
|
-
function canonicalPath(value) {
|
|
18
|
-
const absolute = resolve(value);
|
|
19
|
-
try {
|
|
20
|
-
return realpathSync.native(absolute);
|
|
21
|
-
}
|
|
22
|
-
catch (error) {
|
|
23
|
-
const code = error.code;
|
|
24
|
-
if (code === "ENOTDIR")
|
|
25
|
-
return absolute;
|
|
26
|
-
if (code !== "ENOENT")
|
|
27
|
-
throw error;
|
|
28
|
-
}
|
|
29
|
-
const missing = [];
|
|
30
|
-
let ancestor = absolute;
|
|
31
|
-
for (;;) {
|
|
32
|
-
const parent = resolve(ancestor, "..");
|
|
33
|
-
if (parent === ancestor)
|
|
34
|
-
return absolute;
|
|
35
|
-
missing.unshift(ancestor.slice(parent.length + (parent.endsWith("/") ? 0 : 1)));
|
|
36
|
-
ancestor = parent;
|
|
37
|
-
try {
|
|
38
|
-
return resolve(realpathSync.native(ancestor), ...missing);
|
|
39
|
-
}
|
|
40
|
-
catch (error) {
|
|
41
|
-
const code = error.code;
|
|
42
|
-
if (code === "ENOTDIR")
|
|
43
|
-
return absolute;
|
|
44
|
-
if (code !== "ENOENT")
|
|
45
|
-
throw error;
|
|
46
|
-
}
|
|
47
|
-
}
|
|
48
|
-
}
|
|
49
|
-
const samePath = (left, right) => canonicalPath(left) === canonicalPath(right);
|
|
50
|
-
function environmentMatchesServer(serverUrl, environment) {
|
|
51
|
-
const hostname = new URL(serverUrl).hostname.toLowerCase();
|
|
52
|
-
if (hostname === "nowwork-dev.nowcoder.com")
|
|
53
|
-
return environment === "dev";
|
|
54
|
-
if (isProductionServerHostname(hostname))
|
|
55
|
-
return environment === "production";
|
|
56
|
-
return true;
|
|
57
|
-
}
|
|
58
|
-
function findSourceRecord(records, input) {
|
|
59
|
-
const matches = records.filter((record) => record.profile === input.profile
|
|
60
|
-
&& samePath(record.daemonHome, input.currentDaemonHome)
|
|
61
|
-
&& samePath(record.agentsRoot, input.currentAgentsRoot));
|
|
62
|
-
return matches.length === 1 ? matches[0] : null;
|
|
63
|
-
}
|
|
64
|
-
export async function probeStorageMigrationRuntime(rawInput, injected = {}) {
|
|
65
|
-
if (rawInput.platform !== "darwin" && rawInput.platform !== "linux") {
|
|
66
|
-
return notReady("platform_not_supported");
|
|
67
|
-
}
|
|
68
|
-
if (rawInput.platform === "darwin" && rawInput.uid === undefined) {
|
|
69
|
-
return notReady("platform_identity_missing");
|
|
70
|
-
}
|
|
71
|
-
const configuredNowworkHome = rawInput.environment.NOWWORK_HOME;
|
|
72
|
-
if (configuredNowworkHome === undefined)
|
|
73
|
-
return notReady("nowwork_home_missing");
|
|
74
|
-
const input = {
|
|
75
|
-
...rawInput,
|
|
76
|
-
profile: z.string().regex(/^[a-z0-9][a-z0-9_-]{0,47}$/u).parse(rawInput.profile),
|
|
77
|
-
serverUrl: z.string().url().parse(rawInput.serverUrl),
|
|
78
|
-
platform: rawInput.platform,
|
|
79
|
-
};
|
|
80
|
-
let sourceLocalHome;
|
|
81
|
-
let targetLocalHome;
|
|
82
|
-
let targetEnvironment;
|
|
83
|
-
let nowworkHome;
|
|
84
|
-
let environment;
|
|
85
|
-
let local;
|
|
86
|
-
try {
|
|
87
|
-
sourceLocalHome = resolveDaemonLocalStateRoot({
|
|
88
|
-
userHome: rawInput.userHome,
|
|
89
|
-
platform: rawInput.platform,
|
|
90
|
-
profile: input.profile,
|
|
91
|
-
daemonHome: input.currentDaemonHome,
|
|
92
|
-
});
|
|
93
|
-
const requestedLocalHomeMode = parseDaemonLocalHomeMode(rawInput.environment.NOWWORK_LOCAL_HOME_MODE ?? rawInput.environment.NOWWORK_STORAGE_MODE);
|
|
94
|
-
if (requestedLocalHomeMode === "legacy")
|
|
95
|
-
throw new Error("storage migration target must use the canonical local .nowwork root");
|
|
96
|
-
targetLocalHome = resolveDaemonLocalStateRoot({
|
|
97
|
-
userHome: rawInput.userHome,
|
|
98
|
-
platform: rawInput.platform,
|
|
99
|
-
explicit: rawInput.environment.NOWWORK_LOCAL_HOME ?? resolve(rawInput.userHome, ".nowwork"),
|
|
100
|
-
mode: "nowwork",
|
|
101
|
-
});
|
|
102
|
-
targetEnvironment = {
|
|
103
|
-
...input.environment,
|
|
104
|
-
NOWWORK_LOCAL_HOME: targetLocalHome,
|
|
105
|
-
NOWWORK_LOCAL_HOME_MODE: "nowwork",
|
|
106
|
-
};
|
|
107
|
-
const home = resolveNowworkHome({
|
|
108
|
-
environment: configuredNowworkHome,
|
|
109
|
-
userHome: input.userHome,
|
|
110
|
-
platform: input.platform,
|
|
111
|
-
});
|
|
112
|
-
if (home.mode !== "standard")
|
|
113
|
-
return notReady("nowwork_home_missing");
|
|
114
|
-
nowworkHome = home.root;
|
|
115
|
-
environment = resolveEnvTag({
|
|
116
|
-
...(input.environment.NOWWORK_ENV_TAG === undefined
|
|
117
|
-
? {}
|
|
118
|
-
: { environment: input.environment.NOWWORK_ENV_TAG }),
|
|
119
|
-
}).environment;
|
|
120
|
-
local = resolveLocalStorageRoots({
|
|
121
|
-
platform: input.platform,
|
|
122
|
-
userHome: input.userHome,
|
|
123
|
-
environment: targetEnvironment,
|
|
124
|
-
});
|
|
125
|
-
}
|
|
126
|
-
catch {
|
|
127
|
-
return notReady("storage_environment_invalid");
|
|
128
|
-
}
|
|
129
|
-
if (!environmentMatchesServer(input.serverUrl, environment)) {
|
|
130
|
-
return notReady("environment_server_mismatch");
|
|
131
|
-
}
|
|
132
|
-
const policyPath = input.environment.NOWWORK_STORAGE_POLICY_PATH;
|
|
133
|
-
if (policyPath === undefined)
|
|
134
|
-
return notReady("migration_policy_missing");
|
|
135
|
-
if (!isAbsolute(policyPath) || /[\0\r\n]/u.test(policyPath))
|
|
136
|
-
return notReady("migration_policy_invalid");
|
|
137
|
-
const dependencies = {
|
|
138
|
-
readManagedServices: readManagedServiceRegistry,
|
|
139
|
-
assertManagedServiceIdentity,
|
|
140
|
-
readPolicy: readStorageMigrationPolicy,
|
|
141
|
-
createController: createStorageMigrationRuntimeController,
|
|
142
|
-
readOwnership: readStorageOwnershipClaim,
|
|
143
|
-
...injected,
|
|
144
|
-
};
|
|
145
|
-
let sourceRecord;
|
|
146
|
-
try {
|
|
147
|
-
const records = await dependencies.readManagedServices({ userHome: input.userHome, localHome: sourceLocalHome });
|
|
148
|
-
const found = findSourceRecord(records, input);
|
|
149
|
-
if (found === null)
|
|
150
|
-
return notReady("source_service_record_missing");
|
|
151
|
-
sourceRecord = found;
|
|
152
|
-
}
|
|
153
|
-
catch {
|
|
154
|
-
return notReady("source_service_registry_invalid");
|
|
155
|
-
}
|
|
156
|
-
const sourceSpec = buildServiceSpec({
|
|
157
|
-
platform: input.platform,
|
|
158
|
-
profile: input.profile,
|
|
159
|
-
userHome: input.userHome,
|
|
160
|
-
uid: input.uid,
|
|
161
|
-
nodePath: sourceRecord.nodePath,
|
|
162
|
-
entryPath: sourceRecord.entryPath,
|
|
163
|
-
profileHome: input.currentDaemonHome,
|
|
164
|
-
environment: input.environment,
|
|
165
|
-
});
|
|
166
|
-
const assertSourceIdentity = () => dependencies.assertManagedServiceIdentity({
|
|
167
|
-
spec: sourceSpec,
|
|
168
|
-
record: sourceRecord,
|
|
169
|
-
registry: { userHome: input.userHome, localHome: sourceLocalHome },
|
|
170
|
-
});
|
|
171
|
-
try {
|
|
172
|
-
await assertSourceIdentity();
|
|
173
|
-
}
|
|
174
|
-
catch {
|
|
175
|
-
return notReady("source_service_identity_drift");
|
|
176
|
-
}
|
|
177
|
-
let policy;
|
|
178
|
-
try {
|
|
179
|
-
policy = await dependencies.readPolicy(policyPath, {
|
|
180
|
-
profile: input.profile,
|
|
181
|
-
environment,
|
|
182
|
-
nowworkHome,
|
|
183
|
-
sourceDaemonHome: sourceRecord.daemonHome,
|
|
184
|
-
sourceAgentsRoot: sourceRecord.agentsRoot,
|
|
185
|
-
});
|
|
186
|
-
}
|
|
187
|
-
catch {
|
|
188
|
-
return notReady("migration_policy_invalid");
|
|
189
|
-
}
|
|
190
|
-
let ownership;
|
|
191
|
-
try {
|
|
192
|
-
ownership = await dependencies.readOwnership(nowworkHome);
|
|
193
|
-
}
|
|
194
|
-
catch (error) {
|
|
195
|
-
return notReady(error.code === "ENOENT"
|
|
196
|
-
? "storage_ownership_claim_missing"
|
|
197
|
-
: "storage_ownership_claim_invalid");
|
|
198
|
-
}
|
|
199
|
-
if (ownership.environment !== environment)
|
|
200
|
-
return notReady("storage_ownership_claim_invalid");
|
|
201
|
-
const targetInstallationsPath = installationsPath(local.configRoot);
|
|
202
|
-
let targetInstallationsArtifact = null;
|
|
203
|
-
try {
|
|
204
|
-
const targetInstallations = await lstat(targetInstallationsPath);
|
|
205
|
-
if (!targetInstallations.isFile())
|
|
206
|
-
return notReady("target_installations_invalid");
|
|
207
|
-
targetInstallationsArtifact = { name: "installations.json", path: targetInstallationsPath };
|
|
208
|
-
}
|
|
209
|
-
catch (error) {
|
|
210
|
-
if (error.code !== "ENOENT")
|
|
211
|
-
return notReady("target_installations_invalid");
|
|
212
|
-
}
|
|
213
|
-
const runtime = dependencies.createController({
|
|
214
|
-
profile: input.profile,
|
|
215
|
-
serverUrl: input.serverUrl,
|
|
216
|
-
environment,
|
|
217
|
-
nowworkHome,
|
|
218
|
-
userHome: input.userHome,
|
|
219
|
-
platform: input.platform,
|
|
220
|
-
...(input.uid === undefined ? {} : { uid: input.uid }),
|
|
221
|
-
sourceRecord,
|
|
222
|
-
local,
|
|
223
|
-
policy,
|
|
224
|
-
ownership,
|
|
225
|
-
serviceEnvironment: {
|
|
226
|
-
NOWWORK_HOME: nowworkHome,
|
|
227
|
-
NOWWORK_ENV_TAG: environment,
|
|
228
|
-
NOWWORK_STORAGE_POLICY_PATH: policyPath,
|
|
229
|
-
...(targetEnvironment.NOWWORK_LOCAL_HOME === undefined ? {} : { NOWWORK_LOCAL_HOME: targetEnvironment.NOWWORK_LOCAL_HOME }),
|
|
230
|
-
...(targetEnvironment.NOWWORK_LOCAL_HOME_MODE === undefined
|
|
231
|
-
? {} : { NOWWORK_LOCAL_HOME_MODE: targetEnvironment.NOWWORK_LOCAL_HOME_MODE }),
|
|
232
|
-
},
|
|
233
|
-
backupArtifacts: [
|
|
234
|
-
{ name: "profile.json", path: profilePath(input.profile, input.currentDaemonHome) },
|
|
235
|
-
{ name: "service-descriptor", path: sourceRecord.descriptorPath },
|
|
236
|
-
{ name: "managed-service-registry.json", path: managedServiceRegistryPath(input.userHome, sourceLocalHome) },
|
|
237
|
-
...(targetInstallationsArtifact === null ? [] : [targetInstallationsArtifact]),
|
|
238
|
-
],
|
|
239
|
-
}, { assertSourceIdentity });
|
|
240
|
-
return Object.freeze({ ready: true, runtime });
|
|
241
|
-
}
|