@topolo/cli 0.6.7 → 0.6.10
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 +17 -1
- package/dist/index.js +164 -11
- package/package.json +17 -17
- package/skills/topolo/SKILL.md +12 -0
package/README.md
CHANGED
|
@@ -12,7 +12,7 @@ via `stdin`/`stdout`.
|
|
|
12
12
|
- First-class `production`, `staging`, `development`, and `local` environments with separate stored credentials and service URL overrides.
|
|
13
13
|
- **Per-context credentials** — hold and switch between your personal space and multiple organizations with `topolo context`.
|
|
14
14
|
- **One-command agent setup** — `topolo skills install` wires the skill + MCP into Claude Code / Codex.
|
|
15
|
-
- Commands: `auth`, `whoami`, `context`, `skills`, `apps`, `services`, credential-scoped `actions`, generic `api` passthrough, and internal `platform` scaffolding.
|
|
15
|
+
- Commands: `auth`, `whoami`, `context`, `skills`, `apps`, `services`, `workspaces`, credential-scoped `actions`, generic `api` passthrough, and internal `platform` scaffolding.
|
|
16
16
|
|
|
17
17
|
## Install
|
|
18
18
|
|
|
@@ -177,6 +177,22 @@ Both resource flags are required together. This supports concurrent agent
|
|
|
177
177
|
operations across many app-local workspaces without changing the CLI/MCP org
|
|
178
178
|
context or the user's web switcher.
|
|
179
179
|
|
|
180
|
+
### `topolo workspaces`
|
|
181
|
+
|
|
182
|
+
Discovers canonical workspaces and the caller-specific creation policy for a
|
|
183
|
+
target application. Auth owns workspace identity, while its runtime policy
|
|
184
|
+
decides access separately for each app and caller.
|
|
185
|
+
|
|
186
|
+
```bash
|
|
187
|
+
topolo workspaces list design --json
|
|
188
|
+
topolo workspaces create design --name "Campaigns" --confirm --json
|
|
189
|
+
topolo workspaces rename design <workspace-id> --name "Social campaigns" --confirm --json
|
|
190
|
+
topolo workspaces default design <workspace-id> --confirm --json
|
|
191
|
+
```
|
|
192
|
+
|
|
193
|
+
Always read `data.policy.canCreate` and `data.policy.reason` from `list` before
|
|
194
|
+
creation. Organization ownership is not treated as permission for every app.
|
|
195
|
+
|
|
180
196
|
### `topolo apps`
|
|
181
197
|
|
|
182
198
|
Lists launchable Topolo applications available to the current credential. This
|
package/dist/index.js
CHANGED
|
@@ -754,7 +754,9 @@ var APPLICATIONS = {
|
|
|
754
754
|
"domains": [
|
|
755
755
|
"developers.topolo.app"
|
|
756
756
|
],
|
|
757
|
-
"routes": [
|
|
757
|
+
"routes": [
|
|
758
|
+
"developers.topolo.app"
|
|
759
|
+
],
|
|
758
760
|
"appId": "developers"
|
|
759
761
|
},
|
|
760
762
|
{
|
|
@@ -1442,7 +1444,9 @@ var APPLICATIONS = {
|
|
|
1442
1444
|
"domains": [
|
|
1443
1445
|
"people.topolo.app"
|
|
1444
1446
|
],
|
|
1445
|
-
"routes": [
|
|
1447
|
+
"routes": [
|
|
1448
|
+
"people.topolo.app"
|
|
1449
|
+
],
|
|
1446
1450
|
"appId": "people"
|
|
1447
1451
|
},
|
|
1448
1452
|
{
|
|
@@ -1521,7 +1525,9 @@ var APPLICATIONS = {
|
|
|
1521
1525
|
"domains": [
|
|
1522
1526
|
"roadmapper.topolo.app"
|
|
1523
1527
|
],
|
|
1524
|
-
"routes": [
|
|
1528
|
+
"routes": [
|
|
1529
|
+
"roadmapper.topolo.app"
|
|
1530
|
+
],
|
|
1525
1531
|
"appId": "roadmapper"
|
|
1526
1532
|
},
|
|
1527
1533
|
{
|
|
@@ -1551,7 +1557,9 @@ var APPLICATIONS = {
|
|
|
1551
1557
|
"domains": [
|
|
1552
1558
|
"sign.topolo.app"
|
|
1553
1559
|
],
|
|
1554
|
-
"routes": [
|
|
1560
|
+
"routes": [
|
|
1561
|
+
"sign.topolo.app"
|
|
1562
|
+
],
|
|
1555
1563
|
"appId": "sign"
|
|
1556
1564
|
},
|
|
1557
1565
|
{
|
|
@@ -1665,7 +1673,9 @@ var APPLICATIONS = {
|
|
|
1665
1673
|
"domains": [
|
|
1666
1674
|
"spaces.topolo.app"
|
|
1667
1675
|
],
|
|
1668
|
-
"routes": [
|
|
1676
|
+
"routes": [
|
|
1677
|
+
"spaces.topolo.app"
|
|
1678
|
+
],
|
|
1669
1679
|
"appId": "spaces"
|
|
1670
1680
|
},
|
|
1671
1681
|
{
|
|
@@ -3725,10 +3735,19 @@ function mapActionInput(action, input3) {
|
|
|
3725
3735
|
delete remaining[key];
|
|
3726
3736
|
return encodeURIComponent(String(value));
|
|
3727
3737
|
});
|
|
3728
|
-
const
|
|
3729
|
-
|
|
3730
|
-
const
|
|
3731
|
-
|
|
3738
|
+
const schemaProperties = plainObject(plainObject(action.inputSchema).properties);
|
|
3739
|
+
const schemaOwnsQuery = Object.prototype.hasOwnProperty.call(schemaProperties, "query");
|
|
3740
|
+
const schemaOwnsBody = Object.prototype.hasOwnProperty.call(schemaProperties, "body");
|
|
3741
|
+
let explicitQuery = {};
|
|
3742
|
+
if (!schemaOwnsQuery) {
|
|
3743
|
+
explicitQuery = plainObject(remaining.query);
|
|
3744
|
+
delete remaining.query;
|
|
3745
|
+
}
|
|
3746
|
+
let explicitBody;
|
|
3747
|
+
if (!schemaOwnsBody) {
|
|
3748
|
+
explicitBody = Object.prototype.hasOwnProperty.call(remaining, "body") ? remaining.body : void 0;
|
|
3749
|
+
delete remaining.body;
|
|
3750
|
+
}
|
|
3732
3751
|
if (action.method === "GET") {
|
|
3733
3752
|
return {
|
|
3734
3753
|
path: path9,
|
|
@@ -3852,6 +3871,65 @@ var IdentityModule = class {
|
|
|
3852
3871
|
return this.client.introspect();
|
|
3853
3872
|
}
|
|
3854
3873
|
};
|
|
3874
|
+
var ACTIONS = {
|
|
3875
|
+
list: "app_topolo_auth.workspaces.list",
|
|
3876
|
+
create: "app_topolo_auth.workspaces.create",
|
|
3877
|
+
rename: "app_topolo_auth.workspaces.rename",
|
|
3878
|
+
setDefault: "app_topolo_auth.workspaces.default.set"
|
|
3879
|
+
};
|
|
3880
|
+
var WorkspacesModule = class {
|
|
3881
|
+
constructor(client) {
|
|
3882
|
+
this.client = client;
|
|
3883
|
+
}
|
|
3884
|
+
client;
|
|
3885
|
+
async list(application) {
|
|
3886
|
+
const context = await this.resolveContext(application);
|
|
3887
|
+
return this.client.callAction(ACTIONS.list, {
|
|
3888
|
+
org_id: context.organizationId,
|
|
3889
|
+
app_id: context.appId
|
|
3890
|
+
});
|
|
3891
|
+
}
|
|
3892
|
+
async create(application, input3) {
|
|
3893
|
+
const current = await this.list(application);
|
|
3894
|
+
if (!current.data.policy.canCreate) {
|
|
3895
|
+
throw new TopoloSdkError(
|
|
3896
|
+
"workspace_creation_not_allowed",
|
|
3897
|
+
`Workspace creation is not allowed for ${current.data.appId}: ${current.data.policy.reason}`
|
|
3898
|
+
);
|
|
3899
|
+
}
|
|
3900
|
+
return this.client.callAction(ACTIONS.create, {
|
|
3901
|
+
organizationId: current.data.organizationId,
|
|
3902
|
+
appId: current.data.appId,
|
|
3903
|
+
name: input3.name,
|
|
3904
|
+
...input3.isDefault === void 0 ? {} : { isDefault: input3.isDefault }
|
|
3905
|
+
}, { confirm: input3.confirm === true });
|
|
3906
|
+
}
|
|
3907
|
+
async rename(application, workspaceId, name, confirm = false) {
|
|
3908
|
+
const context = await this.resolveContext(application);
|
|
3909
|
+
return this.client.callAction(ACTIONS.rename, {
|
|
3910
|
+
workspaceId,
|
|
3911
|
+
organizationId: context.organizationId,
|
|
3912
|
+
appId: context.appId,
|
|
3913
|
+
name
|
|
3914
|
+
}, { confirm });
|
|
3915
|
+
}
|
|
3916
|
+
async setDefault(application, workspaceId, confirm = false) {
|
|
3917
|
+
const context = await this.resolveContext(application);
|
|
3918
|
+
return this.client.callAction(ACTIONS.setDefault, {
|
|
3919
|
+
workspaceId,
|
|
3920
|
+
organizationId: context.organizationId,
|
|
3921
|
+
appId: context.appId
|
|
3922
|
+
}, { confirm });
|
|
3923
|
+
}
|
|
3924
|
+
async resolveContext(application) {
|
|
3925
|
+
const identity = await this.client.introspect();
|
|
3926
|
+
if (!identity.organization?.id) {
|
|
3927
|
+
throw new TopoloSdkError("organization_required", "Workspace lifecycle requires an organization-scoped credential.");
|
|
3928
|
+
}
|
|
3929
|
+
const service = await this.client.getService(application);
|
|
3930
|
+
return { organizationId: identity.organization.id, appId: service.appId };
|
|
3931
|
+
}
|
|
3932
|
+
};
|
|
3855
3933
|
var DEVICE_GRANT = "urn:ietf:params:oauth:grant-type:device_code";
|
|
3856
3934
|
var TopoloOAuth = class {
|
|
3857
3935
|
baseUrl;
|
|
@@ -3935,6 +4013,7 @@ function createTopolo(options) {
|
|
|
3935
4013
|
return {
|
|
3936
4014
|
client,
|
|
3937
4015
|
identity: new IdentityModule(client),
|
|
4016
|
+
workspaces: new WorkspacesModule(client),
|
|
3938
4017
|
crm: new CrmModule(client)
|
|
3939
4018
|
};
|
|
3940
4019
|
}
|
|
@@ -5089,13 +5168,13 @@ function registerActionsCommand(program2) {
|
|
|
5089
5168
|
fail(err, mode);
|
|
5090
5169
|
}
|
|
5091
5170
|
});
|
|
5092
|
-
actions.command("capabilities").description("
|
|
5171
|
+
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) => {
|
|
5093
5172
|
const mode = resolveOutputMode(opts.json ?? command.parent?.opts().json);
|
|
5094
5173
|
try {
|
|
5095
5174
|
const topolo = await requiredClient();
|
|
5096
5175
|
const service = opts.service || command.parent?.opts().service;
|
|
5097
5176
|
if (!service?.trim()) throw new Error("--service is required for capability discovery");
|
|
5098
|
-
const result = await topolo.client.searchActions({ service: service.trim(),
|
|
5177
|
+
const result = await topolo.client.searchActions({ service: service.trim(), limit: 100 });
|
|
5099
5178
|
emit(result, mode, () => formatActionRows(result.actions, result.page.nextCursor));
|
|
5100
5179
|
} catch (err) {
|
|
5101
5180
|
fail(err, mode);
|
|
@@ -13451,6 +13530,79 @@ function formatResources(result) {
|
|
|
13451
13530
|
return lines.join("\n");
|
|
13452
13531
|
}
|
|
13453
13532
|
|
|
13533
|
+
// src/commands/workspaces.ts
|
|
13534
|
+
function registerWorkspacesCommand(program2) {
|
|
13535
|
+
const workspaces = program2.command("workspaces").description("Discover and manage caller-authorized workspaces for one Topolo application");
|
|
13536
|
+
workspaces.command("list <application>").description("List workspaces and the caller-specific creation policy for one application").option("--json", "Output JSON").option("--no-json", "Output human-readable text").action(async (application, opts) => {
|
|
13537
|
+
const mode = resolveOutputMode(opts.json);
|
|
13538
|
+
try {
|
|
13539
|
+
const topolo = await requiredClient2();
|
|
13540
|
+
const result = await topolo.workspaces.list(application);
|
|
13541
|
+
emit(result, mode, () => formatWorkspaceList(result));
|
|
13542
|
+
} catch (error) {
|
|
13543
|
+
fail(error, mode);
|
|
13544
|
+
}
|
|
13545
|
+
});
|
|
13546
|
+
workspaces.command("create <application>").description("Create a workspace when the target application policy permits it").requiredOption("--name <name>", "Workspace display name").option("--default", "Make the new workspace the application default").option("--confirm", "Acknowledge workspace creation").option("--json", "Output JSON").option("--no-json", "Output human-readable text").action(async (application, opts) => {
|
|
13547
|
+
const mode = resolveOutputMode(opts.json);
|
|
13548
|
+
try {
|
|
13549
|
+
const topolo = await requiredClient2();
|
|
13550
|
+
const result = await topolo.workspaces.create(application, {
|
|
13551
|
+
name: opts.name.trim(),
|
|
13552
|
+
isDefault: opts.default === true,
|
|
13553
|
+
confirm: opts.confirm === true
|
|
13554
|
+
});
|
|
13555
|
+
emit(result, mode, () => formatWorkspaceMutation(result));
|
|
13556
|
+
} catch (error) {
|
|
13557
|
+
fail(error, mode);
|
|
13558
|
+
}
|
|
13559
|
+
});
|
|
13560
|
+
workspaces.command("rename <application> <workspace-id>").description("Rename a caller-authorized workspace without changing its stable id").requiredOption("--name <name>", "New workspace display name").option("--confirm", "Acknowledge workspace rename").option("--json", "Output JSON").option("--no-json", "Output human-readable text").action(async (application, workspaceId, opts) => {
|
|
13561
|
+
const mode = resolveOutputMode(opts.json);
|
|
13562
|
+
try {
|
|
13563
|
+
const topolo = await requiredClient2();
|
|
13564
|
+
const result = await topolo.workspaces.rename(application, workspaceId, opts.name.trim(), opts.confirm === true);
|
|
13565
|
+
emit(result, mode, () => formatWorkspaceMutation(result));
|
|
13566
|
+
} catch (error) {
|
|
13567
|
+
fail(error, mode);
|
|
13568
|
+
}
|
|
13569
|
+
});
|
|
13570
|
+
workspaces.command("default <application> <workspace-id>").description("Set the default workspace for one application").option("--confirm", "Acknowledge the default-workspace change").option("--json", "Output JSON").option("--no-json", "Output human-readable text").action(async (application, workspaceId, opts) => {
|
|
13571
|
+
const mode = resolveOutputMode(opts.json);
|
|
13572
|
+
try {
|
|
13573
|
+
const topolo = await requiredClient2();
|
|
13574
|
+
const result = await topolo.workspaces.setDefault(application, workspaceId, opts.confirm === true);
|
|
13575
|
+
emit(result, mode, () => formatWorkspaceMutation(result));
|
|
13576
|
+
} catch (error) {
|
|
13577
|
+
fail(error, mode);
|
|
13578
|
+
}
|
|
13579
|
+
});
|
|
13580
|
+
}
|
|
13581
|
+
async function requiredClient2() {
|
|
13582
|
+
const topolo = await buildClient();
|
|
13583
|
+
if (!topolo) throw new Error("No credential configured. Run `topolo auth login` or set TOPOLO_API_KEY.");
|
|
13584
|
+
return topolo;
|
|
13585
|
+
}
|
|
13586
|
+
function formatWorkspaceList(result) {
|
|
13587
|
+
const { data } = result;
|
|
13588
|
+
const lines = [
|
|
13589
|
+
`Application: ${data.appId}`,
|
|
13590
|
+
`Organization: ${data.organizationId}`,
|
|
13591
|
+
`Can create: ${data.policy.canCreate ? "yes" : "no"} (${data.policy.reason})`
|
|
13592
|
+
];
|
|
13593
|
+
if (!data.workspaces.length) return [...lines, "", "No accessible workspaces."].join("\n");
|
|
13594
|
+
lines.push("", "DEFAULT ID NAME ACCESS");
|
|
13595
|
+
for (const workspace of data.workspaces) {
|
|
13596
|
+
lines.push(`${workspace.isDefault ? "yes" : "no"} ${workspace.id} ${workspace.name} ${workspace.accessLevel}`);
|
|
13597
|
+
}
|
|
13598
|
+
return lines.join("\n");
|
|
13599
|
+
}
|
|
13600
|
+
function formatWorkspaceMutation(result) {
|
|
13601
|
+
const workspace = result.data.workspace;
|
|
13602
|
+
return `${result.message}
|
|
13603
|
+
Workspace: ${workspace.name} (${workspace.id})${workspace.isDefault ? "\nDefault: yes" : ""}`;
|
|
13604
|
+
}
|
|
13605
|
+
|
|
13454
13606
|
// src/index.ts
|
|
13455
13607
|
var program = new Command();
|
|
13456
13608
|
var argv;
|
|
@@ -13482,6 +13634,7 @@ registerSeedCommands(program);
|
|
|
13482
13634
|
registerSkillsCommand(program);
|
|
13483
13635
|
registerContextCommand(program);
|
|
13484
13636
|
registerResourcesCommand(program);
|
|
13637
|
+
registerWorkspacesCommand(program);
|
|
13485
13638
|
program.parseAsync(argv).catch((err) => {
|
|
13486
13639
|
process.stderr.write(`Unexpected error: ${err instanceof Error ? err.message : String(err)}
|
|
13487
13640
|
`);
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@topolo/cli",
|
|
3
|
-
"version": "0.6.
|
|
4
|
-
"topoloPublishedVersion": "0.6.
|
|
3
|
+
"version": "0.6.10",
|
|
4
|
+
"topoloPublishedVersion": "0.6.10",
|
|
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": {
|
|
@@ -11,20 +11,6 @@
|
|
|
11
11
|
"dist",
|
|
12
12
|
"skills"
|
|
13
13
|
],
|
|
14
|
-
"dependencies": {
|
|
15
|
-
"commander": "^12.1.0"
|
|
16
|
-
},
|
|
17
|
-
"devDependencies": {
|
|
18
|
-
"@topolo/sdk": "file:../topolo-sdk",
|
|
19
|
-
"@types/node": "^20.12.0",
|
|
20
|
-
"@vitest/coverage-v8": "3.2.6",
|
|
21
|
-
"tsup": "^8.0.0",
|
|
22
|
-
"typescript": "^5.4.0",
|
|
23
|
-
"vitest": "3.2.6"
|
|
24
|
-
},
|
|
25
|
-
"engines": {
|
|
26
|
-
"node": ">=20"
|
|
27
|
-
},
|
|
28
14
|
"scripts": {
|
|
29
15
|
"build:sdk": "python3 ../../tooling/with-resource-lock.py ../topolo-sdk -- pnpm --dir ../topolo-sdk build",
|
|
30
16
|
"prebuild": "pnpm run build:sdk",
|
|
@@ -38,5 +24,19 @@
|
|
|
38
24
|
"test:watch": "vitest",
|
|
39
25
|
"precoverage": "pnpm run build:sdk",
|
|
40
26
|
"coverage": "vitest run --coverage"
|
|
27
|
+
},
|
|
28
|
+
"dependencies": {
|
|
29
|
+
"commander": "^12.1.0"
|
|
30
|
+
},
|
|
31
|
+
"devDependencies": {
|
|
32
|
+
"@topolo/sdk": "file:../topolo-sdk",
|
|
33
|
+
"@types/node": "^20.12.0",
|
|
34
|
+
"@vitest/coverage-v8": "3.2.6",
|
|
35
|
+
"tsup": "^8.0.0",
|
|
36
|
+
"typescript": "^5.4.0",
|
|
37
|
+
"vitest": "3.2.6"
|
|
38
|
+
},
|
|
39
|
+
"engines": {
|
|
40
|
+
"node": ">=20"
|
|
41
41
|
}
|
|
42
|
-
}
|
|
42
|
+
}
|
package/skills/topolo/SKILL.md
CHANGED
|
@@ -72,12 +72,24 @@ topolo resources <application> --json
|
|
|
72
72
|
|
|
73
73
|
Use the returned app id or slug. Read `apiKeyResources` to learn whether calls can be scoped to aliases such as a workspace, site, mailbox, brand, or venue. Use `topolo resources` to resolve the actual credential-scoped resource ids. With an API key it returns only the key's bound resources; with OAuth it uses the published Auth resource catalog. Never assume that an organization id is a valid app resource id.
|
|
74
74
|
|
|
75
|
+
Workspace lifecycle is canonically owned by Topolo Auth but authorized against the concrete target application. Never conclude that workspace provisioning is unavailable because the target app's own action catalog does not duplicate Auth actions. Always run:
|
|
76
|
+
|
|
77
|
+
```bash
|
|
78
|
+
topolo workspaces list <application> --json
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
Its `data.policy.canCreate` and `data.policy.reason` are the caller-specific creation decision for that application. Organization ownership does not imply permission to create workspaces for every app. When permitted, the first-class mutations are `topolo workspaces create`, `topolo workspaces rename`, and `topolo workspaces default`; each still requires confirmation and Auth performs the final runtime authorization.
|
|
82
|
+
|
|
75
83
|
MCP equivalents:
|
|
76
84
|
|
|
77
85
|
- `topolo_search_applications`
|
|
78
86
|
- `topolo_get_application`
|
|
79
87
|
- `topolo_get_resource_types`
|
|
80
88
|
- `topolo_list_resources`
|
|
89
|
+
- `topolo_list_workspaces`
|
|
90
|
+
- `topolo_create_workspace`
|
|
91
|
+
- `topolo_rename_workspace`
|
|
92
|
+
- `topolo_set_default_workspace`
|
|
81
93
|
|
|
82
94
|
### 2. Discover credential-scoped actions
|
|
83
95
|
|