@birdybeep/cli 0.2.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/dist/bin.cjs +463 -74
- package/dist/bin.cjs.map +1 -1
- package/dist/bin.js +1 -1
- package/dist/{chunk-OCS5IDYI.js → chunk-U4EIHC5C.js} +452 -76
- package/dist/chunk-U4EIHC5C.js.map +1 -0
- package/dist/index.cjs +451 -74
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +18 -1
- package/dist/index.d.ts +18 -1
- package/dist/index.js +1 -1
- package/package.json +7 -5
- package/dist/chunk-OCS5IDYI.js.map +0 -1
|
@@ -69,8 +69,11 @@ function parseGlobalFlags(argv) {
|
|
|
69
69
|
}
|
|
70
70
|
return { flags, rest };
|
|
71
71
|
}
|
|
72
|
-
function isUnknownFlag(token) {
|
|
73
|
-
|
|
72
|
+
function isUnknownFlag(token, allowed) {
|
|
73
|
+
if (!token.startsWith("-")) return false;
|
|
74
|
+
if (GLOBAL_FLAG_TOKENS.has(token)) return false;
|
|
75
|
+
const eq = token.indexOf("=");
|
|
76
|
+
return !allowed.has(eq >= 0 ? token.slice(0, eq) : token);
|
|
74
77
|
}
|
|
75
78
|
function renderRootHelp(version, commands) {
|
|
76
79
|
const width = Math.max(...commands.map((c) => c.name.length));
|
|
@@ -91,6 +94,16 @@ function renderRootHelp(version, commands) {
|
|
|
91
94
|
" -v, --version Show the CLI version"
|
|
92
95
|
].join("\n");
|
|
93
96
|
}
|
|
97
|
+
function commandFlagTokens(...commands) {
|
|
98
|
+
const tokens = /* @__PURE__ */ new Set();
|
|
99
|
+
for (const command of commands) {
|
|
100
|
+
for (const option of command?.options ?? []) {
|
|
101
|
+
tokens.add(option.flag);
|
|
102
|
+
for (const alias of option.aliases ?? []) tokens.add(alias);
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
return tokens;
|
|
106
|
+
}
|
|
94
107
|
function renderCommandHelp(path, command) {
|
|
95
108
|
const lines = [
|
|
96
109
|
`birdybeep ${path} \u2014 ${command.summary}`,
|
|
@@ -98,6 +111,17 @@ function renderCommandHelp(path, command) {
|
|
|
98
111
|
"Usage:",
|
|
99
112
|
` ${command.usage ?? `birdybeep ${path} [options]`}`
|
|
100
113
|
];
|
|
114
|
+
if (command.options && command.options.length > 0) {
|
|
115
|
+
const labels = command.options.map(
|
|
116
|
+
(o) => `${[o.flag, ...o.aliases ?? []].join(", ")}${o.value ? ` ${o.value}` : ""}`
|
|
117
|
+
);
|
|
118
|
+
const width = Math.max(...labels.map((l) => l.length));
|
|
119
|
+
lines.push(
|
|
120
|
+
"",
|
|
121
|
+
"Options:",
|
|
122
|
+
...command.options.map((o, i) => ` ${labels[i]?.padEnd(width)} ${o.summary}`)
|
|
123
|
+
);
|
|
124
|
+
}
|
|
101
125
|
if (command.subcommands && command.subcommands.length > 0) {
|
|
102
126
|
const width = Math.max(...command.subcommands.map((c) => c.name.length));
|
|
103
127
|
lines.push(
|
|
@@ -122,6 +146,7 @@ async function dispatch(argv, deps) {
|
|
|
122
146
|
return EXIT.OK;
|
|
123
147
|
}
|
|
124
148
|
let command = deps.commands.find((c) => c.name === rest[0]);
|
|
149
|
+
let parent;
|
|
125
150
|
const pathParts = [];
|
|
126
151
|
let argsStart = 1;
|
|
127
152
|
if (command) {
|
|
@@ -129,6 +154,7 @@ async function dispatch(argv, deps) {
|
|
|
129
154
|
if (command.subcommands && command.subcommands.length > 0) {
|
|
130
155
|
const sub = command.subcommands.find((c) => c.name === rest[1]);
|
|
131
156
|
if (sub) {
|
|
157
|
+
parent = command;
|
|
132
158
|
command = sub;
|
|
133
159
|
pathParts.push(sub.name);
|
|
134
160
|
argsStart = 2;
|
|
@@ -152,6 +178,7 @@ async function dispatch(argv, deps) {
|
|
|
152
178
|
name: path,
|
|
153
179
|
summary: command.summary,
|
|
154
180
|
usage: command.usage,
|
|
181
|
+
options: command.options,
|
|
155
182
|
subcommands: command.subcommands?.map((c) => ({ name: c.name, summary: c.summary }))
|
|
156
183
|
});
|
|
157
184
|
return EXIT.OK;
|
|
@@ -161,7 +188,8 @@ async function dispatch(argv, deps) {
|
|
|
161
188
|
return EXIT.USAGE;
|
|
162
189
|
}
|
|
163
190
|
const args = rest.slice(argsStart);
|
|
164
|
-
const
|
|
191
|
+
const allowed = commandFlagTokens(command, parent);
|
|
192
|
+
const unknown = args.find((token) => isUnknownFlag(token, allowed));
|
|
165
193
|
if (unknown !== void 0) {
|
|
166
194
|
io.errline(`birdybeep ${path}: unknown option "${unknown}".`);
|
|
167
195
|
return EXIT.USAGE;
|
|
@@ -189,19 +217,36 @@ async function dispatch(argv, deps) {
|
|
|
189
217
|
}
|
|
190
218
|
|
|
191
219
|
// src/version.ts
|
|
192
|
-
var CLI_VERSION = "0.
|
|
220
|
+
var CLI_VERSION = "0.3.0".length > 0 ? "0.3.0" : "0.0.0";
|
|
193
221
|
|
|
194
222
|
// src/commands/agent.ts
|
|
195
223
|
import { claudeCodeAdapter } from "@birdybeep/claude-code";
|
|
196
224
|
import { codexAdapter } from "@birdybeep/codex";
|
|
225
|
+
import { copilotAdapter } from "@birdybeep/copilot";
|
|
226
|
+
import { cursorAdapter } from "@birdybeep/cursor";
|
|
197
227
|
import { opencodeAdapter } from "@birdybeep/opencode";
|
|
198
|
-
var DEFAULT_ADAPTERS = [
|
|
228
|
+
var DEFAULT_ADAPTERS = [
|
|
229
|
+
claudeCodeAdapter,
|
|
230
|
+
codexAdapter,
|
|
231
|
+
opencodeAdapter,
|
|
232
|
+
cursorAdapter,
|
|
233
|
+
copilotAdapter
|
|
234
|
+
];
|
|
199
235
|
var TARGET_TO_ID = {
|
|
200
236
|
claude: "claude_code",
|
|
201
237
|
codex: "codex",
|
|
202
|
-
opencode: "opencode"
|
|
238
|
+
opencode: "opencode",
|
|
239
|
+
cursor: "cursor",
|
|
240
|
+
copilot: "copilot"
|
|
203
241
|
};
|
|
204
|
-
var AGENT_TARGETS = [
|
|
242
|
+
var AGENT_TARGETS = [
|
|
243
|
+
"all",
|
|
244
|
+
"claude",
|
|
245
|
+
"codex",
|
|
246
|
+
"opencode",
|
|
247
|
+
"cursor",
|
|
248
|
+
"copilot"
|
|
249
|
+
];
|
|
205
250
|
function selectAdapters(target, adapters) {
|
|
206
251
|
if (target === "all") return adapters;
|
|
207
252
|
const id = TARGET_TO_ID[target];
|
|
@@ -292,18 +337,18 @@ function createAgentCommand(deps = {}) {
|
|
|
292
337
|
return {
|
|
293
338
|
name: "agent",
|
|
294
339
|
summary: "Install or uninstall harness adapters",
|
|
295
|
-
usage: "birdybeep agent <install|uninstall> [all|claude|codex|opencode]",
|
|
340
|
+
usage: "birdybeep agent <install|uninstall> [all|claude|codex|opencode|cursor|copilot]",
|
|
296
341
|
subcommands: [
|
|
297
342
|
{
|
|
298
343
|
name: "install",
|
|
299
|
-
summary: "Install adapters (all | claude | codex | opencode)",
|
|
300
|
-
usage: "birdybeep agent install [all|claude|codex|opencode]",
|
|
344
|
+
summary: "Install adapters (all | claude | codex | opencode | cursor | copilot)",
|
|
345
|
+
usage: "birdybeep agent install [all|claude|codex|opencode|cursor|copilot]",
|
|
301
346
|
run: (ctx) => installSelected(adapters, ctx)
|
|
302
347
|
},
|
|
303
348
|
{
|
|
304
349
|
name: "uninstall",
|
|
305
350
|
summary: "Restore harness config to its pre-install state",
|
|
306
|
-
usage: "birdybeep agent uninstall [all|claude|codex|opencode]",
|
|
351
|
+
usage: "birdybeep agent uninstall [all|claude|codex|opencode|cursor|copilot]",
|
|
307
352
|
run: (ctx) => uninstallSelected(adapters, ctx)
|
|
308
353
|
}
|
|
309
354
|
]
|
|
@@ -316,6 +361,8 @@ import {
|
|
|
316
361
|
} from "@birdybeep/agent-core";
|
|
317
362
|
import { claudeCodeAdapter as claudeCodeAdapter2 } from "@birdybeep/claude-code";
|
|
318
363
|
import { codexAdapter as codexAdapter2 } from "@birdybeep/codex";
|
|
364
|
+
import { copilotAdapter as copilotAdapter2 } from "@birdybeep/copilot";
|
|
365
|
+
import { cursorAdapter as cursorAdapter2 } from "@birdybeep/cursor";
|
|
319
366
|
import { opencodeAdapter as opencodeAdapter2 } from "@birdybeep/opencode";
|
|
320
367
|
|
|
321
368
|
// src/config.ts
|
|
@@ -340,6 +387,8 @@ function writeCliConfig(patch) {
|
|
|
340
387
|
const merged = {};
|
|
341
388
|
const apiUrl = patch.apiUrl ?? current.apiUrl;
|
|
342
389
|
if (apiUrl !== void 0) merged.apiUrl = apiUrl;
|
|
390
|
+
const expectEmail = patch.expectEmail ?? current.expectEmail;
|
|
391
|
+
if (expectEmail !== void 0) merged.expectEmail = expectEmail;
|
|
343
392
|
mkdirSync2(birdyBeepConfigDir2(), { recursive: true, mode: 448 });
|
|
344
393
|
writeFileSync(cliConfigPath(), `${JSON.stringify(merged, null, 2)}
|
|
345
394
|
`, { mode: 384 });
|
|
@@ -382,7 +431,13 @@ function machineIdentity() {
|
|
|
382
431
|
}
|
|
383
432
|
|
|
384
433
|
// src/commands/doctor.ts
|
|
385
|
-
var DEFAULT_ADAPTERS2 = [
|
|
434
|
+
var DEFAULT_ADAPTERS2 = [
|
|
435
|
+
claudeCodeAdapter2,
|
|
436
|
+
codexAdapter2,
|
|
437
|
+
opencodeAdapter2,
|
|
438
|
+
cursorAdapter2,
|
|
439
|
+
copilotAdapter2
|
|
440
|
+
];
|
|
386
441
|
async function defaultProbeNetwork(baseUrl) {
|
|
387
442
|
try {
|
|
388
443
|
const controller = new AbortController();
|
|
@@ -465,18 +520,36 @@ function createDoctorCommand(deps = {}) {
|
|
|
465
520
|
}
|
|
466
521
|
|
|
467
522
|
// src/commands/hook.ts
|
|
523
|
+
import { spawn } from "child_process";
|
|
524
|
+
import { randomBytes } from "crypto";
|
|
525
|
+
import { closeSync, openSync, rmSync, writeFileSync as writeFileSync2 } from "fs";
|
|
526
|
+
import { tmpdir } from "os";
|
|
527
|
+
import { basename, dirname, join as join2 } from "path";
|
|
468
528
|
import {
|
|
469
|
-
createSender as defaultCreateSender2
|
|
529
|
+
createSender as defaultCreateSender2,
|
|
530
|
+
resolveOnPath
|
|
470
531
|
} from "@birdybeep/agent-core";
|
|
471
532
|
import { runClaudeHook } from "@birdybeep/claude-code";
|
|
472
533
|
import { runCodexHook } from "@birdybeep/codex";
|
|
534
|
+
import {
|
|
535
|
+
isCopilotHookEventName,
|
|
536
|
+
runCopilotHook
|
|
537
|
+
} from "@birdybeep/copilot";
|
|
538
|
+
import { runCursorHook } from "@birdybeep/cursor";
|
|
473
539
|
import { runOpenCodeHook } from "@birdybeep/opencode";
|
|
474
540
|
var RUNNERS = {
|
|
475
541
|
claude: runClaudeHook,
|
|
476
542
|
codex: runCodexHook,
|
|
477
|
-
opencode: runOpenCodeHook
|
|
543
|
+
opencode: runOpenCodeHook,
|
|
544
|
+
cursor: runCursorHook
|
|
478
545
|
};
|
|
479
|
-
var HOOK_HARNESSES = [
|
|
546
|
+
var HOOK_HARNESSES = [
|
|
547
|
+
"claude",
|
|
548
|
+
"codex",
|
|
549
|
+
"opencode",
|
|
550
|
+
"cursor",
|
|
551
|
+
"copilot"
|
|
552
|
+
];
|
|
480
553
|
var STDIN_READ_TIMEOUT_MS = 3e3;
|
|
481
554
|
function withTimeout(promise, ms, fallback) {
|
|
482
555
|
return new Promise((resolve) => {
|
|
@@ -493,9 +566,13 @@ function withTimeout(promise, ms, fallback) {
|
|
|
493
566
|
});
|
|
494
567
|
}
|
|
495
568
|
function isHarnessName(value) {
|
|
496
|
-
return value === "claude" || value === "codex" || value === "opencode";
|
|
569
|
+
return value === "claude" || value === "codex" || value === "opencode" || value === "cursor" || value === "copilot";
|
|
497
570
|
}
|
|
498
|
-
function runHookCommand(harness, payload, sender) {
|
|
571
|
+
function runHookCommand(harness, payload, sender, copilotEventName) {
|
|
572
|
+
if (harness === "copilot") {
|
|
573
|
+
if (copilotEventName === void 0) return Promise.resolve({ outcome: "skipped" });
|
|
574
|
+
return runCopilotHook(copilotEventName, payload, { sender });
|
|
575
|
+
}
|
|
499
576
|
return RUNNERS[harness](payload, { sender });
|
|
500
577
|
}
|
|
501
578
|
function readStdinDefault() {
|
|
@@ -511,24 +588,90 @@ function readStdinDefault() {
|
|
|
511
588
|
process.stdin.on("error", () => resolve(""));
|
|
512
589
|
});
|
|
513
590
|
}
|
|
514
|
-
async function readHookPayload(args, readStdin) {
|
|
515
|
-
return args[1] ?? await readStdin();
|
|
591
|
+
async function readHookPayload(args, readStdin, stdinOnly = false) {
|
|
592
|
+
return stdinOnly ? readStdin() : args[1] ?? await readStdin();
|
|
593
|
+
}
|
|
594
|
+
var NOTIFY_STDIN_FILE_ENV = "BIRDYBEEP_CODEX_NOTIFY_STDIN_FILE";
|
|
595
|
+
function detachCodexNotifyWorker(payload) {
|
|
596
|
+
if (process.platform === "win32") return false;
|
|
597
|
+
let file;
|
|
598
|
+
let fd;
|
|
599
|
+
try {
|
|
600
|
+
const birdybeep = resolveOnPath("birdybeep");
|
|
601
|
+
if (birdybeep === null) return false;
|
|
602
|
+
const tmpFile = join2(tmpdir(), `birdybeep-notify-${randomBytes(16).toString("hex")}.json`);
|
|
603
|
+
file = tmpFile;
|
|
604
|
+
writeFileSync2(tmpFile, payload, { mode: 384 });
|
|
605
|
+
fd = openSync(tmpFile, "r");
|
|
606
|
+
const child = spawn(birdybeep, ["hook", "codex"], {
|
|
607
|
+
cwd: dirname(birdybeep),
|
|
608
|
+
// trusted dir, never the inherited/attacker cwd
|
|
609
|
+
detached: true,
|
|
610
|
+
// new session (setsid) → survives `codex exec` reaping the group
|
|
611
|
+
stdio: [fd, "ignore", "ignore"],
|
|
612
|
+
// stdin = the temp file; this process holds no pipe
|
|
613
|
+
env: { ...process.env, [NOTIFY_STDIN_FILE_ENV]: tmpFile },
|
|
614
|
+
// worker cleans it up post-read
|
|
615
|
+
windowsHide: true
|
|
616
|
+
});
|
|
617
|
+
child.on("error", () => {
|
|
618
|
+
try {
|
|
619
|
+
rmSync(tmpFile, { force: true });
|
|
620
|
+
} catch {
|
|
621
|
+
}
|
|
622
|
+
});
|
|
623
|
+
child.unref();
|
|
624
|
+
return true;
|
|
625
|
+
} catch {
|
|
626
|
+
if (file !== void 0) {
|
|
627
|
+
try {
|
|
628
|
+
rmSync(file, { force: true });
|
|
629
|
+
} catch {
|
|
630
|
+
}
|
|
631
|
+
}
|
|
632
|
+
return false;
|
|
633
|
+
} finally {
|
|
634
|
+
if (fd !== void 0) {
|
|
635
|
+
try {
|
|
636
|
+
closeSync(fd);
|
|
637
|
+
} catch {
|
|
638
|
+
}
|
|
639
|
+
}
|
|
640
|
+
}
|
|
516
641
|
}
|
|
517
642
|
function createHookCommand(deps = {}) {
|
|
518
643
|
const makeSender = deps.createSender ?? ((baseUrl) => defaultCreateSender2({ baseUrl }));
|
|
519
644
|
const readStdin = deps.readStdin ?? readStdinDefault;
|
|
520
645
|
const stdinTimeoutMs = deps.stdinTimeoutMs ?? STDIN_READ_TIMEOUT_MS;
|
|
646
|
+
const detachCodexNotify = deps.detachCodexNotify ?? detachCodexNotifyWorker;
|
|
521
647
|
return {
|
|
522
648
|
name: "hook",
|
|
523
649
|
summary: "Internal: normalize + send an event fired by a harness hook",
|
|
524
|
-
usage: "birdybeep hook <claude|codex|opencode>",
|
|
650
|
+
usage: "birdybeep hook <claude|codex|opencode|cursor|copilot> [copilot-event]",
|
|
525
651
|
run: async (ctx) => {
|
|
526
652
|
const harness = ctx.args[0];
|
|
527
653
|
if (!isHarnessName(harness)) {
|
|
528
654
|
ctx.io.errline(`birdybeep hook: expected one of ${HOOK_HARNESSES.join("|")}`);
|
|
529
655
|
return EXIT.USAGE;
|
|
530
656
|
}
|
|
531
|
-
const
|
|
657
|
+
const notifyPayload = ctx.args[1];
|
|
658
|
+
if (harness === "codex" && notifyPayload !== void 0 && notifyPayload.length > 0 && detachCodexNotify(notifyPayload)) {
|
|
659
|
+
ctx.io.result({ harness, outcome: "detached" });
|
|
660
|
+
return EXIT.OK;
|
|
661
|
+
}
|
|
662
|
+
const copilotEventName = harness === "copilot" && isCopilotHookEventName(ctx.args[1]) ? ctx.args[1] : void 0;
|
|
663
|
+
const raw = await withTimeout(
|
|
664
|
+
readHookPayload(ctx.args, readStdin, harness === "copilot"),
|
|
665
|
+
stdinTimeoutMs,
|
|
666
|
+
""
|
|
667
|
+
);
|
|
668
|
+
const notifyStdinFile = process.env[NOTIFY_STDIN_FILE_ENV];
|
|
669
|
+
if (notifyStdinFile !== void 0 && dirname(notifyStdinFile) === tmpdir() && basename(notifyStdinFile).startsWith("birdybeep-notify-")) {
|
|
670
|
+
try {
|
|
671
|
+
rmSync(notifyStdinFile, { force: true });
|
|
672
|
+
} catch {
|
|
673
|
+
}
|
|
674
|
+
}
|
|
532
675
|
let payload;
|
|
533
676
|
try {
|
|
534
677
|
payload = JSON.parse(raw);
|
|
@@ -537,52 +680,79 @@ function createHookCommand(deps = {}) {
|
|
|
537
680
|
return EXIT.OK;
|
|
538
681
|
}
|
|
539
682
|
const sender = makeSender(resolveApiUrl());
|
|
540
|
-
const result = await runHookCommand(harness, payload, sender);
|
|
541
|
-
ctx.io.result({
|
|
683
|
+
const result = await runHookCommand(harness, payload, sender, copilotEventName);
|
|
684
|
+
ctx.io.result({
|
|
685
|
+
harness,
|
|
686
|
+
...copilotEventName !== void 0 ? { event: copilotEventName } : {},
|
|
687
|
+
outcome: result.outcome,
|
|
688
|
+
eventType: result.eventType,
|
|
689
|
+
...result.send?.decision ? { decision: result.send.decision } : {},
|
|
690
|
+
...result.send?.status !== void 0 ? { status: result.send.status } : {}
|
|
691
|
+
});
|
|
542
692
|
return EXIT.OK;
|
|
543
693
|
}
|
|
544
694
|
};
|
|
545
695
|
}
|
|
546
696
|
|
|
547
697
|
// src/commands/logout.ts
|
|
548
|
-
import { clearToken } from "@birdybeep/agent-core";
|
|
549
|
-
|
|
698
|
+
import { clearToken, getToken as getToken2 } from "@birdybeep/agent-core";
|
|
699
|
+
var base = (apiUrl) => apiUrl.replace(/\/$/, "");
|
|
700
|
+
function createLogoutCommand(deps = {}) {
|
|
550
701
|
return {
|
|
551
|
-
name:
|
|
552
|
-
summary:
|
|
553
|
-
usage:
|
|
702
|
+
name: "logout",
|
|
703
|
+
summary: "Remove the local machine token (does NOT revoke the machine server-side)",
|
|
704
|
+
usage: "birdybeep logout",
|
|
554
705
|
run: async (ctx) => {
|
|
555
706
|
await clearToken(deps.tokenOptions ?? {});
|
|
556
|
-
ctx.io.emit(
|
|
707
|
+
ctx.io.emit("Logged out \u2014 the machine token was removed.", { loggedOut: true });
|
|
557
708
|
return EXIT.OK;
|
|
558
709
|
}
|
|
559
710
|
};
|
|
560
711
|
}
|
|
561
|
-
function
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
712
|
+
async function revokeSelf(token, fetchImpl, timeoutMs) {
|
|
713
|
+
const controller = new AbortController();
|
|
714
|
+
const timer = setTimeout(() => controller.abort(), timeoutMs);
|
|
715
|
+
try {
|
|
716
|
+
const res = await fetchImpl(`${base(resolveApiUrl())}/v1/machine/revoke-self`, {
|
|
717
|
+
method: "POST",
|
|
718
|
+
headers: { authorization: `Bearer ${token}` },
|
|
719
|
+
signal: controller.signal
|
|
720
|
+
});
|
|
721
|
+
if (res.ok || res.status === 403) return "revoked";
|
|
722
|
+
return "rejected";
|
|
723
|
+
} catch {
|
|
724
|
+
return "unreachable";
|
|
725
|
+
} finally {
|
|
726
|
+
clearTimeout(timer);
|
|
727
|
+
}
|
|
571
728
|
}
|
|
572
729
|
function createUnpairCommand(deps = {}) {
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
730
|
+
const fetchImpl = deps.fetchImpl ?? fetch;
|
|
731
|
+
const timeoutMs = deps.timeoutMs ?? 1e4;
|
|
732
|
+
return {
|
|
733
|
+
name: "unpair",
|
|
734
|
+
summary: "Unpair this machine \u2014 revoke it server-side and remove the local token",
|
|
735
|
+
usage: "birdybeep unpair",
|
|
736
|
+
run: async (ctx) => {
|
|
737
|
+
const token = await getToken2(deps.tokenOptions ?? {});
|
|
738
|
+
const outcome = token === null ? "no_token" : await revokeSelf(token, fetchImpl, timeoutMs);
|
|
739
|
+
await clearToken(deps.tokenOptions ?? {});
|
|
740
|
+
const serverRevoked = outcome === "revoked";
|
|
741
|
+
const human = outcome === "revoked" ? "Unpaired \u2014 the machine was revoked and removed from your account." : outcome === "no_token" ? "Already unpaired \u2014 there was no local token to remove." : outcome === "unreachable" ? "Unpaired locally, but the server was unreachable \u2014 the machine may still show in the app. Open BirdyBeep and revoke it there to fully remove it." : "Unpaired locally, but the server didn't confirm removal \u2014 if the machine still shows in the app, revoke it there.";
|
|
742
|
+
ctx.io.emit(human, { unpaired: true, serverRevoked });
|
|
743
|
+
return EXIT.OK;
|
|
744
|
+
}
|
|
745
|
+
};
|
|
582
746
|
}
|
|
583
747
|
|
|
584
748
|
// src/commands/pair.ts
|
|
585
|
-
import {
|
|
749
|
+
import { closeSync as closeSync2, openSync as openSync2 } from "fs";
|
|
750
|
+
import {
|
|
751
|
+
deriveCodeChallengeS256,
|
|
752
|
+
generateCodeVerifier,
|
|
753
|
+
getMachineIdentity as getMachineIdentity2,
|
|
754
|
+
setToken
|
|
755
|
+
} from "@birdybeep/agent-core";
|
|
586
756
|
import { renderUnicodeCompact } from "uqr";
|
|
587
757
|
|
|
588
758
|
// src/pairing.ts
|
|
@@ -591,16 +761,17 @@ import {
|
|
|
591
761
|
pairStartResponseSchema,
|
|
592
762
|
pairTokenResponseSchema
|
|
593
763
|
} from "@birdybeep/agent-core";
|
|
594
|
-
function
|
|
764
|
+
function base2(apiUrl) {
|
|
595
765
|
return apiUrl.replace(/\/$/, "");
|
|
596
766
|
}
|
|
597
767
|
async function pairStart(apiUrl, input, fetchImpl) {
|
|
598
768
|
const body = {
|
|
599
769
|
machine_label: input.machineLabel,
|
|
600
770
|
...input.os !== void 0 ? { os: input.os } : {},
|
|
601
|
-
...input.cliVersion !== void 0 ? { cli_version: input.cliVersion } : {}
|
|
771
|
+
...input.cliVersion !== void 0 ? { cli_version: input.cliVersion } : {},
|
|
772
|
+
...input.codeChallenge !== void 0 ? { code_challenge: input.codeChallenge } : {}
|
|
602
773
|
};
|
|
603
|
-
const res = await fetchImpl(`${
|
|
774
|
+
const res = await fetchImpl(`${base2(apiUrl)}/v1/pair/start`, {
|
|
604
775
|
method: "POST",
|
|
605
776
|
headers: { "content-type": "application/json" },
|
|
606
777
|
body: JSON.stringify(body)
|
|
@@ -618,12 +789,13 @@ var TERMINAL_TOKEN_ERRORS = /* @__PURE__ */ new Set([
|
|
|
618
789
|
"not_found",
|
|
619
790
|
"payload_too_large"
|
|
620
791
|
]);
|
|
621
|
-
async function pairTokenPoll(apiUrl, deviceCode, fetchImpl, machineFingerprint) {
|
|
792
|
+
async function pairTokenPoll(apiUrl, deviceCode, fetchImpl, machineFingerprint, codeVerifier) {
|
|
622
793
|
const body = {
|
|
623
794
|
device_code: deviceCode,
|
|
624
|
-
...machineFingerprint !== void 0 ? { machine_fingerprint: machineFingerprint } : {}
|
|
795
|
+
...machineFingerprint !== void 0 ? { machine_fingerprint: machineFingerprint } : {},
|
|
796
|
+
...codeVerifier !== void 0 ? { code_verifier: codeVerifier } : {}
|
|
625
797
|
};
|
|
626
|
-
const res = await fetchImpl(`${
|
|
798
|
+
const res = await fetchImpl(`${base2(apiUrl)}/v1/pair/token`, {
|
|
627
799
|
method: "POST",
|
|
628
800
|
headers: { "content-type": "application/json" },
|
|
629
801
|
body: JSON.stringify(body)
|
|
@@ -634,7 +806,10 @@ async function pairTokenPoll(apiUrl, deviceCode, fetchImpl, machineFingerprint)
|
|
|
634
806
|
return {
|
|
635
807
|
status: "paired",
|
|
636
808
|
machineToken: parsed.data.machine_token,
|
|
637
|
-
machineId: parsed.data.machine_id
|
|
809
|
+
machineId: parsed.data.machine_id,
|
|
810
|
+
// Only surface the key when the server reported it (exactOptionalPropertyTypes: no explicit
|
|
811
|
+
// undefined). Older servers omit approved_by_email; newer ones (dgxd) include it.
|
|
812
|
+
...parsed.data.approved_by_email !== void 0 ? { approvedByEmail: parsed.data.approved_by_email } : {}
|
|
638
813
|
};
|
|
639
814
|
}
|
|
640
815
|
let errBody = null;
|
|
@@ -660,22 +835,172 @@ var HEARTBEAT_MS = 15e3;
|
|
|
660
835
|
function renderQrMatrix(qrPayload) {
|
|
661
836
|
return renderUnicodeCompact(qrPayload, { border: 2 });
|
|
662
837
|
}
|
|
838
|
+
function parsePairFlags(args) {
|
|
839
|
+
const flags = { yes: false };
|
|
840
|
+
for (let i = 0; i < args.length; i += 1) {
|
|
841
|
+
const token = args[i] ?? "";
|
|
842
|
+
if (token === "--yes" || token === "-y") {
|
|
843
|
+
flags.yes = true;
|
|
844
|
+
} else if (token === "--expect-email" || token.startsWith("--expect-email=")) {
|
|
845
|
+
const inline = token.startsWith("--expect-email=") ? token.slice("--expect-email=".length) : void 0;
|
|
846
|
+
const value = inline ?? args[++i];
|
|
847
|
+
if (value === void 0 || value.length === 0 || value.startsWith("-")) {
|
|
848
|
+
return { ...flags, error: "--expect-email requires an email address" };
|
|
849
|
+
}
|
|
850
|
+
flags.expectEmail = value;
|
|
851
|
+
} else {
|
|
852
|
+
return { ...flags, error: `unexpected argument "${token}"` };
|
|
853
|
+
}
|
|
854
|
+
}
|
|
855
|
+
return flags;
|
|
856
|
+
}
|
|
857
|
+
function sameEmail(a, b) {
|
|
858
|
+
const fold = (v) => v.trim().toLowerCase();
|
|
859
|
+
const rawEqual = fold(a) === fold(b);
|
|
860
|
+
const nfkcEqual = fold(a.normalize("NFKC")) === fold(b.normalize("NFKC"));
|
|
861
|
+
return rawEqual && nfkcEqual;
|
|
862
|
+
}
|
|
863
|
+
function decidePairConfirmation(input) {
|
|
864
|
+
const { approvedByEmail, expectEmail } = input;
|
|
865
|
+
const platform = input.platform ?? process.platform;
|
|
866
|
+
if (expectEmail !== void 0) {
|
|
867
|
+
if (approvedByEmail === void 0) {
|
|
868
|
+
const remedy = input.expectEmailSource === "config" ? `Remove or correct the "expectEmail" key in ${input.configPath ?? "the BirdyBeep CLI config"} (or upgrade the backend to one that reports the approving account) and re-run.` : "Re-run without --expect-email (and confirm interactively) if that is expected.";
|
|
869
|
+
return {
|
|
870
|
+
action: "reject",
|
|
871
|
+
reason: "expected_email_unverifiable",
|
|
872
|
+
message: `Pairing refused: ${expectEmail} was pinned as the expected approving account, but the server did not report which account approved this machine, so the pin could not be verified. The machine token was NOT stored. ${remedy}`
|
|
873
|
+
};
|
|
874
|
+
}
|
|
875
|
+
if (sameEmail(approvedByEmail, expectEmail)) {
|
|
876
|
+
return { action: "approve", reason: "expected_email_match" };
|
|
877
|
+
}
|
|
878
|
+
return {
|
|
879
|
+
action: "reject",
|
|
880
|
+
reason: "expected_email_mismatch",
|
|
881
|
+
message: `Pairing refused: this machine was approved by ${approvedByEmail}, but ${expectEmail} was expected. The machine token was NOT stored. If you did not expect that account to approve it, open BirdyBeep and revoke the machine, then re-run \`birdybeep pair\`.`
|
|
882
|
+
};
|
|
883
|
+
}
|
|
884
|
+
if (input.yes) return { action: "approve", reason: "yes_flag" };
|
|
885
|
+
const question = approvedByEmail !== void 0 ? `Pair this machine to ${approvedByEmail}? [y/N] ` : "The server did not report which account approved this machine. Pair anyway? [y/N] ";
|
|
886
|
+
if (!input.nonInteractive) {
|
|
887
|
+
if (input.stdinIsTTY) return { action: "prompt", question, on: "stdin" };
|
|
888
|
+
if (input.controllingTerminalAvailable) {
|
|
889
|
+
return { action: "prompt", question, on: "controlling-terminal" };
|
|
890
|
+
}
|
|
891
|
+
}
|
|
892
|
+
const who = approvedByEmail !== void 0 ? ` (approved by ${approvedByEmail})` : "";
|
|
893
|
+
const winptyHint = platform === "win32" && !input.nonInteractive ? " In Git Bash / MSYS, `winpty birdybeep pair` attaches a real console so the prompt can appear." : "";
|
|
894
|
+
return {
|
|
895
|
+
action: "reject",
|
|
896
|
+
reason: "non_interactive",
|
|
897
|
+
message: `Pairing needs confirmation${who}, but there is no terminal to ask on, so the machine token was NOT stored. Re-run with \`--expect-email <addr>\` to pin the approving account (recommended for CI), or \`--yes\` to accept whichever account approved it.` + winptyHint
|
|
898
|
+
};
|
|
899
|
+
}
|
|
900
|
+
function isAffirmative(answer) {
|
|
901
|
+
return /^(y|yes)$/i.test(answer.trim());
|
|
902
|
+
}
|
|
903
|
+
function controllingTerminalPath() {
|
|
904
|
+
return "/dev/tty";
|
|
905
|
+
}
|
|
906
|
+
function canOpenControllingTerminal(path = controllingTerminalPath(), platform = process.platform) {
|
|
907
|
+
if (platform === "win32") return false;
|
|
908
|
+
let fd;
|
|
909
|
+
try {
|
|
910
|
+
fd = openSync2(path, "r");
|
|
911
|
+
return true;
|
|
912
|
+
} catch {
|
|
913
|
+
return false;
|
|
914
|
+
} finally {
|
|
915
|
+
if (fd !== void 0) {
|
|
916
|
+
try {
|
|
917
|
+
closeSync2(fd);
|
|
918
|
+
} catch {
|
|
919
|
+
}
|
|
920
|
+
}
|
|
921
|
+
}
|
|
922
|
+
}
|
|
923
|
+
async function promptForAnswer(question, on) {
|
|
924
|
+
const { createInterface } = await import("readline/promises");
|
|
925
|
+
let ttyFd;
|
|
926
|
+
let input;
|
|
927
|
+
if (on === "stdin") {
|
|
928
|
+
input = process.stdin;
|
|
929
|
+
} else {
|
|
930
|
+
const { ReadStream } = await import("tty");
|
|
931
|
+
ttyFd = openSync2(controllingTerminalPath(), "r");
|
|
932
|
+
input = new ReadStream(ttyFd);
|
|
933
|
+
}
|
|
934
|
+
return new Promise((resolve) => {
|
|
935
|
+
const rl = createInterface({ input, output: process.stderr });
|
|
936
|
+
let settled = false;
|
|
937
|
+
const done = (value) => {
|
|
938
|
+
if (settled) return;
|
|
939
|
+
settled = true;
|
|
940
|
+
rl.close();
|
|
941
|
+
if (on === "stdin") {
|
|
942
|
+
process.stdin.unref?.();
|
|
943
|
+
} else {
|
|
944
|
+
try {
|
|
945
|
+
input.unref?.();
|
|
946
|
+
input.destroy?.();
|
|
947
|
+
} catch {
|
|
948
|
+
}
|
|
949
|
+
if (ttyFd !== void 0) {
|
|
950
|
+
try {
|
|
951
|
+
closeSync2(ttyFd);
|
|
952
|
+
} catch {
|
|
953
|
+
}
|
|
954
|
+
}
|
|
955
|
+
}
|
|
956
|
+
resolve(value);
|
|
957
|
+
};
|
|
958
|
+
rl.question(question).then(done, () => done(""));
|
|
959
|
+
rl.once("close", () => done(""));
|
|
960
|
+
input.once?.("error", () => done(""));
|
|
961
|
+
});
|
|
962
|
+
}
|
|
663
963
|
function createPairCommand(deps = {}) {
|
|
664
964
|
const fetchImpl = deps.fetchImpl ?? fetch;
|
|
665
965
|
const sleep = deps.sleep ?? ((ms) => new Promise((r) => setTimeout(r, ms)));
|
|
666
966
|
const clock = deps.now ?? (() => Date.now());
|
|
667
967
|
const renderQr = deps.renderQr ?? renderQrMatrix;
|
|
668
968
|
const intervalMs = deps.pollIntervalMs ?? DEFAULT_POLL_INTERVAL_MS;
|
|
969
|
+
const promptLine = deps.promptLine ?? promptForAnswer;
|
|
970
|
+
const hasControllingTerminal = deps.hasControllingTerminal ?? (() => canOpenControllingTerminal());
|
|
971
|
+
const configuredExpectEmail = deps.configuredExpectEmail ?? (() => {
|
|
972
|
+
const pinned = readCliConfig().expectEmail;
|
|
973
|
+
return typeof pinned === "string" && pinned.trim().length > 0 ? pinned : void 0;
|
|
974
|
+
});
|
|
669
975
|
return {
|
|
670
976
|
name: "pair",
|
|
671
977
|
summary: "Pair this machine with your BirdyBeep account (QR or manual)",
|
|
672
|
-
usage: "birdybeep pair [--json]",
|
|
978
|
+
usage: "birdybeep pair [--yes] [--expect-email <addr>] [--json]",
|
|
979
|
+
options: [
|
|
980
|
+
{
|
|
981
|
+
flag: "--yes",
|
|
982
|
+
aliases: ["-y"],
|
|
983
|
+
summary: "Skip the approving-account confirmation (headless/CI)"
|
|
984
|
+
},
|
|
985
|
+
{
|
|
986
|
+
flag: "--expect-email",
|
|
987
|
+
value: "<addr>",
|
|
988
|
+
summary: "Only trust the pairing if this account approved it (else fail)"
|
|
989
|
+
}
|
|
990
|
+
],
|
|
673
991
|
run: async (ctx) => {
|
|
992
|
+
const pairFlags = parsePairFlags(ctx.args);
|
|
993
|
+
if (pairFlags.error !== void 0) {
|
|
994
|
+
ctx.io.errline(`birdybeep pair: ${pairFlags.error}.`);
|
|
995
|
+
return EXIT.USAGE;
|
|
996
|
+
}
|
|
674
997
|
const apiUrl = resolveApiUrl();
|
|
675
998
|
const identity = getMachineIdentity2();
|
|
999
|
+
const codeVerifier = generateCodeVerifier();
|
|
1000
|
+
const codeChallenge = deriveCodeChallengeS256(codeVerifier);
|
|
676
1001
|
const start = await pairStart(
|
|
677
1002
|
apiUrl,
|
|
678
|
-
{ machineLabel: identity.label, os: identity.os, cliVersion: CLI_VERSION },
|
|
1003
|
+
{ machineLabel: identity.label, os: identity.os, cliVersion: CLI_VERSION, codeChallenge },
|
|
679
1004
|
fetchImpl
|
|
680
1005
|
);
|
|
681
1006
|
if (ctx.flags.json) {
|
|
@@ -687,12 +1012,14 @@ function createPairCommand(deps = {}) {
|
|
|
687
1012
|
});
|
|
688
1013
|
} else {
|
|
689
1014
|
ctx.io.line(
|
|
690
|
-
"To pair this machine, open the BirdyBeep app, tap \u201Cpair a machine\u201D, and scan this QR
|
|
1015
|
+
"To pair this machine, open the BirdyBeep app, tap \u201Cpair a machine\u201D, and scan this QR or open the complete link:"
|
|
691
1016
|
);
|
|
692
1017
|
const isTTY = deps.isTTY ?? process.stdout.isTTY === true;
|
|
693
1018
|
if (isTTY) ctx.io.line(renderQr(start.qr_payload));
|
|
694
1019
|
ctx.io.line(` Scan or open: ${start.qr_payload}`);
|
|
695
|
-
ctx.io.line(
|
|
1020
|
+
ctx.io.line(
|
|
1021
|
+
` Session code (display only; cannot approve by itself): ${start.user_code}`
|
|
1022
|
+
);
|
|
696
1023
|
ctx.io.line("Waiting for you to approve this machine in the app\u2026");
|
|
697
1024
|
}
|
|
698
1025
|
const deadline = Date.parse(start.expires_at);
|
|
@@ -708,7 +1035,9 @@ function createPairCommand(deps = {}) {
|
|
|
708
1035
|
apiUrl,
|
|
709
1036
|
start.device_code,
|
|
710
1037
|
fetchImpl,
|
|
711
|
-
identity.fingerprintHash
|
|
1038
|
+
identity.fingerprintHash,
|
|
1039
|
+
codeVerifier
|
|
1040
|
+
// PKCE proof-of-possession (dgxd) — sent on every poll
|
|
712
1041
|
);
|
|
713
1042
|
if (poll.status === "paired") {
|
|
714
1043
|
paired = poll;
|
|
@@ -733,15 +1062,44 @@ function createPairCommand(deps = {}) {
|
|
|
733
1062
|
if (paired === void 0 || paired.status !== "paired") {
|
|
734
1063
|
ctx.io.result({ paired: false, reason: "timeout" });
|
|
735
1064
|
ctx.io.errline(
|
|
736
|
-
"Pairing timed out before you approved it. In the BirdyBeep app, tap \u201Cpair a machine\u201D, scan
|
|
1065
|
+
"Pairing timed out before you approved it. In the BirdyBeep app, tap \u201Cpair a machine\u201D, scan a fresh QR or open its complete link, then run `birdybeep pair` again."
|
|
1066
|
+
);
|
|
1067
|
+
return EXIT.ERROR;
|
|
1068
|
+
}
|
|
1069
|
+
const approvedBy = paired.approvedByEmail;
|
|
1070
|
+
const expectEmail = pairFlags.expectEmail ?? configuredExpectEmail();
|
|
1071
|
+
const stdinIsTTY = deps.isStdinTTY ?? process.stdin.isTTY === true;
|
|
1072
|
+
const decision = decidePairConfirmation({
|
|
1073
|
+
...approvedBy !== void 0 ? { approvedByEmail: approvedBy } : {},
|
|
1074
|
+
...expectEmail !== void 0 ? { expectEmail } : {},
|
|
1075
|
+
...expectEmail !== void 0 ? { expectEmailSource: pairFlags.expectEmail !== void 0 ? "flag" : "config" } : {},
|
|
1076
|
+
yes: pairFlags.yes,
|
|
1077
|
+
nonInteractive: ctx.flags.nonInteractive,
|
|
1078
|
+
stdinIsTTY,
|
|
1079
|
+
// Probed ONLY when stdin can't answer — opening /dev/tty is a syscall, and when stdin is
|
|
1080
|
+
// already a terminal the answer is irrelevant.
|
|
1081
|
+
controllingTerminalAvailable: stdinIsTTY ? false : hasControllingTerminal(),
|
|
1082
|
+
configPath: cliConfigPath()
|
|
1083
|
+
});
|
|
1084
|
+
if (decision.action === "reject") {
|
|
1085
|
+
ctx.io.result({ paired: false, reason: decision.reason });
|
|
1086
|
+
ctx.io.errline(decision.message);
|
|
1087
|
+
return EXIT.ERROR;
|
|
1088
|
+
}
|
|
1089
|
+
if (decision.action === "prompt" && !isAffirmative(await promptLine(decision.question, decision.on))) {
|
|
1090
|
+
ctx.io.result({ paired: false, reason: "declined" });
|
|
1091
|
+
ctx.io.errline(
|
|
1092
|
+
"Pairing declined \u2014 the machine token was NOT stored, and this machine will send no events. The machine may still appear in the BirdyBeep app; revoke it there if you did not intend to pair it."
|
|
737
1093
|
);
|
|
738
1094
|
return EXIT.ERROR;
|
|
739
1095
|
}
|
|
740
1096
|
await setToken(paired.machineToken, deps.tokenOptions ?? {});
|
|
741
1097
|
writeCliConfig({ apiUrl });
|
|
742
|
-
|
|
1098
|
+
const humanSuffix = approvedBy !== void 0 ? ` to ${approvedBy}` : "";
|
|
1099
|
+
ctx.io.emit(`\u2713 Paired${humanSuffix}. Run \`birdybeep test\` to send a test Beep.`, {
|
|
743
1100
|
paired: true,
|
|
744
|
-
machineId: paired.machineId
|
|
1101
|
+
machineId: paired.machineId,
|
|
1102
|
+
...approvedBy !== void 0 ? { approvedByEmail: approvedBy } : {}
|
|
745
1103
|
});
|
|
746
1104
|
return EXIT.OK;
|
|
747
1105
|
}
|
|
@@ -773,19 +1131,29 @@ function createQueueCommand() {
|
|
|
773
1131
|
// src/commands/report-status.ts
|
|
774
1132
|
import {
|
|
775
1133
|
errorEnvelopeSchema as errorEnvelopeSchema2,
|
|
776
|
-
getToken as
|
|
1134
|
+
getToken as getToken3,
|
|
777
1135
|
integrationStatusResponseSchema
|
|
778
1136
|
} from "@birdybeep/agent-core";
|
|
779
1137
|
import { CLAUDE_CODE_ADAPTER_VERSION, claudeCodeAdapter as claudeCodeAdapter3 } from "@birdybeep/claude-code";
|
|
780
1138
|
import { CODEX_ADAPTER_VERSION, codexAdapter as codexAdapter3 } from "@birdybeep/codex";
|
|
1139
|
+
import { COPILOT_ADAPTER_VERSION, copilotAdapter as copilotAdapter3 } from "@birdybeep/copilot";
|
|
1140
|
+
import { CURSOR_ADAPTER_VERSION, cursorAdapter as cursorAdapter3 } from "@birdybeep/cursor";
|
|
781
1141
|
import { OPENCODE_ADAPTER_VERSION, opencodeAdapter as opencodeAdapter3 } from "@birdybeep/opencode";
|
|
782
|
-
var DEFAULT_ADAPTERS3 = [
|
|
1142
|
+
var DEFAULT_ADAPTERS3 = [
|
|
1143
|
+
claudeCodeAdapter3,
|
|
1144
|
+
codexAdapter3,
|
|
1145
|
+
opencodeAdapter3,
|
|
1146
|
+
cursorAdapter3,
|
|
1147
|
+
copilotAdapter3
|
|
1148
|
+
];
|
|
783
1149
|
var ADAPTER_VERSIONS = {
|
|
784
1150
|
claude_code: CLAUDE_CODE_ADAPTER_VERSION,
|
|
785
1151
|
codex: CODEX_ADAPTER_VERSION,
|
|
786
|
-
opencode: OPENCODE_ADAPTER_VERSION
|
|
1152
|
+
opencode: OPENCODE_ADAPTER_VERSION,
|
|
1153
|
+
cursor: CURSOR_ADAPTER_VERSION,
|
|
1154
|
+
copilot: COPILOT_ADAPTER_VERSION
|
|
787
1155
|
};
|
|
788
|
-
var
|
|
1156
|
+
var base3 = (apiUrl) => apiUrl.replace(/\/$/, "");
|
|
789
1157
|
async function gatherItems(adapters) {
|
|
790
1158
|
return Promise.all(
|
|
791
1159
|
adapters.map(async (a) => {
|
|
@@ -806,7 +1174,7 @@ function createReportStatusCommand(deps = {}) {
|
|
|
806
1174
|
summary: "Internal: report integration status to the backend",
|
|
807
1175
|
usage: "birdybeep report-status [--json]",
|
|
808
1176
|
run: async (ctx) => {
|
|
809
|
-
const token = await
|
|
1177
|
+
const token = await getToken3(deps.tokenOptions ?? {});
|
|
810
1178
|
if (token === null) {
|
|
811
1179
|
ctx.io.errline("No machine token \u2014 run `birdybeep pair` first.");
|
|
812
1180
|
return EXIT.ERROR;
|
|
@@ -820,7 +1188,7 @@ function createReportStatusCommand(deps = {}) {
|
|
|
820
1188
|
let outcome = "deferred";
|
|
821
1189
|
let errorCode;
|
|
822
1190
|
try {
|
|
823
|
-
const res = await fetchImpl(`${
|
|
1191
|
+
const res = await fetchImpl(`${base3(resolveApiUrl())}/v1/integrations/status`, {
|
|
824
1192
|
method: "POST",
|
|
825
1193
|
headers: { authorization: `Bearer ${token}`, "content-type": "application/json" },
|
|
826
1194
|
body: JSON.stringify({ integrations: items })
|
|
@@ -873,8 +1241,16 @@ import {
|
|
|
873
1241
|
} from "@birdybeep/agent-core";
|
|
874
1242
|
import { claudeCodeAdapter as claudeCodeAdapter4 } from "@birdybeep/claude-code";
|
|
875
1243
|
import { codexAdapter as codexAdapter4 } from "@birdybeep/codex";
|
|
1244
|
+
import { copilotAdapter as copilotAdapter4 } from "@birdybeep/copilot";
|
|
1245
|
+
import { cursorAdapter as cursorAdapter4 } from "@birdybeep/cursor";
|
|
876
1246
|
import { opencodeAdapter as opencodeAdapter4 } from "@birdybeep/opencode";
|
|
877
|
-
var DEFAULT_ADAPTERS4 = [
|
|
1247
|
+
var DEFAULT_ADAPTERS4 = [
|
|
1248
|
+
claudeCodeAdapter4,
|
|
1249
|
+
codexAdapter4,
|
|
1250
|
+
opencodeAdapter4,
|
|
1251
|
+
cursorAdapter4,
|
|
1252
|
+
copilotAdapter4
|
|
1253
|
+
];
|
|
878
1254
|
function createStatusCommand(deps = {}) {
|
|
879
1255
|
const adapters = deps.adapters ?? DEFAULT_ADAPTERS4;
|
|
880
1256
|
const makeSender = deps.createSender ?? ((baseUrl) => defaultCreateSender3(
|
|
@@ -1000,8 +1376,8 @@ function buildCommands() {
|
|
|
1000
1376
|
}
|
|
1001
1377
|
|
|
1002
1378
|
// src/update-check.ts
|
|
1003
|
-
import { mkdirSync as mkdirSync3, readFileSync as readFileSync2, writeFileSync as
|
|
1004
|
-
import { join as
|
|
1379
|
+
import { mkdirSync as mkdirSync3, readFileSync as readFileSync2, writeFileSync as writeFileSync3 } from "fs";
|
|
1380
|
+
import { join as join3 } from "path";
|
|
1005
1381
|
import { birdyBeepConfigDir as birdyBeepConfigDir3 } from "@birdybeep/agent-core";
|
|
1006
1382
|
var PACKAGE_NAME = "@birdybeep/cli";
|
|
1007
1383
|
var PACKAGE_PATH = "@birdybeep%2Fcli";
|
|
@@ -1056,7 +1432,7 @@ function isNewer(current, latest) {
|
|
|
1056
1432
|
return cur !== null && lat !== null && compareSemver(cur, lat) < 0;
|
|
1057
1433
|
}
|
|
1058
1434
|
function updateCachePath() {
|
|
1059
|
-
return
|
|
1435
|
+
return join3(birdyBeepConfigDir3(), UPDATE_CACHE_FILE);
|
|
1060
1436
|
}
|
|
1061
1437
|
function readUpdateCache() {
|
|
1062
1438
|
try {
|
|
@@ -1072,7 +1448,7 @@ function readUpdateCache() {
|
|
|
1072
1448
|
}
|
|
1073
1449
|
function writeUpdateCache(cache) {
|
|
1074
1450
|
mkdirSync3(birdyBeepConfigDir3(), { recursive: true, mode: 448 });
|
|
1075
|
-
|
|
1451
|
+
writeFileSync3(updateCachePath(), `${JSON.stringify(cache)}
|
|
1076
1452
|
`, { mode: 384 });
|
|
1077
1453
|
}
|
|
1078
1454
|
async function fetchLatestVersion(registryUrl, fetchImpl, timeoutMs) {
|
|
@@ -1160,4 +1536,4 @@ export {
|
|
|
1160
1536
|
buildCommands,
|
|
1161
1537
|
runCli
|
|
1162
1538
|
};
|
|
1163
|
-
//# sourceMappingURL=chunk-
|
|
1539
|
+
//# sourceMappingURL=chunk-U4EIHC5C.js.map
|