@senso-ai/cli 0.4.0 → 0.5.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/dist/cli.js +73 -12
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -636,13 +636,16 @@ function output(format, data) {
|
|
|
636
636
|
// src/commands/search.ts
|
|
637
637
|
function registerSearchCommands(program2) {
|
|
638
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.");
|
|
639
|
-
search.argument("<query>", "Search query").option("--max-results <n>", "Maximum number of results", "5").action(async (query, cmdOpts) => {
|
|
639
|
+
search.argument("<query>", "Search query").option("--max-results <n>", "Maximum number of results", "5").option("--content-ids <ids...>", "Restrict search to specific content item IDs (space-separated UUIDs)").option("--require-scoped-ids", "Only return results from the specified --content-ids (omit to allow fallback to all content)").action(async (query, cmdOpts) => {
|
|
640
640
|
const opts = program2.opts();
|
|
641
641
|
try {
|
|
642
|
+
const body = { query, max_results: parseInt(cmdOpts.maxResults) };
|
|
643
|
+
if (cmdOpts.contentIds) body.content_ids = cmdOpts.contentIds;
|
|
644
|
+
if (cmdOpts.requireScopedIds) body.require_scoped_ids = true;
|
|
642
645
|
const data = await apiRequest({
|
|
643
646
|
method: "POST",
|
|
644
647
|
path: "/org/search",
|
|
645
|
-
body
|
|
648
|
+
body,
|
|
646
649
|
apiKey: opts.apiKey,
|
|
647
650
|
baseUrl: opts.baseUrl
|
|
648
651
|
});
|
|
@@ -675,13 +678,16 @@ function registerSearchCommands(program2) {
|
|
|
675
678
|
process.exit(1);
|
|
676
679
|
}
|
|
677
680
|
});
|
|
678
|
-
search.command("context <query>").description("Search the knowledge base \u2014 returns matching content chunks only, without AI answer generation.
|
|
681
|
+
search.command("context <query>").description("Search the knowledge base \u2014 returns matching content chunks only, without AI answer generation. Use this to feed verified chunks into your own LLM pipeline instead of using Senso's generated answer.").option("--max-results <n>", "Maximum results", "5").option("--content-ids <ids...>", "Restrict search to specific content item IDs (space-separated UUIDs)").option("--require-scoped-ids", "Only return results from the specified --content-ids").action(async (query, cmdOpts) => {
|
|
679
682
|
const opts = program2.opts();
|
|
680
683
|
try {
|
|
684
|
+
const body = { query, max_results: parseInt(cmdOpts.maxResults) };
|
|
685
|
+
if (cmdOpts.contentIds) body.content_ids = cmdOpts.contentIds;
|
|
686
|
+
if (cmdOpts.requireScopedIds) body.require_scoped_ids = true;
|
|
681
687
|
const data = await apiRequest({
|
|
682
688
|
method: "POST",
|
|
683
689
|
path: "/org/search/context",
|
|
684
|
-
body
|
|
690
|
+
body,
|
|
685
691
|
apiKey: opts.apiKey,
|
|
686
692
|
baseUrl: opts.baseUrl
|
|
687
693
|
});
|
|
@@ -691,13 +697,16 @@ function registerSearchCommands(program2) {
|
|
|
691
697
|
process.exit(1);
|
|
692
698
|
}
|
|
693
699
|
});
|
|
694
|
-
search.command("content <query>").description("Search the knowledge base \u2014 returns deduplicated content IDs and titles only.
|
|
700
|
+
search.command("content <query>").description("Search the knowledge base \u2014 returns deduplicated content IDs and titles only. Use this to discover which documents are relevant before fetching full content with 'content get <id>'.").option("--max-results <n>", "Maximum results", "5").option("--content-ids <ids...>", "Restrict search to specific content item IDs (space-separated UUIDs)").option("--require-scoped-ids", "Only return results from the specified --content-ids").action(async (query, cmdOpts) => {
|
|
695
701
|
const opts = program2.opts();
|
|
696
702
|
try {
|
|
703
|
+
const body = { query, max_results: parseInt(cmdOpts.maxResults) };
|
|
704
|
+
if (cmdOpts.contentIds) body.content_ids = cmdOpts.contentIds;
|
|
705
|
+
if (cmdOpts.requireScopedIds) body.require_scoped_ids = true;
|
|
697
706
|
const data = await apiRequest({
|
|
698
707
|
method: "POST",
|
|
699
708
|
path: "/org/search/content",
|
|
700
|
-
body
|
|
709
|
+
body,
|
|
701
710
|
apiKey: opts.apiKey,
|
|
702
711
|
baseUrl: opts.baseUrl
|
|
703
712
|
});
|
|
@@ -767,7 +776,7 @@ async function uploadToS3(url, buffer, contentType) {
|
|
|
767
776
|
}
|
|
768
777
|
function registerIngestCommands(program2) {
|
|
769
778
|
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) => {
|
|
779
|
+
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. Poll 'senso content get <content-id>' until processing_status is 'complete' before searching the uploaded content.").action(async (files) => {
|
|
771
780
|
const opts = program2.opts();
|
|
772
781
|
if (files.length > 10) {
|
|
773
782
|
error("Maximum 10 files per upload request.");
|
|
@@ -1000,7 +1009,7 @@ function registerGenerateCommands(program2) {
|
|
|
1000
1009
|
process.exit(1);
|
|
1001
1010
|
}
|
|
1002
1011
|
});
|
|
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>", "
|
|
1012
|
+
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. Use 'prompts list' to find a prompt ID, and 'content-types list' to find a content-type ID.").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 (use 'content-types list' to find)").option("--destination <dest>", "Publisher slug to publish to immediately after generation. Omit to save as draft only.").action(async (cmdOpts) => {
|
|
1004
1013
|
const opts = program2.opts();
|
|
1005
1014
|
try {
|
|
1006
1015
|
const body = {
|
|
@@ -1023,7 +1032,7 @@ function registerGenerateCommands(program2) {
|
|
|
1023
1032
|
process.exit(1);
|
|
1024
1033
|
}
|
|
1025
1034
|
});
|
|
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) => {
|
|
1035
|
+
gen.command("run").description("Trigger a content generation run. Processes all prompts (or a specific subset) through the content engine. Runs asynchronously \u2014 use 'content verification' to monitor generated drafts after the run completes.").option("--prompt-ids <ids...>", "Optional list of prompt IDs to process (omit to run all)").action(async (cmdOpts) => {
|
|
1027
1036
|
const opts = program2.opts();
|
|
1028
1037
|
try {
|
|
1029
1038
|
const body = cmdOpts.promptIds ? { prompt_ids: cmdOpts.promptIds } : void 0;
|
|
@@ -1091,7 +1100,7 @@ function registerBrandKitCommands(program2) {
|
|
|
1091
1100
|
process.exit(1);
|
|
1092
1101
|
}
|
|
1093
1102
|
});
|
|
1094
|
-
bk.command("set").description("
|
|
1103
|
+
bk.command("set").description("Replace the entire brand kit (PUT). All existing fields are overwritten \u2014 run 'brand-kit get' first to preserve fields you are not changing. For a safe partial update, use 'brand-kit patch'.").requiredOption("--data <json>", 'JSON: { "guidelines": { "brand_name": "Acme", "voice_and_tone": "...", "author_persona": "...", "global_writing_rules": [] } }').action(async (cmdOpts) => {
|
|
1095
1104
|
const opts = program2.opts();
|
|
1096
1105
|
try {
|
|
1097
1106
|
const body = JSON.parse(cmdOpts.data);
|
|
@@ -1109,6 +1118,24 @@ function registerBrandKitCommands(program2) {
|
|
|
1109
1118
|
process.exit(1);
|
|
1110
1119
|
}
|
|
1111
1120
|
});
|
|
1121
|
+
bk.command("patch").description("Partially update the brand kit (PATCH). Only the fields you provide are changed \u2014 existing fields are preserved. Preferred over 'set' for targeted updates.").requiredOption("--data <json>", 'JSON: { "guidelines": { "voice_and_tone": "Warm and approachable" } }').action(async (cmdOpts) => {
|
|
1122
|
+
const opts = program2.opts();
|
|
1123
|
+
try {
|
|
1124
|
+
const body = JSON.parse(cmdOpts.data);
|
|
1125
|
+
const data = await apiRequest({
|
|
1126
|
+
method: "PATCH",
|
|
1127
|
+
path: "/org/brand-kit",
|
|
1128
|
+
body,
|
|
1129
|
+
apiKey: opts.apiKey,
|
|
1130
|
+
baseUrl: opts.baseUrl
|
|
1131
|
+
});
|
|
1132
|
+
success("Brand kit updated.");
|
|
1133
|
+
console.log(JSON.stringify(data, null, 2));
|
|
1134
|
+
} catch (err) {
|
|
1135
|
+
error(err instanceof SyntaxError ? "Invalid JSON in --data" : formatApiError(err));
|
|
1136
|
+
process.exit(1);
|
|
1137
|
+
}
|
|
1138
|
+
});
|
|
1112
1139
|
}
|
|
1113
1140
|
|
|
1114
1141
|
// src/commands/content-types.ts
|
|
@@ -1157,7 +1184,7 @@ function registerContentTypeCommands(program2) {
|
|
|
1157
1184
|
process.exit(1);
|
|
1158
1185
|
}
|
|
1159
1186
|
});
|
|
1160
|
-
ct.command("update <id>").description("
|
|
1187
|
+
ct.command("update <id>").description("Replace a content type's name and config (PUT). Both fields are required \u2014 run 'get <id>' first to preserve existing values. For single-field updates, use 'content-types patch <id>'.").requiredOption("--data <json>", 'JSON: { "name": "Updated Name", "config": { "template": "...", "cta_text": "...", "cta_destination": "...", "writing_rules": [] } }').action(async (id, cmdOpts) => {
|
|
1161
1188
|
const opts = program2.opts();
|
|
1162
1189
|
try {
|
|
1163
1190
|
const body = JSON.parse(cmdOpts.data);
|
|
@@ -1175,6 +1202,24 @@ function registerContentTypeCommands(program2) {
|
|
|
1175
1202
|
process.exit(1);
|
|
1176
1203
|
}
|
|
1177
1204
|
});
|
|
1205
|
+
ct.command("patch <id>").description("Partially update a content type (PATCH). Only the fields you provide are changed \u2014 existing fields are preserved. Preferred over 'update' for targeted changes like updating just the template.").requiredOption("--data <json>", 'JSON: { "config": { "template": "Updated template instruction" } }').action(async (id, cmdOpts) => {
|
|
1206
|
+
const opts = program2.opts();
|
|
1207
|
+
try {
|
|
1208
|
+
const body = JSON.parse(cmdOpts.data);
|
|
1209
|
+
const data = await apiRequest({
|
|
1210
|
+
method: "PATCH",
|
|
1211
|
+
path: `/org/content-types/${id}`,
|
|
1212
|
+
body,
|
|
1213
|
+
apiKey: opts.apiKey,
|
|
1214
|
+
baseUrl: opts.baseUrl
|
|
1215
|
+
});
|
|
1216
|
+
success(`Content type ${id} updated.`);
|
|
1217
|
+
console.log(JSON.stringify(data, null, 2));
|
|
1218
|
+
} catch (err) {
|
|
1219
|
+
error(err instanceof SyntaxError ? "Invalid JSON in --data" : formatApiError(err));
|
|
1220
|
+
process.exit(1);
|
|
1221
|
+
}
|
|
1222
|
+
});
|
|
1178
1223
|
ct.command("delete <id>").description("Delete a content type. This cannot be undone.").action(async (id) => {
|
|
1179
1224
|
const opts = program2.opts();
|
|
1180
1225
|
try {
|
|
@@ -1189,7 +1234,7 @@ function registerContentTypeCommands(program2) {
|
|
|
1189
1234
|
|
|
1190
1235
|
// src/commands/prompts.ts
|
|
1191
1236
|
function registerPromptCommands(program2) {
|
|
1192
|
-
const prompts = program2.command("prompts").description("Manage prompts (
|
|
1237
|
+
const prompts = program2.command("prompts").description("Manage prompts (GEO questions). Each prompt is a question that drives both AI content generation (use with 'generate sample --prompt-id') and brand visibility monitoring \u2014 tracking how AI models mention your brand, products, and competitors.");
|
|
1193
1238
|
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) => {
|
|
1194
1239
|
const opts = program2.opts();
|
|
1195
1240
|
try {
|
|
@@ -1365,6 +1410,21 @@ function registerNotificationCommands(program2) {
|
|
|
1365
1410
|
});
|
|
1366
1411
|
}
|
|
1367
1412
|
|
|
1413
|
+
// src/commands/credits.ts
|
|
1414
|
+
function registerCreditsCommands(program2) {
|
|
1415
|
+
const credits = program2.command("credits").description("View your organisation's credit balance. Credits are consumed by AI content generation and search operations.");
|
|
1416
|
+
credits.command("balance").description("Get the current credit balance for the organisation. Returns available credits and any spend limit configured.").action(async () => {
|
|
1417
|
+
const opts = program2.opts();
|
|
1418
|
+
try {
|
|
1419
|
+
const data = await apiRequest({ path: "/org/credits/balance", apiKey: opts.apiKey, baseUrl: opts.baseUrl });
|
|
1420
|
+
console.log(JSON.stringify(data, null, 2));
|
|
1421
|
+
} catch (err) {
|
|
1422
|
+
error(formatApiError(err));
|
|
1423
|
+
process.exit(1);
|
|
1424
|
+
}
|
|
1425
|
+
});
|
|
1426
|
+
}
|
|
1427
|
+
|
|
1368
1428
|
// src/commands/update.ts
|
|
1369
1429
|
import semver2 from "semver";
|
|
1370
1430
|
import pc7 from "picocolors";
|
|
@@ -1423,6 +1483,7 @@ registerPromptCommands(program);
|
|
|
1423
1483
|
registerRunConfigCommands(program);
|
|
1424
1484
|
registerMemberCommands(program);
|
|
1425
1485
|
registerNotificationCommands(program);
|
|
1486
|
+
registerCreditsCommands(program);
|
|
1426
1487
|
registerUpdateCommand(program);
|
|
1427
1488
|
async function main() {
|
|
1428
1489
|
const quiet = process.argv.includes("--quiet") || process.argv.includes("--output") && process.argv[process.argv.indexOf("--output") + 1] === "json";
|