@topolo/cli 0.13.1 → 0.13.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.
Files changed (2) hide show
  1. package/dist/index.js +205 -65
  2. package/package.json +4 -4
package/dist/index.js CHANGED
@@ -90,7 +90,7 @@ import { platform as platform2 } from "os";
90
90
  import { createInterface } from "readline/promises";
91
91
  import { stdin as input } from "process";
92
92
 
93
- // node_modules/.pnpm/@topolo+sdk@0.23.17/node_modules/@topolo/sdk/dist/index.js
93
+ // node_modules/@topolo/sdk/dist/index.js
94
94
  function record(value) {
95
95
  return Boolean(value) && typeof value === "object" && !Array.isArray(value);
96
96
  }
@@ -3187,9 +3187,18 @@ var TopoloOAuth = class {
3187
3187
  if (!res.ok) {
3188
3188
  if (parsed && typeof parsed === "object" && "error" in parsed) {
3189
3189
  const err = parsed;
3190
- throw new TopoloAuthError(err.error_description || err.error, err.error);
3190
+ throw new TopoloAuthError(err.error_description || err.error, err.error, {
3191
+ status: res.status,
3192
+ service: "auth",
3193
+ path,
3194
+ requestId: res.headers.get("x-topolo-request-id") ?? res.headers.get("x-request-id") ?? void 0,
3195
+ retryAfter: res.headers.get("retry-after") ?? void 0
3196
+ });
3191
3197
  }
3192
- throw new TopoloHttpError("auth", path, res.status, parsed);
3198
+ throw new TopoloHttpError("auth", path, res.status, parsed, void 0, {
3199
+ requestId: res.headers.get("x-topolo-request-id") ?? res.headers.get("x-request-id") ?? void 0,
3200
+ retryAfter: res.headers.get("retry-after") ?? void 0
3201
+ });
3193
3202
  }
3194
3203
  return parsed;
3195
3204
  }
@@ -3691,9 +3700,10 @@ async function pollUntilApproved(params) {
3691
3700
  throw new Error("Device code expired. Run `topolo auth login` again.");
3692
3701
  }
3693
3702
 
3694
- // node_modules/.pnpm/@topolo+sdk@0.23.17/node_modules/@topolo/sdk/dist/node/credential-store.js
3695
- import { mkdir as mkdir2, readFile as readFile2, writeFile as writeFile2, chmod as chmod2, rm as rm2, stat } from "fs/promises";
3696
- import { existsSync as existsSync2 } from "fs";
3703
+ // node_modules/@topolo/sdk/dist/node/credential-store.js
3704
+ import { mkdir as mkdir2, readFile as readFile2, writeFile as writeFile2, chmod as chmod2, lstat, readdir as readdir2, rename as rename2, link } from "fs/promises";
3705
+ import { existsSync as existsSync2, lstatSync, readdirSync, readFileSync, openSync, writeFileSync, fsyncSync, closeSync, renameSync, unlinkSync, rmdirSync } from "fs";
3706
+ import { randomUUID as randomUUID2 } from "crypto";
3697
3707
  import { homedir as homedir2, platform } from "os";
3698
3708
  import { dirname as dirname2, join as join3 } from "path";
3699
3709
  import { createHash, randomUUID } from "crypto";
@@ -4101,9 +4111,18 @@ var TopoloOAuth2 = class {
4101
4111
  if (!res.ok) {
4102
4112
  if (parsed && typeof parsed === "object" && "error" in parsed) {
4103
4113
  const err = parsed;
4104
- throw new TopoloAuthError2(err.error_description || err.error, err.error);
4114
+ throw new TopoloAuthError2(err.error_description || err.error, err.error, {
4115
+ status: res.status,
4116
+ service: "auth",
4117
+ path,
4118
+ requestId: res.headers.get("x-topolo-request-id") ?? res.headers.get("x-request-id") ?? void 0,
4119
+ retryAfter: res.headers.get("retry-after") ?? void 0
4120
+ });
4105
4121
  }
4106
- throw new TopoloHttpError2("auth", path, res.status, parsed);
4122
+ throw new TopoloHttpError2("auth", path, res.status, parsed, void 0, {
4123
+ requestId: res.headers.get("x-topolo-request-id") ?? res.headers.get("x-request-id") ?? void 0,
4124
+ retryAfter: res.headers.get("retry-after") ?? void 0
4125
+ });
4107
4126
  }
4108
4127
  return parsed;
4109
4128
  }
@@ -4550,38 +4569,149 @@ async function resolveCredential(environment = selectedEnvironment()) {
4550
4569
  var REFRESH_SKEW_SECONDS = 60;
4551
4570
  var CONFIG_LOCK_RETRY_MS = 25;
4552
4571
  var CONFIG_LOCK_TIMEOUT_MS = 5e3;
4553
- var CONFIG_LOCK_STALE_MS = 12e4;
4572
+ var ConfigOwnershipError = class extends TopoloSdkError2 {
4573
+ constructor() {
4574
+ super("state_conflict", "Credential configuration ownership cannot be verified. Retain the unresolved operation instead of starting another renewal.");
4575
+ }
4576
+ };
4554
4577
  async function withConfigWriteLock(operation) {
4555
4578
  const lockPath = `${configPath()}.lock`;
4556
4579
  await mkdir2(dirname2(lockPath), { recursive: true });
4557
- const startedAt = Date.now();
4580
+ const startedAt = performance.now();
4558
4581
  while (true) {
4559
4582
  try {
4560
- await mkdir2(lockPath);
4583
+ await mkdir2(lockPath, { mode: 448 });
4561
4584
  break;
4562
4585
  } catch (error) {
4563
4586
  if (error.code !== "EEXIST") throw error;
4564
- try {
4565
- const lock = await stat(lockPath);
4566
- if (Date.now() - lock.mtimeMs > CONFIG_LOCK_STALE_MS) {
4567
- await rm2(lockPath, { recursive: true, force: true });
4568
- continue;
4569
- }
4570
- } catch (inspectionError) {
4571
- if (inspectionError.code !== "ENOENT") throw inspectionError;
4572
- continue;
4573
- }
4574
- if (Date.now() - startedAt >= CONFIG_LOCK_TIMEOUT_MS) {
4575
- throw new Error(`Timed out waiting for credential configuration lock at ${lockPath}.`);
4587
+ if (performance.now() - startedAt >= CONFIG_LOCK_TIMEOUT_MS) {
4588
+ throw new TopoloSdkError2("state_conflict", "Credential configuration is still held or unresolved. This waiter did not dispatch a renewal.");
4576
4589
  }
4577
4590
  await new Promise((resolve4) => setTimeout(resolve4, CONFIG_LOCK_RETRY_MS));
4578
4591
  }
4579
4592
  }
4593
+ const directory = await lstat(lockPath);
4594
+ const identity = randomUUID2();
4595
+ const markerName = "owner-" + identity + ".json";
4596
+ const markerPath = join3(lockPath, markerName);
4597
+ const marker = JSON.stringify({ version: 1, identity, pid: process.pid });
4598
+ await writeFile2(markerPath, marker, { flag: "wx", mode: 384 });
4599
+ const assertDirectory = async () => {
4600
+ const current = await lstat(lockPath);
4601
+ if (!current.isDirectory() || current.isSymbolicLink() || current.dev !== directory.dev || current.ino !== directory.ino)
4602
+ throw new ConfigOwnershipError();
4603
+ };
4604
+ const assertOwned = async () => {
4605
+ try {
4606
+ await assertDirectory();
4607
+ const entries = await readdir2(lockPath);
4608
+ if (entries.length !== 1 || entries[0] !== markerName) throw new ConfigOwnershipError();
4609
+ const entry = await lstat(markerPath);
4610
+ if (!entry.isFile() || entry.isSymbolicLink() || await readFile2(markerPath, "utf8") !== marker)
4611
+ throw new ConfigOwnershipError();
4612
+ } catch {
4613
+ throw new ConfigOwnershipError();
4614
+ }
4615
+ };
4616
+ const assertDirectorySync = () => {
4617
+ const current = lstatSync(lockPath);
4618
+ if (!current.isDirectory() || current.isSymbolicLink() || current.dev !== directory.dev || current.ino !== directory.ino)
4619
+ throw new ConfigOwnershipError();
4620
+ };
4621
+ const assertOwnedSync = () => {
4622
+ assertDirectorySync();
4623
+ const entries = readdirSync(lockPath);
4624
+ const entry = lstatSync(markerPath);
4625
+ if (entries.length !== 1 || entries[0] !== markerName || !entry.isFile() || entry.isSymbolicLink() || readFileSync(markerPath, "utf8") !== marker) throw new ConfigOwnershipError();
4626
+ };
4627
+ const persist = async (update) => {
4628
+ const path = configPath();
4629
+ await mkdir2(dirname2(path), { recursive: true });
4630
+ const temporary = join3(dirname2(path), ".credential-write-" + randomUUID2());
4631
+ let temporaryOwned = false;
4632
+ try {
4633
+ assertOwnedSync();
4634
+ const entry = lstatSync(path);
4635
+ if (!entry.isFile() || entry.isSymbolicLink()) throw new ConfigOwnershipError();
4636
+ const before = readFileSync(path, "utf8");
4637
+ const updated = update(JSON.parse(before));
4638
+ const fd = openSync(temporary, "wx", 384);
4639
+ temporaryOwned = true;
4640
+ try {
4641
+ writeFileSync(fd, JSON.stringify(updated, null, 2) + "\n");
4642
+ fsyncSync(fd);
4643
+ } finally {
4644
+ closeSync(fd);
4645
+ }
4646
+ assertOwnedSync();
4647
+ if (readFileSync(path, "utf8") !== before) throw new ConfigOwnershipError();
4648
+ renameSync(temporary, path);
4649
+ temporaryOwned = false;
4650
+ } finally {
4651
+ if (temporaryOwned) unlinkSync(temporary);
4652
+ }
4653
+ };
4654
+ const release = async () => {
4655
+ await assertOwned();
4656
+ const quarantine = join3(dirname2(lockPath), ".credential-release-" + randomUUID2());
4657
+ await mkdir2(quarantine, { mode: 448 });
4658
+ const captured = join3(quarantine, markerName);
4659
+ try {
4660
+ await assertOwned();
4661
+ await rename2(markerPath, captured);
4662
+ assertDirectorySync();
4663
+ const entry = lstatSync(captured);
4664
+ if (!entry.isFile() || entry.isSymbolicLink() || readFileSync(captured, "utf8") !== marker || readdirSync(lockPath).length) throw new ConfigOwnershipError();
4665
+ unlinkSync(captured);
4666
+ rmdirSync(quarantine);
4667
+ assertDirectorySync();
4668
+ if (readdirSync(lockPath).length) throw new ConfigOwnershipError();
4669
+ rmdirSync(lockPath);
4670
+ } catch {
4671
+ if (existsSync2(captured)) {
4672
+ try {
4673
+ await link(captured, markerPath);
4674
+ } catch {
4675
+ }
4676
+ }
4677
+ throw new ConfigOwnershipError();
4678
+ }
4679
+ };
4580
4680
  try {
4581
- return await operation();
4681
+ await assertOwned();
4682
+ const result = await operation(assertOwned, persist);
4683
+ await assertOwned();
4684
+ return result;
4582
4685
  } finally {
4583
- await rm2(lockPath, { recursive: true, force: true });
4686
+ await release();
4687
+ }
4688
+ }
4689
+ function renewalFailure(error) {
4690
+ const path = "/api/developer-oauth/token";
4691
+ if (error instanceof ConfigOwnershipError) return new ConfigOwnershipError();
4692
+ if (error instanceof TopoloAuthError2 && error.code === "invalid_grant") {
4693
+ return new TopoloAuthError2("The saved Topolo sign-in can no longer be renewed. Sign in again.", "invalid_grant", {
4694
+ status: error.status,
4695
+ service: "auth",
4696
+ path,
4697
+ requestId: error.requestId
4698
+ });
4699
+ }
4700
+ if (error instanceof TopoloAuthError2 || error instanceof TopoloHttpError2) {
4701
+ return new TopoloHttpError2(
4702
+ "auth",
4703
+ path,
4704
+ error.status,
4705
+ void 0,
4706
+ "Topolo sign-in renewal failed. Retry the request; the saved sign-in has been retained.",
4707
+ { requestId: error.requestId, retryAfter: error.retryAfter }
4708
+ );
4584
4709
  }
4710
+ const timeout = error instanceof Error && ["AbortError", "TimeoutError"].includes(error.name);
4711
+ return new TopoloSdkError2(
4712
+ timeout ? "request_timeout" : "network_error",
4713
+ "Topolo sign-in renewal could not reach Auth. Retry the request; the saved sign-in has been retained."
4714
+ );
4585
4715
  }
4586
4716
  async function refreshCredentialIfNeeded(environment = selectedEnvironment(), options = {}) {
4587
4717
  const prefix = envVarPrefix(environment);
@@ -4591,16 +4721,16 @@ async function refreshCredentialIfNeeded(environment = selectedEnvironment(), op
4591
4721
  const observedProfile = profileForEnvironment(await readConfig(), environment);
4592
4722
  const observed = observedProfile.credential;
4593
4723
  if (!observed || observed.kind !== "access_token" || !observed.refreshToken || !observedProfile.oauthClientId || !options.force && observed.expiresAt && observed.expiresAt - REFRESH_SKEW_SECONDS > Math.floor(Date.now() / 1e3)) return observed ?? null;
4594
- return withConfigWriteLock(async () => {
4724
+ return withConfigWriteLock(async (_assertOwned, persist) => {
4595
4725
  const config = await readConfig();
4596
4726
  const profile = profileForEnvironment(config, environment);
4597
4727
  const cred = profile.credential;
4598
4728
  if (!cred || cred.kind !== "access_token") return cred ?? null;
4599
4729
  if (!cred.refreshToken || !profile.oauthClientId) return cred;
4600
- if (options.force && observed?.kind === "access_token" && observed.accessToken !== cred.accessToken) {
4730
+ const now = Math.floor(Date.now() / 1e3);
4731
+ if (options.force && observed?.kind === "access_token" && observed.accessToken !== cred.accessToken && cred.expiresAt !== void 0 && cred.expiresAt > now) {
4601
4732
  return cred;
4602
4733
  }
4603
- const now = Math.floor(Date.now() / 1e3);
4604
4734
  if (!options.force && cred.expiresAt && cred.expiresAt - REFRESH_SKEW_SECONDS > now) return cred;
4605
4735
  const serviceUrls = resolveServiceUrls(profile.serviceUrls, environment);
4606
4736
  if (environment === "local" && !serviceUrls?.auth) {
@@ -4623,23 +4753,27 @@ async function refreshCredentialIfNeeded(environment = selectedEnvironment(), op
4623
4753
  ...tokens.refresh_token ? { refreshToken: tokens.refresh_token } : { refreshToken: cred.refreshToken },
4624
4754
  expiresAt: Math.floor(Date.now() / 1e3) + tokens.expires_in
4625
4755
  };
4626
- const activeContext = profile.activeContext;
4627
- const activeEntry = activeContext ? profile.contexts?.[activeContext] : void 0;
4628
- const updated = activeContext && activeEntry ? writeProfileConfig(config, environment, {
4629
- credential: refreshed,
4630
- contexts: {
4631
- ...profile.contexts ?? {},
4632
- [activeContext]: {
4756
+ await persist((current) => {
4757
+ const latest = profileForEnvironment(current, environment);
4758
+ const same = (a, b) => JSON.stringify(a) === JSON.stringify(b);
4759
+ if (!same(latest.credential, cred) || latest.oauthClientId !== profile.oauthClientId || latest.activeContext !== profile.activeContext)
4760
+ throw new ConfigOwnershipError();
4761
+ const activeContext = latest.activeContext;
4762
+ const activeEntry = activeContext ? latest.contexts?.[activeContext] : void 0;
4763
+ const previousEntry = activeContext ? profile.contexts?.[activeContext] : void 0;
4764
+ if (!same(activeEntry?.credential, previousEntry?.credential)) throw new ConfigOwnershipError();
4765
+ return activeContext && activeEntry ? writeProfileConfig(current, environment, {
4766
+ credential: refreshed,
4767
+ contexts: { ...latest.contexts, [activeContext]: {
4633
4768
  ...activeEntry,
4634
4769
  credential: refreshed,
4635
- oauthClientId: activeEntry.oauthClientId ?? profile.oauthClientId
4636
- }
4637
- }
4638
- }) : environment === "production" && !config.profiles?.production ? { ...config, credential: refreshed } : writeProfileConfig(config, environment, { credential: refreshed });
4639
- await writeConfig(updated);
4770
+ oauthClientId: activeEntry.oauthClientId ?? latest.oauthClientId
4771
+ } }
4772
+ }) : environment === "production" && !current.profiles?.production ? { ...current, credential: refreshed } : writeProfileConfig(current, environment, { credential: refreshed });
4773
+ });
4640
4774
  return refreshed;
4641
- } catch {
4642
- return cred;
4775
+ } catch (error) {
4776
+ throw renewalFailure(error);
4643
4777
  }
4644
4778
  });
4645
4779
  }
@@ -4648,7 +4782,7 @@ async function refreshContextCredentialIfNeeded(environment, key, options = {})
4648
4782
  const observedContext = observedProfile.contexts?.[key];
4649
4783
  const observed = observedContext?.credential;
4650
4784
  if (!observed || observed.kind !== "access_token" || !observed.refreshToken || !(observedContext.oauthClientId ?? observedProfile.oauthClientId) || !options.force && observed.expiresAt && observed.expiresAt - REFRESH_SKEW_SECONDS > Math.floor(Date.now() / 1e3)) return observed ?? null;
4651
- return withConfigWriteLock(async () => {
4785
+ return withConfigWriteLock(async (_assertOwned, persist) => {
4652
4786
  const config = await readConfig();
4653
4787
  const profile = profileForEnvironment(config, environment);
4654
4788
  const context = profile.contexts?.[key];
@@ -4657,6 +4791,7 @@ async function refreshContextCredentialIfNeeded(environment, key, options = {})
4657
4791
  const oauthClientId = context.oauthClientId ?? profile.oauthClientId;
4658
4792
  if (!credential.refreshToken || !oauthClientId) return credential;
4659
4793
  const now = Math.floor(Date.now() / 1e3);
4794
+ if (options.force && observed.accessToken !== credential.accessToken && credential.expiresAt !== void 0 && credential.expiresAt > now) return credential;
4660
4795
  if (!options.force && credential.expiresAt && credential.expiresAt - REFRESH_SKEW_SECONDS > now) return credential;
4661
4796
  const serviceUrls = resolveServiceUrls(profile.serviceUrls, environment);
4662
4797
  if (environment === "local" && !serviceUrls?.auth) {
@@ -4677,16 +4812,21 @@ async function refreshContextCredentialIfNeeded(environment, key, options = {})
4677
4812
  ...tokens.refresh_token ? { refreshToken: tokens.refresh_token } : { refreshToken: credential.refreshToken },
4678
4813
  expiresAt: Math.floor(Date.now() / 1e3) + tokens.expires_in
4679
4814
  };
4680
- await writeConfig(writeProfileConfig(config, environment, {
4681
- ...profile.activeContext === key ? { credential: refreshed } : {},
4682
- contexts: {
4683
- ...profile.contexts ?? {},
4684
- [key]: { ...context, credential: refreshed, oauthClientId }
4685
- }
4686
- }));
4815
+ await persist((current) => {
4816
+ const latest = profileForEnvironment(current, environment);
4817
+ const latestContext = latest.contexts?.[key];
4818
+ if (!latestContext || JSON.stringify(latestContext.credential) !== JSON.stringify(credential) || (latestContext.oauthClientId ?? latest.oauthClientId) !== oauthClientId || latestContext.contextType !== context.contextType || latestContext.organizationId !== context.organizationId)
4819
+ throw new ConfigOwnershipError();
4820
+ if (latest.activeContext === key && JSON.stringify(latest.credential) !== JSON.stringify(profile.activeContext === key ? profile.credential : credential))
4821
+ throw new ConfigOwnershipError();
4822
+ return writeProfileConfig(current, environment, {
4823
+ ...latest.activeContext === key ? { credential: refreshed } : {},
4824
+ contexts: { ...latest.contexts, [key]: { ...latestContext, credential: refreshed, oauthClientId } }
4825
+ });
4826
+ });
4687
4827
  return refreshed;
4688
- } catch {
4689
- return credential;
4828
+ } catch (error) {
4829
+ throw renewalFailure(error);
4690
4830
  }
4691
4831
  });
4692
4832
  }
@@ -4845,7 +4985,7 @@ function normalizeError(err) {
4845
4985
  }
4846
4986
 
4847
4987
  // src/version.ts
4848
- import { readFileSync } from "fs";
4988
+ import { readFileSync as readFileSync2 } from "fs";
4849
4989
  import { dirname as dirname4, join as join4 } from "path";
4850
4990
  import { fileURLToPath as fileURLToPath2 } from "url";
4851
4991
  function readCliVersion() {
@@ -4857,7 +4997,7 @@ function readCliVersion() {
4857
4997
  ];
4858
4998
  for (const path of candidates) {
4859
4999
  try {
4860
- const pkg = JSON.parse(readFileSync(path, "utf8"));
5000
+ const pkg = JSON.parse(readFileSync2(path, "utf8"));
4861
5001
  if (typeof pkg.version === "string" && pkg.version) return pkg.version;
4862
5002
  } catch {
4863
5003
  }
@@ -5193,19 +5333,19 @@ function formatPretty(info) {
5193
5333
 
5194
5334
  // src/commands/actions.ts
5195
5335
  import { spawn as spawn2 } from "child_process";
5196
- import { readFileSync as readFileSync3 } from "fs";
5336
+ import { readFileSync as readFileSync4 } from "fs";
5197
5337
  import { platform as platform3 } from "os";
5198
5338
  import { basename, extname } from "path";
5199
5339
 
5200
5340
  // src/json-input.ts
5201
- import { readFileSync as readFileSync2 } from "fs";
5341
+ import { readFileSync as readFileSync3 } from "fs";
5202
5342
  function parseJsonInput(value) {
5203
5343
  let source = value;
5204
5344
  if (value.startsWith("@")) {
5205
5345
  const path = value.slice(1);
5206
5346
  if (!path) throw new Error("--data @file path must not be empty");
5207
5347
  try {
5208
- source = readFileSync2(path, "utf8");
5348
+ source = readFileSync3(path, "utf8");
5209
5349
  } catch (err) {
5210
5350
  throw new Error(`Could not read --data file ${path}: ${err.message}`);
5211
5351
  }
@@ -5402,7 +5542,7 @@ Approval required. ${opened ? "Opening" : "Open"} ${approval.verificationUrl}` :
5402
5542
  const resource = parseResourceOption(opts.resourceType, opts.resourceId);
5403
5543
  const result = await topolo.client.uploadAction(
5404
5544
  action,
5405
- readFileSync3(opts.file),
5545
+ readFileSync4(opts.file),
5406
5546
  parseJsonObject(opts.data || "{}"),
5407
5547
  {
5408
5548
  fileName: basename(opts.file),
@@ -5974,7 +6114,7 @@ function normalizeList(values, fallback = []) {
5974
6114
  }
5975
6115
 
5976
6116
  // src/commands/integrations.ts
5977
- import { readdir as readdir2, readFile as readFile3, stat as stat2 } from "fs/promises";
6117
+ import { readdir as readdir3, readFile as readFile3, stat } from "fs/promises";
5978
6118
  import { join as join5, relative } from "path";
5979
6119
  function registerIntegrationsCommand(program2) {
5980
6120
  const integrations = program2.command("integrations").description("Generate and validate Topolo integration handoffs.");
@@ -6128,7 +6268,7 @@ async function collectFiles(repoRoot) {
6128
6268
  const root = repoRoot;
6129
6269
  async function walk(dir) {
6130
6270
  if (result.length > 2e3) return;
6131
- const entries = await readdir2(dir, { withFileTypes: true });
6271
+ const entries = await readdir3(dir, { withFileTypes: true });
6132
6272
  for (const entry of entries) {
6133
6273
  const fullPath = join5(dir, entry.name);
6134
6274
  if (entry.isDirectory()) {
@@ -6137,7 +6277,7 @@ async function collectFiles(repoRoot) {
6137
6277
  continue;
6138
6278
  }
6139
6279
  if (!entry.isFile() || !isInspectable(entry.name)) continue;
6140
- const info = await stat2(fullPath);
6280
+ const info = await stat(fullPath);
6141
6281
  if (info.size > 25e4) continue;
6142
6282
  result.push({
6143
6283
  path: relative(root, fullPath),
@@ -6159,7 +6299,7 @@ function formatCheckResult(result) {
6159
6299
  // src/commands/skills.ts
6160
6300
  import { spawnSync as spawnSync4 } from "child_process";
6161
6301
  import { existsSync as existsSync6 } from "fs";
6162
- import { readFile as readFile6, rm as rm3 } from "fs/promises";
6302
+ import { readFile as readFile6, rm as rm2 } from "fs/promises";
6163
6303
  import { homedir as homedir5 } from "os";
6164
6304
  import { dirname as dirname6, join as join7, resolve as resolve2 } from "path";
6165
6305
  import { fileURLToPath as fileURLToPath3 } from "url";
@@ -6620,7 +6760,7 @@ function message(error) {
6620
6760
 
6621
6761
  // src/commands/agent-config.ts
6622
6762
  import { existsSync as existsSync5 } from "fs";
6623
- import { mkdir as mkdir3, readFile as readFile5, rename as rename2, writeFile as writeFile3 } from "fs/promises";
6763
+ import { mkdir as mkdir3, readFile as readFile5, rename as rename3, writeFile as writeFile3 } from "fs/promises";
6624
6764
  import { dirname as dirname5 } from "path";
6625
6765
  async function installAgentSkill(agent, source, homeDir, projectDir) {
6626
6766
  const path = agentSkillPath(agent, homeDir, projectDir);
@@ -6787,7 +6927,7 @@ async function atomicWrite(path, contents) {
6787
6927
  await mkdir3(dirname5(path), { recursive: true });
6788
6928
  const temporaryPath = path + ".topolo-" + process.pid + ".tmp";
6789
6929
  await writeFile3(temporaryPath, contents, { encoding: "utf8", mode: 384 });
6790
- await rename2(temporaryPath, path);
6930
+ await rename3(temporaryPath, path);
6791
6931
  }
6792
6932
  function withoutMcpServerSections(text2, name) {
6793
6933
  const section = "[mcp_servers." + name + "]";
@@ -7025,7 +7165,7 @@ async function uninstallAgentSurfaces(options) {
7025
7165
  const remaining = await readAgentRegistrations(agent, homeDir, options.project);
7026
7166
  const skillPath = agentSkillPath(agent, homeDir, options.project);
7027
7167
  const skillRemoved = remaining.length === 0 && Boolean(skillPath && existsSync6(skillPath));
7028
- if (skillRemoved && skillPath) await rm3(dirname6(skillPath), { recursive: true, force: true });
7168
+ if (skillRemoved && skillPath) await rm2(dirname6(skillPath), { recursive: true, force: true });
7029
7169
  removed.push({ agent, name, registrationRemoved, skillRemoved });
7030
7170
  }
7031
7171
  return removed;
package/package.json CHANGED
@@ -1,8 +1,8 @@
1
1
  {
2
2
  "name": "@topolo/cli",
3
3
  "packageManager": "pnpm@10.17.1",
4
- "version": "0.13.1",
5
- "topoloPublishedVersion": "0.13.1",
4
+ "version": "0.13.2",
5
+ "topoloPublishedVersion": "0.13.2",
6
6
  "description": "Public command-line interface for Topolo customer organizations, developers, and agents.",
7
7
  "license": "UNLICENSED",
8
8
  "author": "Topolo",
@@ -31,8 +31,8 @@
31
31
  },
32
32
  "dependencies": {
33
33
  "@modelcontextprotocol/sdk": "^1.0.0",
34
- "@topolo/mcp": "0.14.22",
35
- "@topolo/sdk": "0.23.17",
34
+ "@topolo/mcp": "0.14.23",
35
+ "@topolo/sdk": "0.23.18",
36
36
  "commander": "^12.1.0",
37
37
  "undici": "6.29.0"
38
38
  },