@whyshu/argent 0.1.22 → 0.1.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/bin/argent.mjs +37 -5
- package/lib/index.mjs +93 -3
- package/package.json +2 -2
package/bin/argent.mjs
CHANGED
|
@@ -18,7 +18,7 @@ import {
|
|
|
18
18
|
DSH_EXACT_VERSION, PROFILE_NAME, PROD_BUNDLES, buildLaunchdPlist, buildProfile, dshHomeOf, runnerScriptPath,
|
|
19
19
|
depsInstalled, health, installDeps, loginWithPhone, pairRunner, probeDshVersion, readAuthToken, repoRoot, runnerConfigPath, sendLoginCode, versionMatches,
|
|
20
20
|
engineRoot, materializeRuntime, checkForUpdate, compareVersions, launcherVersion,
|
|
21
|
-
PUBLISHED_BUNDLE_VERSIONS, resolveDshBin, updateProfilePluginVersions,
|
|
21
|
+
PUBLISHED_BUNDLE_VERSIONS, resolveDshBin, updateProfilePluginVersions, writeEngineContract,
|
|
22
22
|
} from '../lib/index.mjs';
|
|
23
23
|
|
|
24
24
|
const argv = process.argv.slice(2);
|
|
@@ -218,6 +218,19 @@ function runCli(bin, args, { cwd = '', env = {} } = {}) {
|
|
|
218
218
|
|
|
219
219
|
function inherit_stdout() { return !JSON_OUT }
|
|
220
220
|
|
|
221
|
+
|
|
222
|
+
/** desktop profile 的依赖安装器:ARGENT_PNPM → 宿主自带 pnpm → PATH 上的 pnpm */
|
|
223
|
+
function resolvePnpm() {
|
|
224
|
+
const cands = [
|
|
225
|
+
process.env.ARGENT_PNPM,
|
|
226
|
+
'/Applications/DeepSeek Harness.app/Contents/Resources/runtime/primary-runtime/dependencies/pnpm/bin/pnpm.mjs',
|
|
227
|
+
].filter(Boolean)
|
|
228
|
+
for (const c of cands) { if (existsSync(c)) return { cmd: process.execPath, args: [c] } }
|
|
229
|
+
const which = spawnSync('pnpm', ['--version'], { encoding: 'utf8' })
|
|
230
|
+
if (which.status === 0) return { cmd: 'pnpm', args: [] }
|
|
231
|
+
return null
|
|
232
|
+
}
|
|
233
|
+
|
|
221
234
|
async function updateCommand(sub) {
|
|
222
235
|
if (sub !== 'check' && sub !== 'run') {
|
|
223
236
|
console.error('用法:argent update check|run [--to <version>] [--yes] [--registry <url>] [--dsh-home <dir>] [--target <dir>] [--json] [--dry-run]');
|
|
@@ -252,7 +265,7 @@ async function updateCommand(sub) {
|
|
|
252
265
|
const npx = process.platform === 'win32' ? 'npx.cmd' : 'npx'
|
|
253
266
|
const common = ['--yes', `@whyshu/argent@${targetVersion}`]
|
|
254
267
|
const installArgs = [...common, 'runtime', 'install', '--target', opts.target, '--skills-target', opts.skillsTarget]
|
|
255
|
-
const healthArgs = [...common, 'health', '--dsh-home', opts.dshHome, '--json', '--dsh-bin', opts.dshBin]
|
|
268
|
+
const healthArgs = [...common, 'health', '--dsh-home', opts.dshHome, '--profile', opts.profile, '--json', '--dsh-bin', opts.dshBin]
|
|
256
269
|
if (DRY_RUN) {
|
|
257
270
|
print({ ok: true, dryRun: true, current, targetVersion, dshBin: opts.dshBin, commands: [
|
|
258
271
|
[npx, ...installArgs].join(' '),
|
|
@@ -277,17 +290,32 @@ async function updateCommand(sub) {
|
|
|
277
290
|
const profileDir = join(opts.dshHome, 'profiles', opts.profile)
|
|
278
291
|
const backupDir = join(opts.dshHome, 'argent', 'backup', `update-${stamp}`)
|
|
279
292
|
mkdirSync(backupDir, { recursive: true })
|
|
280
|
-
for (const f of ['package.json', 'cordis.patch.yml', 'pnpm-lock.yaml', 'pnpm-workspace.yaml']) {
|
|
293
|
+
for (const f of ['package.json', 'cordis.patch.yml', 'pnpm-lock.yaml', 'pnpm-workspace.yaml', 'argent.json']) {
|
|
281
294
|
const src = join(profileDir, f)
|
|
282
295
|
if (existsSync(src)) { mkdirSync(dirname(join(backupDir, f)), { recursive: true }); cpSync(src, join(backupDir, f)) }
|
|
283
296
|
}
|
|
284
297
|
const env = { NPM_CONFIG_REGISTRY: opts.registry }
|
|
298
|
+
let eng = null
|
|
285
299
|
try {
|
|
286
300
|
runCli(npx, installArgs, { env })
|
|
287
301
|
// 不调用 setup:setup 会重建 profile,可能丢掉客户现有 bundles/依赖结构(2026-10-22 实测)。
|
|
288
302
|
const prof = updateProfilePluginVersions({ dshHome: opts.dshHome, profile: opts.profile, versions: PUBLISHED_BUNDLE_VERSIONS })
|
|
289
303
|
if (!prof.ok) throw new Error(`更新 profile 插件版本失败:${prof.message}`)
|
|
290
|
-
|
|
304
|
+
// 方案 C · P3:runtime 换完必须重写契约文件(否则壳仍按旧 engineDir/版本读)
|
|
305
|
+
eng = writeEngineContract(profileDir, { repo: opts.repo, target: opts.target })
|
|
306
|
+
// ⚠️ dsh CLI **拒绝管理 desktop profile**("managed exclusively by the Electron application")
|
|
307
|
+
// ⇒ desktop 必须由宿主自己的 pnpm 装依赖(2026-11-02 实测:`dsh plugin --profile desktop install` 直接 exit 1)。
|
|
308
|
+
if (opts.profile === 'desktop') {
|
|
309
|
+
const ws = join(profileDir, 'pnpm-workspace.yaml')
|
|
310
|
+
if (!existsSync(ws)) {
|
|
311
|
+
writeFileSync(ws, 'packages:\n - .\nnodeLinker: hoisted\nautoInstallPeers: false\nallowBuilds:\n protobufjs: true\n')
|
|
312
|
+
}
|
|
313
|
+
const pnpm = resolvePnpm()
|
|
314
|
+
if (!pnpm) throw new Error('desktop profile 需要 pnpm:设置 ARGENT_PNPM,或确保 PATH 上有 pnpm')
|
|
315
|
+
runCli(pnpm.cmd, pnpm.args.concat(['install', '--reporter=append-only']), { cwd: profileDir, env })
|
|
316
|
+
} else {
|
|
317
|
+
runCli(opts.dshBin, ['plugin', '--profile', opts.profile, 'install'], { env: { ...env, DSH_HOME: opts.dshHome }, cwd: opts.dshHome })
|
|
318
|
+
}
|
|
291
319
|
runCli(npx, healthArgs, { env })
|
|
292
320
|
} catch (e) {
|
|
293
321
|
console.error(`❌ 更新失败:${e.message}`)
|
|
@@ -295,7 +323,11 @@ async function updateCommand(sub) {
|
|
|
295
323
|
console.error(' 可分步重试:npx -y @whyshu/argent@' + targetVersion + ' runtime install,再按 dsh plugin install 重试;必要时用备份恢复 package.json/patch。')
|
|
296
324
|
process.exit(2)
|
|
297
325
|
}
|
|
298
|
-
print({
|
|
326
|
+
print({
|
|
327
|
+
ok: true, from: current, to: targetVersion, runtimeTarget: opts.target, profile: profileDir, backupDir,
|
|
328
|
+
contract: { engineDir: eng.engineDir, engineVersion: eng.engineVersion, contract: eng.contract, source: eng.source },
|
|
329
|
+
restartRequired: true, next: '空闲窗口重启 dsh web(service install/launchd 或现有重启脚本)',
|
|
330
|
+
})
|
|
299
331
|
}
|
|
300
332
|
|
|
301
333
|
function serviceCommand(sub) {
|
package/lib/index.mjs
CHANGED
|
@@ -31,6 +31,11 @@ export const PROD_BUNDLES = [
|
|
|
31
31
|
];
|
|
32
32
|
export const RUNNER_LABEL = 'com.whyshu.argent-runner';
|
|
33
33
|
|
|
34
|
+
/** 启动器自身版本(写进 argent.json 的 writtenBy,便于事后追溯是谁写的契约) */
|
|
35
|
+
export const LAUNCHER_VERSION = (() => {
|
|
36
|
+
try { return JSON.parse(readFileSync(join(dirname(fileURLToPath(import.meta.url)), '..', 'package.json'), 'utf8')).version } catch { return '' }
|
|
37
|
+
})();
|
|
38
|
+
|
|
34
39
|
/** 仓库根:launcher/lib → ../../(本地联调);发布包内由 ARGENT_REPO 覆盖 */
|
|
35
40
|
export function repoRoot({ repo } = {}) {
|
|
36
41
|
if (repo) return resolve(repo);
|
|
@@ -73,6 +78,8 @@ export function pluginDirs({ repo = repoRoot() } = {}) {
|
|
|
73
78
|
|
|
74
79
|
/** 已发布插件的版本(发布模式装配 profile 用;与 npm 上实际可见版本对齐) */
|
|
75
80
|
export const PUBLISHED_BUNDLE_VERSIONS = {
|
|
81
|
+
// ⚠️ 每个包**只允许出现一次**(2026-11-02 修:argent-desktop 曾同时写 0.2.2/0.2.3,
|
|
82
|
+
// 后者静默覆盖前者 ⇒ 改上面那行的人会以为生效了)
|
|
76
83
|
'@whyshu/argent-presets': '0.1.2',
|
|
77
84
|
'@whyshu/argent-core': '0.1.6',
|
|
78
85
|
'@whyshu/argent-dsh': '0.1.10',
|
|
@@ -80,10 +87,9 @@ export const PUBLISHED_BUNDLE_VERSIONS = {
|
|
|
80
87
|
'@whyshu/argent-gateway': '0.2.3',
|
|
81
88
|
'@whyshu/argent-flow': '0.1.1',
|
|
82
89
|
'@whyshu/argent-ml': '0.1.1',
|
|
83
|
-
'@whyshu/argent-desktop': '0.2.2',
|
|
84
90
|
'@michengai/dsh-im-connect': '1.0.2',
|
|
85
91
|
'@linxin666/dsh-client-ui-task-board': '0.4.7',
|
|
86
|
-
'@whyshu/argent-desktop': '0.2.
|
|
92
|
+
'@whyshu/argent-desktop': '0.2.4',
|
|
87
93
|
};
|
|
88
94
|
|
|
89
95
|
|
|
@@ -125,6 +131,57 @@ export function engineRoot({ env = process.env, launcherLib = dirname(fileURLToP
|
|
|
125
131
|
return '';
|
|
126
132
|
}
|
|
127
133
|
|
|
134
|
+
/**
|
|
135
|
+
* 引擎定位(方案 C 契约的**写入侧**)。
|
|
136
|
+
*
|
|
137
|
+
* 判据与 engineRoot 一致但不猜:`<dir>/VERSION` + `<dir>/core/lib/argent-datadir.mjs`。
|
|
138
|
+
* 优先级:本地仓库(开发联调)→ npm 包 `engine/` → 已物化 runtime(~/.argent-dsh)。
|
|
139
|
+
* 找不到就返回 ok:false —— 由调用方决定写空值还是报错,⛔ 不要在这里编一个路径。
|
|
140
|
+
*/
|
|
141
|
+
export function engineInfo({ repo = repoRoot(), target = process.env.ARGENT_RUNTIME_TARGET, env = process.env } = {}) {
|
|
142
|
+
const valid = (d) => Boolean(d) && existsSync(join(d, 'VERSION')) && existsSync(join(d, 'core', 'lib', 'argent-datadir.mjs'));
|
|
143
|
+
const cands = [];
|
|
144
|
+
// ① 显式 runtime 目标(update run --target):客户机上**唯一稳定**的引擎位置
|
|
145
|
+
if (valid(target)) cands.push({ dir: target, source: 'target' });
|
|
146
|
+
// ② 本地仓库(开发联调):必须真的是 git 检出,⛔ 不要把 npx 缓存里的包当"仓库"
|
|
147
|
+
if (valid(repo) && existsSync(join(repo, '.git'))) cands.push({ dir: repo, source: 'repo' });
|
|
148
|
+
// ③ 已物化 runtime(默认 ~/.argent-dsh)
|
|
149
|
+
const runtime = join(homedir(), '.argent-dsh');
|
|
150
|
+
if (valid(runtime)) cands.push({ dir: runtime, source: 'runtime' });
|
|
151
|
+
// ④ 最后才是 npm 包(npx 缓存路径,随时会被清 —— 只作兜底)
|
|
152
|
+
const pkg = engineRoot({ env });
|
|
153
|
+
if (valid(pkg)) cands.push({ dir: pkg, source: 'package' });
|
|
154
|
+
for (const c of cands) {
|
|
155
|
+
let version = '';
|
|
156
|
+
try { version = readFileSync(join(c.dir, 'VERSION'), 'utf8').trim(); } catch { /* 保留空串 */ }
|
|
157
|
+
return { ok: true, dir: c.dir, source: c.source, version };
|
|
158
|
+
}
|
|
159
|
+
return { ok: false, dir: '', source: 'none', version: '' };
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
/** 读引擎契约版本(唯一真源 = `<engine>/core/lib/contract.mjs`;读不到返回 null,⛔ 不猜) */
|
|
163
|
+
export function engineContract(dir) {
|
|
164
|
+
try {
|
|
165
|
+
const m = readFileSync(join(dir, 'core', 'lib', 'contract.mjs'), 'utf8').match(/export const CONTRACT\s*=\s*(\d+)/);
|
|
166
|
+
return m ? Number(m[1]) : null;
|
|
167
|
+
} catch { return null; }
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
/** 写 `<profile>/argent.json`:宿主壳只读这个文件,不再猜引擎在哪(方案 C · P1) */
|
|
171
|
+
export function writeEngineContract(dir, { repo = repoRoot(), target = process.env.ARGENT_RUNTIME_TARGET, env = process.env } = {}) {
|
|
172
|
+
const info = engineInfo({ repo, target, env });
|
|
173
|
+
const payload = {
|
|
174
|
+
engineDir: info.ok ? info.dir : '',
|
|
175
|
+
engineVersion: info.ok ? info.version : '',
|
|
176
|
+
contract: info.ok ? engineContract(info.dir) : null,
|
|
177
|
+
source: info.source,
|
|
178
|
+
writtenBy: LAUNCHER_VERSION ? `@whyshu/argent@${LAUNCHER_VERSION}` : '@whyshu/argent',
|
|
179
|
+
writtenAt: new Date().toISOString(),
|
|
180
|
+
};
|
|
181
|
+
writeFileSync(join(dir, 'argent.json'), JSON.stringify(payload, null, 2) + '\n');
|
|
182
|
+
return payload;
|
|
183
|
+
}
|
|
184
|
+
|
|
128
185
|
function walkFiles(dir, base = dir) {
|
|
129
186
|
const out = [];
|
|
130
187
|
for (const e of readdirSync(dir)) {
|
|
@@ -205,6 +262,8 @@ export function buildProfile({ dshHome = dshHomeOf(), profile = PROFILE_NAME, re
|
|
|
205
262
|
dsh: { profile: { bundles } },
|
|
206
263
|
};
|
|
207
264
|
writeFileSync(join(dir, 'package.json'), JSON.stringify(pkg, null, 2) + '\n');
|
|
265
|
+
// 方案 C · P1:把"引擎在哪、什么版本、契约几"写成机器可读的契约文件(宿主壳只读它)
|
|
266
|
+
const engine = writeEngineContract(dir, { repo });
|
|
208
267
|
const patchLines = [
|
|
209
268
|
'# @whyshu/argent 生成(幂等;手工改动会在此文件重生成时被覆盖)',
|
|
210
269
|
'- id: agent-preset-registry',
|
|
@@ -218,7 +277,7 @@ export function buildProfile({ dshHome = dshHomeOf(), profile = PROFILE_NAME, re
|
|
|
218
277
|
writeFileSync(join(dir, 'cordis.patch.yml'), patch);
|
|
219
278
|
// pnpm 配置必须与现有 web profile 同口径:否则 pnpm 会尝试从 registry 自动装 peer(含未发布版本)⇒ 安装失败
|
|
220
279
|
writeFileSync(join(dir, 'pnpm-workspace.yaml'), 'packages:\n - .\nnodeLinker: hoisted\nautoInstallPeers: false\nallowBuilds:\n protobufjs: true\n');
|
|
221
|
-
return { ok: true, dir, plugins: Object.keys(plugins), bundles: [...bundles], preset: DEFAULT_PRESET };
|
|
280
|
+
return { ok: true, dir, plugins: Object.keys(plugins), bundles: [...bundles], preset: DEFAULT_PRESET, engine };
|
|
222
281
|
}
|
|
223
282
|
|
|
224
283
|
export function depsInstalled(dshHome = dshHomeOf(), profile = PROFILE_NAME) {
|
|
@@ -316,6 +375,36 @@ export function buildLaunchdPlist({ dshHome = dshHomeOf(), repo = repoRoot(), no
|
|
|
316
375
|
}
|
|
317
376
|
|
|
318
377
|
/** 状态自检(不启动任何东西;JSON 友好) */
|
|
378
|
+
/**
|
|
379
|
+
* 两构件对齐检查(方案 C · P3):`<profile>/argent.json` ⇄ 实际引擎 ⇄ 插件声明。
|
|
380
|
+
* ⛔ 只报告,不修;`problems` 为空才算对齐。
|
|
381
|
+
*/
|
|
382
|
+
export function contractStatus({ dshHome = dshHomeOf(), profile = PROFILE_NAME, repo = repoRoot() } = {}) {
|
|
383
|
+
const dir = profileDir(dshHome, profile);
|
|
384
|
+
const file = join(dir, 'argent.json');
|
|
385
|
+
let contract = null;
|
|
386
|
+
try { contract = JSON.parse(readFileSync(file, 'utf8')); } catch { /* 缺文件/坏 JSON 都算未建立 */ }
|
|
387
|
+
const info = engineInfo({ repo });
|
|
388
|
+
const expected = info.ok ? engineContract(info.dir) : null;
|
|
389
|
+
let plugin = null;
|
|
390
|
+
try {
|
|
391
|
+
const pkg = JSON.parse(readFileSync(join(dir, 'node_modules', '@whyshu', 'argent-desktop', 'package.json'), 'utf8'));
|
|
392
|
+
plugin = { version: pkg.version || '', requires: pkg.argentEngine || '', contract: pkg.argentContract ?? null };
|
|
393
|
+
} catch { /* 未装插件(web profile)属正常 */ }
|
|
394
|
+
const problems = [];
|
|
395
|
+
if (!contract) problems.push(`缺少 ${file}(运行 argent setup 生成)`);
|
|
396
|
+
else {
|
|
397
|
+
if (contract.contract == null) problems.push('argent.json 缺 contract');
|
|
398
|
+
else if (expected !== null && contract.contract !== expected) problems.push(`契约不符:argent.json=${contract.contract},引擎=${expected}`);
|
|
399
|
+
if (!contract.engineDir) problems.push('argent.json 未指向 engineDir');
|
|
400
|
+
else if (info.ok && contract.engineVersion !== info.version) problems.push(`引擎版本漂移:argent.json=${contract.engineVersion},实际=${info.version}`);
|
|
401
|
+
}
|
|
402
|
+
if (plugin && plugin.contract != null && expected !== null && plugin.contract !== expected) {
|
|
403
|
+
problems.push(`插件要求契约 ${plugin.contract},引擎提供 ${expected}`);
|
|
404
|
+
}
|
|
405
|
+
return { ok: problems.length === 0, file, contract, engine: info, plugin, expectedContract: expected, problems };
|
|
406
|
+
}
|
|
407
|
+
|
|
319
408
|
export function health({ dshHome = dshHomeOf(), profile = PROFILE_NAME, repo = repoRoot(), dshBin = 'dsh', port = 3080 } = {}) {
|
|
320
409
|
const version = probeDshVersion({ dshBin });
|
|
321
410
|
const dir = profileDir(dshHome, profile);
|
|
@@ -330,6 +419,7 @@ export function health({ dshHome = dshHomeOf(), profile = PROFILE_NAME, repo = r
|
|
|
330
419
|
profile: { dir, packageJson: existsSync(profilePackage), patch: existsSync(profilePatch) },
|
|
331
420
|
runner: { configPath: runnerCfg, configPresent: existsSync(runnerCfg), mode: runnerCfgMode },
|
|
332
421
|
webUrl: `http://127.0.0.1:${port}`,
|
|
422
|
+
contract: contractStatus({ dshHome, profile, repo }),
|
|
333
423
|
repo,
|
|
334
424
|
};
|
|
335
425
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@whyshu/argent",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.24",
|
|
4
4
|
"description": "Argent 启动器:一条命令启动「打包好的 dsh + Argent 插件/角色预设」,并管理本机 runner 常驻。",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -15,7 +15,7 @@
|
|
|
15
15
|
},
|
|
16
16
|
"dependencies": {
|
|
17
17
|
"@deepseek-ai/dsh": "0.2.1-rc.2",
|
|
18
|
-
"@whyshu/argent-engine": "0.8.0-rc.
|
|
18
|
+
"@whyshu/argent-engine": "0.8.0-rc.9"
|
|
19
19
|
},
|
|
20
20
|
"files": [
|
|
21
21
|
"bin/",
|