evolcore 0.0.23 → 0.0.24
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/CHANGELOG.md +50 -0
- package/README.md +3 -1
- package/bin/codex-managed-acl.ps1 +7 -2
- package/bin/codex-managed-hook.mjs +127 -10
- package/bin/ec-safe-output.js +85 -3
- package/bin/install-codex-managed-hooks.mjs +17 -6
- package/dist/agents/baseagent-availability.js +4 -4
- package/dist/agents/baseagent.js +29 -1
- package/dist/agents/claude-runner.js +148 -13
- package/dist/agents/codex/app-server-client.js +18 -8
- package/dist/agents/codex/catalog.js +1 -1
- package/dist/agents/codex/diagnostics.js +215 -0
- package/dist/agents/codex-runner.js +438 -59
- package/dist/agents/ecagent-runner.js +17 -3
- package/dist/agents/gemini-runner.js +60 -2
- package/dist/agents/runner-types.js +1 -1
- package/dist/aun/aid/managed-operation.js +28 -6
- package/dist/aun/aid/store.js +41 -0
- package/dist/aun/msg/managed-operation.js +6 -1
- package/dist/aun/outbox.js +56 -18
- package/dist/aun/rpc/client.js +4 -5
- package/dist/channels/aun.js +147 -76
- package/dist/channels/daemon.js +7 -1
- package/dist/channels/dingtalk.js +1 -0
- package/dist/channels/feishu.js +10 -4
- package/dist/channels/wecom.js +2 -1
- package/dist/cli/agent-command.js +29 -3
- package/dist/cli/agent-create.js +538 -0
- package/dist/cli/agent.js +26 -675
- package/dist/cli/aun-commands.js +60 -20
- package/dist/cli/ctl-command.js +2 -0
- package/dist/cli/daemon-commands.js +148 -17
- package/dist/cli/index.js +2 -2
- package/dist/cli/managed-command-guard.js +3 -0
- package/dist/cli/restart-monitor.js +432 -8
- package/dist/cli/stats.js +110 -0
- package/dist/config/builtin-roles.js +2 -0
- package/dist/core/auth/auth-gateway.js +3 -2
- package/dist/core/auth/authorization-audit.js +72 -19
- package/dist/core/auth/operation-authorizer.js +63 -3
- package/dist/core/auth/operation-catalog.js +10 -2
- package/dist/core/auth/remote-access-store.js +3 -2
- package/dist/core/baseagent-loader.js +4 -4
- package/dist/core/bootstrap-messages.js +39 -18
- package/dist/core/bootstrap-service.js +32 -26
- package/dist/core/capability/capability-manager.js +8 -4
- package/dist/core/capability/providers/codex-capability-provider.js +1 -1
- package/dist/core/command/agent-control.js +7 -2
- package/dist/core/command/command-handler.js +87 -25
- package/dist/core/command/menu-catalog.js +3 -1
- package/dist/core/command/menu-handler.js +274 -70
- package/dist/core/command/menu-protocol.js +6 -2
- package/dist/core/command/slash-gate.js +1 -3
- package/dist/core/command/slash-handler.js +157 -48
- package/dist/core/data-migration.js +5 -15
- package/dist/core/event-catalog.js +3 -0
- package/dist/core/handoff/runtime.js +3 -1
- package/dist/core/handoff/store.js +7 -1
- package/dist/core/message/im-renderer.js +13 -6
- package/dist/core/message/message-bridge.js +5 -36
- package/dist/core/message/message-utils.js +6 -3
- package/dist/core/message/response-engine.js +73 -21
- package/dist/core/model/catalog-service.js +3 -1
- package/dist/core/model/model-catalog.js +10 -0
- package/dist/core/model/model-permission.js +4 -1
- package/dist/core/permission/approval-gateway.js +169 -33
- package/dist/core/permission/tool-policy.js +13 -6
- package/dist/core/session/adapters/codex-session-file-adapter.js +71 -3
- package/dist/core/session/session-manager.js +49 -13
- package/dist/core/session/session-renew.js +1 -0
- package/dist/index.js +174 -28
- package/dist/ipc.js +68 -21
- package/dist/paths.js +23 -1
- package/dist/stats/billing.js +18 -4
- package/dist/utils/atomic-write.js +26 -2
- package/dist/utils/cross-platform.js +436 -1
- package/dist/utils/error-utils.js +11 -4
- package/dist/utils/model-prices.jsonl +23 -20
- package/dist/utils/npm-ops.js +24 -1
- package/dist/utils/windows-autostart.js +1 -1
- package/kits/templates/roles/admin.json +2 -0
- package/package.json +7 -3
- package/AGENTS.md +0 -62
- package/dist/utils/codex-cli.js +0 -43
|
@@ -1,12 +1,197 @@
|
|
|
1
1
|
import path from 'path';
|
|
2
|
-
import { fileURLToPath } from 'url';
|
|
2
|
+
import { fileURLToPath, pathToFileURL } from 'url';
|
|
3
|
+
import { createRequire } from 'module';
|
|
3
4
|
import { execFileSync, execFile, spawn, spawnSync } from 'child_process';
|
|
4
5
|
import { promisify } from 'util';
|
|
5
6
|
import fs from 'fs';
|
|
6
7
|
import { getProcessStartTime, parseCimDate } from './process-introspect.js';
|
|
7
8
|
import { decodeWindowsOutput } from './windows-output.js';
|
|
8
9
|
const execFileAsync = promisify(execFile);
|
|
10
|
+
const CLI_PROBE_FAILURE_BACKOFF_MS = 5_000;
|
|
11
|
+
const CLI_PROBE_PACKAGES = {
|
|
12
|
+
codex: '@openai/codex',
|
|
13
|
+
claude: '@anthropic-ai/claude-code',
|
|
14
|
+
gemini: '@google/gemini-cli',
|
|
15
|
+
};
|
|
16
|
+
const cliPathCache = new Map();
|
|
17
|
+
const cliInstallationCache = new Map();
|
|
18
|
+
const cliProbeCache = new Map();
|
|
9
19
|
export const isWindows = process.platform === 'win32';
|
|
20
|
+
/** Resolve a CLI executable without spawning where/which. */
|
|
21
|
+
export function resolveProbePath(command) {
|
|
22
|
+
const pathKey = `${command}\0${process.cwd()}\0${process.env.PATH ?? ''}\0${process.env.PATHEXT ?? ''}`;
|
|
23
|
+
const cachedPath = cliPathCache.get(pathKey);
|
|
24
|
+
if (cachedPath && fs.existsSync(cachedPath))
|
|
25
|
+
return cachedPath;
|
|
26
|
+
cliPathCache.delete(pathKey);
|
|
27
|
+
const directories = (process.env.PATH ?? '').split(path.delimiter);
|
|
28
|
+
if (isWindows)
|
|
29
|
+
directories.unshift(process.cwd());
|
|
30
|
+
const extensions = isWindows
|
|
31
|
+
? (process.env.PATHEXT ?? '.COM;.EXE;.BAT;.CMD').split(';').filter(ext => /\.(com|exe|bat|cmd)$/i.test(ext))
|
|
32
|
+
: [''];
|
|
33
|
+
for (const directory of directories) {
|
|
34
|
+
for (const extension of extensions) {
|
|
35
|
+
const candidate = path.resolve(directory.replace(/^"|"$/g, ''), command + extension);
|
|
36
|
+
try {
|
|
37
|
+
if (!fs.statSync(candidate).isFile())
|
|
38
|
+
continue;
|
|
39
|
+
fs.accessSync(candidate, isWindows ? fs.constants.F_OK : fs.constants.X_OK);
|
|
40
|
+
cliPathCache.set(pathKey, candidate);
|
|
41
|
+
return candidate;
|
|
42
|
+
}
|
|
43
|
+
catch { /* Continue PATH lookup. */ }
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
return null;
|
|
47
|
+
}
|
|
48
|
+
function cliFileIdentity(file) {
|
|
49
|
+
try {
|
|
50
|
+
const real = fs.realpathSync(file);
|
|
51
|
+
const stat = fs.statSync(real);
|
|
52
|
+
return `${real}:${stat.ino}:${stat.size}:${stat.mtimeMs}:${stat.ctimeMs}`;
|
|
53
|
+
}
|
|
54
|
+
catch {
|
|
55
|
+
return `${file}:missing`;
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
/** Return an installation fingerprint so probes refresh after CLI upgrades. */
|
|
59
|
+
export function probeInstallationIdentity(command, executable) {
|
|
60
|
+
const cacheKey = `${command}\0${executable}`;
|
|
61
|
+
const cached = cliInstallationCache.get(cacheKey);
|
|
62
|
+
if (cached && cached.files.map(cliFileIdentity).join('|') === cached.identity)
|
|
63
|
+
return cached.identity;
|
|
64
|
+
const files = [executable];
|
|
65
|
+
const packageName = CLI_PROBE_PACKAGES[command];
|
|
66
|
+
if (packageName) {
|
|
67
|
+
files.push(path.join(path.dirname(executable), 'node_modules', packageName, 'package.json'));
|
|
68
|
+
try {
|
|
69
|
+
const real = fs.realpathSync(executable);
|
|
70
|
+
const require = createRequire(real);
|
|
71
|
+
const packageFile = require.resolve(`${packageName}/package.json`);
|
|
72
|
+
files.push(packageFile);
|
|
73
|
+
if (command === 'codex') {
|
|
74
|
+
const platformPackage = `@openai/codex-${process.platform}-${process.arch}`;
|
|
75
|
+
const platformFile = createRequire(packageFile).resolve(`${platformPackage}/package.json`);
|
|
76
|
+
files.push(platformFile);
|
|
77
|
+
const arch = process.arch === 'x64' ? 'x86_64' : 'aarch64';
|
|
78
|
+
const target = isWindows ? `${arch}-pc-windows-msvc`
|
|
79
|
+
: process.platform === 'darwin' ? `${arch}-apple-darwin` : `${arch}-unknown-linux-musl`;
|
|
80
|
+
for (const root of [path.dirname(platformFile), path.dirname(packageFile)]) {
|
|
81
|
+
files.push(path.join(root, 'vendor', target, 'bin', isWindows ? 'codex.exe' : 'codex'));
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
catch { /* Standalone executables have no npm package. */ }
|
|
86
|
+
}
|
|
87
|
+
const uniqueFiles = [...new Set(files)];
|
|
88
|
+
const identity = uniqueFiles.map(cliFileIdentity).join('|');
|
|
89
|
+
cliInstallationCache.set(cacheKey, { files: uniqueFiles, identity });
|
|
90
|
+
return identity;
|
|
91
|
+
}
|
|
92
|
+
/** Clear CLI probe caches after an in-process CLI installation. */
|
|
93
|
+
export function invalidateCliProbes() {
|
|
94
|
+
cliPathCache.clear();
|
|
95
|
+
cliInstallationCache.clear();
|
|
96
|
+
cliProbeCache.clear();
|
|
97
|
+
}
|
|
98
|
+
function cliProbeTimeouts(command, options) {
|
|
99
|
+
return options.timeouts?.length ? options.timeouts : command === 'codex' ? [3_000, 15_000] : [3_000];
|
|
100
|
+
}
|
|
101
|
+
function prepareCliProbe(command, args) {
|
|
102
|
+
const executable = resolveProbePath(command);
|
|
103
|
+
if (!executable)
|
|
104
|
+
throw Object.assign(new Error(`${command} CLI not found`), { code: 'ENOENT' });
|
|
105
|
+
const identity = `${probeInstallationIdentity(command, executable)}|${process.env.ComSpec ?? ''}`;
|
|
106
|
+
const key = JSON.stringify([command, args]);
|
|
107
|
+
let entry = cliProbeCache.get(key);
|
|
108
|
+
if (!entry || entry.identity !== identity || (entry.retryAt !== undefined && Date.now() >= entry.retryAt)) {
|
|
109
|
+
entry = { identity };
|
|
110
|
+
cliProbeCache.set(key, entry);
|
|
111
|
+
}
|
|
112
|
+
const shim = isWindows && /\.(cmd|bat)$/i.test(executable);
|
|
113
|
+
return { entry, command: shim ? process.env.ComSpec ?? 'cmd.exe' : executable,
|
|
114
|
+
args: shim ? ['/d', '/s', '/c', executable, ...args] : args };
|
|
115
|
+
}
|
|
116
|
+
function failCliProbe(entry, error) {
|
|
117
|
+
entry.error = error;
|
|
118
|
+
entry.retryAt = Date.now() + CLI_PROBE_FAILURE_BACKOFF_MS;
|
|
119
|
+
throw error;
|
|
120
|
+
}
|
|
121
|
+
export function probeCliSync(command, args, options = {}) {
|
|
122
|
+
const request = prepareCliProbe(command, args);
|
|
123
|
+
if (request.entry.output !== undefined)
|
|
124
|
+
return request.entry.output;
|
|
125
|
+
if (request.entry.error !== undefined)
|
|
126
|
+
throw request.entry.error;
|
|
127
|
+
for (const [index, timeout] of cliProbeTimeouts(command, options).entries()) {
|
|
128
|
+
const startedAt = Date.now();
|
|
129
|
+
try {
|
|
130
|
+
return request.entry.output = execFileSync(request.command, request.args, { encoding: 'utf8', timeout, windowsHide: true, stdio: ['ignore', 'pipe', 'pipe'] });
|
|
131
|
+
}
|
|
132
|
+
catch (error) {
|
|
133
|
+
options.onFailure?.(args, startedAt, error, timeout);
|
|
134
|
+
if (error.code !== 'ETIMEDOUT' || index === cliProbeTimeouts(command, options).length - 1)
|
|
135
|
+
return failCliProbe(request.entry, error);
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
throw new Error('CLI probe exhausted');
|
|
139
|
+
}
|
|
140
|
+
export async function probeCli(command, args, options = {}) {
|
|
141
|
+
const request = prepareCliProbe(command, args);
|
|
142
|
+
if (request.entry.output !== undefined)
|
|
143
|
+
return request.entry.output;
|
|
144
|
+
if (request.entry.error !== undefined)
|
|
145
|
+
throw request.entry.error;
|
|
146
|
+
if (request.entry.pending)
|
|
147
|
+
return request.entry.pending;
|
|
148
|
+
const run = async () => {
|
|
149
|
+
const timeouts = cliProbeTimeouts(command, options);
|
|
150
|
+
for (const [index, timeout] of timeouts.entries()) {
|
|
151
|
+
const startedAt = Date.now();
|
|
152
|
+
try {
|
|
153
|
+
const output = await new Promise((resolve, reject) => {
|
|
154
|
+
const child = execFile(request.command, request.args, { encoding: 'utf8', timeout, windowsHide: true }, (error, stdout, stderr) => {
|
|
155
|
+
if (error) {
|
|
156
|
+
if (error.killed && error.signal === 'SIGTERM' && error.code == null)
|
|
157
|
+
error.code = 'ETIMEDOUT';
|
|
158
|
+
Object.assign(error, { stdout, stderr, path: request.command, spawnargs: request.args });
|
|
159
|
+
reject(error);
|
|
160
|
+
}
|
|
161
|
+
else
|
|
162
|
+
resolve(stdout);
|
|
163
|
+
});
|
|
164
|
+
child.stdin?.end();
|
|
165
|
+
});
|
|
166
|
+
return request.entry.output = output;
|
|
167
|
+
}
|
|
168
|
+
catch (error) {
|
|
169
|
+
options.onFailure?.(args, startedAt, error, timeout);
|
|
170
|
+
if (error.code !== 'ETIMEDOUT' || index === timeouts.length - 1)
|
|
171
|
+
return failCliProbe(request.entry, error);
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
throw new Error('CLI probe exhausted');
|
|
175
|
+
};
|
|
176
|
+
request.entry.pending = run();
|
|
177
|
+
try {
|
|
178
|
+
return await request.entry.pending;
|
|
179
|
+
}
|
|
180
|
+
finally {
|
|
181
|
+
request.entry.pending = undefined;
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
/** Non-blocking version lookup used by status menus. */
|
|
185
|
+
export function cachedCliVersion(command) {
|
|
186
|
+
try {
|
|
187
|
+
const { entry } = prepareCliProbe(command, ['--version']);
|
|
188
|
+
if (entry.output !== undefined)
|
|
189
|
+
return entry.output.match(/\b\d+\.\d+\.\d+(?:[-+][\w.-]+)?/)?.[0] ?? null;
|
|
190
|
+
void probeCli(command, ['--version']).catch(() => { });
|
|
191
|
+
}
|
|
192
|
+
catch { /* Missing CLI. */ }
|
|
193
|
+
return null;
|
|
194
|
+
}
|
|
10
195
|
/**
|
|
11
196
|
* Start a detached EvolCore Node process without creating a console window on
|
|
12
197
|
* Windows. The child is unref'd here so callers cannot accidentally keep the
|
|
@@ -22,6 +207,256 @@ export function spawnDetachedNode(args, env = {}) {
|
|
|
22
207
|
child.unref();
|
|
23
208
|
return child;
|
|
24
209
|
}
|
|
210
|
+
/**
|
|
211
|
+
* Spawn the restart monitor and wait until it proves it is running.
|
|
212
|
+
*
|
|
213
|
+
* Every caller stops (or kills) the daemon right after this returns, so a
|
|
214
|
+
* monitor that dies while loading leaves nothing running and nobody watching.
|
|
215
|
+
* In a checkout the monitor is executed through tsx, where a syntax error
|
|
216
|
+
* anywhere in its import graph is fatal at load time — and the usual
|
|
217
|
+
* `stdio: 'ignore'` would throw that diagnostic away. So stderr is captured,
|
|
218
|
+
* and success requires the child to still be alive after it had a chance to
|
|
219
|
+
* fail, not merely to have been spawned.
|
|
220
|
+
*
|
|
221
|
+
* `isAliveInRegistry` lets the caller define proof-of-life without dragging the
|
|
222
|
+
* instance registry into this module.
|
|
223
|
+
*/
|
|
224
|
+
export async function launchMonitorAndConfirm(args, env, isAliveInRegistry, hasOtherLiveMonitor, timeoutMs = 15_000) {
|
|
225
|
+
const child = spawn(process.execPath, [...args], {
|
|
226
|
+
detached: true,
|
|
227
|
+
stdio: ['ignore', 'ignore', 'pipe'],
|
|
228
|
+
windowsHide: isWindows,
|
|
229
|
+
env: { ...process.env, ...env },
|
|
230
|
+
});
|
|
231
|
+
let stderrTail = '';
|
|
232
|
+
child.stderr?.on('data', (chunk) => {
|
|
233
|
+
stderrTail = (stderrTail + chunk.toString()).slice(-2000);
|
|
234
|
+
});
|
|
235
|
+
// Capturing stderr must not defeat detached-process semantics. A referenced
|
|
236
|
+
// pipe keeps the caller's event loop alive until the monitor exits, which
|
|
237
|
+
// makes restart commands wait for the monitor's full lifetime.
|
|
238
|
+
child.stderr?.unref?.();
|
|
239
|
+
let exitCode = null;
|
|
240
|
+
let spawnError = null;
|
|
241
|
+
child.on('exit', code => { exitCode = code; });
|
|
242
|
+
child.on('error', err => { spawnError = err.message; });
|
|
243
|
+
const childPid = child.pid;
|
|
244
|
+
child.unref();
|
|
245
|
+
if (!childPid)
|
|
246
|
+
return { ok: false, reason: 'spawn 未返回 PID' };
|
|
247
|
+
const deadline = Date.now() + timeoutMs;
|
|
248
|
+
while (Date.now() < deadline) {
|
|
249
|
+
await new Promise(resolve => setTimeout(resolve, 300));
|
|
250
|
+
if (spawnError)
|
|
251
|
+
return { ok: false, reason: spawnError, stderrTail };
|
|
252
|
+
if (exitCode !== null) {
|
|
253
|
+
// Exiting cleanly is legitimate when another monitor already owns this
|
|
254
|
+
// restart (single-instance election) — that one is alive and in charge.
|
|
255
|
+
if (exitCode === 0 && hasOtherLiveMonitor(childPid))
|
|
256
|
+
return { ok: true };
|
|
257
|
+
return { ok: false, reason: `进程已退出,exit=${exitCode}`, stderrTail };
|
|
258
|
+
}
|
|
259
|
+
// Registering means the module graph loaded and the monitor started work.
|
|
260
|
+
if (isAliveInRegistry(childPid))
|
|
261
|
+
return { ok: true };
|
|
262
|
+
}
|
|
263
|
+
// Still running but not yet registered: a slow start is not a failure.
|
|
264
|
+
if (isProcessRunning(childPid))
|
|
265
|
+
return { ok: true };
|
|
266
|
+
return { ok: false, reason: '进程未登记且已消失', stderrTail };
|
|
267
|
+
}
|
|
268
|
+
/**
|
|
269
|
+
* Copy the monitor out of the package directory so an upgrade cannot replace it
|
|
270
|
+
* mid-flight.
|
|
271
|
+
*
|
|
272
|
+
* This is the installed-package counterpart of running the monitor from `src/`
|
|
273
|
+
* in a checkout: `npm install -g` swaps the whole package directory, and the
|
|
274
|
+
* monitor has ~170 lazy `await import()` sites, so a module it has not loaded
|
|
275
|
+
* yet would come from the new version while the rest is still the old one.
|
|
276
|
+
* Staging `dist/` elsewhere keeps its module graph on one consistent version.
|
|
277
|
+
*
|
|
278
|
+
* Only `dist/` and `package.json` are copied (a few MB). `node_modules` is
|
|
279
|
+
* linked, because copying it is hundreds of MB. The link degrades in steps so
|
|
280
|
+
* that no platform is left without a path forward:
|
|
281
|
+
*
|
|
282
|
+
* 1. symlink — `junction` on Windows (works without elevation, unlike a
|
|
283
|
+
* `dir` symlink which needs admin or developer mode), plain dir symlink
|
|
284
|
+
* elsewhere. Junctions cannot span volumes, hence the fallbacks.
|
|
285
|
+
* 2. full copy — slow but always permitted.
|
|
286
|
+
* 3. skip staging — caller runs the monitor in place, exactly as before.
|
|
287
|
+
* Losing the upgrade isolation is much better than losing the restart.
|
|
288
|
+
*/
|
|
289
|
+
export function prepareMonitorStage(packageRoot, stageParent) {
|
|
290
|
+
const distSource = path.join(packageRoot, 'dist');
|
|
291
|
+
const modulesSource = path.join(packageRoot, 'node_modules');
|
|
292
|
+
if (!fs.existsSync(distSource)) {
|
|
293
|
+
return { stageDir: null, linkMode: 'none', note: 'dist/ 不存在,跳过 stage' };
|
|
294
|
+
}
|
|
295
|
+
// Clear stages left behind by a monitor that was killed before self-cleanup.
|
|
296
|
+
try {
|
|
297
|
+
const cutoff = Date.now() - 60 * 60 * 1000;
|
|
298
|
+
for (const entry of fs.readdirSync(stageParent, { withFileTypes: true })) {
|
|
299
|
+
if (!entry.isDirectory())
|
|
300
|
+
continue;
|
|
301
|
+
const full = path.join(stageParent, entry.name);
|
|
302
|
+
if (fs.statSync(full).mtimeMs < cutoff)
|
|
303
|
+
fs.rmSync(full, { recursive: true, force: true });
|
|
304
|
+
}
|
|
305
|
+
}
|
|
306
|
+
catch { /* stageParent may not exist yet */ }
|
|
307
|
+
const stageDir = path.join(stageParent, `${process.pid}-${Date.now()}`);
|
|
308
|
+
try {
|
|
309
|
+
fs.mkdirSync(stageDir, { recursive: true });
|
|
310
|
+
fs.cpSync(distSource, path.join(stageDir, 'dist'), { recursive: true });
|
|
311
|
+
const pkgJson = path.join(packageRoot, 'package.json');
|
|
312
|
+
if (fs.existsSync(pkgJson))
|
|
313
|
+
fs.copyFileSync(pkgJson, path.join(stageDir, 'package.json'));
|
|
314
|
+
}
|
|
315
|
+
catch (error) {
|
|
316
|
+
try {
|
|
317
|
+
fs.rmSync(stageDir, { recursive: true, force: true });
|
|
318
|
+
}
|
|
319
|
+
catch { }
|
|
320
|
+
return { stageDir: null, linkMode: 'none', note: `复制 dist/ 失败:${error.message}` };
|
|
321
|
+
}
|
|
322
|
+
if (!fs.existsSync(modulesSource)) {
|
|
323
|
+
// Dependencies hoisted elsewhere; nothing to link, node resolves upward.
|
|
324
|
+
return { stageDir, linkMode: 'none' };
|
|
325
|
+
}
|
|
326
|
+
const stageModules = path.join(stageDir, 'node_modules');
|
|
327
|
+
try {
|
|
328
|
+
fs.symlinkSync(modulesSource, stageModules, isWindows ? 'junction' : 'dir');
|
|
329
|
+
// A junction to another volume is created but unusable, so prove it reads.
|
|
330
|
+
fs.readdirSync(stageModules);
|
|
331
|
+
return { stageDir, linkMode: 'symlink' };
|
|
332
|
+
}
|
|
333
|
+
catch (linkError) {
|
|
334
|
+
try {
|
|
335
|
+
fs.rmSync(stageModules, { recursive: true, force: true });
|
|
336
|
+
}
|
|
337
|
+
catch { }
|
|
338
|
+
try {
|
|
339
|
+
fs.cpSync(modulesSource, stageModules, { recursive: true });
|
|
340
|
+
return {
|
|
341
|
+
stageDir,
|
|
342
|
+
linkMode: 'copy',
|
|
343
|
+
note: `链接 node_modules 失败(${linkError.code ?? 'unknown'}),已改为完整复制`,
|
|
344
|
+
};
|
|
345
|
+
}
|
|
346
|
+
catch (copyError) {
|
|
347
|
+
try {
|
|
348
|
+
fs.rmSync(stageDir, { recursive: true, force: true });
|
|
349
|
+
}
|
|
350
|
+
catch { }
|
|
351
|
+
return {
|
|
352
|
+
stageDir: null,
|
|
353
|
+
linkMode: 'none',
|
|
354
|
+
note: `无法准备 stage(link: ${linkError.message}; copy: ${copyError.message})`,
|
|
355
|
+
};
|
|
356
|
+
}
|
|
357
|
+
}
|
|
358
|
+
}
|
|
359
|
+
/** Remove a staging directory created by prepareMonitorStage. */
|
|
360
|
+
export function cleanupMonitorStage(stageDir) {
|
|
361
|
+
try {
|
|
362
|
+
// Remove the link itself, never what it points at.
|
|
363
|
+
const modules = path.join(stageDir, 'node_modules');
|
|
364
|
+
const stat = fs.lstatSync(modules, { throwIfNoEntry: false });
|
|
365
|
+
if (stat?.isSymbolicLink())
|
|
366
|
+
fs.unlinkSync(modules);
|
|
367
|
+
}
|
|
368
|
+
catch { /* fall through to the recursive removal */ }
|
|
369
|
+
try {
|
|
370
|
+
fs.rmSync(stageDir, { recursive: true, force: true });
|
|
371
|
+
}
|
|
372
|
+
catch { }
|
|
373
|
+
}
|
|
374
|
+
/** True when the current process may write into `dir`. */
|
|
375
|
+
export function isDirWritable(dir) {
|
|
376
|
+
try {
|
|
377
|
+
fs.accessSync(dir, fs.constants.W_OK);
|
|
378
|
+
return true;
|
|
379
|
+
}
|
|
380
|
+
catch {
|
|
381
|
+
return false;
|
|
382
|
+
}
|
|
383
|
+
}
|
|
384
|
+
/**
|
|
385
|
+
* Resolve the launch argv for the restart monitor, staging it out of the
|
|
386
|
+
* package directory when that package can be replaced underneath it.
|
|
387
|
+
*
|
|
388
|
+
* Checkout: `resolveCliEntry` already points at `src/` via tsx, which no build
|
|
389
|
+
* can delete — nothing to stage.
|
|
390
|
+
*
|
|
391
|
+
* Installed: the monitor lives in the very directory `npm install -g` swaps,
|
|
392
|
+
* so it is copied to `stageParent` first and told where the real package is
|
|
393
|
+
* through `EVOLCORE_TARGET_PACKAGE_ROOT`. If staging cannot be done we fall
|
|
394
|
+
* back to launching in place: losing upgrade isolation is far better than
|
|
395
|
+
* losing the ability to restart.
|
|
396
|
+
*/
|
|
397
|
+
export function resolveMonitorLaunch(packageRoot, stageParent) {
|
|
398
|
+
const resolved = resolveCliEntry(packageRoot);
|
|
399
|
+
if (resolved.usingSource) {
|
|
400
|
+
return { args: resolved.args, entry: resolved.entry, env: {}, stageDir: null };
|
|
401
|
+
}
|
|
402
|
+
const stage = prepareMonitorStage(packageRoot, stageParent);
|
|
403
|
+
if (!stage.stageDir) {
|
|
404
|
+
return {
|
|
405
|
+
args: resolved.args,
|
|
406
|
+
entry: resolved.entry,
|
|
407
|
+
env: {},
|
|
408
|
+
stageDir: null,
|
|
409
|
+
note: stage.note ? `${stage.note};本次未启用 stage 隔离` : undefined,
|
|
410
|
+
};
|
|
411
|
+
}
|
|
412
|
+
const stagedEntry = path.join(stage.stageDir, 'dist', 'cli', 'index.js');
|
|
413
|
+
if (!fs.existsSync(stagedEntry)) {
|
|
414
|
+
cleanupMonitorStage(stage.stageDir);
|
|
415
|
+
return { args: resolved.args, entry: resolved.entry, env: {}, stageDir: null, note: 'stage 缺少入口,已退回原地启动' };
|
|
416
|
+
}
|
|
417
|
+
return {
|
|
418
|
+
args: [stagedEntry],
|
|
419
|
+
entry: stagedEntry,
|
|
420
|
+
env: {
|
|
421
|
+
EVOLCORE_TARGET_PACKAGE_ROOT: packageRoot,
|
|
422
|
+
EVOLCORE_MONITOR_STAGE: stage.stageDir,
|
|
423
|
+
},
|
|
424
|
+
stageDir: stage.stageDir,
|
|
425
|
+
note: stage.note,
|
|
426
|
+
};
|
|
427
|
+
}
|
|
428
|
+
/**
|
|
429
|
+
* Resolve how to launch the EvolCore CLI (`restart`, `restart-monitor`, ...).
|
|
430
|
+
*
|
|
431
|
+
* In a development checkout the compiled `dist/` is deleted and rebuilt by
|
|
432
|
+
* `npm run build`, so anything spawned from `dist/cli/index.js` disappears
|
|
433
|
+
* exactly when a build is in flight. The restart monitor is the watchdog that
|
|
434
|
+
* has to outlive that window, so it must not live inside the artifact it
|
|
435
|
+
* supervises: when sources and tsx are both available we run the monitor
|
|
436
|
+
* straight from `src/`, mirroring what `bin/ec.js` already does for the CLI.
|
|
437
|
+
*
|
|
438
|
+
* An npm-installed package ships `dist/` without `src/` and without tsx (a
|
|
439
|
+
* devDependency), so both checks fail and the caller keeps the previous
|
|
440
|
+
* behaviour byte for byte.
|
|
441
|
+
*/
|
|
442
|
+
export function resolveCliEntry(packageRoot) {
|
|
443
|
+
const sourceEntry = path.join(packageRoot, 'src', 'cli', 'index.ts');
|
|
444
|
+
const distEntry = path.join(packageRoot, 'dist', 'cli', 'index.js');
|
|
445
|
+
if (fs.existsSync(sourceEntry)) {
|
|
446
|
+
// tsx is a devDependency: present in a checkout, absent from the published
|
|
447
|
+
// package. Resolve it relative to the target root so `ec mode` switching
|
|
448
|
+
// between roots cannot borrow the wrong copy.
|
|
449
|
+
try {
|
|
450
|
+
const require = createRequire(path.join(packageRoot, 'package.json'));
|
|
451
|
+
const tsxUrl = pathToFileURL(require.resolve('tsx')).href;
|
|
452
|
+
return { args: ['--import', tsxUrl, sourceEntry], usingSource: true, entry: sourceEntry };
|
|
453
|
+
}
|
|
454
|
+
catch {
|
|
455
|
+
// tsx missing (e.g. `npm prune --production`): fall through to dist.
|
|
456
|
+
}
|
|
457
|
+
}
|
|
458
|
+
return { args: [distEntry], usingSource: false, entry: distEntry };
|
|
459
|
+
}
|
|
25
460
|
const ENCODE_PATH_MAX = 200;
|
|
26
461
|
function encodePathHash(s) {
|
|
27
462
|
let h = 0;
|
|
@@ -38,6 +38,7 @@ export var ErrorType;
|
|
|
38
38
|
ErrorType["STREAM_ERROR"] = "stream_error";
|
|
39
39
|
ErrorType["CONTEXT_TOO_LONG"] = "context_too_long";
|
|
40
40
|
ErrorType["MODEL_UNAVAILABLE"] = "model_unavailable";
|
|
41
|
+
ErrorType["SESSION_UNAVAILABLE"] = "session_unavailable";
|
|
41
42
|
ErrorType["SANDBOX_INITIALIZATION_FAILED"] = "sandbox_initialization_failed";
|
|
42
43
|
ErrorType["CLAUDE_STARTUP_ARGUMENTS_TOO_LONG"] = "claude_startup_arguments_too_long";
|
|
43
44
|
ErrorType["UNKNOWN"] = "unknown";
|
|
@@ -278,6 +279,9 @@ function hasRetryableHttpStatus(text) {
|
|
|
278
279
|
}
|
|
279
280
|
export function classifyError(error) {
|
|
280
281
|
const msg = (error?.message || '').toLowerCase();
|
|
282
|
+
if (error?.code === 'TOPIC_BACKEND_UNAVAILABLE') {
|
|
283
|
+
return ErrorType.SESSION_UNAVAILABLE;
|
|
284
|
+
}
|
|
281
285
|
// Startup-size failures are deterministic local failures and must win over
|
|
282
286
|
// dictionary rules that would otherwise turn them into generic retries.
|
|
283
287
|
if (isClaudeStartupArgumentsTooLong(error)) {
|
|
@@ -380,20 +384,23 @@ export function getErrorMessage(error, terminalReason, includeEmoji = true) {
|
|
|
380
384
|
}
|
|
381
385
|
// 回退到原有的错误消息匹配逻辑
|
|
382
386
|
const msg = error?.message || String(error);
|
|
387
|
+
const warnPrefix = includeEmoji ? '⚠️ ' : '';
|
|
388
|
+
const errPrefix = includeEmoji ? '❌ ' : '';
|
|
383
389
|
// Codex app-server reports this when another app-server still owns the
|
|
384
390
|
// thread writer. Surface the handoff condition instead of hiding it behind
|
|
385
391
|
// the generic "processing failed" message.
|
|
386
392
|
if (error?.code === 'THREAD_ACTIVE_WRITER' || /already has an active writer/i.test(msg)) {
|
|
387
|
-
|
|
388
|
-
|
|
393
|
+
return `${warnPrefix}Codex 会话正在由另一个执行通道占用,请稍后重试(THREAD_ACTIVE_WRITER)`;
|
|
394
|
+
}
|
|
395
|
+
if (error?.code === 'TOPIC_BACKEND_UNAVAILABLE'
|
|
396
|
+
|| /bound topic backend is unavailable/i.test(msg)) {
|
|
397
|
+
return `${warnPrefix}当前会话已失效,请发送 /renew。`;
|
|
389
398
|
}
|
|
390
399
|
// 字典优先 — 命中且有自定义消息则直接返回
|
|
391
400
|
const rule = matchErrorRule(msg.toLowerCase());
|
|
392
401
|
if (rule?.message)
|
|
393
402
|
return rule.message;
|
|
394
403
|
// 内置兜底规则(结构性错误)
|
|
395
|
-
const warnPrefix = includeEmoji ? '⚠️ ' : '';
|
|
396
|
-
const errPrefix = includeEmoji ? '❌ ' : '';
|
|
397
404
|
if (msg.includes('CONTEXT_COMPACT_FAILED') || isContextTooLongText(msg)) {
|
|
398
405
|
const detail = msg.match(/^CONTEXT_COMPACT_FAILED:\s*(.+)$/)?.[1]?.trim();
|
|
399
406
|
return detail
|
|
@@ -1,20 +1,23 @@
|
|
|
1
|
-
{"model":"claude-opus-4-8","effective_from":0,"billing_fn":"per_token_v1","currency":"USD","price_input":15,"price_output":75,"price_cache_creation":18.75,"price_cache_read":1.5}
|
|
2
|
-
{"model":"claude-opus-4-7","effective_from":0,"billing_fn":"per_token_v1","currency":"USD","price_input":15,"price_output":75,"price_cache_creation":18.75,"price_cache_read":1.5}
|
|
3
|
-
{"model":"claude-opus-4-6","effective_from":0,"billing_fn":"per_token_v1","currency":"USD","price_input":15,"price_output":75,"price_cache_creation":18.75,"price_cache_read":1.5}
|
|
4
|
-
{"model":"claude-sonnet-4-6","effective_from":0,"billing_fn":"per_token_v1","currency":"USD","price_input":3,"price_output":15,"price_cache_creation":3.75,"price_cache_read":0.3}
|
|
5
|
-
{"model":"claude-haiku-4-5-20251001","effective_from":0,"billing_fn":"per_token_v1","currency":"USD","price_input":0.8,"price_output":4,"price_cache_creation":1,"price_cache_read":0.08}
|
|
6
|
-
{"model":"gpt-5.6-sol","effective_from":0,"billing_fn":"per_token_v1","currency":"USD","price_input":5,"price_output":30,"price_cache_creation":6.25,"price_cache_read":0.5,"tiers":[{"up_to_input_tokens":272000,"price_input":5,"price_output":30,"price_cache_creation":6.25,"price_cache_read":0.5},{"up_to_input_tokens":null,"price_input":10,"price_output":45,"price_cache_creation":12.5,"price_cache_read":1}]}
|
|
7
|
-
{"model":"gpt-5.6-terra","effective_from":0,"billing_fn":"per_token_v1","currency":"USD","price_input":2.5,"price_output":15,"price_cache_creation":3.125,"price_cache_read":0.25,"tiers":[{"up_to_input_tokens":272000,"price_input":2.5,"price_output":15,"price_cache_creation":3.125,"price_cache_read":0.25},{"up_to_input_tokens":null,"price_input":5,"price_output":22.5,"price_cache_creation":6.25,"price_cache_read":0.5}]}
|
|
8
|
-
{"model":"gpt-5.6-luna","effective_from":0,"billing_fn":"per_token_v1","currency":"USD","price_input":1,"price_output":6,"price_cache_creation":1.25,"price_cache_read":0.1,"tiers":[{"up_to_input_tokens":272000,"price_input":1,"price_output":6,"price_cache_creation":1.25,"price_cache_read":0.1},{"up_to_input_tokens":null,"price_input":2,"price_output":9,"price_cache_creation":2.5,"price_cache_read":0.2}]}
|
|
9
|
-
{"model":"gpt-5.5","effective_from":0,"billing_fn":"per_token_v1","currency":"USD","price_input":5,"price_output":30,"price_cache_creation":0,"price_cache_read":0.5,"tiers":[{"up_to_input_tokens":272000,"price_input":5,"price_output":30,"price_cache_creation":0,"price_cache_read":0.5},{"up_to_input_tokens":null,"price_input":10,"price_output":45,"price_cache_creation":0,"price_cache_read":1}]}
|
|
10
|
-
{"model":"gpt-5.4","effective_from":0,"billing_fn":"per_token_v1","currency":"USD","price_input":2.5,"price_output":15,"price_cache_creation":0,"price_cache_read":0.25,"tiers":[{"up_to_input_tokens":272000,"price_input":2.5,"price_output":15,"price_cache_creation":0,"price_cache_read":0.25},{"up_to_input_tokens":null,"price_input":5,"price_output":22.5,"price_cache_creation":0,"price_cache_read":0.5}]}
|
|
11
|
-
{"model":"gpt-5.3-codex","effective_from":0,"billing_fn":"per_token_v1","currency":"USD","price_input":1.75,"price_output":14,"price_cache_creation":0,"price_cache_read":0.175}
|
|
12
|
-
{"model":"deepseek-v4-pro","effective_from":0,"billing_fn":"per_token_deepseek_v1","currency":"CNY","price_cache_hit":0.5,"price_cache_miss":2,"price_output":8}
|
|
13
|
-
{"model":"deepseek-v4-flash","effective_from":0,"billing_fn":"per_token_deepseek_v1","currency":"CNY","price_cache_hit":0.1,"price_cache_miss":0.5,"price_output":2}
|
|
14
|
-
{"model":"deepseek-v3.2","effective_from":0,"billing_fn":"per_token_deepseek_v1","currency":"CNY","price_cache_hit":0.5,"price_cache_miss":2,"price_output":8}
|
|
15
|
-
{"model":"kimi-k2.6","effective_from":0,"billing_fn":"per_token_v1","currency":"CNY","price_input":2,"price_output":8,"price_cache_creation":0,"price_cache_read":0}
|
|
16
|
-
{"model":"kimi-k2.5","effective_from":0,"billing_fn":"per_token_v1","currency":"CNY","price_input":2,"price_output":8,"price_cache_creation":0,"price_cache_read":0}
|
|
17
|
-
{"model":"glm-5.1","effective_from":0,"billing_fn":"per_token_v1","currency":"CNY","price_input":5,"price_output":20,"price_cache_creation":0,"price_cache_read":0}
|
|
18
|
-
{"model":"glm-5","effective_from":0,"billing_fn":"per_token_v1","currency":"CNY","price_input":5,"price_output":20,"price_cache_creation":0,"price_cache_read":0}
|
|
19
|
-
{"model":"glm-4.7","effective_from":0,"billing_fn":"per_token_v1","currency":"CNY","price_input":1,"price_output":5,"price_cache_creation":0,"price_cache_read":0}
|
|
20
|
-
{"model":"MiniMax-M2.7","effective_from":0,"billing_fn":"per_token_v1","currency":"CNY","price_input":1,"price_output":5,"price_cache_creation":0,"price_cache_read":0}
|
|
1
|
+
{"model":"claude-opus-4-8","effective_from":0,"billing_fn":"per_token_v1","currency":"USD","price_input":15,"price_output":75,"price_cache_creation":18.75,"price_cache_read":1.5,"context_window":1000000,"max_input_tokens":900000,"max_output_tokens":32768,"supports_cache":true,"supports_vision":true}
|
|
2
|
+
{"model":"claude-opus-4-7","effective_from":0,"billing_fn":"per_token_v1","currency":"USD","price_input":15,"price_output":75,"price_cache_creation":18.75,"price_cache_read":1.5,"context_window":1000000,"max_input_tokens":900000,"max_output_tokens":32768,"supports_cache":true,"supports_vision":true}
|
|
3
|
+
{"model":"claude-opus-4-6","effective_from":0,"billing_fn":"per_token_v1","currency":"USD","price_input":15,"price_output":75,"price_cache_creation":18.75,"price_cache_read":1.5,"context_window":1000000,"max_input_tokens":900000,"max_output_tokens":32768,"supports_cache":true,"supports_vision":true}
|
|
4
|
+
{"model":"claude-sonnet-4-6","effective_from":0,"billing_fn":"per_token_v1","currency":"USD","price_input":3,"price_output":15,"price_cache_creation":3.75,"price_cache_read":0.3,"context_window":200000,"max_input_tokens":180000,"max_output_tokens":16384,"supports_cache":true,"supports_vision":true}
|
|
5
|
+
{"model":"claude-haiku-4-5-20251001","effective_from":0,"billing_fn":"per_token_v1","currency":"USD","price_input":0.8,"price_output":4,"price_cache_creation":1,"price_cache_read":0.08,"context_window":200000,"max_input_tokens":180000,"max_output_tokens":16384,"supports_cache":true,"supports_vision":false}
|
|
6
|
+
{"model":"gpt-5.6-sol","effective_from":0,"billing_fn":"per_token_v1","currency":"USD","price_input":5,"price_output":30,"price_cache_creation":6.25,"price_cache_read":0.5,"tiers":[{"up_to_input_tokens":272000,"price_input":5,"price_output":30,"price_cache_creation":6.25,"price_cache_read":0.5},{"up_to_input_tokens":null,"price_input":10,"price_output":45,"price_cache_creation":12.5,"price_cache_read":1}],"context_window":200000,"max_input_tokens":180000,"max_output_tokens":16384}
|
|
7
|
+
{"model":"gpt-5.6-terra","effective_from":0,"billing_fn":"per_token_v1","currency":"USD","price_input":2.5,"price_output":15,"price_cache_creation":3.125,"price_cache_read":0.25,"tiers":[{"up_to_input_tokens":272000,"price_input":2.5,"price_output":15,"price_cache_creation":3.125,"price_cache_read":0.25},{"up_to_input_tokens":null,"price_input":5,"price_output":22.5,"price_cache_creation":6.25,"price_cache_read":0.5}],"context_window":200000,"max_input_tokens":180000,"max_output_tokens":16384}
|
|
8
|
+
{"model":"gpt-5.6-luna","effective_from":0,"billing_fn":"per_token_v1","currency":"USD","price_input":1,"price_output":6,"price_cache_creation":1.25,"price_cache_read":0.1,"tiers":[{"up_to_input_tokens":272000,"price_input":1,"price_output":6,"price_cache_creation":1.25,"price_cache_read":0.1},{"up_to_input_tokens":null,"price_input":2,"price_output":9,"price_cache_creation":2.5,"price_cache_read":0.2}],"context_window":200000,"max_input_tokens":180000,"max_output_tokens":16384}
|
|
9
|
+
{"model":"gpt-5.5","effective_from":0,"billing_fn":"per_token_v1","currency":"USD","price_input":5,"price_output":30,"price_cache_creation":0,"price_cache_read":0.5,"tiers":[{"up_to_input_tokens":272000,"price_input":5,"price_output":30,"price_cache_creation":0,"price_cache_read":0.5},{"up_to_input_tokens":null,"price_input":10,"price_output":45,"price_cache_creation":0,"price_cache_read":1}],"context_window":200000,"max_input_tokens":180000,"max_output_tokens":16384}
|
|
10
|
+
{"model":"gpt-5.4","effective_from":0,"billing_fn":"per_token_v1","currency":"USD","price_input":2.5,"price_output":15,"price_cache_creation":0,"price_cache_read":0.25,"tiers":[{"up_to_input_tokens":272000,"price_input":2.5,"price_output":15,"price_cache_creation":0,"price_cache_read":0.25},{"up_to_input_tokens":null,"price_input":5,"price_output":22.5,"price_cache_creation":0,"price_cache_read":0.5}],"context_window":200000,"max_input_tokens":180000,"max_output_tokens":16384}
|
|
11
|
+
{"model":"gpt-5.3-codex","effective_from":0,"billing_fn":"per_token_v1","currency":"USD","price_input":1.75,"price_output":14,"price_cache_creation":0,"price_cache_read":0.175,"context_window":200000,"max_input_tokens":180000,"max_output_tokens":16384}
|
|
12
|
+
{"model":"deepseek-v4-pro","effective_from":0,"billing_fn":"per_token_deepseek_v1","currency":"CNY","price_cache_hit":0.5,"price_cache_miss":2,"price_output":8,"context_window":1000000,"max_input_tokens":950000,"max_output_tokens":8192,"supports_cache":true}
|
|
13
|
+
{"model":"deepseek-v4-flash","effective_from":0,"billing_fn":"per_token_deepseek_v1","currency":"CNY","price_cache_hit":0.1,"price_cache_miss":0.5,"price_output":2,"context_window":1000000,"max_input_tokens":950000,"max_output_tokens":8192,"supports_cache":true}
|
|
14
|
+
{"model":"deepseek-v3.2","effective_from":0,"billing_fn":"per_token_deepseek_v1","currency":"CNY","price_cache_hit":0.5,"price_cache_miss":2,"price_output":8,"context_window":128000,"max_input_tokens":120000,"max_output_tokens":8192,"supports_cache":true}
|
|
15
|
+
{"model":"kimi-k2.6","effective_from":0,"billing_fn":"per_token_v1","currency":"CNY","price_input":2,"price_output":8,"price_cache_creation":0,"price_cache_read":0,"context_window":128000,"max_input_tokens":120000,"max_output_tokens":8192}
|
|
16
|
+
{"model":"kimi-k2.5","effective_from":0,"billing_fn":"per_token_v1","currency":"CNY","price_input":2,"price_output":8,"price_cache_creation":0,"price_cache_read":0,"context_window":128000,"max_input_tokens":120000,"max_output_tokens":8192}
|
|
17
|
+
{"model":"glm-5.1","effective_from":0,"billing_fn":"per_token_v1","currency":"CNY","price_input":5,"price_output":20,"price_cache_creation":0,"price_cache_read":0,"context_window":128000,"max_input_tokens":120000,"max_output_tokens":8192}
|
|
18
|
+
{"model":"glm-5","effective_from":0,"billing_fn":"per_token_v1","currency":"CNY","price_input":5,"price_output":20,"price_cache_creation":0,"price_cache_read":0,"context_window":128000,"max_input_tokens":120000,"max_output_tokens":8192}
|
|
19
|
+
{"model":"glm-4.7","effective_from":0,"billing_fn":"per_token_v1","currency":"CNY","price_input":1,"price_output":5,"price_cache_creation":0,"price_cache_read":0,"context_window":128000,"max_input_tokens":120000,"max_output_tokens":8192}
|
|
20
|
+
{"model":"MiniMax-M2.7","effective_from":0,"billing_fn":"per_token_v1","currency":"CNY","price_input":1,"price_output":5,"price_cache_creation":0,"price_cache_read":0,"context_window":128000,"max_input_tokens":120000,"max_output_tokens":8192}
|
|
21
|
+
{"model":"claude-opus-5","effective_from":0,"context_window":1000000,"max_input_tokens":900000,"max_output_tokens":131072,"supports_cache":true,"supports_vision":true}
|
|
22
|
+
{"model":"claude-sonnet-5","effective_from":0,"context_window":1000000,"max_input_tokens":900000,"max_output_tokens":131072,"supports_cache":true,"supports_vision":true}
|
|
23
|
+
{"model":"claude-fable-5","effective_from":0,"context_window":1000000,"max_input_tokens":900000,"max_output_tokens":131072,"supports_cache":true,"supports_vision":true}
|
package/dist/utils/npm-ops.js
CHANGED
|
@@ -14,6 +14,26 @@ import { getPackageRoot } from '../paths.js';
|
|
|
14
14
|
import { isWindows } from './cross-platform.js';
|
|
15
15
|
const execFileAsync = promisify(execFile);
|
|
16
16
|
// ── npm install -g (shared) ────────────────────────────────────────────────
|
|
17
|
+
/**
|
|
18
|
+
* Can we run sudo without a password prompt?
|
|
19
|
+
*
|
|
20
|
+
* `sudo -n` never prompts: it exits non-zero instead when credentials are
|
|
21
|
+
* needed. That matters because upgrades run inside a detached monitor with no
|
|
22
|
+
* tty — a plain `sudo` there would block until the 180s timeout rather than
|
|
23
|
+
* fail. NOPASSWD rules and an unexpired credential cache both pass this check,
|
|
24
|
+
* so the cases where sudo can actually succeed are preserved.
|
|
25
|
+
*/
|
|
26
|
+
async function canSudoNonInteractively() {
|
|
27
|
+
if (isWindows)
|
|
28
|
+
return false;
|
|
29
|
+
try {
|
|
30
|
+
await execFileAsync('sudo', ['-n', 'true'], { timeout: 5000 });
|
|
31
|
+
return true;
|
|
32
|
+
}
|
|
33
|
+
catch {
|
|
34
|
+
return false;
|
|
35
|
+
}
|
|
36
|
+
}
|
|
17
37
|
export async function npmInstallGlobal(pkg, options = {}) {
|
|
18
38
|
let cmd;
|
|
19
39
|
let args;
|
|
@@ -41,7 +61,10 @@ export async function npmInstallGlobal(pkg, options = {}) {
|
|
|
41
61
|
if (isWindows) {
|
|
42
62
|
throw new Error('权限不足。请以管理员身份运行 PowerShell 或 CMD,然后重试');
|
|
43
63
|
}
|
|
44
|
-
await
|
|
64
|
+
if (!await canSudoNonInteractively()) {
|
|
65
|
+
throw new Error(`权限不足,且 sudo 需要密码(当前进程无法交互输入)。请手动执行: sudo npm install -g ${pkg}`);
|
|
66
|
+
}
|
|
67
|
+
await execFileAsync('sudo', ['-n', 'npm', 'install', '-g', pkg], {
|
|
45
68
|
timeout: options.timeoutMs ?? 180000,
|
|
46
69
|
windowsHide: false,
|
|
47
70
|
signal: options.signal,
|
|
@@ -693,7 +693,7 @@ export function configureWindowsAutostart(enabled, runtimeRoot) {
|
|
|
693
693
|
'/TN', WINDOWS_AUTOSTART_TASK_NAME,
|
|
694
694
|
'/SC', 'ONLOGON',
|
|
695
695
|
'/TR', taskCommand(scriptPath),
|
|
696
|
-
'/RL', '
|
|
696
|
+
'/RL', 'HIGHEST',
|
|
697
697
|
'/F',
|
|
698
698
|
];
|
|
699
699
|
const result = runSchtasks(args);
|
|
@@ -58,6 +58,8 @@
|
|
|
58
58
|
"scopes": ["agent"],
|
|
59
59
|
"constraints": { "targetCurrentAgentOnly": true, "requireAgentAdmin": true }
|
|
60
60
|
},
|
|
61
|
+
"ec.aid.lookup": { "allow": true, "scopes": ["agent"] },
|
|
62
|
+
"ec.aid.agentmd.get": { "allow": true, "scopes": ["agent"] },
|
|
61
63
|
"ec.aid.agentmd.put": {
|
|
62
64
|
"allow": true,
|
|
63
65
|
"dangerous": true,
|