ente-cli 0.4.84 → 0.4.85

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/dist/SKILL.md CHANGED
@@ -188,7 +188,7 @@ ente activity delete <activityId> [--json]
188
188
  ente activity restore <activityId> [--json]
189
189
 
190
190
  # Database lists (entity + typed columns; each row is a note/task activity; no date column type)
191
- ente list create "<name>" --space "<space>" [--column "key[=Label]:text|number|boolean|single-select|multi-select[:opt,opt]"] [--pin "<parent>" [--share-parents --parent-space-grant-fingerprint <token>]] [--json]
191
+ ente list create "<name>" --space "<space>" [--icon <topic-icon>] [--column "key[=Label]:text|number|boolean|single-select|multi-select[:opt,opt]"] [--pin "<parent>" [--share-parents --parent-space-grant-fingerprint <token>]] [--json]
192
192
  ente list add "<list>" --title "…" [--attr k=v ...] [--key <textColumn>] [--content-file ./row.md] [--json] # --key = upsert by that cell
193
193
  ente list update "<list>" <rowId> [--attr k=v ...] [--title "…"] [--json] # empty value clears a cell
194
194
  ente list import "<list>" --file rows.jsonl --key <textColumn> [--dry-run] [--concurrency 4] [--json] # {"title","attrs","content"} per line → created/updated/unchanged
@@ -443,7 +443,7 @@ ente recording backups join <backupId> [--now] # retry a failed join; --now
443
443
  ### Feedback board
444
444
 
445
445
  ```bash
446
- ente feedback send "<summary>" [--category missing|confusing|wrong_data|workflow|bug|other] \
446
+ ente feedback send "<summary>" \
447
447
  [--situation "…"] [--goal "…"] [--need-to-see "…"] [--need-to-do "…"] [--reason "…"] [--workaround "…"]
448
448
  ente feedback list [--status offen|todo|bearbeitung|klaerung|abgeschlossen|spaeter] [--text "…"] [--limit 50] [--json]
449
449
  ente feedback view <feedbackId> [--json] # full item + comment thread
@@ -452,7 +452,7 @@ ente feedback status <feedbackId> <status> # move to a board colum
452
452
  ```
453
453
 
454
454
  - Same in-app feedback board as the web composer (`convex/feedback.ts` public functions) — no GitHub issue.
455
- - Every authenticated user may submit and manage the shared board. Screenshots/attachments are web-composer only.
455
+ - Human sign-in only: the board refuses agent-bound CLI keys (every CLI key since 2026-10-11) and the CLI says so; use the web board then. Categories became tags (set them in the web). Screenshots/attachments are web-composer only.
456
456
 
457
457
  ### Deadline formats
458
458
 
package/dist/cli.mjs CHANGED
@@ -3800,7 +3800,7 @@ async function runCli(cli2, argv, context) {
3800
3800
  }
3801
3801
 
3802
3802
  // ente/version.ts
3803
- var VERSION = "0.4.84";
3803
+ var VERSION = "0.4.85";
3804
3804
 
3805
3805
  // ente/branding.ts
3806
3806
  var NPM_REGISTRY = "https://registry.npmjs.org";
@@ -3907,17 +3907,20 @@ function keyToFile(cacheDir, key) {
3907
3907
  const safe = key.replace(/[^a-zA-Z0-9._-]/g, "_");
3908
3908
  return path4.join(cacheDir, `jwt-${safe}.json`);
3909
3909
  }
3910
- function loadJwt(cacheDir, key) {
3910
+ function loadJwtEntry(cacheDir, key) {
3911
3911
  try {
3912
3912
  const file = keyToFile(cacheDir, key);
3913
3913
  if (!existsSync4(file)) return null;
3914
3914
  const entry = JSON.parse(readFileSync4(file, "utf8"));
3915
3915
  if (typeof entry.jwt !== "string" || typeof entry.expiresAt !== "number") return null;
3916
- return entry.expiresAt - SKEW_MS > Date.now() ? entry.jwt : null;
3916
+ return isJwtFresh(entry.expiresAt) ? { jwt: entry.jwt, expiresAt: entry.expiresAt } : null;
3917
3917
  } catch {
3918
3918
  return null;
3919
3919
  }
3920
3920
  }
3921
+ function isJwtFresh(expiresAt, now = Date.now()) {
3922
+ return expiresAt - SKEW_MS > now;
3923
+ }
3921
3924
  function storeJwt(cacheDir, key, jwt, expiresAt) {
3922
3925
  const file = keyToFile(cacheDir, key);
3923
3926
  mkdirSync4(path4.dirname(file), { recursive: true, mode: 448 });
@@ -10827,7 +10830,7 @@ async function exchangeToken(siteUrl, apiKey) {
10827
10830
  if (!body.token) throw new Error("/cli/token returned no token.");
10828
10831
  return { jwt: body.token, expiresAt: body.expiresAt ?? Date.now() + DEFAULT_TTL_MS };
10829
10832
  }
10830
- function isAuthError(err) {
10833
+ function isAuthError(err, structuredOnly = false) {
10831
10834
  if (err && typeof err === "object" && "data" in err) {
10832
10835
  const data = err.data;
10833
10836
  if (data && typeof data === "object" && "code" in data) {
@@ -10836,33 +10839,74 @@ function isAuthError(err) {
10836
10839
  }
10837
10840
  }
10838
10841
  const msg = err instanceof Error ? err.message : String(err);
10842
+ if (convexHttpErrorCode(msg) === "InvalidAuthHeader") return true;
10843
+ if (structuredOnly) return false;
10839
10844
  return /\b(401|403|unauthenticated|unauthorized|not authenticated)\b/i.test(msg);
10840
10845
  }
10846
+ function convexHttpErrorCode(message) {
10847
+ try {
10848
+ const body = JSON.parse(message);
10849
+ return body && typeof body === "object" && "code" in body && typeof body.code === "string" ? body.code : null;
10850
+ } catch {
10851
+ return null;
10852
+ }
10853
+ }
10854
+ function isWriteConflict(err) {
10855
+ const msg = err instanceof Error ? err.message : String(err);
10856
+ return /OptimisticConcurrencyControlFailure/u.test(msg);
10857
+ }
10858
+ var MAX_CONFLICT_ATTEMPTS = 6;
10859
+ var DEFAULT_CONFLICT_BACKOFF_MS = 250;
10841
10860
  async function mintAndCache(opts) {
10842
10861
  const { jwt, expiresAt } = await exchangeToken(opts.siteUrl, opts.apiKey);
10843
10862
  storeJwt(opts.cacheDir, opts.cacheKey, jwt, expiresAt);
10844
- return jwt;
10863
+ return { jwt, expiresAt };
10845
10864
  }
10846
10865
  async function makeCaller(opts) {
10847
10866
  const client = new ConvexHttpClient(opts.convexUrl);
10848
- const cached = loadJwt(opts.cacheDir, opts.cacheKey);
10849
- const jwt = cached ?? await mintAndCache(opts);
10850
- client.setAuth(jwt);
10851
- async function withRetry(fn) {
10867
+ const backoffMs = opts.conflictBackoffMs ?? DEFAULT_CONFLICT_BACKOFF_MS;
10868
+ let current = loadJwtEntry(opts.cacheDir, opts.cacheKey) ?? await mintAndCache(opts);
10869
+ client.setAuth(current.jwt);
10870
+ let renewal = null;
10871
+ function renew() {
10872
+ renewal ??= mintAndCache(opts).then((entry) => {
10873
+ current = entry;
10874
+ client.setAuth(entry.jwt);
10875
+ }).finally(() => {
10876
+ renewal = null;
10877
+ });
10878
+ return renewal;
10879
+ }
10880
+ async function withAuth(fn, structuredOnly = false) {
10881
+ if (renewal) await renewal;
10882
+ else if (!isJwtFresh(current.expiresAt)) await renew();
10883
+ const used = current;
10852
10884
  try {
10853
10885
  return await fn();
10854
10886
  } catch (err) {
10855
- if (!isAuthError(err)) throw err;
10856
- client.setAuth(await mintAndCache(opts));
10887
+ if (!isAuthError(err, structuredOnly)) throw err;
10888
+ if (current === used) await renew();
10889
+ else if (renewal) await renewal;
10857
10890
  return fn();
10858
10891
  }
10859
10892
  }
10893
+ async function withConflictRetry(fn) {
10894
+ for (let attempt = 1; ; attempt++) {
10895
+ try {
10896
+ return await withAuth(fn);
10897
+ } catch (err) {
10898
+ if (!isWriteConflict(err) || attempt >= MAX_CONFLICT_ATTEMPTS) throw err;
10899
+ const delay = backoffMs * 2 ** (attempt - 1);
10900
+ await new Promise((resolve2) => setTimeout(resolve2, delay / 2 + Math.random() * delay));
10901
+ }
10902
+ }
10903
+ }
10860
10904
  return {
10861
10905
  convexUrl: opts.convexUrl,
10862
- query: (ref, ...args) => withRetry(() => client.query(ref, ...args)),
10863
- mutation: (ref, ...args) => withRetry(() => client.mutation(ref, ...args)),
10864
- mutationUnqueued: (ref, ...args) => withRetry(() => client.mutation(ref, args[0] ?? {}, { skipQueue: true })),
10865
- action: (ref, ...args) => withRetry(() => client.action(ref, ...args))
10906
+ query: (ref, ...args) => withAuth(() => client.query(ref, ...args)),
10907
+ mutation: (ref, ...args) => withConflictRetry(() => client.mutation(ref, ...args)),
10908
+ mutationUnqueued: (ref, ...args) => withConflictRetry(() => client.mutation(ref, args[0] ?? {}, { skipQueue: true })),
10909
+ action: (ref, ...args) => withAuth(() => client.action(ref, ...args), true)
10866
10910
  };
10867
10911
  }
10868
10912
 
@@ -13729,6 +13773,72 @@ var TASK_COMMANDS = [
13729
13773
  // ente/commands/list.ts
13730
13774
  import { readFileSync as readFileSync10 } from "node:fs";
13731
13775
 
13776
+ // ../packages/shared/src/agent-icon.ts
13777
+ var entityCustomIconIds = [
13778
+ "lightbulb",
13779
+ "star",
13780
+ "bookmark",
13781
+ "target",
13782
+ "flag",
13783
+ "brain",
13784
+ "book-open",
13785
+ "puzzle",
13786
+ "zap",
13787
+ "compass",
13788
+ "map",
13789
+ "wrench",
13790
+ "shield",
13791
+ "heart-handshake",
13792
+ "trophy",
13793
+ "globe-2",
13794
+ "network",
13795
+ "landmark",
13796
+ "leaf",
13797
+ "route",
13798
+ "binoculars",
13799
+ "gem",
13800
+ "crown",
13801
+ "key",
13802
+ "satellite",
13803
+ "sun"
13804
+ ];
13805
+ var activityCustomIconIds = [
13806
+ "fish",
13807
+ "bird",
13808
+ "rabbit",
13809
+ "turtle",
13810
+ "squirrel",
13811
+ "snail",
13812
+ "bug",
13813
+ "wand-sparkles",
13814
+ "drama",
13815
+ "music",
13816
+ "dice-5",
13817
+ "gift",
13818
+ "flame",
13819
+ "coffee",
13820
+ "party-popper",
13821
+ "rainbow",
13822
+ "umbrella",
13823
+ "anchor",
13824
+ "sailboat",
13825
+ "gamepad-2",
13826
+ "ghost",
13827
+ "bot",
13828
+ "cloud",
13829
+ "moon",
13830
+ "smile"
13831
+ ];
13832
+ var AGENT_ICON_IDS = [...entityCustomIconIds, ...activityCustomIconIds];
13833
+ function parseAgentIconInput(raw) {
13834
+ if (!raw || typeof raw !== "object" || Array.isArray(raw)) throw new Error("Expected { icon: curated icon ID or null }");
13835
+ const input = raw;
13836
+ if (Object.keys(input).length !== 1 || !("icon" in input) || input.icon !== null && !AGENT_ICON_IDS.some((icon) => icon === input.icon)) {
13837
+ throw new Error("Expected only icon: a curated icon ID or null; no agent ID is accepted");
13838
+ }
13839
+ return { icon: input.icon };
13840
+ }
13841
+
13732
13842
  // ente/databaseList.ts
13733
13843
  var COLUMN_TYPES = ["text", "number", "boolean", "single-select", "multi-select"];
13734
13844
  var SELECT_TYPES = /* @__PURE__ */ new Set(["single-select", "multi-select"]);
@@ -13950,6 +14060,12 @@ ${failures.map((e) => ` - ${e instanceof Error ? e.message : String(e)}`).join(
13950
14060
 
13951
14061
  // ente/commands/list.ts
13952
14062
  var DATABASE_LIST_TABS = ["database", "activities"];
14063
+ function assertListIcon(icon, type) {
14064
+ if (type !== "topic")
14065
+ throw new Error(`--icon works only for topic lists; drop it or use --type topic (got '${type}').`);
14066
+ if (!entityCustomIconIds.includes(icon))
14067
+ throw new Error(`'${icon}' is not a list icon. Choose one of: ${entityCustomIconIds.join(", ")}.`);
14068
+ }
13953
14069
  var ROW_PAGE_SIZE = 100;
13954
14070
  var entitiesCreate2 = makeFunctionReference("entities:create");
13955
14071
  var updateDatabaseView = makeFunctionReference("entities:updateDatabaseView");
@@ -14105,7 +14221,7 @@ var ListCreate = class extends ProfileCommand {
14105
14221
  });
14106
14222
  description = options_exports.String("--description", { description: "Searchable description for the list" });
14107
14223
  icon = options_exports.String("--icon", {
14108
- description: "Entity icon as named in the web icon picker (e.g. book-open, leaf, star)"
14224
+ description: `Topic icon from the web icon picker: ${entityCustomIconIds.join(", ")}`
14109
14225
  });
14110
14226
  pin = options_exports.Array("--pin", { description: "Pin the list under parent entities (resolved by name)" });
14111
14227
  shareParents = options_exports.Boolean("--share-parents", false, {
@@ -14119,6 +14235,7 @@ var ListCreate = class extends ProfileCommand {
14119
14235
  throw new Error(`--type must be 'topic' or 'project' (got '${this.type}').`);
14120
14236
  }
14121
14237
  const columns = (this.column ?? []).map(parseColumn);
14238
+ if (this.icon !== void 0) assertListIcon(this.icon, this.type);
14122
14239
  const caller = await resolveCaller();
14123
14240
  const spaceId = await resolveSpaceByNameC(caller, this.space);
14124
14241
  const entityId = await caller.mutation(entitiesCreate2, {
@@ -15699,72 +15816,6 @@ var RECORDINGS_COMMANDS = [
15699
15816
  RecordingBackupJoinCommand
15700
15817
  ];
15701
15818
 
15702
- // ../packages/shared/src/agent-icon.ts
15703
- var entityCustomIconIds = [
15704
- "lightbulb",
15705
- "star",
15706
- "bookmark",
15707
- "target",
15708
- "flag",
15709
- "brain",
15710
- "book-open",
15711
- "puzzle",
15712
- "zap",
15713
- "compass",
15714
- "map",
15715
- "wrench",
15716
- "shield",
15717
- "heart-handshake",
15718
- "trophy",
15719
- "globe-2",
15720
- "network",
15721
- "landmark",
15722
- "leaf",
15723
- "route",
15724
- "binoculars",
15725
- "gem",
15726
- "crown",
15727
- "key",
15728
- "satellite",
15729
- "sun"
15730
- ];
15731
- var activityCustomIconIds = [
15732
- "fish",
15733
- "bird",
15734
- "rabbit",
15735
- "turtle",
15736
- "squirrel",
15737
- "snail",
15738
- "bug",
15739
- "wand-sparkles",
15740
- "drama",
15741
- "music",
15742
- "dice-5",
15743
- "gift",
15744
- "flame",
15745
- "coffee",
15746
- "party-popper",
15747
- "rainbow",
15748
- "umbrella",
15749
- "anchor",
15750
- "sailboat",
15751
- "gamepad-2",
15752
- "ghost",
15753
- "bot",
15754
- "cloud",
15755
- "moon",
15756
- "smile"
15757
- ];
15758
- var AGENT_ICON_IDS = [...entityCustomIconIds, ...activityCustomIconIds];
15759
- function parseAgentIconInput(raw) {
15760
- if (!raw || typeof raw !== "object" || Array.isArray(raw)) throw new Error("Expected { icon: curated icon ID or null }");
15761
- const input = raw;
15762
- if (Object.keys(input).length !== 1 || !("icon" in input) || input.icon !== null && !AGENT_ICON_IDS.some((icon) => icon === input.icon)) {
15763
- throw new Error("Expected only icon: a curated icon ID or null; no agent ID is accepted");
15764
- }
15765
- return { icon: input.icon };
15766
- }
15767
-
15768
15819
  // ente/commands/agent.ts
15769
15820
  import { readFileSync as readFileSync12 } from "node:fs";
15770
15821
  import { randomUUID } from "node:crypto";
@@ -16336,7 +16387,6 @@ var AGENDA_COMMANDS = [
16336
16387
  ];
16337
16388
 
16338
16389
  // ente/commands/feedback.ts
16339
- var CATEGORIES = ["missing", "confusing", "wrong_data", "workflow", "bug", "other"];
16340
16390
  var STATUSES = ["offen", "todo", "bearbeitung", "klaerung", "abgeschlossen", "spaeter"];
16341
16391
  var createFeedback = makeFunctionReference(
16342
16392
  "feedback:create"
@@ -16381,9 +16431,23 @@ async function collectColumn(caller, status, text, limit, scope) {
16381
16431
  }
16382
16432
  return items.slice(0, limit);
16383
16433
  }
16384
- function parseCategory(value) {
16385
- if (CATEGORIES.includes(value)) return value;
16386
- throw new Error(`Invalid category '${value}'. Use one of: ${CATEGORIES.join(" | ")}.`);
16434
+ function isAgentRefusal(err) {
16435
+ const data = err && typeof err === "object" && "data" in err ? err.data : null;
16436
+ return !!data && typeof data === "object" && "code" in data && data.code === "FORBIDDEN" && "message" in data && data.message === "Use the agent-scoped operation interface";
16437
+ }
16438
+ async function humanOnlyBoard(fn) {
16439
+ const caller = await resolveCaller();
16440
+ try {
16441
+ return await fn(caller);
16442
+ } catch (err) {
16443
+ if (!isAgentRefusal(err)) throw err;
16444
+ throw new Error(
16445
+ `The feedback board accepts only people signed in to the web app. This CLI key is bound to an Ente agent, and agents may not act as you there (${errorMessage(err)}). File, comment on or move feedback in the web app's feedback board.`
16446
+ );
16447
+ }
16448
+ }
16449
+ function tagNames(item) {
16450
+ return item.tags.map((t) => t.name).join(", ");
16387
16451
  }
16388
16452
  function parseStatus(value) {
16389
16453
  if (STATUSES.includes(value)) return value;
@@ -16396,10 +16460,9 @@ var FeedbackSendCommand = class extends ProfileCommand {
16396
16460
  static paths = [["feedback", "send"]];
16397
16461
  static usage = Command.Usage({
16398
16462
  category: "Feedback",
16399
- description: "File feedback on the in-app board (category + summary + optional detail fields)"
16463
+ description: "File feedback on the in-app board (summary + optional detail fields; human sign-in only)"
16400
16464
  });
16401
16465
  summary = options_exports.String({ required: true, name: "summary" });
16402
- category = options_exports.String("--category", { description: `Category: ${CATEGORIES.join("|")} (default other)` });
16403
16466
  situation = options_exports.String("--situation", { description: "What happened / where you were" });
16404
16467
  goal = options_exports.String("--goal", { description: "What you were trying to do" });
16405
16468
  needToSee = options_exports.String("--need-to-see", { description: "What you expected to see" });
@@ -16409,10 +16472,7 @@ var FeedbackSendCommand = class extends ProfileCommand {
16409
16472
  async run() {
16410
16473
  const summary = this.summary.trim();
16411
16474
  if (summary.length === 0) throw new Error("feedback send: summary must not be empty.");
16412
- const category = parseCategory(this.category ?? "other");
16413
- const caller = await resolveCaller();
16414
- const res = await caller.mutation(createFeedback, {
16415
- category,
16475
+ const res = await humanOnlyBoard((caller) => caller.mutation(createFeedback, {
16416
16476
  summary,
16417
16477
  ...this.situation !== void 0 ? { situation: this.situation } : {},
16418
16478
  ...this.goal !== void 0 ? { goal: this.goal } : {},
@@ -16421,10 +16481,10 @@ var FeedbackSendCommand = class extends ProfileCommand {
16421
16481
  ...this.reason !== void 0 ? { reason: this.reason } : {},
16422
16482
  ...this.workaround !== void 0 ? { currentWorkaround: this.workaround } : {},
16423
16483
  context: CLI_CONTEXT
16424
- });
16484
+ }));
16425
16485
  this.emitText(
16426
- { data: { feedbackId: res.feedbackId, category, summary } },
16427
- () => `Filed feedback ${res.feedbackId} [${category}] "${summary}".`
16486
+ { data: { feedbackId: res.feedbackId, summary } },
16487
+ () => `Filed feedback ${res.feedbackId} "${summary}".`
16428
16488
  );
16429
16489
  }
16430
16490
  };
@@ -16445,12 +16505,13 @@ var FeedbackListCommand = class extends ProfileCommand {
16445
16505
  }
16446
16506
  const statuses = this.status ? [parseStatus(this.status)] : [...STATUSES];
16447
16507
  const scope = this.all ? "all" : "mine";
16448
- const caller = await resolveCaller();
16449
16508
  const collected = [];
16450
- for (const status of statuses) {
16451
- if (collected.length >= limit) break;
16452
- collected.push(...await collectColumn(caller, status, this.text, limit - collected.length, scope));
16453
- }
16509
+ await humanOnlyBoard(async (caller) => {
16510
+ for (const status of statuses) {
16511
+ if (collected.length >= limit) break;
16512
+ collected.push(...await collectColumn(caller, status, this.text, limit - collected.length, scope));
16513
+ }
16514
+ });
16454
16515
  collected.sort((a, b) => b.createdAt - a.createdAt);
16455
16516
  const items = collected.slice(0, limit);
16456
16517
  this.print(
@@ -16458,12 +16519,12 @@ var FeedbackListCommand = class extends ProfileCommand {
16458
16519
  () => items.map((it) => ({
16459
16520
  id: it.id,
16460
16521
  status: it.status,
16461
- category: it.category,
16522
+ tags: tagNames(it),
16462
16523
  summary: it.summary.length > 60 ? `${it.summary.slice(0, 57)}\u2026` : it.summary,
16463
16524
  by: submitter(it),
16464
16525
  created: formatDatetime(it.createdAt)
16465
16526
  })),
16466
- ["id", "status", "category", "summary", "by", "created"]
16527
+ ["id", "status", "tags", "summary", "by", "created"]
16467
16528
  );
16468
16529
  }
16469
16530
  };
@@ -16475,13 +16536,14 @@ var FeedbackViewCommand = class extends ProfileCommand {
16475
16536
  });
16476
16537
  feedbackId = options_exports.String({ required: true, name: "feedbackId" });
16477
16538
  async run() {
16478
- const caller = await resolveCaller();
16479
- const item = await caller.query(getFeedback, { id: this.feedbackId });
16539
+ const { item, comments } = await humanOnlyBoard(async (caller) => ({
16540
+ item: await caller.query(getFeedback, { id: this.feedbackId }),
16541
+ comments: await caller.query(listComments, { id: this.feedbackId })
16542
+ }));
16480
16543
  if (item === null) throw new Error(`No feedback item with id ${this.feedbackId}.`);
16481
- const comments = await caller.query(listComments, { id: this.feedbackId });
16482
16544
  this.emitText({ data: { item, comments } }, () => {
16483
16545
  const lines = [
16484
- `${item.id} [${item.category}] ${item.status}`,
16546
+ `${item.id} ${item.status}`,
16485
16547
  item.summary,
16486
16548
  "",
16487
16549
  `Submitted by: ${submitter(item)} ${formatDatetime(item.createdAt)}`
@@ -16525,8 +16587,7 @@ var FeedbackCommentCommand = class extends ProfileCommand {
16525
16587
  body = options_exports.String({ required: true, name: "body" });
16526
16588
  async run() {
16527
16589
  if (this.body.trim().length === 0) throw new Error("feedback comment: body must not be empty.");
16528
- const caller = await resolveCaller();
16529
- const comment = await caller.mutation(addComment, { id: this.feedbackId, body: this.body });
16590
+ const comment = await humanOnlyBoard((caller) => caller.mutation(addComment, { id: this.feedbackId, body: this.body }));
16530
16591
  this.emitText(
16531
16592
  { data: comment },
16532
16593
  () => `Added comment ${comment.id} by ${comment.authorName || "(unknown)"}.`
@@ -16543,8 +16604,7 @@ var FeedbackStatusCommand = class extends ProfileCommand {
16543
16604
  status = options_exports.String({ required: true, name: "status" });
16544
16605
  async run() {
16545
16606
  const status = parseStatus(this.status);
16546
- const caller = await resolveCaller();
16547
- await caller.mutation(updateStatus, { id: this.feedbackId, status });
16607
+ await humanOnlyBoard((caller) => caller.mutation(updateStatus, { id: this.feedbackId, status }));
16548
16608
  this.emitText(
16549
16609
  { data: { feedbackId: this.feedbackId, status } },
16550
16610
  () => `Moved feedback ${this.feedbackId} to '${status}'.`