@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,93 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.MCP_RESTART_WINDOW_MS = exports.MCP_RESTART_MAX = void 0;
|
|
4
|
+
exports.runMcpSupervisor = runMcpSupervisor;
|
|
5
|
+
const child_process_1 = require("child_process");
|
|
6
|
+
const mcp_restart_1 = require("../lib/mcp-restart");
|
|
7
|
+
const orphan_guard_1 = require("../lib/orphan-guard");
|
|
8
|
+
// The supervising parent for `mla mcp` (see lib/mcp-restart.ts for the why).
|
|
9
|
+
// This process is intentionally dumb: it holds the client's stdio pipe open and
|
|
10
|
+
// keeps a single `mla mcp --child` worker alive under it. The worker serves the
|
|
11
|
+
// MCP protocol; when it detects a newer build on disk and goes idle it exits
|
|
12
|
+
// with MCP_RESTART_EXIT_CODE, and we respawn it on the fresh dist. Because the
|
|
13
|
+
// parent never closes fd 0/1, the MCP client never sees a disconnect across a
|
|
14
|
+
// reload. Every other worker exit (0 clean disconnect via stdin EOF, 1/2 error)
|
|
15
|
+
// is final and we propagate it.
|
|
16
|
+
// Storm cap: tolerate this many reloads inside the window before giving up, so a
|
|
17
|
+
// pathological loop (e.g. a build-info `builtAt` that is perpetually "newer" due
|
|
18
|
+
// to clock skew) degrades to the old manual-restart behaviour instead of
|
|
19
|
+
// respawning without bound. Legitimate dev rebuilds are far rarer than this.
|
|
20
|
+
exports.MCP_RESTART_MAX = 5;
|
|
21
|
+
exports.MCP_RESTART_WINDOW_MS = 60_000;
|
|
22
|
+
// Re-spawn the SAME mla binary as `mla mcp --child`, inheriting our stdio so the
|
|
23
|
+
// worker reads/writes the exact pipe the client connected to us on. process.exit
|
|
24
|
+
// in the worker (the reload signal) closes only the worker's dup'd fds; ours stay
|
|
25
|
+
// open, so the client's connection survives the swap. `onChild` is handed a
|
|
26
|
+
// killer so the supervisor can SIGTERM this worker if the supervisor itself is
|
|
27
|
+
// told to stop (otherwise a TERM to the supervisor would orphan the worker).
|
|
28
|
+
function defaultSpawnChild(childArgv, onChild) {
|
|
29
|
+
return new Promise((resolve) => {
|
|
30
|
+
const child = (0, child_process_1.spawn)(process.execPath, [process.argv[1], "mcp", "--child", ...childArgv], { stdio: "inherit", env: process.env });
|
|
31
|
+
onChild?.(() => {
|
|
32
|
+
try {
|
|
33
|
+
child.kill("SIGTERM");
|
|
34
|
+
}
|
|
35
|
+
catch {
|
|
36
|
+
// already gone; nothing to reap.
|
|
37
|
+
}
|
|
38
|
+
});
|
|
39
|
+
child.on("exit", (code, signal) => resolve(typeof code === "number" ? code : signal ? 1 : 0));
|
|
40
|
+
child.on("error", () => resolve(1));
|
|
41
|
+
});
|
|
42
|
+
}
|
|
43
|
+
// Wire the supervisor's own teardown to the current worker by delegating to the
|
|
44
|
+
// SAME guard the worker uses (lib/orphan-guard.ts), so the supervisor gets both
|
|
45
|
+
// backstops, not just signal handling:
|
|
46
|
+
// - SIGTERM/SIGINT/SIGHUP: kill the worker first (onTerminate), then exit 143
|
|
47
|
+
// (the standard "terminated by SIGTERM" code) so the chain collapses cleanly
|
|
48
|
+
// instead of leaving the worker blocked on a stdin whose EOF never comes.
|
|
49
|
+
// - parent-death watchdog: if the supervisor is orphaned (ppid -> 1) while its
|
|
50
|
+
// worker is still alive beneath it, the worker's ppid is the still-running
|
|
51
|
+
// supervisor (NOT 1), so the worker's own watchdog never fires. The
|
|
52
|
+
// supervisor's does: it kills the worker (onTerminate) and exits 0. That
|
|
53
|
+
// orphaned-supervisor case was the MAJORITY of the measured leak (148 vs 61
|
|
54
|
+
// workers), so the watchdog has to live here too, not just at the leaf.
|
|
55
|
+
// The 'exit' listener is a belt-and-suspenders reap for any OTHER exit path (the
|
|
56
|
+
// guard wires only signals + the watchdog); kill is synchronous, so it is safe
|
|
57
|
+
// to call from inside an 'exit' handler.
|
|
58
|
+
function defaultInstallTeardown(killCurrent) {
|
|
59
|
+
(0, orphan_guard_1.installOrphanGuard)({ onTerminate: killCurrent, signalExitCode: 143 });
|
|
60
|
+
process.on("exit", () => killCurrent());
|
|
61
|
+
}
|
|
62
|
+
async function runMcpSupervisor(argv, deps = {}) {
|
|
63
|
+
const spawnChild = deps.spawnChild ?? defaultSpawnChild;
|
|
64
|
+
const errorLog = deps.errorLog ?? ((m) => console.error(m));
|
|
65
|
+
const now = deps.now ?? Date.now;
|
|
66
|
+
const installTeardown = deps.installTeardown ?? defaultInstallTeardown;
|
|
67
|
+
// The killer for whichever worker is live right now. Reset to a no-op the
|
|
68
|
+
// instant a worker exits, so a signal that races the worker's death (or the
|
|
69
|
+
// gap between reload-respawns) never double-kills or targets a dead pid. The
|
|
70
|
+
// teardown handlers below close over this slot, not over any one worker.
|
|
71
|
+
let killCurrentChild = () => { };
|
|
72
|
+
installTeardown(() => killCurrentChild());
|
|
73
|
+
const restarts = [];
|
|
74
|
+
for (;;) {
|
|
75
|
+
const code = await spawnChild(argv, (kill) => {
|
|
76
|
+
killCurrentChild = kill;
|
|
77
|
+
});
|
|
78
|
+
killCurrentChild = () => { };
|
|
79
|
+
if (code !== mcp_restart_1.MCP_RESTART_EXIT_CODE)
|
|
80
|
+
return code;
|
|
81
|
+
const t = now();
|
|
82
|
+
restarts.push(t);
|
|
83
|
+
const recent = restarts.filter((ts) => t - ts < exports.MCP_RESTART_WINDOW_MS);
|
|
84
|
+
if (recent.length > exports.MCP_RESTART_MAX) {
|
|
85
|
+
errorLog(`Meetless MCP: ${recent.length} reloads within ` +
|
|
86
|
+
`${Math.round(exports.MCP_RESTART_WINDOW_MS / 1000)}s; giving up to avoid a ` +
|
|
87
|
+
`restart storm. Restart your editor to recover.`);
|
|
88
|
+
return code;
|
|
89
|
+
}
|
|
90
|
+
errorLog("Meetless MCP: a newer build is on disk; reloading the server " +
|
|
91
|
+
"(no editor restart needed).");
|
|
92
|
+
}
|
|
93
|
+
}
|
|
@@ -0,0 +1,227 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
+
exports.runMcp = runMcp;
|
|
37
|
+
const path = __importStar(require("path"));
|
|
38
|
+
const config_1 = require("../lib/config");
|
|
39
|
+
const workspace_1 = require("../lib/workspace");
|
|
40
|
+
const mcp_fetchers_1 = require("../lib/mcp-fetchers");
|
|
41
|
+
const staleness_1 = require("../lib/staleness");
|
|
42
|
+
const mcp_restart_1 = require("../lib/mcp-restart");
|
|
43
|
+
const orphan_guard_1 = require("../lib/orphan-guard");
|
|
44
|
+
const packaged_1 = require("../lib/packaged");
|
|
45
|
+
// @meetless/mcp is ESM-only; `mla` compiles to CommonJS. It ships as a CJS
|
|
46
|
+
// bundle (scripts/bundle-esm.js -> dist/bundles/mcp.js) so the CLI loads it with
|
|
47
|
+
// a plain require(), which the pkg V8 snapshot supports. A true import() does
|
|
48
|
+
// NOT work inside the snapshot (no ESM dynamic-import callback) and would die
|
|
49
|
+
// with "A dynamic import callback was not specified". The Function constructor
|
|
50
|
+
// below preserves a TRUE runtime import() for the dev fallback only (ts-node
|
|
51
|
+
// `pnpm dev`, no built dist), where tsc must not downlevel it to require() of an
|
|
52
|
+
// ESM package.
|
|
53
|
+
const trueDynamicImport = new Function("u", "return import(u)");
|
|
54
|
+
// dist/commands/mcp.js -> dist -> dist/bundles/mcp.js
|
|
55
|
+
function mcpBundlePath() {
|
|
56
|
+
return path.resolve(__dirname, "..", "bundles", "mcp.js");
|
|
57
|
+
}
|
|
58
|
+
// Prefer the bundled CJS (require() works in the binary). Fall back to the ESM
|
|
59
|
+
// source via a true import() for dev (ts-node), where no dist/bundles exists.
|
|
60
|
+
// The fallback never runs inside the binary, where a true import() would throw;
|
|
61
|
+
// there a require failure surfaces as-is. Only fall through on a genuine
|
|
62
|
+
// "module not found"; a real load error inside the bundle must surface.
|
|
63
|
+
async function loadAndServe(deps) {
|
|
64
|
+
let mod;
|
|
65
|
+
try {
|
|
66
|
+
mod = require(mcpBundlePath());
|
|
67
|
+
}
|
|
68
|
+
catch (e) {
|
|
69
|
+
if ((0, packaged_1.isPackagedBinary)())
|
|
70
|
+
throw e;
|
|
71
|
+
const code = e?.code;
|
|
72
|
+
if (code !== "MODULE_NOT_FOUND" && code !== "ERR_MODULE_NOT_FOUND")
|
|
73
|
+
throw e;
|
|
74
|
+
mod = (await trueDynamicImport("@meetless/mcp"));
|
|
75
|
+
}
|
|
76
|
+
return mod.runStdioServer(deps);
|
|
77
|
+
}
|
|
78
|
+
// A spawned MCP server is a daemon: it cannot `cd`, and its launch cwd is
|
|
79
|
+
// whatever the client chose, which may sit outside the activated repo. So
|
|
80
|
+
// `mla mcp` must NOT blindly trust process.cwd() for marker resolution. Derive
|
|
81
|
+
// the start dir from an explicit client signal instead, in priority order:
|
|
82
|
+
// 1. an injected startDir (tests, and a future `mla mcp --dir <path>`),
|
|
83
|
+
// 2. MEETLESS_PROJECT_DIR (client-agnostic: any MCP client can pin the repo
|
|
84
|
+
// in its server `env` block),
|
|
85
|
+
// 3. CLAUDE_PROJECT_DIR (Claude Code sets this to the project root for
|
|
86
|
+
// every stdio server it spawns, so CC users get zero-config resolution).
|
|
87
|
+
// Falling through to undefined lets resolveWorkspaceContext default to
|
|
88
|
+
// process.cwd(), which is correct for an interactive `mla mcp` launch.
|
|
89
|
+
function resolveStartDir(env, explicit) {
|
|
90
|
+
return (explicit ?? env.MEETLESS_PROJECT_DIR ?? env.CLAUDE_PROJECT_DIR ?? undefined);
|
|
91
|
+
}
|
|
92
|
+
// notesRoot powers ONLY the INDEX.md canonical matcher, which degrades to
|
|
93
|
+
// retrieval when absent, so an imperfect guess is non-fatal. Honor an explicit
|
|
94
|
+
// override, else derive the standalone notes repo as a sibling of the marker
|
|
95
|
+
// repo (projects/<x>/notes for the dogfood layout).
|
|
96
|
+
function resolveNotesRoot(env, ctx) {
|
|
97
|
+
if (env.MEETLESS_NOTES_ROOT)
|
|
98
|
+
return env.MEETLESS_NOTES_ROOT;
|
|
99
|
+
return path.resolve(ctx.markerDir, "..", "notes");
|
|
100
|
+
}
|
|
101
|
+
function notActivatedStatus() {
|
|
102
|
+
return {
|
|
103
|
+
state: "inactive",
|
|
104
|
+
reason: "not-activated",
|
|
105
|
+
message: "Meetless is installed but inactive in this repository. No Meetless context is being injected.",
|
|
106
|
+
action: { command: "mla activate" },
|
|
107
|
+
};
|
|
108
|
+
}
|
|
109
|
+
function notAuthenticatedStatus() {
|
|
110
|
+
return {
|
|
111
|
+
state: "inactive",
|
|
112
|
+
reason: "not-authenticated",
|
|
113
|
+
message: "Meetless is not logged in, so it is inactive here. No Meetless context is being injected.",
|
|
114
|
+
action: { command: "mla login" },
|
|
115
|
+
};
|
|
116
|
+
}
|
|
117
|
+
function invalidActivationStatus() {
|
|
118
|
+
return {
|
|
119
|
+
state: "inactive",
|
|
120
|
+
reason: "invalid-activation",
|
|
121
|
+
message: "Meetless activation is incomplete in this repository. Run `mla doctor`, then rerun `mla activate` to repair it.",
|
|
122
|
+
action: { command: "mla doctor" },
|
|
123
|
+
};
|
|
124
|
+
}
|
|
125
|
+
// Boot a connected-but-inactive (status-only) server for a KNOWN dormant state.
|
|
126
|
+
// The handshake completes (green in Claude Code), only the status tool is
|
|
127
|
+
// advertised, and no backend is touched. One stderr breadcrumb is written for
|
|
128
|
+
// Claude Code's MCP log so a connected-but-inactive server reads as intentional,
|
|
129
|
+
// not a crash; it is a plain log line, not a TTY branch.
|
|
130
|
+
async function serveInactive(status, startServer, installGuard, err) {
|
|
131
|
+
err(`meetless mcp: inactive (${status.reason}); run \`${status.action.command}\` to enable.`);
|
|
132
|
+
installGuard();
|
|
133
|
+
try {
|
|
134
|
+
await startServer({ mode: "inactive", status });
|
|
135
|
+
return 0;
|
|
136
|
+
}
|
|
137
|
+
catch (e) {
|
|
138
|
+
err(`meetless mcp server exited with an error: ${e.message}`);
|
|
139
|
+
return 1;
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
async function runMcp(argv, deps = {}) {
|
|
143
|
+
const readCfg = deps.readConfig ?? config_1.readConfig;
|
|
144
|
+
const resolveWs = deps.resolveWorkspaceContext ?? workspace_1.resolveWorkspaceContext;
|
|
145
|
+
const makeControlFetch = deps.makeControlFetch ?? mcp_fetchers_1.makeControlFetchFromCli;
|
|
146
|
+
const makeIntelFetch = deps.makeIntelFetch ?? mcp_fetchers_1.makeIntelFetchFromCli;
|
|
147
|
+
const makeIntelAsk = deps.makeIntelAsk ?? mcp_fetchers_1.makeIntelAskFromCli;
|
|
148
|
+
const makeStaleCheck = deps.makeStaleCheck ?? staleness_1.makeMcpStaleCheck;
|
|
149
|
+
const startServer = deps.startServer ?? loadAndServe;
|
|
150
|
+
const installGuard = deps.installOrphanGuard ?? orphan_guard_1.installOrphanGuard;
|
|
151
|
+
const env = deps.env ?? process.env;
|
|
152
|
+
const err = deps.errorLog ?? ((m) => console.error(m));
|
|
153
|
+
const exit = deps.exit ?? ((code) => process.exit(code));
|
|
154
|
+
let cfg;
|
|
155
|
+
try {
|
|
156
|
+
cfg = readCfg();
|
|
157
|
+
}
|
|
158
|
+
catch (e) {
|
|
159
|
+
err(e.message);
|
|
160
|
+
return 2;
|
|
161
|
+
}
|
|
162
|
+
// `none` is terminal for credentials but no longer fatal for the server: boot
|
|
163
|
+
// a status-only server so Claude Code shows a CONNECTED (not red) server that
|
|
164
|
+
// can explain it needs `mla login`.
|
|
165
|
+
if (cfg.auth.mode === "none") {
|
|
166
|
+
return serveInactive(notAuthenticatedStatus(), startServer, installGuard, err);
|
|
167
|
+
}
|
|
168
|
+
let ctx;
|
|
169
|
+
try {
|
|
170
|
+
ctx = resolveWs(resolveStartDir(env, deps.startDir));
|
|
171
|
+
}
|
|
172
|
+
catch (e) {
|
|
173
|
+
// Known-inactive states: serve a green, status-only server instead of dying
|
|
174
|
+
// red. Distinguish a missing activation (`mla activate`) from a present but
|
|
175
|
+
// broken marker (`mla doctor` to repair).
|
|
176
|
+
if (e instanceof workspace_1.NotActivatedError) {
|
|
177
|
+
return serveInactive(notActivatedStatus(), startServer, installGuard, err);
|
|
178
|
+
}
|
|
179
|
+
if (e instanceof workspace_1.MarkerMissingWorkspaceIdError) {
|
|
180
|
+
return serveInactive(invalidActivationStatus(), startServer, installGuard, err);
|
|
181
|
+
}
|
|
182
|
+
// An unanticipated resolution failure stays fatal (red): we cannot truthfully
|
|
183
|
+
// describe a state we did not expect.
|
|
184
|
+
err(e.message);
|
|
185
|
+
return 2;
|
|
186
|
+
}
|
|
187
|
+
const serverDeps = {
|
|
188
|
+
mode: "active",
|
|
189
|
+
// Closures bind the SAME cfg object, so http.ts's in-place token rotation
|
|
190
|
+
// (refreshUserToken) stays visible to every later control / intel call.
|
|
191
|
+
controlFetch: makeControlFetch(cfg),
|
|
192
|
+
intelFetch: makeIntelFetch(cfg),
|
|
193
|
+
intelAsk: makeIntelAsk(cfg),
|
|
194
|
+
defaultWorkspaceId: ctx.workspaceId,
|
|
195
|
+
notesRoot: resolveNotesRoot(env, ctx),
|
|
196
|
+
// Identity is the audited human under user-token; shared-key has none.
|
|
197
|
+
operatorUserId: cfg.auth.mode === "user-token" ? cfg.auth.user.id : null,
|
|
198
|
+
agentRuntime: env.MEETLESS_AGENT_RUNTIME || null,
|
|
199
|
+
// Snapshot the build identity now, at spawn; the probe compares against the
|
|
200
|
+
// on-disk build on every later tool call.
|
|
201
|
+
staleCheck: makeStaleCheck(),
|
|
202
|
+
// Only a supervised child can be respawned, so only it self-heals. A bare /
|
|
203
|
+
// kill-switched run has no parent to come back to, so it leaves this null and
|
|
204
|
+
// relies on the inline staleCheck warning instead.
|
|
205
|
+
onStaleRestart: (0, mcp_restart_1.isMcpChild)(argv, env)
|
|
206
|
+
? () => exit(mcp_restart_1.MCP_RESTART_EXIT_CODE)
|
|
207
|
+
: null,
|
|
208
|
+
};
|
|
209
|
+
// Install the worker's death backstops just before serving. The server below
|
|
210
|
+
// resolves ONLY on stdin EOF; if the client dies without closing the pipe the
|
|
211
|
+
// process would otherwise block forever (reparented to pid 1). Signal handlers
|
|
212
|
+
// + a parent-death watchdog reap it. Applies to BOTH the supervised --child
|
|
213
|
+
// worker and a bare / kill-switched single-process run: in the bare case our
|
|
214
|
+
// direct parent is the client, so its death also flips ppid to 1 and we exit.
|
|
215
|
+
installGuard();
|
|
216
|
+
try {
|
|
217
|
+
// Long-lived: in production this resolves only when the MCP client
|
|
218
|
+
// disconnects (stdin EOF). The entrypoint's process.exit therefore fires
|
|
219
|
+
// after the server is done, never tearing it down mid-session.
|
|
220
|
+
await startServer(serverDeps);
|
|
221
|
+
return 0;
|
|
222
|
+
}
|
|
223
|
+
catch (e) {
|
|
224
|
+
err(`meetless mcp server exited with an error: ${e.message}`);
|
|
225
|
+
return 1;
|
|
226
|
+
}
|
|
227
|
+
}
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.runQueuePrune = runQueuePrune;
|
|
4
|
+
const spool_1 = require("../lib/spool");
|
|
5
|
+
function parseArgs(argv) {
|
|
6
|
+
const out = {
|
|
7
|
+
yes: false,
|
|
8
|
+
dryRun: false,
|
|
9
|
+
flush: true,
|
|
10
|
+
maxAgeHours: null,
|
|
11
|
+
session: undefined,
|
|
12
|
+
error: null,
|
|
13
|
+
};
|
|
14
|
+
for (let i = 0; i < argv.length; i++) {
|
|
15
|
+
const a = argv[i];
|
|
16
|
+
if (a === "--yes" || a === "-y")
|
|
17
|
+
out.yes = true;
|
|
18
|
+
else if (a === "--dry-run")
|
|
19
|
+
out.dryRun = true;
|
|
20
|
+
else if (a === "--no-flush")
|
|
21
|
+
out.flush = false;
|
|
22
|
+
else if (a === "--max-age-hours") {
|
|
23
|
+
const v = Number(argv[++i]);
|
|
24
|
+
if (!Number.isFinite(v) || v < 0) {
|
|
25
|
+
out.error = `--max-age-hours expects a non-negative number, got: ${argv[i] ?? "(missing)"}`;
|
|
26
|
+
return out;
|
|
27
|
+
}
|
|
28
|
+
out.maxAgeHours = v;
|
|
29
|
+
}
|
|
30
|
+
else if (a === "--session") {
|
|
31
|
+
out.session = argv[++i];
|
|
32
|
+
if (!out.session) {
|
|
33
|
+
out.error = "--session expects a session id";
|
|
34
|
+
return out;
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
else {
|
|
38
|
+
out.error = `Unknown flag: ${a}`;
|
|
39
|
+
return out;
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
return out;
|
|
43
|
+
}
|
|
44
|
+
function fmtBytes(n) {
|
|
45
|
+
if (n < 1024)
|
|
46
|
+
return `${n}B`;
|
|
47
|
+
if (n < 1024 * 1024)
|
|
48
|
+
return `${(n / 1024).toFixed(1)}K`;
|
|
49
|
+
return `${(n / (1024 * 1024)).toFixed(1)}M`;
|
|
50
|
+
}
|
|
51
|
+
function fmtAge(sec) {
|
|
52
|
+
if (sec === null)
|
|
53
|
+
return "n/a";
|
|
54
|
+
if (sec < 3600)
|
|
55
|
+
return `${Math.round(sec / 60)}m`;
|
|
56
|
+
if (sec < 86_400)
|
|
57
|
+
return `${(sec / 3600).toFixed(1)}h`;
|
|
58
|
+
return `${(sec / 86_400).toFixed(1)}d`;
|
|
59
|
+
}
|
|
60
|
+
async function runQueuePrune(argv, opts = {}) {
|
|
61
|
+
const a = parseArgs(argv);
|
|
62
|
+
if (a.error) {
|
|
63
|
+
console.error(a.error);
|
|
64
|
+
return 2;
|
|
65
|
+
}
|
|
66
|
+
const maxAgeSec = a.maxAgeHours != null ? Math.round(a.maxAgeHours * 3600) : undefined;
|
|
67
|
+
const plan = (0, spool_1.planQueuePrune)({
|
|
68
|
+
maxAgeSec,
|
|
69
|
+
sessionId: a.session,
|
|
70
|
+
queueDir: opts.queueDir,
|
|
71
|
+
now: opts.now,
|
|
72
|
+
});
|
|
73
|
+
if (plan.candidates.length === 0) {
|
|
74
|
+
console.log(`Nothing to prune (${plan.skippedFresh} session(s) too fresh to be litter).`);
|
|
75
|
+
return 0;
|
|
76
|
+
}
|
|
77
|
+
console.log(`Prune plan: ${plan.candidates.length} dead session(s), ${plan.totalFiles} files, ` +
|
|
78
|
+
`${plan.totalUnflushedEvents} un-flushed event(s), ${fmtBytes(plan.totalBytes)}, ` +
|
|
79
|
+
`oldest ${fmtAge(plan.oldestAgeSec)}.`);
|
|
80
|
+
for (const c of plan.candidates.slice(0, 20)) {
|
|
81
|
+
console.log(` ${c.sessionId} ${c.files.length} files ${c.unflushedEvents} ev ${fmtAge(c.ageSec)}`);
|
|
82
|
+
}
|
|
83
|
+
if (plan.candidates.length > 20) {
|
|
84
|
+
console.log(` ... and ${plan.candidates.length - 20} more`);
|
|
85
|
+
}
|
|
86
|
+
if (!a.yes || a.dryRun) {
|
|
87
|
+
console.log(`\nDry run (no files removed). Re-run with --yes to prune` +
|
|
88
|
+
(a.flush ? "; un-flushed events are flushed best-effort first" : "") +
|
|
89
|
+
`.`);
|
|
90
|
+
return 0;
|
|
91
|
+
}
|
|
92
|
+
const res = (0, spool_1.executeQueuePrune)(plan, { hookDir: opts.hookDir, flush: a.flush });
|
|
93
|
+
console.log(`Pruned ${res.prunedSessions.length} session(s): removed ${res.removedFiles} files` +
|
|
94
|
+
(res.flushedSessions ? `, flushed ${res.flushedSessions} before prune` : "") +
|
|
95
|
+
(res.discardedEvents ? `, discarded ${res.discardedEvents} undeliverable event(s)` : "") +
|
|
96
|
+
`.`);
|
|
97
|
+
return 0;
|
|
98
|
+
}
|