starbridge 0.1.3 → 0.1.4-rc.3
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/README.md +10 -8
- package/dist/starbridge.js +69 -32
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -72,7 +72,7 @@ question; steps 1 and 3 say when it asks more. A rerun repairs only what is miss
|
|
|
72
72
|
4. It installs and starts the background service, `starbridge agent`, as a systemd user unit, a
|
|
73
73
|
launchd agent, or on Windows a Scheduled Task that starts at logon without administrator
|
|
74
74
|
rights and logs to `%LOCALAPPDATA%\starbridge\agent.log`.
|
|
75
|
-
5. It uploads a first quota snapshot, then offers to send a test question to your
|
|
75
|
+
5. It uploads a first quota snapshot, then offers to send a test question to your devices and
|
|
76
76
|
prints your answer.
|
|
77
77
|
|
|
78
78
|
[Permission prompts](#permission-prompts) stay in the terminal unless you turn them on with
|
|
@@ -110,7 +110,9 @@ and asks your devices to revoke the machine. It deletes the keys only when you s
|
|
|
110
110
|
### Check a download
|
|
111
111
|
|
|
112
112
|
The scripts and `starbridge update` install a binary only if its hash is in the release's
|
|
113
|
-
`SHA256SUMS` and the release key signed `SHA256SUMS.minisig`. `install.
|
|
113
|
+
`SHA256SUMS` and the release key signed `SHA256SUMS.minisig`. `install.sh` checks the signature
|
|
114
|
+
with minisign, else OpenSSL 3, else Python 3, which every RHEL 8 has for dnf; with none of them it
|
|
115
|
+
stops and gives the command that installs minisign. `install.ps1` checks the signature
|
|
114
116
|
with minisign's own Windows build, pinned by its hash, since Windows has no Ed25519 check. The key is also in
|
|
115
117
|
[`minisign.pub`](minisign.pub):
|
|
116
118
|
|
|
@@ -170,10 +172,10 @@ starbridge ask --question "Merge #12 now?" \
|
|
|
170
172
|
- Anywhere else, the agent waits for it with `starbridge wait <id> --timeout 5m`, which exits
|
|
171
173
|
with code 2 when the time runs out.
|
|
172
174
|
|
|
173
|
-
To check the path to your
|
|
175
|
+
To check the path to your devices yourself, ask and wait in one command:
|
|
174
176
|
|
|
175
177
|
```bash
|
|
176
|
-
starbridge ask --question "Does this reach
|
|
178
|
+
starbridge ask --question "Does this reach me?" --option Yes --option No --wait
|
|
177
179
|
```
|
|
178
180
|
|
|
179
181
|
When the agent runs out of other work, `starbridge waiting <id>` shows "Waiting for you" on
|
|
@@ -232,7 +234,7 @@ To upload without the service:
|
|
|
232
234
|
starbridge quota push --provider claude --provider codex
|
|
233
235
|
```
|
|
234
236
|
|
|
235
|
-
### Quiet
|
|
237
|
+
### Quiet your other devices while you're here
|
|
236
238
|
|
|
237
239
|
On a desktop or laptop you work at, turn on presence:
|
|
238
240
|
|
|
@@ -242,9 +244,9 @@ starbridge config presence on
|
|
|
242
244
|
|
|
243
245
|
While this machine's screen is unlocked and had keyboard or mouse input in the last minute,
|
|
244
246
|
notifications on your other devices wait for the time set in Starbridge's Settings (30 s by
|
|
245
|
-
default) and come only if the question is still open, so one you answer here doesn't buzz
|
|
246
|
-
|
|
247
|
-
|
|
247
|
+
default) and come only if the question is still open, so one you answer here doesn't buzz them.
|
|
248
|
+
The question itself shows everywhere at once. The machine reads its lock and idle time itself
|
|
249
|
+
and tells the server only yes or no. It works on macOS, Windows, and Linux under GNOME
|
|
248
250
|
or X11 with `xprintidle`; a machine with no screen sends nothing.
|
|
249
251
|
|
|
250
252
|
### Permission prompts
|
package/dist/starbridge.js
CHANGED
|
@@ -10203,6 +10203,7 @@ var PermissionAnswer = object({
|
|
|
10203
10203
|
if (a.behavior === "allow" && a.message !== undefined)
|
|
10204
10204
|
ctx.addIssue({ code: "custom", message: "message is for a deny" });
|
|
10205
10205
|
});
|
|
10206
|
+
var SETTLED_REASON_MAX = 120;
|
|
10206
10207
|
var Settled = object({
|
|
10207
10208
|
v: literal(1),
|
|
10208
10209
|
id: Id,
|
|
@@ -10214,7 +10215,8 @@ var Settled = object({
|
|
|
10214
10215
|
choice: string2().max(100).optional(),
|
|
10215
10216
|
text: string2().max(4000).optional(),
|
|
10216
10217
|
dir: DirectoryHead.optional(),
|
|
10217
|
-
behavior: _enum(["allow", "deny"]).optional()
|
|
10218
|
+
behavior: _enum(["allow", "deny"]).optional(),
|
|
10219
|
+
reason: string2().min(1).max(SETTLED_REASON_MAX).regex(/^[^\r\n]*$/, "one line").optional()
|
|
10218
10220
|
}).refine((s) => s.device !== undefined || s.choice === undefined && s.text === undefined, {
|
|
10219
10221
|
message: "choice and text come only with a device's outcome"
|
|
10220
10222
|
}).refine((s) => s.choice === undefined || s.text === undefined, {
|
|
@@ -14368,6 +14370,7 @@ function checkJoined(dir, me) {
|
|
|
14368
14370
|
if (!entry?.active || entry.member.role !== me.role || entry.member.boxPk !== me.boxPk || entry.member.signPk !== me.signPk)
|
|
14369
14371
|
throw new ProtocolError("unknown-member", "the directory does not hold my keys");
|
|
14370
14372
|
}
|
|
14373
|
+
var CHECK_CONFIRM_MS = 10 * 60000;
|
|
14371
14374
|
function checkInput(entries, id) {
|
|
14372
14375
|
const nul = new Uint8Array([0]);
|
|
14373
14376
|
for (const e of entries) {
|
|
@@ -14426,7 +14429,10 @@ function publicKeys(keys) {
|
|
|
14426
14429
|
// ../packages/protocol/src/notifications.ts
|
|
14427
14430
|
var NotifyState = _enum(["on", "off", "blocked"]);
|
|
14428
14431
|
var DeviceNotifications = object({ state: NotifyState });
|
|
14429
|
-
var NotificationStates = object({
|
|
14432
|
+
var NotificationStates = object({
|
|
14433
|
+
devices: record(string2(), NotifyState),
|
|
14434
|
+
clients: record(string2(), string2()).optional()
|
|
14435
|
+
});
|
|
14430
14436
|
var AppSignedIn = object({ signedIn: boolean2() });
|
|
14431
14437
|
// ../packages/protocol/src/pace.ts
|
|
14432
14438
|
var MIN_ELAPSED_FRACTION = 0.05;
|
|
@@ -14649,7 +14655,7 @@ function processAlive(pid) {
|
|
|
14649
14655
|
// package.json
|
|
14650
14656
|
var package_default = {
|
|
14651
14657
|
name: "starbridge",
|
|
14652
|
-
version: "0.1.3",
|
|
14658
|
+
version: "0.1.4-rc.3",
|
|
14653
14659
|
description: "Post decisions to your Starbridge devices and upload quota windows from CodexBar",
|
|
14654
14660
|
license: "MIT",
|
|
14655
14661
|
repository: {
|
|
@@ -16123,7 +16129,7 @@ function needsHello(ctx, id) {
|
|
|
16123
16129
|
markConversation(ctx, id);
|
|
16124
16130
|
return false;
|
|
16125
16131
|
}
|
|
16126
|
-
var NUDGE = "Starbridge sends this session's permission prompts and questions to the owner's
|
|
16132
|
+
var NUDGE = "Starbridge sends this session's permission prompts and questions to the owner's devices once it has run a starbridge command. Before anything else, run `starbridge hello` once.";
|
|
16127
16133
|
function hookPreInvocation(ctx, text, opts) {
|
|
16128
16134
|
if (opts.agent !== "antigravity")
|
|
16129
16135
|
throw new UsageError(`--agent: antigravity (got ${opts.agent ?? "nothing"})`);
|
|
@@ -16538,6 +16544,12 @@ function sourceFor(input, ctx, machine) {
|
|
|
16538
16544
|
...r.sessionLinks.length > 0 ? { links: r.sessionLinks } : {}
|
|
16539
16545
|
};
|
|
16540
16546
|
}
|
|
16547
|
+
function typedBreaks(context) {
|
|
16548
|
+
if (/[\r\n]/.test(context))
|
|
16549
|
+
return context;
|
|
16550
|
+
return context.split(/(`[^`]+`)/).map((part, i) => i % 2 === 1 ? part : part.replace(/(?<!\\)\\n/g, `
|
|
16551
|
+
`)).join("");
|
|
16552
|
+
}
|
|
16541
16553
|
function buildDecision(input, ctx, machine, to) {
|
|
16542
16554
|
if (!input.question)
|
|
16543
16555
|
throw new UsageError("ask needs --question");
|
|
@@ -16731,16 +16743,33 @@ async function settle2(ctx, opts) {
|
|
|
16731
16743
|
if (opts.outcome !== undefined && opts.outcome !== "elsewhere" && opts.outcome !== "withdrawn")
|
|
16732
16744
|
throw new UsageError("--outcome is elsewhere or withdrawn");
|
|
16733
16745
|
const outcome = opts.outcome;
|
|
16746
|
+
const { reason } = opts;
|
|
16747
|
+
if (reason !== undefined)
|
|
16748
|
+
checkReason(reason);
|
|
16734
16749
|
if (id) {
|
|
16735
16750
|
const asked = ctx.store.state().asked[id];
|
|
16736
16751
|
if (!asked)
|
|
16737
16752
|
throw new UsageError(`${id} is not a decision this machine asked`);
|
|
16753
|
+
const withdrawn = (outcome ?? (asked.answerIn ? "elsewhere" : "withdrawn")) === "withdrawn";
|
|
16754
|
+
if (withdrawn && reason === undefined)
|
|
16755
|
+
throw new UsageError(`say why you withdraw it, in one short line: --reason 'Fixed it myself after rereading the logs'`);
|
|
16756
|
+
if (!withdrawn && reason !== undefined)
|
|
16757
|
+
throw new UsageError("--reason is for --outcome withdrawn");
|
|
16738
16758
|
const s = session(ctx);
|
|
16739
|
-
await settleOne(ctx, s, await refreshDirectory(ctx, s), id, outcome);
|
|
16759
|
+
await settleOne(ctx, s, await refreshDirectory(ctx, s), id, outcome, false, reason);
|
|
16740
16760
|
return 0;
|
|
16741
16761
|
}
|
|
16762
|
+
if (reason !== undefined && outcome !== "withdrawn")
|
|
16763
|
+
throw new UsageError("--reason is for --outcome withdrawn");
|
|
16742
16764
|
return settleMany(ctx, opts, outcome);
|
|
16743
16765
|
}
|
|
16766
|
+
function pickerReason(outcome) {
|
|
16767
|
+
return outcome === "withdrawn" ? { reason: "Its answer could no longer reach the agent" } : {};
|
|
16768
|
+
}
|
|
16769
|
+
function checkReason(reason) {
|
|
16770
|
+
if (!reason.trim() || /[\r\n]/.test(reason) || reason.length > SETTLED_REASON_MAX)
|
|
16771
|
+
throw new UsageError(`--reason is one line of at most ${SETTLED_REASON_MAX} characters`);
|
|
16772
|
+
}
|
|
16744
16773
|
async function settleMany(ctx, opts, outcome) {
|
|
16745
16774
|
const st = ctx.store.state();
|
|
16746
16775
|
const open = Object.entries(st.asked).filter(([id, a]) => (!a.settled || a.unposted) && !a.revoked && !st.answers[id]).filter(([, a]) => opts.all || a.session === opts.session).map(([id]) => id);
|
|
@@ -16761,7 +16790,7 @@ async function settleMany(ctx, opts, outcome) {
|
|
|
16761
16790
|
return 130;
|
|
16762
16791
|
}
|
|
16763
16792
|
try {
|
|
16764
|
-
if (await settleOne(ctx, s, dir, id, outcome, true))
|
|
16793
|
+
if (await settleOne(ctx, s, dir, id, outcome, true, opts.reason))
|
|
16765
16794
|
done++;
|
|
16766
16795
|
break;
|
|
16767
16796
|
} catch (e) {
|
|
@@ -16778,7 +16807,7 @@ async function settleMany(ctx, opts, outcome) {
|
|
|
16778
16807
|
ctx.out(`Settled ${done} decision${done === 1 ? "" : "s"}.`);
|
|
16779
16808
|
return 0;
|
|
16780
16809
|
}
|
|
16781
|
-
async function settleOne(ctx, s, dir, id, outcomeOpt, bulk = false) {
|
|
16810
|
+
async function settleOne(ctx, s, dir, id, outcomeOpt, bulk = false, reason) {
|
|
16782
16811
|
const asked = ctx.store.state().asked[id];
|
|
16783
16812
|
if (!asked || asked.revoked)
|
|
16784
16813
|
return false;
|
|
@@ -16803,6 +16832,7 @@ async function settleOne(ctx, s, dir, id, outcomeOpt, bulk = false) {
|
|
|
16803
16832
|
to: to.map((d) => d.id),
|
|
16804
16833
|
at: iso2(ctx.now()),
|
|
16805
16834
|
outcome,
|
|
16835
|
+
...reason !== undefined && outcome === "withdrawn" ? { reason } : {},
|
|
16806
16836
|
dir: signedHead(dir)
|
|
16807
16837
|
};
|
|
16808
16838
|
try {
|
|
@@ -17636,7 +17666,7 @@ import { existsSync as existsSync6, mkdirSync as mkdirSync7, readFileSync as rea
|
|
|
17636
17666
|
import { dirname as dirname5, join as join11 } from "node:path";
|
|
17637
17667
|
|
|
17638
17668
|
// ../plugin/hooks/rule.md
|
|
17639
|
-
var rule_default = "I am often away from this terminal and may not read your final message for hours. Reach me through the `starbridge` skill, not here, when you need a decision that is mine, and before you end a turn on work that waits on me, such as a PR to review or merge, or a failure only I can fix. In Claude Code, a quick question may use AskUserQuestion instead, since it reaches my devices too. Decide everything else yourself and keep working. Ask here only when `starbridge` fails. Write each card to be read in seconds on a phone, as the skill says
|
|
17669
|
+
var rule_default = "I am often away from this terminal and may not read your final message for hours. Reach me through the `starbridge` skill, not here, when you need a decision that is mine, and before you end a turn on work that waits on me, such as a PR to review or merge, or a failure only I can fix. In Claude Code, a quick question may use AskUserQuestion instead, since it reaches my devices too. Decide everything else yourself and keep working. Ask here only when `starbridge` fails. Write each card to be read in seconds on a phone, as the skill says. When I answer one of your open cards here instead, withdraw it, saying why in a few words: `starbridge settle <id> --outcome withdrawn --reason 'You answered in the terminal'`.\n\nRun a command that blocks me (takes over the screen, keyboard or session, or holds a device I use), or that my instructions ask you to report, whole through `starbridge run`.\n";
|
|
17640
17670
|
|
|
17641
17671
|
// ../plugin/skills/starbridge/SKILL.md
|
|
17642
17672
|
var SKILL_default = `---
|
|
@@ -17714,7 +17744,8 @@ starbridge ask \\
|
|
|
17714
17744
|
\`\`\`
|
|
17715
17745
|
|
|
17716
17746
|
Quote text in single quotes, since double quotes expand \`$\` and backticks,
|
|
17717
|
-
and write apostrophes as ’.
|
|
17747
|
+
and write apostrophes as ’. Break lines inside the quotes with real line
|
|
17748
|
+
breaks, as above, never \`\\n\`: bash and zsh pass it on as typed. Other flags: \`--image\` (up to 4 PNG or JPEG),
|
|
17718
17749
|
\`--link\` (up to 4 HTTPS URLs), \`--waiting\` (only the answer unblocks you), \`--answer-in\`. \`ask\` prints the decision id (\`d_Xk3…\`) and how the
|
|
17719
17750
|
answer comes back.
|
|
17720
17751
|
|
|
@@ -17751,6 +17782,13 @@ if it leaves the choice open. Act on the answer at once. Post again only when
|
|
|
17751
17782
|
the outcome changes what they would do (the merge failed); otherwise say it
|
|
17752
17783
|
in your final message.
|
|
17753
17784
|
|
|
17785
|
+
A card still open that no longer needs an answer (the owner answered in the
|
|
17786
|
+
terminal, or you found it yourself) stays in their inbox until you take it
|
|
17787
|
+
back, before you act: \`starbridge settle <id> --outcome withdrawn --reason
|
|
17788
|
+
'…'\`. The reason shows in their History: one short line, 120 characters at
|
|
17789
|
+
most, such as \`'You answered in the terminal'\` or \`'Fixed it myself after
|
|
17790
|
+
rereading the logs'\`.
|
|
17791
|
+
|
|
17754
17792
|
## Answers in an artifact
|
|
17755
17793
|
|
|
17756
17794
|
When you built a claude.ai artifact whose buttons send the pick to this
|
|
@@ -17759,8 +17797,6 @@ arrives, run \`starbridge settle <id>\`, then act; a card answered in Starbridge
|
|
|
17759
17797
|
needs no settle. When the owner taps Done on the card instead, you get
|
|
17760
17798
|
\`Answer to <id> (…): answered on its page; read the answer there\`: the card is
|
|
17761
17799
|
closed, so read the pick on the page and act, with no settle. An artifact only to look at goes in \`--link\`, with options.
|
|
17762
|
-
\`starbridge settle <id> --outcome withdrawn\` takes back a card you no longer
|
|
17763
|
-
need.
|
|
17764
17800
|
|
|
17765
17801
|
## Report a run
|
|
17766
17802
|
|
|
@@ -18550,7 +18586,7 @@ function pickerOpen(st, session) {
|
|
|
18550
18586
|
return Object.entries(st.asked).filter(([id, a]) => a.picker === session && !a.settled && !st.answers[id]).map(([id]) => id);
|
|
18551
18587
|
}
|
|
18552
18588
|
async function settlePicker(ctx, session, outcome) {
|
|
18553
|
-
await Promise.all(pickerOpen(ctx.store.state(), session).map((id) => settle2({ ...ctx, signal: undefined }, { id, outcome }).catch((e) => ctx.err(`starbridge: could not settle ${id}: ${e.message}`))));
|
|
18589
|
+
await Promise.all(pickerOpen(ctx.store.state(), session).map((id) => settle2({ ...ctx, signal: undefined }, { id, outcome, ...pickerReason(outcome) }).catch((e) => ctx.err(`starbridge: could not settle ${id}: ${e.message}`))));
|
|
18554
18590
|
}
|
|
18555
18591
|
async function racePicker(ctx, outer, hook, waitText) {
|
|
18556
18592
|
const questions = pickerQuestions(hook.tool_input);
|
|
@@ -18686,7 +18722,7 @@ async function hookQuestion(outer, stdin, opts) {
|
|
|
18686
18722
|
return 0;
|
|
18687
18723
|
}
|
|
18688
18724
|
const outcome = outer.signal?.aborted ? "elsewhere" : "withdrawn";
|
|
18689
|
-
await Promise.all(ids.map((id) => id ? settle2({ ...ctx, signal: undefined }, { id, outcome }).catch((e) => ctx.err(`starbridge: could not settle ${id}: ${e.message}`)) : undefined));
|
|
18725
|
+
await Promise.all(ids.map((id) => id ? settle2({ ...ctx, signal: undefined }, { id, outcome, ...pickerReason(outcome) }).catch((e) => ctx.err(`starbridge: could not settle ${id}: ${e.message}`)) : undefined));
|
|
18690
18726
|
} catch (e) {
|
|
18691
18727
|
ctx.err(`starbridge: question not sent: ${e.message}`);
|
|
18692
18728
|
} finally {
|
|
@@ -19036,7 +19072,7 @@ import { configDir } from "./poller.ts";
|
|
|
19036
19072
|
|
|
19037
19073
|
/** The agent API revision this mod speaks, sent in the \`starbridge-api\` header. */
|
|
19038
19074
|
export const API = 1;
|
|
19039
|
-
export const VERSION = "0.1.3";
|
|
19075
|
+
export const VERSION = "0.1.4-rc.3";
|
|
19040
19076
|
const CLIENT = \`starbridge-mod/\${VERSION}\`;
|
|
19041
19077
|
|
|
19042
19078
|
/** Event types this mod submits; others are skipped and left unconfirmed, as the API asks. */
|
|
@@ -23041,7 +23077,7 @@ async function ask2(ctx, hook, questions, signal) {
|
|
|
23041
23077
|
if (picked.every((p) => p !== undefined) && !stop.aborted)
|
|
23042
23078
|
return picked;
|
|
23043
23079
|
const outcome = signal.aborted ? "elsewhere" : "withdrawn";
|
|
23044
|
-
await Promise.all(ids.map((id) => id ? settle2({ ...ctx, signal: undefined }, { id, outcome }).catch((e) => ctx.err(`starbridge: could not settle ${id}: ${e.message}`)) : undefined));
|
|
23080
|
+
await Promise.all(ids.map((id) => id ? settle2({ ...ctx, signal: undefined }, { id, outcome, ...pickerReason(outcome) }).catch((e) => ctx.err(`starbridge: could not settle ${id}: ${e.message}`)) : undefined));
|
|
23045
23081
|
return;
|
|
23046
23082
|
}
|
|
23047
23083
|
|
|
@@ -23897,7 +23933,6 @@ function addedAt(entries, id) {
|
|
|
23897
23933
|
}
|
|
23898
23934
|
}
|
|
23899
23935
|
}
|
|
23900
|
-
var CONFIRM_MS = 10 * 60000;
|
|
23901
23936
|
var waitingFile = (ctx) => join14(ctx.store.dir, "pair-waiting");
|
|
23902
23937
|
var answerFile = (ctx, code) => join14(ctx.store.dir, `pair-answer.${code}`);
|
|
23903
23938
|
function writeWhole(path, text) {
|
|
@@ -23935,7 +23970,7 @@ async function confirmCheck(ctx, code, name) {
|
|
|
23935
23970
|
typed.push(l);
|
|
23936
23971
|
}) : undefined;
|
|
23937
23972
|
try {
|
|
23938
|
-
const until = ctx.now().getTime() +
|
|
23973
|
+
const until = ctx.now().getTime() + CHECK_CONFIRM_MS;
|
|
23939
23974
|
for (;; ) {
|
|
23940
23975
|
if (ctx.signal?.aborted || ctx.now().getTime() > until)
|
|
23941
23976
|
return;
|
|
@@ -24548,14 +24583,14 @@ import {
|
|
|
24548
24583
|
import { dirname as dirname8, join as join16 } from "node:path";
|
|
24549
24584
|
// src/setup/codexbar-pin.json
|
|
24550
24585
|
var codexbar_pin_default = {
|
|
24551
|
-
version: "0.
|
|
24586
|
+
version: "0.74.0",
|
|
24552
24587
|
sha256: {
|
|
24553
|
-
"macos-arm64": "
|
|
24554
|
-
"macos-x86_64": "
|
|
24555
|
-
"linux-aarch64": "
|
|
24556
|
-
"linux-x86_64": "
|
|
24557
|
-
"linux-musl-aarch64": "
|
|
24558
|
-
"linux-musl-x86_64": "
|
|
24588
|
+
"macos-arm64": "9aab1b9229f5d1d77de21b99aa887a60a47921bfd9b66e4b941a0e8a9d6e38e8",
|
|
24589
|
+
"macos-x86_64": "4d239b7412683f1eb529d55b851fe922bb462527850b767d6c9b89ba14be3fdb",
|
|
24590
|
+
"linux-aarch64": "46dc3b259b1df48b3fd6f337c754dd60dbece6772d7840cdc9514ab4977a0007",
|
|
24591
|
+
"linux-x86_64": "fac8b2c474e4e37a2b067224437cb182693411ebf639441cd0facbb57bf086b1",
|
|
24592
|
+
"linux-musl-aarch64": "9dab39f1197d9ccc501d1f678234cf15f94e8b84aa7349ba87103a5866dd793b",
|
|
24593
|
+
"linux-musl-x86_64": "e7b0bdc6d6931a4e6fd5ce4037e1125a073ec6cf38cd72f1b3bd8aeecfde51f5"
|
|
24559
24594
|
}
|
|
24560
24595
|
};
|
|
24561
24596
|
|
|
@@ -25479,7 +25514,7 @@ async function setup(sys, opts) {
|
|
|
25479
25514
|
section(ctx, "Test");
|
|
25480
25515
|
if (upload)
|
|
25481
25516
|
await firstUpload(ctx, upload);
|
|
25482
|
-
if (machine && !opts.yes && await prompt.confirm(" Send a test decision to your
|
|
25517
|
+
if (machine && !opts.yes && await prompt.confirm(" Send a test decision to your devices?", true))
|
|
25483
25518
|
await testDecision(ctx, machine.name);
|
|
25484
25519
|
section(ctx, failed.length === 0 ? "Starbridge is set up." : `Setup is done, but ${failed.length === 1 ? "one step" : `${failed.length} steps`} failed.`);
|
|
25485
25520
|
for (const line of await otherCopies(sys))
|
|
@@ -25489,7 +25524,7 @@ async function setup(sys, opts) {
|
|
|
25489
25524
|
for (const line of commandTable([
|
|
25490
25525
|
["Check it", "starbridge status"],
|
|
25491
25526
|
...permissionsEnabled(ctx) ? [] : [["Send permission prompts", "starbridge config permissions on"]],
|
|
25492
|
-
...ctx.store.agentConfig().presence?.enabled || !["desktop", "laptop"].includes(ctx.store.agentConfig().machineKind ?? "") ? [] : [["Quiet
|
|
25527
|
+
...ctx.store.agentConfig().presence?.enabled || !["desktop", "laptop"].includes(ctx.store.agentConfig().machineKind ?? "") ? [] : [["Quiet other devices while here", "starbridge config presence on"]],
|
|
25493
25528
|
["Remove from one agent", `starbridge uninstall --agent ${one}`],
|
|
25494
25529
|
["Remove everything", "starbridge uninstall"]
|
|
25495
25530
|
]))
|
|
@@ -25781,7 +25816,7 @@ async function testDecision(ctx, name) {
|
|
|
25781
25816
|
session: ""
|
|
25782
25817
|
};
|
|
25783
25818
|
const opts = { wait: true, timeout: "10m" };
|
|
25784
|
-
ctx.out(" Sent. Answer it on
|
|
25819
|
+
ctx.out(" Sent. Answer it on any of your devices (Ctrl-C skips)…");
|
|
25785
25820
|
let id;
|
|
25786
25821
|
const asking = {
|
|
25787
25822
|
...ctx,
|
|
@@ -25809,7 +25844,7 @@ async function testDecision(ctx, name) {
|
|
|
25809
25844
|
if (code !== EXIT_INTERRUPTED || !id)
|
|
25810
25845
|
return;
|
|
25811
25846
|
try {
|
|
25812
|
-
await settle2(ctx, { id, outcome: "withdrawn" });
|
|
25847
|
+
await settle2(ctx, { id, outcome: "withdrawn", reason: "Skipped in starbridge setup" });
|
|
25813
25848
|
ctx.out("– Skipped");
|
|
25814
25849
|
} catch (e) {
|
|
25815
25850
|
ctx.out(`– Skipped, but the card stays open on your devices: ${e.message}`);
|
|
@@ -26254,11 +26289,12 @@ var HELP = `starbridge: post decisions to your devices, report runs, upload quot
|
|
|
26254
26289
|
Show the owner that you are now blocked on the decision ("Waiting for you", which
|
|
26255
26290
|
notifies them once more), or back to working on other things.
|
|
26256
26291
|
|
|
26257
|
-
starbridge settle <decision id> [--outcome elsewhere|withdrawn]
|
|
26292
|
+
starbridge settle <decision id> [--outcome elsewhere|withdrawn] [--reason <text>]
|
|
26258
26293
|
starbridge settle --session <id> | --all [--yes] [--outcome elsewhere|withdrawn]
|
|
26259
26294
|
Close a decision without a Starbridge answer: answered on its --answer-in page
|
|
26260
26295
|
(elsewhere, the default for those) or no longer needed (withdrawn). Devices move it
|
|
26261
|
-
out of the inbox.
|
|
26296
|
+
out of the inbox. A withdrawal says why in --reason, one short line of at most 120
|
|
26297
|
+
characters that History shows: --reason 'Fixed it myself after rereading the logs'. --session closes every open decision that session asked, --all every
|
|
26262
26298
|
one this machine asked, after asking (--yes skips the question), at the pace the
|
|
26263
26299
|
server allows: a looping agent's thousands take over an hour.
|
|
26264
26300
|
|
|
@@ -26313,7 +26349,7 @@ var HELP = `starbridge: post decisions to your devices, report runs, upload quot
|
|
|
26313
26349
|
starbridge plugin's permission hook exits at once. presence: while this machine's
|
|
26314
26350
|
screen is unlocked and had keyboard or mouse input in the last minute, notifications
|
|
26315
26351
|
on your other devices wait a few seconds, so a question you answer here doesn't buzz
|
|
26316
|
-
|
|
26352
|
+
them; the server hears only yes or no. Off by default. machine-kind: the icon
|
|
26317
26353
|
devices show, detected by setup.
|
|
26318
26354
|
|
|
26319
26355
|
starbridge hook permission --agent claude-code|pi|opencode|antigravity [--wait 570s]
|
|
@@ -26465,7 +26501,7 @@ async function run2(argv, ctx) {
|
|
|
26465
26501
|
const input = {
|
|
26466
26502
|
...fromJson,
|
|
26467
26503
|
...v.question !== undefined ? { question: v.question } : {},
|
|
26468
|
-
...v.context !== undefined ? { context: v.context } : {},
|
|
26504
|
+
...v.context !== undefined ? { context: typedBreaks(v.context) } : {},
|
|
26469
26505
|
...v["context-file"] !== undefined ? { context: readText5(v["context-file"]) } : {},
|
|
26470
26506
|
...v.option !== undefined ? { options: v.option } : {},
|
|
26471
26507
|
...v.recommended !== undefined ? { recommended: v.recommended } : {},
|
|
@@ -26490,6 +26526,7 @@ async function run2(argv, ctx) {
|
|
|
26490
26526
|
allowPositionals: true,
|
|
26491
26527
|
options: {
|
|
26492
26528
|
outcome: { type: "string" },
|
|
26529
|
+
reason: { type: "string" },
|
|
26493
26530
|
session: { type: "string" },
|
|
26494
26531
|
all: { type: "boolean" },
|
|
26495
26532
|
yes: { type: "boolean", short: "y" }
|