@topolo/sdk 0.21.1 → 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 +63 -28
- package/dist/node/credential-store.js +63 -28
- package/package.json +2 -2
|
@@ -481,6 +481,8 @@ 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:";
|
|
484
486
|
var MACOS_KEYCHAIN_WRITE_SCRIPT = [
|
|
485
487
|
"log_user 0",
|
|
486
488
|
"set timeout 10",
|
|
@@ -521,40 +523,73 @@ function createPlatformInstallationSecretBackend(options = {}) {
|
|
|
521
523
|
const spawn = options.spawn ?? ((command, args, spawnOptions) => (0, import_node_child_process.spawnSync)(command, args, spawnOptions));
|
|
522
524
|
const root = options.root ?? installationStorePath();
|
|
523
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
|
+
};
|
|
524
565
|
return {
|
|
525
566
|
async set(account, value) {
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
}
|
|
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
|
+
}
|
|
536
580
|
},
|
|
537
581
|
async get(account) {
|
|
538
|
-
const
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
"-w"
|
|
545
|
-
], { encoding: "utf8" });
|
|
546
|
-
if (result.status === 44) return null;
|
|
547
|
-
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("");
|
|
548
588
|
},
|
|
549
589
|
async delete(account) {
|
|
550
|
-
const
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
account,
|
|
554
|
-
"-s",
|
|
555
|
-
KEYCHAIN_SERVICE
|
|
556
|
-
], { encoding: "utf8" });
|
|
557
|
-
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);
|
|
558
593
|
}
|
|
559
594
|
};
|
|
560
595
|
}
|
|
@@ -428,6 +428,8 @@ 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:";
|
|
431
433
|
var MACOS_KEYCHAIN_WRITE_SCRIPT = [
|
|
432
434
|
"log_user 0",
|
|
433
435
|
"set timeout 10",
|
|
@@ -468,40 +470,73 @@ function createPlatformInstallationSecretBackend(options = {}) {
|
|
|
468
470
|
const spawn = options.spawn ?? ((command, args, spawnOptions) => spawnSync(command, args, spawnOptions));
|
|
469
471
|
const root = options.root ?? installationStorePath();
|
|
470
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
|
+
};
|
|
471
512
|
return {
|
|
472
513
|
async set(account, value) {
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
}
|
|
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
|
+
}
|
|
483
527
|
},
|
|
484
528
|
async get(account) {
|
|
485
|
-
const
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
"-w"
|
|
492
|
-
], { encoding: "utf8" });
|
|
493
|
-
if (result.status === 44) return null;
|
|
494
|
-
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("");
|
|
495
535
|
},
|
|
496
536
|
async delete(account) {
|
|
497
|
-
const
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
account,
|
|
501
|
-
"-s",
|
|
502
|
-
KEYCHAIN_SERVICE
|
|
503
|
-
], { encoding: "utf8" });
|
|
504
|
-
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);
|
|
505
540
|
}
|
|
506
541
|
};
|
|
507
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",
|