@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,846 @@
|
|
|
1
|
+
import { relayMethodDescription } from "./calls-catalog.js";
|
|
2
|
+
import { valueSemantics } from "./value-semantics.js";
|
|
3
|
+
import { shellCommand } from "./shell.js";
|
|
4
|
+
import { skillSchema } from "./skills.js";
|
|
5
|
+
import { doctorSchema } from "./diagnostics.js";
|
|
6
|
+
import { readbackPlan } from "./recovery.js";
|
|
7
|
+
import { sdkConfigSchema } from "./sdk-config.js";
|
|
8
|
+
import { definitions, jsonSchema, exampleFor, } from "./catalog.js";
|
|
9
|
+
import { sdkDefinitions, sdkExample, } from "./sdk-catalog.js";
|
|
10
|
+
import { localCommands, setupSchema } from "./local.js";
|
|
11
|
+
import { CliError } from "./core.js";
|
|
12
|
+
import { VERSION } from "./version.js";
|
|
13
|
+
import { stdoutSchema } from "./presentation.js";
|
|
14
|
+
export const introspectionNames = [
|
|
15
|
+
"agent",
|
|
16
|
+
"help",
|
|
17
|
+
"describe",
|
|
18
|
+
"workflows",
|
|
19
|
+
"commands",
|
|
20
|
+
"docs search",
|
|
21
|
+
"docs read",
|
|
22
|
+
];
|
|
23
|
+
export function commandNodes(root) {
|
|
24
|
+
const rows = [];
|
|
25
|
+
function walk(c, prefix = "") {
|
|
26
|
+
for (const child of c.commands) {
|
|
27
|
+
const path = [prefix, child.name()].filter(Boolean).join(" ");
|
|
28
|
+
if (child.commands.length) {
|
|
29
|
+
if (localCommands.some((d) => d.name === path))
|
|
30
|
+
rows.push({ path, command: child });
|
|
31
|
+
walk(child, path);
|
|
32
|
+
}
|
|
33
|
+
else
|
|
34
|
+
rows.push({ path, command: child });
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
walk(root);
|
|
38
|
+
return rows;
|
|
39
|
+
}
|
|
40
|
+
export function definitionFor(path) {
|
|
41
|
+
return [...definitions, ...sdkDefinitions].find((d) => d.name === path);
|
|
42
|
+
}
|
|
43
|
+
export function transportFor(path) {
|
|
44
|
+
const d = definitionFor(path);
|
|
45
|
+
return d?.method === "SDK"
|
|
46
|
+
? "sdk"
|
|
47
|
+
: d ||
|
|
48
|
+
[
|
|
49
|
+
"app sdk-config",
|
|
50
|
+
"auth status",
|
|
51
|
+
"doctor",
|
|
52
|
+
"auth login",
|
|
53
|
+
"auth signup",
|
|
54
|
+
"auth verify-email",
|
|
55
|
+
"auth resend-verification",
|
|
56
|
+
"app use",
|
|
57
|
+
"org use",
|
|
58
|
+
"org region list",
|
|
59
|
+
"org region use",
|
|
60
|
+
].includes(path)
|
|
61
|
+
? "dashboard"
|
|
62
|
+
: "local";
|
|
63
|
+
}
|
|
64
|
+
export function effectFor(path) {
|
|
65
|
+
if (path === "docs read")
|
|
66
|
+
return "read";
|
|
67
|
+
const d = definitionFor(path);
|
|
68
|
+
if (d?.method === "SDK")
|
|
69
|
+
return d.stream
|
|
70
|
+
? "stream"
|
|
71
|
+
: d.mutates
|
|
72
|
+
? "mutation"
|
|
73
|
+
: "sdk-read";
|
|
74
|
+
if (d)
|
|
75
|
+
return d.method === "GET" ? "read" : "mutation";
|
|
76
|
+
return [
|
|
77
|
+
"profile create",
|
|
78
|
+
"profile use",
|
|
79
|
+
"profile delete",
|
|
80
|
+
"org use",
|
|
81
|
+
"org clear",
|
|
82
|
+
"app use",
|
|
83
|
+
"app clear",
|
|
84
|
+
"project init",
|
|
85
|
+
"auth login",
|
|
86
|
+
"auth signup",
|
|
87
|
+
"auth verify-email",
|
|
88
|
+
"auth resend-verification",
|
|
89
|
+
"org region use",
|
|
90
|
+
"auth logout",
|
|
91
|
+
"setup",
|
|
92
|
+
"skills install",
|
|
93
|
+
"skills remove",
|
|
94
|
+
].includes(path)
|
|
95
|
+
? "local/auth mutation"
|
|
96
|
+
: ["auth status", "doctor", "app sdk-config", "org region list"].includes(path)
|
|
97
|
+
? "read"
|
|
98
|
+
: "offline";
|
|
99
|
+
}
|
|
100
|
+
export function agentGuide() {
|
|
101
|
+
return {
|
|
102
|
+
protocol_version: 1,
|
|
103
|
+
version: VERSION,
|
|
104
|
+
entrypoint: "sendbird --json agent",
|
|
105
|
+
purpose: "Sendbird administration and Chat SDK operations.",
|
|
106
|
+
documentation: {
|
|
107
|
+
repository: "https://github.com/sendbird/sendbird-tools-docs",
|
|
108
|
+
},
|
|
109
|
+
first_rules: [
|
|
110
|
+
"Requests are instructions: a named actor plus parent message plus reply text means post that reply now (an empty thread is the starting state); never ask for identifiers already given.",
|
|
111
|
+
"A mute/freeze check ends with a recovery send by the restricted member after restore; the request authorizes that probe.",
|
|
112
|
+
"Search by task verb (commands --search translate|pin|reply|scheduled|poll) before calling an operation unsupported; never substitute model output for a CLI operation.",
|
|
113
|
+
"Operator-only SDK actions without a named actor: use a channel operator from chat channel members list as --as-user.",
|
|
114
|
+
"Future default target = org use + app use (saved profile); repository binding = project init --yes (./sendbird.json).",
|
|
115
|
+
"Diagnosis-only requests: reads and doctor only; report auth login or changes as steps, do not run them.",
|
|
116
|
+
"Reads for another org/app: run them with that exact --org-id/--app-id; on rejection report inaccessible and stop, never substitute the authenticated org's data.",
|
|
117
|
+
"scheduled_at is Unix epoch milliseconds.",
|
|
118
|
+
"Customer code for a staging app must pass the hosts from app sdk-config --raw (customApiHost/customWebSocketHost); default SDK hosts reach production only.",
|
|
119
|
+
],
|
|
120
|
+
onboarding: {
|
|
121
|
+
skill: "sendbird-setup",
|
|
122
|
+
guide: "guide/official/plugins/cli/index#intro",
|
|
123
|
+
purpose: "Optional sendbird-setup Skill: inspect/reuse installation and auth, confirm an existing org/app, then verify an authorized first task. CLI-only registration installs the operational sendbird Skill. Installing a plugin is not authentication or runtime verification.",
|
|
124
|
+
hosts: ["Claude Code", "Codex"],
|
|
125
|
+
},
|
|
126
|
+
skill_registration: {
|
|
127
|
+
inspect: ["sendbird", "skills", "status"],
|
|
128
|
+
read_now: ["sendbird", "--json", "skills", "show"],
|
|
129
|
+
install: {
|
|
130
|
+
claude: [
|
|
131
|
+
"sendbird",
|
|
132
|
+
"skills",
|
|
133
|
+
"install",
|
|
134
|
+
"--agent",
|
|
135
|
+
"claude",
|
|
136
|
+
"--scope",
|
|
137
|
+
"project",
|
|
138
|
+
],
|
|
139
|
+
codex: [
|
|
140
|
+
"sendbird",
|
|
141
|
+
"skills",
|
|
142
|
+
"install",
|
|
143
|
+
"--agent",
|
|
144
|
+
"codex",
|
|
145
|
+
"--scope",
|
|
146
|
+
"project",
|
|
147
|
+
],
|
|
148
|
+
},
|
|
149
|
+
note: "Offline CLI Skill only; current host loading/selection is unverified. Client plugins use their official installation flow.",
|
|
150
|
+
},
|
|
151
|
+
value_meanings: "Numeric enum meanings are available in describe.value_semantics and --response-schema x-sendbird-value-semantics. --json and --raw preserve original wire values; do not guess undocumented codes.",
|
|
152
|
+
offline_discovery: "agent, commands, describe and workflows do not read saved profiles, credentials or remote state. They are already offline; --dry-run is accepted but has no effect on them.",
|
|
153
|
+
project_marker: {
|
|
154
|
+
file: "./sendbird.json",
|
|
155
|
+
purpose: "Optional visible, non-secret app context for a coding project and its Agents.",
|
|
156
|
+
inspect: ["sendbird", "project", "status"],
|
|
157
|
+
create: ["sendbird", "--app-id", "APP_ID", "project", "init", "--yes"],
|
|
158
|
+
first_step: ["sendbird", "--dry-run", "agent"],
|
|
159
|
+
precedence: "The marker is read only with --project. Explicit --profile/--org-id/--app-id flags override its app, organization and region context.",
|
|
160
|
+
},
|
|
161
|
+
discovery: [
|
|
162
|
+
{
|
|
163
|
+
argv: [
|
|
164
|
+
"sendbird",
|
|
165
|
+
"--json",
|
|
166
|
+
"commands",
|
|
167
|
+
"--compact",
|
|
168
|
+
"--search",
|
|
169
|
+
"message send",
|
|
170
|
+
],
|
|
171
|
+
purpose: "Find a small set of operations",
|
|
172
|
+
},
|
|
173
|
+
{
|
|
174
|
+
argv: ["sendbird", "--json", "describe", "chat", "message", "send"],
|
|
175
|
+
purpose: "Exact flags, authentication, constraints, input/output and dry-run argv",
|
|
176
|
+
},
|
|
177
|
+
{
|
|
178
|
+
argv: ["sendbird", "--json", "workflows"],
|
|
179
|
+
purpose: "Choose a declarative, non-executing task recipe",
|
|
180
|
+
},
|
|
181
|
+
{
|
|
182
|
+
argv: ["sendbird", "--json", "docs", "search", "group chat"],
|
|
183
|
+
purpose: "Find a guide or tutorial section without loading the docs site",
|
|
184
|
+
},
|
|
185
|
+
],
|
|
186
|
+
authentication: {
|
|
187
|
+
dashboard: {
|
|
188
|
+
credential: "Environment-bound CLI OAuth Bearer",
|
|
189
|
+
check: [
|
|
190
|
+
"sendbird",
|
|
191
|
+
"--json",
|
|
192
|
+
"--profile",
|
|
193
|
+
"PROFILE",
|
|
194
|
+
"auth",
|
|
195
|
+
"status",
|
|
196
|
+
"--summary",
|
|
197
|
+
],
|
|
198
|
+
login: ["sendbird", "--profile", "PROFILE", "auth", "login"],
|
|
199
|
+
note: "auth login opens browser OAuth for the selected environment. A human must approve it; never request tokens, passwords or cookies in the conversation.",
|
|
200
|
+
},
|
|
201
|
+
sdk: {
|
|
202
|
+
credential: "Existing Chat user access token read into memory through CLI OAuth, or explicit token file",
|
|
203
|
+
required: [
|
|
204
|
+
"--app-id",
|
|
205
|
+
"--as-user",
|
|
206
|
+
"CLI OAuth login, or SDK-command --user-token-file PATH (private token file; check with chat sdk status)",
|
|
207
|
+
],
|
|
208
|
+
token_file: [
|
|
209
|
+
"sendbird",
|
|
210
|
+
"--profile",
|
|
211
|
+
"PROFILE",
|
|
212
|
+
"--app-id",
|
|
213
|
+
"APP_ID",
|
|
214
|
+
"chat",
|
|
215
|
+
"sdk",
|
|
216
|
+
"status",
|
|
217
|
+
"--user-token-file",
|
|
218
|
+
"PRIVATE_TOKEN_FILE",
|
|
219
|
+
"--as-user",
|
|
220
|
+
"USER_ID",
|
|
221
|
+
],
|
|
222
|
+
staging: [
|
|
223
|
+
"sendbird",
|
|
224
|
+
"--profile",
|
|
225
|
+
"PROFILE",
|
|
226
|
+
"--app-id",
|
|
227
|
+
"APP_ID",
|
|
228
|
+
"app",
|
|
229
|
+
"sdk-config",
|
|
230
|
+
"--raw",
|
|
231
|
+
],
|
|
232
|
+
note: "Automatic Chat tokens always validate Gate's app/environment/hosts before retrieval, including host overrides. Only explicit token-file/guest credentials with explicit hosts skip discovery; staging otherwise discovers hosts. Never use management credentials. Guest access is explicit and may create a user.",
|
|
233
|
+
},
|
|
234
|
+
},
|
|
235
|
+
contract: {
|
|
236
|
+
stdout: "Default stdout is a bounded readable view. Add --json for full unstyled JSON envelopes {ok, command, effect, status, target, result, response}; watch then emits NDJSON events and completion. Status: preview=no request, accepted=write acknowledged (read back), verified=read completed, completed=stream ended, succeeded=local. --raw preserves legacy JSON result/event bodies and is required for SDK config files.",
|
|
237
|
+
stderr: "Readable errors by default; --json returns structured errors with recovery suggestions. Unknown means a write may have happened.",
|
|
238
|
+
input: "Typed flags or --body-file; flags win. stdin is read only for --body-file -.",
|
|
239
|
+
mutations: "Preview with --dry-run; destructive operations require --yes. Stay within the user-authorized target.",
|
|
240
|
+
retry: "No CLI automatic mutation retry. Timeout is an uncertain outcome; read back first.",
|
|
241
|
+
context: "Explicit --profile/--org-id/--app-id override saved context. Default profile is prod; confirm the intended environment.",
|
|
242
|
+
placeholders: "UPPERCASE values in templates must be replaced with actual task bindings.",
|
|
243
|
+
},
|
|
244
|
+
known_limits: [
|
|
245
|
+
"Browser signup and OAuth callback use the selected environment where Gate enables CLI OAuth. Email verification is a separate step.",
|
|
246
|
+
"The shared sendbird/sendbird-plugins marketplace owns one catalog read by both hosts; this repository/npm package ships plugin manifests and skills, not marketplace catalogs. Local candidate catalogs use sendbird-cli-local. npm installation alone does not register Skills. No hosted MCP is provided.",
|
|
247
|
+
"Supergroup and reactions depend on app entitlements. A successful read-mark acknowledgement is not proof of updated receipts.",
|
|
248
|
+
],
|
|
249
|
+
};
|
|
250
|
+
}
|
|
251
|
+
export async function compactCommands(root, opts) {
|
|
252
|
+
const limit = Number(opts.limit ?? 20), offset = Number(opts.offset ?? 0);
|
|
253
|
+
if (!Number.isInteger(limit) ||
|
|
254
|
+
limit < 1 ||
|
|
255
|
+
limit > 100 ||
|
|
256
|
+
!Number.isInteger(offset) ||
|
|
257
|
+
offset < 0)
|
|
258
|
+
throw new CliError("E_INPUT", "Discovery limit must be 1–100 and offset a nonnegative integer", 2);
|
|
259
|
+
if (opts.transport && !["dashboard", "sdk", "local"].includes(opts.transport))
|
|
260
|
+
throw new CliError("E_INPUT", "Transport must be dashboard, sdk or local", 2);
|
|
261
|
+
const rows = commandNodes(root).filter((r) => !opts.transport || transportFor(r.path) === opts.transport);
|
|
262
|
+
let matches = rows;
|
|
263
|
+
const reasons = new Map();
|
|
264
|
+
if (opts.search?.trim()) {
|
|
265
|
+
const [{ createSearchIndex }, { commandIntents }] = await Promise.all([
|
|
266
|
+
import("./search-engine.js"),
|
|
267
|
+
import("./search-intents.js"),
|
|
268
|
+
]);
|
|
269
|
+
if (opts.search.length > 256)
|
|
270
|
+
throw new CliError("E_INPUT", "Search query must be at most 256 characters", 2);
|
|
271
|
+
const index = createSearchIndex(rows.map((r) => ({
|
|
272
|
+
id: r.path,
|
|
273
|
+
title: r.path,
|
|
274
|
+
description: r.command.description(),
|
|
275
|
+
intents: commandIntents(r.path),
|
|
276
|
+
})));
|
|
277
|
+
const hits = index.search(opts.search, rows.length);
|
|
278
|
+
matches = hits.map((hit) => {
|
|
279
|
+
reasons.set(hit.document.id, {
|
|
280
|
+
match_type: hit.match_type,
|
|
281
|
+
matched_terms: hit.matched_terms,
|
|
282
|
+
});
|
|
283
|
+
return rows.find((r) => r.path === hit.document.id);
|
|
284
|
+
});
|
|
285
|
+
}
|
|
286
|
+
return {
|
|
287
|
+
version: VERSION,
|
|
288
|
+
total: matches.length,
|
|
289
|
+
offset,
|
|
290
|
+
limit,
|
|
291
|
+
next_offset: offset + limit < matches.length ? offset + limit : null,
|
|
292
|
+
commands: matches.slice(offset, offset + limit).map((r) => ({
|
|
293
|
+
command: `sendbird ${r.path}`,
|
|
294
|
+
description: r.command.description(),
|
|
295
|
+
transport: transportFor(r.path),
|
|
296
|
+
effect: effectFor(r.path),
|
|
297
|
+
inspect: ["sendbird", "describe", ...r.path.split(" ")],
|
|
298
|
+
...(reasons.has(r.path) ? { match: reasons.get(r.path) } : {}),
|
|
299
|
+
})),
|
|
300
|
+
...(matches.length
|
|
301
|
+
? {}
|
|
302
|
+
: {
|
|
303
|
+
next: [
|
|
304
|
+
["sendbird", "commands", "--compact"],
|
|
305
|
+
["sendbird", "workflows"],
|
|
306
|
+
],
|
|
307
|
+
}),
|
|
308
|
+
};
|
|
309
|
+
}
|
|
310
|
+
export function exampleArgv(d) {
|
|
311
|
+
const e = d.method === "SDK" ? sdkExample(d) : exampleFor(d);
|
|
312
|
+
const argv = ["sendbird", ...d.name.split(" ")];
|
|
313
|
+
if (d.scope === "app")
|
|
314
|
+
argv.push("--app-id", "APP_ID");
|
|
315
|
+
if (d.scope === "org" || d.name === "app feature set" || d.requiresOrg)
|
|
316
|
+
argv.push("--org-id", "ORG_UID");
|
|
317
|
+
if (d.method === "SDK")
|
|
318
|
+
argv.push("--as-user", "USER_ID");
|
|
319
|
+
let privateBody = false;
|
|
320
|
+
for (const part of ["params", "query", "body"]) {
|
|
321
|
+
const values = e[part];
|
|
322
|
+
for (const [k, v] of Object.entries(values)) {
|
|
323
|
+
if ((part === "body" && d.privateFields?.includes(k)) ||
|
|
324
|
+
["password", "api_token", "access_token", "access_code"].includes(k)) {
|
|
325
|
+
privateBody = true;
|
|
326
|
+
continue;
|
|
327
|
+
}
|
|
328
|
+
argv.push("--" + k.replace(/_+/g, "-").toLowerCase(), typeof v === "string" ? v : JSON.stringify(v));
|
|
329
|
+
}
|
|
330
|
+
}
|
|
331
|
+
if (privateBody)
|
|
332
|
+
argv.push("--body-file", "PRIVATE_BODY_FILE");
|
|
333
|
+
if ([
|
|
334
|
+
"calls push create",
|
|
335
|
+
"calls push update",
|
|
336
|
+
"chat push create",
|
|
337
|
+
"chat push update",
|
|
338
|
+
].includes(d.name))
|
|
339
|
+
argv.push("--credential-file", "PRIVATE_CREDENTIAL_FILE");
|
|
340
|
+
argv.push("--dry-run");
|
|
341
|
+
return {
|
|
342
|
+
argv,
|
|
343
|
+
shell: shellCommand(argv, /^[a-zA-Z0-9_./:=+-]+$/),
|
|
344
|
+
input: e,
|
|
345
|
+
...(privateBody ? { private_body_file: e.body } : {}),
|
|
346
|
+
note: "Template only. Replace identifiers; dry-run does not validate remote existence. Automatic Chat tokens always validate the authoritative SDK route, including host overrides. Explicit token-file/guest credentials skip discovery only with explicit hosts; staging credentials without hosts discover them.",
|
|
347
|
+
};
|
|
348
|
+
}
|
|
349
|
+
function inputSchema(schema) {
|
|
350
|
+
const value = jsonSchema(schema);
|
|
351
|
+
const properties = (value.properties ?? {});
|
|
352
|
+
return {
|
|
353
|
+
...value,
|
|
354
|
+
required: (value.required ?? []).filter((key) => !Object.hasOwn(properties[key], "default")),
|
|
355
|
+
};
|
|
356
|
+
}
|
|
357
|
+
export function describeCommand(root, path) {
|
|
358
|
+
path = path.replace(/^sendbird\s+/, "").trim();
|
|
359
|
+
const row = commandNodes(root).find((r) => r.path === path);
|
|
360
|
+
if (!row)
|
|
361
|
+
throw new CliError("E_DISCOVERY", "Unknown command path. Search with sendbird commands --compact --search WORDS.", 2);
|
|
362
|
+
const d = definitionFor(path);
|
|
363
|
+
const sdk = d?.method === "SDK";
|
|
364
|
+
const seenFlags = new Set();
|
|
365
|
+
const flags = [
|
|
366
|
+
...row.command.options,
|
|
367
|
+
...root.options.filter((o) => o.long !== "--version"),
|
|
368
|
+
]
|
|
369
|
+
.filter((o) => {
|
|
370
|
+
if (!o.long || seenFlags.has(o.long))
|
|
371
|
+
return false;
|
|
372
|
+
seenFlags.add(o.long);
|
|
373
|
+
return true;
|
|
374
|
+
})
|
|
375
|
+
.map((o) => ({
|
|
376
|
+
flag: o.long,
|
|
377
|
+
syntax: o.flags,
|
|
378
|
+
description: o.description,
|
|
379
|
+
takes_value: o.required || o.optional,
|
|
380
|
+
}));
|
|
381
|
+
const required = [];
|
|
382
|
+
for (const part of ["params", "query", "body"]) {
|
|
383
|
+
const schema = inputSchema(d?.[part]);
|
|
384
|
+
for (const key of schema.required)
|
|
385
|
+
if (!(part === "body" && d?.privateFields?.includes(key)) &&
|
|
386
|
+
!["password", "api_token", "access_code"].includes(key))
|
|
387
|
+
required.push("--" + key.replace(/_+/g, "-").toLowerCase());
|
|
388
|
+
}
|
|
389
|
+
if (d?.scope === "app")
|
|
390
|
+
required.push("--app-id (or saved app)");
|
|
391
|
+
if (d?.scope === "org" || path === "app feature set" || d?.requiresOrg)
|
|
392
|
+
required.push("--org-id (or saved org)");
|
|
393
|
+
if (sdk)
|
|
394
|
+
required.push("--as-user", "CLI OAuth login (or explicit --user-token-file / --allow-guest)");
|
|
395
|
+
const constraints = [];
|
|
396
|
+
if (path === "setup")
|
|
397
|
+
constraints.push("Interactive only. Reviews the selected profile/environment, organization, app and region before asking to save defaults. Saving also activates the profile. Skipping/cancelling does not persist selections; login and any resources created earlier remain, and reauthentication can reset old account context. Returns defaults_saved and explicit next_argv. A default region is for future app creation, not app migration.");
|
|
398
|
+
if (path.startsWith("app feature "))
|
|
399
|
+
constraints.push("Feature activation is distinct from subscription entitlement and runtime readiness. set requires --yes and a bound active self-service org; Gate enforces application.settings.all and plan eligibility. Data Export, Desk and AI Engine are excluded. Enabling advanced_profanity_filter also disables profanity_filter. Message Search observes region/migration locks; its preflight status GET can reset a failed non-live pipeline and is never used by list/doctor/dry-run. Accepted writes include readback; do not replay on mismatch or failed readback. Usage charges may apply under the existing plan; no subscription purchase occurs.");
|
|
400
|
+
if (/^chat (settings|webhook|announcement|push|analytics|moderation) /.test(path) ||
|
|
401
|
+
/^app (delete|ip-allowlist|partition)/.test(path) ||
|
|
402
|
+
path.startsWith("org sso "))
|
|
403
|
+
constraints.push("Pro operations use Gate OAuth and Dashboard role permissions; no management-key fallback. Mutations require the original organization and explicit --yes. Read before changing settings; dry-run validates local inputs but proves neither eligibility nor runtime behavior. See docs search for Pro feature workflows.");
|
|
404
|
+
if (path === "app delete")
|
|
405
|
+
constraints.push("Irreversible app deletion requires an exact --confirm-app-name. Calls deregistration and backend deletion may partially succeed; inspect both before any retry.");
|
|
406
|
+
if (path === "chat push enabled set")
|
|
407
|
+
constraints.push("--confirm-shared-push true acknowledges that this switch affects both Chat and Calls. Credential registration never enables it automatically.");
|
|
408
|
+
if (path.startsWith("org sso "))
|
|
409
|
+
constraints.push("Certificate/IdP URL use private body input and masked output. set/delete require an exact organization name and --yes. Enforcement also needs --login-tested true; this is the user's assertion, not a verified login. Review member impact and recovery before execution.");
|
|
410
|
+
if (/^app ip-allowlist (add|remove)$/.test(path))
|
|
411
|
+
constraints.push("Confirm the app name and current source IP. Requests that would exclude that confirmed IP are rejected. The CLI does not discover or verify the network egress IP; preserve an authorized recovery path.");
|
|
412
|
+
if (path.startsWith("chat sdk poll "))
|
|
413
|
+
constraints.push("Poll create prepares an unattached poll. Attach with chat message send --poll-id; the two commands are not atomic. Preserve a known poll_id and do not recreate a poll after an uncertain result.");
|
|
414
|
+
if (path.startsWith("chat sdk report "))
|
|
415
|
+
constraints.push("A report is a real, non-retractable moderation submission. --yes is required; no CLI processing-status lookup is available. Do not submit test reports outside authorized resources.");
|
|
416
|
+
if (path.startsWith("chat sdk user device "))
|
|
417
|
+
constraints.push("Use private --body-file for device_token. Registration preserves other devices (unique=false); pending does not prove registration or notification delivery.");
|
|
418
|
+
if (path.startsWith("calls "))
|
|
419
|
+
constraints.push("Dashboard role permissions and Calls availability remain server-enforced. No calls are placed or credits purchased.");
|
|
420
|
+
if (/^calls settings general (get|set)$/.test(path))
|
|
421
|
+
constraints.push(relayMethodDescription);
|
|
422
|
+
if (path.startsWith("calls credits "))
|
|
423
|
+
constraints.push("Voucher reads and trial claims are bound to the authenticated organization. claim-trial requires OWNER, a self-service organization, no voucher history (including expired/depleted), --yes or explicit terminal confirmation (default No); JSON/raw and non-interactive use require --yes. Developer plans are blocked. Amount and duration are server-defined; no payment or auto-renewal is requested. A 400 may follow partial voucher creation; read calls credits list before any recovery.");
|
|
424
|
+
if (path === "calls activate")
|
|
425
|
+
constraints.push("Requires organization.applications.all, the original org/app and --yes. Uses the app's current region, never moves it. Trial consent is separate: --claim-trial requests the follow-up; --yes alone never authorizes credits. Eligible human terminals offer a default-No prompt; --no-claim-trial skips it. JSON/raw and non-TTY execution never offer it. Inspect calls_trial for the independent result; never repeat activation to recover credit errors. Readback can lag; inspect calls settings push get before retrying.");
|
|
426
|
+
if ([
|
|
427
|
+
"calls push create",
|
|
428
|
+
"calls push update",
|
|
429
|
+
"chat push create",
|
|
430
|
+
"chat push update",
|
|
431
|
+
].includes(path))
|
|
432
|
+
constraints.push("APNs creation and FCM require --credential-file. Same-format APNs metadata updates may reuse the existing certificate after paginated lookup; format changes require a file. APNs format/environment and p8 identifiers are required; Huawei uses --body-file for its secret. No credentials are written to CLI config.");
|
|
433
|
+
if (path.startsWith("calls ") &&
|
|
434
|
+
d?.method !== "GET" &&
|
|
435
|
+
path !== "calls credits claim-trial")
|
|
436
|
+
constraints.push("--yes required. Writes are acknowledged; inspect readback. Failed readback must not trigger a repeated write.");
|
|
437
|
+
if (path === "app create")
|
|
438
|
+
constraints.push("Calls registration requires --is-calls-enabled true and --yes. Select the SDK family with --is-delight-region; Calls region/subscription eligibility remains server-enforced. After confirmed activation, --claim-trial explicitly authorizes a free trial; otherwise eligible human terminals offer a default-No prompt. --no-claim-trial skips it. --yes alone, JSON/raw and non-TTY runs do not automatically claim. Credit follow-up failure preserves app creation success; inspect calls_trial and do not recreate the app. No paid credit purchase is requested. Read back the created app and Calls settings; a created Chat app is not proof Calls registration succeeded.");
|
|
439
|
+
if (path === "chat channel create")
|
|
440
|
+
constraints.push("Group/Supergroup require user_ids; Open rejects group-only options.", "Supergroup cannot be distinct. App features and quotas remain server-enforced.");
|
|
441
|
+
if (path === "chat channel list" || path === "chat channel search")
|
|
442
|
+
constraints.push("Open supports --url-contains; Group/Supergroup use --channel-urls for exact URLs. The type selects the server's super_mode filter.");
|
|
443
|
+
if (path === "chat message file")
|
|
444
|
+
constraints.push("Exactly one of --file and --file-url. Local files <=25 MiB; URLs use HTTPS.");
|
|
445
|
+
if (path === "chat message list")
|
|
446
|
+
constraints.push("Use either --before-id or --before, not both. Continue using next_before_id.");
|
|
447
|
+
if (path === "chat message thread")
|
|
448
|
+
constraints.push("Timestamp pages may overlap; deduplicate by message_id. Not a complete export.");
|
|
449
|
+
if (d?.danger)
|
|
450
|
+
constraints.push("--yes required for actual execution; never treat this as permission to expand scope.");
|
|
451
|
+
if (sdk)
|
|
452
|
+
constraints.push("Automatic Chat tokens always validate authoritative app/environment/SDK hosts before token retrieval; --sdk-config-file or explicit hosts must match and cannot bypass discovery or its failures. Explicit user-token-file/guest credentials with explicit hosts skip discovery; staging credentials without hosts still discover them. Previews stay offline.", "SDK reads may temporarily enter Open channels. Every invocation disconnects afterward.");
|
|
453
|
+
if ([
|
|
454
|
+
"chat sdk channel my list",
|
|
455
|
+
"chat channel participants list",
|
|
456
|
+
"chat sdk channel members list",
|
|
457
|
+
"chat sdk message pins list",
|
|
458
|
+
"chat sdk message scheduled list",
|
|
459
|
+
"chat sdk poll list",
|
|
460
|
+
].includes(path))
|
|
461
|
+
constraints.push("Start with --page 1 (maximum 100) and follow result.next_page. Keep limit and filters fixed; later pages replay earlier SDK query pages and may shift if data changes. At the page ceiling, next_page is null even when more results exist; inspect pagination and narrow the query.");
|
|
462
|
+
const localRequired = {
|
|
463
|
+
"docs search": ["<query...>"],
|
|
464
|
+
"docs read": ["<id>"],
|
|
465
|
+
"app sdk-config": ["--app-id (or saved app)"],
|
|
466
|
+
"skills install": ["--agent"],
|
|
467
|
+
"skills remove": ["--agent", "--yes (unless --dry-run)"],
|
|
468
|
+
"profile create": ["--name", "--env"],
|
|
469
|
+
"profile use": ["--name"],
|
|
470
|
+
"profile delete": ["--name", "--yes"],
|
|
471
|
+
"org use": ["--id"],
|
|
472
|
+
"org region use": ["--id"],
|
|
473
|
+
"app use": ["--id"],
|
|
474
|
+
};
|
|
475
|
+
required.push(...(localRequired[path] ?? []));
|
|
476
|
+
if (path === "profile create")
|
|
477
|
+
constraints.push("--env is prod or staging. A profile stores defaults; it does not authenticate.");
|
|
478
|
+
if (path === "profile delete")
|
|
479
|
+
constraints.push("Requires --yes. Revokes any saved OAuth grant and removes the local profile; deleting the active profile selects a remaining profile.");
|
|
480
|
+
if (path === "auth login")
|
|
481
|
+
constraints.push("Browser OAuth uses an environment-bound Gate issuer and loopback callback. Gate must advertise and enable CLI OAuth there. A human must approve in a browser; this is not a CI/headless runner grant.");
|
|
482
|
+
if (path === "auth signup")
|
|
483
|
+
constraints.push("Opens the selected environment's Dashboard signup with an OAuth next URL; the person enters details and accepts terms in the browser. After approval, the loopback callback resumes CLI setup. Email verification is separate.");
|
|
484
|
+
if (path === "auth verify-email")
|
|
485
|
+
constraints.push("Requires the signed-in account and a private --token-file containing the emailed link or token. The token is never returned in stdout. --dry-run sends no request.");
|
|
486
|
+
if (path === "app create")
|
|
487
|
+
constraints.push("Pass --region or select one with org region use. Use org region list to see allowed Chat regions.");
|
|
488
|
+
if (path === "docs read")
|
|
489
|
+
constraints.push("Reads only a section ID from the installed index; needs a matching docs server. Start npm run docs in a source checkout or set --base-url/SENDBIRD_DOCS_URL.", "Remote content is bounded to 24 KB and checked against the installed index digest.");
|
|
490
|
+
const local = localCommands.find((l) => l.name === path);
|
|
491
|
+
if (local &&
|
|
492
|
+
!path.startsWith("skills ") &&
|
|
493
|
+
![
|
|
494
|
+
"profile",
|
|
495
|
+
"profile list",
|
|
496
|
+
"project status",
|
|
497
|
+
"auth verify-email",
|
|
498
|
+
"auth resend-verification",
|
|
499
|
+
].includes(path))
|
|
500
|
+
constraints.push("--dry-run is unsupported for this local/interactive command and is rejected before any action. Use --help or --example instead.");
|
|
501
|
+
if (path === "app get")
|
|
502
|
+
constraints.push("current_premium_features is a Dashboard plan snapshot, not a runtime capability test. super_group_channel/reactions/poll=false does not prove an operation is blocked; true does not guarantee success. Missing values are unknown. Use doctor for separate plan, activation and prerequisite evidence.");
|
|
503
|
+
if (path === "auth status")
|
|
504
|
+
constraints.push("role.permissions contains server-reported Dashboard role permissions, not proof of access to every API. Human output shows up to 12 distinct entries; --json retains the full response. Missing permissions are unknown, not an empty grant.");
|
|
505
|
+
if (path === "doctor")
|
|
506
|
+
constraints.push("GET-only evidence collection. --command accepts one exact implemented command path; --type/--id/--as-user describe its target. No SDK connection, Chat user token read, feature activation or mutation probe. False feature snapshots do not prove runtime denial.");
|
|
507
|
+
if (path.startsWith("skills "))
|
|
508
|
+
constraints.push("Offline: no profile, credential or network access. --agent is claude|codex; --scope is project (current directory, default) or user. show has neither filter. Installs only the operational sendbird Skill; modified/unmanaged registrations and symlink search paths are refused. remove requires --yes except previews. Host loading and automatic invocation remain unverified.");
|
|
509
|
+
const docsOutput = skillSchema(path)
|
|
510
|
+
? stdoutSchema(jsonSchema(skillSchema(path)), "local")
|
|
511
|
+
: path === "doctor"
|
|
512
|
+
? stdoutSchema(jsonSchema(doctorSchema), "local")
|
|
513
|
+
: path === "docs search"
|
|
514
|
+
? {
|
|
515
|
+
type: "object",
|
|
516
|
+
required: [
|
|
517
|
+
"version",
|
|
518
|
+
"query",
|
|
519
|
+
"total",
|
|
520
|
+
"offset",
|
|
521
|
+
"limit",
|
|
522
|
+
"next_offset",
|
|
523
|
+
"results",
|
|
524
|
+
],
|
|
525
|
+
properties: {
|
|
526
|
+
version: { type: "string" },
|
|
527
|
+
query: { type: "string" },
|
|
528
|
+
total: { type: "integer" },
|
|
529
|
+
offset: { type: "integer" },
|
|
530
|
+
limit: { type: "integer" },
|
|
531
|
+
next_offset: { type: ["integer", "null"] },
|
|
532
|
+
results: { type: "array", items: { type: "object" } },
|
|
533
|
+
},
|
|
534
|
+
}
|
|
535
|
+
: path === "docs read"
|
|
536
|
+
? {
|
|
537
|
+
type: "object",
|
|
538
|
+
required: [
|
|
539
|
+
"version",
|
|
540
|
+
"id",
|
|
541
|
+
"title",
|
|
542
|
+
"source",
|
|
543
|
+
"source_url",
|
|
544
|
+
"sha256",
|
|
545
|
+
"content",
|
|
546
|
+
],
|
|
547
|
+
properties: {
|
|
548
|
+
version: { type: "string" },
|
|
549
|
+
id: { type: "string" },
|
|
550
|
+
title: { type: "string" },
|
|
551
|
+
source: { type: "string" },
|
|
552
|
+
source_url: { type: "string" },
|
|
553
|
+
sha256: { type: "string" },
|
|
554
|
+
content: {
|
|
555
|
+
type: "string",
|
|
556
|
+
description: "Markdown section, at most 24 KB",
|
|
557
|
+
},
|
|
558
|
+
},
|
|
559
|
+
}
|
|
560
|
+
: undefined;
|
|
561
|
+
return {
|
|
562
|
+
protocol_version: 1,
|
|
563
|
+
version: VERSION,
|
|
564
|
+
command: `sendbird ${path}`,
|
|
565
|
+
description: row.command.description(),
|
|
566
|
+
transport: transportFor(path),
|
|
567
|
+
effect: effectFor(path),
|
|
568
|
+
authentication: path === "auth signup"
|
|
569
|
+
? "None before signup; browser approval returns a Gate CLI OAuth grant"
|
|
570
|
+
: sdk
|
|
571
|
+
? "Existing Chat user token read into memory through environment-bound CLI OAuth, or explicit token file"
|
|
572
|
+
: d
|
|
573
|
+
? "Gate CLI OAuth Bearer for the selected environment"
|
|
574
|
+
: path.startsWith("docs ")
|
|
575
|
+
? "None; docs read needs a matching docs server"
|
|
576
|
+
: path === "app sdk-config" ||
|
|
577
|
+
path === "auth status" ||
|
|
578
|
+
path === "doctor"
|
|
579
|
+
? "Gate CLI OAuth Bearer for the selected environment"
|
|
580
|
+
: "See command flags",
|
|
581
|
+
required,
|
|
582
|
+
flags,
|
|
583
|
+
arguments: row.command.registeredArguments.map((a) => ({
|
|
584
|
+
name: a.name(),
|
|
585
|
+
required: a.required,
|
|
586
|
+
variadic: a.variadic,
|
|
587
|
+
description: a.description,
|
|
588
|
+
})),
|
|
589
|
+
constraints,
|
|
590
|
+
input: {
|
|
591
|
+
params: inputSchema(d?.params),
|
|
592
|
+
query: inputSchema(d?.query),
|
|
593
|
+
body: inputSchema(d?.body),
|
|
594
|
+
},
|
|
595
|
+
output: docsOutput ??
|
|
596
|
+
stdoutSchema(d
|
|
597
|
+
? jsonSchema(d.resultSchema ?? d.response)
|
|
598
|
+
: path === "setup"
|
|
599
|
+
? jsonSchema(setupSchema)
|
|
600
|
+
: path === "app sdk-config"
|
|
601
|
+
? jsonSchema(sdkConfigSchema)
|
|
602
|
+
: { type: "object", additionalProperties: true }, (sdk
|
|
603
|
+
? d.stream
|
|
604
|
+
? "stream"
|
|
605
|
+
: d.mutates
|
|
606
|
+
? "mutation"
|
|
607
|
+
: "sdk-read"
|
|
608
|
+
: d
|
|
609
|
+
? d.method === "GET"
|
|
610
|
+
? "read"
|
|
611
|
+
: "mutation"
|
|
612
|
+
: "local")),
|
|
613
|
+
value_semantics: valueSemantics(path),
|
|
614
|
+
result_schema: (skillSchema(path) ? jsonSchema(skillSchema(path)) : undefined) ??
|
|
615
|
+
(path === "doctor" ? jsonSchema(doctorSchema) : undefined) ??
|
|
616
|
+
docsOutput ??
|
|
617
|
+
(d
|
|
618
|
+
? jsonSchema(d.resultSchema ?? d.response)
|
|
619
|
+
: path === "setup"
|
|
620
|
+
? jsonSchema(setupSchema)
|
|
621
|
+
: path === "app sdk-config"
|
|
622
|
+
? jsonSchema(sdkConfigSchema)
|
|
623
|
+
: { type: "object", additionalProperties: true }),
|
|
624
|
+
example: path === "docs search"
|
|
625
|
+
? {
|
|
626
|
+
argv: ["sendbird", "docs", "search", "group chat"],
|
|
627
|
+
shell: "sendbird docs search 'group chat'",
|
|
628
|
+
}
|
|
629
|
+
: path === "docs read"
|
|
630
|
+
? {
|
|
631
|
+
argv: [
|
|
632
|
+
"sendbird",
|
|
633
|
+
"docs",
|
|
634
|
+
"read",
|
|
635
|
+
"guide/official/getting-started/quickstart#intro",
|
|
636
|
+
],
|
|
637
|
+
shell: "sendbird docs read 'guide/official/getting-started/quickstart#intro'",
|
|
638
|
+
}
|
|
639
|
+
: d
|
|
640
|
+
? exampleArgv(d)
|
|
641
|
+
: { shell: local?.example ?? `sendbird ${path} --help` },
|
|
642
|
+
recovery: [
|
|
643
|
+
"Use next and recovery.reads in structured errors to read the original target. Recovery is a plan, never an automatic retry.",
|
|
644
|
+
"mutation_state distinguishes not_attempted, rejected, unknown and acknowledged. A read match is not proof this request caused the state.",
|
|
645
|
+
"Do not retry uncertain mutations blindly.",
|
|
646
|
+
],
|
|
647
|
+
};
|
|
648
|
+
}
|
|
649
|
+
export function invocationCommand(root, args) {
|
|
650
|
+
let current = root;
|
|
651
|
+
for (let i = 0; i < args.length; i++) {
|
|
652
|
+
const arg = args[i];
|
|
653
|
+
if (arg.startsWith("-")) {
|
|
654
|
+
const opt = [...current.options, ...root.options].find((o) => o.long === arg.split("=")[0] || o.short === arg);
|
|
655
|
+
if (!opt)
|
|
656
|
+
break;
|
|
657
|
+
if ((opt.required || opt.optional) && !arg.includes("="))
|
|
658
|
+
i++;
|
|
659
|
+
continue;
|
|
660
|
+
}
|
|
661
|
+
const child = current.commands.find((c) => c.name() === arg);
|
|
662
|
+
if (!child)
|
|
663
|
+
break;
|
|
664
|
+
current = child;
|
|
665
|
+
}
|
|
666
|
+
return current;
|
|
667
|
+
}
|
|
668
|
+
export function commandPath(command) {
|
|
669
|
+
const names = [];
|
|
670
|
+
let c = command;
|
|
671
|
+
while (c?.parent) {
|
|
672
|
+
names.unshift(c.name());
|
|
673
|
+
c = c.parent;
|
|
674
|
+
}
|
|
675
|
+
return names.join(" ");
|
|
676
|
+
}
|
|
677
|
+
export function recovery(error, command, root) {
|
|
678
|
+
const path = commandPath(command);
|
|
679
|
+
const opts = command.optsWithGlobals();
|
|
680
|
+
const prefix = [
|
|
681
|
+
"sendbird",
|
|
682
|
+
...(error.recoverySnapshot?.profile
|
|
683
|
+
? ["--profile", error.recoverySnapshot.profile]
|
|
684
|
+
: typeof opts.profile === "string"
|
|
685
|
+
? ["--profile", opts.profile]
|
|
686
|
+
: []),
|
|
687
|
+
];
|
|
688
|
+
const next = [];
|
|
689
|
+
const inspect = () => next.push({
|
|
690
|
+
argv: ["sendbird", "describe", ...path.split(" ")],
|
|
691
|
+
reason: "Read exact flags, input requirements and examples.",
|
|
692
|
+
});
|
|
693
|
+
if (path && commandNodes(root).some((r) => r.path === path))
|
|
694
|
+
inspect();
|
|
695
|
+
else
|
|
696
|
+
next.push({
|
|
697
|
+
argv: ["sendbird", "agent"],
|
|
698
|
+
reason: "Start with the compact discovery guide.",
|
|
699
|
+
});
|
|
700
|
+
if (error.code === "E_SCHEMA" &&
|
|
701
|
+
path === "chat channel create" &&
|
|
702
|
+
!error.recoverySnapshot &&
|
|
703
|
+
error.details &&
|
|
704
|
+
typeof error.details === "object" &&
|
|
705
|
+
"channel_url" in error.details &&
|
|
706
|
+
typeof error.details.channel_url === "string") {
|
|
707
|
+
const actual = "actual" in error.details ? error.details.actual : undefined;
|
|
708
|
+
if (actual &&
|
|
709
|
+
typeof actual === "object" &&
|
|
710
|
+
"is_super" in actual &&
|
|
711
|
+
typeof actual.is_super === "boolean")
|
|
712
|
+
next.unshift({
|
|
713
|
+
argv: [
|
|
714
|
+
...prefix,
|
|
715
|
+
...(typeof opts.appId === "string" ? ["--app-id", opts.appId] : []),
|
|
716
|
+
"chat",
|
|
717
|
+
"channel",
|
|
718
|
+
"get",
|
|
719
|
+
"--type",
|
|
720
|
+
actual.is_super ? "supergroup" : "group",
|
|
721
|
+
"--id",
|
|
722
|
+
error.details.channel_url,
|
|
723
|
+
],
|
|
724
|
+
reason: "Read the created channel using its actual type before deciding how to clean up; do not repeat creation.",
|
|
725
|
+
});
|
|
726
|
+
}
|
|
727
|
+
if (path === "chat channel create" &&
|
|
728
|
+
opts.type === "supergroup" &&
|
|
729
|
+
error.details &&
|
|
730
|
+
typeof error.details === "object" &&
|
|
731
|
+
"upstream_code" in error.details &&
|
|
732
|
+
error.details.upstream_code === 400108 &&
|
|
733
|
+
"status" in error.details &&
|
|
734
|
+
error.details.status === 400) {
|
|
735
|
+
const appId = error.recoverySnapshot?.app_id ?? opts.appId;
|
|
736
|
+
const orgId = error.recoverySnapshot?.org_id ?? opts.orgId;
|
|
737
|
+
next.unshift({
|
|
738
|
+
argv: [
|
|
739
|
+
...prefix,
|
|
740
|
+
...(typeof orgId === "string" ? ["--org-id", orgId] : []),
|
|
741
|
+
...(typeof appId === "string" ? ["--app-id", appId] : []),
|
|
742
|
+
"doctor",
|
|
743
|
+
"--command",
|
|
744
|
+
"chat channel create",
|
|
745
|
+
"--type",
|
|
746
|
+
"supergroup",
|
|
747
|
+
"--json",
|
|
748
|
+
],
|
|
749
|
+
reason: "Inspect plan, feature activation and role evidence without creating a channel. 400108 alone does not prove Supergroup is unsupported. Do not enable paid features or change roles automatically.",
|
|
750
|
+
});
|
|
751
|
+
}
|
|
752
|
+
if (error.code === "E_AUTH" && transportFor(path) === "sdk")
|
|
753
|
+
next.push(error.message.startsWith("Login required")
|
|
754
|
+
? {
|
|
755
|
+
argv: [...prefix, "auth", "login"],
|
|
756
|
+
reason: "Approve CLI OAuth for the selected environment before reading the Chat user's existing access token.",
|
|
757
|
+
}
|
|
758
|
+
: {
|
|
759
|
+
argv: ["sendbird", "describe", "chat", "user", "token", "issue"],
|
|
760
|
+
reason: "The Chat user has no access token. Issue one explicitly, use an existing token file, or choose guest mode only when app policy allows it.",
|
|
761
|
+
});
|
|
762
|
+
else if (error.code === "E_AUTH")
|
|
763
|
+
next.push({
|
|
764
|
+
argv: [...prefix, "auth", "login"],
|
|
765
|
+
reason: "Use a human terminal for Dashboard login; never paste credentials into chat.",
|
|
766
|
+
});
|
|
767
|
+
if (error.code === "E_CAPABILITY" && path.startsWith("calls "))
|
|
768
|
+
next.push({
|
|
769
|
+
argv: [
|
|
770
|
+
...prefix,
|
|
771
|
+
...(typeof opts.appId === "string" ? ["--app-id", opts.appId] : []),
|
|
772
|
+
"app",
|
|
773
|
+
"get",
|
|
774
|
+
],
|
|
775
|
+
reason: "Inspect the selected application and region. Calls activation does not guarantee regional API availability; do not change regions, activate trials, or switch apps without authorization.",
|
|
776
|
+
});
|
|
777
|
+
if (error.code === "E_SECRET_STORE")
|
|
778
|
+
next.push({
|
|
779
|
+
argv: ["sendbird", "docs", "search", "Secret Service"],
|
|
780
|
+
reason: "Install and unlock an OS credential store before browser OAuth login.",
|
|
781
|
+
});
|
|
782
|
+
if (error.code === "E_SDK_AUTH")
|
|
783
|
+
next.push({
|
|
784
|
+
argv: ["sendbird", "workflows", "staging-sdk"],
|
|
785
|
+
reason: "Check user-token identity and app/region binding; Dashboard tokens are not SDK credentials.",
|
|
786
|
+
});
|
|
787
|
+
if (error.code === "E_SDK_PERMISSION")
|
|
788
|
+
next.push({
|
|
789
|
+
argv: ["sendbird", "describe", ...path.split(" ")],
|
|
790
|
+
reason: "Check the actor's channel membership, operator role and app feature entitlement; do not switch credentials to bypass it.",
|
|
791
|
+
});
|
|
792
|
+
if (["E_CONTEXT", "E_PROFILE"].includes(error.code))
|
|
793
|
+
next.push({
|
|
794
|
+
argv: [...prefix, "profile", "list"],
|
|
795
|
+
reason: "Inspect saved profile and context before selecting a target.",
|
|
796
|
+
});
|
|
797
|
+
if (error.code === "E_PERMISSION")
|
|
798
|
+
next.push({
|
|
799
|
+
argv: [...prefix, "auth", "status", "--summary"],
|
|
800
|
+
reason: "Check actual role permissions. Never elevate roles automatically.",
|
|
801
|
+
});
|
|
802
|
+
if (error.code === "E_CALLS_NO_VOUCHER")
|
|
803
|
+
next.push({
|
|
804
|
+
argv: ["sendbird", "describe", "calls", "credits", "list"],
|
|
805
|
+
reason: "Inspect voucher status and expiry for the intended organization. The usage error can also result from an upstream lookup failure; it does not prove no voucher was ever issued.",
|
|
806
|
+
});
|
|
807
|
+
if (error.code === "E_INTERRUPTED" && path === "setup")
|
|
808
|
+
next.push({
|
|
809
|
+
argv: [...prefix, "doctor"],
|
|
810
|
+
reason: "Inspect the account, organization and app selected before the prompt was cancelled.",
|
|
811
|
+
});
|
|
812
|
+
if (error.code === "E_DOCS_UNAVAILABLE" || error.code === "E_DOCS_VERSION")
|
|
813
|
+
next.push({
|
|
814
|
+
argv: ["sendbird", "docs", "search", "setup"],
|
|
815
|
+
reason: "Search the offline index; for full text, start a matching docs build or set --base-url.",
|
|
816
|
+
});
|
|
817
|
+
const uncertain = error.mutationState !== undefined
|
|
818
|
+
? error.mutationState === "unknown" ||
|
|
819
|
+
error.mutationState === "acknowledged"
|
|
820
|
+
: [
|
|
821
|
+
"E_NETWORK",
|
|
822
|
+
"E_TIMEOUT",
|
|
823
|
+
"E_INTERRUPTED",
|
|
824
|
+
"E_SDK_RUNTIME",
|
|
825
|
+
"E_SDK",
|
|
826
|
+
"E_SCHEMA",
|
|
827
|
+
"E_UPSTREAM",
|
|
828
|
+
"E_INTERNAL",
|
|
829
|
+
].includes(error.code) &&
|
|
830
|
+
(effectFor(path) === "mutation" ||
|
|
831
|
+
(transportFor(path) === "sdk" && opts.allowGuest === true));
|
|
832
|
+
const readback = uncertain && error.recoverySnapshot
|
|
833
|
+
? readbackPlan(error.recoverySnapshot, error.details)
|
|
834
|
+
: undefined;
|
|
835
|
+
if (readback)
|
|
836
|
+
next.unshift(...readback.reads);
|
|
837
|
+
return {
|
|
838
|
+
command: path ? `sendbird ${path}` : undefined,
|
|
839
|
+
automatic_retry: false,
|
|
840
|
+
outcome_unknown: uncertain,
|
|
841
|
+
...(error.mutationState ? { mutation_state: error.mutationState } : {}),
|
|
842
|
+
...(readback ? { recovery: readback } : {}),
|
|
843
|
+
next: next.filter((entry, index, all) => all.findIndex((other) => JSON.stringify(other.argv) === JSON.stringify(entry.argv)) === index),
|
|
844
|
+
};
|
|
845
|
+
}
|
|
846
|
+
//# sourceMappingURL=discovery.js.map
|