@wayai/cli 0.3.92 → 0.3.94

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/index.js CHANGED
@@ -561,6 +561,15 @@ var init_api_client = __esm({
561
561
  const qs = new URLSearchParams({ hub_id: hubId });
562
562
  return this.request("GET", `/api/setup/connections?${qs.toString()}`);
563
563
  }
564
+ /**
565
+ * Active operational alerts (Status & Notices) for a hub — the same runtime
566
+ * connection/credential-health surface the web app shows. Requires `hub:read`.
567
+ * The read surface never carries a provider error body (only provider +
568
+ * status_code + category + counts), so it's safe to print verbatim.
569
+ */
570
+ async getHubAlerts(hubId) {
571
+ return this.request("GET", `/api/hubs/${encodeURIComponent(hubId)}/alerts`);
572
+ }
564
573
  /** Set a credential on a PREVIEW connection (direct secret or org-credential link). */
565
574
  async setPreviewConnectionCredential(connectionId, body) {
566
575
  return this.request("PATCH", `/api/setup/connections/${connectionId}`, body);
@@ -5475,10 +5484,11 @@ var init_dist = __esm({
5475
5484
  };
5476
5485
  LLM_PROVIDER_HTTP_BRAND = /* @__PURE__ */ Symbol.for("wayai.LlmProviderHttpError");
5477
5486
  LlmProviderHttpError = class extends Error {
5478
- constructor(message, status, provider) {
5487
+ constructor(message, status, provider, providerError) {
5479
5488
  super(message);
5480
5489
  this.status = status;
5481
5490
  this.provider = provider;
5491
+ this.providerError = providerError;
5482
5492
  this.name = "LlmProviderHttpError";
5483
5493
  }
5484
5494
  [LLM_PROVIDER_HTTP_BRAND] = true;
@@ -7906,6 +7916,10 @@ var init_dist = __esm({
7906
7916
  category: external_exports.string(),
7907
7917
  provider: external_exports.string().nullable(),
7908
7918
  status_code: external_exports.number().nullable(),
7919
+ // Credential-safe structured reason (type/code/param, e.g. "invalid_request_error ·
7920
+ // response_format") — NEVER the provider error body. Null when the producer had no
7921
+ // structured detail. gh #3164.
7922
+ detail: external_exports.string().nullable(),
7909
7923
  severity: hubAlertSeverity,
7910
7924
  status: external_exports.enum(["active", "resolved"]),
7911
7925
  count: external_exports.number(),
@@ -8780,7 +8794,8 @@ var init_utils = __esm({
8780
8794
  "eval",
8781
8795
  "admin",
8782
8796
  "report",
8783
- "conversations"
8797
+ "conversations",
8798
+ "alerts"
8784
8799
  ]);
8785
8800
  }
8786
8801
  });
@@ -14797,6 +14812,10 @@ var init_contracts = __esm({
14797
14812
  category: external_exports.string(),
14798
14813
  provider: external_exports.string().nullable(),
14799
14814
  status_code: external_exports.number().nullable(),
14815
+ // Credential-safe structured reason (type/code/param, e.g. "invalid_request_error ·
14816
+ // response_format") — NEVER the provider error body. Null when the producer had no
14817
+ // structured detail. gh #3164.
14818
+ detail: external_exports.string().nullable(),
14800
14819
  severity: hubAlertSeverity2,
14801
14820
  status: external_exports.enum(["active", "resolved"]),
14802
14821
  count: external_exports.number(),
@@ -15998,6 +16017,191 @@ var init_conversations = __esm({
15998
16017
  }
15999
16018
  });
16000
16019
 
16020
+ // src/lib/hub-alerts.ts
16021
+ function severityGlyph(severity) {
16022
+ switch (severity) {
16023
+ case "critical":
16024
+ return "\u2716";
16025
+ case "warn":
16026
+ return "\u26A0";
16027
+ default:
16028
+ return "\u2139";
16029
+ }
16030
+ }
16031
+ function formatProviderBits(alert) {
16032
+ return [alert.provider, alert.status_code != null ? `${alert.status_code}` : null].filter(Boolean).join(" ");
16033
+ }
16034
+ function formatAlertTarget(alert, resolveLabel) {
16035
+ if (alert.connection_id) {
16036
+ return resolveLabel?.(alert.connection_id) ?? `connection ${alert.connection_id}`;
16037
+ }
16038
+ if (alert.channel_id) return `channel ${alert.channel_id}`;
16039
+ return "\u2014";
16040
+ }
16041
+ function describeCategory(category) {
16042
+ switch (category) {
16043
+ case "connection_auth":
16044
+ return {
16045
+ label: "Credential rejected",
16046
+ hint: "The provider rejected this connection's credential (auth). Update the API key/credential on the connection."
16047
+ };
16048
+ case "connection_quota":
16049
+ return {
16050
+ label: "Quota / rate limit",
16051
+ hint: "The provider returned a quota or rate-limit error. Check the provider account's usage/billing."
16052
+ };
16053
+ // gh #3164 — a 400 is a request/config rejection, NOT a bad credential. Labeling
16054
+ // it "Credential rejected" points admins at rotating a healthy key.
16055
+ case "connection_config":
16056
+ return {
16057
+ label: "Request config rejected",
16058
+ hint: "The provider rejected the request configuration (400). Check the connection's model or settings (e.g. an unsupported response_format) \u2014 the credential is fine."
16059
+ };
16060
+ case "channel_token":
16061
+ return {
16062
+ label: "Channel token invalid",
16063
+ hint: "The channel token is expired or revoked. Reconnect the channel to refresh it."
16064
+ };
16065
+ case "tool_auth":
16066
+ return {
16067
+ label: "Tool auth failed",
16068
+ hint: "An external tool/MCP server rejected auth. Re-authorize the tool connection."
16069
+ };
16070
+ case "production_credential_missing":
16071
+ return {
16072
+ label: "Production credential missing",
16073
+ hint: "A production connection has no credential. Set it with `wayai set-connection-credential`."
16074
+ };
16075
+ default:
16076
+ if (category.startsWith("provider:")) {
16077
+ return {
16078
+ label: "Provider degraded",
16079
+ hint: "An upstream provider is degraded (platform-level). Usually clears on its own once the provider recovers."
16080
+ };
16081
+ }
16082
+ return { label: category, hint: "Tenant-actionable connection/credential alert \u2014 inspect the connection in the hub UI." };
16083
+ }
16084
+ }
16085
+ function activeAlerts(alerts) {
16086
+ return alerts.filter((a) => a.status === "active");
16087
+ }
16088
+ function summarizeActiveAlertsForReport(alerts) {
16089
+ const active = activeAlerts(alerts);
16090
+ if (active.length === 0) return null;
16091
+ const lines = active.map((a) => {
16092
+ const { label } = describeCategory(a.category);
16093
+ const target = formatAlertTarget(a);
16094
+ const providerBits = formatProviderBits(a);
16095
+ const detailBit = a.detail ? ` \xB7 ${a.detail}` : "";
16096
+ return `- [${a.severity}] ${a.category} (${label}) \xB7 ${target}${providerBits ? ` \xB7 ${providerBits}` : ""}${detailBit} \xB7 seen ${a.count}\xD7`;
16097
+ });
16098
+ return `Active hub alerts at report time (${active.length}):
16099
+ ${lines.join("\n")}`;
16100
+ }
16101
+ var init_hub_alerts = __esm({
16102
+ "src/lib/hub-alerts.ts"() {
16103
+ "use strict";
16104
+ }
16105
+ });
16106
+
16107
+ // src/commands/alerts.ts
16108
+ var alerts_exports = {};
16109
+ __export(alerts_exports, {
16110
+ alertsCommand: () => alertsCommand
16111
+ });
16112
+ function printAlertsHelp() {
16113
+ console.error(`
16114
+ wayai alerts \u2014 show a hub's active operational alerts (Status & Notices)
16115
+
16116
+ Usage:
16117
+ wayai alerts Active alerts for the active hub
16118
+ wayai alerts --hub <uuid|name> For a specific hub (accepts a raw uuid without a workspace)
16119
+ wayai alerts --json Raw JSON output
16120
+
16121
+ Alerts are tenant-actionable connection/credential health signals raised at
16122
+ runtime \u2014 e.g. a provider rejecting a bad/expired key (\`connection_auth\`),
16123
+ a rate/quota limit (\`connection_quota\`), an expired channel token
16124
+ (\`channel_token\`), or a missing production credential. Run this FIRST when a
16125
+ hub misbehaves (audio/TTS not delivered, agent not replying, a tool failing)
16126
+ before reading code or filing a report.
16127
+ `.trim());
16128
+ }
16129
+ function fmtTime(iso) {
16130
+ const d = new Date(iso);
16131
+ return Number.isNaN(d.getTime()) ? iso : d.toLocaleString();
16132
+ }
16133
+ async function alertsCommand(args2) {
16134
+ if (wantsHelp(args2)) {
16135
+ printAlertsHelp();
16136
+ return;
16137
+ }
16138
+ const jsonIdx = args2.indexOf("--json");
16139
+ const jsonOutput = jsonIdx !== -1;
16140
+ if (jsonOutput) args2.splice(jsonIdx, 1);
16141
+ const hubId = resolveActiveHubId(args2);
16142
+ const { config, accessToken } = await requireAuth();
16143
+ const client = new ApiClient({ apiUrl: config.api_url, accessToken });
16144
+ let alerts;
16145
+ try {
16146
+ ({ alerts } = await client.getHubAlerts(hubId));
16147
+ } catch (err) {
16148
+ if (err instanceof ApiError && err.status === 404) {
16149
+ console.error(`Hub ${hubId} not found, or you lack read access to it.`);
16150
+ process.exit(1);
16151
+ }
16152
+ if (err instanceof ApiError && err.status === 403) {
16153
+ console.error(`You don't have read access to hub ${hubId}.`);
16154
+ process.exit(1);
16155
+ }
16156
+ throw err;
16157
+ }
16158
+ const active = activeAlerts(alerts);
16159
+ if (jsonOutput) {
16160
+ console.log(JSON.stringify({ hub_id: hubId, alerts: active }, null, 2));
16161
+ return;
16162
+ }
16163
+ if (active.length === 0) {
16164
+ console.log(`Hub ${hubId} \u2014 no active alerts \u2713`);
16165
+ return;
16166
+ }
16167
+ const connLabels = /* @__PURE__ */ new Map();
16168
+ try {
16169
+ const { connections } = await client.listConnections(hubId);
16170
+ for (const conn of connections) {
16171
+ if (!conn.connection_id) continue;
16172
+ const name = conn.connection_display_name || conn.connector_name;
16173
+ connLabels.set(conn.connection_id, `${name} (${conn.connector_type})`);
16174
+ }
16175
+ } catch {
16176
+ }
16177
+ const noun = active.length === 1 ? "alert" : "alerts";
16178
+ console.log(`Hub ${hubId} \u2014 ${active.length} active ${noun}
16179
+ `);
16180
+ for (const a of active) {
16181
+ const { label, hint } = describeCategory(a.category);
16182
+ const target = formatAlertTarget(a, (id) => connLabels.get(id));
16183
+ const providerBits = formatProviderBits(a);
16184
+ console.log(`${severityGlyph(a.severity)} ${a.severity} ${a.category} \u2014 ${label}`);
16185
+ console.log(` ${target}${providerBits ? ` \xB7 ${providerBits}` : ""}${a.detail ? ` \xB7 ${a.detail}` : ""}`);
16186
+ console.log(` ${hint}`);
16187
+ if (a.org_credential_id) {
16188
+ console.log(` Shared org credential \u2014 fix it once at the org credential (\`wayai update-credential\`); it fans out to every hub linking it.`);
16189
+ }
16190
+ console.log(` seen ${a.count}\xD7 \xB7 first ${fmtTime(a.first_seen_at)} \xB7 last ${fmtTime(a.last_seen_at)}`);
16191
+ console.log("");
16192
+ }
16193
+ }
16194
+ var init_alerts = __esm({
16195
+ "src/commands/alerts.ts"() {
16196
+ "use strict";
16197
+ init_auth();
16198
+ init_workspace();
16199
+ init_api_client();
16200
+ init_utils();
16201
+ init_hub_alerts();
16202
+ }
16203
+ });
16204
+
16001
16205
  // src/commands/delete-history.ts
16002
16206
  var delete_history_exports = {};
16003
16207
  __export(delete_history_exports, {
@@ -20445,6 +20649,19 @@ async function reportCreateCommand(args2, cliVersion = null) {
20445
20649
  const client = new ApiClient({ apiUrl: config.api_url, accessToken });
20446
20650
  const repoConfig = readRepoConfig();
20447
20651
  const organizationId = repoConfig?.organization_id;
20652
+ let contextWithAlerts = context;
20653
+ if (hubId) {
20654
+ try {
20655
+ const { alerts } = await client.getHubAlerts(hubId);
20656
+ const alertSummary = summarizeActiveAlertsForReport(alerts);
20657
+ if (alertSummary) {
20658
+ contextWithAlerts = context ? `${context}
20659
+
20660
+ ${alertSummary}` : alertSummary;
20661
+ }
20662
+ } catch {
20663
+ }
20664
+ }
20448
20665
  try {
20449
20666
  const result = await client.submitReport({
20450
20667
  title,
@@ -20457,7 +20674,7 @@ async function reportCreateCommand(args2, cliVersion = null) {
20457
20674
  conversation_id: conversationId,
20458
20675
  error_message: errorMessage,
20459
20676
  steps_to_reproduce: steps,
20460
- additional_context: context,
20677
+ additional_context: contextWithAlerts,
20461
20678
  client_version: clientVersion
20462
20679
  });
20463
20680
  if (result.created) {
@@ -20488,6 +20705,7 @@ var init_report_create = __esm({
20488
20705
  init_auth();
20489
20706
  init_repo_config();
20490
20707
  init_api_client();
20708
+ init_hub_alerts();
20491
20709
  init_version_status();
20492
20710
  }
20493
20711
  });
@@ -21054,6 +21272,11 @@ async function main() {
21054
21272
  await conversationsCommand2(args);
21055
21273
  break;
21056
21274
  }
21275
+ case "alerts": {
21276
+ const { alertsCommand: alertsCommand2 } = await Promise.resolve().then(() => (init_alerts(), alerts_exports));
21277
+ await alertsCommand2(args);
21278
+ break;
21279
+ }
21057
21280
  case "delete-history": {
21058
21281
  const { deleteHistoryCommand: deleteHistoryCommand2 } = await Promise.resolve().then(() => (init_delete_history(), delete_history_exports));
21059
21282
  await deleteHistoryCommand2(args);
@@ -21187,6 +21410,7 @@ Commands:
21187
21410
  unbind Clear the worktree hub binding
21188
21411
  send-message Send a test message to a hub (preview or production)
21189
21412
  conversations List or inspect conversations
21413
+ alerts Show a hub's active connection/credential alerts (run this FIRST when a hub misbehaves)
21190
21414
  analytics Show analytics summary and metrics (use \`analytics query\` for structured ClickHouse queries)
21191
21415
  delete-history Delete your conversations on a hub (--conversation-id <id> for just one)
21192
21416
  evals List evaluation scenarios for the current hub (use \`evals sql\` for read-only SQL on eval results)