@withone/cli 1.18.0 → 1.20.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
@@ -162,6 +162,21 @@ one list
162
162
 
163
163
  You need the connection key (rightmost column) when executing actions.
164
164
 
165
+ ### `one connection delete <connection-key>`
166
+
167
+ Remove a connection by its key.
168
+
169
+ ```bash
170
+ one connection delete live::gmail::default::abc123
171
+ one connection rm live::gmail::default::abc123 # alias
172
+ ```
173
+
174
+ Shows the connection details and asks for confirmation before deleting. Use `--force` to skip the confirmation prompt.
175
+
176
+ | Option | What it does |
177
+ |--------|-------------|
178
+ | `-f, --force` | Skip confirmation prompt |
179
+
165
180
  ### `one platforms`
166
181
 
167
182
  Browse all 200+ available platforms.
@@ -10,7 +10,6 @@ import { exec } from "child_process";
10
10
  import { promisify } from "util";
11
11
 
12
12
  // src/lib/api.ts
13
- var API_BASE = "https://api.withone.ai/v1";
14
13
  var ApiError = class extends Error {
15
14
  constructor(status, message) {
16
15
  super(message);
@@ -19,14 +18,16 @@ var ApiError = class extends Error {
19
18
  }
20
19
  };
21
20
  var OneApi = class {
22
- constructor(apiKey) {
21
+ constructor(apiKey, apiBase) {
23
22
  this.apiKey = apiKey;
23
+ this.apiBase = apiBase ?? "https://api.withone.ai/v1";
24
24
  }
25
+ apiBase;
25
26
  async request(path3) {
26
27
  return this.requestFull({ path: path3 });
27
28
  }
28
29
  async requestFull(opts) {
29
- let url = `${API_BASE}${opts.path}`;
30
+ let url = `${this.apiBase}${opts.path}`;
30
31
  if (opts.queryParams && Object.keys(opts.queryParams).length > 0) {
31
32
  const params = new URLSearchParams(opts.queryParams);
32
33
  url += `?${params.toString()}`;
@@ -75,6 +76,9 @@ var OneApi = class {
75
76
  } while (page <= totalPages);
76
77
  return allConnections;
77
78
  }
79
+ async deleteConnection(id) {
80
+ await this.requestFull({ path: `/vault/connections/${id}`, method: "DELETE" });
81
+ }
78
82
  async listPlatforms() {
79
83
  const allPlatforms = [];
80
84
  let page = 1;
@@ -129,7 +133,7 @@ var OneApi = class {
129
133
  };
130
134
  }
131
135
  async requestWithMeta(opts) {
132
- let url = `${API_BASE}${opts.path}`;
136
+ let url = `${this.apiBase}${opts.path}`;
133
137
  if (opts.queryParams && Object.keys(opts.queryParams).length > 0) {
134
138
  const params = new URLSearchParams(opts.queryParams);
135
139
  url += `?${params.toString()}`;
@@ -216,7 +220,7 @@ var OneApi = class {
216
220
  };
217
221
  const finalActionPath = args.pathVariables ? replacePathVariables(action.path, args.pathVariables) : action.path;
218
222
  const normalizedPath = finalActionPath.startsWith("/") ? finalActionPath : `/${finalActionPath}`;
219
- const url = `${API_BASE.replace("/v1", "")}/v1/passthrough${normalizedPath}`;
223
+ const url = `${this.apiBase.replace("/v1", "")}/v1/passthrough${normalizedPath}`;
220
224
  const isCustomAction = action.tags?.includes("custom");
221
225
  let requestData = args.data;
222
226
  if (isCustomAction && method?.toLowerCase() !== "get") {
@@ -778,7 +782,7 @@ async function executeSubflowStep(step, context, api, permissions, allowedAction
778
782
  if (flowStack.includes(resolvedKey)) {
779
783
  throw new Error(`Circular flow detected: ${[...flowStack, resolvedKey].join(" \u2192 ")}`);
780
784
  }
781
- const { loadFlow: loadFlow2 } = await import("./flow-runner-SU4JHSZW.js");
785
+ const { loadFlow: loadFlow2 } = await import("./flow-runner-RFLXA3YY.js");
782
786
  const subFlow = loadFlow2(resolvedKey);
783
787
  const subContext = await executeFlow(
784
788
  subFlow,
@@ -4,7 +4,7 @@ import {
4
4
  loadFlow,
5
5
  resolveFlowPath,
6
6
  saveFlow
7
- } from "./chunk-CTL2YHUH.js";
7
+ } from "./chunk-CJBJQPHS.js";
8
8
  export {
9
9
  FlowRunner,
10
10
  listFlows,
package/dist/index.js CHANGED
@@ -11,7 +11,7 @@ import {
11
11
  loadFlow,
12
12
  resolveFlowPath,
13
13
  saveFlow
14
- } from "./chunk-CTL2YHUH.js";
14
+ } from "./chunk-CJBJQPHS.js";
15
15
 
16
16
  // src/index.ts
17
17
  import { createRequire as createRequire2 } from "module";
@@ -101,6 +101,22 @@ function getAccessControlFromAllSources() {
101
101
  function getAccessControl() {
102
102
  return readConfig()?.accessControl ?? {};
103
103
  }
104
+ var DEFAULT_API_BASE = "https://api.withone.ai/v1";
105
+ function getApiBase() {
106
+ const config = readConfig();
107
+ if (config?.apiBase) return `${config.apiBase}/v1`;
108
+ return DEFAULT_API_BASE;
109
+ }
110
+ function updateApiBase(url) {
111
+ const config = readConfig();
112
+ if (!config) return;
113
+ if (url) {
114
+ config.apiBase = url;
115
+ } else {
116
+ delete config.apiBase;
117
+ }
118
+ writeConfig(config);
119
+ }
104
120
  function getCacheTtl() {
105
121
  if (process.env.ONE_CACHE_TTL) {
106
122
  const val = parseInt(process.env.ONE_CACHE_TTL, 10);
@@ -465,6 +481,113 @@ async function configCommand() {
465
481
  p2.outro("No changes made.");
466
482
  return;
467
483
  }
484
+ const currentBase = getApiBase();
485
+ const isCustomBase = !!readConfig()?.apiBase;
486
+ const baseUrlMode = await p2.select({
487
+ message: "API base URL",
488
+ options: [
489
+ { value: "default", label: "Default", hint: "https://api.withone.ai" },
490
+ { value: "custom", label: "Custom", hint: "Use a different API endpoint" }
491
+ ],
492
+ initialValue: isCustomBase ? "custom" : "default"
493
+ });
494
+ if (p2.isCancel(baseUrlMode)) {
495
+ p2.outro("No changes made.");
496
+ return;
497
+ }
498
+ let newApiKey = config.apiKey;
499
+ if (baseUrlMode === "custom") {
500
+ const customUrl = await p2.text({
501
+ message: "Enter API base URL:",
502
+ placeholder: "https://development-api.withone.ai",
503
+ initialValue: isCustomBase ? currentBase.replace(/\/v1$/, "") : "",
504
+ validate: (value) => {
505
+ if (!value) return "URL is required";
506
+ try {
507
+ new URL(value);
508
+ } catch {
509
+ return "Invalid URL";
510
+ }
511
+ return void 0;
512
+ }
513
+ });
514
+ if (p2.isCancel(customUrl)) {
515
+ p2.outro("No changes made.");
516
+ return;
517
+ }
518
+ const normalized = customUrl.replace(/\/+$/, "").replace(/\/v1$/, "");
519
+ const apiKey = await p2.text({
520
+ message: `Enter your API key for ${pc.cyan(normalized)}:`,
521
+ placeholder: "sk_live_...",
522
+ validate: (value) => {
523
+ if (!value) return "API key is required";
524
+ if (!value.startsWith("sk_live_") && !value.startsWith("sk_test_")) {
525
+ return "API key should start with sk_live_ or sk_test_";
526
+ }
527
+ return void 0;
528
+ }
529
+ });
530
+ if (p2.isCancel(apiKey)) {
531
+ p2.outro("No changes made.");
532
+ return;
533
+ }
534
+ const spinner5 = p2.spinner();
535
+ spinner5.start("Validating API key...");
536
+ let isValid = false;
537
+ try {
538
+ const api = new OneApi(apiKey, `${normalized}/v1`);
539
+ isValid = await api.validateApiKey();
540
+ } catch (err) {
541
+ spinner5.stop("Connection failed");
542
+ const msg = err instanceof Error ? err.message : String(err);
543
+ p2.log.error(`Could not reach ${pc.cyan(normalized)}: ${msg}`);
544
+ return;
545
+ }
546
+ if (!isValid) {
547
+ spinner5.stop("Invalid API key");
548
+ p2.log.error(`Invalid API key for ${pc.cyan(normalized)}.`);
549
+ return;
550
+ }
551
+ spinner5.stop("API key validated");
552
+ updateApiBase(normalized);
553
+ newApiKey = apiKey;
554
+ } else if (isCustomBase) {
555
+ const apiKey = await p2.text({
556
+ message: `Enter your API key for ${pc.cyan("https://api.withone.ai")}:`,
557
+ placeholder: "sk_live_...",
558
+ validate: (value) => {
559
+ if (!value) return "API key is required";
560
+ if (!value.startsWith("sk_live_") && !value.startsWith("sk_test_")) {
561
+ return "API key should start with sk_live_ or sk_test_";
562
+ }
563
+ return void 0;
564
+ }
565
+ });
566
+ if (p2.isCancel(apiKey)) {
567
+ p2.outro("No changes made.");
568
+ return;
569
+ }
570
+ const spinner5 = p2.spinner();
571
+ spinner5.start("Validating API key...");
572
+ let isValid = false;
573
+ try {
574
+ const api = new OneApi(apiKey, "https://api.withone.ai/v1");
575
+ isValid = await api.validateApiKey();
576
+ } catch (err) {
577
+ spinner5.stop("Connection failed");
578
+ const msg = err instanceof Error ? err.message : String(err);
579
+ p2.log.error(`Could not reach ${pc.cyan("https://api.withone.ai")}: ${msg}`);
580
+ return;
581
+ }
582
+ if (!isValid) {
583
+ spinner5.stop("Invalid API key");
584
+ p2.log.error(`Invalid API key. Get a valid key at ${getApiKeyUrl()}`);
585
+ return;
586
+ }
587
+ spinner5.stop("API key validated");
588
+ updateApiBase(null);
589
+ newApiKey = apiKey;
590
+ }
468
591
  const settings = {
469
592
  permissions,
470
593
  connectionKeys: connectionKeys ?? ["*"],
@@ -472,30 +595,35 @@ async function configCommand() {
472
595
  knowledgeAgent
473
596
  };
474
597
  updateAccessControl(settings);
598
+ const updatedConfig = readConfig();
599
+ if (updatedConfig && newApiKey !== config.apiKey) {
600
+ updatedConfig.apiKey = newApiKey;
601
+ writeConfig(updatedConfig);
602
+ }
475
603
  const ac = getAccessControl();
476
604
  const statuses = getAgentStatuses();
477
605
  const reinstalled = [];
478
606
  for (const s of statuses) {
479
607
  if (s.globalMcp) {
480
- installMcpConfig(s.agent, config.apiKey, "global", ac);
608
+ installMcpConfig(s.agent, newApiKey, "global", ac);
481
609
  reinstalled.push(`${s.agent.name} (global)`);
482
610
  }
483
611
  if (s.projectMcp) {
484
- installMcpConfig(s.agent, config.apiKey, "project", ac);
612
+ installMcpConfig(s.agent, newApiKey, "project", ac);
485
613
  reinstalled.push(`${s.agent.name} (project)`);
486
614
  }
487
615
  }
488
616
  if (reinstalled.length > 0) {
489
617
  p2.log.success(`Updated MCP configs: ${reinstalled.join(", ")}`);
490
618
  }
491
- p2.outro("Access control updated.");
619
+ p2.outro("Configuration updated.");
492
620
  }
493
621
  async function selectConnections(apiKey) {
494
622
  const spinner5 = p2.spinner();
495
623
  spinner5.start("Fetching connections...");
496
624
  let connections;
497
625
  try {
498
- const api = new OneApi(apiKey);
626
+ const api = new OneApi(apiKey, getApiBase());
499
627
  const rawConnections = await api.listConnections();
500
628
  connections = rawConnections.map((c) => ({ platform: c.platform, key: c.key }));
501
629
  spinner5.stop(`Found ${connections.length} connection(s)`);
@@ -665,7 +793,7 @@ ${pc2.cyan(getApiKeyUrl())}`, "API Key");
665
793
  }
666
794
  const spinner5 = p3.spinner();
667
795
  spinner5.start("Validating API key...");
668
- const api = new OneApi(newKey);
796
+ const api = new OneApi(newKey, getApiBase());
669
797
  const isValid = await api.validateApiKey();
670
798
  if (!isValid) {
671
799
  spinner5.stop("Invalid API key");
@@ -884,7 +1012,7 @@ ${pc2.cyan(getApiKeyUrl())}`, "API Key");
884
1012
  }
885
1013
  const spinner5 = p3.spinner();
886
1014
  spinner5.start("Validating API key...");
887
- const api = new OneApi(apiKey);
1015
+ const api = new OneApi(apiKey, getApiBase());
888
1016
  const isValid = await api.validateApiKey();
889
1017
  if (!isValid) {
890
1018
  spinner5.stop("Invalid API key");
@@ -928,7 +1056,7 @@ var TOP_INTEGRATIONS = [
928
1056
  { value: "notion", label: "Notion", hint: "Access pages, databases, and docs" }
929
1057
  ];
930
1058
  async function promptConnectIntegrations(apiKey) {
931
- const api = new OneApi(apiKey);
1059
+ const api = new OneApi(apiKey, getApiBase());
932
1060
  const connected = [];
933
1061
  try {
934
1062
  const existing = await api.listConnections();
@@ -1090,7 +1218,7 @@ async function connectionAddCommand(platformArg) {
1090
1218
  p4.cancel("Not configured. Run `one init` first.");
1091
1219
  process.exit(1);
1092
1220
  }
1093
- const api = new OneApi(apiKey);
1221
+ const api = new OneApi(apiKey, getApiBase());
1094
1222
  const spinner5 = p4.spinner();
1095
1223
  spinner5.start("Loading platforms...");
1096
1224
  let platforms;
@@ -1194,7 +1322,7 @@ async function connectionListCommand(options) {
1194
1322
  if (!apiKey) {
1195
1323
  error("Not configured. Run `one init` first.");
1196
1324
  }
1197
- const api = new OneApi(apiKey);
1325
+ const api = new OneApi(apiKey, getApiBase());
1198
1326
  const spinner5 = createSpinner();
1199
1327
  spinner5.start("Loading connections...");
1200
1328
  try {
@@ -1266,6 +1394,65 @@ Add one with: ${pc4.cyan("one connection add gmail")}`,
1266
1394
  error(`Error: ${error2 instanceof Error ? error2.message : "Unknown error"}`);
1267
1395
  }
1268
1396
  }
1397
+ async function connectionDeleteCommand(connectionKey, options) {
1398
+ const apiKey = getApiKey();
1399
+ if (!apiKey) {
1400
+ error("Not configured. Run `one init` first.");
1401
+ }
1402
+ const api = new OneApi(apiKey, getApiBase());
1403
+ const spinner5 = createSpinner();
1404
+ spinner5.start("Finding connection...");
1405
+ let allConnections;
1406
+ try {
1407
+ allConnections = await api.listConnections();
1408
+ } catch (error2) {
1409
+ spinner5.stop("Failed to load connections");
1410
+ error(`Error: ${error2 instanceof Error ? error2.message : "Unknown error"}`);
1411
+ return;
1412
+ }
1413
+ const ac = getAccessControlFromAllSources();
1414
+ const allowedKeys = ac.connectionKeys || ["*"];
1415
+ const connections = allowedKeys.includes("*") ? allConnections : allConnections.filter((conn) => allowedKeys.includes(conn.key));
1416
+ const match = connections.find((conn) => conn.key === connectionKey);
1417
+ if (!match) {
1418
+ spinner5.stop("Connection not found");
1419
+ error(`No connection found with key: ${connectionKey}`);
1420
+ return;
1421
+ }
1422
+ const connection2 = match;
1423
+ spinner5.stop(`Found ${connection2.platform} (${connection2.state})`);
1424
+ if (!isAgentMode() && !options?.force) {
1425
+ console.log();
1426
+ console.log(` ${getStatusIndicator(connection2.state)} ${connection2.platform} ${pc4.dim(connection2.key)}`);
1427
+ console.log();
1428
+ const confirmed = await p4.confirm({
1429
+ message: "Are you sure you want to delete this connection?",
1430
+ initialValue: false
1431
+ });
1432
+ if (p4.isCancel(confirmed) || !confirmed) {
1433
+ p4.cancel("Deletion cancelled.");
1434
+ process.exit(0);
1435
+ }
1436
+ }
1437
+ const deleteSpinner = createSpinner();
1438
+ deleteSpinner.start("Deleting connection...");
1439
+ try {
1440
+ await api.deleteConnection(connection2.id);
1441
+ deleteSpinner.stop("Connection deleted");
1442
+ if (isAgentMode()) {
1443
+ json({
1444
+ deleted: true,
1445
+ platform: connection2.platform,
1446
+ key: connection2.key
1447
+ });
1448
+ return;
1449
+ }
1450
+ p4.log.success(`${pc4.green("\u2713")} ${connection2.platform} connection removed.`);
1451
+ } catch (error2) {
1452
+ deleteSpinner.stop("Failed to delete connection");
1453
+ error(`Error: ${error2 instanceof Error ? error2.message : "Unknown error"}`);
1454
+ }
1455
+ }
1269
1456
  function getStatusIndicator(state) {
1270
1457
  switch (state) {
1271
1458
  case "operational":
@@ -1290,7 +1477,7 @@ async function platformsCommand(options) {
1290
1477
  if (isAgentMode()) {
1291
1478
  options.json = true;
1292
1479
  }
1293
- const api = new OneApi(apiKey);
1480
+ const api = new OneApi(apiKey, getApiBase());
1294
1481
  const spinner5 = createSpinner();
1295
1482
  spinner5.start("Loading platforms...");
1296
1483
  try {
@@ -1498,7 +1685,7 @@ function parseJsonArg(value, argName) {
1498
1685
  async function actionsSearchCommand(platform, query, options) {
1499
1686
  intro2(pc6.bgCyan(pc6.black(" One ")));
1500
1687
  const { apiKey, permissions, actionIds, knowledgeAgent } = getConfig();
1501
- const api = new OneApi(apiKey);
1688
+ const api = new OneApi(apiKey, getApiBase());
1502
1689
  const spinner5 = createSpinner();
1503
1690
  spinner5.start(`Searching actions on ${pc6.cyan(platform)} for "${query}"...`);
1504
1691
  try {
@@ -1640,7 +1827,7 @@ async function actionsKnowledgeCommand(platform, actionId, options) {
1640
1827
  }
1641
1828
  intro2(pc6.bgCyan(pc6.black(" One ")));
1642
1829
  const { apiKey, actionIds, connectionKeys } = getConfig();
1643
- const api = new OneApi(apiKey);
1830
+ const api = new OneApi(apiKey, getApiBase());
1644
1831
  if (!isActionAllowed(actionId, actionIds)) {
1645
1832
  error(`Action "${actionId}" is not in the allowed action list.`);
1646
1833
  }
@@ -1747,7 +1934,7 @@ async function actionsExecuteCommand(platform, actionId, connectionKey, options)
1747
1934
  if (!connectionKeys.includes("*") && !connectionKeys.includes(connectionKey)) {
1748
1935
  error(`Connection key "${connectionKey}" is not allowed.`);
1749
1936
  }
1750
- const api = new OneApi(apiKey);
1937
+ const api = new OneApi(apiKey, getApiBase());
1751
1938
  const spinner5 = createSpinner();
1752
1939
  spinner5.start("Loading action details...");
1753
1940
  try {
@@ -2746,7 +2933,7 @@ Execute: ${pc7.cyan(`one flow execute ${flow2.key}`)}`);
2746
2933
  async function flowExecuteCommand(keyOrPath, options) {
2747
2934
  intro2(pc7.bgCyan(pc7.black(" One Workflow ")));
2748
2935
  const { apiKey, permissions, actionIds } = getConfig2();
2749
- const api = new OneApi(apiKey);
2936
+ const api = new OneApi(apiKey, getApiBase());
2750
2937
  const spinner5 = createSpinner();
2751
2938
  spinner5.start(`Loading workflow "${keyOrPath}"...`);
2752
2939
  let flow2;
@@ -2918,7 +3105,7 @@ async function flowResumeCommand(runId) {
2918
3105
  error(`Run "${runId}" is ${state.status} \u2014 can only resume paused or failed runs`);
2919
3106
  }
2920
3107
  const { apiKey, permissions, actionIds } = getConfig2();
2921
- const api = new OneApi(apiKey);
3108
+ const api = new OneApi(apiKey, getApiBase());
2922
3109
  let flow2;
2923
3110
  try {
2924
3111
  flow2 = loadFlow(state.flowKey);
@@ -3236,7 +3423,7 @@ async function relayCreateCommand(options) {
3236
3423
  if (!connectionKeys.includes("*") && !connectionKeys.includes(options.connectionKey)) {
3237
3424
  error(`Connection key "${options.connectionKey}" is not allowed.`);
3238
3425
  }
3239
- const api = new OneApi(apiKey);
3426
+ const api = new OneApi(apiKey, getApiBase());
3240
3427
  const spinner5 = createSpinner();
3241
3428
  spinner5.start("Creating relay endpoint...");
3242
3429
  try {
@@ -3268,7 +3455,7 @@ async function relayCreateCommand(options) {
3268
3455
  }
3269
3456
  async function relayListCommand(options) {
3270
3457
  const { apiKey } = getConfig3();
3271
- const api = new OneApi(apiKey);
3458
+ const api = new OneApi(apiKey, getApiBase());
3272
3459
  const spinner5 = createSpinner();
3273
3460
  spinner5.start("Loading relay endpoints...");
3274
3461
  try {
@@ -3315,7 +3502,7 @@ async function relayListCommand(options) {
3315
3502
  }
3316
3503
  async function relayGetCommand(id) {
3317
3504
  const { apiKey } = getConfig3();
3318
- const api = new OneApi(apiKey);
3505
+ const api = new OneApi(apiKey, getApiBase());
3319
3506
  const spinner5 = createSpinner();
3320
3507
  spinner5.start("Loading relay endpoint...");
3321
3508
  try {
@@ -3346,7 +3533,7 @@ async function relayGetCommand(id) {
3346
3533
  }
3347
3534
  async function relayUpdateCommand(id, options) {
3348
3535
  const { apiKey } = getConfig3();
3349
- const api = new OneApi(apiKey);
3536
+ const api = new OneApi(apiKey, getApiBase());
3350
3537
  const spinner5 = createSpinner();
3351
3538
  spinner5.start("Updating relay endpoint...");
3352
3539
  try {
@@ -3373,7 +3560,7 @@ async function relayUpdateCommand(id, options) {
3373
3560
  }
3374
3561
  async function relayDeleteCommand(id) {
3375
3562
  const { apiKey } = getConfig3();
3376
- const api = new OneApi(apiKey);
3563
+ const api = new OneApi(apiKey, getApiBase());
3377
3564
  const spinner5 = createSpinner();
3378
3565
  spinner5.start("Deleting relay endpoint...");
3379
3566
  try {
@@ -3392,7 +3579,7 @@ async function relayDeleteCommand(id) {
3392
3579
  }
3393
3580
  async function relayActivateCommand(id, options) {
3394
3581
  const { apiKey } = getConfig3();
3395
- const api = new OneApi(apiKey);
3582
+ const api = new OneApi(apiKey, getApiBase());
3396
3583
  const spinner5 = createSpinner();
3397
3584
  spinner5.start("Activating relay endpoint...");
3398
3585
  try {
@@ -3416,7 +3603,7 @@ async function relayActivateCommand(id, options) {
3416
3603
  }
3417
3604
  async function relayEventsCommand(options) {
3418
3605
  const { apiKey } = getConfig3();
3419
- const api = new OneApi(apiKey);
3606
+ const api = new OneApi(apiKey, getApiBase());
3420
3607
  const spinner5 = createSpinner();
3421
3608
  spinner5.start("Loading relay events...");
3422
3609
  try {
@@ -3463,7 +3650,7 @@ async function relayEventsCommand(options) {
3463
3650
  }
3464
3651
  async function relayEventGetCommand(id) {
3465
3652
  const { apiKey } = getConfig3();
3466
- const api = new OneApi(apiKey);
3653
+ const api = new OneApi(apiKey, getApiBase());
3467
3654
  const spinner5 = createSpinner();
3468
3655
  spinner5.start("Loading relay event...");
3469
3656
  try {
@@ -3491,7 +3678,7 @@ async function relayDeliveriesCommand(options) {
3491
3678
  error("Provide either --endpoint-id or --event-id");
3492
3679
  }
3493
3680
  const { apiKey } = getConfig3();
3494
- const api = new OneApi(apiKey);
3681
+ const api = new OneApi(apiKey, getApiBase());
3495
3682
  const spinner5 = createSpinner();
3496
3683
  spinner5.start("Loading deliveries...");
3497
3684
  try {
@@ -3523,7 +3710,7 @@ async function relayDeliveriesCommand(options) {
3523
3710
  }
3524
3711
  async function relayEventTypesCommand(platform) {
3525
3712
  const { apiKey } = getConfig3();
3526
- const api = new OneApi(apiKey);
3713
+ const api = new OneApi(apiKey, getApiBase());
3527
3714
  const spinner5 = createSpinner();
3528
3715
  spinner5.start(`Loading event types for ${pc8.cyan(platform)}...`);
3529
3716
  try {
@@ -3616,7 +3803,7 @@ async function cacheUpdateAllCommand() {
3616
3803
  if (!apiKey) {
3617
3804
  error("Not configured. Run `one init` first.");
3618
3805
  }
3619
- const api = new OneApi(apiKey);
3806
+ const api = new OneApi(apiKey, getApiBase());
3620
3807
  const entries = listCacheEntries();
3621
3808
  if (entries.length === 0) {
3622
3809
  if (isAgentMode()) {
@@ -3698,6 +3885,7 @@ Search for actions, read their docs, and execute them. This is the core workflow
3698
3885
  **Quick start:**
3699
3886
  \`\`\`bash
3700
3887
  one --agent connection list # See connected platforms
3888
+ one --agent connection delete <connection-key> # Remove a connection
3701
3889
  one --agent actions search <platform> "<query>" -t execute # Find an action
3702
3890
  one --agent actions knowledge <platform> <actionId> # Read docs (REQUIRED)
3703
3891
  one --agent actions execute <platform> <actionId> <key> -d '{}' # Execute it
@@ -3781,6 +3969,14 @@ one --agent connection list
3781
3969
 
3782
3970
  Returns platforms, status, connection keys, and tags.
3783
3971
 
3972
+ ### 1b. Delete a Connection
3973
+
3974
+ \`\`\`bash
3975
+ one --agent connection delete <connection-key>
3976
+ \`\`\`
3977
+
3978
+ Removes a connection by its key. In agent mode, returns \`{"deleted": true, "platform": "...", "key": "..."}\`. The connection key comes from \`one connection list\`.
3979
+
3784
3980
  ### 2. Search Actions
3785
3981
 
3786
3982
  \`\`\`bash
@@ -4119,7 +4315,7 @@ async function onboardCommand(step) {
4119
4315
  let connections = [];
4120
4316
  if (currentStep >= 2) {
4121
4317
  try {
4122
- const api = new OneApi(apiKey);
4318
+ const api = new OneApi(apiKey, getApiBase());
4123
4319
  connections = await api.listConnections();
4124
4320
  } catch {
4125
4321
  }
@@ -4452,6 +4648,14 @@ async function updateCommand() {
4452
4648
  error("Update failed \u2014 try running: npm install -g @withone/cli@latest");
4453
4649
  }
4454
4650
  }
4651
+ function isNewerVersion(latest, current) {
4652
+ const parse = (v) => v.split(".").map(Number);
4653
+ const [lMaj, lMin, lPat] = parse(latest);
4654
+ const [cMaj, cMin, cPat] = parse(current);
4655
+ if (lMaj !== cMaj) return lMaj > cMaj;
4656
+ if (lMin !== cMin) return lMin > cMin;
4657
+ return lPat > cPat;
4658
+ }
4455
4659
  function autoUpdate(targetVersion, publishedAt) {
4456
4660
  if (publishedAt) {
4457
4661
  const age = Date.now() - new Date(publishedAt).getTime();
@@ -4474,6 +4678,7 @@ program.name("one").option("--agent", "Machine-readable JSON output (no colors,
4474
4678
  Setup:
4475
4679
  one init Set up API key and install MCP server
4476
4680
  one add <platform> Connect a platform via OAuth (e.g. gmail, slack, shopify)
4681
+ one connection delete <key> Remove a connection (alias: one connection rm)
4477
4682
  one config Configure access control (permissions, scoping)
4478
4683
 
4479
4684
  Workflow (use these in order):
@@ -4535,7 +4740,7 @@ program.hook("postAction", async () => {
4535
4740
  const info = await updateCheckPromise;
4536
4741
  if (!info) return;
4537
4742
  const current = getCurrentVersion();
4538
- if (current === info.version) return;
4743
+ if (!isNewerVersion(info.version, current)) return;
4539
4744
  autoUpdate(info.version, info.publishedAt);
4540
4745
  });
4541
4746
  program.command("init").description("Set up One and install MCP to your AI agents").option("-y, --yes", "Skip confirmations").option("-g, --global", "Install MCP globally (available in all projects)").option("-p, --project", "Install MCP for this project only (creates .mcp.json)").action(async (options) => {
@@ -4551,6 +4756,9 @@ connection.command("add [platform]").alias("a").description("Add a new connectio
4551
4756
  connection.command("list").alias("ls").description("List your connections").option("-s, --search <query>", "Filter connections by platform name").option("-l, --limit <n>", "Max connections to return (agent mode default: 20)").action(async (options) => {
4552
4757
  await connectionListCommand(options);
4553
4758
  });
4759
+ connection.command("delete <connection-key>").alias("rm").description("Delete a connection").option("-f, --force", "Skip confirmation prompt").action(async (connectionKey, options) => {
4760
+ await connectionDeleteCommand(connectionKey, options);
4761
+ });
4554
4762
  program.command("platforms").alias("p").description("List available platforms").option("-c, --category <category>", "Filter by category").option("--json", "Output as JSON").action(async (options) => {
4555
4763
  await platformsCommand(options);
4556
4764
  });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@withone/cli",
3
- "version": "1.18.0",
3
+ "version": "1.20.0",
4
4
  "description": "CLI for managing One",
5
5
  "type": "module",
6
6
  "files": [
@@ -34,6 +34,14 @@ one --agent connection list
34
34
 
35
35
  Returns connected platforms with their connection keys (needed for execution) and platform names in kebab-case (needed for searching).
36
36
 
37
+ ### 1b. Delete a connection
38
+
39
+ ```bash
40
+ one --agent connection delete <connection-key>
41
+ ```
42
+
43
+ Removes a connection. Returns `{"deleted": true, "platform": "...", "key": "..."}` on success. Use the connection key from `one --agent connection list`.
44
+
37
45
  ### 2. Search for the right action
38
46
 
39
47
  ```bash
@@ -126,3 +134,11 @@ If the user needs a platform that isn't connected yet, tell them to run:
126
134
  one add <platform>
127
135
  ```
128
136
  This is interactive and opens the browser for OAuth. After connecting, the platform will appear in `one --agent connection list`.
137
+
138
+ ## Removing Connections
139
+
140
+ To delete a connection that is no longer needed:
141
+ ```bash
142
+ one --agent connection delete <connection-key>
143
+ ```
144
+ The connection key comes from `one --agent connection list`. Returns `{"deleted": true, "platform": "...", "key": "..."}` on success.