@rogeriq/cli 0.1.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/AGENTS.md +166 -0
- package/README.md +136 -0
- package/dist/index.mjs +2326 -0
- package/package.json +37 -0
package/dist/index.mjs
ADDED
|
@@ -0,0 +1,2326 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
// src/lib/args.ts
|
|
4
|
+
function parseArgs(argv, specs = []) {
|
|
5
|
+
const byLong = new Map(specs.map((s) => [s.name, s]));
|
|
6
|
+
const byShort = new Map(specs.filter((s) => s.alias).map((s) => [s.alias, s]));
|
|
7
|
+
const positional = [];
|
|
8
|
+
const flags = {};
|
|
9
|
+
let i = 0;
|
|
10
|
+
while (i < argv.length) {
|
|
11
|
+
const tok = argv[i];
|
|
12
|
+
if (tok === "--") {
|
|
13
|
+
positional.push(...argv.slice(i + 1));
|
|
14
|
+
break;
|
|
15
|
+
}
|
|
16
|
+
if (tok.startsWith("--")) {
|
|
17
|
+
const eq = tok.indexOf("=");
|
|
18
|
+
const name = eq >= 0 ? tok.slice(2, eq) : tok.slice(2);
|
|
19
|
+
const inline = eq >= 0 ? tok.slice(eq + 1) : void 0;
|
|
20
|
+
const spec = byLong.get(name);
|
|
21
|
+
i = applyFlag(flags, name, spec, inline, argv, i);
|
|
22
|
+
continue;
|
|
23
|
+
}
|
|
24
|
+
if (tok.startsWith("-") && tok.length > 1) {
|
|
25
|
+
const name = tok.slice(1);
|
|
26
|
+
const spec = byShort.get(name);
|
|
27
|
+
const longName = spec?.name ?? name;
|
|
28
|
+
i = applyFlag(flags, longName, spec, void 0, argv, i);
|
|
29
|
+
continue;
|
|
30
|
+
}
|
|
31
|
+
positional.push(tok);
|
|
32
|
+
i += 1;
|
|
33
|
+
}
|
|
34
|
+
return { positional, flags };
|
|
35
|
+
}
|
|
36
|
+
function applyFlag(flags, name, spec, inline, argv, i) {
|
|
37
|
+
const type = spec?.type ?? (inline !== void 0 ? "string" : "boolean");
|
|
38
|
+
if (type === "boolean") {
|
|
39
|
+
flags[name] = inline === void 0 ? true : inline !== "false";
|
|
40
|
+
return i + 1;
|
|
41
|
+
}
|
|
42
|
+
let value;
|
|
43
|
+
if (inline !== void 0) {
|
|
44
|
+
value = inline;
|
|
45
|
+
i += 1;
|
|
46
|
+
} else {
|
|
47
|
+
const next = argv[i + 1];
|
|
48
|
+
if (next === void 0 || next.startsWith("-")) {
|
|
49
|
+
throw new Error(`Flag --${name} requires a value`);
|
|
50
|
+
}
|
|
51
|
+
value = next;
|
|
52
|
+
i += 2;
|
|
53
|
+
}
|
|
54
|
+
if (type === "array") {
|
|
55
|
+
const existing = flags[name];
|
|
56
|
+
if (Array.isArray(existing)) existing.push(value);
|
|
57
|
+
else flags[name] = [value];
|
|
58
|
+
} else {
|
|
59
|
+
flags[name] = value;
|
|
60
|
+
}
|
|
61
|
+
return i;
|
|
62
|
+
}
|
|
63
|
+
function flagString(flags, name, fallback) {
|
|
64
|
+
const v = flags[name];
|
|
65
|
+
if (typeof v === "string") return v;
|
|
66
|
+
return fallback;
|
|
67
|
+
}
|
|
68
|
+
function flagBool(flags, name) {
|
|
69
|
+
return flags[name] === true;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
// src/lib/errors.ts
|
|
73
|
+
var CliError = class extends Error {
|
|
74
|
+
code;
|
|
75
|
+
exitCode;
|
|
76
|
+
requestId;
|
|
77
|
+
statusCode;
|
|
78
|
+
issues;
|
|
79
|
+
constructor(message, code = "CLI_ERROR", exitCode = 1) {
|
|
80
|
+
super(message);
|
|
81
|
+
this.code = code;
|
|
82
|
+
this.exitCode = exitCode;
|
|
83
|
+
}
|
|
84
|
+
};
|
|
85
|
+
|
|
86
|
+
// src/lib/output.ts
|
|
87
|
+
var globalOpts = {};
|
|
88
|
+
function setOutputOpts(opts) {
|
|
89
|
+
globalOpts = { ...opts };
|
|
90
|
+
}
|
|
91
|
+
function shouldJson() {
|
|
92
|
+
if (globalOpts.json) return true;
|
|
93
|
+
return !process.stdout.isTTY;
|
|
94
|
+
}
|
|
95
|
+
function emit(value, opts = {}) {
|
|
96
|
+
if (globalOpts.quiet) {
|
|
97
|
+
if (Array.isArray(value)) {
|
|
98
|
+
for (const row of value) printId(row, opts.idField);
|
|
99
|
+
} else {
|
|
100
|
+
printId(value, opts.idField);
|
|
101
|
+
}
|
|
102
|
+
return;
|
|
103
|
+
}
|
|
104
|
+
if (shouldJson()) {
|
|
105
|
+
process.stdout.write(JSON.stringify(value, null, 2) + "\n");
|
|
106
|
+
return;
|
|
107
|
+
}
|
|
108
|
+
if (Array.isArray(value)) {
|
|
109
|
+
printTable(value);
|
|
110
|
+
} else if (value && typeof value === "object") {
|
|
111
|
+
printRecord(value);
|
|
112
|
+
} else {
|
|
113
|
+
process.stdout.write(String(value) + "\n");
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
function printId(value, idField = "id") {
|
|
117
|
+
if (value && typeof value === "object" && idField in value) {
|
|
118
|
+
const id = value[idField];
|
|
119
|
+
if (id !== void 0 && id !== null) {
|
|
120
|
+
process.stdout.write(String(id) + "\n");
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
function printTable(rows) {
|
|
125
|
+
if (rows.length === 0) {
|
|
126
|
+
process.stderr.write("(no results)\n");
|
|
127
|
+
return;
|
|
128
|
+
}
|
|
129
|
+
const keys2 = preferredKeys(rows);
|
|
130
|
+
const widths = keys2.map(
|
|
131
|
+
(k) => Math.max(k.length, ...rows.map((r) => formatCell(r[k]).length))
|
|
132
|
+
);
|
|
133
|
+
const header = keys2.map((k, i) => k.padEnd(widths[i])).join(" ");
|
|
134
|
+
const sep = widths.map((w) => "\u2500".repeat(w)).join(" ");
|
|
135
|
+
process.stdout.write(header + "\n");
|
|
136
|
+
process.stdout.write(sep + "\n");
|
|
137
|
+
for (const row of rows) {
|
|
138
|
+
const line = keys2.map((k, i) => formatCell(row[k]).padEnd(widths[i])).join(" ");
|
|
139
|
+
process.stdout.write(line + "\n");
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
function preferredKeys(rows) {
|
|
143
|
+
const first = rows.find((r) => r && typeof r === "object");
|
|
144
|
+
if (!first) return [];
|
|
145
|
+
const all = Object.keys(first);
|
|
146
|
+
const priority = ["id", "name", "email", "url", "status", "subject", "channel", "priority", "event", "events", "scopes", "key_prefix", "created_at"];
|
|
147
|
+
const out = [];
|
|
148
|
+
for (const k of priority) if (all.includes(k)) out.push(k);
|
|
149
|
+
for (const k of all) {
|
|
150
|
+
if (out.includes(k)) continue;
|
|
151
|
+
const v = first[k];
|
|
152
|
+
if (v === null) continue;
|
|
153
|
+
if (typeof v === "object") continue;
|
|
154
|
+
out.push(k);
|
|
155
|
+
}
|
|
156
|
+
return out.slice(0, 8);
|
|
157
|
+
}
|
|
158
|
+
function printRecord(obj) {
|
|
159
|
+
const keys2 = Object.keys(obj);
|
|
160
|
+
const w = Math.max(...keys2.map((k) => k.length));
|
|
161
|
+
for (const k of keys2) {
|
|
162
|
+
const v = obj[k];
|
|
163
|
+
const s = v === null || v === void 0 ? "" : typeof v === "object" ? JSON.stringify(v) : String(v);
|
|
164
|
+
process.stdout.write(`${k.padEnd(w)} ${s}
|
|
165
|
+
`);
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
function formatCell(v) {
|
|
169
|
+
if (v === null || v === void 0) return "";
|
|
170
|
+
if (Array.isArray(v)) return v.join(",");
|
|
171
|
+
if (typeof v === "object") return "{\u2026}";
|
|
172
|
+
const s = String(v);
|
|
173
|
+
return s.length > 60 ? s.slice(0, 57) + "\u2026" : s;
|
|
174
|
+
}
|
|
175
|
+
function logInfo(msg) {
|
|
176
|
+
if (globalOpts.quiet) return;
|
|
177
|
+
process.stderr.write(msg + "\n");
|
|
178
|
+
}
|
|
179
|
+
function logError(err) {
|
|
180
|
+
if (shouldJson()) {
|
|
181
|
+
const payload = {
|
|
182
|
+
error: err.message,
|
|
183
|
+
code: err.code ?? "UNKNOWN"
|
|
184
|
+
};
|
|
185
|
+
const ce = err;
|
|
186
|
+
if (ce.requestId) payload["request_id"] = ce.requestId;
|
|
187
|
+
if (ce.statusCode) payload["status_code"] = ce.statusCode;
|
|
188
|
+
if (ce.issues) payload["issues"] = ce.issues;
|
|
189
|
+
process.stderr.write(JSON.stringify(payload) + "\n");
|
|
190
|
+
} else {
|
|
191
|
+
process.stderr.write(`error: ${err.message}
|
|
192
|
+
`);
|
|
193
|
+
const ce = err;
|
|
194
|
+
if (ce.code) process.stderr.write(` code: ${ce.code}
|
|
195
|
+
`);
|
|
196
|
+
if (ce.requestId) process.stderr.write(` request_id: ${ce.requestId}
|
|
197
|
+
`);
|
|
198
|
+
if (ce.issues?.length) {
|
|
199
|
+
for (const issue of ce.issues) {
|
|
200
|
+
process.stderr.write(` - ${issue.path}: ${issue.message}
|
|
201
|
+
`);
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
// src/lib/config.ts
|
|
208
|
+
import { existsSync, mkdirSync, readFileSync, writeFileSync, chmodSync } from "node:fs";
|
|
209
|
+
import { homedir } from "node:os";
|
|
210
|
+
import { dirname, join } from "node:path";
|
|
211
|
+
var CONFIG_PATH = join(homedir(), ".rogeriq", "config.json");
|
|
212
|
+
var DEFAULT_API_BASE = "https://api.rogeriq.com";
|
|
213
|
+
function readFile() {
|
|
214
|
+
if (!existsSync(CONFIG_PATH)) return {};
|
|
215
|
+
try {
|
|
216
|
+
const raw = readFileSync(CONFIG_PATH, "utf8");
|
|
217
|
+
return JSON.parse(raw);
|
|
218
|
+
} catch {
|
|
219
|
+
return {};
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
function writeFile(cfg) {
|
|
223
|
+
const dir = dirname(CONFIG_PATH);
|
|
224
|
+
if (!existsSync(dir)) mkdirSync(dir, { recursive: true, mode: 448 });
|
|
225
|
+
writeFileSync(CONFIG_PATH, JSON.stringify(cfg, null, 2));
|
|
226
|
+
try {
|
|
227
|
+
chmodSync(CONFIG_PATH, 384);
|
|
228
|
+
} catch {
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
function loadConfig() {
|
|
232
|
+
const file = readFile();
|
|
233
|
+
return {
|
|
234
|
+
apiKey: process.env["RIQ_API_KEY"] || file.apiKey,
|
|
235
|
+
apiBase: process.env["RIQ_API_BASE"] || file.apiBase || DEFAULT_API_BASE,
|
|
236
|
+
orgId: process.env["RIQ_ORG_ID"] || file.orgId,
|
|
237
|
+
projectId: process.env["RIQ_PROJECT_ID"] || file.projectId,
|
|
238
|
+
email: file.email
|
|
239
|
+
};
|
|
240
|
+
}
|
|
241
|
+
function saveConfig(updates) {
|
|
242
|
+
const current = readFile();
|
|
243
|
+
const merged = { ...current, ...updates };
|
|
244
|
+
writeFile(merged);
|
|
245
|
+
return merged;
|
|
246
|
+
}
|
|
247
|
+
function clearConfig() {
|
|
248
|
+
writeFile({});
|
|
249
|
+
}
|
|
250
|
+
function configPath() {
|
|
251
|
+
return CONFIG_PATH;
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
// src/lib/api-client.ts
|
|
255
|
+
var ApiClient = class {
|
|
256
|
+
apiBase;
|
|
257
|
+
apiKey;
|
|
258
|
+
projectId;
|
|
259
|
+
orgId;
|
|
260
|
+
constructor(opts = {}) {
|
|
261
|
+
const cfg = loadConfig();
|
|
262
|
+
this.apiBase = (opts.apiBase ?? cfg.apiBase).replace(/\/$/, "");
|
|
263
|
+
this.apiKey = opts.apiKey ?? cfg.apiKey ?? "";
|
|
264
|
+
this.projectId = opts.projectId ?? cfg.projectId;
|
|
265
|
+
this.orgId = cfg.orgId;
|
|
266
|
+
if (!this.apiKey) {
|
|
267
|
+
throw new CliError(
|
|
268
|
+
"No API key configured. Run `rogeriq auth login --api-key riq_xxx` or set RIQ_API_KEY.",
|
|
269
|
+
"AUTH_REQUIRED",
|
|
270
|
+
3
|
|
271
|
+
);
|
|
272
|
+
}
|
|
273
|
+
}
|
|
274
|
+
async request(method, path, body, init = {}) {
|
|
275
|
+
const url = path.startsWith("http") ? path : `${this.apiBase}${path}`;
|
|
276
|
+
const headers = {
|
|
277
|
+
"X-API-Key": this.apiKey,
|
|
278
|
+
"User-Agent": `rogeriq-cli/0.1.0 node/${process.versions.node}`,
|
|
279
|
+
"Accept": "application/json",
|
|
280
|
+
...init.headers
|
|
281
|
+
};
|
|
282
|
+
if (body !== void 0) headers["Content-Type"] = "application/json";
|
|
283
|
+
let res;
|
|
284
|
+
try {
|
|
285
|
+
res = await fetch(url, {
|
|
286
|
+
method,
|
|
287
|
+
headers,
|
|
288
|
+
body: body === void 0 ? void 0 : JSON.stringify(body),
|
|
289
|
+
...init
|
|
290
|
+
});
|
|
291
|
+
} catch (e) {
|
|
292
|
+
throw new CliError(
|
|
293
|
+
`Network error contacting ${url}: ${e instanceof Error ? e.message : String(e)}`,
|
|
294
|
+
"NETWORK_ERROR",
|
|
295
|
+
2
|
|
296
|
+
);
|
|
297
|
+
}
|
|
298
|
+
const requestId = res.headers.get("X-Request-Id") ?? void 0;
|
|
299
|
+
const limit = res.headers.get("X-RateLimit-Limit");
|
|
300
|
+
const remaining = res.headers.get("X-RateLimit-Remaining");
|
|
301
|
+
const resetAt = res.headers.get("X-RateLimit-Reset");
|
|
302
|
+
const rateLimit = limit ? {
|
|
303
|
+
limit: Number(limit),
|
|
304
|
+
remaining: Number(remaining),
|
|
305
|
+
resetAt: Number(resetAt)
|
|
306
|
+
} : void 0;
|
|
307
|
+
const text = await res.text();
|
|
308
|
+
let parsed = void 0;
|
|
309
|
+
if (text) {
|
|
310
|
+
try {
|
|
311
|
+
parsed = JSON.parse(text);
|
|
312
|
+
} catch {
|
|
313
|
+
parsed = text;
|
|
314
|
+
}
|
|
315
|
+
}
|
|
316
|
+
if (!res.ok) {
|
|
317
|
+
const err = parsed;
|
|
318
|
+
const code = err?.code ?? `HTTP_${res.status}`;
|
|
319
|
+
const message = err?.error ?? `Request failed: ${res.status} ${res.statusText}`;
|
|
320
|
+
const exitCode = res.status === 401 || res.status === 403 ? 3 : res.status === 429 ? 4 : 2;
|
|
321
|
+
if (res.status === 429 && err?.retry_after && err.retry_after <= 30) {
|
|
322
|
+
await sleep(err.retry_after * 1e3);
|
|
323
|
+
return this.request(method, path, body, init);
|
|
324
|
+
}
|
|
325
|
+
const ce = new CliError(message, code, exitCode);
|
|
326
|
+
ce.requestId = requestId;
|
|
327
|
+
ce.statusCode = res.status;
|
|
328
|
+
ce.issues = err?.issues;
|
|
329
|
+
throw ce;
|
|
330
|
+
}
|
|
331
|
+
const data = parsed?.data;
|
|
332
|
+
return { data, raw: parsed, status: res.status, requestId, rateLimit };
|
|
333
|
+
}
|
|
334
|
+
get(path) {
|
|
335
|
+
return this.request("GET", path);
|
|
336
|
+
}
|
|
337
|
+
post(path, body) {
|
|
338
|
+
return this.request("POST", path, body);
|
|
339
|
+
}
|
|
340
|
+
patch(path, body) {
|
|
341
|
+
return this.request("PATCH", path, body);
|
|
342
|
+
}
|
|
343
|
+
put(path, body) {
|
|
344
|
+
return this.request("PUT", path, body);
|
|
345
|
+
}
|
|
346
|
+
del(path) {
|
|
347
|
+
return this.request("DELETE", path);
|
|
348
|
+
}
|
|
349
|
+
requireProject() {
|
|
350
|
+
if (!this.projectId) {
|
|
351
|
+
throw new CliError(
|
|
352
|
+
"No project selected. Run `rogeriq projects use <id>`, pass --project <id>, or set RIQ_PROJECT_ID.",
|
|
353
|
+
"PROJECT_REQUIRED",
|
|
354
|
+
1
|
|
355
|
+
);
|
|
356
|
+
}
|
|
357
|
+
return this.projectId;
|
|
358
|
+
}
|
|
359
|
+
requireOrg() {
|
|
360
|
+
if (!this.orgId) {
|
|
361
|
+
throw new CliError(
|
|
362
|
+
"No org selected. Run `rogeriq orgs use <id>` or set RIQ_ORG_ID.",
|
|
363
|
+
"ORG_REQUIRED",
|
|
364
|
+
1
|
|
365
|
+
);
|
|
366
|
+
}
|
|
367
|
+
return this.orgId;
|
|
368
|
+
}
|
|
369
|
+
};
|
|
370
|
+
function sleep(ms) {
|
|
371
|
+
return new Promise((r) => setTimeout(r, ms));
|
|
372
|
+
}
|
|
373
|
+
|
|
374
|
+
// src/commands/auth.ts
|
|
375
|
+
var auth = {
|
|
376
|
+
summary: "Manage CLI authentication",
|
|
377
|
+
subcommands: {
|
|
378
|
+
login: {
|
|
379
|
+
summary: "Save an API key for future commands",
|
|
380
|
+
usage: "rogeriq auth login --api-key riq_xxx [--api-base https://api.rogeriq.com]",
|
|
381
|
+
flags: [
|
|
382
|
+
{ name: "api-key", type: "string" },
|
|
383
|
+
{ name: "api-base", type: "string" }
|
|
384
|
+
],
|
|
385
|
+
async run({ flags }) {
|
|
386
|
+
const apiKey = flags["api-key"];
|
|
387
|
+
const apiBase = flags["api-base"];
|
|
388
|
+
if (!apiKey) {
|
|
389
|
+
throw new CliError(
|
|
390
|
+
"Pass --api-key riq_xxx. Generate one with `rogeriq keys create <name>` (after a one-time browser login) or in the dashboard.",
|
|
391
|
+
"MISSING_API_KEY",
|
|
392
|
+
1
|
|
393
|
+
);
|
|
394
|
+
}
|
|
395
|
+
if (!apiKey.startsWith("riq_")) {
|
|
396
|
+
throw new CliError("API key must start with riq_", "INVALID_API_KEY", 1);
|
|
397
|
+
}
|
|
398
|
+
const updates = { apiKey };
|
|
399
|
+
if (apiBase) updates["apiBase"] = apiBase;
|
|
400
|
+
saveConfig(updates);
|
|
401
|
+
const client = new ApiClient();
|
|
402
|
+
const me = await client.get("/api/orgs");
|
|
403
|
+
const list = me.raw?.data ?? [];
|
|
404
|
+
if (list[0]?.id) saveConfig({ orgId: list[0].id });
|
|
405
|
+
logInfo(`Saved API key to ${configPath()}.`);
|
|
406
|
+
emit({ ok: true, org_id: list[0]?.id ?? null });
|
|
407
|
+
}
|
|
408
|
+
},
|
|
409
|
+
logout: {
|
|
410
|
+
summary: "Clear stored API key",
|
|
411
|
+
run() {
|
|
412
|
+
clearConfig();
|
|
413
|
+
logInfo(`Cleared ${configPath()}.`);
|
|
414
|
+
emit({ ok: true });
|
|
415
|
+
}
|
|
416
|
+
},
|
|
417
|
+
whoami: {
|
|
418
|
+
summary: "Show current API key + default org/project",
|
|
419
|
+
async run() {
|
|
420
|
+
const cfg = loadConfig();
|
|
421
|
+
if (!cfg.apiKey) {
|
|
422
|
+
throw new CliError("Not logged in. Run `rogeriq auth login --api-key riq_xxx`.", "NOT_LOGGED_IN", 3);
|
|
423
|
+
}
|
|
424
|
+
const client = new ApiClient();
|
|
425
|
+
const orgsRes = await client.get("/api/orgs");
|
|
426
|
+
const list = orgsRes.raw?.data ?? [];
|
|
427
|
+
emit({
|
|
428
|
+
api_key_prefix: cfg.apiKey.slice(0, 12) + "...",
|
|
429
|
+
api_base: cfg.apiBase,
|
|
430
|
+
org_id: cfg.orgId ?? null,
|
|
431
|
+
project_id: cfg.projectId ?? null,
|
|
432
|
+
orgs: list
|
|
433
|
+
});
|
|
434
|
+
}
|
|
435
|
+
}
|
|
436
|
+
}
|
|
437
|
+
};
|
|
438
|
+
|
|
439
|
+
// src/commands/config.ts
|
|
440
|
+
var KEYS = ["apiKey", "apiBase", "orgId", "projectId"];
|
|
441
|
+
var config = {
|
|
442
|
+
summary: "View / set CLI config (~/.rogeriq/config.json)",
|
|
443
|
+
subcommands: {
|
|
444
|
+
get: {
|
|
445
|
+
summary: "Print current config (apiKey redacted)",
|
|
446
|
+
run() {
|
|
447
|
+
const cfg = loadConfig();
|
|
448
|
+
emit({
|
|
449
|
+
...cfg,
|
|
450
|
+
apiKey: cfg.apiKey ? cfg.apiKey.slice(0, 12) + "..." : null,
|
|
451
|
+
path: configPath()
|
|
452
|
+
});
|
|
453
|
+
}
|
|
454
|
+
},
|
|
455
|
+
set: {
|
|
456
|
+
summary: "Set a config key (apiKey | apiBase | orgId | projectId)",
|
|
457
|
+
usage: "rogeriq config set <key> <value>",
|
|
458
|
+
async run({ positional }) {
|
|
459
|
+
const [key, value] = positional;
|
|
460
|
+
if (!key || !value) throw new CliError("Usage: rogeriq config set <key> <value>", "BAD_USAGE", 1);
|
|
461
|
+
if (!KEYS.includes(key)) {
|
|
462
|
+
throw new CliError(`Unknown key. One of: ${KEYS.join(", ")}`, "BAD_KEY", 1);
|
|
463
|
+
}
|
|
464
|
+
saveConfig({ [key]: value });
|
|
465
|
+
logInfo(`Saved ${key}.`);
|
|
466
|
+
emit({ ok: true });
|
|
467
|
+
}
|
|
468
|
+
},
|
|
469
|
+
path: {
|
|
470
|
+
summary: "Print config file path",
|
|
471
|
+
run() {
|
|
472
|
+
process.stdout.write(configPath() + "\n");
|
|
473
|
+
}
|
|
474
|
+
}
|
|
475
|
+
}
|
|
476
|
+
};
|
|
477
|
+
|
|
478
|
+
// src/commands/orgs.ts
|
|
479
|
+
var orgs = {
|
|
480
|
+
summary: "Manage organizations",
|
|
481
|
+
subcommands: {
|
|
482
|
+
list: {
|
|
483
|
+
summary: "List organizations you belong to",
|
|
484
|
+
async run() {
|
|
485
|
+
const client = new ApiClient();
|
|
486
|
+
const res = await client.get("/api/orgs");
|
|
487
|
+
emit(res.raw?.data ?? res.raw);
|
|
488
|
+
}
|
|
489
|
+
},
|
|
490
|
+
get: {
|
|
491
|
+
summary: "Show one organization with members",
|
|
492
|
+
usage: "rogeriq orgs get <org-id>",
|
|
493
|
+
async run({ positional }) {
|
|
494
|
+
const [id] = positional;
|
|
495
|
+
if (!id) throw new CliError("Usage: rogeriq orgs get <org-id>", "BAD_USAGE", 1);
|
|
496
|
+
const client = new ApiClient();
|
|
497
|
+
const res = await client.get(`/api/orgs/${id}`);
|
|
498
|
+
emit(res.raw?.data ?? res.raw);
|
|
499
|
+
}
|
|
500
|
+
},
|
|
501
|
+
create: {
|
|
502
|
+
summary: "Create a new organization",
|
|
503
|
+
usage: "rogeriq orgs create <name>",
|
|
504
|
+
async run({ positional }) {
|
|
505
|
+
const [name] = positional;
|
|
506
|
+
if (!name) throw new CliError("Usage: rogeriq orgs create <name>", "BAD_USAGE", 1);
|
|
507
|
+
const client = new ApiClient();
|
|
508
|
+
const res = await client.post("/api/orgs", { name });
|
|
509
|
+
emit(res.raw?.data ?? res.raw);
|
|
510
|
+
}
|
|
511
|
+
},
|
|
512
|
+
use: {
|
|
513
|
+
summary: "Set default org for future commands",
|
|
514
|
+
usage: "rogeriq orgs use <org-id>",
|
|
515
|
+
async run({ positional }) {
|
|
516
|
+
const [id] = positional;
|
|
517
|
+
if (!id) throw new CliError("Usage: rogeriq orgs use <org-id>", "BAD_USAGE", 1);
|
|
518
|
+
saveConfig({ orgId: id });
|
|
519
|
+
logInfo(`Default org set to ${id}`);
|
|
520
|
+
emit({ ok: true, org_id: id });
|
|
521
|
+
}
|
|
522
|
+
},
|
|
523
|
+
members: {
|
|
524
|
+
summary: "List members of an org",
|
|
525
|
+
usage: "rogeriq orgs members [--org <id>]",
|
|
526
|
+
flags: [{ name: "org", type: "string" }],
|
|
527
|
+
async run({ flags }) {
|
|
528
|
+
const client = new ApiClient();
|
|
529
|
+
const orgId = flags["org"] ?? client.requireOrg();
|
|
530
|
+
const res = await client.get(`/api/orgs/${orgId}/members`);
|
|
531
|
+
emit(res.raw?.data ?? res.raw);
|
|
532
|
+
}
|
|
533
|
+
}
|
|
534
|
+
}
|
|
535
|
+
};
|
|
536
|
+
|
|
537
|
+
// src/commands/projects.ts
|
|
538
|
+
var projects = {
|
|
539
|
+
summary: "Manage projects (a project is the unit of data isolation)",
|
|
540
|
+
subcommands: {
|
|
541
|
+
list: {
|
|
542
|
+
summary: "List projects in current org",
|
|
543
|
+
flags: [{ name: "org", type: "string" }],
|
|
544
|
+
async run({ flags }) {
|
|
545
|
+
const client = new ApiClient();
|
|
546
|
+
const orgId = flags["org"] ?? client.requireOrg();
|
|
547
|
+
const res = await client.get(`/api/v1/orgs/${orgId}/projects`);
|
|
548
|
+
emit(res.raw?.data ?? res.raw);
|
|
549
|
+
}
|
|
550
|
+
},
|
|
551
|
+
get: {
|
|
552
|
+
summary: "Get one project",
|
|
553
|
+
usage: "rogeriq projects get <project-id>",
|
|
554
|
+
flags: [{ name: "org", type: "string" }],
|
|
555
|
+
async run({ positional, flags }) {
|
|
556
|
+
const [id] = positional;
|
|
557
|
+
if (!id) throw new CliError("Usage: rogeriq projects get <project-id>", "BAD_USAGE", 1);
|
|
558
|
+
const client = new ApiClient();
|
|
559
|
+
const orgId = flags["org"] ?? client.requireOrg();
|
|
560
|
+
const res = await client.get(`/api/v1/orgs/${orgId}/projects/${id}`);
|
|
561
|
+
emit(res.raw?.data ?? res.raw);
|
|
562
|
+
}
|
|
563
|
+
},
|
|
564
|
+
create: {
|
|
565
|
+
summary: "Create a new project",
|
|
566
|
+
usage: "rogeriq projects create <name> [--slug ...] [--domain ...]",
|
|
567
|
+
flags: [
|
|
568
|
+
{ name: "org", type: "string" },
|
|
569
|
+
{ name: "slug", type: "string" },
|
|
570
|
+
{ name: "domain", type: "string" }
|
|
571
|
+
],
|
|
572
|
+
async run({ positional, flags }) {
|
|
573
|
+
const [name] = positional;
|
|
574
|
+
if (!name) throw new CliError("Usage: rogeriq projects create <name>", "BAD_USAGE", 1);
|
|
575
|
+
const client = new ApiClient();
|
|
576
|
+
const orgId = flags["org"] ?? client.requireOrg();
|
|
577
|
+
const body = { name };
|
|
578
|
+
if (flags["slug"]) body["slug"] = flags["slug"];
|
|
579
|
+
if (flags["domain"]) body["domain"] = flags["domain"];
|
|
580
|
+
const res = await client.post(`/api/v1/orgs/${orgId}/projects`, body);
|
|
581
|
+
emit(res.raw?.data ?? res.raw);
|
|
582
|
+
}
|
|
583
|
+
},
|
|
584
|
+
update: {
|
|
585
|
+
summary: "Update a project",
|
|
586
|
+
usage: "rogeriq projects update <project-id> [--name ...] [--domain ...] [--agent-mode autopilot|copilot|assist] [--ai-model ...]",
|
|
587
|
+
flags: [
|
|
588
|
+
{ name: "org", type: "string" },
|
|
589
|
+
{ name: "name", type: "string" },
|
|
590
|
+
{ name: "domain", type: "string" },
|
|
591
|
+
{ name: "agent-mode", type: "string" },
|
|
592
|
+
{ name: "ai-model", type: "string" }
|
|
593
|
+
],
|
|
594
|
+
async run({ positional, flags }) {
|
|
595
|
+
const [id] = positional;
|
|
596
|
+
if (!id) throw new CliError("Usage: rogeriq projects update <project-id>", "BAD_USAGE", 1);
|
|
597
|
+
const client = new ApiClient();
|
|
598
|
+
const orgId = flags["org"] ?? client.requireOrg();
|
|
599
|
+
const body = {};
|
|
600
|
+
if (flags["name"]) body["name"] = flags["name"];
|
|
601
|
+
if (flags["domain"]) body["domain"] = flags["domain"];
|
|
602
|
+
if (flags["agent-mode"]) body["agent_mode"] = flags["agent-mode"];
|
|
603
|
+
if (flags["ai-model"]) body["ai_model"] = flags["ai-model"];
|
|
604
|
+
if (Object.keys(body).length === 0) {
|
|
605
|
+
throw new CliError("Provide at least one --name/--domain/--agent-mode/--ai-model", "NO_UPDATES", 1);
|
|
606
|
+
}
|
|
607
|
+
const res = await client.patch(`/api/v1/orgs/${orgId}/projects/${id}`, body);
|
|
608
|
+
emit(res.raw?.data ?? res.raw);
|
|
609
|
+
}
|
|
610
|
+
},
|
|
611
|
+
delete: {
|
|
612
|
+
summary: "Delete a project (owner only)",
|
|
613
|
+
usage: "rogeriq projects delete <project-id>",
|
|
614
|
+
flags: [{ name: "org", type: "string" }],
|
|
615
|
+
async run({ positional, flags }) {
|
|
616
|
+
const [id] = positional;
|
|
617
|
+
if (!id) throw new CliError("Usage: rogeriq projects delete <project-id>", "BAD_USAGE", 1);
|
|
618
|
+
const client = new ApiClient();
|
|
619
|
+
const orgId = flags["org"] ?? client.requireOrg();
|
|
620
|
+
await client.del(`/api/v1/orgs/${orgId}/projects/${id}`);
|
|
621
|
+
emit({ ok: true, deleted: id });
|
|
622
|
+
}
|
|
623
|
+
},
|
|
624
|
+
use: {
|
|
625
|
+
summary: "Set default project for future commands",
|
|
626
|
+
usage: "rogeriq projects use <project-id>",
|
|
627
|
+
async run({ positional }) {
|
|
628
|
+
const [id] = positional;
|
|
629
|
+
if (!id) throw new CliError("Usage: rogeriq projects use <project-id>", "BAD_USAGE", 1);
|
|
630
|
+
saveConfig({ projectId: id });
|
|
631
|
+
logInfo(`Default project set to ${id}`);
|
|
632
|
+
emit({ ok: true, project_id: id });
|
|
633
|
+
}
|
|
634
|
+
}
|
|
635
|
+
}
|
|
636
|
+
};
|
|
637
|
+
|
|
638
|
+
// src/commands/keys.ts
|
|
639
|
+
var keys = {
|
|
640
|
+
summary: "Manage API keys",
|
|
641
|
+
subcommands: {
|
|
642
|
+
list: {
|
|
643
|
+
summary: "List API keys for current org",
|
|
644
|
+
flags: [{ name: "org", type: "string" }],
|
|
645
|
+
async run({ flags }) {
|
|
646
|
+
const client = new ApiClient();
|
|
647
|
+
const orgId = flags["org"] ?? client.requireOrg();
|
|
648
|
+
const res = await client.get(`/api/orgs/${orgId}/api-keys`);
|
|
649
|
+
emit(res.raw?.data ?? res.raw);
|
|
650
|
+
}
|
|
651
|
+
},
|
|
652
|
+
create: {
|
|
653
|
+
summary: "Create an API key. Raw key returned ONCE \u2014 copy it now.",
|
|
654
|
+
usage: "rogeriq keys create <name> [--scopes read,write,admin] [--expires-days 90]",
|
|
655
|
+
flags: [
|
|
656
|
+
{ name: "org", type: "string" },
|
|
657
|
+
{ name: "scopes", type: "string" },
|
|
658
|
+
{ name: "expires-days", type: "string" }
|
|
659
|
+
],
|
|
660
|
+
async run({ positional, flags }) {
|
|
661
|
+
const [name] = positional;
|
|
662
|
+
if (!name) throw new CliError("Usage: rogeriq keys create <name>", "BAD_USAGE", 1);
|
|
663
|
+
const client = new ApiClient();
|
|
664
|
+
const orgId = flags["org"] ?? client.requireOrg();
|
|
665
|
+
const scopes = flags["scopes"]?.split(",").map((s) => s.trim()) ?? ["read", "write"];
|
|
666
|
+
const body = { name, scopes };
|
|
667
|
+
if (flags["expires-days"]) body["expires_in_days"] = Number(flags["expires-days"]);
|
|
668
|
+
const res = await client.post(`/api/orgs/${orgId}/api-keys`, body);
|
|
669
|
+
emit(res.raw?.data ?? res.raw);
|
|
670
|
+
}
|
|
671
|
+
},
|
|
672
|
+
revoke: {
|
|
673
|
+
summary: "Revoke an API key",
|
|
674
|
+
usage: "rogeriq keys revoke <key-id>",
|
|
675
|
+
flags: [{ name: "org", type: "string" }],
|
|
676
|
+
async run({ positional, flags }) {
|
|
677
|
+
const [id] = positional;
|
|
678
|
+
if (!id) throw new CliError("Usage: rogeriq keys revoke <key-id>", "BAD_USAGE", 1);
|
|
679
|
+
const client = new ApiClient();
|
|
680
|
+
const orgId = flags["org"] ?? client.requireOrg();
|
|
681
|
+
await client.del(`/api/orgs/${orgId}/api-keys/${id}`);
|
|
682
|
+
emit({ ok: true, revoked: id });
|
|
683
|
+
}
|
|
684
|
+
}
|
|
685
|
+
}
|
|
686
|
+
};
|
|
687
|
+
|
|
688
|
+
// src/commands/conversations.ts
|
|
689
|
+
function buildQuery(params) {
|
|
690
|
+
const u = new URLSearchParams();
|
|
691
|
+
for (const [k, v] of Object.entries(params)) if (v) u.set(k, v);
|
|
692
|
+
const s = u.toString();
|
|
693
|
+
return s ? `?${s}` : "";
|
|
694
|
+
}
|
|
695
|
+
var conversations = {
|
|
696
|
+
summary: "Manage tickets / conversations",
|
|
697
|
+
subcommands: {
|
|
698
|
+
list: {
|
|
699
|
+
summary: "List conversations (uses v1 public API)",
|
|
700
|
+
usage: "rogeriq conversations list [--status open|snoozed|resolved|closed] [--priority ...] [--channel ...] [--q 'search'] [--limit 50] [--cursor ...]",
|
|
701
|
+
flags: [
|
|
702
|
+
{ name: "status", type: "string" },
|
|
703
|
+
{ name: "priority", type: "string" },
|
|
704
|
+
{ name: "channel", type: "string" },
|
|
705
|
+
{ name: "assigned-to", type: "string" },
|
|
706
|
+
{ name: "contact", type: "string" },
|
|
707
|
+
{ name: "q", type: "string" },
|
|
708
|
+
{ name: "limit", type: "string" },
|
|
709
|
+
{ name: "cursor", type: "string" }
|
|
710
|
+
],
|
|
711
|
+
async run({ flags }) {
|
|
712
|
+
const client = new ApiClient({ projectId: flags["project"] });
|
|
713
|
+
const pid = client.requireProject();
|
|
714
|
+
const qs = buildQuery({
|
|
715
|
+
status: flags["status"],
|
|
716
|
+
priority: flags["priority"],
|
|
717
|
+
channel: flags["channel"],
|
|
718
|
+
assigned_to: flags["assigned-to"],
|
|
719
|
+
contact_id: flags["contact"],
|
|
720
|
+
q: flags["q"],
|
|
721
|
+
limit: flags["limit"],
|
|
722
|
+
cursor: flags["cursor"]
|
|
723
|
+
});
|
|
724
|
+
const res = await client.get(`/api/v1/projects/${pid}/conversations${qs}`);
|
|
725
|
+
emit(res.raw?.data ?? res.raw);
|
|
726
|
+
}
|
|
727
|
+
},
|
|
728
|
+
get: {
|
|
729
|
+
summary: "Get one conversation (with recent messages)",
|
|
730
|
+
usage: "rogeriq conversations get <conversation-id>",
|
|
731
|
+
async run({ positional, flags }) {
|
|
732
|
+
const [id] = positional;
|
|
733
|
+
if (!id) throw new CliError("Usage: rogeriq conversations get <id>", "BAD_USAGE", 1);
|
|
734
|
+
const client = new ApiClient({ projectId: flags["project"] });
|
|
735
|
+
const pid = client.requireProject();
|
|
736
|
+
const res = await client.get(`/api/v1/projects/${pid}/conversations/${id}`);
|
|
737
|
+
emit(res.raw?.data ?? res.raw);
|
|
738
|
+
}
|
|
739
|
+
},
|
|
740
|
+
create: {
|
|
741
|
+
summary: "Create a conversation",
|
|
742
|
+
usage: "rogeriq conversations create [--subject ...] [--contact <id>] [--channel api]",
|
|
743
|
+
flags: [
|
|
744
|
+
{ name: "subject", type: "string" },
|
|
745
|
+
{ name: "contact", type: "string" },
|
|
746
|
+
{ name: "channel", type: "string" },
|
|
747
|
+
{ name: "priority", type: "string" },
|
|
748
|
+
{ name: "tags", type: "string" }
|
|
749
|
+
],
|
|
750
|
+
async run({ flags }) {
|
|
751
|
+
const client = new ApiClient({ projectId: flags["project"] });
|
|
752
|
+
const pid = client.requireProject();
|
|
753
|
+
const body = {};
|
|
754
|
+
if (flags["subject"]) body["subject"] = flags["subject"];
|
|
755
|
+
if (flags["contact"]) body["contact_id"] = flags["contact"];
|
|
756
|
+
if (flags["channel"]) body["channel"] = flags["channel"];
|
|
757
|
+
if (flags["priority"]) body["priority"] = flags["priority"];
|
|
758
|
+
if (flags["tags"]) body["tags"] = flags["tags"].split(",");
|
|
759
|
+
const res = await client.post(`/api/v1/projects/${pid}/conversations`, body);
|
|
760
|
+
emit(res.raw?.data ?? res.raw);
|
|
761
|
+
}
|
|
762
|
+
},
|
|
763
|
+
update: {
|
|
764
|
+
summary: "Update a conversation (subject/status/priority/assignee/tags)",
|
|
765
|
+
usage: "rogeriq conversations update <id> [--status ...] [--priority ...] [--assigned-to <user-id>] [--tags a,b]",
|
|
766
|
+
flags: [
|
|
767
|
+
{ name: "subject", type: "string" },
|
|
768
|
+
{ name: "status", type: "string" },
|
|
769
|
+
{ name: "priority", type: "string" },
|
|
770
|
+
{ name: "assigned-to", type: "string" },
|
|
771
|
+
{ name: "tags", type: "string" }
|
|
772
|
+
],
|
|
773
|
+
async run({ positional, flags }) {
|
|
774
|
+
const [id] = positional;
|
|
775
|
+
if (!id) throw new CliError("Usage: rogeriq conversations update <id>", "BAD_USAGE", 1);
|
|
776
|
+
const client = new ApiClient({ projectId: flags["project"] });
|
|
777
|
+
const pid = client.requireProject();
|
|
778
|
+
const body = {};
|
|
779
|
+
if (flags["subject"]) body["subject"] = flags["subject"];
|
|
780
|
+
if (flags["status"]) body["status"] = flags["status"];
|
|
781
|
+
if (flags["priority"]) body["priority"] = flags["priority"];
|
|
782
|
+
if (flags["assigned-to"]) body["assigned_to"] = flags["assigned-to"];
|
|
783
|
+
if (flags["tags"]) body["tags"] = flags["tags"].split(",");
|
|
784
|
+
if (Object.keys(body).length === 0) throw new CliError("No updates provided", "NO_UPDATES", 1);
|
|
785
|
+
const res = await client.patch(`/api/v1/projects/${pid}/conversations/${id}`, body);
|
|
786
|
+
emit(res.raw?.data ?? res.raw);
|
|
787
|
+
}
|
|
788
|
+
},
|
|
789
|
+
reply: {
|
|
790
|
+
summary: "Send a reply to a conversation",
|
|
791
|
+
usage: "rogeriq conversations reply <id> <content> [--internal]",
|
|
792
|
+
flags: [
|
|
793
|
+
{ name: "internal", type: "boolean" },
|
|
794
|
+
{ name: "sender-type", type: "string" }
|
|
795
|
+
],
|
|
796
|
+
async run({ positional, flags }) {
|
|
797
|
+
const [id, ...rest] = positional;
|
|
798
|
+
const content = rest.join(" ");
|
|
799
|
+
if (!id || !content) throw new CliError("Usage: rogeriq conversations reply <id> <content>", "BAD_USAGE", 1);
|
|
800
|
+
const client = new ApiClient({ projectId: flags["project"] });
|
|
801
|
+
const pid = client.requireProject();
|
|
802
|
+
const body = {
|
|
803
|
+
content,
|
|
804
|
+
is_internal: flags["internal"] === true
|
|
805
|
+
};
|
|
806
|
+
if (flags["sender-type"]) body["sender_type"] = flags["sender-type"];
|
|
807
|
+
const res = await client.post(`/api/v1/projects/${pid}/conversations/${id}/messages`, body);
|
|
808
|
+
emit(res.raw?.data ?? res.raw);
|
|
809
|
+
}
|
|
810
|
+
},
|
|
811
|
+
resolve: {
|
|
812
|
+
summary: "Resolve a conversation",
|
|
813
|
+
usage: "rogeriq conversations resolve <id>",
|
|
814
|
+
async run({ positional, flags }) {
|
|
815
|
+
const [id] = positional;
|
|
816
|
+
if (!id) throw new CliError("Usage: rogeriq conversations resolve <id>", "BAD_USAGE", 1);
|
|
817
|
+
const client = new ApiClient({ projectId: flags["project"] });
|
|
818
|
+
const pid = client.requireProject();
|
|
819
|
+
const res = await client.patch(`/api/v1/projects/${pid}/conversations/${id}`, { status: "resolved" });
|
|
820
|
+
emit(res.raw?.data ?? res.raw);
|
|
821
|
+
}
|
|
822
|
+
},
|
|
823
|
+
snooze: {
|
|
824
|
+
summary: "Snooze a conversation",
|
|
825
|
+
usage: "rogeriq conversations snooze <id>",
|
|
826
|
+
async run({ positional, flags }) {
|
|
827
|
+
const [id] = positional;
|
|
828
|
+
if (!id) throw new CliError("Usage: rogeriq conversations snooze <id>", "BAD_USAGE", 1);
|
|
829
|
+
const client = new ApiClient({ projectId: flags["project"] });
|
|
830
|
+
const pid = client.requireProject();
|
|
831
|
+
const res = await client.patch(`/api/v1/projects/${pid}/conversations/${id}`, { status: "snoozed" });
|
|
832
|
+
emit(res.raw?.data ?? res.raw);
|
|
833
|
+
}
|
|
834
|
+
},
|
|
835
|
+
assign: {
|
|
836
|
+
summary: "Assign conversation to a user",
|
|
837
|
+
usage: "rogeriq conversations assign <id> <user-id>",
|
|
838
|
+
async run({ positional, flags }) {
|
|
839
|
+
const [id, userId] = positional;
|
|
840
|
+
if (!id || !userId) throw new CliError("Usage: rogeriq conversations assign <id> <user-id>", "BAD_USAGE", 1);
|
|
841
|
+
const client = new ApiClient({ projectId: flags["project"] });
|
|
842
|
+
const pid = client.requireProject();
|
|
843
|
+
const res = await client.patch(`/api/v1/projects/${pid}/conversations/${id}`, { assigned_to: userId });
|
|
844
|
+
emit(res.raw?.data ?? res.raw);
|
|
845
|
+
}
|
|
846
|
+
}
|
|
847
|
+
}
|
|
848
|
+
};
|
|
849
|
+
|
|
850
|
+
// src/commands/messages.ts
|
|
851
|
+
var messages = {
|
|
852
|
+
summary: "List / send messages on a conversation",
|
|
853
|
+
subcommands: {
|
|
854
|
+
list: {
|
|
855
|
+
summary: "List messages for a conversation",
|
|
856
|
+
usage: "rogeriq messages list <conversation-id> [--limit 100] [--cursor ...]",
|
|
857
|
+
flags: [
|
|
858
|
+
{ name: "limit", type: "string" },
|
|
859
|
+
{ name: "cursor", type: "string" }
|
|
860
|
+
],
|
|
861
|
+
async run({ positional, flags }) {
|
|
862
|
+
const [conv] = positional;
|
|
863
|
+
if (!conv) throw new CliError("Usage: rogeriq messages list <conversation-id>", "BAD_USAGE", 1);
|
|
864
|
+
const client = new ApiClient({ projectId: flags["project"] });
|
|
865
|
+
const pid = client.requireProject();
|
|
866
|
+
const qs = new URLSearchParams();
|
|
867
|
+
if (flags["limit"]) qs.set("limit", flags["limit"]);
|
|
868
|
+
if (flags["cursor"]) qs.set("cursor", flags["cursor"]);
|
|
869
|
+
const suffix = qs.toString() ? `?${qs}` : "";
|
|
870
|
+
const res = await client.get(`/api/v1/projects/${pid}/conversations/${conv}/messages${suffix}`);
|
|
871
|
+
emit(res.raw?.data ?? res.raw);
|
|
872
|
+
}
|
|
873
|
+
},
|
|
874
|
+
send: {
|
|
875
|
+
summary: "Send a message on a conversation",
|
|
876
|
+
usage: "rogeriq messages send <conversation-id> <content> [--internal] [--sender-type system|agent]",
|
|
877
|
+
flags: [
|
|
878
|
+
{ name: "internal", type: "boolean" },
|
|
879
|
+
{ name: "sender-type", type: "string" }
|
|
880
|
+
],
|
|
881
|
+
async run({ positional, flags }) {
|
|
882
|
+
const [conv, ...rest] = positional;
|
|
883
|
+
const content = rest.join(" ");
|
|
884
|
+
if (!conv || !content) throw new CliError("Usage: rogeriq messages send <conversation-id> <content>", "BAD_USAGE", 1);
|
|
885
|
+
const client = new ApiClient({ projectId: flags["project"] });
|
|
886
|
+
const pid = client.requireProject();
|
|
887
|
+
const body = {
|
|
888
|
+
content,
|
|
889
|
+
is_internal: flags["internal"] === true
|
|
890
|
+
};
|
|
891
|
+
if (flags["sender-type"]) body["sender_type"] = flags["sender-type"];
|
|
892
|
+
const res = await client.post(`/api/v1/projects/${pid}/conversations/${conv}/messages`, body);
|
|
893
|
+
emit(res.raw?.data ?? res.raw);
|
|
894
|
+
}
|
|
895
|
+
}
|
|
896
|
+
}
|
|
897
|
+
};
|
|
898
|
+
|
|
899
|
+
// src/commands/contacts.ts
|
|
900
|
+
var contacts = {
|
|
901
|
+
summary: "Manage contacts (people on the customer side)",
|
|
902
|
+
subcommands: {
|
|
903
|
+
list: {
|
|
904
|
+
summary: "List contacts",
|
|
905
|
+
usage: "rogeriq contacts list [--q 'search'] [--email ...] [--external-id ...] [--limit 50] [--cursor ...]",
|
|
906
|
+
flags: [
|
|
907
|
+
{ name: "q", type: "string" },
|
|
908
|
+
{ name: "email", type: "string" },
|
|
909
|
+
{ name: "external-id", type: "string" },
|
|
910
|
+
{ name: "limit", type: "string" },
|
|
911
|
+
{ name: "cursor", type: "string" }
|
|
912
|
+
],
|
|
913
|
+
async run({ flags }) {
|
|
914
|
+
const client = new ApiClient({ projectId: flags["project"] });
|
|
915
|
+
const pid = client.requireProject();
|
|
916
|
+
const qs = new URLSearchParams();
|
|
917
|
+
if (flags["q"]) qs.set("q", flags["q"]);
|
|
918
|
+
if (flags["email"]) qs.set("email", flags["email"]);
|
|
919
|
+
if (flags["external-id"]) qs.set("external_id", flags["external-id"]);
|
|
920
|
+
if (flags["limit"]) qs.set("limit", flags["limit"]);
|
|
921
|
+
if (flags["cursor"]) qs.set("cursor", flags["cursor"]);
|
|
922
|
+
const suffix = qs.toString() ? `?${qs}` : "";
|
|
923
|
+
const res = await client.get(`/api/v1/projects/${pid}/contacts${suffix}`);
|
|
924
|
+
emit(res.raw?.data ?? res.raw);
|
|
925
|
+
}
|
|
926
|
+
},
|
|
927
|
+
get: {
|
|
928
|
+
summary: "Get one contact (with conversation history)",
|
|
929
|
+
usage: "rogeriq contacts get <contact-id>",
|
|
930
|
+
async run({ positional, flags }) {
|
|
931
|
+
const [id] = positional;
|
|
932
|
+
if (!id) throw new CliError("Usage: rogeriq contacts get <contact-id>", "BAD_USAGE", 1);
|
|
933
|
+
const client = new ApiClient({ projectId: flags["project"] });
|
|
934
|
+
const pid = client.requireProject();
|
|
935
|
+
const res = await client.get(`/api/v1/projects/${pid}/contacts/${id}`);
|
|
936
|
+
emit(res.raw?.data ?? res.raw);
|
|
937
|
+
}
|
|
938
|
+
},
|
|
939
|
+
upsert: {
|
|
940
|
+
summary: "Create or update a contact by email/external_id",
|
|
941
|
+
usage: "rogeriq contacts upsert [--email ...] [--name ...] [--external-id ...] [--phone ...] [--company ...] [--metadata '{...}']",
|
|
942
|
+
flags: [
|
|
943
|
+
{ name: "email", type: "string" },
|
|
944
|
+
{ name: "name", type: "string" },
|
|
945
|
+
{ name: "phone", type: "string" },
|
|
946
|
+
{ name: "company", type: "string" },
|
|
947
|
+
{ name: "job-title", type: "string" },
|
|
948
|
+
{ name: "external-id", type: "string" },
|
|
949
|
+
{ name: "metadata", type: "string" }
|
|
950
|
+
],
|
|
951
|
+
async run({ flags }) {
|
|
952
|
+
const client = new ApiClient({ projectId: flags["project"] });
|
|
953
|
+
const pid = client.requireProject();
|
|
954
|
+
const body = {};
|
|
955
|
+
if (flags["email"]) body["email"] = flags["email"];
|
|
956
|
+
if (flags["name"]) body["name"] = flags["name"];
|
|
957
|
+
if (flags["phone"]) body["phone"] = flags["phone"];
|
|
958
|
+
if (flags["company"]) body["company"] = flags["company"];
|
|
959
|
+
if (flags["job-title"]) body["job_title"] = flags["job-title"];
|
|
960
|
+
if (flags["external-id"]) body["external_id"] = flags["external-id"];
|
|
961
|
+
if (flags["metadata"]) {
|
|
962
|
+
try {
|
|
963
|
+
body["metadata"] = JSON.parse(flags["metadata"]);
|
|
964
|
+
} catch {
|
|
965
|
+
throw new CliError("--metadata must be valid JSON", "BAD_JSON", 1);
|
|
966
|
+
}
|
|
967
|
+
}
|
|
968
|
+
if (Object.keys(body).length === 0) throw new CliError("Provide at least one field", "NO_FIELDS", 1);
|
|
969
|
+
const res = await client.post(`/api/v1/projects/${pid}/contacts`, body);
|
|
970
|
+
emit(res.raw?.data ?? res.raw);
|
|
971
|
+
}
|
|
972
|
+
},
|
|
973
|
+
update: {
|
|
974
|
+
summary: "Update a contact",
|
|
975
|
+
usage: "rogeriq contacts update <contact-id> [--name ...] [--email ...] etc",
|
|
976
|
+
flags: [
|
|
977
|
+
{ name: "email", type: "string" },
|
|
978
|
+
{ name: "name", type: "string" },
|
|
979
|
+
{ name: "phone", type: "string" },
|
|
980
|
+
{ name: "company", type: "string" },
|
|
981
|
+
{ name: "job-title", type: "string" },
|
|
982
|
+
{ name: "external-id", type: "string" },
|
|
983
|
+
{ name: "metadata", type: "string" }
|
|
984
|
+
],
|
|
985
|
+
async run({ positional, flags }) {
|
|
986
|
+
const [id] = positional;
|
|
987
|
+
if (!id) throw new CliError("Usage: rogeriq contacts update <contact-id>", "BAD_USAGE", 1);
|
|
988
|
+
const client = new ApiClient({ projectId: flags["project"] });
|
|
989
|
+
const pid = client.requireProject();
|
|
990
|
+
const body = {};
|
|
991
|
+
for (const [src, dst] of [
|
|
992
|
+
["email", "email"],
|
|
993
|
+
["name", "name"],
|
|
994
|
+
["phone", "phone"],
|
|
995
|
+
["company", "company"],
|
|
996
|
+
["job-title", "job_title"],
|
|
997
|
+
["external-id", "external_id"]
|
|
998
|
+
]) {
|
|
999
|
+
if (flags[src]) body[dst] = flags[src];
|
|
1000
|
+
}
|
|
1001
|
+
if (flags["metadata"]) {
|
|
1002
|
+
try {
|
|
1003
|
+
body["metadata"] = JSON.parse(flags["metadata"]);
|
|
1004
|
+
} catch {
|
|
1005
|
+
throw new CliError("--metadata must be valid JSON", "BAD_JSON", 1);
|
|
1006
|
+
}
|
|
1007
|
+
}
|
|
1008
|
+
if (Object.keys(body).length === 0) throw new CliError("No updates provided", "NO_UPDATES", 1);
|
|
1009
|
+
const res = await client.patch(`/api/v1/projects/${pid}/contacts/${id}`, body);
|
|
1010
|
+
emit(res.raw?.data ?? res.raw);
|
|
1011
|
+
}
|
|
1012
|
+
}
|
|
1013
|
+
}
|
|
1014
|
+
};
|
|
1015
|
+
|
|
1016
|
+
// src/commands/agent.ts
|
|
1017
|
+
var agent = {
|
|
1018
|
+
summary: "AI agent: status, config, respond, classify",
|
|
1019
|
+
subcommands: {
|
|
1020
|
+
status: {
|
|
1021
|
+
summary: "Show agent status (mode, model, recent activity)",
|
|
1022
|
+
async run({ flags }) {
|
|
1023
|
+
const client = new ApiClient({ projectId: flags["project"] });
|
|
1024
|
+
const pid = client.requireProject();
|
|
1025
|
+
const res = await client.get(`/api/projects/${pid}/agent/status`);
|
|
1026
|
+
emit(res.raw?.data ?? res.raw);
|
|
1027
|
+
}
|
|
1028
|
+
},
|
|
1029
|
+
config: {
|
|
1030
|
+
summary: "Get or update agent config",
|
|
1031
|
+
usage: "rogeriq agent config [--mode autopilot|copilot|assist] [--model openai/gpt-4o]",
|
|
1032
|
+
flags: [
|
|
1033
|
+
{ name: "mode", type: "string" },
|
|
1034
|
+
{ name: "model", type: "string" },
|
|
1035
|
+
{ name: "persona", type: "string" },
|
|
1036
|
+
{ name: "tone", type: "string" }
|
|
1037
|
+
],
|
|
1038
|
+
async run({ flags }) {
|
|
1039
|
+
const client = new ApiClient({ projectId: flags["project"] });
|
|
1040
|
+
const pid = client.requireProject();
|
|
1041
|
+
const body = {};
|
|
1042
|
+
if (flags["mode"]) body["agent_mode"] = flags["mode"];
|
|
1043
|
+
if (flags["model"]) body["ai_model"] = flags["model"];
|
|
1044
|
+
if (flags["persona"]) body["persona"] = flags["persona"];
|
|
1045
|
+
if (flags["tone"]) body["tone"] = flags["tone"];
|
|
1046
|
+
if (Object.keys(body).length === 0) {
|
|
1047
|
+
const res2 = await client.get(`/api/projects/${pid}/agent/config`);
|
|
1048
|
+
emit(res2.raw?.data ?? res2.raw);
|
|
1049
|
+
return;
|
|
1050
|
+
}
|
|
1051
|
+
const res = await client.patch(`/api/projects/${pid}/agent/config`, body);
|
|
1052
|
+
emit(res.raw?.data ?? res.raw);
|
|
1053
|
+
}
|
|
1054
|
+
},
|
|
1055
|
+
models: {
|
|
1056
|
+
summary: "List available LLM models",
|
|
1057
|
+
async run() {
|
|
1058
|
+
const client = new ApiClient();
|
|
1059
|
+
const res = await client.get(`/api/projects/agent/models`);
|
|
1060
|
+
emit(res.raw?.data ?? res.raw);
|
|
1061
|
+
}
|
|
1062
|
+
},
|
|
1063
|
+
respond: {
|
|
1064
|
+
summary: "Trigger AI to draft / send a response on a conversation",
|
|
1065
|
+
usage: "rogeriq agent respond <conversation-id>",
|
|
1066
|
+
async run({ positional, flags }) {
|
|
1067
|
+
const [conv] = positional;
|
|
1068
|
+
if (!conv) throw new CliError("Usage: rogeriq agent respond <conversation-id>", "BAD_USAGE", 1);
|
|
1069
|
+
const client = new ApiClient({ projectId: flags["project"] });
|
|
1070
|
+
const pid = client.requireProject();
|
|
1071
|
+
const res = await client.post(`/api/projects/${pid}/agent/respond`, {
|
|
1072
|
+
conversation_id: conv
|
|
1073
|
+
});
|
|
1074
|
+
emit(res.raw?.data ?? res.raw);
|
|
1075
|
+
}
|
|
1076
|
+
},
|
|
1077
|
+
classify: {
|
|
1078
|
+
summary: "Classify a conversation (intent, sentiment, priority)",
|
|
1079
|
+
usage: "rogeriq agent classify <conversation-id>",
|
|
1080
|
+
async run({ positional, flags }) {
|
|
1081
|
+
const [conv] = positional;
|
|
1082
|
+
if (!conv) throw new CliError("Usage: rogeriq agent classify <conversation-id>", "BAD_USAGE", 1);
|
|
1083
|
+
const client = new ApiClient({ projectId: flags["project"] });
|
|
1084
|
+
const pid = client.requireProject();
|
|
1085
|
+
const res = await client.post(`/api/projects/${pid}/agent/classify`, {
|
|
1086
|
+
conversation_id: conv
|
|
1087
|
+
});
|
|
1088
|
+
emit(res.raw?.data ?? res.raw);
|
|
1089
|
+
}
|
|
1090
|
+
},
|
|
1091
|
+
suggest: {
|
|
1092
|
+
summary: "Generate suggested replies for a conversation",
|
|
1093
|
+
usage: "rogeriq agent suggest <conversation-id>",
|
|
1094
|
+
async run({ positional, flags }) {
|
|
1095
|
+
const [conv] = positional;
|
|
1096
|
+
if (!conv) throw new CliError("Usage: rogeriq agent suggest <conversation-id>", "BAD_USAGE", 1);
|
|
1097
|
+
const client = new ApiClient({ projectId: flags["project"] });
|
|
1098
|
+
const pid = client.requireProject();
|
|
1099
|
+
const res = await client.post(`/api/projects/${pid}/agent/suggest`, {
|
|
1100
|
+
conversation_id: conv
|
|
1101
|
+
});
|
|
1102
|
+
emit(res.raw?.data ?? res.raw);
|
|
1103
|
+
}
|
|
1104
|
+
},
|
|
1105
|
+
"knowledge-map": {
|
|
1106
|
+
summary: "Show what the agent knows (knowledge map)",
|
|
1107
|
+
async run({ flags }) {
|
|
1108
|
+
const client = new ApiClient({ projectId: flags["project"] });
|
|
1109
|
+
const pid = client.requireProject();
|
|
1110
|
+
const res = await client.get(`/api/projects/${pid}/agent/knowledge-map`);
|
|
1111
|
+
emit(res.raw?.data ?? res.raw);
|
|
1112
|
+
}
|
|
1113
|
+
}
|
|
1114
|
+
}
|
|
1115
|
+
};
|
|
1116
|
+
|
|
1117
|
+
// src/commands/kb.ts
|
|
1118
|
+
import { readFileSync as readFileSync2, statSync } from "node:fs";
|
|
1119
|
+
var kb = {
|
|
1120
|
+
summary: "Knowledge base: articles, search, ingest, crawl",
|
|
1121
|
+
subcommands: {
|
|
1122
|
+
list: {
|
|
1123
|
+
summary: "List KB articles",
|
|
1124
|
+
flags: [{ name: "limit", type: "string" }, { name: "cursor", type: "string" }],
|
|
1125
|
+
async run({ flags }) {
|
|
1126
|
+
const client = new ApiClient({ projectId: flags["project"] });
|
|
1127
|
+
const pid = client.requireProject();
|
|
1128
|
+
const qs = new URLSearchParams();
|
|
1129
|
+
if (flags["limit"]) qs.set("limit", flags["limit"]);
|
|
1130
|
+
if (flags["cursor"]) qs.set("cursor", flags["cursor"]);
|
|
1131
|
+
const suffix = qs.toString() ? `?${qs}` : "";
|
|
1132
|
+
const res = await client.get(`/api/projects/${pid}/kb/articles${suffix}`);
|
|
1133
|
+
emit(res.raw?.data ?? res.raw);
|
|
1134
|
+
}
|
|
1135
|
+
},
|
|
1136
|
+
get: {
|
|
1137
|
+
summary: "Get one article",
|
|
1138
|
+
usage: "rogeriq kb get <article-id>",
|
|
1139
|
+
async run({ positional, flags }) {
|
|
1140
|
+
const [id] = positional;
|
|
1141
|
+
if (!id) throw new CliError("Usage: rogeriq kb get <article-id>", "BAD_USAGE", 1);
|
|
1142
|
+
const client = new ApiClient({ projectId: flags["project"] });
|
|
1143
|
+
const pid = client.requireProject();
|
|
1144
|
+
const res = await client.get(`/api/projects/${pid}/kb/articles/${id}`);
|
|
1145
|
+
emit(res.raw?.data ?? res.raw);
|
|
1146
|
+
}
|
|
1147
|
+
},
|
|
1148
|
+
create: {
|
|
1149
|
+
summary: "Create an article",
|
|
1150
|
+
usage: "rogeriq kb create --title 'Title' [--content-file path.md] [--content 'inline'] [--category-id ...]",
|
|
1151
|
+
flags: [
|
|
1152
|
+
{ name: "title", type: "string" },
|
|
1153
|
+
{ name: "content", type: "string" },
|
|
1154
|
+
{ name: "content-file", type: "string" },
|
|
1155
|
+
{ name: "category-id", type: "string" },
|
|
1156
|
+
{ name: "status", type: "string" }
|
|
1157
|
+
],
|
|
1158
|
+
async run({ flags }) {
|
|
1159
|
+
const client = new ApiClient({ projectId: flags["project"] });
|
|
1160
|
+
const pid = client.requireProject();
|
|
1161
|
+
const title = flags["title"];
|
|
1162
|
+
if (!title) throw new CliError("--title is required", "MISSING_TITLE", 1);
|
|
1163
|
+
let content = flags["content"];
|
|
1164
|
+
if (flags["content-file"]) content = readFileSync2(flags["content-file"], "utf8");
|
|
1165
|
+
if (!content) throw new CliError("--content or --content-file is required", "MISSING_CONTENT", 1);
|
|
1166
|
+
const body = { title, content };
|
|
1167
|
+
if (flags["category-id"]) body["category_id"] = flags["category-id"];
|
|
1168
|
+
if (flags["status"]) body["status"] = flags["status"];
|
|
1169
|
+
const res = await client.post(`/api/projects/${pid}/kb/articles`, body);
|
|
1170
|
+
emit(res.raw?.data ?? res.raw);
|
|
1171
|
+
}
|
|
1172
|
+
},
|
|
1173
|
+
update: {
|
|
1174
|
+
summary: "Update an article",
|
|
1175
|
+
usage: "rogeriq kb update <id> [--title ...] [--content-file ...] [--status published|draft]",
|
|
1176
|
+
flags: [
|
|
1177
|
+
{ name: "title", type: "string" },
|
|
1178
|
+
{ name: "content", type: "string" },
|
|
1179
|
+
{ name: "content-file", type: "string" },
|
|
1180
|
+
{ name: "status", type: "string" }
|
|
1181
|
+
],
|
|
1182
|
+
async run({ positional, flags }) {
|
|
1183
|
+
const [id] = positional;
|
|
1184
|
+
if (!id) throw new CliError("Usage: rogeriq kb update <id>", "BAD_USAGE", 1);
|
|
1185
|
+
const client = new ApiClient({ projectId: flags["project"] });
|
|
1186
|
+
const pid = client.requireProject();
|
|
1187
|
+
const body = {};
|
|
1188
|
+
if (flags["title"]) body["title"] = flags["title"];
|
|
1189
|
+
if (flags["content-file"]) body["content"] = readFileSync2(flags["content-file"], "utf8");
|
|
1190
|
+
else if (flags["content"]) body["content"] = flags["content"];
|
|
1191
|
+
if (flags["status"]) body["status"] = flags["status"];
|
|
1192
|
+
if (Object.keys(body).length === 0) throw new CliError("No updates provided", "NO_UPDATES", 1);
|
|
1193
|
+
const res = await client.patch(`/api/projects/${pid}/kb/articles/${id}`, body);
|
|
1194
|
+
emit(res.raw?.data ?? res.raw);
|
|
1195
|
+
}
|
|
1196
|
+
},
|
|
1197
|
+
delete: {
|
|
1198
|
+
summary: "Delete an article",
|
|
1199
|
+
usage: "rogeriq kb delete <id>",
|
|
1200
|
+
async run({ positional, flags }) {
|
|
1201
|
+
const [id] = positional;
|
|
1202
|
+
if (!id) throw new CliError("Usage: rogeriq kb delete <id>", "BAD_USAGE", 1);
|
|
1203
|
+
const client = new ApiClient({ projectId: flags["project"] });
|
|
1204
|
+
const pid = client.requireProject();
|
|
1205
|
+
await client.del(`/api/projects/${pid}/kb/articles/${id}`);
|
|
1206
|
+
emit({ ok: true, deleted: id });
|
|
1207
|
+
}
|
|
1208
|
+
},
|
|
1209
|
+
publish: {
|
|
1210
|
+
summary: "Publish an article",
|
|
1211
|
+
usage: "rogeriq kb publish <id>",
|
|
1212
|
+
async run({ positional, flags }) {
|
|
1213
|
+
const [id] = positional;
|
|
1214
|
+
if (!id) throw new CliError("Usage: rogeriq kb publish <id>", "BAD_USAGE", 1);
|
|
1215
|
+
const client = new ApiClient({ projectId: flags["project"] });
|
|
1216
|
+
const pid = client.requireProject();
|
|
1217
|
+
const res = await client.post(`/api/projects/${pid}/kb/articles/${id}/publish`);
|
|
1218
|
+
emit(res.raw?.data ?? res.raw);
|
|
1219
|
+
}
|
|
1220
|
+
},
|
|
1221
|
+
search: {
|
|
1222
|
+
summary: "Search KB by query",
|
|
1223
|
+
usage: "rogeriq kb search 'how to refund'",
|
|
1224
|
+
async run({ positional, flags }) {
|
|
1225
|
+
const q = positional.join(" ");
|
|
1226
|
+
if (!q) throw new CliError("Usage: rogeriq kb search <query>", "BAD_USAGE", 1);
|
|
1227
|
+
const client = new ApiClient({ projectId: flags["project"] });
|
|
1228
|
+
const pid = client.requireProject();
|
|
1229
|
+
const res = await client.get(`/api/projects/${pid}/kb/search?q=${encodeURIComponent(q)}`);
|
|
1230
|
+
emit(res.raw?.data ?? res.raw);
|
|
1231
|
+
}
|
|
1232
|
+
},
|
|
1233
|
+
ingest: {
|
|
1234
|
+
summary: "Ingest a URL or file as a KB article",
|
|
1235
|
+
usage: "rogeriq kb ingest <url-or-file>",
|
|
1236
|
+
async run({ positional, flags }) {
|
|
1237
|
+
const [target] = positional;
|
|
1238
|
+
if (!target) throw new CliError("Usage: rogeriq kb ingest <url-or-file>", "BAD_USAGE", 1);
|
|
1239
|
+
const client = new ApiClient({ projectId: flags["project"] });
|
|
1240
|
+
const pid = client.requireProject();
|
|
1241
|
+
const isUrl = /^https?:\/\//.test(target);
|
|
1242
|
+
if (isUrl) {
|
|
1243
|
+
const res = await client.post(`/api/projects/${pid}/kb/ingest-url`, { url: target });
|
|
1244
|
+
emit(res.raw?.data ?? res.raw);
|
|
1245
|
+
} else {
|
|
1246
|
+
statSync(target);
|
|
1247
|
+
const content = readFileSync2(target, "utf8");
|
|
1248
|
+
const title = target.split("/").pop().replace(/\.[^.]+$/, "");
|
|
1249
|
+
const res = await client.post(`/api/projects/${pid}/kb/articles`, { title, content });
|
|
1250
|
+
emit(res.raw?.data ?? res.raw);
|
|
1251
|
+
}
|
|
1252
|
+
}
|
|
1253
|
+
},
|
|
1254
|
+
crawl: {
|
|
1255
|
+
summary: "Start a site crawl (or list / get job status)",
|
|
1256
|
+
usage: "rogeriq kb crawl <url> | rogeriq kb crawl status <job-id> | rogeriq kb crawl list",
|
|
1257
|
+
async run({ positional, flags }) {
|
|
1258
|
+
const client = new ApiClient({ projectId: flags["project"] });
|
|
1259
|
+
const pid = client.requireProject();
|
|
1260
|
+
const [arg1, arg2] = positional;
|
|
1261
|
+
if (!arg1) throw new CliError("Usage: rogeriq kb crawl <url|status|list>", "BAD_USAGE", 1);
|
|
1262
|
+
if (arg1 === "list") {
|
|
1263
|
+
const res2 = await client.get(`/api/projects/${pid}/kb/crawl`);
|
|
1264
|
+
emit(res2.raw?.data ?? res2.raw);
|
|
1265
|
+
return;
|
|
1266
|
+
}
|
|
1267
|
+
if (arg1 === "status") {
|
|
1268
|
+
if (!arg2) throw new CliError("Usage: rogeriq kb crawl status <job-id>", "BAD_USAGE", 1);
|
|
1269
|
+
const res2 = await client.get(`/api/projects/${pid}/kb/crawl/${arg2}`);
|
|
1270
|
+
emit(res2.raw?.data ?? res2.raw);
|
|
1271
|
+
return;
|
|
1272
|
+
}
|
|
1273
|
+
const res = await client.post(`/api/projects/${pid}/kb/crawl`, { url: arg1 });
|
|
1274
|
+
emit(res.raw?.data ?? res.raw);
|
|
1275
|
+
}
|
|
1276
|
+
},
|
|
1277
|
+
categories: {
|
|
1278
|
+
summary: "List KB categories",
|
|
1279
|
+
async run({ flags }) {
|
|
1280
|
+
const client = new ApiClient({ projectId: flags["project"] });
|
|
1281
|
+
const pid = client.requireProject();
|
|
1282
|
+
const res = await client.get(`/api/projects/${pid}/kb/categories`);
|
|
1283
|
+
emit(res.raw?.data ?? res.raw);
|
|
1284
|
+
}
|
|
1285
|
+
}
|
|
1286
|
+
}
|
|
1287
|
+
};
|
|
1288
|
+
|
|
1289
|
+
// src/commands/widget.ts
|
|
1290
|
+
var widget = {
|
|
1291
|
+
summary: "Configure the embeddable chat widget",
|
|
1292
|
+
subcommands: {
|
|
1293
|
+
config: {
|
|
1294
|
+
summary: "Get current widget config",
|
|
1295
|
+
async run({ flags }) {
|
|
1296
|
+
const client = new ApiClient({ projectId: flags["project"] });
|
|
1297
|
+
const pid = client.requireProject();
|
|
1298
|
+
const res = await client.get(`/api/v1/projects/${pid}/widget/config`);
|
|
1299
|
+
emit(res.raw?.data ?? res.raw);
|
|
1300
|
+
}
|
|
1301
|
+
},
|
|
1302
|
+
set: {
|
|
1303
|
+
summary: "Update widget config",
|
|
1304
|
+
usage: "rogeriq widget set [--theme dark|light] [--launcher circle|rounded] [--primary-color '#xxxxxx'] [--position bottom-right] [--json-patch '{...}']",
|
|
1305
|
+
flags: [
|
|
1306
|
+
{ name: "theme", type: "string" },
|
|
1307
|
+
{ name: "launcher", type: "string" },
|
|
1308
|
+
{ name: "primary-color", type: "string" },
|
|
1309
|
+
{ name: "position", type: "string" },
|
|
1310
|
+
{ name: "welcome-message", type: "string" },
|
|
1311
|
+
{ name: "json-patch", type: "string" }
|
|
1312
|
+
],
|
|
1313
|
+
async run({ flags }) {
|
|
1314
|
+
const client = new ApiClient({ projectId: flags["project"] });
|
|
1315
|
+
const pid = client.requireProject();
|
|
1316
|
+
let body = {};
|
|
1317
|
+
if (flags["json-patch"]) {
|
|
1318
|
+
try {
|
|
1319
|
+
body = JSON.parse(flags["json-patch"]);
|
|
1320
|
+
} catch {
|
|
1321
|
+
throw new CliError("--json-patch must be valid JSON", "BAD_JSON", 1);
|
|
1322
|
+
}
|
|
1323
|
+
}
|
|
1324
|
+
if (flags["theme"]) body["theme"] = flags["theme"];
|
|
1325
|
+
if (flags["launcher"]) body["launcher_style"] = flags["launcher"];
|
|
1326
|
+
if (flags["primary-color"]) body["primary_color"] = flags["primary-color"];
|
|
1327
|
+
if (flags["position"]) body["position"] = flags["position"];
|
|
1328
|
+
if (flags["welcome-message"]) body["welcome_message"] = flags["welcome-message"];
|
|
1329
|
+
if (Object.keys(body).length === 0) throw new CliError("No fields to update", "NO_UPDATES", 1);
|
|
1330
|
+
const res = await client.patch(`/api/v1/projects/${pid}/widget/config`, body);
|
|
1331
|
+
emit(res.raw?.data ?? res.raw);
|
|
1332
|
+
}
|
|
1333
|
+
},
|
|
1334
|
+
snippet: {
|
|
1335
|
+
summary: "Print the install snippet (HTML <script>) for the widget",
|
|
1336
|
+
async run({ flags }) {
|
|
1337
|
+
const client = new ApiClient({ projectId: flags["project"] });
|
|
1338
|
+
const pid = client.requireProject();
|
|
1339
|
+
const res = await client.get(`/api/projects/${pid}/widget/snippet`);
|
|
1340
|
+
emit(res.raw?.data ?? res.raw);
|
|
1341
|
+
}
|
|
1342
|
+
},
|
|
1343
|
+
"custom-fields": {
|
|
1344
|
+
summary: "List widget custom fields",
|
|
1345
|
+
async run({ flags }) {
|
|
1346
|
+
const client = new ApiClient({ projectId: flags["project"] });
|
|
1347
|
+
const pid = client.requireProject();
|
|
1348
|
+
const res = await client.get(`/api/projects/${pid}/widget/custom-fields`);
|
|
1349
|
+
emit(res.raw?.data ?? res.raw);
|
|
1350
|
+
}
|
|
1351
|
+
}
|
|
1352
|
+
}
|
|
1353
|
+
};
|
|
1354
|
+
|
|
1355
|
+
// src/commands/integrations.ts
|
|
1356
|
+
var integrations = {
|
|
1357
|
+
summary: "Manage third-party integrations (Slack, Shopify, Stripe, Linear, GitHub, Discord)",
|
|
1358
|
+
subcommands: {
|
|
1359
|
+
list: {
|
|
1360
|
+
summary: "List integrations connected to current project",
|
|
1361
|
+
async run({ flags }) {
|
|
1362
|
+
const client = new ApiClient({ projectId: flags["project"] });
|
|
1363
|
+
const pid = client.requireProject();
|
|
1364
|
+
const res = await client.get(`/api/projects/${pid}/integrations`);
|
|
1365
|
+
emit(res.raw?.data ?? res.raw);
|
|
1366
|
+
}
|
|
1367
|
+
},
|
|
1368
|
+
get: {
|
|
1369
|
+
summary: "Get one integration",
|
|
1370
|
+
usage: "rogeriq integrations get <integration-id>",
|
|
1371
|
+
async run({ positional, flags }) {
|
|
1372
|
+
const [id] = positional;
|
|
1373
|
+
if (!id) throw new CliError("Usage: rogeriq integrations get <id>", "BAD_USAGE", 1);
|
|
1374
|
+
const client = new ApiClient({ projectId: flags["project"] });
|
|
1375
|
+
const pid = client.requireProject();
|
|
1376
|
+
const res = await client.get(`/api/projects/${pid}/integrations/${id}`);
|
|
1377
|
+
emit(res.raw?.data ?? res.raw);
|
|
1378
|
+
}
|
|
1379
|
+
},
|
|
1380
|
+
connect: {
|
|
1381
|
+
summary: "Get OAuth install URL for a provider (open in browser to complete)",
|
|
1382
|
+
usage: "rogeriq integrations connect <slack|github|shopify|stripe|linear|discord>",
|
|
1383
|
+
async run({ positional, flags }) {
|
|
1384
|
+
const [provider] = positional;
|
|
1385
|
+
if (!provider) throw new CliError("Usage: rogeriq integrations connect <provider>", "BAD_USAGE", 1);
|
|
1386
|
+
const client = new ApiClient({ projectId: flags["project"] });
|
|
1387
|
+
const pid = client.requireProject();
|
|
1388
|
+
const res = await client.get(`/api/projects/${pid}/integrations/${provider}/install-url`);
|
|
1389
|
+
emit(res.raw?.data ?? res.raw);
|
|
1390
|
+
}
|
|
1391
|
+
},
|
|
1392
|
+
configure: {
|
|
1393
|
+
summary: "Update integration config (provider-specific JSON payload)",
|
|
1394
|
+
usage: "rogeriq integrations configure <id> --config '{...}'",
|
|
1395
|
+
flags: [{ name: "config", type: "string" }],
|
|
1396
|
+
async run({ positional, flags }) {
|
|
1397
|
+
const [id] = positional;
|
|
1398
|
+
const raw = flags["config"];
|
|
1399
|
+
if (!id || !raw) throw new CliError("Usage: rogeriq integrations configure <id> --config '{...}'", "BAD_USAGE", 1);
|
|
1400
|
+
let body;
|
|
1401
|
+
try {
|
|
1402
|
+
body = JSON.parse(raw);
|
|
1403
|
+
} catch {
|
|
1404
|
+
throw new CliError("--config must be valid JSON", "BAD_JSON", 1);
|
|
1405
|
+
}
|
|
1406
|
+
const client = new ApiClient({ projectId: flags["project"] });
|
|
1407
|
+
const pid = client.requireProject();
|
|
1408
|
+
const res = await client.patch(`/api/projects/${pid}/integrations/${id}`, body);
|
|
1409
|
+
emit(res.raw?.data ?? res.raw);
|
|
1410
|
+
}
|
|
1411
|
+
},
|
|
1412
|
+
disconnect: {
|
|
1413
|
+
summary: "Disconnect an integration",
|
|
1414
|
+
usage: "rogeriq integrations disconnect <id>",
|
|
1415
|
+
async run({ positional, flags }) {
|
|
1416
|
+
const [id] = positional;
|
|
1417
|
+
if (!id) throw new CliError("Usage: rogeriq integrations disconnect <id>", "BAD_USAGE", 1);
|
|
1418
|
+
const client = new ApiClient({ projectId: flags["project"] });
|
|
1419
|
+
const pid = client.requireProject();
|
|
1420
|
+
await client.del(`/api/projects/${pid}/integrations/${id}`);
|
|
1421
|
+
emit({ ok: true, disconnected: id });
|
|
1422
|
+
}
|
|
1423
|
+
}
|
|
1424
|
+
}
|
|
1425
|
+
};
|
|
1426
|
+
|
|
1427
|
+
// src/commands/forms.ts
|
|
1428
|
+
import { readFileSync as readFileSync3 } from "node:fs";
|
|
1429
|
+
var forms = {
|
|
1430
|
+
summary: "Manage public-facing intake forms",
|
|
1431
|
+
subcommands: {
|
|
1432
|
+
list: {
|
|
1433
|
+
summary: "List forms",
|
|
1434
|
+
async run({ flags }) {
|
|
1435
|
+
const client = new ApiClient({ projectId: flags["project"] });
|
|
1436
|
+
const pid = client.requireProject();
|
|
1437
|
+
const res = await client.get(`/api/projects/${pid}/forms`);
|
|
1438
|
+
emit(res.raw?.data ?? res.raw);
|
|
1439
|
+
}
|
|
1440
|
+
},
|
|
1441
|
+
get: {
|
|
1442
|
+
summary: "Get one form",
|
|
1443
|
+
usage: "rogeriq forms get <form-id>",
|
|
1444
|
+
async run({ positional, flags }) {
|
|
1445
|
+
const [id] = positional;
|
|
1446
|
+
if (!id) throw new CliError("Usage: rogeriq forms get <id>", "BAD_USAGE", 1);
|
|
1447
|
+
const client = new ApiClient({ projectId: flags["project"] });
|
|
1448
|
+
const pid = client.requireProject();
|
|
1449
|
+
const res = await client.get(`/api/projects/${pid}/forms/${id}`);
|
|
1450
|
+
emit(res.raw?.data ?? res.raw);
|
|
1451
|
+
}
|
|
1452
|
+
},
|
|
1453
|
+
create: {
|
|
1454
|
+
summary: "Create a form (pass schema as JSON or file)",
|
|
1455
|
+
usage: "rogeriq forms create --name 'Contact us' --schema-file form.json",
|
|
1456
|
+
flags: [
|
|
1457
|
+
{ name: "name", type: "string" },
|
|
1458
|
+
{ name: "schema", type: "string" },
|
|
1459
|
+
{ name: "schema-file", type: "string" }
|
|
1460
|
+
],
|
|
1461
|
+
async run({ flags }) {
|
|
1462
|
+
const name = flags["name"];
|
|
1463
|
+
if (!name) throw new CliError("--name required", "MISSING_NAME", 1);
|
|
1464
|
+
let schema;
|
|
1465
|
+
if (flags["schema-file"]) schema = JSON.parse(readFileSync3(flags["schema-file"], "utf8"));
|
|
1466
|
+
else if (flags["schema"]) schema = JSON.parse(flags["schema"]);
|
|
1467
|
+
else throw new CliError("--schema or --schema-file required", "MISSING_SCHEMA", 1);
|
|
1468
|
+
const client = new ApiClient({ projectId: flags["project"] });
|
|
1469
|
+
const pid = client.requireProject();
|
|
1470
|
+
const res = await client.post(`/api/projects/${pid}/forms`, { name, schema });
|
|
1471
|
+
emit(res.raw?.data ?? res.raw);
|
|
1472
|
+
}
|
|
1473
|
+
},
|
|
1474
|
+
update: {
|
|
1475
|
+
summary: "Update a form",
|
|
1476
|
+
usage: "rogeriq forms update <id> [--name ...] [--schema-file ...]",
|
|
1477
|
+
flags: [
|
|
1478
|
+
{ name: "name", type: "string" },
|
|
1479
|
+
{ name: "schema", type: "string" },
|
|
1480
|
+
{ name: "schema-file", type: "string" }
|
|
1481
|
+
],
|
|
1482
|
+
async run({ positional, flags }) {
|
|
1483
|
+
const [id] = positional;
|
|
1484
|
+
if (!id) throw new CliError("Usage: rogeriq forms update <id>", "BAD_USAGE", 1);
|
|
1485
|
+
const body = {};
|
|
1486
|
+
if (flags["name"]) body["name"] = flags["name"];
|
|
1487
|
+
if (flags["schema-file"]) body["schema"] = JSON.parse(readFileSync3(flags["schema-file"], "utf8"));
|
|
1488
|
+
else if (flags["schema"]) body["schema"] = JSON.parse(flags["schema"]);
|
|
1489
|
+
if (Object.keys(body).length === 0) throw new CliError("No updates provided", "NO_UPDATES", 1);
|
|
1490
|
+
const client = new ApiClient({ projectId: flags["project"] });
|
|
1491
|
+
const pid = client.requireProject();
|
|
1492
|
+
const res = await client.patch(`/api/projects/${pid}/forms/${id}`, body);
|
|
1493
|
+
emit(res.raw?.data ?? res.raw);
|
|
1494
|
+
}
|
|
1495
|
+
},
|
|
1496
|
+
archive: {
|
|
1497
|
+
summary: "Archive a form",
|
|
1498
|
+
usage: "rogeriq forms archive <id>",
|
|
1499
|
+
async run({ positional, flags }) {
|
|
1500
|
+
const [id] = positional;
|
|
1501
|
+
if (!id) throw new CliError("Usage: rogeriq forms archive <id>", "BAD_USAGE", 1);
|
|
1502
|
+
const client = new ApiClient({ projectId: flags["project"] });
|
|
1503
|
+
const pid = client.requireProject();
|
|
1504
|
+
const res = await client.post(`/api/projects/${pid}/forms/${id}/archive`);
|
|
1505
|
+
emit(res.raw?.data ?? res.raw);
|
|
1506
|
+
}
|
|
1507
|
+
},
|
|
1508
|
+
submissions: {
|
|
1509
|
+
summary: "List submissions for a form",
|
|
1510
|
+
usage: "rogeriq forms submissions <id>",
|
|
1511
|
+
async run({ positional, flags }) {
|
|
1512
|
+
const [id] = positional;
|
|
1513
|
+
if (!id) throw new CliError("Usage: rogeriq forms submissions <id>", "BAD_USAGE", 1);
|
|
1514
|
+
const client = new ApiClient({ projectId: flags["project"] });
|
|
1515
|
+
const pid = client.requireProject();
|
|
1516
|
+
const res = await client.get(`/api/projects/${pid}/forms/${id}/submissions`);
|
|
1517
|
+
emit(res.raw?.data ?? res.raw);
|
|
1518
|
+
}
|
|
1519
|
+
}
|
|
1520
|
+
}
|
|
1521
|
+
};
|
|
1522
|
+
|
|
1523
|
+
// src/commands/beacons.ts
|
|
1524
|
+
var beacons = {
|
|
1525
|
+
summary: "Manage floating help beacons",
|
|
1526
|
+
subcommands: {
|
|
1527
|
+
list: {
|
|
1528
|
+
summary: "List beacons",
|
|
1529
|
+
async run({ flags }) {
|
|
1530
|
+
const client = new ApiClient({ projectId: flags["project"] });
|
|
1531
|
+
const pid = client.requireProject();
|
|
1532
|
+
const res = await client.get(`/api/projects/${pid}/beacons`);
|
|
1533
|
+
emit(res.raw?.data ?? res.raw);
|
|
1534
|
+
}
|
|
1535
|
+
},
|
|
1536
|
+
get: {
|
|
1537
|
+
summary: "Get one beacon",
|
|
1538
|
+
usage: "rogeriq beacons get <id>",
|
|
1539
|
+
async run({ positional, flags }) {
|
|
1540
|
+
const [id] = positional;
|
|
1541
|
+
if (!id) throw new CliError("Usage: rogeriq beacons get <id>", "BAD_USAGE", 1);
|
|
1542
|
+
const client = new ApiClient({ projectId: flags["project"] });
|
|
1543
|
+
const pid = client.requireProject();
|
|
1544
|
+
const res = await client.get(`/api/projects/${pid}/beacons/${id}`);
|
|
1545
|
+
emit(res.raw?.data ?? res.raw);
|
|
1546
|
+
}
|
|
1547
|
+
},
|
|
1548
|
+
create: {
|
|
1549
|
+
summary: "Create a beacon (provide config JSON)",
|
|
1550
|
+
usage: "rogeriq beacons create --name '...' --config '{...}'",
|
|
1551
|
+
flags: [
|
|
1552
|
+
{ name: "name", type: "string" },
|
|
1553
|
+
{ name: "config", type: "string" }
|
|
1554
|
+
],
|
|
1555
|
+
async run({ flags }) {
|
|
1556
|
+
const name = flags["name"];
|
|
1557
|
+
const cfgStr = flags["config"];
|
|
1558
|
+
if (!name) throw new CliError("--name required", "MISSING_NAME", 1);
|
|
1559
|
+
const body = { name };
|
|
1560
|
+
if (cfgStr) {
|
|
1561
|
+
try {
|
|
1562
|
+
Object.assign(body, JSON.parse(cfgStr));
|
|
1563
|
+
} catch {
|
|
1564
|
+
throw new CliError("--config must be valid JSON", "BAD_JSON", 1);
|
|
1565
|
+
}
|
|
1566
|
+
}
|
|
1567
|
+
const client = new ApiClient({ projectId: flags["project"] });
|
|
1568
|
+
const pid = client.requireProject();
|
|
1569
|
+
const res = await client.post(`/api/projects/${pid}/beacons`, body);
|
|
1570
|
+
emit(res.raw?.data ?? res.raw);
|
|
1571
|
+
}
|
|
1572
|
+
},
|
|
1573
|
+
update: {
|
|
1574
|
+
summary: "Update a beacon",
|
|
1575
|
+
usage: "rogeriq beacons update <id> --config '{...}'",
|
|
1576
|
+
flags: [{ name: "config", type: "string" }],
|
|
1577
|
+
async run({ positional, flags }) {
|
|
1578
|
+
const [id] = positional;
|
|
1579
|
+
const cfgStr = flags["config"];
|
|
1580
|
+
if (!id || !cfgStr) throw new CliError("Usage: rogeriq beacons update <id> --config '{...}'", "BAD_USAGE", 1);
|
|
1581
|
+
let body;
|
|
1582
|
+
try {
|
|
1583
|
+
body = JSON.parse(cfgStr);
|
|
1584
|
+
} catch {
|
|
1585
|
+
throw new CliError("--config must be valid JSON", "BAD_JSON", 1);
|
|
1586
|
+
}
|
|
1587
|
+
const client = new ApiClient({ projectId: flags["project"] });
|
|
1588
|
+
const pid = client.requireProject();
|
|
1589
|
+
const res = await client.patch(`/api/projects/${pid}/beacons/${id}`, body);
|
|
1590
|
+
emit(res.raw?.data ?? res.raw);
|
|
1591
|
+
}
|
|
1592
|
+
},
|
|
1593
|
+
archive: {
|
|
1594
|
+
summary: "Archive a beacon",
|
|
1595
|
+
usage: "rogeriq beacons archive <id>",
|
|
1596
|
+
async run({ positional, flags }) {
|
|
1597
|
+
const [id] = positional;
|
|
1598
|
+
if (!id) throw new CliError("Usage: rogeriq beacons archive <id>", "BAD_USAGE", 1);
|
|
1599
|
+
const client = new ApiClient({ projectId: flags["project"] });
|
|
1600
|
+
const pid = client.requireProject();
|
|
1601
|
+
const res = await client.post(`/api/projects/${pid}/beacons/${id}/archive`);
|
|
1602
|
+
emit(res.raw?.data ?? res.raw);
|
|
1603
|
+
}
|
|
1604
|
+
}
|
|
1605
|
+
}
|
|
1606
|
+
};
|
|
1607
|
+
|
|
1608
|
+
// src/commands/webhooks.ts
|
|
1609
|
+
var webhooks = {
|
|
1610
|
+
summary: "Manage outbound webhooks (event subscriptions)",
|
|
1611
|
+
subcommands: {
|
|
1612
|
+
list: {
|
|
1613
|
+
summary: "List webhooks",
|
|
1614
|
+
async run({ flags }) {
|
|
1615
|
+
const client = new ApiClient({ projectId: flags["project"] });
|
|
1616
|
+
const pid = client.requireProject();
|
|
1617
|
+
const res = await client.get(`/api/v1/projects/${pid}/webhooks`);
|
|
1618
|
+
emit(res.raw?.data ?? res.raw);
|
|
1619
|
+
}
|
|
1620
|
+
},
|
|
1621
|
+
get: {
|
|
1622
|
+
summary: "Get one webhook",
|
|
1623
|
+
usage: "rogeriq webhooks get <id>",
|
|
1624
|
+
async run({ positional, flags }) {
|
|
1625
|
+
const [id] = positional;
|
|
1626
|
+
if (!id) throw new CliError("Usage: rogeriq webhooks get <id>", "BAD_USAGE", 1);
|
|
1627
|
+
const client = new ApiClient({ projectId: flags["project"] });
|
|
1628
|
+
const pid = client.requireProject();
|
|
1629
|
+
const res = await client.get(`/api/v1/projects/${pid}/webhooks/${id}`);
|
|
1630
|
+
emit(res.raw?.data ?? res.raw);
|
|
1631
|
+
}
|
|
1632
|
+
},
|
|
1633
|
+
create: {
|
|
1634
|
+
summary: "Create a webhook subscription",
|
|
1635
|
+
usage: "rogeriq webhooks create --url https://example.com/hook --events conversation.*,message.created",
|
|
1636
|
+
flags: [
|
|
1637
|
+
{ name: "url", type: "string" },
|
|
1638
|
+
{ name: "events", type: "string" }
|
|
1639
|
+
],
|
|
1640
|
+
async run({ flags }) {
|
|
1641
|
+
const url = flags["url"];
|
|
1642
|
+
const events = flags["events"];
|
|
1643
|
+
if (!url || !events) throw new CliError("--url and --events required", "BAD_USAGE", 1);
|
|
1644
|
+
const client = new ApiClient({ projectId: flags["project"] });
|
|
1645
|
+
const pid = client.requireProject();
|
|
1646
|
+
const res = await client.post(`/api/v1/projects/${pid}/webhooks`, {
|
|
1647
|
+
url,
|
|
1648
|
+
events: events.split(",").map((e) => e.trim())
|
|
1649
|
+
});
|
|
1650
|
+
emit(res.raw?.data ?? res.raw);
|
|
1651
|
+
}
|
|
1652
|
+
},
|
|
1653
|
+
update: {
|
|
1654
|
+
summary: "Update a webhook (url, events, or status)",
|
|
1655
|
+
usage: "rogeriq webhooks update <id> [--url ...] [--events ...] [--status active|disabled]",
|
|
1656
|
+
flags: [
|
|
1657
|
+
{ name: "url", type: "string" },
|
|
1658
|
+
{ name: "events", type: "string" },
|
|
1659
|
+
{ name: "status", type: "string" }
|
|
1660
|
+
],
|
|
1661
|
+
async run({ positional, flags }) {
|
|
1662
|
+
const [id] = positional;
|
|
1663
|
+
if (!id) throw new CliError("Usage: rogeriq webhooks update <id>", "BAD_USAGE", 1);
|
|
1664
|
+
const body = {};
|
|
1665
|
+
if (flags["url"]) body["url"] = flags["url"];
|
|
1666
|
+
if (flags["events"]) body["events"] = flags["events"].split(",").map((e) => e.trim());
|
|
1667
|
+
if (flags["status"]) body["status"] = flags["status"];
|
|
1668
|
+
if (Object.keys(body).length === 0) throw new CliError("No updates provided", "NO_UPDATES", 1);
|
|
1669
|
+
const client = new ApiClient({ projectId: flags["project"] });
|
|
1670
|
+
const pid = client.requireProject();
|
|
1671
|
+
const res = await client.patch(`/api/v1/projects/${pid}/webhooks/${id}`, body);
|
|
1672
|
+
emit(res.raw?.data ?? res.raw);
|
|
1673
|
+
}
|
|
1674
|
+
},
|
|
1675
|
+
delete: {
|
|
1676
|
+
summary: "Delete a webhook",
|
|
1677
|
+
usage: "rogeriq webhooks delete <id>",
|
|
1678
|
+
async run({ positional, flags }) {
|
|
1679
|
+
const [id] = positional;
|
|
1680
|
+
if (!id) throw new CliError("Usage: rogeriq webhooks delete <id>", "BAD_USAGE", 1);
|
|
1681
|
+
const client = new ApiClient({ projectId: flags["project"] });
|
|
1682
|
+
const pid = client.requireProject();
|
|
1683
|
+
await client.del(`/api/v1/projects/${pid}/webhooks/${id}`);
|
|
1684
|
+
emit({ ok: true, deleted: id });
|
|
1685
|
+
}
|
|
1686
|
+
},
|
|
1687
|
+
test: {
|
|
1688
|
+
summary: "Fire a sample webhook.test event",
|
|
1689
|
+
usage: "rogeriq webhooks test <id>",
|
|
1690
|
+
async run({ positional, flags }) {
|
|
1691
|
+
const [id] = positional;
|
|
1692
|
+
if (!id) throw new CliError("Usage: rogeriq webhooks test <id>", "BAD_USAGE", 1);
|
|
1693
|
+
const client = new ApiClient({ projectId: flags["project"] });
|
|
1694
|
+
const pid = client.requireProject();
|
|
1695
|
+
const res = await client.post(`/api/v1/projects/${pid}/webhooks/${id}/test`);
|
|
1696
|
+
emit(res.raw?.data ?? res.raw);
|
|
1697
|
+
}
|
|
1698
|
+
},
|
|
1699
|
+
deliveries: {
|
|
1700
|
+
summary: "List recent deliveries (last 50)",
|
|
1701
|
+
usage: "rogeriq webhooks deliveries <id>",
|
|
1702
|
+
async run({ positional, flags }) {
|
|
1703
|
+
const [id] = positional;
|
|
1704
|
+
if (!id) throw new CliError("Usage: rogeriq webhooks deliveries <id>", "BAD_USAGE", 1);
|
|
1705
|
+
const client = new ApiClient({ projectId: flags["project"] });
|
|
1706
|
+
const pid = client.requireProject();
|
|
1707
|
+
const res = await client.get(`/api/v1/projects/${pid}/webhooks/${id}/deliveries`);
|
|
1708
|
+
emit(res.raw?.data ?? res.raw);
|
|
1709
|
+
}
|
|
1710
|
+
},
|
|
1711
|
+
"rotate-secret": {
|
|
1712
|
+
summary: "Rotate the HMAC signing secret",
|
|
1713
|
+
usage: "rogeriq webhooks rotate-secret <id>",
|
|
1714
|
+
async run({ positional, flags }) {
|
|
1715
|
+
const [id] = positional;
|
|
1716
|
+
if (!id) throw new CliError("Usage: rogeriq webhooks rotate-secret <id>", "BAD_USAGE", 1);
|
|
1717
|
+
const client = new ApiClient({ projectId: flags["project"] });
|
|
1718
|
+
const pid = client.requireProject();
|
|
1719
|
+
const res = await client.post(`/api/v1/projects/${pid}/webhooks/${id}/rotate-secret`);
|
|
1720
|
+
emit(res.raw?.data ?? res.raw);
|
|
1721
|
+
}
|
|
1722
|
+
}
|
|
1723
|
+
}
|
|
1724
|
+
};
|
|
1725
|
+
|
|
1726
|
+
// src/commands/analytics.ts
|
|
1727
|
+
var analytics = {
|
|
1728
|
+
summary: "Analytics \u2014 overview, conversation metrics, AI performance",
|
|
1729
|
+
subcommands: {
|
|
1730
|
+
overview: {
|
|
1731
|
+
summary: "Project analytics overview",
|
|
1732
|
+
flags: [{ name: "period", type: "string" }],
|
|
1733
|
+
async run({ flags }) {
|
|
1734
|
+
const client = new ApiClient({ projectId: flags["project"] });
|
|
1735
|
+
const pid = client.requireProject();
|
|
1736
|
+
const qs = flags["period"] ? `?period=${flags["period"]}` : "";
|
|
1737
|
+
const res = await client.get(`/api/projects/${pid}/analytics/overview${qs}`);
|
|
1738
|
+
emit(res.raw?.data ?? res.raw);
|
|
1739
|
+
}
|
|
1740
|
+
},
|
|
1741
|
+
"report-views": {
|
|
1742
|
+
summary: "List saved report views",
|
|
1743
|
+
async run({ flags }) {
|
|
1744
|
+
const client = new ApiClient({ projectId: flags["project"] });
|
|
1745
|
+
const pid = client.requireProject();
|
|
1746
|
+
const res = await client.get(`/api/projects/${pid}/report-views`);
|
|
1747
|
+
emit(res.raw?.data ?? res.raw);
|
|
1748
|
+
}
|
|
1749
|
+
}
|
|
1750
|
+
}
|
|
1751
|
+
};
|
|
1752
|
+
|
|
1753
|
+
// src/commands/status.ts
|
|
1754
|
+
var status = {
|
|
1755
|
+
summary: "One-shot dashboard: open conversations, AI stats, project health",
|
|
1756
|
+
subcommands: {
|
|
1757
|
+
_default: {
|
|
1758
|
+
summary: "Show project status overview",
|
|
1759
|
+
async run({ flags }) {
|
|
1760
|
+
const client = new ApiClient({ projectId: flags["project"] });
|
|
1761
|
+
const pid = client.requireProject();
|
|
1762
|
+
const [counts, agent2, overview] = await Promise.all([
|
|
1763
|
+
client.get(`/api/projects/${pid}/conversations/count`).catch((e) => ({ raw: { error: e.message } })),
|
|
1764
|
+
client.get(`/api/projects/${pid}/agent/status`).catch((e) => ({ raw: { error: e.message } })),
|
|
1765
|
+
client.get(`/api/projects/${pid}/analytics/overview`).catch((e) => ({ raw: { error: e.message } }))
|
|
1766
|
+
]);
|
|
1767
|
+
emit({
|
|
1768
|
+
project_id: pid,
|
|
1769
|
+
conversations: counts.raw?.data ?? counts.raw,
|
|
1770
|
+
agent: agent2.raw?.data ?? agent2.raw,
|
|
1771
|
+
analytics: overview.raw?.data ?? overview.raw
|
|
1772
|
+
});
|
|
1773
|
+
}
|
|
1774
|
+
}
|
|
1775
|
+
}
|
|
1776
|
+
};
|
|
1777
|
+
|
|
1778
|
+
// src/commands/mcp.ts
|
|
1779
|
+
var tools = [
|
|
1780
|
+
{
|
|
1781
|
+
name: "list_conversations",
|
|
1782
|
+
description: "List support conversations (tickets) for the current project. Filter by status, priority, channel, contact, or free-text query.",
|
|
1783
|
+
inputSchema: {
|
|
1784
|
+
type: "object",
|
|
1785
|
+
properties: {
|
|
1786
|
+
status: { type: "string", enum: ["open", "snoozed", "resolved", "closed"] },
|
|
1787
|
+
priority: { type: "string", enum: ["low", "normal", "high", "urgent"] },
|
|
1788
|
+
channel: { type: "string" },
|
|
1789
|
+
contact_id: { type: "string" },
|
|
1790
|
+
q: { type: "string", description: "Free-text search" },
|
|
1791
|
+
limit: { type: "number", default: 50 }
|
|
1792
|
+
}
|
|
1793
|
+
},
|
|
1794
|
+
async call(client, args) {
|
|
1795
|
+
const pid = client.requireProject();
|
|
1796
|
+
const qs = new URLSearchParams();
|
|
1797
|
+
for (const k of ["status", "priority", "channel", "contact_id", "q", "limit"]) {
|
|
1798
|
+
const v = args[k];
|
|
1799
|
+
if (v !== void 0 && v !== null) qs.set(k, String(v));
|
|
1800
|
+
}
|
|
1801
|
+
const res = await client.get(`/api/v1/projects/${pid}/conversations?${qs}`);
|
|
1802
|
+
return res.raw;
|
|
1803
|
+
}
|
|
1804
|
+
},
|
|
1805
|
+
{
|
|
1806
|
+
name: "get_conversation",
|
|
1807
|
+
description: "Fetch a single conversation with its recent messages.",
|
|
1808
|
+
inputSchema: {
|
|
1809
|
+
type: "object",
|
|
1810
|
+
properties: { id: { type: "string" } },
|
|
1811
|
+
required: ["id"]
|
|
1812
|
+
},
|
|
1813
|
+
async call(client, args) {
|
|
1814
|
+
const pid = client.requireProject();
|
|
1815
|
+
const res = await client.get(`/api/v1/projects/${pid}/conversations/${args["id"]}`);
|
|
1816
|
+
return res.raw;
|
|
1817
|
+
}
|
|
1818
|
+
},
|
|
1819
|
+
{
|
|
1820
|
+
name: "reply_to_conversation",
|
|
1821
|
+
description: "Send a reply message on a conversation. Set is_internal=true for an internal note that the customer cannot see.",
|
|
1822
|
+
inputSchema: {
|
|
1823
|
+
type: "object",
|
|
1824
|
+
properties: {
|
|
1825
|
+
conversation_id: { type: "string" },
|
|
1826
|
+
content: { type: "string" },
|
|
1827
|
+
is_internal: { type: "boolean", default: false }
|
|
1828
|
+
},
|
|
1829
|
+
required: ["conversation_id", "content"]
|
|
1830
|
+
},
|
|
1831
|
+
async call(client, args) {
|
|
1832
|
+
const pid = client.requireProject();
|
|
1833
|
+
const res = await client.post(
|
|
1834
|
+
`/api/v1/projects/${pid}/conversations/${args["conversation_id"]}/messages`,
|
|
1835
|
+
{ content: args["content"], is_internal: args["is_internal"] === true }
|
|
1836
|
+
);
|
|
1837
|
+
return res.raw;
|
|
1838
|
+
}
|
|
1839
|
+
},
|
|
1840
|
+
{
|
|
1841
|
+
name: "update_conversation",
|
|
1842
|
+
description: "Update a conversation's status, priority, assignee, or tags.",
|
|
1843
|
+
inputSchema: {
|
|
1844
|
+
type: "object",
|
|
1845
|
+
properties: {
|
|
1846
|
+
id: { type: "string" },
|
|
1847
|
+
status: { type: "string", enum: ["open", "snoozed", "resolved", "closed"] },
|
|
1848
|
+
priority: { type: "string", enum: ["low", "normal", "high", "urgent"] },
|
|
1849
|
+
assigned_to: { type: "string", description: "User ID to assign the conversation to" },
|
|
1850
|
+
tags: { type: "array", items: { type: "string" } },
|
|
1851
|
+
subject: { type: "string" }
|
|
1852
|
+
},
|
|
1853
|
+
required: ["id"]
|
|
1854
|
+
},
|
|
1855
|
+
async call(client, args) {
|
|
1856
|
+
const pid = client.requireProject();
|
|
1857
|
+
const { id, ...body } = args;
|
|
1858
|
+
const res = await client.patch(`/api/v1/projects/${pid}/conversations/${id}`, body);
|
|
1859
|
+
return res.raw;
|
|
1860
|
+
}
|
|
1861
|
+
},
|
|
1862
|
+
{
|
|
1863
|
+
name: "list_contacts",
|
|
1864
|
+
description: "List contacts (customers) for the current project.",
|
|
1865
|
+
inputSchema: {
|
|
1866
|
+
type: "object",
|
|
1867
|
+
properties: {
|
|
1868
|
+
q: { type: "string" },
|
|
1869
|
+
email: { type: "string" },
|
|
1870
|
+
external_id: { type: "string" },
|
|
1871
|
+
limit: { type: "number", default: 50 }
|
|
1872
|
+
}
|
|
1873
|
+
},
|
|
1874
|
+
async call(client, args) {
|
|
1875
|
+
const pid = client.requireProject();
|
|
1876
|
+
const qs = new URLSearchParams();
|
|
1877
|
+
for (const k of ["q", "email", "external_id", "limit"]) {
|
|
1878
|
+
const v = args[k];
|
|
1879
|
+
if (v !== void 0 && v !== null) qs.set(k, String(v));
|
|
1880
|
+
}
|
|
1881
|
+
const res = await client.get(`/api/v1/projects/${pid}/contacts?${qs}`);
|
|
1882
|
+
return res.raw;
|
|
1883
|
+
}
|
|
1884
|
+
},
|
|
1885
|
+
{
|
|
1886
|
+
name: "upsert_contact",
|
|
1887
|
+
description: "Create or update a contact, keyed by email or external_id.",
|
|
1888
|
+
inputSchema: {
|
|
1889
|
+
type: "object",
|
|
1890
|
+
properties: {
|
|
1891
|
+
email: { type: "string" },
|
|
1892
|
+
name: { type: "string" },
|
|
1893
|
+
phone: { type: "string" },
|
|
1894
|
+
external_id: { type: "string" },
|
|
1895
|
+
company: { type: "string" },
|
|
1896
|
+
metadata: { type: "object" }
|
|
1897
|
+
}
|
|
1898
|
+
},
|
|
1899
|
+
async call(client, args) {
|
|
1900
|
+
const pid = client.requireProject();
|
|
1901
|
+
const res = await client.post(`/api/v1/projects/${pid}/contacts`, args);
|
|
1902
|
+
return res.raw;
|
|
1903
|
+
}
|
|
1904
|
+
},
|
|
1905
|
+
{
|
|
1906
|
+
name: "search_knowledge_base",
|
|
1907
|
+
description: "Search the project's knowledge base for relevant articles.",
|
|
1908
|
+
inputSchema: {
|
|
1909
|
+
type: "object",
|
|
1910
|
+
properties: { q: { type: "string" } },
|
|
1911
|
+
required: ["q"]
|
|
1912
|
+
},
|
|
1913
|
+
async call(client, args) {
|
|
1914
|
+
const pid = client.requireProject();
|
|
1915
|
+
const res = await client.get(`/api/projects/${pid}/kb/search?q=${encodeURIComponent(String(args["q"]))}`);
|
|
1916
|
+
return res.raw;
|
|
1917
|
+
}
|
|
1918
|
+
},
|
|
1919
|
+
{
|
|
1920
|
+
name: "list_kb_articles",
|
|
1921
|
+
description: "List knowledge base articles.",
|
|
1922
|
+
inputSchema: { type: "object", properties: {} },
|
|
1923
|
+
async call(client) {
|
|
1924
|
+
const pid = client.requireProject();
|
|
1925
|
+
const res = await client.get(`/api/projects/${pid}/kb/articles`);
|
|
1926
|
+
return res.raw;
|
|
1927
|
+
}
|
|
1928
|
+
},
|
|
1929
|
+
{
|
|
1930
|
+
name: "create_kb_article",
|
|
1931
|
+
description: "Add a new article to the knowledge base.",
|
|
1932
|
+
inputSchema: {
|
|
1933
|
+
type: "object",
|
|
1934
|
+
properties: {
|
|
1935
|
+
title: { type: "string" },
|
|
1936
|
+
content: { type: "string", description: "Markdown body" },
|
|
1937
|
+
category_id: { type: "string" },
|
|
1938
|
+
status: { type: "string", enum: ["draft", "published"] }
|
|
1939
|
+
},
|
|
1940
|
+
required: ["title", "content"]
|
|
1941
|
+
},
|
|
1942
|
+
async call(client, args) {
|
|
1943
|
+
const pid = client.requireProject();
|
|
1944
|
+
const res = await client.post(`/api/projects/${pid}/kb/articles`, args);
|
|
1945
|
+
return res.raw;
|
|
1946
|
+
}
|
|
1947
|
+
},
|
|
1948
|
+
{
|
|
1949
|
+
name: "agent_respond",
|
|
1950
|
+
description: "Ask the AI agent to draft and send a response on a conversation.",
|
|
1951
|
+
inputSchema: {
|
|
1952
|
+
type: "object",
|
|
1953
|
+
properties: { conversation_id: { type: "string" } },
|
|
1954
|
+
required: ["conversation_id"]
|
|
1955
|
+
},
|
|
1956
|
+
async call(client, args) {
|
|
1957
|
+
const pid = client.requireProject();
|
|
1958
|
+
const res = await client.post(`/api/projects/${pid}/agent/respond`, args);
|
|
1959
|
+
return res.raw;
|
|
1960
|
+
}
|
|
1961
|
+
},
|
|
1962
|
+
{
|
|
1963
|
+
name: "list_projects",
|
|
1964
|
+
description: "List projects in the current org.",
|
|
1965
|
+
inputSchema: { type: "object", properties: {} },
|
|
1966
|
+
async call(client) {
|
|
1967
|
+
const orgId = client.requireOrg();
|
|
1968
|
+
const res = await client.get(`/api/v1/orgs/${orgId}/projects`);
|
|
1969
|
+
return res.raw;
|
|
1970
|
+
}
|
|
1971
|
+
},
|
|
1972
|
+
{
|
|
1973
|
+
name: "create_project",
|
|
1974
|
+
description: "Create a new project in the current org.",
|
|
1975
|
+
inputSchema: {
|
|
1976
|
+
type: "object",
|
|
1977
|
+
properties: {
|
|
1978
|
+
name: { type: "string" },
|
|
1979
|
+
domain: { type: "string" }
|
|
1980
|
+
},
|
|
1981
|
+
required: ["name"]
|
|
1982
|
+
},
|
|
1983
|
+
async call(client, args) {
|
|
1984
|
+
const orgId = client.requireOrg();
|
|
1985
|
+
const res = await client.post(`/api/v1/orgs/${orgId}/projects`, args);
|
|
1986
|
+
return res.raw;
|
|
1987
|
+
}
|
|
1988
|
+
},
|
|
1989
|
+
{
|
|
1990
|
+
name: "get_widget_config",
|
|
1991
|
+
description: "Get the current chat widget configuration for the project.",
|
|
1992
|
+
inputSchema: { type: "object", properties: {} },
|
|
1993
|
+
async call(client) {
|
|
1994
|
+
const pid = client.requireProject();
|
|
1995
|
+
const res = await client.get(`/api/v1/projects/${pid}/widget/config`);
|
|
1996
|
+
return res.raw;
|
|
1997
|
+
}
|
|
1998
|
+
},
|
|
1999
|
+
{
|
|
2000
|
+
name: "update_widget_config",
|
|
2001
|
+
description: "Patch the widget configuration (theme, launcher, primary color, etc).",
|
|
2002
|
+
inputSchema: {
|
|
2003
|
+
type: "object",
|
|
2004
|
+
properties: {
|
|
2005
|
+
theme: { type: "string" },
|
|
2006
|
+
launcher_style: { type: "string" },
|
|
2007
|
+
primary_color: { type: "string" },
|
|
2008
|
+
position: { type: "string" },
|
|
2009
|
+
welcome_message: { type: "string" }
|
|
2010
|
+
}
|
|
2011
|
+
},
|
|
2012
|
+
async call(client, args) {
|
|
2013
|
+
const pid = client.requireProject();
|
|
2014
|
+
const res = await client.patch(`/api/v1/projects/${pid}/widget/config`, args);
|
|
2015
|
+
return res.raw;
|
|
2016
|
+
}
|
|
2017
|
+
},
|
|
2018
|
+
{
|
|
2019
|
+
name: "list_integrations",
|
|
2020
|
+
description: "List integrations (Slack, Shopify, Stripe, Linear, GitHub, Discord) connected to the project.",
|
|
2021
|
+
inputSchema: { type: "object", properties: {} },
|
|
2022
|
+
async call(client) {
|
|
2023
|
+
const pid = client.requireProject();
|
|
2024
|
+
const res = await client.get(`/api/projects/${pid}/integrations`);
|
|
2025
|
+
return res.raw;
|
|
2026
|
+
}
|
|
2027
|
+
},
|
|
2028
|
+
{
|
|
2029
|
+
name: "list_webhooks",
|
|
2030
|
+
description: "List outbound webhooks for the project.",
|
|
2031
|
+
inputSchema: { type: "object", properties: {} },
|
|
2032
|
+
async call(client) {
|
|
2033
|
+
const pid = client.requireProject();
|
|
2034
|
+
const res = await client.get(`/api/v1/projects/${pid}/webhooks`);
|
|
2035
|
+
return res.raw;
|
|
2036
|
+
}
|
|
2037
|
+
},
|
|
2038
|
+
{
|
|
2039
|
+
name: "create_webhook",
|
|
2040
|
+
description: "Subscribe a URL to webhook events. Events can use wildcards like conversation.*",
|
|
2041
|
+
inputSchema: {
|
|
2042
|
+
type: "object",
|
|
2043
|
+
properties: {
|
|
2044
|
+
url: { type: "string" },
|
|
2045
|
+
events: { type: "array", items: { type: "string" } }
|
|
2046
|
+
},
|
|
2047
|
+
required: ["url", "events"]
|
|
2048
|
+
},
|
|
2049
|
+
async call(client, args) {
|
|
2050
|
+
const pid = client.requireProject();
|
|
2051
|
+
const res = await client.post(`/api/v1/projects/${pid}/webhooks`, args);
|
|
2052
|
+
return res.raw;
|
|
2053
|
+
}
|
|
2054
|
+
}
|
|
2055
|
+
];
|
|
2056
|
+
var mcp = {
|
|
2057
|
+
summary: "Run an MCP server (stdio) so Claude / agents can call the API directly",
|
|
2058
|
+
subcommands: {
|
|
2059
|
+
_default: {
|
|
2060
|
+
summary: "Start MCP stdio server (use from Claude Desktop / Claude Code)",
|
|
2061
|
+
async run() {
|
|
2062
|
+
await runMcpServer();
|
|
2063
|
+
}
|
|
2064
|
+
},
|
|
2065
|
+
"list-tools": {
|
|
2066
|
+
summary: "Print the list of MCP tools (for inspection / docs)",
|
|
2067
|
+
run() {
|
|
2068
|
+
const list = tools.map((t) => ({
|
|
2069
|
+
name: t.name,
|
|
2070
|
+
description: t.description,
|
|
2071
|
+
inputSchema: t.inputSchema
|
|
2072
|
+
}));
|
|
2073
|
+
process.stdout.write(JSON.stringify(list, null, 2) + "\n");
|
|
2074
|
+
}
|
|
2075
|
+
}
|
|
2076
|
+
}
|
|
2077
|
+
};
|
|
2078
|
+
async function runMcpServer() {
|
|
2079
|
+
let client;
|
|
2080
|
+
try {
|
|
2081
|
+
client = new ApiClient();
|
|
2082
|
+
} catch (e) {
|
|
2083
|
+
logInfo(`MCP: ApiClient init failed: ${e instanceof Error ? e.message : String(e)}`);
|
|
2084
|
+
}
|
|
2085
|
+
process.stdin.setEncoding("utf8");
|
|
2086
|
+
let buffer = "";
|
|
2087
|
+
const handle = async (req) => {
|
|
2088
|
+
switch (req.method) {
|
|
2089
|
+
case "initialize":
|
|
2090
|
+
return {
|
|
2091
|
+
protocolVersion: "2024-11-05",
|
|
2092
|
+
capabilities: { tools: {} },
|
|
2093
|
+
serverInfo: { name: "rogeriq", version: "0.1.0" }
|
|
2094
|
+
};
|
|
2095
|
+
case "notifications/initialized":
|
|
2096
|
+
return void 0;
|
|
2097
|
+
case "tools/list":
|
|
2098
|
+
return {
|
|
2099
|
+
tools: tools.map((t) => ({
|
|
2100
|
+
name: t.name,
|
|
2101
|
+
description: t.description,
|
|
2102
|
+
inputSchema: t.inputSchema
|
|
2103
|
+
}))
|
|
2104
|
+
};
|
|
2105
|
+
case "tools/call": {
|
|
2106
|
+
const name = req.params?.["name"] ?? "";
|
|
2107
|
+
const args = req.params?.["arguments"] ?? {};
|
|
2108
|
+
const tool = tools.find((t) => t.name === name);
|
|
2109
|
+
if (!tool) {
|
|
2110
|
+
return {
|
|
2111
|
+
content: [{ type: "text", text: `Unknown tool: ${name}` }],
|
|
2112
|
+
isError: true
|
|
2113
|
+
};
|
|
2114
|
+
}
|
|
2115
|
+
if (!client) {
|
|
2116
|
+
return {
|
|
2117
|
+
content: [{ type: "text", text: "No API key. Set RIQ_API_KEY env var or run `rogeriq auth login`." }],
|
|
2118
|
+
isError: true
|
|
2119
|
+
};
|
|
2120
|
+
}
|
|
2121
|
+
try {
|
|
2122
|
+
const result = await tool.call(client, args);
|
|
2123
|
+
return {
|
|
2124
|
+
content: [{ type: "text", text: JSON.stringify(result, null, 2) }]
|
|
2125
|
+
};
|
|
2126
|
+
} catch (e) {
|
|
2127
|
+
const msg = e instanceof Error ? e.message : String(e);
|
|
2128
|
+
return {
|
|
2129
|
+
content: [{ type: "text", text: `Error: ${msg}` }],
|
|
2130
|
+
isError: true
|
|
2131
|
+
};
|
|
2132
|
+
}
|
|
2133
|
+
}
|
|
2134
|
+
case "ping":
|
|
2135
|
+
return {};
|
|
2136
|
+
default:
|
|
2137
|
+
throw rpcError(-32601, `Method not found: ${req.method}`);
|
|
2138
|
+
}
|
|
2139
|
+
};
|
|
2140
|
+
process.stdin.on("data", (chunk) => {
|
|
2141
|
+
buffer += chunk;
|
|
2142
|
+
let idx;
|
|
2143
|
+
while ((idx = buffer.indexOf("\n")) >= 0) {
|
|
2144
|
+
const line = buffer.slice(0, idx).trim();
|
|
2145
|
+
buffer = buffer.slice(idx + 1);
|
|
2146
|
+
if (!line) continue;
|
|
2147
|
+
void processLine(line, handle);
|
|
2148
|
+
}
|
|
2149
|
+
});
|
|
2150
|
+
process.stdin.on("end", () => process.exit(0));
|
|
2151
|
+
await new Promise(() => {
|
|
2152
|
+
});
|
|
2153
|
+
}
|
|
2154
|
+
async function processLine(line, handle) {
|
|
2155
|
+
let req;
|
|
2156
|
+
try {
|
|
2157
|
+
req = JSON.parse(line);
|
|
2158
|
+
} catch {
|
|
2159
|
+
return;
|
|
2160
|
+
}
|
|
2161
|
+
try {
|
|
2162
|
+
const result = await handle(req);
|
|
2163
|
+
if (req.id !== void 0 && req.id !== null && result !== void 0) {
|
|
2164
|
+
respond({ jsonrpc: "2.0", id: req.id, result });
|
|
2165
|
+
}
|
|
2166
|
+
} catch (e) {
|
|
2167
|
+
const err = e;
|
|
2168
|
+
if (req.id !== void 0 && req.id !== null) {
|
|
2169
|
+
respond({
|
|
2170
|
+
jsonrpc: "2.0",
|
|
2171
|
+
id: req.id,
|
|
2172
|
+
error: { code: err.code ?? -32603, message: err.message ?? "Internal error" }
|
|
2173
|
+
});
|
|
2174
|
+
}
|
|
2175
|
+
}
|
|
2176
|
+
}
|
|
2177
|
+
function respond(msg) {
|
|
2178
|
+
process.stdout.write(JSON.stringify(msg) + "\n");
|
|
2179
|
+
}
|
|
2180
|
+
function rpcError(code, message) {
|
|
2181
|
+
const err = new Error(message);
|
|
2182
|
+
err.code = code;
|
|
2183
|
+
return err;
|
|
2184
|
+
}
|
|
2185
|
+
|
|
2186
|
+
// src/commands/index.ts
|
|
2187
|
+
var commands = {
|
|
2188
|
+
auth,
|
|
2189
|
+
config,
|
|
2190
|
+
orgs,
|
|
2191
|
+
projects,
|
|
2192
|
+
keys,
|
|
2193
|
+
conversations,
|
|
2194
|
+
messages,
|
|
2195
|
+
contacts,
|
|
2196
|
+
agent,
|
|
2197
|
+
kb,
|
|
2198
|
+
widget,
|
|
2199
|
+
integrations,
|
|
2200
|
+
forms,
|
|
2201
|
+
beacons,
|
|
2202
|
+
webhooks,
|
|
2203
|
+
analytics,
|
|
2204
|
+
status,
|
|
2205
|
+
mcp
|
|
2206
|
+
};
|
|
2207
|
+
|
|
2208
|
+
// src/index.ts
|
|
2209
|
+
var GLOBAL_FLAGS = [
|
|
2210
|
+
{ name: "json", type: "boolean" },
|
|
2211
|
+
{ name: "quiet", alias: "q", type: "boolean" },
|
|
2212
|
+
{ name: "help", alias: "h", type: "boolean" },
|
|
2213
|
+
{ name: "version", alias: "v", type: "boolean" },
|
|
2214
|
+
{ name: "project", type: "string" }
|
|
2215
|
+
];
|
|
2216
|
+
var VERSION = "0.1.0";
|
|
2217
|
+
async function main() {
|
|
2218
|
+
const argv = process.argv.slice(2);
|
|
2219
|
+
const { positional, flags } = parseArgs(argv, GLOBAL_FLAGS);
|
|
2220
|
+
setOutputOpts({
|
|
2221
|
+
json: flagBool(flags, "json"),
|
|
2222
|
+
quiet: flagBool(flags, "quiet")
|
|
2223
|
+
});
|
|
2224
|
+
if (flagBool(flags, "version")) {
|
|
2225
|
+
process.stdout.write(`rogeriq ${VERSION}
|
|
2226
|
+
`);
|
|
2227
|
+
return;
|
|
2228
|
+
}
|
|
2229
|
+
if (positional.length === 0 || flagBool(flags, "help")) {
|
|
2230
|
+
printRootHelp();
|
|
2231
|
+
return;
|
|
2232
|
+
}
|
|
2233
|
+
const [groupName, ...rest] = positional;
|
|
2234
|
+
if (!groupName) {
|
|
2235
|
+
printRootHelp();
|
|
2236
|
+
process.exit(1);
|
|
2237
|
+
}
|
|
2238
|
+
const group = commands[groupName];
|
|
2239
|
+
if (!group) {
|
|
2240
|
+
process.stderr.write(`Unknown command: ${groupName}
|
|
2241
|
+
`);
|
|
2242
|
+
printRootHelp();
|
|
2243
|
+
process.exit(1);
|
|
2244
|
+
}
|
|
2245
|
+
const [subName, ...subArgs] = rest;
|
|
2246
|
+
const cmd = subName && group.subcommands[subName] || group.subcommands["_default"];
|
|
2247
|
+
if (!cmd) {
|
|
2248
|
+
printGroupHelp(groupName, group);
|
|
2249
|
+
process.exit(1);
|
|
2250
|
+
}
|
|
2251
|
+
const cmdArgsRaw = subName && group.subcommands[subName] ? subArgs : rest;
|
|
2252
|
+
const cmdParsed = parseArgs(cmdArgsRaw, [...GLOBAL_FLAGS, ...cmd.flags ?? []]);
|
|
2253
|
+
const project = flagString(cmdParsed.flags, "project") ?? flagString(flags, "project");
|
|
2254
|
+
if (project) cmdParsed.flags["project"] = project;
|
|
2255
|
+
if (flagBool(cmdParsed.flags, "help")) {
|
|
2256
|
+
printCommandHelp(groupName, subName ?? "", cmd);
|
|
2257
|
+
return;
|
|
2258
|
+
}
|
|
2259
|
+
try {
|
|
2260
|
+
await cmd.run(cmdParsed);
|
|
2261
|
+
} catch (e) {
|
|
2262
|
+
if (e instanceof CliError) {
|
|
2263
|
+
logError(e);
|
|
2264
|
+
process.exit(e.exitCode);
|
|
2265
|
+
}
|
|
2266
|
+
if (e instanceof Error) {
|
|
2267
|
+
logError(e);
|
|
2268
|
+
process.exit(1);
|
|
2269
|
+
}
|
|
2270
|
+
process.stderr.write(String(e) + "\n");
|
|
2271
|
+
process.exit(1);
|
|
2272
|
+
}
|
|
2273
|
+
}
|
|
2274
|
+
function printRootHelp() {
|
|
2275
|
+
const lines = [
|
|
2276
|
+
`rogeriq ${VERSION} \u2014 RogerIQ CLI`,
|
|
2277
|
+
"",
|
|
2278
|
+
"Usage:",
|
|
2279
|
+
" rogeriq <command> <subcommand> [options]",
|
|
2280
|
+
"",
|
|
2281
|
+
"Commands:"
|
|
2282
|
+
];
|
|
2283
|
+
for (const [name, group] of Object.entries(commands)) {
|
|
2284
|
+
lines.push(` ${name.padEnd(16)} ${group.summary}`);
|
|
2285
|
+
}
|
|
2286
|
+
lines.push(
|
|
2287
|
+
"",
|
|
2288
|
+
"Global flags:",
|
|
2289
|
+
" --json Machine-readable JSON output (auto-enabled when piped)",
|
|
2290
|
+
" --quiet, -q Print only IDs",
|
|
2291
|
+
" --project <id> Override project ID for this command",
|
|
2292
|
+
" --help, -h Show help",
|
|
2293
|
+
" --version, -v Show version",
|
|
2294
|
+
"",
|
|
2295
|
+
"Auth:",
|
|
2296
|
+
" rogeriq auth login --api-key riq_xxx",
|
|
2297
|
+
" RIQ_API_KEY=riq_xxx rogeriq conversations list",
|
|
2298
|
+
"",
|
|
2299
|
+
"AI agents: see AGENTS.md or run `rogeriq mcp` for an MCP server."
|
|
2300
|
+
);
|
|
2301
|
+
process.stdout.write(lines.join("\n") + "\n");
|
|
2302
|
+
}
|
|
2303
|
+
function printGroupHelp(name, group) {
|
|
2304
|
+
const lines = [`rogeriq ${name} \u2014 ${group.summary}`, "", "Subcommands:"];
|
|
2305
|
+
for (const [subName, cmd] of Object.entries(group.subcommands)) {
|
|
2306
|
+
if (subName === "_default") continue;
|
|
2307
|
+
lines.push(` ${subName.padEnd(16)} ${cmd.summary ?? ""}`);
|
|
2308
|
+
}
|
|
2309
|
+
process.stdout.write(lines.join("\n") + "\n");
|
|
2310
|
+
}
|
|
2311
|
+
function printCommandHelp(group, sub, cmd) {
|
|
2312
|
+
const lines = [`rogeriq ${group} ${sub} \u2014 ${cmd.summary ?? ""}`];
|
|
2313
|
+
if (cmd.usage) lines.push("", `Usage: ${cmd.usage}`);
|
|
2314
|
+
if (cmd.flags?.length) {
|
|
2315
|
+
lines.push("", "Flags:");
|
|
2316
|
+
for (const f of cmd.flags) {
|
|
2317
|
+
lines.push(` --${f.name}${f.alias ? `, -${f.alias}` : ""} (${f.type ?? "boolean"})`);
|
|
2318
|
+
}
|
|
2319
|
+
}
|
|
2320
|
+
if (cmd.examples?.length) {
|
|
2321
|
+
lines.push("", "Examples:");
|
|
2322
|
+
for (const ex of cmd.examples) lines.push(` ${ex}`);
|
|
2323
|
+
}
|
|
2324
|
+
process.stdout.write(lines.join("\n") + "\n");
|
|
2325
|
+
}
|
|
2326
|
+
main();
|