@inerrata-corporation/errata 2.0.20 → 2.0.21-dev.3716
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/consolidate-worker.mjs +39 -16
- package/errata.mjs +305 -46
- package/generalize-worker.mjs +17184 -0
- package/install.ps1 +1 -1
- package/package.json +2 -1
- package/pass-worker.mjs +29 -2
- package/resources/context-plugin/.codex-plugin/plugin.json +1 -1
- package/resources/context-plugin/SHA256SUMS +1 -1
package/errata.mjs
CHANGED
|
@@ -137,6 +137,9 @@ var init_castalia = __esm({
|
|
|
137
137
|
"probe-failed"
|
|
138
138
|
];
|
|
139
139
|
ROUTING_REASONS = [
|
|
140
|
+
"trusted-system-public",
|
|
141
|
+
"public-target-staged",
|
|
142
|
+
"source-membrane",
|
|
140
143
|
/** Harness org (CTF lane, e2e battery, canary) — forced org-private. */
|
|
141
144
|
"harness-clamp",
|
|
142
145
|
"witnessed-vulnerability-private",
|
|
@@ -18594,6 +18597,11 @@ CREATE INDEX IF NOT EXISTS nodes_live_description ON nodes(description, label) W
|
|
|
18594
18597
|
-- Problems by the file their anchor hint names: the harvest upgrades hints for
|
|
18595
18598
|
-- every edited file of every turn, and a scan parses every Problem's attrs.
|
|
18596
18599
|
CREATE INDEX IF NOT EXISTS nodes_anchor_hint_path ON nodes(json_extract(attrs_json, '$.anchorHint.path')) WHERE valid_to IS NULL;
|
|
18600
|
+
-- Nodes still waiting on a root-cause ask. The MCP server checks this on EVERY
|
|
18601
|
+
-- tool response (the enrichment piggyback), so it must not parse every
|
|
18602
|
+
-- Solution's and Problem's attrs per call. Not partial: the Problem pull reads
|
|
18603
|
+
-- closed versions too.
|
|
18604
|
+
CREATE INDEX IF NOT EXISTS nodes_enrichment_pending ON nodes(json_extract(attrs_json, '$.enrichmentPending'));
|
|
18597
18605
|
`;
|
|
18598
18606
|
FINGERPRINT_INDEXED_LABELS = ["Problem", "Vulnerability", "Solution", "RootCause"];
|
|
18599
18607
|
NODE_LABELS_CHUNK = 256;
|
|
@@ -19194,7 +19202,10 @@ CREATE INDEX IF NOT EXISTS nodes_anchor_hint_path ON nodes(json_extract(attrs_js
|
|
|
19194
19202
|
}));
|
|
19195
19203
|
}
|
|
19196
19204
|
findAllVersionsByLabel(label, attribute) {
|
|
19197
|
-
|
|
19205
|
+
if (attribute && !/^[A-Za-z0-9_]+(\.[A-Za-z0-9_]+)*$/.test(attribute.path)) {
|
|
19206
|
+
throw new Error(`invalid attr path: ${attribute.path}`);
|
|
19207
|
+
}
|
|
19208
|
+
const rows = attribute ? this.db.prepare(`SELECT * FROM nodes WHERE json_extract(attrs_json, '$.${attribute.path}') = ? AND label = ?`).all(attribute.value, label) : this.stmts.findByLabelAll.all(label);
|
|
19198
19209
|
return rows.map(rowToNode);
|
|
19199
19210
|
}
|
|
19200
19211
|
searchNodes(opts) {
|
|
@@ -19219,6 +19230,21 @@ CREATE INDEX IF NOT EXISTS nodes_anchor_hint_path ON nodes(json_extract(attrs_js
|
|
|
19219
19230
|
const rows = this.db.prepare(sql).all(params);
|
|
19220
19231
|
return rows.map(rowToNode);
|
|
19221
19232
|
}
|
|
19233
|
+
searchDescriptionTerms(terms, limit) {
|
|
19234
|
+
if (terms.length === 0) return [];
|
|
19235
|
+
const score2 = terms.map(() => "(description LIKE ?)").join(" + ");
|
|
19236
|
+
const rows = this.db.prepare(
|
|
19237
|
+
`SELECT n.*, h.m AS matched
|
|
19238
|
+
FROM (SELECT rowid AS rid, ${score2} AS m
|
|
19239
|
+
FROM nodes INDEXED BY nodes_live_description
|
|
19240
|
+
WHERE valid_to IS NULL) h
|
|
19241
|
+
JOIN nodes n ON n.rowid = h.rid
|
|
19242
|
+
WHERE h.m > 0
|
|
19243
|
+
ORDER BY h.m DESC, n.page_rank DESC, n.last_updated_at DESC
|
|
19244
|
+
LIMIT ?`
|
|
19245
|
+
).all(...terms.map((t) => `%${t}%`), Math.max(1, limit));
|
|
19246
|
+
return rows.map((r) => ({ node: rowToNode(r), matched: Number(r.matched) }));
|
|
19247
|
+
}
|
|
19222
19248
|
typedEdgeStmt(sql) {
|
|
19223
19249
|
let stmt = this.typedEdgeStmts.get(sql);
|
|
19224
19250
|
if (!stmt) {
|
|
@@ -21681,7 +21707,11 @@ function priorsForFile(store, relPath) {
|
|
|
21681
21707
|
const resolvedProblems = [];
|
|
21682
21708
|
const seenProblem = /* @__PURE__ */ new Set();
|
|
21683
21709
|
const related = /* @__PURE__ */ new Map();
|
|
21684
|
-
|
|
21710
|
+
const anchorEdges = [
|
|
21711
|
+
...store.inEdges(file2.id, ["ANCHORED_AT"]),
|
|
21712
|
+
...store.outEdges(file2.id, ["DEFINES"]).flatMap((d) => store.inEdges(d.to, ["ANCHORED_AT"]))
|
|
21713
|
+
];
|
|
21714
|
+
for (const e of anchorEdges) {
|
|
21685
21715
|
const n = store.getNode(e.from);
|
|
21686
21716
|
if (!n) continue;
|
|
21687
21717
|
if (n.label === "Problem" || n.label === "Vulnerability") {
|
|
@@ -21929,12 +21959,21 @@ function resolveDesignProblemByStatement(store, statement, fixNote, t) {
|
|
|
21929
21959
|
const problemId = identityId({ kind: "DesignProblem", statement });
|
|
21930
21960
|
return closeDesignProblem(store, problemId, fixNote, t);
|
|
21931
21961
|
}
|
|
21962
|
+
function closableByCaller(node2) {
|
|
21963
|
+
if (node2.attrs["source"] !== "cloud") return true;
|
|
21964
|
+
const visibility = node2.attrs["visibility"];
|
|
21965
|
+
return typeof visibility === "string" && MEMBER_VISIBILITIES.has(visibility);
|
|
21966
|
+
}
|
|
21967
|
+
function isVerdictable(node2) {
|
|
21968
|
+
return node2.label === "Problem" && node2.attrs["resolvedAt"] == null && (node2.validTo ?? null) === null && !isConstraintProblem(node2) && node2.attrs["kind"] !== "todo";
|
|
21969
|
+
}
|
|
21932
21970
|
function resolveDesignProblemById(store, id, fixNote, t) {
|
|
21933
21971
|
return closeDesignProblem(store, id.trim(), fixNote, t);
|
|
21934
21972
|
}
|
|
21935
21973
|
function closeDesignProblem(store, problemId, fixNote, t) {
|
|
21936
21974
|
const p = store.getNode(problemId);
|
|
21937
21975
|
if (!p || p.label !== "Problem" && p.label !== "Vulnerability") return false;
|
|
21976
|
+
if (!closableByCaller(p)) return false;
|
|
21938
21977
|
if (p.attrs["resolvedAs"]) return false;
|
|
21939
21978
|
if (fixNote?.trim() && !isFragmentFixNote(fixNote)) {
|
|
21940
21979
|
const sols = store.outEdges(problemId, ["SOLVED_BY"]).map((e) => store.getNode(e.to)).filter((n) => n !== null);
|
|
@@ -22007,6 +22046,7 @@ function retractDesignProblemByStatement(store, statement, resolvedAs, reason, t
|
|
|
22007
22046
|
function retractDesignProblem(store, problemId, resolvedAs, reason, t) {
|
|
22008
22047
|
const p = store.getNode(problemId);
|
|
22009
22048
|
if (!p || p.label !== "Problem" || p.attrs["resolvedAt"]) return false;
|
|
22049
|
+
if (!closableByCaller(p)) return false;
|
|
22010
22050
|
store.updateNode(problemId, {
|
|
22011
22051
|
attrs: {
|
|
22012
22052
|
...p.attrs,
|
|
@@ -22142,7 +22182,7 @@ function priorExposure(store, nodeId, atSeq) {
|
|
|
22142
22182
|
if ((n.attrs["evictedCount"] ?? 0) > 0) return "evicted";
|
|
22143
22183
|
return "unshown";
|
|
22144
22184
|
}
|
|
22145
|
-
var DESIGN_PROMOTE_AT, STATEMENT_ALIAS_CAP, RECURRENCE_DEBOUNCE_MS, PATTERN_DEDUP_COSINE, PATTERN_ALIAS_CAP, DEDUP_STOPWORDS, SAME_ANCHOR_DEDUP_JACCARD, EMPTY_FOLD_REPORT, SAME_ANCHOR_SCOUT_AUTO_COSINE, SAME_ANCHOR_SCOUT_MIN_COSINE, SAME_ANCHOR_NOMINATE_JACCARD, REOPEN_MIN_SOURCES, RECURRENCE_SOURCES_CAP, CITABLE_PRIOR_LABELS, MAX_ANCHOR_FILES, ENCLOSING_SYMBOL_KINDS, ENCLOSING_WALK_DEPTH, FIX_CANDIDATE_ASK_LIMIT;
|
|
22185
|
+
var DESIGN_PROMOTE_AT, STATEMENT_ALIAS_CAP, RECURRENCE_DEBOUNCE_MS, PATTERN_DEDUP_COSINE, PATTERN_ALIAS_CAP, DEDUP_STOPWORDS, SAME_ANCHOR_DEDUP_JACCARD, EMPTY_FOLD_REPORT, SAME_ANCHOR_SCOUT_AUTO_COSINE, SAME_ANCHOR_SCOUT_MIN_COSINE, SAME_ANCHOR_NOMINATE_JACCARD, REOPEN_MIN_SOURCES, RECURRENCE_SOURCES_CAP, CITABLE_PRIOR_LABELS, MAX_ANCHOR_FILES, ENCLOSING_SYMBOL_KINDS, ENCLOSING_WALK_DEPTH, MEMBER_VISIBILITIES, FIX_CANDIDATE_ASK_LIMIT;
|
|
22146
22186
|
var init_design_problem = __esm({
|
|
22147
22187
|
"../../packages/local-graph/src/design-problem.ts"() {
|
|
22148
22188
|
"use strict";
|
|
@@ -22219,6 +22259,7 @@ var init_design_problem = __esm({
|
|
|
22219
22259
|
"Property"
|
|
22220
22260
|
]);
|
|
22221
22261
|
ENCLOSING_WALK_DEPTH = 8;
|
|
22262
|
+
MEMBER_VISIBILITIES = /* @__PURE__ */ new Set(["project", "team", "org"]);
|
|
22222
22263
|
FIX_CANDIDATE_ASK_LIMIT = 5;
|
|
22223
22264
|
}
|
|
22224
22265
|
});
|
|
@@ -25358,6 +25399,7 @@ __export(src_exports2, {
|
|
|
25358
25399
|
clearAnsweredRevisits: () => clearAnsweredRevisits,
|
|
25359
25400
|
clearRevisit: () => clearRevisit,
|
|
25360
25401
|
clearSettledFixCandidates: () => clearSettledFixCandidates,
|
|
25402
|
+
closableByCaller: () => closableByCaller,
|
|
25361
25403
|
communityInductionRequests: () => communityInductionRequests,
|
|
25362
25404
|
communitySeeds: () => communitySeeds,
|
|
25363
25405
|
compareSemver: () => compareSemver,
|
|
@@ -25396,6 +25438,7 @@ __export(src_exports2, {
|
|
|
25396
25438
|
isFragmentFixNote: () => isFragmentFixNote,
|
|
25397
25439
|
isPlaceholderStatement: () => isPlaceholderStatement,
|
|
25398
25440
|
isUnfilledPlaceholder: () => isUnfilledPlaceholder,
|
|
25441
|
+
isVerdictable: () => isVerdictable,
|
|
25399
25442
|
learnedConductance: () => learnedConductance,
|
|
25400
25443
|
linkProblemToLanguages: () => linkProblemToLanguages,
|
|
25401
25444
|
linkProblemToPackages: () => linkProblemToPackages,
|
|
@@ -25923,6 +25966,13 @@ function renderSnapshot(s) {
|
|
|
25923
25966
|
lines.push("_Priors that speak to it \u2014 cite one with its `[handle]` if you lean on it:_");
|
|
25924
25967
|
for (const h of si.hits) {
|
|
25925
25968
|
lines.push(`- **${h.description}** \u2014 \`${h.id}\``);
|
|
25969
|
+
if (h.verdict === "own") {
|
|
25970
|
+
lines.push(
|
|
25971
|
+
` - still open \u2014 did this work settle it? fixed \u2192 (fix:[${h.id}]) \xB7 not a real problem \u2192 an \`errata-resolved\` block with \`id: ` + h.id + "`, `disposition: false_positive`, `reason: \u2026` \xB7 still open \u2192 say nothing"
|
|
25972
|
+
);
|
|
25973
|
+
} else if (h.verdict === "foreign") {
|
|
25974
|
+
lines.push(` - open in another membrane \u2014 not yours to close: (confirm:[${h.id}]) it held \xB7 (refute:[${h.id}] one line why)`);
|
|
25975
|
+
}
|
|
25926
25976
|
}
|
|
25927
25977
|
}
|
|
25928
25978
|
if (si.remoteHits?.length) {
|
|
@@ -27795,8 +27845,8 @@ var init_config = __esm({
|
|
|
27795
27845
|
"use strict";
|
|
27796
27846
|
init_paths();
|
|
27797
27847
|
LEGACY_CLOUD_URL = "https://inerrata-gateway.onrender.com";
|
|
27798
|
-
DEFAULT_CLOUD_URL = true ? "https://inerrata.dev" : DEV_CLOUD_URL;
|
|
27799
|
-
DEFAULT_UPDATE_CHANNEL =
|
|
27848
|
+
DEFAULT_CLOUD_URL = true ? "https://dev.inerrata.dev" : DEV_CLOUD_URL;
|
|
27849
|
+
DEFAULT_UPDATE_CHANNEL = false ? "latest" : "dev";
|
|
27800
27850
|
}
|
|
27801
27851
|
});
|
|
27802
27852
|
|
|
@@ -32524,7 +32574,10 @@ var init_agent_signals = __esm({
|
|
|
32524
32574
|
// lean-on corroboration lived, because only corroboration had an inline path. Grammar
|
|
32525
32575
|
// single-sourced with parseInlineTags' CONFIRM_PREFIX/REFUTE_PREFIX.
|
|
32526
32576
|
confirm: (ref) => `(confirm:[${ref}])`,
|
|
32527
|
-
refute: (ref) => `(refute:[${ref}] one line why)
|
|
32577
|
+
refute: (ref) => `(refute:[${ref}] one line why)`,
|
|
32578
|
+
// RETRACT — "not a real problem": the errata-resolved block with a disposition
|
|
32579
|
+
// (parseDesignResolutions, turn.ts). No inline form exists; the block is the grammar.
|
|
32580
|
+
retract: (id) => ["```errata-resolved", `id: ${id}`, "disposition: false_positive", "reason: why it was never a real problem", "```"].join("\n")
|
|
32528
32581
|
};
|
|
32529
32582
|
TAG_OPENERS = [
|
|
32530
32583
|
...new Set(
|
|
@@ -34414,6 +34467,19 @@ function buildSymbolLexicon(store) {
|
|
|
34414
34467
|
}
|
|
34415
34468
|
return lex;
|
|
34416
34469
|
}
|
|
34470
|
+
function codeGraphKey(store) {
|
|
34471
|
+
const files = store.labelFingerprint?.(["File"]);
|
|
34472
|
+
if (files === void 0) return null;
|
|
34473
|
+
return [files, ...LEXICON_SYMBOL_LABELS.map((l) => store.countNodesByLabel(l))].join("|");
|
|
34474
|
+
}
|
|
34475
|
+
function lexiconsFor(store) {
|
|
34476
|
+
const key = codeGraphKey(store);
|
|
34477
|
+
const hit = key !== null ? lexiconCache.get(store) : void 0;
|
|
34478
|
+
if (hit && hit.key === key) return hit;
|
|
34479
|
+
const built = { symbols: buildSymbolLexicon(store), files: buildFileLexicon(store) };
|
|
34480
|
+
if (key !== null) lexiconCache.set(store, { key, ...built });
|
|
34481
|
+
return built;
|
|
34482
|
+
}
|
|
34417
34483
|
function replaceWithPhrase(text, before, escapedKey, after, phrase2) {
|
|
34418
34484
|
if (!RE_LEADING_ARTICLE2.test(phrase2)) {
|
|
34419
34485
|
return text.replace(new RegExp(`${before}${escapedKey}${after}`, "g"), phrase2);
|
|
@@ -34579,7 +34645,7 @@ function registerRenderedContributorLabels(labels) {
|
|
|
34579
34645
|
if (trimmed.length >= 3) renderedContributorLabels.add(trimmed);
|
|
34580
34646
|
}
|
|
34581
34647
|
}
|
|
34582
|
-
var SYMBOL_LABELS2, KIND_PHRASE, BUILTIN_TYPE_NAMES, DOC_COMMENT_KINDS, MAX_DOC_CHARS, RE_RESERVED2, escapeRe4, RE_LEADING_ARTICLE2, CONVENTION_FILENAMES, CONVENTION_BASENAME_MIN_FILES, CONVENTION_STEMS, FILE_TOKEN_RE, STEM_TOKEN_RE, SOURCE_EXTENSIONS, TOKEN_RE3, renderedContributorLabels;
|
|
34648
|
+
var SYMBOL_LABELS2, KIND_PHRASE, BUILTIN_TYPE_NAMES, DOC_COMMENT_KINDS, MAX_DOC_CHARS, LEXICON_SYMBOL_LABELS, lexiconCache, RE_RESERVED2, escapeRe4, RE_LEADING_ARTICLE2, CONVENTION_FILENAMES, CONVENTION_BASENAME_MIN_FILES, CONVENTION_STEMS, FILE_TOKEN_RE, STEM_TOKEN_RE, SOURCE_EXTENSIONS, TOKEN_RE3, renderedContributorLabels;
|
|
34583
34649
|
var init_generalize_graph = __esm({
|
|
34584
34650
|
"src/generalize-graph.ts"() {
|
|
34585
34651
|
"use strict";
|
|
@@ -34629,6 +34695,8 @@ var init_generalize_graph = __esm({
|
|
|
34629
34695
|
]);
|
|
34630
34696
|
DOC_COMMENT_KINDS = /* @__PURE__ */ new Set(["docstring", "explanation"]);
|
|
34631
34697
|
MAX_DOC_CHARS = 600;
|
|
34698
|
+
LEXICON_SYMBOL_LABELS = ["Function", "Method", "Class", "Interface", "Const", "Comment"];
|
|
34699
|
+
lexiconCache = /* @__PURE__ */ new WeakMap();
|
|
34632
34700
|
RE_RESERVED2 = /[.*+?^${}()|[\]\\]/g;
|
|
34633
34701
|
escapeRe4 = (s) => s.replace(RE_RESERVED2, "\\$&");
|
|
34634
34702
|
RE_LEADING_ARTICLE2 = /^(?:an?|the)\s/i;
|
|
@@ -34831,19 +34899,18 @@ function isQuotaError(err2) {
|
|
|
34831
34899
|
return status === 403 && /quota/i.test(msg);
|
|
34832
34900
|
}
|
|
34833
34901
|
async function dualAugment(opts) {
|
|
34834
|
-
const { store, cloud, localHits, seedNode, queryText, limit } = opts;
|
|
34902
|
+
const { store, cloud, localHits, seedNode, queryText, limit, generalizeSoft } = opts;
|
|
34835
34903
|
const cloudHits = [];
|
|
34836
34904
|
let seedUsageCount;
|
|
34837
34905
|
let seedProvenance = "local";
|
|
34838
34906
|
const calls = [];
|
|
34839
34907
|
try {
|
|
34840
|
-
const lexicon = buildSymbolLexicon(store);
|
|
34841
34908
|
const hardIds = [.../* @__PURE__ */ new Set([...seedNode ? [seedNode.id] : [], ...localHits.map((h) => h.id)])].filter((nid) => {
|
|
34842
34909
|
const n = seedNode && nid === seedNode.id ? seedNode : store.getNode(nid);
|
|
34843
34910
|
return !!n && isHardBridge(n);
|
|
34844
34911
|
});
|
|
34845
34912
|
const softText = queryText ?? (seedNode && BRIDGE_LABELS.has(seedNode.label) ? seedNode.description : void 0);
|
|
34846
|
-
const softQ = softText ? generalizeSymbols(softText,
|
|
34913
|
+
const softQ = softText ? await generalizeSoft?.(softText) ?? generalizeSymbols(softText, lexiconsFor(store).symbols, 1) : void 0;
|
|
34847
34914
|
if (seedNode?.label === "Package") {
|
|
34848
34915
|
calls.push(
|
|
34849
34916
|
cloud.packages(seedNode.id).then((pn) => {
|
|
@@ -34955,6 +35022,50 @@ var init_dual_augment = __esm({
|
|
|
34955
35022
|
}
|
|
34956
35023
|
});
|
|
34957
35024
|
|
|
35025
|
+
// src/daemon-generalize-client.ts
|
|
35026
|
+
function daemonLoopbackUrl(lockPath = globalDaemonLock()) {
|
|
35027
|
+
const url2 = readDaemonLock(lockPath)?.webUiUrl;
|
|
35028
|
+
if (!url2) return null;
|
|
35029
|
+
try {
|
|
35030
|
+
const u = new URL(url2);
|
|
35031
|
+
const isLoopback = u.hostname === "127.0.0.1" || u.hostname === "localhost" || u.hostname === "[::1]";
|
|
35032
|
+
return u.protocol === "http:" && isLoopback ? u.origin : null;
|
|
35033
|
+
} catch {
|
|
35034
|
+
return null;
|
|
35035
|
+
}
|
|
35036
|
+
}
|
|
35037
|
+
function daemonGeneralizer(workspaceRoot, opts = {}) {
|
|
35038
|
+
const baseUrl = opts.baseUrl ?? (() => daemonLoopbackUrl());
|
|
35039
|
+
const fetchFn = opts.fetchFn ?? fetch;
|
|
35040
|
+
const timeoutMs = opts.timeoutMs ?? DAEMON_GENERALIZE_TIMEOUT_MS;
|
|
35041
|
+
return async (text) => {
|
|
35042
|
+
try {
|
|
35043
|
+
const base = baseUrl();
|
|
35044
|
+
if (!base) return void 0;
|
|
35045
|
+
const res = await fetchFn(`${base}/api/generalize`, {
|
|
35046
|
+
method: "POST",
|
|
35047
|
+
headers: { "content-type": "application/json" },
|
|
35048
|
+
body: JSON.stringify({ workspaceRoot, text }),
|
|
35049
|
+
signal: AbortSignal.timeout(timeoutMs)
|
|
35050
|
+
});
|
|
35051
|
+
if (!res.ok) return void 0;
|
|
35052
|
+
const body2 = await res.json();
|
|
35053
|
+
return typeof body2.generalized === "string" ? body2.generalized : void 0;
|
|
35054
|
+
} catch {
|
|
35055
|
+
return void 0;
|
|
35056
|
+
}
|
|
35057
|
+
};
|
|
35058
|
+
}
|
|
35059
|
+
var DAEMON_GENERALIZE_TIMEOUT_MS;
|
|
35060
|
+
var init_daemon_generalize_client = __esm({
|
|
35061
|
+
"src/daemon-generalize-client.ts"() {
|
|
35062
|
+
"use strict";
|
|
35063
|
+
init_lock();
|
|
35064
|
+
init_paths();
|
|
35065
|
+
DAEMON_GENERALIZE_TIMEOUT_MS = 3e3;
|
|
35066
|
+
}
|
|
35067
|
+
});
|
|
35068
|
+
|
|
34958
35069
|
// ../../packages/indexer/src/parse-cache.ts
|
|
34959
35070
|
import { createHash as createHash6 } from "node:crypto";
|
|
34960
35071
|
import { existsSync as existsSync15, mkdirSync as mkdirSync6, readFileSync as readFileSync14, readdirSync as readdirSync2, rmSync as rmSync2, statSync as statSync2, writeFileSync as writeFileSync11 } from "node:fs";
|
|
@@ -43897,7 +44008,7 @@ __export(mcp_exports, {
|
|
|
43897
44008
|
import { existsSync as existsSync17, readFileSync as readFileSync16 } from "node:fs";
|
|
43898
44009
|
import { resolve as resolve4 } from "node:path";
|
|
43899
44010
|
function collectEnrichmentPulls(store, opts = {}) {
|
|
43900
|
-
const pending = store.
|
|
44011
|
+
const pending = store.findLiveNodesByAttr("Solution", "enrichmentPending", 1).filter((s) => s.attrs["enrichmentPending"] === true).filter((s) => !opts.onlyUnsurfaced || s.attrs["enrichmentSurfaced"] !== true);
|
|
43901
44012
|
const byEpisode = /* @__PURE__ */ new Map();
|
|
43902
44013
|
for (const s of pending) {
|
|
43903
44014
|
const ep = s.attrs["episodeId"] ?? s.id;
|
|
@@ -44014,20 +44125,8 @@ function searchGraph(store, query, limit) {
|
|
|
44014
44125
|
const terms = q.split(/\s+/).filter((t) => t.length >= 2);
|
|
44015
44126
|
const pool = Math.max(limit * 3, 60);
|
|
44016
44127
|
const raw2 = /* @__PURE__ */ new Map();
|
|
44017
|
-
const
|
|
44018
|
-
|
|
44019
|
-
const e = raw2.get(n.id);
|
|
44020
|
-
if (e) e.matched++;
|
|
44021
|
-
else raw2.set(n.id, { node: n, matched: 1 });
|
|
44022
|
-
}
|
|
44023
|
-
};
|
|
44024
|
-
if (terms.length <= 1) {
|
|
44025
|
-
collect2(store.searchNodes({ descriptionLike: q, limit: pool }));
|
|
44026
|
-
} else {
|
|
44027
|
-
for (const term of terms) {
|
|
44028
|
-
collect2(store.searchNodes({ descriptionLike: term, limit: 200 }));
|
|
44029
|
-
}
|
|
44030
|
-
}
|
|
44128
|
+
const scored = !q ? store.searchNodes({ limit: pool }).map((node2) => ({ node: node2, matched: 1 })) : terms.length <= 1 ? store.searchDescriptionTerms([q], pool) : store.searchDescriptionTerms(terms, Math.max(pool, 600));
|
|
44129
|
+
for (const hit of scored) raw2.set(hit.node.id, hit);
|
|
44031
44130
|
const merged = /* @__PURE__ */ new Map();
|
|
44032
44131
|
const add = (node2, matched, via) => {
|
|
44033
44132
|
const e = merged.get(node2.id);
|
|
@@ -44327,6 +44426,7 @@ async function runMcpServer(workspaceRoot, options = {}) {
|
|
|
44327
44426
|
const store = openGraphStore({ path: paths.castalia });
|
|
44328
44427
|
const handle2 = createMcpHandler(store, {
|
|
44329
44428
|
...buildToolContext(workspaceRoot),
|
|
44429
|
+
generalizeSoft: daemonGeneralizer(workspaceRoot),
|
|
44330
44430
|
sharedDbPath: sharedStorePath()
|
|
44331
44431
|
}, options);
|
|
44332
44432
|
process.stdin.setEncoding("utf8");
|
|
@@ -44372,6 +44472,7 @@ var init_mcp = __esm({
|
|
|
44372
44472
|
init_src8();
|
|
44373
44473
|
init_dual_burst();
|
|
44374
44474
|
init_paths();
|
|
44475
|
+
init_daemon_generalize_client();
|
|
44375
44476
|
init_webui();
|
|
44376
44477
|
init_reconcile();
|
|
44377
44478
|
init_agent_signals();
|
|
@@ -44447,6 +44548,7 @@ var init_mcp = __esm({
|
|
|
44447
44548
|
const dual = await dualAugment({
|
|
44448
44549
|
store,
|
|
44449
44550
|
cloud: ctx.cloud,
|
|
44551
|
+
...ctx.generalizeSoft ? { generalizeSoft: ctx.generalizeSoft } : {},
|
|
44450
44552
|
localHits,
|
|
44451
44553
|
queryText: query,
|
|
44452
44554
|
limit
|
|
@@ -44626,6 +44728,7 @@ var init_mcp = __esm({
|
|
|
44626
44728
|
const dual = await dualAugment({
|
|
44627
44729
|
store,
|
|
44628
44730
|
cloud: ctx.cloud,
|
|
44731
|
+
...ctx.generalizeSoft ? { generalizeSoft: ctx.generalizeSoft } : {},
|
|
44629
44732
|
localHits,
|
|
44630
44733
|
seedNode,
|
|
44631
44734
|
limit: args2["limit"] != null ? Number(args2["limit"]) : 20
|
|
@@ -45197,6 +45300,7 @@ var init_mcp = __esm({
|
|
|
45197
45300
|
const dual = await dualAugment({
|
|
45198
45301
|
store,
|
|
45199
45302
|
cloud: ctx.cloud,
|
|
45303
|
+
...ctx.generalizeSoft ? { generalizeSoft: ctx.generalizeSoft } : {},
|
|
45200
45304
|
localHits,
|
|
45201
45305
|
seedNode: node2,
|
|
45202
45306
|
limit
|
|
@@ -53491,6 +53595,7 @@ __export(webui_exports, {
|
|
|
53491
53595
|
asFailedPostToolUse: () => asFailedPostToolUse,
|
|
53492
53596
|
buildWebUi: () => buildWebUi,
|
|
53493
53597
|
decideContextInject: () => decideContextInject,
|
|
53598
|
+
editVerdict: () => editVerdict,
|
|
53494
53599
|
errorRecallTerms: () => errorRecallTerms,
|
|
53495
53600
|
fileRecall: () => fileRecall,
|
|
53496
53601
|
fileUriToFsPath: () => fileUriToFsPath,
|
|
@@ -53601,6 +53706,26 @@ ${lines.join("\n")}
|
|
|
53601
53706
|
card
|
|
53602
53707
|
};
|
|
53603
53708
|
}
|
|
53709
|
+
function editVerdict(store, relPath) {
|
|
53710
|
+
const priors = priorsForFile(store, relPath);
|
|
53711
|
+
if (!priors || priors.openProblems.length === 0) return null;
|
|
53712
|
+
const ranked = rankByRelevance(store, priors.file.id, priors.openProblems.map((n) => n.id));
|
|
53713
|
+
const rank = new Map(ranked.map((r, i2) => [r.id, i2]));
|
|
53714
|
+
const asked = [...priors.openProblems].sort((a, b) => (rank.get(a.id) ?? 0) - (rank.get(b.id) ?? 0)).slice(0, 3);
|
|
53715
|
+
const lines = asked.map(
|
|
53716
|
+
(p) => closableByCaller(p) ? recallLine(p, `fixed: ${TAG_EXAMPLE.fix(p.id)} \xB7 not a real problem: retract it (block below) \xB7 still open: say nothing`) : recallLine(p, `another membrane's \u2014 not yours to close: ${TAG_EXAMPLE.confirm(p.id)} it held \xB7 ${TAG_EXAMPLE.refute(p.id)}`)
|
|
53717
|
+
);
|
|
53718
|
+
const retractable = asked.find((p) => closableByCaller(p));
|
|
53719
|
+
const shown = asked.map(deliveredPrior);
|
|
53720
|
+
const headline = `verdict asked: ${plural(shown.length, "open problem")} on ${relPath.split("/").slice(-2).join("/")}`;
|
|
53721
|
+
return {
|
|
53722
|
+
context: `errata \u2014 you just edited ${relPath}; ${plural(priors.openProblems.length, "open problem")} recorded on it. Give each one below a verdict in your reply:
|
|
53723
|
+
${lines.join("\n")}
|
|
53724
|
+
` + (retractable ? `Retract (not a real problem):
|
|
53725
|
+
${TAG_EXAMPLE.retract(retractable.id)}` : ""),
|
|
53726
|
+
card: deliveredCard(store, headline, shown) ?? { headline, items: [] }
|
|
53727
|
+
};
|
|
53728
|
+
}
|
|
53604
53729
|
function workspacePackageIds(store) {
|
|
53605
53730
|
const hit = wsPackagesCache.get(store);
|
|
53606
53731
|
const now = Date.now();
|
|
@@ -53671,6 +53796,7 @@ function buildWebUi(deps) {
|
|
|
53671
53796
|
const B = deps.basePath ?? "";
|
|
53672
53797
|
const lastInjectedHash = /* @__PURE__ */ new Map();
|
|
53673
53798
|
const fileRecallSeen = /* @__PURE__ */ new Map();
|
|
53799
|
+
const editVerdictAsked = /* @__PURE__ */ new Map();
|
|
53674
53800
|
const lastSubagentInjectAt = /* @__PURE__ */ new Map();
|
|
53675
53801
|
const noteSessionFrom = (headerOrigin, body2, ended = false) => {
|
|
53676
53802
|
const { sessionId, agentId } = hookAudience(body2);
|
|
@@ -53935,6 +54061,27 @@ function buildWebUi(deps) {
|
|
|
53935
54061
|
}
|
|
53936
54062
|
}
|
|
53937
54063
|
}
|
|
54064
|
+
if (event === "PostToolUse" && !recall && rel && (tool === "Edit" || tool === "MultiEdit" || tool === "Write" || tool === "NotebookEdit")) {
|
|
54065
|
+
const sid = hookAudience(body2).contextKey;
|
|
54066
|
+
let asked = editVerdictAsked.get(sid);
|
|
54067
|
+
if (!asked) {
|
|
54068
|
+
asked = /* @__PURE__ */ new Set();
|
|
54069
|
+
editVerdictAsked.set(sid, asked);
|
|
54070
|
+
}
|
|
54071
|
+
if (!asked.has(rel)) {
|
|
54072
|
+
const verdict = editVerdict(deps.store, rel);
|
|
54073
|
+
if (verdict) {
|
|
54074
|
+
asked.add(rel);
|
|
54075
|
+
let seen = fileRecallSeen.get(sid);
|
|
54076
|
+
if (!seen) {
|
|
54077
|
+
seen = /* @__PURE__ */ new Set();
|
|
54078
|
+
fileRecallSeen.set(sid, seen);
|
|
54079
|
+
}
|
|
54080
|
+
seen.add(rel);
|
|
54081
|
+
recall = verdict;
|
|
54082
|
+
}
|
|
54083
|
+
}
|
|
54084
|
+
}
|
|
53938
54085
|
if (event === "PostToolUse" && !recall && rel && (tool === "Read" || tool === "Edit" || tool === "Write" || tool === "NotebookEdit")) {
|
|
53939
54086
|
const sid = hookAudience(body2).contextKey;
|
|
53940
54087
|
let seen = fileRecallSeen.get(sid);
|
|
@@ -82706,7 +82853,7 @@ function createWatchBreaker(deps) {
|
|
|
82706
82853
|
}
|
|
82707
82854
|
|
|
82708
82855
|
// src/engine.ts
|
|
82709
|
-
var DAEMON_VERSION = true ? "2.0.
|
|
82856
|
+
var DAEMON_VERSION = true ? "2.0.21-dev.3716" : "2.0.0-alpha.0";
|
|
82710
82857
|
var IGNORED_PATH = /[\\/](?:\.git|node_modules|\.errata|\.claude|\.codex|\.cursor|\.turbo|\.next|dist|coverage|test-results|playwright-report|__pycache__)(?:[\\/]|$)/;
|
|
82711
82858
|
var IGNORED_NOISE = /castalia\.db|eventlog\.sqlite|turn-cursors/;
|
|
82712
82859
|
var GIT_OP_MUTE_MS = 4e3;
|
|
@@ -83167,7 +83314,8 @@ function createWorkspaceEngine(opts) {
|
|
|
83167
83314
|
id: h.node.id,
|
|
83168
83315
|
label: h.node.label,
|
|
83169
83316
|
description: h.node.description,
|
|
83170
|
-
coverage: h.coverage
|
|
83317
|
+
coverage: h.coverage,
|
|
83318
|
+
...isVerdictable(h.node) ? { verdict: closableByCaller(h.node) ? "own" : "foreign" } : {}
|
|
83171
83319
|
})),
|
|
83172
83320
|
...collective && collective.hits.length > 0 ? {
|
|
83173
83321
|
remoteHits: collective.hits.slice(0, 4).map((n) => ({ id: n.id, label: n.label, description: n.description }))
|
|
@@ -84176,7 +84324,11 @@ function createWorkspaceEngine(opts) {
|
|
|
84176
84324
|
const closed = resolveDesignProblemById(store, pid, fx.fixNote, t);
|
|
84177
84325
|
if (!closed) {
|
|
84178
84326
|
const n = store.getNode(pid);
|
|
84179
|
-
if (
|
|
84327
|
+
if (n && n.attrs["resolvedAt"] == null && !closableByCaller(n)) {
|
|
84328
|
+
console.warn(
|
|
84329
|
+
`[errata] fix tag not applied: ${pid.slice(0, 32)} belongs to a membrane you are not in \u2014 a verdict closes only your own problems; cite it with (confirm:[id]) or (refute:[id] why) instead`
|
|
84330
|
+
);
|
|
84331
|
+
} else if (!n || n.label !== "Problem" && n.label !== "Vulnerability" || n.attrs["resolvedAs"] != null) {
|
|
84180
84332
|
console.warn(
|
|
84181
84333
|
`[errata] fix tag went nowhere: ${!n ? `id ${pid.slice(0, 32)} unknown` : n.label !== "Problem" ? `${pid.slice(0, 32)} is a ${n.label}, not a Problem` : "the problem was retracted \u2014 a retraction is not fixable"} \u2014 the fix was NOT recorded`
|
|
84182
84334
|
);
|
|
@@ -86732,21 +86884,7 @@ function liveBackoffMs(failures) {
|
|
|
86732
86884
|
|
|
86733
86885
|
// src/drain-build.ts
|
|
86734
86886
|
init_generalize_graph();
|
|
86735
|
-
|
|
86736
|
-
function codeGraphKey(store) {
|
|
86737
|
-
const files = store.labelFingerprint?.(["File"]);
|
|
86738
|
-
if (files === void 0) return null;
|
|
86739
|
-
return [files, ...LEXICON_SYMBOL_LABELS.map((l) => store.countNodesByLabel(l))].join("|");
|
|
86740
|
-
}
|
|
86741
|
-
var lexiconCache = /* @__PURE__ */ new WeakMap();
|
|
86742
|
-
function lexiconsFor(store) {
|
|
86743
|
-
const key = codeGraphKey(store);
|
|
86744
|
-
const hit = key !== null ? lexiconCache.get(store) : void 0;
|
|
86745
|
-
if (hit && hit.key === key) return hit;
|
|
86746
|
-
const built = { symbols: buildSymbolLexicon(store), files: buildFileLexicon(store) };
|
|
86747
|
-
if (key !== null) lexiconCache.set(store, { key, ...built });
|
|
86748
|
-
return built;
|
|
86749
|
-
}
|
|
86887
|
+
init_generalize_graph();
|
|
86750
86888
|
function buildDrainContext(store, input) {
|
|
86751
86889
|
return buildContextIngest(store, input.profile, input.daemonVersion, input.ignorePatterns, {
|
|
86752
86890
|
...input.includePackages !== void 0 ? { includePackages: input.includePackages } : {}
|
|
@@ -86881,6 +87019,98 @@ var ConsolidateWorker = class {
|
|
|
86881
87019
|
}
|
|
86882
87020
|
};
|
|
86883
87021
|
|
|
87022
|
+
// src/generalize-worker-client.ts
|
|
87023
|
+
import { Worker as Worker3 } from "node:worker_threads";
|
|
87024
|
+
var GENERALIZE_WORKER_IDLE_MS = 30 * 6e4;
|
|
87025
|
+
var GeneralizeWorker = class {
|
|
87026
|
+
worker = null;
|
|
87027
|
+
ready = null;
|
|
87028
|
+
pending = /* @__PURE__ */ new Map();
|
|
87029
|
+
nextId = 1;
|
|
87030
|
+
stopped = false;
|
|
87031
|
+
idleTimer = null;
|
|
87032
|
+
async generalize(castaliaPath, text) {
|
|
87033
|
+
if (this.stopped) throw new Error("generalize worker stopped");
|
|
87034
|
+
this.ensureWorker();
|
|
87035
|
+
this.touch();
|
|
87036
|
+
await this.ready;
|
|
87037
|
+
const id = this.nextId++;
|
|
87038
|
+
return new Promise((resolve10, reject) => {
|
|
87039
|
+
this.pending.set(id, { resolve: resolve10, reject });
|
|
87040
|
+
const req = { id, castaliaPath, text };
|
|
87041
|
+
this.worker.postMessage(req);
|
|
87042
|
+
});
|
|
87043
|
+
}
|
|
87044
|
+
touch() {
|
|
87045
|
+
if (this.idleTimer) clearTimeout(this.idleTimer);
|
|
87046
|
+
this.idleTimer = setTimeout(() => {
|
|
87047
|
+
if (this.pending.size > 0) return this.touch();
|
|
87048
|
+
const w = this.worker;
|
|
87049
|
+
this.worker = null;
|
|
87050
|
+
this.ready = null;
|
|
87051
|
+
void w?.terminate().catch(() => {
|
|
87052
|
+
});
|
|
87053
|
+
}, GENERALIZE_WORKER_IDLE_MS);
|
|
87054
|
+
this.idleTimer.unref?.();
|
|
87055
|
+
}
|
|
87056
|
+
ensureWorker() {
|
|
87057
|
+
if (this.worker) return;
|
|
87058
|
+
const worker = new Worker3(new URL("./generalize-worker.mjs", import.meta.url), {
|
|
87059
|
+
workerData: {},
|
|
87060
|
+
execArgv: process.execArgv,
|
|
87061
|
+
resourceLimits: workerResourceLimits()
|
|
87062
|
+
});
|
|
87063
|
+
noteIsolateSpawned();
|
|
87064
|
+
this.worker = worker;
|
|
87065
|
+
this.ready = new Promise((resolve10, reject) => {
|
|
87066
|
+
const onReady = (m) => {
|
|
87067
|
+
if (m?.ready) {
|
|
87068
|
+
worker.off("message", onReady);
|
|
87069
|
+
worker.off("error", onErr);
|
|
87070
|
+
resolve10();
|
|
87071
|
+
}
|
|
87072
|
+
};
|
|
87073
|
+
const onErr = (err2) => {
|
|
87074
|
+
worker.off("message", onReady);
|
|
87075
|
+
reject(err2);
|
|
87076
|
+
};
|
|
87077
|
+
worker.once("error", onErr);
|
|
87078
|
+
worker.on("message", onReady);
|
|
87079
|
+
});
|
|
87080
|
+
worker.on("message", (m) => {
|
|
87081
|
+
if (typeof m?.id !== "number") return;
|
|
87082
|
+
const p = this.pending.get(m.id);
|
|
87083
|
+
if (!p) return;
|
|
87084
|
+
this.pending.delete(m.id);
|
|
87085
|
+
if (m.ok && typeof m.result === "string") p.resolve(m.result);
|
|
87086
|
+
else p.reject(new Error(m.error ?? "generalization failed"));
|
|
87087
|
+
});
|
|
87088
|
+
const onGone = (err2) => {
|
|
87089
|
+
const e = err2 instanceof Error ? err2 : new Error("generalize worker exited");
|
|
87090
|
+
for (const [, p] of this.pending) p.reject(e);
|
|
87091
|
+
this.pending.clear();
|
|
87092
|
+
if (this.worker === worker) {
|
|
87093
|
+
this.worker = null;
|
|
87094
|
+
this.ready = null;
|
|
87095
|
+
}
|
|
87096
|
+
};
|
|
87097
|
+
worker.on("error", onGone);
|
|
87098
|
+
worker.on("exit", () => {
|
|
87099
|
+
noteIsolateExited();
|
|
87100
|
+
onGone();
|
|
87101
|
+
});
|
|
87102
|
+
}
|
|
87103
|
+
async stop() {
|
|
87104
|
+
this.stopped = true;
|
|
87105
|
+
if (this.idleTimer) clearTimeout(this.idleTimer);
|
|
87106
|
+
if (this.worker) {
|
|
87107
|
+
await this.worker.terminate().catch(() => {
|
|
87108
|
+
});
|
|
87109
|
+
this.worker = null;
|
|
87110
|
+
}
|
|
87111
|
+
}
|
|
87112
|
+
};
|
|
87113
|
+
|
|
86884
87114
|
// src/prepare-store.ts
|
|
86885
87115
|
init_src5();
|
|
86886
87116
|
async function prepareStore(path2, root, workspaceId2) {
|
|
@@ -89836,6 +90066,7 @@ async function startMultiDaemon(opts = {}) {
|
|
|
89836
90066
|
const sharedStore = openGraphStore({ path: sharedDbPath });
|
|
89837
90067
|
const bundledRun = import.meta.url.endsWith(".mjs");
|
|
89838
90068
|
const consolidateWorker = bundledRun && sharedDbPath !== ":memory:" ? new ConsolidateWorker() : null;
|
|
90069
|
+
const generalizeWorker = bundledRun ? new GeneralizeWorker() : null;
|
|
89839
90070
|
startLoopLagMonitor();
|
|
89840
90071
|
let baseUrl = "";
|
|
89841
90072
|
const records = [];
|
|
@@ -91523,6 +91754,7 @@ async function startMultiDaemon(opts = {}) {
|
|
|
91523
91754
|
await Promise.allSettled(attaching.values());
|
|
91524
91755
|
for (const r of records) await r.engine.stop();
|
|
91525
91756
|
if (consolidateWorker) await consolidateWorker.stop();
|
|
91757
|
+
if (generalizeWorker) await generalizeWorker.stop();
|
|
91526
91758
|
try {
|
|
91527
91759
|
sharedStore.close();
|
|
91528
91760
|
} catch {
|
|
@@ -91551,6 +91783,33 @@ async function startMultiDaemon(opts = {}) {
|
|
|
91551
91783
|
}
|
|
91552
91784
|
});
|
|
91553
91785
|
app.get("/api/sync", (c) => c.json(flushSnapshot()));
|
|
91786
|
+
const GENERALIZE_MAX_CHARS = 8e3;
|
|
91787
|
+
app.post("/api/generalize", async (c) => {
|
|
91788
|
+
const host = (c.req.header("host") ?? "").toLowerCase();
|
|
91789
|
+
if (!/^(127\.0\.0\.1|localhost|\[::1\])(:\d+)?$/.test(host)) return c.json({ error: "forbidden" }, 403);
|
|
91790
|
+
if (!(c.req.header("content-type") ?? "").toLowerCase().startsWith("application/json")) {
|
|
91791
|
+
return c.json({ error: "json required" }, 415);
|
|
91792
|
+
}
|
|
91793
|
+
let body2;
|
|
91794
|
+
try {
|
|
91795
|
+
body2 = await c.req.json();
|
|
91796
|
+
} catch {
|
|
91797
|
+
return c.json({ error: "bad json" }, 400);
|
|
91798
|
+
}
|
|
91799
|
+
if (typeof body2.workspaceRoot !== "string" || typeof body2.text !== "string") {
|
|
91800
|
+
return c.json({ error: "workspaceRoot and text required" }, 400);
|
|
91801
|
+
}
|
|
91802
|
+
if (body2.text.length > GENERALIZE_MAX_CHARS) return c.json({ error: "text too long" }, 413);
|
|
91803
|
+
const entry = workspaceEntryForPath(pathResolve(body2.workspaceRoot));
|
|
91804
|
+
if (!entry) return c.json({ error: "unregistered workspace" }, 404);
|
|
91805
|
+
if (!generalizeWorker) return c.json({ error: "no worker" }, 503);
|
|
91806
|
+
try {
|
|
91807
|
+
const generalized = await generalizeWorker.generalize(workspacePaths(entry.path).castalia, body2.text);
|
|
91808
|
+
return c.json({ generalized });
|
|
91809
|
+
} catch (err2) {
|
|
91810
|
+
return c.json({ error: err2 instanceof Error ? err2.message : String(err2) }, 503);
|
|
91811
|
+
}
|
|
91812
|
+
});
|
|
91554
91813
|
app.post("/api/tick", async (c) => {
|
|
91555
91814
|
try {
|
|
91556
91815
|
return c.json({ reports: Object.fromEntries(await daemon.tickAll()) });
|
|
@@ -94099,7 +94358,7 @@ async function cmdInit() {
|
|
|
94099
94358
|
}
|
|
94100
94359
|
console.log("");
|
|
94101
94360
|
console.log("connecting your cloud account...");
|
|
94102
|
-
await cmdLoginOAuth(cfgNow);
|
|
94361
|
+
await cmdLoginOAuth(cfgNow, false, false);
|
|
94103
94362
|
cfgNow = loadConfig();
|
|
94104
94363
|
if (!hasCloudCredential(cfgNow)) {
|
|
94105
94364
|
console.error("init requires login \u2014 re-run `errata init`, or `errata init --no-login` to stay unlinked.");
|
|
@@ -94742,7 +95001,7 @@ async function warnIfApprovalUnreachable(url2) {
|
|
|
94742
95001
|
} catch {
|
|
94743
95002
|
}
|
|
94744
95003
|
}
|
|
94745
|
-
async function cmdLoginOAuth(cfg, useBrowserLoopback = false) {
|
|
95004
|
+
async function cmdLoginOAuth(cfg, useBrowserLoopback = false, linkAfterLogin = true) {
|
|
94746
95005
|
const inspection = await inspectCloudEndpoint(cfg.cloudUrl);
|
|
94747
95006
|
try {
|
|
94748
95007
|
assertCloudEndpointAllowed(cfg.cloudUrl, inspection);
|
|
@@ -94779,7 +95038,7 @@ async function cmdLoginOAuth(cfg, useBrowserLoopback = false) {
|
|
|
94779
95038
|
nextCfg.email = me.handle;
|
|
94780
95039
|
saveConfig(nextCfg);
|
|
94781
95040
|
console.log(`logged in as ${me.handle} (${me.tier}) \u2014 tokens stored at ${globalConfigPath()}`);
|
|
94782
|
-
await linkRegisteredWorkspaces(nextCfg);
|
|
95041
|
+
if (linkAfterLogin) await linkRegisteredWorkspaces(nextCfg);
|
|
94783
95042
|
} catch (err2) {
|
|
94784
95043
|
console.error(`signed in, but identity check failed: ${err2 instanceof Error ? err2.message : err2}`);
|
|
94785
95044
|
console.error(` credentials were not stored; try again or paste a key: errata login --token <key>`);
|