cruo-agent 0.1.6 → 0.1.7

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/VERSION CHANGED
@@ -1 +1 @@
1
- 0.1.6
1
+ 0.1.7
package/dist/cli.js CHANGED
@@ -45905,6 +45905,14 @@ function stripTokenFlag(argv2) {
45905
45905
  if (at < 0) return [...argv2];
45906
45906
  return [...argv2.slice(0, at), ...argv2.slice(at + 2)];
45907
45907
  }
45908
+ function isKnownCommandWord(first) {
45909
+ if (first === void 0) return true;
45910
+ if (first.startsWith("-")) return true;
45911
+ return CRUO_COMMANDS.includes(first);
45912
+ }
45913
+ function nearestCommands(first) {
45914
+ return CRUO_COMMANDS.filter((k) => k[0] === first[0]).slice(0, 3);
45915
+ }
45908
45916
  function numericIn(argv2, name, spec = {}) {
45909
45917
  const i = argv2.indexOf(`--${name}`);
45910
45918
  if (i < 0) return spec.fallback;
@@ -45939,10 +45947,24 @@ function requiredNumericIn(argv2, name, fallback, spec = {}) {
45939
45947
  function secondsIn(argv2, name, fallbackSeconds) {
45940
45948
  return requiredNumericIn(argv2, name, fallbackSeconds, { unit: "seconds" }) * 1e3;
45941
45949
  }
45942
- var OptionError, flagIn, optIn;
45950
+ var CRUO_COMMANDS, OptionError, flagIn, optIn;
45943
45951
  var init_options = __esm({
45944
45952
  "src/options.ts"() {
45945
45953
  "use strict";
45954
+ CRUO_COMMANDS = [
45955
+ "login",
45956
+ "logout",
45957
+ "agents",
45958
+ "whoami",
45959
+ "usage",
45960
+ "help",
45961
+ "start",
45962
+ "ps",
45963
+ "stop",
45964
+ "pause",
45965
+ "resume",
45966
+ "logs"
45967
+ ];
45946
45968
  OptionError = class extends Error {
45947
45969
  constructor(message) {
45948
45970
  super(message);
@@ -46978,11 +47000,22 @@ async function invokeHarness(ctx, identity, hit, worktree) {
46978
47000
  const cwd = worktree?.path ?? options.cwd;
46979
47001
  const { path: mcpConfig, cleanup } = await writeMcpConfig();
46980
47002
  const streaming = options.usage === true;
47003
+ const builtinTools = [
47004
+ ...new Set(
47005
+ options.allow.split(",").map((t) => t.trim()).filter((t) => t !== "" && !t.startsWith("mcp__")).map((t) => t.replace(/\(.*$/, ""))
47006
+ )
47007
+ ].join(",");
46981
47008
  const args = [
46982
47009
  "-p",
46983
47010
  userPrompt(hit),
46984
47011
  "--mcp-config",
46985
47012
  mcpConfig,
47013
+ // Both, always. See the note above: without `--restricted` the harness
47014
+ // inherits the operator's settings, and without `--tools` it keeps every
47015
+ // built-in that `--restricted` does not itself remove.
47016
+ "--restricted",
47017
+ "--tools",
47018
+ builtinTools,
46986
47019
  // Only the server we just described. Without this the harness would also
46987
47020
  // load the developer's own .mcp.json — including, on this machine, a Cruo
46988
47021
  // token belonging to a human.
@@ -47416,6 +47449,22 @@ async function main() {
47416
47449
  installControls();
47417
47450
  const runName = identityName();
47418
47451
  if (!options.once && !options.dryRun) {
47452
+ const existing = await liveness(runName, true);
47453
+ if (existing.state === "running") {
47454
+ console.error(
47455
+ `
47456
+ ${runName} is already running here (pid ${existing.record.pid}, up ${since(existing.record.startedAt)}).
47457
+
47458
+ Two supervisors for one agent share a token, a queue and a heartbeat,
47459
+ and the second would take over the first one's record \u2014 leaving the
47460
+ first with no way to be stopped except by pid.
47461
+
47462
+ stop it cruo stop ${runName}
47463
+ look at it cruo ps
47464
+ `
47465
+ );
47466
+ process.exit(2);
47467
+ }
47419
47468
  await writeRecord({
47420
47469
  agent: runName,
47421
47470
  pid: process.pid,
@@ -47962,7 +48011,19 @@ ${name} is not running. Start it with \`cruo start --as ${name}\`.
47962
48011
  async function startDetached(argv2, asName) {
47963
48012
  const { spawn: spawn2 } = await import("node:child_process");
47964
48013
  const { open, mkdir: mkdir4 } = await import("node:fs/promises");
47965
- const name = await targetName(null, asName);
48014
+ const positional = argv2[1] && !argv2[1].startsWith("--") ? argv2[1] : null;
48015
+ if (positional && asName && safeName(positional) !== safeName(asName)) {
48016
+ console.error(
48017
+ `
48018
+ Two different agents named: \`${positional}\` and \`--as ${asName}\`.
48019
+
48020
+ Say one: cruo start ${positional} \u2026
48021
+ or cruo start --as ${asName} \u2026
48022
+ `
48023
+ );
48024
+ process.exit(2);
48025
+ }
48026
+ const name = await targetName(positional, asName);
47966
48027
  const already = await liveness(name, true);
47967
48028
  if (already.state === "running") {
47968
48029
  console.error(
@@ -47973,10 +48034,30 @@ Stop it first, or use --as <name> to run a different agent.
47973
48034
  );
47974
48035
  process.exit(1);
47975
48036
  }
48037
+ const config3 = await readConfig2();
48038
+ const storedKey = Object.keys(config3.agents).find((k) => safeName(k) === name) ?? null;
48039
+ const hasToken = Boolean(process.env.CRUO_TOKEN?.trim()) || argv2.includes("--token") || storedKey !== null;
48040
+ if (!hasToken) {
48041
+ const stored = Object.keys(config3.agents);
48042
+ console.error(
48043
+ `
48044
+ Nothing stored for \`${name}\`, so there is no token to run it with.
48045
+
48046
+ ` + (stored.length > 0 ? `Stored: ${stored.join(", ")}
48047
+ Either \`cruo start ${stored[0]}\`, or add this one:
48048
+ ` : `Nothing is stored at all. Add one:
48049
+ `) + ` cruo login <token>
48050
+ `
48051
+ );
48052
+ process.exit(2);
48053
+ }
47976
48054
  await mkdir4(logDir(), { recursive: true });
47977
48055
  const logPath = join4(logDir(), `${name}.log`);
47978
48056
  const handle = await open(logPath, "a");
47979
- const forwarded = argv2.slice(1).filter((a) => a !== "--all");
48057
+ const rest = argv2.slice(positional ? 2 : 1).filter((a) => a !== "--all");
48058
+ const chosen = storedKey ?? positional ?? asName;
48059
+ const withoutAs = rest.filter((a, i, all) => a !== "--as" && all[i - 1] !== "--as");
48060
+ const forwarded = chosen ? ["--as", chosen, ...withoutAs] : withoutAs;
47980
48061
  const child = spawn2(process.execPath, [process.argv[1], ...forwarded], {
47981
48062
  detached: true,
47982
48063
  stdio: ["ignore", handle.fd, handle.fd],
@@ -48120,6 +48201,20 @@ Usage: cruo login <token> [--as <name>]
48120
48201
  console.log(USAGE);
48121
48202
  return;
48122
48203
  }
48204
+ if (!isKnownCommandWord(first)) {
48205
+ const near = nearestCommands(first);
48206
+ console.error(
48207
+ `
48208
+ \`${first}\` is not a cruo command.
48209
+
48210
+ ` + (near.length > 0 ? `Did you mean: ${near.map((k) => `cruo ${k}`).join(" ")}
48211
+
48212
+ ` : "") + `To run the agent, give no command: cruo [options]
48213
+ Everything it takes: cruo --help
48214
+ `
48215
+ );
48216
+ process.exit(2);
48217
+ }
48123
48218
  if (first === "start") return startDetached(argv2, flagValue("--as"));
48124
48219
  const flagIndex = argv2.indexOf("--token");
48125
48220
  const fromFlag = flagIndex >= 0 ? argv2[flagIndex + 1] : void 0;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "cruo-agent",
3
- "version": "0.1.6",
3
+ "version": "0.1.7",
4
4
  "description": "Run a Cruo agent: it watches your board, picks up the cards you assign it, and works them.",
5
5
  "license": "MIT",
6
6
  "homepage": "https://cruo.space/docs#agents",