@topolo/cli 0.7.0 → 0.7.1
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/README.md +9 -2
- package/dist/index.js +83 -27
- package/package.json +2 -2
- package/skills/topolo/SKILL.md +16 -0
package/README.md
CHANGED
|
@@ -153,6 +153,7 @@ and the CLI calls them through the shared SDK.
|
|
|
153
153
|
|
|
154
154
|
```bash
|
|
155
155
|
topolo actions --service mail --json
|
|
156
|
+
topolo actions --service mail --detail --json
|
|
156
157
|
topolo actions get app_topolo_mail.messages.send --json
|
|
157
158
|
topolo actions call app_topolo_mail.messages.send \
|
|
158
159
|
--data '{"mailboxId":"primary","subject":"Hello","text":"..."}' \
|
|
@@ -163,6 +164,11 @@ Use `topolo actions` before `topolo api` when an app has published a machine
|
|
|
163
164
|
contract for the operation. `topolo api` remains the escape hatch for endpoints
|
|
164
165
|
that are not yet in the action catalog.
|
|
165
166
|
|
|
167
|
+
Action search, capability discovery, validation, and planning return compact
|
|
168
|
+
agent guidance by default. Use `--detail` only when the full schemas and agent
|
|
169
|
+
contract are required. `topolo resources --service <app>` is accepted alongside
|
|
170
|
+
the positional application selector.
|
|
171
|
+
|
|
166
172
|
Workspace-scoped actions carry resource context independently from the active
|
|
167
173
|
personal/organization credential and independently from the browser session:
|
|
168
174
|
|
|
@@ -215,8 +221,9 @@ Agents should call `topolo apps requirements <app> --json` before creating or
|
|
|
215
221
|
expanding a Topolo app. The output is the versioned platform contract for app
|
|
216
222
|
metadata, canonical docs, shared auth, shared shell, service registration,
|
|
217
223
|
deployment metadata, agent surfaces, observability, and verification.
|
|
218
|
-
`topolo apps audit --json`
|
|
219
|
-
|
|
224
|
+
`topolo apps audit --json` returns compact per-app scores and a migration queue
|
|
225
|
+
count. Add `--detail` for every finding and migration item, or
|
|
226
|
+
`--local --repo-root <topolo-checkout>` to
|
|
220
227
|
include filesystem evidence from the app repo, CloudControl metadata, and
|
|
221
228
|
TopoloDocs. Add `--fail-on needs_review` for the default new-work gate, or
|
|
222
229
|
`--fail-on partial` for strict release readiness.
|
package/dist/index.js
CHANGED
|
@@ -3914,6 +3914,52 @@ function mergeSignals(a, b) {
|
|
|
3914
3914
|
b.addEventListener("abort", onAbort, { once: true });
|
|
3915
3915
|
return controller.signal;
|
|
3916
3916
|
}
|
|
3917
|
+
function compactActionCatalogEntry(action) {
|
|
3918
|
+
const required = Array.isArray(action.inputSchema["required"]) ? action.inputSchema["required"].filter((value) => typeof value === "string") : [];
|
|
3919
|
+
const examples = action.agentContract?.examples ?? [];
|
|
3920
|
+
return {
|
|
3921
|
+
actionId: action.actionId,
|
|
3922
|
+
title: action.title,
|
|
3923
|
+
description: action.description,
|
|
3924
|
+
method: action.method,
|
|
3925
|
+
readOnly: action.readOnly,
|
|
3926
|
+
requiresConfirmation: action.requiresConfirmation,
|
|
3927
|
+
agentAccess: action.agentAccess ?? (action.requiresConfirmation ? "confirm" : action.readOnly ? "auto" : "confirm"),
|
|
3928
|
+
requiredPermission: action.requiredPermission,
|
|
3929
|
+
requiredInput: required,
|
|
3930
|
+
resourceType: action.agentContract?.resource?.kind ?? null,
|
|
3931
|
+
exampleInput: examples[0]?.input ?? null,
|
|
3932
|
+
docsUrl: action.agentContract?.docsUrl ?? null
|
|
3933
|
+
};
|
|
3934
|
+
}
|
|
3935
|
+
function compactActionValidation(actionId, validation) {
|
|
3936
|
+
return { actionId, valid: validation.valid, errors: validation.errors };
|
|
3937
|
+
}
|
|
3938
|
+
function compactActionPlan(plan) {
|
|
3939
|
+
return {
|
|
3940
|
+
actionId: plan.action.actionId,
|
|
3941
|
+
valid: plan.validation.valid,
|
|
3942
|
+
errors: plan.validation.errors,
|
|
3943
|
+
executable: plan.executable,
|
|
3944
|
+
confirmationRequired: plan.confirmationRequired,
|
|
3945
|
+
confirmationProvided: plan.confirmationProvided,
|
|
3946
|
+
request: plan.request,
|
|
3947
|
+
verification: plan.verification,
|
|
3948
|
+
rollbackActionId: plan.rollbackActionId,
|
|
3949
|
+
nextActionIds: plan.nextActionIds
|
|
3950
|
+
};
|
|
3951
|
+
}
|
|
3952
|
+
function compactApplicationAudit(report) {
|
|
3953
|
+
return {
|
|
3954
|
+
version: report.version,
|
|
3955
|
+
applications: report.applications.map((audit) => ({
|
|
3956
|
+
applicationId: audit.application.id,
|
|
3957
|
+
applicationName: audit.application.name,
|
|
3958
|
+
score: audit.score
|
|
3959
|
+
})),
|
|
3960
|
+
migrationQueueCount: report.migrationQueue.length
|
|
3961
|
+
};
|
|
3962
|
+
}
|
|
3917
3963
|
var CrmModule = class {
|
|
3918
3964
|
constructor(client) {
|
|
3919
3965
|
this.client = client;
|
|
@@ -5107,7 +5153,7 @@ function parseQueryPairs(pairs) {
|
|
|
5107
5153
|
|
|
5108
5154
|
// src/commands/actions.ts
|
|
5109
5155
|
function registerActionsCommand(program2) {
|
|
5110
|
-
const actions = program2.command("actions").description("Search permission-scoped Topolo actions for one application").option("-s, --service <service>", "App id, slug, or name (required for search)").option("-q, --query <query>", "Filter by action id, name, or title").option("--cursor <cursor>", "Continuation cursor from the previous page").option("--limit <limit>", "Page size (1-100)", "25").option("--json", "Output JSON").option("--no-json", "Output human-readable table").action(async (opts) => {
|
|
5156
|
+
const actions = program2.command("actions").description("Search permission-scoped Topolo actions for one application").option("-s, --service <service>", "App id, slug, or name (required for search)").option("-q, --query <query>", "Filter by action id, name, or title").option("--cursor <cursor>", "Continuation cursor from the previous page").option("--limit <limit>", "Page size (1-100)", "25").option("--detail", "Include full schemas and agent contracts").option("--json", "Output JSON").option("--no-json", "Output human-readable table").action(async (opts) => {
|
|
5111
5157
|
const mode = resolveOutputMode(opts.json);
|
|
5112
5158
|
try {
|
|
5113
5159
|
const topolo = await buildClient();
|
|
@@ -5121,7 +5167,8 @@ function registerActionsCommand(program2) {
|
|
|
5121
5167
|
cursor: opts.cursor,
|
|
5122
5168
|
limit: parsePageSize(opts.limit)
|
|
5123
5169
|
});
|
|
5124
|
-
|
|
5170
|
+
const output2 = opts.detail ? result : { actions: result.actions.map(compactActionCatalogEntry), page: result.page };
|
|
5171
|
+
emit(output2, mode, () => formatActionRows(result.actions, result.page.nextCursor));
|
|
5125
5172
|
} catch (err) {
|
|
5126
5173
|
fail(err, mode);
|
|
5127
5174
|
}
|
|
@@ -5181,17 +5228,18 @@ function registerActionsCommand(program2) {
|
|
|
5181
5228
|
fail(err, mode);
|
|
5182
5229
|
}
|
|
5183
5230
|
});
|
|
5184
|
-
actions.command("validate <action>").description("Validate action input locally against the published JSON Schema").option("-d, --data <json-or-@file>", "JSON action input or @path to a JSON file", "{}").option("--json", "Output JSON").option("--no-json", "Output human-readable text").action(async (action, opts, command) => {
|
|
5231
|
+
actions.command("validate <action>").description("Validate action input locally against the published JSON Schema").option("-d, --data <json-or-@file>", "JSON action input or @path to a JSON file", "{}").option("--detail", "Include the full action contract").option("--json", "Output JSON").option("--no-json", "Output human-readable text").action(async (action, opts, command) => {
|
|
5185
5232
|
const mode = resolveOutputMode(opts.json ?? command.parent?.opts().json);
|
|
5186
5233
|
try {
|
|
5187
5234
|
const topolo = await requiredClient();
|
|
5188
5235
|
const result = await topolo.client.validateActionInput(action, parseJsonObject(opts.data || "{}"));
|
|
5189
|
-
|
|
5236
|
+
const output2 = detailRequested(opts.detail, command) ? result : compactActionValidation(result.action.actionId, result.validation);
|
|
5237
|
+
emit(output2, mode, () => result.validation.valid ? `Valid input for ${result.action.actionId}.` : result.validation.errors.map((issue) => `${issue.path}: ${issue.message}`).join("\n"));
|
|
5190
5238
|
} catch (err) {
|
|
5191
5239
|
fail(err, mode);
|
|
5192
5240
|
}
|
|
5193
5241
|
});
|
|
5194
|
-
actions.command("plan <action>").description("Validate and preview the exact request, confirmation, verification, and rollback plan").option("-d, --data <json-or-@file>", "JSON action input or @path to a JSON file", "{}").option("--resource-type <type>", "App-local resource alias, such as workspace").option("--resource-id <id>", "Resource id for the selected resource type").option("--confirm", "Include confirmation in the plan without executing it").option("--json", "Output JSON").option("--no-json", "Output human-readable text").action(async (action, opts, command) => {
|
|
5242
|
+
actions.command("plan <action>").description("Validate and preview the exact request, confirmation, verification, and rollback plan").option("-d, --data <json-or-@file>", "JSON action input or @path to a JSON file", "{}").option("--resource-type <type>", "App-local resource alias, such as workspace").option("--resource-id <id>", "Resource id for the selected resource type").option("--confirm", "Include confirmation in the plan without executing it").option("--detail", "Include the full action contract").option("--json", "Output JSON").option("--no-json", "Output human-readable text").action(async (action, opts, command) => {
|
|
5195
5243
|
const mode = resolveOutputMode(opts.json ?? command.parent?.opts().json);
|
|
5196
5244
|
try {
|
|
5197
5245
|
const topolo = await requiredClient();
|
|
@@ -5200,7 +5248,7 @@ function registerActionsCommand(program2) {
|
|
|
5200
5248
|
confirm: opts.confirm === true,
|
|
5201
5249
|
...resource ? { resource } : {}
|
|
5202
5250
|
});
|
|
5203
|
-
emit(plan, mode, () => formatActionPlan(plan));
|
|
5251
|
+
emit(detailRequested(opts.detail, command) ? plan : compactActionPlan(plan), mode, () => formatActionPlan(plan));
|
|
5204
5252
|
} catch (err) {
|
|
5205
5253
|
fail(err, mode);
|
|
5206
5254
|
}
|
|
@@ -5220,19 +5268,23 @@ function registerActionsCommand(program2) {
|
|
|
5220
5268
|
fail(err, mode);
|
|
5221
5269
|
}
|
|
5222
5270
|
});
|
|
5223
|
-
actions.command("capabilities").description("List every published action for one application (its capability contract)").option("-s, --service <service>", "App id, slug, or name").option("--json", "Output JSON").option("--no-json", "Output human-readable table").action(async (opts, command) => {
|
|
5271
|
+
actions.command("capabilities").description("List every published action for one application (its capability contract)").option("-s, --service <service>", "App id, slug, or name").option("--detail", "Include full schemas and agent contracts").option("--json", "Output JSON").option("--no-json", "Output human-readable table").action(async (opts, command) => {
|
|
5224
5272
|
const mode = resolveOutputMode(opts.json ?? command.parent?.opts().json);
|
|
5225
5273
|
try {
|
|
5226
5274
|
const topolo = await requiredClient();
|
|
5227
5275
|
const service = opts.service || command.parent?.opts().service;
|
|
5228
5276
|
if (!service?.trim()) throw new Error("--service is required for capability discovery");
|
|
5229
5277
|
const result = await topolo.client.searchActions({ service: service.trim(), limit: 100 });
|
|
5230
|
-
|
|
5278
|
+
const output2 = detailRequested(opts.detail, command) ? result : { actions: result.actions.map(compactActionCatalogEntry), page: result.page };
|
|
5279
|
+
emit(output2, mode, () => formatActionRows(result.actions, result.page.nextCursor));
|
|
5231
5280
|
} catch (err) {
|
|
5232
5281
|
fail(err, mode);
|
|
5233
5282
|
}
|
|
5234
5283
|
});
|
|
5235
5284
|
}
|
|
5285
|
+
function detailRequested(local, command) {
|
|
5286
|
+
return local === true || command.parent?.opts().detail === true;
|
|
5287
|
+
}
|
|
5236
5288
|
async function requiredClient() {
|
|
5237
5289
|
const topolo = await buildClient();
|
|
5238
5290
|
if (!topolo) throw new Error("No credential configured. Run `topolo auth login` or set TOPOLO_API_KEY.");
|
|
@@ -5336,7 +5388,7 @@ function formatActionPlan(plan) {
|
|
|
5336
5388
|
|
|
5337
5389
|
// src/commands/apps.ts
|
|
5338
5390
|
import { execSync } from "child_process";
|
|
5339
|
-
import { existsSync as
|
|
5391
|
+
import { existsSync as existsSync5, mkdirSync as mkdirSync2, readFileSync as readFileSync6, writeFileSync as writeFileSync2 } from "fs";
|
|
5340
5392
|
import path6 from "path";
|
|
5341
5393
|
|
|
5342
5394
|
// src/commands/app-audit-local.ts
|
|
@@ -6639,7 +6691,7 @@ function firstPartyAppPath(appDir, _audience) {
|
|
|
6639
6691
|
}
|
|
6640
6692
|
|
|
6641
6693
|
// src/commands/app-scaffold-platform-versions.ts
|
|
6642
|
-
import { readdirSync as readdirSync2, readFileSync as readFileSync4 } from "fs";
|
|
6694
|
+
import { existsSync as existsSync3, readdirSync as readdirSync2, readFileSync as readFileSync4 } from "fs";
|
|
6643
6695
|
import path2 from "path";
|
|
6644
6696
|
var versionCache = /* @__PURE__ */ new Map();
|
|
6645
6697
|
function platformPackagePin(packageName, options = {}) {
|
|
@@ -6647,7 +6699,7 @@ function platformPackagePin(packageName, options = {}) {
|
|
|
6647
6699
|
options.repoRoot ?? process.env.TOPOLO_WORKSPACE_ROOT ?? void 0,
|
|
6648
6700
|
options.cwd ?? process.cwd()
|
|
6649
6701
|
);
|
|
6650
|
-
if (!workspaceRoot) {
|
|
6702
|
+
if (!workspaceRoot || !existsSync3(path2.join(workspaceRoot, "topolo-platform", "packages"))) {
|
|
6651
6703
|
throw new Error(
|
|
6652
6704
|
`Cannot resolve a concrete version for ${packageName}: platform-target scaffolds must run inside the Topolo workspace checkout (a directory containing apps/ and system-apps/), or receive --repo-root. The fleet pinning policy requires exact ^<latest> pins, never "latest".`
|
|
6653
6705
|
);
|
|
@@ -7194,6 +7246,7 @@ function readme(application, target, options) {
|
|
|
7194
7246
|
"- `@topolo-io/app-launcher` provides shared app switching and handoff helpers.",
|
|
7195
7247
|
"- `@topolo-io/ui-kit` provides every control, table, panel, workbench, inspector, icon, transition, and bug reporter primitive used by the generated product surface.",
|
|
7196
7248
|
"- `@topolo-io/auth-client` owns browser auth, callback exchange, token refresh, and logout.",
|
|
7249
|
+
'- Build the app data client with `authClient.createJsonClient()` (or `createAuthenticatedFetch()`) from `@topolo-io/auth-client`. It already attaches the bearer token, refreshes proactively near expiry, and refreshes-and-retries once on a 401. NEVER hand-roll an `Authorization: Bearer` header in browser code: every app that has done so forgot the 401 recovery and showed raw auth errors ("Token expired") to the user on an ordinary 15-minute expiry. The `browser-auth-refresh` fleet static guard fails any browser file that builds its own bearer header.',
|
|
7197
7250
|
"- `@topolo-io/api-key-management`, `@topolo-io/chat-widget`, `@topolo-io/notifications`, and `@topolo-io/resource-context` complete the AppShell capability baseline.",
|
|
7198
7251
|
"- Every Topolo-owned browser dependency is emitted at `latest`; product-domain packages are added only when the app actually adopts that domain.",
|
|
7199
7252
|
"- `.npmrc` routes private `@topolo-io` packages to GitHub Packages without storing credentials; public `@topolo` packages remain on npm. Local and CI environments must provide a GitHub Packages token.",
|
|
@@ -8760,7 +8813,7 @@ function developerWorkerTs(application) {
|
|
|
8760
8813
|
}
|
|
8761
8814
|
|
|
8762
8815
|
// src/commands/app-scaffold-generate.ts
|
|
8763
|
-
import { existsSync as
|
|
8816
|
+
import { existsSync as existsSync4, mkdirSync, mkdtempSync, readFileSync as readFileSync5, rmSync, writeFileSync } from "fs";
|
|
8764
8817
|
import { spawnSync } from "child_process";
|
|
8765
8818
|
import { tmpdir } from "os";
|
|
8766
8819
|
import path3 from "path";
|
|
@@ -8774,7 +8827,7 @@ function resolveGeneratorPath() {
|
|
|
8774
8827
|
];
|
|
8775
8828
|
for (const candidate of candidates) {
|
|
8776
8829
|
const p = decodeURIComponent(candidate.pathname);
|
|
8777
|
-
if (
|
|
8830
|
+
if (existsSync4(p)) return p;
|
|
8778
8831
|
}
|
|
8779
8832
|
throw new Error(
|
|
8780
8833
|
`topolo: could not locate generate-wrangler.mjs. A published CLI vendors it into dist/ at build time; in-repo it is read from topolo-platform/tooling/. Looked in:
|
|
@@ -11668,7 +11721,7 @@ function registerAppsCommand(program2) {
|
|
|
11668
11721
|
});
|
|
11669
11722
|
apps.command("audit [application]").description(
|
|
11670
11723
|
"Audit Topolo applications against the versioned application-build requirements."
|
|
11671
|
-
).option("--json", "Output JSON", true).option("--no-json", "Output human-readable table").option("--local", "Inspect the local Topolo checkout for filesystem-backed evidence").option("--repo-root <path>", "Topolo checkout root to inspect when --local is set").option(
|
|
11724
|
+
).option("--json", "Output JSON", true).option("--no-json", "Output human-readable table").option("--detail", "Include all findings and migration queue items").option("--local", "Inspect the local Topolo checkout for filesystem-backed evidence").option("--repo-root <path>", "Topolo checkout root to inspect when --local is set").option(
|
|
11672
11725
|
"--fail-on <status>",
|
|
11673
11726
|
"Exit non-zero when audit has findings at this severity or worse: missing, needs_review, partial"
|
|
11674
11727
|
).action(async (application, opts) => {
|
|
@@ -11680,8 +11733,9 @@ function registerAppsCommand(program2) {
|
|
|
11680
11733
|
client: await directoryClient()
|
|
11681
11734
|
});
|
|
11682
11735
|
const gate = opts.failOn ? evaluateApplicationAuditGate(payload, opts.failOn) : null;
|
|
11683
|
-
const
|
|
11684
|
-
|
|
11736
|
+
const compact = opts.detail ? payload : compactApplicationAudit(payload);
|
|
11737
|
+
const output2 = gate ? { ...compact, conformanceGate: gate } : compact;
|
|
11738
|
+
emit(output2, mode, () => formatAudit(gate ? { ...payload, conformanceGate: gate } : payload));
|
|
11685
11739
|
if (gate && !gate.passed) {
|
|
11686
11740
|
if (mode === "json") {
|
|
11687
11741
|
process.stderr.write(JSON.stringify({ error: gate.message, conformanceGate: gate }, null, 2) + "\n");
|
|
@@ -11824,7 +11878,7 @@ async function applicationAuditPayload(application, options = {}) {
|
|
|
11824
11878
|
async function recordApplicationEvidence(application, options = {}) {
|
|
11825
11879
|
const appId = (await resolveApplication(application, options.client ?? null)).id;
|
|
11826
11880
|
const root = path6.resolve(options.appRoot ?? process.cwd());
|
|
11827
|
-
if (!
|
|
11881
|
+
if (!existsSync5(root)) {
|
|
11828
11882
|
throw new Error(`Application root does not exist: ${root}`);
|
|
11829
11883
|
}
|
|
11830
11884
|
let commit = options.commit;
|
|
@@ -11838,7 +11892,7 @@ async function recordApplicationEvidence(application, options = {}) {
|
|
|
11838
11892
|
const evidenceDir = path6.join(root, ".topolo");
|
|
11839
11893
|
const evidencePath = path6.join(evidenceDir, "evidence.json");
|
|
11840
11894
|
let before = null;
|
|
11841
|
-
if (
|
|
11895
|
+
if (existsSync5(evidencePath)) {
|
|
11842
11896
|
try {
|
|
11843
11897
|
const raw = JSON.parse(readFileSync6(evidencePath, "utf8"));
|
|
11844
11898
|
if (raw && typeof raw === "object" && raw.version === 1) {
|
|
@@ -13110,7 +13164,7 @@ function formatCheckResult(result) {
|
|
|
13110
13164
|
|
|
13111
13165
|
// src/commands/skills.ts
|
|
13112
13166
|
import { spawnSync as spawnSync2 } from "child_process";
|
|
13113
|
-
import { existsSync as
|
|
13167
|
+
import { existsSync as existsSync6 } from "fs";
|
|
13114
13168
|
import { mkdir as mkdir4, copyFile, readFile as readFile7, writeFile as writeFile7, rm } from "fs/promises";
|
|
13115
13169
|
import { homedir as homedir2 } from "os";
|
|
13116
13170
|
import { dirname as dirname4, join as join5, resolve as resolve3 } from "path";
|
|
@@ -13122,7 +13176,7 @@ function skillsSourceDir() {
|
|
|
13122
13176
|
const distCandidate = resolve3(here, "../skills");
|
|
13123
13177
|
const srcCandidate = resolve3(here, "../../skills");
|
|
13124
13178
|
for (const candidate of [distCandidate, srcCandidate]) {
|
|
13125
|
-
if (
|
|
13179
|
+
if (existsSync6(join5(candidate, "topolo", "SKILL.md"))) return candidate;
|
|
13126
13180
|
}
|
|
13127
13181
|
return distCandidate;
|
|
13128
13182
|
}
|
|
@@ -13167,7 +13221,7 @@ function registerViaOfficialCli(cli, mcp, agentName) {
|
|
|
13167
13221
|
async function registerClaudeViaConfig(mcp) {
|
|
13168
13222
|
const path9 = join5(homedir2(), ".claude.json");
|
|
13169
13223
|
let config = {};
|
|
13170
|
-
if (
|
|
13224
|
+
if (existsSync6(path9)) {
|
|
13171
13225
|
try {
|
|
13172
13226
|
config = JSON.parse(await readFile7(path9, "utf8"));
|
|
13173
13227
|
} catch {
|
|
@@ -13188,7 +13242,7 @@ async function registerClaudeViaConfig(mcp) {
|
|
|
13188
13242
|
async function registerCodexViaConfig(mcp) {
|
|
13189
13243
|
const path9 = join5(homedir2(), ".codex", "config.toml");
|
|
13190
13244
|
let text = "";
|
|
13191
|
-
if (
|
|
13245
|
+
if (existsSync6(path9)) text = await readFile7(path9, "utf8");
|
|
13192
13246
|
text = text.replace(
|
|
13193
13247
|
/(^|\n)\[mcp_servers\.topolo(?:\.[a-z]+)?\][^\[]*/g,
|
|
13194
13248
|
(match) => match.startsWith("\n") ? "\n" : ""
|
|
@@ -13243,7 +13297,7 @@ function registerSkillsCommand(program2) {
|
|
|
13243
13297
|
const mode = resolveOutputMode(opts.json);
|
|
13244
13298
|
try {
|
|
13245
13299
|
const sourceDir = skillsSourceDir();
|
|
13246
|
-
if (!
|
|
13300
|
+
if (!existsSync6(join5(sourceDir, "topolo", "SKILL.md"))) {
|
|
13247
13301
|
throw new Error(
|
|
13248
13302
|
"Bundled skill file not found. Reinstall the CLI: npm install -g @topolo/cli"
|
|
13249
13303
|
);
|
|
@@ -13272,7 +13326,7 @@ function registerSkillsCommand(program2) {
|
|
|
13272
13326
|
const skillDir = agent === "claude" ? claudeSkillDir() : codexSkillDir();
|
|
13273
13327
|
return {
|
|
13274
13328
|
agent,
|
|
13275
|
-
skillInstalled:
|
|
13329
|
+
skillInstalled: existsSync6(join5(skillDir, "SKILL.md")),
|
|
13276
13330
|
skillPath: join5(skillDir, "SKILL.md"),
|
|
13277
13331
|
cliAvailable: agentBinaryExists(agent)
|
|
13278
13332
|
};
|
|
@@ -13289,7 +13343,7 @@ function registerSkillsCommand(program2) {
|
|
|
13289
13343
|
for (const agent of resolveTargets(opts)) {
|
|
13290
13344
|
const skillDir = agent === "claude" ? claudeSkillDir() : codexSkillDir();
|
|
13291
13345
|
const skillFile = join5(skillDir, "SKILL.md");
|
|
13292
|
-
const skillRemoved =
|
|
13346
|
+
const skillRemoved = existsSync6(skillFile);
|
|
13293
13347
|
if (skillRemoved) await rm(skillFile, { force: true });
|
|
13294
13348
|
if (agentBinaryExists(agent)) {
|
|
13295
13349
|
spawnSync2(agent, ["mcp", "remove", "topolo"], { stdio: "ignore" });
|
|
@@ -13541,12 +13595,14 @@ function registerContextCommand(program2) {
|
|
|
13541
13595
|
|
|
13542
13596
|
// src/commands/resources.ts
|
|
13543
13597
|
function registerResourcesCommand(program2) {
|
|
13544
|
-
program2.command("resources
|
|
13598
|
+
program2.command("resources [application]").description("List bindable resource ids for one credential-scoped Topolo application").option("-s, --service <service>", "App id, slug, or name").option("--json", "Output JSON").option("--no-json", "Output human-readable text").action(async (application, opts) => {
|
|
13545
13599
|
const mode = resolveOutputMode(opts.json);
|
|
13546
13600
|
try {
|
|
13547
13601
|
const topolo = await buildClient();
|
|
13548
13602
|
if (!topolo) throw new Error("No credential configured. Run `topolo auth login` or set TOPOLO_API_KEY.");
|
|
13549
|
-
const
|
|
13603
|
+
const selector = application?.trim() || opts.service?.trim();
|
|
13604
|
+
if (!selector) throw new Error("application or --service is required");
|
|
13605
|
+
const result = await topolo.client.listBindableResources(selector);
|
|
13550
13606
|
emit(result, mode, () => formatResources(result));
|
|
13551
13607
|
} catch (err) {
|
|
13552
13608
|
fail(err, mode);
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@topolo/cli",
|
|
3
|
-
"version": "0.7.
|
|
4
|
-
"topoloPublishedVersion": "0.7.
|
|
3
|
+
"version": "0.7.1",
|
|
4
|
+
"topoloPublishedVersion": "0.7.1",
|
|
5
5
|
"description": "Command-line interface for Topolo developers, operators, and agents, with internal first-party scaffolding available under the topolo platform namespace.",
|
|
6
6
|
"type": "module",
|
|
7
7
|
"bin": {
|
package/skills/topolo/SKILL.md
CHANGED
|
@@ -20,6 +20,22 @@ topolo skills install
|
|
|
20
20
|
|
|
21
21
|
`topolo skills install` installs this skill and registers the Topolo MCP server for Claude Code and Codex. Both use the same credential written by `topolo auth login`.
|
|
22
22
|
|
|
23
|
+
Before testing platform behavior, verify every installed agent surface against
|
|
24
|
+
the package registry. Do not diagnose behavior with stale tooling:
|
|
25
|
+
|
|
26
|
+
```bash
|
|
27
|
+
topolo --version
|
|
28
|
+
npm list -g --depth=0 @topolo/cli @topolo/mcp @topolo/sdk
|
|
29
|
+
npm view @topolo/cli version
|
|
30
|
+
npm view @topolo/mcp version
|
|
31
|
+
npm view @topolo/sdk version
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
If any version differs, install the three latest released versions together,
|
|
35
|
+
run `topolo skills install`, and restart the MCP client before testing. Record
|
|
36
|
+
the versions used with the results. Source builds may test a candidate fix, but
|
|
37
|
+
must be labelled separately from released-tooling proof.
|
|
38
|
+
|
|
23
39
|
Production is the default. Use `--env staging` on every staging command, or set `TOPOLO_ENV=staging`. Never mix staging discovery with production execution.
|
|
24
40
|
|
|
25
41
|
## Authentication gate
|