@harness-mix/cli 0.2.3 → 0.3.0

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.
Files changed (72) hide show
  1. package/CHANGELOG.md +24 -0
  2. package/README.md +469 -467
  3. package/docs/harness-management.md +1 -1
  4. package/docs/multi-agent-collaboration.md +3 -1
  5. package/docs/native-acp.md +138 -127
  6. package/output/native-build/desktop-controller.mjs +1 -1
  7. package/output/native-build/renderer-extension.js +172 -23
  8. package/package.json +13 -9
  9. package/scripts/acp-image-test.cjs +69 -0
  10. package/scripts/adapters-test.cjs +25 -0
  11. package/scripts/antigravity-adapter-test.cjs +647 -626
  12. package/scripts/codex-accounts-test.cjs +34 -0
  13. package/scripts/codex-adapter-test.cjs +238 -127
  14. package/scripts/collaboration-test.cjs +577 -262
  15. package/scripts/collaboration-ui-smoke.cjs +25 -3
  16. package/scripts/e2e-delegate.cjs +1 -1
  17. package/scripts/e2e-hermes-image.cjs +60 -0
  18. package/scripts/jsonl-stdin-test.cjs +40 -31
  19. package/scripts/kiro-cursor-adapters-test.cjs +124 -100
  20. package/scripts/native-acp-depth-test.cjs +30 -5
  21. package/scripts/native-update-apply-test.cjs +269 -215
  22. package/scripts/native-update.cjs +78 -0
  23. package/scripts/native-vendor-adapters-test.cjs +196 -154
  24. package/scripts/openclaw-adapter-test.cjs +121 -3
  25. package/scripts/openclaw-mcp-probe.cjs +100 -0
  26. package/scripts/openclaw-mcp-tool-probe.cjs +55 -0
  27. package/scripts/openclaw-thinking-probe.cjs +73 -0
  28. package/scripts/salvage-rollout-writes.cjs +72 -0
  29. package/scripts/storage-verification-test.cjs +11 -0
  30. package/scripts/zcode-adapter-test.cjs +329 -0
  31. package/scripts/zcode-live-probe.cjs +66 -0
  32. package/src/main/adapters/antigravity.js +1428 -1418
  33. package/src/main/adapters/claude.js +15 -7
  34. package/src/main/adapters/codex-app-server.js +29 -11
  35. package/src/main/adapters/codex.js +690 -649
  36. package/src/main/adapters/native-acp-command.js +51 -48
  37. package/src/main/adapters/native-acp.js +47 -12
  38. package/src/main/adapters/omp.js +7 -2
  39. package/src/main/adapters/openclaw.js +534 -343
  40. package/src/main/adapters/pi-family.js +35 -8
  41. package/src/main/adapters/qoder.js +12 -8
  42. package/src/main/adapters/zcode.js +925 -10
  43. package/src/main/host/collaboration-tools.js +1 -1
  44. package/src/main/host/collaboration.js +971 -714
  45. package/src/main/host/jsonl.js +130 -120
  46. package/src/main/host/runtime.js +9 -3
  47. package/src/main/host/verification-gates.js +14 -2
  48. package/src/main/native/codex-accounts.js +15 -4
  49. package/src/main/native/config.js +9 -9
  50. package/src/main/native/launcher.js +252 -237
  51. package/src/main/native/process-utils.js +157 -57
  52. package/src/main/native/protocol.js +1227 -1187
  53. package/src/main/native/update-state.js +123 -110
  54. package/src/main/native/updater.js +460 -394
  55. package/src/native-ui/desktop-control/dist/renderer-cdp-control-session.js +3 -1
  56. package/src/native-ui/desktop-control/dist/renderer-cdp-control-session.js.map +1 -1
  57. package/src/native-ui/desktop-control/dist/tsconfig.tsbuildinfo +1 -1
  58. package/src/native-ui/desktop-control/src/renderer-cdp-control-session.ts +358 -358
  59. package/src/native-ui/renderer-extension/dist/types/renderer-binding-probe.d.ts.map +1 -1
  60. package/src/native-ui/renderer-extension/dist/types/renderer-collab-cards.d.ts +1 -0
  61. package/src/native-ui/renderer-extension/dist/types/renderer-collab-cards.d.ts.map +1 -1
  62. package/src/native-ui/renderer-extension/dist/types/renderer-model-client.d.ts +29 -0
  63. package/src/native-ui/renderer-extension/dist/types/renderer-model-client.d.ts.map +1 -1
  64. package/src/native-ui/renderer-extension/dist/types/renderer-team-cards.d.ts +4 -0
  65. package/src/native-ui/renderer-extension/dist/types/renderer-team-cards.d.ts.map +1 -1
  66. package/src/native-ui/renderer-extension/dist/types/tsconfig.tsbuildinfo +1 -1
  67. package/src/native-ui/renderer-extension/src/renderer-binding-probe.ts +3224 -3181
  68. package/src/native-ui/renderer-extension/src/renderer-collab-cards.ts +25 -0
  69. package/src/native-ui/renderer-extension/src/renderer-model-client.ts +27 -0
  70. package/src/native-ui/renderer-extension/src/renderer-team-cards.ts +93 -13
  71. package/src/native-ui/renderer-extension/src/settings/connections-page.ts +2 -2
  72. package/src/native-ui/renderer-extension/test/renderer-model-client.test.ts +47 -1
@@ -1,110 +1,123 @@
1
- // Update bookkeeping for updater.js: channel detection, version compare,
2
- // the update state file and its lock. Pure Node built-ins; `dataDir` is
3
- // injected so tests can use temp directories.
4
- const fs = require('node:fs');
5
- const path = require('node:path');
6
-
7
- const STATE_FILE = 'update-state.json';
8
- const LOCK_FILE = 'update.lock';
9
- const LOCK_STALE_MS = 30 * 60 * 1000;
10
-
11
- function defaultState() {
12
- return {
13
- schema: 1,
14
- channel: null,
15
- phase: 'idle',
16
- appliedVersion: null,
17
- prevVersion: null,
18
- appliedAt: 0,
19
- attempts: 0,
20
- lastBootOkAt: 0,
21
- pendingVersion: null,
22
- lastCheckAt: 0,
23
- preUpdateHead: null,
24
- rolledBackAt: 0,
25
- };
26
- }
27
-
28
- function readState(dataDir) {
29
- try {
30
- const parsed = JSON.parse(fs.readFileSync(path.join(dataDir, STATE_FILE), 'utf8'));
31
- return { ...defaultState(), ...(parsed && typeof parsed === 'object' ? parsed : {}) };
32
- } catch {
33
- return defaultState(); // missing or corrupt: rebuild, never block launch
34
- }
35
- }
36
-
37
- function writeState(dataDir, state) {
38
- fs.mkdirSync(dataDir, { recursive: true });
39
- const file = path.join(dataDir, STATE_FILE);
40
- fs.writeFileSync(`${file}.tmp`, JSON.stringify(state, null, 2));
41
- fs.renameSync(`${file}.tmp`, file);
42
- return state;
43
- }
44
-
45
- function updateState(dataDir, patch) {
46
- return writeState(dataDir, { ...readState(dataDir), ...patch });
47
- }
48
-
49
- // Called by the launcher once the desktop is up and the controller survived long
50
- // enough to count as a healthy boot of the freshly applied version.
51
- function markBootOk(dataDir) {
52
- const state = readState(dataDir);
53
- return writeState(dataDir, { ...state, lastBootOkAt: Date.now(), attempts: 0 });
54
- }
55
-
56
- function pidAlive(pid) {
57
- if (!pid || typeof pid !== 'number') return false;
58
- try { process.kill(pid, 0); return true; }
59
- catch (error) { return error.code === 'EPERM'; }
60
- }
61
-
62
- // Mutating update operations (apply / rollback / repair) take this lock so two
63
- // launchers cannot fight over the same install. Stale locks are reaped.
64
- function acquireLock(dataDir, op) {
65
- fs.mkdirSync(dataDir, { recursive: true });
66
- const file = path.join(dataDir, LOCK_FILE);
67
- let existing = null;
68
- try { existing = JSON.parse(fs.readFileSync(file, 'utf8')); } catch { /* no lock yet */ }
69
- if (existing && pidAlive(existing.pid) && Date.now() - Number(existing.ts || 0) < LOCK_STALE_MS) {
70
- throw new Error(`更新锁被进程 ${existing.pid} 持有(${existing.op || 'update'})`);
71
- }
72
- fs.writeFileSync(file, JSON.stringify({ pid: process.pid, ts: Date.now(), op }));
73
- let released = false;
74
- return function release() {
75
- if (released) return;
76
- released = true;
77
- try {
78
- const current = JSON.parse(fs.readFileSync(file, 'utf8'));
79
- if (current.pid === process.pid) fs.unlinkSync(file);
80
- } catch { /* already gone */ }
81
- };
82
- }
83
-
84
- function compareVersions(a, b) {
85
- const parse = value => String(value || '0').split('-')[0].split('.').map(part => parseInt(part, 10) || 0);
86
- const left = parse(a);
87
- const right = parse(b);
88
- for (let i = 0; i < 3; i += 1) {
89
- const x = left[i] || 0;
90
- const y = right[i] || 0;
91
- if (x !== y) return x > y ? 1 : -1;
92
- }
93
- const preA = String(a || '').includes('-');
94
- const preB = String(b || '').includes('-');
95
- if (preA !== preB) return preA ? -1 : 1; // release beats prerelease
96
- if (String(a) === String(b)) return 0;
97
- return String(a) > String(b) ? 1 : -1; // prerelease ordering is lexical (good enough)
98
- }
99
-
100
- function detectChannel(root) {
101
- if (fs.existsSync(path.join(root, '.git'))) return 'git';
102
- if (path.basename(path.dirname(path.resolve(root))) === 'node_modules') return 'npm';
103
- return 'portable';
104
- }
105
-
106
- module.exports = {
107
- STATE_FILE, LOCK_FILE, LOCK_STALE_MS,
108
- defaultState, readState, writeState, updateState, markBootOk,
109
- acquireLock, pidAlive, compareVersions, detectChannel,
110
- };
1
+ // Update bookkeeping for updater.js: channel detection, version compare,
2
+ // the update state file and its lock. Pure Node built-ins; `dataDir` is
3
+ // injected so tests can use temp directories.
4
+ const fs = require('node:fs');
5
+ const path = require('node:path');
6
+
7
+ const STATE_FILE = 'update-state.json';
8
+ const LOCK_FILE = 'update.lock';
9
+ const LOCK_STALE_MS = 30 * 60 * 1000;
10
+
11
+ function defaultState() {
12
+ return {
13
+ schema: 1,
14
+ channel: null,
15
+ phase: 'idle',
16
+ appliedVersion: null,
17
+ prevVersion: null,
18
+ appliedAt: 0,
19
+ attempts: 0,
20
+ lastBootOkAt: 0,
21
+ pendingVersion: null,
22
+ pendingBuild: null,
23
+ lastCheckAt: 0,
24
+ preUpdateHead: null,
25
+ rolledBackAt: 0,
26
+ };
27
+ }
28
+
29
+ function readState(dataDir) {
30
+ try {
31
+ const parsed = JSON.parse(fs.readFileSync(path.join(dataDir, STATE_FILE), 'utf8'));
32
+ return { ...defaultState(), ...(parsed && typeof parsed === 'object' ? parsed : {}) };
33
+ } catch {
34
+ return defaultState(); // missing or corrupt: rebuild, never block launch
35
+ }
36
+ }
37
+
38
+ function writeState(dataDir, state) {
39
+ fs.mkdirSync(dataDir, { recursive: true });
40
+ const file = path.join(dataDir, STATE_FILE);
41
+ fs.writeFileSync(`${file}.tmp`, JSON.stringify(state, null, 2));
42
+ fs.renameSync(`${file}.tmp`, file);
43
+ return state;
44
+ }
45
+
46
+ function updateState(dataDir, patch) {
47
+ return writeState(dataDir, { ...readState(dataDir), ...patch });
48
+ }
49
+
50
+ // Called by the launcher once the desktop is up and the controller survived long
51
+ // enough to count as a healthy boot of the freshly applied version.
52
+ function markBootOk(dataDir) {
53
+ const state = readState(dataDir);
54
+ return writeState(dataDir, { ...state, lastBootOkAt: Date.now(), attempts: 0 });
55
+ }
56
+
57
+ function pidAlive(pid) {
58
+ if (!pid || typeof pid !== 'number') return false;
59
+ try { process.kill(pid, 0); return true; }
60
+ catch (error) { return error.code === 'EPERM'; }
61
+ }
62
+
63
+ // Mutating update operations (apply / rollback / repair) take this lock so two
64
+ // updaters the launcher at boot and the desktop-triggered helper — cannot
65
+ // fight over the same install. Creation is exclusive (O_EXCL via 'wx'); stale
66
+ // locks are reaped and the create is retried, so a plain read-check-write race
67
+ // between two processes cannot both "win".
68
+ function acquireLock(dataDir, op) {
69
+ fs.mkdirSync(dataDir, { recursive: true });
70
+ const file = path.join(dataDir, LOCK_FILE);
71
+ for (let attempt = 0; ; attempt += 1) {
72
+ try {
73
+ fs.writeFileSync(file, JSON.stringify({ pid: process.pid, ts: Date.now(), op }), { flag: 'wx' });
74
+ break;
75
+ } catch (error) {
76
+ if (error.code !== 'EEXIST') throw error;
77
+ let existing = null;
78
+ try { existing = JSON.parse(fs.readFileSync(file, 'utf8')); } catch { /* corrupt lock */ }
79
+ if (existing && pidAlive(existing.pid) && Date.now() - Number(existing.ts || 0) < LOCK_STALE_MS) {
80
+ throw new Error(`更新锁被进程 ${existing.pid} 持有(${existing.op || 'update'})`);
81
+ }
82
+ if (attempt >= 20) throw new Error('更新锁冲突:过期的更新锁无法清除');
83
+ try { fs.unlinkSync(file); } catch { /* another process reaped it first; retry the create */ }
84
+ }
85
+ }
86
+ let released = false;
87
+ return function release() {
88
+ if (released) return;
89
+ released = true;
90
+ try {
91
+ const current = JSON.parse(fs.readFileSync(file, 'utf8'));
92
+ if (current.pid === process.pid) fs.unlinkSync(file);
93
+ } catch { /* already gone */ }
94
+ };
95
+ }
96
+
97
+ function compareVersions(a, b) {
98
+ const parse = value => String(value || '0').split('-')[0].split('.').map(part => parseInt(part, 10) || 0);
99
+ const left = parse(a);
100
+ const right = parse(b);
101
+ for (let i = 0; i < 3; i += 1) {
102
+ const x = left[i] || 0;
103
+ const y = right[i] || 0;
104
+ if (x !== y) return x > y ? 1 : -1;
105
+ }
106
+ const preA = String(a || '').includes('-');
107
+ const preB = String(b || '').includes('-');
108
+ if (preA !== preB) return preA ? -1 : 1; // release beats prerelease
109
+ if (String(a) === String(b)) return 0;
110
+ return String(a) > String(b) ? 1 : -1; // prerelease ordering is lexical (good enough)
111
+ }
112
+
113
+ function detectChannel(root) {
114
+ if (fs.existsSync(path.join(root, '.git'))) return 'git';
115
+ if (path.basename(path.dirname(path.resolve(root))) === 'node_modules') return 'npm';
116
+ return 'portable';
117
+ }
118
+
119
+ module.exports = {
120
+ STATE_FILE, LOCK_FILE, LOCK_STALE_MS,
121
+ defaultState, readState, writeState, updateState, markBootOk,
122
+ acquireLock, pidAlive, compareVersions, detectChannel,
123
+ };