@retasc/cli 1.19.0 → 1.20.0

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/CHANGELOG.md CHANGED
@@ -6,6 +6,13 @@ release commits and the issues they reference.
6
6
 
7
7
  Dates are the npm publish date. Each entry names the RTSC issue behind it.
8
8
 
9
+ ## 1.20.0 (2026-08-04)
10
+
11
+ - **RTSC-561** — the org gained an **admin** role, and the CLI stopped refusing it.
12
+ `retasc billing` gated on `role !== "owner"` client-side, so it would have refused an
13
+ admin locally for a call the server allows. Owner-only copy in `bind` and the command
14
+ help now names both roles.
15
+
9
16
  ## 1.19.0 (2026-08-03)
10
17
 
11
18
  - **RTSC-495** — `retasc bind --setup <code>` sets a folder up with no sign-in and nothing
@@ -44,8 +44,10 @@ export async function billingAction(opts) {
44
44
  const org = me.orgs.find((o) => o.id === orgId);
45
45
  orgLabel = org ? `${org.name}${org.slug ? ` (${org.slug})` : ""}` : String(orgId);
46
46
  // Fail early with the actionable message rather than a raw FORBIDDEN from the server.
47
- if (org && org.role !== "owner") {
48
- throw new Error(`Billing is owner-only your role in ${orgLabel} is "${org.role}".`);
47
+ // Owner OR admin (RTSC-561) this mirrors `requireOwnerOrAdmin`, and a stale copy of
48
+ // the rule here would refuse an admin locally for a call the server would have allowed.
49
+ if (org && org.role !== "owner" && org.role !== "admin") {
50
+ throw new Error(`Billing needs the owner or admin role — yours in ${orgLabel} is "${org.role}".`);
49
51
  }
50
52
  const [status, charges] = await Promise.all([
51
53
  api.billingStatus({ orgId: orgId }),
@@ -286,7 +286,7 @@ export async function completeWorkspaceSetup(args) {
286
286
  let prefix;
287
287
  if (!projectId && opts.project && opts.prefix) {
288
288
  if (!args.canCreateProject) {
289
- cliError("FORBIDDEN", "Only an owner can create a project.", `Ask an owner to add one, then pass --project-id <id>.`);
289
+ cliError("FORBIDDEN", "Only an owner or admin can create a project.", `Ask one of them to add one, then pass --project-id <id>.`);
290
290
  }
291
291
  const p = (await api.createProject({ orgId, name: opts.project, prefix: opts.prefix }));
292
292
  projectId = p.projectId;
@@ -301,7 +301,7 @@ export async function completeWorkspaceSetup(args) {
301
301
  // what to ask for, by name. A bare FORBIDDEN from `createProject` would be both
302
302
  // wrong (they never asked to create anything) and unactionable.
303
303
  if (!list.length) {
304
- cliError("NO_PROJECTS", `Org ${org} has no projects yet, and only an owner can create one.`, `Ask an owner to add a project, then finish this folder with: ${selfCommand(VERSION)} bind --org-id ${orgId}`);
304
+ cliError("NO_PROJECTS", `Org ${org} has no projects yet, and only an owner or admin can create one.`, `Ask one of them to add a project, then finish this folder with: ${selfCommand(VERSION)} bind --org-id ${orgId}`);
305
305
  }
306
306
  // One project is not a choice. Asking anyway would be the only question in the
307
307
  // common invite path, for an answer that was never in doubt.
@@ -223,8 +223,9 @@ export async function joinAction(link, opts) {
223
223
  orgLabel,
224
224
  opts,
225
225
  existing: guard.existing,
226
- // `createProject` is `requireOwner`, and an invite only ever grants `member`. Offering
227
- // "+ create new" here would be offering a refusal dressed as a choice.
226
+ // `createProject` is `requireOwnerOrAdmin`, and an invite only ever grants `member`
227
+ // (admin is granted by `setMemberRole` after joining, never by a bearer code
228
+ // RTSC-561). Offering "+ create new" here would be offering a refusal as a choice.
228
229
  canCreateProject: false,
229
230
  });
230
231
  }
package/dist/index.js CHANGED
@@ -211,7 +211,7 @@ program
211
211
  });
212
212
  program
213
213
  .command("billing")
214
- .description("Show the org's billing: subscription, what's owed now, and the charge + on-chain payment history across every payment link ever used (owner only).")
214
+ .description("Show the org's billing: subscription, what's owed now, and the charge + on-chain payment history across every payment link ever used (owner or admin).")
215
215
  .option("--org-id <id>", "Which org (defaults to your only one).")
216
216
  .option("--json", "Emit the raw payload instead of the summary.")
217
217
  .action(async (opts) => {
@@ -356,7 +356,7 @@ key
356
356
  const members = program.command("members").description("Invite people to an org and manage invites.");
357
357
  members
358
358
  .command("invite")
359
- .description("Mint a single-use invite code for an org (owner only). Shown once.")
359
+ .description("Mint a single-use invite code for an org (owner or admin). Shown once.")
360
360
  .requiredOption("--org-id <id>")
361
361
  .option("--expires-days <n>", "Days until the code expires (1–30, default 7)", (v) => parseInt(v, 10))
362
362
  .action(async (opts) => {
@@ -377,7 +377,7 @@ members
377
377
  });
378
378
  members
379
379
  .command("list")
380
- .description("List an org's invites and their status (owner only).")
380
+ .description("List an org's invites and their status (owner or admin).")
381
381
  .requiredOption("--org-id <id>")
382
382
  .option("--json", "Emit the raw payload instead of the table.")
383
383
  .action(async (opts) => {
@@ -392,7 +392,7 @@ members
392
392
  });
393
393
  members
394
394
  .command("revoke")
395
- .description("Revoke an unused invite (owner only).")
395
+ .description("Revoke an unused invite (owner or admin).")
396
396
  .requiredOption("--invite-id <id>")
397
397
  .action(async (opts) => {
398
398
  requireLogin();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@retasc/cli",
3
- "version": "1.19.0",
3
+ "version": "1.20.0",
4
4
  "description": "Retasc CLI \u2014 the issue tracker AI agents pull work from. Sign in with GitHub or Google, create projects, mint agent API keys, and wire your agent to the Retasc MCP server in one command.",
5
5
  "type": "module",
6
6
  "bin": {