@agentbridge1/cli 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (69) hide show
  1. package/bin/agentbridge.js +11 -0
  2. package/dist/acceptance-block.js +21 -0
  3. package/dist/acceptance-preflight.js +91 -0
  4. package/dist/api-client.js +6 -0
  5. package/dist/authority-request.js +25 -0
  6. package/dist/briefing.js +26 -0
  7. package/dist/bug-registry.js +350 -0
  8. package/dist/build-info.json +6 -0
  9. package/dist/canonical-state.js +11 -0
  10. package/dist/claimed-paths.js +42 -0
  11. package/dist/cli-failure-log.js +34 -0
  12. package/dist/commands/accept.js +241 -0
  13. package/dist/commands/attention.js +85 -0
  14. package/dist/commands/autopilot.js +93 -0
  15. package/dist/commands/bug.js +106 -0
  16. package/dist/commands/check.js +283 -0
  17. package/dist/commands/connect.js +159 -0
  18. package/dist/commands/dist-freshness.js +105 -0
  19. package/dist/commands/doctor.js +300 -0
  20. package/dist/commands/done.js +292 -0
  21. package/dist/commands/handoff.js +189 -0
  22. package/dist/commands/handshake.js +78 -0
  23. package/dist/commands/health.js +154 -0
  24. package/dist/commands/identity.js +57 -0
  25. package/dist/commands/init.js +5 -0
  26. package/dist/commands/memory.js +400 -0
  27. package/dist/commands/next.js +21 -0
  28. package/dist/commands/precommit-check.js +17 -0
  29. package/dist/commands/recover.js +116 -0
  30. package/dist/commands/session.js +229 -0
  31. package/dist/commands/setup-mcp.js +56 -0
  32. package/dist/commands/start.js +626 -0
  33. package/dist/commands/status.js +486 -0
  34. package/dist/commands/use.js +13 -0
  35. package/dist/commands/verify.js +264 -0
  36. package/dist/commands/version.js +32 -0
  37. package/dist/commands/watch.js +1718 -0
  38. package/dist/config.js +55 -0
  39. package/dist/domain-resolution.js +63 -0
  40. package/dist/error-catalog.js +494 -0
  41. package/dist/errors.js +276 -0
  42. package/dist/file-fingerprints.js +45 -0
  43. package/dist/gates.js +200 -0
  44. package/dist/git-evidence.js +285 -0
  45. package/dist/git-status.js +81 -0
  46. package/dist/http.js +151 -0
  47. package/dist/index.js +622 -0
  48. package/dist/init.js +458 -0
  49. package/dist/memory-context-render.js +51 -0
  50. package/dist/operator-snapshot.js +99 -0
  51. package/dist/precommit.js +72 -0
  52. package/dist/preflight-changed-files.js +109 -0
  53. package/dist/proof-guidance.js +110 -0
  54. package/dist/redact-secrets.js +15 -0
  55. package/dist/revert-crossing.js +73 -0
  56. package/dist/server-sync.js +433 -0
  57. package/dist/session-state.js +138 -0
  58. package/dist/session.js +89 -0
  59. package/dist/supervision.js +212 -0
  60. package/dist/terminal-ui.js +18 -0
  61. package/dist/test-runner.js +62 -0
  62. package/dist/types.js +2 -0
  63. package/dist/verification-conditions.js +185 -0
  64. package/dist/watch-core.js +208 -0
  65. package/dist/watch-packet-handshake.js +71 -0
  66. package/dist/watcher.js +62 -0
  67. package/dist/work-context-resolver.js +412 -0
  68. package/dist/work-contract.js +110 -0
  69. package/package.json +44 -0
@@ -0,0 +1,285 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.classifyEvidencePath = classifyEvidencePath;
4
+ exports.compactEvidencePayload = compactEvidencePayload;
5
+ exports.collectGitEvidenceFromLog = collectGitEvidenceFromLog;
6
+ exports.collectGitEvidence = collectGitEvidence;
7
+ const node_child_process_1 = require("node:child_process");
8
+ const PATH_LENGTH_LIMIT = 240;
9
+ const DEFAULT_MAX_COMMITS = 50;
10
+ const DEFAULT_MAX_PAYLOAD_BYTES = 750 * 1024;
11
+ const BASE_CAPS = [
12
+ { files: 500, coChange: 50, churn: 100 },
13
+ { files: 300, coChange: 25, churn: 50 },
14
+ { files: 150, coChange: 10, churn: 25 },
15
+ ];
16
+ const PREFERRED_PREFIXES = [
17
+ "src/",
18
+ "app/",
19
+ "pages/",
20
+ "server/",
21
+ "api/",
22
+ "routes/",
23
+ "db/",
24
+ "prisma/",
25
+ "supabase/",
26
+ "lib/",
27
+ "services/",
28
+ "modules/",
29
+ "components/",
30
+ ];
31
+ const PREFERRED_EXACT_FILES = new Set([
32
+ "package.json",
33
+ "tsconfig.json",
34
+ "netlify.toml",
35
+ "vercel.json",
36
+ "railway.json",
37
+ "render.yaml",
38
+ "Dockerfile",
39
+ "README.md",
40
+ "CLAUDE.md",
41
+ ]);
42
+ const NOISY_PREFIXES = [
43
+ "node_modules/",
44
+ ".git/",
45
+ ".agentbridge/",
46
+ ".cursor/",
47
+ ".agents/",
48
+ "dist/",
49
+ "build/",
50
+ ".next/",
51
+ "coverage/",
52
+ ".turbo/",
53
+ ".cache/",
54
+ "DerivedData/",
55
+ "logs/",
56
+ "log/",
57
+ "tmp/",
58
+ "temp/",
59
+ ".tmp/",
60
+ ];
61
+ const NOISY_SEGMENTS = [
62
+ "/node_modules/",
63
+ "/.git/",
64
+ "/.agentbridge/",
65
+ "/.cursor/",
66
+ "/.agents/",
67
+ "/dist/",
68
+ "/build/",
69
+ "/.next/",
70
+ "/coverage/",
71
+ "/.turbo/",
72
+ "/.cache/",
73
+ "/DerivedData/",
74
+ "/logs/",
75
+ "/log/",
76
+ "/tmp/",
77
+ "/temp/",
78
+ "/.tmp/",
79
+ ];
80
+ const NOISY_LOCKFILES = new Set(["package-lock.json", "pnpm-lock.yaml", "yarn.lock"]);
81
+ const BINARY_MEDIA_EXTENSIONS = [
82
+ ".png",
83
+ ".jpg",
84
+ ".jpeg",
85
+ ".gif",
86
+ ".webp",
87
+ ".mp4",
88
+ ".mov",
89
+ ".pdf",
90
+ ".zip",
91
+ ".sqlite",
92
+ ".db",
93
+ ];
94
+ function runGit(args) {
95
+ return (0, node_child_process_1.execFileSync)("git", args, {
96
+ encoding: "utf8",
97
+ stdio: ["ignore", "pipe", "pipe"],
98
+ });
99
+ }
100
+ function normalizePath(path) {
101
+ return path.replace(/\\/g, "/").replace(/^\.\/+/, "").trim();
102
+ }
103
+ function increment(map, key) {
104
+ map.set(key, (map.get(key) ?? 0) + 1);
105
+ }
106
+ function isNoisyDirectory(path) {
107
+ return (NOISY_PREFIXES.some((prefix) => path.startsWith(prefix)) ||
108
+ NOISY_SEGMENTS.some((segment) => path.includes(segment)));
109
+ }
110
+ function isNoisyXcodePath(path) {
111
+ return (path.includes(".xcworkspace/") ||
112
+ path.includes(".xcodeproj/project.xcworkspace/") ||
113
+ path.endsWith(".xcuserstate") ||
114
+ path.endsWith(".pbxuser") ||
115
+ path.endsWith(".mode1v3") ||
116
+ path.endsWith(".mode2v3") ||
117
+ path.endsWith(".perspectivev3"));
118
+ }
119
+ function isNoisyBinaryMedia(path) {
120
+ const lower = path.toLowerCase();
121
+ return BINARY_MEDIA_EXTENSIONS.some((ext) => lower.endsWith(ext));
122
+ }
123
+ function classifyEvidencePath(pathRaw) {
124
+ const path = normalizePath(pathRaw);
125
+ if (!path) {
126
+ return { include: false, reason: "empty_path" };
127
+ }
128
+ if (path.length > PATH_LENGTH_LIMIT) {
129
+ return { include: false, reason: "path_too_long" };
130
+ }
131
+ if (isNoisyDirectory(path)) {
132
+ return { include: false, reason: "noisy_directory" };
133
+ }
134
+ if (isNoisyXcodePath(path)) {
135
+ return { include: false, reason: "xcode_workspace_noise" };
136
+ }
137
+ if (NOISY_LOCKFILES.has(path)) {
138
+ return { include: false, reason: "lockfile_noise" };
139
+ }
140
+ if (isNoisyBinaryMedia(path)) {
141
+ return { include: false, reason: "binary_or_media" };
142
+ }
143
+ return { include: true };
144
+ }
145
+ function pathScore(path) {
146
+ if (PREFERRED_EXACT_FILES.has(path))
147
+ return 1_000;
148
+ const preferredIndex = PREFERRED_PREFIXES.findIndex((prefix) => path.startsWith(prefix));
149
+ if (preferredIndex >= 0)
150
+ return 500 - preferredIndex * 10;
151
+ return 10;
152
+ }
153
+ function sortPathsByPriority(paths) {
154
+ return Array.from(paths).sort((a, b) => {
155
+ const scoreDiff = pathScore(b) - pathScore(a);
156
+ if (scoreDiff !== 0)
157
+ return scoreDiff;
158
+ return a.localeCompare(b);
159
+ });
160
+ }
161
+ function sortChurn(entries) {
162
+ return Array.from(entries).sort((a, b) => {
163
+ const commitDiff = b.commits - a.commits;
164
+ if (commitDiff !== 0)
165
+ return commitDiff;
166
+ const scoreDiff = pathScore(b.path) - pathScore(a.path);
167
+ if (scoreDiff !== 0)
168
+ return scoreDiff;
169
+ return a.path.localeCompare(b.path);
170
+ });
171
+ }
172
+ function sortCoChange(entries) {
173
+ return Array.from(entries).sort((a, b) => {
174
+ const weightDiff = (b.weight ?? 0) - (a.weight ?? 0);
175
+ if (weightDiff !== 0)
176
+ return weightDiff;
177
+ const leftDiff = a.left.localeCompare(b.left);
178
+ if (leftDiff !== 0)
179
+ return leftDiff;
180
+ return a.right.localeCompare(b.right);
181
+ });
182
+ }
183
+ function compactEvidencePayload(raw, skippedByReason, commitCount, maxPayloadBytes = DEFAULT_MAX_PAYLOAD_BYTES) {
184
+ const rawFileSet = new Set(raw.files.map((path) => normalizePath(path)));
185
+ const sortedRawFiles = sortPathsByPriority(rawFileSet);
186
+ const sortedRawChurn = sortChurn(raw.churn.map((entry) => ({ ...entry, path: normalizePath(entry.path) })));
187
+ const sortedRawCoChange = sortCoChange(raw.coChange.map((entry) => ({
188
+ left: normalizePath(entry.left),
189
+ right: normalizePath(entry.right),
190
+ weight: entry.weight ?? 0,
191
+ })));
192
+ for (let stageIndex = 0; stageIndex < BASE_CAPS.length; stageIndex += 1) {
193
+ const stage = BASE_CAPS[stageIndex];
194
+ const selectedFiles = sortedRawFiles.slice(0, stage.files);
195
+ const selectedFileSet = new Set(selectedFiles);
196
+ const churn = sortedRawChurn
197
+ .filter((entry) => selectedFileSet.has(entry.path))
198
+ .slice(0, stage.churn);
199
+ const coChange = sortedRawCoChange
200
+ .filter((entry) => selectedFileSet.has(entry.left) && selectedFileSet.has(entry.right))
201
+ .slice(0, stage.coChange);
202
+ const payload = {
203
+ files: selectedFiles,
204
+ coChange,
205
+ churn,
206
+ };
207
+ const payloadBytes = Buffer.byteLength(JSON.stringify(payload), "utf8");
208
+ if (payloadBytes <= maxPayloadBytes) {
209
+ return {
210
+ payload,
211
+ report: {
212
+ commitCount,
213
+ beforeCounts: {
214
+ files: sortedRawFiles.length,
215
+ coChange: sortedRawCoChange.length,
216
+ churn: sortedRawChurn.length,
217
+ },
218
+ afterCounts: {
219
+ files: payload.files.length,
220
+ coChange: payload.coChange.length,
221
+ churn: payload.churn.length,
222
+ },
223
+ payloadBytes,
224
+ skippedByReason,
225
+ compactionStage: (stageIndex + 1),
226
+ },
227
+ };
228
+ }
229
+ }
230
+ throw new Error(`Recovery evidence payload too large after compaction (budget=${maxPayloadBytes} bytes).`);
231
+ }
232
+ function collectGitEvidenceFromLog(log) {
233
+ const filesSet = new Set();
234
+ const churn = new Map();
235
+ const pairWeight = new Map();
236
+ const skippedByReason = new Map();
237
+ let commitCount = 0;
238
+ const chunks = log.split("__COMMIT__").map((s) => s.trim()).filter(Boolean);
239
+ for (const chunk of chunks) {
240
+ commitCount += 1;
241
+ const files = chunk
242
+ .split("\n")
243
+ .map((line) => normalizePath(line))
244
+ .filter((line) => line.length > 0);
245
+ const commitFiles = new Set();
246
+ for (const file of files) {
247
+ const classification = classifyEvidencePath(file);
248
+ if (!classification.include) {
249
+ increment(skippedByReason, classification.reason ?? "filtered");
250
+ continue;
251
+ }
252
+ commitFiles.add(file);
253
+ }
254
+ const prioritized = sortPathsByPriority(commitFiles).slice(0, 30);
255
+ if (commitFiles.size > prioritized.length) {
256
+ increment(skippedByReason, "commit_file_cap");
257
+ }
258
+ const unique = Array.from(new Set(prioritized));
259
+ for (const file of unique) {
260
+ filesSet.add(file);
261
+ churn.set(file, (churn.get(file) ?? 0) + 1);
262
+ }
263
+ for (let i = 0; i < unique.length; i += 1) {
264
+ for (let j = i + 1; j < unique.length; j += 1) {
265
+ const left = unique[i];
266
+ const right = unique[j];
267
+ const key = left < right ? `${left}::${right}` : `${right}::${left}`;
268
+ pairWeight.set(key, (pairWeight.get(key) ?? 0) + 1);
269
+ }
270
+ }
271
+ }
272
+ const coChange = Array.from(pairWeight.entries()).map(([key, weight]) => {
273
+ const [left, right] = key.split("::");
274
+ return { left: left, right: right, weight };
275
+ });
276
+ return compactEvidencePayload({
277
+ files: Array.from(filesSet),
278
+ coChange,
279
+ churn: Array.from(churn.entries()).map(([path, commits]) => ({ path, commits })),
280
+ }, Object.fromEntries(Array.from(skippedByReason.entries()).sort((a, b) => b[1] - a[1])), commitCount);
281
+ }
282
+ function collectGitEvidence(maxCommits = DEFAULT_MAX_COMMITS) {
283
+ const log = runGit(["log", `-${maxCommits}`, "--name-only", "--pretty=format:__COMMIT__"]);
284
+ return collectGitEvidenceFromLog(log);
285
+ }
@@ -0,0 +1,81 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.isAgentbridgeLocalPath = isAgentbridgeLocalPath;
4
+ exports.parseGitStatusPorcelainZ = parseGitStatusPorcelainZ;
5
+ exports.collectChangedFilesFromStatusEntries = collectChangedFilesFromStatusEntries;
6
+ exports.repoDirtySnapshotFromGitStatus = repoDirtySnapshotFromGitStatus;
7
+ exports.getBranchName = getBranchName;
8
+ exports.getDirtyWorkingTreeFiles = getDirtyWorkingTreeFiles;
9
+ const node_child_process_1 = require("node:child_process");
10
+ function normalizePath(path) {
11
+ return path.trim().replaceAll("\\", "/");
12
+ }
13
+ function isAgentbridgeLocalPath(path) {
14
+ const normalized = normalizePath(path);
15
+ return normalized === ".agentbridge" || normalized.startsWith(".agentbridge/");
16
+ }
17
+ function isRenameOrCopy(xy) {
18
+ return xy.includes("R") || xy.includes("C");
19
+ }
20
+ function parseGitStatusPorcelainZ(raw) {
21
+ const tokens = raw.split("\0");
22
+ const entries = [];
23
+ for (let idx = 0; idx < tokens.length; idx += 1) {
24
+ const token = tokens[idx];
25
+ if (!token || token.length < 4)
26
+ continue;
27
+ const xy = token.slice(0, 2);
28
+ const payload = token.slice(3);
29
+ if (!payload)
30
+ continue;
31
+ if (isRenameOrCopy(xy)) {
32
+ const originalPath = normalizePath(payload);
33
+ const renamedTo = normalizePath(tokens[idx + 1] ?? "");
34
+ if (renamedTo.length > 0) {
35
+ entries.push({ xy, path: renamedTo, originalPath });
36
+ }
37
+ idx += 1;
38
+ continue;
39
+ }
40
+ entries.push({ xy, path: normalizePath(payload) });
41
+ }
42
+ return entries;
43
+ }
44
+ function collectChangedFilesFromStatusEntries(entries) {
45
+ const files = new Set();
46
+ for (const entry of entries) {
47
+ if (entry.path && !isAgentbridgeLocalPath(entry.path)) {
48
+ files.add(entry.path);
49
+ }
50
+ if (entry.originalPath && !isAgentbridgeLocalPath(entry.originalPath)) {
51
+ files.add(entry.originalPath);
52
+ }
53
+ }
54
+ return [...files].sort();
55
+ }
56
+ function repoDirtySnapshotFromGitStatus() {
57
+ try {
58
+ const raw = (0, node_child_process_1.execSync)("git status --porcelain=v1 -z --untracked-files=all", {
59
+ encoding: "utf8",
60
+ stdio: ["ignore", "pipe", "ignore"],
61
+ });
62
+ return collectChangedFilesFromStatusEntries(parseGitStatusPorcelainZ(raw));
63
+ }
64
+ catch {
65
+ return [];
66
+ }
67
+ }
68
+ function getBranchName() {
69
+ try {
70
+ return (0, node_child_process_1.execSync)("git rev-parse --abbrev-ref HEAD", {
71
+ encoding: "utf8",
72
+ stdio: ["ignore", "pipe", "ignore"],
73
+ }).trim();
74
+ }
75
+ catch {
76
+ return "unknown";
77
+ }
78
+ }
79
+ function getDirtyWorkingTreeFiles() {
80
+ return repoDirtySnapshotFromGitStatus();
81
+ }
package/dist/http.js ADDED
@@ -0,0 +1,151 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.CliMissingConfigError = exports.CliHttpError = void 0;
4
+ exports.isCliHttpError = isCliHttpError;
5
+ exports.parseCliHttpErrorBody = parseCliHttpErrorBody;
6
+ exports.postJson = postJson;
7
+ exports.putJson = putJson;
8
+ exports.getJson = getJson;
9
+ class CliHttpError extends Error {
10
+ status;
11
+ body;
12
+ constructor(message, status, body) {
13
+ super(message);
14
+ this.status = status;
15
+ this.body = body;
16
+ this.name = "CliHttpError";
17
+ }
18
+ }
19
+ exports.CliHttpError = CliHttpError;
20
+ function isCliHttpError(err) {
21
+ return err instanceof CliHttpError;
22
+ }
23
+ /** Safely parse the JSON body of a CliHttpError. Returns null on parse failure. */
24
+ function parseCliHttpErrorBody(err) {
25
+ try {
26
+ const parsed = JSON.parse(err.body);
27
+ if (parsed !== null && typeof parsed === "object" && !Array.isArray(parsed)) {
28
+ return parsed;
29
+ }
30
+ }
31
+ catch {
32
+ // non-JSON body
33
+ }
34
+ return null;
35
+ }
36
+ class CliMissingConfigError extends Error {
37
+ code = "CONFIG_INCOMPLETE";
38
+ constructor(message) {
39
+ super(message);
40
+ this.name = "CliMissingConfigError";
41
+ }
42
+ }
43
+ exports.CliMissingConfigError = CliMissingConfigError;
44
+ function requireApiKey(ctx) {
45
+ if (!ctx.apiKey || ctx.apiKey.trim().length === 0) {
46
+ throw new CliMissingConfigError("Missing AGENTBRIDGE_API_KEY. Provide --api-key, set AGENTBRIDGE_API_KEY, or save apiKey in .agentbridge/config.json.");
47
+ }
48
+ }
49
+ async function postJson(ctx, path, body) {
50
+ requireApiKey(ctx);
51
+ const url = new URL(path, ctx.apiBaseUrl).toString();
52
+ const debugHttp = process.env.AGENTBRIDGE_DEBUG_HTTP === "1";
53
+ if (debugHttp) {
54
+ process.stderr.write(`[agentbridge:http] request POST ${url}\n`);
55
+ }
56
+ const res = await fetch(url, {
57
+ method: "POST",
58
+ headers: {
59
+ "Content-Type": "application/json",
60
+ Authorization: `Bearer ${ctx.apiKey}`,
61
+ "x-api-key": ctx.apiKey,
62
+ },
63
+ body: JSON.stringify(body),
64
+ });
65
+ const text = await res.text();
66
+ if (!res.ok) {
67
+ if (debugHttp) {
68
+ const headers = Object.fromEntries(res.headers.entries());
69
+ process.stderr.write(`[agentbridge:http] response status ${res.status}\n`);
70
+ process.stderr.write(`[agentbridge:http] response headers ${JSON.stringify(headers)}\n`);
71
+ process.stderr.write(`[agentbridge:http] response body ${text || "<empty>"}\n`);
72
+ }
73
+ throw new CliHttpError(`POST ${path} failed (${res.status}): ${text || "<empty>"}`, res.status, text);
74
+ }
75
+ if (debugHttp) {
76
+ process.stderr.write(`[agentbridge:http] response status ${res.status}\n`);
77
+ process.stderr.write(`[agentbridge:http] response body ${text || "<empty>"}\n`);
78
+ }
79
+ if (!text) {
80
+ return {};
81
+ }
82
+ return JSON.parse(text);
83
+ }
84
+ async function putJson(ctx, path, body) {
85
+ requireApiKey(ctx);
86
+ const url = new URL(path, ctx.apiBaseUrl).toString();
87
+ const debugHttp = process.env.AGENTBRIDGE_DEBUG_HTTP === "1";
88
+ if (debugHttp) {
89
+ process.stderr.write(`[agentbridge:http] request PUT ${url}\n`);
90
+ }
91
+ const res = await fetch(url, {
92
+ method: "PUT",
93
+ headers: {
94
+ "Content-Type": "application/json",
95
+ Authorization: `Bearer ${ctx.apiKey}`,
96
+ "x-api-key": ctx.apiKey,
97
+ },
98
+ body: JSON.stringify(body),
99
+ });
100
+ const text = await res.text();
101
+ if (!res.ok) {
102
+ if (debugHttp) {
103
+ const headers = Object.fromEntries(res.headers.entries());
104
+ process.stderr.write(`[agentbridge:http] response status ${res.status}\n`);
105
+ process.stderr.write(`[agentbridge:http] response headers ${JSON.stringify(headers)}\n`);
106
+ process.stderr.write(`[agentbridge:http] response body ${text || "<empty>"}\n`);
107
+ }
108
+ throw new CliHttpError(`PUT ${path} failed (${res.status}): ${text || "<empty>"}`, res.status, text);
109
+ }
110
+ if (debugHttp) {
111
+ process.stderr.write(`[agentbridge:http] response status ${res.status}\n`);
112
+ process.stderr.write(`[agentbridge:http] response body ${text || "<empty>"}\n`);
113
+ }
114
+ if (!text) {
115
+ return {};
116
+ }
117
+ return JSON.parse(text);
118
+ }
119
+ async function getJson(ctx, path) {
120
+ requireApiKey(ctx);
121
+ const url = new URL(path, ctx.apiBaseUrl).toString();
122
+ const debugHttp = process.env.AGENTBRIDGE_DEBUG_HTTP === "1";
123
+ if (debugHttp) {
124
+ process.stderr.write(`[agentbridge:http] request GET ${url}\n`);
125
+ }
126
+ const res = await fetch(url, {
127
+ method: "GET",
128
+ headers: {
129
+ Authorization: `Bearer ${ctx.apiKey}`,
130
+ "x-api-key": ctx.apiKey,
131
+ },
132
+ });
133
+ const text = await res.text();
134
+ if (!res.ok) {
135
+ if (debugHttp) {
136
+ const headers = Object.fromEntries(res.headers.entries());
137
+ process.stderr.write(`[agentbridge:http] response status ${res.status}\n`);
138
+ process.stderr.write(`[agentbridge:http] response headers ${JSON.stringify(headers)}\n`);
139
+ process.stderr.write(`[agentbridge:http] response body ${text || "<empty>"}\n`);
140
+ }
141
+ throw new CliHttpError(`GET ${path} failed (${res.status}): ${text || "<empty>"}`, res.status, text);
142
+ }
143
+ if (debugHttp) {
144
+ process.stderr.write(`[agentbridge:http] response status ${res.status}\n`);
145
+ process.stderr.write(`[agentbridge:http] response body ${text || "<empty>"}\n`);
146
+ }
147
+ if (!text) {
148
+ return {};
149
+ }
150
+ return JSON.parse(text);
151
+ }