@senso-ai/cli 0.2.3 → 0.3.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/README.md +53 -63
- package/dist/cli.js +249 -292
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -104,7 +104,7 @@ For non-interactive use (CI, agents), set the env var or pass the flag.
|
|
|
104
104
|
### Authentication
|
|
105
105
|
|
|
106
106
|
```
|
|
107
|
-
senso login
|
|
107
|
+
senso login Authenticate with Senso (interactive)
|
|
108
108
|
senso logout Remove stored credentials
|
|
109
109
|
senso whoami Show current org and auth status
|
|
110
110
|
```
|
|
@@ -112,9 +112,9 @@ senso whoami Show current org and auth status
|
|
|
112
112
|
### Search
|
|
113
113
|
|
|
114
114
|
```
|
|
115
|
-
senso search <query> Semantic search with AI-generated answer
|
|
116
|
-
senso search context <query>
|
|
117
|
-
senso search content <query>
|
|
115
|
+
senso search <query> Semantic search with AI-generated answer + source chunks
|
|
116
|
+
senso search context <query> Chunks only (no AI answer, faster)
|
|
117
|
+
senso search content <query> Content IDs only (deduplicated)
|
|
118
118
|
```
|
|
119
119
|
|
|
120
120
|
Options: `--max-results <n>`
|
|
@@ -122,111 +122,101 @@ Options: `--max-results <n>`
|
|
|
122
122
|
### Content
|
|
123
123
|
|
|
124
124
|
```
|
|
125
|
-
senso content list List
|
|
126
|
-
senso content get <id> Get content
|
|
127
|
-
senso content delete <id> Delete content (
|
|
128
|
-
senso content unpublish <id> Unpublish
|
|
129
|
-
senso content verification List
|
|
125
|
+
senso content list List all knowledge base items
|
|
126
|
+
senso content get <id> Get full content detail by ID
|
|
127
|
+
senso content delete <id> Delete content (knowledge base + external)
|
|
128
|
+
senso content unpublish <id> Unpublish and revert to draft
|
|
129
|
+
senso content verification List items in verification workflow
|
|
130
130
|
senso content reject <versionId> Reject a content version
|
|
131
|
-
senso content restore <versionId> Restore
|
|
132
|
-
senso content owners <id> List content
|
|
133
|
-
senso content set-owners <id> Replace
|
|
134
|
-
senso content remove-owner <id> <userId> Remove
|
|
131
|
+
senso content restore <versionId> Restore rejected version to draft
|
|
132
|
+
senso content owners <id> List owners of a content item
|
|
133
|
+
senso content set-owners <id> Replace owners (--user-ids)
|
|
134
|
+
senso content remove-owner <id> <userId> Remove a single owner
|
|
135
135
|
```
|
|
136
136
|
|
|
137
|
+
Options: `content list` supports `--limit`, `--offset`, `--search`, `--sort`. `content verification` supports `--limit`, `--offset`, `--search`, `--status`. `content reject` supports `--reason`.
|
|
138
|
+
|
|
137
139
|
### Content Generation
|
|
138
140
|
|
|
139
141
|
```
|
|
140
|
-
senso generate settings Get
|
|
141
|
-
senso generate update-settings Update
|
|
142
|
-
senso generate sample Generate
|
|
143
|
-
senso generate run Trigger content engine run
|
|
142
|
+
senso generate settings Get generation settings
|
|
143
|
+
senso generate update-settings Update generation settings (--data)
|
|
144
|
+
senso generate sample Generate sample for a prompt (--prompt-id, --content-type-id)
|
|
145
|
+
senso generate run Trigger a content engine run (--prompt-ids)
|
|
144
146
|
```
|
|
145
147
|
|
|
146
148
|
### Content Engine
|
|
147
149
|
|
|
148
150
|
```
|
|
149
|
-
senso engine publish Publish content
|
|
150
|
-
senso engine draft Save content as draft
|
|
151
|
+
senso engine publish Publish content to external destinations (--data)
|
|
152
|
+
senso engine draft Save content as draft for review (--data)
|
|
151
153
|
```
|
|
152
154
|
|
|
153
155
|
### Ingestion
|
|
154
156
|
|
|
155
157
|
```
|
|
156
|
-
senso ingest upload
|
|
157
|
-
senso ingest reprocess <contentId>
|
|
158
|
-
```
|
|
159
|
-
|
|
160
|
-
### Categories & Topics
|
|
161
|
-
|
|
162
|
-
```
|
|
163
|
-
senso categories list List categories
|
|
164
|
-
senso categories list-all List all categories with their topics
|
|
165
|
-
senso categories create <name> Create a category
|
|
166
|
-
senso categories get <id> Get category by ID
|
|
167
|
-
senso categories update <id> Update a category
|
|
168
|
-
senso categories delete <id> Delete a category
|
|
169
|
-
senso categories batch-create Batch create categories with topics
|
|
170
|
-
|
|
171
|
-
senso topics list <categoryId> List topics in a category
|
|
172
|
-
senso topics create <categoryId> Create a topic
|
|
173
|
-
senso topics get <catId> <topicId> Get a topic
|
|
174
|
-
senso topics update <catId> <topicId> Update a topic
|
|
175
|
-
senso topics delete <catId> <topicId> Delete a topic
|
|
176
|
-
senso topics batch-create <catId> Batch create topics
|
|
158
|
+
senso ingest upload <files...> Upload files to knowledge base (up to 10)
|
|
159
|
+
senso ingest reprocess <contentId> <file> Re-ingest content with new file
|
|
177
160
|
```
|
|
178
161
|
|
|
179
162
|
### Brand Kit & Content Types
|
|
180
163
|
|
|
181
164
|
```
|
|
182
|
-
senso brand-kit get Get brand kit
|
|
183
|
-
senso brand-kit set
|
|
165
|
+
senso brand-kit get Get brand kit guidelines
|
|
166
|
+
senso brand-kit set Create or replace brand kit (--data)
|
|
184
167
|
|
|
185
168
|
senso content-types list List content types
|
|
186
|
-
senso content-types create Create a content type
|
|
187
|
-
senso content-types get <id> Get content type
|
|
188
|
-
senso content-types update <id> Update content type
|
|
189
|
-
senso content-types delete <id> Delete content type
|
|
169
|
+
senso content-types create Create a content type (--data)
|
|
170
|
+
senso content-types get <id> Get content type by ID
|
|
171
|
+
senso content-types update <id> Update a content type (--data)
|
|
172
|
+
senso content-types delete <id> Delete a content type
|
|
190
173
|
```
|
|
191
174
|
|
|
175
|
+
Options: `content-types list` supports `--limit`, `--offset`.
|
|
176
|
+
|
|
192
177
|
### Prompts
|
|
193
178
|
|
|
194
179
|
```
|
|
195
|
-
senso prompts list List prompts
|
|
196
|
-
senso prompts create Create a prompt
|
|
197
|
-
senso prompts get <promptId> Get prompt with
|
|
180
|
+
senso prompts list List prompts (geo questions)
|
|
181
|
+
senso prompts create Create a prompt (--data)
|
|
182
|
+
senso prompts get <promptId> Get prompt with run history
|
|
198
183
|
senso prompts delete <promptId> Delete a prompt
|
|
199
184
|
```
|
|
200
185
|
|
|
186
|
+
Options: `prompts list` supports `--limit`, `--offset`, `--search`, `--sort`.
|
|
187
|
+
|
|
201
188
|
### Organization
|
|
202
189
|
|
|
203
190
|
```
|
|
204
191
|
senso org get Get organization details
|
|
205
|
-
senso org update Update organization details
|
|
192
|
+
senso org update Update organization details (--data)
|
|
206
193
|
|
|
207
194
|
senso users list List users
|
|
208
|
-
senso users add Add a user
|
|
209
|
-
senso users get <userId> Get
|
|
210
|
-
senso users update <userId> Update a user's role
|
|
195
|
+
senso users add Add a user (--data)
|
|
196
|
+
senso users get <userId> Get user details
|
|
197
|
+
senso users update <userId> Update a user's role (--data)
|
|
211
198
|
senso users remove <userId> Remove a user
|
|
199
|
+
senso users set-current <userId> Set org as current for a user
|
|
212
200
|
|
|
213
201
|
senso api-keys list List API keys
|
|
214
|
-
senso api-keys create Create API key
|
|
202
|
+
senso api-keys create Create API key (--data)
|
|
215
203
|
senso api-keys get <keyId> Get API key details
|
|
216
|
-
senso api-keys update <keyId> Update API key
|
|
204
|
+
senso api-keys update <keyId> Update API key (--data)
|
|
217
205
|
senso api-keys delete <keyId> Delete API key
|
|
218
206
|
senso api-keys revoke <keyId> Revoke API key
|
|
219
207
|
|
|
220
208
|
senso members list List organization members
|
|
221
209
|
```
|
|
222
210
|
|
|
211
|
+
Options: `users list` supports `--limit`, `--offset`. `api-keys list` supports `--limit`, `--offset`. `members list` supports `--limit`, `--offset`, `--search`, `--sort`.
|
|
212
|
+
|
|
223
213
|
### Run Configuration
|
|
224
214
|
|
|
225
215
|
```
|
|
226
216
|
senso run-config models Get configured AI models
|
|
227
|
-
senso run-config set-models Set AI models
|
|
228
|
-
senso run-config schedule Get run schedule
|
|
229
|
-
senso run-config set-schedule Set run schedule
|
|
217
|
+
senso run-config set-models Set AI models (--data)
|
|
218
|
+
senso run-config schedule Get run schedule (days of week)
|
|
219
|
+
senso run-config set-schedule Set run schedule (--data)
|
|
230
220
|
```
|
|
231
221
|
|
|
232
222
|
### Notifications
|
|
@@ -236,6 +226,8 @@ senso notifications list List notifications
|
|
|
236
226
|
senso notifications read <id> Mark notification as read
|
|
237
227
|
```
|
|
238
228
|
|
|
229
|
+
Options: `notifications list` supports `--limit`, `--offset`, `--unread-only`.
|
|
230
|
+
|
|
239
231
|
### CLI Management
|
|
240
232
|
|
|
241
233
|
```
|
|
@@ -352,20 +344,18 @@ npm test
|
|
|
352
344
|
```
|
|
353
345
|
src/
|
|
354
346
|
├── cli.ts # Entry point — arg parsing, command dispatch
|
|
355
|
-
├── commands/ # One file per command group (
|
|
347
|
+
├── commands/ # One file per command group (16 files)
|
|
356
348
|
│ ├── auth.ts # login, logout, whoami
|
|
357
349
|
│ ├── search.ts # search, search context, search content
|
|
358
350
|
│ ├── content.ts # CRUD + verification + owners
|
|
359
351
|
│ ├── generate.ts # content generation settings + triggers
|
|
360
352
|
│ ├── engine.ts # publish, draft
|
|
361
|
-
│ ├── ingest.ts # upload, reprocess
|
|
362
|
-
│ ├── categories.ts # CRUD + batch
|
|
363
|
-
│ ├── topics.ts # CRUD + batch
|
|
353
|
+
│ ├── ingest.ts # upload, reprocess (with S3 upload)
|
|
364
354
|
│ ├── brand-kit.ts # get, set
|
|
365
355
|
│ ├── content-types.ts # CRUD
|
|
366
356
|
│ ├── prompts.ts # CRUD
|
|
367
357
|
│ ├── org.ts # get, update
|
|
368
|
-
│ ├── users.ts # CRUD
|
|
358
|
+
│ ├── users.ts # CRUD + set-current
|
|
369
359
|
│ ├── api-keys.ts # CRUD + revoke
|
|
370
360
|
│ ├── members.ts # list
|
|
371
361
|
│ ├── run-config.ts # models, schedule
|
package/dist/cli.js
CHANGED
|
@@ -211,17 +211,23 @@ async function apiRequest(opts) {
|
|
|
211
211
|
});
|
|
212
212
|
if (!res.ok) {
|
|
213
213
|
let body;
|
|
214
|
+
const text3 = await res.text();
|
|
214
215
|
try {
|
|
215
|
-
body =
|
|
216
|
+
body = JSON.parse(text3);
|
|
216
217
|
} catch {
|
|
217
|
-
body =
|
|
218
|
+
body = text3;
|
|
218
219
|
}
|
|
219
220
|
throw new ApiError(res.status, res.statusText, body);
|
|
220
221
|
}
|
|
221
222
|
if (res.status === 204) {
|
|
222
223
|
return void 0;
|
|
223
224
|
}
|
|
224
|
-
|
|
225
|
+
const text2 = await res.text();
|
|
226
|
+
try {
|
|
227
|
+
return JSON.parse(text2);
|
|
228
|
+
} catch {
|
|
229
|
+
throw new Error(`Invalid JSON response from ${opts.path}`);
|
|
230
|
+
}
|
|
225
231
|
} finally {
|
|
226
232
|
clearTimeout(timeout);
|
|
227
233
|
}
|
|
@@ -280,7 +286,7 @@ async function verifyApiKey(apiKey, baseUrl) {
|
|
|
280
286
|
});
|
|
281
287
|
}
|
|
282
288
|
function registerAuthCommands(program2) {
|
|
283
|
-
program2.command("login").description("
|
|
289
|
+
program2.command("login").description("Authenticate with Senso. Paste your API key and it will be validated against your organization, then stored locally.").action(async () => {
|
|
284
290
|
const opts = program2.opts();
|
|
285
291
|
banner();
|
|
286
292
|
console.log(` ${pc3.bold("Welcome to Senso CLI!")}
|
|
@@ -322,11 +328,11 @@ function registerAuthCommands(program2) {
|
|
|
322
328
|
process.exit(1);
|
|
323
329
|
}
|
|
324
330
|
});
|
|
325
|
-
program2.command("logout").description("Remove stored
|
|
331
|
+
program2.command("logout").description("Remove stored API key and organization info from local config.").action(() => {
|
|
326
332
|
clearConfig();
|
|
327
333
|
success("Credentials removed.");
|
|
328
334
|
});
|
|
329
|
-
program2.command("whoami").description("Show
|
|
335
|
+
program2.command("whoami").description("Show which organization you are authenticated as, including org ID, slug, tier, and API key prefix.").action(async () => {
|
|
330
336
|
const opts = program2.opts();
|
|
331
337
|
const apiKey = getApiKey({ apiKey: opts.apiKey });
|
|
332
338
|
if (!apiKey) {
|
|
@@ -372,8 +378,8 @@ function registerAuthCommands(program2) {
|
|
|
372
378
|
|
|
373
379
|
// src/commands/org.ts
|
|
374
380
|
function registerOrgCommands(program2) {
|
|
375
|
-
const org = program2.command("org").description("
|
|
376
|
-
org.command("get").description("Get organization details").action(async () => {
|
|
381
|
+
const org = program2.command("org").description("View and update organization profile and settings. Includes name, slug, logo, websites, locations, and tier information.");
|
|
382
|
+
org.command("get").description("Get full organization details including name, slug, tier, websites, locations, configured AI models, publishers, and schedule.").action(async () => {
|
|
377
383
|
const opts = program2.opts();
|
|
378
384
|
try {
|
|
379
385
|
const data = await apiRequest({ path: "/org/me", apiKey: opts.apiKey, baseUrl: opts.baseUrl });
|
|
@@ -383,12 +389,12 @@ function registerOrgCommands(program2) {
|
|
|
383
389
|
process.exit(1);
|
|
384
390
|
}
|
|
385
391
|
});
|
|
386
|
-
org.command("update").description("Update organization details").requiredOption("--data <json>",
|
|
392
|
+
org.command("update").description("Update organization details. All fields are optional \u2014 only provided fields are changed. Pass an empty array for websites/locations to clear them.").requiredOption("--data <json>", 'JSON: { "name": "...", "slug": "...", "logo_url": "...", "websites": [...], "locations": [...] }').action(async (cmdOpts) => {
|
|
387
393
|
const opts = program2.opts();
|
|
388
394
|
try {
|
|
389
395
|
const body = JSON.parse(cmdOpts.data);
|
|
390
396
|
const data = await apiRequest({
|
|
391
|
-
method: "
|
|
397
|
+
method: "PUT",
|
|
392
398
|
path: "/org/me",
|
|
393
399
|
body,
|
|
394
400
|
apiKey: opts.apiKey,
|
|
@@ -405,18 +411,23 @@ function registerOrgCommands(program2) {
|
|
|
405
411
|
|
|
406
412
|
// src/commands/users.ts
|
|
407
413
|
function registerUserCommands(program2) {
|
|
408
|
-
const users = program2.command("users").description("Manage users
|
|
409
|
-
users.command("list").description("List users in organization").action(async () => {
|
|
414
|
+
const users = program2.command("users").description("Manage users within the organization. Add, update roles, remove users, or set the active organization for a user.");
|
|
415
|
+
users.command("list").description("List all users in the organization. Returns user IDs, roles, and membership status.").option("--limit <n>", "Maximum number of users to return").option("--offset <n>", "Number of users to skip (for pagination)").action(async (cmdOpts) => {
|
|
410
416
|
const opts = program2.opts();
|
|
411
417
|
try {
|
|
412
|
-
const data = await apiRequest({
|
|
418
|
+
const data = await apiRequest({
|
|
419
|
+
path: "/org/users",
|
|
420
|
+
params: { limit: cmdOpts.limit, offset: cmdOpts.offset },
|
|
421
|
+
apiKey: opts.apiKey,
|
|
422
|
+
baseUrl: opts.baseUrl
|
|
423
|
+
});
|
|
413
424
|
console.log(JSON.stringify(data, null, 2));
|
|
414
425
|
} catch (err) {
|
|
415
426
|
error(formatApiError(err));
|
|
416
427
|
process.exit(1);
|
|
417
428
|
}
|
|
418
429
|
});
|
|
419
|
-
users.command("add").description("Add user to organization").requiredOption("--data <json>",
|
|
430
|
+
users.command("add").description("Add an existing platform user to the organization. Requires user_id and role_id.").requiredOption("--data <json>", 'JSON: { "user_id": "uuid", "role_id": "uuid", "is_current": false }').action(async (cmdOpts) => {
|
|
420
431
|
const opts = program2.opts();
|
|
421
432
|
try {
|
|
422
433
|
const body = JSON.parse(cmdOpts.data);
|
|
@@ -434,7 +445,7 @@ function registerUserCommands(program2) {
|
|
|
434
445
|
process.exit(1);
|
|
435
446
|
}
|
|
436
447
|
});
|
|
437
|
-
users.command("get <userId>").description("Get a user in the organization").action(async (userId) => {
|
|
448
|
+
users.command("get <userId>").description("Get a user's details including their role and membership status in the organization.").action(async (userId) => {
|
|
438
449
|
const opts = program2.opts();
|
|
439
450
|
try {
|
|
440
451
|
const data = await apiRequest({ path: `/org/users/${userId}`, apiKey: opts.apiKey, baseUrl: opts.baseUrl });
|
|
@@ -444,12 +455,12 @@ function registerUserCommands(program2) {
|
|
|
444
455
|
process.exit(1);
|
|
445
456
|
}
|
|
446
457
|
});
|
|
447
|
-
users.command("update <userId>").description("Update a user's role").requiredOption("--data <json>",
|
|
458
|
+
users.command("update <userId>").description("Update a user's role in the organization. Requires role_id in the JSON body.").requiredOption("--data <json>", 'JSON: { "role_id": "uuid", "is_current": true }').action(async (userId, cmdOpts) => {
|
|
448
459
|
const opts = program2.opts();
|
|
449
460
|
try {
|
|
450
461
|
const body = JSON.parse(cmdOpts.data);
|
|
451
462
|
const data = await apiRequest({
|
|
452
|
-
method: "
|
|
463
|
+
method: "PUT",
|
|
453
464
|
path: `/org/users/${userId}`,
|
|
454
465
|
body,
|
|
455
466
|
apiKey: opts.apiKey,
|
|
@@ -462,7 +473,7 @@ function registerUserCommands(program2) {
|
|
|
462
473
|
process.exit(1);
|
|
463
474
|
}
|
|
464
475
|
});
|
|
465
|
-
users.command("remove <userId>").description("Remove a user from the organization").action(async (userId) => {
|
|
476
|
+
users.command("remove <userId>").description("Remove a user from the organization. This does not delete the platform user account.").action(async (userId) => {
|
|
466
477
|
const opts = program2.opts();
|
|
467
478
|
try {
|
|
468
479
|
await apiRequest({ method: "DELETE", path: `/org/users/${userId}`, apiKey: opts.apiKey, baseUrl: opts.baseUrl });
|
|
@@ -472,82 +483,17 @@ function registerUserCommands(program2) {
|
|
|
472
483
|
process.exit(1);
|
|
473
484
|
}
|
|
474
485
|
});
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
// src/commands/api-keys.ts
|
|
478
|
-
function registerApiKeyCommands(program2) {
|
|
479
|
-
const keys = program2.command("api-keys").description("Manage API keys");
|
|
480
|
-
keys.command("list").description("List API keys").action(async () => {
|
|
481
|
-
const opts = program2.opts();
|
|
482
|
-
try {
|
|
483
|
-
const data = await apiRequest({ path: "/org/api-keys", apiKey: opts.apiKey, baseUrl: opts.baseUrl });
|
|
484
|
-
console.log(JSON.stringify(data, null, 2));
|
|
485
|
-
} catch (err) {
|
|
486
|
-
error(formatApiError(err));
|
|
487
|
-
process.exit(1);
|
|
488
|
-
}
|
|
489
|
-
});
|
|
490
|
-
keys.command("create").description("Create API key").requiredOption("--data <json>", "JSON key configuration").action(async (cmdOpts) => {
|
|
486
|
+
users.command("set-current <userId>").description("Set this organization as the current (active) organization for a user.").action(async (userId) => {
|
|
491
487
|
const opts = program2.opts();
|
|
492
488
|
try {
|
|
493
|
-
|
|
494
|
-
const data = await apiRequest({
|
|
495
|
-
method: "POST",
|
|
496
|
-
path: "/org/api-keys",
|
|
497
|
-
body,
|
|
498
|
-
apiKey: opts.apiKey,
|
|
499
|
-
baseUrl: opts.baseUrl
|
|
500
|
-
});
|
|
501
|
-
success("API key created.");
|
|
502
|
-
console.log(JSON.stringify(data, null, 2));
|
|
503
|
-
} catch (err) {
|
|
504
|
-
error(err instanceof SyntaxError ? "Invalid JSON in --data" : formatApiError(err));
|
|
505
|
-
process.exit(1);
|
|
506
|
-
}
|
|
507
|
-
});
|
|
508
|
-
keys.command("get <keyId>").description("Get API key details").action(async (keyId) => {
|
|
509
|
-
const opts = program2.opts();
|
|
510
|
-
try {
|
|
511
|
-
const data = await apiRequest({ path: `/org/api-keys/${keyId}`, apiKey: opts.apiKey, baseUrl: opts.baseUrl });
|
|
512
|
-
console.log(JSON.stringify(data, null, 2));
|
|
513
|
-
} catch (err) {
|
|
514
|
-
error(formatApiError(err));
|
|
515
|
-
process.exit(1);
|
|
516
|
-
}
|
|
517
|
-
});
|
|
518
|
-
keys.command("update <keyId>").description("Update API key").requiredOption("--data <json>", "JSON key updates").action(async (keyId, cmdOpts) => {
|
|
519
|
-
const opts = program2.opts();
|
|
520
|
-
try {
|
|
521
|
-
const body = JSON.parse(cmdOpts.data);
|
|
522
|
-
const data = await apiRequest({
|
|
489
|
+
await apiRequest({
|
|
523
490
|
method: "PATCH",
|
|
524
|
-
path: `/org/
|
|
525
|
-
body,
|
|
491
|
+
path: `/org/users/${userId}/current`,
|
|
492
|
+
body: { is_current: true },
|
|
526
493
|
apiKey: opts.apiKey,
|
|
527
494
|
baseUrl: opts.baseUrl
|
|
528
495
|
});
|
|
529
|
-
success(`
|
|
530
|
-
console.log(JSON.stringify(data, null, 2));
|
|
531
|
-
} catch (err) {
|
|
532
|
-
error(err instanceof SyntaxError ? "Invalid JSON in --data" : formatApiError(err));
|
|
533
|
-
process.exit(1);
|
|
534
|
-
}
|
|
535
|
-
});
|
|
536
|
-
keys.command("delete <keyId>").description("Delete API key").action(async (keyId) => {
|
|
537
|
-
const opts = program2.opts();
|
|
538
|
-
try {
|
|
539
|
-
await apiRequest({ method: "DELETE", path: `/org/api-keys/${keyId}`, apiKey: opts.apiKey, baseUrl: opts.baseUrl });
|
|
540
|
-
success(`API key ${keyId} deleted.`);
|
|
541
|
-
} catch (err) {
|
|
542
|
-
error(formatApiError(err));
|
|
543
|
-
process.exit(1);
|
|
544
|
-
}
|
|
545
|
-
});
|
|
546
|
-
keys.command("revoke <keyId>").description("Revoke API key").action(async (keyId) => {
|
|
547
|
-
const opts = program2.opts();
|
|
548
|
-
try {
|
|
549
|
-
await apiRequest({ method: "POST", path: `/org/api-keys/${keyId}/revoke`, apiKey: opts.apiKey, baseUrl: opts.baseUrl });
|
|
550
|
-
success(`API key ${keyId} revoked.`);
|
|
496
|
+
success(`Organization set as current for user ${userId}.`);
|
|
551
497
|
} catch (err) {
|
|
552
498
|
error(formatApiError(err));
|
|
553
499
|
process.exit(1);
|
|
@@ -555,201 +501,90 @@ function registerApiKeyCommands(program2) {
|
|
|
555
501
|
});
|
|
556
502
|
}
|
|
557
503
|
|
|
558
|
-
// src/commands/
|
|
559
|
-
function
|
|
560
|
-
const
|
|
561
|
-
|
|
562
|
-
const opts = program2.opts();
|
|
563
|
-
try {
|
|
564
|
-
const data = await apiRequest({ path: "/org/categories", apiKey: opts.apiKey, baseUrl: opts.baseUrl });
|
|
565
|
-
console.log(JSON.stringify(data, null, 2));
|
|
566
|
-
} catch (err) {
|
|
567
|
-
error(formatApiError(err));
|
|
568
|
-
process.exit(1);
|
|
569
|
-
}
|
|
570
|
-
});
|
|
571
|
-
cat.command("list-all").description("List all categories with their topics").action(async () => {
|
|
572
|
-
const opts = program2.opts();
|
|
573
|
-
try {
|
|
574
|
-
const data = await apiRequest({ path: "/org/categories/all", apiKey: opts.apiKey, baseUrl: opts.baseUrl });
|
|
575
|
-
console.log(JSON.stringify(data, null, 2));
|
|
576
|
-
} catch (err) {
|
|
577
|
-
error(formatApiError(err));
|
|
578
|
-
process.exit(1);
|
|
579
|
-
}
|
|
580
|
-
});
|
|
581
|
-
cat.command("create <name>").description("Create a category").action(async (name) => {
|
|
582
|
-
const opts = program2.opts();
|
|
583
|
-
try {
|
|
584
|
-
const data = await apiRequest({
|
|
585
|
-
method: "POST",
|
|
586
|
-
path: "/org/categories",
|
|
587
|
-
body: { name },
|
|
588
|
-
apiKey: opts.apiKey,
|
|
589
|
-
baseUrl: opts.baseUrl
|
|
590
|
-
});
|
|
591
|
-
success(`Category "${name}" created.`);
|
|
592
|
-
console.log(JSON.stringify(data, null, 2));
|
|
593
|
-
} catch (err) {
|
|
594
|
-
error(formatApiError(err));
|
|
595
|
-
process.exit(1);
|
|
596
|
-
}
|
|
597
|
-
});
|
|
598
|
-
cat.command("get <id>").description("Get category by ID").action(async (id) => {
|
|
599
|
-
const opts = program2.opts();
|
|
600
|
-
try {
|
|
601
|
-
const data = await apiRequest({ path: `/org/categories/${id}`, apiKey: opts.apiKey, baseUrl: opts.baseUrl });
|
|
602
|
-
console.log(JSON.stringify(data, null, 2));
|
|
603
|
-
} catch (err) {
|
|
604
|
-
error(formatApiError(err));
|
|
605
|
-
process.exit(1);
|
|
606
|
-
}
|
|
607
|
-
});
|
|
608
|
-
cat.command("update <id>").description("Update a category").requiredOption("--name <name>", "New category name").action(async (id, cmdOpts) => {
|
|
504
|
+
// src/commands/api-keys.ts
|
|
505
|
+
function registerApiKeyCommands(program2) {
|
|
506
|
+
const keys = program2.command("api-keys").description("Manage org-scoped API keys. Create, rotate, revoke, or list API keys used to authenticate with the Senso API.");
|
|
507
|
+
keys.command("list").description("List all API keys for the organization. Shows name, expiry, revocation status, and last usage.").option("--limit <n>", "Maximum number of keys to return").option("--offset <n>", "Number of keys to skip (for pagination)").action(async (cmdOpts) => {
|
|
609
508
|
const opts = program2.opts();
|
|
610
509
|
try {
|
|
611
510
|
const data = await apiRequest({
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
body: { name: cmdOpts.name },
|
|
511
|
+
path: "/org/api-keys",
|
|
512
|
+
params: { limit: cmdOpts.limit, offset: cmdOpts.offset },
|
|
615
513
|
apiKey: opts.apiKey,
|
|
616
514
|
baseUrl: opts.baseUrl
|
|
617
515
|
});
|
|
618
|
-
success(`Category ${id} updated.`);
|
|
619
516
|
console.log(JSON.stringify(data, null, 2));
|
|
620
517
|
} catch (err) {
|
|
621
518
|
error(formatApiError(err));
|
|
622
519
|
process.exit(1);
|
|
623
520
|
}
|
|
624
521
|
});
|
|
625
|
-
|
|
626
|
-
const opts = program2.opts();
|
|
627
|
-
try {
|
|
628
|
-
await apiRequest({ method: "DELETE", path: `/org/categories/${id}`, apiKey: opts.apiKey, baseUrl: opts.baseUrl });
|
|
629
|
-
success(`Category ${id} deleted.`);
|
|
630
|
-
} catch (err) {
|
|
631
|
-
error(formatApiError(err));
|
|
632
|
-
process.exit(1);
|
|
633
|
-
}
|
|
634
|
-
});
|
|
635
|
-
cat.command("batch-create").description("Batch create categories with topics").requiredOption("--data <json>", "JSON array of categories with topics").action(async (cmdOpts) => {
|
|
522
|
+
keys.command("create").description("Create a new API key. The key value is returned only once \u2014 store it securely.").requiredOption("--data <json>", 'JSON: { "name": "my-key", "expires_at": "2025-12-31T00:00:00Z" }').action(async (cmdOpts) => {
|
|
636
523
|
const opts = program2.opts();
|
|
637
524
|
try {
|
|
638
525
|
const body = JSON.parse(cmdOpts.data);
|
|
639
526
|
const data = await apiRequest({
|
|
640
527
|
method: "POST",
|
|
641
|
-
path: "/org/
|
|
528
|
+
path: "/org/api-keys",
|
|
642
529
|
body,
|
|
643
530
|
apiKey: opts.apiKey,
|
|
644
531
|
baseUrl: opts.baseUrl
|
|
645
532
|
});
|
|
646
|
-
success("
|
|
533
|
+
success("API key created.");
|
|
647
534
|
console.log(JSON.stringify(data, null, 2));
|
|
648
535
|
} catch (err) {
|
|
649
536
|
error(err instanceof SyntaxError ? "Invalid JSON in --data" : formatApiError(err));
|
|
650
537
|
process.exit(1);
|
|
651
538
|
}
|
|
652
539
|
});
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
// src/commands/topics.ts
|
|
656
|
-
function registerTopicCommands(program2) {
|
|
657
|
-
const topics = program2.command("topics").description("Manage topics within categories");
|
|
658
|
-
topics.command("list <categoryId>").description("List topics for a category").action(async (categoryId) => {
|
|
659
|
-
const opts = program2.opts();
|
|
660
|
-
try {
|
|
661
|
-
const data = await apiRequest({
|
|
662
|
-
path: `/org/categories/${categoryId}/topics`,
|
|
663
|
-
apiKey: opts.apiKey,
|
|
664
|
-
baseUrl: opts.baseUrl
|
|
665
|
-
});
|
|
666
|
-
console.log(JSON.stringify(data, null, 2));
|
|
667
|
-
} catch (err) {
|
|
668
|
-
error(formatApiError(err));
|
|
669
|
-
process.exit(1);
|
|
670
|
-
}
|
|
671
|
-
});
|
|
672
|
-
topics.command("create <categoryId>").description("Create topic in category").requiredOption("--name <name>", "Topic name").action(async (categoryId, cmdOpts) => {
|
|
540
|
+
keys.command("get <keyId>").description("Get details for a specific API key including name, expiry, and last used timestamp.").action(async (keyId) => {
|
|
673
541
|
const opts = program2.opts();
|
|
674
542
|
try {
|
|
675
|
-
const data = await apiRequest({
|
|
676
|
-
method: "POST",
|
|
677
|
-
path: `/org/categories/${categoryId}/topics`,
|
|
678
|
-
body: { name: cmdOpts.name },
|
|
679
|
-
apiKey: opts.apiKey,
|
|
680
|
-
baseUrl: opts.baseUrl
|
|
681
|
-
});
|
|
682
|
-
success(`Topic "${cmdOpts.name}" created.`);
|
|
543
|
+
const data = await apiRequest({ path: `/org/api-keys/${keyId}`, apiKey: opts.apiKey, baseUrl: opts.baseUrl });
|
|
683
544
|
console.log(JSON.stringify(data, null, 2));
|
|
684
545
|
} catch (err) {
|
|
685
546
|
error(formatApiError(err));
|
|
686
547
|
process.exit(1);
|
|
687
548
|
}
|
|
688
549
|
});
|
|
689
|
-
|
|
550
|
+
keys.command("update <keyId>").description("Update an API key's name or expiry date.").requiredOption("--data <json>", 'JSON: { "name": "new-name", "expires_at": "2026-06-01T00:00:00Z" }').action(async (keyId, cmdOpts) => {
|
|
690
551
|
const opts = program2.opts();
|
|
691
552
|
try {
|
|
553
|
+
const body = JSON.parse(cmdOpts.data);
|
|
692
554
|
const data = await apiRequest({
|
|
693
|
-
|
|
555
|
+
method: "PUT",
|
|
556
|
+
path: `/org/api-keys/${keyId}`,
|
|
557
|
+
body,
|
|
694
558
|
apiKey: opts.apiKey,
|
|
695
559
|
baseUrl: opts.baseUrl
|
|
696
560
|
});
|
|
561
|
+
success(`API key ${keyId} updated.`);
|
|
697
562
|
console.log(JSON.stringify(data, null, 2));
|
|
698
563
|
} catch (err) {
|
|
699
|
-
error(formatApiError(err));
|
|
564
|
+
error(err instanceof SyntaxError ? "Invalid JSON in --data" : formatApiError(err));
|
|
700
565
|
process.exit(1);
|
|
701
566
|
}
|
|
702
567
|
});
|
|
703
|
-
|
|
568
|
+
keys.command("delete <keyId>").description("Permanently delete an API key. This cannot be undone.").action(async (keyId) => {
|
|
704
569
|
const opts = program2.opts();
|
|
705
570
|
try {
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
path: `/org/categories/${categoryId}/topics/${topicId}`,
|
|
709
|
-
body: { name: cmdOpts.name },
|
|
710
|
-
apiKey: opts.apiKey,
|
|
711
|
-
baseUrl: opts.baseUrl
|
|
712
|
-
});
|
|
713
|
-
success(`Topic ${topicId} updated.`);
|
|
714
|
-
console.log(JSON.stringify(data, null, 2));
|
|
571
|
+
await apiRequest({ method: "DELETE", path: `/org/api-keys/${keyId}`, apiKey: opts.apiKey, baseUrl: opts.baseUrl });
|
|
572
|
+
success(`API key ${keyId} deleted.`);
|
|
715
573
|
} catch (err) {
|
|
716
574
|
error(formatApiError(err));
|
|
717
575
|
process.exit(1);
|
|
718
576
|
}
|
|
719
577
|
});
|
|
720
|
-
|
|
578
|
+
keys.command("revoke <keyId>").description("Revoke an API key. The key remains visible but can no longer be used for authentication.").action(async (keyId) => {
|
|
721
579
|
const opts = program2.opts();
|
|
722
580
|
try {
|
|
723
|
-
await apiRequest({
|
|
724
|
-
|
|
725
|
-
path: `/org/categories/${categoryId}/topics/${topicId}`,
|
|
726
|
-
apiKey: opts.apiKey,
|
|
727
|
-
baseUrl: opts.baseUrl
|
|
728
|
-
});
|
|
729
|
-
success(`Topic ${topicId} deleted.`);
|
|
581
|
+
await apiRequest({ method: "POST", path: `/org/api-keys/${keyId}/revoke`, apiKey: opts.apiKey, baseUrl: opts.baseUrl });
|
|
582
|
+
success(`API key ${keyId} revoked.`);
|
|
730
583
|
} catch (err) {
|
|
731
584
|
error(formatApiError(err));
|
|
732
585
|
process.exit(1);
|
|
733
586
|
}
|
|
734
587
|
});
|
|
735
|
-
topics.command("batch-create <categoryId>").description("Batch create topics in a category").requiredOption("--data <json>", "JSON array of topics").action(async (categoryId, cmdOpts) => {
|
|
736
|
-
const opts = program2.opts();
|
|
737
|
-
try {
|
|
738
|
-
const body = JSON.parse(cmdOpts.data);
|
|
739
|
-
const data = await apiRequest({
|
|
740
|
-
method: "POST",
|
|
741
|
-
path: `/org/categories/${categoryId}/topics/batch`,
|
|
742
|
-
body,
|
|
743
|
-
apiKey: opts.apiKey,
|
|
744
|
-
baseUrl: opts.baseUrl
|
|
745
|
-
});
|
|
746
|
-
success("Batch create completed.");
|
|
747
|
-
console.log(JSON.stringify(data, null, 2));
|
|
748
|
-
} catch (err) {
|
|
749
|
-
error(err instanceof SyntaxError ? "Invalid JSON in --data" : formatApiError(err));
|
|
750
|
-
process.exit(1);
|
|
751
|
-
}
|
|
752
|
-
});
|
|
753
588
|
}
|
|
754
589
|
|
|
755
590
|
// src/commands/search.ts
|
|
@@ -807,7 +642,7 @@ function output(format, data) {
|
|
|
807
642
|
|
|
808
643
|
// src/commands/search.ts
|
|
809
644
|
function registerSearchCommands(program2) {
|
|
810
|
-
const search = program2.command("search").description("
|
|
645
|
+
const search = program2.command("search").description("Search the knowledge base with natural language queries. Returns AI-generated answers synthesised from matching content chunks, or raw chunks/content IDs.");
|
|
811
646
|
search.argument("<query>", "Search query").option("--max-results <n>", "Maximum number of results", "5").action(async (query, cmdOpts) => {
|
|
812
647
|
const opts = program2.opts();
|
|
813
648
|
try {
|
|
@@ -847,7 +682,7 @@ function registerSearchCommands(program2) {
|
|
|
847
682
|
process.exit(1);
|
|
848
683
|
}
|
|
849
684
|
});
|
|
850
|
-
search.command("context <query>").description("
|
|
685
|
+
search.command("context <query>").description("Search the knowledge base \u2014 returns matching content chunks only, without AI answer generation. Faster than full search.").option("--max-results <n>", "Maximum results", "5").action(async (query, cmdOpts) => {
|
|
851
686
|
const opts = program2.opts();
|
|
852
687
|
try {
|
|
853
688
|
const data = await apiRequest({
|
|
@@ -863,7 +698,7 @@ function registerSearchCommands(program2) {
|
|
|
863
698
|
process.exit(1);
|
|
864
699
|
}
|
|
865
700
|
});
|
|
866
|
-
search.command("content <query>").description("
|
|
701
|
+
search.command("content <query>").description("Search the knowledge base \u2014 returns deduplicated content IDs and titles only. No chunks or AI answer.").option("--max-results <n>", "Maximum results", "5").action(async (query, cmdOpts) => {
|
|
867
702
|
const opts = program2.opts();
|
|
868
703
|
try {
|
|
869
704
|
const data = await apiRequest({
|
|
@@ -889,34 +724,119 @@ function outputByFormat(format, data) {
|
|
|
889
724
|
}
|
|
890
725
|
|
|
891
726
|
// src/commands/ingest.ts
|
|
727
|
+
import { createHash } from "crypto";
|
|
728
|
+
import { readFile, stat } from "fs/promises";
|
|
729
|
+
import { basename, resolve } from "path";
|
|
730
|
+
var MIME_TYPES = {
|
|
731
|
+
".pdf": "application/pdf",
|
|
732
|
+
".txt": "text/plain",
|
|
733
|
+
".csv": "text/csv",
|
|
734
|
+
".doc": "application/msword",
|
|
735
|
+
".docx": "application/vnd.openxmlformats-officedocument.wordprocessingml.document",
|
|
736
|
+
".xls": "application/vnd.ms-excel",
|
|
737
|
+
".xlsx": "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
|
|
738
|
+
".ppt": "application/vnd.ms-powerpoint",
|
|
739
|
+
".pptx": "application/vnd.openxmlformats-officedocument.presentationml.presentation",
|
|
740
|
+
".html": "text/html",
|
|
741
|
+
".htm": "text/html",
|
|
742
|
+
".md": "text/markdown",
|
|
743
|
+
".json": "application/json",
|
|
744
|
+
".xml": "application/xml"
|
|
745
|
+
};
|
|
746
|
+
function getMimeType(filename) {
|
|
747
|
+
const ext = filename.slice(filename.lastIndexOf(".")).toLowerCase();
|
|
748
|
+
return MIME_TYPES[ext] || "application/octet-stream";
|
|
749
|
+
}
|
|
750
|
+
async function getFileMetadata(filePath) {
|
|
751
|
+
const absPath = resolve(filePath);
|
|
752
|
+
const buffer = await readFile(absPath);
|
|
753
|
+
const stats = await stat(absPath);
|
|
754
|
+
const hash = createHash("md5").update(buffer).digest("hex");
|
|
755
|
+
return {
|
|
756
|
+
meta: {
|
|
757
|
+
filename: basename(absPath),
|
|
758
|
+
file_size_bytes: stats.size,
|
|
759
|
+
content_type: getMimeType(basename(absPath)),
|
|
760
|
+
content_hash_md5: hash
|
|
761
|
+
},
|
|
762
|
+
buffer
|
|
763
|
+
};
|
|
764
|
+
}
|
|
765
|
+
async function uploadToS3(url, buffer, contentType) {
|
|
766
|
+
const res = await fetch(url, {
|
|
767
|
+
method: "PUT",
|
|
768
|
+
headers: { "Content-Type": contentType },
|
|
769
|
+
body: buffer
|
|
770
|
+
});
|
|
771
|
+
if (!res.ok) {
|
|
772
|
+
throw new Error(`S3 upload failed: ${res.status} ${res.statusText}`);
|
|
773
|
+
}
|
|
774
|
+
}
|
|
892
775
|
function registerIngestCommands(program2) {
|
|
893
|
-
const ingest = program2.command("ingest").description("
|
|
894
|
-
ingest.command("upload").description("
|
|
776
|
+
const ingest = program2.command("ingest").description("Ingest files into the knowledge base. Upload documents (PDF, TXT, DOCX, etc.) to be parsed, chunked, and embedded for semantic search.");
|
|
777
|
+
ingest.command("upload <files...>").description("Upload files to the knowledge base. Accepts local file paths (up to 10). Files are hashed, uploaded to S3, then parsed and embedded by a background worker.").action(async (files) => {
|
|
895
778
|
const opts = program2.opts();
|
|
779
|
+
if (files.length > 10) {
|
|
780
|
+
error("Maximum 10 files per upload request.");
|
|
781
|
+
process.exit(1);
|
|
782
|
+
}
|
|
896
783
|
try {
|
|
897
|
-
const
|
|
784
|
+
const fileData = await Promise.all(files.map(getFileMetadata));
|
|
785
|
+
const results = await apiRequest({
|
|
898
786
|
method: "POST",
|
|
899
787
|
path: "/org/ingestion/upload",
|
|
900
|
-
body: { files:
|
|
788
|
+
body: { files: fileData.map((f) => f.meta) },
|
|
901
789
|
apiKey: opts.apiKey,
|
|
902
790
|
baseUrl: opts.baseUrl
|
|
903
791
|
});
|
|
904
|
-
|
|
792
|
+
const items = Array.isArray(results) ? results : [];
|
|
793
|
+
let uploaded = 0;
|
|
794
|
+
for (const item of items) {
|
|
795
|
+
if (item.status === "upload_pending" && item.upload_url) {
|
|
796
|
+
const match = fileData.find((f) => f.meta.filename === item.filename);
|
|
797
|
+
if (match) {
|
|
798
|
+
await uploadToS3(item.upload_url, match.buffer, match.meta.content_type);
|
|
799
|
+
uploaded++;
|
|
800
|
+
success(`Uploaded ${item.filename} (content_id: ${item.content_id})`);
|
|
801
|
+
}
|
|
802
|
+
} else {
|
|
803
|
+
warn(`Skipped ${item.filename}: ${item.status}${item.message ? ` \u2014 ${item.message}` : ""}`);
|
|
804
|
+
}
|
|
805
|
+
}
|
|
806
|
+
if (uploaded > 0) {
|
|
807
|
+
success(`${uploaded} file(s) uploaded. Background processing will parse, chunk, and embed them.`);
|
|
808
|
+
}
|
|
809
|
+
if (opts.output === "json") {
|
|
810
|
+
console.log(JSON.stringify(results, null, 2));
|
|
811
|
+
}
|
|
905
812
|
} catch (err) {
|
|
906
813
|
error(formatApiError(err));
|
|
907
814
|
process.exit(1);
|
|
908
815
|
}
|
|
909
816
|
});
|
|
910
|
-
ingest.command("reprocess <contentId>").description("
|
|
817
|
+
ingest.command("reprocess <contentId> <file>").description("Re-ingest an existing content item with a new file version. Provide the content ID and the path to the replacement file.").action(async (contentId, file) => {
|
|
911
818
|
const opts = program2.opts();
|
|
912
819
|
try {
|
|
913
|
-
await
|
|
914
|
-
|
|
915
|
-
|
|
820
|
+
const { meta, buffer } = await getFileMetadata(file);
|
|
821
|
+
const results = await apiRequest({
|
|
822
|
+
method: "PUT",
|
|
823
|
+
path: `/org/ingestion/content/${contentId}`,
|
|
824
|
+
body: { file: meta },
|
|
916
825
|
apiKey: opts.apiKey,
|
|
917
826
|
baseUrl: opts.baseUrl
|
|
918
827
|
});
|
|
919
|
-
|
|
828
|
+
const items = Array.isArray(results) ? results : [];
|
|
829
|
+
for (const item of items) {
|
|
830
|
+
if (item.status === "upload_pending" && item.upload_url) {
|
|
831
|
+
await uploadToS3(item.upload_url, buffer, meta.content_type);
|
|
832
|
+
success(`Uploaded ${meta.filename} for content ${contentId}. Background re-processing started.`);
|
|
833
|
+
} else {
|
|
834
|
+
warn(`Skipped: ${item.status}${item.message ? ` \u2014 ${item.message}` : ""}`);
|
|
835
|
+
}
|
|
836
|
+
}
|
|
837
|
+
if (opts.output === "json") {
|
|
838
|
+
console.log(JSON.stringify(results, null, 2));
|
|
839
|
+
}
|
|
920
840
|
} catch (err) {
|
|
921
841
|
error(formatApiError(err));
|
|
922
842
|
process.exit(1);
|
|
@@ -927,13 +847,13 @@ function registerIngestCommands(program2) {
|
|
|
927
847
|
// src/commands/content.ts
|
|
928
848
|
import pc6 from "picocolors";
|
|
929
849
|
function registerContentCommands(program2) {
|
|
930
|
-
const content = program2.command("content").description("Manage content items");
|
|
931
|
-
content.command("list").description("List content items").option("--limit <n>", "Items per page", "10").option("--offset <n>", "Pagination offset", "0").action(async (cmdOpts) => {
|
|
850
|
+
const content = program2.command("content").description("Manage content items in the knowledge base. List, inspect, delete, unpublish, and manage the verification workflow and ownership of content.");
|
|
851
|
+
content.command("list").description("List all content items in the knowledge base. Returns title, status, and ID for each item. Use --search to filter by title, --sort to order results.").option("--limit <n>", "Items per page", "10").option("--offset <n>", "Pagination offset", "0").option("--search <query>", "Filter content by title").option("--sort <order>", "Sort order: title_asc, title_desc, created_asc, created_desc").action(async (cmdOpts) => {
|
|
932
852
|
const opts = program2.opts();
|
|
933
853
|
try {
|
|
934
854
|
const data = await apiRequest({
|
|
935
855
|
path: "/org/content",
|
|
936
|
-
params: { limit: cmdOpts.limit, offset: cmdOpts.offset },
|
|
856
|
+
params: { limit: cmdOpts.limit, offset: cmdOpts.offset, search: cmdOpts.search, sort: cmdOpts.sort },
|
|
937
857
|
apiKey: opts.apiKey,
|
|
938
858
|
baseUrl: opts.baseUrl
|
|
939
859
|
});
|
|
@@ -958,7 +878,7 @@ function registerContentCommands(program2) {
|
|
|
958
878
|
process.exit(1);
|
|
959
879
|
}
|
|
960
880
|
});
|
|
961
|
-
content.command("get <id>").description("Get content item by ID").action(async (id) => {
|
|
881
|
+
content.command("get <id>").description("Get a content item by ID. Returns the full content detail including versions, metadata, and publish status.").action(async (id) => {
|
|
962
882
|
const opts = program2.opts();
|
|
963
883
|
try {
|
|
964
884
|
const data = await apiRequest({ path: `/org/content/${id}`, apiKey: opts.apiKey, baseUrl: opts.baseUrl });
|
|
@@ -968,7 +888,7 @@ function registerContentCommands(program2) {
|
|
|
968
888
|
process.exit(1);
|
|
969
889
|
}
|
|
970
890
|
});
|
|
971
|
-
content.command("delete <id>").description("Delete content
|
|
891
|
+
content.command("delete <id>").description("Delete a content item from the knowledge base and any external publish destinations. This cannot be undone.").action(async (id) => {
|
|
972
892
|
const opts = program2.opts();
|
|
973
893
|
try {
|
|
974
894
|
await apiRequest({ method: "DELETE", path: `/org/content/${id}`, apiKey: opts.apiKey, baseUrl: opts.baseUrl });
|
|
@@ -978,7 +898,7 @@ function registerContentCommands(program2) {
|
|
|
978
898
|
process.exit(1);
|
|
979
899
|
}
|
|
980
900
|
});
|
|
981
|
-
content.command("unpublish <id>").description("Unpublish content
|
|
901
|
+
content.command("unpublish <id>").description("Unpublish a content item. Removes it from external destinations and sets its status back to draft.").action(async (id) => {
|
|
982
902
|
const opts = program2.opts();
|
|
983
903
|
try {
|
|
984
904
|
await apiRequest({ method: "POST", path: `/org/content/${id}/unpublish`, apiKey: opts.apiKey, baseUrl: opts.baseUrl });
|
|
@@ -988,27 +908,33 @@ function registerContentCommands(program2) {
|
|
|
988
908
|
process.exit(1);
|
|
989
909
|
}
|
|
990
910
|
});
|
|
991
|
-
content.command("verification").description("List content
|
|
911
|
+
content.command("verification").description("List content items in the verification workflow. Filter by editorial status (draft, review, rejected, published) to manage the review pipeline.").option("--limit <n>", "Maximum items to return").option("--offset <n>", "Number of items to skip (for pagination)").option("--search <query>", "Filter by title").option("--status <status>", "Filter by status: all, draft, review, rejected, published").action(async (cmdOpts) => {
|
|
992
912
|
const opts = program2.opts();
|
|
993
913
|
try {
|
|
994
|
-
const data = await apiRequest({
|
|
914
|
+
const data = await apiRequest({
|
|
915
|
+
path: "/org/content/verification",
|
|
916
|
+
params: { limit: cmdOpts.limit, offset: cmdOpts.offset, search: cmdOpts.search, status: cmdOpts.status },
|
|
917
|
+
apiKey: opts.apiKey,
|
|
918
|
+
baseUrl: opts.baseUrl
|
|
919
|
+
});
|
|
995
920
|
console.log(JSON.stringify(data, null, 2));
|
|
996
921
|
} catch (err) {
|
|
997
922
|
error(formatApiError(err));
|
|
998
923
|
process.exit(1);
|
|
999
924
|
}
|
|
1000
925
|
});
|
|
1001
|
-
content.command("reject <versionId>").description("Reject a content version").action(async (versionId) => {
|
|
926
|
+
content.command("reject <versionId>").description("Reject a content version in the verification workflow. Optionally provide a reason for the rejection.").option("--reason <text>", "Reason for rejection").action(async (versionId, cmdOpts) => {
|
|
1002
927
|
const opts = program2.opts();
|
|
1003
928
|
try {
|
|
1004
|
-
|
|
929
|
+
const body = cmdOpts.reason ? { reason: cmdOpts.reason } : void 0;
|
|
930
|
+
await apiRequest({ method: "POST", path: `/org/content/versions/${versionId}/reject`, body, apiKey: opts.apiKey, baseUrl: opts.baseUrl });
|
|
1005
931
|
success(`Version ${versionId} rejected.`);
|
|
1006
932
|
} catch (err) {
|
|
1007
933
|
error(formatApiError(err));
|
|
1008
934
|
process.exit(1);
|
|
1009
935
|
}
|
|
1010
936
|
});
|
|
1011
|
-
content.command("restore <versionId>").description("Restore a content version to draft").action(async (versionId) => {
|
|
937
|
+
content.command("restore <versionId>").description("Restore a rejected content version back to draft status for further editing.").action(async (versionId) => {
|
|
1012
938
|
const opts = program2.opts();
|
|
1013
939
|
try {
|
|
1014
940
|
await apiRequest({ method: "POST", path: `/org/content/versions/${versionId}/restore`, apiKey: opts.apiKey, baseUrl: opts.baseUrl });
|
|
@@ -1018,7 +944,7 @@ function registerContentCommands(program2) {
|
|
|
1018
944
|
process.exit(1);
|
|
1019
945
|
}
|
|
1020
946
|
});
|
|
1021
|
-
content.command("owners <id>").description("List content
|
|
947
|
+
content.command("owners <id>").description("List the owners assigned to a content item. Owners are responsible for reviewing and approving content.").action(async (id) => {
|
|
1022
948
|
const opts = program2.opts();
|
|
1023
949
|
try {
|
|
1024
950
|
const data = await apiRequest({ path: `/org/content/${id}/owners`, apiKey: opts.apiKey, baseUrl: opts.baseUrl });
|
|
@@ -1028,7 +954,7 @@ function registerContentCommands(program2) {
|
|
|
1028
954
|
process.exit(1);
|
|
1029
955
|
}
|
|
1030
956
|
});
|
|
1031
|
-
content.command("set-owners <id>").description("Replace content
|
|
957
|
+
content.command("set-owners <id>").description("Replace all owners of a content item with a new set of user IDs.").requiredOption("--user-ids <ids...>", "User IDs to set as owners").action(async (id, cmdOpts) => {
|
|
1032
958
|
const opts = program2.opts();
|
|
1033
959
|
try {
|
|
1034
960
|
await apiRequest({
|
|
@@ -1044,7 +970,7 @@ function registerContentCommands(program2) {
|
|
|
1044
970
|
process.exit(1);
|
|
1045
971
|
}
|
|
1046
972
|
});
|
|
1047
|
-
content.command("remove-owner <id> <userId>").description("Remove content
|
|
973
|
+
content.command("remove-owner <id> <userId>").description("Remove a single owner from a content item.").action(async (id, userId) => {
|
|
1048
974
|
const opts = program2.opts();
|
|
1049
975
|
try {
|
|
1050
976
|
await apiRequest({ method: "DELETE", path: `/org/content/${id}/owners/${userId}`, apiKey: opts.apiKey, baseUrl: opts.baseUrl });
|
|
@@ -1058,8 +984,8 @@ function registerContentCommands(program2) {
|
|
|
1058
984
|
|
|
1059
985
|
// src/commands/generate.ts
|
|
1060
986
|
function registerGenerateCommands(program2) {
|
|
1061
|
-
const gen = program2.command("generate").description("
|
|
1062
|
-
gen.command("settings").description("Get content generation settings").action(async () => {
|
|
987
|
+
const gen = program2.command("generate").description("AI content generation. Configure settings, generate content samples from prompts, or trigger full content engine runs.");
|
|
988
|
+
gen.command("settings").description("Get content generation settings. Shows whether generation and auto-publish are enabled, the content schedule, and configured publishers.").action(async () => {
|
|
1063
989
|
const opts = program2.opts();
|
|
1064
990
|
try {
|
|
1065
991
|
const data = await apiRequest({ path: "/org/content-generation", apiKey: opts.apiKey, baseUrl: opts.baseUrl });
|
|
@@ -1069,24 +995,32 @@ function registerGenerateCommands(program2) {
|
|
|
1069
995
|
process.exit(1);
|
|
1070
996
|
}
|
|
1071
997
|
});
|
|
1072
|
-
gen.command("update-settings").description("Update content generation settings").requiredOption("--data <json>",
|
|
998
|
+
gen.command("update-settings").description("Update content generation settings. Control auto-publish, generation toggle, and schedule (days of week 0-6).").requiredOption("--data <json>", 'JSON settings: { "enable_content_generation": bool, "content_auto_publish": bool, "content_schedule": [0-6] }').action(async (cmdOpts) => {
|
|
1073
999
|
const opts = program2.opts();
|
|
1074
1000
|
try {
|
|
1075
1001
|
const body = JSON.parse(cmdOpts.data);
|
|
1076
1002
|
const data = await apiRequest({ method: "PATCH", path: "/org/content-generation", body, apiKey: opts.apiKey, baseUrl: opts.baseUrl });
|
|
1003
|
+
success("Content generation settings updated.");
|
|
1077
1004
|
console.log(JSON.stringify(data, null, 2));
|
|
1078
1005
|
} catch (err) {
|
|
1079
1006
|
error(err instanceof SyntaxError ? "Invalid JSON in --data" : formatApiError(err));
|
|
1080
1007
|
process.exit(1);
|
|
1081
1008
|
}
|
|
1082
1009
|
});
|
|
1083
|
-
gen.command("sample").description("Generate ad hoc content sample").requiredOption("--
|
|
1010
|
+
gen.command("sample").description("Generate an ad hoc content sample for a specific prompt and content type. Returns the generated markdown, SEO title, and publish results.").requiredOption("--prompt-id <id>", "Prompt (geo question) ID to generate content for").requiredOption("--content-type-id <id>", "Content type ID that defines the output format").option("--destination <dest>", "Publish destination (e.g. citeables)").action(async (cmdOpts) => {
|
|
1084
1011
|
const opts = program2.opts();
|
|
1085
1012
|
try {
|
|
1013
|
+
const body = {
|
|
1014
|
+
geo_question_id: cmdOpts.promptId,
|
|
1015
|
+
content_type_id: cmdOpts.contentTypeId
|
|
1016
|
+
};
|
|
1017
|
+
if (cmdOpts.destination) {
|
|
1018
|
+
body.publish_destination = cmdOpts.destination;
|
|
1019
|
+
}
|
|
1086
1020
|
const data = await apiRequest({
|
|
1087
1021
|
method: "POST",
|
|
1088
1022
|
path: "/org/content-generation/sample",
|
|
1089
|
-
body
|
|
1023
|
+
body,
|
|
1090
1024
|
apiKey: opts.apiKey,
|
|
1091
1025
|
baseUrl: opts.baseUrl
|
|
1092
1026
|
});
|
|
@@ -1096,10 +1030,11 @@ function registerGenerateCommands(program2) {
|
|
|
1096
1030
|
process.exit(1);
|
|
1097
1031
|
}
|
|
1098
1032
|
});
|
|
1099
|
-
gen.command("run").description("Trigger content engine run").action(async () => {
|
|
1033
|
+
gen.command("run").description("Trigger a content generation run. Processes all prompts (or a specific subset) through the content engine. Runs asynchronously.").option("--prompt-ids <ids...>", "Optional list of prompt IDs to process (omit to run all)").action(async (cmdOpts) => {
|
|
1100
1034
|
const opts = program2.opts();
|
|
1101
1035
|
try {
|
|
1102
|
-
const
|
|
1036
|
+
const body = cmdOpts.promptIds ? { prompt_ids: cmdOpts.promptIds } : void 0;
|
|
1037
|
+
const data = await apiRequest({ method: "POST", path: "/org/content-generation/run", body, apiKey: opts.apiKey, baseUrl: opts.baseUrl });
|
|
1103
1038
|
success("Content generation run triggered.");
|
|
1104
1039
|
console.log(JSON.stringify(data, null, 2));
|
|
1105
1040
|
} catch (err) {
|
|
@@ -1111,8 +1046,8 @@ function registerGenerateCommands(program2) {
|
|
|
1111
1046
|
|
|
1112
1047
|
// src/commands/engine.ts
|
|
1113
1048
|
function registerEngineCommands(program2) {
|
|
1114
|
-
const engine = program2.command("engine").description("
|
|
1115
|
-
engine.command("publish").description("Publish content via content engine").requiredOption("--data <json>",
|
|
1049
|
+
const engine = program2.command("engine").description("Publish or draft content through the content engine. Used to push AI-generated content to external destinations or save it as a draft for review.");
|
|
1050
|
+
engine.command("publish").description("Publish content to external destinations via the content engine. Requires geo_question_id, raw_markdown, and seo_title.").requiredOption("--data <json>", 'JSON: { "geo_question_id": "uuid", "raw_markdown": "...", "seo_title": "...", "summary": "..." }').action(async (cmdOpts) => {
|
|
1116
1051
|
const opts = program2.opts();
|
|
1117
1052
|
try {
|
|
1118
1053
|
const body = JSON.parse(cmdOpts.data);
|
|
@@ -1130,7 +1065,7 @@ function registerEngineCommands(program2) {
|
|
|
1130
1065
|
process.exit(1);
|
|
1131
1066
|
}
|
|
1132
1067
|
});
|
|
1133
|
-
engine.command("draft").description("Save content as draft
|
|
1068
|
+
engine.command("draft").description("Save content as a draft for review before publishing. Requires geo_question_id, raw_markdown, and seo_title.").requiredOption("--data <json>", 'JSON: { "geo_question_id": "uuid", "raw_markdown": "...", "seo_title": "...", "summary": "..." }').action(async (cmdOpts) => {
|
|
1134
1069
|
const opts = program2.opts();
|
|
1135
1070
|
try {
|
|
1136
1071
|
const body = JSON.parse(cmdOpts.data);
|
|
@@ -1152,8 +1087,8 @@ function registerEngineCommands(program2) {
|
|
|
1152
1087
|
|
|
1153
1088
|
// src/commands/brand-kit.ts
|
|
1154
1089
|
function registerBrandKitCommands(program2) {
|
|
1155
|
-
const bk = program2.command("brand-kit").description("Manage brand kit");
|
|
1156
|
-
bk.command("get").description("Get brand kit").action(async () => {
|
|
1090
|
+
const bk = program2.command("brand-kit").description("Manage the organization's brand kit guidelines. The brand kit is a free-form JSON object that informs AI content generation about your brand voice, tone, and style.");
|
|
1091
|
+
bk.command("get").description("Get the current brand kit guidelines.").action(async () => {
|
|
1157
1092
|
const opts = program2.opts();
|
|
1158
1093
|
try {
|
|
1159
1094
|
const data = await apiRequest({ path: "/org/brand-kit", apiKey: opts.apiKey, baseUrl: opts.baseUrl });
|
|
@@ -1163,7 +1098,7 @@ function registerBrandKitCommands(program2) {
|
|
|
1163
1098
|
process.exit(1);
|
|
1164
1099
|
}
|
|
1165
1100
|
});
|
|
1166
|
-
bk.command("set").description("
|
|
1101
|
+
bk.command("set").description("Create or replace the brand kit. The guidelines field is a free-form JSON object defining your brand voice.").requiredOption("--data <json>", 'JSON: { "guidelines": { "tone": "professional", "voice": "..." } }').action(async (cmdOpts) => {
|
|
1167
1102
|
const opts = program2.opts();
|
|
1168
1103
|
try {
|
|
1169
1104
|
const body = JSON.parse(cmdOpts.data);
|
|
@@ -1185,18 +1120,23 @@ function registerBrandKitCommands(program2) {
|
|
|
1185
1120
|
|
|
1186
1121
|
// src/commands/content-types.ts
|
|
1187
1122
|
function registerContentTypeCommands(program2) {
|
|
1188
|
-
const ct = program2.command("content-types").description("Manage content types");
|
|
1189
|
-
ct.command("list").description("List content types").action(async () => {
|
|
1123
|
+
const ct = program2.command("content-types").description("Manage content type configurations. Content types define the output format and structure for AI-generated content (e.g. blog post, FAQ, landing page).");
|
|
1124
|
+
ct.command("list").description("List all content types configured for the organization.").option("--limit <n>", "Maximum number of content types to return (default: 50)").option("--offset <n>", "Number of items to skip (for pagination)").action(async (cmdOpts) => {
|
|
1190
1125
|
const opts = program2.opts();
|
|
1191
1126
|
try {
|
|
1192
|
-
const data = await apiRequest({
|
|
1127
|
+
const data = await apiRequest({
|
|
1128
|
+
path: "/org/content-types",
|
|
1129
|
+
params: { limit: cmdOpts.limit, offset: cmdOpts.offset },
|
|
1130
|
+
apiKey: opts.apiKey,
|
|
1131
|
+
baseUrl: opts.baseUrl
|
|
1132
|
+
});
|
|
1193
1133
|
console.log(JSON.stringify(data, null, 2));
|
|
1194
1134
|
} catch (err) {
|
|
1195
1135
|
error(formatApiError(err));
|
|
1196
1136
|
process.exit(1);
|
|
1197
1137
|
}
|
|
1198
1138
|
});
|
|
1199
|
-
ct.command("create").description("Create a content type").requiredOption("--data <json>",
|
|
1139
|
+
ct.command("create").description("Create a new content type. Requires a name and configuration defining the output structure.").requiredOption("--data <json>", 'JSON: { "name": "Blog Post", "config": { ... } }').action(async (cmdOpts) => {
|
|
1200
1140
|
const opts = program2.opts();
|
|
1201
1141
|
try {
|
|
1202
1142
|
const body = JSON.parse(cmdOpts.data);
|
|
@@ -1214,7 +1154,7 @@ function registerContentTypeCommands(program2) {
|
|
|
1214
1154
|
process.exit(1);
|
|
1215
1155
|
}
|
|
1216
1156
|
});
|
|
1217
|
-
ct.command("get <id>").description("Get content type by ID").action(async (id) => {
|
|
1157
|
+
ct.command("get <id>").description("Get a content type by ID, including its full configuration.").action(async (id) => {
|
|
1218
1158
|
const opts = program2.opts();
|
|
1219
1159
|
try {
|
|
1220
1160
|
const data = await apiRequest({ path: `/org/content-types/${id}`, apiKey: opts.apiKey, baseUrl: opts.baseUrl });
|
|
@@ -1224,12 +1164,12 @@ function registerContentTypeCommands(program2) {
|
|
|
1224
1164
|
process.exit(1);
|
|
1225
1165
|
}
|
|
1226
1166
|
});
|
|
1227
|
-
ct.command("update <id>").description("Update a content type").requiredOption("--data <json>",
|
|
1167
|
+
ct.command("update <id>").description("Update a content type's name or configuration.").requiredOption("--data <json>", 'JSON: { "name": "Updated Name", "config": { ... } }').action(async (id, cmdOpts) => {
|
|
1228
1168
|
const opts = program2.opts();
|
|
1229
1169
|
try {
|
|
1230
1170
|
const body = JSON.parse(cmdOpts.data);
|
|
1231
1171
|
const data = await apiRequest({
|
|
1232
|
-
method: "
|
|
1172
|
+
method: "PUT",
|
|
1233
1173
|
path: `/org/content-types/${id}`,
|
|
1234
1174
|
body,
|
|
1235
1175
|
apiKey: opts.apiKey,
|
|
@@ -1242,7 +1182,7 @@ function registerContentTypeCommands(program2) {
|
|
|
1242
1182
|
process.exit(1);
|
|
1243
1183
|
}
|
|
1244
1184
|
});
|
|
1245
|
-
ct.command("delete <id>").description("Delete a content type").action(async (id) => {
|
|
1185
|
+
ct.command("delete <id>").description("Delete a content type. This cannot be undone.").action(async (id) => {
|
|
1246
1186
|
const opts = program2.opts();
|
|
1247
1187
|
try {
|
|
1248
1188
|
await apiRequest({ method: "DELETE", path: `/org/content-types/${id}`, apiKey: opts.apiKey, baseUrl: opts.baseUrl });
|
|
@@ -1256,18 +1196,23 @@ function registerContentTypeCommands(program2) {
|
|
|
1256
1196
|
|
|
1257
1197
|
// src/commands/prompts.ts
|
|
1258
1198
|
function registerPromptCommands(program2) {
|
|
1259
|
-
const prompts = program2.command("prompts").description("Manage prompts (geo questions)");
|
|
1260
|
-
prompts.command("list").description("List prompts").action(async () => {
|
|
1199
|
+
const prompts = program2.command("prompts").description("Manage prompts (geo questions). Prompts are the questions that drive AI content generation \u2014 each prompt is run against configured AI models to track brand mentions, claims, and competitor visibility.");
|
|
1200
|
+
prompts.command("list").description("List all prompts in the organization. Use --search to filter by question text, --sort to order results.").option("--limit <n>", "Maximum prompts to return (max: 100)").option("--offset <n>", "Number of prompts to skip (for pagination)").option("--search <query>", "Filter prompts by question text").option("--sort <order>", "Sort order: created_desc, created_asc, text_asc, text_desc, type_asc, type_desc").action(async (cmdOpts) => {
|
|
1261
1201
|
const opts = program2.opts();
|
|
1262
1202
|
try {
|
|
1263
|
-
const data = await apiRequest({
|
|
1203
|
+
const data = await apiRequest({
|
|
1204
|
+
path: "/org/prompts",
|
|
1205
|
+
params: { limit: cmdOpts.limit, offset: cmdOpts.offset, search: cmdOpts.search, sort: cmdOpts.sort },
|
|
1206
|
+
apiKey: opts.apiKey,
|
|
1207
|
+
baseUrl: opts.baseUrl
|
|
1208
|
+
});
|
|
1264
1209
|
console.log(JSON.stringify(data, null, 2));
|
|
1265
1210
|
} catch (err) {
|
|
1266
1211
|
error(formatApiError(err));
|
|
1267
1212
|
process.exit(1);
|
|
1268
1213
|
}
|
|
1269
1214
|
});
|
|
1270
|
-
prompts.command("create").description("Create a prompt").requiredOption("--data <json>",
|
|
1215
|
+
prompts.command("create").description("Create a new prompt. Type must be one of: decision, consideration, awareness, evaluation.").requiredOption("--data <json>", 'JSON: { "question_text": "What are the best...", "type": "decision" }').action(async (cmdOpts) => {
|
|
1271
1216
|
const opts = program2.opts();
|
|
1272
1217
|
try {
|
|
1273
1218
|
const body = JSON.parse(cmdOpts.data);
|
|
@@ -1285,7 +1230,7 @@ function registerPromptCommands(program2) {
|
|
|
1285
1230
|
process.exit(1);
|
|
1286
1231
|
}
|
|
1287
1232
|
});
|
|
1288
|
-
prompts.command("get <promptId>").description("Get prompt with full run history").action(async (promptId) => {
|
|
1233
|
+
prompts.command("get <promptId>").description("Get a prompt with its full run history. Includes all question runs with mentions, claims, citations, and competitor data.").action(async (promptId) => {
|
|
1289
1234
|
const opts = program2.opts();
|
|
1290
1235
|
try {
|
|
1291
1236
|
const data = await apiRequest({ path: `/org/prompts/${promptId}`, apiKey: opts.apiKey, baseUrl: opts.baseUrl });
|
|
@@ -1295,7 +1240,7 @@ function registerPromptCommands(program2) {
|
|
|
1295
1240
|
process.exit(1);
|
|
1296
1241
|
}
|
|
1297
1242
|
});
|
|
1298
|
-
prompts.command("delete <promptId>").description("Delete a prompt").action(async (promptId) => {
|
|
1243
|
+
prompts.command("delete <promptId>").description("Delete a prompt and all its associated run history. This cannot be undone.").action(async (promptId) => {
|
|
1299
1244
|
const opts = program2.opts();
|
|
1300
1245
|
try {
|
|
1301
1246
|
await apiRequest({ method: "DELETE", path: `/org/prompts/${promptId}`, apiKey: opts.apiKey, baseUrl: opts.baseUrl });
|
|
@@ -1309,8 +1254,8 @@ function registerPromptCommands(program2) {
|
|
|
1309
1254
|
|
|
1310
1255
|
// src/commands/run-config.ts
|
|
1311
1256
|
function registerRunConfigCommands(program2) {
|
|
1312
|
-
const rc = program2.command("run-config").description("
|
|
1313
|
-
rc.command("models").description("Get
|
|
1257
|
+
const rc = program2.command("run-config").description("Configure which AI models are used for question runs and on which days they run. Models include chatgpt, gemini, etc.");
|
|
1258
|
+
rc.command("models").description("Get the AI models currently configured for question runs (e.g. chatgpt, gemini).").action(async () => {
|
|
1314
1259
|
const opts = program2.opts();
|
|
1315
1260
|
try {
|
|
1316
1261
|
const data = await apiRequest({ path: "/org/run-models", apiKey: opts.apiKey, baseUrl: opts.baseUrl });
|
|
@@ -1320,7 +1265,7 @@ function registerRunConfigCommands(program2) {
|
|
|
1320
1265
|
process.exit(1);
|
|
1321
1266
|
}
|
|
1322
1267
|
});
|
|
1323
|
-
rc.command("set-models").description("
|
|
1268
|
+
rc.command("set-models").description("Replace the configured AI models for question runs. At least one model name is required.").requiredOption("--data <json>", 'JSON: { "models": ["chatgpt", "gemini"] }').action(async (cmdOpts) => {
|
|
1324
1269
|
const opts = program2.opts();
|
|
1325
1270
|
try {
|
|
1326
1271
|
const body = JSON.parse(cmdOpts.data);
|
|
@@ -1338,7 +1283,7 @@ function registerRunConfigCommands(program2) {
|
|
|
1338
1283
|
process.exit(1);
|
|
1339
1284
|
}
|
|
1340
1285
|
});
|
|
1341
|
-
rc.command("schedule").description("Get
|
|
1286
|
+
rc.command("schedule").description("Get the days of the week when question runs are triggered (0=Sunday, 1=Monday, ..., 6=Saturday).").action(async () => {
|
|
1342
1287
|
const opts = program2.opts();
|
|
1343
1288
|
try {
|
|
1344
1289
|
const data = await apiRequest({ path: "/org/run-schedule", apiKey: opts.apiKey, baseUrl: opts.baseUrl });
|
|
@@ -1348,7 +1293,7 @@ function registerRunConfigCommands(program2) {
|
|
|
1348
1293
|
process.exit(1);
|
|
1349
1294
|
}
|
|
1350
1295
|
});
|
|
1351
|
-
rc.command("set-schedule").description("Set
|
|
1296
|
+
rc.command("set-schedule").description("Set which days of the week question runs are triggered. Values must be 0-6 (Sunday-Saturday).").requiredOption("--data <json>", 'JSON: { "schedule": [1, 3, 5] }').action(async (cmdOpts) => {
|
|
1352
1297
|
const opts = program2.opts();
|
|
1353
1298
|
try {
|
|
1354
1299
|
const body = JSON.parse(cmdOpts.data);
|
|
@@ -1370,11 +1315,16 @@ function registerRunConfigCommands(program2) {
|
|
|
1370
1315
|
|
|
1371
1316
|
// src/commands/members.ts
|
|
1372
1317
|
function registerMemberCommands(program2) {
|
|
1373
|
-
const members = program2.command("members").description("
|
|
1374
|
-
members.command("list").description("List organization members").action(async () => {
|
|
1318
|
+
const members = program2.command("members").description("View the organization member directory. Lists all users who belong to the organization with their names and emails.");
|
|
1319
|
+
members.command("list").description("List all organization members. Use --search to filter by name or email, --sort to order results.").option("--limit <n>", "Maximum members to return (max: 1000)").option("--offset <n>", "Number of members to skip (for pagination)").option("--search <query>", "Filter by name or email").option("--sort <order>", "Sort order: name_asc, name_desc, email_asc, email_desc, created_asc, created_desc").action(async (cmdOpts) => {
|
|
1375
1320
|
const opts = program2.opts();
|
|
1376
1321
|
try {
|
|
1377
|
-
const data = await apiRequest({
|
|
1322
|
+
const data = await apiRequest({
|
|
1323
|
+
path: "/org/members",
|
|
1324
|
+
params: { limit: cmdOpts.limit, offset: cmdOpts.offset, search: cmdOpts.search, sort: cmdOpts.sort },
|
|
1325
|
+
apiKey: opts.apiKey,
|
|
1326
|
+
baseUrl: opts.baseUrl
|
|
1327
|
+
});
|
|
1378
1328
|
console.log(JSON.stringify(data, null, 2));
|
|
1379
1329
|
} catch (err) {
|
|
1380
1330
|
error(formatApiError(err));
|
|
@@ -1385,22 +1335,31 @@ function registerMemberCommands(program2) {
|
|
|
1385
1335
|
|
|
1386
1336
|
// src/commands/notifications.ts
|
|
1387
1337
|
function registerNotificationCommands(program2) {
|
|
1388
|
-
const notif = program2.command("notifications").description("
|
|
1389
|
-
notif.command("list").description("List notifications").action(async () => {
|
|
1338
|
+
const notif = program2.command("notifications").description("View and manage user notifications. Notifications are triggered by content verification, generation runs, and other system events.");
|
|
1339
|
+
notif.command("list").description("List notifications for the current user. Use --unread-only to filter to unread notifications.").option("--limit <n>", "Maximum notifications to return (default: 50, max: 200)").option("--offset <n>", "Number of notifications to skip (for pagination)").option("--unread-only", "Only return unread notifications").action(async (cmdOpts) => {
|
|
1390
1340
|
const opts = program2.opts();
|
|
1391
1341
|
try {
|
|
1392
|
-
const data = await apiRequest({
|
|
1342
|
+
const data = await apiRequest({
|
|
1343
|
+
path: "/app/v1/notifications",
|
|
1344
|
+
params: {
|
|
1345
|
+
limit: cmdOpts.limit,
|
|
1346
|
+
offset: cmdOpts.offset,
|
|
1347
|
+
unread_only: cmdOpts.unreadOnly ? "true" : void 0
|
|
1348
|
+
},
|
|
1349
|
+
apiKey: opts.apiKey,
|
|
1350
|
+
baseUrl: opts.baseUrl
|
|
1351
|
+
});
|
|
1393
1352
|
console.log(JSON.stringify(data, null, 2));
|
|
1394
1353
|
} catch (err) {
|
|
1395
1354
|
error(formatApiError(err));
|
|
1396
1355
|
process.exit(1);
|
|
1397
1356
|
}
|
|
1398
1357
|
});
|
|
1399
|
-
notif.command("read <id>").description("Mark notification as read").action(async (id) => {
|
|
1358
|
+
notif.command("read <id>").description("Mark a notification as read.").action(async (id) => {
|
|
1400
1359
|
const opts = program2.opts();
|
|
1401
1360
|
try {
|
|
1402
1361
|
await apiRequest({
|
|
1403
|
-
method: "
|
|
1362
|
+
method: "PATCH",
|
|
1404
1363
|
path: `/app/v1/notifications/${id}/read`,
|
|
1405
1364
|
apiKey: opts.apiKey,
|
|
1406
1365
|
baseUrl: opts.baseUrl
|
|
@@ -1477,8 +1436,6 @@ registerAuthCommands(program);
|
|
|
1477
1436
|
registerOrgCommands(program);
|
|
1478
1437
|
registerUserCommands(program);
|
|
1479
1438
|
registerApiKeyCommands(program);
|
|
1480
|
-
registerCategoryCommands(program);
|
|
1481
|
-
registerTopicCommands(program);
|
|
1482
1439
|
registerSearchCommands(program);
|
|
1483
1440
|
registerIngestCommands(program);
|
|
1484
1441
|
registerContentCommands(program);
|