@thinkingai/ae-cli 6.1.18 → 6.1.20
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 +97 -38
- package/README.zh.md +101 -38
- package/dist/{auth-ROB2EDYV.js → auth-FUM37MHF.js} +241 -126
- package/dist/{capability-DKMYUTLC.js → capability-AXFQW5WM.js} +49 -35
- package/dist/{chunk-JHENBQ5B.js → chunk-4P355ZWB.js} +70 -1
- package/dist/chunk-6ZIQV5GW.js +26 -0
- package/dist/{chunk-TUKQZTMI.js → chunk-7K24F7T2.js} +2 -0
- package/dist/{chunk-OO6XR6LK.js → chunk-AMBFK2K3.js} +2 -0
- package/dist/chunk-ATSM5XAW.js +623 -0
- package/dist/{chunk-4XXOWOTA.js → chunk-BBEFP4SB.js} +31 -38
- package/dist/{chunk-BYYS3ANB.js → chunk-CZU3V3DQ.js} +5 -15
- package/dist/{chunk-VTWMIC5L.js → chunk-E2JKXMVO.js} +2 -0
- package/dist/chunk-ECBLHAZO.js +15 -0
- package/dist/{sync-HKIOZXQE.js → chunk-I4WQAEYB.js} +31 -540
- package/dist/{chunk-DWO43OIB.js → chunk-JQ3ENZZH.js} +2 -0
- package/dist/chunk-LNZBEQXW.js +25216 -0
- package/dist/{chunk-ZQ47LWTI.js → chunk-QJQZH5GQ.js} +49 -79
- package/dist/{chunk-QZ3AS4KK.js → chunk-RSG4ONOI.js} +9 -8
- package/dist/{chunk-4NN5IWVN.js → chunk-T6OSFQZD.js} +2 -0
- package/dist/{chunk-Y3LOALAV.js → chunk-TAL6CZH6.js} +8 -7
- package/dist/{chunk-3KWQYGYI.js → chunk-TKHSULJT.js} +2 -0
- package/dist/chunk-VXNMYUXU.js +202 -0
- package/dist/{chunk-3FY3RJ26.js → chunk-WZ6YVQSF.js} +15 -14
- package/dist/{chunk-6EIJSNBD.js → chunk-Y74WTIKJ.js} +2 -0
- package/dist/{community-report-client-C7WDGET3.js → community-report-client-XXWGSBSD.js} +3 -4
- package/dist/{config-BMYZX2UE.js → config-EXUGQN5T.js} +10 -10
- package/dist/{data-integration-QEKDWQDY.js → data-integration-4NZ53OMT.js} +920 -97
- package/dist/index.js +56 -81
- package/dist/{local-data-upload-client-4YYHSYD6.js → local-data-upload-client-KYOKVYZV.js} +3 -4
- package/dist/{memory-I2WXDTV2.js → memory-ATNKZNW5.js} +6 -7
- package/dist/{metadata-I4C2EWUN.js → metadata-VZVC7YMH.js} +10 -11
- package/dist/{model-HLHIEFMU.js → model-E4JMQ4V2.js} +8 -9
- package/dist/{personal-semantic-preference-LIPACBDX.js → personal-semantic-preference-7S2SQ3UV.js} +9 -10
- package/dist/{project-semantic-RT3R2VQD.js → project-semantic-2SZP2OPO.js} +13 -14
- package/dist/sync-YV3E66IF.js +520 -0
- package/dist/{te-agent-BR6VDBNX.js → te-agent-JB5T3PO7.js} +396 -92
- package/dist/{te-analysis-7VUNUYWZ.js → te-analysis-3YJAAT2D.js} +196 -43
- package/dist/{te-community-5DMNKJWY.js → te-community-UDBI672N.js} +12 -34
- package/dist/{te-dataops-6P5IKWNJ.js → te-dataops-ZLYOCXZ4.js} +480 -81
- package/dist/{te-engage-KZPR5R22.js → te-engage-4XG6OJML.js} +88 -16
- package/dist/{te-experiment-6BITX4RD.js → te-experiment-VXUWPINJ.js} +83 -12
- package/dist/te-kb-WYQWHFSC.js +1732 -0
- package/dist/{te-system-FXITO2JG.js → te-system-7G6F2LJA.js} +569 -35
- package/dist/{te-team-ADOC2ROP.js → te-team-E7FBBXMQ.js} +8 -7
- package/dist/{update-YCYCKJOO.js → update-D47BUG25.js} +8 -8
- package/package.json +22 -10
- package/skills/ae-agent/SKILL.md +30 -13
- package/skills/ae-agent/references/agent-distribution.md +94 -0
- package/skills/ae-agent/references/approval-request.md +4 -0
- package/skills/ae-agent/references/command_index.md +9 -2
- package/skills/ae-agent/references/create-automation.md +20 -2
- package/skills/ae-agent/references/get-agent-context.md +70 -0
- package/skills/ae-agent/references/list-automations.md +18 -3
- package/skills/ae-agent/references/list-mcp-credentials.md +1 -1
- package/skills/ae-agent/references/mcp-token.md +3 -3
- package/skills/ae-agent/references/set-mcp-credential.md +0 -1
- package/skills/ae-agent/references/update-automation.md +18 -0
- package/skills/ae-analysis/SKILL.md +11 -2
- package/skills/ae-analysis/references/adhoc_run.md +2 -0
- package/skills/ae-analysis/references/ai_models.md +23 -3
- package/skills/ae-analysis/references/analysis_gateway_assets.md +3 -3
- package/skills/ae-analysis/references/audience_models.md +18 -0
- package/skills/ae-analysis/references/command_index.md +9 -9
- package/skills/ae-analysis/references/cross_source_config.md +84 -0
- package/skills/ae-analysis/references/dashboard_update.md +1 -1
- package/skills/ae-analysis/references/project_timezone_update.md +13 -4
- package/skills/ae-analysis/references/property_create.md +2 -0
- package/skills/ae-analysis/references/super_metadata_batch_create.md +2 -0
- package/skills/ae-analysis/references/user_cluster_models.md +2 -0
- package/skills/ae-analysis/references/user_cluster_update.md +8 -4
- package/skills/ae-analysis/references/user_tag_create.md +30 -2
- package/skills/ae-analysis/references/user_tag_models.md +17 -3
- package/skills/ae-analysis/references/user_tag_update.md +14 -2
- package/skills/ae-data-integration/SKILL.md +3 -1
- package/skills/ae-data-integration/references/dimension-routing.md +36 -0
- package/skills/ae-data-integration/references/error-handling.md +54 -1
- package/skills/ae-data-integration/references/local-analysis.md +2 -0
- package/skills/ae-data-integration/references/source-inspect.md +18 -2
- package/skills/ae-data-integration/references/tracking-plan.md +1 -1
- package/skills/ae-data-integration/references/transform.md +4 -2
- package/skills/ae-data-integration/references/ue-mapping.md +5 -2
- package/skills/ae-data-integration/references/ue-routing.md +40 -1
- package/skills/ae-dataops/SKILL.md +11 -1
- package/skills/ae-dataops/references/dataops-backfill.md +135 -0
- package/skills/ae-engage/SKILL.md +5 -0
- package/skills/ae-engage/references/build-task-save-guide.md +5 -1
- package/skills/ae-engage/references/save-flow.md +37 -1
- package/skills/ae-engage/references/save-task.md +6 -0
- package/skills/ae-experiment/SKILL.md +6 -2
- package/skills/ae-experiment/references/save_metric.md +20 -8
- package/skills/ae-generate-tracking-plan/SKILL.md +25 -13
- package/skills/ae-generate-tracking-plan/references/business-dimension-mapping.md +1 -1
- package/skills/ae-kb/SKILL.md +218 -36
- package/skills/ae-kb/references/query-workflow.md +59 -29
- package/skills/ae-kb/references/versions.md +46 -0
- package/skills/ae-system/SKILL.md +29 -31
- package/skills/ae-system/references/channel-management.md +303 -0
- package/skills/ae-use-agent/SKILL.md +42 -0
- package/skills/ae-use-agent/references/local-agent.md +114 -0
- package/dist/auth-GBMV6TEJ.js +0 -14
- package/dist/capability-HYVVPG25.js +0 -352
- package/dist/chunk-EFH4XWYC.js +0 -556
- package/dist/chunk-J2DEBMRF.js +0 -313
- package/dist/chunk-JRJY5DMJ.js +0 -71
- package/dist/chunk-OMPRXM3V.js +0 -349
- package/dist/chunk-QNOLN2LJ.js +0 -509
- package/dist/chunk-RJDU7NYP.js +0 -1198
- package/dist/chunk-RNAALWJK.js +0 -98
- package/dist/chunk-SERWF6G5.js +0 -13
- package/dist/chunk-UW5UN47B.js +0 -70
- package/dist/chunk-ZQKDZXDO.js +0 -317
- package/dist/client-L2YDMHQ6.js +0 -203
- package/dist/memory-3ORCR7JH.js +0 -893
- package/dist/metadata-VUOQJE26.js +0 -339
- package/dist/model-UGRDX4MW.js +0 -139
- package/dist/personal-semantic-preference-OEISBRHM.js +0 -239
- package/dist/project-semantic-FFPWFPIW.js +0 -1114
- package/dist/sync-TFHU2UTG.js +0 -10261
- package/dist/te-agent-VLYOV7S4.js +0 -3894
- package/dist/te-analysis-4YGQL5RC.js +0 -9357
- package/dist/te-community-ISDQWJU7.js +0 -1859
- package/dist/te-dataops-CVULXNVB.js +0 -2209
- package/dist/te-engage-N5WI32H6.js +0 -4898
- package/dist/te-experiment-UVR4HLND.js +0 -988
- package/dist/te-kb-RCLSSH2Q.js +0 -935
- package/dist/te-system-K2GYMCTB.js +0 -2213
- package/skills/ae-agent/references/auto-provision-mcp-credentials.md +0 -57
|
@@ -0,0 +1,1732 @@
|
|
|
1
|
+
import { createRequire as __createRequire } from 'node:module'; const require = __createRequire(import.meta.url);
|
|
2
|
+
import {
|
|
3
|
+
kbApi,
|
|
4
|
+
kbUpload
|
|
5
|
+
} from "./chunk-VXNMYUXU.js";
|
|
6
|
+
import {
|
|
7
|
+
TeAgentApiError
|
|
8
|
+
} from "./chunk-BBEFP4SB.js";
|
|
9
|
+
import {
|
|
10
|
+
CapabilityGatewayError
|
|
11
|
+
} from "./chunk-QJQZH5GQ.js";
|
|
12
|
+
import {
|
|
13
|
+
printError
|
|
14
|
+
} from "./chunk-CZU3V3DQ.js";
|
|
15
|
+
import "./chunk-Y74WTIKJ.js";
|
|
16
|
+
import "./chunk-ATSM5XAW.js";
|
|
17
|
+
import "./chunk-WZ6YVQSF.js";
|
|
18
|
+
import {
|
|
19
|
+
CliValidationError,
|
|
20
|
+
PermissionError
|
|
21
|
+
} from "./chunk-4P355ZWB.js";
|
|
22
|
+
|
|
23
|
+
// src/commands/te-kb/add.ts
|
|
24
|
+
import { promises as fs } from "fs";
|
|
25
|
+
import * as path from "path";
|
|
26
|
+
import TurndownService from "turndown";
|
|
27
|
+
|
|
28
|
+
// src/commands/te-kb/target-scope.ts
|
|
29
|
+
function getExternalKnowledgeBaseTargetScope(ctx) {
|
|
30
|
+
const scope = ctx.str("scope").trim();
|
|
31
|
+
if (!scope) return void 0;
|
|
32
|
+
if (scope === "personal" || scope === "company") return scope;
|
|
33
|
+
throw new CliValidationError(
|
|
34
|
+
`Invalid --scope: ${scope}. Must be one of: personal | company`
|
|
35
|
+
);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
// src/commands/te-kb/add.ts
|
|
39
|
+
var API_PATH = "/agent/api/external/knowledge-bases/sources/upload";
|
|
40
|
+
var SUPPORTED_EXTENSIONS = /* @__PURE__ */ new Set([
|
|
41
|
+
".zip",
|
|
42
|
+
".md",
|
|
43
|
+
".markdown",
|
|
44
|
+
".txt",
|
|
45
|
+
".csv",
|
|
46
|
+
".pdf",
|
|
47
|
+
".doc",
|
|
48
|
+
".docx",
|
|
49
|
+
".xls",
|
|
50
|
+
".xlsx",
|
|
51
|
+
".ppt",
|
|
52
|
+
".pptx",
|
|
53
|
+
".jpg",
|
|
54
|
+
".jpeg",
|
|
55
|
+
".png",
|
|
56
|
+
".gif",
|
|
57
|
+
".webp",
|
|
58
|
+
".bmp",
|
|
59
|
+
".svg"
|
|
60
|
+
]);
|
|
61
|
+
function normalizeFilesInput(raw) {
|
|
62
|
+
if (!Array.isArray(raw)) {
|
|
63
|
+
throw new Error(`--files must be a JSON array of strings, e.g. '["./a.md","./docs","https://example.com/page"]'`);
|
|
64
|
+
}
|
|
65
|
+
const items = [];
|
|
66
|
+
for (const v of raw) {
|
|
67
|
+
if (typeof v !== "string") {
|
|
68
|
+
throw new Error(`--files entries must be strings (got: ${JSON.stringify(v)})`);
|
|
69
|
+
}
|
|
70
|
+
const trimmed = v.trim();
|
|
71
|
+
if (trimmed) items.push(trimmed);
|
|
72
|
+
}
|
|
73
|
+
return items;
|
|
74
|
+
}
|
|
75
|
+
function isUrl(s) {
|
|
76
|
+
return /^https?:\/\//i.test(s);
|
|
77
|
+
}
|
|
78
|
+
function classifyInput(item) {
|
|
79
|
+
return isUrl(item) ? "url" : "path";
|
|
80
|
+
}
|
|
81
|
+
function sanitizeFilename(name) {
|
|
82
|
+
const cleaned = name.replace(/[\\/:*?"<>|\s]+/g, "-").replace(/^-+|-+$/g, "");
|
|
83
|
+
return cleaned || "document";
|
|
84
|
+
}
|
|
85
|
+
function getMimeType(filename) {
|
|
86
|
+
const ext = path.extname(filename).toLowerCase();
|
|
87
|
+
switch (ext) {
|
|
88
|
+
case ".zip":
|
|
89
|
+
return "application/zip";
|
|
90
|
+
case ".md":
|
|
91
|
+
case ".markdown":
|
|
92
|
+
return "text/markdown";
|
|
93
|
+
case ".txt":
|
|
94
|
+
return "text/plain";
|
|
95
|
+
case ".csv":
|
|
96
|
+
return "text/csv";
|
|
97
|
+
case ".pdf":
|
|
98
|
+
return "application/pdf";
|
|
99
|
+
case ".doc":
|
|
100
|
+
return "application/msword";
|
|
101
|
+
case ".docx":
|
|
102
|
+
return "application/vnd.openxmlformats-officedocument.wordprocessingml.document";
|
|
103
|
+
case ".xls":
|
|
104
|
+
return "application/vnd.ms-excel";
|
|
105
|
+
case ".xlsx":
|
|
106
|
+
return "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet";
|
|
107
|
+
case ".ppt":
|
|
108
|
+
return "application/vnd.ms-powerpoint";
|
|
109
|
+
case ".pptx":
|
|
110
|
+
return "application/vnd.openxmlformats-officedocument.presentationml.presentation";
|
|
111
|
+
case ".jpg":
|
|
112
|
+
case ".jpeg":
|
|
113
|
+
return "image/jpeg";
|
|
114
|
+
case ".png":
|
|
115
|
+
return "image/png";
|
|
116
|
+
case ".gif":
|
|
117
|
+
return "image/gif";
|
|
118
|
+
case ".webp":
|
|
119
|
+
return "image/webp";
|
|
120
|
+
case ".bmp":
|
|
121
|
+
return "image/bmp";
|
|
122
|
+
case ".svg":
|
|
123
|
+
return "image/svg+xml";
|
|
124
|
+
default:
|
|
125
|
+
return "application/octet-stream";
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
async function readLocalFile(filePath) {
|
|
129
|
+
const ext = path.extname(filePath).toLowerCase();
|
|
130
|
+
if (!SUPPORTED_EXTENSIONS.has(ext)) {
|
|
131
|
+
throw new Error(`Unsupported file extension (${ext || "none"}): ${filePath}`);
|
|
132
|
+
}
|
|
133
|
+
const content = await fs.readFile(filePath);
|
|
134
|
+
const filename = path.basename(filePath);
|
|
135
|
+
return { filename, content, mimeType: getMimeType(filename), origin: "file", source: filePath };
|
|
136
|
+
}
|
|
137
|
+
async function readDirectory(dir) {
|
|
138
|
+
const entries = await fs.readdir(dir, { withFileTypes: true });
|
|
139
|
+
const files = [];
|
|
140
|
+
for (const entry of entries) {
|
|
141
|
+
if (!entry.isFile()) continue;
|
|
142
|
+
const ext = path.extname(entry.name).toLowerCase();
|
|
143
|
+
if (!SUPPORTED_EXTENSIONS.has(ext)) continue;
|
|
144
|
+
const full = path.join(dir, entry.name);
|
|
145
|
+
const content = await fs.readFile(full);
|
|
146
|
+
files.push({ filename: entry.name, content, mimeType: getMimeType(entry.name), origin: "dir", source: full });
|
|
147
|
+
}
|
|
148
|
+
if (files.length === 0) {
|
|
149
|
+
throw new Error(`No supported files found in directory: ${dir}`);
|
|
150
|
+
}
|
|
151
|
+
return files;
|
|
152
|
+
}
|
|
153
|
+
function deriveFilenameFromUrl(rawUrl) {
|
|
154
|
+
try {
|
|
155
|
+
const u = new URL(rawUrl);
|
|
156
|
+
const lastSeg = u.pathname.split("/").filter(Boolean).pop();
|
|
157
|
+
const base = lastSeg ? lastSeg.replace(/\.[^.]+$/, "") : u.hostname;
|
|
158
|
+
return `${sanitizeFilename(base)}.md`;
|
|
159
|
+
} catch {
|
|
160
|
+
return `${sanitizeFilename(rawUrl)}.md`;
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
async function fetchAsMarkdown(rawUrl) {
|
|
164
|
+
const resp = await fetch(rawUrl);
|
|
165
|
+
if (!resp.ok) {
|
|
166
|
+
throw new Error(`Failed to fetch ${rawUrl}: HTTP ${resp.status}`);
|
|
167
|
+
}
|
|
168
|
+
const html = await resp.text();
|
|
169
|
+
const turndown = new TurndownService({ headingStyle: "atx", codeBlockStyle: "fenced" });
|
|
170
|
+
const markdown = turndown.turndown(html);
|
|
171
|
+
return {
|
|
172
|
+
filename: deriveFilenameFromUrl(rawUrl),
|
|
173
|
+
content: markdown,
|
|
174
|
+
mimeType: "text/markdown",
|
|
175
|
+
origin: "url",
|
|
176
|
+
source: rawUrl
|
|
177
|
+
};
|
|
178
|
+
}
|
|
179
|
+
async function resolveLocalPath(p) {
|
|
180
|
+
const abs = path.resolve(p);
|
|
181
|
+
let stat;
|
|
182
|
+
try {
|
|
183
|
+
stat = await fs.stat(abs);
|
|
184
|
+
} catch {
|
|
185
|
+
throw new Error(`Path not found: ${p}`);
|
|
186
|
+
}
|
|
187
|
+
if (stat.isDirectory()) {
|
|
188
|
+
return readDirectory(abs);
|
|
189
|
+
}
|
|
190
|
+
if (stat.isFile()) {
|
|
191
|
+
return [await readLocalFile(abs)];
|
|
192
|
+
}
|
|
193
|
+
throw new Error(`Unsupported path (not file or directory): ${p}`);
|
|
194
|
+
}
|
|
195
|
+
async function collectFiles(ctx) {
|
|
196
|
+
const items = normalizeFilesInput(ctx.json("files"));
|
|
197
|
+
if (items.length === 0) {
|
|
198
|
+
throw new Error("--files must contain at least one entry");
|
|
199
|
+
}
|
|
200
|
+
const collected = [];
|
|
201
|
+
for (const item of items) {
|
|
202
|
+
if (classifyInput(item) === "url") {
|
|
203
|
+
collected.push(await fetchAsMarkdown(item));
|
|
204
|
+
} else {
|
|
205
|
+
collected.push(...await resolveLocalPath(item));
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
if (collected.length === 0) {
|
|
209
|
+
throw new Error("No supported files collected from the given inputs");
|
|
210
|
+
}
|
|
211
|
+
return dedupeByFilename(collected);
|
|
212
|
+
}
|
|
213
|
+
function dedupeByFilename(files) {
|
|
214
|
+
const used = /* @__PURE__ */ new Map();
|
|
215
|
+
return files.map((f) => {
|
|
216
|
+
const count = used.get(f.filename) ?? 0;
|
|
217
|
+
used.set(f.filename, count + 1);
|
|
218
|
+
if (count === 0) return f;
|
|
219
|
+
const ext = path.extname(f.filename);
|
|
220
|
+
const stem = f.filename.slice(0, f.filename.length - ext.length);
|
|
221
|
+
return { ...f, filename: `${stem}-${count}${ext}` };
|
|
222
|
+
});
|
|
223
|
+
}
|
|
224
|
+
function buildForm(name, scope, files) {
|
|
225
|
+
const form = new FormData();
|
|
226
|
+
form.append("name", name);
|
|
227
|
+
if (scope) form.append("scope", scope);
|
|
228
|
+
for (const f of files) {
|
|
229
|
+
form.append("files", new Blob([f.content], { type: f.mimeType }), f.filename);
|
|
230
|
+
}
|
|
231
|
+
return form;
|
|
232
|
+
}
|
|
233
|
+
var add = {
|
|
234
|
+
service: "kb",
|
|
235
|
+
command: "+add",
|
|
236
|
+
description: "Upload sources to a knowledge. --files accepts a JSON array; each entry can be a supported file path, a directory path (supported files inside, non-recursive), or an http(s) URL (HTML auto-converted to markdown).",
|
|
237
|
+
flags: [
|
|
238
|
+
{ name: "name", type: "string", required: true, desc: "Knowledge base name" },
|
|
239
|
+
{ name: "scope", type: "string", required: false, desc: "Exact knowledge base scope: personal | company (omit for personal \u2192 company fallback)" },
|
|
240
|
+
{
|
|
241
|
+
name: "files",
|
|
242
|
+
type: "json",
|
|
243
|
+
required: true,
|
|
244
|
+
desc: `JSON array of strings. Each entry can be a supported file path, a directory path, or an http(s) URL. Example: '["./a.md","./docs","https://example.com/page"]'`
|
|
245
|
+
}
|
|
246
|
+
],
|
|
247
|
+
risk: "write",
|
|
248
|
+
validate: (ctx) => {
|
|
249
|
+
getExternalKnowledgeBaseTargetScope(ctx);
|
|
250
|
+
normalizeFilesInput(ctx.json("files"));
|
|
251
|
+
},
|
|
252
|
+
dryRun: (ctx) => {
|
|
253
|
+
const items = normalizeFilesInput(ctx.json("files"));
|
|
254
|
+
const body = {
|
|
255
|
+
name: ctx.str("name")
|
|
256
|
+
};
|
|
257
|
+
const scope = getExternalKnowledgeBaseTargetScope(ctx);
|
|
258
|
+
if (scope) body.scope = scope;
|
|
259
|
+
body.files = items.map((item) => ({ value: item, type: classifyInput(item) }));
|
|
260
|
+
body.contentType = "multipart/form-data";
|
|
261
|
+
return {
|
|
262
|
+
method: "POST",
|
|
263
|
+
url: `${ctx.host().replace(/\/$/, "")}${API_PATH}`,
|
|
264
|
+
body
|
|
265
|
+
};
|
|
266
|
+
},
|
|
267
|
+
execute: async (ctx) => {
|
|
268
|
+
const name = ctx.str("name");
|
|
269
|
+
const scope = getExternalKnowledgeBaseTargetScope(ctx);
|
|
270
|
+
const files = await collectFiles(ctx);
|
|
271
|
+
const form = buildForm(name, scope, files);
|
|
272
|
+
const result = await kbUpload(ctx, API_PATH, form);
|
|
273
|
+
return {
|
|
274
|
+
uploaded: files.map((f) => ({ filename: f.filename, origin: f.origin, source: f.source })),
|
|
275
|
+
result
|
|
276
|
+
};
|
|
277
|
+
}
|
|
278
|
+
};
|
|
279
|
+
|
|
280
|
+
// src/commands/te-kb/compile.ts
|
|
281
|
+
var API_PATH2 = "/agent/api/external/knowledge-bases/compile";
|
|
282
|
+
var VALID_MODES = /* @__PURE__ */ new Set(["incremental", "full"]);
|
|
283
|
+
function getMode(ctx) {
|
|
284
|
+
const mode = ctx.str("mode") || "incremental";
|
|
285
|
+
if (!VALID_MODES.has(mode)) {
|
|
286
|
+
throw new Error(`Invalid --mode: ${mode}. Must be one of: incremental | full`);
|
|
287
|
+
}
|
|
288
|
+
return mode;
|
|
289
|
+
}
|
|
290
|
+
function buildBody(ctx) {
|
|
291
|
+
const body = {
|
|
292
|
+
name: ctx.str("name"),
|
|
293
|
+
mode: getMode(ctx)
|
|
294
|
+
};
|
|
295
|
+
const scope = getExternalKnowledgeBaseTargetScope(ctx);
|
|
296
|
+
if (scope) body.scope = scope;
|
|
297
|
+
const model = ctx.str("model");
|
|
298
|
+
if (model) body.model = model;
|
|
299
|
+
return body;
|
|
300
|
+
}
|
|
301
|
+
var compile = {
|
|
302
|
+
service: "kb",
|
|
303
|
+
command: "+compile",
|
|
304
|
+
description: "Compile a knowledge.",
|
|
305
|
+
flags: [
|
|
306
|
+
{ name: "name", type: "string", required: true, desc: "Knowledge base name" },
|
|
307
|
+
{ name: "mode", type: "string", required: false, default: "incremental", desc: "Compile mode: incremental | full (default: incremental)" },
|
|
308
|
+
{ name: "scope", type: "string", required: false, desc: "Exact knowledge base scope: personal | company (omit for personal \u2192 company fallback)" },
|
|
309
|
+
{ name: "model", type: "string", required: false, desc: "Optional model reference: Model.id, legacy modelId, or modelId::scope" }
|
|
310
|
+
],
|
|
311
|
+
risk: "write",
|
|
312
|
+
validate: (ctx) => {
|
|
313
|
+
getMode(ctx);
|
|
314
|
+
getExternalKnowledgeBaseTargetScope(ctx);
|
|
315
|
+
},
|
|
316
|
+
dryRun: (ctx) => ({
|
|
317
|
+
method: "POST",
|
|
318
|
+
url: `${ctx.host().replace(/\/$/, "")}${API_PATH2}`,
|
|
319
|
+
body: buildBody(ctx)
|
|
320
|
+
}),
|
|
321
|
+
execute: async (ctx) => kbApi(ctx, "POST", API_PATH2, {}, buildBody(ctx))
|
|
322
|
+
};
|
|
323
|
+
|
|
324
|
+
// src/commands/te-kb/remove.ts
|
|
325
|
+
var API_PATH3 = "/agent/api/external/knowledge-bases";
|
|
326
|
+
function buildBody2(ctx) {
|
|
327
|
+
const body = { name: ctx.str("name") };
|
|
328
|
+
const scope = getExternalKnowledgeBaseTargetScope(ctx);
|
|
329
|
+
if (scope) body.scope = scope;
|
|
330
|
+
return body;
|
|
331
|
+
}
|
|
332
|
+
var remove = {
|
|
333
|
+
service: "kb",
|
|
334
|
+
command: "+remove",
|
|
335
|
+
description: "Delete an entire knowledge.",
|
|
336
|
+
flags: [
|
|
337
|
+
{ name: "name", type: "string", required: true, desc: "Knowledge base name to delete" },
|
|
338
|
+
{ name: "scope", type: "string", required: false, desc: "Exact knowledge base scope: personal | company (omit for personal \u2192 company fallback)" }
|
|
339
|
+
],
|
|
340
|
+
risk: "high-risk-write",
|
|
341
|
+
validate: (ctx) => {
|
|
342
|
+
getExternalKnowledgeBaseTargetScope(ctx);
|
|
343
|
+
},
|
|
344
|
+
dryRun: (ctx) => ({
|
|
345
|
+
method: "DELETE",
|
|
346
|
+
url: `${ctx.host().replace(/\/$/, "")}${API_PATH3}`,
|
|
347
|
+
body: buildBody2(ctx)
|
|
348
|
+
}),
|
|
349
|
+
execute: async (ctx) => kbApi(ctx, "DELETE", API_PATH3, {}, buildBody2(ctx))
|
|
350
|
+
};
|
|
351
|
+
|
|
352
|
+
// src/commands/te-kb/create.ts
|
|
353
|
+
var API_PATH4 = "/agent/api/external/knowledge-bases/create";
|
|
354
|
+
var VALID_SCOPES = /* @__PURE__ */ new Set(["personal", "company"]);
|
|
355
|
+
function getScope(ctx) {
|
|
356
|
+
const scope = ctx.str("scope") || "company";
|
|
357
|
+
validateScope(scope);
|
|
358
|
+
return scope;
|
|
359
|
+
}
|
|
360
|
+
function validateScope(scope) {
|
|
361
|
+
if (!VALID_SCOPES.has(scope)) {
|
|
362
|
+
throw new Error(`Invalid --scope: ${scope}. Must be one of: personal | company`);
|
|
363
|
+
}
|
|
364
|
+
}
|
|
365
|
+
function normalizeTags(raw) {
|
|
366
|
+
if (raw === void 0 || raw === null) return void 0;
|
|
367
|
+
if (!Array.isArray(raw)) {
|
|
368
|
+
throw new Error(`--tags must be a JSON array of strings, e.g. '["t1","t2"]'`);
|
|
369
|
+
}
|
|
370
|
+
const tags = [];
|
|
371
|
+
for (const v of raw) {
|
|
372
|
+
if (typeof v !== "string") {
|
|
373
|
+
throw new Error(`--tags entries must be strings (got: ${JSON.stringify(v)})`);
|
|
374
|
+
}
|
|
375
|
+
const t = v.trim();
|
|
376
|
+
if (t) tags.push(t);
|
|
377
|
+
}
|
|
378
|
+
return tags;
|
|
379
|
+
}
|
|
380
|
+
function buildBody3(ctx) {
|
|
381
|
+
const body = {
|
|
382
|
+
scope: getScope(ctx),
|
|
383
|
+
name: ctx.str("name")
|
|
384
|
+
};
|
|
385
|
+
const description = ctx.str("description");
|
|
386
|
+
if (description) body.description = description;
|
|
387
|
+
const tags = normalizeTags(ctx.json("tags"));
|
|
388
|
+
if (tags && tags.length > 0) body.tags = tags;
|
|
389
|
+
const projectId = ctx.str("project-id");
|
|
390
|
+
if (projectId) body.projectId = projectId;
|
|
391
|
+
const projectName = ctx.str("project-name");
|
|
392
|
+
if (projectName) body.projectName = projectName;
|
|
393
|
+
return body;
|
|
394
|
+
}
|
|
395
|
+
var create = {
|
|
396
|
+
service: "kb",
|
|
397
|
+
command: "+new",
|
|
398
|
+
description: "Create a new knowledge.",
|
|
399
|
+
flags: [
|
|
400
|
+
{ name: "scope", type: "string", required: false, default: "company", desc: "Knowledge base scope: personal | company (default: company)" },
|
|
401
|
+
{ name: "name", type: "string", required: true, desc: "Knowledge base name (\u226430 chars, unique per scope)" },
|
|
402
|
+
{ name: "description", type: "string", required: false, desc: "Optional description (\u2264200 chars)" },
|
|
403
|
+
{ name: "tags", type: "json", required: false, desc: `Optional JSON array of tags (max 2, each \u226415 chars). Example: '["t1","t2"]'` },
|
|
404
|
+
{ name: "project-id", type: "string", required: false, desc: "Optional project ID to bind" },
|
|
405
|
+
{ name: "project-name", type: "string", required: false, desc: "Optional project display name" }
|
|
406
|
+
],
|
|
407
|
+
risk: "write",
|
|
408
|
+
validate: (ctx) => {
|
|
409
|
+
getScope(ctx);
|
|
410
|
+
normalizeTags(ctx.json("tags"));
|
|
411
|
+
},
|
|
412
|
+
dryRun: (ctx) => ({
|
|
413
|
+
method: "POST",
|
|
414
|
+
url: `${ctx.host().replace(/\/$/, "")}${API_PATH4}`,
|
|
415
|
+
body: buildBody3(ctx)
|
|
416
|
+
}),
|
|
417
|
+
execute: async (ctx) => kbApi(ctx, "POST", API_PATH4, {}, buildBody3(ctx))
|
|
418
|
+
};
|
|
419
|
+
|
|
420
|
+
// src/commands/te-kb/rm-source.ts
|
|
421
|
+
var API_PATH5 = "/agent/api/external/knowledge-bases/sources";
|
|
422
|
+
function sourceSelector(ctx) {
|
|
423
|
+
const id = ctx.str("id").trim();
|
|
424
|
+
if (id) return { id };
|
|
425
|
+
const displayName = ctx.str("display-name").trim();
|
|
426
|
+
if (displayName) return { displayName };
|
|
427
|
+
throw new CliValidationError("One of --id or --display-name is required.");
|
|
428
|
+
}
|
|
429
|
+
function buildBody4(ctx) {
|
|
430
|
+
const body = {
|
|
431
|
+
name: ctx.str("name")
|
|
432
|
+
};
|
|
433
|
+
const scope = getExternalKnowledgeBaseTargetScope(ctx);
|
|
434
|
+
if (scope) body.scope = scope;
|
|
435
|
+
return { ...body, ...sourceSelector(ctx) };
|
|
436
|
+
}
|
|
437
|
+
var rmSource = {
|
|
438
|
+
service: "kb",
|
|
439
|
+
command: "+rm-source",
|
|
440
|
+
description: "Delete one source from a knowledge base by ID or exact display name.",
|
|
441
|
+
flags: [
|
|
442
|
+
{ name: "name", type: "string", required: true, desc: "Knowledge base name (looked up personal \u2192 company)" },
|
|
443
|
+
{ name: "scope", type: "string", required: false, desc: "Exact knowledge base scope: personal | company (omit for personal \u2192 company fallback)" },
|
|
444
|
+
{ name: "id", type: "string", required: false, desc: "Source ID copied from +list-sources (preferred)" },
|
|
445
|
+
{ name: "display-name", type: "string", required: false, desc: "Exact source display name for legacy compatibility" }
|
|
446
|
+
],
|
|
447
|
+
risk: "high-risk-write",
|
|
448
|
+
validate: (ctx) => {
|
|
449
|
+
getExternalKnowledgeBaseTargetScope(ctx);
|
|
450
|
+
sourceSelector(ctx);
|
|
451
|
+
},
|
|
452
|
+
dryRun: (ctx) => ({
|
|
453
|
+
method: "DELETE",
|
|
454
|
+
url: `${ctx.host().replace(/\/$/, "")}${API_PATH5}`,
|
|
455
|
+
body: buildBody4(ctx)
|
|
456
|
+
}),
|
|
457
|
+
execute: async (ctx) => kbApi(ctx, "DELETE", API_PATH5, {}, buildBody4(ctx), {
|
|
458
|
+
preserveErrorMetadata: true
|
|
459
|
+
})
|
|
460
|
+
};
|
|
461
|
+
|
|
462
|
+
// src/commands/te-kb/schema.ts
|
|
463
|
+
var API_PATH6 = "/agent/api/external/knowledge-bases/schema";
|
|
464
|
+
function buildBody5(ctx) {
|
|
465
|
+
const body = {
|
|
466
|
+
name: ctx.str("name")
|
|
467
|
+
};
|
|
468
|
+
const scope = getExternalKnowledgeBaseTargetScope(ctx);
|
|
469
|
+
if (scope) body.scope = scope;
|
|
470
|
+
if (ctx.bool("force")) body.force = true;
|
|
471
|
+
const model = ctx.str("model");
|
|
472
|
+
if (model) body.model = model;
|
|
473
|
+
const customInstructions = ctx.str("custom-instructions");
|
|
474
|
+
if (customInstructions) body.customInstructions = customInstructions;
|
|
475
|
+
return body;
|
|
476
|
+
}
|
|
477
|
+
async function executeSchema(ctx, api = kbApi) {
|
|
478
|
+
return api(ctx, "POST", API_PATH6, {}, buildBody5(ctx), {
|
|
479
|
+
preserveBusinessErrorCode: true
|
|
480
|
+
});
|
|
481
|
+
}
|
|
482
|
+
var schema = {
|
|
483
|
+
service: "kb",
|
|
484
|
+
command: "+schema",
|
|
485
|
+
description: "Generate the compile schema for a knowledge base via POST /agent/api/external/knowledge-bases/schema.",
|
|
486
|
+
flags: [
|
|
487
|
+
{ name: "name", type: "string", required: true, desc: "Knowledge base name (looked up personal \u2192 company)" },
|
|
488
|
+
{ name: "scope", type: "string", required: false, desc: "Exact knowledge base scope: personal | company (omit for personal \u2192 company fallback)" },
|
|
489
|
+
{ name: "force", type: "boolean", required: false, desc: "Preempt generation even when status is `generating` (use only for stuck recovery)" },
|
|
490
|
+
{ name: "model", type: "string", required: false, desc: "Optional model reference: Model.id, legacy modelId, or modelId::scope" },
|
|
491
|
+
{
|
|
492
|
+
name: "custom-instructions",
|
|
493
|
+
type: "string",
|
|
494
|
+
required: false,
|
|
495
|
+
sensitive: true,
|
|
496
|
+
desc: "Optional per-run instructions for generating this knowledge base schema"
|
|
497
|
+
}
|
|
498
|
+
],
|
|
499
|
+
risk: "write",
|
|
500
|
+
validate: (ctx) => {
|
|
501
|
+
getExternalKnowledgeBaseTargetScope(ctx);
|
|
502
|
+
},
|
|
503
|
+
dryRun: (ctx) => ({
|
|
504
|
+
method: "POST",
|
|
505
|
+
url: `${ctx.host().replace(/\/$/, "")}${API_PATH6}`,
|
|
506
|
+
body: buildBody5(ctx)
|
|
507
|
+
}),
|
|
508
|
+
execute: executeSchema
|
|
509
|
+
};
|
|
510
|
+
|
|
511
|
+
// src/commands/te-kb/status.ts
|
|
512
|
+
var API_PATH7 = "/agent/api/external/knowledge-bases/status";
|
|
513
|
+
function buildBody6(ctx) {
|
|
514
|
+
const body = {
|
|
515
|
+
name: ctx.str("name")
|
|
516
|
+
};
|
|
517
|
+
const scope = getExternalKnowledgeBaseTargetScope(ctx);
|
|
518
|
+
if (scope) body.scope = scope;
|
|
519
|
+
return body;
|
|
520
|
+
}
|
|
521
|
+
var status = {
|
|
522
|
+
service: "kb",
|
|
523
|
+
command: "+status",
|
|
524
|
+
description: "Query knowledge base status.",
|
|
525
|
+
flags: [
|
|
526
|
+
{ name: "name", type: "string", required: true, desc: "Knowledge base name" },
|
|
527
|
+
{ name: "scope", type: "string", required: false, desc: "Exact knowledge base scope: personal | company (omit for personal \u2192 company fallback)" }
|
|
528
|
+
],
|
|
529
|
+
risk: "read",
|
|
530
|
+
validate: (ctx) => {
|
|
531
|
+
getExternalKnowledgeBaseTargetScope(ctx);
|
|
532
|
+
},
|
|
533
|
+
dryRun: (ctx) => ({
|
|
534
|
+
method: "POST",
|
|
535
|
+
url: `${ctx.host().replace(/\/$/, "")}${API_PATH7}`,
|
|
536
|
+
body: buildBody6(ctx)
|
|
537
|
+
}),
|
|
538
|
+
execute: async (ctx) => kbApi(ctx, "POST", API_PATH7, {}, buildBody6(ctx))
|
|
539
|
+
};
|
|
540
|
+
|
|
541
|
+
// src/commands/te-kb/kb-index.ts
|
|
542
|
+
var API_PATH8 = "/agent/api/external/knowledge-bases/index";
|
|
543
|
+
function buildBody7(ctx) {
|
|
544
|
+
const body = {};
|
|
545
|
+
const sources = ctx.json("sources");
|
|
546
|
+
if (sources) body.sources = sources;
|
|
547
|
+
const locale = ctx.str("locale");
|
|
548
|
+
if (locale) body.locale = locale;
|
|
549
|
+
return body;
|
|
550
|
+
}
|
|
551
|
+
var kbIndex = {
|
|
552
|
+
service: "kb",
|
|
553
|
+
command: "+index",
|
|
554
|
+
description: "List accessible knowledge bases and their index.md navigation maps via POST /agent/api/external/knowledge-bases/index. Call this first to see the layout, then use +grep to locate and +read to open pages.",
|
|
555
|
+
flags: [
|
|
556
|
+
{
|
|
557
|
+
name: "sources",
|
|
558
|
+
type: "json",
|
|
559
|
+
required: false,
|
|
560
|
+
desc: 'Optional JSON array of knowledge base refs to scope the index, e.g. [{"scope":"company","name":"engineering-handbook"}]. Omit to list all accessible knowledge bases.'
|
|
561
|
+
},
|
|
562
|
+
{ name: "locale", type: "string", required: false, desc: "Optional locale: zh | en | ja | ko" }
|
|
563
|
+
],
|
|
564
|
+
risk: "read",
|
|
565
|
+
dryRun: (ctx) => ({
|
|
566
|
+
method: "POST",
|
|
567
|
+
url: `${ctx.host().replace(/\/$/, "")}${API_PATH8}`,
|
|
568
|
+
body: buildBody7(ctx)
|
|
569
|
+
}),
|
|
570
|
+
execute: async (ctx) => kbApi(ctx, "POST", API_PATH8, {}, buildBody7(ctx))
|
|
571
|
+
};
|
|
572
|
+
|
|
573
|
+
// src/commands/te-kb/kb-list.ts
|
|
574
|
+
var API_PATH9 = "/agent/api/external/knowledge-bases/list";
|
|
575
|
+
var DEFAULT_BUILD_STATUS = "compiled";
|
|
576
|
+
var VALID_BUILD_STATUSES = ["idle", "pending", "compiling", "compiled", "failed"];
|
|
577
|
+
function getBuildStatus(ctx) {
|
|
578
|
+
return ctx.str("build-status") || DEFAULT_BUILD_STATUS;
|
|
579
|
+
}
|
|
580
|
+
function validateBuildStatus(ctx) {
|
|
581
|
+
const value = ctx.str("build-status");
|
|
582
|
+
if (value && !VALID_BUILD_STATUSES.includes(value)) {
|
|
583
|
+
printError(
|
|
584
|
+
"validation",
|
|
585
|
+
`Invalid --build-status: ${value}. Valid values: ${VALID_BUILD_STATUSES.join(" | ")} (omit to default to ${DEFAULT_BUILD_STATUS}).`
|
|
586
|
+
);
|
|
587
|
+
process.exit(1);
|
|
588
|
+
}
|
|
589
|
+
}
|
|
590
|
+
function buildBody8(ctx) {
|
|
591
|
+
const body = {
|
|
592
|
+
buildStatus: getBuildStatus(ctx)
|
|
593
|
+
};
|
|
594
|
+
const locale = ctx.str("locale");
|
|
595
|
+
if (locale) body.locale = locale;
|
|
596
|
+
return body;
|
|
597
|
+
}
|
|
598
|
+
var kbList = {
|
|
599
|
+
service: "kb",
|
|
600
|
+
command: "+list",
|
|
601
|
+
description: "List accessible knowledge bases via POST /agent/api/external/knowledge-bases/list. Filters by buildStatus (default: compiled). Returns KB metadata including buildStatus, without index.md navigation maps.",
|
|
602
|
+
flags: [
|
|
603
|
+
{
|
|
604
|
+
name: "build-status",
|
|
605
|
+
type: "string",
|
|
606
|
+
required: false,
|
|
607
|
+
default: DEFAULT_BUILD_STATUS,
|
|
608
|
+
desc: `Filter by knowledge base build status: ${VALID_BUILD_STATUSES.join(" | ")} (default: ${DEFAULT_BUILD_STATUS} when omitted)`
|
|
609
|
+
},
|
|
610
|
+
{ name: "locale", type: "string", required: false, desc: "Optional locale: zh | en | ja | ko" }
|
|
611
|
+
],
|
|
612
|
+
risk: "read",
|
|
613
|
+
validate: (ctx) => {
|
|
614
|
+
validateBuildStatus(ctx);
|
|
615
|
+
},
|
|
616
|
+
dryRun: (ctx) => ({
|
|
617
|
+
method: "POST",
|
|
618
|
+
url: `${ctx.host().replace(/\/$/, "")}${API_PATH9}`,
|
|
619
|
+
body: buildBody8(ctx)
|
|
620
|
+
}),
|
|
621
|
+
execute: async (ctx) => kbApi(ctx, "POST", API_PATH9, {}, buildBody8(ctx))
|
|
622
|
+
};
|
|
623
|
+
|
|
624
|
+
// src/commands/te-kb/list-sources.ts
|
|
625
|
+
var API_PATH10 = "/agent/api/external/knowledge-bases/sources";
|
|
626
|
+
var MAX_KNOWLEDGE_BASE_NAME_LENGTH = 200;
|
|
627
|
+
function knowledgeBaseName(ctx) {
|
|
628
|
+
return ctx.str("name").trim();
|
|
629
|
+
}
|
|
630
|
+
function buildUrl(ctx) {
|
|
631
|
+
const url2 = new URL(`${ctx.host().replace(/\/$/, "")}${API_PATH10}`);
|
|
632
|
+
url2.searchParams.set("name", knowledgeBaseName(ctx));
|
|
633
|
+
const scope = getExternalKnowledgeBaseTargetScope(ctx);
|
|
634
|
+
if (scope) url2.searchParams.set("scope", scope);
|
|
635
|
+
return url2.toString();
|
|
636
|
+
}
|
|
637
|
+
var listSources = {
|
|
638
|
+
service: "kb",
|
|
639
|
+
command: "+list-sources",
|
|
640
|
+
description: "List source metadata for a knowledge base via GET /agent/api/external/knowledge-bases/sources?name=<name>. Copy the stable source ID from this output before using +rm-source. Preserves deletionProjection, pendingDeletions (null means undetermined), and separate cleanup state.",
|
|
641
|
+
flags: [
|
|
642
|
+
{
|
|
643
|
+
name: "name",
|
|
644
|
+
type: "string",
|
|
645
|
+
required: true,
|
|
646
|
+
minLength: 1,
|
|
647
|
+
maxLength: MAX_KNOWLEDGE_BASE_NAME_LENGTH,
|
|
648
|
+
desc: "Knowledge base name"
|
|
649
|
+
},
|
|
650
|
+
{
|
|
651
|
+
name: "scope",
|
|
652
|
+
type: "string",
|
|
653
|
+
required: false,
|
|
654
|
+
desc: "Exact knowledge base scope: personal | company (omit for personal \u2192 company fallback)"
|
|
655
|
+
}
|
|
656
|
+
],
|
|
657
|
+
risk: "read",
|
|
658
|
+
validate: (ctx) => {
|
|
659
|
+
const name = knowledgeBaseName(ctx);
|
|
660
|
+
if (!name) {
|
|
661
|
+
throw new CliValidationError("Invalid --name: must be non-empty.");
|
|
662
|
+
}
|
|
663
|
+
if (name.length > MAX_KNOWLEDGE_BASE_NAME_LENGTH) {
|
|
664
|
+
throw new CliValidationError(
|
|
665
|
+
`Invalid --name length: ${name.length}. Must be at most ${MAX_KNOWLEDGE_BASE_NAME_LENGTH} characters.`
|
|
666
|
+
);
|
|
667
|
+
}
|
|
668
|
+
getExternalKnowledgeBaseTargetScope(ctx);
|
|
669
|
+
},
|
|
670
|
+
dryRun: (ctx) => ({
|
|
671
|
+
method: "GET",
|
|
672
|
+
url: buildUrl(ctx)
|
|
673
|
+
}),
|
|
674
|
+
execute: async (ctx) => {
|
|
675
|
+
const params = { name: knowledgeBaseName(ctx) };
|
|
676
|
+
const scope = getExternalKnowledgeBaseTargetScope(ctx);
|
|
677
|
+
if (scope) params.scope = scope;
|
|
678
|
+
return kbApi(ctx, "GET", API_PATH10, params, void 0, {
|
|
679
|
+
preserveErrorMetadata: true,
|
|
680
|
+
retryUnauthorized: true
|
|
681
|
+
});
|
|
682
|
+
}
|
|
683
|
+
};
|
|
684
|
+
|
|
685
|
+
// src/commands/te-kb/kb-grep.ts
|
|
686
|
+
var API_PATH11 = "/agent/api/external/knowledge-bases/grep";
|
|
687
|
+
function buildBody9(ctx) {
|
|
688
|
+
const body = {
|
|
689
|
+
query: ctx.str("query"),
|
|
690
|
+
sources: ctx.json("sources"),
|
|
691
|
+
paths: ctx.json("paths")
|
|
692
|
+
};
|
|
693
|
+
const topK = ctx.optionalNum("top-k");
|
|
694
|
+
if (topK !== void 0) body.topK = topK;
|
|
695
|
+
const locale = ctx.str("locale");
|
|
696
|
+
if (locale) body.locale = locale;
|
|
697
|
+
return body;
|
|
698
|
+
}
|
|
699
|
+
var kbGrep = {
|
|
700
|
+
service: "kb",
|
|
701
|
+
command: "+grep",
|
|
702
|
+
description: "Keyword-search knowledge base pages via POST /agent/api/external/knowledge-bases/grep. Returns a page-level results array: one entry per page with hitCount (full match count), pageKind (content/catalog), up to 4 sections previews (breadcrumb, sectionStartLine/sectionEndLine, text excerpt) and moreSections. Copy --paths from +index (wiki pages, module catalog pages, or subdirectories); read the matched section range with +read.",
|
|
703
|
+
flags: [
|
|
704
|
+
{
|
|
705
|
+
name: "query",
|
|
706
|
+
type: "string",
|
|
707
|
+
required: true,
|
|
708
|
+
alias: "q",
|
|
709
|
+
desc: "Keywords to search across the knowledge bases"
|
|
710
|
+
},
|
|
711
|
+
{
|
|
712
|
+
name: "sources",
|
|
713
|
+
type: "json",
|
|
714
|
+
required: true,
|
|
715
|
+
desc: 'JSON array of knowledge base refs, e.g. [{"scope":"company","name":"engineering-handbook"}]'
|
|
716
|
+
},
|
|
717
|
+
{
|
|
718
|
+
name: "paths",
|
|
719
|
+
type: "json",
|
|
720
|
+
required: true,
|
|
721
|
+
desc: 'JSON array of wiki pages or subdirectories copied from +index, e.g. ["wiki/sandbox.md"] or ["wiki/guides"]'
|
|
722
|
+
},
|
|
723
|
+
{ name: "top-k", type: "number", required: false, min: 1, max: 50, desc: "Max number of pages to return (1-50, default 10)" },
|
|
724
|
+
{ name: "locale", type: "string", required: false, desc: "Optional locale: zh | en | ja | ko" }
|
|
725
|
+
],
|
|
726
|
+
risk: "read",
|
|
727
|
+
dryRun: (ctx) => ({
|
|
728
|
+
method: "POST",
|
|
729
|
+
url: `${ctx.host().replace(/\/$/, "")}${API_PATH11}`,
|
|
730
|
+
body: buildBody9(ctx)
|
|
731
|
+
}),
|
|
732
|
+
execute: async (ctx) => kbApi(ctx, "POST", API_PATH11, {}, buildBody9(ctx))
|
|
733
|
+
};
|
|
734
|
+
|
|
735
|
+
// src/commands/te-kb/kb-read.ts
|
|
736
|
+
var API_PATH12 = "/agent/api/external/knowledge-bases/read";
|
|
737
|
+
var VALID_EXPAND_MODES = /* @__PURE__ */ new Set(["block", "none"]);
|
|
738
|
+
var MAX_READ_LIMIT = 2e3;
|
|
739
|
+
function readExpand(ctx) {
|
|
740
|
+
const expand = ctx.str("expand").trim();
|
|
741
|
+
if (!expand) return void 0;
|
|
742
|
+
if (!VALID_EXPAND_MODES.has(expand)) {
|
|
743
|
+
throw new CliValidationError(
|
|
744
|
+
`Invalid --expand: ${expand}. Must be one of: block | none`
|
|
745
|
+
);
|
|
746
|
+
}
|
|
747
|
+
return expand;
|
|
748
|
+
}
|
|
749
|
+
function readLimit(ctx) {
|
|
750
|
+
const limit = ctx.optionalNum("limit");
|
|
751
|
+
if (limit === void 0) return void 0;
|
|
752
|
+
if (!Number.isInteger(limit) || limit < 1 || limit > MAX_READ_LIMIT) {
|
|
753
|
+
throw new CliValidationError(
|
|
754
|
+
`Invalid --limit: ${limit}. Must be an integer from 1 to ${MAX_READ_LIMIT}`
|
|
755
|
+
);
|
|
756
|
+
}
|
|
757
|
+
return limit;
|
|
758
|
+
}
|
|
759
|
+
function buildBody10(ctx) {
|
|
760
|
+
const body = {
|
|
761
|
+
source: ctx.json("source"),
|
|
762
|
+
path: ctx.str("path")
|
|
763
|
+
};
|
|
764
|
+
const offset = ctx.optionalNum("offset");
|
|
765
|
+
if (offset !== void 0) body.offset = offset;
|
|
766
|
+
const limit = ctx.optionalNum("limit");
|
|
767
|
+
if (limit !== void 0) body.limit = limit;
|
|
768
|
+
const expand = readExpand(ctx);
|
|
769
|
+
if (expand) body.expand = expand;
|
|
770
|
+
if (ctx.bool("outline")) body.outline = true;
|
|
771
|
+
const locale = ctx.str("locale");
|
|
772
|
+
if (locale) body.locale = locale;
|
|
773
|
+
return body;
|
|
774
|
+
}
|
|
775
|
+
var kbRead = {
|
|
776
|
+
service: "kb",
|
|
777
|
+
command: "+read",
|
|
778
|
+
description: "Read a full knowledge base page (or a line window) via POST /agent/api/external/knowledge-bases/read. Use after +index / +grep locate a candidate page. Pass --outline to get only the page heading tree (no content) and pick a section to read.",
|
|
779
|
+
flags: [
|
|
780
|
+
{
|
|
781
|
+
name: "source",
|
|
782
|
+
type: "json",
|
|
783
|
+
required: true,
|
|
784
|
+
desc: 'Knowledge base ref pointing to exactly one base, e.g. {"scope":"company","name":"engineering-handbook"}'
|
|
785
|
+
},
|
|
786
|
+
{
|
|
787
|
+
name: "path",
|
|
788
|
+
type: "string",
|
|
789
|
+
required: true,
|
|
790
|
+
desc: "Page path relative to the knowledge base root, e.g. wiki/concepts/data-model.md or index.md"
|
|
791
|
+
},
|
|
792
|
+
{ name: "offset", type: "number", required: false, min: 1, desc: "Start line (1-based integer). Omit to read from the beginning." },
|
|
793
|
+
{ name: "limit", type: "number", required: false, min: 1, max: MAX_READ_LIMIT, desc: "Max number of lines to return (1-2000)." },
|
|
794
|
+
{ name: "expand", type: "string", required: false, desc: "Read-window expansion mode: block | none (omit for the server default: block)" },
|
|
795
|
+
{
|
|
796
|
+
name: "outline",
|
|
797
|
+
type: "boolean",
|
|
798
|
+
required: false,
|
|
799
|
+
desc: "Return only the page outline (headings with line numbers), no content. Use it to decide which section to read on long pages."
|
|
800
|
+
},
|
|
801
|
+
{ name: "locale", type: "string", required: false, desc: "Optional locale: zh | en | ja | ko" }
|
|
802
|
+
],
|
|
803
|
+
risk: "read",
|
|
804
|
+
validate: (ctx) => {
|
|
805
|
+
const offset = ctx.optionalNum("offset");
|
|
806
|
+
if (offset !== void 0 && !Number.isInteger(offset)) {
|
|
807
|
+
throw new CliValidationError("--offset must be an integer.");
|
|
808
|
+
}
|
|
809
|
+
readLimit(ctx);
|
|
810
|
+
readExpand(ctx);
|
|
811
|
+
},
|
|
812
|
+
dryRun: (ctx) => ({
|
|
813
|
+
method: "POST",
|
|
814
|
+
url: `${ctx.host().replace(/\/$/, "")}${API_PATH12}`,
|
|
815
|
+
body: buildBody10(ctx)
|
|
816
|
+
}),
|
|
817
|
+
execute: async (ctx) => kbApi(ctx, "POST", API_PATH12, {}, buildBody10(ctx))
|
|
818
|
+
};
|
|
819
|
+
|
|
820
|
+
// src/commands/te-kb/ask-shared.ts
|
|
821
|
+
var ASK_API_PATH = "/agent/api/external/knowledge-bases/ask";
|
|
822
|
+
var ASK_POLL_INTERVAL_MS = 5e3;
|
|
823
|
+
var ASK_POLL_TIMEOUT_MS = 10 * 60 * 1e3;
|
|
824
|
+
var ASK_MAX_TRANSIENT_POLL_FAILURES = 3;
|
|
825
|
+
function isTransientAskPollError(error) {
|
|
826
|
+
if (error instanceof PermissionError) return false;
|
|
827
|
+
if (error instanceof TypeError) return true;
|
|
828
|
+
if (!(error instanceof Error)) return false;
|
|
829
|
+
if (/KB MCP token auth failed|unauthorized|Invalid access token/i.test(error.message)) {
|
|
830
|
+
return false;
|
|
831
|
+
}
|
|
832
|
+
return /fetch|network|socket|ECONNRESET|ETIMEDOUT|EAI_AGAIN|ENOTFOUND|timeout/i.test(
|
|
833
|
+
error.message
|
|
834
|
+
);
|
|
835
|
+
}
|
|
836
|
+
async function pollUntilSettled(getStatus, executionId, options = {}) {
|
|
837
|
+
const intervalMs = options.intervalMs ?? ASK_POLL_INTERVAL_MS;
|
|
838
|
+
const timeoutMs = options.timeoutMs ?? ASK_POLL_TIMEOUT_MS;
|
|
839
|
+
const maxTransientFailures = options.maxTransientFailures ?? ASK_MAX_TRANSIENT_POLL_FAILURES;
|
|
840
|
+
const now = options.now ?? Date.now;
|
|
841
|
+
const sleep = options.sleep ?? ((ms) => new Promise((resolve2) => setTimeout(resolve2, ms)));
|
|
842
|
+
const startTime = now();
|
|
843
|
+
let transientFailures = 0;
|
|
844
|
+
while (true) {
|
|
845
|
+
const elapsed = now() - startTime;
|
|
846
|
+
if (elapsed > timeoutMs) {
|
|
847
|
+
throw new Error(
|
|
848
|
+
`Ask execution timeout after ${Math.floor(elapsed / 1e3)} seconds (executionId: ${executionId})`
|
|
849
|
+
);
|
|
850
|
+
}
|
|
851
|
+
let response;
|
|
852
|
+
try {
|
|
853
|
+
response = await getStatus(executionId);
|
|
854
|
+
transientFailures = 0;
|
|
855
|
+
} catch (err) {
|
|
856
|
+
if (!isTransientAskPollError(err) || ++transientFailures >= maxTransientFailures) {
|
|
857
|
+
throw err;
|
|
858
|
+
}
|
|
859
|
+
await sleep(intervalMs);
|
|
860
|
+
continue;
|
|
861
|
+
}
|
|
862
|
+
if (response.status !== "running") {
|
|
863
|
+
return response;
|
|
864
|
+
}
|
|
865
|
+
await sleep(intervalMs);
|
|
866
|
+
}
|
|
867
|
+
}
|
|
868
|
+
function transformCompletedResponse(response) {
|
|
869
|
+
const result = {
|
|
870
|
+
executionId: response.executionId,
|
|
871
|
+
answer: response.answer ?? "",
|
|
872
|
+
sources: response.sources ?? []
|
|
873
|
+
};
|
|
874
|
+
if (response.modelId !== void 0) result.modelId = response.modelId;
|
|
875
|
+
if (response.modelUsage !== void 0) result.modelUsage = response.modelUsage;
|
|
876
|
+
if (response.toolCallCount !== void 0) result.toolCallCount = response.toolCallCount;
|
|
877
|
+
return result;
|
|
878
|
+
}
|
|
879
|
+
|
|
880
|
+
// src/commands/te-kb/ask.ts
|
|
881
|
+
var VALID_LOCALES = /* @__PURE__ */ new Set(["zh", "en", "ja", "ko"]);
|
|
882
|
+
var MIN_QUESTION_LEN = 1;
|
|
883
|
+
var MAX_QUESTION_LEN = 2e3;
|
|
884
|
+
function validateQuestion(question) {
|
|
885
|
+
const len = question.length;
|
|
886
|
+
if (len < MIN_QUESTION_LEN || len > MAX_QUESTION_LEN) {
|
|
887
|
+
throw new Error(
|
|
888
|
+
`Invalid --question length: ${len}. Must be between ${MIN_QUESTION_LEN} and ${MAX_QUESTION_LEN} characters.`
|
|
889
|
+
);
|
|
890
|
+
}
|
|
891
|
+
}
|
|
892
|
+
function validateLocale(locale) {
|
|
893
|
+
if (locale && !VALID_LOCALES.has(locale)) {
|
|
894
|
+
throw new Error(`Invalid --locale: ${locale}. Must be one of: zh | en | ja | ko`);
|
|
895
|
+
}
|
|
896
|
+
}
|
|
897
|
+
function buildBody11(ctx) {
|
|
898
|
+
const body = {
|
|
899
|
+
question: ctx.str("question")
|
|
900
|
+
};
|
|
901
|
+
const sources = ctx.json("sources");
|
|
902
|
+
if (sources) body.sources = sources;
|
|
903
|
+
const modelId = ctx.str("model-id");
|
|
904
|
+
if (modelId) body.modelId = modelId;
|
|
905
|
+
const locale = ctx.str("locale");
|
|
906
|
+
if (locale) body.locale = locale;
|
|
907
|
+
return body;
|
|
908
|
+
}
|
|
909
|
+
var ask = {
|
|
910
|
+
service: "kb",
|
|
911
|
+
command: "+ask",
|
|
912
|
+
description: "Ask knowledge bases via LLM-powered retrieval (POST /agent/api/external/knowledge-bases/ask). Consumes platform tokens. Prefer +index -> +grep -> +read for deterministic, token-free lookup. By default, polls for completion; use --no-wait to return immediately with executionId.",
|
|
913
|
+
flags: [
|
|
914
|
+
{
|
|
915
|
+
name: "question",
|
|
916
|
+
type: "string",
|
|
917
|
+
required: true,
|
|
918
|
+
alias: "q",
|
|
919
|
+
desc: "Natural-language question (1-2000 characters). Required."
|
|
920
|
+
},
|
|
921
|
+
{
|
|
922
|
+
name: "sources",
|
|
923
|
+
type: "json",
|
|
924
|
+
required: false,
|
|
925
|
+
desc: 'Optional JSON array of knowledge base refs to scope the search, e.g. [{"scope":"company","name":"engineering-handbook"}]. Omit to search all accessible knowledge bases.'
|
|
926
|
+
},
|
|
927
|
+
{
|
|
928
|
+
name: "model-id",
|
|
929
|
+
type: "string",
|
|
930
|
+
required: false,
|
|
931
|
+
desc: "Optional LLM model ID (e.g. claude-sonnet-4-6). Omit to use the platform default model."
|
|
932
|
+
},
|
|
933
|
+
{ name: "locale", type: "string", required: false, desc: "Optional locale: zh | en | ja | ko" },
|
|
934
|
+
{
|
|
935
|
+
name: "no-wait",
|
|
936
|
+
type: "boolean",
|
|
937
|
+
required: false,
|
|
938
|
+
desc: "Return immediately after submission with executionId, without polling for completion."
|
|
939
|
+
}
|
|
940
|
+
],
|
|
941
|
+
risk: "read",
|
|
942
|
+
validate: (ctx) => {
|
|
943
|
+
validateQuestion(ctx.str("question"));
|
|
944
|
+
validateLocale(ctx.str("locale"));
|
|
945
|
+
},
|
|
946
|
+
dryRun: (ctx) => ({
|
|
947
|
+
method: "POST",
|
|
948
|
+
url: `${ctx.host().replace(/\/$/, "")}${ASK_API_PATH}`,
|
|
949
|
+
body: buildBody11(ctx)
|
|
950
|
+
}),
|
|
951
|
+
execute: async (ctx) => {
|
|
952
|
+
const noWait = ctx.bool("no-wait");
|
|
953
|
+
const submitResponse = await kbApi(ctx, "POST", ASK_API_PATH, {}, buildBody11(ctx));
|
|
954
|
+
if (!submitResponse.executionId) {
|
|
955
|
+
throw new Error("Invalid submit response: missing executionId");
|
|
956
|
+
}
|
|
957
|
+
const executionId = submitResponse.executionId;
|
|
958
|
+
if (noWait) {
|
|
959
|
+
return {
|
|
960
|
+
executionId,
|
|
961
|
+
status: submitResponse.status
|
|
962
|
+
};
|
|
963
|
+
}
|
|
964
|
+
const finalResponse = await pollUntilSettled(
|
|
965
|
+
(id) => kbApi(ctx, "GET", ASK_API_PATH, { executionId: id }),
|
|
966
|
+
executionId
|
|
967
|
+
);
|
|
968
|
+
if (finalResponse.status === "completed") {
|
|
969
|
+
return transformCompletedResponse(finalResponse);
|
|
970
|
+
}
|
|
971
|
+
if (finalResponse.status === "failed") {
|
|
972
|
+
const code = finalResponse.error?.code || "provider_failed";
|
|
973
|
+
const message = finalResponse.error?.message || "Ask execution failed";
|
|
974
|
+
throw new TeAgentApiError(
|
|
975
|
+
`${message} (executionId: ${finalResponse.executionId})`,
|
|
976
|
+
200,
|
|
977
|
+
code,
|
|
978
|
+
finalResponse
|
|
979
|
+
);
|
|
980
|
+
}
|
|
981
|
+
throw new Error(`Unexpected execution status: ${finalResponse.status}`);
|
|
982
|
+
}
|
|
983
|
+
};
|
|
984
|
+
|
|
985
|
+
// src/commands/te-kb/ask-status.ts
|
|
986
|
+
var MAX_EXECUTION_ID_LEN = 64;
|
|
987
|
+
function executionIdFrom(ctx) {
|
|
988
|
+
return ctx.str("execution-id").trim();
|
|
989
|
+
}
|
|
990
|
+
var askStatus = {
|
|
991
|
+
service: "kb",
|
|
992
|
+
command: "+ask-status",
|
|
993
|
+
description: "Query the status of an in-progress or completed knowledge base ask execution (GET /agent/api/external/knowledge-bases/ask?executionId=<id>). Does not poll; returns the current state.",
|
|
994
|
+
flags: [
|
|
995
|
+
{
|
|
996
|
+
name: "execution-id",
|
|
997
|
+
type: "string",
|
|
998
|
+
required: true,
|
|
999
|
+
minLength: 1,
|
|
1000
|
+
maxLength: MAX_EXECUTION_ID_LEN,
|
|
1001
|
+
desc: "The execution ID returned by kb +ask submission."
|
|
1002
|
+
}
|
|
1003
|
+
],
|
|
1004
|
+
risk: "read",
|
|
1005
|
+
validate: (ctx) => {
|
|
1006
|
+
const executionId = executionIdFrom(ctx);
|
|
1007
|
+
if (!executionId) {
|
|
1008
|
+
throw new Error("Invalid --execution-id: must be non-empty.");
|
|
1009
|
+
}
|
|
1010
|
+
if (executionId.length > MAX_EXECUTION_ID_LEN) {
|
|
1011
|
+
throw new Error(
|
|
1012
|
+
`Invalid --execution-id length: ${executionId.length}. Must be at most ${MAX_EXECUTION_ID_LEN} characters.`
|
|
1013
|
+
);
|
|
1014
|
+
}
|
|
1015
|
+
},
|
|
1016
|
+
dryRun: (ctx) => ({
|
|
1017
|
+
method: "GET",
|
|
1018
|
+
url: `${ctx.host().replace(/\/$/, "")}${ASK_API_PATH}?executionId=${encodeURIComponent(executionIdFrom(ctx))}`
|
|
1019
|
+
}),
|
|
1020
|
+
execute: async (ctx) => {
|
|
1021
|
+
const executionId = executionIdFrom(ctx);
|
|
1022
|
+
return await kbApi(ctx, "GET", ASK_API_PATH, { executionId });
|
|
1023
|
+
}
|
|
1024
|
+
};
|
|
1025
|
+
|
|
1026
|
+
// src/commands/te-kb/url.ts
|
|
1027
|
+
var API_PATH13 = "/agent/api/external/knowledge-bases/sources/url";
|
|
1028
|
+
function validateUrl(url2) {
|
|
1029
|
+
if (!/^https?:\/\//i.test(url2)) {
|
|
1030
|
+
throw new Error(`Invalid --url: ${url2}. Must be an http(s) URL`);
|
|
1031
|
+
}
|
|
1032
|
+
}
|
|
1033
|
+
function buildBody12(ctx) {
|
|
1034
|
+
const body = {
|
|
1035
|
+
name: ctx.str("name"),
|
|
1036
|
+
url: ctx.str("url")
|
|
1037
|
+
};
|
|
1038
|
+
const scope = getExternalKnowledgeBaseTargetScope(ctx);
|
|
1039
|
+
if (scope) body.scope = scope;
|
|
1040
|
+
const displayName = ctx.str("display-name");
|
|
1041
|
+
if (displayName) body.displayName = displayName;
|
|
1042
|
+
const parseInstruction = ctx.str("parse-instruction");
|
|
1043
|
+
if (parseInstruction) body.parseInstruction = parseInstruction;
|
|
1044
|
+
return body;
|
|
1045
|
+
}
|
|
1046
|
+
var url = {
|
|
1047
|
+
service: "kb",
|
|
1048
|
+
command: "+url",
|
|
1049
|
+
description: "Upload a URL source to a knowledge base.",
|
|
1050
|
+
flags: [
|
|
1051
|
+
{ name: "name", type: "string", required: true, desc: "Knowledge base name" },
|
|
1052
|
+
{ name: "scope", type: "string", required: false, desc: "Exact knowledge base scope: personal | company (omit for personal \u2192 company fallback)" },
|
|
1053
|
+
{ name: "url", type: "string", required: true, desc: "Source URL to upload" },
|
|
1054
|
+
{ name: "display-name", type: "string", required: false, desc: "Optional display name for the URL source" },
|
|
1055
|
+
{ name: "parse-instruction", type: "string", required: false, desc: "Optional parsing instruction for the URL source; ignored for Lark/Feishu doc URLs (the server applies its own Feishu parsing)" }
|
|
1056
|
+
],
|
|
1057
|
+
risk: "write",
|
|
1058
|
+
validate: (ctx) => {
|
|
1059
|
+
getExternalKnowledgeBaseTargetScope(ctx);
|
|
1060
|
+
validateUrl(ctx.str("url"));
|
|
1061
|
+
},
|
|
1062
|
+
dryRun: (ctx) => ({
|
|
1063
|
+
method: "POST",
|
|
1064
|
+
url: `${ctx.host().replace(/\/$/, "")}${API_PATH13}`,
|
|
1065
|
+
body: buildBody12(ctx)
|
|
1066
|
+
}),
|
|
1067
|
+
execute: async (ctx) => kbApi(ctx, "POST", API_PATH13, {}, buildBody12(ctx))
|
|
1068
|
+
};
|
|
1069
|
+
|
|
1070
|
+
// src/commands/te-kb/import.ts
|
|
1071
|
+
import { promises as fs2 } from "fs";
|
|
1072
|
+
import path2 from "path";
|
|
1073
|
+
var API_PATH14 = "/agent/api/external/knowledge-bases/import";
|
|
1074
|
+
var MAX_SNAPSHOT_ARCHIVE_BYTES = 50 * 1024 * 1024;
|
|
1075
|
+
var VALID_SCOPES2 = /* @__PURE__ */ new Set(["personal", "company"]);
|
|
1076
|
+
function getScope2(ctx) {
|
|
1077
|
+
const scope = ctx.str("scope") || "personal";
|
|
1078
|
+
if (!VALID_SCOPES2.has(scope)) {
|
|
1079
|
+
throw new CliValidationError("Invalid --scope. Must be one of: personal | company", {
|
|
1080
|
+
location: { field: "scope" }
|
|
1081
|
+
});
|
|
1082
|
+
}
|
|
1083
|
+
return scope;
|
|
1084
|
+
}
|
|
1085
|
+
function normalizeTags2(raw) {
|
|
1086
|
+
if (raw === void 0 || raw === null) return [];
|
|
1087
|
+
if (!Array.isArray(raw)) {
|
|
1088
|
+
throw new Error(
|
|
1089
|
+
`--tags must be a JSON array of strings, e.g. '["docs","product"]'`
|
|
1090
|
+
);
|
|
1091
|
+
}
|
|
1092
|
+
const tags = [];
|
|
1093
|
+
for (const value of raw) {
|
|
1094
|
+
if (typeof value !== "string") {
|
|
1095
|
+
throw new Error(
|
|
1096
|
+
`--tags entries must be strings (got: ${JSON.stringify(value)})`
|
|
1097
|
+
);
|
|
1098
|
+
}
|
|
1099
|
+
const tag = value.trim();
|
|
1100
|
+
if (tag && !tags.includes(tag)) tags.push(tag);
|
|
1101
|
+
}
|
|
1102
|
+
if (tags.length > 2) throw new Error("--tags accepts at most 2 unique tags");
|
|
1103
|
+
if (tags.some((tag) => tag.length > 15)) {
|
|
1104
|
+
throw new Error("--tags entries must be at most 15 characters");
|
|
1105
|
+
}
|
|
1106
|
+
return tags;
|
|
1107
|
+
}
|
|
1108
|
+
function validateArchivePath(filePath) {
|
|
1109
|
+
if (path2.extname(filePath).toLocaleLowerCase("en-US") !== ".zip") {
|
|
1110
|
+
throw new Error("--file must point to a .zip archive");
|
|
1111
|
+
}
|
|
1112
|
+
}
|
|
1113
|
+
async function readArchive(filePath) {
|
|
1114
|
+
validateArchivePath(filePath);
|
|
1115
|
+
const absolutePath = path2.resolve(filePath);
|
|
1116
|
+
let stat;
|
|
1117
|
+
try {
|
|
1118
|
+
stat = await fs2.stat(absolutePath);
|
|
1119
|
+
} catch {
|
|
1120
|
+
throw new Error(`File not found: ${filePath}`);
|
|
1121
|
+
}
|
|
1122
|
+
if (!stat.isFile())
|
|
1123
|
+
throw new Error(`--file must point to a file: ${filePath}`);
|
|
1124
|
+
if (stat.size === 0) throw new Error(`ZIP archive is empty: ${filePath}`);
|
|
1125
|
+
if (stat.size > MAX_SNAPSHOT_ARCHIVE_BYTES) {
|
|
1126
|
+
throw new Error("--file must not exceed 50 MB");
|
|
1127
|
+
}
|
|
1128
|
+
return { absolutePath, bytes: await fs2.readFile(absolutePath) };
|
|
1129
|
+
}
|
|
1130
|
+
function appendOptionalFields(ctx, form) {
|
|
1131
|
+
const description = ctx.str("description").trim();
|
|
1132
|
+
if (description) form.append("description", description);
|
|
1133
|
+
for (const tag of normalizeTags2(ctx.json("tags"))) form.append("tags", tag);
|
|
1134
|
+
const projectId = ctx.str("project-id").trim();
|
|
1135
|
+
if (projectId) form.append("projectId", projectId);
|
|
1136
|
+
}
|
|
1137
|
+
var importSnapshot = {
|
|
1138
|
+
service: "kb",
|
|
1139
|
+
command: "+import",
|
|
1140
|
+
description: "Import a compiled Markdown ZIP snapshot as a personal or company read-only knowledge base.",
|
|
1141
|
+
flags: [
|
|
1142
|
+
{
|
|
1143
|
+
name: "scope",
|
|
1144
|
+
type: "string",
|
|
1145
|
+
required: false,
|
|
1146
|
+
default: "personal",
|
|
1147
|
+
desc: "Target scope: personal | company (default: personal)"
|
|
1148
|
+
},
|
|
1149
|
+
{
|
|
1150
|
+
name: "file",
|
|
1151
|
+
type: "string",
|
|
1152
|
+
required: true,
|
|
1153
|
+
desc: "Local .zip snapshot containing root index.md and wiki/**/*.md"
|
|
1154
|
+
},
|
|
1155
|
+
{
|
|
1156
|
+
name: "name",
|
|
1157
|
+
type: "string",
|
|
1158
|
+
required: true,
|
|
1159
|
+
maxLength: 30,
|
|
1160
|
+
desc: "Knowledge base name (max 30 characters)"
|
|
1161
|
+
},
|
|
1162
|
+
{
|
|
1163
|
+
name: "description",
|
|
1164
|
+
type: "string",
|
|
1165
|
+
required: false,
|
|
1166
|
+
maxLength: 200,
|
|
1167
|
+
desc: "Optional description (max 200 characters)"
|
|
1168
|
+
},
|
|
1169
|
+
{
|
|
1170
|
+
name: "tags",
|
|
1171
|
+
type: "json",
|
|
1172
|
+
required: false,
|
|
1173
|
+
desc: "Optional JSON array of tags (max 2, each max 15 characters)"
|
|
1174
|
+
},
|
|
1175
|
+
{
|
|
1176
|
+
name: "project-id",
|
|
1177
|
+
type: "string",
|
|
1178
|
+
required: false,
|
|
1179
|
+
maxLength: 128,
|
|
1180
|
+
desc: "Optional project ID to bind"
|
|
1181
|
+
}
|
|
1182
|
+
],
|
|
1183
|
+
risk: "write",
|
|
1184
|
+
validate: (ctx) => {
|
|
1185
|
+
getScope2(ctx);
|
|
1186
|
+
validateArchivePath(ctx.str("file"));
|
|
1187
|
+
normalizeTags2(ctx.json("tags"));
|
|
1188
|
+
},
|
|
1189
|
+
execute: async (ctx) => {
|
|
1190
|
+
const archive = await readArchive(ctx.str("file"));
|
|
1191
|
+
const form = new FormData();
|
|
1192
|
+
form.append(
|
|
1193
|
+
"file",
|
|
1194
|
+
new Blob([Uint8Array.from(archive.bytes)], { type: "application/zip" }),
|
|
1195
|
+
path2.basename(archive.absolutePath)
|
|
1196
|
+
);
|
|
1197
|
+
form.append("name", ctx.str("name").trim());
|
|
1198
|
+
form.append("scope", getScope2(ctx));
|
|
1199
|
+
appendOptionalFields(ctx, form);
|
|
1200
|
+
try {
|
|
1201
|
+
return await kbUpload(
|
|
1202
|
+
ctx,
|
|
1203
|
+
API_PATH14,
|
|
1204
|
+
form,
|
|
1205
|
+
{ scope: getScope2(ctx) },
|
|
1206
|
+
{ preserveErrorMetadata: true, retryUnauthorized: true }
|
|
1207
|
+
);
|
|
1208
|
+
} catch (error) {
|
|
1209
|
+
const hint = "If a requestId was returned, run ae-cli kb +import-status --request-id <requestId>; otherwise run ae-cli kb +list before retrying the same name.";
|
|
1210
|
+
if (error instanceof CapabilityGatewayError) {
|
|
1211
|
+
throw new CapabilityGatewayError(
|
|
1212
|
+
`${error.message} ${hint}`,
|
|
1213
|
+
error.code,
|
|
1214
|
+
error.httpStatus,
|
|
1215
|
+
error.hint ?? hint,
|
|
1216
|
+
error.meta
|
|
1217
|
+
);
|
|
1218
|
+
}
|
|
1219
|
+
if (error instanceof Error) {
|
|
1220
|
+
if (error.constructor === Error) {
|
|
1221
|
+
throw new CapabilityGatewayError(`${error.message} ${hint}`, void 0, void 0, hint);
|
|
1222
|
+
}
|
|
1223
|
+
error.message = `${error.message} ${hint}`;
|
|
1224
|
+
throw error;
|
|
1225
|
+
}
|
|
1226
|
+
throw new Error(`${String(error)} ${hint}`);
|
|
1227
|
+
}
|
|
1228
|
+
}
|
|
1229
|
+
};
|
|
1230
|
+
|
|
1231
|
+
// src/commands/te-kb/import-status.ts
|
|
1232
|
+
var API_PATH15 = "/agent/api/external/knowledge-bases/import";
|
|
1233
|
+
var MAX_REQUEST_ID_LENGTH = 191;
|
|
1234
|
+
function requestIdFrom(ctx) {
|
|
1235
|
+
return ctx.str("request-id").trim();
|
|
1236
|
+
}
|
|
1237
|
+
var importStatus = {
|
|
1238
|
+
service: "kb",
|
|
1239
|
+
command: "+import-status",
|
|
1240
|
+
description: "Query one asynchronous knowledge-base snapshot import by its request ID without polling.",
|
|
1241
|
+
flags: [
|
|
1242
|
+
{
|
|
1243
|
+
name: "request-id",
|
|
1244
|
+
type: "string",
|
|
1245
|
+
required: true,
|
|
1246
|
+
minLength: 1,
|
|
1247
|
+
maxLength: MAX_REQUEST_ID_LENGTH,
|
|
1248
|
+
desc: "The requestId returned by kb +import."
|
|
1249
|
+
}
|
|
1250
|
+
],
|
|
1251
|
+
risk: "read",
|
|
1252
|
+
validate: (ctx) => {
|
|
1253
|
+
const requestId = requestIdFrom(ctx);
|
|
1254
|
+
if (!requestId) throw new Error("Invalid --request-id: must be non-empty.");
|
|
1255
|
+
if (requestId.length > MAX_REQUEST_ID_LENGTH) {
|
|
1256
|
+
throw new Error(
|
|
1257
|
+
`Invalid --request-id length: ${requestId.length}. Must be at most ${MAX_REQUEST_ID_LENGTH} characters.`
|
|
1258
|
+
);
|
|
1259
|
+
}
|
|
1260
|
+
},
|
|
1261
|
+
dryRun: (ctx) => ({
|
|
1262
|
+
method: "GET",
|
|
1263
|
+
url: `${ctx.host().replace(/\/$/, "")}${API_PATH15}?requestId=${encodeURIComponent(requestIdFrom(ctx))}`
|
|
1264
|
+
}),
|
|
1265
|
+
execute: async (ctx) => {
|
|
1266
|
+
const hint = "Verify the request ID and that the server supports kb +import-status. Do not retry the import until its status is known.";
|
|
1267
|
+
try {
|
|
1268
|
+
return await kbApi(
|
|
1269
|
+
ctx,
|
|
1270
|
+
"GET",
|
|
1271
|
+
API_PATH15,
|
|
1272
|
+
{ requestId: requestIdFrom(ctx) },
|
|
1273
|
+
void 0,
|
|
1274
|
+
{ preserveErrorMetadata: true }
|
|
1275
|
+
);
|
|
1276
|
+
} catch (error) {
|
|
1277
|
+
if (error instanceof PermissionError) {
|
|
1278
|
+
throw new PermissionError(error.message, error.code, error.hint ?? hint);
|
|
1279
|
+
}
|
|
1280
|
+
if (error instanceof CapabilityGatewayError) {
|
|
1281
|
+
if (error.hint) throw error;
|
|
1282
|
+
throw new CapabilityGatewayError(
|
|
1283
|
+
error.message,
|
|
1284
|
+
error.code,
|
|
1285
|
+
error.httpStatus,
|
|
1286
|
+
hint,
|
|
1287
|
+
error.meta
|
|
1288
|
+
);
|
|
1289
|
+
}
|
|
1290
|
+
if (error instanceof Error) {
|
|
1291
|
+
if (error.constructor === Error) {
|
|
1292
|
+
throw new CapabilityGatewayError(error.message, void 0, void 0, hint);
|
|
1293
|
+
}
|
|
1294
|
+
throw error;
|
|
1295
|
+
}
|
|
1296
|
+
throw error;
|
|
1297
|
+
}
|
|
1298
|
+
}
|
|
1299
|
+
};
|
|
1300
|
+
|
|
1301
|
+
// src/commands/te-kb/source-directory.ts
|
|
1302
|
+
import { promises as fs3 } from "fs";
|
|
1303
|
+
import path3 from "path";
|
|
1304
|
+
var sourceFlags = [
|
|
1305
|
+
{ name: "name", type: "string", required: true, desc: "Knowledge base name (personal then company)" },
|
|
1306
|
+
{ name: "id", type: "string", required: true, desc: "ZIP source ID copied from +list-sources" }
|
|
1307
|
+
];
|
|
1308
|
+
var pathFlag = { name: "path", type: "string", required: true, desc: "Exact path relative to the ZIP source root" };
|
|
1309
|
+
var revisionFlag = {
|
|
1310
|
+
name: "expected-revision",
|
|
1311
|
+
type: "number",
|
|
1312
|
+
required: true,
|
|
1313
|
+
min: 1,
|
|
1314
|
+
desc: "Revision returned by +source-ls; a conflict requires a fresh read and explicit retry"
|
|
1315
|
+
};
|
|
1316
|
+
var apiOptions = { preserveErrorMetadata: true, retryUnauthorized: true };
|
|
1317
|
+
var maxFileBytes = 50 * 1024 * 1024;
|
|
1318
|
+
function validateIdentity(ctx) {
|
|
1319
|
+
for (const [flag, max] of [["name", 200], ["id", 191]]) {
|
|
1320
|
+
const value = ctx.str(flag).trim();
|
|
1321
|
+
if (!value || value.length > max) throw new CliValidationError(`Invalid --${flag}.`);
|
|
1322
|
+
}
|
|
1323
|
+
}
|
|
1324
|
+
function relativePath(ctx, allowRoot = false) {
|
|
1325
|
+
const value = ctx.str("path");
|
|
1326
|
+
if (allowRoot && value === "") return value;
|
|
1327
|
+
if (!value || value.length > 1024 || /[\\\x00-\x1f\x7f]/.test(value) || /^[A-Za-z]:/.test(value) || value.split("/").some((part) => !part || part === "." || part === "..")) {
|
|
1328
|
+
throw new CliValidationError("--path must be a normalized relative path inside the ZIP source.");
|
|
1329
|
+
}
|
|
1330
|
+
return value;
|
|
1331
|
+
}
|
|
1332
|
+
function validateMutation(ctx) {
|
|
1333
|
+
validateIdentity(ctx);
|
|
1334
|
+
relativePath(ctx);
|
|
1335
|
+
if (!Number.isSafeInteger(ctx.num("expected-revision")) || ctx.num("expected-revision") < 1) {
|
|
1336
|
+
throw new CliValidationError("--expected-revision must be a positive safe integer.");
|
|
1337
|
+
}
|
|
1338
|
+
}
|
|
1339
|
+
function endpoint(ctx, suffix = "directory") {
|
|
1340
|
+
return `/agent/api/external/knowledge-bases/sources/${encodeURIComponent(ctx.str("id").trim())}/${suffix}`;
|
|
1341
|
+
}
|
|
1342
|
+
function query(ctx) {
|
|
1343
|
+
return { name: ctx.str("name").trim(), path: ctx.str("path") };
|
|
1344
|
+
}
|
|
1345
|
+
function preview(ctx, method, suffix = "directory", body) {
|
|
1346
|
+
return { method, url: `${ctx.host().replace(/\/$/, "")}${endpoint(ctx, suffix)}`, ...body ? { body } : { params: query(ctx) } };
|
|
1347
|
+
}
|
|
1348
|
+
function mutationBody(ctx, operation) {
|
|
1349
|
+
return { name: ctx.str("name").trim(), expectedRevision: ctx.num("expected-revision"), operations: [operation] };
|
|
1350
|
+
}
|
|
1351
|
+
var sourceLs = {
|
|
1352
|
+
service: "kb",
|
|
1353
|
+
command: "+source-ls",
|
|
1354
|
+
risk: "read",
|
|
1355
|
+
description: "List one ZIP source directory, its revision, and changes since successful compilation.",
|
|
1356
|
+
flags: [
|
|
1357
|
+
...sourceFlags,
|
|
1358
|
+
{ ...pathFlag, required: false, default: "" },
|
|
1359
|
+
{ name: "cursor", type: "string", desc: "Opaque nextCursor returned by the previous page" },
|
|
1360
|
+
{ name: "limit", type: "number", default: 100, min: 1, max: 200, desc: "Page size (1-200)" }
|
|
1361
|
+
],
|
|
1362
|
+
validate: (ctx) => {
|
|
1363
|
+
validateIdentity(ctx);
|
|
1364
|
+
relativePath(ctx, true);
|
|
1365
|
+
if (!Number.isInteger(ctx.num("limit")) || ctx.num("limit") < 1 || ctx.num("limit") > 200) {
|
|
1366
|
+
throw new CliValidationError("--limit must be an integer from 1 to 200.");
|
|
1367
|
+
}
|
|
1368
|
+
},
|
|
1369
|
+
dryRun: (ctx) => ({ ...preview(ctx, "GET"), params: { ...query(ctx), limit: ctx.num("limit"), ...ctx.str("cursor") ? { cursor: ctx.str("cursor") } : {} } }),
|
|
1370
|
+
execute: (ctx) => kbApi(ctx, "GET", endpoint(ctx), {
|
|
1371
|
+
...query(ctx),
|
|
1372
|
+
limit: ctx.num("limit"),
|
|
1373
|
+
...ctx.str("cursor") ? { cursor: ctx.str("cursor") } : {}
|
|
1374
|
+
}, void 0, apiOptions)
|
|
1375
|
+
};
|
|
1376
|
+
var sourceRead = {
|
|
1377
|
+
service: "kb",
|
|
1378
|
+
command: "+source-read",
|
|
1379
|
+
risk: "read",
|
|
1380
|
+
description: "Read a ZIP source file as UTF-8/base64 or save its original bytes to a new local file.",
|
|
1381
|
+
flags: [
|
|
1382
|
+
...sourceFlags,
|
|
1383
|
+
pathFlag,
|
|
1384
|
+
{ name: "encoding", type: "string", default: "utf8", desc: "JSON content encoding: utf8 or base64 (ignored with --output)" },
|
|
1385
|
+
{ name: "output", type: "string", desc: "Save original bytes to this local path; existing files are never overwritten" }
|
|
1386
|
+
],
|
|
1387
|
+
validate: (ctx) => {
|
|
1388
|
+
validateIdentity(ctx);
|
|
1389
|
+
relativePath(ctx);
|
|
1390
|
+
if (!["utf8", "base64"].includes(ctx.str("encoding"))) throw new CliValidationError("--encoding must be utf8 or base64.");
|
|
1391
|
+
},
|
|
1392
|
+
dryRun: (ctx) => preview(ctx, "GET", "raw"),
|
|
1393
|
+
execute: async (ctx) => {
|
|
1394
|
+
const result = await kbApi(ctx, "GET", endpoint(ctx, "raw"), query(ctx), void 0, { ...apiOptions, responseType: "bytes" });
|
|
1395
|
+
const bytes = Buffer.from(result.bytes);
|
|
1396
|
+
const metadata = { path: ctx.str("path"), sizeBytes: bytes.length, contentType: result.contentType };
|
|
1397
|
+
if (ctx.str("output")) {
|
|
1398
|
+
const output = path3.resolve(ctx.str("output"));
|
|
1399
|
+
await fs3.writeFile(output, bytes, { flag: "wx" });
|
|
1400
|
+
return { ...metadata, output };
|
|
1401
|
+
}
|
|
1402
|
+
if (ctx.str("encoding") === "base64") return { ...metadata, encoding: "base64", content: bytes.toString("base64") };
|
|
1403
|
+
try {
|
|
1404
|
+
return { ...metadata, encoding: "utf8", content: new TextDecoder("utf-8", { fatal: true }).decode(bytes) };
|
|
1405
|
+
} catch {
|
|
1406
|
+
throw new CliValidationError("This file is not UTF-8. Use --encoding base64 or --output.");
|
|
1407
|
+
}
|
|
1408
|
+
}
|
|
1409
|
+
};
|
|
1410
|
+
async function fileOperation(ctx, dryRun) {
|
|
1411
|
+
const file = path3.resolve(ctx.str("file"));
|
|
1412
|
+
const handle = await fs3.open(file, "r");
|
|
1413
|
+
try {
|
|
1414
|
+
const stat = await handle.stat();
|
|
1415
|
+
if (!stat.isFile() || stat.size > maxFileBytes) throw new CliValidationError("--file must be a regular file of at most 50 MB.");
|
|
1416
|
+
const action = ctx.str("action");
|
|
1417
|
+
const content = dryRun ? void 0 : await handle.readFile();
|
|
1418
|
+
if (content && content.length > maxFileBytes) throw new CliValidationError("--file exceeds 50 MB.");
|
|
1419
|
+
return {
|
|
1420
|
+
action,
|
|
1421
|
+
path: ctx.str("path"),
|
|
1422
|
+
...action === "replace" ? { confirmed: true } : {},
|
|
1423
|
+
...dryRun ? { file, sizeBytes: stat.size, contentBase64: "<redacted>" } : { contentBase64: content.toString("base64") }
|
|
1424
|
+
};
|
|
1425
|
+
} finally {
|
|
1426
|
+
await handle.close();
|
|
1427
|
+
}
|
|
1428
|
+
}
|
|
1429
|
+
var sourcePut = {
|
|
1430
|
+
service: "kb",
|
|
1431
|
+
command: "+source-put",
|
|
1432
|
+
risk: "write",
|
|
1433
|
+
description: "Add or explicitly replace one file at an exact ZIP-relative path using a checked revision.",
|
|
1434
|
+
flags: [
|
|
1435
|
+
...sourceFlags,
|
|
1436
|
+
pathFlag,
|
|
1437
|
+
revisionFlag,
|
|
1438
|
+
{ name: "file", type: "string", required: true, desc: "Local file to upload (at most 50 MB)" },
|
|
1439
|
+
{ name: "action", type: "string", default: "add", desc: "add rejects an existing path; replace explicitly overwrites an existing file" }
|
|
1440
|
+
],
|
|
1441
|
+
validate: (ctx) => {
|
|
1442
|
+
validateMutation(ctx);
|
|
1443
|
+
if (!ctx.str("file").trim()) throw new CliValidationError("--file must be non-empty.");
|
|
1444
|
+
if (!["add", "replace"].includes(ctx.str("action"))) throw new CliValidationError("--action must be add or replace.");
|
|
1445
|
+
},
|
|
1446
|
+
dryRun: async (ctx) => preview(ctx, "POST", "directory", mutationBody(ctx, await fileOperation(ctx, true))),
|
|
1447
|
+
execute: async (ctx) => kbApi(ctx, "POST", endpoint(ctx), {}, mutationBody(ctx, await fileOperation(ctx, false)), apiOptions)
|
|
1448
|
+
};
|
|
1449
|
+
function deleteOperation(ctx) {
|
|
1450
|
+
return { action: "delete", path: ctx.str("path"), confirmed: true, ...ctx.bool("recursive") ? { recursive: true } : {} };
|
|
1451
|
+
}
|
|
1452
|
+
var sourceRm = {
|
|
1453
|
+
service: "kb",
|
|
1454
|
+
command: "+source-rm",
|
|
1455
|
+
risk: "high-risk-write",
|
|
1456
|
+
description: "Delete one ZIP child file or recursively delete one directory; keeps the parent source.",
|
|
1457
|
+
flags: [
|
|
1458
|
+
...sourceFlags,
|
|
1459
|
+
pathFlag,
|
|
1460
|
+
revisionFlag,
|
|
1461
|
+
{ name: "recursive", type: "boolean", desc: "Explicitly allow recursive directory deletion" }
|
|
1462
|
+
],
|
|
1463
|
+
validate: validateMutation,
|
|
1464
|
+
dryRun: (ctx) => preview(ctx, "POST", "directory", mutationBody(ctx, deleteOperation(ctx))),
|
|
1465
|
+
execute: (ctx) => kbApi(ctx, "POST", endpoint(ctx), {}, mutationBody(ctx, deleteOperation(ctx)), apiOptions)
|
|
1466
|
+
};
|
|
1467
|
+
|
|
1468
|
+
// src/commands/te-kb/version-management.ts
|
|
1469
|
+
import { promises as fs4 } from "fs";
|
|
1470
|
+
import path4 from "path";
|
|
1471
|
+
var API = "/agent/api/external/knowledge-bases/versions";
|
|
1472
|
+
var apiOptions2 = { preserveErrorMetadata: true, retryUnauthorized: true };
|
|
1473
|
+
var targetFlags = [
|
|
1474
|
+
{ name: "name", type: "string", required: true, minLength: 1, maxLength: 200, desc: "Exact knowledge base name copied from +list" },
|
|
1475
|
+
{ name: "scope", type: "string", desc: "Exact personal or company scope; omit for personal then company lookup" }
|
|
1476
|
+
];
|
|
1477
|
+
var versionFlag = { name: "version", type: "number", required: true, min: 1, max: 2147483647, desc: "Historical version number from +versions, without the v prefix" };
|
|
1478
|
+
var sourceFlag = { name: "id", type: "string", required: true, minLength: 1, maxLength: 191, desc: "Stable historical source ID copied from +version-sources" };
|
|
1479
|
+
var pagingFlags = [
|
|
1480
|
+
{ name: "cursor", type: "string", desc: "nextCursor from the previous page; omit for the first page" },
|
|
1481
|
+
{ name: "limit", type: "number", default: 100, min: 1, max: 200, desc: "Page size from 1 to 200" }
|
|
1482
|
+
];
|
|
1483
|
+
var pathFlag2 = { name: "path", type: "string", desc: "Exact path relative to the historical source directory" };
|
|
1484
|
+
function positiveInteger(ctx, flag) {
|
|
1485
|
+
const value = ctx.num(flag);
|
|
1486
|
+
if (!Number.isSafeInteger(value) || value < 1 || value > 2147483647) throw new CliValidationError(`--${flag} must be a positive integer no greater than 2147483647.`);
|
|
1487
|
+
return value;
|
|
1488
|
+
}
|
|
1489
|
+
function validateTarget(ctx) {
|
|
1490
|
+
const name = ctx.str("name").trim();
|
|
1491
|
+
if (!name || name.length > 200) throw new CliValidationError("--name must contain 1 to 200 characters.");
|
|
1492
|
+
getExternalKnowledgeBaseTargetScope(ctx);
|
|
1493
|
+
}
|
|
1494
|
+
function validateVersion(ctx) {
|
|
1495
|
+
validateTarget(ctx);
|
|
1496
|
+
positiveInteger(ctx, "version");
|
|
1497
|
+
}
|
|
1498
|
+
function validateSource(ctx) {
|
|
1499
|
+
validateVersion(ctx);
|
|
1500
|
+
const id = ctx.str("id").trim();
|
|
1501
|
+
if (!id || id.length > 191) throw new CliValidationError("--id must contain 1 to 191 characters.");
|
|
1502
|
+
}
|
|
1503
|
+
function validatePage(ctx) {
|
|
1504
|
+
const limit = ctx.num("limit");
|
|
1505
|
+
if (!Number.isInteger(limit) || limit < 1 || limit > 200) throw new CliValidationError("--limit must be an integer from 1 to 200.");
|
|
1506
|
+
if (ctx.str("cursor").length > 500) throw new CliValidationError("--cursor must be at most 500 characters.");
|
|
1507
|
+
}
|
|
1508
|
+
function validatePath(ctx, required = false) {
|
|
1509
|
+
const value = ctx.str("path");
|
|
1510
|
+
if (!value && !required) return;
|
|
1511
|
+
if (!value || value.length > 500 || /[\\\x00-\x1f\x7f]/.test(value) || /^[A-Za-z]:/.test(value) || value.split("/").some((part) => !part || part === "." || part === "..")) {
|
|
1512
|
+
throw new CliValidationError("--path must be a normalized relative path inside the historical source.");
|
|
1513
|
+
}
|
|
1514
|
+
}
|
|
1515
|
+
function query2(ctx) {
|
|
1516
|
+
const scope = getExternalKnowledgeBaseTargetScope(ctx);
|
|
1517
|
+
return { name: ctx.str("name").trim(), ...scope ? { scope } : {} };
|
|
1518
|
+
}
|
|
1519
|
+
function pageQuery(ctx) {
|
|
1520
|
+
return { ...query2(ctx), limit: ctx.num("limit"), ...ctx.str("cursor") ? { cursor: ctx.str("cursor") } : {} };
|
|
1521
|
+
}
|
|
1522
|
+
function versionEndpoint(ctx, suffix = "") {
|
|
1523
|
+
return `${API}/${positiveInteger(ctx, "version")}${suffix}`;
|
|
1524
|
+
}
|
|
1525
|
+
function sourceEndpoint(ctx, action) {
|
|
1526
|
+
return versionEndpoint(ctx, `/sources/${encodeURIComponent(ctx.str("id").trim())}/${action}`);
|
|
1527
|
+
}
|
|
1528
|
+
function preview2(ctx, endpoint2, params, body) {
|
|
1529
|
+
return { method: body ? "POST" : "GET", url: `${ctx.host().replace(/\/$/, "")}${endpoint2}`, params, ...body ? { body } : {} };
|
|
1530
|
+
}
|
|
1531
|
+
var versions = {
|
|
1532
|
+
service: "kb",
|
|
1533
|
+
command: "+versions",
|
|
1534
|
+
risk: "read",
|
|
1535
|
+
description: "List immutable published versions and the latest version ID, one page at a time.",
|
|
1536
|
+
flags: [...targetFlags, ...pagingFlags],
|
|
1537
|
+
validate: (ctx) => {
|
|
1538
|
+
validateTarget(ctx);
|
|
1539
|
+
validatePage(ctx);
|
|
1540
|
+
if (ctx.str("cursor") && (!/^[1-9][0-9]*$/.test(ctx.str("cursor")) || Number(ctx.str("cursor")) > 2147483647)) throw new CliValidationError("--cursor must be a version number returned as nextCursor.");
|
|
1541
|
+
},
|
|
1542
|
+
dryRun: (ctx) => preview2(ctx, API, pageQuery(ctx)),
|
|
1543
|
+
execute: (ctx) => kbApi(ctx, "GET", API, pageQuery(ctx), void 0, apiOptions2)
|
|
1544
|
+
};
|
|
1545
|
+
var versionShow = {
|
|
1546
|
+
service: "kb",
|
|
1547
|
+
command: "+version-show",
|
|
1548
|
+
risk: "read",
|
|
1549
|
+
description: "Read one published version summary without reading current sources.",
|
|
1550
|
+
flags: [...targetFlags, versionFlag],
|
|
1551
|
+
validate: validateVersion,
|
|
1552
|
+
dryRun: (ctx) => preview2(ctx, versionEndpoint(ctx), query2(ctx)),
|
|
1553
|
+
execute: (ctx) => kbApi(ctx, "GET", versionEndpoint(ctx), query2(ctx), void 0, apiOptions2)
|
|
1554
|
+
};
|
|
1555
|
+
var versionSources = {
|
|
1556
|
+
service: "kb",
|
|
1557
|
+
command: "+version-sources",
|
|
1558
|
+
risk: "read",
|
|
1559
|
+
description: "List historical sources and stable IDs from one immutable version.",
|
|
1560
|
+
flags: [...targetFlags, versionFlag, ...pagingFlags, { name: "source-type", type: "string", desc: "Optional source type filter: file | url | zip" }],
|
|
1561
|
+
validate: (ctx) => {
|
|
1562
|
+
validateVersion(ctx);
|
|
1563
|
+
validatePage(ctx);
|
|
1564
|
+
if (ctx.str("source-type") && !["file", "url", "zip"].includes(ctx.str("source-type"))) throw new CliValidationError("--source-type must be file, url, or zip.");
|
|
1565
|
+
},
|
|
1566
|
+
dryRun: (ctx) => preview2(ctx, versionEndpoint(ctx, "/sources"), { ...pageQuery(ctx), ...ctx.str("source-type") ? { sourceType: ctx.str("source-type") } : {} }),
|
|
1567
|
+
execute: (ctx) => kbApi(ctx, "GET", versionEndpoint(ctx, "/sources"), { ...pageQuery(ctx), ...ctx.str("source-type") ? { sourceType: ctx.str("source-type") } : {} }, void 0, apiOptions2)
|
|
1568
|
+
};
|
|
1569
|
+
var versionDiff = {
|
|
1570
|
+
service: "kb",
|
|
1571
|
+
command: "+version-diff",
|
|
1572
|
+
risk: "read",
|
|
1573
|
+
description: "Compare historical source additions, deletions, content, and metadata between two versions.",
|
|
1574
|
+
flags: [...targetFlags, { ...versionFlag, name: "from", desc: "Before version number" }, { ...versionFlag, name: "to", desc: "After version number" }],
|
|
1575
|
+
validate: (ctx) => {
|
|
1576
|
+
validateTarget(ctx);
|
|
1577
|
+
positiveInteger(ctx, "from");
|
|
1578
|
+
positiveInteger(ctx, "to");
|
|
1579
|
+
},
|
|
1580
|
+
dryRun: (ctx) => preview2(ctx, `${API}/compare`, { ...query2(ctx), from: ctx.num("from"), to: ctx.num("to") }),
|
|
1581
|
+
execute: (ctx) => kbApi(ctx, "GET", `${API}/compare`, { ...query2(ctx), from: ctx.num("from"), to: ctx.num("to") }, void 0, apiOptions2)
|
|
1582
|
+
};
|
|
1583
|
+
var versionTree = {
|
|
1584
|
+
service: "kb",
|
|
1585
|
+
command: "+version-tree",
|
|
1586
|
+
risk: "read",
|
|
1587
|
+
description: "List a historical ZIP or URL source directory without changing current files.",
|
|
1588
|
+
flags: [...targetFlags, versionFlag, sourceFlag, pathFlag2, ...pagingFlags],
|
|
1589
|
+
validate: (ctx) => {
|
|
1590
|
+
validateSource(ctx);
|
|
1591
|
+
validatePath(ctx);
|
|
1592
|
+
validatePage(ctx);
|
|
1593
|
+
},
|
|
1594
|
+
dryRun: (ctx) => preview2(ctx, sourceEndpoint(ctx, "tree"), { ...pageQuery(ctx), path: ctx.str("path") }),
|
|
1595
|
+
execute: (ctx) => kbApi(ctx, "GET", sourceEndpoint(ctx, "tree"), { ...pageQuery(ctx), path: ctx.str("path") }, void 0, apiOptions2)
|
|
1596
|
+
};
|
|
1597
|
+
var versionRead = {
|
|
1598
|
+
service: "kb",
|
|
1599
|
+
command: "+version-read",
|
|
1600
|
+
risk: "read",
|
|
1601
|
+
description: "Preview one historical directory child file; unsupported or oversized files return metadata only.",
|
|
1602
|
+
flags: [...targetFlags, versionFlag, sourceFlag, { ...pathFlag2, required: true }],
|
|
1603
|
+
validate: (ctx) => {
|
|
1604
|
+
validateSource(ctx);
|
|
1605
|
+
validatePath(ctx, true);
|
|
1606
|
+
},
|
|
1607
|
+
dryRun: (ctx) => preview2(ctx, sourceEndpoint(ctx, "file"), { ...query2(ctx), path: ctx.str("path") }),
|
|
1608
|
+
execute: (ctx) => kbApi(ctx, "GET", sourceEndpoint(ctx, "file"), { ...query2(ctx), path: ctx.str("path") }, void 0, apiOptions2)
|
|
1609
|
+
};
|
|
1610
|
+
var versionDownload = {
|
|
1611
|
+
service: "kb",
|
|
1612
|
+
command: "+version-download",
|
|
1613
|
+
risk: "read",
|
|
1614
|
+
description: "Download one ordinary historical file source to a new local file. ZIP/URL directories cannot be downloaded.",
|
|
1615
|
+
flags: [...targetFlags, versionFlag, sourceFlag, { name: "output", type: "string", required: true, desc: "New local output path; existing files are never overwritten" }],
|
|
1616
|
+
validate: (ctx) => {
|
|
1617
|
+
validateSource(ctx);
|
|
1618
|
+
if (!ctx.str("output").trim()) throw new CliValidationError("--output is required.");
|
|
1619
|
+
},
|
|
1620
|
+
dryRun: (ctx) => ({ ...preview2(ctx, sourceEndpoint(ctx, "raw"), query2(ctx)), output: path4.resolve(ctx.str("output")) }),
|
|
1621
|
+
execute: async (ctx) => {
|
|
1622
|
+
const result = await kbApi(ctx, "GET", sourceEndpoint(ctx, "raw"), query2(ctx), void 0, { ...apiOptions2, responseType: "bytes" });
|
|
1623
|
+
const output = path4.resolve(ctx.str("output"));
|
|
1624
|
+
const bytes = Buffer.from(result.bytes);
|
|
1625
|
+
await fs4.writeFile(output, bytes, { flag: "wx" });
|
|
1626
|
+
return { versionNumber: ctx.num("version"), sourceId: ctx.str("id").trim(), sizeBytes: String(bytes.length), output };
|
|
1627
|
+
}
|
|
1628
|
+
};
|
|
1629
|
+
function rollbackBody(ctx) {
|
|
1630
|
+
return { requestId: ctx.str("request-id"), expectedLatestVersionId: ctx.str("expected-latest-version-id") };
|
|
1631
|
+
}
|
|
1632
|
+
var rollback = {
|
|
1633
|
+
service: "kb",
|
|
1634
|
+
command: "+rollback",
|
|
1635
|
+
risk: "high-risk-write",
|
|
1636
|
+
description: "Restore an earlier version as a new publication. Replace target-source drafts, delete other compiled sources, and preserve unpublished additions. Reuse request-id on network retries.",
|
|
1637
|
+
flags: [
|
|
1638
|
+
...targetFlags,
|
|
1639
|
+
versionFlag,
|
|
1640
|
+
{ name: "request-id", type: "string", required: true, maxLength: 191, desc: "Caller-generated stable ASCII request ID; reuse exactly for retries of this logical operation" },
|
|
1641
|
+
{ name: "expected-latest-version-id", type: "string", required: true, maxLength: 191, desc: "latestVersionId returned by +versions; conflicts require fresh discovery and a new intentional request" }
|
|
1642
|
+
],
|
|
1643
|
+
validate: (ctx) => {
|
|
1644
|
+
validateVersion(ctx);
|
|
1645
|
+
if (!/^[A-Za-z0-9_-]{1,191}$/.test(ctx.str("request-id"))) throw new CliValidationError("--request-id must contain 1-191 ASCII letters, digits, underscores, or hyphens.");
|
|
1646
|
+
const latest = ctx.str("expected-latest-version-id");
|
|
1647
|
+
if (!latest || latest.length > 191) throw new CliValidationError("--expected-latest-version-id must contain 1 to 191 characters.");
|
|
1648
|
+
},
|
|
1649
|
+
dryRun: (ctx) => preview2(ctx, versionEndpoint(ctx, "/rollback"), query2(ctx), rollbackBody(ctx)),
|
|
1650
|
+
execute: (ctx) => kbApi(ctx, "POST", versionEndpoint(ctx, "/rollback"), query2(ctx), rollbackBody(ctx), apiOptions2)
|
|
1651
|
+
};
|
|
1652
|
+
var rollbackStatus = {
|
|
1653
|
+
service: "kb",
|
|
1654
|
+
command: "+rollback-status",
|
|
1655
|
+
risk: "read",
|
|
1656
|
+
description: "Read one persisted rollback operation without polling. A running operation with errorCode requires administrator review; never retry it as a new write.",
|
|
1657
|
+
flags: [...targetFlags, { name: "operation-id", type: "string", required: true, minLength: 1, maxLength: 191, desc: "operationId returned by +rollback" }],
|
|
1658
|
+
validate: (ctx) => {
|
|
1659
|
+
validateTarget(ctx);
|
|
1660
|
+
if (!ctx.str("operation-id").trim() || ctx.str("operation-id").length > 191) throw new CliValidationError("--operation-id must contain 1 to 191 characters.");
|
|
1661
|
+
},
|
|
1662
|
+
dryRun: (ctx) => preview2(ctx, `${API}/operations/${encodeURIComponent(ctx.str("operation-id").trim())}`, query2(ctx)),
|
|
1663
|
+
execute: (ctx) => kbApi(ctx, "GET", `${API}/operations/${encodeURIComponent(ctx.str("operation-id").trim())}`, query2(ctx), void 0, apiOptions2)
|
|
1664
|
+
};
|
|
1665
|
+
|
|
1666
|
+
// src/commands/te-kb/index.ts
|
|
1667
|
+
var commands = [
|
|
1668
|
+
versions,
|
|
1669
|
+
versionShow,
|
|
1670
|
+
versionSources,
|
|
1671
|
+
versionDiff,
|
|
1672
|
+
versionTree,
|
|
1673
|
+
versionRead,
|
|
1674
|
+
versionDownload,
|
|
1675
|
+
rollback,
|
|
1676
|
+
rollbackStatus,
|
|
1677
|
+
sourceLs,
|
|
1678
|
+
sourceRead,
|
|
1679
|
+
sourcePut,
|
|
1680
|
+
sourceRm,
|
|
1681
|
+
ask,
|
|
1682
|
+
askStatus,
|
|
1683
|
+
add,
|
|
1684
|
+
url,
|
|
1685
|
+
compile,
|
|
1686
|
+
remove,
|
|
1687
|
+
create,
|
|
1688
|
+
listSources,
|
|
1689
|
+
rmSource,
|
|
1690
|
+
schema,
|
|
1691
|
+
status,
|
|
1692
|
+
kbList,
|
|
1693
|
+
kbIndex,
|
|
1694
|
+
kbGrep,
|
|
1695
|
+
kbRead,
|
|
1696
|
+
importSnapshot,
|
|
1697
|
+
importStatus
|
|
1698
|
+
];
|
|
1699
|
+
var te_kb_default = commands;
|
|
1700
|
+
export {
|
|
1701
|
+
add,
|
|
1702
|
+
ask,
|
|
1703
|
+
askStatus,
|
|
1704
|
+
compile,
|
|
1705
|
+
create,
|
|
1706
|
+
te_kb_default as default,
|
|
1707
|
+
importSnapshot,
|
|
1708
|
+
importStatus,
|
|
1709
|
+
kbGrep,
|
|
1710
|
+
kbIndex,
|
|
1711
|
+
kbList,
|
|
1712
|
+
kbRead,
|
|
1713
|
+
listSources,
|
|
1714
|
+
remove,
|
|
1715
|
+
rmSource,
|
|
1716
|
+
rollback,
|
|
1717
|
+
rollbackStatus,
|
|
1718
|
+
schema,
|
|
1719
|
+
sourceLs,
|
|
1720
|
+
sourcePut,
|
|
1721
|
+
sourceRead,
|
|
1722
|
+
sourceRm,
|
|
1723
|
+
status,
|
|
1724
|
+
url,
|
|
1725
|
+
versionDiff,
|
|
1726
|
+
versionDownload,
|
|
1727
|
+
versionRead,
|
|
1728
|
+
versionShow,
|
|
1729
|
+
versionSources,
|
|
1730
|
+
versionTree,
|
|
1731
|
+
versions
|
|
1732
|
+
};
|