@commonlyai/cli 0.1.220 → 0.1.221

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@commonlyai/cli",
3
- "version": "0.1.220",
3
+ "version": "0.1.221",
4
4
  "license": "Apache-2.0",
5
5
  "description": "The Commonly CLI — connect agents, manage pods, iterate fast",
6
6
  "type": "module",
@@ -188,22 +188,58 @@ export const runInboxAck = async (itemId, opts = {}, deps = {}) => {
188
188
  }
189
189
  };
190
190
 
191
+ // `value` is either a 1-based option index (position in the decision's options,
192
+ // shown on refusal) or an option label. The server stores whatever string it is sent, so
193
+ // the label is resolved here against the open queue and only the label is posted:
194
+ // a bare "2" recorded in the ruling would mean nothing to anyone reading it later.
195
+ export const resolveDecisionChoice = (options, value) => {
196
+ const choices = (Array.isArray(options) ? options : [])
197
+ .map((option) => String(option?.label ?? '').trim())
198
+ .filter(Boolean);
199
+ const listing = choices.map((label, i) => ` ${i + 1}. ${label}`).join('\n');
200
+ const raw = String(value).trim();
201
+ if (/^\d+$/.test(raw)) {
202
+ const index = Number(raw);
203
+ const labelled = choices.findIndex((label) => label === raw);
204
+ // A digit-only label is also a position in the list. When the two point at
205
+ // different options, neither reading is safe to guess, so refuse.
206
+ if (labelled !== -1 && labelled !== index - 1) {
207
+ throw new InboxRefusal(`"${raw}" is both option ${labelled + 1}'s label and position ${raw}; refusing to guess. Choose by position (1-${choices.length}) or by a label that is not a number:\n${listing}`);
208
+ }
209
+ if (index < 1 || index > choices.length) {
210
+ throw new InboxRefusal(`option ${raw} is out of range; choose 1-${choices.length}:\n${listing}`);
211
+ }
212
+ return choices[index - 1];
213
+ }
214
+ const match = choices.find((label) => label.toLocaleLowerCase() === raw.toLocaleLowerCase());
215
+ if (!match) throw new InboxRefusal(`"${raw}" is not an option; the options are:\n${listing}`);
216
+ return match;
217
+ };
218
+
191
219
  export const runInboxChoose = async (decisionId, value, opts = {}, deps = {}) => {
192
220
  const {
193
221
  env = process.env, fetchImpl = fetch, log = console.log, error = console.error, sleep = undefined,
194
222
  } = deps;
195
223
  try {
196
- if (!decisionId) throw new InboxRefusal('a decision id is required: commonly inbox choose <decisionId> <value>');
197
- if (value === undefined) throw new InboxRefusal('a value is required: commonly inbox choose <decisionId> <value>');
224
+ if (!decisionId) throw new InboxRefusal('a decision id is required: commonly inbox choose <decisionId> <n|label>');
225
+ if (value === undefined) throw new InboxRefusal('a value is required: commonly inbox choose <decisionId> <n|label>');
198
226
  const {
199
227
  baseUrl, request, account,
200
228
  } = await connect(opts, { env, fetchImpl, sleep });
201
229
  log(formatAccountLine({ account, baseUrl }));
230
+ // The options live on the open queue, not on the decision route, so the
231
+ // choice is resolved against the same queue this account would see in `list`.
232
+ const { items } = await fetchQueue(request);
233
+ const open = items.find((item) => String(item?.id) === String(decisionId) && item?.kind === 'decision');
234
+ if (!open) {
235
+ throw new InboxRefusal(`decision ${decisionId} is not open on this account's queue; nothing to choose`);
236
+ }
237
+ const label = resolveDecisionChoice(open.options, value);
202
238
  const result = await request(`/api/activity/decisions/${encodeURIComponent(decisionId)}/choose`, {
203
239
  method: 'POST',
204
- body: { value },
240
+ body: { value: label },
205
241
  });
206
- log(`chose ${decisionId} = ${value}`);
242
+ log(`chose ${decisionId} = ${label}`);
207
243
  log(`-> ${JSON.stringify(result)}`);
208
244
  return 0;
209
245
  } catch (err) {
@@ -237,7 +273,8 @@ Examples:
237
273
  $ commonly inbox list --cursor-file ~/.commonly/inbox/connector-ops.cursor # only what is new
238
274
  $ commonly inbox list --cursor-file ~/.commonly/inbox/c.cursor --window 300 # widen the re-read to 5m
239
275
  $ commonly inbox ack <attentionItemId>
240
- $ commonly inbox choose <decisionId> 2
276
+ $ commonly inbox choose <decisionId> 2 # second option of the card, by position
277
+ $ commonly inbox choose <decisionId> "Ship it" # or by its label
241
278
 
242
279
  Cost: with no cursor, \`list\` reads the WHOLE queue, one request per 50 items —
243
280
  550 open items is 11 requests. /api/activity is limited to 60 a minute **keyed on
@@ -285,7 +322,7 @@ their attention as runtime events, not from this queue.
285
322
 
286
323
  inbox
287
324
  .command('choose <decisionId> <value>')
288
- .description('Rule a decision card (the source= id from `list`)')
325
+ .description('Rule a decision card (the source= id from `list`). <value> is an option number (1-based) or its label')
289
326
  .option('--token-file <path>', INBOX_TOKEN_HELP)
290
327
  .option('--instance <url>', 'Target Commonly instance')
291
328
  .action(async (decisionId, value, opts) => { process.exit(await runInboxChoose(decisionId, value, opts)); });