openpond-code 0.1.3 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,17 @@
1
1
  # openpond-code
2
2
 
3
+ ## 0.2.0
4
+
5
+ ### Minor Changes
6
+
7
+ - b6e5f07: added multi accounts
8
+
9
+ ## 0.1.4
10
+
11
+ ### Patch Changes
12
+
13
+ - bb79cf8: cleaned up naming
14
+
3
15
  ## 0.1.3
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,12 @@ 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
+ - `OPENPOND_ACCOUNT=<name>` sets the default selected account for a shell/session.
56
+ - If omitted, CLI uses the last active stored account.
57
+
48
58
  Command reference:
49
59
 
50
60
  - `openpond login`: prompt for API key and save to `~/.openpond/config.json`.
@@ -99,8 +109,8 @@ await client.apps.agentCreate(
99
109
  );
100
110
  ```
101
111
 
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`.
112
+ You can override hosts with `apiUrl` and `toolUrl` in `createClient`, or
113
+ via `OPENPOND_API_URL` and `OPENPOND_TOOL_URL`.
104
114
 
105
115
  Examples live in `examples`.
106
116
 
package/dist/api.d.ts CHANGED
@@ -105,7 +105,6 @@ export type AppListItem = {
105
105
  gitRepo: string | null;
106
106
  gitProvider: string | null;
107
107
  gitHost: string | null;
108
- internalToolName: string | null;
109
108
  defaultBranch: string | null;
110
109
  createdAt: string;
111
110
  updatedAt: string;
package/dist/cli.js CHANGED
@@ -434,6 +434,15 @@ import os2 from "node:os";
434
434
  import path2 from "node:path";
435
435
  var GLOBAL_DIRNAME = ".openpond";
436
436
  var GLOBAL_CONFIG_FILENAME = "config.json";
437
+ var DEFAULT_ACCOUNT_HANDLE = "default";
438
+ var ACCOUNT_SCOPED_KEYS = [
439
+ "apiKey",
440
+ "baseUrl",
441
+ "token",
442
+ "deviceCode",
443
+ "appId",
444
+ "conversationId"
445
+ ];
437
446
  function getGlobalConfigPath() {
438
447
  return path2.join(os2.homedir(), GLOBAL_DIRNAME, GLOBAL_CONFIG_FILENAME);
439
448
  }
@@ -445,29 +454,254 @@ async function loadConfigFile(filePath) {
445
454
  return {};
446
455
  }
447
456
  }
448
- async function loadGlobalConfig() {
449
- return loadConfigFile(getGlobalConfigPath());
457
+ function hasOwn(value, key) {
458
+ return Object.prototype.hasOwnProperty.call(value, key);
450
459
  }
451
- async function loadConfig() {
452
- return loadGlobalConfig();
460
+ function normalizeHandle(value) {
461
+ if (typeof value !== "string")
462
+ return null;
463
+ const trimmed = value.trim();
464
+ return trimmed.length > 0 ? trimmed : null;
453
465
  }
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)
466
+ function handleEquals(left, right) {
467
+ return left.trim().toLowerCase() === right.trim().toLowerCase();
468
+ }
469
+ function findAccountIndex(accounts, handle) {
470
+ return accounts.findIndex((candidate) => handleEquals(candidate.handle, handle));
471
+ }
472
+ function sanitizeSession(value) {
473
+ if (!value || typeof value !== "object" || Array.isArray(value)) {
474
+ return;
475
+ }
476
+ const input = value;
477
+ const out = {};
478
+ if (typeof input.token === "string")
479
+ out.token = input.token;
480
+ if (typeof input.deviceCode === "string" || input.deviceCode === null) {
481
+ out.deviceCode = input.deviceCode;
482
+ }
483
+ if (typeof input.appId === "string" || input.appId === null) {
484
+ out.appId = input.appId;
485
+ }
486
+ if (typeof input.conversationId === "string" || input.conversationId === null) {
487
+ out.conversationId = input.conversationId;
488
+ }
489
+ return Object.keys(out).length > 0 ? out : undefined;
490
+ }
491
+ function sanitizeAccount(value) {
492
+ if (!value || typeof value !== "object" || Array.isArray(value))
493
+ return null;
494
+ const input = value;
495
+ const handle = normalizeHandle(typeof input.handle === "string" ? input.handle : undefined);
496
+ if (!handle)
497
+ return null;
498
+ const out = { handle };
499
+ if (typeof input.apiKey === "string")
500
+ out.apiKey = input.apiKey;
501
+ if (typeof input.baseUrl === "string")
502
+ out.baseUrl = input.baseUrl;
503
+ if (typeof input.environment === "string")
504
+ out.environment = input.environment;
505
+ const session = sanitizeSession(input.session);
506
+ if (session)
507
+ out.session = session;
508
+ return out;
509
+ }
510
+ function extractLegacySession(raw) {
511
+ const session = {};
512
+ if (typeof raw.token === "string")
513
+ session.token = raw.token;
514
+ if (typeof raw.deviceCode === "string" || raw.deviceCode === null) {
515
+ session.deviceCode = raw.deviceCode;
516
+ }
517
+ if (typeof raw.appId === "string" || raw.appId === null) {
518
+ session.appId = raw.appId;
519
+ }
520
+ if (typeof raw.conversationId === "string" || raw.conversationId === null) {
521
+ session.conversationId = raw.conversationId;
522
+ }
523
+ return Object.keys(session).length > 0 ? session : undefined;
524
+ }
525
+ function extractLegacyAccount(raw, handle) {
526
+ const out = { handle };
527
+ if (typeof raw.apiKey === "string")
528
+ out.apiKey = raw.apiKey;
529
+ if (typeof raw.baseUrl === "string")
530
+ out.baseUrl = raw.baseUrl;
531
+ const session = extractLegacySession(raw);
532
+ if (session)
533
+ out.session = session;
534
+ return out;
535
+ }
536
+ function normalizeGlobalConfig(raw) {
537
+ const normalized = {};
538
+ if (typeof raw.lspEnabled === "boolean")
539
+ normalized.lspEnabled = raw.lspEnabled;
540
+ if (raw.executionMode === "local" || raw.executionMode === "hosted") {
541
+ normalized.executionMode = raw.executionMode;
542
+ }
543
+ if (raw.mode === "general" || raw.mode === "builder") {
544
+ normalized.mode = raw.mode;
545
+ }
546
+ const accounts = [];
547
+ const sourceAccounts = Array.isArray(raw.accounts) ? raw.accounts : [];
548
+ for (const candidate of sourceAccounts) {
549
+ const account = sanitizeAccount(candidate);
550
+ if (!account)
461
551
  continue;
462
- if (value === null) {
463
- delete merged[key];
464
- } else {
465
- merged[key] = value;
552
+ if (findAccountIndex(accounts, account.handle) !== -1)
553
+ continue;
554
+ accounts.push(account);
555
+ }
556
+ if (accounts.length === 0) {
557
+ const legacyHandle = normalizeHandle(raw.activeHandle) || DEFAULT_ACCOUNT_HANDLE;
558
+ accounts.push(extractLegacyAccount(raw, legacyHandle));
559
+ }
560
+ const requested = normalizeHandle(raw.activeHandle);
561
+ const resolvedHandle = requested && findAccountIndex(accounts, requested) !== -1 ? accounts[findAccountIndex(accounts, requested)].handle : accounts[0].handle;
562
+ normalized.accounts = accounts;
563
+ normalized.activeHandle = resolvedHandle;
564
+ return normalized;
565
+ }
566
+ function resolveRequestedHandle(global, explicitAccount) {
567
+ const accounts = global.accounts ?? [];
568
+ const requested = normalizeHandle(explicitAccount) || normalizeHandle(process.env.OPENPOND_ACCOUNT) || normalizeHandle(global.activeHandle) || accounts[0]?.handle || DEFAULT_ACCOUNT_HANDLE;
569
+ const idx = findAccountIndex(accounts, requested);
570
+ return idx === -1 ? requested : accounts[idx].handle;
571
+ }
572
+ function ensureAccount(accounts, handle) {
573
+ const idx = findAccountIndex(accounts, handle);
574
+ if (idx !== -1) {
575
+ return accounts[idx];
576
+ }
577
+ const next = { handle };
578
+ accounts.push(next);
579
+ return next;
580
+ }
581
+ function cleanupAccount(account) {
582
+ if (account.session && Object.keys(account.session).length === 0) {
583
+ delete account.session;
584
+ }
585
+ }
586
+ function applyScopedKey(account, key, value, options) {
587
+ const shouldDelete = value === null || value === undefined && options.undefinedDeletes;
588
+ switch (key) {
589
+ case "apiKey": {
590
+ if (shouldDelete) {
591
+ delete account.apiKey;
592
+ return;
593
+ }
594
+ if (typeof value === "string") {
595
+ account.apiKey = value;
596
+ }
597
+ return;
466
598
  }
599
+ case "baseUrl": {
600
+ if (shouldDelete) {
601
+ delete account.baseUrl;
602
+ return;
603
+ }
604
+ if (typeof value === "string") {
605
+ account.baseUrl = value;
606
+ }
607
+ return;
608
+ }
609
+ case "token":
610
+ case "deviceCode":
611
+ case "appId":
612
+ case "conversationId": {
613
+ if (!account.session)
614
+ account.session = {};
615
+ if (shouldDelete) {
616
+ delete account.session[key];
617
+ cleanupAccount(account);
618
+ return;
619
+ }
620
+ if (typeof value === "string" || value === null) {
621
+ account.session[key] = value;
622
+ }
623
+ cleanupAccount(account);
624
+ return;
625
+ }
626
+ }
627
+ }
628
+ function applyAccountPatch(global, source, options) {
629
+ const hasScopedPatch = ACCOUNT_SCOPED_KEYS.some((key) => hasOwn(source, key));
630
+ if (!hasScopedPatch)
631
+ return false;
632
+ const accounts = global.accounts ?? [];
633
+ const handle = resolveRequestedHandle(global, source.activeHandle);
634
+ const account = ensureAccount(accounts, handle);
635
+ for (const key of ACCOUNT_SCOPED_KEYS) {
636
+ if (!hasOwn(source, key))
637
+ continue;
638
+ applyScopedKey(account, key, source[key], options);
639
+ }
640
+ global.accounts = accounts;
641
+ global.activeHandle = handle;
642
+ return true;
643
+ }
644
+ function applyTopLevelPatch(global, source) {
645
+ if (hasOwn(source, "lspEnabled")) {
646
+ if (typeof source.lspEnabled === "boolean") {
647
+ global.lspEnabled = source.lspEnabled;
648
+ } else if (source.lspEnabled === null) {
649
+ delete global.lspEnabled;
650
+ }
651
+ }
652
+ if (hasOwn(source, "executionMode")) {
653
+ if (source.executionMode === "local" || source.executionMode === "hosted") {
654
+ global.executionMode = source.executionMode;
655
+ } else if (source.executionMode === null) {
656
+ delete global.executionMode;
657
+ }
658
+ }
659
+ if (hasOwn(source, "mode")) {
660
+ if (source.mode === "general" || source.mode === "builder") {
661
+ global.mode = source.mode;
662
+ } else if (source.mode === null) {
663
+ delete global.mode;
664
+ }
665
+ }
666
+ if (typeof source.activeHandle === "string" && source.activeHandle.trim().length > 0) {
667
+ const requested = resolveRequestedHandle(global, source.activeHandle);
668
+ global.activeHandle = requested;
467
669
  }
468
- const payload = JSON.stringify(merged, null, 2);
670
+ }
671
+ async function writeGlobalConfig(next) {
672
+ const filePath = getGlobalConfigPath();
673
+ await fs2.mkdir(path2.dirname(filePath), { recursive: true });
674
+ const payload = JSON.stringify(next, null, 2);
469
675
  await fs2.writeFile(filePath, payload, "utf-8");
470
676
  }
677
+ async function loadGlobalConfig() {
678
+ const raw = await loadConfigFile(getGlobalConfigPath());
679
+ return normalizeGlobalConfig(raw);
680
+ }
681
+ async function loadConfig(options = {}) {
682
+ const global = await loadGlobalConfig();
683
+ const accounts = global.accounts ?? [];
684
+ const requested = resolveRequestedHandle(global, options.account);
685
+ const idx = findAccountIndex(accounts, requested);
686
+ const account = idx === -1 ? null : accounts[idx];
687
+ const session = account?.session;
688
+ return {
689
+ ...global,
690
+ activeHandle: requested,
691
+ apiKey: account?.apiKey,
692
+ baseUrl: account?.baseUrl,
693
+ token: session?.token,
694
+ deviceCode: session?.deviceCode,
695
+ appId: session?.appId,
696
+ conversationId: session?.conversationId
697
+ };
698
+ }
699
+ async function saveGlobalConfig(next) {
700
+ const current = await loadGlobalConfig();
701
+ applyTopLevelPatch(current, next);
702
+ applyAccountPatch(current, next, { undefinedDeletes: false });
703
+ await writeGlobalConfig(current);
704
+ }
471
705
 
472
706
  // src/stream.ts
473
707
  function normalizeDataFrames(raw) {
@@ -644,7 +878,7 @@ function formatStreamItem(item) {
644
878
  // src/cli-package.ts
645
879
  function parseArgs(argv) {
646
880
  const args = [...argv];
647
- const command = args.shift() || "";
881
+ let command = "";
648
882
  const options = {};
649
883
  const rest = [];
650
884
  while (args.length > 0) {
@@ -655,11 +889,25 @@ function parseArgs(argv) {
655
889
  const value = args[0] && !args[0].startsWith("--") ? args.shift() : "true";
656
890
  options[key] = value;
657
891
  } else {
658
- rest.push(next);
892
+ if (!command) {
893
+ command = next;
894
+ } else {
895
+ rest.push(next);
896
+ }
659
897
  }
660
898
  }
661
899
  return { command, options, rest };
662
900
  }
901
+ function resolveAccountOption(options) {
902
+ const raw = typeof options.account === "string" ? options.account : typeof options.profile === "string" ? options.profile : null;
903
+ if (!raw)
904
+ return null;
905
+ const trimmed = raw.trim();
906
+ if (!trimmed || trimmed === "true") {
907
+ throw new Error("account must be a non-empty value");
908
+ }
909
+ return trimmed;
910
+ }
663
911
  function resolveBaseUrl(config) {
664
912
  const envBase = process.env.OPENPOND_BASE_URL;
665
913
  const base = envBase || config.baseUrl || "https://openpond.ai";
@@ -759,7 +1007,7 @@ async function ensureApiKey(config, baseUrl) {
759
1007
  if (existing)
760
1008
  return existing;
761
1009
  const apiKey = await promptForApiKey();
762
- await saveGlobalConfig({ apiKey, baseUrl });
1010
+ await saveGlobalConfig({ apiKey, baseUrl, activeHandle: config.activeHandle });
763
1011
  console.log("saved api key to ~/.openpond/config.json");
764
1012
  return apiKey;
765
1013
  }
@@ -906,8 +1154,6 @@ async function resolveAppTarget(apiBase, apiKey, target) {
906
1154
  const candidates = [
907
1155
  app.repo,
908
1156
  app.gitRepo,
909
- app.internalToolName,
910
- app.name,
911
1157
  app.id
912
1158
  ].map(normalizeRepoName);
913
1159
  return candidates.includes(normalizedRepo);
@@ -1003,8 +1249,11 @@ function printHelp() {
1003
1249
  console.log(" openpond apps positions tx [--method <GET|POST>] [--body <json>] [--params <json>]");
1004
1250
  console.log(" openpond opentool <init|validate|build> [args]");
1005
1251
  console.log("");
1252
+ console.log("Global options:");
1253
+ console.log(" --account <name> (alias: --profile <name>)");
1254
+ console.log("");
1006
1255
  console.log("Env:");
1007
- console.log(" OPENPOND_API_KEY, OPENPOND_BASE_URL, OPENPOND_API_URL, OPENPOND_TOOL_URL");
1256
+ console.log(" OPENPOND_API_KEY, OPENPOND_ACCOUNT, OPENPOND_API_URL, OPENPOND_TOOL_URL");
1008
1257
  }
1009
1258
  async function runLogin(options) {
1010
1259
  const config = await loadConfig();
@@ -1017,7 +1266,7 @@ async function runLogin(options) {
1017
1266
  if (!apiKey.startsWith("opk_")) {
1018
1267
  console.log("warning: API keys usually start with opk_.");
1019
1268
  }
1020
- await saveGlobalConfig({ apiKey, baseUrl });
1269
+ await saveGlobalConfig({ apiKey, baseUrl, activeHandle: config.activeHandle });
1021
1270
  console.log("saved api key to ~/.openpond/config.json");
1022
1271
  }
1023
1272
  async function runToolList(options, target) {
@@ -1354,7 +1603,7 @@ async function runAppsList(options) {
1354
1603
  }
1355
1604
  for (const app of filtered) {
1356
1605
  const owner = app.handle || app.gitOwner || "unknown";
1357
- const repo = app.repo || app.gitRepo || app.name || app.id;
1606
+ const repo = app.repo || app.gitRepo || app.id;
1358
1607
  const status = app.latestDeployment?.status || "no-deploy";
1359
1608
  const branch = app.latestDeployment?.gitBranch || app.defaultBranch || "-";
1360
1609
  console.log(`${owner}/${repo} ${status} ${branch} ${app.id}`);
@@ -1663,6 +1912,10 @@ async function runAppsTradeFacts(options) {
1663
1912
  }
1664
1913
  async function main() {
1665
1914
  const { command, options, rest } = parseArgs(process.argv.slice(2));
1915
+ const selectedAccount = resolveAccountOption(options);
1916
+ if (selectedAccount) {
1917
+ process.env.OPENPOND_ACCOUNT = selectedAccount;
1918
+ }
1666
1919
  if (!command || command === "help") {
1667
1920
  printHelp();
1668
1921
  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,11 @@ export type LocalConfig = {
9
24
  executionMode?: "local" | "hosted";
10
25
  mode?: "general" | "builder";
11
26
  };
27
+ export type LoadConfigOptions = {
28
+ account?: string;
29
+ };
12
30
  export declare function getConfigPath(): string;
13
31
  export declare function loadGlobalConfig(): Promise<LocalConfig>;
14
- export declare function loadConfig(): Promise<LocalConfig>;
32
+ export declare function loadConfig(options?: LoadConfigOptions): Promise<LocalConfig>;
15
33
  export declare function saveConfig(next: LocalConfig): Promise<void>;
16
34
  export declare function saveGlobalConfig(next: LocalConfig): Promise<void>;
package/dist/index.js CHANGED
@@ -827,6 +827,15 @@ import os2 from "node:os";
827
827
  import path2 from "node:path";
828
828
  var GLOBAL_DIRNAME = ".openpond";
829
829
  var GLOBAL_CONFIG_FILENAME = "config.json";
830
+ var DEFAULT_ACCOUNT_HANDLE = "default";
831
+ var ACCOUNT_SCOPED_KEYS = [
832
+ "apiKey",
833
+ "baseUrl",
834
+ "token",
835
+ "deviceCode",
836
+ "appId",
837
+ "conversationId"
838
+ ];
830
839
  function getConfigPath() {
831
840
  return getGlobalConfigPath();
832
841
  }
@@ -841,34 +850,259 @@ async function loadConfigFile(filePath) {
841
850
  return {};
842
851
  }
843
852
  }
844
- async function loadGlobalConfig() {
845
- return loadConfigFile(getGlobalConfigPath());
853
+ function hasOwn(value, key) {
854
+ return Object.prototype.hasOwnProperty.call(value, key);
855
+ }
856
+ function normalizeHandle(value) {
857
+ if (typeof value !== "string")
858
+ return null;
859
+ const trimmed = value.trim();
860
+ return trimmed.length > 0 ? trimmed : null;
846
861
  }
847
- async function loadConfig() {
848
- return loadGlobalConfig();
862
+ function handleEquals(left, right) {
863
+ return left.trim().toLowerCase() === right.trim().toLowerCase();
849
864
  }
850
- async function saveConfig(next) {
865
+ function findAccountIndex(accounts, handle) {
866
+ return accounts.findIndex((candidate) => handleEquals(candidate.handle, handle));
867
+ }
868
+ function sanitizeSession(value) {
869
+ if (!value || typeof value !== "object" || Array.isArray(value)) {
870
+ return;
871
+ }
872
+ const input = value;
873
+ const out = {};
874
+ if (typeof input.token === "string")
875
+ out.token = input.token;
876
+ if (typeof input.deviceCode === "string" || input.deviceCode === null) {
877
+ out.deviceCode = input.deviceCode;
878
+ }
879
+ if (typeof input.appId === "string" || input.appId === null) {
880
+ out.appId = input.appId;
881
+ }
882
+ if (typeof input.conversationId === "string" || input.conversationId === null) {
883
+ out.conversationId = input.conversationId;
884
+ }
885
+ return Object.keys(out).length > 0 ? out : undefined;
886
+ }
887
+ function sanitizeAccount(value) {
888
+ if (!value || typeof value !== "object" || Array.isArray(value))
889
+ return null;
890
+ const input = value;
891
+ const handle = normalizeHandle(typeof input.handle === "string" ? input.handle : undefined);
892
+ if (!handle)
893
+ return null;
894
+ const out = { handle };
895
+ if (typeof input.apiKey === "string")
896
+ out.apiKey = input.apiKey;
897
+ if (typeof input.baseUrl === "string")
898
+ out.baseUrl = input.baseUrl;
899
+ if (typeof input.environment === "string")
900
+ out.environment = input.environment;
901
+ const session = sanitizeSession(input.session);
902
+ if (session)
903
+ out.session = session;
904
+ return out;
905
+ }
906
+ function extractLegacySession(raw) {
907
+ const session = {};
908
+ if (typeof raw.token === "string")
909
+ session.token = raw.token;
910
+ if (typeof raw.deviceCode === "string" || raw.deviceCode === null) {
911
+ session.deviceCode = raw.deviceCode;
912
+ }
913
+ if (typeof raw.appId === "string" || raw.appId === null) {
914
+ session.appId = raw.appId;
915
+ }
916
+ if (typeof raw.conversationId === "string" || raw.conversationId === null) {
917
+ session.conversationId = raw.conversationId;
918
+ }
919
+ return Object.keys(session).length > 0 ? session : undefined;
920
+ }
921
+ function extractLegacyAccount(raw, handle) {
922
+ const out = { handle };
923
+ if (typeof raw.apiKey === "string")
924
+ out.apiKey = raw.apiKey;
925
+ if (typeof raw.baseUrl === "string")
926
+ out.baseUrl = raw.baseUrl;
927
+ const session = extractLegacySession(raw);
928
+ if (session)
929
+ out.session = session;
930
+ return out;
931
+ }
932
+ function normalizeGlobalConfig(raw) {
933
+ const normalized = {};
934
+ if (typeof raw.lspEnabled === "boolean")
935
+ normalized.lspEnabled = raw.lspEnabled;
936
+ if (raw.executionMode === "local" || raw.executionMode === "hosted") {
937
+ normalized.executionMode = raw.executionMode;
938
+ }
939
+ if (raw.mode === "general" || raw.mode === "builder") {
940
+ normalized.mode = raw.mode;
941
+ }
942
+ const accounts = [];
943
+ const sourceAccounts = Array.isArray(raw.accounts) ? raw.accounts : [];
944
+ for (const candidate of sourceAccounts) {
945
+ const account = sanitizeAccount(candidate);
946
+ if (!account)
947
+ continue;
948
+ if (findAccountIndex(accounts, account.handle) !== -1)
949
+ continue;
950
+ accounts.push(account);
951
+ }
952
+ if (accounts.length === 0) {
953
+ const legacyHandle = normalizeHandle(raw.activeHandle) || DEFAULT_ACCOUNT_HANDLE;
954
+ accounts.push(extractLegacyAccount(raw, legacyHandle));
955
+ }
956
+ const requested = normalizeHandle(raw.activeHandle);
957
+ const resolvedHandle = requested && findAccountIndex(accounts, requested) !== -1 ? accounts[findAccountIndex(accounts, requested)].handle : accounts[0].handle;
958
+ normalized.accounts = accounts;
959
+ normalized.activeHandle = resolvedHandle;
960
+ return normalized;
961
+ }
962
+ function resolveRequestedHandle(global, explicitAccount) {
963
+ const accounts = global.accounts ?? [];
964
+ const requested = normalizeHandle(explicitAccount) || normalizeHandle(process.env.OPENPOND_ACCOUNT) || normalizeHandle(global.activeHandle) || accounts[0]?.handle || DEFAULT_ACCOUNT_HANDLE;
965
+ const idx = findAccountIndex(accounts, requested);
966
+ return idx === -1 ? requested : accounts[idx].handle;
967
+ }
968
+ function ensureAccount(accounts, handle) {
969
+ const idx = findAccountIndex(accounts, handle);
970
+ if (idx !== -1) {
971
+ return accounts[idx];
972
+ }
973
+ const next = { handle };
974
+ accounts.push(next);
975
+ return next;
976
+ }
977
+ function cleanupAccount(account) {
978
+ if (account.session && Object.keys(account.session).length === 0) {
979
+ delete account.session;
980
+ }
981
+ }
982
+ function applyScopedKey(account, key, value, options) {
983
+ const shouldDelete = value === null || value === undefined && options.undefinedDeletes;
984
+ switch (key) {
985
+ case "apiKey": {
986
+ if (shouldDelete) {
987
+ delete account.apiKey;
988
+ return;
989
+ }
990
+ if (typeof value === "string") {
991
+ account.apiKey = value;
992
+ }
993
+ return;
994
+ }
995
+ case "baseUrl": {
996
+ if (shouldDelete) {
997
+ delete account.baseUrl;
998
+ return;
999
+ }
1000
+ if (typeof value === "string") {
1001
+ account.baseUrl = value;
1002
+ }
1003
+ return;
1004
+ }
1005
+ case "token":
1006
+ case "deviceCode":
1007
+ case "appId":
1008
+ case "conversationId": {
1009
+ if (!account.session)
1010
+ account.session = {};
1011
+ if (shouldDelete) {
1012
+ delete account.session[key];
1013
+ cleanupAccount(account);
1014
+ return;
1015
+ }
1016
+ if (typeof value === "string" || value === null) {
1017
+ account.session[key] = value;
1018
+ }
1019
+ cleanupAccount(account);
1020
+ return;
1021
+ }
1022
+ }
1023
+ }
1024
+ function applyAccountPatch(global, source, options) {
1025
+ const hasScopedPatch = ACCOUNT_SCOPED_KEYS.some((key) => hasOwn(source, key));
1026
+ if (!hasScopedPatch)
1027
+ return false;
1028
+ const accounts = global.accounts ?? [];
1029
+ const handle = resolveRequestedHandle(global, source.activeHandle);
1030
+ const account = ensureAccount(accounts, handle);
1031
+ for (const key of ACCOUNT_SCOPED_KEYS) {
1032
+ if (!hasOwn(source, key))
1033
+ continue;
1034
+ applyScopedKey(account, key, source[key], options);
1035
+ }
1036
+ global.accounts = accounts;
1037
+ global.activeHandle = handle;
1038
+ return true;
1039
+ }
1040
+ function applyTopLevelPatch(global, source) {
1041
+ if (hasOwn(source, "lspEnabled")) {
1042
+ if (typeof source.lspEnabled === "boolean") {
1043
+ global.lspEnabled = source.lspEnabled;
1044
+ } else if (source.lspEnabled === null) {
1045
+ delete global.lspEnabled;
1046
+ }
1047
+ }
1048
+ if (hasOwn(source, "executionMode")) {
1049
+ if (source.executionMode === "local" || source.executionMode === "hosted") {
1050
+ global.executionMode = source.executionMode;
1051
+ } else if (source.executionMode === null) {
1052
+ delete global.executionMode;
1053
+ }
1054
+ }
1055
+ if (hasOwn(source, "mode")) {
1056
+ if (source.mode === "general" || source.mode === "builder") {
1057
+ global.mode = source.mode;
1058
+ } else if (source.mode === null) {
1059
+ delete global.mode;
1060
+ }
1061
+ }
1062
+ if (typeof source.activeHandle === "string" && source.activeHandle.trim().length > 0) {
1063
+ const requested = resolveRequestedHandle(global, source.activeHandle);
1064
+ global.activeHandle = requested;
1065
+ }
1066
+ }
1067
+ async function writeGlobalConfig(next) {
851
1068
  const filePath = getGlobalConfigPath();
852
1069
  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);
1070
+ const payload = JSON.stringify(next, null, 2);
854
1071
  await fs2.writeFile(filePath, payload, "utf-8");
855
1072
  }
1073
+ async function loadGlobalConfig() {
1074
+ const raw = await loadConfigFile(getGlobalConfigPath());
1075
+ return normalizeGlobalConfig(raw);
1076
+ }
1077
+ async function loadConfig(options = {}) {
1078
+ const global = await loadGlobalConfig();
1079
+ const accounts = global.accounts ?? [];
1080
+ const requested = resolveRequestedHandle(global, options.account);
1081
+ const idx = findAccountIndex(accounts, requested);
1082
+ const account = idx === -1 ? null : accounts[idx];
1083
+ const session = account?.session;
1084
+ return {
1085
+ ...global,
1086
+ activeHandle: requested,
1087
+ apiKey: account?.apiKey,
1088
+ baseUrl: account?.baseUrl,
1089
+ token: session?.token,
1090
+ deviceCode: session?.deviceCode,
1091
+ appId: session?.appId,
1092
+ conversationId: session?.conversationId
1093
+ };
1094
+ }
1095
+ async function saveConfig(next) {
1096
+ const global = normalizeGlobalConfig(next);
1097
+ applyTopLevelPatch(global, next);
1098
+ applyAccountPatch(global, next, { undefinedDeletes: true });
1099
+ await writeGlobalConfig(global);
1100
+ }
856
1101
  async function saveGlobalConfig(next) {
857
- const filePath = getGlobalConfigPath();
858
- await fs2.mkdir(path2.dirname(filePath), { recursive: true });
859
1102
  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");
1103
+ applyTopLevelPatch(current, next);
1104
+ applyAccountPatch(current, next, { undefinedDeletes: false });
1105
+ await writeGlobalConfig(current);
872
1106
  }
873
1107
 
874
1108
  // src/index.ts
@@ -952,8 +1186,6 @@ async function resolveAppTarget(params) {
952
1186
  const candidates = [
953
1187
  app.repo,
954
1188
  app.gitRepo,
955
- app.internalToolName,
956
- app.name,
957
1189
  app.id
958
1190
  ].map(normalizeRepoName);
959
1191
  return candidates.includes(normalizedRepo);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "openpond-code",
3
- "version": "0.1.3",
3
+ "version": "0.2.0",
4
4
  "description": "OpenPond CLI (API key only)",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",