oasis_test 0.1.128 → 0.1.129
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/index.js +43019 -13510
- package/dist/outline/outline-kb.mjs +76 -8
- package/package.json +2 -2
|
@@ -23,6 +23,8 @@ const CONTENT_HASH_PENDING = "pending";
|
|
|
23
23
|
const USAGE = "Usage: outline-kb <command> [arguments] [--collection ID] [--root ID]. Run outline-kb <command> --help for the exact contract.";
|
|
24
24
|
const WRITE_COMMANDS = new Set(["init", "create", "update", "append", "import-file", "import-url", "import-wechat", "apply"]);
|
|
25
25
|
const QUERY_COMMANDS = new Set(["status", "search", "read", "read-many", "search-and-read"]);
|
|
26
|
+
const INGEST_FORBIDDEN_COMMANDS = new Set(["init", "create", "update", "append", "apply", "lint"]);
|
|
27
|
+
const MAINTENANCE_ALLOWED_COMMANDS = new Set(["status", "validate-maintenance-plan"]);
|
|
26
28
|
const WECHAT_ARTICLE_HOSTS = new Set(["mp.weixin.qq.com", "weixin.qq.com"]);
|
|
27
29
|
const WECHAT_FETCH_HEADERS = Object.freeze({
|
|
28
30
|
"user-agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0 Safari/537.36",
|
|
@@ -57,6 +59,32 @@ const PLAN_EXAMPLE = {
|
|
|
57
59
|
{ type: "append", path: "log", contentFile: "log.md" },
|
|
58
60
|
],
|
|
59
61
|
};
|
|
62
|
+
const MAINTENANCE_PLAN_SCHEMA = {
|
|
63
|
+
$schema: "https://json-schema.org/draft/2020-12/schema",
|
|
64
|
+
type: "object",
|
|
65
|
+
required: ["jobId", "operations"],
|
|
66
|
+
additionalProperties: false,
|
|
67
|
+
properties: {
|
|
68
|
+
jobId: { type: "string", minLength: 1 },
|
|
69
|
+
sourceHash: { type: "string" },
|
|
70
|
+
operations: {
|
|
71
|
+
type: "array",
|
|
72
|
+
items: {
|
|
73
|
+
oneOf: [
|
|
74
|
+
{ type: "object", required: ["type", "path", "contentFile"], properties: { type: { const: "create" }, path: { type: "string" }, contentFile: { type: "string" } }, additionalProperties: false },
|
|
75
|
+
{ type: "object", required: ["type", "path", "contentFile"], properties: { type: { const: "update" }, path: { type: "string" }, contentFile: { type: "string" } }, additionalProperties: false },
|
|
76
|
+
],
|
|
77
|
+
},
|
|
78
|
+
},
|
|
79
|
+
},
|
|
80
|
+
};
|
|
81
|
+
const MAINTENANCE_PLAN_EXAMPLE = {
|
|
82
|
+
jobId: "knowledge-maintenance-20260831-001",
|
|
83
|
+
operations: [
|
|
84
|
+
{ type: "create", path: "wiki/concepts/example", contentFile: "concept.md" },
|
|
85
|
+
{ type: "update", path: "wiki/synthesis/existing", contentFile: "synthesis.md" },
|
|
86
|
+
],
|
|
87
|
+
};
|
|
60
88
|
const HELP_FAILURE_EXAMPLE = { ok: false, error: { code: "USAGE", message: "missing required argument" } };
|
|
61
89
|
const PLAN_FAILURE_EXAMPLES = [
|
|
62
90
|
{ ok: false, error: { code: "PLAN_FILE_NOT_FOUND", message: "Plan file not found", details: { path: "/workspace/kb-plan/plan.json" } } },
|
|
@@ -79,6 +107,7 @@ const COMMAND_HELP = {
|
|
|
79
107
|
"import-url": { usage: "outline-kb import-url URL --path raw/PATH --collection ID [--root ID]", description: "Fetch a public URL and import immutable raw evidence. WeChat article URLs are routed to the dedicated parser automatically." },
|
|
80
108
|
"import-wechat": { usage: "outline-kb import-wechat WECHAT_URL --path raw/PATH --collection ID [--root ID]", description: "Capture a WeChat Official Account article with #js_content and publisher metadata validation." },
|
|
81
109
|
"validate-plan": { usage: "outline-kb validate-plan PLAN.json --collection ID [--root ID]", description: "Validate local files, plan schema and every CAS precondition without writing.", pathResolution: "Every contentFile is resolved relative to the PLAN.json directory.", planSchema: PLAN_SCHEMA, example: PLAN_EXAMPLE, failureExamples: PLAN_FAILURE_EXAMPLES },
|
|
110
|
+
"validate-maintenance-plan": { usage: "outline-kb validate-maintenance-plan PLAN.json", description: "Validate local maintenance Candidate files without reading or writing Outline. Available only in the system-owned network-maintenance profile.", pathResolution: "Every contentFile is resolved relative to the PLAN.json directory.", planSchema: MAINTENANCE_PLAN_SCHEMA, example: MAINTENANCE_PLAN_EXAMPLE, failureExamples: PLAN_FAILURE_EXAMPLES },
|
|
82
111
|
apply: { usage: "outline-kb apply PLAN.json --collection ID [--root ID]", description: "Preflight all local files and the complete plan, apply it, then return deterministic lint.", pathResolution: "Every contentFile is resolved relative to the PLAN.json directory.", planSchema: PLAN_SCHEMA, example: PLAN_EXAMPLE, failureExamples: PLAN_FAILURE_EXAMPLES },
|
|
83
112
|
lint: { usage: "outline-kb lint --collection ID [--root ID]", description: "Full-library deterministic health check. Do not run inside Query." },
|
|
84
113
|
};
|
|
@@ -89,6 +118,7 @@ function successExample(command) {
|
|
|
89
118
|
}
|
|
90
119
|
if (command === "read") return { ok: true, document: { id: "document-id", title: "Example", path: "wiki/concepts/example", url: "https://outline.example/doc/example", text: "..." } };
|
|
91
120
|
if (command === "validate-plan") return { ok: true, jobId: PLAN_EXAMPLE.jobId, sourceHash: null, operations: PLAN_EXAMPLE.operations };
|
|
121
|
+
if (command === "validate-maintenance-plan") return { ok: true, jobId: MAINTENANCE_PLAN_EXAMPLE.jobId, sourceHash: null, operations: MAINTENANCE_PLAN_EXAMPLE.operations };
|
|
92
122
|
if (command === "apply") return { ok: true, jobId: PLAN_EXAMPLE.jobId, results: [], lint: { ok: true, errors: [], warnings: [] }, status: "succeeded" };
|
|
93
123
|
return { ok: true };
|
|
94
124
|
}
|
|
@@ -822,12 +852,15 @@ export class OutlineWiki {
|
|
|
822
852
|
const tree = snapshot ?? await this.snapshot();
|
|
823
853
|
const pathOf = new Map(tree.documents.map((document) => [document.id, document.path]));
|
|
824
854
|
const prefix = under ? `${normalizePath(under)}/` : null;
|
|
855
|
+
const explicitRaw = under ? (normalizePath(under) === "raw" || normalizePath(under).startsWith("raw/")) : false;
|
|
825
856
|
return matches
|
|
826
857
|
.map((document) => {
|
|
827
858
|
const path = pathOf.get(document.id);
|
|
828
859
|
return path === undefined ? null : { ...document, path };
|
|
829
860
|
})
|
|
830
|
-
.filter((document) => document
|
|
861
|
+
.filter((document) => document
|
|
862
|
+
&& (explicitRaw || !(document.path === "raw" || document.path.startsWith("raw/")))
|
|
863
|
+
&& (!under || document.path === normalizePath(under) || document.path.startsWith(prefix)))
|
|
831
864
|
.slice(0, Math.max(1, Math.min(Number(limit) || 10, 20)));
|
|
832
865
|
}
|
|
833
866
|
|
|
@@ -1068,8 +1101,21 @@ export class OutlineWiki {
|
|
|
1068
1101
|
if (operation.type === "update") results.push({ path: operation.path, ...(await this.update(operation.path, operation.content, operation.expectedUpdatedAt, tree)) });
|
|
1069
1102
|
if (operation.type === "append") results.push({ path: operation.path, ...(await this.appendLog(operation.content, validated.jobId, tree)) });
|
|
1070
1103
|
}
|
|
1071
|
-
const
|
|
1072
|
-
|
|
1104
|
+
const fullLint = await this.lint(tree);
|
|
1105
|
+
const changedPaths = validated.operations.map((operation) => operation.path);
|
|
1106
|
+
const changesKnowledge = changedPaths.some((path) => path === "raw" || path.startsWith("raw/") || path === "wiki" || path.startsWith("wiki/"));
|
|
1107
|
+
const changesSources = changedPaths.some((path) => path.startsWith("wiki/sources/"));
|
|
1108
|
+
const touchesCurrentChange = (issue) => {
|
|
1109
|
+
const issuePath = typeof issue?.path === "string" ? normalizePath(issue.path) : "";
|
|
1110
|
+
if (!issuePath) return true;
|
|
1111
|
+
if (changesKnowledge && issue.code === "MISSING_REQUIRED") return true;
|
|
1112
|
+
if (changesSources && issue.code === "SOURCE_COUNT_MISMATCH") return true;
|
|
1113
|
+
return changedPaths.some((path) => path === issuePath || path.startsWith(`${issuePath}/`) || issuePath.startsWith(`${path}/`));
|
|
1114
|
+
};
|
|
1115
|
+
const errors = fullLint.errors.filter(touchesCurrentChange);
|
|
1116
|
+
const healthErrors = fullLint.errors.filter((issue) => !touchesCurrentChange(issue));
|
|
1117
|
+
const lint = { ...fullLint, ok: errors.length === 0, errors, ...(healthErrors.length ? { healthErrors } : {}) };
|
|
1118
|
+
return { jobId: validated.jobId, results, lint, status: lint.ok ? "succeeded" : "needs_repair" };
|
|
1073
1119
|
}
|
|
1074
1120
|
|
|
1075
1121
|
async lint(snapshot = null) {
|
|
@@ -1266,16 +1312,17 @@ async function readPlanContent(contentFile, planFile, operation, operationIndex)
|
|
|
1266
1312
|
}
|
|
1267
1313
|
}
|
|
1268
1314
|
|
|
1269
|
-
async function preflightPlanFiles(plan, planFile = null) {
|
|
1315
|
+
async function preflightPlanFiles(plan, planFile = null, options = {}) {
|
|
1270
1316
|
if (!plan || typeof plan !== "object" || Array.isArray(plan) || !Array.isArray(plan.operations)) {
|
|
1271
1317
|
throw new OutlineKbError("INVALID_PLAN", "Plan must contain an operations array");
|
|
1272
1318
|
}
|
|
1273
1319
|
if (!plan.jobId || typeof plan.jobId !== "string") throw new OutlineKbError("INVALID_PLAN", "Plan requires jobId");
|
|
1274
1320
|
const seen = new Set();
|
|
1275
1321
|
const operations = [];
|
|
1322
|
+
const allowedOperationTypes = options.allowedOperationTypes ?? new Set(["create", "update", "append"]);
|
|
1276
1323
|
for (let index = 0; index < plan.operations.length; index++) {
|
|
1277
1324
|
const operation = plan.operations[index];
|
|
1278
|
-
if (!operation || !
|
|
1325
|
+
if (!operation || !allowedOperationTypes.has(operation.type)) {
|
|
1279
1326
|
throw new OutlineKbError("INVALID_PLAN", `Unsupported operation at index ${index}`);
|
|
1280
1327
|
}
|
|
1281
1328
|
const path = normalizePath(operation.path);
|
|
@@ -1287,7 +1334,7 @@ async function preflightPlanFiles(plan, planFile = null) {
|
|
|
1287
1334
|
if (path === "log" && operation.type !== "append") throw new OutlineKbError("LOG_APPEND_ONLY", "Plan may only append log");
|
|
1288
1335
|
if (operation.type === "append" && path !== "log") throw new OutlineKbError("INVALID_PLAN", "append is only supported for log");
|
|
1289
1336
|
const content = await readPlanContent(operation.contentFile, planFile, operation, index);
|
|
1290
|
-
if (operation.type !== "append") assertDocument(path, content);
|
|
1337
|
+
if (operation.type !== "append" && options.validateMetadata !== false) assertDocument(path, content);
|
|
1291
1338
|
operations.push({ ...operation, path, content });
|
|
1292
1339
|
}
|
|
1293
1340
|
return { jobId: plan.jobId, sourceHash: plan.sourceHash ?? null, operations };
|
|
@@ -1342,10 +1389,31 @@ async function run(argv, env = process.env) {
|
|
|
1342
1389
|
if (env.OUTLINE_KB_READ_ONLY === "1" && WRITE_COMMANDS.has(command)) {
|
|
1343
1390
|
throw new OutlineKbError("READ_ONLY", `outline-kb ${command} is disabled for a read-only knowledge query`);
|
|
1344
1391
|
}
|
|
1392
|
+
if (env.OUTLINE_KB_INGEST_MODE === "1" && INGEST_FORBIDDEN_COMMANDS.has(command)) {
|
|
1393
|
+
throw new OutlineKbError("INGEST_COMMAND_DISABLED", `outline-kb ${command} is disabled until the ingest Candidate is approved`);
|
|
1394
|
+
}
|
|
1395
|
+
if (command === "validate-maintenance-plan" && env.OUTLINE_KB_MAINTENANCE_MODE !== "1") {
|
|
1396
|
+
throw new OutlineKbError("MAINTENANCE_MODE_REQUIRED", "validate-maintenance-plan is only available to a system-owned knowledge maintenance session");
|
|
1397
|
+
}
|
|
1398
|
+
if (env.OUTLINE_KB_MAINTENANCE_MODE === "1" && !MAINTENANCE_ALLOWED_COMMANDS.has(command)) {
|
|
1399
|
+
throw new OutlineKbError("MAINTENANCE_COMMAND_DISABLED", `outline-kb ${command ?? "<missing>"} is disabled for a knowledge maintenance Candidate session`);
|
|
1400
|
+
}
|
|
1345
1401
|
let planInput = null;
|
|
1346
|
-
if (command === "validate-plan" || command === "apply") {
|
|
1402
|
+
if (command === "validate-plan" || command === "validate-maintenance-plan" || command === "apply") {
|
|
1347
1403
|
const loaded = await readPlanFile(need(positional[0], `${command} requires a JSON plan file`));
|
|
1348
|
-
|
|
1404
|
+
const systemManagedMetadata = command === "validate-maintenance-plan" || (command === "validate-plan"
|
|
1405
|
+
&& env.OUTLINE_KB_INGEST_MODE === "1"
|
|
1406
|
+
&& env.OASIS_KNOWLEDGE_SYSTEM_METADATA === "1");
|
|
1407
|
+
planInput = {
|
|
1408
|
+
...loaded,
|
|
1409
|
+
preflight: await preflightPlanFiles(loaded.plan, loaded.path, {
|
|
1410
|
+
validateMetadata: !systemManagedMetadata,
|
|
1411
|
+
...(command === "validate-maintenance-plan" ? { allowedOperationTypes: new Set(["create", "update"]) } : {}),
|
|
1412
|
+
}),
|
|
1413
|
+
};
|
|
1414
|
+
}
|
|
1415
|
+
if (command === "validate-maintenance-plan") {
|
|
1416
|
+
return { ok: true, ...planInput.preflight };
|
|
1349
1417
|
}
|
|
1350
1418
|
const client = new OutlineApiClient({ baseUrl: need(env.OUTLINE_BASE_URL, "OUTLINE_BASE_URL is required"), apiToken: need(env.OUTLINE_API_TOKEN, "OUTLINE_API_TOKEN is required") });
|
|
1351
1419
|
if (command === "status") {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "oasis_test",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.129",
|
|
4
4
|
"description": "Oasis node daemon + CLI — background daemon, auto-start, full server CLI",
|
|
5
5
|
"bin": {
|
|
6
6
|
"oasis": "./dist/index.js"
|
|
@@ -26,6 +26,6 @@
|
|
|
26
26
|
"node": ">=20"
|
|
27
27
|
},
|
|
28
28
|
"oasisRelease": {
|
|
29
|
-
"sourceHead": "
|
|
29
|
+
"sourceHead": "d5063bab558443d535d4c4370a53ed4e169e445e"
|
|
30
30
|
}
|
|
31
31
|
}
|