@quantiya/codevibe-codex-plugin 2.0.10 → 2.0.13
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/dist/server.js +31 -24
- package/libexec/codex-hook-config.js +190 -0
- package/libexec/companion-launcher +9 -158
- package/node_modules/@quantiya/codevibe-core/dist/appsync/__tests__/appsync-client-flag-locator.test.d.ts +1 -0
- package/node_modules/@quantiya/codevibe-core/dist/appsync/__tests__/dispose-watcher-socket.test.d.ts +1 -0
- package/node_modules/@quantiya/codevibe-core/dist/appsync/appsync-client.d.ts +62 -0
- package/node_modules/@quantiya/codevibe-core/dist/auth/__tests__/keychain-cli-nudge.test.d.ts +1 -0
- package/node_modules/@quantiya/codevibe-core/dist/auth/keychain-cli.d.ts +8 -0
- package/node_modules/@quantiya/codevibe-core/dist/index.js +523 -351
- package/node_modules/@quantiya/codevibe-core/dist/keychain/__tests__/migrate-nudge.test.d.ts +1 -0
- package/node_modules/@quantiya/codevibe-core/dist/keychain/keychain-migrate.d.ts +17 -0
- package/node_modules/@quantiya/codevibe-core/dist/keychain/migrate-nudge.d.ts +38 -0
- package/node_modules/@quantiya/codevibe-core/dist/local-executor/__tests__/protocol-backlog-wiring.test.d.ts +1 -0
- package/node_modules/@quantiya/codevibe-core/dist/local-executor/__tests__/protocol-backlog.test.d.ts +1 -0
- package/node_modules/@quantiya/codevibe-core/dist/local-executor/__tests__/workspace-cleanup-audit.test.d.ts +1 -0
- package/node_modules/@quantiya/codevibe-core/dist/local-executor/anchored-fs.d.ts +7 -0
- package/node_modules/@quantiya/codevibe-core/dist/local-executor/bounded-directory.d.ts +14 -0
- package/node_modules/@quantiya/codevibe-core/dist/local-executor/process-identity.d.ts +30 -0
- package/node_modules/@quantiya/codevibe-core/dist/local-executor/process-tree.d.ts +1 -1
- package/node_modules/@quantiya/codevibe-core/dist/local-executor/protocol-backlog.d.ts +62 -0
- package/node_modules/@quantiya/codevibe-core/dist/local-executor/workspace-cleanup-audit.d.ts +3 -0
- package/node_modules/@quantiya/codevibe-core/dist/local-executor/workspace-materializer.d.ts +23 -2
- package/node_modules/@quantiya/codevibe-core/dist/local-executor/workspace-shadow.d.ts +2 -0
- package/node_modules/@quantiya/codevibe-core/dist/orchestration-shell/__tests__/direct-file-delete.test.d.ts +1 -0
- package/node_modules/@quantiya/codevibe-core/dist/orchestration-shell/__tests__/flag-command.test.d.ts +1 -0
- package/node_modules/@quantiya/codevibe-core/dist/orchestration-shell/__tests__/halt-message-wire-content.test.d.ts +1 -0
- package/node_modules/@quantiya/codevibe-core/dist/orchestration-shell/audit-browser.d.ts +54 -1
- package/node_modules/@quantiya/codevibe-core/dist/orchestration-shell/cli.d.ts +10 -0
- package/node_modules/@quantiya/codevibe-core/dist/orchestration-shell/cli.js +2711 -1474
- package/node_modules/@quantiya/codevibe-core/dist/orchestration-shell/context-items.d.ts +3 -0
- package/node_modules/@quantiya/codevibe-core/dist/orchestration-shell/direct-file-delete.d.ts +22 -0
- package/node_modules/@quantiya/codevibe-core/dist/orchestration-shell/flag-command.d.ts +81 -0
- package/node_modules/@quantiya/codevibe-core/dist/orchestration-shell/gate-prompts.d.ts +41 -0
- package/node_modules/@quantiya/codevibe-core/dist/orchestration-shell/index.d.ts +31 -6
- package/node_modules/@quantiya/codevibe-core/dist/orchestration-shell/quorum-loop.d.ts +42 -1
- package/node_modules/@quantiya/codevibe-core/dist/orchestration-shell/task-progress.d.ts +3 -0
- package/node_modules/@quantiya/codevibe-core/dist/orchestration-shell/types.d.ts +58 -0
- package/node_modules/@quantiya/codevibe-core/dist/orchestration-shell/workspace-shutdown.d.ts +20 -0
- package/node_modules/@quantiya/codevibe-core/dist/reviewer/providers/codex.d.ts +5 -3
- package/node_modules/@quantiya/codevibe-core/dist/reviewer/token-usage.d.ts +41 -12
- package/node_modules/@quantiya/codevibe-core/dist/reviewer/types.d.ts +3 -0
- package/node_modules/@quantiya/codevibe-core/dist/substrate/__tests__/command-runner.test.d.ts +1 -0
- package/node_modules/@quantiya/codevibe-core/dist/substrate/types.d.ts +8 -0
- package/node_modules/@quantiya/codevibe-core/dist/track-presentation.d.ts +2 -0
- package/node_modules/@quantiya/codevibe-core/dist/track-presentation.test.d.ts +1 -0
- package/node_modules/@quantiya/codevibe-core/package.json +1 -1
- package/node_modules/fs-ext/build/Makefile +2 -2
- package/node_modules/fs-ext/build/Release/fs_ext.node +0 -0
- package/node_modules/fs-ext/build/Release/obj.target/fs_ext/fs-ext.o +0 -0
- package/node_modules/fs-ext/build/config.gypi +1 -1
- package/package.json +5 -2
- package/node_modules/@iarna/toml/CHANGELOG.md +0 -278
- package/node_modules/color-convert/CHANGELOG.md +0 -54
- package/node_modules/deep-extend/CHANGELOG.md +0 -46
- package/node_modules/es-toolkit/CHANGELOG.md +0 -810
- package/node_modules/es-toolkit/src/compat/_internal/Equals.d.ts +0 -1
- package/node_modules/es-toolkit/src/compat/_internal/IsWritable.d.ts +0 -3
- package/node_modules/es-toolkit/src/compat/_internal/MutableList.d.ts +0 -4
- package/node_modules/es-toolkit/src/compat/_internal/RejectReadonly.d.ts +0 -4
- package/node_modules/keytar/build/Release/keytar.node +0 -0
- package/node_modules/which/CHANGELOG.md +0 -166
|
@@ -0,0 +1,190 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
'use strict';
|
|
4
|
+
|
|
5
|
+
const fs = require('fs');
|
|
6
|
+
|
|
7
|
+
const OUR_HOOK_FILES = new Set([
|
|
8
|
+
'session-start.sh',
|
|
9
|
+
'user-prompt.sh',
|
|
10
|
+
'pre-tool-use.sh',
|
|
11
|
+
'permission-request.sh',
|
|
12
|
+
'post-tool-use.sh',
|
|
13
|
+
'stop.sh',
|
|
14
|
+
]);
|
|
15
|
+
|
|
16
|
+
function hookScriptPath(command) {
|
|
17
|
+
if (typeof command !== 'string') return null;
|
|
18
|
+
const match = command.trim().match(/^bash\s+(?:"([^"]+)"|'([^']+)'|(\S+))$/);
|
|
19
|
+
return match ? (match[1] || match[2] || match[3]) : null;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
function isOurCommand(command) {
|
|
23
|
+
const scriptPath = hookScriptPath(command);
|
|
24
|
+
if (!scriptPath) return false;
|
|
25
|
+
|
|
26
|
+
const filename = scriptPath.slice(scriptPath.lastIndexOf('/') + 1);
|
|
27
|
+
if (!OUR_HOOK_FILES.has(filename) || !scriptPath.endsWith(`/hooks/${filename}`)) {
|
|
28
|
+
return false;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
if (scriptPath.includes('/codevibe-codex-plugin/hooks/')) {
|
|
32
|
+
return true;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
// Development links can use task-specific repository names rather than the
|
|
36
|
+
// canonical package directory. Keep this deliberately limited to CodeVibe's
|
|
37
|
+
// agent-worktree layout so an unrelated hook with the same filename survives.
|
|
38
|
+
return /\/\.agent-worktrees\/[^/]+\/codevibe-codex-[^/]+\/hooks\/[^/]+$/.test(scriptPath);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
function stripOurHooksFromEntry(entry) {
|
|
42
|
+
if (!entry || typeof entry !== 'object' || !Array.isArray(entry.hooks)) {
|
|
43
|
+
return entry;
|
|
44
|
+
}
|
|
45
|
+
const filtered = entry.hooks.filter((hook) => !(hook && isOurCommand(hook.command)));
|
|
46
|
+
if (filtered.length === entry.hooks.length) return entry;
|
|
47
|
+
if (filtered.length === 0) return null;
|
|
48
|
+
return { ...entry, hooks: filtered };
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
function ownedCommands(entries) {
|
|
52
|
+
return entries
|
|
53
|
+
.flatMap((entry) => entry && typeof entry === 'object' && Array.isArray(entry.hooks)
|
|
54
|
+
? entry.hooks
|
|
55
|
+
: [])
|
|
56
|
+
.map((hook) => hook && hook.command)
|
|
57
|
+
.filter(isOurCommand)
|
|
58
|
+
.sort();
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
function mergeHookConfig(existingObj, nextObj, existed = true) {
|
|
62
|
+
const existingHooks = existingObj && typeof existingObj === 'object'
|
|
63
|
+
&& existingObj.hooks && typeof existingObj.hooks === 'object'
|
|
64
|
+
? existingObj.hooks
|
|
65
|
+
: {};
|
|
66
|
+
const nextHooks = nextObj && typeof nextObj === 'object'
|
|
67
|
+
&& nextObj.hooks && typeof nextObj.hooks === 'object'
|
|
68
|
+
? nextObj.hooks
|
|
69
|
+
: {};
|
|
70
|
+
|
|
71
|
+
let allPresent = existed;
|
|
72
|
+
for (const key of Object.keys(nextHooks)) {
|
|
73
|
+
const existingEntries = Array.isArray(existingHooks[key]) ? existingHooks[key] : [];
|
|
74
|
+
const nextEntries = Array.isArray(nextHooks[key]) ? nextHooks[key] : [];
|
|
75
|
+
if (JSON.stringify(ownedCommands(existingEntries)) !== JSON.stringify(ownedCommands(nextEntries))) {
|
|
76
|
+
allPresent = false;
|
|
77
|
+
break;
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
if (allPresent) {
|
|
81
|
+
return { outcome: 'already_installed', config: existingObj };
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
const mergedHooks = { ...existingHooks };
|
|
85
|
+
for (const key of Object.keys(nextHooks)) {
|
|
86
|
+
const existingEntries = Array.isArray(existingHooks[key]) ? existingHooks[key] : [];
|
|
87
|
+
const cleanedExisting = existingEntries
|
|
88
|
+
.map(stripOurHooksFromEntry)
|
|
89
|
+
.filter((entry) => entry !== null && entry !== undefined);
|
|
90
|
+
const nextEntries = Array.isArray(nextHooks[key]) ? nextHooks[key] : [];
|
|
91
|
+
mergedHooks[key] = [...cleanedExisting, ...nextEntries];
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
return {
|
|
95
|
+
outcome: existed ? 'merged' : 'fresh_install',
|
|
96
|
+
config: existingObj && typeof existingObj === 'object'
|
|
97
|
+
? { ...existingObj, hooks: mergedHooks }
|
|
98
|
+
: { hooks: mergedHooks },
|
|
99
|
+
};
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
function cleanHookConfig(existingObj) {
|
|
103
|
+
if (!existingObj || typeof existingObj !== 'object'
|
|
104
|
+
|| !existingObj.hooks || typeof existingObj.hooks !== 'object') {
|
|
105
|
+
return existingObj;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
const hooks = { ...existingObj.hooks };
|
|
109
|
+
for (const key of Object.keys(hooks)) {
|
|
110
|
+
if (!Array.isArray(hooks[key])) continue;
|
|
111
|
+
hooks[key] = hooks[key]
|
|
112
|
+
.map(stripOurHooksFromEntry)
|
|
113
|
+
.filter((entry) => entry !== null && entry !== undefined);
|
|
114
|
+
}
|
|
115
|
+
return { ...existingObj, hooks };
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
function writeAtomic(target, value) {
|
|
119
|
+
const temporary = `${target}.${process.pid}.${Date.now()}.tmp`;
|
|
120
|
+
try {
|
|
121
|
+
fs.writeFileSync(temporary, JSON.stringify(value, null, 2));
|
|
122
|
+
fs.renameSync(temporary, target);
|
|
123
|
+
} catch (error) {
|
|
124
|
+
try { fs.unlinkSync(temporary); } catch {}
|
|
125
|
+
throw error;
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
function runMerge() {
|
|
130
|
+
const target = process.env.CV_EXISTING;
|
|
131
|
+
let existingObj = null;
|
|
132
|
+
let existed = false;
|
|
133
|
+
try {
|
|
134
|
+
if (fs.existsSync(target)) {
|
|
135
|
+
existed = true;
|
|
136
|
+
existingObj = JSON.parse(fs.readFileSync(target, 'utf8'));
|
|
137
|
+
}
|
|
138
|
+
} catch (error) {
|
|
139
|
+
process.stderr.write(`PARSE_EXISTING_FAILED:${error.message || String(error)}`);
|
|
140
|
+
return 2;
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
let nextObj;
|
|
144
|
+
try {
|
|
145
|
+
nextObj = JSON.parse(process.env.CV_NEW);
|
|
146
|
+
} catch (error) {
|
|
147
|
+
process.stderr.write(`PARSE_NEW_FAILED:${error.message || String(error)}`);
|
|
148
|
+
return 3;
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
const result = mergeHookConfig(existingObj, nextObj, existed);
|
|
152
|
+
if (result.outcome !== 'already_installed') {
|
|
153
|
+
try {
|
|
154
|
+
writeAtomic(target, result.config);
|
|
155
|
+
} catch (error) {
|
|
156
|
+
process.stderr.write(`WRITE_FAILED:${error.message || String(error)}`);
|
|
157
|
+
return 4;
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
process.stdout.write(`OUTCOME:${result.outcome}\n`);
|
|
161
|
+
return 0;
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
function runCleanup() {
|
|
165
|
+
const target = process.env.CV_HOOKS_FILE;
|
|
166
|
+
try {
|
|
167
|
+
const existingObj = JSON.parse(fs.readFileSync(target, 'utf8'));
|
|
168
|
+
writeAtomic(target, cleanHookConfig(existingObj));
|
|
169
|
+
return 0;
|
|
170
|
+
} catch (error) {
|
|
171
|
+
process.stderr.write(`CLEAN_FAILED:${error.message || String(error)}`);
|
|
172
|
+
return 1;
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
if (require.main === module) {
|
|
177
|
+
const action = process.argv[2];
|
|
178
|
+
const exitCode = action === 'merge'
|
|
179
|
+
? runMerge()
|
|
180
|
+
: action === 'cleanup'
|
|
181
|
+
? runCleanup()
|
|
182
|
+
: 64;
|
|
183
|
+
process.exit(exitCode);
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
module.exports = {
|
|
187
|
+
cleanHookConfig,
|
|
188
|
+
isOurCommand,
|
|
189
|
+
mergeHookConfig,
|
|
190
|
+
};
|
|
@@ -259,55 +259,10 @@ cleanup() {
|
|
|
259
259
|
# Only remove if no other codevibe-codex sessions are running
|
|
260
260
|
if [ "$(pgrep -f 'codevibe-codex' | wc -l)" -le 1 ]; then
|
|
261
261
|
log "Removing CodeVibe hooks from $HOME/.codex/hooks.json"
|
|
262
|
-
#
|
|
263
|
-
#
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
if CV_HOOKS_FILE="$HOME/.codex/hooks.json" node -e '
|
|
267
|
-
const fs = require("fs");
|
|
268
|
-
const OUR_HOOK_FILES = new Set([
|
|
269
|
-
"session-start.sh",
|
|
270
|
-
"user-prompt.sh",
|
|
271
|
-
"pre-tool-use.sh",
|
|
272
|
-
"post-tool-use.sh",
|
|
273
|
-
"stop.sh",
|
|
274
|
-
]);
|
|
275
|
-
const OWN_PATH_MARKER = "codevibe-codex-plugin/hooks/";
|
|
276
|
-
const isOurCommand = (command) => {
|
|
277
|
-
if (typeof command !== "string") return false;
|
|
278
|
-
if (command.indexOf(OWN_PATH_MARKER) === -1) return false;
|
|
279
|
-
for (const f of OUR_HOOK_FILES) {
|
|
280
|
-
if (command.endsWith("/" + f)) return true;
|
|
281
|
-
}
|
|
282
|
-
return false;
|
|
283
|
-
};
|
|
284
|
-
const stripOurHooksFromEntry = (entry) => {
|
|
285
|
-
if (!entry || typeof entry !== "object" || !Array.isArray(entry.hooks)) return entry;
|
|
286
|
-
const filtered = entry.hooks.filter((h) => !(h && isOurCommand(h.command)));
|
|
287
|
-
if (filtered.length === entry.hooks.length) return entry;
|
|
288
|
-
if (filtered.length === 0) return null;
|
|
289
|
-
return { ...entry, hooks: filtered };
|
|
290
|
-
};
|
|
291
|
-
try {
|
|
292
|
-
const target = process.env.CV_HOOKS_FILE;
|
|
293
|
-
const data = JSON.parse(fs.readFileSync(target, "utf8"));
|
|
294
|
-
if (data && typeof data === "object" && data.hooks && typeof data.hooks === "object") {
|
|
295
|
-
for (const k of Object.keys(data.hooks)) {
|
|
296
|
-
if (Array.isArray(data.hooks[k])) {
|
|
297
|
-
data.hooks[k] = data.hooks[k]
|
|
298
|
-
.map(stripOurHooksFromEntry)
|
|
299
|
-
.filter((entry) => entry !== null && entry !== undefined);
|
|
300
|
-
}
|
|
301
|
-
}
|
|
302
|
-
}
|
|
303
|
-
const tmp = target + "." + process.pid + "." + Date.now() + ".tmp";
|
|
304
|
-
fs.writeFileSync(tmp, JSON.stringify(data, null, 2));
|
|
305
|
-
fs.renameSync(tmp, target);
|
|
306
|
-
} catch (e) {
|
|
307
|
-
process.stderr.write("CLEAN_FAILED:" + (e && e.message ? e.message : String(e)));
|
|
308
|
-
process.exit(1);
|
|
309
|
-
}
|
|
310
|
-
' 2>>"$LOG_FILE"; then
|
|
262
|
+
# The shared helper keeps install and cleanup ownership rules in
|
|
263
|
+
# lockstep, including task-named CodeVibe agent worktrees.
|
|
264
|
+
if CV_HOOKS_FILE="$HOME/.codex/hooks.json" \
|
|
265
|
+
node "$PLUGIN_DIR/libexec/codex-hook-config.js" cleanup 2>>"$LOG_FILE"; then
|
|
311
266
|
:
|
|
312
267
|
else
|
|
313
268
|
log "WARN: failed to clean CodeVibe hooks from hooks.json (left as-is)"
|
|
@@ -539,9 +494,9 @@ _CV_HOOKS_REASON=""
|
|
|
539
494
|
# keeps the user hook (only the CodeVibe hooks get stripped, not
|
|
540
495
|
# the surrounding entry).
|
|
541
496
|
#
|
|
542
|
-
# - Ownership predicate:
|
|
543
|
-
#
|
|
544
|
-
# Rejects user paths
|
|
497
|
+
# - Ownership predicate: canonical package paths or CodeVibe's strict
|
|
498
|
+
# `.agent-worktrees/<agent>/codevibe-codex-*/hooks/` layout, plus a
|
|
499
|
+
# filename allowlist. Rejects unrelated user hook paths.
|
|
545
500
|
#
|
|
546
501
|
# - Per-process tmp file (.<pid>.<time>.tmp): two concurrent installs
|
|
547
502
|
# won't clobber each other's intermediate state. Final rename is
|
|
@@ -555,112 +510,8 @@ _CV_HOOKS_REASON=""
|
|
|
555
510
|
GENERATED_HOOKS=$(generate_hooks_json)
|
|
556
511
|
if INSTALLER_OUTPUT=$(CV_EXISTING="$CODEX_HOOKS_FILE" \
|
|
557
512
|
CV_NEW="$GENERATED_HOOKS" \
|
|
558
|
-
node -
|
|
559
|
-
|
|
560
|
-
const OUR_HOOK_FILES = new Set([
|
|
561
|
-
"session-start.sh",
|
|
562
|
-
"user-prompt.sh",
|
|
563
|
-
"pre-tool-use.sh",
|
|
564
|
-
"permission-request.sh",
|
|
565
|
-
"post-tool-use.sh",
|
|
566
|
-
"stop.sh",
|
|
567
|
-
]);
|
|
568
|
-
const OWN_PATH_MARKER = "codevibe-codex-plugin/hooks/";
|
|
569
|
-
const isOurCommand = (command) => {
|
|
570
|
-
if (typeof command !== "string") return false;
|
|
571
|
-
if (command.indexOf(OWN_PATH_MARKER) === -1) return false;
|
|
572
|
-
for (const f of OUR_HOOK_FILES) {
|
|
573
|
-
if (command.endsWith("/" + f)) return true;
|
|
574
|
-
}
|
|
575
|
-
return false;
|
|
576
|
-
};
|
|
577
|
-
// Strip our hooks from a single matcher entrys inner hooks[] array.
|
|
578
|
-
// Returns null if NO non-owned hooks remain (whole entry should be
|
|
579
|
-
// dropped); otherwise returns a new entry with only the user-owned
|
|
580
|
-
// hooks. Returns the original entry unchanged if it contains none of
|
|
581
|
-
// ours (preserves identity for unrelated entries).
|
|
582
|
-
const stripOurHooksFromEntry = (entry) => {
|
|
583
|
-
if (!entry || typeof entry !== "object" || !Array.isArray(entry.hooks)) {
|
|
584
|
-
return entry;
|
|
585
|
-
}
|
|
586
|
-
const filtered = entry.hooks.filter((h) => !(h && isOurCommand(h.command)));
|
|
587
|
-
if (filtered.length === entry.hooks.length) return entry;
|
|
588
|
-
if (filtered.length === 0) return null;
|
|
589
|
-
return { ...entry, hooks: filtered };
|
|
590
|
-
};
|
|
591
|
-
const ownedCommands = (entries) => entries
|
|
592
|
-
.flatMap((entry) => entry && typeof entry === "object" && Array.isArray(entry.hooks)
|
|
593
|
-
? entry.hooks
|
|
594
|
-
: [])
|
|
595
|
-
.map((hook) => hook && hook.command)
|
|
596
|
-
.filter(isOurCommand)
|
|
597
|
-
.sort();
|
|
598
|
-
|
|
599
|
-
let existingObj = null;
|
|
600
|
-
let existed = false;
|
|
601
|
-
try {
|
|
602
|
-
if (fs.existsSync(process.env.CV_EXISTING)) {
|
|
603
|
-
existed = true;
|
|
604
|
-
existingObj = JSON.parse(fs.readFileSync(process.env.CV_EXISTING, "utf8"));
|
|
605
|
-
}
|
|
606
|
-
} catch (e) {
|
|
607
|
-
process.stderr.write("PARSE_EXISTING_FAILED:" + (e && e.message ? e.message : String(e)));
|
|
608
|
-
process.exit(2);
|
|
609
|
-
}
|
|
610
|
-
let nextObj;
|
|
611
|
-
try {
|
|
612
|
-
nextObj = JSON.parse(process.env.CV_NEW);
|
|
613
|
-
} catch (e) {
|
|
614
|
-
process.stderr.write("PARSE_NEW_FAILED:" + (e && e.message ? e.message : String(e)));
|
|
615
|
-
process.exit(3);
|
|
616
|
-
}
|
|
617
|
-
|
|
618
|
-
const existingHooks = (existingObj && typeof existingObj === "object" &&
|
|
619
|
-
existingObj.hooks && typeof existingObj.hooks === "object") ? existingObj.hooks : {};
|
|
620
|
-
const nextHooks = (nextObj && typeof nextObj === "object" &&
|
|
621
|
-
nextObj.hooks && typeof nextObj.hooks === "object") ? nextObj.hooks : {};
|
|
622
|
-
let allPresent = existed;
|
|
623
|
-
for (const k of Object.keys(nextHooks)) {
|
|
624
|
-
const existingArr = Array.isArray(existingHooks[k]) ? existingHooks[k] : [];
|
|
625
|
-
const nextArr = Array.isArray(nextHooks[k]) ? nextHooks[k] : [];
|
|
626
|
-
if (JSON.stringify(ownedCommands(existingArr)) !== JSON.stringify(ownedCommands(nextArr))) {
|
|
627
|
-
allPresent = false;
|
|
628
|
-
break;
|
|
629
|
-
}
|
|
630
|
-
}
|
|
631
|
-
if (allPresent) {
|
|
632
|
-
process.stdout.write("OUTCOME:already_installed\n");
|
|
633
|
-
process.exit(0);
|
|
634
|
-
}
|
|
635
|
-
|
|
636
|
-
const mergedHooks = { ...existingHooks };
|
|
637
|
-
for (const k of Object.keys(nextHooks)) {
|
|
638
|
-
const existingArr = Array.isArray(existingHooks[k]) ? existingHooks[k] : [];
|
|
639
|
-
const cleanedExisting = existingArr
|
|
640
|
-
.map(stripOurHooksFromEntry)
|
|
641
|
-
.filter((entry) => entry !== null && entry !== undefined);
|
|
642
|
-
const nextArr = Array.isArray(nextHooks[k]) ? nextHooks[k] : [];
|
|
643
|
-
mergedHooks[k] = [...cleanedExisting, ...nextArr];
|
|
644
|
-
}
|
|
645
|
-
|
|
646
|
-
const out = (existingObj && typeof existingObj === "object")
|
|
647
|
-
? { ...existingObj, hooks: mergedHooks }
|
|
648
|
-
: { hooks: mergedHooks };
|
|
649
|
-
const outcome = existed ? "merged" : "fresh_install";
|
|
650
|
-
const target = process.env.CV_EXISTING;
|
|
651
|
-
// Per-process tmp suffix avoids two concurrent installs both writing
|
|
652
|
-
// ${target}.tmp and one mvs the others half-written file.
|
|
653
|
-
const tmp = target + "." + process.pid + "." + Date.now() + ".tmp";
|
|
654
|
-
try {
|
|
655
|
-
fs.writeFileSync(tmp, JSON.stringify(out, null, 2));
|
|
656
|
-
fs.renameSync(tmp, target);
|
|
657
|
-
} catch (e) {
|
|
658
|
-
try { fs.unlinkSync(tmp); } catch {}
|
|
659
|
-
process.stderr.write("WRITE_FAILED:" + (e && e.message ? e.message : String(e)));
|
|
660
|
-
process.exit(4);
|
|
661
|
-
}
|
|
662
|
-
process.stdout.write("OUTCOME:" + outcome + "\n");
|
|
663
|
-
' 2>>"$LOG_FILE"); then
|
|
513
|
+
node "$PLUGIN_DIR/libexec/codex-hook-config.js" merge \
|
|
514
|
+
2>>"$LOG_FILE"); then
|
|
664
515
|
INSTALLER_RC=0
|
|
665
516
|
else
|
|
666
517
|
INSTALLER_RC=$?
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
package/node_modules/@quantiya/codevibe-core/dist/appsync/__tests__/dispose-watcher-socket.test.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -1,8 +1,30 @@
|
|
|
1
|
+
import WebSocket from 'ws';
|
|
1
2
|
import { CreateEventInput, CreateSessionInput, UpdateSessionInput, UpdateEventStatusInput, Event, Session, EventSource, DeviceKey, GrantSessionKeyInput, UpdateReviewerPolicyInput, UserReviewerPolicySnapshot } from '../types';
|
|
2
3
|
import type { PostDecisionAction } from '../orchestration-shell/types';
|
|
4
|
+
import type { InvocationUsageSnapshot } from '../reviewer/token-usage';
|
|
3
5
|
import type { CreateTaskGroupInput, CreateTaskGroupResult, SubmitMergeGateVerdictInput, SubmitMergeGateVerdictResult, SubmitTrackVerificationOutcomeInput, SubmitTrackVerificationOutcomeResult, ClaimGroupDecisionInput, ClaimGroupDecisionResult } from '../local-executor/task-group-types';
|
|
4
6
|
import type { ReviewerVerdict, ReviewerRole, AgentKind as ReviewerAgentKind } from '../reviewer/types';
|
|
5
7
|
import type { PlannerDecision } from '../planner/types';
|
|
8
|
+
/**
|
|
9
|
+
* Dispose a watcher/subscription WebSocket without crashing the process
|
|
10
|
+
* (#657 D-1 fix-forward — the clean-quit session/admission leak).
|
|
11
|
+
*
|
|
12
|
+
* `ws.close()` on a still-CONNECTING socket aborts the handshake and emits an
|
|
13
|
+
* ASYNC 'error' ("WebSocket was closed before the connection was established")
|
|
14
|
+
* on a LATER tick. Every prior disposal site paired `close(1000)` with
|
|
15
|
+
* `removeAllListeners()` — in either order — so that late emission always
|
|
16
|
+
* found a listener-less socket and crashed Node with an unhandled 'error'
|
|
17
|
+
* mid-teardown, BEFORE `retireHostedSession` could land the session INACTIVE
|
|
18
|
+
* mutation. Net effect: every clean `/quit` whose Class B watcher (or any
|
|
19
|
+
* other watcher) was mid-(re)connect leaked an ACTIVE session row AND an
|
|
20
|
+
* `activeSessionAdmissionCount` slot (#101 class), hard-capping the
|
|
21
|
+
* 19-scenario GA baseline after exactly the free headroom.
|
|
22
|
+
*
|
|
23
|
+
* Order is load-bearing: strip stale handlers FIRST, re-arm a swallow-all
|
|
24
|
+
* 'error' handler, THEN close. Exported for the regression test only — not
|
|
25
|
+
* part of the public SDK surface.
|
|
26
|
+
*/
|
|
27
|
+
export declare function disposeWatcherSocket(ws: WebSocket, subscriptionId?: string): void;
|
|
6
28
|
/**
|
|
7
29
|
* One assigned reviewer seat returned by `getInReviewAssignments`. Mirrors the
|
|
8
30
|
* engine `InReviewAssignment` serde (camelCase) in
|
|
@@ -101,6 +123,24 @@ export interface RecordExecutionEventInput {
|
|
|
101
123
|
/** Free-form JSON; encrypted before crossing AppSync. */
|
|
102
124
|
payload: unknown;
|
|
103
125
|
dedupKeyHex: string;
|
|
126
|
+
/**
|
|
127
|
+
* O1 C1 (#652) §3.1 item 3 — the plaintext flag-target locator, REQUIRED
|
|
128
|
+
* when `kind = 'FLAG_BAD_APPROVAL'` and absent otherwise (non-flag events
|
|
129
|
+
* stay byte-identical to the pre-O1-C1 wire). Row COORDINATES only — zero
|
|
130
|
+
* content. Field names mirror the Rust handler DTO
|
|
131
|
+
* (`RecordExecutionEventInput` in core-rs `types.rs`, camelCase serde) —
|
|
132
|
+
* the wire-shape source of truth. The server rejects a request whose
|
|
133
|
+
* `taskId` ≠ `flaggedTaskId` (the per-task dedup-partition pin).
|
|
134
|
+
*/
|
|
135
|
+
flaggedTaskId?: string;
|
|
136
|
+
/**
|
|
137
|
+
* The flagged row's audit-table sort key — the zero-padded 16-digit
|
|
138
|
+
* `seqNum` string exactly as surfaced by `queryAudit`'s additive
|
|
139
|
+
* `AuditEntryWireRow.seqNum` projection.
|
|
140
|
+
*/
|
|
141
|
+
flaggedSeqNum?: string;
|
|
142
|
+
/** The flagged entry's `entry_id` (also the dedup-key derivation input). */
|
|
143
|
+
flaggedEntryId?: string;
|
|
104
144
|
}
|
|
105
145
|
/**
|
|
106
146
|
* PHASE-CP-1-F.1 Part A (LOCK L2) — tolerant `AWSJSON!` object read.
|
|
@@ -180,8 +220,29 @@ export interface TaskReviewSummaryDurations {
|
|
|
180
220
|
}
|
|
181
221
|
export interface TaskReviewSummaryTokenUsage {
|
|
182
222
|
knownTotal?: number;
|
|
223
|
+
knownInput?: number;
|
|
224
|
+
knownOutput?: number;
|
|
225
|
+
knownCachedInput?: number;
|
|
183
226
|
availability: string;
|
|
184
227
|
unavailableRoles: string[];
|
|
228
|
+
invocations?: TaskReviewSummaryTokenInvocation[];
|
|
229
|
+
}
|
|
230
|
+
export interface TaskReviewSummaryTokenInvocation {
|
|
231
|
+
role: string;
|
|
232
|
+
agent: string;
|
|
233
|
+
taskId?: string;
|
|
234
|
+
trackIndex?: number;
|
|
235
|
+
round?: number;
|
|
236
|
+
gateId?: string;
|
|
237
|
+
seatId?: number;
|
|
238
|
+
reviewerRole?: string;
|
|
239
|
+
inputTokens?: number;
|
|
240
|
+
outputTokens?: number;
|
|
241
|
+
cachedInputTokens?: number;
|
|
242
|
+
totalTokens?: number;
|
|
243
|
+
elapsedMs?: number;
|
|
244
|
+
availability: string;
|
|
245
|
+
unavailableReason?: string;
|
|
185
246
|
}
|
|
186
247
|
/** One round in the per-round summary (decrypted). */
|
|
187
248
|
export interface TaskReviewSummaryRound {
|
|
@@ -862,6 +923,7 @@ export declare class AppSyncClient {
|
|
|
862
923
|
sessionId: string;
|
|
863
924
|
roundNumber: number;
|
|
864
925
|
rawOutput: string;
|
|
926
|
+
usage?: InvocationUsageSnapshot;
|
|
865
927
|
}, sessionKeyBase64: string): Promise<{
|
|
866
928
|
gateId: string;
|
|
867
929
|
workflowState: string;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -4,3 +4,11 @@
|
|
|
4
4
|
* the raw process argv (node, script, "keychain", subcommand, flags…).
|
|
5
5
|
*/
|
|
6
6
|
export declare function runKeychainCli(argv: string[]): Promise<number>;
|
|
7
|
+
/**
|
|
8
|
+
* Advisory nudge for `codevibe login` / `codevibe status` (GA follow-up to #642):
|
|
9
|
+
* when this machine still holds file-backed credentials, has NOT migrated, and the
|
|
10
|
+
* OS keyring is actually usable, print the `keychain migrate` suggestion. Purely
|
|
11
|
+
* additive output — never throws, never mutates state, silent on any ambiguity
|
|
12
|
+
* (see migrate-nudge.ts invariants).
|
|
13
|
+
*/
|
|
14
|
+
export declare function maybePrintKeychainMigrationNudge(print?: (line: string) => void): Promise<void>;
|