@withone/cli 1.31.0 → 1.32.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.
@@ -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();
@@ -1104,10 +1134,17 @@ async function handleExistingConfig(apiKey, scope, options) {
1104
1134
  printOnboardingPrompt();
1105
1135
  p3.outro("Paste the prompt above to your AI agent.");
1106
1136
  break;
1107
- case "add-connection":
1108
- await promptConnectIntegrations(apiKey);
1137
+ case "add-connection": {
1138
+ const whoamiCached = getWhoAmI();
1139
+ const addConnParams = {
1140
+ env: getEnvFromApiKey(apiKey),
1141
+ ...whoamiCached?.organization && { orgId: whoamiCached.organization.id },
1142
+ ...whoamiCached?.project && { projectId: whoamiCached.project.id }
1143
+ };
1144
+ await promptConnectIntegrations(apiKey, addConnParams);
1109
1145
  p3.outro("Done.");
1110
1146
  break;
1147
+ }
1111
1148
  case "update-key":
1112
1149
  await handleUpdateKey(statuses, scope);
1113
1150
  break;
@@ -1151,13 +1188,24 @@ ${pc2.cyan(getApiKeyUrl())}`, `API Key ${scopeLabel(scope)}`);
1151
1188
  const spinner5 = p3.spinner();
1152
1189
  spinner5.start("Validating API key...");
1153
1190
  const api = new OneApi(newKey, getApiBase());
1154
- const isValid = await api.validateApiKey();
1155
- if (!isValid) {
1191
+ const whoamiResult = await api.validateApiKey();
1192
+ if (!whoamiResult) {
1156
1193
  spinner5.stop("Invalid API key");
1157
1194
  p3.cancel(`Invalid API key. Get a valid key at ${getApiKeyUrl()}`);
1158
1195
  process.exit(1);
1159
1196
  }
1160
1197
  spinner5.stop("API key validated");
1198
+ const env = getEnvFromApiKey(newKey);
1199
+ const contextParts = [];
1200
+ if (whoamiResult.organization) contextParts.push(whoamiResult.organization.name);
1201
+ if (whoamiResult.project) contextParts.push(whoamiResult.project.name);
1202
+ const scopeDisplay = contextParts.length > 0 ? contextParts.join(" / ") : "Personal";
1203
+ const envLabel = env === "test" ? pc2.yellow("test") : pc2.green("live");
1204
+ p3.note(
1205
+ `${scopeDisplay} ${pc2.dim("\xB7")} ${envLabel}
1206
+ ${whoamiResult.user.name} ${pc2.dim(`(${whoamiResult.user.email})`)}`,
1207
+ "Account"
1208
+ );
1161
1209
  const ac = getAccessControl();
1162
1210
  const reinstalled = [];
1163
1211
  for (const s of statuses) {
@@ -1178,7 +1226,8 @@ ${pc2.cyan(getApiKeyUrl())}`, `API Key ${scopeLabel(scope)}`);
1178
1226
  createdAt: current?.createdAt ?? (/* @__PURE__ */ new Date()).toISOString(),
1179
1227
  accessControl: current?.accessControl,
1180
1228
  apiBase: current?.apiBase,
1181
- cacheTtl: current?.cacheTtl
1229
+ cacheTtl: current?.cacheTtl,
1230
+ whoami: whoamiResult
1182
1231
  },
1183
1232
  scope
1184
1233
  );
@@ -1376,23 +1425,40 @@ ${pc2.cyan(getApiKeyUrl())}`, `API Key ${scopeLabel(scope)}`);
1376
1425
  const spinner5 = p3.spinner();
1377
1426
  spinner5.start("Validating API key...");
1378
1427
  const api = new OneApi(apiKey, getApiBase());
1379
- const isValid = await api.validateApiKey();
1380
- if (!isValid) {
1428
+ const whoami = await api.validateApiKey();
1429
+ if (!whoami) {
1381
1430
  spinner5.stop("Invalid API key");
1382
1431
  p3.cancel(`Invalid API key. Get a valid key at ${getApiKeyUrl()}`);
1383
1432
  process.exit(1);
1384
1433
  }
1385
1434
  spinner5.stop("API key validated");
1435
+ const env = getEnvFromApiKey(apiKey);
1436
+ const contextParts = [];
1437
+ if (whoami.organization) contextParts.push(whoami.organization.name);
1438
+ if (whoami.project) contextParts.push(whoami.project.name);
1439
+ const scope_label = contextParts.length > 0 ? contextParts.join(" / ") : "Personal";
1440
+ const envLabel = env === "test" ? pc2.yellow("test") : pc2.green("live");
1441
+ p3.note(
1442
+ `${scope_label} ${pc2.dim("\xB7")} ${envLabel}
1443
+ ${whoami.user.name} ${pc2.dim(`(${whoami.user.email})`)}`,
1444
+ "Account"
1445
+ );
1386
1446
  writeConfig(
1387
1447
  {
1388
1448
  apiKey,
1389
1449
  installedAgents: [],
1390
- createdAt: (/* @__PURE__ */ new Date()).toISOString()
1450
+ createdAt: (/* @__PURE__ */ new Date()).toISOString(),
1451
+ whoami
1391
1452
  },
1392
1453
  scope
1393
1454
  );
1394
1455
  await promptSkillInstall();
1395
- await promptConnectIntegrations(apiKey);
1456
+ const connParams = {
1457
+ env,
1458
+ ...whoami.organization && { orgId: whoami.organization.id },
1459
+ ...whoami.project && { projectId: whoami.project.id }
1460
+ };
1461
+ await promptConnectIntegrations(apiKey, connParams);
1396
1462
  const savedPath = scope === "project" ? getProjectConfigPath() : getGlobalConfigPath();
1397
1463
  const resolutionHint = scope === "project" ? `When you run ${pc2.cyan("one")} from ${pc2.bold(path4.basename(getProjectRoot()))}, it uses this project config.
1398
1464
  From anywhere else, it falls back to your global config.` : `This config applies to every folder unless a project config is set.`;
@@ -1427,7 +1493,7 @@ var TOP_INTEGRATIONS = [
1427
1493
  { value: "google-calendar", label: "Google Calendar", hint: "Manage events and schedules" },
1428
1494
  { value: "notion", label: "Notion", hint: "Access pages, databases, and docs" }
1429
1495
  ];
1430
- async function promptConnectIntegrations(apiKey) {
1496
+ async function promptConnectIntegrations(apiKey, connParams) {
1431
1497
  const api = new OneApi(apiKey, getApiBase());
1432
1498
  const connected = [];
1433
1499
  try {
@@ -1472,9 +1538,9 @@ async function promptConnectIntegrations(apiKey) {
1472
1538
  const label = integration?.label ?? platform;
1473
1539
  p3.log.info(`Opening browser to connect ${pc2.cyan(label)}...`);
1474
1540
  try {
1475
- await openConnectionPage(platform);
1541
+ await openConnectionPage(platform, connParams);
1476
1542
  } catch {
1477
- const url = getConnectionUrl(platform);
1543
+ const url = getConnectionUrl(platform, connParams);
1478
1544
  p3.log.warn("Could not open browser automatically.");
1479
1545
  p3.note(url, "Open manually");
1480
1546
  }
@@ -1654,11 +1720,17 @@ Run ${pc4.cyan("one platforms")} to see available platforms.`);
1654
1720
  process.exit(1);
1655
1721
  }
1656
1722
  }
1657
- const url = getConnectionUrl(platform);
1723
+ const whoami = await ensureWhoAmI(api);
1724
+ const connParams = {
1725
+ env: getEnvFromApiKey(apiKey),
1726
+ ...whoami?.organization && { orgId: whoami.organization.id },
1727
+ ...whoami?.project && { projectId: whoami.project.id }
1728
+ };
1729
+ const url = getConnectionUrl(platform, connParams);
1658
1730
  p4.log.info(`Opening browser to connect ${pc4.cyan(platform)}...`);
1659
1731
  p4.note(pc4.dim(url), "URL");
1660
1732
  try {
1661
- await openConnectionPage(platform);
1733
+ await openConnectionPage(platform, connParams);
1662
1734
  } catch {
1663
1735
  p4.log.warn("Could not open browser automatically.");
1664
1736
  p4.note(`Open this URL manually:
@@ -1748,14 +1820,17 @@ Add one with: ${pc4.cyan("one connection add gmail")}`,
1748
1820
  status: getStatusIndicator(conn.state),
1749
1821
  platform: conn.platform,
1750
1822
  state: conn.state,
1751
- key: conn.key
1823
+ key: conn.key,
1824
+ tags: conn.tags?.length ? conn.tags.join(", ") : ""
1752
1825
  }));
1826
+ const hasTags = rows.some((r) => r.tags);
1753
1827
  printTable(
1754
1828
  [
1755
1829
  { key: "status", label: "" },
1756
1830
  { key: "platform", label: "Platform" },
1757
1831
  { key: "state", label: "Status" },
1758
- { key: "key", label: "Connection Key", color: pc4.dim }
1832
+ { key: "key", label: "Connection Key", color: pc4.dim },
1833
+ ...hasTags ? [{ key: "tags", label: "Tags", color: pc4.dim }] : []
1759
1834
  ],
1760
1835
  rows
1761
1836
  );
@@ -3988,14 +4063,20 @@ async function relayListCommand(options) {
3988
4063
  return;
3989
4064
  }
3990
4065
  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
- ])
4066
+ [
4067
+ { key: "status", label: "" },
4068
+ { key: "description", label: "Description" },
4069
+ { key: "events", label: "Events" },
4070
+ { key: "actions", label: "Actions" },
4071
+ { key: "id", label: "ID", color: pc8.dim }
4072
+ ],
4073
+ endpoints.map((e) => ({
4074
+ status: e.active ? pc8.green("\u25CF") : pc8.dim("\u25CB"),
4075
+ description: e.description || pc8.dim("(none)"),
4076
+ events: e.eventFilters?.join(", ") || pc8.dim("all"),
4077
+ actions: String(e.actions?.length || 0),
4078
+ id: e.id.slice(0, 8)
4079
+ }))
3999
4080
  );
4000
4081
  } catch (error2) {
4001
4082
  spinner5.stop("Failed to list relay endpoints");
@@ -4137,13 +4218,18 @@ async function relayEventsCommand(options) {
4137
4218
  return;
4138
4219
  }
4139
4220
  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
- ])
4221
+ [
4222
+ { key: "platform", label: "Platform" },
4223
+ { key: "eventType", label: "Event Type" },
4224
+ { key: "timestamp", label: "Timestamp" },
4225
+ { key: "id", label: "ID", color: pc8.dim }
4226
+ ],
4227
+ events.map((e) => ({
4228
+ platform: e.platform,
4229
+ eventType: e.eventType || pc8.dim("unknown"),
4230
+ timestamp: e.timestamp || e.createdAt,
4231
+ id: e.id.slice(0, 8)
4232
+ }))
4147
4233
  );
4148
4234
  } catch (error2) {
4149
4235
  spinner5.stop("Failed to list relay events");
@@ -4196,14 +4282,20 @@ async function relayDeliveriesCommand(options) {
4196
4282
  return;
4197
4283
  }
4198
4284
  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
- ])
4285
+ [
4286
+ { key: "status", label: "Status" },
4287
+ { key: "code", label: "Code" },
4288
+ { key: "attempt", label: "Attempt" },
4289
+ { key: "deliveredAt", label: "Delivered At" },
4290
+ { key: "error", label: "Error" }
4291
+ ],
4292
+ items.map((d) => ({
4293
+ status: d.status === "success" ? pc8.green(d.status) : pc8.red(d.status),
4294
+ code: d.statusCode != null ? String(d.statusCode) : pc8.dim("-"),
4295
+ attempt: String(d.attempt),
4296
+ deliveredAt: d.deliveredAt || pc8.dim("-"),
4297
+ error: d.error ? pc8.red(d.error.slice(0, 50)) : pc8.dim("-")
4298
+ }))
4207
4299
  );
4208
4300
  } catch (error2) {
4209
4301
  spinner5.stop("Failed to load deliveries");
@@ -8570,6 +8662,7 @@ program.name("one").option("--agent", "Machine-readable JSON output (no colors,
8570
8662
  one add <platform> Connect a platform via OAuth (e.g. gmail, slack, shopify)
8571
8663
  one connection delete <key> Remove a connection (alias: one connection rm)
8572
8664
  one config Configure access control (permissions, scoping)
8665
+ one whoami Show current user, organization, and project
8573
8666
 
8574
8667
  Workflow (use these in order):
8575
8668
  1. one list List your connected platforms and connection keys
@@ -8840,6 +8933,53 @@ program.command("onboard").description("Agent onboarding \u2014 teaches your age
8840
8933
  program.command("update").description("Update the One CLI to the latest version").action(async () => {
8841
8934
  await updateCommand();
8842
8935
  });
8936
+ program.command("whoami").description("Show the user, organization, and project for the current API key").action(async () => {
8937
+ const apiKey = getApiKey();
8938
+ if (!apiKey) {
8939
+ error("Not configured. Run `one init` first.");
8940
+ return;
8941
+ }
8942
+ const api = new OneApi(apiKey, getApiBase());
8943
+ let whoami;
8944
+ try {
8945
+ whoami = await api.whoami();
8946
+ updateWhoAmI(whoami);
8947
+ } catch (error2) {
8948
+ error(`Failed to fetch account info: ${error2 instanceof Error ? error2.message : "Unknown error"}`);
8949
+ return;
8950
+ }
8951
+ const env = getEnvFromApiKey(apiKey);
8952
+ const resolved = resolveConfig();
8953
+ const configScope = resolved.scope ?? "global";
8954
+ const apiBase = getApiBase();
8955
+ if (isAgentMode()) {
8956
+ json({
8957
+ user: whoami.user,
8958
+ organization: whoami.organization,
8959
+ project: whoami.project,
8960
+ env,
8961
+ configScope,
8962
+ apiBase
8963
+ });
8964
+ return;
8965
+ }
8966
+ const pc12 = (await import("picocolors")).default;
8967
+ const contextParts = [];
8968
+ if (whoami.organization) contextParts.push(whoami.organization.name);
8969
+ if (whoami.project) contextParts.push(whoami.project.name);
8970
+ const scopeDisplay = contextParts.length > 0 ? contextParts.join(" / ") : "Personal";
8971
+ const envLabel = env === "test" ? pc12.yellow("test") : pc12.green("live");
8972
+ const configLabel = configScope === "project" ? pc12.cyan("project config") : pc12.magenta("global config");
8973
+ console.log();
8974
+ console.log(` ${pc12.bold(scopeDisplay)} ${pc12.dim("\xB7")} ${envLabel}`);
8975
+ console.log(` ${whoami.user.name} ${pc12.dim(`(${whoami.user.email})`)}`);
8976
+ if (whoami.organization) console.log(` ${pc12.dim("Org:")} ${whoami.organization.name} ${pc12.dim(`(${whoami.organization.id})`)}`);
8977
+ if (whoami.project) console.log(` ${pc12.dim("Project:")} ${whoami.project.name} ${pc12.dim(`(${whoami.project.id})`)}`);
8978
+ console.log();
8979
+ console.log(` ${pc12.dim("Using")} ${configLabel}`);
8980
+ console.log(` ${pc12.dim("API:")} ${apiBase}`);
8981
+ console.log();
8982
+ });
8843
8983
  program.command("add [platform]").description("Shortcut for: connection add").action(async (platform) => {
8844
8984
  await connectionAddCommand(platform);
8845
8985
  });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@withone/cli",
3
- "version": "1.31.0",
3
+ "version": "1.32.0",
4
4
  "description": "CLI for managing One",
5
5
  "type": "module",
6
6
  "files": [