cruo-agent 0.1.10 → 0.1.12

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 CHANGED
@@ -6,9 +6,26 @@ works them — on your machine, under your own Claude account.
6
6
  ```bash
7
7
  npx cruo-agent login cruo_pat_… # the token Cruo showed you once
8
8
  npx cruo-agent --dry-run # what would it pick up? starts no model
9
- npx cruo-agent # let it work
9
+ npx cruo-agent run # let it work
10
10
  ```
11
11
 
12
+ ## Before you start
13
+
14
+ | you need | check it with |
15
+ |---|---|
16
+ | [Claude Code](https://claude.com/claude-code) installed and signed in — it is the harness, and the agent reaches no model without it | `claude -p 'say ok'` |
17
+ | Node 20 or newer | `node -v` |
18
+
19
+ Worth ten seconds, because both failures are silent. A missing harness exits 127
20
+ with no output; a signed-out one prints `Not logged in` and exits **zero**. The
21
+ supervisor reports either as a run that reached no model and backs off — which
22
+ on the board is indistinguishable from having nothing to do.
23
+
24
+ `npx cruo-agent …` resolves the package on every run. If you are going to use it
25
+ daily, `npm i -g cruo-agent` puts `cruo` on your PATH and every command below
26
+ gets shorter. Typing `cruo` on its own prints the command list and starts
27
+ nothing: running an agent is `cruo run`, or any command with flags.
28
+
12
29
  ## What this actually is
13
30
 
14
31
  Three things people tend to run together, kept apart because they fail
@@ -107,6 +124,14 @@ product, revocable from Settings.
107
124
 
108
125
  The harness still runs under your Claude account, on that box.
109
126
 
127
+ `cruo start` survives the terminal closing, not the machine restarting. For that,
128
+ use what the box already has — a launchd agent on macOS, a systemd unit on Linux
129
+ — pointing at the same command. Both files are written out, filled in, at
130
+ <https://cruo.space/docs#agents>, along with the two traps that make them fail
131
+ silently: neither reads your shell profile, so every path must be absolute and
132
+ `PATH` must name wherever `claude` lives; and both restart what dies, so a token
133
+ the service cannot find becomes a crashloop rather than one loud error.
134
+
110
135
  ## The name
111
136
 
112
137
  The package is `cruo-agent`; the command it installs is `cruo`. npm refuses the
package/dist/VERSION CHANGED
@@ -1 +1 @@
1
- 0.1.10
1
+ 0.1.12
package/dist/cli.js CHANGED
@@ -36895,12 +36895,13 @@ async function delegatedSessionFor(config3, principal) {
36895
36895
  workspaceName: principal.workspaceName
36896
36896
  };
36897
36897
  }
36898
- async function fetchDelegatedSession(endpoint, token) {
36898
+ async function fetchDelegatedSession(endpoint, token, options2 = {}) {
36899
36899
  let response;
36900
36900
  try {
36901
36901
  response = await fetch(endpoint, {
36902
36902
  method: "POST",
36903
- headers: { authorization: `Bearer ${token}`, accept: "application/json" }
36903
+ headers: { authorization: `Bearer ${token}`, accept: "application/json" },
36904
+ signal: options2.signal
36904
36905
  });
36905
36906
  } catch (error51) {
36906
36907
  throw new Error(`Could not reach ${endpoint}: ${error51 instanceof Error ? error51.message : String(error51)}`);
@@ -48180,15 +48181,21 @@ async function writeConfig(config3) {
48180
48181
  await writeFile3(CONFIG_PATH, JSON.stringify(config3, null, 2) + "\n", { mode: 384 });
48181
48182
  await chmod(CONFIG_PATH, 384);
48182
48183
  }
48183
- async function identify2(token) {
48184
+ async function lookup(token, timeoutMs = 5e3) {
48184
48185
  const endpoint = process.env.CRUO_SESSION_URL?.trim() || CRUO_CLOUD.sessionUrl;
48185
48186
  try {
48186
- const session = await fetchDelegatedSession(endpoint, token);
48187
- return { email: session.email, workspace: session.workspaceName };
48188
- } catch {
48189
- return null;
48187
+ const session = await fetchDelegatedSession(endpoint, token, {
48188
+ signal: AbortSignal.timeout(timeoutMs)
48189
+ });
48190
+ return { state: "known", who: { email: session.email, workspace: session.workspaceName } };
48191
+ } catch (error51) {
48192
+ return error51 instanceof TokenRefused ? { state: "refused" } : { state: "unreachable" };
48190
48193
  }
48191
48194
  }
48195
+ async function identify2(token) {
48196
+ const found = await lookup(token);
48197
+ return found.state === "known" ? found.who : null;
48198
+ }
48192
48199
  async function login(token, as) {
48193
48200
  if (!token.startsWith("cruo_pat_")) {
48194
48201
  console.error(`
@@ -48196,7 +48203,7 @@ That does not look like a Cruo token \u2014 they begin "cruo_pat_".
48196
48203
  `);
48197
48204
  process.exit(2);
48198
48205
  }
48199
- const who = as ? null : await identify2(token);
48206
+ const who = await identify2(token);
48200
48207
  const name = as ?? (who ? nameFromEmail(who.email) : null);
48201
48208
  if (!name) {
48202
48209
  console.error(
@@ -48294,12 +48301,32 @@ No tokens stored. \`cruo login <token>\` to add one.
48294
48301
  `);
48295
48302
  return;
48296
48303
  }
48304
+ const found = await Promise.all(names.map((n2) => lookup(config3.agents[n2].token)));
48305
+ let changed = false;
48306
+ found.forEach((f, i) => {
48307
+ if (f.state !== "known") return;
48308
+ const a = config3.agents[names[i]];
48309
+ if (a.email !== f.who.email || a.workspace !== f.who.workspace) {
48310
+ a.email = f.who.email;
48311
+ a.workspace = f.who.workspace;
48312
+ changed = true;
48313
+ }
48314
+ });
48315
+ if (changed) await writeConfig(config3);
48297
48316
  console.log("");
48298
- for (const name of names) {
48317
+ names.forEach((name, i) => {
48299
48318
  const a = config3.agents[name];
48300
48319
  const mark = config3.default === name ? "*" : " ";
48301
48320
  const who = a.email ? ` \u2014 ${a.email}${a.workspace ? ` in ${a.workspace}` : ""}` : "";
48302
- console.log(` ${mark} ${name}${who}`);
48321
+ const note = found[i].state === "refused" ? " (token refused \u2014 revoked? `cruo login` a new one)" : "";
48322
+ console.log(` ${mark} ${name}${who}${note}`);
48323
+ });
48324
+ const unchecked = found.filter((f) => f.state === "unreachable").length;
48325
+ if (unchecked > 0) {
48326
+ console.log(
48327
+ `
48328
+ (${unchecked === names.length ? "Could not reach Cruo" : `${unchecked} could not be checked`} just now \u2014 showing what was stored.)`
48329
+ );
48303
48330
  }
48304
48331
  console.log(`
48305
48332
  * is what \`cruo run\` runs. Use \`--as <name>\` for another.
package/dist/index.js CHANGED
@@ -43087,12 +43087,13 @@ async function sessionFor(config3, principal) {
43087
43087
  return attempt;
43088
43088
  }
43089
43089
  var DELEGATED_SESSION_ENV = "CRUO_SESSION";
43090
- async function fetchDelegatedSession(endpoint, token) {
43090
+ async function fetchDelegatedSession(endpoint, token, options = {}) {
43091
43091
  let response;
43092
43092
  try {
43093
43093
  response = await fetch(endpoint, {
43094
43094
  method: "POST",
43095
- headers: { authorization: `Bearer ${token}`, accept: "application/json" }
43095
+ headers: { authorization: `Bearer ${token}`, accept: "application/json" },
43096
+ signal: options.signal
43096
43097
  });
43097
43098
  } catch (error51) {
43098
43099
  throw new Error(`Could not reach ${endpoint}: ${error51 instanceof Error ? error51.message : String(error51)}`);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "cruo-agent",
3
- "version": "0.1.10",
3
+ "version": "0.1.12",
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",