@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.
@@ -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
- runOrThrow(spawn("security", [
511
- "add-generic-password",
512
- "-U",
513
- "-a",
514
- account,
515
- "-s",
516
- KEYCHAIN_SERVICE,
517
- "-w"
518
- ], { input: `${value}
519
- `, encoding: "utf8" }), "macOS Keychain write");
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 result = spawn("security", [
523
- "find-generic-password",
524
- "-a",
525
- account,
526
- "-s",
527
- KEYCHAIN_SERVICE,
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 result = spawn("security", [
535
- "delete-generic-password",
536
- "-a",
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
- runOrThrow(spawn("security", [
458
- "add-generic-password",
459
- "-U",
460
- "-a",
461
- account,
462
- "-s",
463
- KEYCHAIN_SERVICE,
464
- "-w"
465
- ], { input: `${value}
466
- `, encoding: "utf8" }), "macOS Keychain write");
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 result = spawn("security", [
470
- "find-generic-password",
471
- "-a",
472
- account,
473
- "-s",
474
- KEYCHAIN_SERVICE,
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 result = spawn("security", [
482
- "delete-generic-password",
483
- "-a",
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.0",
5
- "topoloPublishedVersion": "0.21.0",
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",