@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.
- package/bin/agentbridge.js +11 -0
- package/dist/acceptance-block.js +21 -0
- package/dist/acceptance-preflight.js +91 -0
- package/dist/api-client.js +6 -0
- package/dist/authority-request.js +25 -0
- package/dist/briefing.js +26 -0
- package/dist/bug-registry.js +350 -0
- package/dist/build-info.json +6 -0
- package/dist/canonical-state.js +11 -0
- package/dist/claimed-paths.js +42 -0
- package/dist/cli-failure-log.js +34 -0
- package/dist/commands/accept.js +241 -0
- package/dist/commands/attention.js +85 -0
- package/dist/commands/autopilot.js +93 -0
- package/dist/commands/bug.js +106 -0
- package/dist/commands/check.js +283 -0
- package/dist/commands/connect.js +159 -0
- package/dist/commands/dist-freshness.js +105 -0
- package/dist/commands/doctor.js +300 -0
- package/dist/commands/done.js +292 -0
- package/dist/commands/handoff.js +189 -0
- package/dist/commands/handshake.js +78 -0
- package/dist/commands/health.js +154 -0
- package/dist/commands/identity.js +57 -0
- package/dist/commands/init.js +5 -0
- package/dist/commands/memory.js +400 -0
- package/dist/commands/next.js +21 -0
- package/dist/commands/precommit-check.js +17 -0
- package/dist/commands/recover.js +116 -0
- package/dist/commands/session.js +229 -0
- package/dist/commands/setup-mcp.js +56 -0
- package/dist/commands/start.js +626 -0
- package/dist/commands/status.js +486 -0
- package/dist/commands/use.js +13 -0
- package/dist/commands/verify.js +264 -0
- package/dist/commands/version.js +32 -0
- package/dist/commands/watch.js +1718 -0
- package/dist/config.js +55 -0
- package/dist/domain-resolution.js +63 -0
- package/dist/error-catalog.js +494 -0
- package/dist/errors.js +276 -0
- package/dist/file-fingerprints.js +45 -0
- package/dist/gates.js +200 -0
- package/dist/git-evidence.js +285 -0
- package/dist/git-status.js +81 -0
- package/dist/http.js +151 -0
- package/dist/index.js +622 -0
- package/dist/init.js +458 -0
- package/dist/memory-context-render.js +51 -0
- package/dist/operator-snapshot.js +99 -0
- package/dist/precommit.js +72 -0
- package/dist/preflight-changed-files.js +109 -0
- package/dist/proof-guidance.js +110 -0
- package/dist/redact-secrets.js +15 -0
- package/dist/revert-crossing.js +73 -0
- package/dist/server-sync.js +433 -0
- package/dist/session-state.js +138 -0
- package/dist/session.js +89 -0
- package/dist/supervision.js +212 -0
- package/dist/terminal-ui.js +18 -0
- package/dist/test-runner.js +62 -0
- package/dist/types.js +2 -0
- package/dist/verification-conditions.js +185 -0
- package/dist/watch-core.js +208 -0
- package/dist/watch-packet-handshake.js +71 -0
- package/dist/watcher.js +62 -0
- package/dist/work-context-resolver.js +412 -0
- package/dist/work-contract.js +110 -0
- package/package.json +44 -0
|
@@ -0,0 +1,283 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.buildStaleProofPrompt = buildStaleProofPrompt;
|
|
4
|
+
exports.renderAcceptanceReport = renderAcceptanceReport;
|
|
5
|
+
exports.runCheck = runCheck;
|
|
6
|
+
const config_1 = require("../config");
|
|
7
|
+
const errors_1 = require("../errors");
|
|
8
|
+
const acceptance_preflight_1 = require("../acceptance-preflight");
|
|
9
|
+
const work_context_resolver_1 = require("../work-context-resolver");
|
|
10
|
+
const operator_snapshot_1 = require("../operator-snapshot");
|
|
11
|
+
const proof_guidance_1 = require("../proof-guidance");
|
|
12
|
+
const memory_context_render_1 = require("../memory-context-render");
|
|
13
|
+
const acceptance_block_1 = require("../acceptance-block");
|
|
14
|
+
function resolveNetworkContext() {
|
|
15
|
+
const cfg = (0, config_1.readConfig)();
|
|
16
|
+
const projectId = process.env.AGENTBRIDGE_PROJECT_ID ?? cfg.projectId;
|
|
17
|
+
const apiKey = process.env.AGENTBRIDGE_API_KEY ?? cfg.apiKey ?? "";
|
|
18
|
+
const apiBaseUrl = process.env.AGENTBRIDGE_BASE_URL ?? cfg.apiBaseUrl ?? "https://agentauth-api-production.up.railway.app";
|
|
19
|
+
if (!projectId) {
|
|
20
|
+
throw new errors_1.SafeCliError("Missing AGENTBRIDGE_PROJECT_ID. Set AGENTBRIDGE_PROJECT_ID (or run `agentbridge init --project ...`).");
|
|
21
|
+
}
|
|
22
|
+
if (!apiKey) {
|
|
23
|
+
throw new errors_1.SafeCliError("Missing AGENTBRIDGE_API_KEY. Provide --api-key, set AGENTBRIDGE_API_KEY, or save apiKey in .agentbridge/config.json.");
|
|
24
|
+
}
|
|
25
|
+
return { projectId, apiKey, apiBaseUrl };
|
|
26
|
+
}
|
|
27
|
+
function mark(proof, presentSet) {
|
|
28
|
+
return `${presentSet.has(proof) ? "[present]" : "[missing]"} ${proof}`;
|
|
29
|
+
}
|
|
30
|
+
function renderNoActiveSessionGuidance(input) {
|
|
31
|
+
const lines = [
|
|
32
|
+
"No active tracked work session found.",
|
|
33
|
+
`Existing project: ${input.projectId ? "yes" : "no"}`,
|
|
34
|
+
"Next action:",
|
|
35
|
+
"- run agentbridge watch to start/resume work",
|
|
36
|
+
"- or resolve work context/start a scoped session",
|
|
37
|
+
];
|
|
38
|
+
if (input.lastAcceptedSessionId) {
|
|
39
|
+
const suffix = input.lastAcceptedAt ? ` (${input.lastAcceptedAt})` : "";
|
|
40
|
+
lines.push(`Last accepted session: ${input.lastAcceptedSessionId}${suffix}`);
|
|
41
|
+
lines.push("Next: agentbridge watch");
|
|
42
|
+
}
|
|
43
|
+
return lines.join("\n");
|
|
44
|
+
}
|
|
45
|
+
function buildStaleProofPrompt(report) {
|
|
46
|
+
const staleFiles = report.stale_files ?? [];
|
|
47
|
+
const files = staleFiles.length > 0
|
|
48
|
+
? staleFiles.join(", ")
|
|
49
|
+
: report.evidence_missing.length > 0
|
|
50
|
+
? report.evidence_missing.join(", ")
|
|
51
|
+
: report.changed_files.join(", ") || "changed files";
|
|
52
|
+
return [
|
|
53
|
+
"Verification is stale — files changed after the last passing proof.",
|
|
54
|
+
`Affected: ${files}`,
|
|
55
|
+
"Run `agentbridge verify --command \"<your test command>\"` to refresh proof before handoff or accept.",
|
|
56
|
+
].join("\n");
|
|
57
|
+
}
|
|
58
|
+
function renderAcceptanceReport(report) {
|
|
59
|
+
const lines = [];
|
|
60
|
+
lines.push(`Current work: ${report.work_session_id}${report.change_request_id ? ` (CR ${report.change_request_id})` : ""}`);
|
|
61
|
+
lines.push(`Detected work type: ${report.detected_work_type}`);
|
|
62
|
+
lines.push(`Changed files: ${report.changed_files.length}`);
|
|
63
|
+
if (report.changed_files.length > 0) {
|
|
64
|
+
lines.push(`Changed file list: ${report.changed_files.join(", ")}`);
|
|
65
|
+
}
|
|
66
|
+
lines.push(`Scope status: ${report.scope_status}`);
|
|
67
|
+
lines.push(`Boundary status: ${report.boundary_status}`);
|
|
68
|
+
lines.push("Required proof:");
|
|
69
|
+
const presentSet = new Set(report.evidence_present);
|
|
70
|
+
for (const proof of report.required_proof) {
|
|
71
|
+
lines.push(` - ${mark(proof, presentSet)}`);
|
|
72
|
+
}
|
|
73
|
+
lines.push(`Known traps: ${report.known_traps.length > 0 ? report.known_traps.join(" | ") : "none"}`);
|
|
74
|
+
if (report.memory_overlay) {
|
|
75
|
+
const overlay = report.memory_overlay;
|
|
76
|
+
if (!overlay.enabled) {
|
|
77
|
+
const reason = overlay.disabled_reason ?? "unknown";
|
|
78
|
+
lines.push(`Memory overlay: disabled (${reason})`);
|
|
79
|
+
}
|
|
80
|
+
else if (overlay.warnings.length === 0) {
|
|
81
|
+
const domains = overlay.source_domains.length > 0 ? overlay.source_domains.join(", ") : "none";
|
|
82
|
+
lines.push(`Memory overlay: clean (domains: ${domains})`);
|
|
83
|
+
}
|
|
84
|
+
else {
|
|
85
|
+
lines.push("Memory overlay (memory-derived, advisory):");
|
|
86
|
+
for (const warning of overlay.warnings) {
|
|
87
|
+
lines.push(` - [${warning.code}] ${warning.domain}: ${warning.message}`);
|
|
88
|
+
if (warning.excerpt) {
|
|
89
|
+
lines.push(` ${warning.excerpt}`);
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
if (overlay.disclaimer) {
|
|
94
|
+
lines.push(overlay.disclaimer);
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
const memoryContextLines = (0, memory_context_render_1.renderMemoryContextLines)(report.memory_context);
|
|
98
|
+
if (memoryContextLines.length > 0) {
|
|
99
|
+
lines.push("");
|
|
100
|
+
lines.push(...memoryContextLines);
|
|
101
|
+
}
|
|
102
|
+
lines.push("Evidence:");
|
|
103
|
+
if (report.verification_runs.length === 0) {
|
|
104
|
+
lines.push(" - none recorded");
|
|
105
|
+
}
|
|
106
|
+
else {
|
|
107
|
+
const freshRuns = report.verification_runs.filter((run) => !run.stale);
|
|
108
|
+
const staleRuns = report.verification_runs.filter((run) => run.stale);
|
|
109
|
+
lines.push(" Fresh:");
|
|
110
|
+
if (freshRuns.length === 0) {
|
|
111
|
+
lines.push(" - none");
|
|
112
|
+
}
|
|
113
|
+
else {
|
|
114
|
+
for (const run of freshRuns) {
|
|
115
|
+
lines.push(` - [${run.status}] ${run.command}`);
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
lines.push(" Stale:");
|
|
119
|
+
if (staleRuns.length === 0) {
|
|
120
|
+
lines.push(" - none");
|
|
121
|
+
}
|
|
122
|
+
else {
|
|
123
|
+
for (const run of staleRuns) {
|
|
124
|
+
lines.push(` - [${run.status}] ${run.command} (stale)`);
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
lines.push("Missing proof:");
|
|
129
|
+
const missingProofDetails = (0, proof_guidance_1.getMissingProofDetails)(report);
|
|
130
|
+
if (missingProofDetails.length === 0) {
|
|
131
|
+
lines.push(" - none");
|
|
132
|
+
}
|
|
133
|
+
else {
|
|
134
|
+
for (const detail of missingProofDetails) {
|
|
135
|
+
lines.push(` - ${detail.message}`);
|
|
136
|
+
lines.push(` why: ${detail.why}`);
|
|
137
|
+
lines.push(` source/rule: ${detail.source_rule}`);
|
|
138
|
+
lines.push(` recipe: ${detail.proof_recipe}`);
|
|
139
|
+
if (detail.suggested_verify) {
|
|
140
|
+
lines.push(` suggested verify: ${detail.suggested_verify}`);
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
lines.push("Suggested verify commands:");
|
|
145
|
+
const suggested = [
|
|
146
|
+
...missingProofDetails
|
|
147
|
+
.map((detail) => detail.suggested_verify?.trim())
|
|
148
|
+
.filter((value) => Boolean(value && value.length > 0)),
|
|
149
|
+
...(0, proof_guidance_1.suggestVerifyCommands)(report.changed_files, report.detected_work_type),
|
|
150
|
+
];
|
|
151
|
+
for (const command of [...new Set(suggested)]) {
|
|
152
|
+
lines.push(` - ${command}`);
|
|
153
|
+
}
|
|
154
|
+
lines.push("Risks remaining:");
|
|
155
|
+
if (report.risks_remaining.length === 0) {
|
|
156
|
+
lines.push(" - none detected");
|
|
157
|
+
}
|
|
158
|
+
else {
|
|
159
|
+
for (const risk of report.risks_remaining) {
|
|
160
|
+
lines.push(` - ${risk}`);
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
lines.push("Protocol warnings:");
|
|
164
|
+
if (!report.protocol_warnings || report.protocol_warnings.length === 0) {
|
|
165
|
+
lines.push(" - none");
|
|
166
|
+
}
|
|
167
|
+
else {
|
|
168
|
+
for (const warning of report.protocol_warnings) {
|
|
169
|
+
lines.push(` - ${warning.code}: ${warning.message}`);
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
if (report.proof_quality) {
|
|
173
|
+
lines.push(`Proof quality confidence: ${report.proof_quality.confidence_level}`);
|
|
174
|
+
if (report.proof_quality.missing_robustness.length > 0) {
|
|
175
|
+
lines.push("Missing robustness:");
|
|
176
|
+
for (const item of report.proof_quality.missing_robustness) {
|
|
177
|
+
lines.push(` - ${item}`);
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
if (report.proof_quality.warnings.length > 0) {
|
|
181
|
+
lines.push("Proof quality warnings:");
|
|
182
|
+
for (const warning of report.proof_quality.warnings) {
|
|
183
|
+
lines.push(` - ${warning.code}: ${warning.message}`);
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
lines.push(`Decision: ${report.decision}`);
|
|
188
|
+
if (report.decision === "needs_proof") {
|
|
189
|
+
lines.push("✗ Error code: PROOF_MISSING");
|
|
190
|
+
lines.push(" What happened: No verification proof exists for this work session.");
|
|
191
|
+
lines.push(" Why it matters: Unverified work cannot be accepted — proof is required.");
|
|
192
|
+
lines.push(" Next action: Run `agentbridge verify -- <your test command>`");
|
|
193
|
+
}
|
|
194
|
+
if (report.decision === "stale_evidence") {
|
|
195
|
+
lines.push("✗ Error code: PROOF_STALE_AFTER_CHANGE");
|
|
196
|
+
lines.push(" What happened: Proof existed but files changed after verification.");
|
|
197
|
+
lines.push(" Why it matters: Stale proof does not cover recent changes — re-verification required.");
|
|
198
|
+
const files = (report.stale_files ?? []).length > 0
|
|
199
|
+
? (report.stale_files ?? []).join(", ")
|
|
200
|
+
: report.evidence_missing.length > 0
|
|
201
|
+
? report.evidence_missing.join(", ")
|
|
202
|
+
: report.changed_files.join(", ") || "changed files";
|
|
203
|
+
lines.push(` Affected files: ${files}`);
|
|
204
|
+
lines.push(" Next action: Run `agentbridge verify -- <your test command>` to refresh proof.");
|
|
205
|
+
if (report.evidence_missing.length > 0) {
|
|
206
|
+
lines.push(" Files needing re-verification:");
|
|
207
|
+
for (const file of report.evidence_missing) {
|
|
208
|
+
lines.push(` - ${file}`);
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
lines.push("Copy this back to the agent:");
|
|
212
|
+
lines.push(buildStaleProofPrompt(report));
|
|
213
|
+
}
|
|
214
|
+
if (report.out_of_scope_files && report.out_of_scope_files.length > 0) {
|
|
215
|
+
lines.push("✗ Error code: SCOPE_DRIFT_OUT_OF_SCOPE_FILE");
|
|
216
|
+
lines.push(" What happened: Files changed outside the declared work scope.");
|
|
217
|
+
lines.push(" Why it matters: Out-of-scope changes require review before acceptance.");
|
|
218
|
+
lines.push(" Out-of-scope changed files:");
|
|
219
|
+
for (const file of report.out_of_scope_files) {
|
|
220
|
+
lines.push(` - ${file}`);
|
|
221
|
+
}
|
|
222
|
+
lines.push(" Next action: Revert/remove out-of-scope files, then rerun `agentbridge check`.");
|
|
223
|
+
}
|
|
224
|
+
if (report.decision === "needs_review" && (!report.out_of_scope_files || report.out_of_scope_files.length === 0)) {
|
|
225
|
+
lines.push("✗ Error code: ACCEPTANCE_BLOCKED");
|
|
226
|
+
lines.push(" What happened: Work session requires manual review before it can be accepted.");
|
|
227
|
+
lines.push(" Why it matters: Automated acceptance gates are not satisfied.");
|
|
228
|
+
lines.push(" Next action: Address all blocking issues listed above, then re-run `agentbridge check`.");
|
|
229
|
+
}
|
|
230
|
+
if (report.decision === "failed") {
|
|
231
|
+
lines.push("✗ Error code: ACCEPTANCE_BLOCKED");
|
|
232
|
+
lines.push(" What happened: Verification proof failed — tests or checks did not pass.");
|
|
233
|
+
lines.push(" Why it matters: Failed proof cannot be used to accept work.");
|
|
234
|
+
lines.push(" Next action: Fix the failing tests and re-run `agentbridge verify -- <your test command>`.");
|
|
235
|
+
}
|
|
236
|
+
lines.push("Next action:");
|
|
237
|
+
for (const action of report.next_required_action) {
|
|
238
|
+
lines.push(` - ${action}`);
|
|
239
|
+
}
|
|
240
|
+
return lines.join("\n");
|
|
241
|
+
}
|
|
242
|
+
async function runCheck(options = {}) {
|
|
243
|
+
const ctx = resolveNetworkContext();
|
|
244
|
+
const { resolution, report } = await (0, acceptance_preflight_1.resolveAcceptanceWorkContext)(ctx);
|
|
245
|
+
const snapshot = (0, operator_snapshot_1.buildOperatorSnapshot)(resolution);
|
|
246
|
+
if (resolution.state !== "current_session_resolved" || !report) {
|
|
247
|
+
if (options.json) {
|
|
248
|
+
process.stdout.write(`${JSON.stringify({ snapshot }, null, 2)}\n`);
|
|
249
|
+
process.exitCode = 1;
|
|
250
|
+
return;
|
|
251
|
+
}
|
|
252
|
+
const cfg = (0, config_1.readConfig)();
|
|
253
|
+
const workContextLines = (0, work_context_resolver_1.renderWorkContextLines)(resolution);
|
|
254
|
+
const lines = resolution.state === "no_current_session"
|
|
255
|
+
? [
|
|
256
|
+
...workContextLines,
|
|
257
|
+
"",
|
|
258
|
+
renderNoActiveSessionGuidance({
|
|
259
|
+
projectId: ctx.projectId,
|
|
260
|
+
lastAcceptedSessionId: cfg.lastAcceptedSessionId,
|
|
261
|
+
lastAcceptedAt: cfg.lastAcceptedAt,
|
|
262
|
+
}),
|
|
263
|
+
]
|
|
264
|
+
: workContextLines;
|
|
265
|
+
process.stdout.write(`${lines.join("\n")}\n`);
|
|
266
|
+
process.exitCode = 1;
|
|
267
|
+
return;
|
|
268
|
+
}
|
|
269
|
+
const reportResolved = report;
|
|
270
|
+
if (options.json) {
|
|
271
|
+
process.stdout.write(`${JSON.stringify({ snapshot, acceptance_report: reportResolved }, null, 2)}\n`);
|
|
272
|
+
// Phase 6: exit 1 for blocking states even in JSON mode
|
|
273
|
+
if ((0, acceptance_block_1.shouldBlockAcceptanceAction)(reportResolved)) {
|
|
274
|
+
process.exitCode = 1;
|
|
275
|
+
}
|
|
276
|
+
return;
|
|
277
|
+
}
|
|
278
|
+
process.stdout.write(`${renderAcceptanceReport(reportResolved)}\n`);
|
|
279
|
+
// Phase 6: exit 1 for blocking states so CI pipelines can trust the exit code
|
|
280
|
+
if ((0, acceptance_block_1.shouldBlockAcceptanceAction)(reportResolved)) {
|
|
281
|
+
process.exitCode = 1;
|
|
282
|
+
}
|
|
283
|
+
}
|
|
@@ -0,0 +1,159 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.runConnect = runConnect;
|
|
4
|
+
const config_1 = require("../config");
|
|
5
|
+
const errors_1 = require("../errors");
|
|
6
|
+
const http_1 = require("../http");
|
|
7
|
+
function resolveBaseUrl(override) {
|
|
8
|
+
return (override ??
|
|
9
|
+
process.env.AGENTBRIDGE_BASE_URL ??
|
|
10
|
+
"https://agentauth-api-production.up.railway.app");
|
|
11
|
+
}
|
|
12
|
+
async function verifyCredentials(projectId, apiKey, apiBaseUrl) {
|
|
13
|
+
const url = `${apiBaseUrl}/v1/dev/projects/${projectId}/health`;
|
|
14
|
+
let res;
|
|
15
|
+
try {
|
|
16
|
+
res = await fetch(url, {
|
|
17
|
+
method: "GET",
|
|
18
|
+
headers: {
|
|
19
|
+
Authorization: `Bearer ${apiKey}`,
|
|
20
|
+
"Content-Type": "application/json",
|
|
21
|
+
},
|
|
22
|
+
});
|
|
23
|
+
}
|
|
24
|
+
catch (e) {
|
|
25
|
+
const msg = e instanceof Error ? e.message : String(e);
|
|
26
|
+
throw new errors_1.SafeCliError(`Could not reach AgentBridge server at ${apiBaseUrl}.\nNetwork error: ${msg}\n\nCheck your AGENTBRIDGE_BASE_URL or internet connection.`);
|
|
27
|
+
}
|
|
28
|
+
if (res.status === 401 || res.status === 403) {
|
|
29
|
+
throw new errors_1.SafeCliError(`API key rejected (HTTP ${res.status}).\n\nGet a valid key at https://agentbridge.dev/dashboard and set AGENTBRIDGE_API_KEY.`);
|
|
30
|
+
}
|
|
31
|
+
if (res.status === 404) {
|
|
32
|
+
throw new errors_1.SafeCliError(`Project ${projectId} not found (HTTP 404).\n\nConfirm AGENTBRIDGE_PROJECT_ID is correct for this project.`);
|
|
33
|
+
}
|
|
34
|
+
if (!res.ok) {
|
|
35
|
+
const text = await res.text();
|
|
36
|
+
throw new http_1.CliHttpError(`GET ${url} failed (${res.status}): ${text || "<empty>"}`, res.status, text);
|
|
37
|
+
}
|
|
38
|
+
const body = (await res.json());
|
|
39
|
+
return {
|
|
40
|
+
projectName: body.projectName ?? projectId,
|
|
41
|
+
agentCount: Array.isArray(body.agents) ? body.agents.length : 0,
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
async function listAgents(projectId, apiKey, apiBaseUrl) {
|
|
45
|
+
const url = `${apiBaseUrl}/v1/dev/projects/${projectId}/agents`;
|
|
46
|
+
const res = await fetch(url, {
|
|
47
|
+
headers: { Authorization: `Bearer ${apiKey}` },
|
|
48
|
+
});
|
|
49
|
+
if (!res.ok)
|
|
50
|
+
return [];
|
|
51
|
+
const body = (await res.json());
|
|
52
|
+
return Array.isArray(body.agents) ? body.agents : [];
|
|
53
|
+
}
|
|
54
|
+
async function runConnect(options = {}) {
|
|
55
|
+
process.stdout.write("AgentBridge connect\n");
|
|
56
|
+
process.stdout.write("─────────────────────────────────────────\n");
|
|
57
|
+
const cfg = (0, config_1.readConfig)();
|
|
58
|
+
const projectId = options.projectId ??
|
|
59
|
+
process.env.AGENTBRIDGE_PROJECT_ID ??
|
|
60
|
+
cfg.projectId;
|
|
61
|
+
const apiKey = options.apiKey ??
|
|
62
|
+
process.env.AGENTBRIDGE_API_KEY ??
|
|
63
|
+
cfg.apiKey;
|
|
64
|
+
const apiBaseUrl = resolveBaseUrl(options.apiBaseUrl);
|
|
65
|
+
if (!projectId) {
|
|
66
|
+
process.stderr.write([
|
|
67
|
+
"",
|
|
68
|
+
"Missing project ID.",
|
|
69
|
+
"",
|
|
70
|
+
" Option 1 — pass it directly:",
|
|
71
|
+
" agentbridge connect --project <project-id> --api-key <key>",
|
|
72
|
+
"",
|
|
73
|
+
" Option 2 — set env vars in your shell or .env:",
|
|
74
|
+
" export AGENTBRIDGE_PROJECT_ID=<project-id>",
|
|
75
|
+
" export AGENTBRIDGE_API_KEY=<key>",
|
|
76
|
+
"",
|
|
77
|
+
" Option 3 — run the interactive setup wizard:",
|
|
78
|
+
" agentbridge init",
|
|
79
|
+
"",
|
|
80
|
+
"Get your project ID from https://agentbridge.dev/dashboard",
|
|
81
|
+
"",
|
|
82
|
+
].join("\n"));
|
|
83
|
+
process.exit(1);
|
|
84
|
+
}
|
|
85
|
+
if (!apiKey) {
|
|
86
|
+
process.stderr.write([
|
|
87
|
+
"",
|
|
88
|
+
"Missing API key.",
|
|
89
|
+
"",
|
|
90
|
+
" Set AGENTBRIDGE_API_KEY in your environment:",
|
|
91
|
+
" export AGENTBRIDGE_API_KEY=<key>",
|
|
92
|
+
"",
|
|
93
|
+
" Or pass it directly:",
|
|
94
|
+
" agentbridge connect --project <id> --api-key <key>",
|
|
95
|
+
"",
|
|
96
|
+
"Get an API key from https://agentbridge.dev/dashboard",
|
|
97
|
+
"",
|
|
98
|
+
].join("\n"));
|
|
99
|
+
process.exit(1);
|
|
100
|
+
}
|
|
101
|
+
process.stdout.write(`Connecting to project ${projectId} … `);
|
|
102
|
+
let projectName;
|
|
103
|
+
let agentCount;
|
|
104
|
+
try {
|
|
105
|
+
({ projectName, agentCount } = await verifyCredentials(projectId, apiKey, apiBaseUrl));
|
|
106
|
+
}
|
|
107
|
+
catch (err) {
|
|
108
|
+
process.stdout.write("FAILED\n");
|
|
109
|
+
throw err;
|
|
110
|
+
}
|
|
111
|
+
process.stdout.write("OK\n");
|
|
112
|
+
// Persist credentials
|
|
113
|
+
(0, config_1.updateConfig)({ projectId, apiKey, apiBaseUrl });
|
|
114
|
+
process.stdout.write("Credentials saved to .agentbridge/config.json\n");
|
|
115
|
+
// Pick or auto-select an agent identity
|
|
116
|
+
let activeAgentId = options.agentId ?? cfg.activeAgentId;
|
|
117
|
+
if (!activeAgentId) {
|
|
118
|
+
const agents = await listAgents(projectId, apiKey, apiBaseUrl);
|
|
119
|
+
if (agents.length === 1 && agents[0]) {
|
|
120
|
+
activeAgentId = agents[0].id;
|
|
121
|
+
(0, config_1.updateConfig)({ activeAgentId });
|
|
122
|
+
process.stdout.write(`Agent identity set to: ${agents[0].name ?? activeAgentId} (${activeAgentId})\n`);
|
|
123
|
+
}
|
|
124
|
+
else if (agents.length > 1) {
|
|
125
|
+
process.stdout.write("\nAvailable agent identities:\n");
|
|
126
|
+
for (const a of agents) {
|
|
127
|
+
process.stdout.write(` ${a.id} ${a.name ?? ""} ${a.role ?? ""}\n`.trimEnd() + "\n");
|
|
128
|
+
}
|
|
129
|
+
process.stdout.write('\nRun `agentbridge use <agent-id>` to select one, then `agentbridge watch` to start.\n');
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
else {
|
|
133
|
+
(0, config_1.updateConfig)({ activeAgentId });
|
|
134
|
+
process.stdout.write(`Agent identity: ${activeAgentId}\n`);
|
|
135
|
+
}
|
|
136
|
+
process.stdout.write("\n");
|
|
137
|
+
process.stdout.write(`Project: ${projectName}\n`);
|
|
138
|
+
process.stdout.write(`Agents: ${agentCount}\n`);
|
|
139
|
+
process.stdout.write(`Server: ${apiBaseUrl}\n`);
|
|
140
|
+
process.stdout.write("\n");
|
|
141
|
+
if (activeAgentId) {
|
|
142
|
+
process.stdout.write("✓ Connected. Next steps:\n");
|
|
143
|
+
process.stdout.write([
|
|
144
|
+
"",
|
|
145
|
+
" 1. Start a work session:",
|
|
146
|
+
" agentbridge start --summary \"your task\" --scope \"src/\"",
|
|
147
|
+
"",
|
|
148
|
+
" 2. Run the watcher (keep it running in a terminal):",
|
|
149
|
+
" agentbridge watch",
|
|
150
|
+
"",
|
|
151
|
+
" 3. Code normally — AgentBridge watches in the background.",
|
|
152
|
+
" Approvals surface when you cross domain boundaries.",
|
|
153
|
+
"",
|
|
154
|
+
].join("\n"));
|
|
155
|
+
}
|
|
156
|
+
else {
|
|
157
|
+
process.stdout.write("✓ Connected. Run `agentbridge use <agent-id>` then `agentbridge watch`.\n");
|
|
158
|
+
}
|
|
159
|
+
}
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.getDistFreshnessReport = getDistFreshnessReport;
|
|
4
|
+
const node_fs_1 = require("node:fs");
|
|
5
|
+
const node_path_1 = require("node:path");
|
|
6
|
+
function readJsonFile(path) {
|
|
7
|
+
if (!(0, node_fs_1.existsSync)(path))
|
|
8
|
+
return null;
|
|
9
|
+
return JSON.parse((0, node_fs_1.readFileSync)(path, "utf8"));
|
|
10
|
+
}
|
|
11
|
+
function collectSourceTsFiles(dir, acc = []) {
|
|
12
|
+
if (!(0, node_fs_1.existsSync)(dir))
|
|
13
|
+
return acc;
|
|
14
|
+
for (const entry of (0, node_fs_1.readdirSync)(dir, { withFileTypes: true })) {
|
|
15
|
+
if (entry.name === "node_modules" || entry.name === "dist")
|
|
16
|
+
continue;
|
|
17
|
+
const full = (0, node_path_1.resolve)(dir, entry.name);
|
|
18
|
+
if (entry.isDirectory()) {
|
|
19
|
+
collectSourceTsFiles(full, acc);
|
|
20
|
+
}
|
|
21
|
+
else if (entry.isFile() &&
|
|
22
|
+
entry.name.endsWith(".ts") &&
|
|
23
|
+
!entry.name.endsWith(".test.ts")) {
|
|
24
|
+
acc.push(full);
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
return acc;
|
|
28
|
+
}
|
|
29
|
+
function getTrackedSourceCandidates(cliRoot) {
|
|
30
|
+
const srcDir = (0, node_path_1.resolve)(cliRoot, "src");
|
|
31
|
+
return [
|
|
32
|
+
...collectSourceTsFiles(srcDir),
|
|
33
|
+
(0, node_path_1.resolve)(cliRoot, "build.mjs"),
|
|
34
|
+
(0, node_path_1.resolve)(cliRoot, "..", "package.json"),
|
|
35
|
+
];
|
|
36
|
+
}
|
|
37
|
+
function getDistFreshnessReport(cliRoot) {
|
|
38
|
+
const toCliRelativePath = (path) => {
|
|
39
|
+
const rel = (0, node_path_1.relative)(cliRoot, path);
|
|
40
|
+
return rel === "" ? "." : rel;
|
|
41
|
+
};
|
|
42
|
+
const buildInfoPath = (0, node_path_1.resolve)(cliRoot, "dist", "build-info.json");
|
|
43
|
+
const buildInfo = readJsonFile(buildInfoPath);
|
|
44
|
+
if (!buildInfo?.builtAt) {
|
|
45
|
+
return {
|
|
46
|
+
state: "unknown",
|
|
47
|
+
sourceNewerThanDist: "unknown",
|
|
48
|
+
reason: "build-info missing builtAt",
|
|
49
|
+
staleFiles: [],
|
|
50
|
+
};
|
|
51
|
+
}
|
|
52
|
+
const builtAtMs = Date.parse(buildInfo.builtAt);
|
|
53
|
+
if (!Number.isFinite(builtAtMs)) {
|
|
54
|
+
return {
|
|
55
|
+
state: "unknown",
|
|
56
|
+
sourceNewerThanDist: "unknown",
|
|
57
|
+
builtAt: buildInfo.builtAt,
|
|
58
|
+
gitHead: buildInfo.gitHead,
|
|
59
|
+
reason: "build-info builtAt is invalid",
|
|
60
|
+
staleFiles: [],
|
|
61
|
+
sourceLatestMtime: buildInfo.sourceLatestMtime,
|
|
62
|
+
sourceLatestFile: buildInfo.sourceLatestFile,
|
|
63
|
+
};
|
|
64
|
+
}
|
|
65
|
+
const existingCandidates = getTrackedSourceCandidates(cliRoot).filter((path) => (0, node_fs_1.existsSync)(path));
|
|
66
|
+
if (existingCandidates.length === 0) {
|
|
67
|
+
return {
|
|
68
|
+
state: "unknown",
|
|
69
|
+
sourceNewerThanDist: "unknown",
|
|
70
|
+
builtAt: buildInfo.builtAt,
|
|
71
|
+
gitHead: buildInfo.gitHead,
|
|
72
|
+
reason: "source files not available in current runtime path",
|
|
73
|
+
staleFiles: [],
|
|
74
|
+
sourceLatestMtime: buildInfo.sourceLatestMtime,
|
|
75
|
+
sourceLatestFile: buildInfo.sourceLatestFile,
|
|
76
|
+
};
|
|
77
|
+
}
|
|
78
|
+
const staleFiles = existingCandidates
|
|
79
|
+
.filter((path) => (0, node_fs_1.statSync)(path).mtimeMs > builtAtMs)
|
|
80
|
+
.map((path) => toCliRelativePath(path))
|
|
81
|
+
.sort();
|
|
82
|
+
if (staleFiles.length > 0) {
|
|
83
|
+
const sample = staleFiles.slice(0, 3).join(", ");
|
|
84
|
+
const more = staleFiles.length > 3 ? ` (+${staleFiles.length - 3} more)` : "";
|
|
85
|
+
return {
|
|
86
|
+
state: "stale",
|
|
87
|
+
sourceNewerThanDist: "yes",
|
|
88
|
+
builtAt: buildInfo.builtAt,
|
|
89
|
+
gitHead: buildInfo.gitHead,
|
|
90
|
+
reason: `source newer than build (${sample}${more})`,
|
|
91
|
+
staleFiles,
|
|
92
|
+
sourceLatestMtime: buildInfo.sourceLatestMtime,
|
|
93
|
+
sourceLatestFile: buildInfo.sourceLatestFile,
|
|
94
|
+
};
|
|
95
|
+
}
|
|
96
|
+
return {
|
|
97
|
+
state: "fresh",
|
|
98
|
+
sourceNewerThanDist: "no",
|
|
99
|
+
builtAt: buildInfo.builtAt,
|
|
100
|
+
gitHead: buildInfo.gitHead,
|
|
101
|
+
staleFiles: [],
|
|
102
|
+
sourceLatestMtime: buildInfo.sourceLatestMtime,
|
|
103
|
+
sourceLatestFile: buildInfo.sourceLatestFile,
|
|
104
|
+
};
|
|
105
|
+
}
|