openpond-code 0.1.4 → 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 +6 -0
- package/README.md +12 -2
- package/dist/cli.js +276 -21
- package/dist/config.d.ts +19 -1
- package/dist/index.js +254 -20
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
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 `
|
|
103
|
-
via `
|
|
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/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
|
-
|
|
449
|
-
return
|
|
457
|
+
function hasOwn(value, key) {
|
|
458
|
+
return Object.prototype.hasOwnProperty.call(value, key);
|
|
450
459
|
}
|
|
451
|
-
|
|
452
|
-
|
|
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
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
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 (
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
}
|
|
@@ -1001,8 +1249,11 @@ function printHelp() {
|
|
|
1001
1249
|
console.log(" openpond apps positions tx [--method <GET|POST>] [--body <json>] [--params <json>]");
|
|
1002
1250
|
console.log(" openpond opentool <init|validate|build> [args]");
|
|
1003
1251
|
console.log("");
|
|
1252
|
+
console.log("Global options:");
|
|
1253
|
+
console.log(" --account <name> (alias: --profile <name>)");
|
|
1254
|
+
console.log("");
|
|
1004
1255
|
console.log("Env:");
|
|
1005
|
-
console.log(" OPENPOND_API_KEY,
|
|
1256
|
+
console.log(" OPENPOND_API_KEY, OPENPOND_ACCOUNT, OPENPOND_API_URL, OPENPOND_TOOL_URL");
|
|
1006
1257
|
}
|
|
1007
1258
|
async function runLogin(options) {
|
|
1008
1259
|
const config = await loadConfig();
|
|
@@ -1015,7 +1266,7 @@ async function runLogin(options) {
|
|
|
1015
1266
|
if (!apiKey.startsWith("opk_")) {
|
|
1016
1267
|
console.log("warning: API keys usually start with opk_.");
|
|
1017
1268
|
}
|
|
1018
|
-
await saveGlobalConfig({ apiKey, baseUrl });
|
|
1269
|
+
await saveGlobalConfig({ apiKey, baseUrl, activeHandle: config.activeHandle });
|
|
1019
1270
|
console.log("saved api key to ~/.openpond/config.json");
|
|
1020
1271
|
}
|
|
1021
1272
|
async function runToolList(options, target) {
|
|
@@ -1661,6 +1912,10 @@ async function runAppsTradeFacts(options) {
|
|
|
1661
1912
|
}
|
|
1662
1913
|
async function main() {
|
|
1663
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
|
+
}
|
|
1664
1919
|
if (!command || command === "help") {
|
|
1665
1920
|
printHelp();
|
|
1666
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
|
-
|
|
845
|
-
return
|
|
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
|
-
|
|
848
|
-
return
|
|
862
|
+
function handleEquals(left, right) {
|
|
863
|
+
return left.trim().toLowerCase() === right.trim().toLowerCase();
|
|
849
864
|
}
|
|
850
|
-
|
|
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(
|
|
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
|
-
|
|
861
|
-
|
|
862
|
-
|
|
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
|