@senso-ai/cli 0.2.3 → 0.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +53 -63
- package/dist/cli.js +267 -334
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -113,7 +113,7 @@ function getConfigPath() {
|
|
|
113
113
|
}
|
|
114
114
|
|
|
115
115
|
// src/utils/updater.ts
|
|
116
|
-
var
|
|
116
|
+
var NPM_PACKAGE = "@senso-ai/cli";
|
|
117
117
|
var CHECK_INTERVAL_MS = 24 * 60 * 60 * 1e3;
|
|
118
118
|
async function checkForUpdate(quiet) {
|
|
119
119
|
if (process.env.SENSO_NO_UPDATE_CHECK === "1" || quiet) {
|
|
@@ -128,16 +128,8 @@ async function checkForUpdate(quiet) {
|
|
|
128
128
|
return;
|
|
129
129
|
}
|
|
130
130
|
try {
|
|
131
|
-
const
|
|
132
|
-
|
|
133
|
-
{
|
|
134
|
-
headers: { Accept: "application/vnd.github.v3+json" },
|
|
135
|
-
signal: AbortSignal.timeout(5e3)
|
|
136
|
-
}
|
|
137
|
-
);
|
|
138
|
-
if (!res.ok) return;
|
|
139
|
-
const release = await res.json();
|
|
140
|
-
const latest = release.tag_name.replace(/^v/, "");
|
|
131
|
+
const latest = await getLatestVersion();
|
|
132
|
+
if (!latest) return;
|
|
141
133
|
updateConfig({
|
|
142
134
|
lastUpdateCheck: (/* @__PURE__ */ new Date()).toISOString(),
|
|
143
135
|
latestVersion: latest
|
|
@@ -148,17 +140,18 @@ async function checkForUpdate(quiet) {
|
|
|
148
140
|
} catch {
|
|
149
141
|
}
|
|
150
142
|
}
|
|
151
|
-
async function
|
|
143
|
+
async function getLatestVersion() {
|
|
152
144
|
try {
|
|
153
145
|
const res = await fetch(
|
|
154
|
-
`https://
|
|
146
|
+
`https://registry.npmjs.org/${NPM_PACKAGE}`,
|
|
155
147
|
{
|
|
156
|
-
headers: { Accept: "application/
|
|
148
|
+
headers: { Accept: "application/json" },
|
|
157
149
|
signal: AbortSignal.timeout(1e4)
|
|
158
150
|
}
|
|
159
151
|
);
|
|
160
152
|
if (!res.ok) return null;
|
|
161
|
-
|
|
153
|
+
const data = await res.json();
|
|
154
|
+
return data["dist-tags"]?.latest ?? null;
|
|
162
155
|
} catch {
|
|
163
156
|
return null;
|
|
164
157
|
}
|
|
@@ -211,17 +204,23 @@ async function apiRequest(opts) {
|
|
|
211
204
|
});
|
|
212
205
|
if (!res.ok) {
|
|
213
206
|
let body;
|
|
207
|
+
const text3 = await res.text();
|
|
214
208
|
try {
|
|
215
|
-
body =
|
|
209
|
+
body = JSON.parse(text3);
|
|
216
210
|
} catch {
|
|
217
|
-
body =
|
|
211
|
+
body = text3;
|
|
218
212
|
}
|
|
219
213
|
throw new ApiError(res.status, res.statusText, body);
|
|
220
214
|
}
|
|
221
215
|
if (res.status === 204) {
|
|
222
216
|
return void 0;
|
|
223
217
|
}
|
|
224
|
-
|
|
218
|
+
const text2 = await res.text();
|
|
219
|
+
try {
|
|
220
|
+
return JSON.parse(text2);
|
|
221
|
+
} catch {
|
|
222
|
+
throw new Error(`Invalid JSON response from ${opts.path}`);
|
|
223
|
+
}
|
|
225
224
|
} finally {
|
|
226
225
|
clearTimeout(timeout);
|
|
227
226
|
}
|
|
@@ -280,7 +279,7 @@ async function verifyApiKey(apiKey, baseUrl) {
|
|
|
280
279
|
});
|
|
281
280
|
}
|
|
282
281
|
function registerAuthCommands(program2) {
|
|
283
|
-
program2.command("login").description("
|
|
282
|
+
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
283
|
const opts = program2.opts();
|
|
285
284
|
banner();
|
|
286
285
|
console.log(` ${pc3.bold("Welcome to Senso CLI!")}
|
|
@@ -322,11 +321,11 @@ function registerAuthCommands(program2) {
|
|
|
322
321
|
process.exit(1);
|
|
323
322
|
}
|
|
324
323
|
});
|
|
325
|
-
program2.command("logout").description("Remove stored
|
|
324
|
+
program2.command("logout").description("Remove stored API key and organization info from local config.").action(() => {
|
|
326
325
|
clearConfig();
|
|
327
326
|
success("Credentials removed.");
|
|
328
327
|
});
|
|
329
|
-
program2.command("whoami").description("Show
|
|
328
|
+
program2.command("whoami").description("Show which organization you are authenticated as, including org ID, slug, tier, and API key prefix.").action(async () => {
|
|
330
329
|
const opts = program2.opts();
|
|
331
330
|
const apiKey = getApiKey({ apiKey: opts.apiKey });
|
|
332
331
|
if (!apiKey) {
|
|
@@ -372,8 +371,8 @@ function registerAuthCommands(program2) {
|
|
|
372
371
|
|
|
373
372
|
// src/commands/org.ts
|
|
374
373
|
function registerOrgCommands(program2) {
|
|
375
|
-
const org = program2.command("org").description("
|
|
376
|
-
org.command("get").description("Get organization details").action(async () => {
|
|
374
|
+
const org = program2.command("org").description("View and update organization profile and settings. Includes name, slug, logo, websites, locations, and tier information.");
|
|
375
|
+
org.command("get").description("Get full organization details including name, slug, tier, websites, locations, configured AI models, publishers, and schedule.").action(async () => {
|
|
377
376
|
const opts = program2.opts();
|
|
378
377
|
try {
|
|
379
378
|
const data = await apiRequest({ path: "/org/me", apiKey: opts.apiKey, baseUrl: opts.baseUrl });
|
|
@@ -383,12 +382,12 @@ function registerOrgCommands(program2) {
|
|
|
383
382
|
process.exit(1);
|
|
384
383
|
}
|
|
385
384
|
});
|
|
386
|
-
org.command("update").description("Update organization details").requiredOption("--data <json>",
|
|
385
|
+
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
386
|
const opts = program2.opts();
|
|
388
387
|
try {
|
|
389
388
|
const body = JSON.parse(cmdOpts.data);
|
|
390
389
|
const data = await apiRequest({
|
|
391
|
-
method: "
|
|
390
|
+
method: "PUT",
|
|
392
391
|
path: "/org/me",
|
|
393
392
|
body,
|
|
394
393
|
apiKey: opts.apiKey,
|
|
@@ -405,18 +404,23 @@ function registerOrgCommands(program2) {
|
|
|
405
404
|
|
|
406
405
|
// src/commands/users.ts
|
|
407
406
|
function registerUserCommands(program2) {
|
|
408
|
-
const users = program2.command("users").description("Manage users
|
|
409
|
-
users.command("list").description("List users in organization").action(async () => {
|
|
407
|
+
const users = program2.command("users").description("Manage users within the organization. Add, update roles, remove users, or set the active organization for a user.");
|
|
408
|
+
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
409
|
const opts = program2.opts();
|
|
411
410
|
try {
|
|
412
|
-
const data = await apiRequest({
|
|
411
|
+
const data = await apiRequest({
|
|
412
|
+
path: "/org/users",
|
|
413
|
+
params: { limit: cmdOpts.limit, offset: cmdOpts.offset },
|
|
414
|
+
apiKey: opts.apiKey,
|
|
415
|
+
baseUrl: opts.baseUrl
|
|
416
|
+
});
|
|
413
417
|
console.log(JSON.stringify(data, null, 2));
|
|
414
418
|
} catch (err) {
|
|
415
419
|
error(formatApiError(err));
|
|
416
420
|
process.exit(1);
|
|
417
421
|
}
|
|
418
422
|
});
|
|
419
|
-
users.command("add").description("Add user to organization").requiredOption("--data <json>",
|
|
423
|
+
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
424
|
const opts = program2.opts();
|
|
421
425
|
try {
|
|
422
426
|
const body = JSON.parse(cmdOpts.data);
|
|
@@ -434,7 +438,7 @@ function registerUserCommands(program2) {
|
|
|
434
438
|
process.exit(1);
|
|
435
439
|
}
|
|
436
440
|
});
|
|
437
|
-
users.command("get <userId>").description("Get a user in the organization").action(async (userId) => {
|
|
441
|
+
users.command("get <userId>").description("Get a user's details including their role and membership status in the organization.").action(async (userId) => {
|
|
438
442
|
const opts = program2.opts();
|
|
439
443
|
try {
|
|
440
444
|
const data = await apiRequest({ path: `/org/users/${userId}`, apiKey: opts.apiKey, baseUrl: opts.baseUrl });
|
|
@@ -444,12 +448,12 @@ function registerUserCommands(program2) {
|
|
|
444
448
|
process.exit(1);
|
|
445
449
|
}
|
|
446
450
|
});
|
|
447
|
-
users.command("update <userId>").description("Update a user's role").requiredOption("--data <json>",
|
|
451
|
+
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
452
|
const opts = program2.opts();
|
|
449
453
|
try {
|
|
450
454
|
const body = JSON.parse(cmdOpts.data);
|
|
451
455
|
const data = await apiRequest({
|
|
452
|
-
method: "
|
|
456
|
+
method: "PUT",
|
|
453
457
|
path: `/org/users/${userId}`,
|
|
454
458
|
body,
|
|
455
459
|
apiKey: opts.apiKey,
|
|
@@ -462,7 +466,7 @@ function registerUserCommands(program2) {
|
|
|
462
466
|
process.exit(1);
|
|
463
467
|
}
|
|
464
468
|
});
|
|
465
|
-
users.command("remove <userId>").description("Remove a user from the organization").action(async (userId) => {
|
|
469
|
+
users.command("remove <userId>").description("Remove a user from the organization. This does not delete the platform user account.").action(async (userId) => {
|
|
466
470
|
const opts = program2.opts();
|
|
467
471
|
try {
|
|
468
472
|
await apiRequest({ method: "DELETE", path: `/org/users/${userId}`, apiKey: opts.apiKey, baseUrl: opts.baseUrl });
|
|
@@ -472,82 +476,17 @@ function registerUserCommands(program2) {
|
|
|
472
476
|
process.exit(1);
|
|
473
477
|
}
|
|
474
478
|
});
|
|
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) => {
|
|
479
|
+
users.command("set-current <userId>").description("Set this organization as the current (active) organization for a user.").action(async (userId) => {
|
|
491
480
|
const opts = program2.opts();
|
|
492
481
|
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({
|
|
482
|
+
await apiRequest({
|
|
523
483
|
method: "PATCH",
|
|
524
|
-
path: `/org/
|
|
525
|
-
body,
|
|
484
|
+
path: `/org/users/${userId}/current`,
|
|
485
|
+
body: { is_current: true },
|
|
526
486
|
apiKey: opts.apiKey,
|
|
527
487
|
baseUrl: opts.baseUrl
|
|
528
488
|
});
|
|
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.`);
|
|
489
|
+
success(`Organization set as current for user ${userId}.`);
|
|
551
490
|
} catch (err) {
|
|
552
491
|
error(formatApiError(err));
|
|
553
492
|
process.exit(1);
|
|
@@ -555,201 +494,90 @@ function registerApiKeyCommands(program2) {
|
|
|
555
494
|
});
|
|
556
495
|
}
|
|
557
496
|
|
|
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) => {
|
|
497
|
+
// src/commands/api-keys.ts
|
|
498
|
+
function registerApiKeyCommands(program2) {
|
|
499
|
+
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.");
|
|
500
|
+
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
501
|
const opts = program2.opts();
|
|
610
502
|
try {
|
|
611
503
|
const data = await apiRequest({
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
body: { name: cmdOpts.name },
|
|
504
|
+
path: "/org/api-keys",
|
|
505
|
+
params: { limit: cmdOpts.limit, offset: cmdOpts.offset },
|
|
615
506
|
apiKey: opts.apiKey,
|
|
616
507
|
baseUrl: opts.baseUrl
|
|
617
508
|
});
|
|
618
|
-
success(`Category ${id} updated.`);
|
|
619
509
|
console.log(JSON.stringify(data, null, 2));
|
|
620
510
|
} catch (err) {
|
|
621
511
|
error(formatApiError(err));
|
|
622
512
|
process.exit(1);
|
|
623
513
|
}
|
|
624
514
|
});
|
|
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) => {
|
|
515
|
+
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
516
|
const opts = program2.opts();
|
|
637
517
|
try {
|
|
638
518
|
const body = JSON.parse(cmdOpts.data);
|
|
639
519
|
const data = await apiRequest({
|
|
640
520
|
method: "POST",
|
|
641
|
-
path: "/org/
|
|
521
|
+
path: "/org/api-keys",
|
|
642
522
|
body,
|
|
643
523
|
apiKey: opts.apiKey,
|
|
644
524
|
baseUrl: opts.baseUrl
|
|
645
525
|
});
|
|
646
|
-
success("
|
|
526
|
+
success("API key created.");
|
|
647
527
|
console.log(JSON.stringify(data, null, 2));
|
|
648
528
|
} catch (err) {
|
|
649
529
|
error(err instanceof SyntaxError ? "Invalid JSON in --data" : formatApiError(err));
|
|
650
530
|
process.exit(1);
|
|
651
531
|
}
|
|
652
532
|
});
|
|
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) => {
|
|
533
|
+
keys.command("get <keyId>").description("Get details for a specific API key including name, expiry, and last used timestamp.").action(async (keyId) => {
|
|
659
534
|
const opts = program2.opts();
|
|
660
535
|
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) => {
|
|
673
|
-
const opts = program2.opts();
|
|
674
|
-
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.`);
|
|
536
|
+
const data = await apiRequest({ path: `/org/api-keys/${keyId}`, apiKey: opts.apiKey, baseUrl: opts.baseUrl });
|
|
683
537
|
console.log(JSON.stringify(data, null, 2));
|
|
684
538
|
} catch (err) {
|
|
685
539
|
error(formatApiError(err));
|
|
686
540
|
process.exit(1);
|
|
687
541
|
}
|
|
688
542
|
});
|
|
689
|
-
|
|
543
|
+
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
544
|
const opts = program2.opts();
|
|
691
545
|
try {
|
|
546
|
+
const body = JSON.parse(cmdOpts.data);
|
|
692
547
|
const data = await apiRequest({
|
|
693
|
-
|
|
548
|
+
method: "PUT",
|
|
549
|
+
path: `/org/api-keys/${keyId}`,
|
|
550
|
+
body,
|
|
694
551
|
apiKey: opts.apiKey,
|
|
695
552
|
baseUrl: opts.baseUrl
|
|
696
553
|
});
|
|
554
|
+
success(`API key ${keyId} updated.`);
|
|
697
555
|
console.log(JSON.stringify(data, null, 2));
|
|
698
556
|
} catch (err) {
|
|
699
|
-
error(formatApiError(err));
|
|
557
|
+
error(err instanceof SyntaxError ? "Invalid JSON in --data" : formatApiError(err));
|
|
700
558
|
process.exit(1);
|
|
701
559
|
}
|
|
702
560
|
});
|
|
703
|
-
|
|
561
|
+
keys.command("delete <keyId>").description("Permanently delete an API key. This cannot be undone.").action(async (keyId) => {
|
|
704
562
|
const opts = program2.opts();
|
|
705
563
|
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));
|
|
564
|
+
await apiRequest({ method: "DELETE", path: `/org/api-keys/${keyId}`, apiKey: opts.apiKey, baseUrl: opts.baseUrl });
|
|
565
|
+
success(`API key ${keyId} deleted.`);
|
|
715
566
|
} catch (err) {
|
|
716
567
|
error(formatApiError(err));
|
|
717
568
|
process.exit(1);
|
|
718
569
|
}
|
|
719
570
|
});
|
|
720
|
-
|
|
571
|
+
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
572
|
const opts = program2.opts();
|
|
722
573
|
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.`);
|
|
574
|
+
await apiRequest({ method: "POST", path: `/org/api-keys/${keyId}/revoke`, apiKey: opts.apiKey, baseUrl: opts.baseUrl });
|
|
575
|
+
success(`API key ${keyId} revoked.`);
|
|
730
576
|
} catch (err) {
|
|
731
577
|
error(formatApiError(err));
|
|
732
578
|
process.exit(1);
|
|
733
579
|
}
|
|
734
580
|
});
|
|
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
581
|
}
|
|
754
582
|
|
|
755
583
|
// src/commands/search.ts
|
|
@@ -807,7 +635,7 @@ function output(format, data) {
|
|
|
807
635
|
|
|
808
636
|
// src/commands/search.ts
|
|
809
637
|
function registerSearchCommands(program2) {
|
|
810
|
-
const search = program2.command("search").description("
|
|
638
|
+
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
639
|
search.argument("<query>", "Search query").option("--max-results <n>", "Maximum number of results", "5").action(async (query, cmdOpts) => {
|
|
812
640
|
const opts = program2.opts();
|
|
813
641
|
try {
|
|
@@ -847,7 +675,7 @@ function registerSearchCommands(program2) {
|
|
|
847
675
|
process.exit(1);
|
|
848
676
|
}
|
|
849
677
|
});
|
|
850
|
-
search.command("context <query>").description("
|
|
678
|
+
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
679
|
const opts = program2.opts();
|
|
852
680
|
try {
|
|
853
681
|
const data = await apiRequest({
|
|
@@ -863,7 +691,7 @@ function registerSearchCommands(program2) {
|
|
|
863
691
|
process.exit(1);
|
|
864
692
|
}
|
|
865
693
|
});
|
|
866
|
-
search.command("content <query>").description("
|
|
694
|
+
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
695
|
const opts = program2.opts();
|
|
868
696
|
try {
|
|
869
697
|
const data = await apiRequest({
|
|
@@ -889,34 +717,119 @@ function outputByFormat(format, data) {
|
|
|
889
717
|
}
|
|
890
718
|
|
|
891
719
|
// src/commands/ingest.ts
|
|
720
|
+
import { createHash } from "crypto";
|
|
721
|
+
import { readFile, stat } from "fs/promises";
|
|
722
|
+
import { basename, resolve } from "path";
|
|
723
|
+
var MIME_TYPES = {
|
|
724
|
+
".pdf": "application/pdf",
|
|
725
|
+
".txt": "text/plain",
|
|
726
|
+
".csv": "text/csv",
|
|
727
|
+
".doc": "application/msword",
|
|
728
|
+
".docx": "application/vnd.openxmlformats-officedocument.wordprocessingml.document",
|
|
729
|
+
".xls": "application/vnd.ms-excel",
|
|
730
|
+
".xlsx": "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
|
|
731
|
+
".ppt": "application/vnd.ms-powerpoint",
|
|
732
|
+
".pptx": "application/vnd.openxmlformats-officedocument.presentationml.presentation",
|
|
733
|
+
".html": "text/html",
|
|
734
|
+
".htm": "text/html",
|
|
735
|
+
".md": "text/markdown",
|
|
736
|
+
".json": "application/json",
|
|
737
|
+
".xml": "application/xml"
|
|
738
|
+
};
|
|
739
|
+
function getMimeType(filename) {
|
|
740
|
+
const ext = filename.slice(filename.lastIndexOf(".")).toLowerCase();
|
|
741
|
+
return MIME_TYPES[ext] || "application/octet-stream";
|
|
742
|
+
}
|
|
743
|
+
async function getFileMetadata(filePath) {
|
|
744
|
+
const absPath = resolve(filePath);
|
|
745
|
+
const buffer = await readFile(absPath);
|
|
746
|
+
const stats = await stat(absPath);
|
|
747
|
+
const hash = createHash("md5").update(buffer).digest("hex");
|
|
748
|
+
return {
|
|
749
|
+
meta: {
|
|
750
|
+
filename: basename(absPath),
|
|
751
|
+
file_size_bytes: stats.size,
|
|
752
|
+
content_type: getMimeType(basename(absPath)),
|
|
753
|
+
content_hash_md5: hash
|
|
754
|
+
},
|
|
755
|
+
buffer
|
|
756
|
+
};
|
|
757
|
+
}
|
|
758
|
+
async function uploadToS3(url, buffer, contentType) {
|
|
759
|
+
const res = await fetch(url, {
|
|
760
|
+
method: "PUT",
|
|
761
|
+
headers: { "Content-Type": contentType },
|
|
762
|
+
body: buffer
|
|
763
|
+
});
|
|
764
|
+
if (!res.ok) {
|
|
765
|
+
throw new Error(`S3 upload failed: ${res.status} ${res.statusText}`);
|
|
766
|
+
}
|
|
767
|
+
}
|
|
892
768
|
function registerIngestCommands(program2) {
|
|
893
|
-
const ingest = program2.command("ingest").description("
|
|
894
|
-
ingest.command("upload").description("
|
|
769
|
+
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.");
|
|
770
|
+
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
771
|
const opts = program2.opts();
|
|
772
|
+
if (files.length > 10) {
|
|
773
|
+
error("Maximum 10 files per upload request.");
|
|
774
|
+
process.exit(1);
|
|
775
|
+
}
|
|
896
776
|
try {
|
|
897
|
-
const
|
|
777
|
+
const fileData = await Promise.all(files.map(getFileMetadata));
|
|
778
|
+
const results = await apiRequest({
|
|
898
779
|
method: "POST",
|
|
899
780
|
path: "/org/ingestion/upload",
|
|
900
|
-
body: { files:
|
|
781
|
+
body: { files: fileData.map((f) => f.meta) },
|
|
901
782
|
apiKey: opts.apiKey,
|
|
902
783
|
baseUrl: opts.baseUrl
|
|
903
784
|
});
|
|
904
|
-
|
|
785
|
+
const items = Array.isArray(results) ? results : [];
|
|
786
|
+
let uploaded = 0;
|
|
787
|
+
for (const item of items) {
|
|
788
|
+
if (item.status === "upload_pending" && item.upload_url) {
|
|
789
|
+
const match = fileData.find((f) => f.meta.filename === item.filename);
|
|
790
|
+
if (match) {
|
|
791
|
+
await uploadToS3(item.upload_url, match.buffer, match.meta.content_type);
|
|
792
|
+
uploaded++;
|
|
793
|
+
success(`Uploaded ${item.filename} (content_id: ${item.content_id})`);
|
|
794
|
+
}
|
|
795
|
+
} else {
|
|
796
|
+
warn(`Skipped ${item.filename}: ${item.status}${item.message ? ` \u2014 ${item.message}` : ""}`);
|
|
797
|
+
}
|
|
798
|
+
}
|
|
799
|
+
if (uploaded > 0) {
|
|
800
|
+
success(`${uploaded} file(s) uploaded. Background processing will parse, chunk, and embed them.`);
|
|
801
|
+
}
|
|
802
|
+
if (opts.output === "json") {
|
|
803
|
+
console.log(JSON.stringify(results, null, 2));
|
|
804
|
+
}
|
|
905
805
|
} catch (err) {
|
|
906
806
|
error(formatApiError(err));
|
|
907
807
|
process.exit(1);
|
|
908
808
|
}
|
|
909
809
|
});
|
|
910
|
-
ingest.command("reprocess <contentId>").description("
|
|
810
|
+
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
811
|
const opts = program2.opts();
|
|
912
812
|
try {
|
|
913
|
-
await
|
|
914
|
-
|
|
915
|
-
|
|
813
|
+
const { meta, buffer } = await getFileMetadata(file);
|
|
814
|
+
const results = await apiRequest({
|
|
815
|
+
method: "PUT",
|
|
816
|
+
path: `/org/ingestion/content/${contentId}`,
|
|
817
|
+
body: { file: meta },
|
|
916
818
|
apiKey: opts.apiKey,
|
|
917
819
|
baseUrl: opts.baseUrl
|
|
918
820
|
});
|
|
919
|
-
|
|
821
|
+
const items = Array.isArray(results) ? results : [];
|
|
822
|
+
for (const item of items) {
|
|
823
|
+
if (item.status === "upload_pending" && item.upload_url) {
|
|
824
|
+
await uploadToS3(item.upload_url, buffer, meta.content_type);
|
|
825
|
+
success(`Uploaded ${meta.filename} for content ${contentId}. Background re-processing started.`);
|
|
826
|
+
} else {
|
|
827
|
+
warn(`Skipped: ${item.status}${item.message ? ` \u2014 ${item.message}` : ""}`);
|
|
828
|
+
}
|
|
829
|
+
}
|
|
830
|
+
if (opts.output === "json") {
|
|
831
|
+
console.log(JSON.stringify(results, null, 2));
|
|
832
|
+
}
|
|
920
833
|
} catch (err) {
|
|
921
834
|
error(formatApiError(err));
|
|
922
835
|
process.exit(1);
|
|
@@ -927,13 +840,13 @@ function registerIngestCommands(program2) {
|
|
|
927
840
|
// src/commands/content.ts
|
|
928
841
|
import pc6 from "picocolors";
|
|
929
842
|
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) => {
|
|
843
|
+
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.");
|
|
844
|
+
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
845
|
const opts = program2.opts();
|
|
933
846
|
try {
|
|
934
847
|
const data = await apiRequest({
|
|
935
848
|
path: "/org/content",
|
|
936
|
-
params: { limit: cmdOpts.limit, offset: cmdOpts.offset },
|
|
849
|
+
params: { limit: cmdOpts.limit, offset: cmdOpts.offset, search: cmdOpts.search, sort: cmdOpts.sort },
|
|
937
850
|
apiKey: opts.apiKey,
|
|
938
851
|
baseUrl: opts.baseUrl
|
|
939
852
|
});
|
|
@@ -958,7 +871,7 @@ function registerContentCommands(program2) {
|
|
|
958
871
|
process.exit(1);
|
|
959
872
|
}
|
|
960
873
|
});
|
|
961
|
-
content.command("get <id>").description("Get content item by ID").action(async (id) => {
|
|
874
|
+
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
875
|
const opts = program2.opts();
|
|
963
876
|
try {
|
|
964
877
|
const data = await apiRequest({ path: `/org/content/${id}`, apiKey: opts.apiKey, baseUrl: opts.baseUrl });
|
|
@@ -968,7 +881,7 @@ function registerContentCommands(program2) {
|
|
|
968
881
|
process.exit(1);
|
|
969
882
|
}
|
|
970
883
|
});
|
|
971
|
-
content.command("delete <id>").description("Delete content
|
|
884
|
+
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
885
|
const opts = program2.opts();
|
|
973
886
|
try {
|
|
974
887
|
await apiRequest({ method: "DELETE", path: `/org/content/${id}`, apiKey: opts.apiKey, baseUrl: opts.baseUrl });
|
|
@@ -978,7 +891,7 @@ function registerContentCommands(program2) {
|
|
|
978
891
|
process.exit(1);
|
|
979
892
|
}
|
|
980
893
|
});
|
|
981
|
-
content.command("unpublish <id>").description("Unpublish content
|
|
894
|
+
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
895
|
const opts = program2.opts();
|
|
983
896
|
try {
|
|
984
897
|
await apiRequest({ method: "POST", path: `/org/content/${id}/unpublish`, apiKey: opts.apiKey, baseUrl: opts.baseUrl });
|
|
@@ -988,27 +901,33 @@ function registerContentCommands(program2) {
|
|
|
988
901
|
process.exit(1);
|
|
989
902
|
}
|
|
990
903
|
});
|
|
991
|
-
content.command("verification").description("List content
|
|
904
|
+
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
905
|
const opts = program2.opts();
|
|
993
906
|
try {
|
|
994
|
-
const data = await apiRequest({
|
|
907
|
+
const data = await apiRequest({
|
|
908
|
+
path: "/org/content/verification",
|
|
909
|
+
params: { limit: cmdOpts.limit, offset: cmdOpts.offset, search: cmdOpts.search, status: cmdOpts.status },
|
|
910
|
+
apiKey: opts.apiKey,
|
|
911
|
+
baseUrl: opts.baseUrl
|
|
912
|
+
});
|
|
995
913
|
console.log(JSON.stringify(data, null, 2));
|
|
996
914
|
} catch (err) {
|
|
997
915
|
error(formatApiError(err));
|
|
998
916
|
process.exit(1);
|
|
999
917
|
}
|
|
1000
918
|
});
|
|
1001
|
-
content.command("reject <versionId>").description("Reject a content version").action(async (versionId) => {
|
|
919
|
+
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
920
|
const opts = program2.opts();
|
|
1003
921
|
try {
|
|
1004
|
-
|
|
922
|
+
const body = cmdOpts.reason ? { reason: cmdOpts.reason } : void 0;
|
|
923
|
+
await apiRequest({ method: "POST", path: `/org/content/versions/${versionId}/reject`, body, apiKey: opts.apiKey, baseUrl: opts.baseUrl });
|
|
1005
924
|
success(`Version ${versionId} rejected.`);
|
|
1006
925
|
} catch (err) {
|
|
1007
926
|
error(formatApiError(err));
|
|
1008
927
|
process.exit(1);
|
|
1009
928
|
}
|
|
1010
929
|
});
|
|
1011
|
-
content.command("restore <versionId>").description("Restore a content version to draft").action(async (versionId) => {
|
|
930
|
+
content.command("restore <versionId>").description("Restore a rejected content version back to draft status for further editing.").action(async (versionId) => {
|
|
1012
931
|
const opts = program2.opts();
|
|
1013
932
|
try {
|
|
1014
933
|
await apiRequest({ method: "POST", path: `/org/content/versions/${versionId}/restore`, apiKey: opts.apiKey, baseUrl: opts.baseUrl });
|
|
@@ -1018,7 +937,7 @@ function registerContentCommands(program2) {
|
|
|
1018
937
|
process.exit(1);
|
|
1019
938
|
}
|
|
1020
939
|
});
|
|
1021
|
-
content.command("owners <id>").description("List content
|
|
940
|
+
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
941
|
const opts = program2.opts();
|
|
1023
942
|
try {
|
|
1024
943
|
const data = await apiRequest({ path: `/org/content/${id}/owners`, apiKey: opts.apiKey, baseUrl: opts.baseUrl });
|
|
@@ -1028,7 +947,7 @@ function registerContentCommands(program2) {
|
|
|
1028
947
|
process.exit(1);
|
|
1029
948
|
}
|
|
1030
949
|
});
|
|
1031
|
-
content.command("set-owners <id>").description("Replace content
|
|
950
|
+
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
951
|
const opts = program2.opts();
|
|
1033
952
|
try {
|
|
1034
953
|
await apiRequest({
|
|
@@ -1044,7 +963,7 @@ function registerContentCommands(program2) {
|
|
|
1044
963
|
process.exit(1);
|
|
1045
964
|
}
|
|
1046
965
|
});
|
|
1047
|
-
content.command("remove-owner <id> <userId>").description("Remove content
|
|
966
|
+
content.command("remove-owner <id> <userId>").description("Remove a single owner from a content item.").action(async (id, userId) => {
|
|
1048
967
|
const opts = program2.opts();
|
|
1049
968
|
try {
|
|
1050
969
|
await apiRequest({ method: "DELETE", path: `/org/content/${id}/owners/${userId}`, apiKey: opts.apiKey, baseUrl: opts.baseUrl });
|
|
@@ -1058,8 +977,8 @@ function registerContentCommands(program2) {
|
|
|
1058
977
|
|
|
1059
978
|
// src/commands/generate.ts
|
|
1060
979
|
function registerGenerateCommands(program2) {
|
|
1061
|
-
const gen = program2.command("generate").description("
|
|
1062
|
-
gen.command("settings").description("Get content generation settings").action(async () => {
|
|
980
|
+
const gen = program2.command("generate").description("AI content generation. Configure settings, generate content samples from prompts, or trigger full content engine runs.");
|
|
981
|
+
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
982
|
const opts = program2.opts();
|
|
1064
983
|
try {
|
|
1065
984
|
const data = await apiRequest({ path: "/org/content-generation", apiKey: opts.apiKey, baseUrl: opts.baseUrl });
|
|
@@ -1069,24 +988,32 @@ function registerGenerateCommands(program2) {
|
|
|
1069
988
|
process.exit(1);
|
|
1070
989
|
}
|
|
1071
990
|
});
|
|
1072
|
-
gen.command("update-settings").description("Update content generation settings").requiredOption("--data <json>",
|
|
991
|
+
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
992
|
const opts = program2.opts();
|
|
1074
993
|
try {
|
|
1075
994
|
const body = JSON.parse(cmdOpts.data);
|
|
1076
995
|
const data = await apiRequest({ method: "PATCH", path: "/org/content-generation", body, apiKey: opts.apiKey, baseUrl: opts.baseUrl });
|
|
996
|
+
success("Content generation settings updated.");
|
|
1077
997
|
console.log(JSON.stringify(data, null, 2));
|
|
1078
998
|
} catch (err) {
|
|
1079
999
|
error(err instanceof SyntaxError ? "Invalid JSON in --data" : formatApiError(err));
|
|
1080
1000
|
process.exit(1);
|
|
1081
1001
|
}
|
|
1082
1002
|
});
|
|
1083
|
-
gen.command("sample").description("Generate ad hoc content sample").requiredOption("--
|
|
1003
|
+
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
1004
|
const opts = program2.opts();
|
|
1085
1005
|
try {
|
|
1006
|
+
const body = {
|
|
1007
|
+
geo_question_id: cmdOpts.promptId,
|
|
1008
|
+
content_type_id: cmdOpts.contentTypeId
|
|
1009
|
+
};
|
|
1010
|
+
if (cmdOpts.destination) {
|
|
1011
|
+
body.publish_destination = cmdOpts.destination;
|
|
1012
|
+
}
|
|
1086
1013
|
const data = await apiRequest({
|
|
1087
1014
|
method: "POST",
|
|
1088
1015
|
path: "/org/content-generation/sample",
|
|
1089
|
-
body
|
|
1016
|
+
body,
|
|
1090
1017
|
apiKey: opts.apiKey,
|
|
1091
1018
|
baseUrl: opts.baseUrl
|
|
1092
1019
|
});
|
|
@@ -1096,10 +1023,11 @@ function registerGenerateCommands(program2) {
|
|
|
1096
1023
|
process.exit(1);
|
|
1097
1024
|
}
|
|
1098
1025
|
});
|
|
1099
|
-
gen.command("run").description("Trigger content engine run").action(async () => {
|
|
1026
|
+
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
1027
|
const opts = program2.opts();
|
|
1101
1028
|
try {
|
|
1102
|
-
const
|
|
1029
|
+
const body = cmdOpts.promptIds ? { prompt_ids: cmdOpts.promptIds } : void 0;
|
|
1030
|
+
const data = await apiRequest({ method: "POST", path: "/org/content-generation/run", body, apiKey: opts.apiKey, baseUrl: opts.baseUrl });
|
|
1103
1031
|
success("Content generation run triggered.");
|
|
1104
1032
|
console.log(JSON.stringify(data, null, 2));
|
|
1105
1033
|
} catch (err) {
|
|
@@ -1111,8 +1039,8 @@ function registerGenerateCommands(program2) {
|
|
|
1111
1039
|
|
|
1112
1040
|
// src/commands/engine.ts
|
|
1113
1041
|
function registerEngineCommands(program2) {
|
|
1114
|
-
const engine = program2.command("engine").description("
|
|
1115
|
-
engine.command("publish").description("Publish content via content engine").requiredOption("--data <json>",
|
|
1042
|
+
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.");
|
|
1043
|
+
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
1044
|
const opts = program2.opts();
|
|
1117
1045
|
try {
|
|
1118
1046
|
const body = JSON.parse(cmdOpts.data);
|
|
@@ -1130,7 +1058,7 @@ function registerEngineCommands(program2) {
|
|
|
1130
1058
|
process.exit(1);
|
|
1131
1059
|
}
|
|
1132
1060
|
});
|
|
1133
|
-
engine.command("draft").description("Save content as draft
|
|
1061
|
+
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
1062
|
const opts = program2.opts();
|
|
1135
1063
|
try {
|
|
1136
1064
|
const body = JSON.parse(cmdOpts.data);
|
|
@@ -1152,8 +1080,8 @@ function registerEngineCommands(program2) {
|
|
|
1152
1080
|
|
|
1153
1081
|
// src/commands/brand-kit.ts
|
|
1154
1082
|
function registerBrandKitCommands(program2) {
|
|
1155
|
-
const bk = program2.command("brand-kit").description("Manage brand kit");
|
|
1156
|
-
bk.command("get").description("Get brand kit").action(async () => {
|
|
1083
|
+
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.");
|
|
1084
|
+
bk.command("get").description("Get the current brand kit guidelines.").action(async () => {
|
|
1157
1085
|
const opts = program2.opts();
|
|
1158
1086
|
try {
|
|
1159
1087
|
const data = await apiRequest({ path: "/org/brand-kit", apiKey: opts.apiKey, baseUrl: opts.baseUrl });
|
|
@@ -1163,7 +1091,7 @@ function registerBrandKitCommands(program2) {
|
|
|
1163
1091
|
process.exit(1);
|
|
1164
1092
|
}
|
|
1165
1093
|
});
|
|
1166
|
-
bk.command("set").description("
|
|
1094
|
+
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
1095
|
const opts = program2.opts();
|
|
1168
1096
|
try {
|
|
1169
1097
|
const body = JSON.parse(cmdOpts.data);
|
|
@@ -1185,18 +1113,23 @@ function registerBrandKitCommands(program2) {
|
|
|
1185
1113
|
|
|
1186
1114
|
// src/commands/content-types.ts
|
|
1187
1115
|
function registerContentTypeCommands(program2) {
|
|
1188
|
-
const ct = program2.command("content-types").description("Manage content types");
|
|
1189
|
-
ct.command("list").description("List content types").action(async () => {
|
|
1116
|
+
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).");
|
|
1117
|
+
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
1118
|
const opts = program2.opts();
|
|
1191
1119
|
try {
|
|
1192
|
-
const data = await apiRequest({
|
|
1120
|
+
const data = await apiRequest({
|
|
1121
|
+
path: "/org/content-types",
|
|
1122
|
+
params: { limit: cmdOpts.limit, offset: cmdOpts.offset },
|
|
1123
|
+
apiKey: opts.apiKey,
|
|
1124
|
+
baseUrl: opts.baseUrl
|
|
1125
|
+
});
|
|
1193
1126
|
console.log(JSON.stringify(data, null, 2));
|
|
1194
1127
|
} catch (err) {
|
|
1195
1128
|
error(formatApiError(err));
|
|
1196
1129
|
process.exit(1);
|
|
1197
1130
|
}
|
|
1198
1131
|
});
|
|
1199
|
-
ct.command("create").description("Create a content type").requiredOption("--data <json>",
|
|
1132
|
+
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
1133
|
const opts = program2.opts();
|
|
1201
1134
|
try {
|
|
1202
1135
|
const body = JSON.parse(cmdOpts.data);
|
|
@@ -1214,7 +1147,7 @@ function registerContentTypeCommands(program2) {
|
|
|
1214
1147
|
process.exit(1);
|
|
1215
1148
|
}
|
|
1216
1149
|
});
|
|
1217
|
-
ct.command("get <id>").description("Get content type by ID").action(async (id) => {
|
|
1150
|
+
ct.command("get <id>").description("Get a content type by ID, including its full configuration.").action(async (id) => {
|
|
1218
1151
|
const opts = program2.opts();
|
|
1219
1152
|
try {
|
|
1220
1153
|
const data = await apiRequest({ path: `/org/content-types/${id}`, apiKey: opts.apiKey, baseUrl: opts.baseUrl });
|
|
@@ -1224,12 +1157,12 @@ function registerContentTypeCommands(program2) {
|
|
|
1224
1157
|
process.exit(1);
|
|
1225
1158
|
}
|
|
1226
1159
|
});
|
|
1227
|
-
ct.command("update <id>").description("Update a content type").requiredOption("--data <json>",
|
|
1160
|
+
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
1161
|
const opts = program2.opts();
|
|
1229
1162
|
try {
|
|
1230
1163
|
const body = JSON.parse(cmdOpts.data);
|
|
1231
1164
|
const data = await apiRequest({
|
|
1232
|
-
method: "
|
|
1165
|
+
method: "PUT",
|
|
1233
1166
|
path: `/org/content-types/${id}`,
|
|
1234
1167
|
body,
|
|
1235
1168
|
apiKey: opts.apiKey,
|
|
@@ -1242,7 +1175,7 @@ function registerContentTypeCommands(program2) {
|
|
|
1242
1175
|
process.exit(1);
|
|
1243
1176
|
}
|
|
1244
1177
|
});
|
|
1245
|
-
ct.command("delete <id>").description("Delete a content type").action(async (id) => {
|
|
1178
|
+
ct.command("delete <id>").description("Delete a content type. This cannot be undone.").action(async (id) => {
|
|
1246
1179
|
const opts = program2.opts();
|
|
1247
1180
|
try {
|
|
1248
1181
|
await apiRequest({ method: "DELETE", path: `/org/content-types/${id}`, apiKey: opts.apiKey, baseUrl: opts.baseUrl });
|
|
@@ -1256,18 +1189,23 @@ function registerContentTypeCommands(program2) {
|
|
|
1256
1189
|
|
|
1257
1190
|
// src/commands/prompts.ts
|
|
1258
1191
|
function registerPromptCommands(program2) {
|
|
1259
|
-
const prompts = program2.command("prompts").description("Manage prompts (geo questions)");
|
|
1260
|
-
prompts.command("list").description("List prompts").action(async () => {
|
|
1192
|
+
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.");
|
|
1193
|
+
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
1194
|
const opts = program2.opts();
|
|
1262
1195
|
try {
|
|
1263
|
-
const data = await apiRequest({
|
|
1196
|
+
const data = await apiRequest({
|
|
1197
|
+
path: "/org/prompts",
|
|
1198
|
+
params: { limit: cmdOpts.limit, offset: cmdOpts.offset, search: cmdOpts.search, sort: cmdOpts.sort },
|
|
1199
|
+
apiKey: opts.apiKey,
|
|
1200
|
+
baseUrl: opts.baseUrl
|
|
1201
|
+
});
|
|
1264
1202
|
console.log(JSON.stringify(data, null, 2));
|
|
1265
1203
|
} catch (err) {
|
|
1266
1204
|
error(formatApiError(err));
|
|
1267
1205
|
process.exit(1);
|
|
1268
1206
|
}
|
|
1269
1207
|
});
|
|
1270
|
-
prompts.command("create").description("Create a prompt").requiredOption("--data <json>",
|
|
1208
|
+
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
1209
|
const opts = program2.opts();
|
|
1272
1210
|
try {
|
|
1273
1211
|
const body = JSON.parse(cmdOpts.data);
|
|
@@ -1285,7 +1223,7 @@ function registerPromptCommands(program2) {
|
|
|
1285
1223
|
process.exit(1);
|
|
1286
1224
|
}
|
|
1287
1225
|
});
|
|
1288
|
-
prompts.command("get <promptId>").description("Get prompt with full run history").action(async (promptId) => {
|
|
1226
|
+
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
1227
|
const opts = program2.opts();
|
|
1290
1228
|
try {
|
|
1291
1229
|
const data = await apiRequest({ path: `/org/prompts/${promptId}`, apiKey: opts.apiKey, baseUrl: opts.baseUrl });
|
|
@@ -1295,7 +1233,7 @@ function registerPromptCommands(program2) {
|
|
|
1295
1233
|
process.exit(1);
|
|
1296
1234
|
}
|
|
1297
1235
|
});
|
|
1298
|
-
prompts.command("delete <promptId>").description("Delete a prompt").action(async (promptId) => {
|
|
1236
|
+
prompts.command("delete <promptId>").description("Delete a prompt and all its associated run history. This cannot be undone.").action(async (promptId) => {
|
|
1299
1237
|
const opts = program2.opts();
|
|
1300
1238
|
try {
|
|
1301
1239
|
await apiRequest({ method: "DELETE", path: `/org/prompts/${promptId}`, apiKey: opts.apiKey, baseUrl: opts.baseUrl });
|
|
@@ -1309,8 +1247,8 @@ function registerPromptCommands(program2) {
|
|
|
1309
1247
|
|
|
1310
1248
|
// src/commands/run-config.ts
|
|
1311
1249
|
function registerRunConfigCommands(program2) {
|
|
1312
|
-
const rc = program2.command("run-config").description("
|
|
1313
|
-
rc.command("models").description("Get
|
|
1250
|
+
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.");
|
|
1251
|
+
rc.command("models").description("Get the AI models currently configured for question runs (e.g. chatgpt, gemini).").action(async () => {
|
|
1314
1252
|
const opts = program2.opts();
|
|
1315
1253
|
try {
|
|
1316
1254
|
const data = await apiRequest({ path: "/org/run-models", apiKey: opts.apiKey, baseUrl: opts.baseUrl });
|
|
@@ -1320,7 +1258,7 @@ function registerRunConfigCommands(program2) {
|
|
|
1320
1258
|
process.exit(1);
|
|
1321
1259
|
}
|
|
1322
1260
|
});
|
|
1323
|
-
rc.command("set-models").description("
|
|
1261
|
+
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
1262
|
const opts = program2.opts();
|
|
1325
1263
|
try {
|
|
1326
1264
|
const body = JSON.parse(cmdOpts.data);
|
|
@@ -1338,7 +1276,7 @@ function registerRunConfigCommands(program2) {
|
|
|
1338
1276
|
process.exit(1);
|
|
1339
1277
|
}
|
|
1340
1278
|
});
|
|
1341
|
-
rc.command("schedule").description("Get
|
|
1279
|
+
rc.command("schedule").description("Get the days of the week when question runs are triggered (0=Sunday, 1=Monday, ..., 6=Saturday).").action(async () => {
|
|
1342
1280
|
const opts = program2.opts();
|
|
1343
1281
|
try {
|
|
1344
1282
|
const data = await apiRequest({ path: "/org/run-schedule", apiKey: opts.apiKey, baseUrl: opts.baseUrl });
|
|
@@ -1348,7 +1286,7 @@ function registerRunConfigCommands(program2) {
|
|
|
1348
1286
|
process.exit(1);
|
|
1349
1287
|
}
|
|
1350
1288
|
});
|
|
1351
|
-
rc.command("set-schedule").description("Set
|
|
1289
|
+
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
1290
|
const opts = program2.opts();
|
|
1353
1291
|
try {
|
|
1354
1292
|
const body = JSON.parse(cmdOpts.data);
|
|
@@ -1370,11 +1308,16 @@ function registerRunConfigCommands(program2) {
|
|
|
1370
1308
|
|
|
1371
1309
|
// src/commands/members.ts
|
|
1372
1310
|
function registerMemberCommands(program2) {
|
|
1373
|
-
const members = program2.command("members").description("
|
|
1374
|
-
members.command("list").description("List organization members").action(async () => {
|
|
1311
|
+
const members = program2.command("members").description("View the organization member directory. Lists all users who belong to the organization with their names and emails.");
|
|
1312
|
+
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
1313
|
const opts = program2.opts();
|
|
1376
1314
|
try {
|
|
1377
|
-
const data = await apiRequest({
|
|
1315
|
+
const data = await apiRequest({
|
|
1316
|
+
path: "/org/members",
|
|
1317
|
+
params: { limit: cmdOpts.limit, offset: cmdOpts.offset, search: cmdOpts.search, sort: cmdOpts.sort },
|
|
1318
|
+
apiKey: opts.apiKey,
|
|
1319
|
+
baseUrl: opts.baseUrl
|
|
1320
|
+
});
|
|
1378
1321
|
console.log(JSON.stringify(data, null, 2));
|
|
1379
1322
|
} catch (err) {
|
|
1380
1323
|
error(formatApiError(err));
|
|
@@ -1385,22 +1328,31 @@ function registerMemberCommands(program2) {
|
|
|
1385
1328
|
|
|
1386
1329
|
// src/commands/notifications.ts
|
|
1387
1330
|
function registerNotificationCommands(program2) {
|
|
1388
|
-
const notif = program2.command("notifications").description("
|
|
1389
|
-
notif.command("list").description("List notifications").action(async () => {
|
|
1331
|
+
const notif = program2.command("notifications").description("View and manage user notifications. Notifications are triggered by content verification, generation runs, and other system events.");
|
|
1332
|
+
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
1333
|
const opts = program2.opts();
|
|
1391
1334
|
try {
|
|
1392
|
-
const data = await apiRequest({
|
|
1335
|
+
const data = await apiRequest({
|
|
1336
|
+
path: "/app/v1/notifications",
|
|
1337
|
+
params: {
|
|
1338
|
+
limit: cmdOpts.limit,
|
|
1339
|
+
offset: cmdOpts.offset,
|
|
1340
|
+
unread_only: cmdOpts.unreadOnly ? "true" : void 0
|
|
1341
|
+
},
|
|
1342
|
+
apiKey: opts.apiKey,
|
|
1343
|
+
baseUrl: opts.baseUrl
|
|
1344
|
+
});
|
|
1393
1345
|
console.log(JSON.stringify(data, null, 2));
|
|
1394
1346
|
} catch (err) {
|
|
1395
1347
|
error(formatApiError(err));
|
|
1396
1348
|
process.exit(1);
|
|
1397
1349
|
}
|
|
1398
1350
|
});
|
|
1399
|
-
notif.command("read <id>").description("Mark notification as read").action(async (id) => {
|
|
1351
|
+
notif.command("read <id>").description("Mark a notification as read.").action(async (id) => {
|
|
1400
1352
|
const opts = program2.opts();
|
|
1401
1353
|
try {
|
|
1402
1354
|
await apiRequest({
|
|
1403
|
-
method: "
|
|
1355
|
+
method: "PATCH",
|
|
1404
1356
|
path: `/app/v1/notifications/${id}/read`,
|
|
1405
1357
|
apiKey: opts.apiKey,
|
|
1406
1358
|
baseUrl: opts.baseUrl
|
|
@@ -1417,16 +1369,16 @@ function registerNotificationCommands(program2) {
|
|
|
1417
1369
|
import semver2 from "semver";
|
|
1418
1370
|
import pc7 from "picocolors";
|
|
1419
1371
|
import { execSync } from "child_process";
|
|
1372
|
+
var NPM_PACKAGE2 = "@senso-ai/cli";
|
|
1420
1373
|
function registerUpdateCommand(program2) {
|
|
1421
1374
|
program2.command("update").description("Update CLI to the latest version").action(async () => {
|
|
1422
1375
|
info(`Current version: ${pc7.bold(version)}`);
|
|
1423
|
-
info("Checking for updates...");
|
|
1424
|
-
const
|
|
1425
|
-
if (!
|
|
1376
|
+
info("Checking npm for updates...");
|
|
1377
|
+
const latest = await getLatestVersion();
|
|
1378
|
+
if (!latest) {
|
|
1426
1379
|
error("Could not check for updates. Try again later.");
|
|
1427
1380
|
process.exit(1);
|
|
1428
1381
|
}
|
|
1429
|
-
const latest = release.tag_name.replace(/^v/, "");
|
|
1430
1382
|
if (!semver2.gt(latest, version)) {
|
|
1431
1383
|
success(`Already on the latest version (${version}).`);
|
|
1432
1384
|
return;
|
|
@@ -1434,33 +1386,16 @@ function registerUpdateCommand(program2) {
|
|
|
1434
1386
|
info(`New version available: ${pc7.bold(latest)}`);
|
|
1435
1387
|
info("Updating...");
|
|
1436
1388
|
try {
|
|
1437
|
-
execSync(
|
|
1389
|
+
execSync(`npm install -g ${NPM_PACKAGE2}@latest`, {
|
|
1438
1390
|
stdio: "inherit"
|
|
1439
1391
|
});
|
|
1440
1392
|
success(`Updated to v${latest}.`);
|
|
1441
|
-
if (release.body) {
|
|
1442
|
-
console.log();
|
|
1443
|
-
console.log(pc7.dim("Release notes:"));
|
|
1444
|
-
console.log(pc7.dim(release.body.slice(0, 500)));
|
|
1445
|
-
}
|
|
1446
1393
|
} catch {
|
|
1447
|
-
|
|
1448
|
-
|
|
1449
|
-
|
|
1450
|
-
|
|
1451
|
-
|
|
1452
|
-
);
|
|
1453
|
-
success("Updated via npx cache refresh.");
|
|
1454
|
-
} catch {
|
|
1455
|
-
error("Update failed. Please reinstall manually:");
|
|
1456
|
-
console.log(
|
|
1457
|
-
` ${pc7.cyan("npm install -g senso-user-cli")}`
|
|
1458
|
-
);
|
|
1459
|
-
console.log(
|
|
1460
|
-
` ${pc7.dim("or")} ${pc7.cyan("npx github:AI-Template-SDK/senso-user-cli")}`
|
|
1461
|
-
);
|
|
1462
|
-
process.exit(1);
|
|
1463
|
-
}
|
|
1394
|
+
error("Update failed. Please reinstall manually:");
|
|
1395
|
+
console.log(
|
|
1396
|
+
` ${pc7.cyan(`npm install -g ${NPM_PACKAGE2}`)}`
|
|
1397
|
+
);
|
|
1398
|
+
process.exit(1);
|
|
1464
1399
|
}
|
|
1465
1400
|
});
|
|
1466
1401
|
}
|
|
@@ -1477,8 +1412,6 @@ registerAuthCommands(program);
|
|
|
1477
1412
|
registerOrgCommands(program);
|
|
1478
1413
|
registerUserCommands(program);
|
|
1479
1414
|
registerApiKeyCommands(program);
|
|
1480
|
-
registerCategoryCommands(program);
|
|
1481
|
-
registerTopicCommands(program);
|
|
1482
1415
|
registerSearchCommands(program);
|
|
1483
1416
|
registerIngestCommands(program);
|
|
1484
1417
|
registerContentCommands(program);
|