@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
package/dist/index.cjs
CHANGED
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
+
var __create = Object.create;
|
|
2
3
|
var __defProp = Object.defineProperty;
|
|
3
4
|
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
5
|
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
6
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
5
7
|
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
8
|
var __export = (target, all) => {
|
|
7
9
|
for (var name in all)
|
|
@@ -15,6 +17,14 @@ var __copyProps = (to, from, except, desc) => {
|
|
|
15
17
|
}
|
|
16
18
|
return to;
|
|
17
19
|
};
|
|
20
|
+
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
|
21
|
+
// If the importer is in node compatibility mode or this is not an ESM
|
|
22
|
+
// file that has been converted to a CommonJS file using a Babel-
|
|
23
|
+
// compatible transform (i.e. "__esModule" has not been set), then set
|
|
24
|
+
// "default" to the CommonJS "module.exports" for node compatibility.
|
|
25
|
+
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
|
26
|
+
mod
|
|
27
|
+
));
|
|
18
28
|
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
29
|
|
|
20
30
|
// src/index.ts
|
|
@@ -35,6 +45,8 @@ module.exports = __toCommonJS(index_exports);
|
|
|
35
45
|
// src/commands/agent.ts
|
|
36
46
|
var import_claude_code = require("@birdybeep/claude-code");
|
|
37
47
|
var import_codex = require("@birdybeep/codex");
|
|
48
|
+
var import_copilot = require("@birdybeep/copilot");
|
|
49
|
+
var import_cursor = require("@birdybeep/cursor");
|
|
38
50
|
var import_opencode = require("@birdybeep/opencode");
|
|
39
51
|
|
|
40
52
|
// src/framework.ts
|
|
@@ -108,8 +120,11 @@ function parseGlobalFlags(argv) {
|
|
|
108
120
|
}
|
|
109
121
|
return { flags, rest };
|
|
110
122
|
}
|
|
111
|
-
function isUnknownFlag(token) {
|
|
112
|
-
|
|
123
|
+
function isUnknownFlag(token, allowed) {
|
|
124
|
+
if (!token.startsWith("-")) return false;
|
|
125
|
+
if (GLOBAL_FLAG_TOKENS.has(token)) return false;
|
|
126
|
+
const eq = token.indexOf("=");
|
|
127
|
+
return !allowed.has(eq >= 0 ? token.slice(0, eq) : token);
|
|
113
128
|
}
|
|
114
129
|
function renderRootHelp(version, commands) {
|
|
115
130
|
const width = Math.max(...commands.map((c) => c.name.length));
|
|
@@ -130,6 +145,16 @@ function renderRootHelp(version, commands) {
|
|
|
130
145
|
" -v, --version Show the CLI version"
|
|
131
146
|
].join("\n");
|
|
132
147
|
}
|
|
148
|
+
function commandFlagTokens(...commands) {
|
|
149
|
+
const tokens = /* @__PURE__ */ new Set();
|
|
150
|
+
for (const command of commands) {
|
|
151
|
+
for (const option of command?.options ?? []) {
|
|
152
|
+
tokens.add(option.flag);
|
|
153
|
+
for (const alias of option.aliases ?? []) tokens.add(alias);
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
return tokens;
|
|
157
|
+
}
|
|
133
158
|
function renderCommandHelp(path, command) {
|
|
134
159
|
const lines = [
|
|
135
160
|
`birdybeep ${path} \u2014 ${command.summary}`,
|
|
@@ -137,6 +162,17 @@ function renderCommandHelp(path, command) {
|
|
|
137
162
|
"Usage:",
|
|
138
163
|
` ${command.usage ?? `birdybeep ${path} [options]`}`
|
|
139
164
|
];
|
|
165
|
+
if (command.options && command.options.length > 0) {
|
|
166
|
+
const labels = command.options.map(
|
|
167
|
+
(o) => `${[o.flag, ...o.aliases ?? []].join(", ")}${o.value ? ` ${o.value}` : ""}`
|
|
168
|
+
);
|
|
169
|
+
const width = Math.max(...labels.map((l) => l.length));
|
|
170
|
+
lines.push(
|
|
171
|
+
"",
|
|
172
|
+
"Options:",
|
|
173
|
+
...command.options.map((o, i) => ` ${labels[i]?.padEnd(width)} ${o.summary}`)
|
|
174
|
+
);
|
|
175
|
+
}
|
|
140
176
|
if (command.subcommands && command.subcommands.length > 0) {
|
|
141
177
|
const width = Math.max(...command.subcommands.map((c) => c.name.length));
|
|
142
178
|
lines.push(
|
|
@@ -161,6 +197,7 @@ async function dispatch(argv, deps) {
|
|
|
161
197
|
return EXIT.OK;
|
|
162
198
|
}
|
|
163
199
|
let command = deps.commands.find((c) => c.name === rest[0]);
|
|
200
|
+
let parent;
|
|
164
201
|
const pathParts = [];
|
|
165
202
|
let argsStart = 1;
|
|
166
203
|
if (command) {
|
|
@@ -168,6 +205,7 @@ async function dispatch(argv, deps) {
|
|
|
168
205
|
if (command.subcommands && command.subcommands.length > 0) {
|
|
169
206
|
const sub = command.subcommands.find((c) => c.name === rest[1]);
|
|
170
207
|
if (sub) {
|
|
208
|
+
parent = command;
|
|
171
209
|
command = sub;
|
|
172
210
|
pathParts.push(sub.name);
|
|
173
211
|
argsStart = 2;
|
|
@@ -191,6 +229,7 @@ async function dispatch(argv, deps) {
|
|
|
191
229
|
name: path,
|
|
192
230
|
summary: command.summary,
|
|
193
231
|
usage: command.usage,
|
|
232
|
+
options: command.options,
|
|
194
233
|
subcommands: command.subcommands?.map((c) => ({ name: c.name, summary: c.summary }))
|
|
195
234
|
});
|
|
196
235
|
return EXIT.OK;
|
|
@@ -200,7 +239,8 @@ async function dispatch(argv, deps) {
|
|
|
200
239
|
return EXIT.USAGE;
|
|
201
240
|
}
|
|
202
241
|
const args = rest.slice(argsStart);
|
|
203
|
-
const
|
|
242
|
+
const allowed = commandFlagTokens(command, parent);
|
|
243
|
+
const unknown = args.find((token) => isUnknownFlag(token, allowed));
|
|
204
244
|
if (unknown !== void 0) {
|
|
205
245
|
io.errline(`birdybeep ${path}: unknown option "${unknown}".`);
|
|
206
246
|
return EXIT.USAGE;
|
|
@@ -228,13 +268,28 @@ async function dispatch(argv, deps) {
|
|
|
228
268
|
}
|
|
229
269
|
|
|
230
270
|
// src/commands/agent.ts
|
|
231
|
-
var DEFAULT_ADAPTERS = [
|
|
271
|
+
var DEFAULT_ADAPTERS = [
|
|
272
|
+
import_claude_code.claudeCodeAdapter,
|
|
273
|
+
import_codex.codexAdapter,
|
|
274
|
+
import_opencode.opencodeAdapter,
|
|
275
|
+
import_cursor.cursorAdapter,
|
|
276
|
+
import_copilot.copilotAdapter
|
|
277
|
+
];
|
|
232
278
|
var TARGET_TO_ID = {
|
|
233
279
|
claude: "claude_code",
|
|
234
280
|
codex: "codex",
|
|
235
|
-
opencode: "opencode"
|
|
281
|
+
opencode: "opencode",
|
|
282
|
+
cursor: "cursor",
|
|
283
|
+
copilot: "copilot"
|
|
236
284
|
};
|
|
237
|
-
var AGENT_TARGETS = [
|
|
285
|
+
var AGENT_TARGETS = [
|
|
286
|
+
"all",
|
|
287
|
+
"claude",
|
|
288
|
+
"codex",
|
|
289
|
+
"opencode",
|
|
290
|
+
"cursor",
|
|
291
|
+
"copilot"
|
|
292
|
+
];
|
|
238
293
|
function selectAdapters(target, adapters) {
|
|
239
294
|
if (target === "all") return adapters;
|
|
240
295
|
const id = TARGET_TO_ID[target];
|
|
@@ -325,18 +380,18 @@ function createAgentCommand(deps = {}) {
|
|
|
325
380
|
return {
|
|
326
381
|
name: "agent",
|
|
327
382
|
summary: "Install or uninstall harness adapters",
|
|
328
|
-
usage: "birdybeep agent <install|uninstall> [all|claude|codex|opencode]",
|
|
383
|
+
usage: "birdybeep agent <install|uninstall> [all|claude|codex|opencode|cursor|copilot]",
|
|
329
384
|
subcommands: [
|
|
330
385
|
{
|
|
331
386
|
name: "install",
|
|
332
|
-
summary: "Install adapters (all | claude | codex | opencode)",
|
|
333
|
-
usage: "birdybeep agent install [all|claude|codex|opencode]",
|
|
387
|
+
summary: "Install adapters (all | claude | codex | opencode | cursor | copilot)",
|
|
388
|
+
usage: "birdybeep agent install [all|claude|codex|opencode|cursor|copilot]",
|
|
334
389
|
run: (ctx) => installSelected(adapters, ctx)
|
|
335
390
|
},
|
|
336
391
|
{
|
|
337
392
|
name: "uninstall",
|
|
338
393
|
summary: "Restore harness config to its pre-install state",
|
|
339
|
-
usage: "birdybeep agent uninstall [all|claude|codex|opencode]",
|
|
394
|
+
usage: "birdybeep agent uninstall [all|claude|codex|opencode|cursor|copilot]",
|
|
340
395
|
run: (ctx) => uninstallSelected(adapters, ctx)
|
|
341
396
|
}
|
|
342
397
|
]
|
|
@@ -347,6 +402,8 @@ function createAgentCommand(deps = {}) {
|
|
|
347
402
|
var import_agent_core4 = require("@birdybeep/agent-core");
|
|
348
403
|
var import_claude_code2 = require("@birdybeep/claude-code");
|
|
349
404
|
var import_codex2 = require("@birdybeep/codex");
|
|
405
|
+
var import_copilot2 = require("@birdybeep/copilot");
|
|
406
|
+
var import_cursor2 = require("@birdybeep/cursor");
|
|
350
407
|
var import_opencode2 = require("@birdybeep/opencode");
|
|
351
408
|
|
|
352
409
|
// src/config.ts
|
|
@@ -371,6 +428,8 @@ function writeCliConfig(patch) {
|
|
|
371
428
|
const merged = {};
|
|
372
429
|
const apiUrl = patch.apiUrl ?? current.apiUrl;
|
|
373
430
|
if (apiUrl !== void 0) merged.apiUrl = apiUrl;
|
|
431
|
+
const expectEmail = patch.expectEmail ?? current.expectEmail;
|
|
432
|
+
if (expectEmail !== void 0) merged.expectEmail = expectEmail;
|
|
374
433
|
(0, import_node_fs2.mkdirSync)((0, import_agent_core2.birdyBeepConfigDir)(), { recursive: true, mode: 448 });
|
|
375
434
|
(0, import_node_fs2.writeFileSync)(cliConfigPath(), `${JSON.stringify(merged, null, 2)}
|
|
376
435
|
`, { mode: 384 });
|
|
@@ -409,7 +468,13 @@ function machineIdentity() {
|
|
|
409
468
|
}
|
|
410
469
|
|
|
411
470
|
// src/commands/doctor.ts
|
|
412
|
-
var DEFAULT_ADAPTERS2 = [
|
|
471
|
+
var DEFAULT_ADAPTERS2 = [
|
|
472
|
+
import_claude_code2.claudeCodeAdapter,
|
|
473
|
+
import_codex2.codexAdapter,
|
|
474
|
+
import_opencode2.opencodeAdapter,
|
|
475
|
+
import_cursor2.cursorAdapter,
|
|
476
|
+
import_copilot2.copilotAdapter
|
|
477
|
+
];
|
|
413
478
|
async function defaultProbeNetwork(baseUrl) {
|
|
414
479
|
try {
|
|
415
480
|
const controller = new AbortController();
|
|
@@ -492,16 +557,30 @@ function createDoctorCommand(deps = {}) {
|
|
|
492
557
|
}
|
|
493
558
|
|
|
494
559
|
// src/commands/hook.ts
|
|
560
|
+
var import_node_child_process = require("child_process");
|
|
561
|
+
var import_node_crypto = require("crypto");
|
|
562
|
+
var import_node_fs3 = require("fs");
|
|
563
|
+
var import_node_os = require("os");
|
|
564
|
+
var import_node_path2 = require("path");
|
|
495
565
|
var import_agent_core5 = require("@birdybeep/agent-core");
|
|
496
566
|
var import_claude_code3 = require("@birdybeep/claude-code");
|
|
497
567
|
var import_codex3 = require("@birdybeep/codex");
|
|
568
|
+
var import_copilot3 = require("@birdybeep/copilot");
|
|
569
|
+
var import_cursor3 = require("@birdybeep/cursor");
|
|
498
570
|
var import_opencode3 = require("@birdybeep/opencode");
|
|
499
571
|
var RUNNERS = {
|
|
500
572
|
claude: import_claude_code3.runClaudeHook,
|
|
501
573
|
codex: import_codex3.runCodexHook,
|
|
502
|
-
opencode: import_opencode3.runOpenCodeHook
|
|
574
|
+
opencode: import_opencode3.runOpenCodeHook,
|
|
575
|
+
cursor: import_cursor3.runCursorHook
|
|
503
576
|
};
|
|
504
|
-
var HOOK_HARNESSES = [
|
|
577
|
+
var HOOK_HARNESSES = [
|
|
578
|
+
"claude",
|
|
579
|
+
"codex",
|
|
580
|
+
"opencode",
|
|
581
|
+
"cursor",
|
|
582
|
+
"copilot"
|
|
583
|
+
];
|
|
505
584
|
var STDIN_READ_TIMEOUT_MS = 3e3;
|
|
506
585
|
function withTimeout(promise, ms, fallback) {
|
|
507
586
|
return new Promise((resolve) => {
|
|
@@ -518,9 +597,13 @@ function withTimeout(promise, ms, fallback) {
|
|
|
518
597
|
});
|
|
519
598
|
}
|
|
520
599
|
function isHarnessName(value) {
|
|
521
|
-
return value === "claude" || value === "codex" || value === "opencode";
|
|
600
|
+
return value === "claude" || value === "codex" || value === "opencode" || value === "cursor" || value === "copilot";
|
|
522
601
|
}
|
|
523
|
-
function runHookCommand(harness, payload, sender) {
|
|
602
|
+
function runHookCommand(harness, payload, sender, copilotEventName) {
|
|
603
|
+
if (harness === "copilot") {
|
|
604
|
+
if (copilotEventName === void 0) return Promise.resolve({ outcome: "skipped" });
|
|
605
|
+
return (0, import_copilot3.runCopilotHook)(copilotEventName, payload, { sender });
|
|
606
|
+
}
|
|
524
607
|
return RUNNERS[harness](payload, { sender });
|
|
525
608
|
}
|
|
526
609
|
function readStdinDefault() {
|
|
@@ -536,24 +619,90 @@ function readStdinDefault() {
|
|
|
536
619
|
process.stdin.on("error", () => resolve(""));
|
|
537
620
|
});
|
|
538
621
|
}
|
|
539
|
-
async function readHookPayload(args, readStdin) {
|
|
540
|
-
return args[1] ?? await readStdin();
|
|
622
|
+
async function readHookPayload(args, readStdin, stdinOnly = false) {
|
|
623
|
+
return stdinOnly ? readStdin() : args[1] ?? await readStdin();
|
|
624
|
+
}
|
|
625
|
+
var NOTIFY_STDIN_FILE_ENV = "BIRDYBEEP_CODEX_NOTIFY_STDIN_FILE";
|
|
626
|
+
function detachCodexNotifyWorker(payload) {
|
|
627
|
+
if (process.platform === "win32") return false;
|
|
628
|
+
let file;
|
|
629
|
+
let fd;
|
|
630
|
+
try {
|
|
631
|
+
const birdybeep = (0, import_agent_core5.resolveOnPath)("birdybeep");
|
|
632
|
+
if (birdybeep === null) return false;
|
|
633
|
+
const tmpFile = (0, import_node_path2.join)((0, import_node_os.tmpdir)(), `birdybeep-notify-${(0, import_node_crypto.randomBytes)(16).toString("hex")}.json`);
|
|
634
|
+
file = tmpFile;
|
|
635
|
+
(0, import_node_fs3.writeFileSync)(tmpFile, payload, { mode: 384 });
|
|
636
|
+
fd = (0, import_node_fs3.openSync)(tmpFile, "r");
|
|
637
|
+
const child = (0, import_node_child_process.spawn)(birdybeep, ["hook", "codex"], {
|
|
638
|
+
cwd: (0, import_node_path2.dirname)(birdybeep),
|
|
639
|
+
// trusted dir, never the inherited/attacker cwd
|
|
640
|
+
detached: true,
|
|
641
|
+
// new session (setsid) → survives `codex exec` reaping the group
|
|
642
|
+
stdio: [fd, "ignore", "ignore"],
|
|
643
|
+
// stdin = the temp file; this process holds no pipe
|
|
644
|
+
env: { ...process.env, [NOTIFY_STDIN_FILE_ENV]: tmpFile },
|
|
645
|
+
// worker cleans it up post-read
|
|
646
|
+
windowsHide: true
|
|
647
|
+
});
|
|
648
|
+
child.on("error", () => {
|
|
649
|
+
try {
|
|
650
|
+
(0, import_node_fs3.rmSync)(tmpFile, { force: true });
|
|
651
|
+
} catch {
|
|
652
|
+
}
|
|
653
|
+
});
|
|
654
|
+
child.unref();
|
|
655
|
+
return true;
|
|
656
|
+
} catch {
|
|
657
|
+
if (file !== void 0) {
|
|
658
|
+
try {
|
|
659
|
+
(0, import_node_fs3.rmSync)(file, { force: true });
|
|
660
|
+
} catch {
|
|
661
|
+
}
|
|
662
|
+
}
|
|
663
|
+
return false;
|
|
664
|
+
} finally {
|
|
665
|
+
if (fd !== void 0) {
|
|
666
|
+
try {
|
|
667
|
+
(0, import_node_fs3.closeSync)(fd);
|
|
668
|
+
} catch {
|
|
669
|
+
}
|
|
670
|
+
}
|
|
671
|
+
}
|
|
541
672
|
}
|
|
542
673
|
function createHookCommand(deps = {}) {
|
|
543
674
|
const makeSender = deps.createSender ?? ((baseUrl) => (0, import_agent_core5.createSender)({ baseUrl }));
|
|
544
675
|
const readStdin = deps.readStdin ?? readStdinDefault;
|
|
545
676
|
const stdinTimeoutMs = deps.stdinTimeoutMs ?? STDIN_READ_TIMEOUT_MS;
|
|
677
|
+
const detachCodexNotify = deps.detachCodexNotify ?? detachCodexNotifyWorker;
|
|
546
678
|
return {
|
|
547
679
|
name: "hook",
|
|
548
680
|
summary: "Internal: normalize + send an event fired by a harness hook",
|
|
549
|
-
usage: "birdybeep hook <claude|codex|opencode>",
|
|
681
|
+
usage: "birdybeep hook <claude|codex|opencode|cursor|copilot> [copilot-event]",
|
|
550
682
|
run: async (ctx) => {
|
|
551
683
|
const harness = ctx.args[0];
|
|
552
684
|
if (!isHarnessName(harness)) {
|
|
553
685
|
ctx.io.errline(`birdybeep hook: expected one of ${HOOK_HARNESSES.join("|")}`);
|
|
554
686
|
return EXIT.USAGE;
|
|
555
687
|
}
|
|
556
|
-
const
|
|
688
|
+
const notifyPayload = ctx.args[1];
|
|
689
|
+
if (harness === "codex" && notifyPayload !== void 0 && notifyPayload.length > 0 && detachCodexNotify(notifyPayload)) {
|
|
690
|
+
ctx.io.result({ harness, outcome: "detached" });
|
|
691
|
+
return EXIT.OK;
|
|
692
|
+
}
|
|
693
|
+
const copilotEventName = harness === "copilot" && (0, import_copilot3.isCopilotHookEventName)(ctx.args[1]) ? ctx.args[1] : void 0;
|
|
694
|
+
const raw = await withTimeout(
|
|
695
|
+
readHookPayload(ctx.args, readStdin, harness === "copilot"),
|
|
696
|
+
stdinTimeoutMs,
|
|
697
|
+
""
|
|
698
|
+
);
|
|
699
|
+
const notifyStdinFile = process.env[NOTIFY_STDIN_FILE_ENV];
|
|
700
|
+
if (notifyStdinFile !== void 0 && (0, import_node_path2.dirname)(notifyStdinFile) === (0, import_node_os.tmpdir)() && (0, import_node_path2.basename)(notifyStdinFile).startsWith("birdybeep-notify-")) {
|
|
701
|
+
try {
|
|
702
|
+
(0, import_node_fs3.rmSync)(notifyStdinFile, { force: true });
|
|
703
|
+
} catch {
|
|
704
|
+
}
|
|
705
|
+
}
|
|
557
706
|
let payload;
|
|
558
707
|
try {
|
|
559
708
|
payload = JSON.parse(raw);
|
|
@@ -562,8 +711,15 @@ function createHookCommand(deps = {}) {
|
|
|
562
711
|
return EXIT.OK;
|
|
563
712
|
}
|
|
564
713
|
const sender = makeSender(resolveApiUrl());
|
|
565
|
-
const result = await runHookCommand(harness, payload, sender);
|
|
566
|
-
ctx.io.result({
|
|
714
|
+
const result = await runHookCommand(harness, payload, sender, copilotEventName);
|
|
715
|
+
ctx.io.result({
|
|
716
|
+
harness,
|
|
717
|
+
...copilotEventName !== void 0 ? { event: copilotEventName } : {},
|
|
718
|
+
outcome: result.outcome,
|
|
719
|
+
eventType: result.eventType,
|
|
720
|
+
...result.send?.decision ? { decision: result.send.decision } : {},
|
|
721
|
+
...result.send?.status !== void 0 ? { status: result.send.status } : {}
|
|
722
|
+
});
|
|
567
723
|
return EXIT.OK;
|
|
568
724
|
}
|
|
569
725
|
};
|
|
@@ -571,57 +727,73 @@ function createHookCommand(deps = {}) {
|
|
|
571
727
|
|
|
572
728
|
// src/commands/logout.ts
|
|
573
729
|
var import_agent_core6 = require("@birdybeep/agent-core");
|
|
574
|
-
|
|
730
|
+
var base = (apiUrl) => apiUrl.replace(/\/$/, "");
|
|
731
|
+
function createLogoutCommand(deps = {}) {
|
|
575
732
|
return {
|
|
576
|
-
name:
|
|
577
|
-
summary:
|
|
578
|
-
usage:
|
|
733
|
+
name: "logout",
|
|
734
|
+
summary: "Remove the local machine token (does NOT revoke the machine server-side)",
|
|
735
|
+
usage: "birdybeep logout",
|
|
579
736
|
run: async (ctx) => {
|
|
580
737
|
await (0, import_agent_core6.clearToken)(deps.tokenOptions ?? {});
|
|
581
|
-
ctx.io.emit(
|
|
738
|
+
ctx.io.emit("Logged out \u2014 the machine token was removed.", { loggedOut: true });
|
|
582
739
|
return EXIT.OK;
|
|
583
740
|
}
|
|
584
741
|
};
|
|
585
742
|
}
|
|
586
|
-
function
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
743
|
+
async function revokeSelf(token, fetchImpl, timeoutMs) {
|
|
744
|
+
const controller = new AbortController();
|
|
745
|
+
const timer = setTimeout(() => controller.abort(), timeoutMs);
|
|
746
|
+
try {
|
|
747
|
+
const res = await fetchImpl(`${base(resolveApiUrl())}/v1/machine/revoke-self`, {
|
|
748
|
+
method: "POST",
|
|
749
|
+
headers: { authorization: `Bearer ${token}` },
|
|
750
|
+
signal: controller.signal
|
|
751
|
+
});
|
|
752
|
+
if (res.ok || res.status === 403) return "revoked";
|
|
753
|
+
return "rejected";
|
|
754
|
+
} catch {
|
|
755
|
+
return "unreachable";
|
|
756
|
+
} finally {
|
|
757
|
+
clearTimeout(timer);
|
|
758
|
+
}
|
|
596
759
|
}
|
|
597
760
|
function createUnpairCommand(deps = {}) {
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
761
|
+
const fetchImpl = deps.fetchImpl ?? fetch;
|
|
762
|
+
const timeoutMs = deps.timeoutMs ?? 1e4;
|
|
763
|
+
return {
|
|
764
|
+
name: "unpair",
|
|
765
|
+
summary: "Unpair this machine \u2014 revoke it server-side and remove the local token",
|
|
766
|
+
usage: "birdybeep unpair",
|
|
767
|
+
run: async (ctx) => {
|
|
768
|
+
const token = await (0, import_agent_core6.getToken)(deps.tokenOptions ?? {});
|
|
769
|
+
const outcome = token === null ? "no_token" : await revokeSelf(token, fetchImpl, timeoutMs);
|
|
770
|
+
await (0, import_agent_core6.clearToken)(deps.tokenOptions ?? {});
|
|
771
|
+
const serverRevoked = outcome === "revoked";
|
|
772
|
+
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.";
|
|
773
|
+
ctx.io.emit(human, { unpaired: true, serverRevoked });
|
|
774
|
+
return EXIT.OK;
|
|
775
|
+
}
|
|
776
|
+
};
|
|
607
777
|
}
|
|
608
778
|
|
|
609
779
|
// src/commands/pair.ts
|
|
780
|
+
var import_node_fs4 = require("fs");
|
|
610
781
|
var import_agent_core8 = require("@birdybeep/agent-core");
|
|
611
782
|
var import_uqr = require("uqr");
|
|
612
783
|
|
|
613
784
|
// src/pairing.ts
|
|
614
785
|
var import_agent_core7 = require("@birdybeep/agent-core");
|
|
615
|
-
function
|
|
786
|
+
function base2(apiUrl) {
|
|
616
787
|
return apiUrl.replace(/\/$/, "");
|
|
617
788
|
}
|
|
618
789
|
async function pairStart(apiUrl, input, fetchImpl) {
|
|
619
790
|
const body = {
|
|
620
791
|
machine_label: input.machineLabel,
|
|
621
792
|
...input.os !== void 0 ? { os: input.os } : {},
|
|
622
|
-
...input.cliVersion !== void 0 ? { cli_version: input.cliVersion } : {}
|
|
793
|
+
...input.cliVersion !== void 0 ? { cli_version: input.cliVersion } : {},
|
|
794
|
+
...input.codeChallenge !== void 0 ? { code_challenge: input.codeChallenge } : {}
|
|
623
795
|
};
|
|
624
|
-
const res = await fetchImpl(`${
|
|
796
|
+
const res = await fetchImpl(`${base2(apiUrl)}/v1/pair/start`, {
|
|
625
797
|
method: "POST",
|
|
626
798
|
headers: { "content-type": "application/json" },
|
|
627
799
|
body: JSON.stringify(body)
|
|
@@ -639,12 +811,13 @@ var TERMINAL_TOKEN_ERRORS = /* @__PURE__ */ new Set([
|
|
|
639
811
|
"not_found",
|
|
640
812
|
"payload_too_large"
|
|
641
813
|
]);
|
|
642
|
-
async function pairTokenPoll(apiUrl, deviceCode, fetchImpl, machineFingerprint) {
|
|
814
|
+
async function pairTokenPoll(apiUrl, deviceCode, fetchImpl, machineFingerprint, codeVerifier) {
|
|
643
815
|
const body = {
|
|
644
816
|
device_code: deviceCode,
|
|
645
|
-
...machineFingerprint !== void 0 ? { machine_fingerprint: machineFingerprint } : {}
|
|
817
|
+
...machineFingerprint !== void 0 ? { machine_fingerprint: machineFingerprint } : {},
|
|
818
|
+
...codeVerifier !== void 0 ? { code_verifier: codeVerifier } : {}
|
|
646
819
|
};
|
|
647
|
-
const res = await fetchImpl(`${
|
|
820
|
+
const res = await fetchImpl(`${base2(apiUrl)}/v1/pair/token`, {
|
|
648
821
|
method: "POST",
|
|
649
822
|
headers: { "content-type": "application/json" },
|
|
650
823
|
body: JSON.stringify(body)
|
|
@@ -655,7 +828,10 @@ async function pairTokenPoll(apiUrl, deviceCode, fetchImpl, machineFingerprint)
|
|
|
655
828
|
return {
|
|
656
829
|
status: "paired",
|
|
657
830
|
machineToken: parsed.data.machine_token,
|
|
658
|
-
machineId: parsed.data.machine_id
|
|
831
|
+
machineId: parsed.data.machine_id,
|
|
832
|
+
// Only surface the key when the server reported it (exactOptionalPropertyTypes: no explicit
|
|
833
|
+
// undefined). Older servers omit approved_by_email; newer ones (dgxd) include it.
|
|
834
|
+
...parsed.data.approved_by_email !== void 0 ? { approvedByEmail: parsed.data.approved_by_email } : {}
|
|
659
835
|
};
|
|
660
836
|
}
|
|
661
837
|
let errBody = null;
|
|
@@ -676,7 +852,7 @@ async function pairTokenPoll(apiUrl, deviceCode, fetchImpl, machineFingerprint)
|
|
|
676
852
|
}
|
|
677
853
|
|
|
678
854
|
// src/version.ts
|
|
679
|
-
var CLI_VERSION = "0.
|
|
855
|
+
var CLI_VERSION = "0.3.0".length > 0 ? "0.3.0" : "0.0.0";
|
|
680
856
|
|
|
681
857
|
// src/commands/pair.ts
|
|
682
858
|
var DEFAULT_POLL_INTERVAL_MS = 2e3;
|
|
@@ -684,22 +860,172 @@ var HEARTBEAT_MS = 15e3;
|
|
|
684
860
|
function renderQrMatrix(qrPayload) {
|
|
685
861
|
return (0, import_uqr.renderUnicodeCompact)(qrPayload, { border: 2 });
|
|
686
862
|
}
|
|
863
|
+
function parsePairFlags(args) {
|
|
864
|
+
const flags = { yes: false };
|
|
865
|
+
for (let i = 0; i < args.length; i += 1) {
|
|
866
|
+
const token = args[i] ?? "";
|
|
867
|
+
if (token === "--yes" || token === "-y") {
|
|
868
|
+
flags.yes = true;
|
|
869
|
+
} else if (token === "--expect-email" || token.startsWith("--expect-email=")) {
|
|
870
|
+
const inline = token.startsWith("--expect-email=") ? token.slice("--expect-email=".length) : void 0;
|
|
871
|
+
const value = inline ?? args[++i];
|
|
872
|
+
if (value === void 0 || value.length === 0 || value.startsWith("-")) {
|
|
873
|
+
return { ...flags, error: "--expect-email requires an email address" };
|
|
874
|
+
}
|
|
875
|
+
flags.expectEmail = value;
|
|
876
|
+
} else {
|
|
877
|
+
return { ...flags, error: `unexpected argument "${token}"` };
|
|
878
|
+
}
|
|
879
|
+
}
|
|
880
|
+
return flags;
|
|
881
|
+
}
|
|
882
|
+
function sameEmail(a, b) {
|
|
883
|
+
const fold = (v) => v.trim().toLowerCase();
|
|
884
|
+
const rawEqual = fold(a) === fold(b);
|
|
885
|
+
const nfkcEqual = fold(a.normalize("NFKC")) === fold(b.normalize("NFKC"));
|
|
886
|
+
return rawEqual && nfkcEqual;
|
|
887
|
+
}
|
|
888
|
+
function decidePairConfirmation(input) {
|
|
889
|
+
const { approvedByEmail, expectEmail } = input;
|
|
890
|
+
const platform = input.platform ?? process.platform;
|
|
891
|
+
if (expectEmail !== void 0) {
|
|
892
|
+
if (approvedByEmail === void 0) {
|
|
893
|
+
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.";
|
|
894
|
+
return {
|
|
895
|
+
action: "reject",
|
|
896
|
+
reason: "expected_email_unverifiable",
|
|
897
|
+
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}`
|
|
898
|
+
};
|
|
899
|
+
}
|
|
900
|
+
if (sameEmail(approvedByEmail, expectEmail)) {
|
|
901
|
+
return { action: "approve", reason: "expected_email_match" };
|
|
902
|
+
}
|
|
903
|
+
return {
|
|
904
|
+
action: "reject",
|
|
905
|
+
reason: "expected_email_mismatch",
|
|
906
|
+
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\`.`
|
|
907
|
+
};
|
|
908
|
+
}
|
|
909
|
+
if (input.yes) return { action: "approve", reason: "yes_flag" };
|
|
910
|
+
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] ";
|
|
911
|
+
if (!input.nonInteractive) {
|
|
912
|
+
if (input.stdinIsTTY) return { action: "prompt", question, on: "stdin" };
|
|
913
|
+
if (input.controllingTerminalAvailable) {
|
|
914
|
+
return { action: "prompt", question, on: "controlling-terminal" };
|
|
915
|
+
}
|
|
916
|
+
}
|
|
917
|
+
const who = approvedByEmail !== void 0 ? ` (approved by ${approvedByEmail})` : "";
|
|
918
|
+
const winptyHint = platform === "win32" && !input.nonInteractive ? " In Git Bash / MSYS, `winpty birdybeep pair` attaches a real console so the prompt can appear." : "";
|
|
919
|
+
return {
|
|
920
|
+
action: "reject",
|
|
921
|
+
reason: "non_interactive",
|
|
922
|
+
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
|
|
923
|
+
};
|
|
924
|
+
}
|
|
925
|
+
function isAffirmative(answer) {
|
|
926
|
+
return /^(y|yes)$/i.test(answer.trim());
|
|
927
|
+
}
|
|
928
|
+
function controllingTerminalPath() {
|
|
929
|
+
return "/dev/tty";
|
|
930
|
+
}
|
|
931
|
+
function canOpenControllingTerminal(path = controllingTerminalPath(), platform = process.platform) {
|
|
932
|
+
if (platform === "win32") return false;
|
|
933
|
+
let fd;
|
|
934
|
+
try {
|
|
935
|
+
fd = (0, import_node_fs4.openSync)(path, "r");
|
|
936
|
+
return true;
|
|
937
|
+
} catch {
|
|
938
|
+
return false;
|
|
939
|
+
} finally {
|
|
940
|
+
if (fd !== void 0) {
|
|
941
|
+
try {
|
|
942
|
+
(0, import_node_fs4.closeSync)(fd);
|
|
943
|
+
} catch {
|
|
944
|
+
}
|
|
945
|
+
}
|
|
946
|
+
}
|
|
947
|
+
}
|
|
948
|
+
async function promptForAnswer(question, on) {
|
|
949
|
+
const { createInterface } = await import("readline/promises");
|
|
950
|
+
let ttyFd;
|
|
951
|
+
let input;
|
|
952
|
+
if (on === "stdin") {
|
|
953
|
+
input = process.stdin;
|
|
954
|
+
} else {
|
|
955
|
+
const { ReadStream } = await import("tty");
|
|
956
|
+
ttyFd = (0, import_node_fs4.openSync)(controllingTerminalPath(), "r");
|
|
957
|
+
input = new ReadStream(ttyFd);
|
|
958
|
+
}
|
|
959
|
+
return new Promise((resolve) => {
|
|
960
|
+
const rl = createInterface({ input, output: process.stderr });
|
|
961
|
+
let settled = false;
|
|
962
|
+
const done = (value) => {
|
|
963
|
+
if (settled) return;
|
|
964
|
+
settled = true;
|
|
965
|
+
rl.close();
|
|
966
|
+
if (on === "stdin") {
|
|
967
|
+
process.stdin.unref?.();
|
|
968
|
+
} else {
|
|
969
|
+
try {
|
|
970
|
+
input.unref?.();
|
|
971
|
+
input.destroy?.();
|
|
972
|
+
} catch {
|
|
973
|
+
}
|
|
974
|
+
if (ttyFd !== void 0) {
|
|
975
|
+
try {
|
|
976
|
+
(0, import_node_fs4.closeSync)(ttyFd);
|
|
977
|
+
} catch {
|
|
978
|
+
}
|
|
979
|
+
}
|
|
980
|
+
}
|
|
981
|
+
resolve(value);
|
|
982
|
+
};
|
|
983
|
+
rl.question(question).then(done, () => done(""));
|
|
984
|
+
rl.once("close", () => done(""));
|
|
985
|
+
input.once?.("error", () => done(""));
|
|
986
|
+
});
|
|
987
|
+
}
|
|
687
988
|
function createPairCommand(deps = {}) {
|
|
688
989
|
const fetchImpl = deps.fetchImpl ?? fetch;
|
|
689
990
|
const sleep = deps.sleep ?? ((ms) => new Promise((r) => setTimeout(r, ms)));
|
|
690
991
|
const clock = deps.now ?? (() => Date.now());
|
|
691
992
|
const renderQr = deps.renderQr ?? renderQrMatrix;
|
|
692
993
|
const intervalMs = deps.pollIntervalMs ?? DEFAULT_POLL_INTERVAL_MS;
|
|
994
|
+
const promptLine = deps.promptLine ?? promptForAnswer;
|
|
995
|
+
const hasControllingTerminal = deps.hasControllingTerminal ?? (() => canOpenControllingTerminal());
|
|
996
|
+
const configuredExpectEmail = deps.configuredExpectEmail ?? (() => {
|
|
997
|
+
const pinned = readCliConfig().expectEmail;
|
|
998
|
+
return typeof pinned === "string" && pinned.trim().length > 0 ? pinned : void 0;
|
|
999
|
+
});
|
|
693
1000
|
return {
|
|
694
1001
|
name: "pair",
|
|
695
1002
|
summary: "Pair this machine with your BirdyBeep account (QR or manual)",
|
|
696
|
-
usage: "birdybeep pair [--json]",
|
|
1003
|
+
usage: "birdybeep pair [--yes] [--expect-email <addr>] [--json]",
|
|
1004
|
+
options: [
|
|
1005
|
+
{
|
|
1006
|
+
flag: "--yes",
|
|
1007
|
+
aliases: ["-y"],
|
|
1008
|
+
summary: "Skip the approving-account confirmation (headless/CI)"
|
|
1009
|
+
},
|
|
1010
|
+
{
|
|
1011
|
+
flag: "--expect-email",
|
|
1012
|
+
value: "<addr>",
|
|
1013
|
+
summary: "Only trust the pairing if this account approved it (else fail)"
|
|
1014
|
+
}
|
|
1015
|
+
],
|
|
697
1016
|
run: async (ctx) => {
|
|
1017
|
+
const pairFlags = parsePairFlags(ctx.args);
|
|
1018
|
+
if (pairFlags.error !== void 0) {
|
|
1019
|
+
ctx.io.errline(`birdybeep pair: ${pairFlags.error}.`);
|
|
1020
|
+
return EXIT.USAGE;
|
|
1021
|
+
}
|
|
698
1022
|
const apiUrl = resolveApiUrl();
|
|
699
1023
|
const identity = (0, import_agent_core8.getMachineIdentity)();
|
|
1024
|
+
const codeVerifier = (0, import_agent_core8.generateCodeVerifier)();
|
|
1025
|
+
const codeChallenge = (0, import_agent_core8.deriveCodeChallengeS256)(codeVerifier);
|
|
700
1026
|
const start = await pairStart(
|
|
701
1027
|
apiUrl,
|
|
702
|
-
{ machineLabel: identity.label, os: identity.os, cliVersion: CLI_VERSION },
|
|
1028
|
+
{ machineLabel: identity.label, os: identity.os, cliVersion: CLI_VERSION, codeChallenge },
|
|
703
1029
|
fetchImpl
|
|
704
1030
|
);
|
|
705
1031
|
if (ctx.flags.json) {
|
|
@@ -711,12 +1037,14 @@ function createPairCommand(deps = {}) {
|
|
|
711
1037
|
});
|
|
712
1038
|
} else {
|
|
713
1039
|
ctx.io.line(
|
|
714
|
-
"To pair this machine, open the BirdyBeep app, tap \u201Cpair a machine\u201D, and scan this QR
|
|
1040
|
+
"To pair this machine, open the BirdyBeep app, tap \u201Cpair a machine\u201D, and scan this QR or open the complete link:"
|
|
715
1041
|
);
|
|
716
1042
|
const isTTY = deps.isTTY ?? process.stdout.isTTY === true;
|
|
717
1043
|
if (isTTY) ctx.io.line(renderQr(start.qr_payload));
|
|
718
1044
|
ctx.io.line(` Scan or open: ${start.qr_payload}`);
|
|
719
|
-
ctx.io.line(
|
|
1045
|
+
ctx.io.line(
|
|
1046
|
+
` Session code (display only; cannot approve by itself): ${start.user_code}`
|
|
1047
|
+
);
|
|
720
1048
|
ctx.io.line("Waiting for you to approve this machine in the app\u2026");
|
|
721
1049
|
}
|
|
722
1050
|
const deadline = Date.parse(start.expires_at);
|
|
@@ -732,7 +1060,9 @@ function createPairCommand(deps = {}) {
|
|
|
732
1060
|
apiUrl,
|
|
733
1061
|
start.device_code,
|
|
734
1062
|
fetchImpl,
|
|
735
|
-
identity.fingerprintHash
|
|
1063
|
+
identity.fingerprintHash,
|
|
1064
|
+
codeVerifier
|
|
1065
|
+
// PKCE proof-of-possession (dgxd) — sent on every poll
|
|
736
1066
|
);
|
|
737
1067
|
if (poll.status === "paired") {
|
|
738
1068
|
paired = poll;
|
|
@@ -757,15 +1087,44 @@ function createPairCommand(deps = {}) {
|
|
|
757
1087
|
if (paired === void 0 || paired.status !== "paired") {
|
|
758
1088
|
ctx.io.result({ paired: false, reason: "timeout" });
|
|
759
1089
|
ctx.io.errline(
|
|
760
|
-
"Pairing timed out before you approved it. In the BirdyBeep app, tap \u201Cpair a machine\u201D, scan
|
|
1090
|
+
"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."
|
|
1091
|
+
);
|
|
1092
|
+
return EXIT.ERROR;
|
|
1093
|
+
}
|
|
1094
|
+
const approvedBy = paired.approvedByEmail;
|
|
1095
|
+
const expectEmail = pairFlags.expectEmail ?? configuredExpectEmail();
|
|
1096
|
+
const stdinIsTTY = deps.isStdinTTY ?? process.stdin.isTTY === true;
|
|
1097
|
+
const decision = decidePairConfirmation({
|
|
1098
|
+
...approvedBy !== void 0 ? { approvedByEmail: approvedBy } : {},
|
|
1099
|
+
...expectEmail !== void 0 ? { expectEmail } : {},
|
|
1100
|
+
...expectEmail !== void 0 ? { expectEmailSource: pairFlags.expectEmail !== void 0 ? "flag" : "config" } : {},
|
|
1101
|
+
yes: pairFlags.yes,
|
|
1102
|
+
nonInteractive: ctx.flags.nonInteractive,
|
|
1103
|
+
stdinIsTTY,
|
|
1104
|
+
// Probed ONLY when stdin can't answer — opening /dev/tty is a syscall, and when stdin is
|
|
1105
|
+
// already a terminal the answer is irrelevant.
|
|
1106
|
+
controllingTerminalAvailable: stdinIsTTY ? false : hasControllingTerminal(),
|
|
1107
|
+
configPath: cliConfigPath()
|
|
1108
|
+
});
|
|
1109
|
+
if (decision.action === "reject") {
|
|
1110
|
+
ctx.io.result({ paired: false, reason: decision.reason });
|
|
1111
|
+
ctx.io.errline(decision.message);
|
|
1112
|
+
return EXIT.ERROR;
|
|
1113
|
+
}
|
|
1114
|
+
if (decision.action === "prompt" && !isAffirmative(await promptLine(decision.question, decision.on))) {
|
|
1115
|
+
ctx.io.result({ paired: false, reason: "declined" });
|
|
1116
|
+
ctx.io.errline(
|
|
1117
|
+
"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."
|
|
761
1118
|
);
|
|
762
1119
|
return EXIT.ERROR;
|
|
763
1120
|
}
|
|
764
1121
|
await (0, import_agent_core8.setToken)(paired.machineToken, deps.tokenOptions ?? {});
|
|
765
1122
|
writeCliConfig({ apiUrl });
|
|
766
|
-
|
|
1123
|
+
const humanSuffix = approvedBy !== void 0 ? ` to ${approvedBy}` : "";
|
|
1124
|
+
ctx.io.emit(`\u2713 Paired${humanSuffix}. Run \`birdybeep test\` to send a test Beep.`, {
|
|
767
1125
|
paired: true,
|
|
768
|
-
machineId: paired.machineId
|
|
1126
|
+
machineId: paired.machineId,
|
|
1127
|
+
...approvedBy !== void 0 ? { approvedByEmail: approvedBy } : {}
|
|
769
1128
|
});
|
|
770
1129
|
return EXIT.OK;
|
|
771
1130
|
}
|
|
@@ -798,14 +1157,24 @@ function createQueueCommand() {
|
|
|
798
1157
|
var import_agent_core10 = require("@birdybeep/agent-core");
|
|
799
1158
|
var import_claude_code4 = require("@birdybeep/claude-code");
|
|
800
1159
|
var import_codex4 = require("@birdybeep/codex");
|
|
1160
|
+
var import_copilot4 = require("@birdybeep/copilot");
|
|
1161
|
+
var import_cursor4 = require("@birdybeep/cursor");
|
|
801
1162
|
var import_opencode4 = require("@birdybeep/opencode");
|
|
802
|
-
var DEFAULT_ADAPTERS3 = [
|
|
1163
|
+
var DEFAULT_ADAPTERS3 = [
|
|
1164
|
+
import_claude_code4.claudeCodeAdapter,
|
|
1165
|
+
import_codex4.codexAdapter,
|
|
1166
|
+
import_opencode4.opencodeAdapter,
|
|
1167
|
+
import_cursor4.cursorAdapter,
|
|
1168
|
+
import_copilot4.copilotAdapter
|
|
1169
|
+
];
|
|
803
1170
|
var ADAPTER_VERSIONS = {
|
|
804
1171
|
claude_code: import_claude_code4.CLAUDE_CODE_ADAPTER_VERSION,
|
|
805
1172
|
codex: import_codex4.CODEX_ADAPTER_VERSION,
|
|
806
|
-
opencode: import_opencode4.OPENCODE_ADAPTER_VERSION
|
|
1173
|
+
opencode: import_opencode4.OPENCODE_ADAPTER_VERSION,
|
|
1174
|
+
cursor: import_cursor4.CURSOR_ADAPTER_VERSION,
|
|
1175
|
+
copilot: import_copilot4.COPILOT_ADAPTER_VERSION
|
|
807
1176
|
};
|
|
808
|
-
var
|
|
1177
|
+
var base3 = (apiUrl) => apiUrl.replace(/\/$/, "");
|
|
809
1178
|
async function gatherItems(adapters) {
|
|
810
1179
|
return Promise.all(
|
|
811
1180
|
adapters.map(async (a) => {
|
|
@@ -840,7 +1209,7 @@ function createReportStatusCommand(deps = {}) {
|
|
|
840
1209
|
let outcome = "deferred";
|
|
841
1210
|
let errorCode;
|
|
842
1211
|
try {
|
|
843
|
-
const res = await fetchImpl(`${
|
|
1212
|
+
const res = await fetchImpl(`${base3(resolveApiUrl())}/v1/integrations/status`, {
|
|
844
1213
|
method: "POST",
|
|
845
1214
|
headers: { authorization: `Bearer ${token}`, "content-type": "application/json" },
|
|
846
1215
|
body: JSON.stringify({ integrations: items })
|
|
@@ -891,8 +1260,16 @@ function createReportStatusCommand(deps = {}) {
|
|
|
891
1260
|
var import_agent_core11 = require("@birdybeep/agent-core");
|
|
892
1261
|
var import_claude_code5 = require("@birdybeep/claude-code");
|
|
893
1262
|
var import_codex5 = require("@birdybeep/codex");
|
|
1263
|
+
var import_copilot5 = require("@birdybeep/copilot");
|
|
1264
|
+
var import_cursor5 = require("@birdybeep/cursor");
|
|
894
1265
|
var import_opencode5 = require("@birdybeep/opencode");
|
|
895
|
-
var DEFAULT_ADAPTERS4 = [
|
|
1266
|
+
var DEFAULT_ADAPTERS4 = [
|
|
1267
|
+
import_claude_code5.claudeCodeAdapter,
|
|
1268
|
+
import_codex5.codexAdapter,
|
|
1269
|
+
import_opencode5.opencodeAdapter,
|
|
1270
|
+
import_cursor5.cursorAdapter,
|
|
1271
|
+
import_copilot5.copilotAdapter
|
|
1272
|
+
];
|
|
896
1273
|
function createStatusCommand(deps = {}) {
|
|
897
1274
|
const adapters = deps.adapters ?? DEFAULT_ADAPTERS4;
|
|
898
1275
|
const makeSender = deps.createSender ?? ((baseUrl) => (0, import_agent_core11.createSender)(
|
|
@@ -932,7 +1309,7 @@ function createStatusCommand(deps = {}) {
|
|
|
932
1309
|
}
|
|
933
1310
|
|
|
934
1311
|
// src/commands/test.ts
|
|
935
|
-
var
|
|
1312
|
+
var import_node_crypto2 = require("crypto");
|
|
936
1313
|
var import_agent_core12 = require("@birdybeep/agent-core");
|
|
937
1314
|
function buildTestEvent(opts = {}) {
|
|
938
1315
|
const machine = (0, import_agent_core12.getMachineIdentity)();
|
|
@@ -944,7 +1321,7 @@ function buildTestEvent(opts = {}) {
|
|
|
944
1321
|
// schema requires a harness; the "test" type distinguishes it
|
|
945
1322
|
// Unique per run: a repeat `birdybeep test` inside the backend's dedupe window must
|
|
946
1323
|
// still beep — a constant id made the second test silently "deduped" (9fh).
|
|
947
|
-
source_session_id: `birdybeep-cli-test-${(0,
|
|
1324
|
+
source_session_id: `birdybeep-cli-test-${(0, import_node_crypto2.randomUUID)()}`,
|
|
948
1325
|
machine: { label: machine.label, os: machine.os },
|
|
949
1326
|
workspace: { cwd: process.cwd() },
|
|
950
1327
|
title: "BirdyBeep test event",
|
|
@@ -1014,8 +1391,8 @@ function buildCommands() {
|
|
|
1014
1391
|
}
|
|
1015
1392
|
|
|
1016
1393
|
// src/update-check.ts
|
|
1017
|
-
var
|
|
1018
|
-
var
|
|
1394
|
+
var import_node_fs5 = require("fs");
|
|
1395
|
+
var import_node_path3 = require("path");
|
|
1019
1396
|
var import_agent_core13 = require("@birdybeep/agent-core");
|
|
1020
1397
|
var PACKAGE_NAME = "@birdybeep/cli";
|
|
1021
1398
|
var PACKAGE_PATH = "@birdybeep%2Fcli";
|
|
@@ -1070,11 +1447,11 @@ function isNewer(current, latest) {
|
|
|
1070
1447
|
return cur !== null && lat !== null && compareSemver(cur, lat) < 0;
|
|
1071
1448
|
}
|
|
1072
1449
|
function updateCachePath() {
|
|
1073
|
-
return (0,
|
|
1450
|
+
return (0, import_node_path3.join)((0, import_agent_core13.birdyBeepConfigDir)(), UPDATE_CACHE_FILE);
|
|
1074
1451
|
}
|
|
1075
1452
|
function readUpdateCache() {
|
|
1076
1453
|
try {
|
|
1077
|
-
const parsed = JSON.parse((0,
|
|
1454
|
+
const parsed = JSON.parse((0, import_node_fs5.readFileSync)(updateCachePath(), "utf8"));
|
|
1078
1455
|
if (typeof parsed !== "object" || parsed === null) return null;
|
|
1079
1456
|
const { checkedAt, latest } = parsed;
|
|
1080
1457
|
if (typeof checkedAt !== "number") return null;
|
|
@@ -1085,8 +1462,8 @@ function readUpdateCache() {
|
|
|
1085
1462
|
}
|
|
1086
1463
|
}
|
|
1087
1464
|
function writeUpdateCache(cache) {
|
|
1088
|
-
(0,
|
|
1089
|
-
(0,
|
|
1465
|
+
(0, import_node_fs5.mkdirSync)((0, import_agent_core13.birdyBeepConfigDir)(), { recursive: true, mode: 448 });
|
|
1466
|
+
(0, import_node_fs5.writeFileSync)(updateCachePath(), `${JSON.stringify(cache)}
|
|
1090
1467
|
`, { mode: 384 });
|
|
1091
1468
|
}
|
|
1092
1469
|
async function fetchLatestVersion(registryUrl, fetchImpl, timeoutMs) {
|