@withone/cli 1.31.0 → 1.32.1

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.
@@ -66,10 +66,12 @@ var OneApi = class {
66
66
  if (!text) return {};
67
67
  return JSON.parse(text);
68
68
  }
69
+ async whoami() {
70
+ return this.request("/users/whoami");
71
+ }
69
72
  async validateApiKey() {
70
73
  try {
71
- await this.listConnections();
72
- return true;
74
+ return await this.whoami();
73
75
  } catch (error) {
74
76
  if (error instanceof ApiError && error.status === 401) {
75
77
  return false;
@@ -1040,7 +1042,7 @@ async function executeSubflowStep(step, context, api, permissions, allowedAction
1040
1042
  if (flowStack.includes(resolvedKey)) {
1041
1043
  throw new Error(`Circular flow detected: ${[...flowStack, resolvedKey].join(" \u2192 ")}`);
1042
1044
  }
1043
- const { loadFlowWithMeta: loadFlowWithMeta2 } = await import("./flow-runner-HT74FKPD.js");
1045
+ const { loadFlowWithMeta: loadFlowWithMeta2 } = await import("./flow-runner-6IFPWOS4.js");
1044
1046
  const { flow: subFlow, rootDir: subRootDir } = loadFlowWithMeta2(resolvedKey);
1045
1047
  const subContext = await executeFlow(
1046
1048
  subFlow,
@@ -11,7 +11,7 @@ import {
11
11
  saveFlow,
12
12
  summarizeFlowInputs,
13
13
  walkSteps
14
- } from "./chunk-645Z3ARQ.js";
14
+ } from "./chunk-T7LTS2IE.js";
15
15
  export {
16
16
  FlowRunner,
17
17
  collectStepTypes,
package/dist/index.js CHANGED
@@ -19,7 +19,7 @@ import {
19
19
  resolveFlowPath,
20
20
  saveFlow,
21
21
  validateActionInput
22
- } from "./chunk-645Z3ARQ.js";
22
+ } from "./chunk-T7LTS2IE.js";
23
23
 
24
24
  // src/index.ts
25
25
  import { createRequire as createRequire2 } from "module";
@@ -187,6 +187,7 @@ function updateApiBase(url) {
187
187
  } else {
188
188
  delete config2.apiBase;
189
189
  }
190
+ delete config2.whoami;
190
191
  writeConfig(config2);
191
192
  }
192
193
  function getCacheTtl() {
@@ -221,6 +222,29 @@ function updateAccessControl(settings) {
221
222
  }
222
223
  writeConfig(config2);
223
224
  }
225
+ function getWhoAmI() {
226
+ return readConfig()?.whoami ?? null;
227
+ }
228
+ function updateWhoAmI(whoami) {
229
+ const config2 = readConfig();
230
+ if (!config2) return;
231
+ config2.whoami = whoami;
232
+ writeConfig(config2);
233
+ }
234
+ async function ensureWhoAmI(api) {
235
+ const cached2 = getWhoAmI();
236
+ if (cached2) return cached2;
237
+ try {
238
+ const whoami = await api.whoami();
239
+ updateWhoAmI(whoami);
240
+ return whoami;
241
+ } catch {
242
+ return null;
243
+ }
244
+ }
245
+ function getEnvFromApiKey(apiKey) {
246
+ return apiKey.startsWith("sk_test_") ? "test" : "live";
247
+ }
224
248
 
225
249
  // src/lib/agents.ts
226
250
  import fs2 from "fs";
@@ -403,14 +427,19 @@ function getAgentStatuses() {
403
427
  // src/lib/browser.ts
404
428
  import open from "open";
405
429
  var ONE_APP_URL = "https://app.withone.ai";
406
- function getConnectionUrl(platform) {
407
- return `${ONE_APP_URL}/?#open=${platform}`;
430
+ function getConnectionUrl(platform, params) {
431
+ const searchParams = new URLSearchParams();
432
+ if (params?.orgId) searchParams.set("orgId", params.orgId);
433
+ if (params?.projectId) searchParams.set("projectId", params.projectId);
434
+ if (params?.env) searchParams.set("env", params.env);
435
+ const qs = searchParams.toString();
436
+ return `${ONE_APP_URL}/${qs ? `?${qs}` : ""}#open=${platform}`;
408
437
  }
409
438
  function getApiKeyUrl() {
410
439
  return `${ONE_APP_URL}/settings/api-keys`;
411
440
  }
412
- async function openConnectionPage(platform) {
413
- const url = getConnectionUrl(platform);
441
+ async function openConnectionPage(platform, params) {
442
+ const url = getConnectionUrl(platform, params);
414
443
  await open(url);
415
444
  }
416
445
  async function openApiKeyPage() {
@@ -673,6 +702,7 @@ async function configCommand() {
673
702
  const updatedConfig = readConfig();
674
703
  if (updatedConfig && newApiKey !== config2.apiKey) {
675
704
  updatedConfig.apiKey = newApiKey;
705
+ delete updatedConfig.whoami;
676
706
  writeConfig(updatedConfig);
677
707
  }
678
708
  const ac = getAccessControl();
@@ -817,8 +847,16 @@ async function updateCommand() {
817
847
  }
818
848
  s.stop(`Update available: v${currentVersion} \u2192 v${latestVersion}`);
819
849
  console.log(`Updating @withone/cli: v${currentVersion} \u2192 v${latestVersion}...`);
850
+ await new Promise((resolve) => {
851
+ const child = spawn("npm", ["cache", "clean", "--force"], {
852
+ stdio: "ignore",
853
+ shell: true
854
+ });
855
+ child.on("close", () => resolve());
856
+ child.on("error", () => resolve());
857
+ });
820
858
  const code = await new Promise((resolve) => {
821
- const child = spawn("npm", ["install", "-g", "@withone/cli@latest", "--force"], {
859
+ const child = spawn("npm", ["install", "-g", `@withone/cli@${latestVersion}`, "--force"], {
822
860
  stdio: isAgentMode() ? "pipe" : "inherit",
823
861
  shell: true
824
862
  });
@@ -1104,10 +1142,17 @@ async function handleExistingConfig(apiKey, scope, options) {
1104
1142
  printOnboardingPrompt();
1105
1143
  p3.outro("Paste the prompt above to your AI agent.");
1106
1144
  break;
1107
- case "add-connection":
1108
- await promptConnectIntegrations(apiKey);
1145
+ case "add-connection": {
1146
+ const whoamiCached = getWhoAmI();
1147
+ const addConnParams = {
1148
+ env: getEnvFromApiKey(apiKey),
1149
+ ...whoamiCached?.organization && { orgId: whoamiCached.organization.id },
1150
+ ...whoamiCached?.project && { projectId: whoamiCached.project.id }
1151
+ };
1152
+ await promptConnectIntegrations(apiKey, addConnParams);
1109
1153
  p3.outro("Done.");
1110
1154
  break;
1155
+ }
1111
1156
  case "update-key":
1112
1157
  await handleUpdateKey(statuses, scope);
1113
1158
  break;
@@ -1151,13 +1196,24 @@ ${pc2.cyan(getApiKeyUrl())}`, `API Key ${scopeLabel(scope)}`);
1151
1196
  const spinner5 = p3.spinner();
1152
1197
  spinner5.start("Validating API key...");
1153
1198
  const api = new OneApi(newKey, getApiBase());
1154
- const isValid = await api.validateApiKey();
1155
- if (!isValid) {
1199
+ const whoamiResult = await api.validateApiKey();
1200
+ if (!whoamiResult) {
1156
1201
  spinner5.stop("Invalid API key");
1157
1202
  p3.cancel(`Invalid API key. Get a valid key at ${getApiKeyUrl()}`);
1158
1203
  process.exit(1);
1159
1204
  }
1160
1205
  spinner5.stop("API key validated");
1206
+ const env = getEnvFromApiKey(newKey);
1207
+ const contextParts = [];
1208
+ if (whoamiResult.organization) contextParts.push(whoamiResult.organization.name);
1209
+ if (whoamiResult.project) contextParts.push(whoamiResult.project.name);
1210
+ const scopeDisplay = contextParts.length > 0 ? contextParts.join(" / ") : "Personal";
1211
+ const envLabel = env === "test" ? pc2.yellow("test") : pc2.green("live");
1212
+ p3.note(
1213
+ `${scopeDisplay} ${pc2.dim("\xB7")} ${envLabel}
1214
+ ${whoamiResult.user.name} ${pc2.dim(`(${whoamiResult.user.email})`)}`,
1215
+ "Account"
1216
+ );
1161
1217
  const ac = getAccessControl();
1162
1218
  const reinstalled = [];
1163
1219
  for (const s of statuses) {
@@ -1178,7 +1234,8 @@ ${pc2.cyan(getApiKeyUrl())}`, `API Key ${scopeLabel(scope)}`);
1178
1234
  createdAt: current?.createdAt ?? (/* @__PURE__ */ new Date()).toISOString(),
1179
1235
  accessControl: current?.accessControl,
1180
1236
  apiBase: current?.apiBase,
1181
- cacheTtl: current?.cacheTtl
1237
+ cacheTtl: current?.cacheTtl,
1238
+ whoami: whoamiResult
1182
1239
  },
1183
1240
  scope
1184
1241
  );
@@ -1376,23 +1433,40 @@ ${pc2.cyan(getApiKeyUrl())}`, `API Key ${scopeLabel(scope)}`);
1376
1433
  const spinner5 = p3.spinner();
1377
1434
  spinner5.start("Validating API key...");
1378
1435
  const api = new OneApi(apiKey, getApiBase());
1379
- const isValid = await api.validateApiKey();
1380
- if (!isValid) {
1436
+ const whoami = await api.validateApiKey();
1437
+ if (!whoami) {
1381
1438
  spinner5.stop("Invalid API key");
1382
1439
  p3.cancel(`Invalid API key. Get a valid key at ${getApiKeyUrl()}`);
1383
1440
  process.exit(1);
1384
1441
  }
1385
1442
  spinner5.stop("API key validated");
1443
+ const env = getEnvFromApiKey(apiKey);
1444
+ const contextParts = [];
1445
+ if (whoami.organization) contextParts.push(whoami.organization.name);
1446
+ if (whoami.project) contextParts.push(whoami.project.name);
1447
+ const scope_label = contextParts.length > 0 ? contextParts.join(" / ") : "Personal";
1448
+ const envLabel = env === "test" ? pc2.yellow("test") : pc2.green("live");
1449
+ p3.note(
1450
+ `${scope_label} ${pc2.dim("\xB7")} ${envLabel}
1451
+ ${whoami.user.name} ${pc2.dim(`(${whoami.user.email})`)}`,
1452
+ "Account"
1453
+ );
1386
1454
  writeConfig(
1387
1455
  {
1388
1456
  apiKey,
1389
1457
  installedAgents: [],
1390
- createdAt: (/* @__PURE__ */ new Date()).toISOString()
1458
+ createdAt: (/* @__PURE__ */ new Date()).toISOString(),
1459
+ whoami
1391
1460
  },
1392
1461
  scope
1393
1462
  );
1394
1463
  await promptSkillInstall();
1395
- await promptConnectIntegrations(apiKey);
1464
+ const connParams = {
1465
+ env,
1466
+ ...whoami.organization && { orgId: whoami.organization.id },
1467
+ ...whoami.project && { projectId: whoami.project.id }
1468
+ };
1469
+ await promptConnectIntegrations(apiKey, connParams);
1396
1470
  const savedPath = scope === "project" ? getProjectConfigPath() : getGlobalConfigPath();
1397
1471
  const resolutionHint = scope === "project" ? `When you run ${pc2.cyan("one")} from ${pc2.bold(path4.basename(getProjectRoot()))}, it uses this project config.
1398
1472
  From anywhere else, it falls back to your global config.` : `This config applies to every folder unless a project config is set.`;
@@ -1427,7 +1501,7 @@ var TOP_INTEGRATIONS = [
1427
1501
  { value: "google-calendar", label: "Google Calendar", hint: "Manage events and schedules" },
1428
1502
  { value: "notion", label: "Notion", hint: "Access pages, databases, and docs" }
1429
1503
  ];
1430
- async function promptConnectIntegrations(apiKey) {
1504
+ async function promptConnectIntegrations(apiKey, connParams) {
1431
1505
  const api = new OneApi(apiKey, getApiBase());
1432
1506
  const connected = [];
1433
1507
  try {
@@ -1472,9 +1546,9 @@ async function promptConnectIntegrations(apiKey) {
1472
1546
  const label = integration?.label ?? platform;
1473
1547
  p3.log.info(`Opening browser to connect ${pc2.cyan(label)}...`);
1474
1548
  try {
1475
- await openConnectionPage(platform);
1549
+ await openConnectionPage(platform, connParams);
1476
1550
  } catch {
1477
- const url = getConnectionUrl(platform);
1551
+ const url = getConnectionUrl(platform, connParams);
1478
1552
  p3.log.warn("Could not open browser automatically.");
1479
1553
  p3.note(url, "Open manually");
1480
1554
  }
@@ -1654,11 +1728,17 @@ Run ${pc4.cyan("one platforms")} to see available platforms.`);
1654
1728
  process.exit(1);
1655
1729
  }
1656
1730
  }
1657
- const url = getConnectionUrl(platform);
1731
+ const whoami = await ensureWhoAmI(api);
1732
+ const connParams = {
1733
+ env: getEnvFromApiKey(apiKey),
1734
+ ...whoami?.organization && { orgId: whoami.organization.id },
1735
+ ...whoami?.project && { projectId: whoami.project.id }
1736
+ };
1737
+ const url = getConnectionUrl(platform, connParams);
1658
1738
  p4.log.info(`Opening browser to connect ${pc4.cyan(platform)}...`);
1659
1739
  p4.note(pc4.dim(url), "URL");
1660
1740
  try {
1661
- await openConnectionPage(platform);
1741
+ await openConnectionPage(platform, connParams);
1662
1742
  } catch {
1663
1743
  p4.log.warn("Could not open browser automatically.");
1664
1744
  p4.note(`Open this URL manually:
@@ -1748,14 +1828,17 @@ Add one with: ${pc4.cyan("one connection add gmail")}`,
1748
1828
  status: getStatusIndicator(conn.state),
1749
1829
  platform: conn.platform,
1750
1830
  state: conn.state,
1751
- key: conn.key
1831
+ key: conn.key,
1832
+ tags: conn.tags?.length ? conn.tags.join(", ") : ""
1752
1833
  }));
1834
+ const hasTags = rows.some((r) => r.tags);
1753
1835
  printTable(
1754
1836
  [
1755
1837
  { key: "status", label: "" },
1756
1838
  { key: "platform", label: "Platform" },
1757
1839
  { key: "state", label: "Status" },
1758
- { key: "key", label: "Connection Key", color: pc4.dim }
1840
+ { key: "key", label: "Connection Key", color: pc4.dim },
1841
+ ...hasTags ? [{ key: "tags", label: "Tags", color: pc4.dim }] : []
1759
1842
  ],
1760
1843
  rows
1761
1844
  );
@@ -3988,14 +4071,20 @@ async function relayListCommand(options) {
3988
4071
  return;
3989
4072
  }
3990
4073
  printTable(
3991
- ["Status", "Description", "Events", "Actions", "ID"],
3992
- endpoints.map((e) => [
3993
- e.active ? pc8.green("\u25CF") : pc8.dim("\u25CB"),
3994
- e.description || pc8.dim("(none)"),
3995
- e.eventFilters?.join(", ") || pc8.dim("all"),
3996
- String(e.actions?.length || 0),
3997
- pc8.dim(e.id.slice(0, 8))
3998
- ])
4074
+ [
4075
+ { key: "status", label: "" },
4076
+ { key: "description", label: "Description" },
4077
+ { key: "events", label: "Events" },
4078
+ { key: "actions", label: "Actions" },
4079
+ { key: "id", label: "ID", color: pc8.dim }
4080
+ ],
4081
+ endpoints.map((e) => ({
4082
+ status: e.active ? pc8.green("\u25CF") : pc8.dim("\u25CB"),
4083
+ description: e.description || pc8.dim("(none)"),
4084
+ events: e.eventFilters?.join(", ") || pc8.dim("all"),
4085
+ actions: String(e.actions?.length || 0),
4086
+ id: e.id.slice(0, 8)
4087
+ }))
3999
4088
  );
4000
4089
  } catch (error2) {
4001
4090
  spinner5.stop("Failed to list relay endpoints");
@@ -4137,13 +4226,18 @@ async function relayEventsCommand(options) {
4137
4226
  return;
4138
4227
  }
4139
4228
  printTable(
4140
- ["Platform", "Event Type", "Timestamp", "ID"],
4141
- events.map((e) => [
4142
- e.platform,
4143
- e.eventType || pc8.dim("unknown"),
4144
- e.timestamp || e.createdAt,
4145
- pc8.dim(e.id.slice(0, 8))
4146
- ])
4229
+ [
4230
+ { key: "platform", label: "Platform" },
4231
+ { key: "eventType", label: "Event Type" },
4232
+ { key: "timestamp", label: "Timestamp" },
4233
+ { key: "id", label: "ID", color: pc8.dim }
4234
+ ],
4235
+ events.map((e) => ({
4236
+ platform: e.platform,
4237
+ eventType: e.eventType || pc8.dim("unknown"),
4238
+ timestamp: e.timestamp || e.createdAt,
4239
+ id: e.id.slice(0, 8)
4240
+ }))
4147
4241
  );
4148
4242
  } catch (error2) {
4149
4243
  spinner5.stop("Failed to list relay events");
@@ -4196,14 +4290,20 @@ async function relayDeliveriesCommand(options) {
4196
4290
  return;
4197
4291
  }
4198
4292
  printTable(
4199
- ["Status", "Code", "Attempt", "Delivered At", "Error"],
4200
- items.map((d) => [
4201
- d.status === "success" ? pc8.green(d.status) : pc8.red(d.status),
4202
- d.statusCode != null ? String(d.statusCode) : pc8.dim("-"),
4203
- String(d.attempt),
4204
- d.deliveredAt || pc8.dim("-"),
4205
- d.error ? pc8.red(d.error.slice(0, 50)) : pc8.dim("-")
4206
- ])
4293
+ [
4294
+ { key: "status", label: "Status" },
4295
+ { key: "code", label: "Code" },
4296
+ { key: "attempt", label: "Attempt" },
4297
+ { key: "deliveredAt", label: "Delivered At" },
4298
+ { key: "error", label: "Error" }
4299
+ ],
4300
+ items.map((d) => ({
4301
+ status: d.status === "success" ? pc8.green(d.status) : pc8.red(d.status),
4302
+ code: d.statusCode != null ? String(d.statusCode) : pc8.dim("-"),
4303
+ attempt: String(d.attempt),
4304
+ deliveredAt: d.deliveredAt || pc8.dim("-"),
4305
+ error: d.error ? pc8.red(d.error.slice(0, 50)) : pc8.dim("-")
4306
+ }))
4207
4307
  );
4208
4308
  } catch (error2) {
4209
4309
  spinner5.stop("Failed to load deliveries");
@@ -8570,6 +8670,7 @@ program.name("one").option("--agent", "Machine-readable JSON output (no colors,
8570
8670
  one add <platform> Connect a platform via OAuth (e.g. gmail, slack, shopify)
8571
8671
  one connection delete <key> Remove a connection (alias: one connection rm)
8572
8672
  one config Configure access control (permissions, scoping)
8673
+ one whoami Show current user, organization, and project
8573
8674
 
8574
8675
  Workflow (use these in order):
8575
8676
  1. one list List your connected platforms and connection keys
@@ -8840,6 +8941,53 @@ program.command("onboard").description("Agent onboarding \u2014 teaches your age
8840
8941
  program.command("update").description("Update the One CLI to the latest version").action(async () => {
8841
8942
  await updateCommand();
8842
8943
  });
8944
+ program.command("whoami").description("Show the user, organization, and project for the current API key").action(async () => {
8945
+ const apiKey = getApiKey();
8946
+ if (!apiKey) {
8947
+ error("Not configured. Run `one init` first.");
8948
+ return;
8949
+ }
8950
+ const api = new OneApi(apiKey, getApiBase());
8951
+ let whoami;
8952
+ try {
8953
+ whoami = await api.whoami();
8954
+ updateWhoAmI(whoami);
8955
+ } catch (error2) {
8956
+ error(`Failed to fetch account info: ${error2 instanceof Error ? error2.message : "Unknown error"}`);
8957
+ return;
8958
+ }
8959
+ const env = getEnvFromApiKey(apiKey);
8960
+ const resolved = resolveConfig();
8961
+ const configScope = resolved.scope ?? "global";
8962
+ const apiBase = getApiBase();
8963
+ if (isAgentMode()) {
8964
+ json({
8965
+ user: whoami.user,
8966
+ organization: whoami.organization,
8967
+ project: whoami.project,
8968
+ env,
8969
+ configScope,
8970
+ apiBase
8971
+ });
8972
+ return;
8973
+ }
8974
+ const pc12 = (await import("picocolors")).default;
8975
+ const contextParts = [];
8976
+ if (whoami.organization) contextParts.push(whoami.organization.name);
8977
+ if (whoami.project) contextParts.push(whoami.project.name);
8978
+ const scopeDisplay = contextParts.length > 0 ? contextParts.join(" / ") : "Personal";
8979
+ const envLabel = env === "test" ? pc12.yellow("test") : pc12.green("live");
8980
+ const configLabel = configScope === "project" ? pc12.cyan("project config") : pc12.magenta("global config");
8981
+ console.log();
8982
+ console.log(` ${pc12.bold(scopeDisplay)} ${pc12.dim("\xB7")} ${envLabel}`);
8983
+ console.log(` ${whoami.user.name} ${pc12.dim(`(${whoami.user.email})`)}`);
8984
+ if (whoami.organization) console.log(` ${pc12.dim("Org:")} ${whoami.organization.name} ${pc12.dim(`(${whoami.organization.id})`)}`);
8985
+ if (whoami.project) console.log(` ${pc12.dim("Project:")} ${whoami.project.name} ${pc12.dim(`(${whoami.project.id})`)}`);
8986
+ console.log();
8987
+ console.log(` ${pc12.dim("Using")} ${configLabel}`);
8988
+ console.log(` ${pc12.dim("API:")} ${apiBase}`);
8989
+ console.log();
8990
+ });
8843
8991
  program.command("add [platform]").description("Shortcut for: connection add").action(async (platform) => {
8844
8992
  await connectionAddCommand(platform);
8845
8993
  });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@withone/cli",
3
- "version": "1.31.0",
3
+ "version": "1.32.1",
4
4
  "description": "CLI for managing One",
5
5
  "type": "module",
6
6
  "files": [