@ken-jo/agent-connector 0.1.0 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +183 -24
- package/dist/{amp-QSFH2V7Y.js → amp-YGV66GJT.js} +9 -2
- package/dist/amp-YGV66GJT.js.map +1 -0
- package/dist/{antigravity-CX2YLSY7.js → antigravity-7IBYFO6P.js} +4 -3
- package/dist/{antigravity-cli-YQDFVV4U.js → antigravity-cli-JEOUP2VN.js} +4 -3
- package/dist/{antigravity-cli-YQDFVV4U.js.map → antigravity-cli-JEOUP2VN.js.map} +1 -1
- package/dist/{chunk-LTT56GI3.js → chunk-3JCRUROU.js} +18 -5
- package/dist/chunk-3JCRUROU.js.map +1 -0
- package/dist/chunk-44HUXXC6.js +284 -0
- package/dist/chunk-44HUXXC6.js.map +1 -0
- package/dist/{chunk-54EMKTTX.js → chunk-DS3LUFVN.js} +21 -21
- package/dist/chunk-DS3LUFVN.js.map +1 -0
- package/dist/chunk-GSGDG2BV.js +568 -0
- package/dist/chunk-GSGDG2BV.js.map +1 -0
- package/dist/{chunk-TRKWUNO4.js → chunk-KQSNDFBN.js} +13 -2
- package/dist/chunk-KQSNDFBN.js.map +1 -0
- package/dist/{chunk-KHO5WNTP.js → chunk-KVAAFYRR.js} +18 -2
- package/dist/chunk-KVAAFYRR.js.map +1 -0
- package/dist/chunk-N6HH7K6P.js +494 -0
- package/dist/chunk-N6HH7K6P.js.map +1 -0
- package/dist/{chunk-XUCWLJY5.js → chunk-NJU275US.js} +30 -30
- package/dist/{chunk-WCETQWWK.js → chunk-OP4KBXUO.js} +1 -17
- package/dist/chunk-OP4KBXUO.js.map +1 -0
- package/dist/{chunk-TIIP3RG2.js → chunk-PD4VFEBY.js} +342 -2
- package/dist/chunk-PD4VFEBY.js.map +1 -0
- package/dist/chunk-QADSG2JY.js +1989 -0
- package/dist/chunk-QADSG2JY.js.map +1 -0
- package/dist/{chunk-WV63X7IK.js → chunk-SCVBYSF7.js} +2 -2
- package/dist/chunk-SCVBYSF7.js.map +1 -0
- package/dist/{package-DLOVE2E4.js → chunk-WNREHWKL.js} +26 -115
- package/dist/chunk-WNREHWKL.js.map +1 -0
- package/dist/{chunk-YNZU6PAV.js → chunk-ZAM4M7LI.js} +74 -21
- package/dist/chunk-ZAM4M7LI.js.map +1 -0
- package/dist/claude-code-4275HQU4.js +1432 -0
- package/dist/claude-code-4275HQU4.js.map +1 -0
- package/dist/cli/sdk.js +4 -3
- package/dist/cli/sdk.js.map +1 -1
- package/dist/cli.js +1 -1
- package/dist/{codebuff-QX57FJDN.js → codebuff-LE3KBZGM.js} +9 -2
- package/dist/codebuff-LE3KBZGM.js.map +1 -0
- package/dist/{codex-JFQTS6LU.js → codex-4BY2ZJIW.js} +148 -7
- package/dist/codex-4BY2ZJIW.js.map +1 -0
- package/dist/{copilot-cli-MTCGHZ2T.js → copilot-cli-IPISKO3V.js} +109 -2
- package/dist/copilot-cli-IPISKO3V.js.map +1 -0
- package/dist/{crush-I7PK3FGI.js → crush-X3E25EQG.js} +31 -3
- package/dist/crush-X3E25EQG.js.map +1 -0
- package/dist/{cursor-4TMXKJOZ.js → cursor-UH3W622V.js} +66 -5
- package/dist/cursor-UH3W622V.js.map +1 -0
- package/dist/{detect-WIAGVYS4.js → detect-MNDHGLSV.js} +7 -3
- package/dist/detect-MNDHGLSV.js.map +1 -0
- package/dist/{doctor-EZW2K4NI.js → doctor-N5KZDD55.js} +45 -7
- package/dist/doctor-N5KZDD55.js.map +1 -0
- package/dist/{droid-KN6VRMMT.js → droid-T5VBRXZF.js} +32 -3
- package/dist/droid-T5VBRXZF.js.map +1 -0
- package/dist/{gemini-cli-Y35FFK4M.js → gemini-cli-ZSUK55UA.js} +51 -3
- package/dist/gemini-cli-ZSUK55UA.js.map +1 -0
- package/dist/{goose-ZTGTSUFJ.js → goose-M3RR3CZG.js} +63 -4
- package/dist/goose-M3RR3CZG.js.map +1 -0
- package/dist/{hermes-H2KV44GK.js → hermes-CUNKIAR5.js} +76 -3
- package/dist/hermes-CUNKIAR5.js.map +1 -0
- package/dist/{hook-YVDILAMZ.js → hook-7LUOKIXH.js} +28 -13
- package/dist/hook-7LUOKIXH.js.map +1 -0
- package/dist/index.d.ts +2 -2
- package/dist/index.js +3 -1
- package/dist/install-TQTT4F3I.js +89 -0
- package/dist/install-TQTT4F3I.js.map +1 -0
- package/dist/{jetbrains-copilot-HWA3DMH7.js → jetbrains-copilot-C3HDGGML.js} +16 -2
- package/dist/jetbrains-copilot-C3HDGGML.js.map +1 -0
- package/dist/kilo-PKAJCWPX.js +852 -0
- package/dist/kilo-PKAJCWPX.js.map +1 -0
- package/dist/{kilo-cli-EAQTHXUW.js → kilo-cli-QWUVXG2O.js} +186 -7
- package/dist/kilo-cli-QWUVXG2O.js.map +1 -0
- package/dist/{kimi-GQ7VXNYT.js → kimi-EVD7YKXS.js} +167 -9
- package/dist/kimi-EVD7YKXS.js.map +1 -0
- package/dist/{kiro-VQTT6LO2.js → kiro-UQRY6EIO.js} +98 -4
- package/dist/kiro-UQRY6EIO.js.map +1 -0
- package/dist/{leaderboard-K6ON3SWW.js → leaderboard-MUJ7DURM.js} +3 -3
- package/dist/{mux-T2TV4CIQ.js → mux-5CI5FP2W.js} +9 -2
- package/dist/mux-5CI5FP2W.js.map +1 -0
- package/dist/{omp-PB4VDKS6.js → omp-AXA7XY22.js} +13 -2
- package/dist/omp-AXA7XY22.js.map +1 -0
- package/dist/{openclaw-ETN2M63O.js → openclaw-UZ5BMCGQ.js} +101 -4
- package/dist/openclaw-UZ5BMCGQ.js.map +1 -0
- package/dist/{opencode-FZ33JKUZ.js → opencode-NZBRLJN3.js} +53 -7
- package/dist/opencode-NZBRLJN3.js.map +1 -0
- package/dist/package-SYPA5POO.js +130 -0
- package/dist/package-SYPA5POO.js.map +1 -0
- package/dist/{pi-CIU2QHT4.js → pi-WMTUX6ES.js} +106 -23
- package/dist/pi-WMTUX6ES.js.map +1 -0
- package/dist/{qwen-code-5POCNU6P.js → qwen-code-MRRIUQ3P.js} +187 -6
- package/dist/qwen-code-MRRIUQ3P.js.map +1 -0
- package/dist/{roo-code-IOIUXRDE.js → roo-code-NI5UU6JB.js} +26 -2
- package/dist/roo-code-NI5UU6JB.js.map +1 -0
- package/dist/runtime/index.d.ts +38 -4
- package/dist/runtime/index.js +11 -6
- package/dist/{serve-HVWWY4B6.js → serve-XJOSA7BY.js} +9 -8
- package/dist/{serve-HVWWY4B6.js.map → serve-XJOSA7BY.js.map} +1 -1
- package/dist/{status-FE7TECZD.js → status-DYSS6BOR.js} +19 -9
- package/dist/status-DYSS6BOR.js.map +1 -0
- package/dist/{telemetry-36GESKQQ.js → telemetry-IJ4GP2Q3.js} +5 -4
- package/dist/{telemetry-36GESKQQ.js.map → telemetry-IJ4GP2Q3.js.map} +1 -1
- package/dist/{trae-JSNDZFKL.js → trae-CQC72RHH.js} +9 -2
- package/dist/trae-CQC72RHH.js.map +1 -0
- package/dist/types-CNigfEuR.d.ts +757 -0
- package/dist/uninstall-3DXIBRCV.js +162 -0
- package/dist/uninstall-3DXIBRCV.js.map +1 -0
- package/dist/{upgrade-JV2VK242.js → upgrade-GJEB3ITY.js} +29 -10
- package/dist/upgrade-GJEB3ITY.js.map +1 -0
- package/dist/{usage-6ER6EMEY.js → usage-2BJLM3GJ.js} +3 -3
- package/dist/{usage-event-JKBI6XCF.js → usage-event-472X3DOY.js} +8 -7
- package/dist/{usage-event-JKBI6XCF.js.map → usage-event-472X3DOY.js.map} +1 -1
- package/dist/{vscode-copilot-CK3NO54M.js → vscode-copilot-4RVRTCKV.js} +53 -4
- package/dist/vscode-copilot-4RVRTCKV.js.map +1 -0
- package/dist/{warp-QNVDXOG4.js → warp-X6MB67NT.js} +110 -4
- package/dist/warp-X6MB67NT.js.map +1 -0
- package/dist/{zed-VRJKWYUR.js → zed-NTCYW4V2.js} +120 -5
- package/dist/zed-NTCYW4V2.js.map +1 -0
- package/package.json +4 -3
- package/dist/amp-QSFH2V7Y.js.map +0 -1
- package/dist/chunk-54EMKTTX.js.map +0 -1
- package/dist/chunk-7MSDGHJW.js +0 -316
- package/dist/chunk-7MSDGHJW.js.map +0 -1
- package/dist/chunk-KHO5WNTP.js.map +0 -1
- package/dist/chunk-LTT56GI3.js.map +0 -1
- package/dist/chunk-TIIP3RG2.js.map +0 -1
- package/dist/chunk-TRKWUNO4.js.map +0 -1
- package/dist/chunk-WCETQWWK.js.map +0 -1
- package/dist/chunk-WV63X7IK.js.map +0 -1
- package/dist/chunk-XNAM7F35.js +0 -276
- package/dist/chunk-XNAM7F35.js.map +0 -1
- package/dist/chunk-YNZU6PAV.js.map +0 -1
- package/dist/claude-code-HKDV7T32.js +0 -637
- package/dist/claude-code-HKDV7T32.js.map +0 -1
- package/dist/codebuff-QX57FJDN.js.map +0 -1
- package/dist/codex-JFQTS6LU.js.map +0 -1
- package/dist/copilot-cli-MTCGHZ2T.js.map +0 -1
- package/dist/crush-I7PK3FGI.js.map +0 -1
- package/dist/cursor-4TMXKJOZ.js.map +0 -1
- package/dist/detect-WIAGVYS4.js.map +0 -1
- package/dist/doctor-EZW2K4NI.js.map +0 -1
- package/dist/droid-KN6VRMMT.js.map +0 -1
- package/dist/gemini-cli-Y35FFK4M.js.map +0 -1
- package/dist/goose-ZTGTSUFJ.js.map +0 -1
- package/dist/hermes-H2KV44GK.js.map +0 -1
- package/dist/hook-YVDILAMZ.js.map +0 -1
- package/dist/install-HQRYZOQ2.js +0 -60
- package/dist/install-HQRYZOQ2.js.map +0 -1
- package/dist/jetbrains-copilot-HWA3DMH7.js.map +0 -1
- package/dist/kilo-QXJRCSLT.js +0 -384
- package/dist/kilo-QXJRCSLT.js.map +0 -1
- package/dist/kilo-cli-EAQTHXUW.js.map +0 -1
- package/dist/kimi-GQ7VXNYT.js.map +0 -1
- package/dist/kiro-VQTT6LO2.js.map +0 -1
- package/dist/mux-T2TV4CIQ.js.map +0 -1
- package/dist/omp-PB4VDKS6.js.map +0 -1
- package/dist/openclaw-ETN2M63O.js.map +0 -1
- package/dist/opencode-FZ33JKUZ.js.map +0 -1
- package/dist/package-DLOVE2E4.js.map +0 -1
- package/dist/pi-CIU2QHT4.js.map +0 -1
- package/dist/qwen-code-5POCNU6P.js.map +0 -1
- package/dist/roo-code-IOIUXRDE.js.map +0 -1
- package/dist/status-FE7TECZD.js.map +0 -1
- package/dist/trae-JSNDZFKL.js.map +0 -1
- package/dist/types-CYUB1OTr.d.ts +0 -427
- package/dist/uninstall-IGCKNOSF.js +0 -72
- package/dist/uninstall-IGCKNOSF.js.map +0 -1
- package/dist/upgrade-JV2VK242.js.map +0 -1
- package/dist/vscode-copilot-CK3NO54M.js.map +0 -1
- package/dist/warp-QNVDXOG4.js.map +0 -1
- package/dist/zed-VRJKWYUR.js.map +0 -1
- /package/dist/{antigravity-CX2YLSY7.js.map → antigravity-7IBYFO6P.js.map} +0 -0
- /package/dist/{chunk-XUCWLJY5.js.map → chunk-NJU275US.js.map} +0 -0
- /package/dist/{leaderboard-K6ON3SWW.js.map → leaderboard-MUJ7DURM.js.map} +0 -0
- /package/dist/{usage-6ER6EMEY.js.map → usage-2BJLM3GJ.js.map} +0 -0
package/dist/chunk-7MSDGHJW.js
DELETED
|
@@ -1,316 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
import {
|
|
3
|
-
log
|
|
4
|
-
} from "./chunk-KHO5WNTP.js";
|
|
5
|
-
import {
|
|
6
|
-
REGISTERED_PLATFORM_IDS,
|
|
7
|
-
detectInstalledPlatforms,
|
|
8
|
-
loadAdapter
|
|
9
|
-
} from "./chunk-XUCWLJY5.js";
|
|
10
|
-
import {
|
|
11
|
-
deregisterConnector,
|
|
12
|
-
loadRegisteredConnector,
|
|
13
|
-
registerConnector
|
|
14
|
-
} from "./chunk-LTT56GI3.js";
|
|
15
|
-
import {
|
|
16
|
-
connectorDir,
|
|
17
|
-
connectorsDir,
|
|
18
|
-
dataRoot,
|
|
19
|
-
ensureHomeBin,
|
|
20
|
-
homeBinPath
|
|
21
|
-
} from "./chunk-TRQKHP7D.js";
|
|
22
|
-
|
|
23
|
-
// src/core/installer.ts
|
|
24
|
-
import { existsSync, readdirSync, rmSync } from "fs";
|
|
25
|
-
import { dirname, join } from "path";
|
|
26
|
-
import { fileURLToPath } from "url";
|
|
27
|
-
async function installConnector(opts) {
|
|
28
|
-
const { connector, modulePath, scope, projectDir, dryRun } = opts;
|
|
29
|
-
if (!dryRun) {
|
|
30
|
-
try {
|
|
31
|
-
ensureHomeBin(resolveCliEntry());
|
|
32
|
-
} catch (err) {
|
|
33
|
-
log.warn(`ensureHomeBin failed: ${errMessage(err)}`);
|
|
34
|
-
}
|
|
35
|
-
try {
|
|
36
|
-
registerConnector(connector, modulePath);
|
|
37
|
-
} catch (err) {
|
|
38
|
-
log.warn(`registerConnector failed: ${errMessage(err)}`);
|
|
39
|
-
}
|
|
40
|
-
}
|
|
41
|
-
const targets = await resolveTargets(opts.targets, connector.targets, projectDir);
|
|
42
|
-
const result = newResult(connector.id, dryRun);
|
|
43
|
-
if (targets.length === 0) {
|
|
44
|
-
result.warnings.push(
|
|
45
|
-
"no target platforms resolved (none installed / detected, or all filtered out)"
|
|
46
|
-
);
|
|
47
|
-
return result;
|
|
48
|
-
}
|
|
49
|
-
for (const id of targets) {
|
|
50
|
-
const adapter = await tryLoadAdapter(id, result);
|
|
51
|
-
if (!adapter) continue;
|
|
52
|
-
const ctx = buildContext(connector, id, scope, projectDir, dryRun);
|
|
53
|
-
if (!dryRun) {
|
|
54
|
-
runStep(id, "backupSettings", result, () => {
|
|
55
|
-
const backup = adapter.backupSettings(ctx);
|
|
56
|
-
if (backup) {
|
|
57
|
-
result.changes.push({
|
|
58
|
-
platform: id,
|
|
59
|
-
action: "create",
|
|
60
|
-
path: backup,
|
|
61
|
-
detail: "backed up settings before install"
|
|
62
|
-
});
|
|
63
|
-
}
|
|
64
|
-
});
|
|
65
|
-
}
|
|
66
|
-
runStep(id, "installServer", result, () => {
|
|
67
|
-
pushAll(result.changes, adapter.installServer(ctx));
|
|
68
|
-
});
|
|
69
|
-
runStep(id, "installHooks", result, () => {
|
|
70
|
-
pushAll(result.changes, adapter.installHooks(ctx));
|
|
71
|
-
});
|
|
72
|
-
if (connector.commands.length) {
|
|
73
|
-
runStep(id, "installCommands", result, () => {
|
|
74
|
-
pushAll(result.changes, adapter.installCommands(ctx));
|
|
75
|
-
});
|
|
76
|
-
}
|
|
77
|
-
if (connector.skills.length) {
|
|
78
|
-
runStep(id, "installSkills", result, () => {
|
|
79
|
-
pushAll(result.changes, adapter.installSkills(ctx));
|
|
80
|
-
});
|
|
81
|
-
}
|
|
82
|
-
if (connector.subagents.length) {
|
|
83
|
-
runStep(id, "installSubagents", result, () => {
|
|
84
|
-
pushAll(result.changes, adapter.installSubagents(ctx));
|
|
85
|
-
});
|
|
86
|
-
}
|
|
87
|
-
}
|
|
88
|
-
return result;
|
|
89
|
-
}
|
|
90
|
-
async function syncConnector(opts) {
|
|
91
|
-
return installConnector(opts);
|
|
92
|
-
}
|
|
93
|
-
async function uninstallConnector(opts) {
|
|
94
|
-
const { connectorId, scope, projectDir, dryRun, purge } = opts;
|
|
95
|
-
const connector = await loadConnectorForUninstall(connectorId);
|
|
96
|
-
const result = newResult(connectorId, dryRun);
|
|
97
|
-
const targets = await resolveTargets(opts.targets, connector.targets, projectDir);
|
|
98
|
-
if (targets.length === 0) {
|
|
99
|
-
result.warnings.push(
|
|
100
|
-
"no target platforms resolved for uninstall (none installed / detected, or all filtered out)"
|
|
101
|
-
);
|
|
102
|
-
if (purge) purgeFrameworkState(connectorId, dryRun, result);
|
|
103
|
-
return result;
|
|
104
|
-
}
|
|
105
|
-
for (const id of targets) {
|
|
106
|
-
const adapter = await tryLoadAdapter(id, result);
|
|
107
|
-
if (!adapter) continue;
|
|
108
|
-
const ctx = buildContext(connector, id, scope, projectDir, dryRun);
|
|
109
|
-
if (connector.subagents.length) {
|
|
110
|
-
runStep(id, "uninstallSubagents", result, () => {
|
|
111
|
-
pushAll(result.changes, adapter.uninstallSubagents(ctx));
|
|
112
|
-
});
|
|
113
|
-
}
|
|
114
|
-
if (connector.commands.length) {
|
|
115
|
-
runStep(id, "uninstallCommands", result, () => {
|
|
116
|
-
pushAll(result.changes, adapter.uninstallCommands(ctx));
|
|
117
|
-
});
|
|
118
|
-
}
|
|
119
|
-
if (connector.skills.length) {
|
|
120
|
-
runStep(id, "uninstallSkills", result, () => {
|
|
121
|
-
pushAll(result.changes, adapter.uninstallSkills(ctx));
|
|
122
|
-
});
|
|
123
|
-
}
|
|
124
|
-
runStep(id, "uninstallHooks", result, () => {
|
|
125
|
-
pushAll(result.changes, adapter.uninstallHooks(ctx));
|
|
126
|
-
});
|
|
127
|
-
runStep(id, "uninstallServer", result, () => {
|
|
128
|
-
pushAll(result.changes, adapter.uninstallServer(ctx));
|
|
129
|
-
});
|
|
130
|
-
}
|
|
131
|
-
if (purge) purgeFrameworkState(connectorId, dryRun, result);
|
|
132
|
-
return result;
|
|
133
|
-
}
|
|
134
|
-
function purgeFrameworkState(connectorId, dryRun, result) {
|
|
135
|
-
const platform = connectorId;
|
|
136
|
-
const recordDir = connectorDir(connectorId);
|
|
137
|
-
const recordExisted = existsSync(recordDir);
|
|
138
|
-
if (recordExisted) {
|
|
139
|
-
if (!dryRun) {
|
|
140
|
-
try {
|
|
141
|
-
deregisterConnector(connectorId);
|
|
142
|
-
} catch (err) {
|
|
143
|
-
log.warn(`deregisterConnector failed: ${errMessage(err)}`);
|
|
144
|
-
}
|
|
145
|
-
}
|
|
146
|
-
result.changes.push({
|
|
147
|
-
platform,
|
|
148
|
-
action: "remove",
|
|
149
|
-
path: recordDir,
|
|
150
|
-
detail: "deregistered connector record"
|
|
151
|
-
});
|
|
152
|
-
}
|
|
153
|
-
if (noConnectorsRemain(connectorId, dryRun, recordExisted)) {
|
|
154
|
-
const binPath = homeBinPath();
|
|
155
|
-
if (existsSync(binPath)) {
|
|
156
|
-
if (!dryRun) {
|
|
157
|
-
try {
|
|
158
|
-
rmSync(binPath, { force: true });
|
|
159
|
-
} catch (err) {
|
|
160
|
-
log.warn(`home-bin removal failed: ${errMessage(err)}`);
|
|
161
|
-
}
|
|
162
|
-
}
|
|
163
|
-
result.changes.push({
|
|
164
|
-
platform,
|
|
165
|
-
action: "remove",
|
|
166
|
-
path: binPath,
|
|
167
|
-
detail: "removed home-bin (no connectors remain)"
|
|
168
|
-
});
|
|
169
|
-
}
|
|
170
|
-
}
|
|
171
|
-
}
|
|
172
|
-
function noConnectorsRemain(connectorId, dryRun, recordExisted) {
|
|
173
|
-
const dir = connectorsDir();
|
|
174
|
-
if (!existsSync(dir)) return true;
|
|
175
|
-
let entries;
|
|
176
|
-
try {
|
|
177
|
-
entries = readdirSync(dir);
|
|
178
|
-
} catch {
|
|
179
|
-
return false;
|
|
180
|
-
}
|
|
181
|
-
const remaining = entries.filter(
|
|
182
|
-
(e) => !(dryRun && recordExisted && e === connectorId)
|
|
183
|
-
);
|
|
184
|
-
return remaining.length === 0;
|
|
185
|
-
}
|
|
186
|
-
function resolveCliEntry() {
|
|
187
|
-
try {
|
|
188
|
-
let dir = dirname(fileURLToPath(import.meta.url));
|
|
189
|
-
for (let i = 0; i < 8; i++) {
|
|
190
|
-
if (existsSync(join(dir, "package.json"))) {
|
|
191
|
-
const cli = join(dir, "dist", "cli.js");
|
|
192
|
-
if (existsSync(cli)) return cli;
|
|
193
|
-
}
|
|
194
|
-
const parent = dirname(dir);
|
|
195
|
-
if (parent === dir) break;
|
|
196
|
-
dir = parent;
|
|
197
|
-
}
|
|
198
|
-
const sameDir = fileURLToPath(new URL("./cli.js", import.meta.url));
|
|
199
|
-
if (existsSync(sameDir)) return sameDir;
|
|
200
|
-
} catch {
|
|
201
|
-
}
|
|
202
|
-
const argv1 = process.argv[1];
|
|
203
|
-
if (argv1 && argv1.length > 0) return argv1;
|
|
204
|
-
return "cli.js";
|
|
205
|
-
}
|
|
206
|
-
async function resolveTargets(flagTargets, connectorTargets, projectDir) {
|
|
207
|
-
let requested;
|
|
208
|
-
if (flagTargets && flagTargets.length > 0) {
|
|
209
|
-
requested = flagTargets;
|
|
210
|
-
} else if (Array.isArray(connectorTargets) && connectorTargets.length > 0) {
|
|
211
|
-
requested = connectorTargets;
|
|
212
|
-
} else {
|
|
213
|
-
requested = await autoDetectIds(projectDir);
|
|
214
|
-
}
|
|
215
|
-
const seen = /* @__PURE__ */ new Set();
|
|
216
|
-
const out = [];
|
|
217
|
-
for (const id of requested) {
|
|
218
|
-
if (!REGISTERED_PLATFORM_IDS.has(id)) continue;
|
|
219
|
-
if (seen.has(id)) continue;
|
|
220
|
-
seen.add(id);
|
|
221
|
-
out.push(id);
|
|
222
|
-
}
|
|
223
|
-
return out;
|
|
224
|
-
}
|
|
225
|
-
async function autoDetectIds(projectDir) {
|
|
226
|
-
try {
|
|
227
|
-
const detected = await detectInstalledPlatforms(projectDir);
|
|
228
|
-
return detected.filter((p) => p.installed).map((p) => p.id);
|
|
229
|
-
} catch (err) {
|
|
230
|
-
log.warn(`platform detection failed: ${errMessage(err)}`);
|
|
231
|
-
return [];
|
|
232
|
-
}
|
|
233
|
-
}
|
|
234
|
-
function buildContext(connector, id, defaultScope, projectDir, dryRun) {
|
|
235
|
-
return {
|
|
236
|
-
connector,
|
|
237
|
-
scope: connector.platforms[id]?.scope ?? defaultScope,
|
|
238
|
-
projectDir,
|
|
239
|
-
homeBinPath: homeBinPath(),
|
|
240
|
-
dataRoot: dataRoot(),
|
|
241
|
-
dryRun
|
|
242
|
-
};
|
|
243
|
-
}
|
|
244
|
-
async function tryLoadAdapter(id, result) {
|
|
245
|
-
try {
|
|
246
|
-
const adapter = await loadAdapter(id);
|
|
247
|
-
if (adapter) return adapter;
|
|
248
|
-
const detail = `no adapter registered for ${id}`;
|
|
249
|
-
result.changes.push({ platform: id, action: "warn", detail });
|
|
250
|
-
result.warnings.push(detail);
|
|
251
|
-
return null;
|
|
252
|
-
} catch (err) {
|
|
253
|
-
const detail = `failed to load adapter for ${id}: ${errMessage(err)}`;
|
|
254
|
-
result.changes.push({ platform: id, action: "warn", detail });
|
|
255
|
-
result.warnings.push(detail);
|
|
256
|
-
return null;
|
|
257
|
-
}
|
|
258
|
-
}
|
|
259
|
-
function runStep(id, step, result, fn) {
|
|
260
|
-
try {
|
|
261
|
-
fn();
|
|
262
|
-
} catch (err) {
|
|
263
|
-
const detail = `${step} failed on ${id}: ${errMessage(err)}`;
|
|
264
|
-
result.changes.push({ platform: id, action: "warn", detail });
|
|
265
|
-
result.warnings.push(detail);
|
|
266
|
-
}
|
|
267
|
-
}
|
|
268
|
-
async function loadConnectorForUninstall(connectorId) {
|
|
269
|
-
try {
|
|
270
|
-
return await loadRegisteredConnector(connectorId);
|
|
271
|
-
} catch (err) {
|
|
272
|
-
log.warn(
|
|
273
|
-
`could not load registered connector "${connectorId}" for uninstall (${errMessage(err)}); proceeding with a minimal id-only context`
|
|
274
|
-
);
|
|
275
|
-
return syntheticConnector(connectorId);
|
|
276
|
-
}
|
|
277
|
-
}
|
|
278
|
-
function syntheticConnector(id) {
|
|
279
|
-
return {
|
|
280
|
-
id,
|
|
281
|
-
displayName: id,
|
|
282
|
-
version: "0.0.0",
|
|
283
|
-
hooks: {},
|
|
284
|
-
hookEvents: [],
|
|
285
|
-
telemetry: {
|
|
286
|
-
enabled: true,
|
|
287
|
-
modelFamilyHint: "auto",
|
|
288
|
-
measureToolDefs: true,
|
|
289
|
-
hostNativeUsage: false,
|
|
290
|
-
store: "ndjson",
|
|
291
|
-
calibration: { anthropicCountTokens: false }
|
|
292
|
-
},
|
|
293
|
-
commands: [],
|
|
294
|
-
skills: [],
|
|
295
|
-
subagents: [],
|
|
296
|
-
platforms: {},
|
|
297
|
-
targets: "auto"
|
|
298
|
-
};
|
|
299
|
-
}
|
|
300
|
-
function newResult(connectorId, dryRun) {
|
|
301
|
-
return { connectorId, dryRun, changes: [], warnings: [] };
|
|
302
|
-
}
|
|
303
|
-
function pushAll(target, records) {
|
|
304
|
-
for (const r of records) target.push(r);
|
|
305
|
-
}
|
|
306
|
-
function errMessage(err) {
|
|
307
|
-
return err instanceof Error ? err.message : String(err);
|
|
308
|
-
}
|
|
309
|
-
|
|
310
|
-
export {
|
|
311
|
-
installConnector,
|
|
312
|
-
syncConnector,
|
|
313
|
-
uninstallConnector,
|
|
314
|
-
resolveCliEntry
|
|
315
|
-
};
|
|
316
|
-
//# sourceMappingURL=chunk-7MSDGHJW.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/core/installer.ts"],"sourcesContent":["/**\n * core/installer — install / uninstall / sync orchestration.\n *\n * This is the coordination layer that sits above the per-platform adapters. It\n * does NOT know any platform's native dialect (that is the adapter's job); it\n * owns the cross-cutting choreography that every install flow shares:\n *\n * 1. Refresh the single stable home binary (so every host's pointer config\n * keeps execing a working CLI) and register the connector's serializable\n * metadata so the runtime can re-import live handlers later.\n * 2. Resolve the set of target platforms — explicit flag → connector targets →\n * auto-detected installed hosts — intersected with the registry's known ids.\n * 3. For each target: load its adapter, build a uniform InstallContext, back up\n * the native settings, then render the MCP server + hooks (install) or strip\n * them (uninstall). Per-adapter failures are captured as `warn` ChangeRecords\n * and never abort the whole run — partial success is always reported.\n *\n * `sync` is just an idempotent re-run of `install`: adapters are write-idempotent\n * (identical entries yield \"skip\"), and `ensureHomeBin` heals a stale/missing\n * home-bin pointer on every pass (docs/ARCHITECTURE.md §3 R1 / \"sync … heals\n * stale pointers\").\n */\n\nimport { existsSync, readdirSync, rmSync } from \"node:fs\";\nimport { dirname, join } from \"node:path\";\nimport { fileURLToPath } from \"node:url\";\n\nimport type {\n ChangeRecord,\n InstallResult,\n InstallScope,\n PlatformId,\n ResolvedConnector,\n} from \"./types.js\";\nimport type { Adapter, InstallContext } from \"../adapters/spi.js\";\nimport { REGISTERED_PLATFORM_IDS, loadAdapter } from \"../adapters/registry.js\";\nimport { detectInstalledPlatforms } from \"../adapters/detect.js\";\nimport {\n deregisterConnector,\n loadRegisteredConnector,\n registerConnector,\n} from \"./load-connector.js\";\nimport {\n connectorDir,\n connectorsDir,\n dataRoot,\n ensureHomeBin,\n homeBinPath,\n} from \"./paths.js\";\nimport { log } from \"./logger.js\";\n\n// ─────────────────────────────────────────────────────────────────────────\n// Options\n// ─────────────────────────────────────────────────────────────────────────\n\n/** Options for {@link installConnector} / {@link syncConnector}. */\nexport interface OrchestrationOptions {\n /** The live, resolved connector (with hook handlers) to deploy. */\n connector: ResolvedConnector;\n /** Absolute path to the source module that produced `connector`. */\n modulePath: string;\n /** Default install scope; per-platform overrides win where present. */\n scope: InstallScope;\n /** Resolved project root used by project-scoped adapters and detection. */\n projectDir: string;\n /** Explicit target allow-list. Omit to derive from the connector / detection. */\n targets?: PlatformId[];\n /** Render but do not write anything. */\n dryRun: boolean;\n}\n\n/** Options for {@link uninstallConnector}. */\nexport interface UninstallOptions {\n /** Id of a previously-registered connector to tear down. */\n connectorId: string;\n /** Default scope to uninstall from; per-platform overrides win. */\n scope: InstallScope;\n /** Resolved project root used by project-scoped adapters and detection. */\n projectDir: string;\n /** Explicit target allow-list. Omit to derive from the connector / detection. */\n targets?: PlatformId[];\n /** Compute changes but do not write anything. */\n dryRun: boolean;\n /**\n * Also remove the framework-state for this connector after stripping every\n * per-target registration: the DATA-dir connector record (connectorDir(id)) and,\n * when no connectors remain, the shared home-bin launcher. Respects dryRun (no\n * mutation, but the would-be changes are still reported).\n */\n purge?: boolean;\n}\n\n// ─────────────────────────────────────────────────────────────────────────\n// Public orchestration\n// ─────────────────────────────────────────────────────────────────────────\n\n/**\n * Install a connector across its target platforms.\n *\n * Steps mirror docs/ARCHITECTURE.md §8 (\"Install per target\"):\n * 1. Refresh the stable home binary + register connector metadata.\n * 2. Resolve targets (flag → connector.targets → auto-detect) ∩ registry ids.\n * 3. Per target: backup → installServer → installHooks, collecting changes.\n * Per-adapter errors become `warn` records; the run never aborts midway.\n */\nexport async function installConnector(\n opts: OrchestrationOptions,\n): Promise<InstallResult> {\n const { connector, modulePath, scope, projectDir, dryRun } = opts;\n\n // 1. Stable home binary + connector registry record. These are framework-state\n // mutations (never platform-native config) and are safe to skip on dry-run.\n if (!dryRun) {\n try {\n ensureHomeBin(resolveCliEntry());\n } catch (err) {\n log.warn(`ensureHomeBin failed: ${errMessage(err)}`);\n }\n try {\n registerConnector(connector, modulePath);\n } catch (err) {\n log.warn(`registerConnector failed: ${errMessage(err)}`);\n }\n }\n\n const targets = await resolveTargets(opts.targets, connector.targets, projectDir);\n const result = newResult(connector.id, dryRun);\n\n if (targets.length === 0) {\n result.warnings.push(\n \"no target platforms resolved (none installed / detected, or all filtered out)\",\n );\n return result;\n }\n\n for (const id of targets) {\n const adapter = await tryLoadAdapter(id, result);\n if (!adapter) continue;\n\n const ctx = buildContext(connector, id, scope, projectDir, dryRun);\n\n // Back up native settings before any mutation (no-op on dry-run / absent file).\n if (!dryRun) {\n runStep(id, \"backupSettings\", result, () => {\n const backup = adapter.backupSettings(ctx);\n if (backup) {\n result.changes.push({\n platform: id,\n action: \"create\",\n path: backup,\n detail: \"backed up settings before install\",\n });\n }\n });\n }\n\n runStep(id, \"installServer\", result, () => {\n pushAll(result.changes, adapter.installServer(ctx));\n });\n runStep(id, \"installHooks\", result, () => {\n pushAll(result.changes, adapter.installHooks(ctx));\n });\n\n // Content surfaces: guarded by declaration so undeclared surfaces add no\n // noise. BaseAdapter defines all six, so the `!` is safe; per-step\n // try/catch (runStep) turns any adapter failure into a warn, never aborting.\n if (connector.commands.length) {\n runStep(id, \"installCommands\", result, () => {\n pushAll(result.changes, adapter.installCommands!(ctx));\n });\n }\n if (connector.skills.length) {\n runStep(id, \"installSkills\", result, () => {\n pushAll(result.changes, adapter.installSkills!(ctx));\n });\n }\n if (connector.subagents.length) {\n runStep(id, \"installSubagents\", result, () => {\n pushAll(result.changes, adapter.installSubagents!(ctx));\n });\n }\n }\n\n return result;\n}\n\n/**\n * Idempotent re-run of {@link installConnector}: every adapter re-renders its\n * config (identical entries report \"skip\"), and `ensureHomeBin` repairs a stale\n * or missing home-bin pointer. Use after editing a connector or upgrading the\n * framework. Behaviorally identical to install — the distinct name documents\n * intent and gives the CLI a stable verb to wire `agent-connector sync` to.\n */\nexport async function syncConnector(\n opts: OrchestrationOptions,\n): Promise<InstallResult> {\n return installConnector(opts);\n}\n\n/**\n * Uninstall a previously-registered connector: strip its hook registrations and\n * MCP server entries from every target platform. The registered connector is\n * loaded (best-effort) only to provide adapter context (server id, hook ids,\n * per-platform overrides); when it cannot be loaded a minimal synthetic\n * connector built from the id still lets adapters locate and remove their\n * entries (they key removals off `connector.id` + the home-bin path).\n */\nexport async function uninstallConnector(\n opts: UninstallOptions,\n): Promise<InstallResult> {\n const { connectorId, scope, projectDir, dryRun, purge } = opts;\n\n const connector = await loadConnectorForUninstall(connectorId);\n const result = newResult(connectorId, dryRun);\n\n const targets = await resolveTargets(opts.targets, connector.targets, projectDir);\n if (targets.length === 0) {\n result.warnings.push(\n \"no target platforms resolved for uninstall (none installed / detected, or all filtered out)\",\n );\n // --purge still removes the orphan framework-state even when no host targets\n // resolve (e.g. the user already uninstalled from every host).\n if (purge) purgeFrameworkState(connectorId, dryRun, result);\n return result;\n }\n\n for (const id of targets) {\n const adapter = await tryLoadAdapter(id, result);\n if (!adapter) continue;\n\n const ctx = buildContext(connector, id, scope, projectDir, dryRun);\n\n // Inverse order of install: remove content surfaces, then hooks, then the\n // server entry. Surface removals are guarded by declaration; BaseAdapter\n // defines all six so the `!` is safe.\n if (connector.subagents.length) {\n runStep(id, \"uninstallSubagents\", result, () => {\n pushAll(result.changes, adapter.uninstallSubagents!(ctx));\n });\n }\n if (connector.commands.length) {\n runStep(id, \"uninstallCommands\", result, () => {\n pushAll(result.changes, adapter.uninstallCommands!(ctx));\n });\n }\n if (connector.skills.length) {\n runStep(id, \"uninstallSkills\", result, () => {\n pushAll(result.changes, adapter.uninstallSkills!(ctx));\n });\n }\n runStep(id, \"uninstallHooks\", result, () => {\n pushAll(result.changes, adapter.uninstallHooks(ctx));\n });\n runStep(id, \"uninstallServer\", result, () => {\n pushAll(result.changes, adapter.uninstallServer(ctx));\n });\n }\n\n // --purge: after every per-target registration is stripped, also remove the\n // framework-state this connector left behind (its DATA-dir record and, when no\n // connectors remain, the shared home-bin launcher).\n if (purge) purgeFrameworkState(connectorId, dryRun, result);\n\n return result;\n}\n\n/**\n * Remove the framework-state a connector left behind (the inverse of install's\n * registerConnector + ensureHomeBin):\n * 1. Deregister the DATA-dir connector record (connectorDir(id)).\n * 2. When no connector records remain, remove the shared home-bin launcher.\n * All steps are guarded/best-effort and respect dryRun — on a dry run nothing is\n * mutated, but the would-be `remove` changes are still reported.\n */\nfunction purgeFrameworkState(\n connectorId: string,\n dryRun: boolean,\n result: InstallResult,\n): void {\n // These are framework-state changes, not platform-native ones. ChangeRecord\n // types `platform` as the closed PlatformId union, so label them with the\n // connector id (cast once) — there is no real host platform to attribute.\n const platform = connectorId as PlatformId;\n\n // 1. Deregister the connector record directory.\n const recordDir = connectorDir(connectorId);\n const recordExisted = existsSync(recordDir);\n if (recordExisted) {\n if (!dryRun) {\n try {\n deregisterConnector(connectorId);\n } catch (err) {\n log.warn(`deregisterConnector failed: ${errMessage(err)}`);\n }\n }\n result.changes.push({\n platform,\n action: \"remove\",\n path: recordDir,\n detail: \"deregistered connector record\",\n });\n }\n\n // 2. If no connector records remain, remove the shared home-bin launcher.\n if (noConnectorsRemain(connectorId, dryRun, recordExisted)) {\n const binPath = homeBinPath();\n if (existsSync(binPath)) {\n if (!dryRun) {\n try {\n rmSync(binPath, { force: true });\n } catch (err) {\n log.warn(`home-bin removal failed: ${errMessage(err)}`);\n }\n }\n result.changes.push({\n platform,\n action: \"remove\",\n path: binPath,\n detail: \"removed home-bin (no connectors remain)\",\n });\n }\n }\n}\n\n/**\n * True when `connectorsDir()` holds no remaining connector records. On a dry run\n * the just-deregistered record is still on disk, so it is discounted: the dir is\n * \"empty\" when its only remaining entry is the connector we are purging.\n */\nfunction noConnectorsRemain(\n connectorId: string,\n dryRun: boolean,\n recordExisted: boolean,\n): boolean {\n const dir = connectorsDir();\n if (!existsSync(dir)) return true;\n let entries: string[];\n try {\n entries = readdirSync(dir);\n } catch {\n return false;\n }\n const remaining = entries.filter(\n (e) => !(dryRun && recordExisted && e === connectorId),\n );\n return remaining.length === 0;\n}\n\n// ─────────────────────────────────────────────────────────────────────────\n// Internals\n// ─────────────────────────────────────────────────────────────────────────\n\n/**\n * Absolute path to the CLI entry (`dist/cli.js`) the home binary should exec.\n *\n * Robust to the bundler's output layout: tsup flattens this module into a\n * `dist/chunk-*.js`, so a fixed relative guess like `../cli.js` is wrong. We\n * instead walk up from this module to the package root (the dir whose\n * `package.json` declares the `agent-connector` bin) and use its `dist/cli.js`.\n * Fallbacks: a same-directory `cli.js` (flat-bundle case) then `process.argv[1]`.\n */\nexport function resolveCliEntry(): string {\n try {\n let dir = dirname(fileURLToPath(import.meta.url));\n for (let i = 0; i < 8; i++) {\n if (existsSync(join(dir, \"package.json\"))) {\n const cli = join(dir, \"dist\", \"cli.js\");\n if (existsSync(cli)) return cli;\n }\n const parent = dirname(dir);\n if (parent === dir) break;\n dir = parent;\n }\n // Flat-bundle case: cli.js sits beside this chunk.\n const sameDir = fileURLToPath(new URL(\"./cli.js\", import.meta.url));\n if (existsSync(sameDir)) return sameDir;\n } catch {\n /* fall through */\n }\n const argv1 = process.argv[1];\n if (argv1 && argv1.length > 0) return argv1;\n return \"cli.js\";\n}\n\n/**\n * Resolve the ordered, de-duplicated list of platform targets, intersected with\n * the adapter registry's known ids (an unknown id can have no adapter to drive).\n *\n * Precedence: explicit `flagTargets` → connector's `targets` array → auto-detect\n * installed hosts. \"auto\" connector targets also fall through to detection.\n */\nasync function resolveTargets(\n flagTargets: PlatformId[] | undefined,\n connectorTargets: ResolvedConnector[\"targets\"],\n projectDir: string,\n): Promise<PlatformId[]> {\n let requested: PlatformId[];\n if (flagTargets && flagTargets.length > 0) {\n requested = flagTargets;\n } else if (Array.isArray(connectorTargets) && connectorTargets.length > 0) {\n requested = connectorTargets;\n } else {\n requested = await autoDetectIds(projectDir);\n }\n\n const seen = new Set<PlatformId>();\n const out: PlatformId[] = [];\n for (const id of requested) {\n if (!REGISTERED_PLATFORM_IDS.has(id)) continue; // no adapter registered → cannot drive it\n if (seen.has(id)) continue;\n seen.add(id);\n out.push(id);\n }\n return out;\n}\n\n/** Auto-detect installed platforms and map them to their ids (best-effort). */\nasync function autoDetectIds(projectDir: string): Promise<PlatformId[]> {\n try {\n const detected = await detectInstalledPlatforms(projectDir);\n return detected.filter((p) => p.installed).map((p) => p.id);\n } catch (err) {\n log.warn(`platform detection failed: ${errMessage(err)}`);\n return [];\n }\n}\n\n/**\n * Build the uniform InstallContext for one platform. The effective scope is the\n * connector's per-platform override when present, else the run-wide default.\n */\nfunction buildContext(\n connector: ResolvedConnector,\n id: PlatformId,\n defaultScope: InstallScope,\n projectDir: string,\n dryRun: boolean,\n): InstallContext {\n return {\n connector,\n scope: connector.platforms[id]?.scope ?? defaultScope,\n projectDir,\n homeBinPath: homeBinPath(),\n dataRoot: dataRoot(),\n dryRun,\n };\n}\n\n/**\n * Load an adapter, recording a `warn` change (not throwing) when unavailable.\n * `loadAdapter` returns `undefined` for an unregistered id and may reject if the\n * adapter module itself fails to import; both degrade to a warn here.\n */\nasync function tryLoadAdapter(\n id: PlatformId,\n result: InstallResult,\n): Promise<Adapter | null> {\n try {\n const adapter = await loadAdapter(id);\n if (adapter) return adapter;\n const detail = `no adapter registered for ${id}`;\n result.changes.push({ platform: id, action: \"warn\", detail });\n result.warnings.push(detail);\n return null;\n } catch (err) {\n const detail = `failed to load adapter for ${id}: ${errMessage(err)}`;\n result.changes.push({ platform: id, action: \"warn\", detail });\n result.warnings.push(detail);\n return null;\n }\n}\n\n/**\n * Run one adapter step, converting any thrown error into a `warn` ChangeRecord +\n * warning string so a single bad adapter never aborts the whole run.\n */\nfunction runStep(\n id: PlatformId,\n step: string,\n result: InstallResult,\n fn: () => void,\n): void {\n try {\n fn();\n } catch (err) {\n const detail = `${step} failed on ${id}: ${errMessage(err)}`;\n result.changes.push({ platform: id, action: \"warn\", detail });\n result.warnings.push(detail);\n }\n}\n\n/**\n * Load the registered connector for uninstall context. If it cannot be loaded\n * (record removed, source module deleted/moved), fall back to a minimal\n * synthetic connector carrying just the id — enough for adapters to locate and\n * strip their entries, which they match by `connector.id` + the home-bin path.\n */\nasync function loadConnectorForUninstall(\n connectorId: string,\n): Promise<ResolvedConnector> {\n try {\n return await loadRegisteredConnector(connectorId);\n } catch (err) {\n log.warn(\n `could not load registered connector \"${connectorId}\" for uninstall (${errMessage(err)}); ` +\n \"proceeding with a minimal id-only context\",\n );\n return syntheticConnector(connectorId);\n }\n}\n\n/** Minimal id-only connector used as uninstall fallback context. */\nfunction syntheticConnector(id: string): ResolvedConnector {\n return {\n id,\n displayName: id,\n version: \"0.0.0\",\n hooks: {},\n hookEvents: [],\n telemetry: {\n enabled: true,\n modelFamilyHint: \"auto\",\n measureToolDefs: true,\n hostNativeUsage: false,\n store: \"ndjson\",\n calibration: { anthropicCountTokens: false },\n },\n commands: [],\n skills: [],\n subagents: [],\n platforms: {},\n targets: \"auto\",\n };\n}\n\nfunction newResult(connectorId: string, dryRun: boolean): InstallResult {\n return { connectorId, dryRun, changes: [], warnings: [] };\n}\n\nfunction pushAll(target: ChangeRecord[], records: ChangeRecord[]): void {\n for (const r of records) target.push(r);\n}\n\nfunction errMessage(err: unknown): string {\n return err instanceof Error ? err.message : String(err);\n}\n"],"mappings":"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","names":[]}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/core/logger.ts"],"sourcesContent":["/**\n * core/logger — minimal leveled logger. Writes human output to stderr so stdout\n * stays clean for machine-readable payloads (hook replies, --json output).\n */\n\nexport type LogLevel = \"silent\" | \"error\" | \"warn\" | \"info\" | \"debug\";\n\nconst ORDER: Record<LogLevel, number> = {\n silent: 0,\n error: 1,\n warn: 2,\n info: 3,\n debug: 4,\n};\n\nfunction currentLevel(): LogLevel {\n const env = (process.env.AGENT_CONNECTOR_LOG ?? \"info\").toLowerCase();\n return (env in ORDER ? env : \"info\") as LogLevel;\n}\n\nfunction emit(level: Exclude<LogLevel, \"silent\">, args: unknown[]): void {\n if (ORDER[currentLevel()] < ORDER[level]) return;\n const prefix = { error: \"✗\", warn: \"!\", info: \"›\", debug: \"·\" }[level];\n // eslint-disable-next-line no-console\n console.error(prefix, ...args);\n}\n\nexport const log = {\n error: (...args: unknown[]) => emit(\"error\", args),\n warn: (...args: unknown[]) => emit(\"warn\", args),\n info: (...args: unknown[]) => emit(\"info\", args),\n debug: (...args: unknown[]) => emit(\"debug\", args),\n};\n"],"mappings":";;;AAOA,IAAM,QAAkC;AAAA,EACtC,QAAQ;AAAA,EACR,OAAO;AAAA,EACP,MAAM;AAAA,EACN,MAAM;AAAA,EACN,OAAO;AACT;AAEA,SAAS,eAAyB;AAChC,QAAM,OAAO,QAAQ,IAAI,uBAAuB,QAAQ,YAAY;AACpE,SAAQ,OAAO,QAAQ,MAAM;AAC/B;AAEA,SAAS,KAAK,OAAoC,MAAuB;AACvE,MAAI,MAAM,aAAa,CAAC,IAAI,MAAM,KAAK,EAAG;AAC1C,QAAM,SAAS,EAAE,OAAO,UAAK,MAAM,KAAK,MAAM,UAAK,OAAO,OAAI,EAAE,KAAK;AAErE,UAAQ,MAAM,QAAQ,GAAG,IAAI;AAC/B;AAEO,IAAM,MAAM;AAAA,EACjB,OAAO,IAAI,SAAoB,KAAK,SAAS,IAAI;AAAA,EACjD,MAAM,IAAI,SAAoB,KAAK,QAAQ,IAAI;AAAA,EAC/C,MAAM,IAAI,SAAoB,KAAK,QAAQ,IAAI;AAAA,EAC/C,OAAO,IAAI,SAAoB,KAAK,SAAS,IAAI;AACnD;","names":[]}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/core/load-connector.ts"],"sourcesContent":["/**\n * core/load-connector — discover, load, and register connector definitions.\n *\n * Responsibilities:\n * 1. Locate the developer's `agent-connector.config.{mjs,js,json}` by walking\n * upward from a start directory (the project root convention).\n * 2. Load a config from a path into a LIVE ResolvedConnector (with handlers).\n * 3. Register a connector by persisting SERIALIZABLE metadata (no functions)\n * to `connectorDir(id)/connector.json`, pointing at the source module so\n * live handlers can be re-imported at runtime from a stable record.\n *\n * Why split live vs. serialized? Hook handlers are functions and cannot survive\n * JSON. The registry stores only what JSON can hold plus an absolute\n * `modulePath`; the runtime re-imports that module to recover the live handlers.\n */\n\nimport { existsSync, readdirSync, readFileSync, rmSync, writeFileSync } from \"node:fs\";\nimport { dirname, isAbsolute, join, resolve } from \"node:path\";\nimport { pathToFileURL } from \"node:url\";\n\nimport type {\n CommandDef,\n HookEventName,\n PlatformId,\n PlatformOverride,\n ResolvedConnector,\n ServerDef,\n SkillDef,\n SubagentDef,\n} from \"./types.js\";\nimport { defineConnector } from \"./define-connector.js\";\nimport { connectorDir, connectorsDir, ensureDir } from \"./paths.js\";\n\n/** Candidate config filenames, in resolution-precedence order. */\nconst CONFIG_FILENAMES = [\n \"agent-connector.config.mjs\",\n \"agent-connector.config.js\",\n \"agent-connector.config.json\",\n] as const;\n\n/**\n * The serializable subset of a connector persisted to `connector.json`.\n * Hook handler functions are intentionally absent — only the absolute\n * `modulePath` is stored so the live ResolvedConnector can be re-imported.\n */\nexport interface RegisteredMeta {\n id: string;\n displayName: string;\n version: string;\n /** Absolute path to the source module that produced this connector. */\n modulePath: string;\n telemetry: ResolvedConnector[\"telemetry\"];\n /** Canonical event names the connector handles (handlers themselves stripped). */\n hookEvents: HookEventName[];\n hasServer: boolean;\n server: ServerDef | null;\n /**\n * Content surfaces (commands/skills/subagents). These are JSON-serializable\n * (no functions) so they persist cleanly and let uninstall locate the files\n * to remove even when the source module is gone.\n */\n commands: CommandDef[];\n skills: SkillDef[];\n subagents: SubagentDef[];\n targets: ResolvedConnector[\"targets\"];\n /** Per-platform overrides with any hook handler functions stripped. */\n platforms: Partial<Record<PlatformId, PlatformOverride>>;\n}\n\n/**\n * Search `startDir` and each ancestor for a connector config file. Returns the\n * absolute path to the first match (filesystem-root-stopping), or null.\n */\nexport function findConnectorConfig(startDir: string): string | null {\n let dir = resolve(startDir);\n // Walk upward until `dirname(dir) === dir` (filesystem root).\n for (;;) {\n for (const name of CONFIG_FILENAMES) {\n const candidate = join(dir, name);\n if (existsSync(candidate)) return candidate;\n }\n const parent = dirname(dir);\n if (parent === dir) break;\n dir = parent;\n }\n return null;\n}\n\n/** True if a value already looks like a ResolvedConnector (vs. a raw config). */\nfunction isResolvedConnector(value: unknown): value is ResolvedConnector {\n return (\n typeof value === \"object\" &&\n value !== null &&\n Array.isArray((value as { hookEvents?: unknown }).hookEvents)\n );\n}\n\n/**\n * Load a connector from an absolute or relative path.\n * - `.json` → JSON.parse, then defineConnector().\n * - `.mjs` / `.js` → dynamic import (via pathToFileURL, Windows-safe),\n * take the default export. If it already looks like a\n * ResolvedConnector use it as-is; otherwise treat it as\n * a ConnectorConfig and run it through defineConnector().\n * Returns the live connector plus the absolute module path it came from.\n */\nexport async function loadConnectorFromPath(\n path: string,\n): Promise<{ connector: ResolvedConnector; modulePath: string }> {\n const modulePath = isAbsolute(path) ? path : resolve(path);\n\n if (modulePath.endsWith(\".json\")) {\n const text = readFileSync(modulePath, \"utf8\");\n const config = JSON.parse(text) as unknown;\n const connector = defineConnector(config as Parameters<typeof defineConnector>[0]);\n return { connector, modulePath };\n }\n\n // .mjs / .js — dynamic ESM import. pathToFileURL keeps Windows paths valid.\n const mod = (await import(pathToFileURL(modulePath).href)) as {\n default?: unknown;\n };\n const exported = mod.default;\n if (exported == null) {\n throw new Error(\n `Connector module has no default export: ${modulePath}`,\n );\n }\n const connector = isResolvedConnector(exported)\n ? exported\n : defineConnector(exported as Parameters<typeof defineConnector>[0]);\n return { connector, modulePath };\n}\n\n/**\n * Strip any function-valued `hooks` from per-platform overrides so the result\n * is JSON-serializable. A boolean `hooks` override is preserved; an object\n * override is dropped (it can only carry handler functions, which are not\n * serializable). Everything else on the override is kept verbatim.\n */\nfunction serializablePlatforms(\n platforms: Partial<Record<PlatformId, PlatformOverride>>,\n): Partial<Record<PlatformId, PlatformOverride>> {\n const out: Partial<Record<PlatformId, PlatformOverride>> = {};\n for (const [id, override] of Object.entries(platforms) as [\n PlatformId,\n PlatformOverride | undefined,\n ][]) {\n if (!override) continue;\n const { hooks, ...rest } = override;\n const clean: PlatformOverride = { ...rest };\n // Keep only a boolean hooks flag; object hooks carry handler functions.\n if (typeof hooks === \"boolean\") clean.hooks = hooks;\n out[id] = clean;\n }\n return out;\n}\n\n/**\n * Persist a connector's serializable metadata to `connectorDir(id)/connector.json`.\n * Handlers are NOT serialized — they are re-imported from `modulePath` at runtime.\n * Returns the absolute path of the written record.\n */\nexport function registerConnector(\n connector: ResolvedConnector,\n modulePath: string,\n): string {\n const meta: RegisteredMeta = {\n id: connector.id,\n displayName: connector.displayName,\n version: connector.version,\n modulePath: resolve(modulePath),\n telemetry: connector.telemetry,\n hookEvents: connector.hookEvents,\n hasServer: !!connector.server,\n server: connector.server ?? null,\n commands: connector.commands,\n skills: connector.skills,\n subagents: connector.subagents,\n targets: connector.targets,\n platforms: serializablePlatforms(connector.platforms),\n };\n\n const dir = connectorDir(connector.id);\n ensureDir(dir);\n const outPath = join(dir, \"connector.json\");\n writeFileSync(outPath, `${JSON.stringify(meta, null, 2)}\\n`, \"utf8\");\n return outPath;\n}\n\n/**\n * Deregister a connector by removing its DATA-dir record directory\n * (`connectorDir(id)`) recursively. This is the inverse of {@link registerConnector}:\n * registerConnector writes `connectorDir(id)/connector.json`, but per-target\n * uninstall only strips host-native registrations — it never removes this record,\n * leaving an orphan behind. Best-effort + guarded: returns the directory path and\n * whether it existed before removal.\n */\nexport function deregisterConnector(id: string): {\n removed: boolean;\n path: string;\n} {\n const dir = connectorDir(id);\n const existed = existsSync(dir);\n if (existed) rmSync(dir, { recursive: true, force: true });\n return { removed: existed, path: dir };\n}\n\n/**\n * Read the registered metadata record for `id`, or null if not registered or\n * the record is unreadable/corrupt.\n */\nexport function readRegisteredMeta(id: string): RegisteredMeta | null {\n const recordPath = join(connectorDir(id), \"connector.json\");\n if (!existsSync(recordPath)) return null;\n try {\n return JSON.parse(readFileSync(recordPath, \"utf8\")) as RegisteredMeta;\n } catch {\n return null;\n }\n}\n\n/**\n * Load the LIVE ResolvedConnector (with hook handlers) for a registered id by\n * re-importing its source module. Throws a clear error when the connector is\n * not registered or its source module no longer resolves to it.\n */\nexport async function loadRegisteredConnector(\n id: string,\n): Promise<ResolvedConnector> {\n const meta = readRegisteredMeta(id);\n if (!meta) {\n throw new Error(\n `Connector \"${id}\" is not registered (no record at ${join(connectorDir(id), \"connector.json\")}). ` +\n `Run an install/register step first.`,\n );\n }\n if (!meta.modulePath || !existsSync(meta.modulePath)) {\n throw new Error(\n `Connector \"${id}\" record points at a missing module: ${meta.modulePath}`,\n );\n }\n const { connector } = await loadConnectorFromPath(meta.modulePath);\n return connector;\n}\n\n/**\n * Build a handler-less ResolvedConnector from stored metadata. Suitable for\n * inspection/doctor where live hook handlers are not needed (and where the\n * source module may even be gone). `hooks` is empty; `hookEvents` is preserved.\n */\nexport function connectorFromMeta(meta: RegisteredMeta): ResolvedConnector {\n return {\n id: meta.id,\n displayName: meta.displayName,\n version: meta.version,\n ...(meta.server ? { server: meta.server } : {}),\n hooks: {},\n hookEvents: meta.hookEvents ?? [],\n telemetry: meta.telemetry,\n commands: meta.commands ?? [],\n skills: meta.skills ?? [],\n subagents: meta.subagents ?? [],\n platforms: meta.platforms ?? {},\n targets: meta.targets,\n };\n}\n\n/**\n * List every registered connector as a handler-less ResolvedConnector (read\n * from `connectorsDir()`). Used by doctor to health-check what is actually\n * installed, rather than guessing from the local working directory.\n */\nexport function listRegisteredConnectors(): ResolvedConnector[] {\n const dir = connectorsDir();\n if (!existsSync(dir)) return [];\n const out: ResolvedConnector[] = [];\n for (const entry of readdirSync(dir)) {\n const meta = readRegisteredMeta(entry);\n if (meta) out.push(connectorFromMeta(meta));\n }\n return out;\n}\n"],"mappings":"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","names":["connector"]}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/core/jsonc.ts","../src/adapters/base.ts"],"sourcesContent":["/**\n * core/jsonc — tolerant JSONC / JSON5-lite parsing.\n *\n * Many hosts document their config file as JSONC (or JSON5): // line comments,\n * /* *\\/ block comments, and trailing commas are all legal. A strict JSON.parse\n * false-fails such a perfectly valid file → null → and a careless `?? {}` then\n * OVERWRITES the user's entire config with just our entry (data loss). To avoid\n * that, every JSON/JSONC adapter parses through `parseJsonc`, which strips\n * comments + trailing commas in ONE string-aware pass before JSON.parse.\n *\n * The single pass is deliberately string-literal aware: a // or /* inside a\n * quoted value (e.g. \"http://x//y\") is NOT a comment, and a comma inside a\n * string (e.g. \"a,]\") is NOT a trailing comma — both must survive verbatim.\n */\n\n/**\n * Strip // line comments, /* *\\/ block comments, and trailing commas from JSONC\n * text in a SINGLE string-aware pass.\n *\n * Rules:\n * - While inside a string literal, every character (including //, /*, and ,)\n * is copied verbatim; backslash escapes the next character.\n * - Outside a string, // … runs to end-of-line and /* … *\\/ to its close.\n * - A comma is dropped ONLY when the next non-whitespace, non-comment token is\n * a closing `}` or `]` (a trailing comma). A comma followed by another value\n * or by end-of-input is preserved (the latter lets JSON.parse report the\n * real syntax error instead of us masking it).\n */\nexport function stripJsonc(text: string): string {\n const n = text.length;\n let out = \"\";\n let i = 0;\n let inString = false;\n let quote = \"\";\n\n while (i < n) {\n const ch = text[i] as string;\n\n if (inString) {\n out += ch;\n if (ch === \"\\\\\") {\n // Copy the escaped character verbatim (covers \\\" \\\\ \\/ etc.).\n if (i + 1 < n) {\n out += text[i + 1] as string;\n i += 2;\n continue;\n }\n } else if (ch === quote) {\n inString = false;\n }\n i += 1;\n continue;\n }\n\n const next = i + 1 < n ? (text[i + 1] as string) : \"\";\n\n // Enter a string literal.\n if (ch === '\"' || ch === \"'\") {\n inString = true;\n quote = ch;\n out += ch;\n i += 1;\n continue;\n }\n\n // Line comment — skip to end of line (keep the newline for line tracking).\n if (ch === \"/\" && next === \"/\") {\n i += 2;\n while (i < n && text[i] !== \"\\n\") i += 1;\n continue;\n }\n\n // Block comment — skip to the closing */.\n if (ch === \"/\" && next === \"*\") {\n i += 2;\n while (i < n && !(text[i] === \"*\" && text[i + 1] === \"/\")) i += 1;\n i += 2; // consume the closing */\n continue;\n }\n\n // Trailing comma — drop it only when the next significant token (skipping\n // whitespace AND comments) is a closing } or ]. This stays string-aware\n // because we only reach here outside a string.\n if (ch === \",\") {\n const closer = nextSignificant(text, i + 1);\n if (closer === \"}\" || closer === \"]\") {\n i += 1; // drop the trailing comma\n continue;\n }\n }\n\n out += ch;\n i += 1;\n }\n\n return out;\n}\n\n/**\n * Peek the next significant (non-whitespace, non-comment) character at/after\n * `from`, returning \"\" at end-of-input. Used only outside string literals.\n */\nfunction nextSignificant(text: string, from: number): string {\n const n = text.length;\n let i = from;\n while (i < n) {\n const ch = text[i] as string;\n if (ch === \" \" || ch === \"\\t\" || ch === \"\\r\" || ch === \"\\n\") {\n i += 1;\n continue;\n }\n const next = i + 1 < n ? (text[i + 1] as string) : \"\";\n if (ch === \"/\" && next === \"/\") {\n i += 2;\n while (i < n && text[i] !== \"\\n\") i += 1;\n continue;\n }\n if (ch === \"/\" && next === \"*\") {\n i += 2;\n while (i < n && !(text[i] === \"*\" && text[i + 1] === \"/\")) i += 1;\n i += 2;\n continue;\n }\n return ch;\n }\n return \"\";\n}\n\n/**\n * Parse JSONC text tolerantly: strip comments + trailing commas, then JSON.parse.\n * Throws (like JSON.parse) on genuinely malformed input — callers that want a\n * fail-soft null should wrap in try/catch (BaseAdapter.readJson does).\n */\nexport function parseJsonc<T = unknown>(text: string): T {\n return JSON.parse(stripJsonc(text)) as T;\n}\n","/**\n * adapters/base — shared adapter foundation.\n *\n * Provides JSON config IO, settings backup, and a default doctor. Per-platform\n * specifics (paths, capabilities, native render format, runtime parse/format)\n * are abstract and implemented by each adapter. TOML/YAML adapters override the\n * JSON helpers entirely.\n */\n\nimport {\n copyFileSync,\n existsSync,\n readdirSync,\n readFileSync,\n rmSync,\n statSync,\n writeFileSync,\n} from \"node:fs\";\nimport type { Dirent } from \"node:fs\";\nimport { basename, dirname, join, relative, resolve, sep } from \"node:path\";\n\nimport type {\n ChangeRecord,\n DetectedPlatform,\n DiagnosticResult,\n HealthCheck,\n HookParadigm,\n PlatformCapabilities,\n PlatformId,\n} from \"../core/types.js\";\nimport { backupsDir, ensureDir } from \"../core/paths.js\";\nimport { parseJsonc } from \"../core/jsonc.js\";\nimport { renderFrontmatterMd } from \"./claude-code/render.js\";\nimport type { Adapter, InstallContext } from \"./spi.js\";\n\n/** Content-surface kinds with BaseAdapter default install/uninstall handling. */\ntype ContentSurface = \"commands\" | \"skills\" | \"subagents\";\n\nexport abstract class BaseAdapter implements Adapter {\n abstract readonly id: PlatformId;\n abstract readonly name: string;\n abstract readonly paradigm: HookParadigm;\n abstract readonly capabilities: PlatformCapabilities;\n\n abstract detectInstalled(projectDir: string): DetectedPlatform;\n abstract getConfigDir(ctx: InstallContext): string;\n abstract getServerConfigPath(ctx: InstallContext): string;\n abstract getHookConfigPath(ctx: InstallContext): string;\n abstract installServer(ctx: InstallContext): ChangeRecord[];\n abstract uninstallServer(ctx: InstallContext): ChangeRecord[];\n abstract installHooks(ctx: InstallContext): ChangeRecord[];\n abstract uninstallHooks(ctx: InstallContext): ChangeRecord[];\n\n // ── Content surfaces (commands / skills / subagents) ─────────────────────\n // CONCRETE (overridable) defaults so every adapter inherits all six methods\n // and the installer can call them without optional-chaining. Each returns a\n // single skip when the connector declares no entries of that surface, else a\n // single \"warn\" — mirroring the mcp-only hook handling. Supporting adapters\n // override only the surfaces they honor.\n\n installCommands(ctx: InstallContext): ChangeRecord[] {\n return this.unsupportedSurface(ctx, \"commands\", ctx.connector.commands.length);\n }\n uninstallCommands(ctx: InstallContext): ChangeRecord[] {\n return this.unsupportedSurface(ctx, \"commands\", ctx.connector.commands.length);\n }\n installSkills(ctx: InstallContext): ChangeRecord[] {\n return this.unsupportedSurface(ctx, \"skills\", ctx.connector.skills.length);\n }\n uninstallSkills(ctx: InstallContext): ChangeRecord[] {\n return this.unsupportedSurface(ctx, \"skills\", ctx.connector.skills.length);\n }\n installSubagents(ctx: InstallContext): ChangeRecord[] {\n return this.unsupportedSurface(ctx, \"subagents\", ctx.connector.subagents.length);\n }\n uninstallSubagents(ctx: InstallContext): ChangeRecord[] {\n return this.unsupportedSurface(ctx, \"subagents\", ctx.connector.subagents.length);\n }\n\n /**\n * Default response for a content surface this platform cannot honor: a single\n * skip when the connector declares none of that surface, else a single \"warn\"\n * ChangeRecord noting how many were skipped. Mirrors mcp-only hook handling.\n */\n protected unsupportedSurface(\n _ctx: InstallContext,\n surface: ContentSurface,\n count: number,\n ): ChangeRecord[] {\n if (count === 0) {\n return [{ platform: this.id, action: \"skip\", detail: `connector declares no ${surface}` }];\n }\n return [\n {\n platform: this.id,\n action: \"warn\",\n detail: `${surface} not supported on ${this.id}; ${count} skipped`,\n },\n ];\n }\n\n // ── Content-file helpers (used by surface-supporting adapters) ────────────\n\n /**\n * Write a content file idempotently: \"skip\" when the existing bytes are\n * already identical, else \"create\"/\"update\". Creates parent dirs (mkdir -p).\n * Honors dryRun (computes the action but writes nothing).\n */\n protected writeContentFile(path: string, contents: string, dryRun: boolean): ChangeRecord {\n let action: ChangeRecord[\"action\"];\n if (!existsSync(path)) {\n action = \"create\";\n } else {\n let current: string | null = null;\n try {\n current = readFileSync(path, \"utf8\");\n } catch {\n current = null;\n }\n action = current === contents ? \"skip\" : \"update\";\n }\n if (action !== \"skip\" && !dryRun) {\n ensureDir(dirname(path));\n writeFileSync(path, contents, \"utf8\");\n }\n return { platform: this.id, action, path, detail: basename(path) };\n }\n\n /**\n * Remove a content file we wrote; \"skip\" when already absent. Honors dryRun.\n * Uses rmSync with `force` to also tolerate a path that is a directory tree\n * (e.g. a skill folder removed by the supporting adapter).\n */\n protected removeContentFile(path: string, dryRun: boolean): ChangeRecord {\n if (!existsSync(path)) {\n return { platform: this.id, action: \"skip\", path, detail: `${basename(path)} absent` };\n }\n if (!dryRun) {\n rmSync(path, { recursive: true, force: true });\n }\n return { platform: this.id, action: \"remove\", path, detail: basename(path) };\n }\n\n /**\n * Defense-in-depth path containment: resolve `rel` against `baseDir` and\n * return the absolute path ONLY when it stays inside `baseDir`. Returns null\n * when `rel` escapes (absolute path, `..` traversal, etc.) so a caller can\n * skip-and-warn rather than write/delete outside the surface dir. This is the\n * runtime backstop behind the config-time validation in normalizeSkills.\n */\n protected resolveWithin(baseDir: string, rel: string): string | null {\n const base = resolve(baseDir);\n const target = resolve(base, rel);\n if (target === base) return null; // rel resolved to the dir itself (e.g. \"\" / \".\")\n const rind = relative(base, target);\n // Outside when the relative path climbs out (`..`) or is itself absolute.\n if (rind === \"\" || rind.startsWith(\"..\") || resolve(base, rind) !== target) {\n return null;\n }\n if (rind === \"..\" || rind.startsWith(`..${sep}`)) return null;\n return target;\n }\n\n /**\n * Recursively report whether a directory tree contains any regular FILE (vs\n * only empty/nested-empty subdirectories). Symlinks count as files (we never\n * recurse through them). Used by removeDirIfEmpty to decide if a tree is safe\n * to drop. An unreadable entry is treated as \"a file is present\" (conservative).\n */\n private dirTreeHasFile(dir: string): boolean {\n let entries: Dirent[];\n try {\n entries = readdirSync(dir, { withFileTypes: true });\n } catch {\n return true; // unreadable → assume it holds data; do not remove\n }\n for (const e of entries) {\n if (e.isDirectory()) {\n if (this.dirTreeHasFile(join(dir, e.name))) return true;\n } else {\n return true; // a file / symlink / special → the tree is not \"empty\"\n }\n }\n return false;\n }\n\n /**\n * Remove a directory ONLY when its tree holds NO regular files — i.e. it is\n * empty or contains only (nested) empty directories, such as the intermediate\n * dirs our own nested skill resources created. Replaces the unconditional\n * `rm -rf <dir>` that uninstallSkills used to call, which destroyed user-added\n * files / sibling-tool files / shared skill dirs the connector never wrote.\n * When ANY file remains (at any depth) the whole tree is left in place and the\n * skip is noted. Honors dryRun (computes the action but removes nothing). A\n * missing dir is a no-op \"skip\".\n */\n protected removeDirIfEmpty(dir: string, dryRun: boolean): ChangeRecord {\n if (!existsSync(dir)) {\n return { platform: this.id, action: \"skip\", path: dir, detail: `${basename(dir)} absent` };\n }\n let stat: ReturnType<typeof statSync>;\n try {\n stat = statSync(dir);\n } catch {\n return {\n platform: this.id,\n action: \"skip\",\n path: dir,\n detail: `${basename(dir)} unreadable; left in place`,\n };\n }\n if (!stat.isDirectory()) {\n return {\n platform: this.id,\n action: \"skip\",\n path: dir,\n detail: `${basename(dir)} not a directory; left in place`,\n };\n }\n if (this.dirTreeHasFile(dir)) {\n return {\n platform: this.id,\n action: \"skip\",\n path: dir,\n detail: `${basename(dir)} still holds files we did not write; left in place`,\n };\n }\n if (!dryRun) {\n try {\n // The tree is files-free: only (possibly nested) empty dirs remain, which\n // are ours / inert. A recursive remove drops them without touching data.\n rmSync(dir, { recursive: true, force: true });\n } catch {\n // Lost a race or perms — non-fatal; report as a skip.\n return {\n platform: this.id,\n action: \"skip\",\n path: dir,\n detail: `${basename(dir)} could not be removed; left in place`,\n };\n }\n }\n return { platform: this.id, action: \"remove\", path: dir, detail: basename(dir) };\n }\n\n /**\n * Render a YAML-frontmatter + markdown body document:\n * \"---\\n\" + <yaml> + \"---\\n\\n\" + <body> + \"\\n\".\n * Delegates to the shared renderer (adapters/claude-code/render) so every\n * content-file path — adapters AND the plugin packager — emits identical bytes.\n */\n protected renderFrontmatterMd(\n frontmatter: Record<string, unknown>,\n body: string,\n ): string {\n return renderFrontmatterMd(frontmatter, body);\n }\n\n // ── JSON config helpers (used by JSON-format adapters) ───────────────────\n\n /**\n * Read + parse a JSON/JSONC config file, returning null when it is absent OR\n * unparseable. Parses TOLERANTLY via parseJsonc (strips // and /* *\\/ comments\n * and trailing commas) so a perfectly valid JSONC file never false-fails to\n * null — that null is what `?? {}` would otherwise turn into a clobbering\n * overwrite of the user's whole config.\n */\n protected readJson<T = Record<string, unknown>>(path: string): T | null {\n if (!existsSync(path)) return null;\n try {\n return parseJsonc<T>(readFileSync(path, \"utf8\"));\n } catch {\n return null;\n }\n }\n\n /**\n * True when `path` exists and holds non-whitespace content but readJson cannot\n * parse it (even after JSONC stripping). The overwrite guard: writing a\n * `{}`-based config over a present-but-broken settings file would silently\n * destroy the user's data, so callers warn-and-skip instead.\n */\n protected isPresentButUnparseable(path: string): boolean {\n if (!existsSync(path)) return false;\n let raw: string;\n try {\n if (statSync(path).size === 0) return false;\n raw = readFileSync(path, \"utf8\");\n } catch {\n return false;\n }\n if (raw.trim() === \"\") return false;\n return this.readJson(path) === null;\n }\n\n protected writeJson(path: string, data: unknown, dryRun = false): void {\n if (dryRun) return;\n ensureDir(dirname(path));\n writeFileSync(path, `${JSON.stringify(data, null, 2)}\\n`, \"utf8\");\n }\n\n /**\n * Upsert `entry` at `config[rootKey][serverId]` in a JSON file, creating the\n * file/object as needed. Returns a ChangeRecord describing create/update/skip.\n * Idempotent: an identical entry yields \"skip\".\n */\n protected upsertServerInJson(\n configPath: string,\n rootKey: string,\n serverId: string,\n entry: unknown,\n dryRun = false,\n ): ChangeRecord {\n // OVERWRITE GUARD: never replace a present, non-empty, unparseable settings\n // file with a `{}`-based config — that would silently destroy the user's\n // data. Warn and skip so they can back it up / fix it and re-run.\n if (this.isPresentButUnparseable(configPath)) {\n return {\n platform: this.id,\n action: \"warn\",\n path: configPath,\n detail: `existing ${configPath} is not parseable; left untouched (back it up / fix it, then re-run)`,\n };\n }\n const cfg = this.readJson<Record<string, Record<string, unknown>>>(configPath) ?? {};\n const bucket = (cfg[rootKey] ??= {});\n const before = JSON.stringify(bucket[serverId]);\n const after = JSON.stringify(entry);\n let action: ChangeRecord[\"action\"];\n if (before === undefined) action = \"create\";\n else if (before === after) action = \"skip\";\n else action = \"update\";\n if (action !== \"skip\") {\n bucket[serverId] = entry;\n this.writeJson(configPath, cfg, dryRun);\n }\n return { platform: this.id, action, path: configPath, detail: `${rootKey}.${serverId}` };\n }\n\n /** Remove `config[rootKey][serverId]` from a JSON file. */\n protected removeServerFromJson(\n configPath: string,\n rootKey: string,\n serverId: string,\n dryRun = false,\n ): ChangeRecord {\n // OVERWRITE GUARD (see upsertServerInJson): a present-but-unparseable file\n // would round-trip to `{}` and erase the user's config, so warn and skip.\n if (this.isPresentButUnparseable(configPath)) {\n return {\n platform: this.id,\n action: \"warn\",\n path: configPath,\n detail: `existing ${configPath} is not parseable; left untouched (back it up / fix it, then re-run)`,\n };\n }\n const cfg = this.readJson<Record<string, Record<string, unknown>>>(configPath);\n const bucket = cfg?.[rootKey];\n if (!cfg || !bucket || !(serverId in bucket)) {\n return { platform: this.id, action: \"skip\", path: configPath, detail: `${rootKey}.${serverId} absent` };\n }\n delete bucket[serverId];\n this.writeJson(configPath, cfg, dryRun);\n return { platform: this.id, action: \"remove\", path: configPath, detail: `${rootKey}.${serverId}` };\n }\n\n // ── Backup ───────────────────────────────────────────────────────────────\n\n backupSettings(ctx: InstallContext): string | null {\n const files = [...new Set([this.getServerConfigPath(ctx), this.getHookConfigPath(ctx)])].filter(\n (f) => existsSync(f),\n );\n if (files.length === 0) return null;\n if (ctx.dryRun) return null;\n ensureDir(backupsDir());\n const stamp = new Date().toISOString().replace(/[:.]/g, \"-\");\n let last: string | null = null;\n for (const f of files) {\n const dest = join(backupsDir(), `${this.id}-${stamp}-${basename(f)}`);\n copyFileSync(f, dest);\n last = dest;\n }\n return last;\n }\n\n // ── Diagnostics ─────────────────────────────────────────────────────────\n\n getHealthChecks?(ctx: InstallContext): readonly HealthCheck[];\n\n doctor(ctx: InstallContext): DiagnosticResult[] {\n const results: DiagnosticResult[] = [];\n const cfg = this.getServerConfigPath(ctx);\n results.push(\n existsSync(cfg)\n ? { check: `${this.name}: config present`, status: \"pass\", message: cfg }\n : {\n check: `${this.name}: config present`,\n status: \"warn\",\n message: `not found: ${cfg}`,\n fix: `agent-connector install --targets ${this.id}`,\n },\n );\n for (const hc of this.getHealthChecks?.(ctx) ?? []) {\n const r = hc.check();\n results.push({\n check: hc.name,\n status: r.status === \"OK\" ? \"pass\" : \"fail\",\n message: r.detail ?? r.status,\n });\n }\n return results;\n }\n}\n"],"mappings":"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","names":[]}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/adapters/antigravity/index.ts"],"sourcesContent":["/**\n * adapters/antigravity — Google Antigravity (IDE) platform adapter.\n *\n * Antigravity is a Gemini-family host that, as of the 2.0 line, exposes a real\n * lifecycle-hook system on top of MCP — so this adapter is `json-stdio` (not the\n * old `mcp-only` framing): the host pipes a JSON payload to a hook command on\n * stdin and reads a JSON control object back, the same paradigm as Claude Code /\n * Gemini CLI. It installs the MCP server, lifecycle hooks, slash-command\n * Workflows, and Agent Skills.\n *\n * CONFIRMED-BY-INSTALL (2026-06-03, docs/research/antigravity-paths-confirmed.md):\n * a real machine with Antigravity IDE + `agy` CLI v1.0.0 shows the canonical user\n * customization root is `~/.gemini/antigravity/` — `~/.gemini/antigravity/mcp_config.json`\n * EXISTS and `~/.gemini/config/mcp_config.json` does NOT. So the fresh-install\n * default user MCP path is `~/.gemini/antigravity/mcp_config.json` (candidate[0]);\n * the `config/` and `antigravity-cli/` paths remain probed fallbacks only (honored\n * when a pre-existing config is found there, never the fresh default).\n *\n * STILL MEDIUM-CONFIDENCE / PATH-PROBING: hooks.json and the global skills dir were\n * NOT present on the observed install, so those locations stay corroborated-but-not-\n * byte-verified. Every user-scope path is PROBED at runtime — prefer a candidate\n * that already exists on disk, else fall back to the canonical path — and the doctor\n * flags each probed path with \"verify for your Antigravity version.\" We NEVER\n * hard-code a single guessed path, and any unsupported event or surface warn-skips\n * (never throws) at install time.\n *\n * Native config formats:\n * - MCP config (JSONC; we WRITE plain JSON), root key \"mcpServers\". stdio is\n * `{ command, args, env }`; a remote server is `{ serverUrl, headers }`\n * (NOTE: the key is `serverUrl`, NOT `url` and NOT Gemini CLI's `httpUrl`).\n * user scope → prefer existing of:\n * ~/.gemini/antigravity/mcp_config.json (CONFIRMED canonical)\n * ~/.gemini/config/mcp_config.json (probed fallback)\n * ~/.gemini/antigravity-cli/mcp_config.json (probed fallback)\n * else default to candidate[0] (antigravity/).\n * project scope → <projectDir>/.agents/mcp_config.json\n * - Hooks: a SEPARATE hooks.json in the customization dir (NOT the mcp_config),\n * project → <projectDir>/.agents/hooks.json\n * user → <resolvedUserConfigDir>/hooks.json\n * Shape: { hooks: { <Event>: [ { matcher, hooks:[{ type:\"command\", command }] } ] } }.\n * Supported events: PreToolUse, PostToolUse, SessionStart, Stop. All other\n * normalized events warn-skip. I/O fields are camelCase (see parse/format).\n * - Commands = \"Workflows\": markdown `.md` (body = the prompt; an optional\n * leading description line). Project prefer existing <proj>/.agent/workflows\n * or <proj>/.agents/workflows (default .agent/workflows per launch docs);\n * user ~/.gemini/antigravity/global_workflows.\n * - Skills = Agent Skills (uniform SKILL.md). Project\n * <proj>/.agents/skills/<name>/SKILL.md; user PROBE\n * ~/.gemini/antigravity-cli/skills then ~/.gemini/skills (NEVER\n * ~/.gemini/antigravity/skills — reportedly broken). NOTE: the global skills\n * dir was NOT present on the confirmed install, so it stays medium-confidence\n * + doctor \"verify for your version.\"\n * - Subagents: declarative subagents exist only inside a plugin bundle, which\n * agent-connector does not emit; supportsSubagents is false and the\n * BaseAdapter skip/warn default applies.\n *\n * Env handling: Antigravity (Gemini family) documents no `${env:VAR}` token of\n * the framework's syntax, so env/header/url refs are resolved to literals at\n * install time via resolveEnvRefsDeep — the safe default matching the Gemini CLI\n * adapter.\n */\n\nimport { existsSync } from \"node:fs\";\nimport { homedir } from \"node:os\";\nimport { dirname, join } from \"node:path\";\n\nimport { BaseAdapter } from \"../base.js\";\nimport type { Adapter, HookReply, InstallContext, NormalizedEvent } from \"../spi.js\";\nimport type {\n ChangeRecord,\n CommandDef,\n DetectedPlatform,\n HealthCheck,\n HookEventName,\n HookParadigm,\n HookResponse,\n PlatformCapabilities,\n PlatformId,\n PostToolUseEvent,\n PreToolUseEvent,\n ServerDef,\n SessionStartEvent,\n SkillDef,\n StopEvent,\n Transport,\n} from \"../../core/types.js\";\nimport { resolveEnvRefsDeep } from \"../../core/interpolate.js\";\nimport {\n buildHomeBinHookCommand,\n buildServeWrapperCommand,\n buildUsageEventCommand,\n isHomeBinHookCommand,\n isHostNativeUsageEnabled,\n isUsageEventCommand,\n shouldWrapForTelemetry,\n} from \"../../core/spawn.js\";\n\nconst HOST: PlatformId = \"antigravity\";\nconst MCP_ROOT_KEY = \"mcpServers\";\n\n/**\n * Known user-scope MCP config candidates, in preference order. We pick the first\n * that already exists on disk; otherwise we default to candidate[0]\n * (~/.gemini/antigravity/mcp_config.json — CONFIRMED canonical on a real install:\n * docs/research/antigravity-paths-confirmed.md). The `config/` and CLI-only\n * `antigravity-cli/` paths are kept ONLY as probed fallbacks so a pre-existing\n * config in either is still honored, but neither is ever the fresh-install default.\n */\nconst USER_CONFIG_CANDIDATES = [\n [\".gemini\", \"antigravity\", \"mcp_config.json\"],\n [\".gemini\", \"config\", \"mcp_config.json\"],\n [\".gemini\", \"antigravity-cli\", \"mcp_config.json\"],\n] as const;\n\n/**\n * Antigravity's supported lifecycle hook events. These happen to share the\n * normalized PascalCase names 1:1; everything else has no Antigravity equivalent\n * and is reported as a warn/skip at install time.\n */\nconst SUPPORTED_EVENTS: ReadonlySet<HookEventName> = new Set<HookEventName>([\n \"PreToolUse\",\n \"PostToolUse\",\n \"SessionStart\",\n \"Stop\",\n]);\n\n/**\n * Native hooks.json key for the OPT-IN host-native turn-usage hook (enricher 4a).\n * Antigravity shares the Gemini-family `AfterModel` model-turn hook, whose payload\n * carries `usageMetadata`. This is a host-native-only sink (no connector handler);\n * it is installed ONLY when host-native capture is enabled and records a DISTINCT\n * `model_turn` telemetry row that is never summed with the per-MCP `serve` rows.\n */\nconst USAGE_HOOK_EVENT = \"AfterModel\";\n\n/** A single Antigravity native hook registration entry (nested, Claude-shaped). */\ninterface AntigravityHookEntry {\n matcher: string;\n hooks: Array<{ type: \"command\"; command: string }>;\n}\n\n/** The shape of Antigravity's hooks.json (only the parts we touch). */\ninterface AntigravityHooksFile {\n hooks?: Record<string, AntigravityHookEntry[]>;\n [key: string]: unknown;\n}\n\n/**\n * Native MCP server entry shapes Antigravity accepts under `mcpServers`.\n * A stdio server is `{ command, args, env }`; a remote server is\n * `{ serverUrl, headers }` — the key is `serverUrl` (NOT `url`).\n */\ninterface AntigravityStdioServer {\n command: string;\n args?: string[];\n env?: Record<string, string>;\n}\ninterface AntigravityHttpServer {\n serverUrl: string;\n headers?: Record<string, string>;\n}\n\n/** Raw Antigravity hook stdin payload (camelCase wire fields). */\ninterface AntigravityWireInput {\n connector?: unknown;\n toolName?: string;\n toolInput?: Record<string, unknown>;\n toolOutput?: string;\n isError?: boolean;\n sessionId?: string;\n cwd?: string;\n source?: string;\n reason?: string;\n stopHookActive?: boolean;\n}\n\nexport class AntigravityAdapter extends BaseAdapter implements Adapter {\n readonly id: PlatformId = HOST;\n readonly name: string = \"Google Antigravity\";\n readonly paradigm: HookParadigm = \"json-stdio\";\n\n readonly capabilities: PlatformCapabilities = {\n // Antigravity 2.0 fires PreToolUse / PostToolUse / SessionStart / Stop.\n preToolUse: true,\n postToolUse: true,\n sessionStart: true,\n stop: true,\n // No documented equivalents — kept false; the corresponding normalized\n // events warn-skip at install time.\n preCompact: false,\n sessionEnd: false,\n userPromptSubmit: false,\n notification: false,\n // Transform-category hooks can rewrite tool input and tool output; the\n // SessionStart hook can inject additional context.\n canModifyArgs: true,\n canModifyOutput: true,\n canInjectSessionContext: true,\n // Antigravity registers stdio, SSE, and Streamable HTTP MCP servers.\n transports: [\"stdio\", \"sse\", \"http\"],\n // Content surfaces: Workflows (markdown commands) + Agent Skills (SKILL.md).\n // Declarative subagents exist only inside a plugin bundle (not emitted), so\n // subagents are unsupported and inherit the BaseAdapter skip/warn default.\n supportsCommands: true,\n supportsSkills: true,\n supportsSubagents: false,\n };\n\n // ── Detection ────────────────────────────────────────────────────────────\n\n detectInstalled(projectDir: string): DetectedPlatform {\n const userConfig = this.resolveUserConfigPath();\n const userDir = join(homedir(), \".gemini\");\n const projectConfig = join(projectDir, \".agents\", \"mcp_config.json\");\n\n const userInstalled =\n existsSync(userDir) ||\n this.userConfigCandidates().some((parts) => existsSync(join(homedir(), ...parts)));\n const projInstalled = existsSync(projectConfig);\n const installed = userInstalled || projInstalled;\n\n // Report the scope/path that actually matched, so a project-only install\n // isn't misreported as a (non-existent) user install.\n const scope = projInstalled && !userInstalled ? \"project\" : \"user\";\n const configPath = scope === \"project\" ? projectConfig : userConfig;\n\n return {\n id: this.id,\n name: this.name,\n installed,\n paradigm: this.paradigm,\n capabilities: this.capabilities,\n configPath,\n scope,\n reason: installed\n ? scope === \"project\"\n ? `found project Antigravity config at ${projectConfig}`\n : `found Antigravity config under ${userDir}`\n : `no Antigravity config at ${userDir} or ${projectConfig}`,\n confidence: installed ? \"high\" : \"low\",\n };\n }\n\n // ── Native paths ─────────────────────────────────────────────────────────\n\n getConfigDir(ctx: InstallContext): string {\n if (ctx.scope === \"project\") return join(ctx.projectDir, \".agents\");\n // User scope: parent dir of the resolved user mcp_config.json.\n return dirname(this.resolveUserConfigPath());\n }\n\n getServerConfigPath(ctx: InstallContext): string {\n return ctx.scope === \"project\"\n ? join(ctx.projectDir, \".agents\", \"mcp_config.json\")\n : this.resolveUserConfigPath();\n }\n\n /**\n * Hooks live in a SEPARATE hooks.json in the customization dir — NOT the\n * mcp_config.json. Project → <proj>/.agents/hooks.json; user →\n * <resolvedUserConfigDir>/hooks.json (the dir holding the resolved user MCP\n * config). Path is medium-confidence and surfaced by the doctor.\n */\n getHookConfigPath(ctx: InstallContext): string {\n return join(this.getConfigDir(ctx), \"hooks.json\");\n }\n\n /**\n * The user-scope MCP config candidates, in preference order. Overridable by\n * forks (e.g. the Antigravity CLI) that resolve a different user-scope root\n * while reusing resolveUserConfigPath's prefer-existing-else-candidate[0] logic.\n */\n protected userConfigCandidates(): ReadonlyArray<readonly string[]> {\n return USER_CONFIG_CANDIDATES;\n }\n\n /**\n * Pick the user-scope mcp_config.json: prefer a candidate that already exists,\n * else default to candidate[0] (the CONFIRMED canonical ~/.gemini/antigravity path).\n */\n protected resolveUserConfigPath(): string {\n const home = homedir();\n const candidates = this.userConfigCandidates();\n for (const parts of candidates) {\n const p = join(home, ...parts);\n if (existsSync(p)) return p;\n }\n const fallback = candidates[0];\n if (fallback === undefined) {\n // Defensive: a non-empty candidate list is an invariant, but never throw.\n return join(home, \".gemini\", \"antigravity\", \"mcp_config.json\");\n }\n return join(home, ...fallback);\n }\n\n /**\n * Resolve the Workflows (slash-command) dir, prefer-existing-else-canonical.\n * project → prefer existing <proj>/.agent/workflows or <proj>/.agents/workflows;\n * default .agent/workflows (launch-era singular `.agent`).\n * user → ~/.gemini/antigravity/global_workflows.\n */\n protected resolveWorkflowsDir(ctx: InstallContext): string {\n if (ctx.scope === \"project\") {\n const singular = join(ctx.projectDir, \".agent\", \"workflows\");\n const plural = join(ctx.projectDir, \".agents\", \"workflows\");\n if (existsSync(singular)) return singular;\n if (existsSync(plural)) return plural;\n return singular;\n }\n return join(homedir(), \".gemini\", \"antigravity\", \"global_workflows\");\n }\n\n /**\n * Resolve the Agent Skills dir, prefer-existing-else-canonical.\n * project → <proj>/.agents/skills (plural — matches mcp/skills convention).\n * user → prefer existing ~/.gemini/antigravity-cli/skills, else\n * ~/.gemini/skills. NEVER ~/.gemini/antigravity/skills\n * (reportedly broken), so it is not a candidate.\n */\n protected resolveSkillsDir(ctx: InstallContext): string {\n if (ctx.scope === \"project\") {\n return join(ctx.projectDir, \".agents\", \"skills\");\n }\n const home = homedir();\n const cliSkills = join(home, \".gemini\", \"antigravity-cli\", \"skills\");\n const sharedSkills = join(home, \".gemini\", \"skills\");\n if (existsSync(cliSkills)) return cliSkills;\n if (existsSync(sharedSkills)) return sharedSkills;\n return cliSkills;\n }\n\n // ── MCP server install / uninstall ───────────────────────────────────────\n\n installServer(ctx: InstallContext): ChangeRecord[] {\n const { connector } = ctx;\n const override = connector.platforms[this.id]?.server;\n if (!connector.server || override === false) {\n return [\n {\n platform: this.id,\n action: \"skip\",\n detail: connector.server\n ? `server registration disabled for ${this.id}`\n : \"connector declares no MCP server\",\n },\n ];\n }\n\n // Shallow-merge any per-platform server override into the base ServerDef.\n const server: ServerDef =\n override && typeof override === \"object\"\n ? { ...connector.server, ...override }\n : connector.server;\n\n const serverPath = this.getServerConfigPath(ctx);\n const entry = this.renderServerEntry(ctx, server);\n\n return [\n this.upsertServerInJson(serverPath, MCP_ROOT_KEY, connector.id, entry, ctx.dryRun),\n ];\n }\n\n uninstallServer(ctx: InstallContext): ChangeRecord[] {\n const serverPath = this.getServerConfigPath(ctx);\n return [\n this.removeServerFromJson(serverPath, MCP_ROOT_KEY, ctx.connector.id, ctx.dryRun),\n ];\n }\n\n /** Render a normalized ServerDef into Antigravity's native mcpServers entry. */\n private renderServerEntry(\n ctx: InstallContext,\n server: ServerDef,\n ): AntigravityStdioServer | AntigravityHttpServer {\n const transport: Transport = server.transport;\n\n if (transport === \"stdio\") {\n let command = server.command ?? \"\";\n let args = [...(server.args ?? [])];\n\n // Transparent telemetry wrapping: route the real command through\n // `<homeBin> serve --connector <id> --scope <scope> -- <command> <args...>`.\n if (shouldWrapForTelemetry(server, ctx.connector.telemetry)) {\n const wrapped = buildServeWrapperCommand(\n ctx.homeBinPath,\n ctx.connector.id,\n command,\n args,\n ctx.scope,\n this.id,\n );\n command = wrapped.command;\n args = wrapped.args;\n }\n\n // Antigravity (Gemini family) has no documented native interpolation token,\n // so resolve every ${env:VAR} to a literal at install time.\n const entry: AntigravityStdioServer = { command: resolveEnvRefsDeep(command) };\n if (args.length > 0) entry.args = resolveEnvRefsDeep(args);\n const env = this.renderEnv(server.env);\n if (env) entry.env = env;\n return entry;\n }\n\n // sse / http (and any other remote transport) — Antigravity registers a URL\n // under the `serverUrl` key (NOT `url`).\n const entry: AntigravityHttpServer = { serverUrl: resolveEnvRefsDeep(server.url ?? \"\") };\n const headers = this.renderEnv(server.headers);\n if (headers) entry.headers = headers;\n return entry;\n }\n\n /**\n * Render env/header values. Antigravity documents no native interpolation\n * token, so resolve `${env:VAR}` references to literals at install time.\n */\n private renderEnv(\n env: Record<string, string> | undefined,\n ): Record<string, string> | undefined {\n if (!env || Object.keys(env).length === 0) return undefined;\n return resolveEnvRefsDeep({ ...env });\n }\n\n // ── Hook install / uninstall ─────────────────────────────────────────────\n\n installHooks(ctx: InstallContext): ChangeRecord[] {\n const { connector } = ctx;\n if (connector.platforms[this.id]?.hooks === false) {\n return [{ platform: this.id, action: \"skip\", detail: `hooks disabled for ${this.id}` }];\n }\n // The opt-in host-native usage hook (4a) needs no connector handler, so it may\n // be installed even when the connector declares no normalized hook events.\n const hostNative = isHostNativeUsageEnabled(connector.telemetry);\n if (connector.hookEvents.length === 0 && !hostNative) {\n return [{ platform: this.id, action: \"skip\", detail: \"connector declares no hooks\" }];\n }\n\n const hooksPath = this.getHookConfigPath(ctx);\n // OVERWRITE GUARD: never clobber a present-but-unparseable hooks.json.\n if (this.isPresentButUnparseable(hooksPath)) {\n return [\n {\n platform: this.id,\n action: \"warn\",\n path: hooksPath,\n detail: `existing ${hooksPath} is not parseable; left untouched (back it up / fix it, then re-run)`,\n },\n ];\n }\n\n const file = this.readJson<AntigravityHooksFile>(hooksPath) ?? {};\n const hooks = (file.hooks ??= {});\n\n const changes: ChangeRecord[] = [];\n let mutated = false;\n\n for (const event of connector.hookEvents) {\n if (!SUPPORTED_EVENTS.has(event)) {\n // No Antigravity equivalent for this normalized event — report.\n changes.push({\n platform: this.id,\n action: \"warn\",\n path: hooksPath,\n detail: `${event} has no Antigravity hook equivalent — skipped`,\n });\n continue;\n }\n\n const command = buildHomeBinHookCommand(ctx.homeBinPath, this.id, event, connector.id);\n const matcher = connector.hooks[event]?.matcher ?? \"\";\n const entry: AntigravityHookEntry = {\n matcher,\n hooks: [{ type: \"command\", command }],\n };\n\n const bucket = (hooks[event] ??= []);\n const existingIdx = bucket.findIndex((e) => this.entryHasOurCommand(e, ctx));\n\n if (existingIdx >= 0) {\n if (JSON.stringify(bucket[existingIdx]) === JSON.stringify(entry)) {\n changes.push({\n platform: this.id,\n action: \"skip\",\n path: hooksPath,\n detail: `hooks.${event} already registered`,\n });\n continue;\n }\n bucket[existingIdx] = entry;\n changes.push({\n platform: this.id,\n action: \"update\",\n path: hooksPath,\n detail: `hooks.${event}`,\n });\n } else {\n bucket.push(entry);\n changes.push({\n platform: this.id,\n action: \"create\",\n path: hooksPath,\n detail: `hooks.${event}`,\n });\n }\n mutated = true;\n }\n\n // OPT-IN host-native usage hook (4a): register the AfterModel `usage-event`\n // sink when enabled. Records a DISTINCT `model_turn` row (whole-conversation),\n // never summed with the per-MCP `serve`-proxy rows.\n if (hostNative) {\n if (this.installUsageHook(hooks, ctx, hooksPath, changes)) mutated = true;\n }\n\n if (mutated) this.writeJson(hooksPath, file, ctx.dryRun);\n return changes;\n }\n\n /**\n * Register the opt-in AfterModel host-native usage hook (4a) into `hooks`.\n * Idempotent (byte-identical → skip, drift → update); returns true on mutation.\n * The command is the hidden `usage-event` entrypoint (NOT a connector hook), so\n * it carries an empty matcher. Reads `this.id` so the CLI fork installs its own\n * `usage-event antigravity-cli …` variant.\n */\n private installUsageHook(\n hooks: Record<string, AntigravityHookEntry[]>,\n ctx: InstallContext,\n hooksPath: string,\n changes: ChangeRecord[],\n ): boolean {\n const command = buildUsageEventCommand(ctx.homeBinPath, this.id, ctx.connector.id);\n const entry: AntigravityHookEntry = {\n matcher: \"\",\n hooks: [{ type: \"command\", command }],\n };\n const bucket = (hooks[USAGE_HOOK_EVENT] ??= []);\n const existingIdx = bucket.findIndex((e) =>\n (e.hooks ?? []).some((h) => isUsageEventCommand(h.command, ctx.homeBinPath, ctx.connector.id)),\n );\n\n if (existingIdx >= 0) {\n if (JSON.stringify(bucket[existingIdx]) === JSON.stringify(entry)) {\n changes.push({\n platform: this.id,\n action: \"skip\",\n path: hooksPath,\n detail: `hooks.${USAGE_HOOK_EVENT} (host-native usage) already registered`,\n });\n return false;\n }\n bucket[existingIdx] = entry;\n changes.push({\n platform: this.id,\n action: \"update\",\n path: hooksPath,\n detail: `hooks.${USAGE_HOOK_EVENT} (host-native usage)`,\n });\n } else {\n bucket.push(entry);\n changes.push({\n platform: this.id,\n action: \"create\",\n path: hooksPath,\n detail: `hooks.${USAGE_HOOK_EVENT} (host-native usage)`,\n });\n }\n return true;\n }\n\n uninstallHooks(ctx: InstallContext): ChangeRecord[] {\n const hooksPath = this.getHookConfigPath(ctx);\n const file = this.readJson<AntigravityHooksFile>(hooksPath);\n const hooks = file?.hooks;\n if (!file || !hooks) {\n return [\n {\n platform: this.id,\n action: \"skip\",\n path: hooksPath,\n detail: \"no hooks section present\",\n },\n ];\n }\n\n const changes: ChangeRecord[] = [];\n let mutated = false;\n\n for (const event of Object.keys(hooks)) {\n const bucket = hooks[event];\n if (!Array.isArray(bucket)) continue;\n\n // Strip our hook command from each entry; drop entries left empty so we\n // never remove another connector's (or the user's own) hook commands.\n const next: AntigravityHookEntry[] = [];\n let removed = 0;\n for (const e of bucket) {\n const innerBefore = e.hooks?.length ?? 0;\n const inner = (e.hooks ?? []).filter((h) => !this.isOurCommand(h.command, ctx));\n removed += innerBefore - inner.length;\n if (inner.length > 0) next.push({ matcher: e.matcher ?? \"\", hooks: inner });\n }\n\n if (removed > 0) {\n if (next.length > 0) hooks[event] = next;\n else delete hooks[event];\n changes.push({\n platform: this.id,\n action: \"remove\",\n path: hooksPath,\n detail: `hooks.${event} (${removed})`,\n });\n mutated = true;\n }\n }\n\n if (mutated) this.writeJson(hooksPath, file, ctx.dryRun);\n if (changes.length === 0) {\n changes.push({\n platform: this.id,\n action: \"skip\",\n path: hooksPath,\n detail: \"no matching hook entries\",\n });\n }\n return changes;\n }\n\n private entryHasOurCommand(entry: AntigravityHookEntry, ctx: InstallContext): boolean {\n return (entry.hooks ?? []).some((h) => this.isOurCommand(h.command, ctx));\n }\n\n /**\n * True when a hook command is ONE OF OURS for this connector — the universal\n * `hook` dispatcher OR the opt-in `usage-event` sink. Both are anchored on the\n * connector id (see isHomeBinHookCommand) so a shared-prefix id can't collide,\n * and so uninstall reverses the AfterModel usage hook too.\n */\n private isOurCommand(command: string | undefined, ctx: InstallContext): boolean {\n return (\n isHomeBinHookCommand(command, ctx.homeBinPath, ctx.connector.id) ||\n isUsageEventCommand(command, ctx.homeBinPath, ctx.connector.id)\n );\n }\n\n // ── Content surfaces: commands (Workflows) / skills ───────────────────────\n // CONTENT-ONLY: pure native-file writers under the probed workflows / skills\n // dirs. No runtime dispatch, no home-bin pointer, no telemetry wrap. Each\n // method is idempotent (byte-identical → skip) via BaseAdapter.writeContentFile\n // and reversible via removeContentFile. Honors platforms[\"antigravity\"]\n // per-surface false to skip. Commands are markdown Workflows (NOT TOML, unlike\n // gemini-cli); skills are the uniform Claude-compatible SKILL.md. Subagents are\n // unsupported (plugin-bundle-only) and inherit the BaseAdapter skip/warn.\n\n /** Native Workflow file path: <workflowsDir>/<name>.md. */\n private commandPath(ctx: InstallContext, name: string): string {\n return join(this.resolveWorkflowsDir(ctx), `${name}.md`);\n }\n /** Native skill dir: <skillsDir>/<name>. */\n private skillDir(ctx: InstallContext, name: string): string {\n return join(this.resolveSkillsDir(ctx), name);\n }\n\n // ── Commands (Workflows) ──────────────────────────────────────────────────\n\n override installCommands(ctx: InstallContext): ChangeRecord[] {\n const { connector } = ctx;\n if (connector.platforms[this.id]?.commands === false) {\n return [{ platform: this.id, action: \"skip\", detail: `commands disabled for ${this.id}` }];\n }\n if (connector.commands.length === 0) {\n return [{ platform: this.id, action: \"skip\", detail: \"connector declares no commands\" }];\n }\n return connector.commands.map((cmd) =>\n this.writeContentFile(this.commandPath(ctx, cmd.name), this.renderCommand(cmd), ctx.dryRun),\n );\n }\n\n override uninstallCommands(ctx: InstallContext): ChangeRecord[] {\n const { connector } = ctx;\n if (connector.commands.length === 0) {\n return [{ platform: this.id, action: \"skip\", detail: \"connector declares no commands\" }];\n }\n return connector.commands.map((cmd) =>\n this.removeContentFile(this.commandPath(ctx, cmd.name), ctx.dryRun),\n );\n }\n\n /**\n * Render a command to an Antigravity Workflow: a plain markdown `.md` whose\n * body is the command prompt, optionally preceded by a leading description\n * line. No frontmatter / TOML (unlike gemini-cli).\n */\n private renderCommand(cmd: CommandDef): string {\n const body = cmd.prompt.endsWith(\"\\n\") ? cmd.prompt : `${cmd.prompt}\\n`;\n if (cmd.description !== undefined && cmd.description !== \"\") {\n return `${cmd.description}\\n\\n${body}`;\n }\n return body;\n }\n\n // ── Skills ────────────────────────────────────────────────────────────────\n\n override installSkills(ctx: InstallContext): ChangeRecord[] {\n const { connector } = ctx;\n if (connector.platforms[this.id]?.skills === false) {\n return [{ platform: this.id, action: \"skip\", detail: `skills disabled for ${this.id}` }];\n }\n if (connector.skills.length === 0) {\n return [{ platform: this.id, action: \"skip\", detail: \"connector declares no skills\" }];\n }\n const changes: ChangeRecord[] = [];\n for (const skill of connector.skills) {\n const dir = this.skillDir(ctx, skill.name);\n changes.push(\n this.writeContentFile(join(dir, \"SKILL.md\"), this.renderSkill(skill), ctx.dryRun),\n );\n // Bundle any resource files beside SKILL.md (relative path → contents).\n // Defense-in-depth: skip+warn on any key that escapes the skill dir.\n for (const [rel, contents] of Object.entries(skill.resources ?? {})) {\n const target = this.resolveWithin(dir, rel);\n if (target === null) {\n changes.push({\n platform: this.id,\n action: \"warn\",\n detail: `skill resource \"${rel}\" escapes the skill dir; skipped`,\n });\n continue;\n }\n changes.push(this.writeContentFile(target, contents, ctx.dryRun));\n }\n }\n return changes;\n }\n\n override uninstallSkills(ctx: InstallContext): ChangeRecord[] {\n const { connector } = ctx;\n if (connector.skills.length === 0) {\n return [{ platform: this.id, action: \"skip\", detail: \"connector declares no skills\" }];\n }\n const changes: ChangeRecord[] = [];\n for (const skill of connector.skills) {\n const dir = this.skillDir(ctx, skill.name);\n // Remove only the files we wrote (SKILL.md + declared resources), then the\n // skill dir itself when we own its full contents.\n changes.push(this.removeContentFile(join(dir, \"SKILL.md\"), ctx.dryRun));\n for (const rel of Object.keys(skill.resources ?? {})) {\n const target = this.resolveWithin(dir, rel);\n if (target === null) continue; // never delete outside the skill dir\n changes.push(this.removeContentFile(target, ctx.dryRun));\n }\n // Only remove the skill dir when WE own its full contents — never rm -rf a\n // dir that still holds user-added / sibling-tool / shared files.\n changes.push(this.removeDirIfEmpty(dir, ctx.dryRun));\n }\n return changes;\n }\n\n /**\n * Render a skill's SKILL.md: frontmatter (name, description + optional model,\n * allowed-tools, disable-model-invocation) + body. Uniform across platforms.\n */\n private renderSkill(skill: SkillDef): string {\n const frontmatter: Record<string, unknown> = {\n name: skill.name,\n description: skill.description,\n };\n if (skill.model !== undefined) frontmatter.model = skill.model;\n const allow = skill.tools?.allow;\n if (allow && allow.length > 0) frontmatter[\"allowed-tools\"] = allow.join(\", \");\n if (skill.disableModelInvocation !== undefined) {\n frontmatter[\"disable-model-invocation\"] = skill.disableModelInvocation;\n }\n if (skill.extra) Object.assign(frontmatter, skill.extra);\n return this.renderFrontmatterMd(frontmatter, skill.body);\n }\n\n // ── Diagnostics ──────────────────────────────────────────────────────────\n\n override getHealthChecks(ctx: InstallContext): readonly HealthCheck[] {\n const mcpPath = this.getServerConfigPath(ctx);\n const hooksPath = this.getHookConfigPath(ctx);\n const connectorId = ctx.connector.id;\n const homeBin = ctx.homeBinPath;\n const hookEvents = ctx.connector.hookEvents;\n const workflowsDir = this.resolveWorkflowsDir(ctx);\n const skillsDir = this.resolveSkillsDir(ctx);\n\n const checks: HealthCheck[] = [\n {\n name: `${this.name}: mcp_config.json present`,\n check: () =>\n existsSync(mcpPath)\n ? { status: \"OK\", detail: `${mcpPath} (path probed; verify for your Antigravity version)` }\n : { status: \"FAIL\", detail: `not found: ${mcpPath} (path probed; verify for your Antigravity version)` },\n },\n {\n name: `${this.name}: server entry registered`,\n check: () => {\n const cfg = this.readJson<{ [k: string]: Record<string, unknown> }>(mcpPath);\n const bucket = cfg?.[MCP_ROOT_KEY];\n if (!cfg || !bucket) {\n return { status: \"FAIL\", detail: `no ${MCP_ROOT_KEY} in ${mcpPath}` };\n }\n return connectorId in bucket\n ? { status: \"OK\", detail: `${MCP_ROOT_KEY}.${connectorId} present` }\n : {\n status: \"FAIL\",\n detail: `no ${MCP_ROOT_KEY}.${connectorId} in ${mcpPath}`,\n };\n },\n },\n {\n name: `${this.name}: hooks.json hook command registered`,\n check: () => {\n if (hookEvents.length === 0) {\n return { status: \"OK\", detail: \"no hooks declared\" };\n }\n const file = this.readJson<AntigravityHooksFile>(hooksPath);\n if (!file) {\n return {\n status: \"FAIL\",\n detail: `not found: ${hooksPath} (path probed; verify for your Antigravity version)`,\n };\n }\n const hooks = file.hooks ?? {};\n const registered = Object.values(hooks).some((entries) =>\n (entries ?? []).some((e) =>\n (e.hooks ?? []).some((h) =>\n isHomeBinHookCommand(h.command, homeBin, connectorId),\n ),\n ),\n );\n return registered\n ? { status: \"OK\", detail: `hook command present in ${hooksPath}` }\n : { status: \"FAIL\", detail: `no hook for ${connectorId} in ${hooksPath}` };\n },\n },\n ];\n\n // Content-surface checks: only assert presence of the files this connector\n // declares (skip silently for surfaces it never asked for). The probed dirs\n // are noted so doctor output makes the medium-confidence path explicit.\n for (const cmd of ctx.connector.commands) {\n const p = join(workflowsDir, `${cmd.name}.md`);\n checks.push({\n name: `${this.name}: workflow ${cmd.name} present`,\n check: () =>\n existsSync(p)\n ? { status: \"OK\", detail: `${p} (workflows dir probed; verify for your version)` }\n : { status: \"FAIL\", detail: `not found: ${p} (workflows dir probed; verify for your version)` },\n });\n }\n for (const skill of ctx.connector.skills) {\n const p = join(skillsDir, skill.name, \"SKILL.md\");\n checks.push({\n name: `${this.name}: skill ${skill.name} present`,\n check: () =>\n existsSync(p)\n ? { status: \"OK\", detail: `${p} (skills dir probed; verify for your version)` }\n : { status: \"FAIL\", detail: `not found: ${p} (skills dir probed; verify for your version)` },\n });\n }\n return checks;\n }\n\n // ── Runtime: parse Antigravity stdin JSON → normalized event ──────────────\n\n parseEvent(event: HookEventName, raw: unknown): NormalizedEvent {\n const input = (raw ?? {}) as AntigravityWireInput;\n const connectorId = typeof input.connector === \"string\" ? input.connector : \"\";\n const sessionId = typeof input.sessionId === \"string\" ? input.sessionId : \"\";\n const projectDir = typeof input.cwd === \"string\" ? input.cwd : undefined;\n\n const base = {\n hostPlatform: this.id,\n connectorId,\n sessionId,\n raw,\n ...(projectDir !== undefined ? { projectDir } : {}),\n } as const;\n\n switch (event) {\n case \"PreToolUse\": {\n const ev: PreToolUseEvent = {\n ...base,\n toolName: input.toolName ?? \"\",\n toolInput: input.toolInput ?? {},\n };\n return ev;\n }\n case \"PostToolUse\": {\n const ev: PostToolUseEvent = {\n ...base,\n toolName: input.toolName ?? \"\",\n toolInput: input.toolInput ?? {},\n ...(input.toolOutput !== undefined ? { toolOutput: input.toolOutput } : {}),\n ...(input.isError === true ? { isError: true } : {}),\n };\n return ev;\n }\n case \"SessionStart\": {\n const ev: SessionStartEvent = {\n ...base,\n source: normalizeSessionSource(input.source),\n };\n return ev;\n }\n case \"Stop\": {\n const ev: StopEvent = {\n ...base,\n ...(typeof input.stopHookActive === \"boolean\"\n ? { stopHookActive: input.stopHookActive }\n : {}),\n };\n return ev;\n }\n default: {\n // Antigravity only delivers the four SUPPORTED_EVENTS. If the runtime\n // dispatches anything else, surface it loudly rather than mis-parse.\n throw new Error(`unsupported ${this.id} hook event: ${String(event)}`);\n }\n }\n }\n\n // ── Runtime: normalized response → Antigravity native hook reply ──────────\n\n formatReply(event: HookEventName, response: HookResponse): HookReply {\n const decision = response.decision ?? \"allow\";\n\n // deny → Antigravity blocks the action via a top-level `decision:\"deny\"` +\n // reason (the Decide category).\n if (decision === \"deny\") {\n return this.stdout({\n decision: \"deny\",\n reason: response.reason ?? \"Blocked by hook\",\n });\n }\n\n // ask → Antigravity has no native \"ask\"; degrade to deny to stay fail-safe.\n if (decision === \"ask\") {\n return this.stdout({\n decision: \"deny\",\n reason: response.reason ?? \"Action requires user confirmation (security policy)\",\n });\n }\n\n // modify → Transform category. camelCase: rewrite PreToolUse input via\n // updatedInput; rewrite PostToolUse output via updatedOutput.\n if (decision === \"modify\") {\n if (event === \"PreToolUse\" && response.updatedInput) {\n return this.stdout({ updatedInput: response.updatedInput });\n }\n if (event === \"PostToolUse\" && response.updatedOutput !== undefined) {\n return this.stdout({ updatedOutput: response.updatedOutput });\n }\n // Nothing to apply; fall through to allow.\n }\n\n // context → inject soft guidance via additionalContext (the SessionStart\n // context-injection path).\n if (decision === \"context\" && response.additionalContext) {\n return this.stdout({ additionalContext: response.additionalContext });\n }\n\n // allow / void / unsupported-degradation → pass through with exit 0.\n return { exitCode: 0 };\n }\n\n private stdout(payload: unknown): HookReply {\n return { exitCode: 0, stdout: JSON.stringify(payload) };\n }\n}\n\nfunction normalizeSessionSource(source: string | undefined): SessionStartEvent[\"source\"] {\n switch (source) {\n case \"compact\":\n return \"compact\";\n case \"resume\":\n return \"resume\";\n case \"clear\":\n return \"clear\";\n default:\n return \"startup\";\n }\n}\n\nexport const adapter = new AntigravityAdapter();\nexport default adapter;\n"],"mappings":"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","names":["entry"]}
|