@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 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 explicitQuery = plainObject(remaining.query);
3739
- delete remaining.query;
3740
- const explicitBody = Object.prototype.hasOwnProperty.call(remaining, "body") ? remaining.body : void 0;
3741
- delete remaining.body;
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.8",
4
- "topoloPublishedVersion": "0.6.8",
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": {
@@ -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