@bivy/bivy 0.10.1-staging.284 → 0.10.1-staging.286
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/bin/bivy.mjs +81 -8
- package/dist/automation-cli.js +56 -0
- package/package.json +1 -1
package/bin/bivy.mjs
CHANGED
|
@@ -922,9 +922,10 @@ async function resolveRunSpec(agentId, extraArgs) {
|
|
|
922
922
|
return { spec: { agent: id, label: agent.label, command, args: [...(agent.args ?? []), ...extraArgs] } };
|
|
923
923
|
}
|
|
924
924
|
|
|
925
|
-
// Pull bivy's own
|
|
926
|
-
//
|
|
927
|
-
//
|
|
925
|
+
// Pull bivy's own run flags out of the args so they aren't blindly forwarded.
|
|
926
|
+
// Only honored before a `--` separator, past which everything is the raw command
|
|
927
|
+
// the user asked to run. `--chat` selects the governed app-session path instead
|
|
928
|
+
// of the native PTY path; `--no-open` keeps that path headless after creation.
|
|
928
929
|
// Does a token look like a git remote (URL, scp-style, owner/repo, or a path)
|
|
929
930
|
// rather than an agent id? Lets `--clone <remote>` disambiguate from bare
|
|
930
931
|
// `--clone` (= current folder's repo) without a required `=`.
|
|
@@ -936,10 +937,14 @@ function looksLikeRemote(value) {
|
|
|
936
937
|
function extractRunFlags(args) {
|
|
937
938
|
const rest = [];
|
|
938
939
|
let name, model, node, workspace;
|
|
940
|
+
let chat = false;
|
|
941
|
+
let noOpen = false;
|
|
939
942
|
let clone; // undefined = no clone; true = current repo; string = explicit remote
|
|
940
943
|
for (let i = 0; i < args.length; i++) {
|
|
941
944
|
const a = args[i];
|
|
942
945
|
if (a === "--") { rest.push(...args.slice(i)); break; }
|
|
946
|
+
if (a === "--chat") { chat = true; continue; }
|
|
947
|
+
if (a === "--no-open") { noOpen = true; continue; }
|
|
943
948
|
if (a === "--name" && args[i + 1] !== undefined) { name = args[++i]; continue; }
|
|
944
949
|
if (a.startsWith("--name=")) { name = a.slice("--name=".length); continue; }
|
|
945
950
|
if (a === "--model" && args[i + 1] !== undefined) { model = args[++i]; continue; }
|
|
@@ -952,7 +957,7 @@ function extractRunFlags(args) {
|
|
|
952
957
|
if (a === "--clone") { clone = looksLikeRemote(args[i + 1]) ? args[++i] : true; continue; }
|
|
953
958
|
rest.push(a);
|
|
954
959
|
}
|
|
955
|
-
return { name: name?.trim() || undefined, model: model?.trim() || undefined, node: node?.trim() || undefined, workspace: workspace?.trim() || undefined, clone, rest };
|
|
960
|
+
return { name: name?.trim() || undefined, model: model?.trim() || undefined, node: node?.trim() || undefined, workspace: workspace?.trim() || undefined, chat, noOpen, clone, rest };
|
|
956
961
|
}
|
|
957
962
|
|
|
958
963
|
// A safe-ish workspace dir name from a remote or path (basename minus .git).
|
|
@@ -1730,7 +1735,7 @@ async function cmdExec(args = []) {
|
|
|
1730
1735
|
function cmdCompletions(args = []) {
|
|
1731
1736
|
const shell = (args[0] || "").toLowerCase();
|
|
1732
1737
|
const commands = [
|
|
1733
|
-
"run", "sessions", "ls", "resume", "promote", "rename", "nodes", "agent", "agents", "agents:install", "shim", "takeover", "token", "exec",
|
|
1738
|
+
"run", "runs", "sessions", "ls", "resume", "promote", "rename", "nodes", "agent", "agents", "agents:install", "shim", "takeover", "token", "exec",
|
|
1734
1739
|
"send", "attach", "kill", "setup", "start", "stop", "restart", "status", "doctor", "diagnostics", "logs", "login",
|
|
1735
1740
|
"update", "update:log", "audit", "automation", "config", "plugin", "open", "service", "secrets", "voice", "link", "relay:setup",
|
|
1736
1741
|
"github:connect", "github:app-create", "github:app-connect", "github:app-sync", "prune", "uninstall", "help", "version",
|
|
@@ -1793,12 +1798,67 @@ complete -c bivy -n '__fish_seen_subcommand_from run' -a '${agents.join(" ")}'`)
|
|
|
1793
1798
|
// flag on top of it.
|
|
1794
1799
|
async function cmdRun(args = []) {
|
|
1795
1800
|
if (!(await ensureDeps())) process.exit(1);
|
|
1796
|
-
const { name, model, node, workspace, clone, rest } = extractRunFlags(args);
|
|
1801
|
+
const { name, model, node, workspace, chat, noOpen, clone, rest } = extractRunFlags(args);
|
|
1797
1802
|
// Bare `bivy` (empty rest) resolves to the configured default agent; an
|
|
1798
1803
|
// explicit `bivy run <agent>` keeps that agent verbatim.
|
|
1799
1804
|
const [agentIdArg, ...extraArgs] = rest;
|
|
1800
1805
|
const agentId = agentIdArg || resolveDefaultAgent();
|
|
1801
1806
|
|
|
1807
|
+
if (chat) {
|
|
1808
|
+
if (node) {
|
|
1809
|
+
console.error(c.red("--chat currently starts a governed session on this node; it can't be combined with --node."));
|
|
1810
|
+
process.exit(1);
|
|
1811
|
+
return;
|
|
1812
|
+
}
|
|
1813
|
+
if (agentId === "--" || extraArgs.length) {
|
|
1814
|
+
console.error(c.red("--chat starts a governed agent integration and does not accept native agent arguments or the raw '-- <command>' form."));
|
|
1815
|
+
process.exit(1);
|
|
1816
|
+
return;
|
|
1817
|
+
}
|
|
1818
|
+
let requestedWorkspace;
|
|
1819
|
+
try { requestedWorkspace = resolveWorkspaceDir({ clone, workspace }); }
|
|
1820
|
+
catch (error) { console.error(c.red(error?.message || String(error))); process.exit(1); return; }
|
|
1821
|
+
const config = loadConfig();
|
|
1822
|
+
// Preserve `bivy run`'s cwd contract: from inside a git checkout, a chat run
|
|
1823
|
+
// targets that checkout; elsewhere it uses the node's configured workspace.
|
|
1824
|
+
const sessionWorkspace = requestedWorkspace || defaultRunWorkspace(config);
|
|
1825
|
+
if (!(await ensureNodeRunning(config))) {
|
|
1826
|
+
console.error(c.red(`Could not start the Bivy node at ${url(config)}.`));
|
|
1827
|
+
process.exit(1);
|
|
1828
|
+
return;
|
|
1829
|
+
}
|
|
1830
|
+
try {
|
|
1831
|
+
const created = await localApi(config, "/api/session", {
|
|
1832
|
+
method: "POST",
|
|
1833
|
+
body: JSON.stringify({ agent: agentId, ...(model ? { model: { provider: "", id: model } } : {}), ...(sessionWorkspace ? { workspace: sessionWorkspace } : {}) }),
|
|
1834
|
+
});
|
|
1835
|
+
if (name) {
|
|
1836
|
+
await localApi(config, "/api/sessions/rename", {
|
|
1837
|
+
method: "POST",
|
|
1838
|
+
body: JSON.stringify({ sessionId: created.id, name }),
|
|
1839
|
+
});
|
|
1840
|
+
}
|
|
1841
|
+
const sessionPath = `/sessions/${encodeURIComponent(created.id)}`;
|
|
1842
|
+
const remote = await openRemoteApp({ open: !noOpen, remotePath: sessionPath });
|
|
1843
|
+
console.log(c.green(`Started chat session ${created.id} (${created.agentName || created.runtimeId || agentId}).`));
|
|
1844
|
+
if (remote && (noOpen || !canOpenBrowser())) console.log(`Open it in the Bivy app: ${c.cyan(remote.openUrl)}`);
|
|
1845
|
+
else if (!remote) {
|
|
1846
|
+
console.log(c.yellow("Remote app access is not configured on this node."));
|
|
1847
|
+
console.log(`Run ${c.cyan("bivy relay:setup")}, then open session ${c.cyan(created.id)} in the app.`);
|
|
1848
|
+
}
|
|
1849
|
+
return;
|
|
1850
|
+
} catch (error) {
|
|
1851
|
+
console.error(c.red(`Could not start chat session: ${error?.message || String(error)}`));
|
|
1852
|
+
process.exit(1);
|
|
1853
|
+
return;
|
|
1854
|
+
}
|
|
1855
|
+
}
|
|
1856
|
+
if (noOpen) {
|
|
1857
|
+
console.error(c.red("--no-open is only valid together with --chat."));
|
|
1858
|
+
process.exit(1);
|
|
1859
|
+
return;
|
|
1860
|
+
}
|
|
1861
|
+
|
|
1802
1862
|
// A cloned/explicit workspace lives on THIS machine, so it only applies to the
|
|
1803
1863
|
// local node. For a remote --node run the checkout would need to be made there.
|
|
1804
1864
|
if (node && (clone !== undefined || workspace)) {
|
|
@@ -4518,7 +4578,8 @@ ${c.bold("bivy")} — Bivy node CLI
|
|
|
4518
4578
|
${c.cyan("bivy send <id>")} "..." Send a prompt to an existing session and stream the reply
|
|
4519
4579
|
${c.cyan("bivy kill <id>")} Stop a session/terminal (--delete also removes a saved session)
|
|
4520
4580
|
${c.cyan("bivy prune")} Delete old sessions/workspaces/worktrees (--keep N, --older-than 7d, --dry-run)
|
|
4521
|
-
${c.cyan("bivy exec")} "<prompt>" One-shot headless
|
|
4581
|
+
${c.cyan("bivy exec")} "<prompt>" One-shot headless session: prints the answer to stdout (pipe-friendly)
|
|
4582
|
+
${c.cyan("bivy runs start")} "<instructions>" Queue a one-off unattended Run with checks and a Receipt
|
|
4522
4583
|
${c.cyan("bivy automation")} list | trigger | init | validate | plan | test | apply
|
|
4523
4584
|
${c.cyan("bivy config")} init | validate | show | get | set | explain (typed node config)
|
|
4524
4585
|
${c.cyan("bivy plugin")} init | validate | doctor | test | install | list | remove
|
|
@@ -4573,9 +4634,10 @@ async function main() {
|
|
|
4573
4634
|
// args (including its own --help) pass straight through to it — e.g.
|
|
4574
4635
|
// 'bivy run claude --help' must show Claude's help, not bivy's.
|
|
4575
4636
|
if (args[0] === "-h" || args[0] === "--help") {
|
|
4576
|
-
console.log(`Usage: bivy run <agent> [--name <label>] [--model <model>] [--node <name>] [--clone [remote]] [--workspace <dir>] | -- <command>
|
|
4637
|
+
console.log(`Usage: bivy run <agent> [--chat [--no-open]] [--name <label>] [--model <model>] [--node <name>] [--clone [remote]] [--workspace <dir>] | -- <command>
|
|
4577
4638
|
|
|
4578
4639
|
Run a native agent (real CLI/TUI) as a relay-visible session.
|
|
4640
|
+
Use --chat to start the governed session used by the app instead; --no-open prints its URL without launching a browser.
|
|
4579
4641
|
Agents: ${[...AGENT_INTEGRATIONS.keys()].join(", ")}, or -- <command> for anything else.
|
|
4580
4642
|
An agent's own --help passes through, e.g. 'bivy run claude --help'.`);
|
|
4581
4643
|
break;
|
|
@@ -4637,6 +4699,17 @@ An agent's own --help passes through, e.g. 'bivy run claude --help'.`);
|
|
|
4637
4699
|
case "completion":
|
|
4638
4700
|
cmdCompletions(args);
|
|
4639
4701
|
break;
|
|
4702
|
+
case "runs": {
|
|
4703
|
+
if (!(await ensureDeps())) process.exit(1);
|
|
4704
|
+
const [action, ...runArgs] = args;
|
|
4705
|
+
if (!action || ["-h", "--help", "help"].includes(action)) {
|
|
4706
|
+
console.log('Usage: bivy runs start "<instructions>" [--name <title>] [--repo <owner/name>] [--agent <id>] [--model <id>] [--approval <mode>] [--sandbox <tier>] [--max-attempts <n>] [--json]\n\nStart a one-off unattended Run. Unlike `bivy run`, this queues governed background work with checks, evidence, and a Receipt.');
|
|
4707
|
+
break;
|
|
4708
|
+
}
|
|
4709
|
+
if (action !== "start" && action !== "create") { console.error(c.red(`Unknown runs action: ${action}`)); process.exit(1); break; }
|
|
4710
|
+
process.exit(await run(nodeBin, [...nodeScriptArgs(automationEntry), "start-run", ...runArgs], { cwd: process.cwd(), env: process.env }));
|
|
4711
|
+
break;
|
|
4712
|
+
}
|
|
4640
4713
|
case "automation":
|
|
4641
4714
|
case "automations": {
|
|
4642
4715
|
if (!(await ensureDeps())) process.exit(1);
|
package/dist/automation-cli.js
CHANGED
|
@@ -30,6 +30,11 @@ Commands:
|
|
|
30
30
|
test [path] --event <file> Simulate an event and explain the first match
|
|
31
31
|
apply [path] [--prune] Encrypt instructions and reconcile the control plane
|
|
32
32
|
|
|
33
|
+
One-off Run flags (used by 'bivy runs start'):
|
|
34
|
+
--name <title> --repo <owner/name> --agent <id> --model <id>
|
|
35
|
+
--approval <mode> --sandbox <tier> --max-attempts <1-10> --json
|
|
36
|
+
Pass '-' as the instructions to read them from stdin.
|
|
37
|
+
|
|
33
38
|
Default path: ${DEFAULT_AUTOMATION_CONFIG_PATH}
|
|
34
39
|
List, trigger, and apply require an enrolled node ('bivy setup'). Instructions
|
|
35
40
|
are encrypted on this machine before upload; the control plane receives
|
|
@@ -175,6 +180,57 @@ async function main() {
|
|
|
175
180
|
}
|
|
176
181
|
return;
|
|
177
182
|
}
|
|
183
|
+
if (command === "start-run") {
|
|
184
|
+
if (args.includes("-h") || args.includes("--help"))
|
|
185
|
+
usage();
|
|
186
|
+
const valuedFlags = new Set(["--name", "--repo", "--agent", "--model", "--approval", "--sandbox", "--max-attempts"]);
|
|
187
|
+
const positional = [];
|
|
188
|
+
for (let i = 0; i < args.length; i += 1) {
|
|
189
|
+
const arg = args[i];
|
|
190
|
+
if (valuedFlags.has(arg)) {
|
|
191
|
+
i += 1;
|
|
192
|
+
continue;
|
|
193
|
+
}
|
|
194
|
+
if (arg.startsWith("--") && arg.includes("="))
|
|
195
|
+
continue;
|
|
196
|
+
if (arg === "--json")
|
|
197
|
+
continue;
|
|
198
|
+
positional.push(arg);
|
|
199
|
+
}
|
|
200
|
+
let instructions = positional.join(" ").trim();
|
|
201
|
+
if (instructions === "-")
|
|
202
|
+
instructions = fs.readFileSync(0, "utf8").trim();
|
|
203
|
+
if (!instructions)
|
|
204
|
+
throw new Error("instructions are required (or pass '-' to read stdin)");
|
|
205
|
+
const appDir = appDataDir();
|
|
206
|
+
const relay = relayConfig(appDir);
|
|
207
|
+
const identity = NodeIdentity.load(appDir);
|
|
208
|
+
const pairing = PairingStore.load(appDir, relay.e2eKey);
|
|
209
|
+
const approvalMode = value(args, "--approval") ?? "risky";
|
|
210
|
+
const sandbox = value(args, "--sandbox") ?? "workspace-write";
|
|
211
|
+
const maxAttempts = Number(value(args, "--max-attempts") ?? 2);
|
|
212
|
+
const title = (value(args, "--name") ?? instructions.split(/\r?\n/, 1)[0] ?? "One-off Run").slice(0, 120);
|
|
213
|
+
const created = await request(relay, "/node/automation-runs", {
|
|
214
|
+
method: "POST",
|
|
215
|
+
body: JSON.stringify({
|
|
216
|
+
title,
|
|
217
|
+
body: `bivy-room-v1:${identity.nodeId}:${seal(pairing.roomKey(), instructions)}`,
|
|
218
|
+
repo: value(args, "--repo"),
|
|
219
|
+
runtimeId: value(args, "--agent"),
|
|
220
|
+
model: value(args, "--model"),
|
|
221
|
+
approvalMode,
|
|
222
|
+
sandbox,
|
|
223
|
+
maxAttempts,
|
|
224
|
+
}),
|
|
225
|
+
});
|
|
226
|
+
if (args.includes("--json"))
|
|
227
|
+
console.log(JSON.stringify(created, null, 2));
|
|
228
|
+
else {
|
|
229
|
+
console.log(`Queued Run ${created.id} on ${identity.name}.`);
|
|
230
|
+
console.log(`Status: ${created.status}`);
|
|
231
|
+
}
|
|
232
|
+
return;
|
|
233
|
+
}
|
|
178
234
|
if (command === "init") {
|
|
179
235
|
const file = configPath(args);
|
|
180
236
|
if (fs.existsSync(file) && !args.includes("--force"))
|
package/package.json
CHANGED