@sendbird/cli 0.0.0-stage → 0.4.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/.claude-plugin/plugin.json +11 -0
- package/.codex-plugin/plugin.json +39 -0
- package/LICENSE +13 -0
- package/NOTICE.md +1105 -0
- package/PROVENANCE.md +15 -0
- package/README.md +44 -2
- package/dist/acl.d.ts +2 -0
- package/dist/acl.js +12 -0
- package/dist/acl.js.map +1 -0
- package/dist/billing-plan.d.ts +68 -0
- package/dist/billing-plan.js +235 -0
- package/dist/billing-plan.js.map +1 -0
- package/dist/browser.d.ts +1 -0
- package/dist/browser.js +20 -0
- package/dist/browser.js.map +1 -0
- package/dist/calls-catalog.d.ts +79 -0
- package/dist/calls-catalog.js +322 -0
- package/dist/calls-catalog.js.map +1 -0
- package/dist/calls-credits.d.ts +31 -0
- package/dist/calls-credits.js +224 -0
- package/dist/calls-credits.js.map +1 -0
- package/dist/calls-trial.d.ts +45 -0
- package/dist/calls-trial.js +77 -0
- package/dist/calls-trial.js.map +1 -0
- package/dist/calls.d.ts +10 -0
- package/dist/calls.js +470 -0
- package/dist/calls.js.map +1 -0
- package/dist/catalog.d.ts +67 -0
- package/dist/catalog.js +784 -0
- package/dist/catalog.js.map +1 -0
- package/dist/cli.d.ts +6 -0
- package/dist/cli.js +502 -0
- package/dist/cli.js.map +1 -0
- package/dist/core.d.ts +86 -0
- package/dist/core.js +531 -0
- package/dist/core.js.map +1 -0
- package/dist/diagnostics.d.ts +206 -0
- package/dist/diagnostics.js +664 -0
- package/dist/diagnostics.js.map +1 -0
- package/dist/discovery.d.ts +703 -0
- package/dist/discovery.js +846 -0
- package/dist/discovery.js.map +1 -0
- package/dist/docs.d.ts +31 -0
- package/dist/docs.js +139 -0
- package/dist/docs.js.map +1 -0
- package/dist/features.d.ts +131 -0
- package/dist/features.js +227 -0
- package/dist/features.js.map +1 -0
- package/dist/generated-api.d.ts +116 -0
- package/dist/generated-api.js +118 -0
- package/dist/generated-api.js.map +1 -0
- package/dist/human-format.d.ts +40 -0
- package/dist/human-format.js +205 -0
- package/dist/human-format.js.map +1 -0
- package/dist/human-list.d.ts +2 -0
- package/dist/human-list.js +368 -0
- package/dist/human-list.js.map +1 -0
- package/dist/human-output.d.ts +10 -0
- package/dist/human-output.js +1351 -0
- package/dist/human-output.js.map +1 -0
- package/dist/human-pro.d.ts +3 -0
- package/dist/human-pro.js +201 -0
- package/dist/human-pro.js.map +1 -0
- package/dist/human-progress.d.ts +5 -0
- package/dist/human-progress.js +49 -0
- package/dist/human-progress.js.map +1 -0
- package/dist/local.d.ts +42 -0
- package/dist/local.js +707 -0
- package/dist/local.js.map +1 -0
- package/dist/oauth-callback.d.ts +3 -0
- package/dist/oauth-callback.js +66 -0
- package/dist/oauth-callback.js.map +1 -0
- package/dist/oauth-lock.d.ts +6 -0
- package/dist/oauth-lock.js +212 -0
- package/dist/oauth-lock.js.map +1 -0
- package/dist/oauth.d.ts +32 -0
- package/dist/oauth.js +493 -0
- package/dist/oauth.js.map +1 -0
- package/dist/presentation.d.ts +208 -0
- package/dist/presentation.js +156 -0
- package/dist/presentation.js.map +1 -0
- package/dist/pro-api-catalog.d.ts +24 -0
- package/dist/pro-api-catalog.js +619 -0
- package/dist/pro-api-catalog.js.map +1 -0
- package/dist/pro-api.d.ts +10 -0
- package/dist/pro-api.js +338 -0
- package/dist/pro-api.js.map +1 -0
- package/dist/project.d.ts +34 -0
- package/dist/project.js +62 -0
- package/dist/project.js.map +1 -0
- package/dist/recovery.d.ts +41 -0
- package/dist/recovery.js +417 -0
- package/dist/recovery.js.map +1 -0
- package/dist/runner.d.ts +47 -0
- package/dist/runner.js +603 -0
- package/dist/runner.js.map +1 -0
- package/dist/sdk-catalog.d.ts +69 -0
- package/dist/sdk-catalog.js +527 -0
- package/dist/sdk-catalog.js.map +1 -0
- package/dist/sdk-config.d.ts +19 -0
- package/dist/sdk-config.js +85 -0
- package/dist/sdk-config.js.map +1 -0
- package/dist/sdk-dto.d.ts +244 -0
- package/dist/sdk-dto.js +98 -0
- package/dist/sdk-dto.js.map +1 -0
- package/dist/sdk-limits.d.ts +2 -0
- package/dist/sdk-limits.js +3 -0
- package/dist/sdk-limits.js.map +1 -0
- package/dist/sdk-node-file-reader.d.ts +6 -0
- package/dist/sdk-node-file-reader.js +38 -0
- package/dist/sdk-node-file-reader.js.map +1 -0
- package/dist/sdk-pro-catalog.d.ts +22 -0
- package/dist/sdk-pro-catalog.js +234 -0
- package/dist/sdk-pro-catalog.js.map +1 -0
- package/dist/sdk-pro-runtime.d.ts +31 -0
- package/dist/sdk-pro-runtime.js +293 -0
- package/dist/sdk-pro-runtime.js.map +1 -0
- package/dist/sdk-query.d.ts +10 -0
- package/dist/sdk-query.js +29 -0
- package/dist/sdk-query.js.map +1 -0
- package/dist/sdk-runtime.d.ts +15 -0
- package/dist/sdk-runtime.js +685 -0
- package/dist/sdk-runtime.js.map +1 -0
- package/dist/sdk-worker.d.ts +1 -0
- package/dist/sdk-worker.js +26 -0
- package/dist/sdk-worker.js.map +1 -0
- package/dist/sdk.d.ts +29 -0
- package/dist/sdk.js +346 -0
- package/dist/sdk.js.map +1 -0
- package/dist/search-engine.d.ts +17 -0
- package/dist/search-engine.js +54 -0
- package/dist/search-engine.js.map +1 -0
- package/dist/search-intents.d.ts +2 -0
- package/dist/search-intents.js +106 -0
- package/dist/search-intents.js.map +1 -0
- package/dist/search.d.ts +7 -0
- package/dist/search.js +58 -0
- package/dist/search.js.map +1 -0
- package/dist/secret-output.d.ts +2 -0
- package/dist/secret-output.js +108 -0
- package/dist/secret-output.js.map +1 -0
- package/dist/shell.d.ts +3 -0
- package/dist/shell.js +14 -0
- package/dist/shell.js.map +1 -0
- package/dist/skills.d.ts +210 -0
- package/dist/skills.js +374 -0
- package/dist/skills.js.map +1 -0
- package/dist/value-semantics.d.ts +70 -0
- package/dist/value-semantics.js +173 -0
- package/dist/value-semantics.js.map +1 -0
- package/dist/version.d.ts +1 -0
- package/dist/version.js +3 -0
- package/dist/version.js.map +1 -0
- package/dist/windows.d.ts +4 -0
- package/dist/windows.js +136 -0
- package/dist/windows.js.map +1 -0
- package/dist/workflows.d.ts +228 -0
- package/dist/workflows.js +1207 -0
- package/dist/workflows.js.map +1 -0
- package/package.json +90 -4
- package/plugin.json +25 -0
- package/references/agent-guide.json +199 -0
- package/references/commands.md +247 -0
- package/references/dashboard-inventory.json +14796 -0
- package/references/docs-index.json +5329 -0
- package/references/sdk-commands.json +15065 -0
- package/references/workflows.json +1246 -0
- package/skills/sendbird/SKILL.md +96 -0
- package/skills/sendbird/agents/openai.yaml +4 -0
- package/skills/sendbird/references/build-chat-app.md +50 -0
- package/skills/sendbird/references/client-guide.md +34 -0
- package/skills/sendbird/references/client-plugin-bridge.md +77 -0
- package/skills/sendbird/references/client-plugin-install.md +39 -0
- package/skills/sendbird/references/operational-checks.md +35 -0
- package/skills/sendbird/references/pro-capabilities.md +52 -0
- package/skills/sendbird/scripts/client-guide.mjs +283 -0
- package/skills/sendbird/scripts/client-plugin-bridge.mjs +703 -0
- package/skills/sendbird-setup/SKILL.md +18 -0
- package/skills/sendbird-setup/agents/openai.yaml +4 -0
- package/skills/sendbird-setup/references/onboarding.md +73 -0
- package/skills/sendbird-setup/scripts/install-cli.mjs +447 -0
- package/skills/sendbird-setup/scripts/npm-run.mjs +34 -0
|
@@ -0,0 +1,1351 @@
|
|
|
1
|
+
import { humanValueSemantics } from "./value-semantics.js";
|
|
2
|
+
import { proOutput } from "./human-pro.js";
|
|
3
|
+
import { redact, secretValues } from "./core.js";
|
|
4
|
+
import { clean, paragraph, clip, commandLine, countLabel, fieldLabel, localTime, primary, record, row, table, titleCase, valueText, view, width, } from "./human-format.js";
|
|
5
|
+
import { listOutput } from "./human-list.js";
|
|
6
|
+
/** Shared wizard summary: all prompt details stay on stderr, including in JSON mode. */
|
|
7
|
+
export function setupContextSummary(selection, options = {}) {
|
|
8
|
+
const ctx = view({
|
|
9
|
+
...options,
|
|
10
|
+
color: false,
|
|
11
|
+
width: options.width ?? process.stderr.columns ?? process.stdout.columns,
|
|
12
|
+
});
|
|
13
|
+
return [
|
|
14
|
+
"Review setup defaults",
|
|
15
|
+
row("Profile", selection.profile, ctx),
|
|
16
|
+
row("Environment", selection.environment, ctx),
|
|
17
|
+
row("Organization UID", selection.org_id, ctx),
|
|
18
|
+
row("App ID", selection.app_id, ctx),
|
|
19
|
+
row("App region", selection.app_region ?? "Not reported", ctx),
|
|
20
|
+
row("Default region", selection.region ?? "Not selected", ctx),
|
|
21
|
+
...paragraph("Saving makes this profile active. Explicit flags and environment variables still take precedence. Sign-in and resources created above are kept even if you skip saving; reauthentication may reset the old account context.", ctx),
|
|
22
|
+
].join("\n");
|
|
23
|
+
}
|
|
24
|
+
function detailRows(command, result, target, ctx) {
|
|
25
|
+
const details = [];
|
|
26
|
+
const notes = [];
|
|
27
|
+
let heading = titleCase(entity(command));
|
|
28
|
+
const add = (label, value) => {
|
|
29
|
+
if (value !== undefined && value !== null && value !== "")
|
|
30
|
+
details.push([label, value]);
|
|
31
|
+
};
|
|
32
|
+
switch (command) {
|
|
33
|
+
case "calls settings push get":
|
|
34
|
+
heading = "Calls Push availability";
|
|
35
|
+
add("App", result.app_id ?? target.app_id);
|
|
36
|
+
add("Calls enabled", result.is_calls_enabled ?? "Unknown");
|
|
37
|
+
add("Region", result.region);
|
|
38
|
+
add("Flag source", result.calls_flag_source);
|
|
39
|
+
add("Calls API", "Verify regional access with calls settings general get");
|
|
40
|
+
add("Shared Push enabled", result.push_enabled ?? "Unknown");
|
|
41
|
+
add("Policy", "The shared switch is read-only here");
|
|
42
|
+
break;
|
|
43
|
+
case "calls settings general get":
|
|
44
|
+
heading = "Calls General settings";
|
|
45
|
+
for (const [key, label] of Object.entries({
|
|
46
|
+
relay_method: "Relay",
|
|
47
|
+
direct_call_cloud_recording_enabled: "Direct recording",
|
|
48
|
+
group_call_cloud_recording_enabled: "Group recording",
|
|
49
|
+
direct_call_recording_video: "Recording video",
|
|
50
|
+
direct_call_recording_display_timestamp: "Timestamp overlay",
|
|
51
|
+
direct_call_recording_timestamp_timezone: "Timezone",
|
|
52
|
+
}))
|
|
53
|
+
add(label, key === "direct_call_recording_timestamp_timezone" &&
|
|
54
|
+
record(result.general)?.[key] === ""
|
|
55
|
+
? "Default"
|
|
56
|
+
: key === "relay_method" && record(result.general)?.[key] === "turn"
|
|
57
|
+
? "Server relay only (turn)"
|
|
58
|
+
: key === "relay_method" &&
|
|
59
|
+
record(result.general)?.[key] === "stun&turn"
|
|
60
|
+
? "P2P first, relay fallback (stun&turn)"
|
|
61
|
+
: record(result.general)?.[key]);
|
|
62
|
+
break;
|
|
63
|
+
case "calls settings webhook get":
|
|
64
|
+
case "calls settings chat-integration get": {
|
|
65
|
+
const key = command.includes("webhook") ? "webhook" : "chat_integration";
|
|
66
|
+
const settings = record(result[key]) ?? {};
|
|
67
|
+
heading =
|
|
68
|
+
key === "webhook" ? "Calls Webhook settings" : "Calls Chat integration";
|
|
69
|
+
add("Enabled", settings.enabled);
|
|
70
|
+
add("URL", settings.url);
|
|
71
|
+
add("Enabled events", Array.isArray(settings.enabled_events)
|
|
72
|
+
? settings.enabled_events.join(", ") || "None"
|
|
73
|
+
: undefined);
|
|
74
|
+
add("Available events", Array.isArray(settings.all_webhook_categories ??
|
|
75
|
+
settings.all_chat_integration_categories)
|
|
76
|
+
? (settings.all_webhook_categories ??
|
|
77
|
+
settings.all_chat_integration_categories)
|
|
78
|
+
: undefined);
|
|
79
|
+
break;
|
|
80
|
+
}
|
|
81
|
+
case "profile":
|
|
82
|
+
heading = `Profile ${valueText(result.current)}`;
|
|
83
|
+
add("Environment", result.environment);
|
|
84
|
+
add("Active", result.active);
|
|
85
|
+
if (result.current !== result.active)
|
|
86
|
+
add("Selection", result.source);
|
|
87
|
+
add("Organization UID", result.org_id);
|
|
88
|
+
add("App", result.app_id);
|
|
89
|
+
add("Region", result.region);
|
|
90
|
+
break;
|
|
91
|
+
case "project status": {
|
|
92
|
+
const project = record(result.project);
|
|
93
|
+
heading = project
|
|
94
|
+
? "Sendbird project linked"
|
|
95
|
+
: "No Sendbird project link";
|
|
96
|
+
add("File", result.path);
|
|
97
|
+
if (project) {
|
|
98
|
+
add("App ID", project.app_id);
|
|
99
|
+
add("Organization UID", project.org_id);
|
|
100
|
+
add("Region", project.region);
|
|
101
|
+
add("Profile", project.profile);
|
|
102
|
+
}
|
|
103
|
+
if (Array.isArray(result.next) && result.next.length)
|
|
104
|
+
add("Next", result.next[0]);
|
|
105
|
+
break;
|
|
106
|
+
}
|
|
107
|
+
case "auth status": {
|
|
108
|
+
const user = record(result.user);
|
|
109
|
+
const role = record(result.role);
|
|
110
|
+
heading = `Signed in${user?.email ? ` as ${clean(user.email)}` : ""}`;
|
|
111
|
+
add("Profile", result.profile);
|
|
112
|
+
add("Environment", result.environment);
|
|
113
|
+
add("Role", role?.name);
|
|
114
|
+
const permissions = role?.permissions;
|
|
115
|
+
if (Array.isArray(permissions) &&
|
|
116
|
+
permissions.every((p) => typeof p === "string")) {
|
|
117
|
+
const unique = [...new Set(permissions)];
|
|
118
|
+
add("Permissions", unique.length ? `${unique.length} returned` : "None returned");
|
|
119
|
+
for (const permission of unique.slice(0, 12))
|
|
120
|
+
notes.push(...paragraph(`- ${permission}`, ctx));
|
|
121
|
+
if (unique.length > 12)
|
|
122
|
+
notes.push(...paragraph(`${unique.length - 12} more permissions; use auth status --json for the complete list.`, ctx));
|
|
123
|
+
}
|
|
124
|
+
else
|
|
125
|
+
add("Permissions", "Not reported");
|
|
126
|
+
notes.push(...paragraph("Role permissions reported by the server; command access also depends on the target and API policy.", ctx));
|
|
127
|
+
add("Organizations", Array.isArray(result.organizations)
|
|
128
|
+
? result.organizations.length
|
|
129
|
+
: record(result.organization)
|
|
130
|
+
? 1
|
|
131
|
+
: undefined);
|
|
132
|
+
if (Array.isArray(result.organizations) &&
|
|
133
|
+
result.organizations.length === 1)
|
|
134
|
+
add("Organization", record(result.organizations[0])?.name);
|
|
135
|
+
else if (record(result.organization))
|
|
136
|
+
add("Organization", record(result.organization)?.name);
|
|
137
|
+
break;
|
|
138
|
+
}
|
|
139
|
+
case "doctor":
|
|
140
|
+
if (Array.isArray(result.checks))
|
|
141
|
+
return diagnosticLines(result, ctx);
|
|
142
|
+
heading = "Dashboard access verified";
|
|
143
|
+
add("Profile", result.profile);
|
|
144
|
+
add("Organization UID", result.org_id ?? "Not selected");
|
|
145
|
+
add("App", result.app_id ?? "Not selected");
|
|
146
|
+
add("Role", record(result.role)?.name);
|
|
147
|
+
if (Array.isArray(result.next) && result.next.length)
|
|
148
|
+
add("Next", result.next[0]);
|
|
149
|
+
break;
|
|
150
|
+
case "app auth-policy get":
|
|
151
|
+
heading = "Chat access-token policy";
|
|
152
|
+
add("Token policy", result.guest_user_policy);
|
|
153
|
+
break;
|
|
154
|
+
case "app get": {
|
|
155
|
+
heading = `Application ${valueText(result.app_name ?? result.app_id)}`;
|
|
156
|
+
add("App ID", result.app_id ?? target.app_id);
|
|
157
|
+
add("Region", result.region);
|
|
158
|
+
add("Plan", result.plan);
|
|
159
|
+
add("Token policy", result.guest_user_policy);
|
|
160
|
+
add("SDK family", result.is_delight_region === true ? "Delight" : "Standard");
|
|
161
|
+
const features = record(result.current_premium_features);
|
|
162
|
+
for (const [label, key] of [
|
|
163
|
+
["Supergroup", "super_group_channel"],
|
|
164
|
+
["Reactions", "reactions"],
|
|
165
|
+
["Poll", "poll"],
|
|
166
|
+
]) {
|
|
167
|
+
const reported = features?.[key];
|
|
168
|
+
add(label, reported === true
|
|
169
|
+
? "Reported enabled"
|
|
170
|
+
: reported === false
|
|
171
|
+
? "Reported disabled"
|
|
172
|
+
: "Not reported");
|
|
173
|
+
}
|
|
174
|
+
notes.push(...paragraph("Feature source: current_premium_features (plan snapshot). Runtime access has not been tested; these values do not prove support or denial. Use doctor for separate plan, activation and access checks.", ctx));
|
|
175
|
+
break;
|
|
176
|
+
}
|
|
177
|
+
case "chat user get":
|
|
178
|
+
heading = `User ${valueText(result.nickname ?? result.user_id)}`;
|
|
179
|
+
add("User ID", result.user_id ?? target.user_id);
|
|
180
|
+
add("Active", result.is_active);
|
|
181
|
+
add("Online", result.is_online);
|
|
182
|
+
add("Metadata keys", record(result.metadata)
|
|
183
|
+
? Object.keys(record(result.metadata)).length
|
|
184
|
+
: undefined);
|
|
185
|
+
add("Created", typeof result.created_at === "number"
|
|
186
|
+
? localTime(result.created_at)
|
|
187
|
+
: undefined);
|
|
188
|
+
break;
|
|
189
|
+
case "chat channel get":
|
|
190
|
+
heading = `${titleCase(valueText(target.channel_type || (result.is_super ? "supergroup" : "group")))} channel ${valueText(result.name || result.channel_url)}`;
|
|
191
|
+
add("Channel URL", result.channel_url ?? target.channel_url);
|
|
192
|
+
add("Members", result.member_count);
|
|
193
|
+
add("Public", result.is_public);
|
|
194
|
+
add("Frozen", result.freeze);
|
|
195
|
+
add("Created", typeof result.created_at === "number"
|
|
196
|
+
? localTime(result.created_at)
|
|
197
|
+
: undefined);
|
|
198
|
+
break;
|
|
199
|
+
case "chat sdk status":
|
|
200
|
+
heading = `Connected as ${valueText(record(result.user)?.user_id ?? target.actor_user_id)}`;
|
|
201
|
+
add("App", result.app_id ?? target.app_id);
|
|
202
|
+
break;
|
|
203
|
+
case "chat sdk message translate": {
|
|
204
|
+
heading = `Translated message ${valueText(result.message_id ?? target.message_id)}`;
|
|
205
|
+
add("Channel", result.channel_url ?? target.channel_url);
|
|
206
|
+
add("Original", result.message);
|
|
207
|
+
for (const [language, text] of Object.entries(record(result.translations) ?? {}))
|
|
208
|
+
add(clean(language), text);
|
|
209
|
+
break;
|
|
210
|
+
}
|
|
211
|
+
case "chat message get":
|
|
212
|
+
heading = `Message ${valueText(result.message_id ?? target.message_id)}`;
|
|
213
|
+
add("Sender", record(result.sender)?.user_id);
|
|
214
|
+
add("Channel", result.channel_url ?? target.channel_url);
|
|
215
|
+
add("Message", result.message);
|
|
216
|
+
add("Mention type", result.mention_type);
|
|
217
|
+
if (Array.isArray(result.mentioned_user_ids))
|
|
218
|
+
add("Mentioned users", result.mentioned_user_ids.join(", ") || "None");
|
|
219
|
+
add("File requires SDK auth", result.requires_auth);
|
|
220
|
+
add("Created", typeof result.created_at === "number"
|
|
221
|
+
? localTime(result.created_at)
|
|
222
|
+
: undefined);
|
|
223
|
+
break;
|
|
224
|
+
default: {
|
|
225
|
+
const ignored = new Set([
|
|
226
|
+
"attrs",
|
|
227
|
+
"plans",
|
|
228
|
+
"access_token",
|
|
229
|
+
"api_token",
|
|
230
|
+
"session_tokens",
|
|
231
|
+
"profile_url",
|
|
232
|
+
"current_premium_features",
|
|
233
|
+
"current_core_features",
|
|
234
|
+
"created_by",
|
|
235
|
+
]);
|
|
236
|
+
if (command === "org get") {
|
|
237
|
+
add("Organization UID", result.uid);
|
|
238
|
+
ignored.add("uid");
|
|
239
|
+
}
|
|
240
|
+
for (const [key, value] of Object.entries(result)) {
|
|
241
|
+
if (ignored.has(key))
|
|
242
|
+
continue;
|
|
243
|
+
if (["string", "number", "boolean"].includes(typeof value))
|
|
244
|
+
add(fieldLabel(key), value);
|
|
245
|
+
if (details.length >= 8)
|
|
246
|
+
break;
|
|
247
|
+
}
|
|
248
|
+
if (!details.length && result !== null)
|
|
249
|
+
add("Result", Object.keys(result).length
|
|
250
|
+
? `${Object.keys(result).length} fields; use --json for details`
|
|
251
|
+
: "No data returned");
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
return [
|
|
255
|
+
ctx.color.bold(clip(heading, ctx.width - 2)),
|
|
256
|
+
...details.map(([label, value]) => row(label, value, ctx)),
|
|
257
|
+
...notes,
|
|
258
|
+
];
|
|
259
|
+
}
|
|
260
|
+
const verbs = {
|
|
261
|
+
init: "Linked",
|
|
262
|
+
create: "Created",
|
|
263
|
+
delete: "Deleted",
|
|
264
|
+
revoke: "Revoked",
|
|
265
|
+
issue: "Issued",
|
|
266
|
+
send: "Sent",
|
|
267
|
+
file: "Sent",
|
|
268
|
+
reply: "Sent",
|
|
269
|
+
invite: "Invited",
|
|
270
|
+
update: "Updated",
|
|
271
|
+
set: "Updated",
|
|
272
|
+
rename: "Renamed",
|
|
273
|
+
use: "Selected",
|
|
274
|
+
clear: "Cleared",
|
|
275
|
+
activate: "Activated",
|
|
276
|
+
deactivate: "Deactivated",
|
|
277
|
+
submit: "Submitted",
|
|
278
|
+
accept: "Accepted",
|
|
279
|
+
decline: "Declined",
|
|
280
|
+
join: "Joined",
|
|
281
|
+
leave: "Left",
|
|
282
|
+
login: "Signed in",
|
|
283
|
+
logout: "Signed out",
|
|
284
|
+
signup: "Signed up",
|
|
285
|
+
"verify-email": "Verified email",
|
|
286
|
+
"resend-verification": "Sent verification email",
|
|
287
|
+
"send-now": "Sent",
|
|
288
|
+
cancel: "Canceled",
|
|
289
|
+
pin: "Pinned",
|
|
290
|
+
unpin: "Unpinned",
|
|
291
|
+
freeze: "Froze",
|
|
292
|
+
unfreeze: "Unfroze",
|
|
293
|
+
mute: "Muted",
|
|
294
|
+
unmute: "Unmuted",
|
|
295
|
+
ban: "Banned",
|
|
296
|
+
unban: "Unbanned",
|
|
297
|
+
read: "Marked read",
|
|
298
|
+
};
|
|
299
|
+
export function entity(command) {
|
|
300
|
+
if (command.startsWith("calls settings "))
|
|
301
|
+
return `Calls ${command.split(" ")[2]} settings`;
|
|
302
|
+
if (command.startsWith("calls push "))
|
|
303
|
+
return "Calls push credentials";
|
|
304
|
+
if (command.startsWith("project "))
|
|
305
|
+
return "project";
|
|
306
|
+
if (command.startsWith("chat sdk message scheduled "))
|
|
307
|
+
return "scheduled message";
|
|
308
|
+
if (command.startsWith("chat sdk message "))
|
|
309
|
+
return "message";
|
|
310
|
+
if (command.startsWith("chat sdk user push "))
|
|
311
|
+
return "push notifications";
|
|
312
|
+
if (command.startsWith("chat sdk user invitation "))
|
|
313
|
+
return "invitation preference";
|
|
314
|
+
if (command.startsWith("chat sdk channel count-preference "))
|
|
315
|
+
return "unread count preference";
|
|
316
|
+
if (command.startsWith("chat sdk channel snooze "))
|
|
317
|
+
return "snooze";
|
|
318
|
+
if (command.startsWith("chat sdk channel "))
|
|
319
|
+
return "channel";
|
|
320
|
+
if (command.startsWith("chat message "))
|
|
321
|
+
return "message";
|
|
322
|
+
if (command.startsWith("chat channel metadata "))
|
|
323
|
+
return "channel metadata";
|
|
324
|
+
if (command.startsWith("chat channel members "))
|
|
325
|
+
return "members";
|
|
326
|
+
if (command.startsWith("chat channel "))
|
|
327
|
+
return "channel";
|
|
328
|
+
if (command.startsWith("chat user token "))
|
|
329
|
+
return "user access token";
|
|
330
|
+
if (command.startsWith("chat user metadata "))
|
|
331
|
+
return "user metadata";
|
|
332
|
+
if (command.startsWith("chat user "))
|
|
333
|
+
return "user";
|
|
334
|
+
if (command.startsWith("app feature "))
|
|
335
|
+
return "application feature";
|
|
336
|
+
if (command.startsWith("app auth-policy "))
|
|
337
|
+
return "authentication policy";
|
|
338
|
+
if (command.startsWith("app "))
|
|
339
|
+
return "application";
|
|
340
|
+
if (command.startsWith("org region "))
|
|
341
|
+
return "region";
|
|
342
|
+
if (command.startsWith("org "))
|
|
343
|
+
return "organization";
|
|
344
|
+
if (command.startsWith("profile "))
|
|
345
|
+
return "profile";
|
|
346
|
+
if (command.startsWith("support ticket "))
|
|
347
|
+
return "support ticket";
|
|
348
|
+
return command.split(" ").slice(0, -1).join(" ") || "request";
|
|
349
|
+
}
|
|
350
|
+
function receipt(value, ctx) {
|
|
351
|
+
const command = value.command.replace(/^sendbird /, "");
|
|
352
|
+
const result = record(value.result) ?? {};
|
|
353
|
+
const target = value.target;
|
|
354
|
+
const action = command.slice(command.lastIndexOf(" ") + 1);
|
|
355
|
+
const verb = verbs[action] ?? "Completed";
|
|
356
|
+
const kind = entity(command);
|
|
357
|
+
const identity = command.startsWith("chat message ")
|
|
358
|
+
? (result.message_id ?? target.message_id)
|
|
359
|
+
: command.startsWith("chat channel ")
|
|
360
|
+
? (result.channel_url ?? target.channel_url)
|
|
361
|
+
: command.startsWith("chat user ")
|
|
362
|
+
? (result.user_id ?? target.user_id)
|
|
363
|
+
: command === "app create"
|
|
364
|
+
? (record(result.application)?.app_id ?? target.app_id)
|
|
365
|
+
: command === "org create"
|
|
366
|
+
? ((Array.isArray(result.organizations)
|
|
367
|
+
? record(result.organizations[0])?.uid
|
|
368
|
+
: undefined) ?? target.org_id)
|
|
369
|
+
: command === "app use"
|
|
370
|
+
? (result.app_id ?? target.app_id)
|
|
371
|
+
: command === "org use"
|
|
372
|
+
? (result.org_id ?? target.org_id)
|
|
373
|
+
: command === "org region use"
|
|
374
|
+
? result.region
|
|
375
|
+
: command.startsWith("profile ")
|
|
376
|
+
? (result.name ?? result.active ?? result.deleted)
|
|
377
|
+
: undefined;
|
|
378
|
+
let headline = `${verb} ${kind}${identity ? ` ${clean(identity)}` : ""}`;
|
|
379
|
+
if (command === "chat channel create" && target.channel_type)
|
|
380
|
+
headline = `Created ${titleCase(clean(target.channel_type))} channel ${valueText(identity)}`;
|
|
381
|
+
if (command === "org clear")
|
|
382
|
+
headline = "Cleared organization, app and region";
|
|
383
|
+
if (command === "app clear")
|
|
384
|
+
headline = "Cleared application selection";
|
|
385
|
+
if (command === "auth login" && record(result.user)?.email)
|
|
386
|
+
headline = `Signed in as ${clean(record(result.user)?.email)}`;
|
|
387
|
+
if (command === "auth logout")
|
|
388
|
+
headline = "Signed out";
|
|
389
|
+
if (command === "auth signup")
|
|
390
|
+
headline = "Signed in after signup";
|
|
391
|
+
if (command === "auth verify-email")
|
|
392
|
+
headline = "Verified email";
|
|
393
|
+
if (command === "auth resend-verification")
|
|
394
|
+
headline = "Sent verification email";
|
|
395
|
+
if (command === "setup")
|
|
396
|
+
headline =
|
|
397
|
+
result.defaults_saved === false
|
|
398
|
+
? "Chat setup checked"
|
|
399
|
+
: "Chat setup complete";
|
|
400
|
+
if (command === "project init")
|
|
401
|
+
headline = "Linked Sendbird project";
|
|
402
|
+
if (command === "chat channel members invite")
|
|
403
|
+
headline = `Invited members to ${valueText(target.channel_url)}`;
|
|
404
|
+
if (command === "calls activate")
|
|
405
|
+
headline = "Calls activation accepted";
|
|
406
|
+
const lines = [
|
|
407
|
+
`${ctx.color.green("✓")} ${ctx.color.bold(clip(headline, ctx.width - 2))}`,
|
|
408
|
+
];
|
|
409
|
+
const trial = record(result.calls_trial);
|
|
410
|
+
if (trial) {
|
|
411
|
+
lines.push(row("Free trial", titleCase(clean(trial.status)), ctx));
|
|
412
|
+
lines.push(...paragraph(trial.note, ctx));
|
|
413
|
+
if (trial.code)
|
|
414
|
+
lines.push(row("Trial code", trial.code, ctx));
|
|
415
|
+
const next = Array.isArray(trial.next) ? record(trial.next[0]) : undefined;
|
|
416
|
+
lines.push(row("Next", Array.isArray(next?.argv)
|
|
417
|
+
? commandLine(next.argv)
|
|
418
|
+
: "sendbird calls credits list", ctx, 300));
|
|
419
|
+
}
|
|
420
|
+
if (command.startsWith("chat message ") && typeof result.message === "string")
|
|
421
|
+
lines.push(row("Message", result.message, ctx));
|
|
422
|
+
if (command.startsWith("chat message ")) {
|
|
423
|
+
if (result.mention_type)
|
|
424
|
+
lines.push(row("Mention", result.mention_type, ctx));
|
|
425
|
+
if (Array.isArray(result.mentioned_user_ids))
|
|
426
|
+
lines.push(row("Mentioned users", result.mentioned_user_ids.join(", ") || "None", ctx));
|
|
427
|
+
if (Array.isArray(result.meta_arrays))
|
|
428
|
+
lines.push(row("Meta-array keys", result.meta_arrays
|
|
429
|
+
.map((v) => record(v)?.key)
|
|
430
|
+
.filter(Boolean)
|
|
431
|
+
.join(", ") || "None", ctx));
|
|
432
|
+
}
|
|
433
|
+
if (command === "app auth-policy set")
|
|
434
|
+
lines.push(row("Token policy", result.guest_user_policy, ctx));
|
|
435
|
+
if (command === "app create" && record(result.application)?.app_name)
|
|
436
|
+
lines.push(row("Name", record(result.application)?.app_name, ctx));
|
|
437
|
+
if (command === "org create" && Array.isArray(result.organizations))
|
|
438
|
+
lines.push(row("Organization", primary(result.organizations[0]) || target.org_id, ctx));
|
|
439
|
+
if (target.channel_url && !command.startsWith("chat channel "))
|
|
440
|
+
lines.push(row("Channel", target.channel_url, ctx));
|
|
441
|
+
if (target.app_id &&
|
|
442
|
+
!["app create", "app use", "project init"].includes(command))
|
|
443
|
+
lines.push(row("App", target.app_id, ctx));
|
|
444
|
+
if (command.startsWith("calls ")) {
|
|
445
|
+
if (command === "calls activate") {
|
|
446
|
+
const readback = record(result.readback);
|
|
447
|
+
lines.push(row("Activation", readback.status === "matches"
|
|
448
|
+
? "Enabled (read back)"
|
|
449
|
+
: readback.status === "mismatch"
|
|
450
|
+
? "Not confirmed; inspect app state"
|
|
451
|
+
: "Readback unavailable; registration accepted", ctx));
|
|
452
|
+
lines.push(row("Next", "sendbird calls settings push get", ctx));
|
|
453
|
+
if (!trial)
|
|
454
|
+
lines.push(row("Credits", "No trial credits claimed or credits purchased", ctx), row("Next", "sendbird calls credits list", ctx));
|
|
455
|
+
return lines;
|
|
456
|
+
}
|
|
457
|
+
if (result.configuration_id)
|
|
458
|
+
lines.push(row("Configuration", result.configuration_id, ctx));
|
|
459
|
+
const readback = record(result.readback);
|
|
460
|
+
if (readback) {
|
|
461
|
+
lines.push(row("Readback", readback.status === "read_completed"
|
|
462
|
+
? "Settings read again"
|
|
463
|
+
: "Unavailable; write was accepted", ctx));
|
|
464
|
+
const settings = record(readback.settings);
|
|
465
|
+
if (settings && command.startsWith("calls settings "))
|
|
466
|
+
lines.push(...detailRows(command.replace(/ set$/, " get"), settings, target, ctx));
|
|
467
|
+
lines.push(row("Next", Array.isArray(readback.next) &&
|
|
468
|
+
record(readback.next[0]) &&
|
|
469
|
+
Array.isArray(record(readback.next[0]).argv)
|
|
470
|
+
? commandLine(record(readback.next[0]).argv)
|
|
471
|
+
: command.startsWith("calls push ")
|
|
472
|
+
? `sendbird calls push list --push-type ${valueText(result.push_type ?? "TYPE")}`
|
|
473
|
+
: `sendbird ${command.replace(/ set$/, " get")}`, ctx));
|
|
474
|
+
}
|
|
475
|
+
}
|
|
476
|
+
if (command === "setup") {
|
|
477
|
+
if (target.org_id)
|
|
478
|
+
lines.push(row("Organization UID", target.org_id, ctx));
|
|
479
|
+
if (result.profile)
|
|
480
|
+
lines.push(row("Profile", result.profile, ctx));
|
|
481
|
+
if (result.environment)
|
|
482
|
+
lines.push(row("Environment", result.environment, ctx));
|
|
483
|
+
if ("region" in result)
|
|
484
|
+
lines.push(row("Default region", result.region ?? "Not selected", ctx));
|
|
485
|
+
if ("defaults_saved" in result)
|
|
486
|
+
lines.push(row("Defaults", result.defaults_saved
|
|
487
|
+
? "Saved; profile is active"
|
|
488
|
+
: "Not saved; use the explicit next command", ctx));
|
|
489
|
+
lines.push(row("Next", Array.isArray(result.next_argv)
|
|
490
|
+
? commandLine(result.next_argv.map(String))
|
|
491
|
+
: "sendbird doctor", ctx), row("Agent skills", "sendbird skills status", ctx));
|
|
492
|
+
}
|
|
493
|
+
if (command === "project init") {
|
|
494
|
+
lines.push(row("File", result.path, ctx));
|
|
495
|
+
lines.push(row("App ID", record(result.project)?.app_id, ctx));
|
|
496
|
+
}
|
|
497
|
+
if (command === "auth logout" && result.remote_revocation === "unknown")
|
|
498
|
+
lines.push(` ${ctx.color.yellow("!")} Remote token revocation is uncertain.`);
|
|
499
|
+
if (Array.isArray(result.next) && result.next.length)
|
|
500
|
+
lines.push(row("Next", result.next[0], ctx));
|
|
501
|
+
return lines;
|
|
502
|
+
}
|
|
503
|
+
function preview(value, ctx) {
|
|
504
|
+
const command = value.command.replace(/^sendbird /, "");
|
|
505
|
+
const result = record(value.result) ?? {};
|
|
506
|
+
const request = record(result.request);
|
|
507
|
+
const target = value.target;
|
|
508
|
+
const action = command.slice(command.lastIndexOf(" ") + 1);
|
|
509
|
+
const imperative = {
|
|
510
|
+
create: "Create",
|
|
511
|
+
delete: "Delete",
|
|
512
|
+
set: "Update",
|
|
513
|
+
update: "Update",
|
|
514
|
+
send: "Send",
|
|
515
|
+
invite: "Invite",
|
|
516
|
+
revoke: "Revoke",
|
|
517
|
+
issue: "Issue",
|
|
518
|
+
"claim-trial": "Request free trial for",
|
|
519
|
+
list: "List",
|
|
520
|
+
search: "Search",
|
|
521
|
+
get: "Get",
|
|
522
|
+
join: "Join",
|
|
523
|
+
leave: "Leave",
|
|
524
|
+
};
|
|
525
|
+
const kind = command === "chat channel create" && target.channel_type
|
|
526
|
+
? `${titleCase(clean(target.channel_type))} channel`
|
|
527
|
+
: entity(command);
|
|
528
|
+
const lines = [
|
|
529
|
+
ctx.color.bold(`Preview · ${imperative[action] ?? titleCase(action)} ${kind}`),
|
|
530
|
+
];
|
|
531
|
+
if (command.startsWith("calls credits "))
|
|
532
|
+
lines.push(row("Organization", target.org_id, ctx));
|
|
533
|
+
if (target.app_id)
|
|
534
|
+
lines.push(row("App", target.app_id, ctx));
|
|
535
|
+
if (target.channel_url)
|
|
536
|
+
lines.push(row("Channel", target.channel_url, ctx));
|
|
537
|
+
if (target.user_id)
|
|
538
|
+
lines.push(row("User", target.user_id, ctx));
|
|
539
|
+
if (request?.method && request.path)
|
|
540
|
+
lines.push(row("Request", `${request.method} ${request.path}`, ctx, 200));
|
|
541
|
+
if (result.mode === "dashboard_handoff") {
|
|
542
|
+
lines.push(row("Requested", result.requested_plan, ctx));
|
|
543
|
+
lines.push(` ${ctx.color.dim("Offline billing preview; no quote, eligibility check or subscription change.")}`);
|
|
544
|
+
}
|
|
545
|
+
if (record(result.calls_trial))
|
|
546
|
+
lines.push(row("Free trial", "After activation: check eligibility, then request trial; no calls made in this preview", ctx));
|
|
547
|
+
if (result.sdk)
|
|
548
|
+
lines.push(row("SDK", result.sdk, ctx, 200));
|
|
549
|
+
for (const [scope, fields] of [
|
|
550
|
+
["body", request?.body ?? result.body],
|
|
551
|
+
["query", request?.query],
|
|
552
|
+
["params", result.params],
|
|
553
|
+
]) {
|
|
554
|
+
const source = record(fields);
|
|
555
|
+
if (!source)
|
|
556
|
+
continue;
|
|
557
|
+
for (const [key, item] of Object.entries(source).slice(0, 7))
|
|
558
|
+
lines.push(row(fieldLabel(key), Array.isArray(item) ? item.join(", ") : item, ctx));
|
|
559
|
+
if (Object.keys(source).length > 7)
|
|
560
|
+
lines.push(` ${ctx.color.dim(`More ${scope} fields: --json`)}`);
|
|
561
|
+
}
|
|
562
|
+
if ((command === "app feature set" ||
|
|
563
|
+
command === "calls credits claim-trial") &&
|
|
564
|
+
result.note)
|
|
565
|
+
lines.push(...paragraph(result.note, ctx));
|
|
566
|
+
lines.push(` ${ctx.color.dim("No request sent. Access and remote state were not checked.")}`);
|
|
567
|
+
return lines;
|
|
568
|
+
}
|
|
569
|
+
function profileList(result, ctx) {
|
|
570
|
+
const profiles = record(result.profiles) ?? {};
|
|
571
|
+
const entries = Object.entries(profiles).map(([name, value]) => ({
|
|
572
|
+
name,
|
|
573
|
+
...record(value),
|
|
574
|
+
}));
|
|
575
|
+
const lines = [
|
|
576
|
+
ctx.color.bold(countLabel(entries.length, "profile")),
|
|
577
|
+
row("Active", result.active, ctx),
|
|
578
|
+
];
|
|
579
|
+
lines.push(...table(entries, [
|
|
580
|
+
{ label: "Profile", value: (r) => r.name, max: 30 },
|
|
581
|
+
{ label: "Environment", value: (r) => r.env, max: 18 },
|
|
582
|
+
{
|
|
583
|
+
label: "Organization",
|
|
584
|
+
value: (r) => (r.orgId ? "Selected" : "—"),
|
|
585
|
+
max: 18,
|
|
586
|
+
},
|
|
587
|
+
{ label: "App", value: (r) => (r.appId ? "Selected" : "—"), max: 13 },
|
|
588
|
+
], ctx));
|
|
589
|
+
return lines;
|
|
590
|
+
}
|
|
591
|
+
function billingUsageRead(command, result, target, ctx) {
|
|
592
|
+
const targetRows = [
|
|
593
|
+
...(target.org_id ? [row("Organization UID", target.org_id, ctx)] : []),
|
|
594
|
+
...(target.app_id ? [row("App", target.app_id, ctx)] : []),
|
|
595
|
+
];
|
|
596
|
+
if ([
|
|
597
|
+
"billing plan check",
|
|
598
|
+
"billing plan change",
|
|
599
|
+
"billing plan verify",
|
|
600
|
+
].includes(command)) {
|
|
601
|
+
const handoff = record(result.handoff);
|
|
602
|
+
const organization = record(result.organization);
|
|
603
|
+
const current = record(result.current_subscription);
|
|
604
|
+
const future = record(result.future_subscription);
|
|
605
|
+
const reviewReasons = {
|
|
606
|
+
sales_contract: "Current plan uses a sales contract",
|
|
607
|
+
aws_marketplace: "Billing is managed through AWS Marketplace",
|
|
608
|
+
sales_plan: "Requested plan requires Sales",
|
|
609
|
+
shopify_billing: "Billing is managed through Shopify",
|
|
610
|
+
legacy_organization: "Legacy organization needs Dashboard review",
|
|
611
|
+
managed_payment: "Payment is managed by your account team",
|
|
612
|
+
scheduled_subscription: "A subscription change is already scheduled",
|
|
613
|
+
cancellation_scheduled: "Cancellation is already scheduled",
|
|
614
|
+
already_current: "Requested plan is already current",
|
|
615
|
+
dashboard_selection_required: "Select this plan in Dashboard",
|
|
616
|
+
payment_method_required: "Register a payment method in Dashboard",
|
|
617
|
+
organization_inactive: "Organization is inactive",
|
|
618
|
+
subscription_type_requires_review: "Current contract type needs review",
|
|
619
|
+
};
|
|
620
|
+
const observed = {
|
|
621
|
+
current: "Current subscription",
|
|
622
|
+
scheduled: "Scheduled subscription",
|
|
623
|
+
not_reflected: "No matching subscription yet",
|
|
624
|
+
};
|
|
625
|
+
return [
|
|
626
|
+
ctx.color.bold(command === "billing plan verify"
|
|
627
|
+
? "Chat plan status checked"
|
|
628
|
+
: "Chat plan change requires Dashboard confirmation"),
|
|
629
|
+
...targetRows,
|
|
630
|
+
...(organization ? [row("Name", organization.name, ctx)] : []),
|
|
631
|
+
row("Requested", result.requested_plan, ctx),
|
|
632
|
+
row("Current", current?.subscription_name ?? "None", ctx),
|
|
633
|
+
row("Scheduled", future?.subscription_name ?? "None", ctx),
|
|
634
|
+
...(result.change_state
|
|
635
|
+
? [
|
|
636
|
+
row("Observed state", observed[String(result.change_state)] ?? result.change_state, ctx),
|
|
637
|
+
]
|
|
638
|
+
: []),
|
|
639
|
+
...(result.selection_state
|
|
640
|
+
? [
|
|
641
|
+
row("Selection", result.selection_state === "catalog_candidate"
|
|
642
|
+
? "Catalog candidate; confirm in Dashboard"
|
|
643
|
+
: "Dashboard review required", ctx),
|
|
644
|
+
]
|
|
645
|
+
: []),
|
|
646
|
+
...(Array.isArray(result.reasons)
|
|
647
|
+
? result.reasons.map((reason) => row("Review required", reviewReasons[String(reason)] ?? reason, ctx))
|
|
648
|
+
: []),
|
|
649
|
+
...(typeof result.card_present === "boolean"
|
|
650
|
+
? [row("Card registered", result.card_present, ctx)]
|
|
651
|
+
: []),
|
|
652
|
+
...(handoff
|
|
653
|
+
? [
|
|
654
|
+
row("Dashboard", handoff.url, ctx),
|
|
655
|
+
row("Action", handoff.instruction ??
|
|
656
|
+
"Review the organization, price and effective date in Dashboard", ctx),
|
|
657
|
+
]
|
|
658
|
+
: []),
|
|
659
|
+
` ${ctx.color.dim("No subscription change was submitted by this command.")}`,
|
|
660
|
+
row("Next", result.next, ctx),
|
|
661
|
+
];
|
|
662
|
+
}
|
|
663
|
+
if (command === "billing plan current") {
|
|
664
|
+
const current = record(result.current_subscription);
|
|
665
|
+
const future = record(result.future_subscription);
|
|
666
|
+
return [
|
|
667
|
+
ctx.color.bold("Chat subscription"),
|
|
668
|
+
...targetRows,
|
|
669
|
+
row("Current", current?.display_name ?? current?.subscription_name ?? "None", ctx),
|
|
670
|
+
...(current
|
|
671
|
+
? [
|
|
672
|
+
row("Plan key", current.subscription_name, ctx),
|
|
673
|
+
row("Type", current.subscription_type, ctx),
|
|
674
|
+
row("Auto-renewal", current.auto_renewal, ctx),
|
|
675
|
+
row("Next billing", current.next_billing_date ?? "Not provided", ctx),
|
|
676
|
+
]
|
|
677
|
+
: []),
|
|
678
|
+
row("Scheduled", future?.display_name ?? future?.subscription_name ?? "None", ctx),
|
|
679
|
+
...(future
|
|
680
|
+
? [
|
|
681
|
+
row("Effective", future.apply_date ?? future.start_date ?? "Not provided", ctx),
|
|
682
|
+
]
|
|
683
|
+
: []),
|
|
684
|
+
];
|
|
685
|
+
}
|
|
686
|
+
if (command === "billing plan list") {
|
|
687
|
+
const plans = Object.entries(result).map(([key, value]) => {
|
|
688
|
+
const entry = record(value);
|
|
689
|
+
const plan = record(entry?.plan);
|
|
690
|
+
return {
|
|
691
|
+
key,
|
|
692
|
+
cycle: entry?.billing_cycle_months,
|
|
693
|
+
mau: record(plan?.mau)?.purchased_units,
|
|
694
|
+
};
|
|
695
|
+
});
|
|
696
|
+
return [
|
|
697
|
+
ctx.color.bold(plans.length
|
|
698
|
+
? countLabel(plans.length, "catalog plan")
|
|
699
|
+
: "No catalog plans returned."),
|
|
700
|
+
...targetRows,
|
|
701
|
+
...table(plans.slice(0, 12), [
|
|
702
|
+
{ label: "Plan key", value: (r) => r.key, max: 38 },
|
|
703
|
+
{ label: "Cycle (months)", value: (r) => r.cycle, max: 18 },
|
|
704
|
+
{ label: "MAU", value: (r) => r.mau, max: 15 },
|
|
705
|
+
], ctx),
|
|
706
|
+
...(plans.length > 12
|
|
707
|
+
? [row("More", "Use --json for the full catalog", ctx)]
|
|
708
|
+
: []),
|
|
709
|
+
` ${ctx.color.dim("Catalog entries are not eligibility decisions or price quotes.")}`,
|
|
710
|
+
];
|
|
711
|
+
}
|
|
712
|
+
if (command === "usage chat org") {
|
|
713
|
+
const data = Array.isArray(result.data)
|
|
714
|
+
? result.data.map(record).filter((r) => r !== undefined)
|
|
715
|
+
: [];
|
|
716
|
+
return [
|
|
717
|
+
ctx.color.bold(data.length
|
|
718
|
+
? countLabel(data.length, "Chat usage month")
|
|
719
|
+
: "No Chat usage found for these months."),
|
|
720
|
+
...targetRows,
|
|
721
|
+
...table(data.slice(0, 12), [
|
|
722
|
+
{ label: "Month", value: (r) => r.record_month, max: 12 },
|
|
723
|
+
{ label: "MAU", value: (r) => r.mau, max: 15 },
|
|
724
|
+
{ label: "Peak conn", value: (r) => r.pc, max: 15 },
|
|
725
|
+
{
|
|
726
|
+
label: "Avg storage (bytes)",
|
|
727
|
+
value: (r) => r.avg_file_storage,
|
|
728
|
+
max: 24,
|
|
729
|
+
},
|
|
730
|
+
], ctx),
|
|
731
|
+
...(data.length > 12
|
|
732
|
+
? [row("More", "Use --json for every month and feature", ctx)]
|
|
733
|
+
: []),
|
|
734
|
+
];
|
|
735
|
+
}
|
|
736
|
+
if (command === "usage chat app") {
|
|
737
|
+
const data = record(result.data) ?? {};
|
|
738
|
+
const metrics = [
|
|
739
|
+
"mau",
|
|
740
|
+
"pc",
|
|
741
|
+
"upload_traffic",
|
|
742
|
+
"avg_file_storage",
|
|
743
|
+
"file_storage",
|
|
744
|
+
]
|
|
745
|
+
.map((key) => ({ key, value: record(data[key]) }))
|
|
746
|
+
.filter((entry) => entry.value !== undefined);
|
|
747
|
+
return [
|
|
748
|
+
ctx.color.bold("Application Chat usage"),
|
|
749
|
+
...targetRows,
|
|
750
|
+
row("Updated", data.updated_dt ?? "Not provided", ctx),
|
|
751
|
+
...table(metrics, [
|
|
752
|
+
{ label: "Feature", value: (r) => r.key, max: 24 },
|
|
753
|
+
{ label: "App", value: (r) => record(r.value)?.usage, max: 16 },
|
|
754
|
+
{
|
|
755
|
+
label: "Other apps",
|
|
756
|
+
value: (r) => record(r.value)?.others_usage,
|
|
757
|
+
max: 18,
|
|
758
|
+
},
|
|
759
|
+
{ label: "Quota", value: (r) => record(r.value)?.quota, max: 16 },
|
|
760
|
+
], ctx),
|
|
761
|
+
` ${ctx.color.dim("Units vary by feature; use --json for source values and all fields.")}`,
|
|
762
|
+
];
|
|
763
|
+
}
|
|
764
|
+
if (command === "usage calls org") {
|
|
765
|
+
if (Array.isArray(result.data)) {
|
|
766
|
+
const apps = result.data;
|
|
767
|
+
return [
|
|
768
|
+
ctx.color.bold(apps.length
|
|
769
|
+
? `${countLabel(apps.length, "application")} with voice/video usage`
|
|
770
|
+
: "No voice/video usage found."),
|
|
771
|
+
...targetRows,
|
|
772
|
+
...apps
|
|
773
|
+
.slice(0, 12)
|
|
774
|
+
.flatMap((app) => [
|
|
775
|
+
row("Application", app.app_name, ctx),
|
|
776
|
+
row("App ID", app.app_id, ctx),
|
|
777
|
+
row("Seconds", app.usage, ctx),
|
|
778
|
+
row("Credits", app.used_credit, ctx),
|
|
779
|
+
row("Calls enabled", app.call_enabled, ctx),
|
|
780
|
+
]),
|
|
781
|
+
...(apps.length > 12
|
|
782
|
+
? [row("More", "Use --json for every application", ctx)]
|
|
783
|
+
: []),
|
|
784
|
+
` ${ctx.color.dim("The source can include Live usage; this is not a Chat subscription charge.")}`,
|
|
785
|
+
];
|
|
786
|
+
}
|
|
787
|
+
const data = record(result.data) ?? {};
|
|
788
|
+
const types = Object.entries(data).map(([key, value]) => ({
|
|
789
|
+
key,
|
|
790
|
+
value: record(value),
|
|
791
|
+
}));
|
|
792
|
+
return [
|
|
793
|
+
ctx.color.bold(types.length
|
|
794
|
+
? countLabel(types.length, "voice/video usage type")
|
|
795
|
+
: "No voice/video usage found."),
|
|
796
|
+
...targetRows,
|
|
797
|
+
...table(types.slice(0, 12), [
|
|
798
|
+
{ label: "Type", value: (r) => r.key, max: 40 },
|
|
799
|
+
{ label: "Seconds", value: (r) => record(r.value)?.usage, max: 18 },
|
|
800
|
+
{
|
|
801
|
+
label: "Credits",
|
|
802
|
+
value: (r) => record(r.value)?.used_credit,
|
|
803
|
+
max: 18,
|
|
804
|
+
},
|
|
805
|
+
], ctx),
|
|
806
|
+
...(types.length > 12
|
|
807
|
+
? [row("More", "Use --json for every type", ctx)]
|
|
808
|
+
: []),
|
|
809
|
+
` ${ctx.color.dim("The source can include Live usage; this is not a Chat subscription charge.")}`,
|
|
810
|
+
];
|
|
811
|
+
}
|
|
812
|
+
if (command === "usage calls app") {
|
|
813
|
+
const data = record(result.data) ?? {};
|
|
814
|
+
return [
|
|
815
|
+
ctx.color.bold("Application voice/video usage"),
|
|
816
|
+
...targetRows,
|
|
817
|
+
row("Seconds", data.usage, ctx),
|
|
818
|
+
row("Credits used", data.used_credit, ctx),
|
|
819
|
+
row("Org credits", data.org_credit, ctx),
|
|
820
|
+
row("Org remaining", data.org_remaining_credit, ctx),
|
|
821
|
+
` ${ctx.color.dim("Source totals may include Live. Use --json for all fields.")}`,
|
|
822
|
+
];
|
|
823
|
+
}
|
|
824
|
+
if (command === "usage calls trend") {
|
|
825
|
+
const data = record(result.data) ?? {};
|
|
826
|
+
const calls = Array.isArray(data.calls)
|
|
827
|
+
? data.calls.map(record).filter((r) => r !== undefined)
|
|
828
|
+
: [];
|
|
829
|
+
return [
|
|
830
|
+
ctx.color.bold(calls.length
|
|
831
|
+
? countLabel(calls.length, "Calls usage period")
|
|
832
|
+
: "No Calls usage found for this period."),
|
|
833
|
+
...targetRows,
|
|
834
|
+
...table(calls.slice(0, 12), [
|
|
835
|
+
{ label: "Period", value: (r) => r.record_date, max: 18 },
|
|
836
|
+
{ label: "Seconds", value: (r) => r.usage, max: 18 },
|
|
837
|
+
{ label: "Credits", value: (r) => r.used_credit, max: 18 },
|
|
838
|
+
], ctx),
|
|
839
|
+
...(calls.length > 12
|
|
840
|
+
? [row("More", "Use --json for every period", ctx)]
|
|
841
|
+
: []),
|
|
842
|
+
` ${ctx.color.dim("Calls only here; --json retains total and Live series.")}`,
|
|
843
|
+
];
|
|
844
|
+
}
|
|
845
|
+
if (command === "usage calls app-trend") {
|
|
846
|
+
const periods = Array.isArray(result.data)
|
|
847
|
+
? result.data.map(record).filter((item) => item !== undefined)
|
|
848
|
+
: [];
|
|
849
|
+
return [
|
|
850
|
+
ctx.color.bold(periods.length
|
|
851
|
+
? countLabel(periods.length, "voice/video usage period")
|
|
852
|
+
: "No voice/video usage found for this app and period."),
|
|
853
|
+
...targetRows,
|
|
854
|
+
...table(periods.slice(0, 12), [
|
|
855
|
+
{ label: "Period", value: (r) => r.record_date, max: 18 },
|
|
856
|
+
{ label: "Updated", value: (r) => r.updated_dt, max: 27 },
|
|
857
|
+
{
|
|
858
|
+
label: "Usage types",
|
|
859
|
+
value: (r) => Object.values(r).filter((item) => record(item)?.usage !== undefined).length,
|
|
860
|
+
max: 15,
|
|
861
|
+
},
|
|
862
|
+
], ctx),
|
|
863
|
+
` ${ctx.color.dim("Use --json for seconds and credits by type, including any Live usage.")}`,
|
|
864
|
+
];
|
|
865
|
+
}
|
|
866
|
+
}
|
|
867
|
+
export function humanOperation(value, options = {}) {
|
|
868
|
+
const ctx = view(options);
|
|
869
|
+
const secrets = secretValues(value.result);
|
|
870
|
+
const safeValue = {
|
|
871
|
+
...value,
|
|
872
|
+
target: redact(value.target, secrets),
|
|
873
|
+
result: redact(value.result, secrets),
|
|
874
|
+
};
|
|
875
|
+
const command = safeValue.command.replace(/^sendbird /, "");
|
|
876
|
+
if (safeValue.status !== "preview")
|
|
877
|
+
safeValue.result = humanValueSemantics(command, safeValue.result);
|
|
878
|
+
const result = record(safeValue.result) ?? {};
|
|
879
|
+
if (command.startsWith("skills ")) {
|
|
880
|
+
const lines = [];
|
|
881
|
+
if (command === "skills show") {
|
|
882
|
+
lines.push(ctx.color.bold("Sendbird CLI Skill"), row("Path", result.path, ctx), "", clean(result.content));
|
|
883
|
+
if (Array.isArray(result.references))
|
|
884
|
+
for (const path of result.references)
|
|
885
|
+
lines.push(row("Path", path, ctx));
|
|
886
|
+
}
|
|
887
|
+
else if (Array.isArray(result.installations)) {
|
|
888
|
+
lines.push(ctx.color.bold("Sendbird CLI Skill registrations"), row("CLI version", result.bundled_version, ctx));
|
|
889
|
+
for (const item of result.installations) {
|
|
890
|
+
const r = record(item) ?? {};
|
|
891
|
+
lines.push("", ctx.color.bold(`${clean(r.agent)} · ${clean(r.scope)}`), row("State", clean(r.state).replaceAll("_", " "), ctx), row("Version", r.version ?? "—", ctx), row("Path", r.path ?? "Unavailable", ctx));
|
|
892
|
+
const transaction = record(r.transaction);
|
|
893
|
+
if (transaction?.lock_present)
|
|
894
|
+
lines.push(...paragraph("A registration lock exists; work may be active or interrupted. Inspect before manual recovery.", ctx));
|
|
895
|
+
if (Array.isArray(transaction?.backup_paths))
|
|
896
|
+
for (const path of transaction.backup_paths)
|
|
897
|
+
lines.push(row("Path", path, ctx));
|
|
898
|
+
}
|
|
899
|
+
}
|
|
900
|
+
else {
|
|
901
|
+
const state = clean(result.state).replaceAll("_", " ");
|
|
902
|
+
lines.push(ctx.color.bold(safeValue.status === "preview"
|
|
903
|
+
? `Preview CLI Skill registration · ${state}`
|
|
904
|
+
: ({
|
|
905
|
+
installed: "Registered Sendbird CLI Skill",
|
|
906
|
+
updated: "Updated Sendbird CLI Skill",
|
|
907
|
+
current: "CLI Skill already current",
|
|
908
|
+
removed: "Removed CLI Skill registration",
|
|
909
|
+
absent: "No CLI Skill registered at this location",
|
|
910
|
+
}[String(result.state)] ?? `CLI Skill · ${state}`)), row("Agent", result.agent, ctx), row("Scope", result.scope, ctx), row("Path", result.path, ctx), row("Version", result.version, ctx), row("Host loading", "Not verified", ctx));
|
|
911
|
+
}
|
|
912
|
+
lines.push(...paragraph(result.note, ctx), ...paragraph(result.next, ctx));
|
|
913
|
+
return lines.join("\n");
|
|
914
|
+
}
|
|
915
|
+
if (safeValue.status === "preview")
|
|
916
|
+
return preview(safeValue, ctx).join("\n");
|
|
917
|
+
if (command.startsWith("calls credits ")) {
|
|
918
|
+
const lines = [
|
|
919
|
+
ctx.color.bold(command.endsWith("list")
|
|
920
|
+
? "Calls voucher history"
|
|
921
|
+
: "Free Calls trial request accepted"),
|
|
922
|
+
row("Organization", result.org_id, ctx),
|
|
923
|
+
];
|
|
924
|
+
const entries = (Array.isArray(result.vouchers) ? result.vouchers : [result.voucher])
|
|
925
|
+
.map(record)
|
|
926
|
+
.filter((entry) => Boolean(entry));
|
|
927
|
+
if (!entries.length)
|
|
928
|
+
lines.push(result.total === 0
|
|
929
|
+
? " No voucher history returned."
|
|
930
|
+
: " Voucher details not returned.");
|
|
931
|
+
for (const entry of entries) {
|
|
932
|
+
const voucher = entry;
|
|
933
|
+
lines.push(row("Voucher", voucher.voucher_id, ctx), row("Status", voucher.status, ctx), row("Credits", voucher.credit, ctx), row("Carried credits", voucher.transferred_credit, ctx), row("Used credits", voucher.used_credit, ctx), row("Balance", voucher.remaining_credit, ctx), row("Started", voucher.started_at ? localTime(voucher.started_at) : "Not started", ctx), row("Expires", voucher.expires_at ? localTime(voucher.expires_at) : "Not provided", ctx));
|
|
934
|
+
}
|
|
935
|
+
const readback = record(result.readback);
|
|
936
|
+
if (readback)
|
|
937
|
+
lines.push(row("Readback", readback.status, ctx), ...paragraph(readback.note, ctx));
|
|
938
|
+
else
|
|
939
|
+
lines.push(...paragraph(result.note, ctx));
|
|
940
|
+
if (typeof result.next_offset === "number")
|
|
941
|
+
lines.push(row("Next offset", result.next_offset, ctx));
|
|
942
|
+
lines.push(...paragraph("Check status and expiry. A balance alone does not prove active credit or call availability.", ctx));
|
|
943
|
+
if (command.endsWith("claim-trial")) {
|
|
944
|
+
const next = Array.isArray(readback?.next)
|
|
945
|
+
? readback.next
|
|
946
|
+
.map(record)
|
|
947
|
+
.find((step) => Array.isArray(step?.argv) &&
|
|
948
|
+
step.argv.length > 0 &&
|
|
949
|
+
step.argv.every((arg) => typeof arg === "string"))
|
|
950
|
+
: undefined;
|
|
951
|
+
lines.push(row("Next", next
|
|
952
|
+
? commandLine(next.argv)
|
|
953
|
+
: "sendbird calls credits list", ctx));
|
|
954
|
+
}
|
|
955
|
+
return lines.join("\n");
|
|
956
|
+
}
|
|
957
|
+
if (command.startsWith("app feature ")) {
|
|
958
|
+
const lines = [
|
|
959
|
+
ctx.color.bold(command.endsWith("list")
|
|
960
|
+
? "Application feature settings"
|
|
961
|
+
: "Feature change accepted"),
|
|
962
|
+
row("App", safeValue.target.app_id, ctx),
|
|
963
|
+
];
|
|
964
|
+
if (command.endsWith("list")) {
|
|
965
|
+
lines.push(...table(result.features ?? [], [
|
|
966
|
+
{ label: "Feature", value: (r) => r.feature, min: 30, max: 34 },
|
|
967
|
+
{
|
|
968
|
+
label: "Enabled",
|
|
969
|
+
value: (r) => r.enabled === null ? "Unknown" : r.enabled ? "Yes" : "No",
|
|
970
|
+
min: 10,
|
|
971
|
+
max: 12,
|
|
972
|
+
},
|
|
973
|
+
], ctx));
|
|
974
|
+
}
|
|
975
|
+
else {
|
|
976
|
+
lines.push(row("Feature", result.feature, ctx), row("Requested", result.requested_enabled === true ? "Enabled" : "Disabled", ctx));
|
|
977
|
+
const readback = record(result.readback);
|
|
978
|
+
lines.push(row("Readback", readback?.status === "matches"
|
|
979
|
+
? "Requested settings observed"
|
|
980
|
+
: readback?.status === "mismatch"
|
|
981
|
+
? "Different settings observed; inspect before another change"
|
|
982
|
+
: "Unavailable; write was accepted", ctx));
|
|
983
|
+
if (result.feature === "advanced_profanity_filter" &&
|
|
984
|
+
result.requested_enabled === true)
|
|
985
|
+
lines.push(...paragraph("The basic profanity filter was also requested off, matching Dashboard behavior.", ctx));
|
|
986
|
+
const next = Array.isArray(readback?.next)
|
|
987
|
+
? record(readback.next[0])
|
|
988
|
+
: undefined;
|
|
989
|
+
if (Array.isArray(next?.argv))
|
|
990
|
+
lines.push(...paragraph("Verify: " + commandLine(next.argv), ctx));
|
|
991
|
+
}
|
|
992
|
+
lines.push(...paragraph(result.note, ctx));
|
|
993
|
+
return lines.join("\n");
|
|
994
|
+
}
|
|
995
|
+
const pro = proOutput(command, result, safeValue.target, safeValue.status === "accepted", ctx);
|
|
996
|
+
if (pro)
|
|
997
|
+
return pro.join("\n");
|
|
998
|
+
if (safeValue.status === "accepted" ||
|
|
999
|
+
(safeValue.status === "succeeded" &&
|
|
1000
|
+
(command === "setup" ||
|
|
1001
|
+
/\b(create|use|clear|delete|init|login|logout|signup|verify-email|resend-verification)$/.test(command))))
|
|
1002
|
+
return receipt(safeValue, ctx).join("\n");
|
|
1003
|
+
if (command === "profile list")
|
|
1004
|
+
return profileList(result, ctx).join("\n");
|
|
1005
|
+
const billingUsage = billingUsageRead(command, result, safeValue.target, ctx);
|
|
1006
|
+
if (billingUsage)
|
|
1007
|
+
return billingUsage.join("\n");
|
|
1008
|
+
const listed = listOutput(command, result, safeValue.target, ctx);
|
|
1009
|
+
if (listed)
|
|
1010
|
+
return listed.join("\n");
|
|
1011
|
+
if (command === "chat channel watch" || safeValue.status === "completed") {
|
|
1012
|
+
const lines = [`${ctx.color.green("✓")} ${ctx.color.bold("Stream ended")}`];
|
|
1013
|
+
if (safeValue.target.channel_url)
|
|
1014
|
+
lines.push(row("Channel", safeValue.target.channel_url, ctx));
|
|
1015
|
+
if (result.reason)
|
|
1016
|
+
lines.push(row("Reason", result.reason, ctx));
|
|
1017
|
+
if (typeof result.events === "number")
|
|
1018
|
+
lines.push(row("Events", result.events, ctx));
|
|
1019
|
+
return lines.join("\n");
|
|
1020
|
+
}
|
|
1021
|
+
return detailRows(command, result, safeValue.target, ctx).join("\n");
|
|
1022
|
+
}
|
|
1023
|
+
export function humanEvent(command, value, options = {}) {
|
|
1024
|
+
const ctx = view(options);
|
|
1025
|
+
const event = record(value) ?? {};
|
|
1026
|
+
const channel = record(event.channel);
|
|
1027
|
+
const data = record(event.data);
|
|
1028
|
+
const name = clean(event.event || "event").replaceAll("_", " ");
|
|
1029
|
+
const context = [
|
|
1030
|
+
event.channel_url ?? channel?.channel_url,
|
|
1031
|
+
data?.message_id ? `message ${data.message_id}` : undefined,
|
|
1032
|
+
]
|
|
1033
|
+
.filter(Boolean)
|
|
1034
|
+
.map(clean);
|
|
1035
|
+
return `${ctx.color.dim("•")} ${ctx.color.bold(name)}${context.length ? ` · ${context.join(" · ")}` : ""}`;
|
|
1036
|
+
}
|
|
1037
|
+
function discovery(value, ctx) {
|
|
1038
|
+
if (value.__human_view === "example") {
|
|
1039
|
+
const shell = clean(value.shell);
|
|
1040
|
+
return [
|
|
1041
|
+
ctx.color.bold(`Example · ${clean(value.command)}`),
|
|
1042
|
+
` ${shell}`,
|
|
1043
|
+
` ${ctx.color.dim("Replace placeholder IDs. --example --json returns the structured input.")}`,
|
|
1044
|
+
];
|
|
1045
|
+
}
|
|
1046
|
+
if (value.__human_view === "schema") {
|
|
1047
|
+
const schema = JSON.stringify(value.schema, null, 2);
|
|
1048
|
+
return [
|
|
1049
|
+
ctx.color.bold(`Response schema · ${clean(value.command)}`),
|
|
1050
|
+
...schema.split("\n").map((line) => ` ${line}`),
|
|
1051
|
+
];
|
|
1052
|
+
}
|
|
1053
|
+
if (typeof value.entrypoint === "string" && Array.isArray(value.discovery)) {
|
|
1054
|
+
const steps = value.discovery
|
|
1055
|
+
.map(record)
|
|
1056
|
+
.filter((item) => item !== undefined);
|
|
1057
|
+
return [
|
|
1058
|
+
ctx.color.bold("Sendbird Chat CLI"),
|
|
1059
|
+
" Manage Chat resources and verify user messaging.",
|
|
1060
|
+
row("Get started", "sendbird setup", ctx),
|
|
1061
|
+
row("Agent skills", "sendbird skills status", ctx),
|
|
1062
|
+
row("Staging app hosts", "sendbird --profile staging --app-id APP_ID app sdk-config --raw", ctx),
|
|
1063
|
+
...steps
|
|
1064
|
+
.slice(0, 3)
|
|
1065
|
+
.map((item) => row(clean(item.purpose).split(" ").slice(0, 2).join(" "), commandLine(item.argv), ctx, 140)),
|
|
1066
|
+
` ${ctx.color.dim("For agents: sendbird --json agent")}`,
|
|
1067
|
+
];
|
|
1068
|
+
}
|
|
1069
|
+
if (typeof value.command === "string" && Array.isArray(value.flags)) {
|
|
1070
|
+
const lines = [ctx.color.bold(clean(value.command))];
|
|
1071
|
+
if (value.description)
|
|
1072
|
+
lines.push(` ${clean(value.description)}`);
|
|
1073
|
+
if (value.authentication)
|
|
1074
|
+
lines.push(row("Authentication", value.authentication, ctx));
|
|
1075
|
+
if (Array.isArray(value.required) && value.required.length)
|
|
1076
|
+
lines.push(row("Required", value.required.join(", "), ctx));
|
|
1077
|
+
const example = record(value.example);
|
|
1078
|
+
const meanings = record(value.value_semantics);
|
|
1079
|
+
if (Array.isArray(meanings?.fields))
|
|
1080
|
+
lines.push(row("Value meanings", `${meanings.fields.length} fields; use --json describe`, ctx));
|
|
1081
|
+
if (typeof example?.shell === "string") {
|
|
1082
|
+
lines.push(` ${ctx.color.dim("Example")}`);
|
|
1083
|
+
lines.push(` ${clean(example.shell)}`);
|
|
1084
|
+
}
|
|
1085
|
+
if (Array.isArray(value.constraints) && value.constraints.length)
|
|
1086
|
+
lines.push(` ${ctx.color.dim("Notes")} ${clip(value.constraints[0], ctx.width - 11)}`);
|
|
1087
|
+
lines.push(` ${ctx.color.dim("Use --json describe for schemas and every flag.")}`);
|
|
1088
|
+
return lines;
|
|
1089
|
+
}
|
|
1090
|
+
if (Array.isArray(value.commands)) {
|
|
1091
|
+
const rows = value.commands
|
|
1092
|
+
.map(record)
|
|
1093
|
+
.filter((item) => item !== undefined);
|
|
1094
|
+
const total = typeof value.total === "number" ? value.total : rows.length;
|
|
1095
|
+
const shown = rows.slice(0, 10);
|
|
1096
|
+
return [
|
|
1097
|
+
ctx.color.bold(countLabel(total, "command")),
|
|
1098
|
+
...(total > rows.length
|
|
1099
|
+
? [row("Showing", `${Math.min(rows.length, 10)} of ${total}`, ctx)]
|
|
1100
|
+
: []),
|
|
1101
|
+
...(ctx.width < 80
|
|
1102
|
+
? shown.flatMap((item) => [
|
|
1103
|
+
` ${ctx.color.bold(clean(item.command))}`,
|
|
1104
|
+
` ${ctx.color.dim(clip(item.description, ctx.width - 6))}`,
|
|
1105
|
+
])
|
|
1106
|
+
: table(shown, [
|
|
1107
|
+
{ label: "Command", value: (r) => r.command, max: 55, min: 52 },
|
|
1108
|
+
{
|
|
1109
|
+
label: "Description",
|
|
1110
|
+
value: (r) => r.description,
|
|
1111
|
+
max: 55,
|
|
1112
|
+
min: 20,
|
|
1113
|
+
},
|
|
1114
|
+
], ctx)),
|
|
1115
|
+
` ${ctx.color.dim("Search: sendbird commands --compact --search task words")}`,
|
|
1116
|
+
];
|
|
1117
|
+
}
|
|
1118
|
+
if (Array.isArray(value.workflows)) {
|
|
1119
|
+
const rows = value.workflows
|
|
1120
|
+
.map(record)
|
|
1121
|
+
.filter((item) => item !== undefined);
|
|
1122
|
+
return [
|
|
1123
|
+
ctx.color.bold(countLabel(rows.length, "workflow")),
|
|
1124
|
+
...table(rows, [
|
|
1125
|
+
{ label: "ID", value: (r) => r.id, max: 30 },
|
|
1126
|
+
{ label: "Purpose", value: (r) => r.title, max: 55 },
|
|
1127
|
+
], ctx),
|
|
1128
|
+
` ${ctx.color.dim("Inspect: sendbird workflows WORKFLOW_ID")}`,
|
|
1129
|
+
];
|
|
1130
|
+
}
|
|
1131
|
+
if (Array.isArray(value.steps) && typeof value.id === "string") {
|
|
1132
|
+
const steps = value.steps
|
|
1133
|
+
.map(record)
|
|
1134
|
+
.filter((item) => item !== undefined);
|
|
1135
|
+
const lines = [ctx.color.bold(clean(value.title ?? value.id))];
|
|
1136
|
+
if (value.description)
|
|
1137
|
+
lines.push(` ${clean(value.description)}`);
|
|
1138
|
+
lines.push(` ${ctx.color.dim("Plan only; no requests sent.")}`);
|
|
1139
|
+
for (const [index, step] of steps.entries()) {
|
|
1140
|
+
lines.push(` ${index + 1}. ${ctx.color.bold(clean(step.id))}`);
|
|
1141
|
+
if (Array.isArray(step.argv))
|
|
1142
|
+
lines.push(` ${commandLine(step.argv)}`);
|
|
1143
|
+
}
|
|
1144
|
+
return lines;
|
|
1145
|
+
}
|
|
1146
|
+
if (Array.isArray(value.results) && typeof value.query === "string") {
|
|
1147
|
+
const rows = value.results
|
|
1148
|
+
.map(record)
|
|
1149
|
+
.filter((item) => item !== undefined);
|
|
1150
|
+
const total = typeof value.total === "number" ? value.total : rows.length;
|
|
1151
|
+
const lines = [
|
|
1152
|
+
ctx.color.bold(rows.length
|
|
1153
|
+
? `${total} documentation results for “${clean(value.query)}”`
|
|
1154
|
+
: `No documentation results for “${clean(value.query)}”.`),
|
|
1155
|
+
];
|
|
1156
|
+
if (rows.length) {
|
|
1157
|
+
if (ctx.width < 90 || rows.some((item) => width(clean(item.id)) > 50))
|
|
1158
|
+
lines.push(...rows.flatMap((item) => [
|
|
1159
|
+
` ${ctx.color.bold(clean(item.title))}`,
|
|
1160
|
+
` ${ctx.color.dim("ID")} ${clean(item.id)}`,
|
|
1161
|
+
]));
|
|
1162
|
+
else
|
|
1163
|
+
lines.push(...table(rows, [
|
|
1164
|
+
{ label: "Title", value: (r) => r.title, max: 36 },
|
|
1165
|
+
{ label: "Section ID", value: (r) => r.id, max: 50 },
|
|
1166
|
+
], ctx));
|
|
1167
|
+
}
|
|
1168
|
+
if (typeof value.next_offset === "number")
|
|
1169
|
+
lines.push(row("More", `--offset ${value.next_offset}`, ctx));
|
|
1170
|
+
return lines;
|
|
1171
|
+
}
|
|
1172
|
+
}
|
|
1173
|
+
/** Bounded, semantic terminal rendering. Machine JSON never calls this module. */
|
|
1174
|
+
export function humanValue(value, options = {}) {
|
|
1175
|
+
if (typeof value === "string")
|
|
1176
|
+
return value;
|
|
1177
|
+
if (value === null || value === undefined)
|
|
1178
|
+
return "No data returned.";
|
|
1179
|
+
const ctx = view(options);
|
|
1180
|
+
if (Array.isArray(value)) {
|
|
1181
|
+
const rows = value.map(record).filter((item) => item !== undefined);
|
|
1182
|
+
return rows.length
|
|
1183
|
+
? table(rows.slice(0, 10), Object.keys(rows[0])
|
|
1184
|
+
.slice(0, 3)
|
|
1185
|
+
.map((key) => ({
|
|
1186
|
+
label: titleCase(key.replaceAll("_", " ")),
|
|
1187
|
+
value: (r) => r[key],
|
|
1188
|
+
})), ctx).join("\n")
|
|
1189
|
+
: "No items found.";
|
|
1190
|
+
}
|
|
1191
|
+
const data = record(redact(value, secretValues(value)));
|
|
1192
|
+
if (!data)
|
|
1193
|
+
return clean(value);
|
|
1194
|
+
if (data.ok === true &&
|
|
1195
|
+
typeof data.command === "string" &&
|
|
1196
|
+
typeof data.status === "string" &&
|
|
1197
|
+
"result" in data)
|
|
1198
|
+
return humanOperation(data, options);
|
|
1199
|
+
if (data.ok === true &&
|
|
1200
|
+
data.type === "event" &&
|
|
1201
|
+
typeof data.command === "string")
|
|
1202
|
+
return humanEvent(data.command.replace(/^sendbird /, ""), data.event, options);
|
|
1203
|
+
const specialized = discovery(data, ctx);
|
|
1204
|
+
if (specialized)
|
|
1205
|
+
return specialized.join("\n");
|
|
1206
|
+
const lines = [ctx.color.bold("Result")];
|
|
1207
|
+
for (const [key, item] of Object.entries(data).slice(0, 10)) {
|
|
1208
|
+
if (Array.isArray(item))
|
|
1209
|
+
lines.push(row(titleCase(key), countLabel(item.length, key.endsWith("s") ? key.slice(0, -1) : "item"), ctx));
|
|
1210
|
+
else if (record(item))
|
|
1211
|
+
lines.push(row(titleCase(key), `${Object.keys(record(item)).length} fields`, ctx));
|
|
1212
|
+
else
|
|
1213
|
+
lines.push(row(titleCase(key), item, ctx));
|
|
1214
|
+
}
|
|
1215
|
+
return lines.join("\n");
|
|
1216
|
+
}
|
|
1217
|
+
export function humanFailure(value, options = {}) {
|
|
1218
|
+
const ctx = view(options, true);
|
|
1219
|
+
value = record(redact(value, secretValues(value))) ?? {};
|
|
1220
|
+
const message = clean(value.message) || "Command failed";
|
|
1221
|
+
// Reasons and safe next actions must remain readable on narrow terminals.
|
|
1222
|
+
const lines = paragraph(message, ctx).map((line, index) => index === 0 ? `${ctx.color.red("✗")} ${ctx.color.bold(line.trim())}` : line);
|
|
1223
|
+
if (typeof value.command === "string")
|
|
1224
|
+
lines.push(row("Command", value.command, ctx));
|
|
1225
|
+
if (typeof value.code === "string")
|
|
1226
|
+
lines.push(row("Code", value.code, ctx));
|
|
1227
|
+
const details = record(value.details);
|
|
1228
|
+
if (typeof details?.status === "number")
|
|
1229
|
+
lines.push(row("HTTP", details.status, ctx));
|
|
1230
|
+
if (Array.isArray(value.details))
|
|
1231
|
+
for (const issue of value.details.slice(0, 3)) {
|
|
1232
|
+
const detail = record(issue);
|
|
1233
|
+
if (!detail)
|
|
1234
|
+
continue;
|
|
1235
|
+
const path = Array.isArray(detail.path) ? detail.path.join(".") : "Input";
|
|
1236
|
+
lines.push(row(clean(path || "Input"), detail.message, ctx));
|
|
1237
|
+
}
|
|
1238
|
+
if (value.outcome_unknown === true)
|
|
1239
|
+
lines.push(` ${ctx.color.yellow("!")} Outcome uncertain. Read the resource before retrying.`);
|
|
1240
|
+
const recovery = record(value.recovery);
|
|
1241
|
+
const recoveryTarget = record(recovery?.target);
|
|
1242
|
+
if (typeof value.mutation_state === "string")
|
|
1243
|
+
lines.push(row("Request", {
|
|
1244
|
+
not_attempted: "Change was not attempted",
|
|
1245
|
+
rejected: "Server rejected the change",
|
|
1246
|
+
unknown: "May have reached the server",
|
|
1247
|
+
acknowledged: "Server acknowledged; final result needs verification",
|
|
1248
|
+
}[value.mutation_state] ?? value.mutation_state, ctx));
|
|
1249
|
+
if (recoveryTarget) {
|
|
1250
|
+
if (recoveryTarget.profile)
|
|
1251
|
+
lines.push(row("Profile", recoveryTarget.profile, ctx));
|
|
1252
|
+
if (recoveryTarget.app_id)
|
|
1253
|
+
lines.push(row("App", recoveryTarget.app_id, ctx));
|
|
1254
|
+
if (recoveryTarget.actor_user_id)
|
|
1255
|
+
lines.push(row("Actor", recoveryTarget.actor_user_id, ctx));
|
|
1256
|
+
}
|
|
1257
|
+
if (typeof recovery?.note === "string")
|
|
1258
|
+
lines.push(...paragraph(recovery.note, ctx));
|
|
1259
|
+
if (recovery)
|
|
1260
|
+
lines.push(" A missing result is inconclusive. Do not repeat the change automatically.");
|
|
1261
|
+
const next = Array.isArray(value.next)
|
|
1262
|
+
? value.next.map(record).filter((item) => item !== undefined)
|
|
1263
|
+
: [];
|
|
1264
|
+
const describes = (item) => Array.isArray(item.argv) && item.argv.includes("describe");
|
|
1265
|
+
next.sort((a, b) => Number(describes(a)) - Number(describes(b)));
|
|
1266
|
+
const commands = next
|
|
1267
|
+
.slice(0, 2)
|
|
1268
|
+
.map((item) => item.argv)
|
|
1269
|
+
.filter((argv) => Array.isArray(argv) && argv.every((part) => typeof part === "string"));
|
|
1270
|
+
if (commands.length) {
|
|
1271
|
+
lines.push(` ${ctx.color.dim(commands.length === 1 ? "Next" : "Next steps")}`);
|
|
1272
|
+
for (const argv of commands)
|
|
1273
|
+
lines.push(` ${commandLine(argv)}`);
|
|
1274
|
+
}
|
|
1275
|
+
return lines.join("\n");
|
|
1276
|
+
}
|
|
1277
|
+
/** Keep observations and runtime conclusions separate in the terminal view. */
|
|
1278
|
+
function diagnosticLines(result, ctx) {
|
|
1279
|
+
const lines = [
|
|
1280
|
+
ctx.color.bold("Sendbird diagnostics"),
|
|
1281
|
+
row("Profile", result.profile, ctx),
|
|
1282
|
+
row("Environment", result.environment, ctx),
|
|
1283
|
+
row("Organization UID", result.org_id ?? "Not selected", ctx),
|
|
1284
|
+
row("App", result.app_id ?? "Not selected", ctx),
|
|
1285
|
+
row("Role", record(result.role)?.name ?? "Unknown", ctx),
|
|
1286
|
+
];
|
|
1287
|
+
const sub = record(result.subscription);
|
|
1288
|
+
lines.push(row("Chat plan", sub?.display_name ?? sub?.name ?? "Unknown", ctx));
|
|
1289
|
+
if (sub?.name)
|
|
1290
|
+
lines.push(row("Subscription", sub.name, ctx));
|
|
1291
|
+
const assessment = record(result.assessment);
|
|
1292
|
+
if (assessment) {
|
|
1293
|
+
lines.push(row("Command", assessment.command, ctx), row("Preflight", assessment.status === "allowed"
|
|
1294
|
+
? "Read verified"
|
|
1295
|
+
: assessment.status === "denied"
|
|
1296
|
+
? "Prerequisite denied"
|
|
1297
|
+
: "Needs verification", ctx));
|
|
1298
|
+
if (assessment.resource_id)
|
|
1299
|
+
lines.push(row("Resource", assessment.resource_id, ctx));
|
|
1300
|
+
}
|
|
1301
|
+
const checks = result.checks
|
|
1302
|
+
.map(record)
|
|
1303
|
+
.filter((r) => Boolean(r));
|
|
1304
|
+
lines.push("", ...table(checks, [
|
|
1305
|
+
{ label: "Check", value: (r) => r.id, max: 24 },
|
|
1306
|
+
{ label: "Status", value: (r) => r.status, min: 10, max: 10 },
|
|
1307
|
+
{ label: "Reason", value: (r) => r.reason, max: 64, min: 24 },
|
|
1308
|
+
], ctx));
|
|
1309
|
+
if (Array.isArray(result.features) && result.features.length) {
|
|
1310
|
+
const value = (x) => x === true ? "Yes" : x === false ? "No" : "?";
|
|
1311
|
+
lines.push("", ctx.color.bold("Feature evidence (runtime not tested)"), ...table(result.features.map(record).filter((r) => Boolean(r)), [
|
|
1312
|
+
{ label: "Feature", value: (r) => r.feature, max: 22 },
|
|
1313
|
+
{ label: "Plan", value: (r) => value(r.entitlement), max: 8 },
|
|
1314
|
+
{
|
|
1315
|
+
label: "Enabled API",
|
|
1316
|
+
value: (r) => value(r.reported_enabled),
|
|
1317
|
+
min: 13,
|
|
1318
|
+
max: 13,
|
|
1319
|
+
},
|
|
1320
|
+
{
|
|
1321
|
+
label: "Detail API",
|
|
1322
|
+
value: (r) => value(r.detail_snapshot),
|
|
1323
|
+
max: 12,
|
|
1324
|
+
},
|
|
1325
|
+
], ctx), ...paragraph("Plan = entitlement. API columns = reported settings. No does not establish runtime denial; Insight/runtime may need separate verification.", ctx));
|
|
1326
|
+
}
|
|
1327
|
+
if (Array.isArray(result.limits) && result.limits.length) {
|
|
1328
|
+
lines.push("", ctx.color.bold("Configured limits (source units)"), ...table(result.limits.map(record).filter((r) => Boolean(r)), [
|
|
1329
|
+
{ label: "Metric", value: (r) => r.metric, max: 28 },
|
|
1330
|
+
{ label: "Included", value: (r) => r.included_units ?? "?", max: 16 },
|
|
1331
|
+
{
|
|
1332
|
+
label: "Hard limit",
|
|
1333
|
+
value: (r) => r.configured_hard_limit ?? "?",
|
|
1334
|
+
max: 18,
|
|
1335
|
+
},
|
|
1336
|
+
], ctx), ...paragraph("These are plan configuration values, not remaining usage, a price quote, or a guarantee of unlimited API requests.", ctx));
|
|
1337
|
+
}
|
|
1338
|
+
const capacity = record(result.app_capacity);
|
|
1339
|
+
if (capacity)
|
|
1340
|
+
lines.push(row("Apps", `${capacity.count ?? "?"} / ${capacity.maximum ?? "?"}`, ctx));
|
|
1341
|
+
if (Array.isArray(result.regions) && result.regions.length)
|
|
1342
|
+
lines.push("", ctx.color.bold("Organization regions"), ...table(result.regions.map(record).filter((r) => Boolean(r)), [
|
|
1343
|
+
{ label: "Region", value: (r) => r.id, max: 22 },
|
|
1344
|
+
{ label: "Family", value: (r) => r.family, max: 12 },
|
|
1345
|
+
{ label: "Create", value: (r) => r.available ?? "?", max: 10 },
|
|
1346
|
+
{ label: "Calls", value: (r) => r.calls ?? "?", max: 10 },
|
|
1347
|
+
], ctx));
|
|
1348
|
+
lines.push("", ...paragraph("Read-only diagnosis: no feature activation, SDK connection or mutation probe. Use --json for full evidence and unresolved conditions.", ctx));
|
|
1349
|
+
return lines;
|
|
1350
|
+
}
|
|
1351
|
+
//# sourceMappingURL=human-output.js.map
|