openpond-code 0.1.4 → 0.2.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.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,17 @@
1
1
  # openpond-code
2
2
 
3
+ ## 0.2.1
4
+
5
+ ### Patch Changes
6
+
7
+ - d1479e9: updated base url
8
+
9
+ ## 0.2.0
10
+
11
+ ### Minor Changes
12
+
13
+ - b6e5f07: added multi accounts
14
+
3
15
  ## 0.1.4
4
16
 
5
17
  ### Patch Changes
package/README.md CHANGED
@@ -20,11 +20,15 @@ Login (stores key in `~/.openpond/config.json`):
20
20
  openpond login
21
21
  # or (non-interactive)
22
22
  openpond login --api-key opk_...
23
+ # or save under an account profile
24
+ openpond login --account production --api-key opk_prod_...
25
+ openpond login --account secondary --api-key opk_secondary_...
23
26
  ```
24
27
 
25
28
  Commands:
26
29
 
27
30
  ```bash
31
+ openpond --account production apps list
28
32
  openpond tool list <handle>/<repo>
29
33
  openpond tool run <handle>/<repo> <tool> --body '{"foo":"bar"}'
30
34
  openpond deploy watch <handle>/<repo> --branch main
@@ -45,6 +49,13 @@ openpond apps positions tx --method GET --params '{"status":"open"}'
45
49
  openpond opentool init --dir .
46
50
  ```
47
51
 
52
+ Global account selection:
53
+
54
+ - `--account <name>` (alias `--profile <name>`) selects which stored account/API key to use.
55
+ - `--base-url <url>` (alias `--baseurl`) selects the account entry matching that base URL when duplicate handles exist (for example, staging vs production).
56
+ - `OPENPOND_ACCOUNT=<name>` sets the default selected account for a shell/session.
57
+ - If omitted, CLI uses the last active stored account.
58
+
48
59
  Command reference:
49
60
 
50
61
  - `openpond login`: prompt for API key and save to `~/.openpond/config.json`.
@@ -99,8 +110,8 @@ await client.apps.agentCreate(
99
110
  );
100
111
  ```
101
112
 
102
- You can override hosts with `baseUrl`, `apiUrl`, and `toolUrl` in `createClient`, or
103
- via `OPENPOND_BASE_URL`, `OPENPOND_API_URL`, and `OPENPOND_TOOL_URL`.
113
+ You can override hosts with `apiUrl` and `toolUrl` in `createClient`, or
114
+ via `OPENPOND_API_URL` and `OPENPOND_TOOL_URL`.
104
115
 
105
116
  Examples live in `examples`.
106
117
 
package/dist/cli.js CHANGED
@@ -296,9 +296,15 @@ function resolveWorkerBaseUrl(baseUrl) {
296
296
  if (host === "apps.openpond.live") {
297
297
  return null;
298
298
  }
299
- if (host === "api.openpond.ai" || host === "openpond.live" || host === "www.openpond.live") {
299
+ if (host === "apps.staging.openpond.live") {
300
+ return null;
301
+ }
302
+ if (host === "api.openpond.ai" || host === "openpond.ai" || host === "openpond.live" || host === "www.openpond.live") {
300
303
  return "https://apps.openpond.live";
301
304
  }
305
+ if (host === "api.staging-api.openpond.ai" || host === "staging.openpond.ai") {
306
+ return "https://apps.staging.openpond.live";
307
+ }
302
308
  return null;
303
309
  })();
304
310
  if (mappedHost) {
@@ -434,6 +440,15 @@ import os2 from "node:os";
434
440
  import path2 from "node:path";
435
441
  var GLOBAL_DIRNAME = ".openpond";
436
442
  var GLOBAL_CONFIG_FILENAME = "config.json";
443
+ var DEFAULT_ACCOUNT_HANDLE = "default";
444
+ var ACCOUNT_SCOPED_KEYS = [
445
+ "apiKey",
446
+ "baseUrl",
447
+ "token",
448
+ "deviceCode",
449
+ "appId",
450
+ "conversationId"
451
+ ];
437
452
  function getGlobalConfigPath() {
438
453
  return path2.join(os2.homedir(), GLOBAL_DIRNAME, GLOBAL_CONFIG_FILENAME);
439
454
  }
@@ -445,29 +460,282 @@ async function loadConfigFile(filePath) {
445
460
  return {};
446
461
  }
447
462
  }
448
- async function loadGlobalConfig() {
449
- return loadConfigFile(getGlobalConfigPath());
463
+ function hasOwn(value, key) {
464
+ return Object.prototype.hasOwnProperty.call(value, key);
450
465
  }
451
- async function loadConfig() {
452
- return loadGlobalConfig();
466
+ function normalizeHandle(value) {
467
+ if (typeof value !== "string")
468
+ return null;
469
+ const trimmed = value.trim();
470
+ return trimmed.length > 0 ? trimmed : null;
453
471
  }
454
- async function saveGlobalConfig(next) {
455
- const filePath = getGlobalConfigPath();
456
- await fs2.mkdir(path2.dirname(filePath), { recursive: true });
457
- const current = await loadGlobalConfig();
458
- const merged = { ...current };
459
- for (const [key, value] of Object.entries(next)) {
460
- if (value === undefined)
472
+ function normalizeBaseUrl(value) {
473
+ if (typeof value !== "string")
474
+ return null;
475
+ const trimmed = value.trim();
476
+ if (!trimmed)
477
+ return null;
478
+ return trimmed.replace(/\/$/, "");
479
+ }
480
+ function handleEquals(left, right) {
481
+ return left.trim().toLowerCase() === right.trim().toLowerCase();
482
+ }
483
+ function findAccountIndex(accounts, handle, baseUrl) {
484
+ const normalizedBaseUrl = normalizeBaseUrl(baseUrl);
485
+ return accounts.findIndex((candidate) => {
486
+ if (!handleEquals(candidate.handle, handle))
487
+ return false;
488
+ if (!normalizedBaseUrl)
489
+ return true;
490
+ return normalizeBaseUrl(candidate.baseUrl) === normalizedBaseUrl;
491
+ });
492
+ }
493
+ function sanitizeSession(value) {
494
+ if (!value || typeof value !== "object" || Array.isArray(value)) {
495
+ return;
496
+ }
497
+ const input = value;
498
+ const out = {};
499
+ if (typeof input.token === "string")
500
+ out.token = input.token;
501
+ if (typeof input.deviceCode === "string" || input.deviceCode === null) {
502
+ out.deviceCode = input.deviceCode;
503
+ }
504
+ if (typeof input.appId === "string" || input.appId === null) {
505
+ out.appId = input.appId;
506
+ }
507
+ if (typeof input.conversationId === "string" || input.conversationId === null) {
508
+ out.conversationId = input.conversationId;
509
+ }
510
+ return Object.keys(out).length > 0 ? out : undefined;
511
+ }
512
+ function sanitizeAccount(value) {
513
+ if (!value || typeof value !== "object" || Array.isArray(value))
514
+ return null;
515
+ const input = value;
516
+ const handle = normalizeHandle(typeof input.handle === "string" ? input.handle : undefined);
517
+ if (!handle)
518
+ return null;
519
+ const out = { handle };
520
+ if (typeof input.apiKey === "string")
521
+ out.apiKey = input.apiKey;
522
+ if (typeof input.baseUrl === "string")
523
+ out.baseUrl = input.baseUrl;
524
+ if (typeof input.environment === "string")
525
+ out.environment = input.environment;
526
+ const session = sanitizeSession(input.session);
527
+ if (session)
528
+ out.session = session;
529
+ return out;
530
+ }
531
+ function extractLegacySession(raw) {
532
+ const session = {};
533
+ if (typeof raw.token === "string")
534
+ session.token = raw.token;
535
+ if (typeof raw.deviceCode === "string" || raw.deviceCode === null) {
536
+ session.deviceCode = raw.deviceCode;
537
+ }
538
+ if (typeof raw.appId === "string" || raw.appId === null) {
539
+ session.appId = raw.appId;
540
+ }
541
+ if (typeof raw.conversationId === "string" || raw.conversationId === null) {
542
+ session.conversationId = raw.conversationId;
543
+ }
544
+ return Object.keys(session).length > 0 ? session : undefined;
545
+ }
546
+ function extractLegacyAccount(raw, handle) {
547
+ const out = { handle };
548
+ if (typeof raw.apiKey === "string")
549
+ out.apiKey = raw.apiKey;
550
+ if (typeof raw.baseUrl === "string")
551
+ out.baseUrl = raw.baseUrl;
552
+ const session = extractLegacySession(raw);
553
+ if (session)
554
+ out.session = session;
555
+ return out;
556
+ }
557
+ function normalizeGlobalConfig(raw) {
558
+ const normalized = {};
559
+ if (typeof raw.lspEnabled === "boolean")
560
+ normalized.lspEnabled = raw.lspEnabled;
561
+ if (raw.executionMode === "local" || raw.executionMode === "hosted") {
562
+ normalized.executionMode = raw.executionMode;
563
+ }
564
+ if (raw.mode === "general" || raw.mode === "builder") {
565
+ normalized.mode = raw.mode;
566
+ }
567
+ const accounts = [];
568
+ const sourceAccounts = Array.isArray(raw.accounts) ? raw.accounts : [];
569
+ for (const candidate of sourceAccounts) {
570
+ const account = sanitizeAccount(candidate);
571
+ if (!account)
461
572
  continue;
462
- if (value === null) {
463
- delete merged[key];
464
- } else {
465
- merged[key] = value;
573
+ if (findAccountIndex(accounts, account.handle, account.baseUrl) !== -1)
574
+ continue;
575
+ accounts.push(account);
576
+ }
577
+ if (accounts.length === 0) {
578
+ const legacyHandle = normalizeHandle(raw.activeHandle) || DEFAULT_ACCOUNT_HANDLE;
579
+ accounts.push(extractLegacyAccount(raw, legacyHandle));
580
+ }
581
+ const requested = normalizeHandle(raw.activeHandle);
582
+ const resolvedHandle = requested && findAccountIndex(accounts, requested) !== -1 ? accounts[findAccountIndex(accounts, requested)].handle : accounts[0].handle;
583
+ normalized.accounts = accounts;
584
+ normalized.activeHandle = resolvedHandle;
585
+ return normalized;
586
+ }
587
+ function resolveRequestedHandle(global, explicitAccount) {
588
+ const accounts = global.accounts ?? [];
589
+ const requested = normalizeHandle(explicitAccount) || normalizeHandle(process.env.OPENPOND_ACCOUNT) || normalizeHandle(global.activeHandle) || accounts[0]?.handle || DEFAULT_ACCOUNT_HANDLE;
590
+ const idx = findAccountIndex(accounts, requested);
591
+ return idx === -1 ? requested : accounts[idx].handle;
592
+ }
593
+ function ensureAccount(accounts, handle, baseUrl) {
594
+ const normalizedBaseUrl = normalizeBaseUrl(baseUrl);
595
+ const idx = findAccountIndex(accounts, handle, normalizedBaseUrl);
596
+ if (idx !== -1) {
597
+ return accounts[idx];
598
+ }
599
+ if (!normalizedBaseUrl) {
600
+ const firstByHandle = findAccountIndex(accounts, handle);
601
+ if (firstByHandle !== -1) {
602
+ return accounts[firstByHandle];
603
+ }
604
+ }
605
+ const next = { handle };
606
+ if (normalizedBaseUrl) {
607
+ next.baseUrl = normalizedBaseUrl;
608
+ }
609
+ accounts.push(next);
610
+ return next;
611
+ }
612
+ function cleanupAccount(account) {
613
+ if (account.session && Object.keys(account.session).length === 0) {
614
+ delete account.session;
615
+ }
616
+ }
617
+ function applyScopedKey(account, key, value, options) {
618
+ const shouldDelete = value === null || value === undefined && options.undefinedDeletes;
619
+ switch (key) {
620
+ case "apiKey": {
621
+ if (shouldDelete) {
622
+ delete account.apiKey;
623
+ return;
624
+ }
625
+ if (typeof value === "string") {
626
+ account.apiKey = value;
627
+ }
628
+ return;
629
+ }
630
+ case "baseUrl": {
631
+ if (shouldDelete) {
632
+ delete account.baseUrl;
633
+ return;
634
+ }
635
+ if (typeof value === "string") {
636
+ account.baseUrl = value;
637
+ }
638
+ return;
639
+ }
640
+ case "token":
641
+ case "deviceCode":
642
+ case "appId":
643
+ case "conversationId": {
644
+ if (!account.session)
645
+ account.session = {};
646
+ if (shouldDelete) {
647
+ delete account.session[key];
648
+ cleanupAccount(account);
649
+ return;
650
+ }
651
+ if (typeof value === "string" || value === null) {
652
+ account.session[key] = value;
653
+ }
654
+ cleanupAccount(account);
655
+ return;
466
656
  }
467
657
  }
468
- const payload = JSON.stringify(merged, null, 2);
658
+ }
659
+ function applyAccountPatch(global, source, options) {
660
+ const hasScopedPatch = ACCOUNT_SCOPED_KEYS.some((key) => hasOwn(source, key));
661
+ if (!hasScopedPatch)
662
+ return false;
663
+ const accounts = global.accounts ?? [];
664
+ const handle = resolveRequestedHandle(global, source.activeHandle);
665
+ const requestedBaseUrl = normalizeBaseUrl(hasOwn(source, "baseUrl") ? source.baseUrl ?? null : process.env.OPENPOND_BASE_URL);
666
+ const account = ensureAccount(accounts, handle, requestedBaseUrl);
667
+ for (const key of ACCOUNT_SCOPED_KEYS) {
668
+ if (!hasOwn(source, key))
669
+ continue;
670
+ applyScopedKey(account, key, source[key], options);
671
+ }
672
+ global.accounts = accounts;
673
+ global.activeHandle = handle;
674
+ return true;
675
+ }
676
+ function applyTopLevelPatch(global, source) {
677
+ if (hasOwn(source, "lspEnabled")) {
678
+ if (typeof source.lspEnabled === "boolean") {
679
+ global.lspEnabled = source.lspEnabled;
680
+ } else if (source.lspEnabled === null) {
681
+ delete global.lspEnabled;
682
+ }
683
+ }
684
+ if (hasOwn(source, "executionMode")) {
685
+ if (source.executionMode === "local" || source.executionMode === "hosted") {
686
+ global.executionMode = source.executionMode;
687
+ } else if (source.executionMode === null) {
688
+ delete global.executionMode;
689
+ }
690
+ }
691
+ if (hasOwn(source, "mode")) {
692
+ if (source.mode === "general" || source.mode === "builder") {
693
+ global.mode = source.mode;
694
+ } else if (source.mode === null) {
695
+ delete global.mode;
696
+ }
697
+ }
698
+ if (typeof source.activeHandle === "string" && source.activeHandle.trim().length > 0) {
699
+ const requested = resolveRequestedHandle(global, source.activeHandle);
700
+ global.activeHandle = requested;
701
+ }
702
+ }
703
+ async function writeGlobalConfig(next) {
704
+ const filePath = getGlobalConfigPath();
705
+ await fs2.mkdir(path2.dirname(filePath), { recursive: true });
706
+ const payload = JSON.stringify(next, null, 2);
469
707
  await fs2.writeFile(filePath, payload, "utf-8");
470
708
  }
709
+ async function loadGlobalConfig() {
710
+ const raw = await loadConfigFile(getGlobalConfigPath());
711
+ return normalizeGlobalConfig(raw);
712
+ }
713
+ async function loadConfig(options = {}) {
714
+ const global = await loadGlobalConfig();
715
+ const accounts = global.accounts ?? [];
716
+ const requested = resolveRequestedHandle(global, options.account);
717
+ const requestedBaseUrl = normalizeBaseUrl(options.baseUrl ?? process.env.OPENPOND_BASE_URL);
718
+ const idxWithBase = findAccountIndex(accounts, requested, requestedBaseUrl);
719
+ const idx = idxWithBase !== -1 ? idxWithBase : findAccountIndex(accounts, requested);
720
+ const account = idx === -1 ? null : accounts[idx];
721
+ const session = account?.session;
722
+ return {
723
+ ...global,
724
+ activeHandle: requested,
725
+ apiKey: account?.apiKey,
726
+ baseUrl: account?.baseUrl,
727
+ token: session?.token,
728
+ deviceCode: session?.deviceCode,
729
+ appId: session?.appId,
730
+ conversationId: session?.conversationId
731
+ };
732
+ }
733
+ async function saveGlobalConfig(next) {
734
+ const current = await loadGlobalConfig();
735
+ applyTopLevelPatch(current, next);
736
+ applyAccountPatch(current, next, { undefinedDeletes: false });
737
+ await writeGlobalConfig(current);
738
+ }
471
739
 
472
740
  // src/stream.ts
473
741
  function normalizeDataFrames(raw) {
@@ -644,7 +912,7 @@ function formatStreamItem(item) {
644
912
  // src/cli-package.ts
645
913
  function parseArgs(argv) {
646
914
  const args = [...argv];
647
- const command = args.shift() || "";
915
+ let command = "";
648
916
  const options = {};
649
917
  const rest = [];
650
918
  while (args.length > 0) {
@@ -655,19 +923,62 @@ function parseArgs(argv) {
655
923
  const value = args[0] && !args[0].startsWith("--") ? args.shift() : "true";
656
924
  options[key] = value;
657
925
  } else {
658
- rest.push(next);
926
+ if (!command) {
927
+ command = next;
928
+ } else {
929
+ rest.push(next);
930
+ }
659
931
  }
660
932
  }
661
933
  return { command, options, rest };
662
934
  }
935
+ function resolveAccountOption(options) {
936
+ const raw = typeof options.account === "string" ? options.account : typeof options.profile === "string" ? options.profile : null;
937
+ if (!raw)
938
+ return null;
939
+ const trimmed = raw.trim();
940
+ if (!trimmed || trimmed === "true") {
941
+ throw new Error("account must be a non-empty value");
942
+ }
943
+ return trimmed;
944
+ }
945
+ function resolveBaseUrlOption(options) {
946
+ const raw = typeof options.baseUrl === "string" ? options.baseUrl : typeof options.baseurl === "string" ? options.baseurl : null;
947
+ if (!raw)
948
+ return null;
949
+ const trimmed = raw.trim();
950
+ if (!trimmed || trimmed === "true") {
951
+ throw new Error("baseurl must be a non-empty value");
952
+ }
953
+ return trimmed.replace(/\/$/, "");
954
+ }
663
955
  function resolveBaseUrl(config) {
664
956
  const envBase = process.env.OPENPOND_BASE_URL;
665
957
  const base = envBase || config.baseUrl || "https://openpond.ai";
666
958
  return base.replace(/\/$/, "");
667
959
  }
668
- function resolvePublicApiBaseUrl() {
960
+ function mapUiBaseToApiBase(baseUrl) {
961
+ if (!baseUrl)
962
+ return null;
963
+ const trimmed = baseUrl.replace(/\/$/, "");
964
+ try {
965
+ const url = new URL(trimmed);
966
+ const host = url.hostname.toLowerCase();
967
+ if (host === "staging.openpond.ai") {
968
+ return "https://api.staging-api.openpond.ai";
969
+ }
970
+ if (host === "openpond.ai" || host === "openpond.live" || host === "www.openpond.live") {
971
+ return "https://api.openpond.ai";
972
+ }
973
+ } catch {
974
+ return null;
975
+ }
976
+ return null;
977
+ }
978
+ function resolvePublicApiBaseUrl(config) {
669
979
  const envBase = process.env.OPENPOND_API_URL;
670
- const base = envBase || "https://api.openpond.ai";
980
+ const mapped = mapUiBaseToApiBase(process.env.OPENPOND_BASE_URL || config?.baseUrl);
981
+ const base = envBase || mapped || "https://api.openpond.ai";
671
982
  return base.replace(/\/$/, "");
672
983
  }
673
984
  function normalizeTemplateRepoUrl(input2, baseUrl) {
@@ -759,7 +1070,7 @@ async function ensureApiKey(config, baseUrl) {
759
1070
  if (existing)
760
1071
  return existing;
761
1072
  const apiKey = await promptForApiKey();
762
- await saveGlobalConfig({ apiKey, baseUrl });
1073
+ await saveGlobalConfig({ apiKey, baseUrl, activeHandle: config.activeHandle });
763
1074
  console.log("saved api key to ~/.openpond/config.json");
764
1075
  return apiKey;
765
1076
  }
@@ -945,7 +1256,7 @@ async function pollDeploymentLogs(params) {
945
1256
  async function runTemplateStatus(_options, target) {
946
1257
  const config = await loadConfig();
947
1258
  const uiBase = resolveBaseUrl(config);
948
- const apiBase = resolvePublicApiBaseUrl();
1259
+ const apiBase = resolvePublicApiBaseUrl(config);
949
1260
  const apiKey = await ensureApiKey(config, uiBase);
950
1261
  const { app } = await resolveAppTarget(apiBase, apiKey, target);
951
1262
  const status = await getTemplateStatus(apiBase, apiKey, app.id);
@@ -954,7 +1265,7 @@ async function runTemplateStatus(_options, target) {
954
1265
  async function runTemplateBranches(_options, target) {
955
1266
  const config = await loadConfig();
956
1267
  const uiBase = resolveBaseUrl(config);
957
- const apiBase = resolvePublicApiBaseUrl();
1268
+ const apiBase = resolvePublicApiBaseUrl(config);
958
1269
  const apiKey = await ensureApiKey(config, uiBase);
959
1270
  const { app } = await resolveAppTarget(apiBase, apiKey, target);
960
1271
  const branches = await listTemplateBranches(apiBase, apiKey, app.id);
@@ -963,7 +1274,7 @@ async function runTemplateBranches(_options, target) {
963
1274
  async function runTemplateUpdate(options, target) {
964
1275
  const config = await loadConfig();
965
1276
  const uiBase = resolveBaseUrl(config);
966
- const apiBase = resolvePublicApiBaseUrl();
1277
+ const apiBase = resolvePublicApiBaseUrl(config);
967
1278
  const apiKey = await ensureApiKey(config, uiBase);
968
1279
  const { app } = await resolveAppTarget(apiBase, apiKey, target);
969
1280
  const envRaw = typeof options.env === "string" ? options.env : typeof options.environment === "string" ? options.environment : undefined;
@@ -1001,8 +1312,12 @@ function printHelp() {
1001
1312
  console.log(" openpond apps positions tx [--method <GET|POST>] [--body <json>] [--params <json>]");
1002
1313
  console.log(" openpond opentool <init|validate|build> [args]");
1003
1314
  console.log("");
1315
+ console.log("Global options:");
1316
+ console.log(" --account <name> (alias: --profile <name>)");
1317
+ console.log(" --base-url <url> (alias: --baseurl)");
1318
+ console.log("");
1004
1319
  console.log("Env:");
1005
- console.log(" OPENPOND_API_KEY, OPENPOND_BASE_URL, OPENPOND_API_URL, OPENPOND_TOOL_URL");
1320
+ console.log(" OPENPOND_API_KEY, OPENPOND_ACCOUNT, OPENPOND_BASE_URL, OPENPOND_API_URL, OPENPOND_TOOL_URL");
1006
1321
  }
1007
1322
  async function runLogin(options) {
1008
1323
  const config = await loadConfig();
@@ -1015,13 +1330,13 @@ async function runLogin(options) {
1015
1330
  if (!apiKey.startsWith("opk_")) {
1016
1331
  console.log("warning: API keys usually start with opk_.");
1017
1332
  }
1018
- await saveGlobalConfig({ apiKey, baseUrl });
1333
+ await saveGlobalConfig({ apiKey, baseUrl, activeHandle: config.activeHandle });
1019
1334
  console.log("saved api key to ~/.openpond/config.json");
1020
1335
  }
1021
1336
  async function runToolList(options, target) {
1022
1337
  const config = await loadConfig();
1023
1338
  const uiBase = resolveBaseUrl(config);
1024
- const apiBase = resolvePublicApiBaseUrl();
1339
+ const apiBase = resolvePublicApiBaseUrl(config);
1025
1340
  const apiKey = await ensureApiKey(config, uiBase);
1026
1341
  const { app } = await resolveAppTarget(apiBase, apiKey, target);
1027
1342
  const branch = typeof options.branch === "string" ? String(options.branch) : undefined;
@@ -1048,7 +1363,7 @@ async function runToolList(options, target) {
1048
1363
  async function runToolRun(options, target, toolName) {
1049
1364
  const config = await loadConfig();
1050
1365
  const uiBase = resolveBaseUrl(config);
1051
- const apiBase = resolvePublicApiBaseUrl();
1366
+ const apiBase = resolvePublicApiBaseUrl(config);
1052
1367
  const apiKey = await ensureApiKey(config, uiBase);
1053
1368
  const { app } = await resolveAppTarget(apiBase, apiKey, target);
1054
1369
  const branch = typeof options.branch === "string" ? String(options.branch) : undefined;
@@ -1082,7 +1397,7 @@ async function runToolRun(options, target, toolName) {
1082
1397
  async function runDeployWatch(options, target) {
1083
1398
  const config = await loadConfig();
1084
1399
  const uiBase = resolveBaseUrl(config);
1085
- const apiBase = resolvePublicApiBaseUrl();
1400
+ const apiBase = resolvePublicApiBaseUrl(config);
1086
1401
  const apiKey = await ensureApiKey(config, uiBase);
1087
1402
  const { app, handle, repo } = await resolveAppTarget(apiBase, apiKey, target);
1088
1403
  const branch = typeof options.branch === "string" ? String(options.branch) : undefined;
@@ -1112,7 +1427,7 @@ async function runRepoCreate(options, nameParts) {
1112
1427
  const config = await loadConfig();
1113
1428
  const uiBase = resolveBaseUrl(config);
1114
1429
  const apiKey = await ensureApiKey(config, uiBase);
1115
- const apiBase = resolvePublicApiBaseUrl();
1430
+ const apiBase = resolvePublicApiBaseUrl(config);
1116
1431
  const templateInput = typeof options.template === "string" ? options.template.trim() : "";
1117
1432
  if (templateInput && (options.empty === "true" || options.opentool === "true")) {
1118
1433
  throw new Error("choose one: --template or --empty/--opentool");
@@ -1325,14 +1640,14 @@ async function runAppsTools() {
1325
1640
  const config = await loadConfig();
1326
1641
  const uiBase = resolveBaseUrl(config);
1327
1642
  const apiKey = await ensureApiKey(config, uiBase);
1328
- const apiBase = resolvePublicApiBaseUrl();
1643
+ const apiBase = resolvePublicApiBaseUrl(config);
1329
1644
  const tools = await fetchToolsWithCache({ apiBase, apiKey });
1330
1645
  console.log(JSON.stringify(tools, null, 2));
1331
1646
  }
1332
1647
  async function runAppsList(options) {
1333
1648
  const config = await loadConfig();
1334
1649
  const uiBase = resolveBaseUrl(config);
1335
- const apiBase = resolvePublicApiBaseUrl();
1650
+ const apiBase = resolvePublicApiBaseUrl(config);
1336
1651
  const apiKey = await ensureApiKey(config, uiBase);
1337
1652
  const handle = typeof options.handle === "string" ? String(options.handle) : undefined;
1338
1653
  const normalizedHandle = handle ? normalizeRepoName(handle) : null;
@@ -1362,7 +1677,7 @@ async function runAppsPerformance(options) {
1362
1677
  const config = await loadConfig();
1363
1678
  const uiBase = resolveBaseUrl(config);
1364
1679
  const apiKey = await ensureApiKey(config, uiBase);
1365
- const apiBase = resolvePublicApiBaseUrl();
1680
+ const apiBase = resolvePublicApiBaseUrl(config);
1366
1681
  const appId = typeof options.appId === "string" ? String(options.appId) : undefined;
1367
1682
  const performance = await getUserPerformance(apiBase, apiKey, { appId });
1368
1683
  console.log(JSON.stringify(performance, null, 2));
@@ -1370,7 +1685,7 @@ async function runAppsPerformance(options) {
1370
1685
  async function runAppsSummary(_options, target) {
1371
1686
  const config = await loadConfig();
1372
1687
  const uiBase = resolveBaseUrl(config);
1373
- const apiBase = resolvePublicApiBaseUrl();
1688
+ const apiBase = resolvePublicApiBaseUrl(config);
1374
1689
  const apiKey = await ensureApiKey(config, uiBase);
1375
1690
  const { app } = await resolveAppTarget(apiBase, apiKey, target);
1376
1691
  const summary = await getAppRuntimeSummary(apiBase, apiKey, app.id);
@@ -1383,7 +1698,7 @@ async function runAppsAssistant(options, mode, target, contentParts) {
1383
1698
  }
1384
1699
  const config = await loadConfig();
1385
1700
  const uiBase = resolveBaseUrl(config);
1386
- const apiBase = resolvePublicApiBaseUrl();
1701
+ const apiBase = resolvePublicApiBaseUrl(config);
1387
1702
  const apiKey = await ensureApiKey(config, uiBase);
1388
1703
  const { app } = await resolveAppTarget(apiBase, apiKey, target);
1389
1704
  const result = await runAssistantMode(apiBase, apiKey, {
@@ -1397,7 +1712,7 @@ async function runAppsAgentCreate(options, contentParts) {
1397
1712
  const config = await loadConfig();
1398
1713
  const uiBase = resolveBaseUrl(config);
1399
1714
  const apiKey = await ensureApiKey(config, uiBase);
1400
- const apiBase = resolvePublicApiBaseUrl();
1715
+ const apiBase = resolvePublicApiBaseUrl(config);
1401
1716
  const prompt = (typeof options.prompt === "string" ? options.prompt : null) || contentParts.join(" ");
1402
1717
  if (!prompt.trim()) {
1403
1718
  throw new Error("usage: apps agent create --prompt <text>");
@@ -1481,7 +1796,7 @@ async function runAppsToolsExecute(options, appId, deploymentId, toolName) {
1481
1796
  const config = await loadConfig();
1482
1797
  const uiBase = resolveBaseUrl(config);
1483
1798
  const apiKey = await ensureApiKey(config, uiBase);
1484
- const apiBase = resolvePublicApiBaseUrl();
1799
+ const apiBase = resolvePublicApiBaseUrl(config);
1485
1800
  const methodRaw = typeof options.method === "string" ? String(options.method).toUpperCase() : undefined;
1486
1801
  const method = methodRaw && ["GET", "POST", "PUT", "DELETE"].includes(methodRaw) ? methodRaw : undefined;
1487
1802
  if (methodRaw && !method) {
@@ -1529,7 +1844,7 @@ async function runAppsEnvSet(options, target) {
1529
1844
  }
1530
1845
  const config = await loadConfig();
1531
1846
  const uiBase = resolveBaseUrl(config);
1532
- const apiBase = resolvePublicApiBaseUrl();
1847
+ const apiBase = resolvePublicApiBaseUrl(config);
1533
1848
  const apiKey = await ensureApiKey(config, uiBase);
1534
1849
  const { app } = await resolveAppTarget(apiBase, apiKey, target);
1535
1850
  const result = await updateAppEnvironment(apiBase, apiKey, app.id, { envVars });
@@ -1538,7 +1853,7 @@ async function runAppsEnvSet(options, target) {
1538
1853
  async function runAppsEnvGet(_options, target) {
1539
1854
  const config = await loadConfig();
1540
1855
  const uiBase = resolveBaseUrl(config);
1541
- const apiBase = resolvePublicApiBaseUrl();
1856
+ const apiBase = resolvePublicApiBaseUrl(config);
1542
1857
  const apiKey = await ensureApiKey(config, uiBase);
1543
1858
  const { app } = await resolveAppTarget(apiBase, apiKey, target);
1544
1859
  const result = await getAppEnvironment(apiBase, apiKey, app.id);
@@ -1547,7 +1862,7 @@ async function runAppsEnvGet(_options, target) {
1547
1862
  async function runAppsDeploy(options, target) {
1548
1863
  const config = await loadConfig();
1549
1864
  const uiBase = resolveBaseUrl(config);
1550
- const apiBase = resolvePublicApiBaseUrl();
1865
+ const apiBase = resolvePublicApiBaseUrl(config);
1551
1866
  const apiKey = await ensureApiKey(config, uiBase);
1552
1867
  const { app, handle, repo } = await resolveAppTarget(apiBase, apiKey, target);
1553
1868
  const envRaw = typeof options.env === "string" ? options.env : typeof options.environment === "string" ? options.environment : undefined;
@@ -1570,7 +1885,7 @@ async function runAppsPositionsTx(options) {
1570
1885
  const config = await loadConfig();
1571
1886
  const uiBase = resolveBaseUrl(config);
1572
1887
  const apiKey = await ensureApiKey(config, uiBase);
1573
- const apiBase = resolvePublicApiBaseUrl();
1888
+ const apiBase = resolvePublicApiBaseUrl(config);
1574
1889
  const methodRaw = typeof options.method === "string" ? String(options.method).toUpperCase() : "POST";
1575
1890
  const method = methodRaw === "GET" ? "GET" : "POST";
1576
1891
  if (methodRaw !== "GET" && methodRaw !== "POST") {
@@ -1638,7 +1953,7 @@ async function runAppsStoreEvents(options) {
1638
1953
  const config = await loadConfig();
1639
1954
  const uiBase = resolveBaseUrl(config);
1640
1955
  const apiKey = await ensureApiKey(config, uiBase);
1641
- const apiBase = resolvePublicApiBaseUrl();
1956
+ const apiBase = resolvePublicApiBaseUrl(config);
1642
1957
  const query = resolveStoreEventsParams(options);
1643
1958
  const result = await submitPositionsTx(apiBase, apiKey, {
1644
1959
  method: "GET",
@@ -1650,7 +1965,7 @@ async function runAppsTradeFacts(options) {
1650
1965
  const config = await loadConfig();
1651
1966
  const uiBase = resolveBaseUrl(config);
1652
1967
  const apiKey = await ensureApiKey(config, uiBase);
1653
- const apiBase = resolvePublicApiBaseUrl();
1968
+ const apiBase = resolvePublicApiBaseUrl(config);
1654
1969
  const appId = typeof options.appId === "string" ? options.appId : undefined;
1655
1970
  const performance = await getUserPerformance(apiBase, apiKey, { appId });
1656
1971
  if (performance && typeof performance === "object" && "trades" in performance && Array.isArray(performance.trades)) {
@@ -1661,6 +1976,17 @@ async function runAppsTradeFacts(options) {
1661
1976
  }
1662
1977
  async function main() {
1663
1978
  const { command, options, rest } = parseArgs(process.argv.slice(2));
1979
+ const selectedAccount = resolveAccountOption(options);
1980
+ const selectedBaseUrl = resolveBaseUrlOption(options);
1981
+ if (selectedAccount) {
1982
+ process.env.OPENPOND_ACCOUNT = selectedAccount;
1983
+ }
1984
+ if (!selectedAccount && typeof options.handle === "string" && options.handle.trim().length > 0) {
1985
+ process.env.OPENPOND_ACCOUNT = options.handle.trim();
1986
+ }
1987
+ if (selectedBaseUrl) {
1988
+ process.env.OPENPOND_BASE_URL = selectedBaseUrl;
1989
+ }
1664
1990
  if (!command || command === "help") {
1665
1991
  printHelp();
1666
1992
  return;
package/dist/config.d.ts CHANGED
@@ -1,4 +1,19 @@
1
+ export type LocalSessionConfig = {
2
+ token?: string;
3
+ deviceCode?: string | null;
4
+ appId?: string | null;
5
+ conversationId?: string | null;
6
+ };
7
+ export type LocalAccountConfig = {
8
+ handle: string;
9
+ apiKey?: string;
10
+ baseUrl?: string;
11
+ environment?: string;
12
+ session?: LocalSessionConfig;
13
+ };
1
14
  export type LocalConfig = {
15
+ accounts?: LocalAccountConfig[];
16
+ activeHandle?: string;
2
17
  baseUrl?: string;
3
18
  apiKey?: string;
4
19
  token?: string;
@@ -9,8 +24,12 @@ export type LocalConfig = {
9
24
  executionMode?: "local" | "hosted";
10
25
  mode?: "general" | "builder";
11
26
  };
27
+ export type LoadConfigOptions = {
28
+ account?: string;
29
+ baseUrl?: string;
30
+ };
12
31
  export declare function getConfigPath(): string;
13
32
  export declare function loadGlobalConfig(): Promise<LocalConfig>;
14
- export declare function loadConfig(): Promise<LocalConfig>;
33
+ export declare function loadConfig(options?: LoadConfigOptions): Promise<LocalConfig>;
15
34
  export declare function saveConfig(next: LocalConfig): Promise<void>;
16
35
  export declare function saveGlobalConfig(next: LocalConfig): Promise<void>;
package/dist/index.js CHANGED
@@ -356,9 +356,15 @@ function resolveWorkerBaseUrl(baseUrl) {
356
356
  if (host === "apps.openpond.live") {
357
357
  return null;
358
358
  }
359
- if (host === "api.openpond.ai" || host === "openpond.live" || host === "www.openpond.live") {
359
+ if (host === "apps.staging.openpond.live") {
360
+ return null;
361
+ }
362
+ if (host === "api.openpond.ai" || host === "openpond.ai" || host === "openpond.live" || host === "www.openpond.live") {
360
363
  return "https://apps.openpond.live";
361
364
  }
365
+ if (host === "api.staging-api.openpond.ai" || host === "staging.openpond.ai") {
366
+ return "https://apps.staging.openpond.live";
367
+ }
362
368
  return null;
363
369
  })();
364
370
  if (mappedHost) {
@@ -827,6 +833,15 @@ import os2 from "node:os";
827
833
  import path2 from "node:path";
828
834
  var GLOBAL_DIRNAME = ".openpond";
829
835
  var GLOBAL_CONFIG_FILENAME = "config.json";
836
+ var DEFAULT_ACCOUNT_HANDLE = "default";
837
+ var ACCOUNT_SCOPED_KEYS = [
838
+ "apiKey",
839
+ "baseUrl",
840
+ "token",
841
+ "deviceCode",
842
+ "appId",
843
+ "conversationId"
844
+ ];
830
845
  function getConfigPath() {
831
846
  return getGlobalConfigPath();
832
847
  }
@@ -841,34 +856,287 @@ async function loadConfigFile(filePath) {
841
856
  return {};
842
857
  }
843
858
  }
844
- async function loadGlobalConfig() {
845
- return loadConfigFile(getGlobalConfigPath());
859
+ function hasOwn(value, key) {
860
+ return Object.prototype.hasOwnProperty.call(value, key);
846
861
  }
847
- async function loadConfig() {
848
- return loadGlobalConfig();
862
+ function normalizeHandle(value) {
863
+ if (typeof value !== "string")
864
+ return null;
865
+ const trimmed = value.trim();
866
+ return trimmed.length > 0 ? trimmed : null;
849
867
  }
850
- async function saveConfig(next) {
868
+ function normalizeBaseUrl(value) {
869
+ if (typeof value !== "string")
870
+ return null;
871
+ const trimmed = value.trim();
872
+ if (!trimmed)
873
+ return null;
874
+ return trimmed.replace(/\/$/, "");
875
+ }
876
+ function handleEquals(left, right) {
877
+ return left.trim().toLowerCase() === right.trim().toLowerCase();
878
+ }
879
+ function findAccountIndex(accounts, handle, baseUrl) {
880
+ const normalizedBaseUrl = normalizeBaseUrl(baseUrl);
881
+ return accounts.findIndex((candidate) => {
882
+ if (!handleEquals(candidate.handle, handle))
883
+ return false;
884
+ if (!normalizedBaseUrl)
885
+ return true;
886
+ return normalizeBaseUrl(candidate.baseUrl) === normalizedBaseUrl;
887
+ });
888
+ }
889
+ function sanitizeSession(value) {
890
+ if (!value || typeof value !== "object" || Array.isArray(value)) {
891
+ return;
892
+ }
893
+ const input = value;
894
+ const out = {};
895
+ if (typeof input.token === "string")
896
+ out.token = input.token;
897
+ if (typeof input.deviceCode === "string" || input.deviceCode === null) {
898
+ out.deviceCode = input.deviceCode;
899
+ }
900
+ if (typeof input.appId === "string" || input.appId === null) {
901
+ out.appId = input.appId;
902
+ }
903
+ if (typeof input.conversationId === "string" || input.conversationId === null) {
904
+ out.conversationId = input.conversationId;
905
+ }
906
+ return Object.keys(out).length > 0 ? out : undefined;
907
+ }
908
+ function sanitizeAccount(value) {
909
+ if (!value || typeof value !== "object" || Array.isArray(value))
910
+ return null;
911
+ const input = value;
912
+ const handle = normalizeHandle(typeof input.handle === "string" ? input.handle : undefined);
913
+ if (!handle)
914
+ return null;
915
+ const out = { handle };
916
+ if (typeof input.apiKey === "string")
917
+ out.apiKey = input.apiKey;
918
+ if (typeof input.baseUrl === "string")
919
+ out.baseUrl = input.baseUrl;
920
+ if (typeof input.environment === "string")
921
+ out.environment = input.environment;
922
+ const session = sanitizeSession(input.session);
923
+ if (session)
924
+ out.session = session;
925
+ return out;
926
+ }
927
+ function extractLegacySession(raw) {
928
+ const session = {};
929
+ if (typeof raw.token === "string")
930
+ session.token = raw.token;
931
+ if (typeof raw.deviceCode === "string" || raw.deviceCode === null) {
932
+ session.deviceCode = raw.deviceCode;
933
+ }
934
+ if (typeof raw.appId === "string" || raw.appId === null) {
935
+ session.appId = raw.appId;
936
+ }
937
+ if (typeof raw.conversationId === "string" || raw.conversationId === null) {
938
+ session.conversationId = raw.conversationId;
939
+ }
940
+ return Object.keys(session).length > 0 ? session : undefined;
941
+ }
942
+ function extractLegacyAccount(raw, handle) {
943
+ const out = { handle };
944
+ if (typeof raw.apiKey === "string")
945
+ out.apiKey = raw.apiKey;
946
+ if (typeof raw.baseUrl === "string")
947
+ out.baseUrl = raw.baseUrl;
948
+ const session = extractLegacySession(raw);
949
+ if (session)
950
+ out.session = session;
951
+ return out;
952
+ }
953
+ function normalizeGlobalConfig(raw) {
954
+ const normalized = {};
955
+ if (typeof raw.lspEnabled === "boolean")
956
+ normalized.lspEnabled = raw.lspEnabled;
957
+ if (raw.executionMode === "local" || raw.executionMode === "hosted") {
958
+ normalized.executionMode = raw.executionMode;
959
+ }
960
+ if (raw.mode === "general" || raw.mode === "builder") {
961
+ normalized.mode = raw.mode;
962
+ }
963
+ const accounts = [];
964
+ const sourceAccounts = Array.isArray(raw.accounts) ? raw.accounts : [];
965
+ for (const candidate of sourceAccounts) {
966
+ const account = sanitizeAccount(candidate);
967
+ if (!account)
968
+ continue;
969
+ if (findAccountIndex(accounts, account.handle, account.baseUrl) !== -1)
970
+ continue;
971
+ accounts.push(account);
972
+ }
973
+ if (accounts.length === 0) {
974
+ const legacyHandle = normalizeHandle(raw.activeHandle) || DEFAULT_ACCOUNT_HANDLE;
975
+ accounts.push(extractLegacyAccount(raw, legacyHandle));
976
+ }
977
+ const requested = normalizeHandle(raw.activeHandle);
978
+ const resolvedHandle = requested && findAccountIndex(accounts, requested) !== -1 ? accounts[findAccountIndex(accounts, requested)].handle : accounts[0].handle;
979
+ normalized.accounts = accounts;
980
+ normalized.activeHandle = resolvedHandle;
981
+ return normalized;
982
+ }
983
+ function resolveRequestedHandle(global, explicitAccount) {
984
+ const accounts = global.accounts ?? [];
985
+ const requested = normalizeHandle(explicitAccount) || normalizeHandle(process.env.OPENPOND_ACCOUNT) || normalizeHandle(global.activeHandle) || accounts[0]?.handle || DEFAULT_ACCOUNT_HANDLE;
986
+ const idx = findAccountIndex(accounts, requested);
987
+ return idx === -1 ? requested : accounts[idx].handle;
988
+ }
989
+ function ensureAccount(accounts, handle, baseUrl) {
990
+ const normalizedBaseUrl = normalizeBaseUrl(baseUrl);
991
+ const idx = findAccountIndex(accounts, handle, normalizedBaseUrl);
992
+ if (idx !== -1) {
993
+ return accounts[idx];
994
+ }
995
+ if (!normalizedBaseUrl) {
996
+ const firstByHandle = findAccountIndex(accounts, handle);
997
+ if (firstByHandle !== -1) {
998
+ return accounts[firstByHandle];
999
+ }
1000
+ }
1001
+ const next = { handle };
1002
+ if (normalizedBaseUrl) {
1003
+ next.baseUrl = normalizedBaseUrl;
1004
+ }
1005
+ accounts.push(next);
1006
+ return next;
1007
+ }
1008
+ function cleanupAccount(account) {
1009
+ if (account.session && Object.keys(account.session).length === 0) {
1010
+ delete account.session;
1011
+ }
1012
+ }
1013
+ function applyScopedKey(account, key, value, options) {
1014
+ const shouldDelete = value === null || value === undefined && options.undefinedDeletes;
1015
+ switch (key) {
1016
+ case "apiKey": {
1017
+ if (shouldDelete) {
1018
+ delete account.apiKey;
1019
+ return;
1020
+ }
1021
+ if (typeof value === "string") {
1022
+ account.apiKey = value;
1023
+ }
1024
+ return;
1025
+ }
1026
+ case "baseUrl": {
1027
+ if (shouldDelete) {
1028
+ delete account.baseUrl;
1029
+ return;
1030
+ }
1031
+ if (typeof value === "string") {
1032
+ account.baseUrl = value;
1033
+ }
1034
+ return;
1035
+ }
1036
+ case "token":
1037
+ case "deviceCode":
1038
+ case "appId":
1039
+ case "conversationId": {
1040
+ if (!account.session)
1041
+ account.session = {};
1042
+ if (shouldDelete) {
1043
+ delete account.session[key];
1044
+ cleanupAccount(account);
1045
+ return;
1046
+ }
1047
+ if (typeof value === "string" || value === null) {
1048
+ account.session[key] = value;
1049
+ }
1050
+ cleanupAccount(account);
1051
+ return;
1052
+ }
1053
+ }
1054
+ }
1055
+ function applyAccountPatch(global, source, options) {
1056
+ const hasScopedPatch = ACCOUNT_SCOPED_KEYS.some((key) => hasOwn(source, key));
1057
+ if (!hasScopedPatch)
1058
+ return false;
1059
+ const accounts = global.accounts ?? [];
1060
+ const handle = resolveRequestedHandle(global, source.activeHandle);
1061
+ const requestedBaseUrl = normalizeBaseUrl(hasOwn(source, "baseUrl") ? source.baseUrl ?? null : process.env.OPENPOND_BASE_URL);
1062
+ const account = ensureAccount(accounts, handle, requestedBaseUrl);
1063
+ for (const key of ACCOUNT_SCOPED_KEYS) {
1064
+ if (!hasOwn(source, key))
1065
+ continue;
1066
+ applyScopedKey(account, key, source[key], options);
1067
+ }
1068
+ global.accounts = accounts;
1069
+ global.activeHandle = handle;
1070
+ return true;
1071
+ }
1072
+ function applyTopLevelPatch(global, source) {
1073
+ if (hasOwn(source, "lspEnabled")) {
1074
+ if (typeof source.lspEnabled === "boolean") {
1075
+ global.lspEnabled = source.lspEnabled;
1076
+ } else if (source.lspEnabled === null) {
1077
+ delete global.lspEnabled;
1078
+ }
1079
+ }
1080
+ if (hasOwn(source, "executionMode")) {
1081
+ if (source.executionMode === "local" || source.executionMode === "hosted") {
1082
+ global.executionMode = source.executionMode;
1083
+ } else if (source.executionMode === null) {
1084
+ delete global.executionMode;
1085
+ }
1086
+ }
1087
+ if (hasOwn(source, "mode")) {
1088
+ if (source.mode === "general" || source.mode === "builder") {
1089
+ global.mode = source.mode;
1090
+ } else if (source.mode === null) {
1091
+ delete global.mode;
1092
+ }
1093
+ }
1094
+ if (typeof source.activeHandle === "string" && source.activeHandle.trim().length > 0) {
1095
+ const requested = resolveRequestedHandle(global, source.activeHandle);
1096
+ global.activeHandle = requested;
1097
+ }
1098
+ }
1099
+ async function writeGlobalConfig(next) {
851
1100
  const filePath = getGlobalConfigPath();
852
1101
  await fs2.mkdir(path2.dirname(filePath), { recursive: true });
853
- const payload = JSON.stringify(Object.fromEntries(Object.entries(next).filter(([, value]) => value !== undefined && value !== null)), null, 2);
1102
+ const payload = JSON.stringify(next, null, 2);
854
1103
  await fs2.writeFile(filePath, payload, "utf-8");
855
1104
  }
1105
+ async function loadGlobalConfig() {
1106
+ const raw = await loadConfigFile(getGlobalConfigPath());
1107
+ return normalizeGlobalConfig(raw);
1108
+ }
1109
+ async function loadConfig(options = {}) {
1110
+ const global = await loadGlobalConfig();
1111
+ const accounts = global.accounts ?? [];
1112
+ const requested = resolveRequestedHandle(global, options.account);
1113
+ const requestedBaseUrl = normalizeBaseUrl(options.baseUrl ?? process.env.OPENPOND_BASE_URL);
1114
+ const idxWithBase = findAccountIndex(accounts, requested, requestedBaseUrl);
1115
+ const idx = idxWithBase !== -1 ? idxWithBase : findAccountIndex(accounts, requested);
1116
+ const account = idx === -1 ? null : accounts[idx];
1117
+ const session = account?.session;
1118
+ return {
1119
+ ...global,
1120
+ activeHandle: requested,
1121
+ apiKey: account?.apiKey,
1122
+ baseUrl: account?.baseUrl,
1123
+ token: session?.token,
1124
+ deviceCode: session?.deviceCode,
1125
+ appId: session?.appId,
1126
+ conversationId: session?.conversationId
1127
+ };
1128
+ }
1129
+ async function saveConfig(next) {
1130
+ const global = normalizeGlobalConfig(next);
1131
+ applyTopLevelPatch(global, next);
1132
+ applyAccountPatch(global, next, { undefinedDeletes: true });
1133
+ await writeGlobalConfig(global);
1134
+ }
856
1135
  async function saveGlobalConfig(next) {
857
- const filePath = getGlobalConfigPath();
858
- await fs2.mkdir(path2.dirname(filePath), { recursive: true });
859
1136
  const current = await loadGlobalConfig();
860
- const merged = { ...current };
861
- for (const [key, value] of Object.entries(next)) {
862
- if (value === undefined)
863
- continue;
864
- if (value === null) {
865
- delete merged[key];
866
- } else {
867
- merged[key] = value;
868
- }
869
- }
870
- const payload = JSON.stringify(merged, null, 2);
871
- await fs2.writeFile(filePath, payload, "utf-8");
1137
+ applyTopLevelPatch(current, next);
1138
+ applyAccountPatch(current, next, { undefinedDeletes: false });
1139
+ await writeGlobalConfig(current);
872
1140
  }
873
1141
 
874
1142
  // src/index.ts
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "openpond-code",
3
- "version": "0.1.4",
3
+ "version": "0.2.1",
4
4
  "description": "OpenPond CLI (API key only)",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",