@withone/cli 2.0.0 → 2.2.0

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
@@ -269,6 +269,7 @@ one actions find shopify "list products" -t knowledge
269
269
  - **`--task`** describes the whole job in one line, in general terms, which helps choose between similar actions.
270
270
  - **`-t knowledge`** searches the catalog for writing code and building flows and relays, which includes the platform's own (passthrough) endpoints; the default is the catalog for running actions now.
271
271
  - **`--ai-model`** names the model running the CLI (e.g. `claude-sonnet-5`), for tuning documentation per model later.
272
+ - **`--no-knowledge`** lists the chosen actions (actionId, title, method, path) without fetching their documentation; load it later with `one actions load <actionId>`.
272
273
 
273
274
  Each intent's answer gives the action(s) to use with their docs, any actions also needed beside them, a **substitute** when the model was unsure (use one or the other, never both), and a few alternatives, then says once how to execute. Always read the docs before executing. The access settings from `one config` apply: an action they refuse is never offered.
274
275
 
package/dist/index.js CHANGED
@@ -1476,6 +1476,7 @@ var PORT_RANGE_START = 49152;
1476
1476
  var PORT_RANGE_END = 65535;
1477
1477
  var MAX_PORT_ATTEMPTS = 5;
1478
1478
  var MAX_KEY_NAME_LENGTH = 120;
1479
+ var MAX_GRANT_LENGTH = 4e3;
1479
1480
  var MAX_ERROR_LENGTH = 200;
1480
1481
  var CHECK_MARK = `<div style="width:48px;height:48px;border-radius:50%;background:rgba(34,197,94,0.1);display:flex;align-items:center;justify-content:center;margin:0 auto 16px">
1481
1482
  <svg width="24" height="24" fill="none" stroke="#22c55e" stroke-width="2" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" d="M5 13l4 4L19 7"/></svg>
@@ -1539,9 +1540,10 @@ function startCallbackServer(expectedState) {
1539
1540
  return;
1540
1541
  }
1541
1542
  const keyName = cleanParam(url.searchParams.get("name"), MAX_KEY_NAME_LENGTH);
1543
+ const grant = cleanParam(url.searchParams.get("grant"), MAX_GRANT_LENGTH);
1542
1544
  res.writeHead(200, { "Content-Type": "text/html" });
1543
1545
  res.end(SUCCESS_HTML);
1544
- resolveResult({ kind: "key", apiKey, keyName });
1546
+ resolveResult({ kind: "key", apiKey, keyName, grant });
1545
1547
  return;
1546
1548
  }
1547
1549
  const error2 = cleanParam(url.searchParams.get("error"), MAX_ERROR_LENGTH);
@@ -1593,6 +1595,9 @@ function noteWidth() {
1593
1595
  const usable = (typeof columns === "number" && columns > 0 ? columns : 80) - 8;
1594
1596
  return Math.max(usable, 40);
1595
1597
  }
1598
+ function grantNoteLines(grant, label) {
1599
+ return grant ? wrapForNote(`${label} ${grant}`).split("\n") : [];
1600
+ }
1596
1601
  async function browserLogin(opts) {
1597
1602
  const state = crypto.randomUUID();
1598
1603
  const spin = createSpinner();
@@ -1650,7 +1655,7 @@ ${describeInstallContext(context)}`
1650
1655
  const apiBase = getApiBase();
1651
1656
  const api = new OneApi(outcome.apiKey, apiBase);
1652
1657
  const whoami = await api.whoami();
1653
- return { apiKey: outcome.apiKey, whoami, keyName: outcome.keyName };
1658
+ return { apiKey: outcome.apiKey, whoami, keyName: outcome.keyName, grant: outcome.grant };
1654
1659
  } catch (err) {
1655
1660
  spin.stop("Authentication failed.");
1656
1661
  if (err instanceof Error && err.message === "timeout") {
@@ -1709,7 +1714,7 @@ async function loginCommand() {
1709
1714
  }
1710
1715
  const result = await browserLogin({ scope: targetScope });
1711
1716
  if (!result) return;
1712
- const { apiKey, whoami, keyName } = result;
1717
+ const { apiKey, whoami, keyName, grant } = result;
1713
1718
  saveCredentials(apiKey, targetScope, { keyName, whoami });
1714
1719
  const pc16 = (await import("picocolors")).default;
1715
1720
  const env = getEnvFromApiKey(apiKey);
@@ -1724,6 +1729,7 @@ async function loginCommand() {
1724
1729
  `${whoami.user.name} ${pc16.dim(`(${whoami.user.email})`)}`
1725
1730
  ];
1726
1731
  if (keyName) infoLines.push(`${pc16.dim("Key:")} ${keyName}`);
1732
+ infoLines.push(...grantNoteLines(grant, pc16.dim("Access:")));
1727
1733
  if (whoami.organization) infoLines.push(`${pc16.dim("Org:")} ${whoami.organization.name}`);
1728
1734
  if (whoami.project) infoLines.push(`${pc16.dim("Project:")} ${whoami.project.name}`);
1729
1735
  infoLines.push("");
@@ -1780,6 +1786,7 @@ async function nonInteractiveInit(options) {
1780
1786
  const scope = options.global ? "global" : options.project ? "project" : "global";
1781
1787
  let apiKey;
1782
1788
  let keyName;
1789
+ let grant;
1783
1790
  let whoami;
1784
1791
  if (auth === "browser") {
1785
1792
  const result = await browserLogin({ scope });
@@ -1788,6 +1795,7 @@ async function nonInteractiveInit(options) {
1788
1795
  }
1789
1796
  apiKey = result.apiKey;
1790
1797
  keyName = result.keyName;
1798
+ grant = result.grant;
1791
1799
  whoami = result.whoami;
1792
1800
  } else {
1793
1801
  const key = options.apiKey?.trim();
@@ -1834,6 +1842,7 @@ async function nonInteractiveInit(options) {
1834
1842
  env: getEnvFromApiKey(apiKey)
1835
1843
  },
1836
1844
  keyName,
1845
+ grant,
1837
1846
  skillInstalled: installed,
1838
1847
  skillFailed: failed
1839
1848
  });
@@ -1851,6 +1860,7 @@ async function nonInteractiveInit(options) {
1851
1860
  console.log(` ${pc4.dim("Account:")} ${scopeDisplay} ${pc4.dim("\xB7")} ${envLabel}`);
1852
1861
  console.log(` ${pc4.dim("User:")} ${whoami.user.name} ${pc4.dim(`(${whoami.user.email})`)}`);
1853
1862
  if (keyName) console.log(` ${pc4.dim("Key:")} ${keyName}`);
1863
+ if (grant) console.log(` ${pc4.dim("Access:")} ${grant}`);
1854
1864
  console.log(` ${pc4.dim("Config:")} ${tildify(configPath)}`);
1855
1865
  if (installed.length > 0) {
1856
1866
  console.log(` ${pc4.dim("Skill:")} ${pc4.green("installed")} ${pc4.dim("\xB7 " + installed.join(", "))}`);
@@ -2046,6 +2056,7 @@ async function handleUpdateKey(statuses, scope) {
2046
2056
  }
2047
2057
  let newKey;
2048
2058
  let keyName;
2059
+ let grant;
2049
2060
  let whoamiResult;
2050
2061
  if (authMethod === "browser") {
2051
2062
  const result = await browserLogin({ scope });
@@ -2055,6 +2066,7 @@ async function handleUpdateKey(statuses, scope) {
2055
2066
  }
2056
2067
  newKey = result.apiKey;
2057
2068
  keyName = result.keyName;
2069
+ grant = result.grant;
2058
2070
  whoamiResult = result.whoami;
2059
2071
  } else {
2060
2072
  p3.note(`Get your API key at:
@@ -2108,7 +2120,8 @@ ${pc4.cyan(getApiKeyUrl())}`, `API Key ${scopeLabel(scope)}`);
2108
2120
  [
2109
2121
  `${scopeDisplay} ${pc4.dim("\xB7")} ${envLabel}`,
2110
2122
  `${whoamiResult.user.name} ${pc4.dim(`(${whoamiResult.user.email})`)}`,
2111
- ...keyName ? [`${pc4.dim("Key:")} ${keyName}`] : []
2123
+ ...keyName ? [`${pc4.dim("Key:")} ${keyName}`] : [],
2124
+ ...grantNoteLines(grant, pc4.dim("Access:"))
2112
2125
  ].join("\n"),
2113
2126
  "Account"
2114
2127
  );
@@ -2370,7 +2383,8 @@ async function freshSetup(scope, options) {
2370
2383
  [
2371
2384
  `${scopeDisplay} ${pc4.dim("\xB7")} ${envLabel}`,
2372
2385
  `${result.whoami.user.name} ${pc4.dim(`(${result.whoami.user.email})`)}`,
2373
- ...result.keyName ? [`${pc4.dim("Key:")} ${result.keyName}`] : []
2386
+ ...result.keyName ? [`${pc4.dim("Key:")} ${result.keyName}`] : [],
2387
+ ...grantNoteLines(result.grant, pc4.dim("Access:"))
2374
2388
  ].join("\n"),
2375
2389
  "Account"
2376
2390
  );
@@ -3580,7 +3594,7 @@ function document(action, details, platform, settings) {
3580
3594
  truncated: true
3581
3595
  };
3582
3596
  }
3583
- async function find(requests, task, deps, settings, knowledgeCatalog = false) {
3597
+ async function find(requests, task, deps, settings, { knowledgeCatalog = false, knowledge = true } = {}) {
3584
3598
  if (requests.length === 0 || requests.length > FIND_MAX_INTENTS) {
3585
3599
  throw new FindArgsError(`Send between 1 and ${FIND_MAX_INTENTS} platform and intent pairs.`);
3586
3600
  }
@@ -3600,19 +3614,19 @@ async function find(requests, task, deps, settings, knowledgeCatalog = false) {
3600
3614
  if (!reaches(intent.platform)) return { intent, unreachable: true };
3601
3615
  const narrowed = narrow(answers[next++], settings);
3602
3616
  const { answer } = narrowed;
3603
- const picks = settings.knowledgeAgent ? answer.selected.slice(0, 1) : answer.selected;
3617
+ const picks = knowledge && settings.knowledgeAgent ? answer.selected.slice(0, 1) : answer.selected;
3604
3618
  return {
3605
3619
  intent,
3606
3620
  unreachable: false,
3607
3621
  narrowed,
3608
3622
  picks,
3609
3623
  deferred: [...answer.selected.slice(picks.length), ...answer.alsoSelected ?? []],
3610
- runnerUpDocumented: !settings.knowledgeAgent && answer.runnerUp !== void 0
3624
+ runnerUpDocumented: knowledge && !settings.knowledgeAgent && answer.runnerUp !== void 0
3611
3625
  };
3612
3626
  });
3613
3627
  const ids = /* @__PURE__ */ new Set();
3614
3628
  for (const plan of plans) {
3615
- if (plan.unreachable) continue;
3629
+ if (plan.unreachable || !knowledge) continue;
3616
3630
  plan.picks.forEach((a) => ids.add(a.systemId));
3617
3631
  if (plan.runnerUpDocumented && plan.narrowed.answer.runnerUp) ids.add(plan.narrowed.answer.runnerUp.systemId);
3618
3632
  }
@@ -3646,9 +3660,9 @@ async function find(requests, task, deps, settings, knowledgeCatalog = false) {
3646
3660
  };
3647
3661
  }
3648
3662
  const { answer } = plan.narrowed;
3649
- const selected = [];
3663
+ const selected = knowledge ? [] : plan.picks.map(ref);
3650
3664
  const deferred = [];
3651
- for (const [index, pick] of plan.picks.entries()) {
3665
+ for (const [index, pick] of (knowledge ? plan.picks : []).entries()) {
3652
3666
  const doc = document(ref(pick), details.get(pick.systemId) ?? new Error("not fetched"), plan.intent.platform, settings);
3653
3667
  if (!admit(doc)) {
3654
3668
  deferred.push(...plan.picks.slice(index).map(ref));
@@ -3676,7 +3690,7 @@ async function find(requests, task, deps, settings, knowledgeCatalog = false) {
3676
3690
  result[index].runnerUp = doc;
3677
3691
  }
3678
3692
  });
3679
- const documented = result.some((a) => a.selected.length > 0 || a.runnerUp && "knowledge" in a.runnerUp);
3693
+ const documented = result.some((a) => a.selected.some((s) => "knowledge" in s) || a.runnerUp && "knowledge" in a.runnerUp);
3680
3694
  return { answers: result, ...documented ? { guide: closingGuide(settings) } : {} };
3681
3695
  }
3682
3696
  async function load(actionIds, options, deps, settings) {
@@ -3844,7 +3858,9 @@ ${doc.knowledge}`;
3844
3858
  const parts = [`## ${answer.platform}: ${answer.intent}
3845
3859
 
3846
3860
  ${answer.status}`];
3847
- parts.push(...answer.selected.map(shown));
3861
+ const documented = answer.selected.filter((s) => "knowledge" in s);
3862
+ parts.push(...documented.map(shown));
3863
+ parts.push(...listing("Chosen for this intent", answer.selected.filter((s) => !("knowledge" in s))));
3848
3864
  parts.push(
3849
3865
  ...listing(
3850
3866
  answer.selected.length === 0 ? "Chosen for this intent, documentation left out to keep the answer small" : "Also chosen for this intent, documentation left out to keep the answer small",
@@ -3931,7 +3947,7 @@ async function actionsFindCommand(pairs, options) {
3931
3947
  options.task,
3932
3948
  findDeps(api, connectionKeys, options.cache !== false),
3933
3949
  settings,
3934
- options.type === "knowledge"
3950
+ { knowledgeCatalog: options.type === "knowledge", knowledge: options.knowledge !== false }
3935
3951
  );
3936
3952
  if (isAgentMode()) {
3937
3953
  json(result);
@@ -11427,6 +11443,7 @@ One call for the whole task: one platform and intent pair per operation, on any
11427
11443
  - **\`--task\`** (optional) is the whole job in one line, **in general terms**: what it does, without names, addresses, IDs, or message text. It helps choose between similar actions.
11428
11444
  - **\`-t knowledge\`** searches the catalog for writing code and building flows and relays, which includes the platform's own (passthrough) endpoints; the default, \`-t execute\`, is the catalog for running actions now. Knowledge-only mode always uses \`knowledge\`.
11429
11445
  - **\`--ai-model\`** (optional) names the model running the CLI (e.g. \`claude-sonnet-5\`); it will be used to tune documentation per model.
11446
+ - **\`--no-knowledge\`** (optional) lists the chosen actions without their documentation, when only the actionIds are needed. Load the documentation with \`actions load\` before executing.
11430
11447
 
11431
11448
  In \`--agent\` mode:
11432
11449
 
@@ -13176,7 +13193,7 @@ program.command("platforms").alias("p").description("List available platforms").
13176
13193
  await platformsCommand(options);
13177
13194
  });
13178
13195
  var actions = program.command("actions").alias("a").description("Find, read, and execute platform actions (workflow: find \u2192 execute)");
13179
- actions.command("find <pairs...>").alias("f").description('Find the action for every operation a task needs, across platforms, with its documentation, in one call: one actions find <platform> "<intent>" [<platform> "<intent>" ...] (up to 10 pairs). Each intent names the operation alone ("send an email"), without its data. Large documents come back as a digest; each names the exact `actions load` command for what it left out').option("--task <task>", 'The whole task in one line, in general terms (e.g. "email a report to a contact"): what it does, without names, addresses, IDs or message text. Helps choose between similar actions').option("-t, --type <type>", "execute (default: actions to run now) or knowledge (the catalog for writing code and building flows and relays, with the platform's own endpoints). Knowledge-only mode always uses knowledge").option("--ai-model <model>", "The AI model running the CLI (e.g. claude-sonnet-5), which helps optimize the documentation returned. Optional").option("--no-cache", "Bypass the cache for action documentation and re-fetch it (the fresh response still refreshes the cache)").action(async (pairs, options) => {
13196
+ actions.command("find <pairs...>").alias("f").description('Find the action for every operation a task needs, across platforms, with its documentation, in one call: one actions find <platform> "<intent>" [<platform> "<intent>" ...] (up to 10 pairs). Each intent names the operation alone ("send an email"), without its data. Large documents come back as a digest; each names the exact `actions load` command for what it left out').option("--task <task>", 'The whole task in one line, in general terms (e.g. "email a report to a contact"): what it does, without names, addresses, IDs or message text. Helps choose between similar actions').option("-t, --type <type>", "execute (default: actions to run now) or knowledge (the catalog for writing code and building flows and relays, with the platform's own endpoints). Knowledge-only mode always uses knowledge").option("--ai-model <model>", "The AI model running the CLI (e.g. claude-sonnet-5), which helps optimize the documentation returned. Optional").option("--no-cache", "Bypass the cache for action documentation and re-fetch it (the fresh response still refreshes the cache)").option("--no-knowledge", "List the chosen actions (actionId, title, method, path) without their documentation; load it with `one actions load <actionId>`").action(async (pairs, options) => {
13180
13197
  await actionsFindCommand(pairs, options);
13181
13198
  });
13182
13199
  actions.command("load <actionIds...>").alias("l").description("More of an action's documentation (up to 10 actions): the digest by default, --section for one a digest left out, --full for the whole document, --toc for its contents. Also how to read an alternative from a find answer").option("--section <name>", 'Return only the named section(s) \u2014 by heading, id, or alias (e.g. "Response Fields", response, optional). Repeatable or comma-separated', collect, []).option("--full", "Return the whole document instead of the digest").option("--toc", "List every section (id, heading, size) without the document").option("--no-cache", "Bypass the cache and re-fetch from the API (the fresh response still refreshes the cache)").option("--cache-status", "Print cache metadata without fetching").action(async (actionIds, options) => {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@withone/cli",
3
- "version": "2.0.0",
3
+ "version": "2.2.0",
4
4
  "description": "CLI for managing One",
5
5
  "type": "module",
6
6
  "files": [
@@ -83,6 +83,7 @@ one --agent actions find <platform> "<intent>" [<platform> "<intent>" ...] --tas
83
83
  - **`--task`** (optional) is the whole job in one line, **in general terms**: `"email a weather report to a contact"`, without names, addresses, IDs, or message text. It helps choose between similar actions.
84
84
  - **`-t knowledge`** searches the catalog for writing code and building flows and relays (it includes the platform's own passthrough endpoints); the default, `-t execute`, is the catalog for running actions now.
85
85
  - **`--ai-model`** (optional): the model you are running as (e.g. `claude-sonnet-5`), used to tune documentation per model.
86
+ - **`--no-knowledge`** (optional): list the chosen actions without their documentation, when you only need the actionIds. Load the documentation with `actions load` before executing.
86
87
  - Platform names are lowercase; multi-word names use dashes: `gmail`, `hubspot`, `ship-station`, `google-calendar`.
87
88
 
88
89
  Each entry of `answers[]` carries: