harness-mix 0.1.2 → 0.1.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/CHANGELOG.md +10 -1
- package/README.md +60 -15
- package/docs/images/codex-desktop-home.png +0 -0
- package/docs/images/codex-desktop-session.png +0 -0
- package/docs/multi-agent-collaboration.md +68 -68
- package/output/native-build/renderer-extension.js +2225 -1761
- package/package.json +1 -2
- package/scripts/collaboration-recovery-test.cjs +22 -2
- package/scripts/collaboration-ui-smoke.cjs +10 -5
- package/scripts/native-protocol-test.cjs +20 -0
- package/scripts/workspace-test.cjs +2 -14
- package/src/main/adapters/antigravity.js +49 -18
- package/src/main/adapters/claude.js +3 -2
- package/src/main/adapters/codex.js +10 -8
- package/src/main/adapters/dsh.js +3 -2
- package/src/main/adapters/opencode.js +21 -13
- package/src/main/adapters/pi-family.js +3 -3
- package/src/main/host/collaboration-worktree.js +63 -4
- package/src/main/host/collaboration.js +28 -2
- package/src/main/host/runtime.js +22 -1
- package/src/main/native/protocol.js +250 -24
- package/src/native-ui/desktop-control/dist/tsconfig.tsbuildinfo +1 -1
- package/src/native-ui/renderer-extension/dist/types/harness-mix-settings.d.ts.map +1 -1
- package/src/native-ui/renderer-extension/dist/types/index.d.ts +2 -0
- package/src/native-ui/renderer-extension/dist/types/index.d.ts.map +1 -1
- package/src/native-ui/renderer-extension/dist/types/renderer-binding-probe.d.ts.map +1 -1
- package/src/native-ui/renderer-extension/dist/types/renderer-collab-cards.d.ts +40 -0
- package/src/native-ui/renderer-extension/dist/types/renderer-collab-cards.d.ts.map +1 -0
- package/src/native-ui/renderer-extension/dist/types/renderer-credits-control.d.ts.map +1 -1
- package/src/native-ui/renderer-extension/dist/types/renderer-harness-mentions.d.ts.map +1 -1
- package/src/native-ui/renderer-extension/dist/types/renderer-model-client.d.ts +8 -0
- package/src/native-ui/renderer-extension/dist/types/renderer-model-client.d.ts.map +1 -1
- package/src/native-ui/renderer-extension/dist/types/renderer-usage-control.d.ts.map +1 -1
- package/src/native-ui/renderer-extension/dist/types/settings/connections-page.d.ts +3 -1
- package/src/native-ui/renderer-extension/dist/types/settings/connections-page.d.ts.map +1 -1
- package/src/native-ui/renderer-extension/dist/types/settings/harness-accounts.d.ts +8 -1
- package/src/native-ui/renderer-extension/dist/types/settings/harness-accounts.d.ts.map +1 -1
- package/src/native-ui/renderer-extension/dist/types/settings/localization.d.ts +16 -0
- package/src/native-ui/renderer-extension/dist/types/settings/localization.d.ts.map +1 -1
- package/src/native-ui/renderer-extension/dist/types/settings/pages.d.ts +1 -0
- package/src/native-ui/renderer-extension/dist/types/settings/pages.d.ts.map +1 -1
- package/src/native-ui/renderer-extension/dist/types/tsconfig.tsbuildinfo +1 -1
- package/src/native-ui/renderer-extension/src/assets/README.md +8 -38
- package/src/native-ui/renderer-extension/src/harness-mix-settings.ts +9 -1
- package/src/native-ui/renderer-extension/src/index.ts +9 -0
- package/src/native-ui/renderer-extension/src/renderer-binding-probe.ts +67 -6
- package/src/native-ui/renderer-extension/src/renderer-collab-cards.ts +198 -0
- package/src/native-ui/renderer-extension/src/renderer-credits-control.ts +32 -12
- package/src/native-ui/renderer-extension/src/renderer-harness-mentions.ts +139 -46
- package/src/native-ui/renderer-extension/src/renderer-model-client.ts +7 -2
- package/src/native-ui/renderer-extension/src/renderer-usage-control.ts +34 -3
- package/src/native-ui/renderer-extension/src/settings/accounts.css +80 -4
- package/src/native-ui/renderer-extension/src/settings/connections-page.ts +68 -29
- package/src/native-ui/renderer-extension/src/settings/harness-accounts.ts +89 -28
- package/src/native-ui/renderer-extension/src/settings/localization.ts +48 -0
- package/src/native-ui/renderer-extension/src/settings/pages.ts +19 -2
- package/src/native-ui/renderer-extension/src/settings/shell.css +62 -0
- package/src/native-ui/renderer-extension/src/settings/shell.ts +1 -1
- package/src/native-ui/renderer-extension/test/renderer-collab-cards.test.ts +45 -0
- package/src/native-ui/renderer-extension/test/renderer-model-client.test.ts +1 -0
- package/src/native-ui/shared-contracts/dist/harness-accounts.d.ts +20 -6
- package/src/native-ui/shared-contracts/dist/harness-accounts.d.ts.map +1 -1
- package/src/native-ui/shared-contracts/dist/harness-accounts.js +4 -1
- package/src/native-ui/shared-contracts/dist/harness-accounts.js.map +1 -1
- package/src/native-ui/shared-contracts/dist/harness-models.d.ts +22 -0
- package/src/native-ui/shared-contracts/dist/harness-models.d.ts.map +1 -1
- package/src/native-ui/shared-contracts/dist/harness-models.js +2 -1
- package/src/native-ui/shared-contracts/dist/harness-models.js.map +1 -1
- package/src/native-ui/shared-contracts/dist/tsconfig.tsbuildinfo +1 -1
- package/src/native-ui/shared-contracts/src/harness-accounts.ts +4 -1
- package/src/native-ui/shared-contracts/src/harness-models.ts +6 -1
- package/docs/images/harness-mix-home.png +0 -0
- package/docs/images/harness-mix-session.png +0 -0
- package/scripts/git-test.cjs +0 -33
- package/src/assets/icons/activity-compact.svg +0 -1
- package/src/assets/icons/activity-edit.svg +0 -1
- package/src/assets/icons/activity-terminal.svg +0 -1
- package/src/assets/icons/commands.svg +0 -1
- package/src/assets/icons/home-chat.svg +0 -1
- package/src/assets/icons/home-folder.svg +0 -1
- package/src/assets/icons/thread-fork.svg +0 -1
- package/src/main/workspace/git.js +0 -73
- package/src/main/workspace/ipc.js +0 -50
- package/src/main/workspace/terminal.js +0 -34
- package/src/native-ui/renderer-extension/src/assets/claude-agent.svg +0 -3
- package/src/native-ui/renderer-extension/src/assets/codex-logo-bright.png +0 -0
- package/src/native-ui/renderer-extension/src/assets/codex-logo-transparent.png +0 -0
- package/src/native-ui/renderer-extension/src/assets/codex-logo.png +0 -0
- package/src/native-ui/renderer-extension/src/assets/codexhost-icon.png +0 -0
- package/src/native-ui/renderer-extension/src/assets/codexhost-logo-transparent.png +0 -0
- package/src/native-ui/renderer-extension/src/assets/codexhost-logo.png +0 -0
- package/src/native-ui/renderer-extension/src/assets/codexhost-readme.svg +0 -8
- package/src/native-ui/renderer-extension/src/assets/pi-agent.svg +0 -4
- package/src/native-ui/renderer-extension/src/settings/accounts-quota-interactive.prototype.html +0 -1418
- package/src/native-ui/renderer-extension/src/settings/update-notes.preview.html +0 -491
- package/src/native-ui/renderer-extension/src/settings/windows-update.preview.html +0 -2135
|
@@ -5,8 +5,26 @@ const { prepareInput } = require('./input');
|
|
|
5
5
|
const { projectUsage, projectAccountCredits } = require('./usage');
|
|
6
6
|
const { exec } = require('node:child_process');
|
|
7
7
|
const os = require('node:os');
|
|
8
|
+
const fs = require('node:fs');
|
|
9
|
+
const path = require('node:path');
|
|
8
10
|
const { randomUUID } = require('node:crypto');
|
|
9
11
|
const { diff } = require('../workspace/diff');
|
|
12
|
+
const {
|
|
13
|
+
fetchLatestVersion,
|
|
14
|
+
remoteState,
|
|
15
|
+
runUpdateFlow,
|
|
16
|
+
resolveRegistry,
|
|
17
|
+
} = require('./updater');
|
|
18
|
+
const {
|
|
19
|
+
detectChannel,
|
|
20
|
+
compareVersions,
|
|
21
|
+
readState,
|
|
22
|
+
} = require('./update-state');
|
|
23
|
+
const { nativeEnvironment } = require('./config');
|
|
24
|
+
|
|
25
|
+
const REPO_ROOT = path.resolve(__dirname, '../../..');
|
|
26
|
+
const PACKAGE_JSON_PATH = path.join(REPO_ROOT, 'package.json');
|
|
27
|
+
|
|
10
28
|
const ALIASES = { workbuddy: 'codebuddy', 'claude-code': 'claude', 'deepseek-harness': 'dsh', 'codex-harness': 'codex' };
|
|
11
29
|
const externalId = id => ({ workbuddy: 'codebuddy', claude: 'claude-code', dsh: 'deepseek-harness', codex: 'codex-harness' }[id] || id);
|
|
12
30
|
const modelRef = model => ({ id: Buffer.from(JSON.stringify({ id: model.id, provider: model.provider })).toString('base64url') });
|
|
@@ -25,12 +43,31 @@ const HARNESS_INSTALL_COMMANDS = {
|
|
|
25
43
|
'deepseek-harness': { win32: 'pip install deepseek-harness', default: 'pip3 install deepseek-harness' },
|
|
26
44
|
opencode: { win32: 'npm install -g opencode-ai', default: 'npm install -g opencode-ai' },
|
|
27
45
|
grok: { win32: 'npm install -g @xai/grok-cli', default: 'npm install -g @xai/grok-cli' },
|
|
28
|
-
omp: { win32: 'npm install -g @oh-my-
|
|
46
|
+
omp: { win32: 'npm install -g @oh-my-pi/pi-coding-agent', default: 'npm install -g @oh-my-pi/pi-coding-agent' },
|
|
29
47
|
antigravity: { win32: 'npm install -g @google/antigravity-cli', default: 'npm install -g @google/antigravity-cli' },
|
|
30
48
|
openclaw: { win32: 'npm install -g openclaw', default: 'npm install -g openclaw' },
|
|
31
49
|
hermes: { win32: 'pip install hermes-agent', default: 'pip3 install hermes-agent' },
|
|
32
50
|
};
|
|
33
51
|
|
|
52
|
+
const HARNESS_AUTH_INFO = {
|
|
53
|
+
codex: { name: 'Codex', plan: 'OpenAI / Codex Desktop', label: 'Codex Desktop 内置登录', loginCommand: 'codex login', configHint: 'Codex 官方桌面客户端内置登录状态' },
|
|
54
|
+
'claude-code': { name: 'Claude Code', plan: 'Anthropic Claude', label: 'Claude Code 授权', loginCommand: 'claude login', configHint: '~/.claude.json 或环境变量 ANTHROPIC_API_KEY' },
|
|
55
|
+
'deepseek-harness': { name: 'DeepSeek Harness', plan: 'DeepSeek API', label: 'DeepSeek API Key', loginCommand: null, configHint: '环境变量 DEEPSEEK_API_KEY' },
|
|
56
|
+
antigravity: { name: 'Antigravity CLI', plan: 'Google Gemini', label: 'Google Cloud ADC 认证', loginCommand: 'gcloud auth application-default login', configHint: 'Google Application Default Credentials (ADC) 或 gcloud 认证' },
|
|
57
|
+
pi: { name: 'Pi', plan: 'Pi Multi-Provider', label: 'Pi 模型凭据配置', loginCommand: null, configHint: '~/.pi/agent/settings.json 或各模型提供商 API Key' },
|
|
58
|
+
omp: { name: 'Oh My Pi', plan: 'Oh My Pi', label: 'OMP 模型凭据配置', loginCommand: null, configHint: '~/.omp/ 配置文件或各 Provider API 密钥' },
|
|
59
|
+
grok: { name: 'Grok', plan: 'xAI Grok', label: 'xAI API Key', loginCommand: null, configHint: '环境变量 XAI_API_KEY' },
|
|
60
|
+
opencode: { name: 'OpenCode', plan: 'OpenCode Providers', label: 'OpenCode Provider 配置', loginCommand: null, configHint: '~/.opencode/ 配置文件' },
|
|
61
|
+
openclaw: { name: 'OpenClaw', plan: 'OpenClaw Gateway', label: 'OpenClaw Token 认证', loginCommand: null, configHint: '环境变量 OPENCLAW_TOKEN 或 gateway.auth 配置' },
|
|
62
|
+
'kiro-cli': { name: 'Kiro', plan: 'AWS / Kiro CLI', label: 'Kiro CLI 授权', loginCommand: 'kiro auth login', configHint: '命令行 kiro auth login 或 ~/.kiro/ 目录' },
|
|
63
|
+
'cursor-cli': { name: 'Cursor', plan: 'Cursor Account', label: 'Cursor 账号认证', loginCommand: 'cursor-cli auth login', configHint: 'Cursor 客户端或 cursor-cli auth 登录' },
|
|
64
|
+
qoder: { name: 'Qoder', plan: 'Qoder AI', label: 'Qoder CLI 认证', loginCommand: 'qoder auth login', configHint: '命令行 qoder auth login 或 ~/.qoder/ 配置' },
|
|
65
|
+
codebuddy: { name: 'CodeBuddy', plan: 'CodeBuddy AI', label: 'CodeBuddy 凭据', loginCommand: null, configHint: 'CodeBuddy 客户端或环境变量配置' },
|
|
66
|
+
hermes: { name: 'Hermes', plan: 'Nous Hermes', label: 'Hermes Agent 配置', loginCommand: null, configHint: '~/.hermes/ 配置文件或各模型 API Key' },
|
|
67
|
+
zcode: { name: 'ZCode', plan: 'ZCode AI', label: 'ZCode 账号与配置', loginCommand: null, configHint: 'ZCode 客户端或配置文件' },
|
|
68
|
+
trae: { name: 'Trae', plan: 'Trae AI', label: 'Trae 账号与配置', loginCommand: null, configHint: 'Trae 客户端登录状态' },
|
|
69
|
+
};
|
|
70
|
+
|
|
34
71
|
const MODEL_REF_ID = /^[A-Za-z0-9._~-]{1,512}$/;
|
|
35
72
|
// Positional suffix contract of the versioned Renderer adapter transport ids.
|
|
36
73
|
// Mirrors transportModelId()/decode*TransportModelId() in
|
|
@@ -130,6 +167,11 @@ function projectItem(item) {
|
|
|
130
167
|
status: terminal(item.status) ? (item.state === 'error' ? 'failed' : 'completed') : 'inProgress',
|
|
131
168
|
senderThreadId: job.parent_thread_id, receiverThreadIds: job.child_thread_id ? [job.child_thread_id] : [],
|
|
132
169
|
prompt: job.task || null, model: null, reasoningEffort: null,
|
|
170
|
+
childThreadId: job.child_thread_id || null, agentType: job.agent_type || null,
|
|
171
|
+
diff: job.diff || null, digest: job.digest || null,
|
|
172
|
+
branch: job.branch || job.workspace?.branch || null,
|
|
173
|
+
workspaceMode: job.workspace?.mode || 'shared',
|
|
174
|
+
applied: !!job.appliedDigest || !!job.applied,
|
|
133
175
|
agentsStates: job.child_thread_id ? { [job.child_thread_id]: { status,
|
|
134
176
|
message: job.attention?.message || job.attention?.title || job.result || job.error || null } } : {} };
|
|
135
177
|
}
|
|
@@ -236,7 +278,7 @@ class NativeProtocol {
|
|
|
236
278
|
const defaultSource = sourceModels.find(m => m && m.isDefault === true) || sourceModels[0];
|
|
237
279
|
const result = { status: 'ready', catalog: { models, ...(defaultSource ? { defaultModel: modelRef(defaultSource) } : {}), thinkingOptions,
|
|
238
280
|
...(defaultThinking ? { defaultThinkingOptionId: defaultThinking } : {}) }, capabilities: this.capabilities(local, catalog) };
|
|
239
|
-
if (catalog.permissionModes?.length) result.permissionModes = { modes: catalog.permissionModes.map(m => ({ id: m.id, label: m.label || m.name || m.id, ...(m.description ? { description: m.description } : {}) })), defaultModeId: catalog.permissionModes.find(m => m.default)?.id || catalog.permissionModes[0].id };
|
|
281
|
+
if (catalog.permissionModes?.length) result.permissionModes = { modes: catalog.permissionModes.map(m => ({ id: m.id, label: m.label || m.name || m.id, ...((m.description || m.hint) ? { description: m.description || m.hint } : {}) })), defaultModeId: catalog.permissionModes.find(m => m.default)?.id || catalog.permissionModes[0].id };
|
|
240
282
|
return result;
|
|
241
283
|
} catch (error) { return { status: 'error', error: { code: 'INSPECTION_FAILED', message: error.message, retryable: true } }; }
|
|
242
284
|
}
|
|
@@ -282,31 +324,196 @@ class NativeProtocol {
|
|
|
282
324
|
if (method === 'codexhost/collaboration/agents') return [...this.runtime.adapters.values()].map(a => ({ id: externalId(a.manifest.id), name: a.manifest.name, available: !!this.runtime.status[a.manifest.id]?.available, lead: a.manifest.capabilities?.collaborationTools === true }));
|
|
283
325
|
const thread = this.thread(params.threadId);
|
|
284
326
|
if (method === 'harness-mix/runtime/inspect') return { owner: 'harness-mix', runtime: 'src/main/host/runtime.js', core: 'src/main/protocol-core/protocol-core.js', threads: this.runtime.threads.length };
|
|
327
|
+
// Updates
|
|
328
|
+
if (method === 'codexhost/update/check') {
|
|
329
|
+
const dataDir = nativeEnvironment().CODEXHOST_DATA_DIR;
|
|
330
|
+
let currentVersion = '0.1.2';
|
|
331
|
+
try {
|
|
332
|
+
currentVersion = JSON.parse(fs.readFileSync(PACKAGE_JSON_PATH, 'utf8')).version || '0.1.2';
|
|
333
|
+
} catch { /* fallback */ }
|
|
334
|
+
const channel = detectChannel(REPO_ROOT);
|
|
335
|
+
let latestVersion = currentVersion;
|
|
336
|
+
let updateAvailable = false;
|
|
337
|
+
let checkError = null;
|
|
338
|
+
|
|
339
|
+
if (channel === 'git') {
|
|
340
|
+
try {
|
|
341
|
+
const remote = remoteState(REPO_ROOT);
|
|
342
|
+
if (remote.state === 'available') {
|
|
343
|
+
updateAvailable = true;
|
|
344
|
+
latestVersion = `${currentVersion}+git.${remote.remote.slice(0, 7)}`;
|
|
345
|
+
} else {
|
|
346
|
+
latestVersion = currentVersion;
|
|
347
|
+
}
|
|
348
|
+
} catch (err) {
|
|
349
|
+
checkError = err.message;
|
|
350
|
+
}
|
|
351
|
+
} else {
|
|
352
|
+
try {
|
|
353
|
+
const fetched = await fetchLatestVersion({
|
|
354
|
+
registry: resolveRegistry(),
|
|
355
|
+
dataDir,
|
|
356
|
+
timeoutMs: 6000,
|
|
357
|
+
});
|
|
358
|
+
if (fetched) {
|
|
359
|
+
latestVersion = fetched;
|
|
360
|
+
updateAvailable = compareVersions(latestVersion, currentVersion) > 0;
|
|
361
|
+
}
|
|
362
|
+
} catch (err) {
|
|
363
|
+
checkError = err.message;
|
|
364
|
+
}
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
return {
|
|
368
|
+
currentVersion,
|
|
369
|
+
installation: channel === 'npm' ? 'npm' : 'windows-installer',
|
|
370
|
+
latestVersion,
|
|
371
|
+
updateAvailable,
|
|
372
|
+
installationAvailable: true,
|
|
373
|
+
releaseNotes: updateAvailable ? `发现新版本 ${latestVersion},支持自动快进更新与安全回滚。` : null,
|
|
374
|
+
releaseNotesUrl: `https://github.com/emo-xiaoyu/harness-mix/releases/tag/v${currentVersion}`,
|
|
375
|
+
status: null,
|
|
376
|
+
error: checkError ? `更新检查失败: ${checkError.slice(0, 450)}` : null,
|
|
377
|
+
};
|
|
378
|
+
}
|
|
379
|
+
if (method === 'codexhost/update/start') {
|
|
380
|
+
const dataDir = nativeEnvironment().CODEXHOST_DATA_DIR;
|
|
381
|
+
const channel = detectChannel(REPO_ROOT);
|
|
382
|
+
let currentVersion = '0.1.2';
|
|
383
|
+
try { currentVersion = JSON.parse(fs.readFileSync(PACKAGE_JSON_PATH, 'utf8')).version || '0.1.2'; } catch {}
|
|
384
|
+
try {
|
|
385
|
+
const outcome = await runUpdateFlow({
|
|
386
|
+
root: REPO_ROOT,
|
|
387
|
+
dataDir,
|
|
388
|
+
mode: 'apply',
|
|
389
|
+
log: (msg) => console.log(msg),
|
|
390
|
+
});
|
|
391
|
+
return {
|
|
392
|
+
status: {
|
|
393
|
+
version: outcome.to || currentVersion,
|
|
394
|
+
installation: channel === 'npm' ? 'npm' : 'windows-installer',
|
|
395
|
+
phase: outcome.updated || outcome.repaired ? 'succeeded' : 'failed',
|
|
396
|
+
updatedAt: Date.now(),
|
|
397
|
+
error: outcome.failed ? (outcome.reason || '更新失败并已回退') : null,
|
|
398
|
+
},
|
|
399
|
+
};
|
|
400
|
+
} catch (err) {
|
|
401
|
+
return {
|
|
402
|
+
status: {
|
|
403
|
+
version: currentVersion,
|
|
404
|
+
installation: channel === 'npm' ? 'npm' : 'windows-installer',
|
|
405
|
+
phase: 'failed',
|
|
406
|
+
updatedAt: Date.now(),
|
|
407
|
+
error: err.message.slice(0, 450),
|
|
408
|
+
},
|
|
409
|
+
};
|
|
410
|
+
}
|
|
411
|
+
}
|
|
412
|
+
if (method === 'codexhost/update/status') {
|
|
413
|
+
const dataDir = nativeEnvironment().CODEXHOST_DATA_DIR;
|
|
414
|
+
const state = readState(dataDir);
|
|
415
|
+
const channel = detectChannel(REPO_ROOT);
|
|
416
|
+
let currentVersion = '0.1.2';
|
|
417
|
+
try { currentVersion = JSON.parse(fs.readFileSync(PACKAGE_JSON_PATH, 'utf8')).version || '0.1.2'; } catch {}
|
|
418
|
+
if (state.phase && state.phase !== 'idle') {
|
|
419
|
+
return {
|
|
420
|
+
status: {
|
|
421
|
+
version: state.pendingVersion || state.appliedVersion || currentVersion,
|
|
422
|
+
installation: channel === 'npm' ? 'npm' : 'windows-installer',
|
|
423
|
+
phase: state.phase === 'applying' ? 'installing' : 'succeeded',
|
|
424
|
+
updatedAt: state.appliedAt || Date.now(),
|
|
425
|
+
error: null,
|
|
426
|
+
},
|
|
427
|
+
};
|
|
428
|
+
}
|
|
429
|
+
return { status: null };
|
|
430
|
+
}
|
|
431
|
+
|
|
285
432
|
// No additional managed accounts: native Codex keeps its own signed-in account.
|
|
286
433
|
if (method === 'codexhost/account/list' || method === 'codexhost/account/refresh') return { accounts: [] };
|
|
434
|
+
if (method === 'codexhost/harness/account/login') {
|
|
435
|
+
const extId = params.harnessId;
|
|
436
|
+
const meta = HARNESS_AUTH_INFO[extId] || HARNESS_AUTH_INFO[ALIASES[extId]];
|
|
437
|
+
const cmd = meta?.loginCommand;
|
|
438
|
+
if (!cmd) throw new Error(`Harness ${extId} 暂不支持命令行直接登录,请参考凭据配置指引进行配置`);
|
|
439
|
+
const title = `登录 ${meta.name || extId}`;
|
|
440
|
+
const execCmd = `cmd.exe /c start "${title}" cmd.exe /k "echo 正在为 ${meta.name} 启动登录认证... && echo 执行命令: ${cmd} && echo. && ${cmd} && echo. && echo [Harness Mix] 登录操作已完成,请关闭此窗口并返回 Harness Mix 刷新状态"`;
|
|
441
|
+
exec(execCmd, { windowsHide: false });
|
|
442
|
+
return { success: true, command: cmd };
|
|
443
|
+
}
|
|
287
444
|
if (method === 'codexhost/harness/accounts/list') {
|
|
288
445
|
const accounts = [];
|
|
289
446
|
for (const adapter of this.runtime.adapters.values()) {
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
447
|
+
const id = adapter.manifest.id;
|
|
448
|
+
const extId = externalId(id);
|
|
449
|
+
const meta = HARNESS_AUTH_INFO[extId] || HARNESS_AUTH_INFO[id] || { name: adapter.manifest.name };
|
|
450
|
+
const isAvailable = !!this.runtime.status[id]?.available;
|
|
451
|
+
|
|
452
|
+
let email = undefined;
|
|
453
|
+
let label = meta.label;
|
|
454
|
+
let plan = meta.plan;
|
|
455
|
+
let credits = undefined;
|
|
456
|
+
let status = isAvailable ? 'ready' : 'not_installed';
|
|
457
|
+
|
|
458
|
+
if (typeof adapter.inspectAccount === 'function') {
|
|
459
|
+
try {
|
|
460
|
+
const raw = await Promise.race([
|
|
461
|
+
Promise.resolve().then(() => adapter.inspectAccount()),
|
|
462
|
+
new Promise((resolve) => setTimeout(() => resolve(null), 5_000)),
|
|
463
|
+
]);
|
|
464
|
+
if (raw && typeof raw === 'object') {
|
|
465
|
+
if (raw.email && typeof raw.email === 'string') email = raw.email.trim();
|
|
466
|
+
if (raw.label && typeof raw.label === 'string') label = raw.label.trim();
|
|
467
|
+
if (raw.plan && typeof raw.plan === 'string') plan = raw.plan.trim();
|
|
468
|
+
if (raw.credits) credits = projectAccountCredits(raw.credits) || undefined;
|
|
469
|
+
status = 'ready';
|
|
470
|
+
}
|
|
471
|
+
} catch {
|
|
472
|
+
// keep default
|
|
473
|
+
}
|
|
309
474
|
}
|
|
475
|
+
|
|
476
|
+
if (!email) {
|
|
477
|
+
if (id === 'codex') {
|
|
478
|
+
status = 'ready';
|
|
479
|
+
email = 'Codex Desktop 会话已就绪';
|
|
480
|
+
} else if (id === 'dsh') {
|
|
481
|
+
if (process.env.DEEPSEEK_API_KEY) {
|
|
482
|
+
status = 'ready';
|
|
483
|
+
email = 'API Key (已设置)';
|
|
484
|
+
} else {
|
|
485
|
+
status = isAvailable ? 'unconfigured' : 'not_installed';
|
|
486
|
+
}
|
|
487
|
+
} else if (id === 'grok') {
|
|
488
|
+
if (process.env.XAI_API_KEY) {
|
|
489
|
+
status = 'ready';
|
|
490
|
+
email = 'xAI API Key (已设置)';
|
|
491
|
+
} else {
|
|
492
|
+
status = isAvailable ? 'unconfigured' : 'not_installed';
|
|
493
|
+
}
|
|
494
|
+
} else if (id === 'openclaw') {
|
|
495
|
+
if (process.env.OPENCLAW_TOKEN) {
|
|
496
|
+
status = 'ready';
|
|
497
|
+
email = 'Gateway Token (已配置)';
|
|
498
|
+
} else {
|
|
499
|
+
status = isAvailable ? 'unconfigured' : 'not_installed';
|
|
500
|
+
}
|
|
501
|
+
} else if (!isAvailable) {
|
|
502
|
+
status = 'not_installed';
|
|
503
|
+
}
|
|
504
|
+
}
|
|
505
|
+
|
|
506
|
+
accounts.push({
|
|
507
|
+
harnessId: extId,
|
|
508
|
+
harnessName: meta.name || adapter.manifest.name,
|
|
509
|
+
status,
|
|
510
|
+
...(email ? { email } : {}),
|
|
511
|
+
...(label ? { label } : {}),
|
|
512
|
+
...(plan ? { plan } : {}),
|
|
513
|
+
...(meta.configHint ? { configHint: meta.configHint } : {}),
|
|
514
|
+
...(meta.loginCommand ? { loginCommand: meta.loginCommand } : {}),
|
|
515
|
+
...(credits ? { credits } : {}),
|
|
516
|
+
});
|
|
310
517
|
}
|
|
311
518
|
return { accounts };
|
|
312
519
|
}
|
|
@@ -317,6 +524,14 @@ class NativeProtocol {
|
|
|
317
524
|
const entry = HARNESS_INSTALL_COMMANDS[params.harnessId] || HARNESS_INSTALL_COMMANDS[local];
|
|
318
525
|
if (!entry) throw new Error(`No installation command available for harness: ${params.harnessId}`);
|
|
319
526
|
const command = (process.platform === 'win32' ? entry.win32 : entry.default) || entry.default;
|
|
527
|
+
|
|
528
|
+
if (params.terminal) {
|
|
529
|
+
const title = `安装 ${params.harnessId}`;
|
|
530
|
+
const cmd = `cmd.exe /c start "${title}" cmd.exe /k "echo 正在安装 ${params.harnessId} (${command})... && ${command} && echo. && echo [Harness Mix] 安装执行完毕,请关闭此窗口并返回 Harness Mix 点击【刷新检测】"`;
|
|
531
|
+
exec(cmd, { windowsHide: false });
|
|
532
|
+
return { success: true, command, stdout: '已在独立终端窗口中启动安装' };
|
|
533
|
+
}
|
|
534
|
+
|
|
320
535
|
return new Promise((resolve) => {
|
|
321
536
|
exec(command, { timeout: 180000, shell: true }, async (err, stdout, stderr) => {
|
|
322
537
|
const adapter = this.runtime.adapters.get(local);
|
|
@@ -345,8 +560,14 @@ class NativeProtocol {
|
|
|
345
560
|
if (method === 'codexhost/thread/inspect') {
|
|
346
561
|
if (!thread) return { owner: 'codex', locked: true };
|
|
347
562
|
const catalog = await this.runtime.describe(thread.harnessId);
|
|
563
|
+
const usage = this.runtime.core.getThread(thread.id)?.usage;
|
|
564
|
+
const adapter = this.runtime.adapters.get(thread.harnessId);
|
|
565
|
+
const rawCredits = adapter && typeof adapter.credits === 'function' ? adapter.credits() : null;
|
|
566
|
+
const credits = projectAccountCredits(rawCredits);
|
|
348
567
|
return { owner: 'external', harnessId: externalId(thread.harnessId), transportModelId: routeModel(externalId(thread.harnessId)), locked: true,
|
|
349
|
-
...this.configuration(thread), history: this.capabilities(thread.harnessId, catalog).history
|
|
568
|
+
...this.configuration(thread), history: this.capabilities(thread.harnessId, catalog).history,
|
|
569
|
+
...(usage ? { usage: projectUsage(usage) } : {}),
|
|
570
|
+
...(credits ? { accountCredits: credits } : {}) };
|
|
350
571
|
}
|
|
351
572
|
if (method === 'thread/start') {
|
|
352
573
|
const route = decodeRoute(params.model);
|
|
@@ -369,6 +590,8 @@ class NativeProtocol {
|
|
|
369
590
|
}
|
|
370
591
|
if (method === 'codexhost/thread/workspace/review') return this.runtime.reviewThreadWorkspace(params.threadId);
|
|
371
592
|
if (method === 'codexhost/thread/workspace/apply') return this.runtime.applyThreadWorkspace(params.threadId, params.digest);
|
|
593
|
+
if (method === 'codexhost/thread/workspace/discard') return this.runtime.discardThreadWorkspace(params.threadId);
|
|
594
|
+
if (method === 'codexhost/thread/workspace/push') return this.runtime.pushThreadWorkspace(params.threadId, params);
|
|
372
595
|
if (!thread) {
|
|
373
596
|
if (method.startsWith('codexhost/')) throw new Error(`Harness Mix does not implement ${method}`);
|
|
374
597
|
return undefined;
|
|
@@ -442,10 +665,13 @@ class NativeProtocol {
|
|
|
442
665
|
if (method === 'codexhost/thread/usage/inspect') {
|
|
443
666
|
const usage = params.refresh === 'exact' ? await this.runtime.refreshUsage(thread.id) : this.runtime.core.getThread(thread.id)?.usage;
|
|
444
667
|
const adapter = this.runtime.adapters.get(thread.harnessId);
|
|
445
|
-
|
|
668
|
+
let rawCredits = adapter && typeof adapter.credits === 'function' ? adapter.credits() : null;
|
|
669
|
+
if (!rawCredits && adapter && typeof adapter.refreshCredits === 'function') {
|
|
670
|
+
try { rawCredits = await adapter.refreshCredits(); } catch {}
|
|
671
|
+
} else if (adapter && params.refresh === 'exact' && typeof adapter.refreshCredits === 'function') {
|
|
446
672
|
try { await adapter.refreshCredits(); } catch {}
|
|
673
|
+
rawCredits = adapter && typeof adapter.credits === 'function' ? adapter.credits() : null;
|
|
447
674
|
}
|
|
448
|
-
const rawCredits = adapter && typeof adapter.credits === 'function' ? adapter.credits() : null;
|
|
449
675
|
const credits = projectAccountCredits(rawCredits);
|
|
450
676
|
return {
|
|
451
677
|
threadId: thread.id,
|