@nowcrew/daemon 0.6.98-beta.2 → 0.6.98-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/README.md +41 -1
- package/dist/cloud-report-upload.js +10 -1
- package/dist/computer-main.js +0 -0
- package/dist/computer-setup-client.js +17 -6
- package/dist/computer-setup-orchestration.js +13 -2
- package/dist/computer-setup.js +36 -6
- package/dist/computer-token-setup.js +46 -0
- package/dist/execution-journal.js +1 -3
- package/dist/execution-protocol.js +1 -1
- package/dist/execution-runner.js +1 -4
- package/dist/i18n.js +10 -0
- package/dist/local-execution-prompt.js +3 -8
- package/dist/local-executor.js +39 -63
- package/dist/local-memory/index-budget.js +3 -9
- package/dist/local-memory/inventory-cli.js +8 -52
- package/dist/local-memory/inventory-source.js +8 -15
- package/dist/local-memory/pending-query.js +2 -5
- package/dist/local-memory/pending-schema.js +1 -1
- package/dist/local-memory/pending.js +2 -16
- package/dist/local-memory/prompt.js +5 -21
- package/dist/local-memory/read-cli.js +76 -98
- package/dist/local-memory/rollout.js +11 -79
- package/dist/local-memory/runtime.js +13 -181
- package/dist/local-memory/write.js +21 -189
- package/dist/local-memory-diagnostics.js +13 -94
- package/dist/local-memory-telemetry.js +2 -19
- package/dist/machine-info.js +0 -3
- package/dist/main.js +0 -0
- package/dist/marketplace-protocol.js +1 -1
- package/dist/marketplace-remote.js +1 -1
- package/dist/marketplace-runner.js +1 -1
- package/dist/memory-prune-diagnostics.js +4 -60
- package/dist/message-file-preview.js +1 -11
- package/dist/prompt.js +2 -6
- package/dist/provider-env.js +8 -3
- package/dist/runner.js +1 -1
- package/dist/runtimes/claude-thread-runner.js +4 -25
- package/dist/runtimes/codex-app-server-runner.js +2 -0
- package/dist/runtimes/codex-execution-policy.js +12 -0
- package/dist/runtimes/codex.js +2 -0
- package/dist/serve.js +3 -38
- package/dist/token.js +0 -1
- package/dist/workspace-control-handler.js +3 -16
- package/dist/workspace.js +2 -13
- package/package.json +9 -8
- package/dist/local-memory/commit-telemetry.js +0 -30
- package/dist/local-memory/execution-workspace.js +0 -62
- package/dist/local-memory/full-index-search.js +0 -47
- package/dist/local-memory/initialization-identity.js +0 -36
- package/dist/local-memory/inventory-v2.js +0 -362
- package/dist/local-memory/legacy-adapter.js +0 -233
- package/dist/local-memory/migration-access.js +0 -242
- package/dist/local-memory/migration-cli.js +0 -54
- package/dist/local-memory/migration-io.js +0 -199
- package/dist/local-memory/migration-pending.js +0 -9
- package/dist/local-memory/migration-plan.js +0 -432
- package/dist/local-memory/migration-schema.js +0 -55
- package/dist/local-memory/migration-state-invariants.js +0 -65
- package/dist/local-memory/migration-store.js +0 -414
- package/dist/local-memory/migration-tool-entry.js +0 -21
- package/dist/local-memory/source-path.js +0 -13
- package/dist/local-memory/storage-format.js +0 -152
- package/dist/local-memory/writer-mode.js +0 -13
- package/dist/memory-migration-controller.js +0 -89
- package/dist/memory-migration-inspect.js +0 -108
- package/dist/memory-migration-journal.js +0 -14
- package/dist/memory-migration-protocol.js +0 -14
- package/dist/memory-migration-runtime.js +0 -305
package/README.md
CHANGED
|
@@ -40,7 +40,40 @@ This compatibility form stays resident in the current terminal and reconnects wi
|
|
|
40
40
|
`pnpm --filter @nowcrew/daemon daemon`. `crew-daemon run --agent <handle> --channel <id>` is the legacy
|
|
41
41
|
manual one-shot entry.
|
|
42
42
|
|
|
43
|
-
##
|
|
43
|
+
## Single-Command Computer Setup
|
|
44
|
+
|
|
45
|
+
After the deployment enables Token setup, an owner/admin opens **Add Computer**, copies the complete
|
|
46
|
+
command, and pastes it into the target computer's terminal. The same steps apply on macOS, Linux/SSH,
|
|
47
|
+
and Windows. Node.js 22+ and npm must already be installed. The command launches the installer with npx;
|
|
48
|
+
no separate global installation, browser confirmation, or verification code is needed during execution.
|
|
49
|
+
|
|
50
|
+
The generated command pins an exact published installer version and includes a `--setup-token`.
|
|
51
|
+
This is an already-approved one-time connection request, valid for ten minutes. The program exchanges
|
|
52
|
+
it for the machine credential only when executed, then reuses native service installation, new-hello
|
|
53
|
+
verification, credential finalization and rollback. Merely generating or copying a command does not
|
|
54
|
+
rotate the existing credential. An expired or consumed command must be regenerated in the Web UI.
|
|
55
|
+
|
|
56
|
+
For a machine credential you already hold, the same setup entry accepts `--api-key` or
|
|
57
|
+
`CREW_MACHINE_TOKEN` without browser pairing:
|
|
58
|
+
|
|
59
|
+
```bash
|
|
60
|
+
nowwork-computer setup --server-url https://nowwork.nowcoder.com /nowcoder-app \
|
|
61
|
+
--machine <machine-uuid> --api-key <existing-machine-token>
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
The machine Token must match the specified Workspace and Computer. This path never rotates the
|
|
65
|
+
credential or creates an approval request; it confirms a new control-plane connection with the expected
|
|
66
|
+
profile and paths before reporting success. Token sources cannot be combined. JSON output puts progress
|
|
67
|
+
on stderr and the final result on stdout. On Windows shells that block npm.ps1/npx.ps1, use npx.cmd as
|
|
68
|
+
the package entry; the setup arguments and workflow are unchanged.
|
|
69
|
+
|
|
70
|
+
Deployment requires `CREW_PUBLIC_URL` (or `CREW_SERVER_URL`) and
|
|
71
|
+
`CREW_COMPUTER_TOKEN_SETUP_VERSION` set to an exact, published version known to support the Token flags.
|
|
72
|
+
The server checks its published release catalog and channel before issuing a Token command. Leave the
|
|
73
|
+
version unset until that installer has been released; it returns `computer_token_setup_unavailable`
|
|
74
|
+
instead of generating a command for an unsupported package. Existing clients retain browser setup.
|
|
75
|
+
|
|
76
|
+
## Browser-Approved Computer Setup (Compatibility)
|
|
44
77
|
|
|
45
78
|
The Web UI generates a token-free command for a specific Workspace and Machine:
|
|
46
79
|
|
|
@@ -413,6 +446,13 @@ discovery is unchanged: Claude may still load `~/.claude/skills`, and Codex may
|
|
|
413
446
|
and `~/.agents/skills`. The DeepSeek Codex variant keeps its Agent-private `.codex-deepseek` Home for
|
|
414
447
|
provider credentials. Project bindings do not hide or rewrite any of those sources.
|
|
415
448
|
|
|
449
|
+
Both Codex launchers disable host automatic memories and inherit the execution's prepared environment,
|
|
450
|
+
overriding host `core`, default exclusions, `exclude` and `include_only` filters. These are process-local
|
|
451
|
+
settings: authentication, providers and Skills retain the configured Home, and no other environment
|
|
452
|
+
scope is merged. The policy version in Codex's session fingerprint cold-starts older native sessions
|
|
453
|
+
once, then subsequent runs can resume normally. Explicit `shell_environment_policy.set` entries, shell
|
|
454
|
+
startup scripts and Agent notes remain separate configuration; this change does not correct them.
|
|
455
|
+
|
|
416
456
|
Binding changes reconcile immediately when the Computer is online. On reconnect the server sends the
|
|
417
457
|
complete binding set for every Agent assigned to that Computer, including empty sets that clear stale
|
|
418
458
|
links left by offline unbinds. Every non-empty execution snapshot is also reconciled immediately before
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { existsSync, readFileSync } from "node:fs";
|
|
2
2
|
import { homedir, hostname, platform } from "node:os";
|
|
3
|
+
import { join } from "node:path";
|
|
3
4
|
const ALIYUN_MARKER = /alibaba|aliyun|alicloud/iu;
|
|
4
5
|
function readMarker(path) {
|
|
5
6
|
try {
|
|
@@ -28,7 +29,15 @@ export function detectCloudProvider(env = process.env, platformName = platform()
|
|
|
28
29
|
}
|
|
29
30
|
export function findOssUploadScript(env = process.env, exists = existsSync) {
|
|
30
31
|
const explicit = env.CREW_OSS_UPLOAD_SCRIPT?.trim();
|
|
31
|
-
|
|
32
|
+
if (explicit)
|
|
33
|
+
return exists(explicit) ? explicit : undefined;
|
|
34
|
+
const home = env.HOME?.trim() || homedir();
|
|
35
|
+
const candidates = [
|
|
36
|
+
join(home, ".claude", "skills", "oss-upload", "scripts", "oss_upload.py"),
|
|
37
|
+
join(home, ".codex", "skills", "oss-upload", "scripts", "oss_upload.py"),
|
|
38
|
+
join(home, ".agents", "skills", "oss-upload", "scripts", "oss_upload.py"),
|
|
39
|
+
];
|
|
40
|
+
return candidates.find(exists);
|
|
32
41
|
}
|
|
33
42
|
export function cloudReportUploadFacts(env = process.env, platformName = platform(), hostName = hostname(), readMarkerValue, exists) {
|
|
34
43
|
const provider = detectCloudProvider(env, platformName, hostName, readMarkerValue);
|
package/dist/computer-main.js
CHANGED
|
File without changes
|
|
@@ -39,6 +39,19 @@ const SetupBootstrapSchema = z.object({
|
|
|
39
39
|
expiresAt: z.string().datetime({ offset: true }).optional(),
|
|
40
40
|
machineToken: MachineTokenSchema,
|
|
41
41
|
}).strict();
|
|
42
|
+
const SetupTokenTargetSchema = SetupBootstrapSchema.omit({ setupId: true, machineToken: true }).extend({
|
|
43
|
+
workspaceSlug: z.string().min(1).max(80),
|
|
44
|
+
releaseTag: z.enum(["latest", "beta"]),
|
|
45
|
+
connection: z.object({
|
|
46
|
+
status: z.enum(["online", "offline"]),
|
|
47
|
+
epoch: z.number().int().nonnegative(),
|
|
48
|
+
profile: z.string().nullable(),
|
|
49
|
+
daemonHome: z.string().nullable(),
|
|
50
|
+
agentsRoot: z.string().nullable(),
|
|
51
|
+
storageId: z.string().optional(),
|
|
52
|
+
credentialMatchesConnection: z.boolean(),
|
|
53
|
+
}).strict(),
|
|
54
|
+
}).strict();
|
|
42
55
|
const SetupPendingSchema = z.object({
|
|
43
56
|
status: z.enum(["authorization_pending", "pending"]),
|
|
44
57
|
intervalSeconds: z.number().int().positive().max(300).optional(),
|
|
@@ -237,8 +250,8 @@ export function createComputerSetupClient(options) {
|
|
|
237
250
|
setTimeout(resolve, milliseconds);
|
|
238
251
|
}));
|
|
239
252
|
const now = options.now ?? Date.now;
|
|
240
|
-
let activeMachineId;
|
|
241
253
|
const request = async (method, path, body, bearer) => {
|
|
254
|
+
const secretInputs = [bearer ?? "", body?.deviceCode ?? ""];
|
|
242
255
|
const controller = new AbortController();
|
|
243
256
|
const timeoutId = setTimeout(() => controller.abort(), requestTimeoutMs);
|
|
244
257
|
try {
|
|
@@ -253,7 +266,7 @@ export function createComputerSetupClient(options) {
|
|
|
253
266
|
});
|
|
254
267
|
const payload = await response.json();
|
|
255
268
|
if (!response.ok || payload?.success === false) {
|
|
256
|
-
const message = redactSetupSecrets(payload?.error?.message ?? `computer setup request failed (HTTP ${response.status})
|
|
269
|
+
const message = redactSetupSecrets(payload?.error?.message ?? `computer setup request failed (HTTP ${response.status})`, secretInputs);
|
|
257
270
|
throw new ComputerSetupHttpError(message, response.status, payload?.error?.code ?? `HTTP_${response.status}`);
|
|
258
271
|
}
|
|
259
272
|
return (payload?.success === true ? payload.data : payload);
|
|
@@ -263,15 +276,15 @@ export function createComputerSetupClient(options) {
|
|
|
263
276
|
throw new Error("computer setup request timed out");
|
|
264
277
|
if (error instanceof ComputerSetupHttpError)
|
|
265
278
|
throw error;
|
|
266
|
-
throw new Error(redactSetupSecrets(error instanceof Error ? error.message : String(error)));
|
|
279
|
+
throw new Error(redactSetupSecrets(error instanceof Error ? error.message : String(error), secretInputs));
|
|
267
280
|
}
|
|
268
281
|
finally {
|
|
269
282
|
clearTimeout(timeoutId);
|
|
270
283
|
}
|
|
271
284
|
};
|
|
272
285
|
return {
|
|
286
|
+
tokenTarget: async (machineToken) => SetupTokenTargetSchema.parse(await request("GET", "/daemon/computer-setup/target", undefined, machineToken)),
|
|
273
287
|
start: async (input) => {
|
|
274
|
-
activeMachineId = input.machineId;
|
|
275
288
|
return parseStart(await request("POST", "/api/auth/computer-setup/start", {
|
|
276
289
|
...(input.workspace.kind === "id"
|
|
277
290
|
? { workspaceId: input.workspace.value }
|
|
@@ -282,8 +295,6 @@ export function createComputerSetupClient(options) {
|
|
|
282
295
|
}));
|
|
283
296
|
},
|
|
284
297
|
poll: async (deviceCode) => {
|
|
285
|
-
if (activeMachineId === undefined)
|
|
286
|
-
throw new Error("computer setup polling has no active machine");
|
|
287
298
|
return parsePoll(await request("POST", "/api/auth/computer-setup/poll", { deviceCode }));
|
|
288
299
|
},
|
|
289
300
|
waitForHello: async (input) => {
|
|
@@ -6,7 +6,7 @@ function safeError(error, machineToken) {
|
|
|
6
6
|
const message = redactSetupSecrets(error instanceof Error ? error.message : String(error), machineToken === undefined ? [] : [machineToken]);
|
|
7
7
|
return new Error(message, error instanceof Error ? { cause: error } : undefined);
|
|
8
8
|
}
|
|
9
|
-
function assertBootstrapMatches(bootstrap, input) {
|
|
9
|
+
export function assertBootstrapMatches(bootstrap, input) {
|
|
10
10
|
if (bootstrap.machineId !== input.args.machineId) {
|
|
11
11
|
throw new Error("setup bootstrap machine does not match --machine");
|
|
12
12
|
}
|
|
@@ -40,7 +40,7 @@ function secondsUntil(expiresAt, now) {
|
|
|
40
40
|
const remaining = Date.parse(expiresAt) - now();
|
|
41
41
|
return Math.max(0, Math.ceil(remaining / 1_000));
|
|
42
42
|
}
|
|
43
|
-
function localPathsMatch(left, right, platform) {
|
|
43
|
+
export function localPathsMatch(left, right, platform) {
|
|
44
44
|
const paths = platform === "win32" ? win32 : posix;
|
|
45
45
|
if (!paths.isAbsolute(left) || !paths.isAbsolute(right))
|
|
46
46
|
return false;
|
|
@@ -51,6 +51,14 @@ function localPathsMatch(left, right, platform) {
|
|
|
51
51
|
return normalize(left) === normalize(right);
|
|
52
52
|
}
|
|
53
53
|
async function obtainBootstrap(input, deps) {
|
|
54
|
+
if (input.args.setupToken !== undefined) {
|
|
55
|
+
deps.reportPairing("Checking computer connection information...");
|
|
56
|
+
const result = await deps.client.poll(input.args.setupToken);
|
|
57
|
+
if (result.status !== "authorized") {
|
|
58
|
+
throw new Error("Setup command expired or unavailable; regenerate it in NowWork and run the new command");
|
|
59
|
+
}
|
|
60
|
+
return result.bootstrap;
|
|
61
|
+
}
|
|
54
62
|
const pairing = await deps.client.start({
|
|
55
63
|
workspace: input.args.workspace,
|
|
56
64
|
machineId: input.args.machineId,
|
|
@@ -116,6 +124,7 @@ export async function runComputerSetupOrchestration(input, deps) {
|
|
|
116
124
|
bootstrap = authorized;
|
|
117
125
|
machineToken = token;
|
|
118
126
|
assertBootstrapMatches(authorized, input);
|
|
127
|
+
deps.reportPairing("Downloading, installing, and starting the Daemon service...");
|
|
119
128
|
const installation = await deps.installAutomatically({
|
|
120
129
|
serverUrl: authorized.serverUrl,
|
|
121
130
|
machineToken: token,
|
|
@@ -128,6 +137,7 @@ export async function runComputerSetupOrchestration(input, deps) {
|
|
|
128
137
|
...(authorized.registryUrl === undefined ? {} : { requestedRegistryUrl: authorized.registryUrl }),
|
|
129
138
|
beforeRollback: confirmRollback,
|
|
130
139
|
verifyAfterStart: async (candidate) => {
|
|
140
|
+
deps.reportPairing("Confirming the computer connection...");
|
|
131
141
|
const hello = await deps.client.waitForHello({
|
|
132
142
|
machineToken: token,
|
|
133
143
|
machineId: authorized.machineId,
|
|
@@ -174,6 +184,7 @@ export async function runComputerSetupOrchestration(input, deps) {
|
|
|
174
184
|
});
|
|
175
185
|
return {
|
|
176
186
|
workspaceId: authorized.workspaceId,
|
|
187
|
+
...(authorized.workspaceSlug === undefined ? {} : { workspaceSlug: authorized.workspaceSlug }),
|
|
177
188
|
machineId: authorized.machineId,
|
|
178
189
|
environment: authorized.environment,
|
|
179
190
|
profile: installation.profile,
|
package/dist/computer-setup.js
CHANGED
|
@@ -1,11 +1,13 @@
|
|
|
1
1
|
import { parseArgs } from "node:util";
|
|
2
2
|
import { z } from "zod";
|
|
3
3
|
import { DEFAULT_SERVER_URL } from "./config.js";
|
|
4
|
+
import { MachineTokenSchema } from "./computer-profile.js";
|
|
4
5
|
import { installDaemonAutomatically, } from "./automatic-daemon-installer.js";
|
|
5
6
|
import { createComputerSetupClient, openSetupBrowser, redactSetupSecrets, } from "./computer-setup-client.js";
|
|
6
7
|
import { runComputerSetupOrchestration, } from "./computer-setup-orchestration.js";
|
|
7
8
|
import { detectDaemonLang, formatDaemonText } from "./i18n.js";
|
|
8
9
|
import { systemCommandRunner } from "./computer-service.js";
|
|
10
|
+
import { runMachineTokenSetup } from "./computer-token-setup.js";
|
|
9
11
|
const WorkspaceSlugSchema = z.string()
|
|
10
12
|
.min(2)
|
|
11
13
|
.max(40)
|
|
@@ -70,6 +72,8 @@ export function parseSetupArgs(argv) {
|
|
|
70
72
|
assertRepeatedOptionValuesAgree(argv, "machine", (value) => MachineIdSchema.parse(value).toLowerCase());
|
|
71
73
|
assertRepeatedOptionValuesAgree(argv, "machine-id", (value) => MachineIdSchema.parse(value).toLowerCase());
|
|
72
74
|
assertRepeatedOptionValuesAgree(argv, "server-url", normalizedServerOrigin);
|
|
75
|
+
assertRepeatedOptionValuesAgree(argv, "setup-token", (value) => value);
|
|
76
|
+
assertRepeatedOptionValuesAgree(argv, "api-key", (value) => value);
|
|
73
77
|
const parsed = parseArgs({
|
|
74
78
|
args: [...argv],
|
|
75
79
|
allowPositionals: true,
|
|
@@ -82,6 +86,8 @@ export function parseSetupArgs(argv) {
|
|
|
82
86
|
"server-url": { type: "string" },
|
|
83
87
|
json: { type: "boolean", default: false },
|
|
84
88
|
"no-browser": { type: "boolean", default: false },
|
|
89
|
+
"setup-token": { type: "string" },
|
|
90
|
+
"api-key": { type: "string" },
|
|
85
91
|
help: { type: "boolean", short: "h", default: false },
|
|
86
92
|
},
|
|
87
93
|
});
|
|
@@ -124,6 +130,14 @@ export function parseSetupArgs(argv) {
|
|
|
124
130
|
}
|
|
125
131
|
const machineId = parsedMachine;
|
|
126
132
|
const serverUrl = stringValue(values, "server-url");
|
|
133
|
+
const setupToken = stringValue(values, "setup-token");
|
|
134
|
+
const machineToken = stringValue(values, "api-key");
|
|
135
|
+
if (setupToken !== undefined && machineToken !== undefined)
|
|
136
|
+
throw new Error("Choose one setup credential source");
|
|
137
|
+
if (setupToken !== undefined && !/^[A-Za-z0-9_-]{32,512}$/u.test(setupToken))
|
|
138
|
+
throw new Error("Invalid setup Token");
|
|
139
|
+
if (machineToken !== undefined)
|
|
140
|
+
MachineTokenSchema.parse(machineToken);
|
|
127
141
|
return Object.freeze({
|
|
128
142
|
command: "setup",
|
|
129
143
|
workspace,
|
|
@@ -131,6 +145,8 @@ export function parseSetupArgs(argv) {
|
|
|
131
145
|
...(serverUrl === undefined ? {} : { serverUrl: ServerUrlSchema.parse(serverUrl) }),
|
|
132
146
|
...(booleanValue(values, "json") ? { json: true } : {}),
|
|
133
147
|
...(booleanValue(values, "no-browser") ? { noBrowser: true } : {}),
|
|
148
|
+
...(setupToken === undefined ? {} : { setupToken }),
|
|
149
|
+
...(machineToken === undefined ? {} : { machineToken }),
|
|
134
150
|
});
|
|
135
151
|
}
|
|
136
152
|
function resolveSetupServerUrl(args, env) {
|
|
@@ -148,7 +164,8 @@ export function setupUsage(td = (message) => message) {
|
|
|
148
164
|
" nowwork-computer setup --workspace /workspace --machine <uuid>",
|
|
149
165
|
" nowwork-computer setup --workspace-id <uuid> --machine-id <uuid>",
|
|
150
166
|
"",
|
|
151
|
-
"Options: --server-url <url> --json --no-browser",
|
|
167
|
+
"Options: --server-url <url> --setup-token <token> --api-key <sk_machine_*> --json --no-browser",
|
|
168
|
+
td("Token setup does not require browser authorization. CREW_MACHINE_TOKEN is also supported."),
|
|
152
169
|
].join("\n") + "\n";
|
|
153
170
|
}
|
|
154
171
|
function defaults() {
|
|
@@ -167,6 +184,7 @@ function defaults() {
|
|
|
167
184
|
}
|
|
168
185
|
function humanSummary(result, td) {
|
|
169
186
|
return [
|
|
187
|
+
td("Connected to {{workspace}}.", { workspace: result.workspaceSlug ?? result.workspaceId }),
|
|
170
188
|
td("Workspace: {{value}}", { value: result.workspaceId }),
|
|
171
189
|
td("Machine: {{value}}", { value: result.machineId }),
|
|
172
190
|
td("Environment: {{value}}", { value: result.environment }),
|
|
@@ -205,6 +223,7 @@ export async function runComputerSetupCommand(argv, overrides = {}) {
|
|
|
205
223
|
return null;
|
|
206
224
|
const deps = { ...defaults(), ...overrides };
|
|
207
225
|
const lang = detectDaemonLang(deps.env);
|
|
226
|
+
const secrets = [...optionOccurrences(argv, "setup-token"), ...optionOccurrences(argv, "api-key"), deps.env.CREW_MACHINE_TOKEN ?? ""];
|
|
208
227
|
try {
|
|
209
228
|
if (argv.includes("--help") || argv.includes("-h")) {
|
|
210
229
|
deps.stdout(setupUsage((message, values) => formatDaemonText(lang, message, values)));
|
|
@@ -213,32 +232,43 @@ export async function runComputerSetupCommand(argv, overrides = {}) {
|
|
|
213
232
|
let args;
|
|
214
233
|
try {
|
|
215
234
|
args = parseSetupArgs(argv);
|
|
235
|
+
const envToken = deps.env.CREW_MACHINE_TOKEN?.trim();
|
|
236
|
+
if (envToken) {
|
|
237
|
+
if (args.setupToken !== undefined || (args.machineToken !== undefined && args.machineToken !== envToken)) {
|
|
238
|
+
throw new Error("Choose one setup credential source; clear CREW_MACHINE_TOKEN when using another Token");
|
|
239
|
+
}
|
|
240
|
+
args = Object.freeze({ ...args, machineToken: MachineTokenSchema.parse(envToken) });
|
|
241
|
+
}
|
|
216
242
|
}
|
|
217
243
|
catch (error) {
|
|
218
|
-
deps.stderr(`nowwork-computer: ${redactSetupSecrets(error.message)}\n`);
|
|
244
|
+
deps.stderr(`nowwork-computer: ${formatDaemonText(lang, redactSetupSecrets(error.message, secrets))}\n`);
|
|
219
245
|
return 2;
|
|
220
246
|
}
|
|
221
|
-
if (args.json === true) {
|
|
247
|
+
if (args.json === true && args.setupToken === undefined && args.machineToken === undefined) {
|
|
222
248
|
deps.stderr(`${formatDaemonText(lang, "Starting computer setup pairing...")}\n`);
|
|
223
249
|
}
|
|
224
250
|
const serverUrl = resolveSetupServerUrl(args, deps.env);
|
|
225
251
|
const createClient = overrides.createClient
|
|
226
252
|
?? ((value) => createComputerSetupClient({ serverUrl: value, fetch: deps.fetch }));
|
|
227
|
-
const
|
|
253
|
+
const orchestrationDeps = {
|
|
228
254
|
client: createClient(serverUrl),
|
|
229
255
|
installAutomatically: deps.installAutomatically,
|
|
230
256
|
openBrowser: args.noBrowser === true ? async () => { } : deps.openBrowser,
|
|
231
257
|
sleep: deps.sleep,
|
|
232
258
|
reportPairing: (message) => deps[args.json === true ? "stderr" : "stdout"](`${localizedPairingMessage(lang, message)}\n`),
|
|
233
259
|
now: deps.now,
|
|
234
|
-
|
|
260
|
+
platform: deps.platform,
|
|
261
|
+
};
|
|
262
|
+
const result = args.machineToken === undefined
|
|
263
|
+
? await runComputerSetupOrchestration({ args, serverUrl }, orchestrationDeps)
|
|
264
|
+
: await runMachineTokenSetup({ args, serverUrl }, orchestrationDeps, args.machineToken);
|
|
235
265
|
deps.stdout(args.json === true
|
|
236
266
|
? `${JSON.stringify(result)}\n`
|
|
237
267
|
: humanSummary(result, (message, values) => formatDaemonText(lang, message, values)));
|
|
238
268
|
return 0;
|
|
239
269
|
}
|
|
240
270
|
catch (error) {
|
|
241
|
-
deps.stderr(`nowwork-computer: ${redactSetupSecrets(error.message)}\n`);
|
|
271
|
+
deps.stderr(`nowwork-computer: ${formatDaemonText(lang, redactSetupSecrets(error.message, secrets))}\n`);
|
|
242
272
|
return 1;
|
|
243
273
|
}
|
|
244
274
|
}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import { assertBootstrapMatches, localPathsMatch } from "./computer-setup-orchestration.js";
|
|
2
|
+
import { redactSetupSecrets } from "./computer-setup-client.js";
|
|
3
|
+
export async function runMachineTokenSetup(input, deps, machineToken) {
|
|
4
|
+
try {
|
|
5
|
+
deps.reportPairing("Checking computer connection information...");
|
|
6
|
+
const target = await deps.client.tokenTarget(machineToken);
|
|
7
|
+
assertBootstrapMatches(target, input);
|
|
8
|
+
deps.reportPairing("Downloading, installing, and starting the Daemon service...");
|
|
9
|
+
const installation = await deps.installAutomatically({
|
|
10
|
+
serverUrl: target.serverUrl, machineToken, workspaceId: target.workspaceId, machineId: target.machineId,
|
|
11
|
+
requestedProfile: target.profile,
|
|
12
|
+
...(target.releaseTag === undefined ? {} : { requestedReleaseTag: target.releaseTag }),
|
|
13
|
+
...(target.registryUrl === undefined ? {} : { requestedRegistryUrl: target.registryUrl }),
|
|
14
|
+
verifyAfterStart: async (candidate) => {
|
|
15
|
+
deps.reportPairing("Confirming the computer connection...");
|
|
16
|
+
const deadline = deps.now() + 120_000;
|
|
17
|
+
for (;;) {
|
|
18
|
+
const current = await deps.client.tokenTarget(machineToken);
|
|
19
|
+
assertBootstrapMatches(current, input);
|
|
20
|
+
const connection = current.connection;
|
|
21
|
+
const platform = deps.platform ?? process.platform;
|
|
22
|
+
if (connection.status === "online" && connection.epoch > target.minimumConnectionEpoch
|
|
23
|
+
&& connection.credentialMatchesConnection && current.environment === target.environment
|
|
24
|
+
&& connection.profile === candidate.profile
|
|
25
|
+
&& connection.daemonHome !== null && localPathsMatch(connection.daemonHome, candidate.daemonHome, platform)
|
|
26
|
+
&& connection.agentsRoot !== null && localPathsMatch(connection.agentsRoot, candidate.agentsRoot, platform)
|
|
27
|
+
&& (target.storageId === undefined || connection.storageId === target.storageId))
|
|
28
|
+
return;
|
|
29
|
+
if (deps.now() >= deadline)
|
|
30
|
+
throw new Error("Timed out confirming the new computer connection");
|
|
31
|
+
await deps.sleep(Math.min(2_000, Math.max(0, deadline - deps.now())));
|
|
32
|
+
}
|
|
33
|
+
},
|
|
34
|
+
});
|
|
35
|
+
return {
|
|
36
|
+
workspaceId: target.workspaceId, workspaceSlug: target.workspaceSlug, machineId: target.machineId,
|
|
37
|
+
environment: target.environment, profile: installation.profile, daemonVersion: installation.version,
|
|
38
|
+
serviceId: installation.serviceId, serviceStatus: "running", helloConfirmed: true,
|
|
39
|
+
localNowworkHome: installation.localHome, daemonHome: installation.daemonHome, agentsRoot: installation.agentsRoot,
|
|
40
|
+
...(target.storageId === undefined ? {} : { storageId: target.storageId }),
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
catch (error) {
|
|
44
|
+
throw new Error(redactSetupSecrets(error instanceof Error ? error.message : String(error), [machineToken]));
|
|
45
|
+
}
|
|
46
|
+
}
|
|
@@ -8,7 +8,7 @@ import { provePosixProcessGroupStopped } from "./execution-posix-stop-proof.js";
|
|
|
8
8
|
export { JournalLockedError, JournalLockCorruptionError } from "./execution-journal-lock.js";
|
|
9
9
|
const ExecutionIdSchema = z.string().uuid();
|
|
10
10
|
const TimestampSchema = z.string().datetime({ offset: true });
|
|
11
|
-
const RuntimeSchema = z.enum(["claude", "codex", "kimi", "hermes", "opencode", "deepseek-harness", "
|
|
11
|
+
const RuntimeSchema = z.enum(["claude", "codex", "kimi", "hermes", "opencode", "deepseek-harness", "pi"]);
|
|
12
12
|
const RUNTIME_READY_SUFFIX = ".runtime-ready";
|
|
13
13
|
const RawJournalEntrySchema = z.object({
|
|
14
14
|
executionId: ExecutionIdSchema,
|
|
@@ -523,8 +523,6 @@ export function createExecutionJournal(agentsRoot, options = {}) {
|
|
|
523
523
|
}
|
|
524
524
|
};
|
|
525
525
|
const pruneInternal = async () => {
|
|
526
|
-
if (options.retainCompleted)
|
|
527
|
-
return;
|
|
528
526
|
const protectedExecutionIds = new Set(options.protectedExecutionIds?.() ?? []);
|
|
529
527
|
const entries = await readAll();
|
|
530
528
|
const acknowledged = entries
|
|
@@ -319,7 +319,7 @@ const RawExecutionCompletedSchema = z.object({
|
|
|
319
319
|
providerErrorCategory: ProviderErrorCategorySchema.optional(),
|
|
320
320
|
providerHttpStatus: ProviderHttpStatusSchema.optional(),
|
|
321
321
|
providerErrorCode: ProviderErrorCodeSchema.optional(),
|
|
322
|
-
runtime:
|
|
322
|
+
runtime: RuntimeSchema,
|
|
323
323
|
model: z.string().optional(),
|
|
324
324
|
resumed: z.boolean(),
|
|
325
325
|
finalText: z.string().optional(),
|
package/dist/execution-runner.js
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import { createHash } from "node:crypto";
|
|
2
2
|
import { dirname, join, resolve } from "node:path";
|
|
3
3
|
import { isLocalMemoryV2Enabled, localMemoryV2Environment, resolveReposRoot } from "./config.js";
|
|
4
|
-
import { detectLocalMemoryStorageVersion } from "./local-memory/storage-format.js";
|
|
5
4
|
import { DaemonToServerExecutionFrameSchema, ExecutionCompletedSchema, ExecutionRejectedSchema, ExecutionStartSchema, } from "./execution-protocol.js";
|
|
6
5
|
import { JournalConflictError } from "./execution-journal.js";
|
|
7
6
|
import { boundExecutionFrame } from "./execution-event-limit.js";
|
|
@@ -839,9 +838,8 @@ export async function runExecution(config, input, dependencies) {
|
|
|
839
838
|
const localMemoryGuardInBase = localMemoryV2Environment(config.serverUrl) === "custom";
|
|
840
839
|
const boundedSystemPrompt = (context) => {
|
|
841
840
|
const enabled = context.workspace.localMemoryV2Enabled ?? localMemoryV2Enabled;
|
|
842
|
-
const systemPromptWithLocalFacts = withLocalExecutionFacts(`${spec.instructions.systemPrompt}${enabled && localMemoryGuardInBase
|
|
841
|
+
const systemPromptWithLocalFacts = withLocalExecutionFacts(`${spec.instructions.systemPrompt}${enabled && localMemoryGuardInBase ? renderLocalMemoryV2WriteGuard() : ""}`, baseSystemPromptBudget, {
|
|
843
842
|
...(selfTraining ? { isolateLocalMemory: true } : {}),
|
|
844
|
-
memoryQuery: spec.instructions.wakePrompt,
|
|
845
843
|
collaborationTools: config.codexCollaborationTools && spec.runtime.name === "codex",
|
|
846
844
|
});
|
|
847
845
|
const base = typeof systemPromptWithLocalFacts === "string"
|
|
@@ -881,7 +879,6 @@ export async function runExecution(config, input, dependencies) {
|
|
|
881
879
|
serverUrl: config.serverUrl,
|
|
882
880
|
token: credential.token,
|
|
883
881
|
agentId: credential.agentId,
|
|
884
|
-
...(dependencies.controlIdentity === undefined ? {} : dependencies.controlIdentity),
|
|
885
882
|
agentsRoot: config.agentsRoot,
|
|
886
883
|
reposRoot: resolveReposRoot(config.agentsRoot, config.reposRoot),
|
|
887
884
|
...(config.qaReportRoot === undefined ? {} : { qaReportRoot: config.qaReportRoot }),
|
package/dist/i18n.js
CHANGED
|
@@ -36,6 +36,16 @@ const zh = {
|
|
|
36
36
|
"Code {{code}}": "验证码 {{code}}",
|
|
37
37
|
"Browser open skipped: {{reason}}": "未能自动打开浏览器:{{reason}}",
|
|
38
38
|
"Starting computer setup pairing...": "正在发起电脑接入授权…",
|
|
39
|
+
"Token setup does not require browser authorization. CREW_MACHINE_TOKEN is also supported.": "Token 接入无需浏览器授权,也支持 CREW_MACHINE_TOKEN。",
|
|
40
|
+
"Checking computer connection information...": "正在检查接入信息…",
|
|
41
|
+
"Downloading, installing, and starting the Daemon service...": "正在下载、安装并启动 Daemon 服务…",
|
|
42
|
+
"Confirming the computer connection...": "正在确认电脑连接…",
|
|
43
|
+
"Connected to {{workspace}}.": "已接入 {{workspace}}。",
|
|
44
|
+
"Setup command expired or unavailable; regenerate it in NowWork and run the new command": "接入命令已过期或不可用,请在 NowWork 重新生成并执行新命令。",
|
|
45
|
+
"Timed out confirming the new computer connection": "确认新连接超时,请检查网络和服务状态。",
|
|
46
|
+
"Invalid setup Token": "接入 Token 无效。",
|
|
47
|
+
"Choose one setup credential source": "请只使用一种接入凭据。",
|
|
48
|
+
"Choose one setup credential source; clear CREW_MACHINE_TOKEN when using another Token": "请只使用一种接入凭据;使用其他 Token 时请清除 CREW_MACHINE_TOKEN。",
|
|
39
49
|
"Workspace: {{value}}": "工作区:{{value}}",
|
|
40
50
|
"Machine: {{value}}": "机器: {{value}}",
|
|
41
51
|
"Environment: {{value}}": "环境:{{value}}",
|
|
@@ -2,9 +2,8 @@
|
|
|
2
2
|
* 本机执行提示词的合成与预算裁剪。
|
|
3
3
|
*
|
|
4
4
|
* server 提示词是权威部分,绝不裁剪;项目工作区提示词是绑定执行的强制部分,放不下就直接失败;
|
|
5
|
-
*
|
|
5
|
+
* 只有本机事实(CREW_HOME / work-log / MEMORY 注入)是可选增强,按整段丢弃或按字节截断。
|
|
6
6
|
*/
|
|
7
|
-
import { detectLocalMemoryStorageVersion } from "./local-memory/storage-format.js";
|
|
8
7
|
import { buildCollaborationToolsPrompt, buildRuntimeWorkspacePrompt, capMemoryForInject, capWorkLogForInject, } from "./prompt.js";
|
|
9
8
|
const trainingProjectJson = (project) => JSON.stringify({
|
|
10
9
|
repositoryKey: project.repositoryKey,
|
|
@@ -58,14 +57,10 @@ export function withLocalExecutionFacts(serverPrompt, maxBytes, options = {}) {
|
|
|
58
57
|
}
|
|
59
58
|
const remaining = maxBytes - requiredBytes - Math.max(0, reservedPromptBytes);
|
|
60
59
|
const factsBytes = Buffer.byteLength(localFacts, "utf8");
|
|
61
|
-
if (factsBytes > remaining)
|
|
62
|
-
if (!options.isolateLocalMemory && detectLocalMemoryStorageVersion(workspace.memory) === 2) {
|
|
63
|
-
throw new Error("local_memory_identity_budget_exceeded");
|
|
64
|
-
}
|
|
60
|
+
if (factsBytes > remaining)
|
|
65
61
|
return `${serverPrompt}${collaborationPrompt}${mandatoryWorkspacePrompt}`;
|
|
66
|
-
}
|
|
67
62
|
const memoryHeading = "\n\n## Injected MEMORY.md (bounded local context)\n";
|
|
68
|
-
const memory = options.isolateLocalMemory ? "" : capMemoryForInject(workspace.memory, undefined, remaining - factsBytes - Buffer.byteLength(memoryHeading, "utf8")
|
|
63
|
+
const memory = options.isolateLocalMemory ? "" : capMemoryForInject(workspace.memory, undefined, remaining - factsBytes - Buffer.byteLength(memoryHeading, "utf8"));
|
|
69
64
|
const memorySection = memory === "" ? "" : memoryHeading + memory;
|
|
70
65
|
const workLogSection = workLog === "" ? "" : `\n\n## Injected work-log (bounded local context)\n${workLog}`;
|
|
71
66
|
const workLogBudget = remaining - factsBytes - Buffer.byteLength(memorySection, "utf8");
|