@topolo/sdk 0.21.0 → 0.21.2
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/dist/node/credential-store.cjs +79 -28
- package/dist/node/credential-store.js +79 -28
- package/package.json +2 -2
|
@@ -481,6 +481,24 @@ var import_node_fs = require("fs");
|
|
|
481
481
|
var import_node_os = require("os");
|
|
482
482
|
var import_node_path = require("path");
|
|
483
483
|
var KEYCHAIN_SERVICE = "topolo-external-agent-installation";
|
|
484
|
+
var MACOS_KEYCHAIN_CHUNK_SIZE = 96;
|
|
485
|
+
var MACOS_KEYCHAIN_CHUNK_PREFIX = "topolo-keychain-chunks:v1:";
|
|
486
|
+
var MACOS_KEYCHAIN_WRITE_SCRIPT = [
|
|
487
|
+
"log_user 0",
|
|
488
|
+
"set timeout 10",
|
|
489
|
+
"set secret [gets stdin]",
|
|
490
|
+
"spawn -noecho /usr/bin/security add-generic-password -U -a $env(TOPOLO_KEYCHAIN_ACCOUNT) -s $env(TOPOLO_KEYCHAIN_SERVICE) -w",
|
|
491
|
+
"for {set prompt 0} {$prompt < 2} {incr prompt} {",
|
|
492
|
+
" expect {",
|
|
493
|
+
' -re {(?i)password} { send -- "$secret\\r" }',
|
|
494
|
+
" timeout { exit 70 }",
|
|
495
|
+
" eof { exit 71 }",
|
|
496
|
+
" }",
|
|
497
|
+
"}",
|
|
498
|
+
"expect eof",
|
|
499
|
+
"set result [wait]",
|
|
500
|
+
"exit [lindex $result 3]"
|
|
501
|
+
].join("\n");
|
|
484
502
|
function installationStorePath() {
|
|
485
503
|
const override = process.env.TOPOLO_INSTALLATIONS_PATH?.trim();
|
|
486
504
|
if (override) return override;
|
|
@@ -505,40 +523,73 @@ function createPlatformInstallationSecretBackend(options = {}) {
|
|
|
505
523
|
const spawn = options.spawn ?? ((command, args, spawnOptions) => (0, import_node_child_process.spawnSync)(command, args, spawnOptions));
|
|
506
524
|
const root = options.root ?? installationStorePath();
|
|
507
525
|
if (platform2 === "darwin") {
|
|
526
|
+
const write = (account, value) => runOrThrow(spawn("/usr/bin/expect", ["-c", MACOS_KEYCHAIN_WRITE_SCRIPT], {
|
|
527
|
+
input: `${value}
|
|
528
|
+
`,
|
|
529
|
+
encoding: "utf8",
|
|
530
|
+
env: {
|
|
531
|
+
...process.env,
|
|
532
|
+
TOPOLO_KEYCHAIN_ACCOUNT: account,
|
|
533
|
+
TOPOLO_KEYCHAIN_SERVICE: KEYCHAIN_SERVICE
|
|
534
|
+
}
|
|
535
|
+
}), "macOS Keychain write");
|
|
536
|
+
const read = (account) => {
|
|
537
|
+
const result = spawn("security", [
|
|
538
|
+
"find-generic-password",
|
|
539
|
+
"-a",
|
|
540
|
+
account,
|
|
541
|
+
"-s",
|
|
542
|
+
KEYCHAIN_SERVICE,
|
|
543
|
+
"-w"
|
|
544
|
+
], { encoding: "utf8" });
|
|
545
|
+
if (result.status === 44) return null;
|
|
546
|
+
return runOrThrow(result, "macOS Keychain read");
|
|
547
|
+
};
|
|
548
|
+
const remove = (account) => {
|
|
549
|
+
const result = spawn("security", [
|
|
550
|
+
"delete-generic-password",
|
|
551
|
+
"-a",
|
|
552
|
+
account,
|
|
553
|
+
"-s",
|
|
554
|
+
KEYCHAIN_SERVICE
|
|
555
|
+
], { encoding: "utf8" });
|
|
556
|
+
if (result.status !== 44) runOrThrow(result, "macOS Keychain delete");
|
|
557
|
+
};
|
|
558
|
+
const chunkAccount = (account, index) => `${account}:chunk:${index}`;
|
|
559
|
+
const chunkCount = (value) => {
|
|
560
|
+
if (!value?.startsWith(MACOS_KEYCHAIN_CHUNK_PREFIX)) return 0;
|
|
561
|
+
const count = Number(value.slice(MACOS_KEYCHAIN_CHUNK_PREFIX.length));
|
|
562
|
+
if (!Number.isSafeInteger(count) || count < 1) throw new Error("macOS Keychain chunk manifest is invalid.");
|
|
563
|
+
return count;
|
|
564
|
+
};
|
|
508
565
|
return {
|
|
509
566
|
async set(account, value) {
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
567
|
+
const previousCount = chunkCount(read(account));
|
|
568
|
+
const chunks = Array.from(
|
|
569
|
+
{ length: Math.max(1, Math.ceil(value.length / MACOS_KEYCHAIN_CHUNK_SIZE)) },
|
|
570
|
+
(_, index) => value.slice(index * MACOS_KEYCHAIN_CHUNK_SIZE, (index + 1) * MACOS_KEYCHAIN_CHUNK_SIZE)
|
|
571
|
+
);
|
|
572
|
+
try {
|
|
573
|
+
chunks.forEach((chunk, index) => write(chunkAccount(account, index), chunk));
|
|
574
|
+
write(account, `${MACOS_KEYCHAIN_CHUNK_PREFIX}${chunks.length}`);
|
|
575
|
+
for (let index = chunks.length; index < previousCount; index += 1) remove(chunkAccount(account, index));
|
|
576
|
+
} catch (error) {
|
|
577
|
+
chunks.forEach((_, index) => remove(chunkAccount(account, index)));
|
|
578
|
+
throw error;
|
|
579
|
+
}
|
|
520
580
|
},
|
|
521
581
|
async get(account) {
|
|
522
|
-
const
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
"-w"
|
|
529
|
-
], { encoding: "utf8" });
|
|
530
|
-
if (result.status === 44) return null;
|
|
531
|
-
return runOrThrow(result, "macOS Keychain read");
|
|
582
|
+
const manifest = read(account);
|
|
583
|
+
const count = chunkCount(manifest);
|
|
584
|
+
if (!count) return manifest;
|
|
585
|
+
const chunks = Array.from({ length: count }, (_, index) => read(chunkAccount(account, index)));
|
|
586
|
+
if (chunks.some((chunk) => chunk === null)) throw new Error("macOS Keychain installation secret is incomplete.");
|
|
587
|
+
return chunks.join("");
|
|
532
588
|
},
|
|
533
589
|
async delete(account) {
|
|
534
|
-
const
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
account,
|
|
538
|
-
"-s",
|
|
539
|
-
KEYCHAIN_SERVICE
|
|
540
|
-
], { encoding: "utf8" });
|
|
541
|
-
if (result.status !== 44) runOrThrow(result, "macOS Keychain delete");
|
|
590
|
+
const count = chunkCount(read(account));
|
|
591
|
+
for (let index = 0; index < count; index += 1) remove(chunkAccount(account, index));
|
|
592
|
+
remove(account);
|
|
542
593
|
}
|
|
543
594
|
};
|
|
544
595
|
}
|
|
@@ -428,6 +428,24 @@ import { existsSync } from "fs";
|
|
|
428
428
|
import { homedir, platform as hostPlatform } from "os";
|
|
429
429
|
import { dirname, join } from "path";
|
|
430
430
|
var KEYCHAIN_SERVICE = "topolo-external-agent-installation";
|
|
431
|
+
var MACOS_KEYCHAIN_CHUNK_SIZE = 96;
|
|
432
|
+
var MACOS_KEYCHAIN_CHUNK_PREFIX = "topolo-keychain-chunks:v1:";
|
|
433
|
+
var MACOS_KEYCHAIN_WRITE_SCRIPT = [
|
|
434
|
+
"log_user 0",
|
|
435
|
+
"set timeout 10",
|
|
436
|
+
"set secret [gets stdin]",
|
|
437
|
+
"spawn -noecho /usr/bin/security add-generic-password -U -a $env(TOPOLO_KEYCHAIN_ACCOUNT) -s $env(TOPOLO_KEYCHAIN_SERVICE) -w",
|
|
438
|
+
"for {set prompt 0} {$prompt < 2} {incr prompt} {",
|
|
439
|
+
" expect {",
|
|
440
|
+
' -re {(?i)password} { send -- "$secret\\r" }',
|
|
441
|
+
" timeout { exit 70 }",
|
|
442
|
+
" eof { exit 71 }",
|
|
443
|
+
" }",
|
|
444
|
+
"}",
|
|
445
|
+
"expect eof",
|
|
446
|
+
"set result [wait]",
|
|
447
|
+
"exit [lindex $result 3]"
|
|
448
|
+
].join("\n");
|
|
431
449
|
function installationStorePath() {
|
|
432
450
|
const override = process.env.TOPOLO_INSTALLATIONS_PATH?.trim();
|
|
433
451
|
if (override) return override;
|
|
@@ -452,40 +470,73 @@ function createPlatformInstallationSecretBackend(options = {}) {
|
|
|
452
470
|
const spawn = options.spawn ?? ((command, args, spawnOptions) => spawnSync(command, args, spawnOptions));
|
|
453
471
|
const root = options.root ?? installationStorePath();
|
|
454
472
|
if (platform2 === "darwin") {
|
|
473
|
+
const write = (account, value) => runOrThrow(spawn("/usr/bin/expect", ["-c", MACOS_KEYCHAIN_WRITE_SCRIPT], {
|
|
474
|
+
input: `${value}
|
|
475
|
+
`,
|
|
476
|
+
encoding: "utf8",
|
|
477
|
+
env: {
|
|
478
|
+
...process.env,
|
|
479
|
+
TOPOLO_KEYCHAIN_ACCOUNT: account,
|
|
480
|
+
TOPOLO_KEYCHAIN_SERVICE: KEYCHAIN_SERVICE
|
|
481
|
+
}
|
|
482
|
+
}), "macOS Keychain write");
|
|
483
|
+
const read = (account) => {
|
|
484
|
+
const result = spawn("security", [
|
|
485
|
+
"find-generic-password",
|
|
486
|
+
"-a",
|
|
487
|
+
account,
|
|
488
|
+
"-s",
|
|
489
|
+
KEYCHAIN_SERVICE,
|
|
490
|
+
"-w"
|
|
491
|
+
], { encoding: "utf8" });
|
|
492
|
+
if (result.status === 44) return null;
|
|
493
|
+
return runOrThrow(result, "macOS Keychain read");
|
|
494
|
+
};
|
|
495
|
+
const remove = (account) => {
|
|
496
|
+
const result = spawn("security", [
|
|
497
|
+
"delete-generic-password",
|
|
498
|
+
"-a",
|
|
499
|
+
account,
|
|
500
|
+
"-s",
|
|
501
|
+
KEYCHAIN_SERVICE
|
|
502
|
+
], { encoding: "utf8" });
|
|
503
|
+
if (result.status !== 44) runOrThrow(result, "macOS Keychain delete");
|
|
504
|
+
};
|
|
505
|
+
const chunkAccount = (account, index) => `${account}:chunk:${index}`;
|
|
506
|
+
const chunkCount = (value) => {
|
|
507
|
+
if (!value?.startsWith(MACOS_KEYCHAIN_CHUNK_PREFIX)) return 0;
|
|
508
|
+
const count = Number(value.slice(MACOS_KEYCHAIN_CHUNK_PREFIX.length));
|
|
509
|
+
if (!Number.isSafeInteger(count) || count < 1) throw new Error("macOS Keychain chunk manifest is invalid.");
|
|
510
|
+
return count;
|
|
511
|
+
};
|
|
455
512
|
return {
|
|
456
513
|
async set(account, value) {
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
514
|
+
const previousCount = chunkCount(read(account));
|
|
515
|
+
const chunks = Array.from(
|
|
516
|
+
{ length: Math.max(1, Math.ceil(value.length / MACOS_KEYCHAIN_CHUNK_SIZE)) },
|
|
517
|
+
(_, index) => value.slice(index * MACOS_KEYCHAIN_CHUNK_SIZE, (index + 1) * MACOS_KEYCHAIN_CHUNK_SIZE)
|
|
518
|
+
);
|
|
519
|
+
try {
|
|
520
|
+
chunks.forEach((chunk, index) => write(chunkAccount(account, index), chunk));
|
|
521
|
+
write(account, `${MACOS_KEYCHAIN_CHUNK_PREFIX}${chunks.length}`);
|
|
522
|
+
for (let index = chunks.length; index < previousCount; index += 1) remove(chunkAccount(account, index));
|
|
523
|
+
} catch (error) {
|
|
524
|
+
chunks.forEach((_, index) => remove(chunkAccount(account, index)));
|
|
525
|
+
throw error;
|
|
526
|
+
}
|
|
467
527
|
},
|
|
468
528
|
async get(account) {
|
|
469
|
-
const
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
"-w"
|
|
476
|
-
], { encoding: "utf8" });
|
|
477
|
-
if (result.status === 44) return null;
|
|
478
|
-
return runOrThrow(result, "macOS Keychain read");
|
|
529
|
+
const manifest = read(account);
|
|
530
|
+
const count = chunkCount(manifest);
|
|
531
|
+
if (!count) return manifest;
|
|
532
|
+
const chunks = Array.from({ length: count }, (_, index) => read(chunkAccount(account, index)));
|
|
533
|
+
if (chunks.some((chunk) => chunk === null)) throw new Error("macOS Keychain installation secret is incomplete.");
|
|
534
|
+
return chunks.join("");
|
|
479
535
|
},
|
|
480
536
|
async delete(account) {
|
|
481
|
-
const
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
account,
|
|
485
|
-
"-s",
|
|
486
|
-
KEYCHAIN_SERVICE
|
|
487
|
-
], { encoding: "utf8" });
|
|
488
|
-
if (result.status !== 44) runOrThrow(result, "macOS Keychain delete");
|
|
537
|
+
const count = chunkCount(read(account));
|
|
538
|
+
for (let index = 0; index < count; index += 1) remove(chunkAccount(account, index));
|
|
539
|
+
remove(account);
|
|
489
540
|
}
|
|
490
541
|
};
|
|
491
542
|
}
|
package/package.json
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@topolo/sdk",
|
|
3
3
|
"packageManager": "pnpm@10.17.1",
|
|
4
|
-
"version": "0.21.
|
|
5
|
-
"topoloPublishedVersion": "0.21.
|
|
4
|
+
"version": "0.21.2",
|
|
5
|
+
"topoloPublishedVersion": "0.21.2",
|
|
6
6
|
"description": "Typed client SDK for the Topolo platform. Used by TopoloCli, TopoloMCP, and third-party agents.",
|
|
7
7
|
"license": "UNLICENSED",
|
|
8
8
|
"author": "Topolo",
|