@gravity-rail/cli 0.14.0 → 0.15.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/dist/lib.js CHANGED
@@ -15,6 +15,7 @@ import {
15
15
  formatPaginationWarning,
16
16
  getConciergeChat,
17
17
  getConciergeChatMessages,
18
+ hint,
18
19
  isCursorPaginatedResponse,
19
20
  isListResult,
20
21
  isPagePaginatedResponse,
@@ -31,7 +32,7 @@ import {
31
32
  startAccountPhoneVerify,
32
33
  stderr,
33
34
  unwrapItems
34
- } from "./chunk-M4XMXS5D.js";
35
+ } from "./chunk-EITDLX75.js";
35
36
  import {
36
37
  AGUIClient,
37
38
  formatTimestamp,
@@ -2857,7 +2858,15 @@ var COLUMNS6 = [
2857
2858
  COL.status(),
2858
2859
  COL.created()
2859
2860
  ];
2860
- var PRODUCT_COLUMNS = [COL.id(), COL.uuid(), COL.name(), COL.status()];
2861
+ var PRODUCT_COLUMNS = [
2862
+ COL.id(),
2863
+ COL.uuid(),
2864
+ COL.name(),
2865
+ COL.status(),
2866
+ // The plan a flow picks when it cannot ask (the signup funnel's workspace step,
2867
+ // for one). One per type per catalogue — see `gr workspaces products set-default`.
2868
+ { header: "DEFAULT", getValue: (item) => item.isDefault ? "yes" : "\u2014" }
2869
+ ];
2861
2870
  var INVITATION_COLUMNS = [
2862
2871
  COL.id(),
2863
2872
  { header: "EMAIL", getValue: (item) => item.email ?? "" },
@@ -3293,6 +3302,40 @@ Provisioning started. Watch it come up with:
3293
3302
  outputSingle(result2, ctx, PRODUCT_COLUMNS);
3294
3303
  }
3295
3304
  },
3305
+ {
3306
+ path: ["workspaces", "products", "set-default"],
3307
+ description: "Make a plan the one flows pick when they can't ask",
3308
+ mutates: true,
3309
+ requiresWorkspace: true,
3310
+ args: [
3311
+ { name: "id", description: "Product ID", required: true },
3312
+ {
3313
+ name: "unset",
3314
+ description: "Clear the flag instead, leaving the catalogue with no default",
3315
+ isFlag: true
3316
+ }
3317
+ ],
3318
+ examples: [
3319
+ "gr workspaces products set-default --id 21",
3320
+ "gr workspaces products set-default --id 21 --unset"
3321
+ ],
3322
+ handler: async ({ ctx, args, workspace }) => {
3323
+ const id = requireNumber(args, "id");
3324
+ const isDefault = !args["unset"];
3325
+ const result2 = await requireAuth(ctx).apiClient.updateProduct(workspace, id, {
3326
+ isDefault
3327
+ });
3328
+ outputSingle(result2, ctx, PRODUCT_COLUMNS);
3329
+ hint(ctx, [
3330
+ isDefault ? "Any other plan of this type in the catalogue is no longer the default." : (
3331
+ // Only this plan's flag is cleared. Claiming the catalogue now has
3332
+ // no default would be wrong when a different plan holds it — the
3333
+ // response cannot tell us whether this one was the holder.
3334
+ "This plan no longer holds the default flag. Check which plan does:\n gr workspaces products list"
3335
+ )
3336
+ ]);
3337
+ }
3338
+ },
3296
3339
  // Invitations — workspace-scoped admin methods
3297
3340
  {
3298
3341
  path: ["workspaces", "invitations", "list"],
@@ -7911,6 +7954,13 @@ var PRODUCT_COLUMNS2 = [
7911
7954
  const days = p.trialPeriodDays ?? 0;
7912
7955
  return days > 0 ? `${days}d` : "\u2014";
7913
7956
  }
7957
+ },
7958
+ // Which plan a flow picks when it cannot ask — the signup funnel's workspace
7959
+ // step, for one. Invisible without this: the flag decides what customers get
7960
+ // started on, so reading the catalogue should show it.
7961
+ {
7962
+ header: "DEFAULT",
7963
+ getValue: (p) => p.isDefault ? "yes" : "\u2014"
7914
7964
  }
7915
7965
  ];
7916
7966
  var SUBSCRIPTION_COLUMNS = [
@@ -7935,10 +7985,6 @@ var CONTRACT_BEARING_STATUSES = /* @__PURE__ */ new Set([
7935
7985
  function isTable(ctx) {
7936
7986
  return (ctx.outputFormat ?? "table") === "table";
7937
7987
  }
7938
- function hint(ctx, lines) {
7939
- if (!isTable(ctx)) return;
7940
- process.stdout.write("\n" + lines.join("\n") + "\n");
7941
- }
7942
7988
  function productType(args) {
7943
7989
  const t = args.type ?? args["product-type"];
7944
7990
  if (t === "org" || t === "workspace") return t;
@@ -9801,7 +9847,41 @@ function simulateInboundRouting(phone, opts = {}) {
9801
9847
  steps.push({ step: "inbound_call_handling", outcome: "play_message" });
9802
9848
  return finish("play_message_hangup", false, false, steps, recommendations, callerRole);
9803
9849
  }
9804
- steps.push({ step: "inbound_call_handling", outcome: "ai_path" });
9850
+ if (inboundCallHandling === "ring_operators") {
9851
+ const operatorGroupId = phone.operatorGroupId;
9852
+ const ringWorkflowId = phone.defaultWorkflowId;
9853
+ if (!operatorGroupId || ringWorkflowId === null || ringWorkflowId === void 0) {
9854
+ steps.push({
9855
+ step: "inbound_call_handling",
9856
+ outcome: "ai_path",
9857
+ detail: "ring_operators skipped \u2014 needs BOTH operatorGroupId and defaultWorkflowId"
9858
+ });
9859
+ recommendations.push(
9860
+ "ring_operators is configured but inert: set operatorGroupId (gr operator-groups list -w $WID) and defaultWorkflowId, or the line silently answers with AI"
9861
+ );
9862
+ } else {
9863
+ const noAnswer = phone.operatorNoAnswerAction ?? "voicemail";
9864
+ const timeout = phone.operatorRingTimeoutSeconds ?? 30;
9865
+ steps.push({
9866
+ step: "inbound_call_handling",
9867
+ outcome: "ring_operators",
9868
+ detail: `group=${operatorGroupId} timeout=${timeout}s no_answer=${noAnswer}`
9869
+ });
9870
+ steps.push({
9871
+ step: "operator_presence",
9872
+ outcome: "runtime_dependent",
9873
+ detail: `live operators are resolved at call time \u2014 not simulatable. If none are live, or none answer within ${timeout}s, the call falls back to: ${noAnswer}`
9874
+ });
9875
+ if (noAnswer === "ai") {
9876
+ recommendations.push(
9877
+ "operatorNoAnswerAction=ai \u2014 an unanswered ring lands on the AI assistant, so callers may reach the AI rather than voicemail"
9878
+ );
9879
+ }
9880
+ return finish("ring_operators", true, true, steps, recommendations, callerRole);
9881
+ }
9882
+ } else {
9883
+ steps.push({ step: "inbound_call_handling", outcome: "ai_path" });
9884
+ }
9805
9885
  const workflowId = phone.defaultWorkflowId;
9806
9886
  if (workflowId === null || workflowId === void 0) {
9807
9887
  steps.push({
@@ -9859,7 +9939,8 @@ function finish(code, createsChat, createsPhoneCall, steps, recommendations, cal
9859
9939
  fax_receive: "Create fax chat + receive document",
9860
9940
  no_inbound_workflow: "Play unavailable message (no inbound workflow)",
9861
9941
  workflow_member_role_denied: "Reject \u2014 caller member role lacks workflow USE (memberRoleAccess)",
9862
- ai_voice_chat: "Create chat + connect AI voice"
9942
+ ai_voice_chat: "Create chat + connect AI voice",
9943
+ ring_operators: "Ring live operators (falls back if none answer)"
9863
9944
  };
9864
9945
  return {
9865
9946
  callerRole: callerRole ?? "unknown",
@@ -19390,7 +19471,7 @@ var topLevelCommands = [
19390
19471
  mutates: true,
19391
19472
  args: [{ name: "phone", description: "E.164 phone number (optional; prompts if omitted)" }],
19392
19473
  handler: async (hctx) => {
19393
- const { accountVerifyPhoneCommand } = await import("./account-verify-phone-UA5OUOOR.js");
19474
+ const { accountVerifyPhoneCommand } = await import("./account-verify-phone-Q2NIRSX4.js");
19394
19475
  await accountVerifyPhoneCommand(hctx);
19395
19476
  }
19396
19477
  },