@meetless/mla 0.1.4
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/LICENSE +201 -0
- package/README.md +81 -0
- package/dist/build-info.json +9 -0
- package/dist/bundles/ask-core.js +396 -0
- package/dist/bundles/mcp.js +16592 -0
- package/dist/bundles/trace-core.js +263 -0
- package/dist/cli.js +828 -0
- package/dist/commands/activate.js +781 -0
- package/dist/commands/adoption.js +130 -0
- package/dist/commands/ask.js +290 -0
- package/dist/commands/context.js +114 -0
- package/dist/commands/debug.js +313 -0
- package/dist/commands/doctor.js +1021 -0
- package/dist/commands/enrich.js +427 -0
- package/dist/commands/evidence.js +229 -0
- package/dist/commands/flush.js +184 -0
- package/dist/commands/graph.js +104 -0
- package/dist/commands/init.js +272 -0
- package/dist/commands/internal-active-review.js +322 -0
- package/dist/commands/internal-auto-index.js +188 -0
- package/dist/commands/internal-capture-decisions.js +320 -0
- package/dist/commands/internal-evidence-correlate.js +239 -0
- package/dist/commands/internal-evidence-hooks.js +240 -0
- package/dist/commands/internal-evidence-inject.js +231 -0
- package/dist/commands/internal-finalize.js +221 -0
- package/dist/commands/internal-pretool-observe.js +225 -0
- package/dist/commands/internal-refresh.js +136 -0
- package/dist/commands/internal-session-nudge.js +120 -0
- package/dist/commands/internal-steer-sync.js +117 -0
- package/dist/commands/internal-turn-recap.js +140 -0
- package/dist/commands/kb.js +375 -0
- package/dist/commands/kb_add.js +681 -0
- package/dist/commands/kb_forget.js +283 -0
- package/dist/commands/kb_move.js +45 -0
- package/dist/commands/kb_pending.js +410 -0
- package/dist/commands/kb_personal.js +149 -0
- package/dist/commands/kb_promote.js +188 -0
- package/dist/commands/kb_purge.js +168 -0
- package/dist/commands/kb_reingest.js +335 -0
- package/dist/commands/kb_retime.js +170 -0
- package/dist/commands/kb_review.js +391 -0
- package/dist/commands/kb_revision.js +179 -0
- package/dist/commands/kb_show.js +385 -0
- package/dist/commands/label.js +226 -0
- package/dist/commands/login.js +295 -0
- package/dist/commands/logout.js +108 -0
- package/dist/commands/mcp-supervisor.js +93 -0
- package/dist/commands/mcp.js +227 -0
- package/dist/commands/queue-prune.js +98 -0
- package/dist/commands/review.js +358 -0
- package/dist/commands/rewire.js +124 -0
- package/dist/commands/rules.js +728 -0
- package/dist/commands/scan-context.js +67 -0
- package/dist/commands/session.js +347 -0
- package/dist/commands/stats.js +479 -0
- package/dist/commands/status.js +61 -0
- package/dist/commands/summary.js +250 -0
- package/dist/commands/turn.js +114 -0
- package/dist/commands/uninstall.js +222 -0
- package/dist/commands/whoami.js +102 -0
- package/dist/commands/workspace.js +130 -0
- package/dist/hooks-template/ce0-post-tool-use.sh +34 -0
- package/dist/hooks-template/ce0-session-start.sh +49 -0
- package/dist/hooks-template/ce0-stop.sh +29 -0
- package/dist/hooks-template/ce0-user-prompt-submit.sh +38 -0
- package/dist/hooks-template/common.sh +934 -0
- package/dist/hooks-template/event-batch-filter.jq +67 -0
- package/dist/hooks-template/flush.sh +503 -0
- package/dist/hooks-template/post-tool-use.sh +423 -0
- package/dist/hooks-template/pre-tool-use.sh +69 -0
- package/dist/hooks-template/session-start.sh +140 -0
- package/dist/hooks-template/stop.sh +308 -0
- package/dist/hooks-template/user-prompt-submit.sh +1162 -0
- package/dist/lib/activation.js +79 -0
- package/dist/lib/active-conflict-cache.js +141 -0
- package/dist/lib/active-memory.js +59 -0
- package/dist/lib/active-review-runner.js +26 -0
- package/dist/lib/agent-decision/index.js +25 -0
- package/dist/lib/agent-decision/keys.js +49 -0
- package/dist/lib/agent-decision/normalize-claude.js +183 -0
- package/dist/lib/agent-decision/types.js +21 -0
- package/dist/lib/agent-decision/validate.js +216 -0
- package/dist/lib/analytics/capture.js +96 -0
- package/dist/lib/analytics/command-event.js +267 -0
- package/dist/lib/analytics/consent.js +58 -0
- package/dist/lib/analytics/coverage-gap.js +96 -0
- package/dist/lib/analytics/envelope.js +236 -0
- package/dist/lib/analytics/event-id.js +86 -0
- package/dist/lib/analytics/evidence.js +150 -0
- package/dist/lib/analytics/followthrough.js +194 -0
- package/dist/lib/analytics/forwarder.js +109 -0
- package/dist/lib/analytics/logs.js +78 -0
- package/dist/lib/analytics/metrics.js +78 -0
- package/dist/lib/analytics/recorder.js +92 -0
- package/dist/lib/analytics/review-analytics.js +75 -0
- package/dist/lib/analytics/sequence.js +77 -0
- package/dist/lib/analytics/store.js +131 -0
- package/dist/lib/analytics/turn-recap.js +279 -0
- package/dist/lib/artifact_id.js +108 -0
- package/dist/lib/auth-breaker.js +161 -0
- package/dist/lib/auto-index.js +112 -0
- package/dist/lib/classifier.js +88 -0
- package/dist/lib/config.js +298 -0
- package/dist/lib/conflict-advisory.js +64 -0
- package/dist/lib/debug-bundle.js +520 -0
- package/dist/lib/enrichment/ingest.js +301 -0
- package/dist/lib/enrichment/plan.js +253 -0
- package/dist/lib/enrichment/protocol.js +359 -0
- package/dist/lib/enrichment/scout-brief.js +176 -0
- package/dist/lib/failure-telemetry.js +444 -0
- package/dist/lib/git.js +200 -0
- package/dist/lib/governance-cache.js +77 -0
- package/dist/lib/governed-path-cache.js +76 -0
- package/dist/lib/http.js +677 -0
- package/dist/lib/identity-envelope.js +23 -0
- package/dist/lib/kb-candidate.js +65 -0
- package/dist/lib/kb_acl.js +98 -0
- package/dist/lib/login.js +353 -0
- package/dist/lib/mcp-fetchers.js +130 -0
- package/dist/lib/mcp-restart.js +47 -0
- package/dist/lib/observability.js +805 -0
- package/dist/lib/open-url.js +33 -0
- package/dist/lib/orphan-guard.js +70 -0
- package/dist/lib/packaged.js +21 -0
- package/dist/lib/reconcile-sessions.js +171 -0
- package/dist/lib/redactor.js +89 -0
- package/dist/lib/relationship-candidate-query.js +27 -0
- package/dist/lib/render.js +611 -0
- package/dist/lib/rules/applicability.js +64 -0
- package/dist/lib/rules/attest-code-rule-version.js +47 -0
- package/dist/lib/rules/attest-notes-location.js +217 -0
- package/dist/lib/rules/attest-rule-version.js +69 -0
- package/dist/lib/rules/canonical-json.js +97 -0
- package/dist/lib/rules/ce0-emit.js +64 -0
- package/dist/lib/rules/ce0-evidence.js +281 -0
- package/dist/lib/rules/ce0-recall-sample.js +82 -0
- package/dist/lib/rules/ce0-rule.js +55 -0
- package/dist/lib/rules/ce0-sampling-bucket.js +15 -0
- package/dist/lib/rules/ce0-store.js +683 -0
- package/dist/lib/rules/ce0-telemetry-project.js +93 -0
- package/dist/lib/rules/ce0-telemetry.js +158 -0
- package/dist/lib/rules/code-rule-registry.js +17 -0
- package/dist/lib/rules/command-match.js +185 -0
- package/dist/lib/rules/consult-evidence-binding.js +27 -0
- package/dist/lib/rules/consultation-capture-adapter.js +193 -0
- package/dist/lib/rules/content-match.js +56 -0
- package/dist/lib/rules/deny-admission.js +99 -0
- package/dist/lib/rules/durable-observation.js +190 -0
- package/dist/lib/rules/enforce-notes-version.js +421 -0
- package/dist/lib/rules/evaluation-input-hash.js +126 -0
- package/dist/lib/rules/evaluator.js +108 -0
- package/dist/lib/rules/inert-rule-families.js +51 -0
- package/dist/lib/rules/input-authority-resolver.js +241 -0
- package/dist/lib/rules/interception-schema.js +170 -0
- package/dist/lib/rules/interception-store.js +267 -0
- package/dist/lib/rules/live-input-authority.js +66 -0
- package/dist/lib/rules/local-matcher.js +108 -0
- package/dist/lib/rules/local-observe.js +79 -0
- package/dist/lib/rules/local-rule-version-repo.js +214 -0
- package/dist/lib/rules/memory-requirement.js +109 -0
- package/dist/lib/rules/notes-observe.js +39 -0
- package/dist/lib/rules/notes-path.js +261 -0
- package/dist/lib/rules/notes-rule.js +75 -0
- package/dist/lib/rules/observe-adapter.js +114 -0
- package/dist/lib/rules/observed-rule-hash.js +119 -0
- package/dist/lib/rules/prompt-submit-adapter.js +132 -0
- package/dist/lib/rules/requirement-subject.js +240 -0
- package/dist/lib/rules/rule-activity.js +67 -0
- package/dist/lib/rules/rule-version-hash.js +151 -0
- package/dist/lib/rules/runtime-scope.js +55 -0
- package/dist/lib/rules/stop-adapter.js +116 -0
- package/dist/lib/rules/stop-response-snapshot.js +174 -0
- package/dist/lib/rules/types.js +10 -0
- package/dist/lib/rules/ulid.js +46 -0
- package/dist/lib/rules/version-evaluation.js +156 -0
- package/dist/lib/scanner/agent-memory.js +99 -0
- package/dist/lib/scanner/bootstrap-summary.js +87 -0
- package/dist/lib/scanner/cache.js +59 -0
- package/dist/lib/scanner/frontmatter.js +42 -0
- package/dist/lib/scanner/parse-directives.js +69 -0
- package/dist/lib/scanner/parse-structured.js +72 -0
- package/dist/lib/scanner/render.js +73 -0
- package/dist/lib/scanner/scan.js +132 -0
- package/dist/lib/scanner/score.js +38 -0
- package/dist/lib/scanner/scout-mission.js +126 -0
- package/dist/lib/scanner/types.js +7 -0
- package/dist/lib/session-scope.js +195 -0
- package/dist/lib/spool.js +355 -0
- package/dist/lib/staleness.js +100 -0
- package/dist/lib/steer-cache.js +87 -0
- package/dist/lib/tagged-reference.js +20 -0
- package/dist/lib/temporal.js +109 -0
- package/dist/lib/turn-recap-emit.js +67 -0
- package/dist/lib/unwire.js +253 -0
- package/dist/lib/update-check.js +469 -0
- package/dist/lib/update-notifier.js +217 -0
- package/dist/lib/upgrade-apply.js +643 -0
- package/dist/lib/wire.js +1087 -0
- package/dist/lib/workspace.js +96 -0
- package/dist/lib/zip.js +154 -0
- package/dist/pretool-entry.js +37 -0
- package/package.json +75 -0
|
@@ -0,0 +1,179 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
// `mla kb accept <doc-id>` / `mla kb reject <doc-id>` (revision review).
|
|
3
|
+
//
|
|
4
|
+
// A `mla kb reingest` of a doc that has PROMOTED relationship edges does NOT
|
|
5
|
+
// replace the live bytes outright. It parks the new revision at
|
|
6
|
+
// SHADOW_PENDING_REVIEW (the old revision keeps grounding answers) so a human
|
|
7
|
+
// confirms the change before it ships. These two commands are the consumer side
|
|
8
|
+
// of that park:
|
|
9
|
+
//
|
|
10
|
+
// accept -> POST /internal/v1/kb/documents/<id>/accept
|
|
11
|
+
// the pending revision becomes current; it starts grounding answers,
|
|
12
|
+
// the old revision is superseded, KB_REVISION_ACCEPTED is emitted.
|
|
13
|
+
// reject -> POST /internal/v1/kb/documents/<id>/reject
|
|
14
|
+
// the pending revision is discarded; the old revision keeps serving,
|
|
15
|
+
// KB_REVISION_REJECTED is emitted, and the rejected revision's vector
|
|
16
|
+
// chunks are GC'd best-effort.
|
|
17
|
+
//
|
|
18
|
+
// Both act on a DOCUMENT id; the route resolves the doc's single
|
|
19
|
+
// SHADOW_PENDING_REVIEW revision itself (zero or many is a 409). A `note:` or
|
|
20
|
+
// `kbdocrev:` input is therefore a usage error, not a silent path resolution.
|
|
21
|
+
//
|
|
22
|
+
// Mirrors the kb_retime.ts deps-injection shape: a thin public runner loads the
|
|
23
|
+
// real config (readKbConfig) and wires the real intelPost, while every
|
|
24
|
+
// collaborator is injectable so the unit test drives it offline. Auth + actor:
|
|
25
|
+
// the POST carries Authorization via the shared intel HTTP layer; the actor
|
|
26
|
+
// rides in the BODY as actorUserId (intel stamps only Authorization + X-Trace-ID,
|
|
27
|
+
// never an actor header), so it comes from cfg.actorUserId. For a cli-session
|
|
28
|
+
// caller intel ignores the body actorUserId in favor of the session user.
|
|
29
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
30
|
+
exports.parseKbRevisionArgs = parseKbRevisionArgs;
|
|
31
|
+
exports.runKbAccept = runKbAccept;
|
|
32
|
+
exports.runKbReject = runKbReject;
|
|
33
|
+
const config_1 = require("../lib/config");
|
|
34
|
+
const http_1 = require("../lib/http");
|
|
35
|
+
const VALUE_FLAGS = new Set(["--reason"]);
|
|
36
|
+
const BOOLEAN_FLAGS = new Set(["--json"]);
|
|
37
|
+
const KBDOC_PREFIX = "kbdoc:";
|
|
38
|
+
function usage(action) {
|
|
39
|
+
return `Usage: mla kb ${action} <kbdoc:<id>|<doc-id>> [--reason <s>] [--json]`;
|
|
40
|
+
}
|
|
41
|
+
// Parse a single positional document id, plus optional --reason / --json. The id
|
|
42
|
+
// may carry the canonical `kbdoc:` prefix (stripped to the raw id for the URL
|
|
43
|
+
// path) or be bare. A `note:` or `kbdocrev:` input is rejected: the route is
|
|
44
|
+
// keyed on a document id and resolves the pending revision itself, so neither a
|
|
45
|
+
// path nor a specific revision id is a valid target. The action only shapes the
|
|
46
|
+
// error text; both verbs share this grammar.
|
|
47
|
+
function parseKbRevisionArgs(argv, action = "accept") {
|
|
48
|
+
let documentId = null;
|
|
49
|
+
let reason;
|
|
50
|
+
let json = false;
|
|
51
|
+
for (let i = 0; i < argv.length; i++) {
|
|
52
|
+
const a = argv[i];
|
|
53
|
+
if (VALUE_FLAGS.has(a)) {
|
|
54
|
+
const next = argv[i + 1];
|
|
55
|
+
if (next === undefined || next.startsWith("-")) {
|
|
56
|
+
throw new Error(`Missing value for ${a}. ${usage(action)}`);
|
|
57
|
+
}
|
|
58
|
+
if (a === "--reason")
|
|
59
|
+
reason = next;
|
|
60
|
+
i++;
|
|
61
|
+
}
|
|
62
|
+
else if (BOOLEAN_FLAGS.has(a)) {
|
|
63
|
+
json = true;
|
|
64
|
+
}
|
|
65
|
+
else if (a.startsWith("-")) {
|
|
66
|
+
const supported = [...VALUE_FLAGS, ...BOOLEAN_FLAGS].sort().join(", ");
|
|
67
|
+
throw new Error(`Unknown flag: ${a}. Supported: ${supported}. ${usage(action)}`);
|
|
68
|
+
}
|
|
69
|
+
else if (documentId === null) {
|
|
70
|
+
documentId = a;
|
|
71
|
+
}
|
|
72
|
+
else {
|
|
73
|
+
throw new Error(`Unexpected argument: ${a}. ${usage(action)}`);
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
if (documentId === null) {
|
|
77
|
+
throw new Error(`mla kb ${action} requires a document id. ${usage(action)}`);
|
|
78
|
+
}
|
|
79
|
+
const trimmed = documentId.trim();
|
|
80
|
+
if (trimmed.startsWith("note:")) {
|
|
81
|
+
throw new Error(`mla kb ${action} acts on a document id, not a note path. ` +
|
|
82
|
+
`Run \`mla kb show ${trimmed}\` to find its kbdoc id, then \`mla kb ${action} kbdoc:<id>\`.`);
|
|
83
|
+
}
|
|
84
|
+
if (trimmed.startsWith("kbdocrev:")) {
|
|
85
|
+
throw new Error(`mla kb ${action} acts on a document id, not a revision id. The route resolves ` +
|
|
86
|
+
`the document's single pending revision itself; pass the kbdoc id instead.`);
|
|
87
|
+
}
|
|
88
|
+
const id = trimmed.startsWith(KBDOC_PREFIX) ? trimmed.slice(KBDOC_PREFIX.length).trim() : trimmed;
|
|
89
|
+
if (!id) {
|
|
90
|
+
throw new Error(`mla kb ${action} requires a non-empty document id. ${usage(action)}`);
|
|
91
|
+
}
|
|
92
|
+
return { documentId: id, reason, json };
|
|
93
|
+
}
|
|
94
|
+
// Surface an intel HTTP failure helpfully, mirroring kb_retime.ts's explainRetimeError.
|
|
95
|
+
function explainReviewError(action, err, intelUrl) {
|
|
96
|
+
if (err.status === 404) {
|
|
97
|
+
return `intel returned 404: document not found in this workspace (or this intel does not expose the KB ${action} route).`;
|
|
98
|
+
}
|
|
99
|
+
if (err.status === 409) {
|
|
100
|
+
return (`intel returned 409: the document has no single pending revision to ${action}. ` +
|
|
101
|
+
`A revision is only reviewable when a reingest parked it at SHADOW_PENDING_REVIEW. ` +
|
|
102
|
+
`Details: ${err.body ?? err.message}`);
|
|
103
|
+
}
|
|
104
|
+
if (err.status === 401 || err.status === 403) {
|
|
105
|
+
return `intel rejected the request (HTTP ${err.status}). Check controlToken / workspace in cli-config.json.`;
|
|
106
|
+
}
|
|
107
|
+
if (err.status === undefined) {
|
|
108
|
+
return `intel not reachable at ${intelUrl}. Is it running? Try \`mla doctor\`.`;
|
|
109
|
+
}
|
|
110
|
+
return err.message;
|
|
111
|
+
}
|
|
112
|
+
// Render the review receipt in plain words. No double-dash range separators
|
|
113
|
+
// (An's AI-smell rule); each line spells out what changed.
|
|
114
|
+
function renderReceipt(r) {
|
|
115
|
+
const out = [];
|
|
116
|
+
if (r.action === "accepted") {
|
|
117
|
+
out.push(`Accepted revision ${r.reviewedRevisionId} on ${r.documentId}.`);
|
|
118
|
+
out.push(` current revision: ${r.currentRevisionId} (now grounding answers)`);
|
|
119
|
+
out.push(` the prior revision was superseded; its lexical chunks were soft-tombstoned.`);
|
|
120
|
+
out.push("");
|
|
121
|
+
out.push("The pending revision is now live. Its vector chunks ground answers; the old revision no longer does.");
|
|
122
|
+
}
|
|
123
|
+
else {
|
|
124
|
+
out.push(`Rejected revision ${r.reviewedRevisionId} on ${r.documentId}.`);
|
|
125
|
+
out.push(` current revision: ${r.currentRevisionId} (unchanged; still grounding answers)`);
|
|
126
|
+
out.push(` lexical chunks deleted: the rejected revision's chunk_fts rows were removed.`);
|
|
127
|
+
out.push(` vector chunks deleted: ${r.weaviateChunksDeleted}`);
|
|
128
|
+
out.push("");
|
|
129
|
+
out.push("The pending revision was discarded. The previously live revision keeps serving unchanged.");
|
|
130
|
+
}
|
|
131
|
+
return out.join("\n");
|
|
132
|
+
}
|
|
133
|
+
async function runRevisionReview(action, argv, deps) {
|
|
134
|
+
let cfg;
|
|
135
|
+
try {
|
|
136
|
+
cfg = deps?.cfg ?? (0, config_1.readKbConfig)();
|
|
137
|
+
}
|
|
138
|
+
catch (e) {
|
|
139
|
+
console.error(e.message);
|
|
140
|
+
return 2;
|
|
141
|
+
}
|
|
142
|
+
let parsed;
|
|
143
|
+
try {
|
|
144
|
+
parsed = parseKbRevisionArgs(argv, action);
|
|
145
|
+
}
|
|
146
|
+
catch (e) {
|
|
147
|
+
console.error(e.message);
|
|
148
|
+
return 2;
|
|
149
|
+
}
|
|
150
|
+
const post = deps?.http?.intelPost ?? http_1.intelPost;
|
|
151
|
+
const body = {
|
|
152
|
+
workspaceId: cfg.workspaceId,
|
|
153
|
+
actorUserId: cfg.actorUserId,
|
|
154
|
+
};
|
|
155
|
+
if (parsed.reason !== undefined)
|
|
156
|
+
body.reason = parsed.reason;
|
|
157
|
+
const path = `/internal/v1/kb/documents/${encodeURIComponent(parsed.documentId)}/${action}`;
|
|
158
|
+
let res;
|
|
159
|
+
try {
|
|
160
|
+
res = (await post(cfg, path, body));
|
|
161
|
+
}
|
|
162
|
+
catch (e) {
|
|
163
|
+
const intelUrl = cfg.intelUrl || http_1.DEFAULT_INTEL_URL;
|
|
164
|
+
console.error(explainReviewError(action, e, intelUrl));
|
|
165
|
+
return 1;
|
|
166
|
+
}
|
|
167
|
+
if (parsed.json) {
|
|
168
|
+
console.log(JSON.stringify(res, null, 2));
|
|
169
|
+
return 0;
|
|
170
|
+
}
|
|
171
|
+
console.log(renderReceipt(res));
|
|
172
|
+
return 0;
|
|
173
|
+
}
|
|
174
|
+
async function runKbAccept(argv, deps) {
|
|
175
|
+
return runRevisionReview("accept", argv, deps);
|
|
176
|
+
}
|
|
177
|
+
async function runKbReject(argv, deps) {
|
|
178
|
+
return runRevisionReview("reject", argv, deps);
|
|
179
|
+
}
|
|
@@ -0,0 +1,385 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.parseKbShowArgs = parseKbShowArgs;
|
|
4
|
+
exports.runKbShow = runKbShow;
|
|
5
|
+
const config_1 = require("../lib/config");
|
|
6
|
+
const workspace_1 = require("../lib/workspace");
|
|
7
|
+
const http_1 = require("../lib/http");
|
|
8
|
+
const open_url_1 = require("../lib/open-url");
|
|
9
|
+
const artifact_id_1 = require("../lib/artifact_id");
|
|
10
|
+
const temporal_1 = require("../lib/temporal");
|
|
11
|
+
const render_1 = require("../lib/render");
|
|
12
|
+
const VALUE_FLAGS = new Set(["--workspace", "--posture", "--as-of"]);
|
|
13
|
+
const BOOLEAN_FLAGS = new Set([
|
|
14
|
+
"--include-tombstoned",
|
|
15
|
+
"--json",
|
|
16
|
+
"--all",
|
|
17
|
+
"--audit-all",
|
|
18
|
+
"--open",
|
|
19
|
+
]);
|
|
20
|
+
const REVISION_LIMIT_DEFAULT = 20;
|
|
21
|
+
const REVISION_LIMIT_ALL = 200;
|
|
22
|
+
const AUDIT_LIMIT_DEFAULT = 10;
|
|
23
|
+
const AUDIT_LIMIT_ALL = 500;
|
|
24
|
+
function parseKbShowArgs(argv) {
|
|
25
|
+
const out = {
|
|
26
|
+
posture: "both",
|
|
27
|
+
includeTombstoned: false,
|
|
28
|
+
json: false,
|
|
29
|
+
all: false,
|
|
30
|
+
auditAll: false,
|
|
31
|
+
open: false,
|
|
32
|
+
};
|
|
33
|
+
let positional = null;
|
|
34
|
+
for (let i = 0; i < argv.length; i++) {
|
|
35
|
+
const a = argv[i];
|
|
36
|
+
if (VALUE_FLAGS.has(a)) {
|
|
37
|
+
const v = argv[i + 1];
|
|
38
|
+
if (v === undefined) {
|
|
39
|
+
throw new Error(`Missing value for ${a}`);
|
|
40
|
+
}
|
|
41
|
+
if (v.startsWith("--") || v.startsWith("-")) {
|
|
42
|
+
throw new Error(`Missing value for ${a} (got the next flag ${v} instead)`);
|
|
43
|
+
}
|
|
44
|
+
switch (a) {
|
|
45
|
+
case "--workspace":
|
|
46
|
+
out.workspace = v;
|
|
47
|
+
break;
|
|
48
|
+
case "--posture":
|
|
49
|
+
if (v !== "both" && v !== "LIVE" && v !== "SHADOW") {
|
|
50
|
+
throw new Error(`--posture must be 'both', 'LIVE', or 'SHADOW' (got '${v}')`);
|
|
51
|
+
}
|
|
52
|
+
out.posture = v;
|
|
53
|
+
break;
|
|
54
|
+
case "--as-of":
|
|
55
|
+
// parseAsOf throws on a malformed date so a typo never silently
|
|
56
|
+
// answers as-of "now"; runKbShow maps the throw to exit 2.
|
|
57
|
+
out.asOf = (0, temporal_1.parseAsOf)(v);
|
|
58
|
+
break;
|
|
59
|
+
}
|
|
60
|
+
i += 1;
|
|
61
|
+
continue;
|
|
62
|
+
}
|
|
63
|
+
if (BOOLEAN_FLAGS.has(a)) {
|
|
64
|
+
if (a === "--include-tombstoned")
|
|
65
|
+
out.includeTombstoned = true;
|
|
66
|
+
else if (a === "--json")
|
|
67
|
+
out.json = true;
|
|
68
|
+
else if (a === "--all")
|
|
69
|
+
out.all = true;
|
|
70
|
+
else if (a === "--audit-all")
|
|
71
|
+
out.auditAll = true;
|
|
72
|
+
else if (a === "--open")
|
|
73
|
+
out.open = true;
|
|
74
|
+
continue;
|
|
75
|
+
}
|
|
76
|
+
if (a.startsWith("--") || a.startsWith("-")) {
|
|
77
|
+
throw new Error(`Unknown flag: ${a}. Supported flags: ${[...VALUE_FLAGS, ...BOOLEAN_FLAGS].sort().join(", ")}`);
|
|
78
|
+
}
|
|
79
|
+
if (positional !== null) {
|
|
80
|
+
throw new Error(`\`mla kb show\` takes exactly one positional <input> (got '${positional}' and '${a}')`);
|
|
81
|
+
}
|
|
82
|
+
positional = a;
|
|
83
|
+
}
|
|
84
|
+
if (positional === null) {
|
|
85
|
+
throw new Error("`mla kb show` requires a positional <input> (kbdoc:<id> or a note path)");
|
|
86
|
+
}
|
|
87
|
+
return {
|
|
88
|
+
input: positional,
|
|
89
|
+
workspace: out.workspace,
|
|
90
|
+
posture: out.posture,
|
|
91
|
+
includeTombstoned: !!out.includeTombstoned,
|
|
92
|
+
json: !!out.json,
|
|
93
|
+
all: !!out.all,
|
|
94
|
+
auditAll: !!out.auditAll,
|
|
95
|
+
open: !!out.open,
|
|
96
|
+
asOf: out.asOf,
|
|
97
|
+
};
|
|
98
|
+
}
|
|
99
|
+
function explainIntelError(err, intelUrl) {
|
|
100
|
+
if (err.status === 404) {
|
|
101
|
+
return err.body && err.body.includes("KB_DOCUMENT_PATH_NOT_FOUND")
|
|
102
|
+
? `No KbDocument matches that path in the requested workspace. Try \`mla kb dump\` to list ingested sources.`
|
|
103
|
+
: err.body && err.body.includes("KB_DOCUMENT_NOT_FOUND")
|
|
104
|
+
? `No KbDocument matches that id in the requested workspace.`
|
|
105
|
+
: `intel returned 404. ${err.body.slice(0, 200)}`;
|
|
106
|
+
}
|
|
107
|
+
if (err.status === 401 || err.status === 403) {
|
|
108
|
+
return `intel rejected the token (HTTP ${err.status}). Check controlToken in cli-config.json.`;
|
|
109
|
+
}
|
|
110
|
+
if (err.status === undefined) {
|
|
111
|
+
return `intel not reachable at ${intelUrl}. Is it running? Try \`mla doctor\`.`;
|
|
112
|
+
}
|
|
113
|
+
return err.message;
|
|
114
|
+
}
|
|
115
|
+
function toShowRevision(r) {
|
|
116
|
+
return {
|
|
117
|
+
id: r.revisionId,
|
|
118
|
+
status: r.status,
|
|
119
|
+
ingestRunId: r.ingestRunId,
|
|
120
|
+
postureAtIngest: r.postureAtIngest,
|
|
121
|
+
normalizedBodyHash: r.normalizedBodyHash,
|
|
122
|
+
frontmatterHash: r.frontmatterHash,
|
|
123
|
+
fullDocumentHash: r.fullDocumentHash,
|
|
124
|
+
chunkCount: r.chunkCount,
|
|
125
|
+
createdAt: r.createdAt,
|
|
126
|
+
failureCode: r.status === "FAILED" ? r.failureCode : null,
|
|
127
|
+
failureReason: r.status === "FAILED" ? r.failureReason : null,
|
|
128
|
+
failedAt: r.status === "FAILED" ? r.failedAt : null,
|
|
129
|
+
provenanceOverride: r.provenanceOverride,
|
|
130
|
+
overrideReason: r.overrideReason,
|
|
131
|
+
};
|
|
132
|
+
}
|
|
133
|
+
function toShowChunk(c) {
|
|
134
|
+
return {
|
|
135
|
+
id: c.chunkUuid,
|
|
136
|
+
ordinal: c.chunkIndex,
|
|
137
|
+
bytes: c.contentLength,
|
|
138
|
+
preview: c.preview,
|
|
139
|
+
revisionId: c.revisionId,
|
|
140
|
+
};
|
|
141
|
+
}
|
|
142
|
+
function postureOf(endpoint) {
|
|
143
|
+
// Intel doesn't carry per-edge posture on candidates/promoted edges in this
|
|
144
|
+
// response shape; use the counterpart's tombstone state as a proxy hint so
|
|
145
|
+
// the renderer still has SOMETHING to print. Both posture filters operate
|
|
146
|
+
// post-render via fmtEdgeLine. The wire string is opaque to the renderer.
|
|
147
|
+
return endpoint.counterpartTombstoneState === "ACTIVE" ||
|
|
148
|
+
endpoint.counterpartTombstoneState === null
|
|
149
|
+
? "LIVE"
|
|
150
|
+
: "SHADOW";
|
|
151
|
+
}
|
|
152
|
+
function pickCounterpartEndpoint(fromEndpoint, toEndpoint) {
|
|
153
|
+
// The "self" side is whichever endpoint has isThisDoc=true. The counterpart
|
|
154
|
+
// is the other one. Direction is from-this-doc -> counterpart for outgoing,
|
|
155
|
+
// counterpart -> this-doc for incoming.
|
|
156
|
+
if (fromEndpoint.isThisDoc) {
|
|
157
|
+
return { endpoint: toEndpoint, direction: "outgoing" };
|
|
158
|
+
}
|
|
159
|
+
return { endpoint: fromEndpoint, direction: "incoming" };
|
|
160
|
+
}
|
|
161
|
+
function toCandidateRow(c) {
|
|
162
|
+
const pick = pickCounterpartEndpoint(c.fromEndpoint, c.toEndpoint);
|
|
163
|
+
return {
|
|
164
|
+
id: c.candidateId,
|
|
165
|
+
kind: "candidate",
|
|
166
|
+
direction: pick.direction,
|
|
167
|
+
predicate: c.relationType,
|
|
168
|
+
status: c.status,
|
|
169
|
+
posture: postureOf(pick.endpoint),
|
|
170
|
+
counterpart: {
|
|
171
|
+
documentId: pick.endpoint.counterpartKbDocumentId || pick.endpoint.endpointId,
|
|
172
|
+
canonicalPath: null,
|
|
173
|
+
tombstoneState: pick.endpoint.counterpartTombstoneState,
|
|
174
|
+
tombstonedAt: pick.endpoint.counterpartTombstonedAt,
|
|
175
|
+
},
|
|
176
|
+
};
|
|
177
|
+
}
|
|
178
|
+
function toPromotedRow(e) {
|
|
179
|
+
const pick = pickCounterpartEndpoint(e.fromEndpoint, e.toEndpoint);
|
|
180
|
+
return {
|
|
181
|
+
id: e.edgeId,
|
|
182
|
+
kind: "promoted",
|
|
183
|
+
direction: pick.direction,
|
|
184
|
+
predicate: e.relationType,
|
|
185
|
+
status: e.isRetrievable ? "ACTIVE" : "RETIRED",
|
|
186
|
+
posture: postureOf(pick.endpoint),
|
|
187
|
+
counterpart: {
|
|
188
|
+
documentId: pick.endpoint.counterpartKbDocumentId || pick.endpoint.endpointId,
|
|
189
|
+
canonicalPath: null,
|
|
190
|
+
tombstoneState: pick.endpoint.counterpartTombstoneState,
|
|
191
|
+
tombstonedAt: pick.endpoint.counterpartTombstonedAt,
|
|
192
|
+
},
|
|
193
|
+
// Task 5.2: carry the VALID-time window + clock trust onto the row so the
|
|
194
|
+
// renderer shows when the relation was true and how trustworthy that clock
|
|
195
|
+
// is. UNKNOWN is the honest fallback for a legacy edge with no source.
|
|
196
|
+
validity: {
|
|
197
|
+
validAt: e.validAt ?? null,
|
|
198
|
+
invalidAt: e.invalidAt ?? null,
|
|
199
|
+
validTimeSource: e.validTimeSource ?? "UNKNOWN",
|
|
200
|
+
sourceObservedAt: e.sourceObservedAt ?? null,
|
|
201
|
+
},
|
|
202
|
+
};
|
|
203
|
+
}
|
|
204
|
+
function toAuditEntry(a) {
|
|
205
|
+
return {
|
|
206
|
+
id: a.auditEventId,
|
|
207
|
+
eventType: a.eventType,
|
|
208
|
+
occurredAt: a.occurredAt,
|
|
209
|
+
actorId: a.actorUserId,
|
|
210
|
+
subjectId: a.artifactId,
|
|
211
|
+
payloadDigest: null,
|
|
212
|
+
};
|
|
213
|
+
}
|
|
214
|
+
function filterEdgesByPosture(rows, posture, includeTombstoned) {
|
|
215
|
+
return rows.filter((r) => {
|
|
216
|
+
const cpState = r.counterpart.tombstoneState;
|
|
217
|
+
const cpIsTombstoned = cpState !== null && cpState !== "ACTIVE";
|
|
218
|
+
if (cpIsTombstoned && !includeTombstoned)
|
|
219
|
+
return false;
|
|
220
|
+
if (posture === "both")
|
|
221
|
+
return true;
|
|
222
|
+
return r.posture === posture;
|
|
223
|
+
});
|
|
224
|
+
}
|
|
225
|
+
function buildShowView(resp, flags) {
|
|
226
|
+
const candidates = filterEdgesByPosture(resp.candidates.map(toCandidateRow), flags.posture, flags.includeTombstoned);
|
|
227
|
+
const promoted = filterEdgesByPosture(resp.promotedEdges.map(toPromotedRow), flags.posture, flags.includeTombstoned);
|
|
228
|
+
const totalChunks = resp.chunks.active.length +
|
|
229
|
+
resp.chunks.pendingReview.length +
|
|
230
|
+
resp.chunks.superseded.length;
|
|
231
|
+
const activePreview = resp.chunks.active.slice(0, 5).map(toShowChunk);
|
|
232
|
+
const pendingPreview = resp.chunks.pendingReview
|
|
233
|
+
.slice(0, 5)
|
|
234
|
+
.map(toShowChunk);
|
|
235
|
+
const revisionLimit = flags.all ? REVISION_LIMIT_ALL : REVISION_LIMIT_DEFAULT;
|
|
236
|
+
const auditLimit = flags.auditAll ? AUDIT_LIMIT_ALL : AUDIT_LIMIT_DEFAULT;
|
|
237
|
+
return {
|
|
238
|
+
workspaceId: resp.workspaceId,
|
|
239
|
+
document: {
|
|
240
|
+
id: resp.identity.documentId,
|
|
241
|
+
canonicalPath: resp.identity.canonicalPath,
|
|
242
|
+
pathAliases: resp.identity.pathAliases || [],
|
|
243
|
+
parentUuid: resp.identity.parentUuid,
|
|
244
|
+
provenance: resp.identity.provenance,
|
|
245
|
+
currentPosture: resp.identity.currentPosture,
|
|
246
|
+
effectivePosture: resp.identity.effectivePosture,
|
|
247
|
+
tombstoneState: resp.identity.tombstoneState,
|
|
248
|
+
tombstonedAt: resp.identity.tombstonedAt,
|
|
249
|
+
tombstoneReason: resp.identity.tombstoneReason,
|
|
250
|
+
tombstoneActorId: resp.identity.tombstoneActorId,
|
|
251
|
+
deletedAt: resp.identity.deletedAt,
|
|
252
|
+
deletedBy: resp.identity.deletedBy,
|
|
253
|
+
redactedPathHash: null,
|
|
254
|
+
createdAt: resp.identity.createdAt,
|
|
255
|
+
updatedAt: resp.identity.updatedAt,
|
|
256
|
+
},
|
|
257
|
+
currentRevision: resp.currentRevision
|
|
258
|
+
? toShowRevision(resp.currentRevision)
|
|
259
|
+
: null,
|
|
260
|
+
revisionHistory: resp.revisions.map(toShowRevision),
|
|
261
|
+
revisionHistoryTruncated: resp.revisions.length >= revisionLimit,
|
|
262
|
+
chunks: {
|
|
263
|
+
totalCount: totalChunks,
|
|
264
|
+
totalBytes: resp.chunks.totalContentBytes,
|
|
265
|
+
preview: activePreview,
|
|
266
|
+
pendingReviewPreview: pendingPreview,
|
|
267
|
+
},
|
|
268
|
+
candidates,
|
|
269
|
+
promoted,
|
|
270
|
+
audit: resp.auditTrail.map(toAuditEntry),
|
|
271
|
+
auditTruncated: resp.auditTrail.length >= auditLimit,
|
|
272
|
+
includeTombstoned: flags.includeTombstoned,
|
|
273
|
+
extraction: resp.extraction ?? null,
|
|
274
|
+
};
|
|
275
|
+
}
|
|
276
|
+
async function resolveToDocumentId(cfg, workspaceId, input, intelUrl) {
|
|
277
|
+
if (input.kind === "kbdoc") {
|
|
278
|
+
return input.id;
|
|
279
|
+
}
|
|
280
|
+
if (input.kind === "kbdocrev") {
|
|
281
|
+
throw new Error("`mla kb show` does not accept kbdocrev:<id>. Pass the parent kbdoc:<id> or the note path; revision details are rendered inside the document view.");
|
|
282
|
+
}
|
|
283
|
+
const qs = new URLSearchParams({
|
|
284
|
+
workspaceId,
|
|
285
|
+
path: input.path,
|
|
286
|
+
}).toString();
|
|
287
|
+
try {
|
|
288
|
+
const r = await (0, http_1.intelGet)(cfg, `/internal/v1/kb/documents/resolve?${qs}`, 10000);
|
|
289
|
+
return r.documentId;
|
|
290
|
+
}
|
|
291
|
+
catch (e) {
|
|
292
|
+
const err = e;
|
|
293
|
+
throw new Error(explainIntelError(err, intelUrl));
|
|
294
|
+
}
|
|
295
|
+
}
|
|
296
|
+
async function runKbShow(argv) {
|
|
297
|
+
let cfg;
|
|
298
|
+
try {
|
|
299
|
+
cfg = (0, config_1.readConfig)();
|
|
300
|
+
}
|
|
301
|
+
catch (e) {
|
|
302
|
+
console.error(e.message);
|
|
303
|
+
return 2;
|
|
304
|
+
}
|
|
305
|
+
let flags;
|
|
306
|
+
try {
|
|
307
|
+
flags = parseKbShowArgs(argv);
|
|
308
|
+
}
|
|
309
|
+
catch (e) {
|
|
310
|
+
console.error(e.message);
|
|
311
|
+
return 2;
|
|
312
|
+
}
|
|
313
|
+
let input;
|
|
314
|
+
try {
|
|
315
|
+
input = (0, artifact_id_1.parseArtifactInput)(flags.input);
|
|
316
|
+
}
|
|
317
|
+
catch (e) {
|
|
318
|
+
const msg = e instanceof artifact_id_1.ArtifactInputError ? e.message : e.message;
|
|
319
|
+
console.error(msg);
|
|
320
|
+
return 2;
|
|
321
|
+
}
|
|
322
|
+
// Folder = workspace (T1.1): the workspace comes from the nearest marker;
|
|
323
|
+
// `--workspace <id>` overrides it (admin cross-workspace inspection) and
|
|
324
|
+
// short-circuits marker resolution so an unbound directory does not block it.
|
|
325
|
+
let workspaceId;
|
|
326
|
+
try {
|
|
327
|
+
workspaceId = flags.workspace || (0, workspace_1.resolveWorkspaceId)();
|
|
328
|
+
}
|
|
329
|
+
catch (e) {
|
|
330
|
+
console.error(e.message);
|
|
331
|
+
return 2;
|
|
332
|
+
}
|
|
333
|
+
const intelUrl = cfg.intelUrl || http_1.DEFAULT_INTEL_URL;
|
|
334
|
+
let documentId;
|
|
335
|
+
try {
|
|
336
|
+
documentId = await resolveToDocumentId(cfg, workspaceId, input, intelUrl);
|
|
337
|
+
}
|
|
338
|
+
catch (e) {
|
|
339
|
+
console.error(e.message);
|
|
340
|
+
return 1;
|
|
341
|
+
}
|
|
342
|
+
const revisionLimit = flags.all ? REVISION_LIMIT_ALL : REVISION_LIMIT_DEFAULT;
|
|
343
|
+
const auditLimit = flags.auditAll ? AUDIT_LIMIT_ALL : AUDIT_LIMIT_DEFAULT;
|
|
344
|
+
const qsParams = new URLSearchParams({
|
|
345
|
+
workspaceId,
|
|
346
|
+
revisionLimit: String(revisionLimit),
|
|
347
|
+
auditLimit: String(auditLimit),
|
|
348
|
+
});
|
|
349
|
+
if (flags.asOf) {
|
|
350
|
+
// Point-in-time view: the detail endpoint runs the trusted-source as-of
|
|
351
|
+
// filter so edges not yet valid at this instant (or from an untrusted clock)
|
|
352
|
+
// are excluded. The banner goes to stderr so it never pollutes --json stdout.
|
|
353
|
+
qsParams.set("asOf", flags.asOf);
|
|
354
|
+
console.error(`Point-in-time view as of ${flags.asOf} (relations not yet valid at that instant, or from an untrusted clock, are excluded).`);
|
|
355
|
+
}
|
|
356
|
+
const qs = qsParams.toString();
|
|
357
|
+
let detail;
|
|
358
|
+
try {
|
|
359
|
+
detail = await (0, http_1.intelGet)(cfg, `/internal/v1/kb/documents/${encodeURIComponent(documentId)}/detail?${qs}`, 15000);
|
|
360
|
+
}
|
|
361
|
+
catch (e) {
|
|
362
|
+
console.error(explainIntelError(e, intelUrl));
|
|
363
|
+
return 1;
|
|
364
|
+
}
|
|
365
|
+
const view = buildShowView(detail, flags);
|
|
366
|
+
// B4a: always surface the Console review URL. The renderer stays pure, so the
|
|
367
|
+
// command layer resolves it via getConsoleUrl(cfg) and stamps it on the view.
|
|
368
|
+
view.consoleUrl = `${(0, config_1.getConsoleUrl)(cfg)}/relationships`;
|
|
369
|
+
if (flags.json) {
|
|
370
|
+
console.log(JSON.stringify(view, null, 2));
|
|
371
|
+
}
|
|
372
|
+
else {
|
|
373
|
+
console.log((0, render_1.renderKbShow)(view));
|
|
374
|
+
}
|
|
375
|
+
// B4b: `--open` is opt-in (never auto-open; the URL is always printed above). The
|
|
376
|
+
// status note goes to stderr so it never pollutes `--json` stdout.
|
|
377
|
+
if (flags.open && view.consoleUrl) {
|
|
378
|
+
const res = (0, open_url_1.openUrl)(view.consoleUrl);
|
|
379
|
+
if (res.ok)
|
|
380
|
+
console.error(`opened ${view.consoleUrl} in your browser`);
|
|
381
|
+
else
|
|
382
|
+
console.error(`could not open a browser (${res.error}); the URL is above`);
|
|
383
|
+
}
|
|
384
|
+
return 0;
|
|
385
|
+
}
|