@harness-mix/cli 0.2.2 → 0.2.4
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 +25 -0
- package/README.md +469 -467
- package/output/native-build/desktop-controller.mjs +1 -1
- package/output/native-build/renderer-extension.js +23 -4
- package/package.json +16 -9
- package/scripts/antigravity-adapter-test.cjs +647 -626
- package/scripts/codex-adapter-test.cjs +162 -127
- package/scripts/collaboration-test.cjs +274 -262
- package/scripts/core-review-test.cjs +68 -0
- package/scripts/delegation-await-test.cjs +76 -0
- package/scripts/jsonl-stdin-test.cjs +40 -0
- package/scripts/kiro-cursor-adapters-test.cjs +124 -100
- package/scripts/native-acp-depth-test.cjs +30 -5
- package/scripts/native-protocol-test.cjs +14 -1
- package/scripts/native-update-apply-test.cjs +269 -215
- package/scripts/native-update.cjs +78 -0
- package/scripts/native-vendor-adapters-test.cjs +196 -154
- package/scripts/salvage-rollout-writes.cjs +72 -0
- package/scripts/send-cancel-race-test.cjs +80 -0
- package/scripts/send-pre-turn-cancel-test.cjs +100 -0
- package/scripts/stuck-turn-test.cjs +6 -1
- package/scripts/zcode-adapter-test.cjs +329 -0
- package/scripts/zcode-live-probe.cjs +66 -0
- package/src/main/adapters/antigravity.js +1428 -1415
- package/src/main/adapters/codex.js +656 -649
- package/src/main/adapters/native-acp-command.js +51 -48
- package/src/main/adapters/native-acp.js +47 -12
- package/src/main/adapters/qoder.js +12 -8
- package/src/main/adapters/zcode.js +921 -10
- package/src/main/harness-adapter/event-normalizer.js +5 -2
- package/src/main/host/collaboration.js +723 -715
- package/src/main/host/jsonl.js +130 -116
- package/src/main/host/runtime.js +30 -14
- package/src/main/native/config.js +9 -9
- package/src/main/native/host.js +2 -0
- package/src/main/native/launcher.js +252 -237
- package/src/main/native/process-utils.js +157 -57
- package/src/main/native/protocol.js +1221 -1177
- package/src/main/native/secure-store.js +2 -0
- package/src/main/native/update-state.js +123 -110
- package/src/main/native/updater.js +460 -394
- package/src/main/workspace/core-review.js +13 -5
- package/src/native-ui/desktop-control/src/renderer-cdp-control-session.ts +358 -358
- package/src/native-ui/renderer-extension/src/renderer-binding-probe.ts +3211 -3181
- package/src/native-ui/renderer-extension/src/settings/connections-page.ts +2 -2
|
@@ -33,6 +33,8 @@ function run(args, input) {
|
|
|
33
33
|
}
|
|
34
34
|
resolve({ missing: false, stdout: Buffer.from(stdout || '') });
|
|
35
35
|
});
|
|
36
|
+
// helper 进程提前退出时 stdin.end 会异步抛 EPIPE,无监听即 crash 宿主;真实错误由 execFile 回调覆盖
|
|
37
|
+
if (child.stdin) child.stdin.on('error', () => {});
|
|
36
38
|
if (input !== undefined && child.stdin) child.stdin.end(Buffer.from(input));
|
|
37
39
|
});
|
|
38
40
|
}
|
|
@@ -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
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
fs.
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
//
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
//
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
if (
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
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
|
+
};
|