@birdybeep/cli 0.7.0 → 0.8.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/bin.cjs +114 -67
- package/dist/bin.cjs.map +1 -1
- package/dist/bin.js +1 -1
- package/dist/{chunk-6RIMM4RO.js → chunk-J45QGUDO.js} +118 -68
- package/dist/chunk-J45QGUDO.js.map +1 -0
- package/dist/index.cjs +114 -67
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +1 -1
- package/package.json +8 -8
- package/dist/chunk-6RIMM4RO.js.map +0 -1
package/dist/bin.js
CHANGED
|
@@ -80,7 +80,7 @@ function renderRootHelp(version, commands) {
|
|
|
80
80
|
const lines = commands.map((c) => ` ${c.name.padEnd(width)} ${c.summary}`);
|
|
81
81
|
const featured = commands.filter((c) => c.gettingStarted !== void 0);
|
|
82
82
|
return [
|
|
83
|
-
`birdybeep ${version}
|
|
83
|
+
`birdybeep ${version}: phone alerts for coding agents.`,
|
|
84
84
|
"",
|
|
85
85
|
"Usage:",
|
|
86
86
|
" birdybeep <command> [options]",
|
|
@@ -112,7 +112,7 @@ function commandFlagTokens(...commands) {
|
|
|
112
112
|
}
|
|
113
113
|
function renderCommandHelp(path, command) {
|
|
114
114
|
const lines = [
|
|
115
|
-
`birdybeep ${path}
|
|
115
|
+
`birdybeep ${path}: ${command.summary}`,
|
|
116
116
|
"",
|
|
117
117
|
"Usage:",
|
|
118
118
|
` ${command.usage ?? `birdybeep ${path} [options]`}`
|
|
@@ -227,7 +227,7 @@ async function dispatch(argv, deps) {
|
|
|
227
227
|
}
|
|
228
228
|
|
|
229
229
|
// src/version.ts
|
|
230
|
-
var CLI_VERSION = "0.
|
|
230
|
+
var CLI_VERSION = "0.8.1".length > 0 ? "0.8.1" : "0.0.0";
|
|
231
231
|
|
|
232
232
|
// src/commands/agent.ts
|
|
233
233
|
import { claudeCodeAdapter } from "@birdybeep/claude-code";
|
|
@@ -333,7 +333,7 @@ function filteredActivity() {
|
|
|
333
333
|
function describeFilteredActivity(activity) {
|
|
334
334
|
const types = Object.entries(activity.byType).sort(([, a], [, b]) => b - a).map(([type, n]) => `${type} \xD7${n}`).join(", ");
|
|
335
335
|
const since = new Date(activity.firstAt).toISOString();
|
|
336
|
-
return `${activity.count} local-only event(s) since ${since}${types ? ` (${types})` : ""}
|
|
336
|
+
return `${activity.count} local-only event(s) since ${since}${types ? ` (${types})` : ""}. Hooks are firing; these types never produce notifications, so they are not sent.`;
|
|
337
337
|
}
|
|
338
338
|
function machineIdentity() {
|
|
339
339
|
return getMachineIdentity();
|
|
@@ -421,18 +421,18 @@ function describeSurface(state) {
|
|
|
421
421
|
function describeSurfaceCoverage(state, group) {
|
|
422
422
|
if (state.coverage === "active") {
|
|
423
423
|
const last = state.lastAt !== void 0 ? `, last ${new Date(state.lastAt).toISOString()}` : "";
|
|
424
|
-
return `covered
|
|
424
|
+
return `covered: ${state.events} event(s) from this build${last}`;
|
|
425
425
|
}
|
|
426
426
|
if (state.coverage === "wired") {
|
|
427
|
-
return state.surface.shadowed === true ? `${group.displayName}'s hooks are installed and this build shares them, but another install comes first on PATH
|
|
427
|
+
return state.surface.shadowed === true ? `${group.displayName}'s hooks are installed and this build shares them, but another install comes first on PATH. This build runs only if that order changes.` : `${group.displayName}'s hooks are installed and this build shares them; nothing has fired from it yet`;
|
|
428
428
|
}
|
|
429
429
|
if (!CONFIGURED_STATUSES.has(group.status)) {
|
|
430
|
-
return `not covered
|
|
430
|
+
return `not covered: ${group.displayName} carries no BirdyBeep hooks, so this build cannot produce notifications`;
|
|
431
431
|
}
|
|
432
432
|
const active = group.surfaces.filter((s) => s.coverage === "active").map(describeSurface);
|
|
433
433
|
const delivering = active.join(", ");
|
|
434
434
|
const verb = active.length === 1 ? "is" : "are";
|
|
435
|
-
return `not covered
|
|
435
|
+
return `not covered: nothing has fired from this build, while ${delivering} ${verb} delivering through the same config`;
|
|
436
436
|
}
|
|
437
437
|
function installTarget(harness) {
|
|
438
438
|
return harness === "claude_code" ? "claude" : harness;
|
|
@@ -441,7 +441,7 @@ function surfaceRemedy(state, group) {
|
|
|
441
441
|
if (state.coverage !== "uncovered") return void 0;
|
|
442
442
|
if (!CONFIGURED_STATUSES.has(group.status)) return void 0;
|
|
443
443
|
const install = `\`birdybeep agent install ${installTarget(group.harness)}\``;
|
|
444
|
-
return state.surface.kind === "desktop" ? `Run a turn in ${state.surface.label}. If it stays uncovered, that build cannot run the hook command: a desktop app spawns its engine with your LOGIN shell's PATH, not an interactive shell's, so a bare command is invisible to it. Re-run ${install} from a shell where \`birdybeep\` resolves
|
|
444
|
+
return state.surface.kind === "desktop" ? `Run a turn in ${state.surface.label}. If it stays uncovered, that build cannot run the hook command: a desktop app spawns its engine with your LOGIN shell's PATH, not an interactive shell's, so a bare command is invisible to it. Re-run ${install} from a shell where \`birdybeep\` resolves. This rewrites the entry with absolute paths that do not depend on PATH.` : `Run a turn in ${state.surface.label}. If it stays uncovered, re-run ${install} from a shell where \`birdybeep\` resolves, then check that ${state.surface.enginePath} is the build you are actually running.`;
|
|
445
445
|
}
|
|
446
446
|
|
|
447
447
|
// src/commands/agent.ts
|
|
@@ -509,12 +509,12 @@ async function installSelected(adapters, ctx, tokenOptions) {
|
|
|
509
509
|
return EXIT.OK;
|
|
510
510
|
}
|
|
511
511
|
if (outcomes.length === 0 || outcomes.every((o) => !o.detected)) {
|
|
512
|
-
ctx.io.line("No supported harnesses detected
|
|
512
|
+
ctx.io.line("No supported harnesses detected. Nothing was installed.");
|
|
513
513
|
}
|
|
514
514
|
for (const o of outcomes) {
|
|
515
515
|
if (!o.detected) {
|
|
516
516
|
ctx.io.line(
|
|
517
|
-
`\u2013 ${o.displayName}: not detected (skipped)
|
|
517
|
+
`\u2013 ${o.displayName}: not detected (skipped). Install it, then run \`birdybeep agent install ${installTarget2(o.harness)}\`.`
|
|
518
518
|
);
|
|
519
519
|
continue;
|
|
520
520
|
}
|
|
@@ -524,7 +524,7 @@ async function installSelected(adapters, ctx, tokenOptions) {
|
|
|
524
524
|
}
|
|
525
525
|
if (!paired) {
|
|
526
526
|
ctx.io.line(
|
|
527
|
-
"\u26A0 This machine is not paired
|
|
527
|
+
"\u26A0 This machine is not paired. Run `birdybeep setup` before expecting notifications."
|
|
528
528
|
);
|
|
529
529
|
}
|
|
530
530
|
return EXIT.OK;
|
|
@@ -591,6 +591,8 @@ function createAgentCommand(deps = {}) {
|
|
|
591
591
|
import {
|
|
592
592
|
createSender as defaultCreateSender,
|
|
593
593
|
DEFAULT_QUEUE_MAX_ENTRIES as QUEUE_CAP,
|
|
594
|
+
describeCheckIn,
|
|
595
|
+
describeQuota,
|
|
594
596
|
describeReachability,
|
|
595
597
|
fetchPushReachability
|
|
596
598
|
} from "@birdybeep/agent-core";
|
|
@@ -687,13 +689,12 @@ function createDoctorCommand(deps = {}) {
|
|
|
687
689
|
remedy: tokenStoreRemedy(pairing)
|
|
688
690
|
}
|
|
689
691
|
);
|
|
690
|
-
const
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
);
|
|
692
|
+
const reachability = await fetchPushReachability({
|
|
693
|
+
baseUrl: apiUrl,
|
|
694
|
+
...deps.tokenOptions ? { tokenOptions: deps.tokenOptions } : {},
|
|
695
|
+
...deps.fetchImpl ? { fetchImpl: deps.fetchImpl } : {}
|
|
696
|
+
});
|
|
697
|
+
const reach = describeReachability(reachability);
|
|
697
698
|
if (reach !== null) {
|
|
698
699
|
checks.push({
|
|
699
700
|
name: "Push reachability",
|
|
@@ -702,21 +703,34 @@ function createDoctorCommand(deps = {}) {
|
|
|
702
703
|
...reach.remedy !== void 0 ? { remedy: reach.remedy } : {}
|
|
703
704
|
});
|
|
704
705
|
}
|
|
706
|
+
const checkIn = describeCheckIn(reachability);
|
|
707
|
+
if (checkIn !== null) {
|
|
708
|
+
checks.push({ name: "Device check-in", ok: checkIn.ok, detail: checkIn.detail });
|
|
709
|
+
}
|
|
710
|
+
const quota = describeQuota(reachability);
|
|
711
|
+
if (quota !== null) {
|
|
712
|
+
checks.push({
|
|
713
|
+
name: "Beep quota",
|
|
714
|
+
ok: quota.ok,
|
|
715
|
+
detail: quota.detail,
|
|
716
|
+
...quota.remedy !== void 0 ? { remedy: quota.remedy } : {}
|
|
717
|
+
});
|
|
718
|
+
}
|
|
705
719
|
const unpaired = unpairedActivity();
|
|
706
720
|
if (unpaired !== null) {
|
|
707
721
|
checks.push({
|
|
708
722
|
name: "Events lost while unpaired",
|
|
709
723
|
ok: false,
|
|
710
724
|
detail: describeUnpairedActivity(unpaired),
|
|
711
|
-
remedy: "Run `birdybeep pair`. Events that fired before pairing
|
|
725
|
+
remedy: "Run `birdybeep pair`. Events that fired before pairing were not saved and will not be replayed."
|
|
712
726
|
});
|
|
713
727
|
}
|
|
714
728
|
if (await cursorBridgeOnly(deps.detectCursor ? { detect: deps.detectCursor } : {})) {
|
|
715
729
|
checks.push({
|
|
716
730
|
name: "Approval beeps from Cursor",
|
|
717
731
|
ok: false,
|
|
718
|
-
detail: "Cursor is running your agent through the Claude Code hooks
|
|
719
|
-
remedy: "Run `birdybeep agent install cursor` to get approval beeps from Cursor's own shell and MCP permission prompts. Keeping both installed is safe
|
|
732
|
+
detail: "Cursor is running your agent through the Claude Code hooks. This is why Cursor events arrive without a Cursor install. Its bridge drops Notification and PermissionRequest, so approvals never reach you.",
|
|
733
|
+
remedy: "Run `birdybeep agent install cursor` to get approval beeps from Cursor's own shell and MCP permission prompts. Keeping both installed is safe; duplicate events are collapsed."
|
|
720
734
|
});
|
|
721
735
|
}
|
|
722
736
|
const filtered = filteredActivity();
|
|
@@ -765,7 +779,7 @@ function createDoctorCommand(deps = {}) {
|
|
|
765
779
|
name: "Backend reachable",
|
|
766
780
|
ok: false,
|
|
767
781
|
detail: `Could not reach ${apiUrl}.`,
|
|
768
|
-
remedy: "Check your network
|
|
782
|
+
remedy: "Check your network. Queued events retry automatically."
|
|
769
783
|
}
|
|
770
784
|
);
|
|
771
785
|
const ok = checks.every((c) => c.ok);
|
|
@@ -775,6 +789,14 @@ function createDoctorCommand(deps = {}) {
|
|
|
775
789
|
checks,
|
|
776
790
|
pairing,
|
|
777
791
|
// gcgp.23: paired | unpaired | unknown — never a bare boolean
|
|
792
|
+
// The backend's own quota numbers, unrendered (58l) — a script (or a bug report) gets
|
|
793
|
+
// the window and the counts, not just the sentence built from them.
|
|
794
|
+
...reachability.state === "ok" && reachability.data.quota !== void 0 ? { quota: reachability.data.quota } : {},
|
|
795
|
+
// The raw check-in (2x9s), unrendered. Present ONLY when the server reported the field
|
|
796
|
+
// at all, so a script can still tell "this backend predates check-ins" (key absent)
|
|
797
|
+
// from "no device has ever checked in" (key present, null) — the same distinction the
|
|
798
|
+
// rendered row is careful about.
|
|
799
|
+
...reachability.state === "ok" && reachability.data.most_recent_check_in_at !== void 0 ? { mostRecentCheckInAt: reachability.data.most_recent_check_in_at } : {},
|
|
778
800
|
surfaces: surfaceGroups,
|
|
779
801
|
queue: { depthBefore, delivered: drain.delivered, depthAfter, overflowDropped },
|
|
780
802
|
...unpaired !== null ? { unpairedActivity: unpaired } : {},
|
|
@@ -782,10 +804,10 @@ function createDoctorCommand(deps = {}) {
|
|
|
782
804
|
});
|
|
783
805
|
} else {
|
|
784
806
|
for (const c of checks) {
|
|
785
|
-
ctx.io.line(`${c.ok ? "\u2713" : "\u2717"} ${c.name}${c.detail ?
|
|
807
|
+
ctx.io.line(`${c.ok ? "\u2713" : "\u2717"} ${c.name}${c.detail ? `: ${c.detail}` : ""}`);
|
|
786
808
|
if (!c.ok && c.remedy) ctx.io.line(` \u2192 ${c.remedy}`);
|
|
787
809
|
}
|
|
788
|
-
ctx.io.line(ok ? "\nAll checks passed." : "\nSome checks failed
|
|
810
|
+
ctx.io.line(ok ? "\nAll checks passed." : "\nSome checks failed. See fixes above.");
|
|
789
811
|
}
|
|
790
812
|
return ok ? EXIT.OK : EXIT.ERROR;
|
|
791
813
|
}
|
|
@@ -967,7 +989,7 @@ function createHookCommand(deps = {}) {
|
|
|
967
989
|
const copilotEventName = harness === "copilot" && isCopilotHookEventName(ctx.args[1]) ? ctx.args[1] : void 0;
|
|
968
990
|
if (harness === "copilot" && copilotEventName === void 0) {
|
|
969
991
|
ctx.io.errline(
|
|
970
|
-
`birdybeep hook copilot: second argument must be a Copilot hook event name, got ${JSON.stringify(ctx.args[1] ?? "(none)")}
|
|
992
|
+
`birdybeep hook copilot: second argument must be a Copilot hook event name, got ${JSON.stringify(ctx.args[1] ?? "(none)")}. Nothing was sent.`
|
|
971
993
|
);
|
|
972
994
|
return EXIT.USAGE;
|
|
973
995
|
}
|
|
@@ -985,7 +1007,7 @@ function createHookCommand(deps = {}) {
|
|
|
985
1007
|
}
|
|
986
1008
|
const drop = (reason, detail) => {
|
|
987
1009
|
ctx.io.result({ harness, outcome: "skipped", reason });
|
|
988
|
-
ctx.io.errline(`birdybeep hook ${harness}: ${detail}
|
|
1010
|
+
ctx.io.errline(`birdybeep hook ${harness}: ${detail}. Nothing was sent.`);
|
|
989
1011
|
return EXIT.ERROR;
|
|
990
1012
|
};
|
|
991
1013
|
if (read === null) {
|
|
@@ -1015,7 +1037,7 @@ function createHookCommand(deps = {}) {
|
|
|
1015
1037
|
});
|
|
1016
1038
|
const article = handler === "opencode" ? "an" : "a";
|
|
1017
1039
|
ctx.io.errline(
|
|
1018
|
-
`birdybeep hook ${harness}: ${describeDiscriminator(payload)} is not ${article} ${handler} hook event
|
|
1040
|
+
`birdybeep hook ${harness}: ${describeDiscriminator(payload)} is not ${article} ${handler} hook event. Nothing was sent. Check which tool is running this hook.`
|
|
1019
1041
|
);
|
|
1020
1042
|
return EXIT.ERROR;
|
|
1021
1043
|
}
|
|
@@ -1029,17 +1051,23 @@ function createHookCommand(deps = {}) {
|
|
|
1029
1051
|
eventType: result.eventType,
|
|
1030
1052
|
...result.send?.decision ? { decision: result.send.decision } : {},
|
|
1031
1053
|
...result.send?.status !== void 0 ? { status: result.send.status } : {},
|
|
1054
|
+
...result.send?.queueCause !== void 0 ? { queueCause: result.send.queueCause } : {},
|
|
1032
1055
|
...result.send?.tokenStoreUnavailable !== void 0 ? { tokenStore: "unavailable" } : {}
|
|
1033
1056
|
});
|
|
1034
1057
|
if (result.outcome === "unpaired") {
|
|
1035
1058
|
ctx.io.errline(
|
|
1036
|
-
"birdybeep: this machine is not paired
|
|
1059
|
+
"birdybeep: this machine is not paired. The event was not sent or queued. Run `birdybeep pair`, or run `birdybeep doctor` to see how many events were missed."
|
|
1060
|
+
);
|
|
1061
|
+
}
|
|
1062
|
+
if (result.outcome === "failed") {
|
|
1063
|
+
ctx.io.errline(
|
|
1064
|
+
"birdybeep: the event could not be sent or saved locally. It will not retry. Check that BirdyBeep can write to its user data directory, then run `birdybeep doctor`."
|
|
1037
1065
|
);
|
|
1038
1066
|
}
|
|
1039
|
-
const unavailable = result.send?.tokenStoreUnavailable;
|
|
1067
|
+
const unavailable = result.outcome === "queued" ? result.send?.tokenStoreUnavailable : void 0;
|
|
1040
1068
|
if (unavailable !== void 0) {
|
|
1041
1069
|
ctx.io.errline(
|
|
1042
|
-
`birdybeep:
|
|
1070
|
+
`birdybeep: the machine token is unreadable (${unavailable.reason}). The event is queued. Restore token-store access; \`birdybeep doctor\` drains the queue.`
|
|
1043
1071
|
);
|
|
1044
1072
|
}
|
|
1045
1073
|
return EXIT.OK;
|
|
@@ -1057,7 +1085,7 @@ function createLogoutCommand(deps = {}) {
|
|
|
1057
1085
|
usage: "birdybeep logout",
|
|
1058
1086
|
run: async (ctx) => {
|
|
1059
1087
|
await clearToken(deps.tokenOptions ?? {});
|
|
1060
|
-
ctx.io.emit("Logged out
|
|
1088
|
+
ctx.io.emit("Logged out. The machine token was removed.", { loggedOut: true });
|
|
1061
1089
|
return EXIT.OK;
|
|
1062
1090
|
}
|
|
1063
1091
|
};
|
|
@@ -1084,14 +1112,14 @@ function createUnpairCommand(deps = {}) {
|
|
|
1084
1112
|
const timeoutMs = deps.timeoutMs ?? 1e4;
|
|
1085
1113
|
return {
|
|
1086
1114
|
name: "unpair",
|
|
1087
|
-
summary: "
|
|
1115
|
+
summary: "Revoke this machine and remove its local token",
|
|
1088
1116
|
usage: "birdybeep unpair",
|
|
1089
1117
|
run: async (ctx) => {
|
|
1090
1118
|
const token = await getToken2(deps.tokenOptions ?? {});
|
|
1091
1119
|
const outcome = token === null ? "no_token" : await revokeSelf(token, fetchImpl, timeoutMs);
|
|
1092
1120
|
await clearToken(deps.tokenOptions ?? {});
|
|
1093
1121
|
const serverRevoked = outcome === "revoked";
|
|
1094
|
-
const human = outcome === "revoked" ? "Unpaired
|
|
1122
|
+
const human = outcome === "revoked" ? "Unpaired. The machine was revoked and removed from your account." : outcome === "no_token" ? "Already unpaired. There was no local token to remove." : outcome === "unreachable" ? "Unpaired locally, but the server was unreachable. The machine may still appear in the app. Open BirdyBeep and revoke it there." : "Unpaired locally, but the server did not confirm removal. If the machine still appears in the app, revoke it there.";
|
|
1095
1123
|
ctx.io.emit(human, { unpaired: true, serverRevoked });
|
|
1096
1124
|
return EXIT.OK;
|
|
1097
1125
|
}
|
|
@@ -1196,6 +1224,7 @@ import { opencodeAdapter as opencodeAdapter3 } from "@birdybeep/opencode";
|
|
|
1196
1224
|
import { randomUUID } from "crypto";
|
|
1197
1225
|
import {
|
|
1198
1226
|
createSender as defaultCreateSender3,
|
|
1227
|
+
describeExhaustedQuota,
|
|
1199
1228
|
fetchPushReachability as fetchPushReachability2,
|
|
1200
1229
|
getMachineIdentity as getMachineIdentity2,
|
|
1201
1230
|
normalizeEvent
|
|
@@ -1213,8 +1242,8 @@ function buildTestEvent(opts = {}) {
|
|
|
1213
1242
|
source_session_id: `birdybeep-cli-test-${randomUUID()}`,
|
|
1214
1243
|
machine: { label: machine.label, os: machine.os },
|
|
1215
1244
|
workspace: { cwd: process.cwd() },
|
|
1216
|
-
title: "BirdyBeep test
|
|
1217
|
-
body: "
|
|
1245
|
+
title: "BirdyBeep test",
|
|
1246
|
+
body: "Notifications from this machine are working.",
|
|
1218
1247
|
metadata: { test: true }
|
|
1219
1248
|
},
|
|
1220
1249
|
opts
|
|
@@ -1237,6 +1266,7 @@ function createTestCommand(deps = {}) {
|
|
|
1237
1266
|
outcome: result.outcome,
|
|
1238
1267
|
...result.status ? { status: result.status } : {},
|
|
1239
1268
|
...result.decision ? { decision: result.decision } : {},
|
|
1269
|
+
...result.queueCause ? { queueCause: result.queueCause } : {},
|
|
1240
1270
|
...result.tokenStoreUnavailable !== void 0 ? { tokenStore: "unavailable", tokenStoreReason: result.tokenStoreUnavailable.reason } : {}
|
|
1241
1271
|
});
|
|
1242
1272
|
} else if (result.outcome === "delivered") {
|
|
@@ -1248,42 +1278,62 @@ function createTestCommand(deps = {}) {
|
|
|
1248
1278
|
});
|
|
1249
1279
|
if (reach.state === "ok" && reach.data.active_device_count === 0) {
|
|
1250
1280
|
ctx.io.line(
|
|
1251
|
-
"\u26A0
|
|
1281
|
+
"\u26A0 No active device can receive a Beep. Open BirdyBeep on your phone and sign in to register it. If the device limit is full, free a slot in Settings \u203A devices."
|
|
1252
1282
|
);
|
|
1253
1283
|
} else if (reach.state === "ok") {
|
|
1254
1284
|
ctx.io.line(
|
|
1255
|
-
`\u2713 Test event accepted
|
|
1285
|
+
`\u2713 Test event accepted for ${String(reach.data.active_device_count)} registered device(s). Check your phone for a test Beep.`
|
|
1256
1286
|
);
|
|
1257
1287
|
} else {
|
|
1258
|
-
ctx.io.line("\u2713 Test event accepted by the backend
|
|
1288
|
+
ctx.io.line("\u2713 Test event accepted by the backend. Check your phone for a test Beep.");
|
|
1259
1289
|
}
|
|
1260
1290
|
} else if (result.decision === "suppressed") {
|
|
1261
1291
|
ctx.io.line(
|
|
1262
|
-
"\u26A0
|
|
1292
|
+
"\u26A0 Test event accepted; push suppressed. Check machine and integration mutes in the app."
|
|
1263
1293
|
);
|
|
1264
1294
|
} else if (result.decision === "deduped") {
|
|
1265
|
-
ctx.io.line(
|
|
1266
|
-
"\u26A0 The backend accepted the test event but folded it into a recent duplicate \u2014 wait ~30s and run `birdybeep test` again."
|
|
1267
|
-
);
|
|
1295
|
+
ctx.io.line("\u26A0 Test event matched a recent duplicate. Retry in 30 seconds.");
|
|
1268
1296
|
} else {
|
|
1269
1297
|
ctx.io.line(
|
|
1270
|
-
`\u26A0
|
|
1298
|
+
`\u26A0 Test event accepted; push decision was "${result.decision}". Run \`birdybeep doctor\`.`
|
|
1271
1299
|
);
|
|
1272
1300
|
}
|
|
1273
1301
|
} else if (result.outcome === "unpaired") {
|
|
1274
1302
|
ctx.io.line(
|
|
1275
|
-
"\u2717
|
|
1303
|
+
"\u2717 This machine is not paired. Run `birdybeep pair`. No event was sent or queued."
|
|
1304
|
+
);
|
|
1305
|
+
} else if (result.outcome === "failed") {
|
|
1306
|
+
ctx.io.line(
|
|
1307
|
+
"\u2717 The test event could not be sent or saved locally. Check BirdyBeep's data-directory permissions, then retry."
|
|
1276
1308
|
);
|
|
1277
1309
|
} else if (result.tokenStoreUnavailable !== void 0) {
|
|
1278
1310
|
ctx.io.line(
|
|
1279
|
-
`\u2022
|
|
1311
|
+
`\u2022 The machine token is unreadable (${result.tokenStoreUnavailable.reason}). The test event is queued. Restore token-store access, then retry.`
|
|
1280
1312
|
);
|
|
1313
|
+
} else if (result.outcome === "queued" && result.queueCause === "backend") {
|
|
1314
|
+
const status = result.status !== void 0 ? ` HTTP ${String(result.status)}` : " an error";
|
|
1315
|
+
ctx.io.line(`\u2022 Backend returned${status}. The test event is queued for retry.`);
|
|
1281
1316
|
} else if (result.outcome === "queued") {
|
|
1282
|
-
ctx.io.line("\u2022
|
|
1317
|
+
ctx.io.line("\u2022 Could not reach the backend. The test event is queued.");
|
|
1318
|
+
} else if (result.code === "quota_exceeded") {
|
|
1319
|
+
const reach = await fetchPushReachability2({
|
|
1320
|
+
baseUrl,
|
|
1321
|
+
...deps.tokenOptions ? { tokenOptions: deps.tokenOptions } : {},
|
|
1322
|
+
...deps.fetchImpl ? { fetchImpl: deps.fetchImpl } : {}
|
|
1323
|
+
});
|
|
1324
|
+
const quota = reach.state === "ok" ? reach.data.quota : void 0;
|
|
1325
|
+
const exhausted = quota !== void 0 && quota.beeps_limit !== null ? describeExhaustedQuota({ ...quota, beeps_limit: quota.beeps_limit }) : void 0;
|
|
1326
|
+
ctx.io.line(
|
|
1327
|
+
quota?.beeps_limit === null ? "\u2717 Test event rejected. The backend rejected this event for quota, but this account now reports unlimited beeps on Plus. The plan changed between those requests; run `birdybeep test` again. If it repeats, run `birdybeep doctor`." : quota && exhausted ? `\u2717 Test event rejected. This account's monthly beep quota is used up (${String(quota.beeps_accepted)}/${String(quota.beeps_limit)} beeps on the ${quota.plan} plan, period ${exhausted.window}). Every notifiable event is being rejected. ${exhausted.remedy}` : (
|
|
1328
|
+
// An older backend reports no quota, so there is no period and no reset date to
|
|
1329
|
+
// give — and `doctor` cannot supply them either, since it reads this same response.
|
|
1330
|
+
"\u2717 Test event rejected. This account's monthly beep quota is used up, so no Beep can be sent. This server does not report the period or the reset date; check your usage in the BirdyBeep app."
|
|
1331
|
+
)
|
|
1332
|
+
);
|
|
1283
1333
|
} else {
|
|
1284
1334
|
ctx.io.line("\u2717 Test event was rejected by the backend. Run `birdybeep doctor`.");
|
|
1285
1335
|
}
|
|
1286
|
-
return result.outcome === "dropped" || result.outcome === "unpaired" ? EXIT.ERROR : EXIT.OK;
|
|
1336
|
+
return result.outcome === "dropped" || result.outcome === "unpaired" || result.outcome === "failed" ? EXIT.ERROR : EXIT.OK;
|
|
1287
1337
|
}
|
|
1288
1338
|
};
|
|
1289
1339
|
}
|
|
@@ -1383,7 +1433,7 @@ function buildHarnessReports(installs, groups) {
|
|
|
1383
1433
|
}
|
|
1384
1434
|
] : surfaces.map((state) => {
|
|
1385
1435
|
const graded = rowState(state, group, status);
|
|
1386
|
-
const remedy = surfaceRemedy(state, group) ?? (graded === "not covered" ? `${adapter.displayName} carries no BirdyBeep hooks
|
|
1436
|
+
const remedy = surfaceRemedy(state, group) ?? (graded === "not covered" ? `${adapter.displayName} carries no BirdyBeep hooks. Re-run \`birdybeep agent install ${installTarget2(adapter.id)}\` from a shell where \`birdybeep\` resolves.` : void 0);
|
|
1387
1437
|
return {
|
|
1388
1438
|
harness: adapter.id,
|
|
1389
1439
|
displayName: adapter.displayName,
|
|
@@ -1429,12 +1479,12 @@ function describeMissing(reports) {
|
|
|
1429
1479
|
const names = missing.map((r) => r.displayName);
|
|
1430
1480
|
if (missing.length === reports.length) {
|
|
1431
1481
|
return [
|
|
1432
|
-
"No supported coding agent is installed on this machine
|
|
1433
|
-
`Install one of ${names.join(", ")}, then run \`birdybeep setup\` again
|
|
1482
|
+
"No supported coding agent is installed on this machine.",
|
|
1483
|
+
`Install one of ${names.join(", ")}, then run \`birdybeep setup\` again.`
|
|
1434
1484
|
];
|
|
1435
1485
|
}
|
|
1436
1486
|
return [
|
|
1437
|
-
`Not installed: ${names.join(", ")}. Install any of them, then run \`birdybeep setup\` again
|
|
1487
|
+
`Not installed: ${names.join(", ")}. Install any of them, then run \`birdybeep setup\` again.`
|
|
1438
1488
|
];
|
|
1439
1489
|
}
|
|
1440
1490
|
async function runHarnessSetup(ctx, options, deps = {}) {
|
|
@@ -1657,7 +1707,7 @@ function createPairingCommand(verb, deps = {}) {
|
|
|
1657
1707
|
},
|
|
1658
1708
|
{
|
|
1659
1709
|
flag: "--no-install",
|
|
1660
|
-
summary: "Stop after pairing
|
|
1710
|
+
summary: "Stop after pairing; do not install coding-agent hooks"
|
|
1661
1711
|
},
|
|
1662
1712
|
{
|
|
1663
1713
|
flag: "--no-test",
|
|
@@ -1677,7 +1727,7 @@ function createPairingCommand(verb, deps = {}) {
|
|
|
1677
1727
|
};
|
|
1678
1728
|
if (verb.skipWhenPaired && await getToken3(deps.tokenOptions ?? {}) !== null) {
|
|
1679
1729
|
ctx.io.line(
|
|
1680
|
-
chain !== void 0 ? "\u2713 Already paired
|
|
1730
|
+
chain !== void 0 ? "\u2713 Already paired. Checking installed coding-agent hooks." : "\u2713 Already paired. Nothing else to do with --no-install."
|
|
1681
1731
|
);
|
|
1682
1732
|
const report2 = chain !== void 0 ? await runSetupChain(ctx, setupDeps, pairFlags) : void 0;
|
|
1683
1733
|
ctx.io.result({
|
|
@@ -1713,7 +1763,7 @@ function createPairingCommand(verb, deps = {}) {
|
|
|
1713
1763
|
ctx.io.line(
|
|
1714
1764
|
` Session code (display only; cannot approve by itself): ${start.user_code}`
|
|
1715
1765
|
);
|
|
1716
|
-
ctx.io.line("Waiting for
|
|
1766
|
+
ctx.io.line("Waiting for approval in the BirdyBeep app.");
|
|
1717
1767
|
}
|
|
1718
1768
|
const deadline = Date.parse(start.expires_at);
|
|
1719
1769
|
const startedAt = clock();
|
|
@@ -1742,7 +1792,7 @@ function createPairingCommand(verb, deps = {}) {
|
|
|
1742
1792
|
}
|
|
1743
1793
|
if (!ctx.flags.json && nowMs - lastBeat >= HEARTBEAT_MS) {
|
|
1744
1794
|
ctx.io.line(
|
|
1745
|
-
poll.status === "error" ? `
|
|
1795
|
+
poll.status === "error" ? ` Backend unavailable (${poll.message}); retrying.` : " Waiting for approval in the BirdyBeep app."
|
|
1746
1796
|
);
|
|
1747
1797
|
lastBeat = nowMs;
|
|
1748
1798
|
}
|
|
@@ -1782,7 +1832,7 @@ function createPairingCommand(verb, deps = {}) {
|
|
|
1782
1832
|
if (decision.action === "prompt" && !isAffirmative(await promptLine(decision.question, decision.on))) {
|
|
1783
1833
|
ctx.io.result({ paired: false, reason: "declined" });
|
|
1784
1834
|
ctx.io.errline(
|
|
1785
|
-
"Pairing declined
|
|
1835
|
+
"Pairing declined. 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."
|
|
1786
1836
|
);
|
|
1787
1837
|
return EXIT.ERROR;
|
|
1788
1838
|
}
|
|
@@ -1791,8 +1841,8 @@ function createPairingCommand(verb, deps = {}) {
|
|
|
1791
1841
|
const discarded = new LocalEventQueue2().discardBefore(clock());
|
|
1792
1842
|
clearUnpairedNotice();
|
|
1793
1843
|
const humanSuffix = approvedBy !== void 0 ? ` to ${approvedBy}` : "";
|
|
1794
|
-
const discardedSuffix = discarded > 0 ? ` Discarded ${discarded} event(s) queued before pairing
|
|
1795
|
-
const nextStep = chain === void 0 ? " Run `birdybeep setup` to
|
|
1844
|
+
const discardedSuffix = discarded > 0 ? ` Discarded ${discarded} event(s) queued before pairing. They will not produce notifications.` : "";
|
|
1845
|
+
const nextStep = chain === void 0 ? " Run `birdybeep setup` to install coding-agent hooks." : "";
|
|
1796
1846
|
ctx.io.line(`\u2713 Paired${humanSuffix}.${nextStep}${discardedSuffix}`);
|
|
1797
1847
|
const report = chain !== void 0 ? await runSetupChain(ctx, setupDeps, pairFlags) : void 0;
|
|
1798
1848
|
ctx.io.result({
|
|
@@ -1810,7 +1860,7 @@ function createPairCommand(deps = {}) {
|
|
|
1810
1860
|
return createPairingCommand(
|
|
1811
1861
|
{
|
|
1812
1862
|
name: "pair",
|
|
1813
|
-
summary: "Pair this machine and
|
|
1863
|
+
summary: "Pair this machine and install detected coding-agent hooks",
|
|
1814
1864
|
usage: "birdybeep pair [--yes] [--expect-email <addr>] [--no-install] [--no-test] [--json]",
|
|
1815
1865
|
skipWhenPaired: false
|
|
1816
1866
|
},
|
|
@@ -1821,9 +1871,9 @@ function createSetupCommand(deps = {}) {
|
|
|
1821
1871
|
return createPairingCommand(
|
|
1822
1872
|
{
|
|
1823
1873
|
name: "setup",
|
|
1824
|
-
summary: "Set up BirdyBeep
|
|
1874
|
+
summary: "Set up BirdyBeep on this machine",
|
|
1825
1875
|
usage: "birdybeep setup [--yes] [--expect-email <addr>] [--no-install] [--no-test] [--json]",
|
|
1826
|
-
gettingStarted: "
|
|
1876
|
+
gettingStarted: "Connect this machine and install hooks for detected coding agents.",
|
|
1827
1877
|
skipWhenPaired: true
|
|
1828
1878
|
},
|
|
1829
1879
|
deps
|
|
@@ -1900,7 +1950,7 @@ function createReportStatusCommand(deps = {}) {
|
|
|
1900
1950
|
run: async (ctx) => {
|
|
1901
1951
|
const token = await getToken4(deps.tokenOptions ?? {});
|
|
1902
1952
|
if (token === null) {
|
|
1903
|
-
ctx.io.errline("No machine token
|
|
1953
|
+
ctx.io.errline("No machine token. Run `birdybeep pair` first.");
|
|
1904
1954
|
return EXIT.ERROR;
|
|
1905
1955
|
}
|
|
1906
1956
|
const items = await gatherItems(adapters);
|
|
@@ -1945,12 +1995,12 @@ function createReportStatusCommand(deps = {}) {
|
|
|
1945
1995
|
});
|
|
1946
1996
|
} else if (outcome === "terminal") {
|
|
1947
1997
|
ctx.io.errline(
|
|
1948
|
-
`Report rejected (${errorCode ?? "auth"})
|
|
1998
|
+
`Report rejected (${errorCode ?? "auth"}). Your token may be revoked. Re-run \`birdybeep pair\`.`
|
|
1949
1999
|
);
|
|
1950
2000
|
} else {
|
|
1951
2001
|
for (const e of effective) {
|
|
1952
2002
|
ctx.io.line(
|
|
1953
|
-
outcome === "reported" ? `\u2713 ${e.harness}: ${e.status} (reported)` : `\u2022 ${e.harness}: ${e.status} (deferred
|
|
2003
|
+
outcome === "reported" ? `\u2713 ${e.harness}: ${e.status} (reported)` : `\u2022 ${e.harness}: ${e.status} (deferred; backend unreachable)`
|
|
1954
2004
|
);
|
|
1955
2005
|
}
|
|
1956
2006
|
}
|
|
@@ -2012,7 +2062,7 @@ function createStatusCommand(deps = {}) {
|
|
|
2012
2062
|
} else {
|
|
2013
2063
|
ctx.io.line(`Machine: ${machine.label} (${machine.os})`);
|
|
2014
2064
|
ctx.io.line(
|
|
2015
|
-
pairing.state === "paired" ? "Paired: yes" : pairing.state === "unpaired" ? "Paired: no
|
|
2065
|
+
pairing.state === "paired" ? "Paired: yes" : pairing.state === "unpaired" ? "Paired: no. Run `birdybeep pair`." : `Paired: unknown. ${describeTokenStoreUnavailable(pairing)}`
|
|
2016
2066
|
);
|
|
2017
2067
|
ctx.io.line("Integrations:");
|
|
2018
2068
|
for (const i of integrations) {
|
|
@@ -2020,7 +2070,7 @@ function createStatusCommand(deps = {}) {
|
|
|
2020
2070
|
const group = surfaces.find((g) => g.harness === i.harness);
|
|
2021
2071
|
for (const state of group?.surfaces ?? []) {
|
|
2022
2072
|
const mark = state.coverage === "active" ? "\u2713" : state.coverage === "wired" ? "\xB7" : "\u2717";
|
|
2023
|
-
ctx.io.line(` ${mark} ${describeSurface(state)}
|
|
2073
|
+
ctx.io.line(` ${mark} ${describeSurface(state)}: ${state.coverage}`);
|
|
2024
2074
|
}
|
|
2025
2075
|
}
|
|
2026
2076
|
ctx.io.line(
|
|
@@ -2212,4 +2262,4 @@ export {
|
|
|
2212
2262
|
buildCommands,
|
|
2213
2263
|
runCli
|
|
2214
2264
|
};
|
|
2215
|
-
//# sourceMappingURL=chunk-
|
|
2265
|
+
//# sourceMappingURL=chunk-J45QGUDO.js.map
|