@homespunapps/cli 1.6.17 → 1.6.19

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.
@@ -104,29 +104,42 @@ async function runSetRole(args) {
104
104
  assertKnownFlags(args, ...specFor("members", "set-role"));
105
105
  const appArg = args.flags.get("app");
106
106
  if (!appArg) {
107
- fail("usage: homespun members set-role --app <idOrSlug> --human <humanId> (--custom-role <name> | --clear-role)", "invalid_args");
107
+ fail("usage: homespun members set-role --app <idOrSlug> --human <humanId> (--custom-role <name[,name...]> | --clear-role)", "invalid_args");
108
108
  }
109
109
  const humanId = args.flags.get("human");
110
110
  if (!humanId) {
111
111
  fail("--human is required", "invalid_args");
112
112
  }
113
- // Clearing a role is a real instruction, so it gets its own explicit flag
114
- // rather than being spelled as an omitted or empty --custom-role: an omitted
115
- // value must never silently wipe someone's role.
113
+ // Clearing a member's roles is a real instruction, so it gets its own
114
+ // explicit flag rather than being spelled as an omitted or empty
115
+ // --custom-role: an omitted value must never silently wipe someone's roles.
116
116
  const customRole = args.flags.get("custom-role");
117
117
  const clear = args.bools.has("clear-role");
118
118
  if (clear && customRole !== undefined) {
119
119
  fail("--custom-role and --clear-role are mutually exclusive", "invalid_args");
120
120
  }
121
121
  if (!clear && customRole === undefined) {
122
- fail("one of --custom-role <name> or --clear-role is required", "invalid_args");
123
- }
122
+ fail("one of --custom-role <name[,name...]> or --clear-role is required", "invalid_args");
123
+ }
124
+ // A member may hold several roles, so the flag takes a comma-separated list.
125
+ // A role name can never contain a comma (the manifest validator's role-name
126
+ // grammar is lowercase letters, digits and underscore), so the split is
127
+ // unambiguous. An empty entry is rejected rather than dropped: a trailing
128
+ // comma is a typo, and silently ignoring it would apply a role set the caller
129
+ // did not write.
130
+ const customRoles = clear
131
+ ? []
132
+ : customRole.split(",").map((r) => {
133
+ const trimmed = r.trim();
134
+ if (trimmed.length === 0) {
135
+ fail("--custom-role must not contain an empty role name", "invalid_args");
136
+ }
137
+ return trimmed;
138
+ });
124
139
  const client = makeClient(args);
125
140
  const appId = await resolveAppId(client, appArg);
126
141
  try {
127
- printJson(await client.setAppMemberRole(appId, humanId, {
128
- customRole: clear ? null : customRole,
129
- }));
142
+ printJson(await client.setAppMemberRole(appId, humanId, { customRoles }));
130
143
  }
131
144
  catch (e) {
132
145
  failFromError(e);
@@ -338,7 +338,7 @@ const MEMBERS = {
338
338
  },
339
339
  {
340
340
  verb: "set-role",
341
- summary: "Changes an existing member's custom role in place.",
341
+ summary: "Replaces an existing member's declared roles in place.",
342
342
  flags: [
343
343
  {
344
344
  name: "app",
@@ -352,8 +352,8 @@ const MEMBERS = {
352
352
  },
353
353
  {
354
354
  name: "custom-role",
355
- value: "<name>",
356
- description: "Custom role to assign; must be declared in the app's manifest",
355
+ value: "<name[,name...]>",
356
+ description: "Declared role(s) to assign, comma-separated; each must be declared in the app's manifest",
357
357
  },
358
358
  ],
359
359
  bools: [
@@ -808,7 +808,7 @@ const DEPLOY = {
808
808
  notes: [
809
809
  "Packaging has one canonical shape and one escape hatch. A directory deploy (homespun deploy ./my-app) reads ./my-app/index.html and ./my-app/manifest.json: fixed filenames, no discovery heuristics, and both files are required. The single-file escape hatch (homespun deploy ./index.html --manifest ./manifest.json) takes the manifest from --manifest, which accepts a file path or inline JSON.",
810
810
  "Create versus redeploy is decided by the presence of --app, not by two verbs. With no --app this creates an app (POST /v1/apps); new apps default to private (owner plus invited members, sign-in gated), --slug is accepted with private or public visibility including the default, and an explicit --visibility link always gets a server-generated slug and rejects --slug. With --app <id> this redeploys (POST /v1/apps/:id/versions), where --slug and --visibility are rejected because the slug is immutable and visibility changes go through 'homespun apps update'.",
811
- "--check is a dry run. It runs the full manifest and asset-shape validation, the redeploy compat gate (with --app), and the schedule-timezone advisory, then prints { ok, warnings, compat, breaks } without creating a version or mutating anything. An invalid manifest fails the same way a real deploy would, and a narrowing redeploy reports the compat break instead of applying it.",
811
+ "--check is a dry run. It runs the full manifest and asset-shape validation, the redeploy compat gate (with --app), and the schedule-timezone advisory, then prints { ok, warnings, compat, breaks } without creating a version or mutating anything. An invalid manifest fails the same way a real deploy would, and a redeploy the compat gate would refuse reports the break instead of applying it.",
812
812
  ],
813
813
  outputNote: 'Output is JSON: { app_id, slug, url, version, visibility, created, share_url, compat, breaks, warnings }. share_url is present only when creating a link-visibility app: it carries the app share token in its #k= fragment and is shown ONCE, it is not recoverable later, and it can be rotated with \'homespun apps share-link rotate <app>\'. warnings flags non-fatal issues, for example an app that declares schedules with no timezone set (reminders fire at 08:00 UTC until one is set). Errors go to stderr as {"error":{"code","message"}} with a non-zero exit.',
814
814
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@homespunapps/cli",
3
- "version": "1.6.17",
3
+ "version": "1.6.19",
4
4
  "description": "Command-line client for the Homespun relay: deploy a real multi-user web app from your agent, then keep reading and writing its data.",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -36,7 +36,7 @@
36
36
  "test:unit": "vitest run"
37
37
  },
38
38
  "dependencies": {
39
- "@homespunapps/core": "^1.6.17",
39
+ "@homespunapps/core": "^1.6.19",
40
40
  "qrcode-terminal": "^0.12.0"
41
41
  },
42
42
  "devDependencies": {