@topolo/cli 0.6.8 → 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 +147 -4
- package/package.json +2 -2
- 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
|
@@ -3735,10 +3735,19 @@ function mapActionInput(action, input3) {
|
|
|
3735
3735
|
delete remaining[key];
|
|
3736
3736
|
return encodeURIComponent(String(value));
|
|
3737
3737
|
});
|
|
3738
|
-
const
|
|
3739
|
-
|
|
3740
|
-
const
|
|
3741
|
-
|
|
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
|
+
}
|
|
3742
3751
|
if (action.method === "GET") {
|
|
3743
3752
|
return {
|
|
3744
3753
|
path: path9,
|
|
@@ -3862,6 +3871,65 @@ var IdentityModule = class {
|
|
|
3862
3871
|
return this.client.introspect();
|
|
3863
3872
|
}
|
|
3864
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
|
+
};
|
|
3865
3933
|
var DEVICE_GRANT = "urn:ietf:params:oauth:grant-type:device_code";
|
|
3866
3934
|
var TopoloOAuth = class {
|
|
3867
3935
|
baseUrl;
|
|
@@ -3945,6 +4013,7 @@ function createTopolo(options) {
|
|
|
3945
4013
|
return {
|
|
3946
4014
|
client,
|
|
3947
4015
|
identity: new IdentityModule(client),
|
|
4016
|
+
workspaces: new WorkspacesModule(client),
|
|
3948
4017
|
crm: new CrmModule(client)
|
|
3949
4018
|
};
|
|
3950
4019
|
}
|
|
@@ -13461,6 +13530,79 @@ function formatResources(result) {
|
|
|
13461
13530
|
return lines.join("\n");
|
|
13462
13531
|
}
|
|
13463
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
|
+
|
|
13464
13606
|
// src/index.ts
|
|
13465
13607
|
var program = new Command();
|
|
13466
13608
|
var argv;
|
|
@@ -13492,6 +13634,7 @@ registerSeedCommands(program);
|
|
|
13492
13634
|
registerSkillsCommand(program);
|
|
13493
13635
|
registerContextCommand(program);
|
|
13494
13636
|
registerResourcesCommand(program);
|
|
13637
|
+
registerWorkspacesCommand(program);
|
|
13495
13638
|
program.parseAsync(argv).catch((err) => {
|
|
13496
13639
|
process.stderr.write(`Unexpected error: ${err instanceof Error ? err.message : String(err)}
|
|
13497
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": {
|
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
|
|