@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/bin.cjs
CHANGED
|
@@ -1,9 +1,33 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
"use strict";
|
|
3
|
+
var __create = Object.create;
|
|
4
|
+
var __defProp = Object.defineProperty;
|
|
5
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
6
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
7
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
8
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
9
|
+
var __copyProps = (to, from, except, desc) => {
|
|
10
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
11
|
+
for (let key of __getOwnPropNames(from))
|
|
12
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
13
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
14
|
+
}
|
|
15
|
+
return to;
|
|
16
|
+
};
|
|
17
|
+
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
|
18
|
+
// If the importer is in node compatibility mode or this is not an ESM
|
|
19
|
+
// file that has been converted to a CommonJS file using a Babel-
|
|
20
|
+
// compatible transform (i.e. "__esModule" has not been set), then set
|
|
21
|
+
// "default" to the CommonJS "module.exports" for node compatibility.
|
|
22
|
+
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
|
23
|
+
mod
|
|
24
|
+
));
|
|
3
25
|
|
|
4
26
|
// src/commands/agent.ts
|
|
5
27
|
var import_claude_code = require("@birdybeep/claude-code");
|
|
6
28
|
var import_codex = require("@birdybeep/codex");
|
|
29
|
+
var import_copilot = require("@birdybeep/copilot");
|
|
30
|
+
var import_cursor = require("@birdybeep/cursor");
|
|
7
31
|
var import_opencode = require("@birdybeep/opencode");
|
|
8
32
|
|
|
9
33
|
// src/framework.ts
|
|
@@ -72,8 +96,11 @@ function parseGlobalFlags(argv) {
|
|
|
72
96
|
}
|
|
73
97
|
return { flags, rest };
|
|
74
98
|
}
|
|
75
|
-
function isUnknownFlag(token) {
|
|
76
|
-
|
|
99
|
+
function isUnknownFlag(token, allowed) {
|
|
100
|
+
if (!token.startsWith("-")) return false;
|
|
101
|
+
if (GLOBAL_FLAG_TOKENS.has(token)) return false;
|
|
102
|
+
const eq = token.indexOf("=");
|
|
103
|
+
return !allowed.has(eq >= 0 ? token.slice(0, eq) : token);
|
|
77
104
|
}
|
|
78
105
|
function renderRootHelp(version, commands) {
|
|
79
106
|
const width = Math.max(...commands.map((c) => c.name.length));
|
|
@@ -94,6 +121,16 @@ function renderRootHelp(version, commands) {
|
|
|
94
121
|
" -v, --version Show the CLI version"
|
|
95
122
|
].join("\n");
|
|
96
123
|
}
|
|
124
|
+
function commandFlagTokens(...commands) {
|
|
125
|
+
const tokens = /* @__PURE__ */ new Set();
|
|
126
|
+
for (const command of commands) {
|
|
127
|
+
for (const option of command?.options ?? []) {
|
|
128
|
+
tokens.add(option.flag);
|
|
129
|
+
for (const alias of option.aliases ?? []) tokens.add(alias);
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
return tokens;
|
|
133
|
+
}
|
|
97
134
|
function renderCommandHelp(path, command) {
|
|
98
135
|
const lines = [
|
|
99
136
|
`birdybeep ${path} \u2014 ${command.summary}`,
|
|
@@ -101,6 +138,17 @@ function renderCommandHelp(path, command) {
|
|
|
101
138
|
"Usage:",
|
|
102
139
|
` ${command.usage ?? `birdybeep ${path} [options]`}`
|
|
103
140
|
];
|
|
141
|
+
if (command.options && command.options.length > 0) {
|
|
142
|
+
const labels = command.options.map(
|
|
143
|
+
(o) => `${[o.flag, ...o.aliases ?? []].join(", ")}${o.value ? ` ${o.value}` : ""}`
|
|
144
|
+
);
|
|
145
|
+
const width = Math.max(...labels.map((l) => l.length));
|
|
146
|
+
lines.push(
|
|
147
|
+
"",
|
|
148
|
+
"Options:",
|
|
149
|
+
...command.options.map((o, i) => ` ${labels[i]?.padEnd(width)} ${o.summary}`)
|
|
150
|
+
);
|
|
151
|
+
}
|
|
104
152
|
if (command.subcommands && command.subcommands.length > 0) {
|
|
105
153
|
const width = Math.max(...command.subcommands.map((c) => c.name.length));
|
|
106
154
|
lines.push(
|
|
@@ -125,6 +173,7 @@ async function dispatch(argv, deps) {
|
|
|
125
173
|
return EXIT.OK;
|
|
126
174
|
}
|
|
127
175
|
let command = deps.commands.find((c) => c.name === rest[0]);
|
|
176
|
+
let parent;
|
|
128
177
|
const pathParts = [];
|
|
129
178
|
let argsStart = 1;
|
|
130
179
|
if (command) {
|
|
@@ -132,6 +181,7 @@ async function dispatch(argv, deps) {
|
|
|
132
181
|
if (command.subcommands && command.subcommands.length > 0) {
|
|
133
182
|
const sub = command.subcommands.find((c) => c.name === rest[1]);
|
|
134
183
|
if (sub) {
|
|
184
|
+
parent = command;
|
|
135
185
|
command = sub;
|
|
136
186
|
pathParts.push(sub.name);
|
|
137
187
|
argsStart = 2;
|
|
@@ -155,6 +205,7 @@ async function dispatch(argv, deps) {
|
|
|
155
205
|
name: path,
|
|
156
206
|
summary: command.summary,
|
|
157
207
|
usage: command.usage,
|
|
208
|
+
options: command.options,
|
|
158
209
|
subcommands: command.subcommands?.map((c) => ({ name: c.name, summary: c.summary }))
|
|
159
210
|
});
|
|
160
211
|
return EXIT.OK;
|
|
@@ -164,7 +215,8 @@ async function dispatch(argv, deps) {
|
|
|
164
215
|
return EXIT.USAGE;
|
|
165
216
|
}
|
|
166
217
|
const args = rest.slice(argsStart);
|
|
167
|
-
const
|
|
218
|
+
const allowed = commandFlagTokens(command, parent);
|
|
219
|
+
const unknown = args.find((token) => isUnknownFlag(token, allowed));
|
|
168
220
|
if (unknown !== void 0) {
|
|
169
221
|
io.errline(`birdybeep ${path}: unknown option "${unknown}".`);
|
|
170
222
|
return EXIT.USAGE;
|
|
@@ -192,13 +244,28 @@ async function dispatch(argv, deps) {
|
|
|
192
244
|
}
|
|
193
245
|
|
|
194
246
|
// src/commands/agent.ts
|
|
195
|
-
var DEFAULT_ADAPTERS = [
|
|
247
|
+
var DEFAULT_ADAPTERS = [
|
|
248
|
+
import_claude_code.claudeCodeAdapter,
|
|
249
|
+
import_codex.codexAdapter,
|
|
250
|
+
import_opencode.opencodeAdapter,
|
|
251
|
+
import_cursor.cursorAdapter,
|
|
252
|
+
import_copilot.copilotAdapter
|
|
253
|
+
];
|
|
196
254
|
var TARGET_TO_ID = {
|
|
197
255
|
claude: "claude_code",
|
|
198
256
|
codex: "codex",
|
|
199
|
-
opencode: "opencode"
|
|
257
|
+
opencode: "opencode",
|
|
258
|
+
cursor: "cursor",
|
|
259
|
+
copilot: "copilot"
|
|
200
260
|
};
|
|
201
|
-
var AGENT_TARGETS = [
|
|
261
|
+
var AGENT_TARGETS = [
|
|
262
|
+
"all",
|
|
263
|
+
"claude",
|
|
264
|
+
"codex",
|
|
265
|
+
"opencode",
|
|
266
|
+
"cursor",
|
|
267
|
+
"copilot"
|
|
268
|
+
];
|
|
202
269
|
function selectAdapters(target, adapters) {
|
|
203
270
|
if (target === "all") return adapters;
|
|
204
271
|
const id = TARGET_TO_ID[target];
|
|
@@ -289,18 +356,18 @@ function createAgentCommand(deps = {}) {
|
|
|
289
356
|
return {
|
|
290
357
|
name: "agent",
|
|
291
358
|
summary: "Install or uninstall harness adapters",
|
|
292
|
-
usage: "birdybeep agent <install|uninstall> [all|claude|codex|opencode]",
|
|
359
|
+
usage: "birdybeep agent <install|uninstall> [all|claude|codex|opencode|cursor|copilot]",
|
|
293
360
|
subcommands: [
|
|
294
361
|
{
|
|
295
362
|
name: "install",
|
|
296
|
-
summary: "Install adapters (all | claude | codex | opencode)",
|
|
297
|
-
usage: "birdybeep agent install [all|claude|codex|opencode]",
|
|
363
|
+
summary: "Install adapters (all | claude | codex | opencode | cursor | copilot)",
|
|
364
|
+
usage: "birdybeep agent install [all|claude|codex|opencode|cursor|copilot]",
|
|
298
365
|
run: (ctx) => installSelected(adapters, ctx)
|
|
299
366
|
},
|
|
300
367
|
{
|
|
301
368
|
name: "uninstall",
|
|
302
369
|
summary: "Restore harness config to its pre-install state",
|
|
303
|
-
usage: "birdybeep agent uninstall [all|claude|codex|opencode]",
|
|
370
|
+
usage: "birdybeep agent uninstall [all|claude|codex|opencode|cursor|copilot]",
|
|
304
371
|
run: (ctx) => uninstallSelected(adapters, ctx)
|
|
305
372
|
}
|
|
306
373
|
]
|
|
@@ -311,6 +378,8 @@ function createAgentCommand(deps = {}) {
|
|
|
311
378
|
var import_agent_core4 = require("@birdybeep/agent-core");
|
|
312
379
|
var import_claude_code2 = require("@birdybeep/claude-code");
|
|
313
380
|
var import_codex2 = require("@birdybeep/codex");
|
|
381
|
+
var import_copilot2 = require("@birdybeep/copilot");
|
|
382
|
+
var import_cursor2 = require("@birdybeep/cursor");
|
|
314
383
|
var import_opencode2 = require("@birdybeep/opencode");
|
|
315
384
|
|
|
316
385
|
// src/config.ts
|
|
@@ -335,6 +404,8 @@ function writeCliConfig(patch) {
|
|
|
335
404
|
const merged = {};
|
|
336
405
|
const apiUrl = patch.apiUrl ?? current.apiUrl;
|
|
337
406
|
if (apiUrl !== void 0) merged.apiUrl = apiUrl;
|
|
407
|
+
const expectEmail = patch.expectEmail ?? current.expectEmail;
|
|
408
|
+
if (expectEmail !== void 0) merged.expectEmail = expectEmail;
|
|
338
409
|
(0, import_node_fs2.mkdirSync)((0, import_agent_core2.birdyBeepConfigDir)(), { recursive: true, mode: 448 });
|
|
339
410
|
(0, import_node_fs2.writeFileSync)(cliConfigPath(), `${JSON.stringify(merged, null, 2)}
|
|
340
411
|
`, { mode: 384 });
|
|
@@ -373,7 +444,13 @@ function machineIdentity() {
|
|
|
373
444
|
}
|
|
374
445
|
|
|
375
446
|
// src/commands/doctor.ts
|
|
376
|
-
var DEFAULT_ADAPTERS2 = [
|
|
447
|
+
var DEFAULT_ADAPTERS2 = [
|
|
448
|
+
import_claude_code2.claudeCodeAdapter,
|
|
449
|
+
import_codex2.codexAdapter,
|
|
450
|
+
import_opencode2.opencodeAdapter,
|
|
451
|
+
import_cursor2.cursorAdapter,
|
|
452
|
+
import_copilot2.copilotAdapter
|
|
453
|
+
];
|
|
377
454
|
async function defaultProbeNetwork(baseUrl) {
|
|
378
455
|
try {
|
|
379
456
|
const controller = new AbortController();
|
|
@@ -456,16 +533,30 @@ function createDoctorCommand(deps = {}) {
|
|
|
456
533
|
}
|
|
457
534
|
|
|
458
535
|
// src/commands/hook.ts
|
|
536
|
+
var import_node_child_process = require("child_process");
|
|
537
|
+
var import_node_crypto = require("crypto");
|
|
538
|
+
var import_node_fs3 = require("fs");
|
|
539
|
+
var import_node_os = require("os");
|
|
540
|
+
var import_node_path2 = require("path");
|
|
459
541
|
var import_agent_core5 = require("@birdybeep/agent-core");
|
|
460
542
|
var import_claude_code3 = require("@birdybeep/claude-code");
|
|
461
543
|
var import_codex3 = require("@birdybeep/codex");
|
|
544
|
+
var import_copilot3 = require("@birdybeep/copilot");
|
|
545
|
+
var import_cursor3 = require("@birdybeep/cursor");
|
|
462
546
|
var import_opencode3 = require("@birdybeep/opencode");
|
|
463
547
|
var RUNNERS = {
|
|
464
548
|
claude: import_claude_code3.runClaudeHook,
|
|
465
549
|
codex: import_codex3.runCodexHook,
|
|
466
|
-
opencode: import_opencode3.runOpenCodeHook
|
|
550
|
+
opencode: import_opencode3.runOpenCodeHook,
|
|
551
|
+
cursor: import_cursor3.runCursorHook
|
|
467
552
|
};
|
|
468
|
-
var HOOK_HARNESSES = [
|
|
553
|
+
var HOOK_HARNESSES = [
|
|
554
|
+
"claude",
|
|
555
|
+
"codex",
|
|
556
|
+
"opencode",
|
|
557
|
+
"cursor",
|
|
558
|
+
"copilot"
|
|
559
|
+
];
|
|
469
560
|
var STDIN_READ_TIMEOUT_MS = 3e3;
|
|
470
561
|
function withTimeout(promise, ms, fallback) {
|
|
471
562
|
return new Promise((resolve) => {
|
|
@@ -482,9 +573,13 @@ function withTimeout(promise, ms, fallback) {
|
|
|
482
573
|
});
|
|
483
574
|
}
|
|
484
575
|
function isHarnessName(value) {
|
|
485
|
-
return value === "claude" || value === "codex" || value === "opencode";
|
|
576
|
+
return value === "claude" || value === "codex" || value === "opencode" || value === "cursor" || value === "copilot";
|
|
486
577
|
}
|
|
487
|
-
function runHookCommand(harness, payload, sender) {
|
|
578
|
+
function runHookCommand(harness, payload, sender, copilotEventName) {
|
|
579
|
+
if (harness === "copilot") {
|
|
580
|
+
if (copilotEventName === void 0) return Promise.resolve({ outcome: "skipped" });
|
|
581
|
+
return (0, import_copilot3.runCopilotHook)(copilotEventName, payload, { sender });
|
|
582
|
+
}
|
|
488
583
|
return RUNNERS[harness](payload, { sender });
|
|
489
584
|
}
|
|
490
585
|
function readStdinDefault() {
|
|
@@ -500,24 +595,90 @@ function readStdinDefault() {
|
|
|
500
595
|
process.stdin.on("error", () => resolve(""));
|
|
501
596
|
});
|
|
502
597
|
}
|
|
503
|
-
async function readHookPayload(args, readStdin) {
|
|
504
|
-
return args[1] ?? await readStdin();
|
|
598
|
+
async function readHookPayload(args, readStdin, stdinOnly = false) {
|
|
599
|
+
return stdinOnly ? readStdin() : args[1] ?? await readStdin();
|
|
600
|
+
}
|
|
601
|
+
var NOTIFY_STDIN_FILE_ENV = "BIRDYBEEP_CODEX_NOTIFY_STDIN_FILE";
|
|
602
|
+
function detachCodexNotifyWorker(payload) {
|
|
603
|
+
if (process.platform === "win32") return false;
|
|
604
|
+
let file;
|
|
605
|
+
let fd;
|
|
606
|
+
try {
|
|
607
|
+
const birdybeep = (0, import_agent_core5.resolveOnPath)("birdybeep");
|
|
608
|
+
if (birdybeep === null) return false;
|
|
609
|
+
const tmpFile = (0, import_node_path2.join)((0, import_node_os.tmpdir)(), `birdybeep-notify-${(0, import_node_crypto.randomBytes)(16).toString("hex")}.json`);
|
|
610
|
+
file = tmpFile;
|
|
611
|
+
(0, import_node_fs3.writeFileSync)(tmpFile, payload, { mode: 384 });
|
|
612
|
+
fd = (0, import_node_fs3.openSync)(tmpFile, "r");
|
|
613
|
+
const child = (0, import_node_child_process.spawn)(birdybeep, ["hook", "codex"], {
|
|
614
|
+
cwd: (0, import_node_path2.dirname)(birdybeep),
|
|
615
|
+
// trusted dir, never the inherited/attacker cwd
|
|
616
|
+
detached: true,
|
|
617
|
+
// new session (setsid) → survives `codex exec` reaping the group
|
|
618
|
+
stdio: [fd, "ignore", "ignore"],
|
|
619
|
+
// stdin = the temp file; this process holds no pipe
|
|
620
|
+
env: { ...process.env, [NOTIFY_STDIN_FILE_ENV]: tmpFile },
|
|
621
|
+
// worker cleans it up post-read
|
|
622
|
+
windowsHide: true
|
|
623
|
+
});
|
|
624
|
+
child.on("error", () => {
|
|
625
|
+
try {
|
|
626
|
+
(0, import_node_fs3.rmSync)(tmpFile, { force: true });
|
|
627
|
+
} catch {
|
|
628
|
+
}
|
|
629
|
+
});
|
|
630
|
+
child.unref();
|
|
631
|
+
return true;
|
|
632
|
+
} catch {
|
|
633
|
+
if (file !== void 0) {
|
|
634
|
+
try {
|
|
635
|
+
(0, import_node_fs3.rmSync)(file, { force: true });
|
|
636
|
+
} catch {
|
|
637
|
+
}
|
|
638
|
+
}
|
|
639
|
+
return false;
|
|
640
|
+
} finally {
|
|
641
|
+
if (fd !== void 0) {
|
|
642
|
+
try {
|
|
643
|
+
(0, import_node_fs3.closeSync)(fd);
|
|
644
|
+
} catch {
|
|
645
|
+
}
|
|
646
|
+
}
|
|
647
|
+
}
|
|
505
648
|
}
|
|
506
649
|
function createHookCommand(deps = {}) {
|
|
507
650
|
const makeSender = deps.createSender ?? ((baseUrl) => (0, import_agent_core5.createSender)({ baseUrl }));
|
|
508
651
|
const readStdin = deps.readStdin ?? readStdinDefault;
|
|
509
652
|
const stdinTimeoutMs = deps.stdinTimeoutMs ?? STDIN_READ_TIMEOUT_MS;
|
|
653
|
+
const detachCodexNotify = deps.detachCodexNotify ?? detachCodexNotifyWorker;
|
|
510
654
|
return {
|
|
511
655
|
name: "hook",
|
|
512
656
|
summary: "Internal: normalize + send an event fired by a harness hook",
|
|
513
|
-
usage: "birdybeep hook <claude|codex|opencode>",
|
|
657
|
+
usage: "birdybeep hook <claude|codex|opencode|cursor|copilot> [copilot-event]",
|
|
514
658
|
run: async (ctx) => {
|
|
515
659
|
const harness = ctx.args[0];
|
|
516
660
|
if (!isHarnessName(harness)) {
|
|
517
661
|
ctx.io.errline(`birdybeep hook: expected one of ${HOOK_HARNESSES.join("|")}`);
|
|
518
662
|
return EXIT.USAGE;
|
|
519
663
|
}
|
|
520
|
-
const
|
|
664
|
+
const notifyPayload = ctx.args[1];
|
|
665
|
+
if (harness === "codex" && notifyPayload !== void 0 && notifyPayload.length > 0 && detachCodexNotify(notifyPayload)) {
|
|
666
|
+
ctx.io.result({ harness, outcome: "detached" });
|
|
667
|
+
return EXIT.OK;
|
|
668
|
+
}
|
|
669
|
+
const copilotEventName = harness === "copilot" && (0, import_copilot3.isCopilotHookEventName)(ctx.args[1]) ? ctx.args[1] : void 0;
|
|
670
|
+
const raw = await withTimeout(
|
|
671
|
+
readHookPayload(ctx.args, readStdin, harness === "copilot"),
|
|
672
|
+
stdinTimeoutMs,
|
|
673
|
+
""
|
|
674
|
+
);
|
|
675
|
+
const notifyStdinFile = process.env[NOTIFY_STDIN_FILE_ENV];
|
|
676
|
+
if (notifyStdinFile !== void 0 && (0, import_node_path2.dirname)(notifyStdinFile) === (0, import_node_os.tmpdir)() && (0, import_node_path2.basename)(notifyStdinFile).startsWith("birdybeep-notify-")) {
|
|
677
|
+
try {
|
|
678
|
+
(0, import_node_fs3.rmSync)(notifyStdinFile, { force: true });
|
|
679
|
+
} catch {
|
|
680
|
+
}
|
|
681
|
+
}
|
|
521
682
|
let payload;
|
|
522
683
|
try {
|
|
523
684
|
payload = JSON.parse(raw);
|
|
@@ -526,8 +687,15 @@ function createHookCommand(deps = {}) {
|
|
|
526
687
|
return EXIT.OK;
|
|
527
688
|
}
|
|
528
689
|
const sender = makeSender(resolveApiUrl());
|
|
529
|
-
const result = await runHookCommand(harness, payload, sender);
|
|
530
|
-
ctx.io.result({
|
|
690
|
+
const result = await runHookCommand(harness, payload, sender, copilotEventName);
|
|
691
|
+
ctx.io.result({
|
|
692
|
+
harness,
|
|
693
|
+
...copilotEventName !== void 0 ? { event: copilotEventName } : {},
|
|
694
|
+
outcome: result.outcome,
|
|
695
|
+
eventType: result.eventType,
|
|
696
|
+
...result.send?.decision ? { decision: result.send.decision } : {},
|
|
697
|
+
...result.send?.status !== void 0 ? { status: result.send.status } : {}
|
|
698
|
+
});
|
|
531
699
|
return EXIT.OK;
|
|
532
700
|
}
|
|
533
701
|
};
|
|
@@ -535,57 +703,73 @@ function createHookCommand(deps = {}) {
|
|
|
535
703
|
|
|
536
704
|
// src/commands/logout.ts
|
|
537
705
|
var import_agent_core6 = require("@birdybeep/agent-core");
|
|
538
|
-
|
|
706
|
+
var base = (apiUrl) => apiUrl.replace(/\/$/, "");
|
|
707
|
+
function createLogoutCommand(deps = {}) {
|
|
539
708
|
return {
|
|
540
|
-
name:
|
|
541
|
-
summary:
|
|
542
|
-
usage:
|
|
709
|
+
name: "logout",
|
|
710
|
+
summary: "Remove the local machine token (does NOT revoke the machine server-side)",
|
|
711
|
+
usage: "birdybeep logout",
|
|
543
712
|
run: async (ctx) => {
|
|
544
713
|
await (0, import_agent_core6.clearToken)(deps.tokenOptions ?? {});
|
|
545
|
-
ctx.io.emit(
|
|
714
|
+
ctx.io.emit("Logged out \u2014 the machine token was removed.", { loggedOut: true });
|
|
546
715
|
return EXIT.OK;
|
|
547
716
|
}
|
|
548
717
|
};
|
|
549
718
|
}
|
|
550
|
-
function
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
719
|
+
async function revokeSelf(token, fetchImpl, timeoutMs) {
|
|
720
|
+
const controller = new AbortController();
|
|
721
|
+
const timer = setTimeout(() => controller.abort(), timeoutMs);
|
|
722
|
+
try {
|
|
723
|
+
const res = await fetchImpl(`${base(resolveApiUrl())}/v1/machine/revoke-self`, {
|
|
724
|
+
method: "POST",
|
|
725
|
+
headers: { authorization: `Bearer ${token}` },
|
|
726
|
+
signal: controller.signal
|
|
727
|
+
});
|
|
728
|
+
if (res.ok || res.status === 403) return "revoked";
|
|
729
|
+
return "rejected";
|
|
730
|
+
} catch {
|
|
731
|
+
return "unreachable";
|
|
732
|
+
} finally {
|
|
733
|
+
clearTimeout(timer);
|
|
734
|
+
}
|
|
560
735
|
}
|
|
561
736
|
function createUnpairCommand(deps = {}) {
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
737
|
+
const fetchImpl = deps.fetchImpl ?? fetch;
|
|
738
|
+
const timeoutMs = deps.timeoutMs ?? 1e4;
|
|
739
|
+
return {
|
|
740
|
+
name: "unpair",
|
|
741
|
+
summary: "Unpair this machine \u2014 revoke it server-side and remove the local token",
|
|
742
|
+
usage: "birdybeep unpair",
|
|
743
|
+
run: async (ctx) => {
|
|
744
|
+
const token = await (0, import_agent_core6.getToken)(deps.tokenOptions ?? {});
|
|
745
|
+
const outcome = token === null ? "no_token" : await revokeSelf(token, fetchImpl, timeoutMs);
|
|
746
|
+
await (0, import_agent_core6.clearToken)(deps.tokenOptions ?? {});
|
|
747
|
+
const serverRevoked = outcome === "revoked";
|
|
748
|
+
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.";
|
|
749
|
+
ctx.io.emit(human, { unpaired: true, serverRevoked });
|
|
750
|
+
return EXIT.OK;
|
|
751
|
+
}
|
|
752
|
+
};
|
|
571
753
|
}
|
|
572
754
|
|
|
573
755
|
// src/commands/pair.ts
|
|
756
|
+
var import_node_fs4 = require("fs");
|
|
574
757
|
var import_agent_core8 = require("@birdybeep/agent-core");
|
|
575
758
|
var import_uqr = require("uqr");
|
|
576
759
|
|
|
577
760
|
// src/pairing.ts
|
|
578
761
|
var import_agent_core7 = require("@birdybeep/agent-core");
|
|
579
|
-
function
|
|
762
|
+
function base2(apiUrl) {
|
|
580
763
|
return apiUrl.replace(/\/$/, "");
|
|
581
764
|
}
|
|
582
765
|
async function pairStart(apiUrl, input, fetchImpl) {
|
|
583
766
|
const body = {
|
|
584
767
|
machine_label: input.machineLabel,
|
|
585
768
|
...input.os !== void 0 ? { os: input.os } : {},
|
|
586
|
-
...input.cliVersion !== void 0 ? { cli_version: input.cliVersion } : {}
|
|
769
|
+
...input.cliVersion !== void 0 ? { cli_version: input.cliVersion } : {},
|
|
770
|
+
...input.codeChallenge !== void 0 ? { code_challenge: input.codeChallenge } : {}
|
|
587
771
|
};
|
|
588
|
-
const res = await fetchImpl(`${
|
|
772
|
+
const res = await fetchImpl(`${base2(apiUrl)}/v1/pair/start`, {
|
|
589
773
|
method: "POST",
|
|
590
774
|
headers: { "content-type": "application/json" },
|
|
591
775
|
body: JSON.stringify(body)
|
|
@@ -603,12 +787,13 @@ var TERMINAL_TOKEN_ERRORS = /* @__PURE__ */ new Set([
|
|
|
603
787
|
"not_found",
|
|
604
788
|
"payload_too_large"
|
|
605
789
|
]);
|
|
606
|
-
async function pairTokenPoll(apiUrl, deviceCode, fetchImpl, machineFingerprint) {
|
|
790
|
+
async function pairTokenPoll(apiUrl, deviceCode, fetchImpl, machineFingerprint, codeVerifier) {
|
|
607
791
|
const body = {
|
|
608
792
|
device_code: deviceCode,
|
|
609
|
-
...machineFingerprint !== void 0 ? { machine_fingerprint: machineFingerprint } : {}
|
|
793
|
+
...machineFingerprint !== void 0 ? { machine_fingerprint: machineFingerprint } : {},
|
|
794
|
+
...codeVerifier !== void 0 ? { code_verifier: codeVerifier } : {}
|
|
610
795
|
};
|
|
611
|
-
const res = await fetchImpl(`${
|
|
796
|
+
const res = await fetchImpl(`${base2(apiUrl)}/v1/pair/token`, {
|
|
612
797
|
method: "POST",
|
|
613
798
|
headers: { "content-type": "application/json" },
|
|
614
799
|
body: JSON.stringify(body)
|
|
@@ -619,7 +804,10 @@ async function pairTokenPoll(apiUrl, deviceCode, fetchImpl, machineFingerprint)
|
|
|
619
804
|
return {
|
|
620
805
|
status: "paired",
|
|
621
806
|
machineToken: parsed.data.machine_token,
|
|
622
|
-
machineId: parsed.data.machine_id
|
|
807
|
+
machineId: parsed.data.machine_id,
|
|
808
|
+
// Only surface the key when the server reported it (exactOptionalPropertyTypes: no explicit
|
|
809
|
+
// undefined). Older servers omit approved_by_email; newer ones (dgxd) include it.
|
|
810
|
+
...parsed.data.approved_by_email !== void 0 ? { approvedByEmail: parsed.data.approved_by_email } : {}
|
|
623
811
|
};
|
|
624
812
|
}
|
|
625
813
|
let errBody = null;
|
|
@@ -640,7 +828,7 @@ async function pairTokenPoll(apiUrl, deviceCode, fetchImpl, machineFingerprint)
|
|
|
640
828
|
}
|
|
641
829
|
|
|
642
830
|
// src/version.ts
|
|
643
|
-
var CLI_VERSION = "0.
|
|
831
|
+
var CLI_VERSION = "0.3.0".length > 0 ? "0.3.0" : "0.0.0";
|
|
644
832
|
|
|
645
833
|
// src/commands/pair.ts
|
|
646
834
|
var DEFAULT_POLL_INTERVAL_MS = 2e3;
|
|
@@ -648,22 +836,172 @@ var HEARTBEAT_MS = 15e3;
|
|
|
648
836
|
function renderQrMatrix(qrPayload) {
|
|
649
837
|
return (0, import_uqr.renderUnicodeCompact)(qrPayload, { border: 2 });
|
|
650
838
|
}
|
|
839
|
+
function parsePairFlags(args) {
|
|
840
|
+
const flags = { yes: false };
|
|
841
|
+
for (let i = 0; i < args.length; i += 1) {
|
|
842
|
+
const token = args[i] ?? "";
|
|
843
|
+
if (token === "--yes" || token === "-y") {
|
|
844
|
+
flags.yes = true;
|
|
845
|
+
} else if (token === "--expect-email" || token.startsWith("--expect-email=")) {
|
|
846
|
+
const inline = token.startsWith("--expect-email=") ? token.slice("--expect-email=".length) : void 0;
|
|
847
|
+
const value = inline ?? args[++i];
|
|
848
|
+
if (value === void 0 || value.length === 0 || value.startsWith("-")) {
|
|
849
|
+
return { ...flags, error: "--expect-email requires an email address" };
|
|
850
|
+
}
|
|
851
|
+
flags.expectEmail = value;
|
|
852
|
+
} else {
|
|
853
|
+
return { ...flags, error: `unexpected argument "${token}"` };
|
|
854
|
+
}
|
|
855
|
+
}
|
|
856
|
+
return flags;
|
|
857
|
+
}
|
|
858
|
+
function sameEmail(a, b) {
|
|
859
|
+
const fold = (v) => v.trim().toLowerCase();
|
|
860
|
+
const rawEqual = fold(a) === fold(b);
|
|
861
|
+
const nfkcEqual = fold(a.normalize("NFKC")) === fold(b.normalize("NFKC"));
|
|
862
|
+
return rawEqual && nfkcEqual;
|
|
863
|
+
}
|
|
864
|
+
function decidePairConfirmation(input) {
|
|
865
|
+
const { approvedByEmail, expectEmail } = input;
|
|
866
|
+
const platform = input.platform ?? process.platform;
|
|
867
|
+
if (expectEmail !== void 0) {
|
|
868
|
+
if (approvedByEmail === void 0) {
|
|
869
|
+
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.";
|
|
870
|
+
return {
|
|
871
|
+
action: "reject",
|
|
872
|
+
reason: "expected_email_unverifiable",
|
|
873
|
+
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}`
|
|
874
|
+
};
|
|
875
|
+
}
|
|
876
|
+
if (sameEmail(approvedByEmail, expectEmail)) {
|
|
877
|
+
return { action: "approve", reason: "expected_email_match" };
|
|
878
|
+
}
|
|
879
|
+
return {
|
|
880
|
+
action: "reject",
|
|
881
|
+
reason: "expected_email_mismatch",
|
|
882
|
+
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\`.`
|
|
883
|
+
};
|
|
884
|
+
}
|
|
885
|
+
if (input.yes) return { action: "approve", reason: "yes_flag" };
|
|
886
|
+
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] ";
|
|
887
|
+
if (!input.nonInteractive) {
|
|
888
|
+
if (input.stdinIsTTY) return { action: "prompt", question, on: "stdin" };
|
|
889
|
+
if (input.controllingTerminalAvailable) {
|
|
890
|
+
return { action: "prompt", question, on: "controlling-terminal" };
|
|
891
|
+
}
|
|
892
|
+
}
|
|
893
|
+
const who = approvedByEmail !== void 0 ? ` (approved by ${approvedByEmail})` : "";
|
|
894
|
+
const winptyHint = platform === "win32" && !input.nonInteractive ? " In Git Bash / MSYS, `winpty birdybeep pair` attaches a real console so the prompt can appear." : "";
|
|
895
|
+
return {
|
|
896
|
+
action: "reject",
|
|
897
|
+
reason: "non_interactive",
|
|
898
|
+
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
|
|
899
|
+
};
|
|
900
|
+
}
|
|
901
|
+
function isAffirmative(answer) {
|
|
902
|
+
return /^(y|yes)$/i.test(answer.trim());
|
|
903
|
+
}
|
|
904
|
+
function controllingTerminalPath() {
|
|
905
|
+
return "/dev/tty";
|
|
906
|
+
}
|
|
907
|
+
function canOpenControllingTerminal(path = controllingTerminalPath(), platform = process.platform) {
|
|
908
|
+
if (platform === "win32") return false;
|
|
909
|
+
let fd;
|
|
910
|
+
try {
|
|
911
|
+
fd = (0, import_node_fs4.openSync)(path, "r");
|
|
912
|
+
return true;
|
|
913
|
+
} catch {
|
|
914
|
+
return false;
|
|
915
|
+
} finally {
|
|
916
|
+
if (fd !== void 0) {
|
|
917
|
+
try {
|
|
918
|
+
(0, import_node_fs4.closeSync)(fd);
|
|
919
|
+
} catch {
|
|
920
|
+
}
|
|
921
|
+
}
|
|
922
|
+
}
|
|
923
|
+
}
|
|
924
|
+
async function promptForAnswer(question, on) {
|
|
925
|
+
const { createInterface } = await import("readline/promises");
|
|
926
|
+
let ttyFd;
|
|
927
|
+
let input;
|
|
928
|
+
if (on === "stdin") {
|
|
929
|
+
input = process.stdin;
|
|
930
|
+
} else {
|
|
931
|
+
const { ReadStream } = await import("tty");
|
|
932
|
+
ttyFd = (0, import_node_fs4.openSync)(controllingTerminalPath(), "r");
|
|
933
|
+
input = new ReadStream(ttyFd);
|
|
934
|
+
}
|
|
935
|
+
return new Promise((resolve) => {
|
|
936
|
+
const rl = createInterface({ input, output: process.stderr });
|
|
937
|
+
let settled = false;
|
|
938
|
+
const done = (value) => {
|
|
939
|
+
if (settled) return;
|
|
940
|
+
settled = true;
|
|
941
|
+
rl.close();
|
|
942
|
+
if (on === "stdin") {
|
|
943
|
+
process.stdin.unref?.();
|
|
944
|
+
} else {
|
|
945
|
+
try {
|
|
946
|
+
input.unref?.();
|
|
947
|
+
input.destroy?.();
|
|
948
|
+
} catch {
|
|
949
|
+
}
|
|
950
|
+
if (ttyFd !== void 0) {
|
|
951
|
+
try {
|
|
952
|
+
(0, import_node_fs4.closeSync)(ttyFd);
|
|
953
|
+
} catch {
|
|
954
|
+
}
|
|
955
|
+
}
|
|
956
|
+
}
|
|
957
|
+
resolve(value);
|
|
958
|
+
};
|
|
959
|
+
rl.question(question).then(done, () => done(""));
|
|
960
|
+
rl.once("close", () => done(""));
|
|
961
|
+
input.once?.("error", () => done(""));
|
|
962
|
+
});
|
|
963
|
+
}
|
|
651
964
|
function createPairCommand(deps = {}) {
|
|
652
965
|
const fetchImpl = deps.fetchImpl ?? fetch;
|
|
653
966
|
const sleep = deps.sleep ?? ((ms) => new Promise((r) => setTimeout(r, ms)));
|
|
654
967
|
const clock = deps.now ?? (() => Date.now());
|
|
655
968
|
const renderQr = deps.renderQr ?? renderQrMatrix;
|
|
656
969
|
const intervalMs = deps.pollIntervalMs ?? DEFAULT_POLL_INTERVAL_MS;
|
|
970
|
+
const promptLine = deps.promptLine ?? promptForAnswer;
|
|
971
|
+
const hasControllingTerminal = deps.hasControllingTerminal ?? (() => canOpenControllingTerminal());
|
|
972
|
+
const configuredExpectEmail = deps.configuredExpectEmail ?? (() => {
|
|
973
|
+
const pinned = readCliConfig().expectEmail;
|
|
974
|
+
return typeof pinned === "string" && pinned.trim().length > 0 ? pinned : void 0;
|
|
975
|
+
});
|
|
657
976
|
return {
|
|
658
977
|
name: "pair",
|
|
659
978
|
summary: "Pair this machine with your BirdyBeep account (QR or manual)",
|
|
660
|
-
usage: "birdybeep pair [--json]",
|
|
979
|
+
usage: "birdybeep pair [--yes] [--expect-email <addr>] [--json]",
|
|
980
|
+
options: [
|
|
981
|
+
{
|
|
982
|
+
flag: "--yes",
|
|
983
|
+
aliases: ["-y"],
|
|
984
|
+
summary: "Skip the approving-account confirmation (headless/CI)"
|
|
985
|
+
},
|
|
986
|
+
{
|
|
987
|
+
flag: "--expect-email",
|
|
988
|
+
value: "<addr>",
|
|
989
|
+
summary: "Only trust the pairing if this account approved it (else fail)"
|
|
990
|
+
}
|
|
991
|
+
],
|
|
661
992
|
run: async (ctx) => {
|
|
993
|
+
const pairFlags = parsePairFlags(ctx.args);
|
|
994
|
+
if (pairFlags.error !== void 0) {
|
|
995
|
+
ctx.io.errline(`birdybeep pair: ${pairFlags.error}.`);
|
|
996
|
+
return EXIT.USAGE;
|
|
997
|
+
}
|
|
662
998
|
const apiUrl = resolveApiUrl();
|
|
663
999
|
const identity = (0, import_agent_core8.getMachineIdentity)();
|
|
1000
|
+
const codeVerifier = (0, import_agent_core8.generateCodeVerifier)();
|
|
1001
|
+
const codeChallenge = (0, import_agent_core8.deriveCodeChallengeS256)(codeVerifier);
|
|
664
1002
|
const start = await pairStart(
|
|
665
1003
|
apiUrl,
|
|
666
|
-
{ machineLabel: identity.label, os: identity.os, cliVersion: CLI_VERSION },
|
|
1004
|
+
{ machineLabel: identity.label, os: identity.os, cliVersion: CLI_VERSION, codeChallenge },
|
|
667
1005
|
fetchImpl
|
|
668
1006
|
);
|
|
669
1007
|
if (ctx.flags.json) {
|
|
@@ -675,12 +1013,14 @@ function createPairCommand(deps = {}) {
|
|
|
675
1013
|
});
|
|
676
1014
|
} else {
|
|
677
1015
|
ctx.io.line(
|
|
678
|
-
"To pair this machine, open the BirdyBeep app, tap \u201Cpair a machine\u201D, and scan this QR
|
|
1016
|
+
"To pair this machine, open the BirdyBeep app, tap \u201Cpair a machine\u201D, and scan this QR or open the complete link:"
|
|
679
1017
|
);
|
|
680
1018
|
const isTTY = deps.isTTY ?? process.stdout.isTTY === true;
|
|
681
1019
|
if (isTTY) ctx.io.line(renderQr(start.qr_payload));
|
|
682
1020
|
ctx.io.line(` Scan or open: ${start.qr_payload}`);
|
|
683
|
-
ctx.io.line(
|
|
1021
|
+
ctx.io.line(
|
|
1022
|
+
` Session code (display only; cannot approve by itself): ${start.user_code}`
|
|
1023
|
+
);
|
|
684
1024
|
ctx.io.line("Waiting for you to approve this machine in the app\u2026");
|
|
685
1025
|
}
|
|
686
1026
|
const deadline = Date.parse(start.expires_at);
|
|
@@ -696,7 +1036,9 @@ function createPairCommand(deps = {}) {
|
|
|
696
1036
|
apiUrl,
|
|
697
1037
|
start.device_code,
|
|
698
1038
|
fetchImpl,
|
|
699
|
-
identity.fingerprintHash
|
|
1039
|
+
identity.fingerprintHash,
|
|
1040
|
+
codeVerifier
|
|
1041
|
+
// PKCE proof-of-possession (dgxd) — sent on every poll
|
|
700
1042
|
);
|
|
701
1043
|
if (poll.status === "paired") {
|
|
702
1044
|
paired = poll;
|
|
@@ -721,15 +1063,44 @@ function createPairCommand(deps = {}) {
|
|
|
721
1063
|
if (paired === void 0 || paired.status !== "paired") {
|
|
722
1064
|
ctx.io.result({ paired: false, reason: "timeout" });
|
|
723
1065
|
ctx.io.errline(
|
|
724
|
-
"Pairing timed out before you approved it. In the BirdyBeep app, tap \u201Cpair a machine\u201D, scan
|
|
1066
|
+
"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."
|
|
1067
|
+
);
|
|
1068
|
+
return EXIT.ERROR;
|
|
1069
|
+
}
|
|
1070
|
+
const approvedBy = paired.approvedByEmail;
|
|
1071
|
+
const expectEmail = pairFlags.expectEmail ?? configuredExpectEmail();
|
|
1072
|
+
const stdinIsTTY = deps.isStdinTTY ?? process.stdin.isTTY === true;
|
|
1073
|
+
const decision = decidePairConfirmation({
|
|
1074
|
+
...approvedBy !== void 0 ? { approvedByEmail: approvedBy } : {},
|
|
1075
|
+
...expectEmail !== void 0 ? { expectEmail } : {},
|
|
1076
|
+
...expectEmail !== void 0 ? { expectEmailSource: pairFlags.expectEmail !== void 0 ? "flag" : "config" } : {},
|
|
1077
|
+
yes: pairFlags.yes,
|
|
1078
|
+
nonInteractive: ctx.flags.nonInteractive,
|
|
1079
|
+
stdinIsTTY,
|
|
1080
|
+
// Probed ONLY when stdin can't answer — opening /dev/tty is a syscall, and when stdin is
|
|
1081
|
+
// already a terminal the answer is irrelevant.
|
|
1082
|
+
controllingTerminalAvailable: stdinIsTTY ? false : hasControllingTerminal(),
|
|
1083
|
+
configPath: cliConfigPath()
|
|
1084
|
+
});
|
|
1085
|
+
if (decision.action === "reject") {
|
|
1086
|
+
ctx.io.result({ paired: false, reason: decision.reason });
|
|
1087
|
+
ctx.io.errline(decision.message);
|
|
1088
|
+
return EXIT.ERROR;
|
|
1089
|
+
}
|
|
1090
|
+
if (decision.action === "prompt" && !isAffirmative(await promptLine(decision.question, decision.on))) {
|
|
1091
|
+
ctx.io.result({ paired: false, reason: "declined" });
|
|
1092
|
+
ctx.io.errline(
|
|
1093
|
+
"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."
|
|
725
1094
|
);
|
|
726
1095
|
return EXIT.ERROR;
|
|
727
1096
|
}
|
|
728
1097
|
await (0, import_agent_core8.setToken)(paired.machineToken, deps.tokenOptions ?? {});
|
|
729
1098
|
writeCliConfig({ apiUrl });
|
|
730
|
-
|
|
1099
|
+
const humanSuffix = approvedBy !== void 0 ? ` to ${approvedBy}` : "";
|
|
1100
|
+
ctx.io.emit(`\u2713 Paired${humanSuffix}. Run \`birdybeep test\` to send a test Beep.`, {
|
|
731
1101
|
paired: true,
|
|
732
|
-
machineId: paired.machineId
|
|
1102
|
+
machineId: paired.machineId,
|
|
1103
|
+
...approvedBy !== void 0 ? { approvedByEmail: approvedBy } : {}
|
|
733
1104
|
});
|
|
734
1105
|
return EXIT.OK;
|
|
735
1106
|
}
|
|
@@ -762,14 +1133,24 @@ function createQueueCommand() {
|
|
|
762
1133
|
var import_agent_core10 = require("@birdybeep/agent-core");
|
|
763
1134
|
var import_claude_code4 = require("@birdybeep/claude-code");
|
|
764
1135
|
var import_codex4 = require("@birdybeep/codex");
|
|
1136
|
+
var import_copilot4 = require("@birdybeep/copilot");
|
|
1137
|
+
var import_cursor4 = require("@birdybeep/cursor");
|
|
765
1138
|
var import_opencode4 = require("@birdybeep/opencode");
|
|
766
|
-
var DEFAULT_ADAPTERS3 = [
|
|
1139
|
+
var DEFAULT_ADAPTERS3 = [
|
|
1140
|
+
import_claude_code4.claudeCodeAdapter,
|
|
1141
|
+
import_codex4.codexAdapter,
|
|
1142
|
+
import_opencode4.opencodeAdapter,
|
|
1143
|
+
import_cursor4.cursorAdapter,
|
|
1144
|
+
import_copilot4.copilotAdapter
|
|
1145
|
+
];
|
|
767
1146
|
var ADAPTER_VERSIONS = {
|
|
768
1147
|
claude_code: import_claude_code4.CLAUDE_CODE_ADAPTER_VERSION,
|
|
769
1148
|
codex: import_codex4.CODEX_ADAPTER_VERSION,
|
|
770
|
-
opencode: import_opencode4.OPENCODE_ADAPTER_VERSION
|
|
1149
|
+
opencode: import_opencode4.OPENCODE_ADAPTER_VERSION,
|
|
1150
|
+
cursor: import_cursor4.CURSOR_ADAPTER_VERSION,
|
|
1151
|
+
copilot: import_copilot4.COPILOT_ADAPTER_VERSION
|
|
771
1152
|
};
|
|
772
|
-
var
|
|
1153
|
+
var base3 = (apiUrl) => apiUrl.replace(/\/$/, "");
|
|
773
1154
|
async function gatherItems(adapters) {
|
|
774
1155
|
return Promise.all(
|
|
775
1156
|
adapters.map(async (a) => {
|
|
@@ -804,7 +1185,7 @@ function createReportStatusCommand(deps = {}) {
|
|
|
804
1185
|
let outcome = "deferred";
|
|
805
1186
|
let errorCode;
|
|
806
1187
|
try {
|
|
807
|
-
const res = await fetchImpl(`${
|
|
1188
|
+
const res = await fetchImpl(`${base3(resolveApiUrl())}/v1/integrations/status`, {
|
|
808
1189
|
method: "POST",
|
|
809
1190
|
headers: { authorization: `Bearer ${token}`, "content-type": "application/json" },
|
|
810
1191
|
body: JSON.stringify({ integrations: items })
|
|
@@ -855,8 +1236,16 @@ function createReportStatusCommand(deps = {}) {
|
|
|
855
1236
|
var import_agent_core11 = require("@birdybeep/agent-core");
|
|
856
1237
|
var import_claude_code5 = require("@birdybeep/claude-code");
|
|
857
1238
|
var import_codex5 = require("@birdybeep/codex");
|
|
1239
|
+
var import_copilot5 = require("@birdybeep/copilot");
|
|
1240
|
+
var import_cursor5 = require("@birdybeep/cursor");
|
|
858
1241
|
var import_opencode5 = require("@birdybeep/opencode");
|
|
859
|
-
var DEFAULT_ADAPTERS4 = [
|
|
1242
|
+
var DEFAULT_ADAPTERS4 = [
|
|
1243
|
+
import_claude_code5.claudeCodeAdapter,
|
|
1244
|
+
import_codex5.codexAdapter,
|
|
1245
|
+
import_opencode5.opencodeAdapter,
|
|
1246
|
+
import_cursor5.cursorAdapter,
|
|
1247
|
+
import_copilot5.copilotAdapter
|
|
1248
|
+
];
|
|
860
1249
|
function createStatusCommand(deps = {}) {
|
|
861
1250
|
const adapters = deps.adapters ?? DEFAULT_ADAPTERS4;
|
|
862
1251
|
const makeSender = deps.createSender ?? ((baseUrl) => (0, import_agent_core11.createSender)(
|
|
@@ -896,7 +1285,7 @@ function createStatusCommand(deps = {}) {
|
|
|
896
1285
|
}
|
|
897
1286
|
|
|
898
1287
|
// src/commands/test.ts
|
|
899
|
-
var
|
|
1288
|
+
var import_node_crypto2 = require("crypto");
|
|
900
1289
|
var import_agent_core12 = require("@birdybeep/agent-core");
|
|
901
1290
|
function buildTestEvent(opts = {}) {
|
|
902
1291
|
const machine = (0, import_agent_core12.getMachineIdentity)();
|
|
@@ -908,7 +1297,7 @@ function buildTestEvent(opts = {}) {
|
|
|
908
1297
|
// schema requires a harness; the "test" type distinguishes it
|
|
909
1298
|
// Unique per run: a repeat `birdybeep test` inside the backend's dedupe window must
|
|
910
1299
|
// still beep — a constant id made the second test silently "deduped" (9fh).
|
|
911
|
-
source_session_id: `birdybeep-cli-test-${(0,
|
|
1300
|
+
source_session_id: `birdybeep-cli-test-${(0, import_node_crypto2.randomUUID)()}`,
|
|
912
1301
|
machine: { label: machine.label, os: machine.os },
|
|
913
1302
|
workspace: { cwd: process.cwd() },
|
|
914
1303
|
title: "BirdyBeep test event",
|
|
@@ -978,8 +1367,8 @@ function buildCommands() {
|
|
|
978
1367
|
}
|
|
979
1368
|
|
|
980
1369
|
// src/update-check.ts
|
|
981
|
-
var
|
|
982
|
-
var
|
|
1370
|
+
var import_node_fs5 = require("fs");
|
|
1371
|
+
var import_node_path3 = require("path");
|
|
983
1372
|
var import_agent_core13 = require("@birdybeep/agent-core");
|
|
984
1373
|
var PACKAGE_NAME = "@birdybeep/cli";
|
|
985
1374
|
var PACKAGE_PATH = "@birdybeep%2Fcli";
|
|
@@ -1034,11 +1423,11 @@ function isNewer(current, latest) {
|
|
|
1034
1423
|
return cur !== null && lat !== null && compareSemver(cur, lat) < 0;
|
|
1035
1424
|
}
|
|
1036
1425
|
function updateCachePath() {
|
|
1037
|
-
return (0,
|
|
1426
|
+
return (0, import_node_path3.join)((0, import_agent_core13.birdyBeepConfigDir)(), UPDATE_CACHE_FILE);
|
|
1038
1427
|
}
|
|
1039
1428
|
function readUpdateCache() {
|
|
1040
1429
|
try {
|
|
1041
|
-
const parsed = JSON.parse((0,
|
|
1430
|
+
const parsed = JSON.parse((0, import_node_fs5.readFileSync)(updateCachePath(), "utf8"));
|
|
1042
1431
|
if (typeof parsed !== "object" || parsed === null) return null;
|
|
1043
1432
|
const { checkedAt, latest } = parsed;
|
|
1044
1433
|
if (typeof checkedAt !== "number") return null;
|
|
@@ -1049,8 +1438,8 @@ function readUpdateCache() {
|
|
|
1049
1438
|
}
|
|
1050
1439
|
}
|
|
1051
1440
|
function writeUpdateCache(cache) {
|
|
1052
|
-
(0,
|
|
1053
|
-
(0,
|
|
1441
|
+
(0, import_node_fs5.mkdirSync)((0, import_agent_core13.birdyBeepConfigDir)(), { recursive: true, mode: 448 });
|
|
1442
|
+
(0, import_node_fs5.writeFileSync)(updateCachePath(), `${JSON.stringify(cache)}
|
|
1054
1443
|
`, { mode: 384 });
|
|
1055
1444
|
}
|
|
1056
1445
|
async function fetchLatestVersion(registryUrl, fetchImpl, timeoutMs) {
|