@senso-ai/cli 0.6.1 → 0.8.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 +201 -51
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -164,7 +164,7 @@ import pc3 from "picocolors";
|
|
|
164
164
|
// src/lib/api-client.ts
|
|
165
165
|
var ApiError = class extends Error {
|
|
166
166
|
constructor(status, statusText, body) {
|
|
167
|
-
const msg = typeof body === "object" && body
|
|
167
|
+
const msg = typeof body === "object" && body ? "error" in body ? body.error : "message" in body ? body.message : statusText : statusText;
|
|
168
168
|
super(msg);
|
|
169
169
|
this.status = status;
|
|
170
170
|
this.statusText = statusText;
|
|
@@ -230,8 +230,10 @@ function formatApiError(err) {
|
|
|
230
230
|
switch (err.status) {
|
|
231
231
|
case 401:
|
|
232
232
|
return "Authentication failed. Run `senso login` to update your API key.";
|
|
233
|
+
case 402:
|
|
234
|
+
return "Insufficient credits or spending limit reached. Check your plan at https://app.senso.ai.";
|
|
233
235
|
case 403:
|
|
234
|
-
return
|
|
236
|
+
return `Permission denied: ${err.message}`;
|
|
235
237
|
case 404:
|
|
236
238
|
return "Resource not found.";
|
|
237
239
|
case 409:
|
|
@@ -666,12 +668,17 @@ function output(format, data) {
|
|
|
666
668
|
}
|
|
667
669
|
|
|
668
670
|
// src/commands/search.ts
|
|
671
|
+
function parseMaxResults(value) {
|
|
672
|
+
const n = parseInt(value);
|
|
673
|
+
if (isNaN(n) || n < 1) return 5;
|
|
674
|
+
return Math.min(n, 20);
|
|
675
|
+
}
|
|
669
676
|
function registerSearchCommands(program2) {
|
|
670
677
|
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.");
|
|
671
|
-
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) => {
|
|
678
|
+
search.argument("<query>", "Search query").option("--max-results <n>", "Maximum number of results (max: 20)", "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) => {
|
|
672
679
|
const opts = program2.opts();
|
|
673
680
|
try {
|
|
674
|
-
const body = { query, max_results:
|
|
681
|
+
const body = { query, max_results: parseMaxResults(cmdOpts.maxResults) };
|
|
675
682
|
if (cmdOpts.contentIds) body.content_ids = cmdOpts.contentIds;
|
|
676
683
|
if (cmdOpts.requireScopedIds) body.require_scoped_ids = true;
|
|
677
684
|
const data = await apiRequest({
|
|
@@ -710,10 +717,10 @@ function registerSearchCommands(program2) {
|
|
|
710
717
|
process.exit(1);
|
|
711
718
|
}
|
|
712
719
|
});
|
|
713
|
-
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) => {
|
|
720
|
+
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 (max: 20)", "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) => {
|
|
714
721
|
const opts = program2.opts();
|
|
715
722
|
try {
|
|
716
|
-
const body = { query, max_results:
|
|
723
|
+
const body = { query, max_results: parseMaxResults(cmdOpts.maxResults) };
|
|
717
724
|
if (cmdOpts.contentIds) body.content_ids = cmdOpts.contentIds;
|
|
718
725
|
if (cmdOpts.requireScopedIds) body.require_scoped_ids = true;
|
|
719
726
|
const data = await apiRequest({
|
|
@@ -729,10 +736,10 @@ function registerSearchCommands(program2) {
|
|
|
729
736
|
process.exit(1);
|
|
730
737
|
}
|
|
731
738
|
});
|
|
732
|
-
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) => {
|
|
739
|
+
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 (max: 20)", "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) => {
|
|
733
740
|
const opts = program2.opts();
|
|
734
741
|
try {
|
|
735
|
-
const body = { query, max_results:
|
|
742
|
+
const body = { query, max_results: parseMaxResults(cmdOpts.maxResults) };
|
|
736
743
|
if (cmdOpts.contentIds) body.content_ids = cmdOpts.contentIds;
|
|
737
744
|
if (cmdOpts.requireScopedIds) body.require_scoped_ids = true;
|
|
738
745
|
const data = await apiRequest({
|
|
@@ -748,10 +755,10 @@ function registerSearchCommands(program2) {
|
|
|
748
755
|
process.exit(1);
|
|
749
756
|
}
|
|
750
757
|
});
|
|
751
|
-
search.command("full <query>").description("Alias for the default search \u2014 returns AI answer plus matching chunks. Equivalent to 'senso search <query>'.").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) => {
|
|
758
|
+
search.command("full <query>").description("Alias for the default search \u2014 returns AI answer plus matching chunks. Equivalent to 'senso search <query>'.").option("--max-results <n>", "Maximum results (max: 20)", "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) => {
|
|
752
759
|
const opts = program2.opts();
|
|
753
760
|
try {
|
|
754
|
-
const body = { query, max_results:
|
|
761
|
+
const body = { query, max_results: parseMaxResults(cmdOpts.maxResults) };
|
|
755
762
|
if (cmdOpts.contentIds) body.content_ids = cmdOpts.contentIds;
|
|
756
763
|
if (cmdOpts.requireScopedIds) body.require_scoped_ids = true;
|
|
757
764
|
const data = await apiRequest({
|
|
@@ -835,14 +842,14 @@ function registerIngestCommands(program2) {
|
|
|
835
842
|
}
|
|
836
843
|
try {
|
|
837
844
|
const fileData = await Promise.all(files.map(getFileMetadata));
|
|
838
|
-
const
|
|
845
|
+
const response = await apiRequest({
|
|
839
846
|
method: "POST",
|
|
840
847
|
path: "/org/kb/upload",
|
|
841
848
|
body: { files: fileData.map((f) => f.meta) },
|
|
842
849
|
apiKey: opts.apiKey,
|
|
843
850
|
baseUrl: opts.baseUrl
|
|
844
851
|
});
|
|
845
|
-
const items =
|
|
852
|
+
const items = response.results ?? [];
|
|
846
853
|
let uploaded = 0;
|
|
847
854
|
for (const item of items) {
|
|
848
855
|
if (item.status === "upload_pending" && item.upload_url) {
|
|
@@ -859,42 +866,39 @@ function registerIngestCommands(program2) {
|
|
|
859
866
|
error(`S3 upload failed for ${item.filename}: ${uploadErr instanceof Error ? uploadErr.message : String(uploadErr)}`);
|
|
860
867
|
}
|
|
861
868
|
} else {
|
|
862
|
-
warn(`Skipped ${item.filename}: ${item.status}${item.
|
|
869
|
+
warn(`Skipped ${item.filename}: ${item.status}${item.error ? ` \u2014 ${item.error}` : ""}`);
|
|
863
870
|
}
|
|
864
871
|
}
|
|
865
872
|
if (uploaded > 0) {
|
|
866
873
|
success(`${uploaded} file(s) uploaded. Background processing will parse, chunk, and embed them.`);
|
|
867
874
|
}
|
|
868
875
|
if (opts.output === "json") {
|
|
869
|
-
console.log(JSON.stringify(
|
|
876
|
+
console.log(JSON.stringify(response, null, 2));
|
|
870
877
|
}
|
|
871
878
|
} catch (err) {
|
|
872
879
|
error(formatApiError(err));
|
|
873
880
|
process.exit(1);
|
|
874
881
|
}
|
|
875
882
|
});
|
|
876
|
-
ingest.command("reprocess <
|
|
883
|
+
ingest.command("reprocess <nodeId> <file>").description("Re-ingest an existing document with a new file version. Provide the KB node ID (kb_node_id) and the path to the replacement file.").action(async (nodeId, file) => {
|
|
877
884
|
const opts = program2.opts();
|
|
878
885
|
try {
|
|
879
886
|
const { meta, buffer } = await getFileMetadata(file);
|
|
880
|
-
const
|
|
887
|
+
const item = await apiRequest({
|
|
881
888
|
method: "PUT",
|
|
882
|
-
path: `/org/kb/nodes/${
|
|
889
|
+
path: `/org/kb/nodes/${nodeId}/file`,
|
|
883
890
|
body: { file: meta },
|
|
884
891
|
apiKey: opts.apiKey,
|
|
885
892
|
baseUrl: opts.baseUrl
|
|
886
893
|
});
|
|
887
|
-
|
|
888
|
-
|
|
889
|
-
|
|
890
|
-
|
|
891
|
-
|
|
892
|
-
} else {
|
|
893
|
-
warn(`Skipped: ${item.status}${item.message ? ` \u2014 ${item.message}` : ""}`);
|
|
894
|
-
}
|
|
894
|
+
if (item.status === "upload_pending" && item.upload_url) {
|
|
895
|
+
await uploadToS3(item.upload_url, buffer, meta.content_type);
|
|
896
|
+
success(`Uploaded ${meta.filename} for node ${nodeId}. Background re-processing started.`);
|
|
897
|
+
} else {
|
|
898
|
+
warn(`Skipped: ${item.status}${item.error ? ` \u2014 ${item.error}` : ""}`);
|
|
895
899
|
}
|
|
896
900
|
if (opts.output === "json") {
|
|
897
|
-
console.log(JSON.stringify(
|
|
901
|
+
console.log(JSON.stringify(item, null, 2));
|
|
898
902
|
}
|
|
899
903
|
} catch (err) {
|
|
900
904
|
error(formatApiError(err));
|
|
@@ -922,7 +926,7 @@ function registerContentCommands(program2) {
|
|
|
922
926
|
json: data,
|
|
923
927
|
table: {
|
|
924
928
|
rows: rows.map((r) => ({
|
|
925
|
-
id: r.
|
|
929
|
+
id: r.kb_node_id,
|
|
926
930
|
name: r.name,
|
|
927
931
|
type: r.type,
|
|
928
932
|
status: r.processing_status
|
|
@@ -930,7 +934,7 @@ function registerContentCommands(program2) {
|
|
|
930
934
|
columns: ["id", "name", "type", "status"]
|
|
931
935
|
},
|
|
932
936
|
plain: rows.length ? rows.map(
|
|
933
|
-
(r) => ` ${pc6.bold(String(r.name || "Untitled"))} ${pc6.dim(`(${r.
|
|
937
|
+
(r) => ` ${pc6.bold(String(r.name || "Untitled"))} ${pc6.dim(`(${r.kb_node_id})`)} ${r.type ? pc6.dim(`[${r.type}]`) : ""}`
|
|
934
938
|
) : [" No content found."]
|
|
935
939
|
});
|
|
936
940
|
} catch (err) {
|
|
@@ -1055,7 +1059,7 @@ function registerGenerateCommands(program2) {
|
|
|
1055
1059
|
process.exit(1);
|
|
1056
1060
|
}
|
|
1057
1061
|
});
|
|
1058
|
-
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) => {
|
|
1062
|
+
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], "selected_content_type_id": "<uuid>" }').action(async (cmdOpts) => {
|
|
1059
1063
|
const opts = program2.opts();
|
|
1060
1064
|
try {
|
|
1061
1065
|
const body = JSON.parse(cmdOpts.data);
|
|
@@ -1090,11 +1094,14 @@ function registerGenerateCommands(program2) {
|
|
|
1090
1094
|
process.exit(1);
|
|
1091
1095
|
}
|
|
1092
1096
|
});
|
|
1093
|
-
gen.command("run").description("Trigger a content generation run. Processes all prompts (or a specific subset) through the content engine. Runs asynchronously \u2014 use 'generate runs-list' to monitor progress.").option("--prompt-ids <ids...>", "Optional list of prompt IDs to process (omit to run all)").action(async (cmdOpts) => {
|
|
1097
|
+
gen.command("run").description("Trigger a content generation run. Processes all prompts (or a specific subset) through the content engine. Runs asynchronously \u2014 use 'generate runs-list' to monitor progress.").option("--prompt-ids <ids...>", "Optional list of prompt IDs to process (omit to run all)").option("--content-type-id <id>", "Override the org's default content type for this run").option("--publisher-ids <ids...>", "Restrict publishing to specific publisher IDs").action(async (cmdOpts) => {
|
|
1094
1098
|
const opts = program2.opts();
|
|
1095
1099
|
try {
|
|
1096
|
-
const body =
|
|
1097
|
-
|
|
1100
|
+
const body = {};
|
|
1101
|
+
if (cmdOpts.promptIds) body.prompt_ids = cmdOpts.promptIds;
|
|
1102
|
+
if (cmdOpts.contentTypeId) body.content_type_id = cmdOpts.contentTypeId;
|
|
1103
|
+
if (cmdOpts.publisherIds) body.publisher_ids = cmdOpts.publisherIds;
|
|
1104
|
+
const data = await apiRequest({ method: "POST", path: "/org/content-generation/run", body: Object.keys(body).length > 0 ? body : {}, apiKey: opts.apiKey, baseUrl: opts.baseUrl });
|
|
1098
1105
|
success("Content generation run triggered.");
|
|
1099
1106
|
console.log(JSON.stringify(data, null, 2));
|
|
1100
1107
|
} catch (err) {
|
|
@@ -1481,6 +1488,151 @@ function registerRunConfigCommands(program2) {
|
|
|
1481
1488
|
});
|
|
1482
1489
|
}
|
|
1483
1490
|
|
|
1491
|
+
// src/commands/skills.ts
|
|
1492
|
+
import { execFile } from "child_process";
|
|
1493
|
+
import { promisify } from "util";
|
|
1494
|
+
var execFileAsync = promisify(execFile);
|
|
1495
|
+
var SENSO_SKILLS = [
|
|
1496
|
+
"@senso/senso-search",
|
|
1497
|
+
"@senso/senso-ingest",
|
|
1498
|
+
"@senso/senso-content-gen",
|
|
1499
|
+
"@senso/senso-brand-setup",
|
|
1500
|
+
"@senso/senso-kb-organize",
|
|
1501
|
+
"@senso/senso-review-publish"
|
|
1502
|
+
];
|
|
1503
|
+
var AGENT_FLAGS = {
|
|
1504
|
+
claude: "--claude",
|
|
1505
|
+
cursor: "--cursor",
|
|
1506
|
+
codex: "--codex",
|
|
1507
|
+
copilot: "--copilot",
|
|
1508
|
+
gemini: "--gemini",
|
|
1509
|
+
cline: "--cline"
|
|
1510
|
+
};
|
|
1511
|
+
async function resolveShipables() {
|
|
1512
|
+
try {
|
|
1513
|
+
await execFileAsync("shipables", ["--version"]);
|
|
1514
|
+
return "shipables";
|
|
1515
|
+
} catch {
|
|
1516
|
+
return "npx";
|
|
1517
|
+
}
|
|
1518
|
+
}
|
|
1519
|
+
async function runShipables(args) {
|
|
1520
|
+
const bin = await resolveShipables();
|
|
1521
|
+
if (bin === "npx") {
|
|
1522
|
+
return execFileAsync("npx", ["--yes", "@senso-ai/shipables", ...args], { timeout: 12e4 });
|
|
1523
|
+
}
|
|
1524
|
+
return execFileAsync(bin, args, { timeout: 12e4 });
|
|
1525
|
+
}
|
|
1526
|
+
function buildAgentFlags(agent) {
|
|
1527
|
+
if (!agent) return ["--all"];
|
|
1528
|
+
const flag = AGENT_FLAGS[agent.toLowerCase()];
|
|
1529
|
+
if (!flag) {
|
|
1530
|
+
const valid = Object.keys(AGENT_FLAGS).join(", ");
|
|
1531
|
+
throw new Error(`Unknown agent "${agent}". Valid agents: ${valid}`);
|
|
1532
|
+
}
|
|
1533
|
+
return [flag];
|
|
1534
|
+
}
|
|
1535
|
+
function registerSkillsCommands(program2) {
|
|
1536
|
+
const skills = program2.command("skills").description("Install and manage Senso agent skills. Skills teach AI coding agents (Claude Code, Cursor, Codex, etc.) how to use Senso automatically.");
|
|
1537
|
+
skills.command("install [names...]").description("Install Senso agent skills. Use --all for all six official skills, or pass individual short names (search, ingest, content-gen, brand-setup, kb-organize, review-publish).").option("--all", "Install all six official Senso skills").option("--agent <name>", "Target a specific agent: claude, cursor, codex, copilot, gemini, cline").option("--global", "Install globally instead of project-level").action(async (names, cmdOpts) => {
|
|
1538
|
+
const opts = program2.opts();
|
|
1539
|
+
let skillPackages;
|
|
1540
|
+
if (cmdOpts.all || names.length === 0) {
|
|
1541
|
+
skillPackages = [...SENSO_SKILLS];
|
|
1542
|
+
} else {
|
|
1543
|
+
skillPackages = names.map((n) => {
|
|
1544
|
+
if (n.startsWith("@")) return n;
|
|
1545
|
+
return `@senso/senso-${n}`;
|
|
1546
|
+
});
|
|
1547
|
+
}
|
|
1548
|
+
let agentFlags;
|
|
1549
|
+
try {
|
|
1550
|
+
agentFlags = buildAgentFlags(cmdOpts.agent);
|
|
1551
|
+
} catch (err) {
|
|
1552
|
+
error(err instanceof Error ? err.message : String(err));
|
|
1553
|
+
process.exit(1);
|
|
1554
|
+
}
|
|
1555
|
+
const envFlags = [];
|
|
1556
|
+
const apiKey = getApiKey({ apiKey: opts.apiKey });
|
|
1557
|
+
if (apiKey) {
|
|
1558
|
+
envFlags.push("--env", `SENSO_API_KEY=${apiKey}`);
|
|
1559
|
+
}
|
|
1560
|
+
const globalFlag = cmdOpts.global ? ["--global"] : [];
|
|
1561
|
+
info(`Installing ${skillPackages.length} skill(s)...`);
|
|
1562
|
+
for (const pkg of skillPackages) {
|
|
1563
|
+
try {
|
|
1564
|
+
const args = ["install", pkg, ...agentFlags, ...globalFlag, ...envFlags, "--yes"];
|
|
1565
|
+
const { stdout, stderr } = await runShipables(args);
|
|
1566
|
+
if (!opts.quiet) {
|
|
1567
|
+
if (stdout.trim()) console.log(stdout.trim());
|
|
1568
|
+
if (stderr.trim()) console.error(stderr.trim());
|
|
1569
|
+
}
|
|
1570
|
+
const shortName = pkg.replace("@senso/senso-", "");
|
|
1571
|
+
success(`Installed ${shortName}`);
|
|
1572
|
+
} catch (err) {
|
|
1573
|
+
const shortName = pkg.replace("@senso/senso-", "");
|
|
1574
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
1575
|
+
error(`Failed to install ${shortName}: ${msg}`);
|
|
1576
|
+
}
|
|
1577
|
+
}
|
|
1578
|
+
success("Done. Your agent can now use Senso \u2014 just talk to it naturally.");
|
|
1579
|
+
});
|
|
1580
|
+
skills.command("list").description("List installed Senso skills.").option("--global", "List globally installed skills").action(async (cmdOpts) => {
|
|
1581
|
+
const opts = program2.opts();
|
|
1582
|
+
try {
|
|
1583
|
+
const globalFlag = cmdOpts.global ? ["--global"] : [];
|
|
1584
|
+
const { stdout } = await runShipables(["list", ...globalFlag, "--json"]);
|
|
1585
|
+
const data = JSON.parse(stdout);
|
|
1586
|
+
if (opts.output === "json") {
|
|
1587
|
+
console.log(JSON.stringify(data, null, 2));
|
|
1588
|
+
} else {
|
|
1589
|
+
if (!data || Array.isArray(data) && data.length === 0) {
|
|
1590
|
+
info("No skills installed. Run `senso skills install --all` to get started.");
|
|
1591
|
+
} else {
|
|
1592
|
+
console.log(JSON.stringify(data, null, 2));
|
|
1593
|
+
}
|
|
1594
|
+
}
|
|
1595
|
+
} catch (err) {
|
|
1596
|
+
error(err instanceof Error ? err.message : String(err));
|
|
1597
|
+
process.exit(1);
|
|
1598
|
+
}
|
|
1599
|
+
});
|
|
1600
|
+
skills.command("list-available").description("Show all six official Senso skills available for install.").action(async () => {
|
|
1601
|
+
const opts = program2.opts();
|
|
1602
|
+
const available = SENSO_SKILLS.map((pkg) => ({
|
|
1603
|
+
package: pkg,
|
|
1604
|
+
shortName: pkg.replace("@senso/senso-", "")
|
|
1605
|
+
}));
|
|
1606
|
+
if (opts.output === "json") {
|
|
1607
|
+
console.log(JSON.stringify(available, null, 2));
|
|
1608
|
+
} else {
|
|
1609
|
+
console.log();
|
|
1610
|
+
for (const s of available) {
|
|
1611
|
+
console.log(` ${s.shortName.padEnd(18)} ${s.package}`);
|
|
1612
|
+
}
|
|
1613
|
+
console.log();
|
|
1614
|
+
info("Install all: senso skills install --all");
|
|
1615
|
+
}
|
|
1616
|
+
});
|
|
1617
|
+
skills.command("remove <name>").description("Remove an installed Senso skill. Use the short name (e.g., search, ingest, content-gen).").option("--global", "Remove from global install").action(async (name, cmdOpts) => {
|
|
1618
|
+
const opts = program2.opts();
|
|
1619
|
+
const pkg = name.startsWith("@") ? name : `@senso/senso-${name}`;
|
|
1620
|
+
const globalFlag = cmdOpts.global ? ["--global"] : [];
|
|
1621
|
+
try {
|
|
1622
|
+
const { stdout, stderr } = await runShipables(["uninstall", pkg, ...globalFlag, "--yes"]);
|
|
1623
|
+
if (!opts.quiet) {
|
|
1624
|
+
if (stdout.trim()) console.log(stdout.trim());
|
|
1625
|
+
if (stderr.trim()) console.error(stderr.trim());
|
|
1626
|
+
}
|
|
1627
|
+
const shortName = pkg.replace("@senso/senso-", "");
|
|
1628
|
+
success(`Removed ${shortName}`);
|
|
1629
|
+
} catch (err) {
|
|
1630
|
+
error(err instanceof Error ? err.message : String(err));
|
|
1631
|
+
process.exit(1);
|
|
1632
|
+
}
|
|
1633
|
+
});
|
|
1634
|
+
}
|
|
1635
|
+
|
|
1484
1636
|
// src/commands/members.ts
|
|
1485
1637
|
function registerMemberCommands(program2) {
|
|
1486
1638
|
const members = program2.command("members").description("View the organization member directory. Lists all users who belong to the organization with their names and emails.");
|
|
@@ -1738,7 +1890,7 @@ function registerKBCommands(program2) {
|
|
|
1738
1890
|
const opts = program2.opts();
|
|
1739
1891
|
try {
|
|
1740
1892
|
const body = { name: cmdOpts.name };
|
|
1741
|
-
if (cmdOpts.parentId) body.
|
|
1893
|
+
if (cmdOpts.parentId) body.parent_id = cmdOpts.parentId;
|
|
1742
1894
|
const data = await apiRequest({ method: "POST", path: "/org/kb/folders", body, apiKey: opts.apiKey, baseUrl: opts.baseUrl });
|
|
1743
1895
|
success(`Folder "${cmdOpts.name}" created.`);
|
|
1744
1896
|
console.log(JSON.stringify(data, null, 2));
|
|
@@ -1761,7 +1913,7 @@ function registerKBCommands(program2) {
|
|
|
1761
1913
|
kb.command("move <id>").description("Move a KB node to a different parent folder.").requiredOption("--parent-id <parentId>", "Target parent folder node ID").action(async (id, cmdOpts) => {
|
|
1762
1914
|
const opts = program2.opts();
|
|
1763
1915
|
try {
|
|
1764
|
-
const data = await apiRequest({ method: "PATCH", path: `/org/kb/nodes/${id}/move`, body: {
|
|
1916
|
+
const data = await apiRequest({ method: "PATCH", path: `/org/kb/nodes/${id}/move`, body: { new_parent_id: cmdOpts.parentId }, apiKey: opts.apiKey, baseUrl: opts.baseUrl });
|
|
1765
1917
|
success(`Node ${id} moved.`);
|
|
1766
1918
|
console.log(JSON.stringify(data, null, 2));
|
|
1767
1919
|
} catch (err) {
|
|
@@ -1779,7 +1931,7 @@ function registerKBCommands(program2) {
|
|
|
1779
1931
|
process.exit(1);
|
|
1780
1932
|
}
|
|
1781
1933
|
});
|
|
1782
|
-
kb.command("create-raw").description("Create a raw (text/markdown) content item in the knowledge base.").requiredOption("--data <json>", 'JSON: { "
|
|
1934
|
+
kb.command("create-raw").description("Create a raw (text/markdown) content item in the knowledge base.").requiredOption("--data <json>", 'JSON: { "title": "My doc", "text": "# Hello", "kb_folder_node_id": "<uuid>", "tag_ids": ["<uuid>"] }').action(async (cmdOpts) => {
|
|
1783
1935
|
const opts = program2.opts();
|
|
1784
1936
|
try {
|
|
1785
1937
|
const body = JSON.parse(cmdOpts.data);
|
|
@@ -1791,7 +1943,7 @@ function registerKBCommands(program2) {
|
|
|
1791
1943
|
process.exit(1);
|
|
1792
1944
|
}
|
|
1793
1945
|
});
|
|
1794
|
-
kb.command("update-raw <id>").description("Fully replace the text content of a raw KB node (creates a new version).").requiredOption("--data <json>", 'JSON: { "text": "# Updated content" }').action(async (id, cmdOpts) => {
|
|
1946
|
+
kb.command("update-raw <id>").description("Fully replace the text content of a raw KB node (creates a new version).").requiredOption("--data <json>", 'JSON: { "title": "Title", "text": "# Updated content", "tag_ids": ["<uuid>"] }').action(async (id, cmdOpts) => {
|
|
1795
1947
|
const opts = program2.opts();
|
|
1796
1948
|
try {
|
|
1797
1949
|
const body = JSON.parse(cmdOpts.data);
|
|
@@ -1803,7 +1955,7 @@ function registerKBCommands(program2) {
|
|
|
1803
1955
|
process.exit(1);
|
|
1804
1956
|
}
|
|
1805
1957
|
});
|
|
1806
|
-
kb.command("patch-raw <id>").description("Partially update the text content of a raw KB node.").requiredOption("--data <json>", 'JSON: { "text": "Updated text", "
|
|
1958
|
+
kb.command("patch-raw <id>").description("Partially update the text content of a raw KB node.").requiredOption("--data <json>", 'JSON: { "title": "New title", "text": "Updated text", "summary": "...", "tag_ids": ["<uuid>"] }').action(async (id, cmdOpts) => {
|
|
1807
1959
|
const opts = program2.opts();
|
|
1808
1960
|
try {
|
|
1809
1961
|
const body = JSON.parse(cmdOpts.data);
|
|
@@ -1825,14 +1977,14 @@ function registerKBCommands(program2) {
|
|
|
1825
1977
|
const fileData = await Promise.all(files.map(getFileMetadata2));
|
|
1826
1978
|
const body = { files: fileData.map((f) => f.meta) };
|
|
1827
1979
|
if (cmdOpts.folderId) body.kb_folder_node_id = cmdOpts.folderId;
|
|
1828
|
-
const
|
|
1980
|
+
const response = await apiRequest({
|
|
1829
1981
|
method: "POST",
|
|
1830
1982
|
path: "/org/kb/upload",
|
|
1831
1983
|
body,
|
|
1832
1984
|
apiKey: opts.apiKey,
|
|
1833
1985
|
baseUrl: opts.baseUrl
|
|
1834
1986
|
});
|
|
1835
|
-
const items =
|
|
1987
|
+
const items = response.results ?? [];
|
|
1836
1988
|
let uploaded = 0;
|
|
1837
1989
|
for (const item of items) {
|
|
1838
1990
|
if (item.status === "upload_pending" && item.upload_url) {
|
|
@@ -1849,13 +2001,13 @@ function registerKBCommands(program2) {
|
|
|
1849
2001
|
error(`S3 upload failed for ${item.filename}: ${uploadErr instanceof Error ? uploadErr.message : String(uploadErr)}`);
|
|
1850
2002
|
}
|
|
1851
2003
|
} else {
|
|
1852
|
-
warn(`Skipped ${item.filename}: ${item.status}${item.
|
|
2004
|
+
warn(`Skipped ${item.filename}: ${item.status}${item.error ? ` \u2014 ${item.error}` : ""}`);
|
|
1853
2005
|
}
|
|
1854
2006
|
}
|
|
1855
2007
|
if (uploaded > 0) {
|
|
1856
2008
|
success(`${uploaded} file(s) uploaded. Background processing will parse, chunk, and embed them.`);
|
|
1857
2009
|
}
|
|
1858
|
-
if (opts.output === "json") console.log(JSON.stringify(
|
|
2010
|
+
if (opts.output === "json") console.log(JSON.stringify(response, null, 2));
|
|
1859
2011
|
} catch (err) {
|
|
1860
2012
|
error(formatApiError(err));
|
|
1861
2013
|
process.exit(1);
|
|
@@ -1865,23 +2017,20 @@ function registerKBCommands(program2) {
|
|
|
1865
2017
|
const opts = program2.opts();
|
|
1866
2018
|
try {
|
|
1867
2019
|
const { meta, buffer } = await getFileMetadata2(file);
|
|
1868
|
-
const
|
|
2020
|
+
const item = await apiRequest({
|
|
1869
2021
|
method: "PUT",
|
|
1870
2022
|
path: `/org/kb/nodes/${id}/file`,
|
|
1871
2023
|
body: { file: meta },
|
|
1872
2024
|
apiKey: opts.apiKey,
|
|
1873
2025
|
baseUrl: opts.baseUrl
|
|
1874
2026
|
});
|
|
1875
|
-
|
|
1876
|
-
|
|
1877
|
-
|
|
1878
|
-
|
|
1879
|
-
|
|
1880
|
-
} else {
|
|
1881
|
-
warn(`Skipped: ${item.status}${item.message ? ` \u2014 ${item.message}` : ""}`);
|
|
1882
|
-
}
|
|
2027
|
+
if (item.status === "upload_pending" && item.upload_url) {
|
|
2028
|
+
await uploadToS32(item.upload_url, buffer, meta.content_type);
|
|
2029
|
+
success(`Uploaded ${meta.filename} for node ${id}. Background re-processing started.`);
|
|
2030
|
+
} else {
|
|
2031
|
+
warn(`Skipped: ${item.status}${item.error ? ` \u2014 ${item.error}` : ""}`);
|
|
1883
2032
|
}
|
|
1884
|
-
if (opts.output === "json") console.log(JSON.stringify(
|
|
2033
|
+
if (opts.output === "json") console.log(JSON.stringify(item, null, 2));
|
|
1885
2034
|
} catch (err) {
|
|
1886
2035
|
error(formatApiError(err));
|
|
1887
2036
|
process.exit(1);
|
|
@@ -1960,6 +2109,7 @@ registerBrandKitCommands(program);
|
|
|
1960
2109
|
registerContentTypeCommands(program);
|
|
1961
2110
|
registerPromptCommands(program);
|
|
1962
2111
|
registerRunConfigCommands(program);
|
|
2112
|
+
registerSkillsCommands(program);
|
|
1963
2113
|
registerMemberCommands(program);
|
|
1964
2114
|
registerCreditsCommands(program);
|
|
1965
2115
|
registerQuestionsCommands(program);
|