token-goat 2.9.30 → 2.9.31
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/README.md +24 -7
- package/SECURITY.md +10 -2
- package/THIRD_PARTY_NOTICES.md +1 -1
- package/dist/native/linux-arm64/tg-hook +0 -0
- package/dist/native/linux-x64/tg-hook +0 -0
- package/dist/native/win32-arm64/tg-hook.exe +0 -0
- package/dist/native/win32-x64/tg-hook.exe +0 -0
- package/dist/{token-goat-chunk-QSBRTGM2.mjs → token-goat-chunk-25TRAWET.mjs} +17 -7
- package/dist/token-goat-chunk-2KXO6V2B.mjs +203 -0
- package/dist/{token-goat-chunk-IVKIZWAB.mjs → token-goat-chunk-2M7E4PF7.mjs} +41 -12
- package/dist/{token-goat-chunk-SMVRN2HU.mjs → token-goat-chunk-2UPZGKKY.mjs} +2 -2
- package/dist/token-goat-chunk-2WLPWKHV.mjs +135 -0
- package/dist/token-goat-chunk-2XBMMDBD.mjs +126 -0
- package/dist/token-goat-chunk-32ZOY4N5.mjs +23 -0
- package/dist/token-goat-chunk-3EZRNKTK.mjs +188 -0
- package/dist/token-goat-chunk-4ADZHI7J.mjs +63 -0
- package/dist/token-goat-chunk-4GBVPH3D.mjs +242 -0
- package/dist/{token-goat-chunk-KWPNJBDH.mjs → token-goat-chunk-4GILLLWY.mjs} +7 -4
- package/dist/{token-goat-chunk-67TQJ76B.mjs → token-goat-chunk-4NAPX7BA.mjs} +36 -3
- package/dist/{token-goat-chunk-PU7V3FD6.mjs → token-goat-chunk-4SC7UJYV.mjs} +3354 -2553
- package/dist/token-goat-chunk-4SMRO4TO.mjs +366 -0
- package/dist/{token-goat-chunk-AFLYLHDH.mjs → token-goat-chunk-4ZBFWLXF.mjs} +283 -427
- package/dist/{token-goat-chunk-JDWCRD6M.mjs → token-goat-chunk-57BIAXJL.mjs} +73 -40
- package/dist/token-goat-chunk-5AC66HNP.mjs +115 -0
- package/dist/token-goat-chunk-5IMLKSOP.mjs +145 -0
- package/dist/token-goat-chunk-646E6U44.mjs +398 -0
- package/dist/token-goat-chunk-65N4RHP4.mjs +33 -0
- package/dist/{token-goat-chunk-JN4XMLSD.mjs → token-goat-chunk-6DG6HWP3.mjs} +8 -2
- package/dist/token-goat-chunk-6ENQSNRZ.mjs +573 -0
- package/dist/token-goat-chunk-6ZY4GNF2.mjs +91 -0
- package/dist/{token-goat-chunk-VPBBTSJR.mjs → token-goat-chunk-76FGDLRO.mjs} +262 -255
- package/dist/{token-goat-chunk-7IN2MKDS.mjs → token-goat-chunk-7EZTZOMC.mjs} +121 -289
- package/dist/{token-goat-chunk-OJBNYTJP.mjs → token-goat-chunk-7MAOA6X4.mjs} +640 -80
- package/dist/token-goat-chunk-7R2ZVVAX.mjs +218 -0
- package/dist/{token-goat-chunk-PD5VKH46.mjs → token-goat-chunk-AUNGYYQX.mjs} +2 -2
- package/dist/{token-goat-chunk-HG45AF2B.mjs → token-goat-chunk-AUQYRMMH.mjs} +794 -37
- package/dist/token-goat-chunk-B3PEKBYG.mjs +128 -0
- package/dist/{token-goat-chunk-A3CK6N5Z.mjs → token-goat-chunk-BA4HI6BW.mjs} +1309 -110
- package/dist/{token-goat-chunk-JB6QN6VF.mjs → token-goat-chunk-BFCWY73P.mjs} +59 -23
- package/dist/{token-goat-chunk-B45VMIIH.mjs → token-goat-chunk-BWGFUZZE.mjs} +7 -5
- package/dist/{token-goat-chunk-P4QMM4WK.mjs → token-goat-chunk-C75NA7PL.mjs} +50 -23
- package/dist/token-goat-chunk-CBIQ7WTD.mjs +87 -0
- package/dist/token-goat-chunk-CETM74KC.mjs +307 -0
- package/dist/{token-goat-chunk-H2PHHGM5.mjs → token-goat-chunk-CIMQ2BB3.mjs} +57 -12
- package/dist/{token-goat-chunk-N2NMER56.mjs → token-goat-chunk-CJLS3CDL.mjs} +46 -4
- package/dist/{token-goat-chunk-VES7EL7O.mjs → token-goat-chunk-CO3UYX6G.mjs} +1 -1
- package/dist/token-goat-chunk-COPNXRJM.mjs +154 -0
- package/dist/token-goat-chunk-CSJ5L3NL.mjs +479 -0
- package/dist/{token-goat-chunk-4LCCNRTG.mjs → token-goat-chunk-CVCEVUGK.mjs} +8 -6
- package/dist/{token-goat-chunk-B2RZZCSS.mjs → token-goat-chunk-CXHOFN7R.mjs} +37 -20
- package/dist/token-goat-chunk-D735C4QM.mjs +603 -0
- package/dist/{token-goat-chunk-APGZXSPS.mjs → token-goat-chunk-DGS2LVNA.mjs} +47 -30
- package/dist/{token-goat-chunk-54K3EUV3.mjs → token-goat-chunk-DOWRSMWO.mjs} +1 -1
- package/dist/token-goat-chunk-E45INMY3.mjs +71 -0
- package/dist/token-goat-chunk-EC5F37F4.mjs +244 -0
- package/dist/{token-goat-chunk-XTQAOTSO.mjs → token-goat-chunk-EDBDRVH5.mjs} +4 -1
- package/dist/{token-goat-chunk-5BCIJXMO.mjs → token-goat-chunk-EELTBKDM.mjs} +14 -10
- package/dist/{token-goat-chunk-W3QCJS6G.mjs → token-goat-chunk-F4HYL64R.mjs} +8 -6
- package/dist/token-goat-chunk-F6PAUIEK.mjs +624 -0
- package/dist/token-goat-chunk-FDSREWLX.mjs +692 -0
- package/dist/token-goat-chunk-GGGG7RWB.mjs +236 -0
- package/dist/{token-goat-chunk-C63JQV5K.mjs → token-goat-chunk-GL3ETKSC.mjs} +233 -15
- package/dist/token-goat-chunk-GVB2MUST.mjs +1298 -0
- package/dist/token-goat-chunk-GWFLAUMQ.mjs +307 -0
- package/dist/{token-goat-chunk-ZEJVMUQH.mjs → token-goat-chunk-HLPW63A7.mjs} +2 -2
- package/dist/{token-goat-chunk-GOLEOXZP.mjs → token-goat-chunk-HU4TORC5.mjs} +44 -26
- package/dist/{token-goat-chunk-Z57YODA2.mjs → token-goat-chunk-I7VKZZAW.mjs} +2 -2
- package/dist/{token-goat-chunk-NYKZLXMK.mjs → token-goat-chunk-IA6XSPE3.mjs} +6 -6
- package/dist/{token-goat-chunk-E3OQX2XT.mjs → token-goat-chunk-IAGXMHIK.mjs} +81 -42
- package/dist/{token-goat-chunk-HYBFA5X3.mjs → token-goat-chunk-IK2DRMFX.mjs} +24 -68
- package/dist/{token-goat-chunk-COBQGL5S.mjs → token-goat-chunk-INNPTRUX.mjs} +249 -299
- package/dist/{token-goat-chunk-7NADEDKJ.mjs → token-goat-chunk-JJ5BX4F4.mjs} +6 -1
- package/dist/{token-goat-chunk-PEPTHRFF.mjs → token-goat-chunk-JVFSHVFJ.mjs} +6 -6
- package/dist/token-goat-chunk-KGEFFSHD.mjs +50 -0
- package/dist/token-goat-chunk-KQBLRPBN.mjs +433 -0
- package/dist/{token-goat-chunk-Q5VTGRH7.mjs → token-goat-chunk-LCNQ2CNN.mjs} +55 -319
- package/dist/token-goat-chunk-M3LFJDPU.mjs +58 -0
- package/dist/token-goat-chunk-M52KPT3C.mjs +87 -0
- package/dist/token-goat-chunk-MBEG42H7.mjs +150 -0
- package/dist/{token-goat-chunk-EVC4TOLE.mjs → token-goat-chunk-METECDOH.mjs} +3 -12
- package/dist/{token-goat-chunk-T4FK7VOV.mjs → token-goat-chunk-MHX7XCC3.mjs} +2 -1
- package/dist/token-goat-chunk-N5LSOVSU.mjs +137 -0
- package/dist/{token-goat-chunk-LOBGHXPB.mjs → token-goat-chunk-N5MMR2IW.mjs} +1 -1
- package/dist/token-goat-chunk-N7B3JQ2C.mjs +160 -0
- package/dist/{token-goat-chunk-RHXKCZX3.mjs → token-goat-chunk-NISNMD7T.mjs} +2182 -2959
- package/dist/token-goat-chunk-NMD42SFQ.mjs +434 -0
- package/dist/token-goat-chunk-NOSS5VWY.mjs +174 -0
- package/dist/{token-goat-chunk-KQNZDCBD.mjs → token-goat-chunk-NSNSPPBG.mjs} +25 -3
- package/dist/token-goat-chunk-OBOFIHKN.mjs +38 -0
- package/dist/token-goat-chunk-OBRFJ5O5.mjs +76 -0
- package/dist/{token-goat-chunk-JTQQGXZH.mjs → token-goat-chunk-ODBT2USM.mjs} +124 -57
- package/dist/token-goat-chunk-OS5EM5PP.mjs +107 -0
- package/dist/token-goat-chunk-OUKDTVNU.mjs +1178 -0
- package/dist/token-goat-chunk-OXFMP75N.mjs +566 -0
- package/dist/{token-goat-chunk-DZX2YDPT.mjs → token-goat-chunk-OYIYKDDT.mjs} +2 -2
- package/dist/{token-goat-chunk-QCLDAQTS.mjs → token-goat-chunk-P6HQSWQ3.mjs} +50 -53
- package/dist/token-goat-chunk-P6VGNTQT.mjs +71 -0
- package/dist/{token-goat-chunk-7BXSNBY5.mjs → token-goat-chunk-PFWIMH2E.mjs} +23 -15
- package/dist/token-goat-chunk-PJZXIS3G.mjs +39 -0
- package/dist/token-goat-chunk-PUO5INXP.mjs +606 -0
- package/dist/{token-goat-chunk-GFBSN7BD.mjs → token-goat-chunk-PX5PQWTY.mjs} +22 -250
- package/dist/token-goat-chunk-Q5QDBC6O.mjs +111 -0
- package/dist/token-goat-chunk-QFNQXSDT.mjs +40 -0
- package/dist/{token-goat-chunk-FBNQNSJK.mjs → token-goat-chunk-QH7YVYOY.mjs} +2198 -2782
- package/dist/{token-goat-chunk-64FBPDTK.mjs → token-goat-chunk-QLMUHEPB.mjs} +12 -9
- package/dist/{token-goat-chunk-RZRTDST7.mjs → token-goat-chunk-QQOJIHL3.mjs} +37 -11
- package/dist/{token-goat-chunk-XKI4SEMX.mjs → token-goat-chunk-QQTWJY2O.mjs} +19 -0
- package/dist/{token-goat-chunk-U4N3BN2W.mjs → token-goat-chunk-QYHO5CPH.mjs} +3 -2
- package/dist/token-goat-chunk-RELAVWAD.mjs +27 -0
- package/dist/{token-goat-chunk-ETBHVDB6.mjs → token-goat-chunk-RSLPOBKQ.mjs} +22 -11
- package/dist/token-goat-chunk-SAMFDIS2.mjs +712 -0
- package/dist/token-goat-chunk-SDELXLDM.mjs +48 -0
- package/dist/{token-goat-chunk-4JJS54TR.mjs → token-goat-chunk-SOAFZNNZ.mjs} +161 -38
- package/dist/{token-goat-chunk-664RTTUV.mjs → token-goat-chunk-SRTHT7KV.mjs} +21 -16
- package/dist/{token-goat-chunk-4FVIAHSI.mjs → token-goat-chunk-T5P5ACNQ.mjs} +7 -6
- package/dist/token-goat-chunk-T5YGG4V3.mjs +45 -0
- package/dist/token-goat-chunk-TAUZ4OFS.mjs +110 -0
- package/dist/{token-goat-chunk-COTXZHTS.mjs → token-goat-chunk-TM6MC754.mjs} +4 -3
- package/dist/token-goat-chunk-TWUKBOMP.mjs +72 -0
- package/dist/{token-goat-chunk-3CHUNJ2O.mjs → token-goat-chunk-U2KSB4SG.mjs} +8 -5
- package/dist/{token-goat-chunk-3Z5S4KGM.mjs → token-goat-chunk-U4WHMG6Y.mjs} +29 -49
- package/dist/{token-goat-chunk-PY533T7F.mjs → token-goat-chunk-UGJHGR5A.mjs} +21 -15
- package/dist/{token-goat-chunk-CYCEYAEO.mjs → token-goat-chunk-UOUDVA5U.mjs} +1 -1
- package/dist/{token-goat-chunk-JDHE26RP.mjs → token-goat-chunk-URRJ33GR.mjs} +16 -5
- package/dist/{token-goat-chunk-56EIMS3E.mjs → token-goat-chunk-UZOROKIS.mjs} +23 -17
- package/dist/{token-goat-chunk-CZ3LUXYZ.mjs → token-goat-chunk-VIHGGGEI.mjs} +427 -308
- package/dist/token-goat-chunk-VTJLAA7P.mjs +21 -0
- package/dist/token-goat-chunk-VWDXLT4F.mjs +678 -0
- package/dist/{token-goat-chunk-YARZFCZS.mjs → token-goat-chunk-VWZA2H4P.mjs} +62 -53
- package/dist/{token-goat-chunk-SQSSNQPE.mjs → token-goat-chunk-VXEXMJDL.mjs} +3979 -4745
- package/dist/token-goat-chunk-WBA5SKT4.mjs +547 -0
- package/dist/{token-goat-chunk-SJHWVNDQ.mjs → token-goat-chunk-WDH44VAU.mjs} +7 -4
- package/dist/token-goat-chunk-X2SUP3EI.mjs +107 -0
- package/dist/token-goat-chunk-X3BELZSJ.mjs +125 -0
- package/dist/{token-goat-chunk-FZ3NEW5O.mjs → token-goat-chunk-XLRBNZCC.mjs} +13 -8
- package/dist/{token-goat-chunk-Z44YO3QZ.mjs → token-goat-chunk-XRKUX6CM.mjs} +1 -1
- package/dist/token-goat-chunk-XX6O25RN.mjs +82 -0
- package/dist/token-goat-chunk-XZSUHBVS.mjs +65 -0
- package/dist/{token-goat-chunk-WY3L4DVT.mjs → token-goat-chunk-Y2ZTQOCL.mjs} +13 -8
- package/dist/{token-goat-chunk-UQ5UK2T7.mjs → token-goat-chunk-YB4VMGK2.mjs} +1 -1
- package/dist/token-goat-chunk-YBIIY3HR.mjs +51 -0
- package/dist/{token-goat-chunk-ZQSJMO2R.mjs → token-goat-chunk-YHVDKQBR.mjs} +105 -105
- package/dist/{token-goat-chunk-H2K3QP6H.mjs → token-goat-chunk-YJB5XLWI.mjs} +8 -6
- package/dist/{token-goat-chunk-ZCDTCVIS.mjs → token-goat-chunk-YKB6AQG3.mjs} +5 -2
- package/dist/token-goat-chunk-YMTG2YA5.mjs +35 -0
- package/dist/token-goat-chunk-YPBZYQRV.mjs +40 -0
- package/dist/{token-goat-chunk-HOJLCGDP.mjs → token-goat-chunk-YY6AVUFL.mjs} +2033 -2251
- package/dist/token-goat-chunk-Z7N5OMMP.mjs +98 -0
- package/dist/token-goat-chunk-ZNMNUPAZ.mjs +938 -0
- package/dist/token-goat-hook-client.cjs +1 -1
- package/dist/token-goat-hook-client.mjs +3 -3
- package/dist/token-goat-hook.mjs +67 -38
- package/dist/token-goat.core.mjs +4 -4
- package/docs/C4_RUNTIME_ARCHITECTURE.md +1 -1
- package/docs/cli.md +40 -21
- package/docs/install.md +19 -9
- package/docs/security.md +5 -3
- package/package.json +2 -1
- package/dist/token-goat-chunk-5KDMCMF2.mjs +0 -362
- package/dist/token-goat-chunk-6QMAS7AB.mjs +0 -42
- package/dist/token-goat-chunk-7F2UBPD3.mjs +0 -397
- package/dist/token-goat-chunk-ARDNEC2U.mjs +0 -248
- package/dist/token-goat-chunk-ATCPY7EQ.mjs +0 -134
- package/dist/token-goat-chunk-EFHMMIGP.mjs +0 -68
- package/dist/token-goat-chunk-IVALLT3S.mjs +0 -48
- package/dist/token-goat-chunk-JDU3EXSU.mjs +0 -65
- package/dist/token-goat-chunk-MKPF4CL5.mjs +0 -37
- package/dist/token-goat-chunk-RC35RBUL.mjs +0 -312
- package/dist/token-goat-chunk-USILZ3T3.mjs +0 -53
- package/dist/token-goat-chunk-VL3YIFHZ.mjs +0 -2975
- package/dist/token-goat-chunk-XU4P6Z24.mjs +0 -133
- package/dist/token-goat-chunk-Y776AENC.mjs +0 -1597
|
@@ -2,33 +2,34 @@ import { createRequire as __cjsRequire } from 'node:module';
|
|
|
2
2
|
const require = __cjsRequire(import.meta.url);
|
|
3
3
|
import {
|
|
4
4
|
compareSemver
|
|
5
|
-
} from "./token-goat-chunk-
|
|
5
|
+
} from "./token-goat-chunk-CXHOFN7R.mjs";
|
|
6
6
|
import {
|
|
7
|
+
emitErr,
|
|
7
8
|
jsonc,
|
|
8
9
|
stripBom
|
|
9
|
-
} from "./token-goat-chunk-
|
|
10
|
+
} from "./token-goat-chunk-QQTWJY2O.mjs";
|
|
10
11
|
import {
|
|
11
12
|
HOOKS_CONFIG_FILE,
|
|
12
13
|
copilotHooksOwnersPath,
|
|
13
14
|
readCopilotHooksOwners
|
|
14
15
|
} from "./token-goat-chunk-G5PJ7AGM.mjs";
|
|
16
|
+
import {
|
|
17
|
+
claudeConfigDir
|
|
18
|
+
} from "./token-goat-chunk-BPGCUMR7.mjs";
|
|
15
19
|
import {
|
|
16
20
|
toolMatcherFor
|
|
17
|
-
} from "./token-goat-chunk-
|
|
21
|
+
} from "./token-goat-chunk-C75NA7PL.mjs";
|
|
18
22
|
import {
|
|
19
23
|
COPILOT_CLI_TOOL_NAME_MAP,
|
|
20
24
|
copilotCliUserRoot,
|
|
21
25
|
foldToolName
|
|
22
|
-
} from "./token-goat-chunk-
|
|
23
|
-
import {
|
|
24
|
-
claudeConfigDir
|
|
25
|
-
} from "./token-goat-chunk-BPGCUMR7.mjs";
|
|
26
|
+
} from "./token-goat-chunk-76FGDLRO.mjs";
|
|
26
27
|
import {
|
|
27
28
|
loadConfig,
|
|
28
29
|
nativeHooksEnabled,
|
|
29
30
|
parse,
|
|
30
31
|
stringify
|
|
31
|
-
} from "./token-goat-chunk-
|
|
32
|
+
} from "./token-goat-chunk-7EZTZOMC.mjs";
|
|
32
33
|
import {
|
|
33
34
|
registerReset
|
|
34
35
|
} from "./token-goat-chunk-YZAAZU2S.mjs";
|
|
@@ -65,7 +66,11 @@ import {
|
|
|
65
66
|
writeConfigText,
|
|
66
67
|
writeIfDifferent,
|
|
67
68
|
writeJsonSettings
|
|
68
|
-
} from "./token-goat-chunk-
|
|
69
|
+
} from "./token-goat-chunk-HU4TORC5.mjs";
|
|
70
|
+
import {
|
|
71
|
+
echoedValue,
|
|
72
|
+
fencedCommand
|
|
73
|
+
} from "./token-goat-chunk-GGGG7RWB.mjs";
|
|
69
74
|
import {
|
|
70
75
|
powershellHookLine,
|
|
71
76
|
quotePosixShellWord,
|
|
@@ -78,7 +83,7 @@ import {
|
|
|
78
83
|
} from "./token-goat-chunk-FNFBKLRD.mjs";
|
|
79
84
|
import {
|
|
80
85
|
dataDir
|
|
81
|
-
} from "./token-goat-chunk-
|
|
86
|
+
} from "./token-goat-chunk-CO3UYX6G.mjs";
|
|
82
87
|
import {
|
|
83
88
|
init_define_import_meta_env
|
|
84
89
|
} from "./token-goat-chunk-A37V4PBF.mjs";
|
|
@@ -173,10 +178,18 @@ var SHIM_SPAWN_LADDER = ` const res = entryPath
|
|
|
173
178
|
? spawnSync(process.execPath, [entryPath, 'hook', eventName], {
|
|
174
179
|
input,
|
|
175
180
|
encoding: 'utf8',
|
|
176
|
-
timeout:
|
|
181
|
+
timeout: SHIM_FALLBACK_TIMEOUT_MS,
|
|
177
182
|
killSignal: 'SIGKILL',
|
|
178
183
|
maxBuffer: SHIM_MAX_BUFFER_BYTES,
|
|
179
184
|
})`;
|
|
185
|
+
var SHIM_FALLBACK_TIMEOUT_MS = { claudecode: 2e4, codex: 45e3, copilot: 45e3, grok: 3e3, kimi: 2e4, pi: 2e4, relay: 1e4 };
|
|
186
|
+
var SHIM_SESSION_START_TIMEOUT_MS = 1e4;
|
|
187
|
+
var SESSION_START_TEST_BUDGET_ENV = "TG_TEST_SESSION_START_BUDGET_MS";
|
|
188
|
+
var SESSION_START_TEST_BUDGET_MIN_MS = 1e3;
|
|
189
|
+
var SESSION_START_TEST_BUDGET_MAX_MS = 6e5;
|
|
190
|
+
function shimFallbackTimeoutConst(harness) {
|
|
191
|
+
return `const SHIM_FALLBACK_TIMEOUT_MS = ${SHIM_FALLBACK_TIMEOUT_MS[harness]}`;
|
|
192
|
+
}
|
|
180
193
|
var SHIM_MAX_BUFFER_CONST = `const SHIM_MAX_BUFFER_BYTES = 32 * 1024 * 1024`;
|
|
181
194
|
var SHIM_ASYNC_DETACH = `const ASYNC_DETACH_TOOLS = new Set(['Edit', 'Write', 'MultiEdit', 'NotebookEdit'])
|
|
182
195
|
const ASYNC_DETACH_SKIP_EXT_RE = /\\.(md|mdx|markdown|rst)$/i
|
|
@@ -194,6 +207,14 @@ function bashResultByteLength(resp) {
|
|
|
194
207
|
}
|
|
195
208
|
return 0
|
|
196
209
|
}
|
|
210
|
+
// A token-goat read|section|symbol result is never detached, however short: see isAsyncDetachEligible in src/hook_adapters.ts.
|
|
211
|
+
const TG_LEDGER_PREFIX_RE = /^(?:(?:cd\\s+(?:"[^"]*"|'[^']*'|\\S+)|export[ \\t]+[A-Za-z_]\\w*=(?:"[^"]*"|'[^']*'|[^\\s"'])*(?:[ \\t]+[A-Za-z_]\\w*=(?:"[^"]*"|'[^']*'|[^\\s"'])*)*)[ \\t]*(?:&&|;|\\r?\\n)\\s*)+/
|
|
212
|
+
const TG_LEDGER_READ_RE = /^(?:[A-Za-z_][A-Za-z0-9_]*=\\S+\\s+)*(?:env\\s+(?:(?:(?:-u|--unset)\\s+\\S+|-(?![A-Za-z]*C|-c)\\S*|[A-Za-z_][A-Za-z0-9_]*=\\S+)\\s+)*)?(?:token-goat|tg|node(?:\\.exe)?\\s+["']?(?:\\S*[\\\\/])?token-goat(?:\\.mjs)?["']?)\\s+(?:read|section|symbol)\\b/i
|
|
213
|
+
function isLedgerReadCommand(cmd) {
|
|
214
|
+
if (typeof cmd !== 'string') return false
|
|
215
|
+
const body = cmd.trim().replace(/^\\(\\s*/, '')
|
|
216
|
+
return TG_LEDGER_READ_RE.test((body.replace(TG_LEDGER_PREFIX_RE, '') || body).trim())
|
|
217
|
+
}
|
|
197
218
|
function isAsyncDetachEligible(eventName, input) {
|
|
198
219
|
if (eventName === 'subagent_stop') return true
|
|
199
220
|
if (eventName !== 'post_tool_use') return false
|
|
@@ -210,7 +231,7 @@ function isAsyncDetachEligible(eventName, input) {
|
|
|
210
231
|
: ''
|
|
211
232
|
return !ASYNC_DETACH_SKIP_EXT_RE.test(filePath)
|
|
212
233
|
}
|
|
213
|
-
if (toolName === 'Bash') return bashResultByteLength(payload['tool_response']) < ASYNC_DETACH_BASH_MAX_BYTES
|
|
234
|
+
if (toolName === 'Bash') return !isLedgerReadCommand((payload['tool_input'] || {})['command']) && bashResultByteLength(payload['tool_response']) < ASYNC_DETACH_BASH_MAX_BYTES
|
|
214
235
|
return false
|
|
215
236
|
} catch {
|
|
216
237
|
return false
|
|
@@ -254,6 +275,7 @@ ${SHIM_TRY_SERVER}
|
|
|
254
275
|
${SHIM_TRY_IN_PROCESS}
|
|
255
276
|
|
|
256
277
|
${SHIM_MAX_BUFFER_CONST}
|
|
278
|
+
${shimFallbackTimeoutConst("claudecode")}
|
|
257
279
|
|
|
258
280
|
${SHIM_ASYNC_DETACH}
|
|
259
281
|
|
|
@@ -304,9 +326,9 @@ async function main() {
|
|
|
304
326
|
// spawning a second node process altogether; fall back to the entry path directly via
|
|
305
327
|
// process.execPath, then finally to the old PATH-based shell:true invocation (an
|
|
306
328
|
// older cached hook config with no argv[3], or a config wired directly to
|
|
307
|
-
// \`token-goat hook <event>\` rather than through this shim). A
|
|
308
|
-
// timeout/killSignal on both spawnSync fallbacks means token-goat degrades to its own
|
|
309
|
-
// fail-open '{}' rather than being force-killed by Claude Code's own hook timeout.
|
|
329
|
+
// \`token-goat hook <event>\` rather than through this shim). A
|
|
330
|
+
// SHIM_FALLBACK_TIMEOUT_MS timeout/killSignal on both spawnSync fallbacks means token-goat degrades to its own
|
|
331
|
+
// fail-open '{}' rather than being force-killed by Claude Code's own hook timeout (30 s on UserPromptSubmit).
|
|
310
332
|
const entryPath = process.argv[3]
|
|
311
333
|
let stdout = await tryInProcess(entryPath, eventName, input, asyncDetachAtMs)
|
|
312
334
|
if (stdout === undefined) {
|
|
@@ -322,7 +344,7 @@ ${SHIM_SPAWN_LADDER}
|
|
|
322
344
|
input,
|
|
323
345
|
encoding: 'utf8',
|
|
324
346
|
shell: true,
|
|
325
|
-
timeout:
|
|
347
|
+
timeout: SHIM_FALLBACK_TIMEOUT_MS,
|
|
326
348
|
killSignal: 'SIGKILL',
|
|
327
349
|
maxBuffer: SHIM_MAX_BUFFER_BYTES,
|
|
328
350
|
})
|
|
@@ -351,15 +373,16 @@ function buildGuidanceBody(fallbackToolClause, opts = {}) {
|
|
|
351
373
|
"",
|
|
352
374
|
`This gate decides *whether* to reach for a read tool at all. ${fallbackToolClause} only pick the *fallback* once token-goat has been ruled out for this read \u2014 they never authorize skipping the gate.`,
|
|
353
375
|
"",
|
|
354
|
-
"
|
|
376
|
+
"Shell binaries and editor programs are commands run through the shell tool, never tool identifiers: keep them out of an agent's tools and allowed-tools lists, where loaders harvest the names and warn that each is unknown.",
|
|
355
377
|
"",
|
|
356
378
|
"Exemptions (gate passes, read directly): the file is under ~200 lines and you need all of it; it was never indexed (new, untracked, or generated this turn); it is a genuinely opaque binary (not an image); the target has no symbol handle (e.g. a literal mid-function).",
|
|
357
379
|
"",
|
|
358
380
|
"Failure shapes to catch yourself in, and the command that replaces each:",
|
|
359
|
-
'- not knowing which command answers a question you can already state in words \u2192 `answer "<question>"`, which
|
|
381
|
+
'- not knowing which command answers a question you can already state in words \u2192 `answer "<question>"`, which runs the right command and refuses rather than guess',
|
|
360
382
|
'- a shell text search with context flags to find a function body \u2192 `read "file::symbol"`',
|
|
361
383
|
'- paging one function with view/view_range \u2192 `read "file::symbol"`',
|
|
362
|
-
|
|
384
|
+
'- issuing sequential or overlapping line-range reads across a file (paging) \u2192 `read "file::symbol"` / `outline file` / `skeleton file` / rg <pattern> <file>',
|
|
385
|
+
"- a read refused by a `[tg]` deny, retried with a shifted range, different lines, or another read tool \u2192 never retry reading the file at different lines; run the command the deny names or use bash-output / token-goat read",
|
|
363
386
|
'- reading a symbol plus chasing its callers and containing doc section as separate reads \u2192 `brief "file::symbol"`',
|
|
364
387
|
'- reading one heading of a large doc \u2192 `section "file::Heading"`',
|
|
365
388
|
"- searching for a symbol's callers \u2192 `refs file::symbol --callers`",
|
|
@@ -370,11 +393,10 @@ function buildGuidanceBody(fallbackToolClause, opts = {}) {
|
|
|
370
393
|
"- pulling one value or subtree out of a JSON/YAML/XML file (manifest, lockfile, spec, config) \u2192 `json-query file 'a.b.c'` / `yaml-query file 'a.b.c'` / `xml-query file 'a.b.c'`",
|
|
371
394
|
"- opening an image to check its dimensions, format, or size \u2192 `image-meta file`",
|
|
372
395
|
"- opening a screenshot, diagram, or scan to read the text in it \u2192 `image-text file`",
|
|
373
|
-
"- opening a PDF or Office document \u2192 inspect its format first, then read a narrow slice: PDF `pdf-meta`/`pdf-outline` then `pdf-locate` to find the pages and `pdf-extract` only those; Word `docx-outline` then `docx-tables`/`docx-text`; PowerPoint `pptx-outline` then `pptx-slide`/`pptx-notes`; Excel `xlsx-sheets` then `xlsx-columns`/`xlsx-head`/`xlsx-range`/`xlsx-query`",
|
|
374
396
|
"- running tests immediately on edited code without fast static validation \u2192 run language validator first (e.g. `ruff check --select F821 <file>`, `node --check`, `tsc --noEmit`) to catch missing imports and syntax errors before test execution",
|
|
375
397
|
"- passing concatenated or unquoted paths to shell tools \u2192 verify whitespace and argument separation in multi-path commands (e.g. `git add path1 path2`, never `git add path1path2`)",
|
|
376
398
|
"",
|
|
377
|
-
|
|
399
|
+
"Commands: `symbol NAME`, `changed --symbol`, `config-get file KEY`, `json-outline`/`yaml-outline`/`xml-outline`, " + (gdrive ? "`gdrive-sections <file-id>`, " : "") + "and `token-goat --help` for the rest.",
|
|
378
400
|
"",
|
|
379
401
|
"Sub-agent briefs must carry this gate verbatim: a sub-agent inherits none of this context and its reads spend the same token budget.",
|
|
380
402
|
"",
|
|
@@ -388,17 +410,6 @@ function buildGuidanceBlock(h) {
|
|
|
388
410
|
return [h.beginMarker, buildGuidanceBody(h.fallbackToolClause, h.gdrive === void 0 ? {} : { gdrive: h.gdrive }), h.endMarker].join("\n");
|
|
389
411
|
}
|
|
390
412
|
|
|
391
|
-
// src/install.ts
|
|
392
|
-
init_define_import_meta_env();
|
|
393
|
-
import * as fs2 from "node:fs";
|
|
394
|
-
import * as path2 from "node:path";
|
|
395
|
-
|
|
396
|
-
// src/canonical_skill.ts
|
|
397
|
-
init_define_import_meta_env();
|
|
398
|
-
import { gunzipSync } from "node:zlib";
|
|
399
|
-
var CANONICAL_SKILL_GZIP_BASE64 = "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";
|
|
400
|
-
var CANONICAL_SKILL_MD = gunzipSync(Buffer.from(CANONICAL_SKILL_GZIP_BASE64, "base64")).toString("utf8");
|
|
401
|
-
|
|
402
413
|
// src/native_hook.ts
|
|
403
414
|
init_define_import_meta_env();
|
|
404
415
|
import { spawnSync } from "node:child_process";
|
|
@@ -522,6 +533,9 @@ function nativeSelftest(bin) {
|
|
|
522
533
|
return result;
|
|
523
534
|
}
|
|
524
535
|
var _decisions = /* @__PURE__ */ new Map();
|
|
536
|
+
var COPY_FAILED = "its copy could not be put in place in the data directory (a scanner or another process held the file)";
|
|
537
|
+
var COPY_STALE = "the data-directory copy is missing or differs from the packaged binary";
|
|
538
|
+
var TRANSIENT_WHY = /could not be put in place|no answer within/;
|
|
525
539
|
registerReset(() => {
|
|
526
540
|
_selftests.clear();
|
|
527
541
|
_decisions.clear();
|
|
@@ -533,13 +547,33 @@ function nativeHookBinary(entryPath = process.argv[1], opts = {}) {
|
|
|
533
547
|
const cached = _decisions.get(key);
|
|
534
548
|
if (cached !== void 0) return cached ?? void 0;
|
|
535
549
|
const packaged = packagedNativeBinary(entryPath);
|
|
536
|
-
let bin
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
550
|
+
let bin;
|
|
551
|
+
let why;
|
|
552
|
+
if (packaged !== void 0) {
|
|
553
|
+
for (let attempt = 1; attempt <= 2 && bin === void 0; attempt++) {
|
|
554
|
+
if (attempt === 2) {
|
|
555
|
+
if (why === void 0 || !TRANSIENT_WHY.test(why)) break;
|
|
556
|
+
_selftests.clear();
|
|
557
|
+
}
|
|
558
|
+
why = void 0;
|
|
559
|
+
let candidate = packaged;
|
|
560
|
+
if (process.platform === "win32") {
|
|
561
|
+
const copy = nativeCopyPath(entryPath);
|
|
562
|
+
const current = sync ? syncNativeCopy(packaged, copy) : nativeCopyCurrent(packaged, copy);
|
|
563
|
+
if (current) candidate = copy;
|
|
564
|
+
else {
|
|
565
|
+
candidate = void 0;
|
|
566
|
+
why = sync ? COPY_FAILED : COPY_STALE;
|
|
567
|
+
}
|
|
568
|
+
}
|
|
569
|
+
if (candidate !== void 0) {
|
|
570
|
+
const test = nativeSelftest(candidate);
|
|
571
|
+
if (test.ok) bin = candidate;
|
|
572
|
+
else why = `its self-test failed: ${test.reason ?? "no reason given"}`;
|
|
573
|
+
}
|
|
574
|
+
}
|
|
541
575
|
}
|
|
542
|
-
if (bin !== void 0 &&
|
|
576
|
+
if (bin === void 0 && sync && why !== void 0 && why !== COPY_STALE) emitErr(`token-goat: wrote the Node form of the hooks, not the native hook client, because ${why}. Run \`token-goat doctor --repair\` (or install again) to retry.`);
|
|
543
577
|
_decisions.set(key, bin ?? null);
|
|
544
578
|
if (sync) _decisions.set(`false\0${entryPath}`, bin ?? null);
|
|
545
579
|
return bin;
|
|
@@ -630,6 +664,17 @@ function parseNativeInvocation(argv) {
|
|
|
630
664
|
return { bin, harness: h >= 0 ? flags[h + 1] : void 0, wrapped: argv.slice(split + 1) };
|
|
631
665
|
}
|
|
632
666
|
|
|
667
|
+
// src/install.ts
|
|
668
|
+
init_define_import_meta_env();
|
|
669
|
+
import * as fs2 from "node:fs";
|
|
670
|
+
import * as path2 from "node:path";
|
|
671
|
+
|
|
672
|
+
// src/canonical_skill.ts
|
|
673
|
+
init_define_import_meta_env();
|
|
674
|
+
import { gunzipSync } from "node:zlib";
|
|
675
|
+
var CANONICAL_SKILL_GZIP_BASE64 = "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";
|
|
676
|
+
var CANONICAL_SKILL_MD = gunzipSync(Buffer.from(CANONICAL_SKILL_GZIP_BASE64, "base64")).toString("utf8");
|
|
677
|
+
|
|
633
678
|
// src/install.ts
|
|
634
679
|
var HOOK_EVENT_MAP = [
|
|
635
680
|
["PreToolUse", "pre_tool_use"],
|
|
@@ -639,6 +684,7 @@ var HOOK_EVENT_MAP = [
|
|
|
639
684
|
["PreCompact", "pre_compact"],
|
|
640
685
|
["PostCompact", "post_compact"],
|
|
641
686
|
["UserPromptSubmit", "user_prompt_submit"],
|
|
687
|
+
["Stop", "stop"],
|
|
642
688
|
["SubagentStop", "subagent_stop"],
|
|
643
689
|
["SessionStart", "session_start"]
|
|
644
690
|
];
|
|
@@ -748,7 +794,7 @@ var SettingsParseError = class extends Error {
|
|
|
748
794
|
};
|
|
749
795
|
function readSettings(p, opts = {}) {
|
|
750
796
|
const refuse = (problem, detail) => new SettingsParseError(
|
|
751
|
-
(opts.command === "uninstall" ? `settings file
|
|
797
|
+
(opts.command === "uninstall" ? `settings file ${echoedValue(p)} is unreadable: it ${problem}. Uninstall left it untouched, along with the hook shim it may still name and the file's backups; fix the file and run uninstall again.` : `settings file ${echoedValue(p)} ${problem}. Fix or back up the file before running install.`) + (detail === void 0 ? "" : ` (${detail})`)
|
|
752
798
|
);
|
|
753
799
|
let raw;
|
|
754
800
|
try {
|
|
@@ -919,6 +965,13 @@ function isInstalled(scope = "user") {
|
|
|
919
965
|
const gaps = hookEventGaps(scope);
|
|
920
966
|
return gaps !== null && gaps.missing.length === 0 && gaps.outdated.length === 0 && gaps.broken.length === 0;
|
|
921
967
|
}
|
|
968
|
+
function hasFiringHook(scope) {
|
|
969
|
+
const gaps = hookEventGaps(scope);
|
|
970
|
+
return gaps !== null && gaps.missing.length + gaps.broken.length < HOOK_EVENT_MAP.length;
|
|
971
|
+
}
|
|
972
|
+
function claudeHooksInstalledAnyScope() {
|
|
973
|
+
return hasFiringHook("user") || hasFiringHook("project");
|
|
974
|
+
}
|
|
922
975
|
var LIVE_HOOK_MARKER_PATTERNS = [SHIM_COMMAND_MARKER, COMMAND_MARKER].map(anchoredMarkerPattern);
|
|
923
976
|
var SHIM_FILE_WORD = /(?:^|[\\/])token-goat-shim\.c?js$/;
|
|
924
977
|
function isLiveTokenGoatHookCommand(command, args) {
|
|
@@ -946,7 +999,7 @@ function hookEventGaps(scope = "user") {
|
|
|
946
999
|
const broken = [];
|
|
947
1000
|
for (const [eventKey, eventArg] of HOOK_EVENT_MAP) {
|
|
948
1001
|
const expected = expectedHookEntryFor(scriptPath, eventArg, { sync: false });
|
|
949
|
-
if (groupHasTokenGoat(hooks[eventKey], (c, a) => hookEntryMatches(c, a, expected))) continue;
|
|
1002
|
+
if (groupHasTokenGoat(hooks[eventKey], (c, a) => hookEntryMatches(c, a, expected) && isLiveTokenGoatHookCommand(c, a))) continue;
|
|
950
1003
|
if (groupHasTokenGoat(hooks[eventKey], isLiveTokenGoatHookCommand)) outdated.push(eventKey);
|
|
951
1004
|
else if (groupHasTokenGoat(hooks[eventKey], isBrokenNativeHookCommand)) broken.push(eventKey);
|
|
952
1005
|
else missing.push(eventKey);
|
|
@@ -1149,6 +1202,7 @@ ${SHIM_TRY_SERVER}
|
|
|
1149
1202
|
${SHIM_TRY_IN_PROCESS}
|
|
1150
1203
|
|
|
1151
1204
|
${SHIM_MAX_BUFFER_CONST}
|
|
1205
|
+
${shimFallbackTimeoutConst("codex")}
|
|
1152
1206
|
|
|
1153
1207
|
async function main() {
|
|
1154
1208
|
const eventName = process.argv[2] || ''
|
|
@@ -1172,7 +1226,7 @@ async function main() {
|
|
|
1172
1226
|
// CLI bridge's inner hook call (a bare 'token-goat' on PATH failing to resolve
|
|
1173
1227
|
// crashes this call, and Codex -- like Copilot -- fails closed on a non-zero-exit
|
|
1174
1228
|
// hook). Falls back further to the old PATH-based shell:true invocation when argv[3]
|
|
1175
|
-
// is absent (an older cached hook config). A
|
|
1229
|
+
// is absent (an older cached hook config). A SHIM_FALLBACK_TIMEOUT_MS timeout/killSignal on both
|
|
1176
1230
|
// spawnSync fallbacks bounds them well under Codex's own hook timeout budget, so
|
|
1177
1231
|
// token-goat degrades to its own fail-open '{}' rather than being force-killed by
|
|
1178
1232
|
// Codex first.
|
|
@@ -1184,7 +1238,7 @@ ${SHIM_SPAWN_LADDER}
|
|
|
1184
1238
|
input,
|
|
1185
1239
|
encoding: 'utf8',
|
|
1186
1240
|
shell: true,
|
|
1187
|
-
timeout:
|
|
1241
|
+
timeout: SHIM_FALLBACK_TIMEOUT_MS,
|
|
1188
1242
|
killSignal: 'SIGKILL',
|
|
1189
1243
|
maxBuffer: SHIM_MAX_BUFFER_BYTES,
|
|
1190
1244
|
})
|
|
@@ -1251,35 +1305,41 @@ function groupHasTokenGoat2(groups, matcher, predicate) {
|
|
|
1251
1305
|
|
|
1252
1306
|
// src/bridges/codex_install.ts
|
|
1253
1307
|
var CODEX_COMMAND_MARKER = "token-goat-shim";
|
|
1254
|
-
var CODEX_MATCHERS = ["view_image|Bash|exec|shell|bash", "apply_patch", "web_search"];
|
|
1308
|
+
var CODEX_MATCHERS = ["view_image|Bash|exec|shell|bash", "apply_patch", "web_search", "spawn_agent|multi_agent_v1wait_agent"];
|
|
1255
1309
|
var CODEX_HOOK_EVENTS = ["PreToolUse", "PostToolUse"];
|
|
1256
1310
|
var CODEX_EVENT_ARG = {
|
|
1257
1311
|
PreToolUse: "pre_tool_use",
|
|
1258
1312
|
PostToolUse: "post_tool_use"
|
|
1259
1313
|
};
|
|
1260
|
-
var CODEX_GLOBAL_HOOK_EVENTS = ["PreCompact", "UserPromptSubmit", "SubagentStop"];
|
|
1314
|
+
var CODEX_GLOBAL_HOOK_EVENTS = ["PreCompact", "UserPromptSubmit", "Stop", "SubagentStop"];
|
|
1261
1315
|
var CODEX_GLOBAL_EVENT_ARG = {
|
|
1262
1316
|
PreCompact: "pre_compact",
|
|
1263
1317
|
UserPromptSubmit: "user_prompt_submit",
|
|
1318
|
+
Stop: "stop",
|
|
1264
1319
|
SubagentStop: "subagent_stop"
|
|
1265
1320
|
};
|
|
1266
1321
|
var CodexConfigParseError = class extends Error {
|
|
1267
1322
|
};
|
|
1323
|
+
function codexHomeDir() {
|
|
1324
|
+
const override = process.env["CODEX_HOME"];
|
|
1325
|
+
if (override !== void 0 && override !== "") return path3.resolve(override);
|
|
1326
|
+
return path3.join(os.homedir(), ".codex");
|
|
1327
|
+
}
|
|
1268
1328
|
function codexConfigPath() {
|
|
1269
|
-
return path3.join(
|
|
1329
|
+
return path3.join(codexHomeDir(), "config.toml");
|
|
1270
1330
|
}
|
|
1271
1331
|
function codexAgentsPath() {
|
|
1272
|
-
return path3.join(
|
|
1332
|
+
return path3.join(codexHomeDir(), "AGENTS.md");
|
|
1273
1333
|
}
|
|
1274
1334
|
function codexHookScriptPath() {
|
|
1275
|
-
return path3.join(
|
|
1335
|
+
return path3.join(codexHomeDir(), "hooks", SHIM_FILE);
|
|
1276
1336
|
}
|
|
1277
1337
|
function codexLegacyHookScriptPath() {
|
|
1278
|
-
return path3.join(
|
|
1338
|
+
return path3.join(codexHomeDir(), "hooks", LEGACY_SHIM_FILE);
|
|
1279
1339
|
}
|
|
1280
1340
|
function readCodexConfig(p, opts = {}) {
|
|
1281
1341
|
const refuse = (problem, detail) => new CodexConfigParseError(
|
|
1282
|
-
(opts.command === "uninstall" ? `Codex config file
|
|
1342
|
+
(opts.command === "uninstall" ? `Codex config file ${echoedValue(p)} is unreadable: it ${problem}. Uninstall left it untouched, along with the hook shim it may still name, the AGENTS.md block and the file's backups; fix the file and run uninstall again.` : `Codex config file ${echoedValue(p)} ${problem}. Fix or back up the file before running install.`) + (detail === void 0 ? "" : ` (${detail})`)
|
|
1283
1343
|
);
|
|
1284
1344
|
let raw;
|
|
1285
1345
|
try {
|
|
@@ -1710,6 +1770,449 @@ function stripAgentsBlock(p) {
|
|
|
1710
1770
|
// src/bridges/copilot_cli.ts
|
|
1711
1771
|
init_define_import_meta_env();
|
|
1712
1772
|
|
|
1773
|
+
// src/bridges/copilot_session_start.ts
|
|
1774
|
+
init_define_import_meta_env();
|
|
1775
|
+
var COPILOT_SESSION_START_HELPERS = `
|
|
1776
|
+
const SHIM_SESSION_START_TIMEOUT_MS = ${SHIM_SESSION_START_TIMEOUT_MS}
|
|
1777
|
+
|
|
1778
|
+
// Test-only seam, mirrored by the native owner: a plain decimal in range replaces the budget so a test need not race the clock; anything else is ignored.
|
|
1779
|
+
function sessionStartBudgetMs() {
|
|
1780
|
+
const text = process.env.${SESSION_START_TEST_BUDGET_ENV}
|
|
1781
|
+
if (typeof text !== 'string' || !/^[0-9]+$/.test(text)) return SHIM_SESSION_START_TIMEOUT_MS
|
|
1782
|
+
const value = Number(text)
|
|
1783
|
+
return value >= ${SESSION_START_TEST_BUDGET_MIN_MS} && value <= ${SESSION_START_TEST_BUDGET_MAX_MS} ? value : SHIM_SESSION_START_TIMEOUT_MS
|
|
1784
|
+
}
|
|
1785
|
+
const SESSION_START_CLEANUP_RESERVE_MS = 1000
|
|
1786
|
+
// How long past the deadline the shim keeps its stdin listener for a payload already in the pipe: on Windows the pipe read completes on a worker thread, so the completion can land many event-loop turns after the listener attaches. Measured resume-to-data under 52 busy loops on 26 CPUs: p50 5.7 ms, max 204 ms.
|
|
1787
|
+
const SESSION_START_LATE_PAYLOAD_GRACE_MS = 1000
|
|
1788
|
+
const SESSION_START_MAX_INPUT_BYTES = 1024 * 1024
|
|
1789
|
+
|
|
1790
|
+
function failOpenSessionStart(reason) {
|
|
1791
|
+
process.stderr.write('[token-goat] SessionStart failed open: ' + reason + '\\n')
|
|
1792
|
+
process.stdout.write('{}')
|
|
1793
|
+
}
|
|
1794
|
+
|
|
1795
|
+
function readSessionStartInput(timeoutMs) {
|
|
1796
|
+
return new Promise((resolve, reject) => {
|
|
1797
|
+
const chunks = []
|
|
1798
|
+
const closers = []
|
|
1799
|
+
let totalBytes = 0
|
|
1800
|
+
let frameStart = -1
|
|
1801
|
+
let frameEnd = -1
|
|
1802
|
+
let inString = false
|
|
1803
|
+
let escaped = false
|
|
1804
|
+
let settled = false
|
|
1805
|
+
let timer
|
|
1806
|
+
|
|
1807
|
+
function cleanup() {
|
|
1808
|
+
clearTimeout(timer)
|
|
1809
|
+
process.stdin.removeListener('data', onData)
|
|
1810
|
+
process.stdin.removeListener('end', onEnd)
|
|
1811
|
+
process.stdin.removeListener('error', onError)
|
|
1812
|
+
process.stdin.pause()
|
|
1813
|
+
if (typeof process.stdin.unref === 'function') process.stdin.unref()
|
|
1814
|
+
process.stdin.destroy()
|
|
1815
|
+
}
|
|
1816
|
+
|
|
1817
|
+
function finish(error, value) {
|
|
1818
|
+
if (settled) return
|
|
1819
|
+
settled = true
|
|
1820
|
+
cleanup()
|
|
1821
|
+
if (error) reject(new Error(error))
|
|
1822
|
+
else resolve(value)
|
|
1823
|
+
}
|
|
1824
|
+
|
|
1825
|
+
function onData(chunk) {
|
|
1826
|
+
const bytes = Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk)
|
|
1827
|
+
const chunkStart = totalBytes
|
|
1828
|
+
totalBytes += bytes.length
|
|
1829
|
+
if (totalBytes > SESSION_START_MAX_INPUT_BYTES) {
|
|
1830
|
+
finish('input exceeded 1 MiB')
|
|
1831
|
+
return
|
|
1832
|
+
}
|
|
1833
|
+
chunks.push(bytes)
|
|
1834
|
+
|
|
1835
|
+
for (let index = 0; index < bytes.length; index += 1) {
|
|
1836
|
+
const byte = bytes[index]
|
|
1837
|
+
const position = chunkStart + index
|
|
1838
|
+
if (frameEnd !== -1) {
|
|
1839
|
+
if (byte !== 0x20 && byte !== 0x09 && byte !== 0x0a && byte !== 0x0d) {
|
|
1840
|
+
finish('input contained trailing data')
|
|
1841
|
+
return
|
|
1842
|
+
}
|
|
1843
|
+
continue
|
|
1844
|
+
}
|
|
1845
|
+
if (frameStart === -1) {
|
|
1846
|
+
if (byte === 0x20 || byte === 0x09 || byte === 0x0a || byte === 0x0d) continue
|
|
1847
|
+
if (byte !== 0x7b) {
|
|
1848
|
+
finish('input was not a JSON object')
|
|
1849
|
+
return
|
|
1850
|
+
}
|
|
1851
|
+
frameStart = position
|
|
1852
|
+
closers.push(0x7d)
|
|
1853
|
+
continue
|
|
1854
|
+
}
|
|
1855
|
+
if (inString) {
|
|
1856
|
+
if (escaped) escaped = false
|
|
1857
|
+
else if (byte === 0x5c) escaped = true
|
|
1858
|
+
else if (byte === 0x22) inString = false
|
|
1859
|
+
continue
|
|
1860
|
+
}
|
|
1861
|
+
if (byte === 0x22) {
|
|
1862
|
+
inString = true
|
|
1863
|
+
} else if (byte === 0x7b) {
|
|
1864
|
+
closers.push(0x7d)
|
|
1865
|
+
} else if (byte === 0x5b) {
|
|
1866
|
+
closers.push(0x5d)
|
|
1867
|
+
} else if (byte === 0x7d || byte === 0x5d) {
|
|
1868
|
+
if (closers.pop() !== byte) {
|
|
1869
|
+
finish('input contained malformed JSON')
|
|
1870
|
+
return
|
|
1871
|
+
}
|
|
1872
|
+
if (closers.length === 0) frameEnd = position + 1
|
|
1873
|
+
}
|
|
1874
|
+
}
|
|
1875
|
+
|
|
1876
|
+
if (frameEnd !== -1) {
|
|
1877
|
+
const framed = Buffer.concat(chunks, totalBytes)
|
|
1878
|
+
const raw = framed.subarray(frameStart, frameEnd).toString('utf8')
|
|
1879
|
+
try {
|
|
1880
|
+
const parsed = JSON.parse(raw)
|
|
1881
|
+
if (parsed === null || typeof parsed !== 'object' || Array.isArray(parsed)) {
|
|
1882
|
+
finish('input was not a JSON object')
|
|
1883
|
+
return
|
|
1884
|
+
}
|
|
1885
|
+
} catch {
|
|
1886
|
+
finish('input contained malformed JSON')
|
|
1887
|
+
return
|
|
1888
|
+
}
|
|
1889
|
+
finish(undefined, raw)
|
|
1890
|
+
}
|
|
1891
|
+
}
|
|
1892
|
+
|
|
1893
|
+
function onEnd() {
|
|
1894
|
+
finish(frameStart === -1 ? 'stdin closed without input' : 'stdin closed before the JSON object was complete')
|
|
1895
|
+
}
|
|
1896
|
+
|
|
1897
|
+
function onError() {
|
|
1898
|
+
finish('stdin could not be read')
|
|
1899
|
+
}
|
|
1900
|
+
|
|
1901
|
+
// A payload already in the pipe must be framed even when the deadline has passed: expiry opens one bounded grace window that data, end or error closes at once, so only a payload that never arrives costs the full grace.
|
|
1902
|
+
timer = setTimeout(() => { timer = setTimeout(() => finish('input deadline exceeded'), SESSION_START_LATE_PAYLOAD_GRACE_MS) }, Math.max(0, timeoutMs))
|
|
1903
|
+
process.stdin.on('data', onData)
|
|
1904
|
+
process.stdin.on('end', onEnd)
|
|
1905
|
+
process.stdin.on('error', onError)
|
|
1906
|
+
process.stdin.resume()
|
|
1907
|
+
})
|
|
1908
|
+
}
|
|
1909
|
+
|
|
1910
|
+
// Plain-JS mirror of session_start_owed.ts::resetSessionStartOwed for a main-thread key: tests/session_start_owed_reset.test.ts pins both to the same files. It runs before anything can throw so a helper killed before its own stamp still leaves the start begun, and an earlier start's marker cannot read settled.
|
|
1911
|
+
function resetSessionStartOwedFiles(payload) {
|
|
1912
|
+
stampSessionStartOwedFiles(payload, true)
|
|
1913
|
+
}
|
|
1914
|
+
|
|
1915
|
+
// A start this shim failed open on is owed whatever the helper's own marker says: the helper judges "settled in time" from its own clock, and the shim can still discard its response (a helper whose exit outlasts the budget, a cleanup that fails, an unreadable response). The shim is the party that knows it printed nothing, so it re-stamps the start begun after the helper tree is gone. A plain-JS mirror of session_start_owed.ts::recordSessionStartBegun.
|
|
1916
|
+
function stampSessionStartOwedFiles(payload, dropClaim) {
|
|
1917
|
+
try {
|
|
1918
|
+
const raw = payload && (payload.session_id !== undefined ? payload.session_id : payload.sessionId)
|
|
1919
|
+
if (typeof raw !== 'string' || raw === '' || raw.indexOf(':agent:') !== -1) return
|
|
1920
|
+
// A subagent's key is salted, and its start is not the parent's to reset.
|
|
1921
|
+
if (payload.agent_id || payload.agentId) return
|
|
1922
|
+
const override = (process.env.TOKEN_GOAT_HOME || '').trim()
|
|
1923
|
+
const rooted = override !== '' && require('node:path').isAbsolute(override) &&
|
|
1924
|
+
(process.platform !== 'win32' || !['', '/', String.fromCharCode(92)].includes(require('node:path').parse(override).root))
|
|
1925
|
+
const home = rooted ? override : require('node:path').join(require('node:os').homedir(), '.token-goat')
|
|
1926
|
+
const stem = raw.replace(/[^a-zA-Z0-9_-]/g, '_').slice(0, 64)
|
|
1927
|
+
if (stem === '') return
|
|
1928
|
+
// The claim goes, and the settle file is rewritten as a begun stamp: only a begun, unsettled start is owed.
|
|
1929
|
+
if (dropClaim) require('node:fs').rmSync(require('node:path').join(home, 'sessions', stem + '.session-start-owed'), { force: true })
|
|
1930
|
+
require('node:fs').mkdirSync(require('node:path').join(home, 'sessions'), { recursive: true })
|
|
1931
|
+
require('node:fs').writeFileSync(require('node:path').join(home, 'sessions', stem + '.session-start'), JSON.stringify({ begunAtMs: Date.now() }), 'utf8')
|
|
1932
|
+
} catch {
|
|
1933
|
+
// Best-effort: the helper clears the marker itself when it gets that far.
|
|
1934
|
+
}
|
|
1935
|
+
}
|
|
1936
|
+
|
|
1937
|
+
async function runBoundedSessionStart(entryPath, eventName, payload, harness, deadline) {
|
|
1938
|
+
resetSessionStartOwedFiles(payload)
|
|
1939
|
+
// Do not probe entryPath here: even existsSync can block on a network filesystem.
|
|
1940
|
+
if (typeof entryPath !== 'string' || entryPath === '') throw new Error('hook entry unavailable')
|
|
1941
|
+
const remainingMs = deadline - Date.now()
|
|
1942
|
+
if (remainingMs <= 0) throw new Error('overall deadline exceeded')
|
|
1943
|
+
// Reject before spawning when only the process-tree teardown reserve remains.
|
|
1944
|
+
if (remainingMs <= SESSION_START_CLEANUP_RESERVE_MS) {
|
|
1945
|
+
throw new Error('insufficient time for process-tree cleanup')
|
|
1946
|
+
}
|
|
1947
|
+
|
|
1948
|
+
const args = [entryPath, 'hook', eventName]
|
|
1949
|
+
const env = Object.assign({}, process.env, { TOKEN_GOAT_HOOK_SERVER: '0' })
|
|
1950
|
+
if (process.platform === 'win32') env.TG_SESSION_START_DEFER_WORKER = '1'
|
|
1951
|
+
// Lets the helper's settle marker say how close to the deadline it finished.
|
|
1952
|
+
env.TG_SESSION_START_DEADLINE_EPOCH = String(deadline)
|
|
1953
|
+
if (harness === 'vscode') {
|
|
1954
|
+
args.push('--harness', 'vscode')
|
|
1955
|
+
env.TOKEN_GOAT_HARNESS_OVERRIDE = 'vscode'
|
|
1956
|
+
env.TOKEN_GOAT_VSCODE_HOOKS_DIR = __dirname
|
|
1957
|
+
} else {
|
|
1958
|
+
env.TOKEN_GOAT_HARNESS_OVERRIDE = 'copilot_cli'
|
|
1959
|
+
env.TOKEN_GOAT_COPILOT_HOOKS_DIR = __dirname
|
|
1960
|
+
}
|
|
1961
|
+
|
|
1962
|
+
let stdout
|
|
1963
|
+
try {
|
|
1964
|
+
stdout = await superviseSessionStart({ args, env, payload, deadline })
|
|
1965
|
+
} catch (error) {
|
|
1966
|
+
// The caller prints the failed-open line and exactly {}: the start is owed, whatever marker the helper left.
|
|
1967
|
+
stampSessionStartOwedFiles(payload, false)
|
|
1968
|
+
throw error
|
|
1969
|
+
}
|
|
1970
|
+
try {
|
|
1971
|
+
const parsed = JSON.parse(stdout.trim())
|
|
1972
|
+
if (parsed === null || typeof parsed !== 'object' || Array.isArray(parsed)) {
|
|
1973
|
+
throw new Error('hook helper returned a non-object response')
|
|
1974
|
+
}
|
|
1975
|
+
return JSON.stringify(parsed)
|
|
1976
|
+
} catch {
|
|
1977
|
+
stampSessionStartOwedFiles(payload, false)
|
|
1978
|
+
throw new Error('hook helper returned invalid JSON')
|
|
1979
|
+
}
|
|
1980
|
+
}
|
|
1981
|
+
|
|
1982
|
+
function superviseSessionStart({ args, env, payload, deadline }) {
|
|
1983
|
+
if (process.platform === 'win32') return superviseWindowsSessionStart({ args, env, payload, deadline })
|
|
1984
|
+
const { spawn } = require('node:child_process')
|
|
1985
|
+
// On POSIX, retain the canonical root until its detached group is killed.
|
|
1986
|
+
const supervisor = [
|
|
1987
|
+
"const keeper = setInterval(() => {}, 1000); keeper.unref()",
|
|
1988
|
+
"let finished = false",
|
|
1989
|
+
"function done(code) { if (finished) return; finished = true; keeper.ref(); process.send({ type: 'hookExit', code }) }",
|
|
1990
|
+
"process.exit = code => done(code === undefined ? (process.exitCode || 0) : Number(code))",
|
|
1991
|
+
"process.once('beforeExit', code => done(code))",
|
|
1992
|
+
"process.once('uncaughtException', () => done(1))",
|
|
1993
|
+
"process.once('unhandledRejection', () => done(1))",
|
|
1994
|
+
"import(require('node:url').pathToFileURL(require('node:path').resolve(process.argv[1])).href).catch(() => done(1))",
|
|
1995
|
+
].join(';')
|
|
1996
|
+
return new Promise((resolve, reject) => {
|
|
1997
|
+
const root = spawn(process.execPath, ['-e', supervisor, ...args], {
|
|
1998
|
+
env, windowsHide: true,
|
|
1999
|
+
// owner: this shim, which SIGKILLs the whole group in beginCleanup and, when that fails or the supervisor died first, in cleanupFailed; dies when the helper reports its exit, on an error, a timeout or the hard deadline (descendants that left the group, or the shim itself being SIGKILLed, are not covered).
|
|
2000
|
+
detached: process.platform !== 'win32',
|
|
2001
|
+
stdio: ['pipe', 'pipe', 'pipe', 'ipc'],
|
|
2002
|
+
})
|
|
2003
|
+
let helperCode
|
|
2004
|
+
let rootClosed = false
|
|
2005
|
+
let cleaning = false
|
|
2006
|
+
let cleaned = false
|
|
2007
|
+
let settled = false
|
|
2008
|
+
let failure
|
|
2009
|
+
let outputBytes = 0
|
|
2010
|
+
const output = []
|
|
2011
|
+
let stderrBytes = 0
|
|
2012
|
+
let timer
|
|
2013
|
+
let hardTimer
|
|
2014
|
+
|
|
2015
|
+
function alive(pid) {
|
|
2016
|
+
if (!pid) return false
|
|
2017
|
+
try { process.kill(pid, 0); return true } catch (error) { return error.code === 'EPERM' }
|
|
2018
|
+
}
|
|
2019
|
+
|
|
2020
|
+
function finish(error) {
|
|
2021
|
+
if (settled) return
|
|
2022
|
+
settled = true
|
|
2023
|
+
clearTimeout(timer)
|
|
2024
|
+
clearTimeout(hardTimer)
|
|
2025
|
+
root.removeListener('message', onMessage)
|
|
2026
|
+
root.removeListener('exit', onExit)
|
|
2027
|
+
root.removeListener('close', onClose)
|
|
2028
|
+
root.stdout.removeListener('data', onOutput)
|
|
2029
|
+
root.stderr.removeListener('data', onStderr)
|
|
2030
|
+
root.stdin.destroy()
|
|
2031
|
+
root.stdout.destroy()
|
|
2032
|
+
root.stderr.destroy()
|
|
2033
|
+
if (root.connected) root.disconnect()
|
|
2034
|
+
// owner: this shim, which has killed the group by now (beginCleanup, or the group kill in cleanupFailed); dies when that kill lands, and the unref only lets the shim exit.
|
|
2035
|
+
root.unref()
|
|
2036
|
+
if (error) reject(new Error(error))
|
|
2037
|
+
else resolve(Buffer.concat(output, outputBytes).toString('utf8'))
|
|
2038
|
+
}
|
|
2039
|
+
|
|
2040
|
+
function complete() {
|
|
2041
|
+
// close, not exit, proves all buffered stdout was drained. The hard deadline
|
|
2042
|
+
// below also covers an escaped descendant retaining a pipe indefinitely.
|
|
2043
|
+
if (!cleaned || !rootClosed) return
|
|
2044
|
+
if (!failure && (helperCode !== 0 || outputBytes === 0)) failure = 'hook helper exited without a response'
|
|
2045
|
+
finish(failure)
|
|
2046
|
+
}
|
|
2047
|
+
|
|
2048
|
+
function killGroup() { process.kill(-root.pid, 'SIGKILL') }
|
|
2049
|
+
|
|
2050
|
+
function cleanupFailed(reason) {
|
|
2051
|
+
// The group outlives its leader while any member runs, so this reaches descendants even after the supervisor died; ESRCH means no member is left. Descendants that left the group are still unverified.
|
|
2052
|
+
if (root.pid) {
|
|
2053
|
+
try { killGroup() } catch (error) {
|
|
2054
|
+
if (error.code !== 'ESRCH') reason += '; group kill failed'
|
|
2055
|
+
}
|
|
2056
|
+
}
|
|
2057
|
+
// Verify the known owned PIDs before falling back to direct kills. Unknown
|
|
2058
|
+
// descendants cannot be certified clean when the tree utility has failed.
|
|
2059
|
+
const remaining = [root.pid].filter(alive)
|
|
2060
|
+
for (const pid of remaining) {
|
|
2061
|
+
try { process.kill(pid, 'SIGKILL') } catch (error) {
|
|
2062
|
+
if (error.code !== 'ESRCH') reason += '; direct kill failed for PID ' + pid
|
|
2063
|
+
}
|
|
2064
|
+
}
|
|
2065
|
+
finish((failure ? failure + '; ' : '') + 'process-tree cleanup failed: ' + reason
|
|
2066
|
+
+ '; remaining owned PIDs before fallback: ' + (remaining.join(',') || 'none')
|
|
2067
|
+
+ '; remaining known PIDs after fallback: ' + (remaining.filter(alive).join(',') || 'none')
|
|
2068
|
+
+ '; descendant termination not verified')
|
|
2069
|
+
}
|
|
2070
|
+
|
|
2071
|
+
function beginCleanup(reason) {
|
|
2072
|
+
if (reason && !failure) failure = reason
|
|
2073
|
+
if (settled || cleaning) return
|
|
2074
|
+
cleaning = true
|
|
2075
|
+
clearTimeout(timer)
|
|
2076
|
+
if (!root.pid) { finish(failure || 'hook helper could not run'); return }
|
|
2077
|
+
try {
|
|
2078
|
+
killGroup()
|
|
2079
|
+
cleaned = true
|
|
2080
|
+
complete()
|
|
2081
|
+
} catch (error) {
|
|
2082
|
+
cleanupFailed(error.code || 'group kill failed')
|
|
2083
|
+
}
|
|
2084
|
+
}
|
|
2085
|
+
|
|
2086
|
+
function onOutput(chunk) {
|
|
2087
|
+
outputBytes += chunk.length
|
|
2088
|
+
if (outputBytes > SESSION_START_MAX_INPUT_BYTES) { beginCleanup('hook helper output exceeded 1 MiB'); return }
|
|
2089
|
+
output.push(chunk)
|
|
2090
|
+
}
|
|
2091
|
+
|
|
2092
|
+
function onStderr(chunk) {
|
|
2093
|
+
stderrBytes += chunk.length
|
|
2094
|
+
if (stderrBytes > SESSION_START_MAX_INPUT_BYTES) beginCleanup('hook helper stderr exceeded 1 MiB')
|
|
2095
|
+
}
|
|
2096
|
+
|
|
2097
|
+
function onMessage(message) {
|
|
2098
|
+
if (message && message.type === 'hookExit') {
|
|
2099
|
+
helperCode = message.code
|
|
2100
|
+
beginCleanup(message.code === 0 ? undefined : 'hook helper exited without a response')
|
|
2101
|
+
}
|
|
2102
|
+
}
|
|
2103
|
+
|
|
2104
|
+
function onExit() {
|
|
2105
|
+
if (!cleaning) cleanupFailed('supervisor exited before tree cleanup')
|
|
2106
|
+
}
|
|
2107
|
+
|
|
2108
|
+
function onClose() { rootClosed = true; complete() }
|
|
2109
|
+
|
|
2110
|
+
root.on('message', onMessage)
|
|
2111
|
+
root.once('exit', onExit)
|
|
2112
|
+
root.once('close', onClose)
|
|
2113
|
+
root.once('error', () => beginCleanup('hook helper could not run'))
|
|
2114
|
+
root.stdin.on('error', () => beginCleanup('hook helper input failed'))
|
|
2115
|
+
root.stdout.on('data', onOutput)
|
|
2116
|
+
root.stderr.on('data', onStderr)
|
|
2117
|
+
root.stdout.on('error', () => beginCleanup('hook helper stdout failed'))
|
|
2118
|
+
root.stderr.on('error', () => beginCleanup('hook helper stderr failed'))
|
|
2119
|
+
const remaining = deadline - Date.now()
|
|
2120
|
+
// Reserve the final second of the single budget for owned-tree teardown.
|
|
2121
|
+
timer = setTimeout(() => beginCleanup('hook helper timed out'), Math.max(0, remaining - SESSION_START_CLEANUP_RESERVE_MS))
|
|
2122
|
+
hardTimer = setTimeout(() => cleanupFailed('overall deadline exceeded'), Math.max(0, remaining))
|
|
2123
|
+
root.stdin.end(JSON.stringify(payload))
|
|
2124
|
+
})
|
|
2125
|
+
}
|
|
2126
|
+
|
|
2127
|
+
function superviseWindowsSessionStart({ args, env, payload, deadline }) {
|
|
2128
|
+
const { spawn } = require('node:child_process')
|
|
2129
|
+
const path = require('node:path')
|
|
2130
|
+
// Pure path arithmetic on the trusted installed entry, never PATH lookup or
|
|
2131
|
+
// synchronous filesystem probing in the externally visible parent.
|
|
2132
|
+
const binary = path.join(path.dirname(args[0]), 'native', 'win32-' + process.arch, 'tg-hook.exe')
|
|
2133
|
+
return new Promise((resolve, reject) => {
|
|
2134
|
+
// owner: the native tg-hook client, which holds a strict Job Object and ends every descendant when this shim closes its handle; dies when the job closes or the deadline passes.
|
|
2135
|
+
const root = spawn(binary, ['--session-start-owner-v1', String(deadline), process.execPath, ...args], {
|
|
2136
|
+
env, windowsHide: true, stdio: ['pipe', 'pipe', 'pipe'],
|
|
2137
|
+
})
|
|
2138
|
+
const output = []
|
|
2139
|
+
const diagnostics = []
|
|
2140
|
+
let outputBytes = 0
|
|
2141
|
+
let diagnosticBytes = 0
|
|
2142
|
+
let settled = false
|
|
2143
|
+
let timer
|
|
2144
|
+
function finish(error, candidate) {
|
|
2145
|
+
if (settled) return
|
|
2146
|
+
settled = true
|
|
2147
|
+
clearTimeout(timer)
|
|
2148
|
+
root.stdin.destroy()
|
|
2149
|
+
root.stdout.destroy()
|
|
2150
|
+
root.stderr.destroy()
|
|
2151
|
+
// Killing the owner closes the sole non-inherited job handle. Never
|
|
2152
|
+
// traverse a dead canonical PID or launch an unowned Windows fallback.
|
|
2153
|
+
if (error && root.pid) root.kill('SIGKILL')
|
|
2154
|
+
// owner: this shim, which kills the native owner on any error (its job handle closing ends the tree) and finds it closed on success; dies when that kill lands or the owner has exited.
|
|
2155
|
+
root.unref()
|
|
2156
|
+
if (error) reject(new Error(error))
|
|
2157
|
+
else resolve(JSON.stringify(candidate))
|
|
2158
|
+
}
|
|
2159
|
+
root.once('error', error => finish('native startup owner unavailable: ' + error.message))
|
|
2160
|
+
root.stdin.on('error', error => finish('native startup owner input failed: ' + error.message))
|
|
2161
|
+
root.stdout.on('error', error => finish('native startup owner stdout failed: ' + error.message))
|
|
2162
|
+
root.stderr.on('error', error => finish('native startup owner stderr failed: ' + error.message))
|
|
2163
|
+
root.stdout.on('data', chunk => {
|
|
2164
|
+
outputBytes += chunk.length
|
|
2165
|
+
if (outputBytes > SESSION_START_MAX_INPUT_BYTES + 4096) finish('native startup owner output exceeded 1 MiB')
|
|
2166
|
+
else output.push(chunk)
|
|
2167
|
+
})
|
|
2168
|
+
root.stderr.on('data', chunk => {
|
|
2169
|
+
diagnosticBytes += chunk.length
|
|
2170
|
+
if (diagnosticBytes > SESSION_START_MAX_INPUT_BYTES) finish('native startup owner stderr exceeded 1 MiB')
|
|
2171
|
+
else diagnostics.push(chunk)
|
|
2172
|
+
})
|
|
2173
|
+
root.once('close', code => {
|
|
2174
|
+
if (settled) return
|
|
2175
|
+
const diagnostic = Buffer.concat(diagnostics).toString('utf8').trim()
|
|
2176
|
+
if (code !== 0) { finish('native startup owner failed: ' + (diagnostic || 'exit ' + code)); return }
|
|
2177
|
+
let ack
|
|
2178
|
+
try { ack = JSON.parse(Buffer.concat(output).toString('utf8')) } catch {
|
|
2179
|
+
finish('native startup owner returned invalid protocol JSON'); return
|
|
2180
|
+
}
|
|
2181
|
+
if (!ack || ack.protocol !== 'tg-session-start-owner-v1' || ack.capability !== 'atomic-job-list'
|
|
2182
|
+
|| ack.activeProcesses !== 0 || ack.rootExited !== true || ack.pipesDrained !== true
|
|
2183
|
+
|| !ack.candidate || typeof ack.candidate !== 'object' || Array.isArray(ack.candidate)) {
|
|
2184
|
+
finish('native startup owner incompatible protocol or unverified cleanup'); return
|
|
2185
|
+
}
|
|
2186
|
+
if (ack.workerWake === true) {
|
|
2187
|
+
// The installed native entry owns this shim too. Relay only a bounded
|
|
2188
|
+
// request; it, not an inner owner, must launch outside every startup job.
|
|
2189
|
+
if (process.argv[4] && process.argv[4].startsWith('--session-start-deadline=')) {
|
|
2190
|
+
process.stderr.write('\\x1eTG_WORKER_WAKE_V1\\x1e\\n')
|
|
2191
|
+
} else if (process.env.TOKEN_GOAT_NO_WORKER_SPAWN !== '1') {
|
|
2192
|
+
const workerEnv = Object.assign({}, process.env)
|
|
2193
|
+
delete workerEnv.TG_SESSION_START_DEFER_WORKER
|
|
2194
|
+
try {
|
|
2195
|
+
const worker = spawn(process.execPath, [args[0], '--worker-daemon', '--claim-worker'], {
|
|
2196
|
+
env: workerEnv, cwd: require('node:os').tmpdir(), windowsHide: true, stdio: 'ignore',
|
|
2197
|
+
// owner: none, the daemon outlives this shim on purpose; dies when its pid file or data directory is gone, when it sits idle, or on worker stop.
|
|
2198
|
+
detached: true,
|
|
2199
|
+
})
|
|
2200
|
+
worker.on('error', () => {})
|
|
2201
|
+
// owner: none, the daemon outlives this shim on purpose; dies when its pid file or data directory is gone, when it sits idle, or on worker stop.
|
|
2202
|
+
worker.unref()
|
|
2203
|
+
} catch {
|
|
2204
|
+
// Queue remains durable; ordinary lifecycle activation can retry.
|
|
2205
|
+
}
|
|
2206
|
+
}
|
|
2207
|
+
}
|
|
2208
|
+
finish(undefined, ack.candidate)
|
|
2209
|
+
})
|
|
2210
|
+
timer = setTimeout(() => finish('native startup owner overall deadline exceeded'), Math.max(0, deadline - Date.now()))
|
|
2211
|
+
root.stdin.end(JSON.stringify(payload))
|
|
2212
|
+
})
|
|
2213
|
+
}
|
|
2214
|
+
`;
|
|
2215
|
+
|
|
1713
2216
|
// src/bridges/shrink_block.ts
|
|
1714
2217
|
init_define_import_meta_env();
|
|
1715
2218
|
var MATERIALIZE_SHRUNK_IMAGE_JS = `// Best-effort sweep of previously materialized shrunk copies in the OS temp dir. The temp file only needs to outlive the single tool call whose path argument was rewritten to it, so anything older than an hour is finished with; the "token-goat-shrink-" prefix check confines the sweep to this mechanism's own files (same defense-in-depth rule as pruneShrinkCache in src/image_shrink.ts). Throttled per process; runs only when a shrink payload actually arrives, so the common no-image path never pays for it.
|
|
@@ -1761,12 +2264,17 @@ var COPILOT_CLI_HOOK_SCRIPT = `#!/usr/bin/env node
|
|
|
1761
2264
|
// token-goat Copilot CLI hook shim. Translates Copilot's hook event names and
|
|
1762
2265
|
// request/response schema to/from token-goat's internal hook protocol.
|
|
1763
2266
|
'use strict'
|
|
2267
|
+
${SHIM_MAX_BUFFER_CONST}
|
|
1764
2268
|
const { spawnSync } = require('node:child_process')
|
|
1765
2269
|
const fs = require('node:fs')
|
|
1766
2270
|
const os = require('node:os')
|
|
1767
2271
|
const path = require('node:path')
|
|
1768
2272
|
const { pathToFileURL } = require('node:url')
|
|
1769
2273
|
|
|
2274
|
+
// How long the fallback spawn of the real hook may run before the shim answers an empty fail-open '{}' for it. The hook entries token-goat installs give Copilot 60 s (HOOK_TIMEOUT_SEC in copilot_cli_install.ts); the number is SHIM_FALLBACK_TIMEOUT_MS.copilot in shim_common.ts, and a hook Copilot kills fails open the same way, so a cap of a few seconds only discarded the verdict of a cold start that was merely slow (bundle load, DB open, a loaded machine). Kept under that 60 s so the shim, not Copilot, ends a hook that really is stuck.
|
|
2275
|
+
${shimFallbackTimeoutConst("copilot")}
|
|
2276
|
+
${COPILOT_SESSION_START_HELPERS}
|
|
2277
|
+
|
|
1770
2278
|
// Copilot event name -> token-goat internal HookEventName (src/types.ts's
|
|
1771
2279
|
// HOOK_EVENTS). Only these nine have a token-goat handler; every other real
|
|
1772
2280
|
// Copilot event (sessionEnd, errorOccurred, notification, permissionRequest)
|
|
@@ -1815,6 +2323,7 @@ const COPILOT_TO_TG_EVENT = {
|
|
|
1815
2323
|
// one of these keys is answered with nothing, so a Copilot build that also
|
|
1816
2324
|
// honored the PascalCase spelling could not brief a subagent twice.
|
|
1817
2325
|
const VSCODE_ONLY_TO_TG_EVENT = {
|
|
2326
|
+
SessionStart: 'session_start',
|
|
1818
2327
|
SubagentStart: 'subagent_start',
|
|
1819
2328
|
}
|
|
1820
2329
|
|
|
@@ -2074,9 +2583,9 @@ async function relayVscode(entryPath, tgEvent, payload) {
|
|
|
2074
2583
|
input: JSON.stringify(input),
|
|
2075
2584
|
encoding: 'utf8',
|
|
2076
2585
|
windowsHide: true,
|
|
2077
|
-
timeout:
|
|
2586
|
+
timeout: SHIM_FALLBACK_TIMEOUT_MS,
|
|
2078
2587
|
killSignal: 'SIGKILL',
|
|
2079
|
-
maxBuffer:
|
|
2588
|
+
maxBuffer: SHIM_MAX_BUFFER_BYTES,
|
|
2080
2589
|
env: Object.assign({}, process.env, { TOKEN_GOAT_HARNESS_OVERRIDE: 'vscode', TOKEN_GOAT_VSCODE_HOOKS_DIR: __dirname }),
|
|
2081
2590
|
}
|
|
2082
2591
|
const res = entryPath
|
|
@@ -2094,6 +2603,7 @@ async function relayVscode(entryPath, tgEvent, payload) {
|
|
|
2094
2603
|
}
|
|
2095
2604
|
|
|
2096
2605
|
async function main() {
|
|
2606
|
+
const startedAt = Math.floor(require('node:perf_hooks').performance.timeOrigin)
|
|
2097
2607
|
const copilotEvent = process.argv[2] || ''
|
|
2098
2608
|
|
|
2099
2609
|
// ownGet, not a bare lookup: the map is a plain object literal, so eight keys it never declares ('constructor', 'toString', '__proto__' and the rest of Object.prototype) come back truthy and sail past the check below. tgEvent is then a Function, and 'token-goat hook ' + tgEvent is concatenated into a shell command string by the fallback at the bottom of this shim. No member of Object.prototype stringifies with a shell metacharacter, so this was a failed spawn rather than a second command -- but argv reaching a shell string through a gate that reads like it stops it is the shape worth closing, not the payload that happened to be harmless. The four shims built on shim_common.ts validate against a closed Set, which has no equivalent hole.
|
|
@@ -2104,24 +2614,56 @@ async function main() {
|
|
|
2104
2614
|
return
|
|
2105
2615
|
}
|
|
2106
2616
|
|
|
2617
|
+
let sessionStartDeadline
|
|
2618
|
+
if (tgEvent === 'session_start') {
|
|
2619
|
+
const inherited = process.argv[4]
|
|
2620
|
+
const epoch = inherited === undefined ? startedAt + sessionStartBudgetMs()
|
|
2621
|
+
: inherited.startsWith('--session-start-deadline=') ? Number(inherited.slice('--session-start-deadline='.length)) : NaN
|
|
2622
|
+
if (!Number.isSafeInteger(epoch) || epoch <= 0 || epoch > startedAt + sessionStartBudgetMs()) {
|
|
2623
|
+
failOpenSessionStart('invalid inherited startup deadline')
|
|
2624
|
+
return
|
|
2625
|
+
}
|
|
2626
|
+
sessionStartDeadline = epoch
|
|
2627
|
+
}
|
|
2107
2628
|
let raw = ''
|
|
2108
|
-
|
|
2109
|
-
|
|
2110
|
-
|
|
2111
|
-
|
|
2112
|
-
|
|
2629
|
+
if (sessionStartDeadline !== undefined) {
|
|
2630
|
+
try {
|
|
2631
|
+
raw = await readSessionStartInput(sessionStartDeadline - Date.now())
|
|
2632
|
+
} catch (error) {
|
|
2633
|
+
failOpenSessionStart(error && error.message ? error.message : 'input could not be read')
|
|
2634
|
+
return
|
|
2635
|
+
}
|
|
2636
|
+
} else {
|
|
2637
|
+
try {
|
|
2638
|
+
raw = require('node:fs').readFileSync(0, 'utf8')
|
|
2639
|
+
} catch {
|
|
2640
|
+
process.stdout.write('{}')
|
|
2641
|
+
return
|
|
2642
|
+
}
|
|
2113
2643
|
}
|
|
2114
2644
|
|
|
2115
2645
|
let payload
|
|
2116
2646
|
try {
|
|
2117
2647
|
payload = JSON.parse(raw)
|
|
2118
2648
|
} catch {
|
|
2119
|
-
|
|
2649
|
+
if (sessionStartDeadline !== undefined) failOpenSessionStart('input contained malformed JSON')
|
|
2650
|
+
else process.stdout.write('{}')
|
|
2120
2651
|
return
|
|
2121
2652
|
}
|
|
2122
2653
|
|
|
2123
2654
|
if (isVscodePayload(payload)) {
|
|
2124
|
-
|
|
2655
|
+
if (sessionStartDeadline === undefined) {
|
|
2656
|
+
process.stdout.write(await relayVscode(process.argv[3], tgEvent, payload))
|
|
2657
|
+
} else {
|
|
2658
|
+
try {
|
|
2659
|
+
const input = typeof payload.session_id === 'string' && payload.session_id !== ''
|
|
2660
|
+
? payload
|
|
2661
|
+
: Object.assign({}, payload, { session_id: stableFallbackSessionId(process.cwd()) })
|
|
2662
|
+
process.stdout.write(await runBoundedSessionStart(process.argv[3], tgEvent, input, 'vscode', sessionStartDeadline))
|
|
2663
|
+
} catch (error) {
|
|
2664
|
+
failOpenSessionStart(error && error.message ? error.message : 'hook helper failed')
|
|
2665
|
+
}
|
|
2666
|
+
}
|
|
2125
2667
|
return
|
|
2126
2668
|
}
|
|
2127
2669
|
if (vscodeOnly) {
|
|
@@ -2214,23 +2756,28 @@ async function main() {
|
|
|
2214
2756
|
// template string) is safe here specifically because entryPath and tgEvent never touch a
|
|
2215
2757
|
// shell -- no DEP0190 concern, unlike the shell:true fallback below.
|
|
2216
2758
|
const entryPath = process.argv[3]
|
|
2217
|
-
//
|
|
2218
|
-
|
|
2219
|
-
|
|
2220
|
-
|
|
2221
|
-
|
|
2222
|
-
|
|
2223
|
-
|
|
2224
|
-
|
|
2759
|
+
// SessionStart bypasses the resident server and in-process path: only a child-process deadline can interrupt synchronous handler or SQLite work. TOKEN_GOAT_HOOK_SERVER=0 below also prevents that child from leaving a server process behind.
|
|
2760
|
+
let stdout
|
|
2761
|
+
if (sessionStartDeadline !== undefined) {
|
|
2762
|
+
try {
|
|
2763
|
+
stdout = await runBoundedSessionStart(entryPath, tgEvent, canonical, 'copilot_cli', sessionStartDeadline)
|
|
2764
|
+
} catch (error) {
|
|
2765
|
+
failOpenSessionStart(error && error.message ? error.message : 'hook helper failed')
|
|
2766
|
+
return
|
|
2767
|
+
}
|
|
2768
|
+
} else {
|
|
2769
|
+
// Other events retain the in-process fast path and existing fallback behavior.
|
|
2770
|
+
stdout = await tryInProcess(entryPath, tgEvent, canonical, 'copilot_cli')
|
|
2771
|
+
}
|
|
2225
2772
|
if (stdout === undefined) {
|
|
2226
2773
|
const res = entryPath
|
|
2227
2774
|
? spawnSync(process.execPath, [entryPath, 'hook', tgEvent], {
|
|
2228
2775
|
input: JSON.stringify(canonical),
|
|
2229
2776
|
encoding: 'utf8',
|
|
2230
2777
|
windowsHide: true,
|
|
2231
|
-
timeout:
|
|
2778
|
+
timeout: SHIM_FALLBACK_TIMEOUT_MS,
|
|
2232
2779
|
killSignal: 'SIGKILL',
|
|
2233
|
-
maxBuffer:
|
|
2780
|
+
maxBuffer: SHIM_MAX_BUFFER_BYTES,
|
|
2234
2781
|
env: Object.assign({}, process.env, { TOKEN_GOAT_HARNESS_OVERRIDE: 'copilot_cli', TOKEN_GOAT_COPILOT_HOOKS_DIR: __dirname }),
|
|
2235
2782
|
})
|
|
2236
2783
|
: spawnSync('token-goat hook ' + tgEvent, {
|
|
@@ -2238,9 +2785,9 @@ async function main() {
|
|
|
2238
2785
|
encoding: 'utf8',
|
|
2239
2786
|
shell: true,
|
|
2240
2787
|
windowsHide: true,
|
|
2241
|
-
timeout:
|
|
2788
|
+
timeout: SHIM_FALLBACK_TIMEOUT_MS,
|
|
2242
2789
|
killSignal: 'SIGKILL',
|
|
2243
|
-
maxBuffer:
|
|
2790
|
+
maxBuffer: SHIM_MAX_BUFFER_BYTES,
|
|
2244
2791
|
env: Object.assign({}, process.env, { TOKEN_GOAT_HARNESS_OVERRIDE: 'copilot_cli', TOKEN_GOAT_COPILOT_HOOKS_DIR: __dirname }),
|
|
2245
2792
|
})
|
|
2246
2793
|
if (res.status !== 0 || !res.stdout) {
|
|
@@ -2586,7 +3133,7 @@ function installCopilotMcpServer() {
|
|
|
2586
3133
|
const config = readServersJson(mcpPath, LABEL);
|
|
2587
3134
|
const current = serversOf(config, mcpPath, LABEL, MCP_SERVERS_KEY)[TOKEN_GOAT_ENTRY_KEY];
|
|
2588
3135
|
if (current !== void 0 && !isCopilotManagedServer(current) && !isReadmeServer(current)) {
|
|
2589
|
-
throw new Error(`Copilot CLI mcp-config.json already has a
|
|
3136
|
+
throw new Error(`Copilot CLI mcp-config.json already has a ${echoedValue(TOKEN_GOAT_ENTRY_KEY)} MCP server entry token-goat did not write, at ${mcpPath}; remove it manually first`);
|
|
2590
3137
|
}
|
|
2591
3138
|
const nextText = setTokenGoatServer(config.text, copilotManagedServer(), MCP_SERVERS_KEY);
|
|
2592
3139
|
if (nextText === config.text) return false;
|
|
@@ -2656,8 +3203,8 @@ function visualStudioDuplicateNote(projectRoot = process.cwd()) {
|
|
|
2656
3203
|
const found = visualStudioManagedRegistrations(projectRoot);
|
|
2657
3204
|
if (found.length < 2) return null;
|
|
2658
3205
|
const lead = `Note: Visual Studio reads ${found.join(" and ")}, and each registers token-goat, so it will list the token-goat server more than once for this solution.`;
|
|
2659
|
-
if (!found.includes(visualStudioSolutionVscodeMcpPath(projectRoot))) return `${lead} Keep one: run "token-goat uninstall --visualstudio" or "token-goat uninstall --visualstudio -p".`;
|
|
2660
|
-
return `${lead} Keep one: if you do not use VS Code in this folder, run "token-goat uninstall --vscode -p"; otherwise run "token-goat uninstall --visualstudio
|
|
3206
|
+
if (!found.includes(visualStudioSolutionVscodeMcpPath(projectRoot))) return `${lead} Keep one: run ${fencedCommand("token-goat uninstall --visualstudio")} or ${fencedCommand("token-goat uninstall --visualstudio -p")}.`;
|
|
3207
|
+
return `${lead} Keep one: if you do not use VS Code in this folder, run ${fencedCommand("token-goat uninstall --vscode -p")}; otherwise run ${fencedCommand("token-goat uninstall --visualstudio" + (found.includes(visualStudioUserMcpPath()) ? "" : " -p"))}.`;
|
|
2661
3208
|
}
|
|
2662
3209
|
function visualStudioMcpStatus(opts = {}) {
|
|
2663
3210
|
const checkedPaths = [visualStudioUserMcpPath()];
|
|
@@ -2708,7 +3255,7 @@ function installVisualStudioScoped(opts) {
|
|
|
2708
3255
|
if (visualStudioOtherScopeHasManagedServer(opts)) {
|
|
2709
3256
|
const otherScope = scope === "project" ? "user" : "project";
|
|
2710
3257
|
throw new Error(
|
|
2711
|
-
`token-goat is already registered in Visual Studio ${otherScope} scope (${otherScopeMcpPath(opts)}). Visual Studio reads both files, so installing into ${scope} scope too would register it twice. Run "token-goat uninstall --visualstudio
|
|
3258
|
+
`token-goat is already registered in Visual Studio ${otherScope} scope (${otherScopeMcpPath(opts)}). Visual Studio reads both files, so installing into ${scope} scope too would register it twice. Run ${fencedCommand("token-goat uninstall --visualstudio" + (otherScope === "project" ? " --project" : ""))} first if you want to move it, or drop --visualstudio from this run.`
|
|
2712
3259
|
);
|
|
2713
3260
|
}
|
|
2714
3261
|
const mcpExisted = fs6.existsSync(mcpPath);
|
|
@@ -2926,6 +3473,20 @@ function releaseCopilotHooksFile(hooksDir, owner, keepBackups = false) {
|
|
|
2926
3473
|
function installCopilotCli(opts = {}) {
|
|
2927
3474
|
return withInstallScope(projectScopeRoot(opts), () => installCopilotCliScoped(opts));
|
|
2928
3475
|
}
|
|
3476
|
+
function syncCopilotHooksAndInstructions(opts) {
|
|
3477
|
+
const hooks = installCopilotHooksFile(copilotCliHooksDir(opts), "copilot");
|
|
3478
|
+
const instructionsPath = copilotCliInstructionsPath(opts);
|
|
3479
|
+
const instructionsExisted = fs7.existsSync(instructionsPath);
|
|
3480
|
+
const instructionsChanged = writeCopilotInstructionsBlock(instructionsPath);
|
|
3481
|
+
if (!instructionsExisted) recordCreatedConfig(instructionsPath);
|
|
3482
|
+
return { hooks, instructionsChanged };
|
|
3483
|
+
}
|
|
3484
|
+
function repairCopilotCliHooks(opts = {}) {
|
|
3485
|
+
return withInstallScope(projectScopeRoot(opts), () => {
|
|
3486
|
+
const { hooks, instructionsChanged } = syncCopilotHooksAndInstructions(opts);
|
|
3487
|
+
return hooks.changed || instructionsChanged;
|
|
3488
|
+
});
|
|
3489
|
+
}
|
|
2929
3490
|
function installCopilotCliScoped(opts) {
|
|
2930
3491
|
const instructionsPath = copilotCliInstructionsPath(opts);
|
|
2931
3492
|
const userScope = opts.local !== true;
|
|
@@ -2933,10 +3494,8 @@ function installCopilotCliScoped(opts) {
|
|
|
2933
3494
|
const scopeDirExisted = fs7.existsSync(scopeDir);
|
|
2934
3495
|
const mcpChanged = userScope ? installCopilotMcpServer() : false;
|
|
2935
3496
|
if (!scopeDirExisted && fs7.existsSync(scopeDir)) recordCreatedConfig(scopeDir);
|
|
2936
|
-
const hooks =
|
|
2937
|
-
|
|
2938
|
-
let instructionsChanged = writeCopilotInstructionsBlock(instructionsPath);
|
|
2939
|
-
if (!instructionsExisted) recordCreatedConfig(instructionsPath);
|
|
3497
|
+
const { hooks, instructionsChanged: scopeInstructionsChanged } = syncCopilotHooksAndInstructions(opts);
|
|
3498
|
+
let instructionsChanged = scopeInstructionsChanged;
|
|
2940
3499
|
if (userScope) {
|
|
2941
3500
|
const projectRoot = path7.resolve(opts.projectRoot ?? process.cwd());
|
|
2942
3501
|
const projectInstructions = copilotCliInstructionsPath({ local: true, projectRoot });
|
|
@@ -3001,6 +3560,7 @@ export {
|
|
|
3001
3560
|
SHIM_VALID_HOOK_EVENTS,
|
|
3002
3561
|
SHIM_TRY_IN_PROCESS,
|
|
3003
3562
|
SHIM_SPAWN_LADDER,
|
|
3563
|
+
shimFallbackTimeoutConst,
|
|
3004
3564
|
SHIM_MAX_BUFFER_CONST,
|
|
3005
3565
|
CLAUDECODE_HOOK_SCRIPT,
|
|
3006
3566
|
buildGuidanceBody,
|
|
@@ -3024,6 +3584,7 @@ export {
|
|
|
3024
3584
|
uninstallHooks,
|
|
3025
3585
|
finishHookUninstall,
|
|
3026
3586
|
isInstalled,
|
|
3587
|
+
claudeHooksInstalledAnyScope,
|
|
3027
3588
|
hookEventGaps,
|
|
3028
3589
|
claudeMdPath,
|
|
3029
3590
|
installedClaudeMdBlock,
|
|
@@ -3070,11 +3631,9 @@ export {
|
|
|
3070
3631
|
installVisualStudio,
|
|
3071
3632
|
uninstallVisualStudio,
|
|
3072
3633
|
canonicalProjectRoot,
|
|
3073
|
-
copilotCliHooksDir,
|
|
3074
3634
|
copilotCliConfigPath,
|
|
3075
3635
|
copilotCliScriptPath,
|
|
3076
3636
|
copilotCliInstructionsPath,
|
|
3077
|
-
writeCopilotInstructionsBlock,
|
|
3078
3637
|
wiredCopilotHookWords,
|
|
3079
3638
|
HOOKS_SCRIPT_FILE,
|
|
3080
3639
|
LEGACY_HOOKS_SCRIPT_FILE,
|
|
@@ -3082,6 +3641,7 @@ export {
|
|
|
3082
3641
|
installCopilotHooksFile,
|
|
3083
3642
|
releaseCopilotHooksFile,
|
|
3084
3643
|
installCopilotCli,
|
|
3644
|
+
repairCopilotCliHooks,
|
|
3085
3645
|
uninstallCopilotCli,
|
|
3086
3646
|
isCopilotCliInstalled
|
|
3087
3647
|
};
|