@earntori/cli 0.2.9 → 0.2.10

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.
Files changed (2) hide show
  1. package/dist/index.js +27 -11
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -55,7 +55,7 @@ function out(o, human) { console.log(json ? JSON.stringify(o, null, 2) : human);
55
55
  function die(msg) { console.error(json ? JSON.stringify({ error: msg }) : `\u2717 ${msg}`); process.exit(1); }
56
56
  const usd = (c) => `$${(c / 100).toFixed(2)}`;
57
57
  async function api(cfg, method, path, body) {
58
- const r = await fetch(`${cfg.base}${path}`, { method, headers: { Authorization: `Bearer ${cfg.token}`, "Content-Type": "application/json", "User-Agent": "@earntori/cli/0.2.9" }, body: body ? JSON.stringify(body) : undefined });
58
+ const r = await fetch(`${cfg.base}${path}`, { method, headers: { Authorization: `Bearer ${cfg.token}`, "Content-Type": "application/json", "User-Agent": "@earntori/cli/0.2.10" }, body: body ? JSON.stringify(body) : undefined });
59
59
  const t = await r.text();
60
60
  let j = null;
61
61
  try {
@@ -464,7 +464,7 @@ async function connector() {
464
464
  if (sub === "new") {
465
465
  const label = flag("label") ?? "cowork";
466
466
  const r = await api(cfg, "POST", "/api/v1/connectors", { label });
467
- out(r, `\u2713 Connector "${r.label}" created (id ${r.id})\n\nAdd this URL as a custom connector (Claude Cowork / claude.ai \u2192 Settings \u2192 Connectors \u2192 Add custom connector):\n\n ${r.url}\n\nThe URL is the secret \u2014 it is shown once. Revoke with: tori connector revoke ${r.id}\nReceipt: ${r.receipt.short}`);
467
+ out(r, `\u2713 Connector "${r.label}" created (id ${r.id})\n\nAdd this URL as a custom connector (Claude Cowork / claude.ai \u2192 Settings \u2192 Connectors \u2192 Add custom connector):\n\n ${r.url}\n\nThe URL is the secret \u2014 it is shown once. Revoke with: tori connector revoke ${r.id}\n\nThis connector's project is connector:${r.label}. Choose its standards and add rules with:\n tori standards enable <id\u2026> --connector ${r.label}\n tori rules add "<rule>" --connector ${r.label}\nReceipt: ${r.receipt.short}`);
468
468
  return;
469
469
  }
470
470
  if (sub === "revoke") {
@@ -476,13 +476,15 @@ async function connector() {
476
476
  return;
477
477
  }
478
478
  const r = await api(cfg, "GET", "/api/v1/connectors");
479
- out(r, r.connectors.length ? r.connectors.map((c) => `${c.revoked_at ? "revoked " : "active "} ${c.id} ${c.label} (${c.harness}) created ${String(c.created_at).slice(0, 10)}${c.last_used_at ? " last used " + String(c.last_used_at).slice(0, 16) : ""}`).join("\n") : "No connectors. Create one: tori connector new --label cowork");
479
+ out(r, r.connectors.length ? r.connectors.map((c) => `${c.revoked_at ? "revoked " : "active "} ${c.id} ${c.label} (${c.harness}) project connector:${c.label} created ${String(c.created_at).slice(0, 10)}${c.last_used_at ? " last used " + String(c.last_used_at).slice(0, 16) : ""}`).join("\n") : "No connectors. Create one: tori connector new --label cowork");
480
480
  }
481
+ /** The project a standards/rules command targets: `--connector <label>` for a desktop connector's project, else this directory's. */
482
+ function targetProject() { const c = flag("connector"); return c ? `connector:${c}` : projectId(); }
481
483
  async function standards() {
482
484
  const cfg = loadCfg();
483
485
  if (!cfg)
484
486
  die("Not signed in \u2014 run: tori login");
485
- const project = projectId();
487
+ const project = targetProject();
486
488
  const sub = args[1];
487
489
  if (sub === "enable" || sub === "disable") {
488
490
  const ids = args.slice(2).filter((a) => !a.startsWith("--"));
@@ -494,21 +496,35 @@ async function standards() {
494
496
  }
495
497
  const r = await api(cfg, "GET", `/api/v1/standards?project=${encodeURIComponent(project)}`);
496
498
  const line = (s) => ` ${s.enabled ? "on " : "off"} ${s.id.padEnd(20)} ${s.name}${s.rules ? ` (${s.rules} checks)` : " (no checks yet)"}`;
497
- out(r, `Coding standards \u2014 always on:\n${r.standards.filter((s) => s.tier === "coding").map(line).join("\n")}\n\nDomain standards \u2014 choose per project:\n${r.standards.filter((s) => s.tier === "domain").map(line).join("\n")}\n\nEnable: tori standards enable <id…> Disable: tori standards disable <id…>`);
499
+ out(r, `${project.startsWith("connector:") ? `Project: ${project}\n\n` : ""}Coding standards \u2014 always on:\n${r.standards.filter((s) => s.tier === "coding").map(line).join("\n")}\n\nDomain standards \u2014 choose per project:\n${r.standards.filter((s) => s.tier === "domain").map(line).join("\n")}\n\nEnable: tori standards enable <id…> Disable: tori standards disable <id…>`);
498
500
  }
499
501
  async function rules() {
500
502
  const cfg = loadCfg();
501
503
  if (!cfg)
502
504
  die("Not signed in \u2014 run: tori login");
503
- const project = projectId();
505
+ const project = targetProject();
504
506
  const sub = args[1];
505
507
  if (sub === "add") {
506
- const text = args.slice(2).filter((a) => !a.startsWith("--")).join(" ").trim();
508
+ const words = [];
509
+ for (let i = 2; i < args.length; i++) {
510
+ const a = args[i];
511
+ if (a.startsWith("--")) {
512
+ if (!a.includes("=") && i + 1 < args.length && !args[i + 1].startsWith("--"))
513
+ i++;
514
+ continue;
515
+ }
516
+ words.push(a);
517
+ }
518
+ const text = words.join(" ").trim();
507
519
  if (!text)
508
- die('Usage: tori rules add "<rule in plain English>"');
520
+ die('Usage: tori rules add "<rule in plain English>" [--connector <label>]');
509
521
  const r = await api(cfg, "POST", "/api/v1/rules", { project, text });
510
- out(r, r.enforced ? `\u2713 Rule added and enforced: "${text}"\n id ${r.id} \u00b7 Tori will check it on every run. Receipt: ${r.receipt.short}`
511
- : `\u2713 Rule recorded (not enforced): "${text}"\n id ${r.id} \u00b7 Tori can't turn this wording into a check yet. The agent still sees it as a rule it is held to. Enforceable shapes: "no X [in|outside <where>]", "<where> must contain X", "every <where> ships with X". Receipt: ${r.receipt.short}`);
522
+ if (r.existing) {
523
+ out(r, `Already there: "${text}" (id ${r.id}, ${r.enforced ? "enforced" : "recorded"}). Nothing added.`);
524
+ return;
525
+ }
526
+ out(r, r.enforced ? `\u2713 Rule added and enforced: "${text}"\n id ${r.id} \u00b7 Tori will check it on every run. Receipt: ${r.receipt?.short}`
527
+ : `\u2713 Rule recorded (not enforced): "${text}"\n id ${r.id} \u00b7 Tori can't turn this wording into a check yet. The agent still sees it as a rule it is held to. Enforceable shapes: "no X [in|outside <where>]", "no X without Y", "<where> must contain X", "every <where> ships with X". Receipt: ${r.receipt?.short}`);
512
528
  return;
513
529
  }
514
530
  if (sub === "remove") {
@@ -524,7 +540,7 @@ async function rules() {
524
540
  }
525
541
  function logout() { if (existsSync(CONFIG))
526
542
  unlinkSync(CONFIG); out({ ok: true }, "\u2713 Signed out"); }
527
- function help() { console.log(`tori \u2014 Tori CLI\n\n tori login [--base URL] sign in with GitHub (device flow)\n tori status [--json] balance + your AI keys\n tori key --limit 5 [--write env] [--file .env] [--json]\n tori stats [--json] your integration + developer program numbers\n tori init [--local] [--codex|--no-codex] wire Tori into this project (Claude Code, Cursor, Codex)\n tori check [paths...] [--json] run Tori's checks on changed files\n tori ledger [--project X|all] [--type a,b] [--since ISO] [--limit N] [--json]\n tori receipt <event_id|seq> fetch a receipt and verify it locally\n tori connector new [--label X] | list | revoke <id> Tori as a remote MCP connector (Claude Cowork etc.)\n tori standards [enable|disable <id…>] standards that apply to this project (coding standards are always on)\n tori rules [add "<rule>"|list|remove <id>] your own rules for this project, in plain English\n tori init --base https://sandbox.earntori.com pin this repo to a Tori environment (.tori/project.json)\n tori logout\n`); }
543
+ function help() { console.log(`tori \u2014 Tori CLI\n\n tori login [--base URL] sign in with GitHub (device flow)\n tori status [--json] balance + your AI keys\n tori key --limit 5 [--write env] [--file .env] [--json]\n tori stats [--json] your integration + developer program numbers\n tori init [--local] [--codex|--no-codex] wire Tori into this project (Claude Code, Cursor, Codex)\n tori check [paths...] [--json] run Tori's checks on changed files\n tori ledger [--project X|all] [--type a,b] [--since ISO] [--limit N] [--json]\n tori receipt <event_id|seq> fetch a receipt and verify it locally\n tori connector new [--label X] | list | revoke <id> Tori as a remote MCP connector (Claude Cowork etc.)\n tori standards [enable|disable <id…>] [--connector <label>] standards that apply to this project, or to a connector's\n tori rules [add "<rule>"|list|remove <id>] [--connector <label>] your own rules, in plain English\n tori init --base https://sandbox.earntori.com pin this repo to a Tori environment (.tori/project.json)\n tori logout\n`); }
528
544
  (async () => {
529
545
  switch (cmd) {
530
546
  case "login": return login();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@earntori/cli",
3
- "version": "0.2.9",
3
+ "version": "0.2.10",
4
4
  "description": "Tori CLI \u2014 sign in with GitHub, check your credits, mint a capped AI key into your tools.",
5
5
  "license": "MIT",
6
6
  "type": "module",