@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.
- package/dist/index.js +205 -65
- 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
|
|
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
|
|
3695
|
-
import { mkdir as mkdir2, readFile as readFile2, writeFile as writeFile2, chmod as chmod2,
|
|
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
|
|
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 =
|
|
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
|
-
|
|
4565
|
-
|
|
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
|
-
|
|
4681
|
+
await assertOwned();
|
|
4682
|
+
const result = await operation(assertOwned, persist);
|
|
4683
|
+
await assertOwned();
|
|
4684
|
+
return result;
|
|
4582
4685
|
} finally {
|
|
4583
|
-
await
|
|
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
|
-
|
|
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
|
-
|
|
4627
|
-
|
|
4628
|
-
|
|
4629
|
-
credential
|
|
4630
|
-
|
|
4631
|
-
|
|
4632
|
-
|
|
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 ??
|
|
4636
|
-
}
|
|
4637
|
-
}
|
|
4638
|
-
})
|
|
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
|
-
|
|
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
|
|
4681
|
-
|
|
4682
|
-
contexts
|
|
4683
|
-
|
|
4684
|
-
|
|
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
|
-
|
|
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(
|
|
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
|
|
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
|
|
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 =
|
|
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
|
-
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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.
|
|
5
|
-
"topoloPublishedVersion": "0.13.
|
|
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.
|
|
35
|
-
"@topolo/sdk": "0.23.
|
|
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
|
},
|