@inerrata-corporation/errata 2.0.1-dev.15 → 2.0.1-dev.52
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/errata.mjs +743 -100
- package/package.json +1 -1
package/errata.mjs
CHANGED
|
@@ -21786,11 +21786,24 @@ var init_client = __esm({
|
|
|
21786
21786
|
async actableAgents() {
|
|
21787
21787
|
return this.json("GET", "/api/gate/actable-agents");
|
|
21788
21788
|
}
|
|
21789
|
+
/** Resolve the exact installation and credential context used by this client. */
|
|
21790
|
+
async context() {
|
|
21791
|
+
return this.json("GET", "/api/gate/context");
|
|
21792
|
+
}
|
|
21793
|
+
/** Resolve context after an audited, sessionless act-as re-mint. Useful for
|
|
21794
|
+
* CLI diagnostics that must report the same identity a selected agent uses. */
|
|
21795
|
+
async contextAs(handle2, teamId) {
|
|
21796
|
+
const acting = await this.actAs(handle2, teamId);
|
|
21797
|
+
return this.json("GET", "/api/gate/context", void 0, { bearerToken: acting.token });
|
|
21798
|
+
}
|
|
21789
21799
|
/** Mint a fresh gateway JWT stamped with `handle` (authz-checked + audited
|
|
21790
21800
|
* server-side). The returned `token` bears the acting-agent identity for
|
|
21791
21801
|
* subsequent cloud calls. Authenticated with the caller's base JWT. */
|
|
21792
|
-
async actAs(handle2) {
|
|
21793
|
-
return this.json("POST", "/api/gate/act-as", {
|
|
21802
|
+
async actAs(handle2, teamId) {
|
|
21803
|
+
return this.json("POST", "/api/gate/act-as", {
|
|
21804
|
+
handle: handle2,
|
|
21805
|
+
...teamId !== void 0 ? { teamId } : {}
|
|
21806
|
+
});
|
|
21794
21807
|
}
|
|
21795
21808
|
/** Resolve-or-create the org's project for a repo locator (ambient link,
|
|
21796
21809
|
* PJ-entity). Requires user identity on the JWT — login-gated like actAs. */
|
|
@@ -21838,8 +21851,14 @@ var init_client = __esm({
|
|
|
21838
21851
|
* the per-decision response into flush accounting.
|
|
21839
21852
|
*
|
|
21840
21853
|
* The v1 door caps a payload at `MAX_NODES_PER_PAYLOAD` / `MAX_EDGES_PER_PAYLOAD`
|
|
21841
|
-
* (413 over). A batch above the cap is split and drained across calls
|
|
21842
|
-
*
|
|
21854
|
+
* (413 over). A batch above the cap is split and drained across calls, every
|
|
21855
|
+
* NODE chunk first (edges empty), then EDGE chunks (nodes empty). Each chunk
|
|
21856
|
+
* gets its OWN runId: the door's durable idempotency claim is keyed on
|
|
21857
|
+
* (agent, org, runId) with the payload digest, so reusing one runId across
|
|
21858
|
+
* different chunk payloads 409s "runId was already used with a different
|
|
21859
|
+
* payload" on the second chunk — exactly how the first real >25-node drain
|
|
21860
|
+
* died (2026-07-22). The runId never grouped anything server-side; it exists
|
|
21861
|
+
* for duplicate-POST protection, which is per-request by nature.
|
|
21843
21862
|
* Recognition resolves an edge's endpoints against nodes already ingested this
|
|
21844
21863
|
* drain (endpoint-label validation is deferred to the service when an endpoint
|
|
21845
21864
|
* isn't in-payload), so the split never orphans an edge. Sub-results concat into
|
|
@@ -21859,7 +21878,7 @@ var init_client = __esm({
|
|
|
21859
21878
|
const shipped = new Set(inChunk);
|
|
21860
21879
|
pendingEdges = pendingEdges.filter((e) => !shipped.has(e));
|
|
21861
21880
|
}
|
|
21862
|
-
const r = await this.ingestWire(toWirePayload({ ...batch, nodes: nodeChunk, edges: inChunk },
|
|
21881
|
+
const r = await this.ingestWire(toWirePayload({ ...batch, nodes: nodeChunk, edges: inChunk }, randomUUID()));
|
|
21863
21882
|
merged.nodes.push(...r.nodes);
|
|
21864
21883
|
merged.edges.push(...r.edges);
|
|
21865
21884
|
if (r.patternReconciliation) {
|
|
@@ -21867,7 +21886,7 @@ var init_client = __esm({
|
|
|
21867
21886
|
}
|
|
21868
21887
|
}
|
|
21869
21888
|
for (const edgeChunk of chunkArray(pendingEdges, MAX_EDGES_PER_PAYLOAD)) {
|
|
21870
|
-
const r = await this.ingestWire(toWirePayload({ ...batch, nodes: [], edges: edgeChunk },
|
|
21889
|
+
const r = await this.ingestWire(toWirePayload({ ...batch, nodes: [], edges: edgeChunk }, randomUUID()));
|
|
21871
21890
|
merged.edges.push(...r.edges);
|
|
21872
21891
|
}
|
|
21873
21892
|
return { ...summarizeIngestResult(merged), result: merged };
|
|
@@ -22163,7 +22182,7 @@ var init_client = __esm({
|
|
|
22163
22182
|
...provenanceHeaders(this.provenance)
|
|
22164
22183
|
};
|
|
22165
22184
|
if (!opts?.skipAuth) {
|
|
22166
|
-
const token = await this.authToken();
|
|
22185
|
+
const token = opts?.bearerToken ?? await this.authToken();
|
|
22167
22186
|
if (token) headers["authorization"] = `Bearer ${token}`;
|
|
22168
22187
|
}
|
|
22169
22188
|
const ac = new AbortController();
|
|
@@ -22377,7 +22396,9 @@ function defaultConfig() {
|
|
|
22377
22396
|
onboardedAt: null,
|
|
22378
22397
|
machineId: null,
|
|
22379
22398
|
updateChannel: "dev",
|
|
22380
|
-
activeAgent: null
|
|
22399
|
+
activeAgent: null,
|
|
22400
|
+
installationProfiles: {},
|
|
22401
|
+
activeInstallationProfile: null
|
|
22381
22402
|
};
|
|
22382
22403
|
}
|
|
22383
22404
|
function loadConfig() {
|
|
@@ -22402,14 +22423,89 @@ function loadConfig() {
|
|
|
22402
22423
|
cloudUrl: envCloudUrl ?? (migrateLegacyDefault ? DEFAULT_CLOUD_URL : parsed.cloudUrl ?? base.cloudUrl),
|
|
22403
22424
|
// Deep-merge consent so an old/partial config keeps the opt-in defaults
|
|
22404
22425
|
// for any channel it doesn't mention.
|
|
22405
|
-
consent: { ...base.consent, ...parsed.consent ?? {} }
|
|
22426
|
+
consent: { ...base.consent, ...parsed.consent ?? {} },
|
|
22427
|
+
installationProfiles: { ...parsed.installationProfiles ?? {} }
|
|
22406
22428
|
};
|
|
22407
22429
|
if (migrateLegacyDefault && !envCloudUrl) saveConfig(resolved);
|
|
22408
22430
|
return resolved;
|
|
22409
22431
|
}
|
|
22410
22432
|
function saveConfig(cfg) {
|
|
22411
22433
|
ensureDir(globalDir());
|
|
22412
|
-
|
|
22434
|
+
const normalized = snapshotActiveInstallationProfile(cfg);
|
|
22435
|
+
writeFileSync5(globalConfigPath(), JSON.stringify(normalized, null, 2), { encoding: "utf8", mode: 384 });
|
|
22436
|
+
}
|
|
22437
|
+
function profileFromCurrent(cfg, installationId) {
|
|
22438
|
+
return {
|
|
22439
|
+
installationId,
|
|
22440
|
+
cloudUrl: cfg.cloudUrl,
|
|
22441
|
+
apiKey: cfg.apiKey,
|
|
22442
|
+
accessToken: cfg.accessToken,
|
|
22443
|
+
refreshToken: cfg.refreshToken,
|
|
22444
|
+
tokenEndpoint: cfg.tokenEndpoint,
|
|
22445
|
+
oauthClientId: cfg.oauthClientId,
|
|
22446
|
+
activeAgent: cfg.activeAgent
|
|
22447
|
+
};
|
|
22448
|
+
}
|
|
22449
|
+
function snapshotActiveInstallationProfile(cfg) {
|
|
22450
|
+
const name2 = cfg.activeInstallationProfile;
|
|
22451
|
+
if (!name2) return cfg;
|
|
22452
|
+
const existing = cfg.installationProfiles[name2];
|
|
22453
|
+
if (!existing) return { ...cfg, activeInstallationProfile: null };
|
|
22454
|
+
return {
|
|
22455
|
+
...cfg,
|
|
22456
|
+
installationProfiles: {
|
|
22457
|
+
...cfg.installationProfiles,
|
|
22458
|
+
[name2]: profileFromCurrent(cfg, existing.installationId)
|
|
22459
|
+
}
|
|
22460
|
+
};
|
|
22461
|
+
}
|
|
22462
|
+
function saveInstallationProfile(name2, installationId, cfg = loadConfig()) {
|
|
22463
|
+
const normalizedName = name2.trim();
|
|
22464
|
+
if (!normalizedName || normalizedName.length > 80) {
|
|
22465
|
+
throw new Error("profile name must be 1\u201380 characters");
|
|
22466
|
+
}
|
|
22467
|
+
if (!/^[0-9a-f]{8}-[0-9a-f]{4}-[1-8][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i.test(installationId)) {
|
|
22468
|
+
throw new Error("installation id must be a UUID");
|
|
22469
|
+
}
|
|
22470
|
+
const next = {
|
|
22471
|
+
...cfg,
|
|
22472
|
+
installationProfiles: {
|
|
22473
|
+
...cfg.installationProfiles,
|
|
22474
|
+
[normalizedName]: profileFromCurrent(cfg, installationId)
|
|
22475
|
+
},
|
|
22476
|
+
activeInstallationProfile: normalizedName
|
|
22477
|
+
};
|
|
22478
|
+
saveConfig(next);
|
|
22479
|
+
return next;
|
|
22480
|
+
}
|
|
22481
|
+
function useInstallationProfile(name2, cfg = loadConfig()) {
|
|
22482
|
+
const profile = cfg.installationProfiles[name2];
|
|
22483
|
+
if (!profile) throw new Error(`installation profile not found: ${name2}`);
|
|
22484
|
+
const next = {
|
|
22485
|
+
...cfg,
|
|
22486
|
+
cloudUrl: profile.cloudUrl,
|
|
22487
|
+
apiKey: profile.apiKey,
|
|
22488
|
+
accessToken: profile.accessToken,
|
|
22489
|
+
refreshToken: profile.refreshToken,
|
|
22490
|
+
tokenEndpoint: profile.tokenEndpoint,
|
|
22491
|
+
oauthClientId: profile.oauthClientId,
|
|
22492
|
+
activeAgent: profile.activeAgent,
|
|
22493
|
+
activeInstallationProfile: name2
|
|
22494
|
+
};
|
|
22495
|
+
saveConfig(next);
|
|
22496
|
+
return next;
|
|
22497
|
+
}
|
|
22498
|
+
function removeInstallationProfile(name2, cfg = loadConfig()) {
|
|
22499
|
+
if (!cfg.installationProfiles[name2]) return cfg;
|
|
22500
|
+
const installationProfiles = { ...cfg.installationProfiles };
|
|
22501
|
+
delete installationProfiles[name2];
|
|
22502
|
+
const next = {
|
|
22503
|
+
...cfg,
|
|
22504
|
+
installationProfiles,
|
|
22505
|
+
activeInstallationProfile: cfg.activeInstallationProfile === name2 ? null : cfg.activeInstallationProfile
|
|
22506
|
+
};
|
|
22507
|
+
saveConfig(next);
|
|
22508
|
+
return next;
|
|
22413
22509
|
}
|
|
22414
22510
|
function machineId() {
|
|
22415
22511
|
const fromEnv = process.env["ERRATA_MACHINE_ID"];
|
|
@@ -22444,12 +22540,30 @@ var init_config = __esm({
|
|
|
22444
22540
|
});
|
|
22445
22541
|
|
|
22446
22542
|
// src/cloud-endpoint-policy.ts
|
|
22543
|
+
function warnDirectV1OverrideOnce() {
|
|
22544
|
+
if (warnedDirectV1Override) return;
|
|
22545
|
+
warnedDirectV1Override = true;
|
|
22546
|
+
console.error(
|
|
22547
|
+
[
|
|
22548
|
+
"",
|
|
22549
|
+
"!!! DIRECT V1 OVERRIDE ACTIVE !!!",
|
|
22550
|
+
`${DIRECT_V1_OVERRIDE_ENV} + ${DIRECT_V1_ACK_ENV} are set: this daemon is talking`,
|
|
22551
|
+
"straight to the legacy v1 data plane, BYPASSING the Console Gateway \u2014",
|
|
22552
|
+
"no metering, no signed context enforcement, no write-target binding.",
|
|
22553
|
+
"Staff testing only. This escape hatch is removed at v1 EOL.",
|
|
22554
|
+
""
|
|
22555
|
+
].join("\n")
|
|
22556
|
+
);
|
|
22557
|
+
}
|
|
22558
|
+
function isDirectV1OverrideRequested() {
|
|
22559
|
+
const raw2 = process.env[DIRECT_V1_OVERRIDE_ENV]?.trim().toLowerCase();
|
|
22560
|
+
return raw2 === "1" || raw2 === "true" || raw2 === "yes" || raw2 === "on";
|
|
22561
|
+
}
|
|
22447
22562
|
function normalizeCloudUrl(url2) {
|
|
22448
22563
|
return url2.replace(/\/+$/, "");
|
|
22449
22564
|
}
|
|
22450
22565
|
function isDirectV1OverrideEnabled() {
|
|
22451
|
-
|
|
22452
|
-
return raw2 === "1" || raw2 === "true" || raw2 === "yes" || raw2 === "on";
|
|
22566
|
+
return isDirectV1OverrideRequested() && process.env[DIRECT_V1_ACK_ENV]?.trim() === DIRECT_V1_ACK_VALUE;
|
|
22453
22567
|
}
|
|
22454
22568
|
function isLocalCloudUrl(url2) {
|
|
22455
22569
|
try {
|
|
@@ -22481,18 +22595,20 @@ function evaluateCloudEndpoint(url2, inspection) {
|
|
|
22481
22595
|
return { allowed: true, label: "gateway-configured" };
|
|
22482
22596
|
}
|
|
22483
22597
|
if (isDirectV1OverrideEnabled()) {
|
|
22598
|
+
warnDirectV1OverrideOnce();
|
|
22484
22599
|
return {
|
|
22485
22600
|
allowed: true,
|
|
22486
22601
|
label: "legacy-direct (override)",
|
|
22487
|
-
reason: `${DIRECT_V1_OVERRIDE_ENV}
|
|
22602
|
+
reason: `${DIRECT_V1_OVERRIDE_ENV} acknowledged via ${DIRECT_V1_ACK_ENV}`
|
|
22488
22603
|
};
|
|
22489
22604
|
}
|
|
22490
22605
|
const service = inspection?.service ? ` (${inspection.service})` : "";
|
|
22606
|
+
const unacked = isDirectV1OverrideRequested() ? ` ${DIRECT_V1_OVERRIDE_ENV} is set but unacknowledged \u2014 staff testing additionally requires ${DIRECT_V1_ACK_ENV}=${DIRECT_V1_ACK_VALUE}.` : "";
|
|
22491
22607
|
return {
|
|
22492
22608
|
allowed: false,
|
|
22493
22609
|
label: "legacy-direct blocked",
|
|
22494
22610
|
reason: `non-local cloud endpoint is not the Console Gateway${service}`,
|
|
22495
|
-
guidance: `Point ERRATA_CLOUD_URL at the Console Gateway
|
|
22611
|
+
guidance: `Point ERRATA_CLOUD_URL at the Console Gateway.${unacked}`
|
|
22496
22612
|
};
|
|
22497
22613
|
}
|
|
22498
22614
|
function formatCloudEndpointBlock(url2, decision) {
|
|
@@ -22634,13 +22750,16 @@ async function fetchHealthJson(fetchFn, url2, timeoutMs) {
|
|
|
22634
22750
|
clearTimeout(tid);
|
|
22635
22751
|
}
|
|
22636
22752
|
}
|
|
22637
|
-
var DIRECT_V1_OVERRIDE_ENV, GATEWAY_SERVICE, PROBE_TIMEOUT_MS, CloudEndpointPolicyError, BLOCKED_RECHECK_MS;
|
|
22753
|
+
var DIRECT_V1_OVERRIDE_ENV, DIRECT_V1_ACK_ENV, DIRECT_V1_ACK_VALUE, GATEWAY_SERVICE, PROBE_TIMEOUT_MS, warnedDirectV1Override, CloudEndpointPolicyError, BLOCKED_RECHECK_MS;
|
|
22638
22754
|
var init_cloud_endpoint_policy = __esm({
|
|
22639
22755
|
"src/cloud-endpoint-policy.ts"() {
|
|
22640
22756
|
"use strict";
|
|
22641
22757
|
DIRECT_V1_OVERRIDE_ENV = "ERRATA_ALLOW_DIRECT_V1_CLOUD";
|
|
22758
|
+
DIRECT_V1_ACK_ENV = "ERRATA_DIRECT_V1_ACK";
|
|
22759
|
+
DIRECT_V1_ACK_VALUE = "I_ACCEPT_UNMETERED_DIRECT_V1";
|
|
22642
22760
|
GATEWAY_SERVICE = "console-gateway";
|
|
22643
22761
|
PROBE_TIMEOUT_MS = 2e3;
|
|
22762
|
+
warnedDirectV1Override = false;
|
|
22644
22763
|
CloudEndpointPolicyError = class extends Error {
|
|
22645
22764
|
decision;
|
|
22646
22765
|
constructor(message, decision) {
|
|
@@ -22678,6 +22797,7 @@ function authedCloudClient(cfg, opts = {}) {
|
|
|
22678
22797
|
const versionOpts = opts.daemonVersion ? { daemonVersion: opts.daemonVersion, daemonChannel: cfg.updateChannel } : {};
|
|
22679
22798
|
if (cfg.accessToken) {
|
|
22680
22799
|
const tokenEndpoint = cfg.tokenEndpoint;
|
|
22800
|
+
const installationId = cfg.activeInstallationProfile ? cfg.installationProfiles[cfg.activeInstallationProfile]?.installationId : void 0;
|
|
22681
22801
|
return new CloudClient({
|
|
22682
22802
|
baseUrl,
|
|
22683
22803
|
accessToken: cfg.accessToken,
|
|
@@ -22687,7 +22807,11 @@ function authedCloudClient(cfg, opts = {}) {
|
|
|
22687
22807
|
provenance: daemonProvenance(),
|
|
22688
22808
|
...opts.timeoutMs ? { timeoutMs: opts.timeoutMs } : {},
|
|
22689
22809
|
...tokenEndpoint && cfg.refreshToken ? {
|
|
22690
|
-
refreshFn: (rt) =>
|
|
22810
|
+
refreshFn: (rt) => tokenEndpoint.endsWith("/api/gate/session-token") && installationId ? refreshDeviceSessionToken(opts.fetchFn ?? fetch, tokenEndpoint, {
|
|
22811
|
+
sessionToken: rt,
|
|
22812
|
+
installationId,
|
|
22813
|
+
clientId: cfg.oauthClientId ?? oauthClientId()
|
|
22814
|
+
}) : refreshAccessToken(opts.fetchFn ?? fetch, tokenEndpoint, {
|
|
22691
22815
|
refreshToken: rt,
|
|
22692
22816
|
clientId: cfg.oauthClientId ?? oauthClientId()
|
|
22693
22817
|
}),
|
|
@@ -22710,6 +22834,30 @@ function authedCloudClient(cfg, opts = {}) {
|
|
|
22710
22834
|
...opts.timeoutMs ? { timeoutMs: opts.timeoutMs } : {}
|
|
22711
22835
|
});
|
|
22712
22836
|
}
|
|
22837
|
+
async function refreshDeviceSessionToken(fetchFn, endpoint, input) {
|
|
22838
|
+
const response = await fetchFn(endpoint, {
|
|
22839
|
+
method: "POST",
|
|
22840
|
+
headers: {
|
|
22841
|
+
authorization: `Bearer ${input.sessionToken}`,
|
|
22842
|
+
"content-type": "application/json",
|
|
22843
|
+
accept: "application/json"
|
|
22844
|
+
},
|
|
22845
|
+
body: JSON.stringify({
|
|
22846
|
+
installationId: input.installationId,
|
|
22847
|
+
clientId: input.clientId,
|
|
22848
|
+
scopes: ["graph:read", "graph:write", "mcp:tools"]
|
|
22849
|
+
})
|
|
22850
|
+
});
|
|
22851
|
+
const body2 = await response.json().catch(() => null);
|
|
22852
|
+
if (!response.ok || typeof body2?.["access_token"] !== "string") {
|
|
22853
|
+
throw new Error(`device session refresh failed: HTTP ${response.status}`);
|
|
22854
|
+
}
|
|
22855
|
+
return {
|
|
22856
|
+
accessToken: body2["access_token"],
|
|
22857
|
+
refreshToken: input.sessionToken,
|
|
22858
|
+
...typeof body2["expires_in"] === "number" ? { expiresInSec: body2["expires_in"] } : {}
|
|
22859
|
+
};
|
|
22860
|
+
}
|
|
22713
22861
|
function hasCloudCredential(cfg) {
|
|
22714
22862
|
return Boolean(cfg.accessToken || cfg.apiKey);
|
|
22715
22863
|
}
|
|
@@ -36701,14 +36849,15 @@ function createMcpHandler(store, ctx = {}) {
|
|
|
36701
36849
|
};
|
|
36702
36850
|
}
|
|
36703
36851
|
function buildToolContext() {
|
|
36852
|
+
const base = { switchInstallationProfile: useInstallationProfile };
|
|
36704
36853
|
try {
|
|
36705
36854
|
const cfg = loadConfig();
|
|
36706
36855
|
if (cfg.consent.sync && hasCloudCredential(cfg) && cfg.cloudUrl) {
|
|
36707
|
-
return { cloud: authedCloudClient(cfg) };
|
|
36856
|
+
return { ...base, cloud: authedCloudClient(cfg) };
|
|
36708
36857
|
}
|
|
36709
36858
|
} catch {
|
|
36710
36859
|
}
|
|
36711
|
-
return
|
|
36860
|
+
return base;
|
|
36712
36861
|
}
|
|
36713
36862
|
async function runMcpServer(workspaceRoot) {
|
|
36714
36863
|
const paths = workspacePaths(workspaceRoot);
|
|
@@ -36817,6 +36966,41 @@ var init_mcp = __esm({
|
|
|
36817
36966
|
AUDIT_NUDGE_COOLDOWN = 12;
|
|
36818
36967
|
AUDIT_FLAG_NUDGE = "\u26A1 errata \u2014 reading/auditing? anything you notice that's off, flag it inline as `[!one line]` (`[?\u2026]` = TODO) \u2014 no tool call, we harvest it.";
|
|
36819
36968
|
TOOLS = [
|
|
36969
|
+
{
|
|
36970
|
+
name: "errata.switch_context",
|
|
36971
|
+
description: "Switch this daemon to an already-installed Personal, organization, or Team profile. The profile's full credential set changes with the selection; this never widens the current token or carries consent between installations. Returns the selected installation descriptor so the caller can verify what changed.",
|
|
36972
|
+
inputSchema: {
|
|
36973
|
+
type: "object",
|
|
36974
|
+
properties: {
|
|
36975
|
+
profile: {
|
|
36976
|
+
type: "string",
|
|
36977
|
+
minLength: 1,
|
|
36978
|
+
maxLength: 80,
|
|
36979
|
+
description: "Exact name from `errata profile list`."
|
|
36980
|
+
}
|
|
36981
|
+
},
|
|
36982
|
+
required: ["profile"],
|
|
36983
|
+
additionalProperties: false
|
|
36984
|
+
},
|
|
36985
|
+
handler: (args2, _store, ctx) => {
|
|
36986
|
+
const profileName = typeof args2["profile"] === "string" ? args2["profile"].trim() : "";
|
|
36987
|
+
if (!profileName) throw new Error("profile is required");
|
|
36988
|
+
const next = (ctx.switchInstallationProfile ?? useInstallationProfile)(profileName);
|
|
36989
|
+
const profile = next.installationProfiles[profileName];
|
|
36990
|
+
if (!profile) throw new Error(`installation profile not found: ${profileName}`);
|
|
36991
|
+
return {
|
|
36992
|
+
switched: true,
|
|
36993
|
+
profile: profileName,
|
|
36994
|
+
installation_id: profile.installationId,
|
|
36995
|
+
cloud_url: profile.cloudUrl,
|
|
36996
|
+
credential_type: profile.accessToken ? "oauth" : profile.apiKey ? "api_key" : "none",
|
|
36997
|
+
actor: profile.activeAgent,
|
|
36998
|
+
authority_changed: true,
|
|
36999
|
+
standing_consent_carried: false,
|
|
37000
|
+
note: "Live graph visibility and write target are resolved server-side for each request."
|
|
37001
|
+
};
|
|
37002
|
+
}
|
|
37003
|
+
},
|
|
36820
37004
|
{
|
|
36821
37005
|
name: "errata.search",
|
|
36822
37006
|
description: "Find a symbol, string, or comment across the live code graph \u2014 reaches matches by structure, not just text. Multi-word queries are tokenized and OR'd (more terms matched ranks higher, then pageRank), so word order and punctuation don't matter. Returns up to `limit` hits with id, label, description, and pageRank (higher pageRank = more central \u2014 start there). A comment that documents a symbol resolves to that SYMBOL (the comment rides along as `via` \u2014 it's the why, not the destination); test scaffolding and floating comments sort below production code. When the collective is reachable, results BLEND cross-project knowledge, each tagged `provenance` (local | collective | corroborated) with `usageCount`.",
|
|
@@ -49644,8 +49828,8 @@ function tagEdgeCorroborated(targetAnchorIds, sessionTouchedNodeIds, independent
|
|
|
49644
49828
|
for (const a of targetAnchorIds) if (sessionTouchedNodeIds.has(a)) return true;
|
|
49645
49829
|
return false;
|
|
49646
49830
|
}
|
|
49647
|
-
function typePriorEdge(sourceLabel,
|
|
49648
|
-
const direct = LABEL_PAIR[`${sourceLabel}>${
|
|
49831
|
+
function typePriorEdge(sourceLabel, targetLabel2, sentence = "") {
|
|
49832
|
+
const direct = LABEL_PAIR[`${sourceLabel}>${targetLabel2}`];
|
|
49649
49833
|
if (direct) return direct;
|
|
49650
49834
|
if (sentence && !NEG.test(sentence)) {
|
|
49651
49835
|
const t = TIEBREAK.find((c) => c.re.test(sentence));
|
|
@@ -51471,7 +51655,7 @@ function startLoopLagMonitor(thresholdMs = 1e3) {
|
|
|
51471
51655
|
}
|
|
51472
51656
|
|
|
51473
51657
|
// src/engine.ts
|
|
51474
|
-
var DAEMON_VERSION = true ? "2.0.1-dev.
|
|
51658
|
+
var DAEMON_VERSION = true ? "2.0.1-dev.52" : "2.0.0-alpha.0";
|
|
51475
51659
|
var IGNORED_PATH = /[\\/](?:\.git|node_modules|\.errata|\.claude|\.codex|\.cursor|\.turbo|\.next|dist|coverage|test-results|playwright-report|__pycache__)(?:[\\/]|$)/;
|
|
51476
51660
|
var IGNORED_NOISE = /castalia\.db|eventlog\.sqlite|turn-cursors/;
|
|
51477
51661
|
var GIT_OP_MUTE_MS = 4e3;
|
|
@@ -53060,10 +53244,13 @@ function buildContextIngest(store, profile, daemonVersion, ignorePatterns = [],
|
|
|
53060
53244
|
// Package ids are already the purl (= the cross-stratum canonicalId).
|
|
53061
53245
|
id: label === "Language" ? languageCanonicalId(String(n.attrs["name"] ?? "").trim() || n.description) : n.id,
|
|
53062
53246
|
embedding: [],
|
|
53247
|
+
// No `version` attr on the wire: the purl already encodes the resolved
|
|
53248
|
+
// version, and the door's temporal guard rejects ANY `attrs.version` as
|
|
53249
|
+
// bi-temporal bookkeeping (ingest-temporal-guard.ts) — shipping it 422s
|
|
53250
|
+
// the whole batch. `resolved` still travels (range-vs-lockfile signal).
|
|
53063
53251
|
attrs: label === "Package" ? {
|
|
53064
53252
|
purl: n.attrs["purl"],
|
|
53065
53253
|
name: n.attrs["name"],
|
|
53066
|
-
version: n.attrs["version"],
|
|
53067
53254
|
ecosystem: n.attrs["ecosystem"],
|
|
53068
53255
|
resolved: n.attrs["resolved"]
|
|
53069
53256
|
} : { name: n.attrs["name"] }
|
|
@@ -53178,9 +53365,10 @@ function wireContextId(n) {
|
|
|
53178
53365
|
}
|
|
53179
53366
|
function shareableContext(n, wireId) {
|
|
53180
53367
|
const attrs = n.label === "Package" ? {
|
|
53368
|
+
// No `version` attr: the purl encodes it, and the door's temporal
|
|
53369
|
+
// guard 422s any `attrs.version` (mirrors buildContextIngest).
|
|
53181
53370
|
purl: n.attrs["purl"],
|
|
53182
53371
|
name: n.attrs["name"],
|
|
53183
|
-
version: n.attrs["version"],
|
|
53184
53372
|
ecosystem: n.attrs["ecosystem"],
|
|
53185
53373
|
resolved: n.attrs["resolved"]
|
|
53186
53374
|
} : n.label === "Domain" ? { name: n.description, canonicalId: n.attrs["canonicalId"] } : { name: n.attrs["name"] };
|
|
@@ -54355,14 +54543,27 @@ async function startMultiDaemon(opts = {}) {
|
|
|
54355
54543
|
const ignore = loadClaimIgnorePatterns(globalDir());
|
|
54356
54544
|
const client = cloudNow();
|
|
54357
54545
|
let uploaded = 0;
|
|
54546
|
+
const errors = [];
|
|
54547
|
+
const lane = async (name2, projectName, run3) => {
|
|
54548
|
+
try {
|
|
54549
|
+
await run3();
|
|
54550
|
+
} catch (err2) {
|
|
54551
|
+
const msg = `[${projectName}] ${name2}: ${err2 instanceof Error ? err2.message : String(err2)}`;
|
|
54552
|
+
errors.push(msg);
|
|
54553
|
+
console.error(`[sync\u2192cloud] ${msg}`);
|
|
54554
|
+
}
|
|
54555
|
+
};
|
|
54358
54556
|
for (const r of records) {
|
|
54359
54557
|
const store = r.engine.store;
|
|
54558
|
+
const projectName = r.engine.profile.name ?? r.engine.profile.id;
|
|
54360
54559
|
const context = buildContextIngest(store, r.engine.profile, DAEMON_VERSION, ignore, {
|
|
54361
54560
|
includePackages: cfg2.consent.contributePackages
|
|
54362
54561
|
});
|
|
54363
54562
|
if (context) {
|
|
54364
|
-
|
|
54365
|
-
|
|
54563
|
+
await lane("context", projectName, async () => {
|
|
54564
|
+
const res = await client.ingest(context);
|
|
54565
|
+
uploaded += res.accepted;
|
|
54566
|
+
});
|
|
54366
54567
|
}
|
|
54367
54568
|
let lexicon;
|
|
54368
54569
|
try {
|
|
@@ -54385,34 +54586,36 @@ async function startMultiDaemon(opts = {}) {
|
|
|
54385
54586
|
});
|
|
54386
54587
|
if (instances) {
|
|
54387
54588
|
if (project) instances.projectId = project.projectId;
|
|
54388
|
-
|
|
54389
|
-
|
|
54390
|
-
|
|
54391
|
-
|
|
54392
|
-
|
|
54393
|
-
|
|
54394
|
-
|
|
54395
|
-
const
|
|
54396
|
-
|
|
54397
|
-
|
|
54398
|
-
|
|
54399
|
-
|
|
54400
|
-
|
|
54401
|
-
|
|
54402
|
-
|
|
54403
|
-
|
|
54404
|
-
|
|
54405
|
-
|
|
54406
|
-
|
|
54407
|
-
const
|
|
54408
|
-
|
|
54409
|
-
|
|
54410
|
-
|
|
54411
|
-
|
|
54412
|
-
|
|
54589
|
+
await lane("instances", projectName, async () => {
|
|
54590
|
+
const res = await client.ingest(instances);
|
|
54591
|
+
uploaded += res.accepted;
|
|
54592
|
+
const seq = store.currentIngestSeq();
|
|
54593
|
+
const cloudIdByLocal = new Map(
|
|
54594
|
+
res.result.nodes.filter((rn) => rn.nodeId).map((rn) => [rn.canonicalId, rn.nodeId])
|
|
54595
|
+
);
|
|
54596
|
+
for (const n of instances.nodes) {
|
|
54597
|
+
const local = store.getNode(n.id);
|
|
54598
|
+
if (!local || !["Problem", "Solution", "RootCause"].includes(local.label)) continue;
|
|
54599
|
+
const cloudNodeId = cloudIdByLocal.get(n.id);
|
|
54600
|
+
store.updateNode(n.id, {
|
|
54601
|
+
attrs: {
|
|
54602
|
+
...local.attrs,
|
|
54603
|
+
contributedAtSeq: seq,
|
|
54604
|
+
...cloudNodeId && cloudNodeId !== n.id ? { cloudNodeId } : {}
|
|
54605
|
+
}
|
|
54606
|
+
});
|
|
54607
|
+
}
|
|
54608
|
+
for (const [localId, dg] of Object.entries(instances.anchorDigests)) {
|
|
54609
|
+
const local = store.getNode(localId);
|
|
54610
|
+
if (!local) continue;
|
|
54611
|
+
store.updateNode(localId, {
|
|
54612
|
+
attrs: { ...local.attrs, anchorsContributedDigest: dg }
|
|
54613
|
+
});
|
|
54614
|
+
}
|
|
54615
|
+
});
|
|
54413
54616
|
}
|
|
54414
54617
|
}
|
|
54415
|
-
return { uploaded };
|
|
54618
|
+
return { uploaded, ...errors.length > 0 ? { errors } : {} };
|
|
54416
54619
|
},
|
|
54417
54620
|
async pullTriagePublic() {
|
|
54418
54621
|
if (!loadConfig().consent.sync) return { merged: 0, skipped: "consent-off" };
|
|
@@ -54462,7 +54665,12 @@ async function startMultiDaemon(opts = {}) {
|
|
|
54462
54665
|
}
|
|
54463
54666
|
const inst = await daemon.syncInstancesPublic();
|
|
54464
54667
|
const skills = await daemon.syncSkillsAll();
|
|
54465
|
-
return {
|
|
54668
|
+
return {
|
|
54669
|
+
uploaded: inst.uploaded,
|
|
54670
|
+
written: skills.written,
|
|
54671
|
+
pruned: skills.pruned,
|
|
54672
|
+
...inst.errors ? { errors: inst.errors } : {}
|
|
54673
|
+
};
|
|
54466
54674
|
} finally {
|
|
54467
54675
|
boundaryFlushing = false;
|
|
54468
54676
|
}
|
|
@@ -54629,6 +54837,102 @@ init_cloud_auth();
|
|
|
54629
54837
|
import { createServer } from "node:http";
|
|
54630
54838
|
var DEFAULT_OAUTH_SCOPE = "openid profile graph:read graph:write mcp:tools";
|
|
54631
54839
|
var strip = (u) => u.replace(/\/+$/, "");
|
|
54840
|
+
function deviceEndpoints(tokenEndpoint) {
|
|
54841
|
+
const token = new URL(tokenEndpoint);
|
|
54842
|
+
const authRoot = token.pathname.replace(/\/(?:oauth2|mcp)\/token\/?$/, "");
|
|
54843
|
+
if (authRoot === token.pathname) throw new Error("authorization server exposes no device endpoint");
|
|
54844
|
+
const prefix = authRoot.replace(/\/api\/auth\/?$/, "");
|
|
54845
|
+
const at = (path2) => new URL(`${path2}`, `${token.origin}${prefix || "/"}`).toString();
|
|
54846
|
+
return {
|
|
54847
|
+
code: at(`${prefix}/api/auth/device/code`),
|
|
54848
|
+
token: at(`${prefix}/api/auth/device/token`),
|
|
54849
|
+
resource: at(`${prefix}/api/gate/session-token`)
|
|
54850
|
+
};
|
|
54851
|
+
}
|
|
54852
|
+
async function loginOAuthDevice(opts) {
|
|
54853
|
+
const fetchFn = opts.fetchFn ?? fetch;
|
|
54854
|
+
const endpoints = await discoverEndpoints(opts.cloudUrl, fetchFn);
|
|
54855
|
+
const device = deviceEndpoints(endpoints.tokenEndpoint);
|
|
54856
|
+
const clientId = oauthClientId();
|
|
54857
|
+
const scope = opts.scope ?? DEFAULT_OAUTH_SCOPE;
|
|
54858
|
+
const codeResponse = await fetchFn(device.code, {
|
|
54859
|
+
method: "POST",
|
|
54860
|
+
headers: { "content-type": "application/json", accept: "application/json" },
|
|
54861
|
+
body: JSON.stringify({ client_id: clientId, scope })
|
|
54862
|
+
});
|
|
54863
|
+
if (!codeResponse.ok) {
|
|
54864
|
+
throw new Error(`device authorization failed: HTTP ${codeResponse.status} ${(await codeResponse.text()).slice(0, 200)}`);
|
|
54865
|
+
}
|
|
54866
|
+
const code = await codeResponse.json();
|
|
54867
|
+
if (!code.device_code || !code.user_code || !code.verification_uri || !code.expires_in) {
|
|
54868
|
+
throw new Error("device authorization returned an incomplete response");
|
|
54869
|
+
}
|
|
54870
|
+
opts.onVerification?.({
|
|
54871
|
+
url: code.verification_uri_complete ?? code.verification_uri,
|
|
54872
|
+
userCode: code.user_code,
|
|
54873
|
+
expiresInSec: code.expires_in
|
|
54874
|
+
});
|
|
54875
|
+
const deadline = Date.now() + Math.min(code.expires_in * 1e3, opts.timeoutMs ?? Number.POSITIVE_INFINITY);
|
|
54876
|
+
let intervalMs = Math.max(2, code.interval ?? 5) * 1e3;
|
|
54877
|
+
let deviceSession = null;
|
|
54878
|
+
let approvedInstallation = null;
|
|
54879
|
+
while (Date.now() < deadline) {
|
|
54880
|
+
await (opts.sleep ?? ((ms) => new Promise((resolve6) => setTimeout(resolve6, ms))))(intervalMs);
|
|
54881
|
+
const poll = await fetchFn(device.token, {
|
|
54882
|
+
method: "POST",
|
|
54883
|
+
headers: { "content-type": "application/json", accept: "application/json" },
|
|
54884
|
+
body: JSON.stringify({
|
|
54885
|
+
grant_type: "urn:ietf:params:oauth:grant-type:device_code",
|
|
54886
|
+
device_code: code.device_code,
|
|
54887
|
+
client_id: clientId
|
|
54888
|
+
})
|
|
54889
|
+
});
|
|
54890
|
+
const payload = await poll.json().catch(() => null);
|
|
54891
|
+
if (!poll.ok) {
|
|
54892
|
+
const error48 = typeof payload?.["error"] === "string" ? payload["error"] : "device_poll_failed";
|
|
54893
|
+
if (error48 === "authorization_pending") continue;
|
|
54894
|
+
if (error48 === "slow_down") {
|
|
54895
|
+
intervalMs += 5e3;
|
|
54896
|
+
continue;
|
|
54897
|
+
}
|
|
54898
|
+
throw new Error(`device authorization failed: ${error48}`);
|
|
54899
|
+
}
|
|
54900
|
+
deviceSession = typeof payload?.["access_token"] === "string" ? payload["access_token"] : null;
|
|
54901
|
+
approvedInstallation = typeof payload?.["installation_id"] === "string" ? payload["installation_id"] : null;
|
|
54902
|
+
break;
|
|
54903
|
+
}
|
|
54904
|
+
if (!deviceSession || !approvedInstallation) throw new Error("device authorization expired");
|
|
54905
|
+
if (approvedInstallation !== opts.installationId) {
|
|
54906
|
+
throw new Error("approved installation does not match the terminal request");
|
|
54907
|
+
}
|
|
54908
|
+
const resourceResponse = await fetchFn(device.resource, {
|
|
54909
|
+
method: "POST",
|
|
54910
|
+
headers: {
|
|
54911
|
+
authorization: `Bearer ${deviceSession}`,
|
|
54912
|
+
"content-type": "application/json",
|
|
54913
|
+
accept: "application/json"
|
|
54914
|
+
},
|
|
54915
|
+
body: JSON.stringify({
|
|
54916
|
+
installationId: approvedInstallation,
|
|
54917
|
+
clientId,
|
|
54918
|
+
scopes: scope.split(/\s+/).filter((value) => !["openid", "profile", "email", "offline_access"].includes(value))
|
|
54919
|
+
})
|
|
54920
|
+
});
|
|
54921
|
+
const resource = await resourceResponse.json().catch(() => null);
|
|
54922
|
+
if (!resourceResponse.ok || typeof resource?.["access_token"] !== "string") {
|
|
54923
|
+
throw new Error(`device resource exchange failed: HTTP ${resourceResponse.status}`);
|
|
54924
|
+
}
|
|
54925
|
+
return {
|
|
54926
|
+
installationId: approvedInstallation,
|
|
54927
|
+
clientId,
|
|
54928
|
+
tokenEndpoint: device.resource,
|
|
54929
|
+
tokens: {
|
|
54930
|
+
accessToken: resource["access_token"],
|
|
54931
|
+
refreshToken: deviceSession,
|
|
54932
|
+
...typeof resource["expires_in"] === "number" ? { expiresInSec: resource["expires_in"] } : {}
|
|
54933
|
+
}
|
|
54934
|
+
};
|
|
54935
|
+
}
|
|
54632
54936
|
async function discoverEndpoints(cloudUrl, fetchFn = fetch) {
|
|
54633
54937
|
let consoleUrl = cloudUrl;
|
|
54634
54938
|
try {
|
|
@@ -54998,6 +55302,116 @@ function shouldUseOAuthLogin(flags2, env2 = process.env) {
|
|
|
54998
55302
|
return Boolean(flags2.oauth || !flags2.device && isDaemonOAuthDefaultEnabled(env2));
|
|
54999
55303
|
}
|
|
55000
55304
|
|
|
55305
|
+
// src/doctor.ts
|
|
55306
|
+
async function diagnoseInstallation(cfg, dependencies) {
|
|
55307
|
+
const checks = [];
|
|
55308
|
+
const profileName = cfg.activeInstallationProfile;
|
|
55309
|
+
const profile = profileName ? cfg.installationProfiles[profileName] : void 0;
|
|
55310
|
+
checks.push(
|
|
55311
|
+
profile ? { id: "installation_profile", status: "pass", detail: `${profileName} is selected` } : {
|
|
55312
|
+
id: "installation_profile",
|
|
55313
|
+
status: "fail",
|
|
55314
|
+
detail: "No installation profile is selected"
|
|
55315
|
+
}
|
|
55316
|
+
);
|
|
55317
|
+
const credentialType = cfg.accessToken ? "oauth" : cfg.apiKey ? "api_key" : "none";
|
|
55318
|
+
checks.push(
|
|
55319
|
+
credentialType === "none" ? { id: "credential", status: "fail", detail: "No credential is installed" } : { id: "credential", status: "pass", detail: `${credentialType} credential is present` }
|
|
55320
|
+
);
|
|
55321
|
+
const authorityMatches = !profile || profile.cloudUrl === cfg.cloudUrl && profile.apiKey === cfg.apiKey && profile.accessToken === cfg.accessToken && profile.refreshToken === cfg.refreshToken;
|
|
55322
|
+
checks.push(
|
|
55323
|
+
authorityMatches ? {
|
|
55324
|
+
id: "profile_authority",
|
|
55325
|
+
status: profile ? "pass" : "warn",
|
|
55326
|
+
detail: profile ? "Selected profile and active credential set match" : "Cannot compare authority without a selected profile"
|
|
55327
|
+
} : {
|
|
55328
|
+
id: "profile_authority",
|
|
55329
|
+
status: "fail",
|
|
55330
|
+
detail: "Selected profile and active credential set differ; reselect the profile"
|
|
55331
|
+
}
|
|
55332
|
+
);
|
|
55333
|
+
if (credentialType === "oauth") {
|
|
55334
|
+
checks.push(
|
|
55335
|
+
cfg.refreshToken && cfg.tokenEndpoint ? { id: "renewal", status: "pass", detail: "OAuth renewal is configured" } : {
|
|
55336
|
+
id: "renewal",
|
|
55337
|
+
status: "fail",
|
|
55338
|
+
detail: "OAuth access exists without a refresh credential or token endpoint"
|
|
55339
|
+
}
|
|
55340
|
+
);
|
|
55341
|
+
}
|
|
55342
|
+
try {
|
|
55343
|
+
const inspection = await dependencies.inspect();
|
|
55344
|
+
checks.push(
|
|
55345
|
+
inspection.reachable ? {
|
|
55346
|
+
id: "endpoint",
|
|
55347
|
+
status: "pass",
|
|
55348
|
+
detail: `Cloud is reachable via ${inspection.probe}`
|
|
55349
|
+
} : {
|
|
55350
|
+
id: "endpoint",
|
|
55351
|
+
status: "fail",
|
|
55352
|
+
detail: inspection.error ?? "Cloud endpoint is unreachable"
|
|
55353
|
+
}
|
|
55354
|
+
);
|
|
55355
|
+
} catch (cause) {
|
|
55356
|
+
checks.push({
|
|
55357
|
+
id: "endpoint",
|
|
55358
|
+
status: "fail",
|
|
55359
|
+
detail: cause instanceof Error ? cause.message : String(cause)
|
|
55360
|
+
});
|
|
55361
|
+
}
|
|
55362
|
+
if (credentialType !== "none") {
|
|
55363
|
+
try {
|
|
55364
|
+
const identity = await dependencies.authenticate();
|
|
55365
|
+
checks.push({
|
|
55366
|
+
id: "authentication",
|
|
55367
|
+
status: "pass",
|
|
55368
|
+
detail: `Authenticated as ${identity.handle} (${identity.tier})`
|
|
55369
|
+
});
|
|
55370
|
+
} catch (cause) {
|
|
55371
|
+
checks.push({
|
|
55372
|
+
id: "authentication",
|
|
55373
|
+
status: "fail",
|
|
55374
|
+
detail: cause instanceof Error ? cause.message : String(cause)
|
|
55375
|
+
});
|
|
55376
|
+
}
|
|
55377
|
+
}
|
|
55378
|
+
return {
|
|
55379
|
+
ok: checks.every((check2) => check2.status !== "fail"),
|
|
55380
|
+
profile: profileName,
|
|
55381
|
+
installationId: profile?.installationId ?? null,
|
|
55382
|
+
credentialType,
|
|
55383
|
+
checks
|
|
55384
|
+
};
|
|
55385
|
+
}
|
|
55386
|
+
|
|
55387
|
+
// src/whoami.ts
|
|
55388
|
+
async function resolveWhoami(client, actingHandle) {
|
|
55389
|
+
return actingHandle ? client.contextAs(actingHandle) : client.context();
|
|
55390
|
+
}
|
|
55391
|
+
function targetLabel(context) {
|
|
55392
|
+
const target = context.default_write_target;
|
|
55393
|
+
if (!target) return "read-only";
|
|
55394
|
+
if (target.visibility === "public") return "public graph";
|
|
55395
|
+
if (target.visibility === "org") return `organization graph (${target.org_id})`;
|
|
55396
|
+
return `private Team graph (${target.team_id})`;
|
|
55397
|
+
}
|
|
55398
|
+
function formatWhoami(response, localProfile) {
|
|
55399
|
+
const { context, identity } = response;
|
|
55400
|
+
return [
|
|
55401
|
+
`inErrata context \u2014 ${context.profile_name ?? localProfile ?? "unbound credential"}`,
|
|
55402
|
+
` workspace: ${context.workspace_kind} \xB7 ${context.org_id}`,
|
|
55403
|
+
` team: ${context.team_id ?? "(none)"}`,
|
|
55404
|
+
` agent: ${context.agent_id ?? "(default identity)"}`,
|
|
55405
|
+
` installation: ${context.installation_id ?? "(unbound)"}`,
|
|
55406
|
+
` environment: ${context.environment} \xB7 ${context.client_kind}`,
|
|
55407
|
+
` reads: ${context.read_visibilities.join(" + ")}`,
|
|
55408
|
+
` contributes: ${targetLabel(context)}`,
|
|
55409
|
+
` policy: ${context.policy_version ?? "(legacy)"}`,
|
|
55410
|
+
` credential: ${identity.credential_type} \xB7 ${identity.credential_id}`,
|
|
55411
|
+
` actor: ${identity.actor_type} \xB7 ${identity.actor_id}`
|
|
55412
|
+
];
|
|
55413
|
+
}
|
|
55414
|
+
|
|
55001
55415
|
// src/consolidation-trigger.ts
|
|
55002
55416
|
var DEFAULT_CONSOLIDATION_POLICY = {
|
|
55003
55417
|
baseFloorMs: 6e4,
|
|
@@ -55055,8 +55469,13 @@ async function main() {
|
|
|
55055
55469
|
return cmdStop();
|
|
55056
55470
|
case "status":
|
|
55057
55471
|
return cmdStatus();
|
|
55472
|
+
case "doctor":
|
|
55473
|
+
case "verify":
|
|
55474
|
+
return cmdDoctor(rest);
|
|
55058
55475
|
case "usage":
|
|
55059
55476
|
return cmdUsage();
|
|
55477
|
+
case "whoami":
|
|
55478
|
+
return cmdWhoami(rest);
|
|
55060
55479
|
case "login":
|
|
55061
55480
|
return cmdLogin();
|
|
55062
55481
|
case "logout":
|
|
@@ -55067,6 +55486,8 @@ async function main() {
|
|
|
55067
55486
|
return cmdUnlink();
|
|
55068
55487
|
case "use":
|
|
55069
55488
|
return cmdUse(rest);
|
|
55489
|
+
case "profile":
|
|
55490
|
+
return cmdInstallationProfile(rest);
|
|
55070
55491
|
case "review":
|
|
55071
55492
|
return cmdReview();
|
|
55072
55493
|
case "tick":
|
|
@@ -55145,6 +55566,8 @@ Commands:
|
|
|
55145
55566
|
status Print workspace + cloud status (incl. version + pending update)
|
|
55146
55567
|
usage Show current cloud plan, units remaining, estimated cost,
|
|
55147
55568
|
and per-tool usage (reads the gateway's batched Hono ledger)
|
|
55569
|
+
whoami [--json] Show the Console-resolved Personal/org/Team, agent,
|
|
55570
|
+
read visibility, and exact default contribution target.
|
|
55148
55571
|
update [--channel dev|latest] [--check]
|
|
55149
55572
|
Pull the newest build on this machine's channel via npm.
|
|
55150
55573
|
--check only reports; --channel switches + persists channel.
|
|
@@ -55193,8 +55616,12 @@ Commands:
|
|
|
55193
55616
|
surface (navigation, problems, claims, burst, health)
|
|
55194
55617
|
login Sign in with the cloud. Default: short verification URL +
|
|
55195
55618
|
typeable code (device-bridged OAuth). Flags: --browser
|
|
55196
|
-
(loopback code flow) \xB7 --token <key> \xB7
|
|
55619
|
+
(loopback code flow) \xB7 --token <key> \xB7
|
|
55620
|
+
--device --installation <uuid> [--profile <name>]
|
|
55621
|
+
(headless, installation-bound console device authorization)
|
|
55197
55622
|
logout Clear local cloud credentials
|
|
55623
|
+
doctor [--json] Verify profile, credential authority, renewal, endpoint,
|
|
55624
|
+
and authentication without printing secrets (alias: verify)
|
|
55198
55625
|
link Corrective project link (ambient linking covers the happy path).
|
|
55199
55626
|
Flags: --project <id> adopt an existing project (fork\u2192upstream);
|
|
55200
55627
|
--remote <name> derive the locator from a non-origin remote;
|
|
@@ -55203,6 +55630,12 @@ Commands:
|
|
|
55203
55630
|
use [<handle>] Set the sticky session active agent the daemon acts as.
|
|
55204
55631
|
No arg lists the agents you can act as + the current one;
|
|
55205
55632
|
--none (or --clear) reverts to your default identity.
|
|
55633
|
+
profile List named installation profiles and the active one.
|
|
55634
|
+
profile save <name> --installation <uuid>
|
|
55635
|
+
Bind the current credential to a named Console installation.
|
|
55636
|
+
profile use <name> Switch credential + agent context to an installed profile.
|
|
55637
|
+
profile remove <name>
|
|
55638
|
+
Remove a local profile (does not revoke it in the Console).
|
|
55206
55639
|
sync now Flush outbox to the cloud once
|
|
55207
55640
|
privacy Show what is collected/scrubbed + your consent state
|
|
55208
55641
|
consent <channel> <on|off>
|
|
@@ -55215,7 +55648,8 @@ Commands:
|
|
|
55215
55648
|
|
|
55216
55649
|
Environment:
|
|
55217
55650
|
ERRATA_CLOUD_URL Cloud base URL (default ${DEFAULT_CLOUD_URL})
|
|
55218
|
-
ERRATA_ALLOW_DIRECT_V1_CLOUD=1
|
|
55651
|
+
ERRATA_ALLOW_DIRECT_V1_CLOUD=1 Staff-only: request direct legacy v1 (bypasses the gateway;
|
|
55652
|
+
also requires ERRATA_DIRECT_V1_ACK=I_ACCEPT_UNMETERED_DIRECT_V1)
|
|
55219
55653
|
${OAUTH_DEFAULT_ENV}=0 Test/dev escape hatch: plain \`errata login\` uses legacy device-code
|
|
55220
55654
|
`);
|
|
55221
55655
|
}
|
|
@@ -55237,9 +55671,7 @@ function resolveHookPort(explicit) {
|
|
|
55237
55671
|
async function cmdInit() {
|
|
55238
55672
|
const skipHooks = rest.includes("--skip-hooks");
|
|
55239
55673
|
const portIdx = rest.indexOf("--port");
|
|
55240
|
-
const port = resolveHookPort(
|
|
55241
|
-
portIdx >= 0 && rest[portIdx + 1] ? Number(rest[portIdx + 1]) : void 0
|
|
55242
|
-
);
|
|
55674
|
+
const port = resolveHookPort(portIdx >= 0 && rest[portIdx + 1] ? Number(rest[portIdx + 1]) : void 0);
|
|
55243
55675
|
const existing = loadProfile(ROOT);
|
|
55244
55676
|
if (existing) {
|
|
55245
55677
|
console.log(`already initialized: ${existing.id} (${existing.name})`);
|
|
@@ -55356,9 +55788,7 @@ async function cmdStart() {
|
|
|
55356
55788
|
if (claimed) {
|
|
55357
55789
|
const adopted = autodetectProfile(ROOT);
|
|
55358
55790
|
saveProfile(ROOT, adopted);
|
|
55359
|
-
console.log(
|
|
55360
|
-
`adopted: ${locator} is claimed by your org (project ${claimed.id}) \u2014 workspace initialized`
|
|
55361
|
-
);
|
|
55791
|
+
console.log(`adopted: ${locator} is claimed by your org (project ${claimed.id}) \u2014 workspace initialized`);
|
|
55362
55792
|
}
|
|
55363
55793
|
} catch {
|
|
55364
55794
|
}
|
|
@@ -55423,7 +55853,9 @@ async function cmdStatus() {
|
|
|
55423
55853
|
console.log(` workspace: ${profile ? `${profile.name} (${profile.id})` : "(not initialized)"}`);
|
|
55424
55854
|
if (profile) {
|
|
55425
55855
|
const link = profile.projectId ? ` \u2192 project ${profile.projectId.slice(0, 8)}` : " (unlinked)";
|
|
55426
|
-
console.log(
|
|
55856
|
+
console.log(
|
|
55857
|
+
` repo: ${profile.repoLocator ? `${profile.repoLocator}${link}` : "(no git remote \u2014 unlinked)"}`
|
|
55858
|
+
);
|
|
55427
55859
|
console.log(` stack: ${profile.stack.join(", ") || "(none)"}`);
|
|
55428
55860
|
console.log(` languages: ${profile.languages.join(", ") || "(none)"}`);
|
|
55429
55861
|
}
|
|
@@ -55445,12 +55877,13 @@ async function cmdStatus() {
|
|
|
55445
55877
|
);
|
|
55446
55878
|
console.log(` cloud:`);
|
|
55447
55879
|
console.log(` url: ${cfg.cloudUrl}`);
|
|
55880
|
+
console.log(` profile: ${cfg.activeInstallationProfile ?? "(unbound legacy credential)"}`);
|
|
55448
55881
|
if (!hasCloudCredential(cfg)) {
|
|
55449
55882
|
console.log(` logged in: no`);
|
|
55450
55883
|
} else if (cfg.accessToken) {
|
|
55451
55884
|
console.log(` logged in: yes (${cfg.email}) \u2014 attested daemon, can contribute`);
|
|
55452
55885
|
} else {
|
|
55453
|
-
console.log(` logged in: yes (${cfg.email}) \u2014
|
|
55886
|
+
console.log(` logged in: yes (${cfg.email}) \u2014 key capability enforced by Console installation/scopes`);
|
|
55454
55887
|
}
|
|
55455
55888
|
const active = getActiveAgent(cfg);
|
|
55456
55889
|
console.log(
|
|
@@ -55490,6 +55923,53 @@ async function cmdStatus() {
|
|
|
55490
55923
|
\u26A1 update available: ${upd.current} \u2192 ${upd.latest} \u2014 run: errata update`);
|
|
55491
55924
|
}
|
|
55492
55925
|
}
|
|
55926
|
+
async function cmdWhoami(args2) {
|
|
55927
|
+
const cfg = loadConfig();
|
|
55928
|
+
if (!hasCloudCredential(cfg)) {
|
|
55929
|
+
console.error("not logged in \u2014 run `errata login` or select an installation profile");
|
|
55930
|
+
process.exitCode = 1;
|
|
55931
|
+
return;
|
|
55932
|
+
}
|
|
55933
|
+
try {
|
|
55934
|
+
const active = getActiveAgent(cfg);
|
|
55935
|
+
const response = await resolveWhoami(authedCloudClient(cfg), active?.handle ?? null);
|
|
55936
|
+
if (args2.includes("--json")) {
|
|
55937
|
+
console.log(JSON.stringify(response, null, 2));
|
|
55938
|
+
return;
|
|
55939
|
+
}
|
|
55940
|
+
for (const line of formatWhoami(response, cfg.activeInstallationProfile)) console.log(line);
|
|
55941
|
+
} catch (err2) {
|
|
55942
|
+
console.error(`could not resolve context: ${err2 instanceof Error ? err2.message : err2}`);
|
|
55943
|
+
process.exitCode = 1;
|
|
55944
|
+
}
|
|
55945
|
+
}
|
|
55946
|
+
async function cmdDoctor(args2) {
|
|
55947
|
+
const cfg = loadConfig();
|
|
55948
|
+
const report = await diagnoseInstallation(cfg, {
|
|
55949
|
+
inspect: () => inspectCloudEndpoint(cfg.cloudUrl),
|
|
55950
|
+
authenticate: async () => {
|
|
55951
|
+
const me = await authedCloudClient(cfg).me();
|
|
55952
|
+
return { handle: me.handle, tier: me.tier };
|
|
55953
|
+
}
|
|
55954
|
+
});
|
|
55955
|
+
if (args2.includes("--json")) {
|
|
55956
|
+
console.log(JSON.stringify(report, null, 2));
|
|
55957
|
+
} else {
|
|
55958
|
+
console.log(`inErrata doctor \u2014 ${report.ok ? "ready" : "needs attention"}`);
|
|
55959
|
+
console.log(` profile: ${report.profile ?? "(none)"}`);
|
|
55960
|
+
console.log(` installation: ${report.installationId ?? "(none)"}`);
|
|
55961
|
+
for (const check2 of report.checks) {
|
|
55962
|
+
const marker = check2.status === "pass" ? "\u2713" : check2.status === "warn" ? "!" : "\u2717";
|
|
55963
|
+
console.log(` ${marker} ${check2.id}: ${check2.detail}`);
|
|
55964
|
+
}
|
|
55965
|
+
if (!report.ok) {
|
|
55966
|
+
console.log(
|
|
55967
|
+
" recovery: `errata profile list`, then `errata profile use <name>`; re-login if authentication still fails."
|
|
55968
|
+
);
|
|
55969
|
+
}
|
|
55970
|
+
}
|
|
55971
|
+
if (!report.ok) process.exitCode = 1;
|
|
55972
|
+
}
|
|
55493
55973
|
async function cmdUpdate(args2) {
|
|
55494
55974
|
const cfg = loadConfig();
|
|
55495
55975
|
let channel = cfg.updateChannel;
|
|
@@ -55550,6 +56030,10 @@ function parseFlags(args2) {
|
|
|
55550
56030
|
else if (a === "--oauth") out2.oauth = true;
|
|
55551
56031
|
else if (a === "--device") out2.device = true;
|
|
55552
56032
|
else if (a === "--browser") out2.browser = true;
|
|
56033
|
+
else if (a === "--installation") out2.installation = args2[++i2];
|
|
56034
|
+
else if (a.startsWith("--installation=")) out2.installation = a.slice("--installation=".length);
|
|
56035
|
+
else if (a === "--profile") out2.profile = args2[++i2];
|
|
56036
|
+
else if (a.startsWith("--profile=")) out2.profile = a.slice("--profile=".length);
|
|
55553
56037
|
else if (!a.startsWith("--")) out2._.push(a);
|
|
55554
56038
|
}
|
|
55555
56039
|
return out2;
|
|
@@ -55580,14 +56064,20 @@ async function cmdUsage() {
|
|
|
55580
56064
|
console.log(` held: ${status.quota.units_reserved.toLocaleString()} units reserved by in-flight calls`);
|
|
55581
56065
|
}
|
|
55582
56066
|
console.log(` cost: $${(status.quota.estimated_cents_current / 100).toFixed(2)} estimated`);
|
|
55583
|
-
console.log(
|
|
56067
|
+
console.log(
|
|
56068
|
+
` rate: $${(status.pricing.cents_per_1000_units / 100).toFixed(2)} / 1,000 units${status.pricing.estimate_only ? " (estimate, not an invoice)" : ""}`
|
|
56069
|
+
);
|
|
55584
56070
|
if (status.by_tool.length > 0) {
|
|
55585
56071
|
console.log(" tools:");
|
|
55586
56072
|
for (const row of [...status.by_tool].sort((a, b) => b.units - a.units).slice(0, 10)) {
|
|
55587
|
-
console.log(
|
|
56073
|
+
console.log(
|
|
56074
|
+
` ${row.tool.padEnd(24)} ${row.units.toLocaleString().padStart(8)} units ${row.event_count.toLocaleString().padStart(6)} calls`
|
|
56075
|
+
);
|
|
55588
56076
|
}
|
|
55589
56077
|
}
|
|
55590
|
-
console.log(
|
|
56078
|
+
console.log(
|
|
56079
|
+
` as of: ${status.as_of} (ledger + live reservations; poll again after ${Math.ceil(status.poll_after_ms / 1e3)}s)`
|
|
56080
|
+
);
|
|
55591
56081
|
} catch (err2) {
|
|
55592
56082
|
console.error(`usage unavailable: ${err2 instanceof Error ? err2.message : err2}`);
|
|
55593
56083
|
process.exitCode = 1;
|
|
@@ -55647,6 +56137,7 @@ async function cmdLoginOAuth(cfg, useBrowserLoopback = false) {
|
|
|
55647
56137
|
} catch (err2) {
|
|
55648
56138
|
console.error(`oauth login failed: ${err2 instanceof Error ? err2.message : err2}`);
|
|
55649
56139
|
if (!useBrowserLoopback) console.error(` try the browser flow: errata login --browser`);
|
|
56140
|
+
console.error(` use headless device login: errata login --device --installation <uuid>`);
|
|
55650
56141
|
console.error(` or paste a key: errata login --token <key>`);
|
|
55651
56142
|
process.exitCode = 1;
|
|
55652
56143
|
return;
|
|
@@ -55672,6 +56163,68 @@ async function cmdLoginOAuth(cfg, useBrowserLoopback = false) {
|
|
|
55672
56163
|
process.exitCode = 1;
|
|
55673
56164
|
}
|
|
55674
56165
|
}
|
|
56166
|
+
async function cmdLoginOAuthDevice(cfg, input) {
|
|
56167
|
+
const profileName = input.profileName?.trim() || `device-${input.installationId.slice(0, 8)}`;
|
|
56168
|
+
if (!/^[0-9a-f]{8}-[0-9a-f]{4}-[1-8][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i.test(input.installationId)) {
|
|
56169
|
+
console.error("--installation must be a UUID from Console \u2192 Connect");
|
|
56170
|
+
process.exitCode = 1;
|
|
56171
|
+
return;
|
|
56172
|
+
}
|
|
56173
|
+
const inspection = await inspectCloudEndpoint(cfg.cloudUrl);
|
|
56174
|
+
try {
|
|
56175
|
+
assertCloudEndpointAllowed(cfg.cloudUrl, inspection);
|
|
56176
|
+
} catch (err2) {
|
|
56177
|
+
console.error(err2 instanceof Error ? err2.message : cloudPolicyMessage(cfg, inspection));
|
|
56178
|
+
process.exitCode = 1;
|
|
56179
|
+
return;
|
|
56180
|
+
}
|
|
56181
|
+
try {
|
|
56182
|
+
const result = await loginOAuthDevice({
|
|
56183
|
+
cloudUrl: cfg.cloudUrl,
|
|
56184
|
+
installationId: input.installationId,
|
|
56185
|
+
timeoutMs: 15 * 6e4,
|
|
56186
|
+
onVerification: ({ url: url2, userCode, expiresInSec }) => {
|
|
56187
|
+
console.log(`approve this device in the Console:
|
|
56188
|
+
${url2}`);
|
|
56189
|
+
console.log(`code: ${userCode} \xB7 expires in ${Math.round(expiresInSec / 60)} minutes`);
|
|
56190
|
+
}
|
|
56191
|
+
});
|
|
56192
|
+
const nextCfg = {
|
|
56193
|
+
...cfg,
|
|
56194
|
+
apiKey: null,
|
|
56195
|
+
accessToken: result.tokens.accessToken,
|
|
56196
|
+
refreshToken: result.tokens.refreshToken ?? null,
|
|
56197
|
+
tokenEndpoint: result.tokenEndpoint,
|
|
56198
|
+
oauthClientId: result.clientId,
|
|
56199
|
+
activeInstallationProfile: profileName,
|
|
56200
|
+
installationProfiles: {
|
|
56201
|
+
...cfg.installationProfiles,
|
|
56202
|
+
[profileName]: {
|
|
56203
|
+
installationId: result.installationId,
|
|
56204
|
+
cloudUrl: cfg.cloudUrl,
|
|
56205
|
+
apiKey: null,
|
|
56206
|
+
accessToken: result.tokens.accessToken,
|
|
56207
|
+
refreshToken: result.tokens.refreshToken ?? null,
|
|
56208
|
+
tokenEndpoint: result.tokenEndpoint,
|
|
56209
|
+
oauthClientId: result.clientId,
|
|
56210
|
+
activeAgent: cfg.activeAgent
|
|
56211
|
+
}
|
|
56212
|
+
}
|
|
56213
|
+
};
|
|
56214
|
+
const me = await authedCloudClient(nextCfg).me();
|
|
56215
|
+
nextCfg.userId = me.agentId;
|
|
56216
|
+
nextCfg.email = me.handle;
|
|
56217
|
+
saveConfig(nextCfg);
|
|
56218
|
+
console.log(
|
|
56219
|
+
`logged in as ${me.handle} (${me.tier}) \xB7 profile ${profileName} \xB7 installation ${result.installationId}`
|
|
56220
|
+
);
|
|
56221
|
+
await linkRegisteredWorkspaces(nextCfg);
|
|
56222
|
+
} catch (err2) {
|
|
56223
|
+
console.error(`device login failed: ${err2 instanceof Error ? err2.message : err2}`);
|
|
56224
|
+
console.error(" no credential was stored; retry from Console \u2192 Connect");
|
|
56225
|
+
process.exitCode = 1;
|
|
56226
|
+
}
|
|
56227
|
+
}
|
|
55675
56228
|
async function linkRegisteredWorkspaces(cfg) {
|
|
55676
56229
|
if (!cfg.consent.sync) return;
|
|
55677
56230
|
const client = authedCloudClient(cfg);
|
|
@@ -55682,9 +56235,7 @@ async function linkRegisteredWorkspaces(cfg) {
|
|
|
55682
56235
|
refreshRepoLocator(w.path, profile);
|
|
55683
56236
|
const out2 = await ensureProjectLink(w.path, profile, client);
|
|
55684
56237
|
if (out2.linked) {
|
|
55685
|
-
console.log(
|
|
55686
|
-
` linked ${profile.name} \u2192 project ${out2.projectId}${out2.created ? " (created)" : ""}`
|
|
55687
|
-
);
|
|
56238
|
+
console.log(` linked ${profile.name} \u2192 project ${out2.projectId}${out2.created ? " (created)" : ""}`);
|
|
55688
56239
|
}
|
|
55689
56240
|
} catch {
|
|
55690
56241
|
}
|
|
@@ -55766,7 +56317,9 @@ async function cmdUnlink() {
|
|
|
55766
56317
|
delete profile.projectLocator;
|
|
55767
56318
|
profile.projectLinkDisabled = true;
|
|
55768
56319
|
saveProfile(ROOT, profile);
|
|
55769
|
-
console.log(
|
|
56320
|
+
console.log(
|
|
56321
|
+
had ? `unlinked from project ${had} (ambient linking disabled \u2014 re-enable with \`errata link\`)` : "already unlinked (ambient linking disabled)"
|
|
56322
|
+
);
|
|
55770
56323
|
}
|
|
55771
56324
|
async function cmdLogin() {
|
|
55772
56325
|
const cfg = loadConfig();
|
|
@@ -55776,6 +56329,18 @@ async function cmdLogin() {
|
|
|
55776
56329
|
await applyToken(cfg, flags2.token);
|
|
55777
56330
|
return;
|
|
55778
56331
|
}
|
|
56332
|
+
if (flags2.device) {
|
|
56333
|
+
if (!flags2.installation) {
|
|
56334
|
+
console.error("device login requires --installation <uuid> from Console \u2192 Connect");
|
|
56335
|
+
process.exitCode = 1;
|
|
56336
|
+
return;
|
|
56337
|
+
}
|
|
56338
|
+
await cmdLoginOAuthDevice(cfg, {
|
|
56339
|
+
installationId: flags2.installation,
|
|
56340
|
+
...flags2.profile ? { profileName: flags2.profile } : {}
|
|
56341
|
+
});
|
|
56342
|
+
return;
|
|
56343
|
+
}
|
|
55779
56344
|
if (shouldUseOAuthLogin(flags2)) {
|
|
55780
56345
|
await cmdLoginOAuth(cfg, flags2.browser ?? false);
|
|
55781
56346
|
return;
|
|
@@ -55810,7 +56375,9 @@ async function cmdLogin() {
|
|
|
55810
56375
|
await warnIfApprovalUnreachable(dc.verificationUrl);
|
|
55811
56376
|
console.log(`approve this device in your browser:
|
|
55812
56377
|
${dc.verificationUrl}`);
|
|
55813
|
-
console.log(
|
|
56378
|
+
console.log(
|
|
56379
|
+
`(code expires in ${Math.round(dc.expiresIn / 60)} minutes \u2014 or paste a key: errata login --token <key>)`
|
|
56380
|
+
);
|
|
55814
56381
|
const deadline = Date.now() + dc.expiresIn * 1e3;
|
|
55815
56382
|
const intervalMs = Math.max(2, dc.interval) * 1e3;
|
|
55816
56383
|
for (; ; ) {
|
|
@@ -55879,6 +56446,48 @@ async function cmdUse(args2) {
|
|
|
55879
56446
|
const code = await runUse(action, ctx);
|
|
55880
56447
|
if (code !== 0) process.exitCode = code;
|
|
55881
56448
|
}
|
|
56449
|
+
function cmdInstallationProfile(args2) {
|
|
56450
|
+
const cfg = loadConfig();
|
|
56451
|
+
const action = args2[0] ?? "list";
|
|
56452
|
+
if (action === "list") {
|
|
56453
|
+
const entries = Object.entries(cfg.installationProfiles).sort(([a], [b]) => a.localeCompare(b));
|
|
56454
|
+
if (entries.length === 0) {
|
|
56455
|
+
console.log("(no installation profiles \u2014 create one in the Console, then run profile save)");
|
|
56456
|
+
return;
|
|
56457
|
+
}
|
|
56458
|
+
for (const [name3, profile] of entries) {
|
|
56459
|
+
const marker = cfg.activeInstallationProfile === name3 ? "*" : " ";
|
|
56460
|
+
console.log(`${marker} ${name3} \xB7 ${profile.installationId} \xB7 ${profile.cloudUrl}`);
|
|
56461
|
+
}
|
|
56462
|
+
return;
|
|
56463
|
+
}
|
|
56464
|
+
const name2 = args2[1]?.trim();
|
|
56465
|
+
if (!name2) throw new Error(`usage: errata profile ${action} <name>`);
|
|
56466
|
+
if (action === "use") {
|
|
56467
|
+
const selected = useInstallationProfile(name2, cfg);
|
|
56468
|
+
const profile = selected.installationProfiles[name2];
|
|
56469
|
+
console.log(`profile active: ${name2} \xB7 installation ${profile.installationId}`);
|
|
56470
|
+
return;
|
|
56471
|
+
}
|
|
56472
|
+
if (action === "remove") {
|
|
56473
|
+
removeInstallationProfile(name2, cfg);
|
|
56474
|
+
console.log(`profile removed locally: ${name2}`);
|
|
56475
|
+
return;
|
|
56476
|
+
}
|
|
56477
|
+
if (action === "save") {
|
|
56478
|
+
const direct = args2.find((arg) => arg.startsWith("--installation="))?.slice("--installation=".length);
|
|
56479
|
+
const flag = args2.indexOf("--installation");
|
|
56480
|
+
const installationId = direct ?? (flag >= 0 ? args2[flag + 1] : void 0);
|
|
56481
|
+
if (!installationId) throw new Error("usage: errata profile save <name> --installation <uuid>");
|
|
56482
|
+
if (!hasCloudCredential(cfg)) {
|
|
56483
|
+
throw new Error("not logged in \u2014 install the Console credential before saving a profile");
|
|
56484
|
+
}
|
|
56485
|
+
saveInstallationProfile(name2, installationId, cfg);
|
|
56486
|
+
console.log(`profile saved: ${name2} \xB7 installation ${installationId}`);
|
|
56487
|
+
return;
|
|
56488
|
+
}
|
|
56489
|
+
throw new Error(`unknown profile action: ${action}`);
|
|
56490
|
+
}
|
|
55882
56491
|
async function cmdReview() {
|
|
55883
56492
|
const paths = workspacePaths(ROOT);
|
|
55884
56493
|
if (!existsSync24(paths.reviewQueue)) {
|
|
@@ -55959,9 +56568,7 @@ async function cmdLocate(relPath) {
|
|
|
55959
56568
|
const file2 = findFileByPath2(store, relPath);
|
|
55960
56569
|
if (!file2) {
|
|
55961
56570
|
console.error(`not indexed: ${relPath}`);
|
|
55962
|
-
console.error(
|
|
55963
|
-
" \u2192 did you run `errata reindex --clean`? did you spell the relative path right?"
|
|
55964
|
-
);
|
|
56571
|
+
console.error(" \u2192 did you run `errata reindex --clean`? did you spell the relative path right?");
|
|
55965
56572
|
process.exit(2);
|
|
55966
56573
|
}
|
|
55967
56574
|
const symbols = store.outEdges(file2.id, ["DEFINES", "CONTAINS"]).map((e) => store.getNode(e.to)).filter((n) => n != null);
|
|
@@ -56209,9 +56816,7 @@ async function cmdSearch(args2) {
|
|
|
56209
56816
|
for (const h of dual.results) {
|
|
56210
56817
|
console.log(`${h.id}`);
|
|
56211
56818
|
console.log(` [${h.label}] ${h.name.slice(0, 100)}`);
|
|
56212
|
-
console.log(
|
|
56213
|
-
` score ${Number(h.score).toFixed(4)}${h.provenance ? ` \xB7 ${h.provenance}` : ""}`
|
|
56214
|
-
);
|
|
56819
|
+
console.log(` score ${Number(h.score).toFixed(4)}${h.provenance ? ` \xB7 ${h.provenance}` : ""}`);
|
|
56215
56820
|
}
|
|
56216
56821
|
return;
|
|
56217
56822
|
}
|
|
@@ -56321,9 +56926,7 @@ async function cmdSimilar(args2) {
|
|
|
56321
56926
|
}
|
|
56322
56927
|
console.log(`seed: ${r.seedId}`);
|
|
56323
56928
|
for (const h of r.hits) {
|
|
56324
|
-
console.log(
|
|
56325
|
-
` ${h.score.toFixed(4)} [${h.label}] ${h.name.slice(0, 80)} (${h.id})`
|
|
56326
|
-
);
|
|
56929
|
+
console.log(` ${h.score.toFixed(4)} [${h.label}] ${h.name.slice(0, 80)} (${h.id})`);
|
|
56327
56930
|
}
|
|
56328
56931
|
});
|
|
56329
56932
|
}
|
|
@@ -56355,7 +56958,10 @@ unresolved \u2014 no node matched "${seed}". Try \`errata search ${seed}\` for f
|
|
|
56355
56958
|
return;
|
|
56356
56959
|
}
|
|
56357
56960
|
process.stdout.write(
|
|
56358
|
-
formatBurstMd2(result, {
|
|
56961
|
+
formatBurstMd2(result, {
|
|
56962
|
+
limit,
|
|
56963
|
+
seedLabel: seed
|
|
56964
|
+
})
|
|
56359
56965
|
);
|
|
56360
56966
|
});
|
|
56361
56967
|
}
|
|
@@ -56433,8 +57039,18 @@ async function gatherRepo(store, ws) {
|
|
|
56433
57039
|
const sol = firstHop(store, p.id, ["SOLVED_BY", "FIXED_BY"]);
|
|
56434
57040
|
const cause = firstHop(store, p.id, ["CAUSED_BY", "MANIFESTS_AS"]);
|
|
56435
57041
|
const pi = addSpine(p, "Problem");
|
|
56436
|
-
if (cause)
|
|
56437
|
-
|
|
57042
|
+
if (cause)
|
|
57043
|
+
spineEdges.push({
|
|
57044
|
+
a: pi,
|
|
57045
|
+
b: addSpine(cause, spineType(cause.label, "RootCause")),
|
|
57046
|
+
kind: "causal"
|
|
57047
|
+
});
|
|
57048
|
+
if (sol)
|
|
57049
|
+
spineEdges.push({
|
|
57050
|
+
a: pi,
|
|
57051
|
+
b: addSpine(sol, spineType(sol.label, "Solution")),
|
|
57052
|
+
kind: "causal"
|
|
57053
|
+
});
|
|
56438
57054
|
if (pIdx >= LEARNED_MAX) continue;
|
|
56439
57055
|
const srcCount = Array.isArray(p.attrs["sources"]) ? p.attrs["sources"].length : p.attrs["sources"] ? 1 : 0;
|
|
56440
57056
|
const agents = Math.max(1, srcCount + Number(p.attrs["corroborations"] ?? 0));
|
|
@@ -56499,11 +57115,21 @@ async function gatherRepo(store, ws) {
|
|
|
56499
57115
|
if (byFile.size > 0) {
|
|
56500
57116
|
const ranked = [...byFile].sort((a, b) => b[1] - a[1]).slice(0, 6);
|
|
56501
57117
|
const maxC = ranked[0]?.[1] ?? 1;
|
|
56502
|
-
hotspots = ranked.map(([file2, c]) => ({
|
|
57118
|
+
hotspots = ranked.map(([file2, c]) => ({
|
|
57119
|
+
file: file2.split(/[\\/]/).pop() ?? file2,
|
|
57120
|
+
problemCount: c,
|
|
57121
|
+
weight: c / maxC,
|
|
57122
|
+
unit: "probs"
|
|
57123
|
+
}));
|
|
56503
57124
|
} else {
|
|
56504
57125
|
const fan = await runTool2("errata.hotspots", { kind: "fan-in", limit: 6 }, store);
|
|
56505
57126
|
const maxF = fan.items[0]?.fanIn ?? 1;
|
|
56506
|
-
hotspots = fan.items.map((h) => ({
|
|
57127
|
+
hotspots = fan.items.map((h) => ({
|
|
57128
|
+
file: h.name.split(/[\\/]/).pop() ?? h.name,
|
|
57129
|
+
problemCount: h.fanIn,
|
|
57130
|
+
weight: (h.fanIn ?? 0) / maxF,
|
|
57131
|
+
unit: "fan-in"
|
|
57132
|
+
}));
|
|
56507
57133
|
}
|
|
56508
57134
|
const revisit = await runTool2("errata.needs_revisit", {}, store);
|
|
56509
57135
|
const machineOnly = SEMANTIC_LABELS2.reduce((sum, l) => sum + store.findNodesByLabel(l).length, 0);
|
|
@@ -56594,14 +57220,14 @@ async function cmdReport(args2) {
|
|
|
56594
57220
|
for (const f of files) writeFileSync18(join26(outDir, f.name), f.html, "utf8");
|
|
56595
57221
|
const indexPath = join26(outDir, "report.html");
|
|
56596
57222
|
console.log(`report \u2192 ${indexPath}`);
|
|
56597
|
-
console.log(
|
|
57223
|
+
console.log(
|
|
57224
|
+
` ${data.repos.length} repo(s) \xB7 ${files.length} file(s) \xB7 ${data.rollup.nodes.toLocaleString("en-US")} nodes`
|
|
57225
|
+
);
|
|
56598
57226
|
console.log(` open: file://${indexPath.replace(/\\/g, "/")}`);
|
|
56599
57227
|
}
|
|
56600
57228
|
async function cmdStop() {
|
|
56601
57229
|
const { unlinkSync: unlinkSync4 } = await import("node:fs");
|
|
56602
|
-
const lockPath = [globalDaemonLock(), workspacePaths(ROOT).daemonLock].find(
|
|
56603
|
-
(p) => readDaemonLock(p) !== null
|
|
56604
|
-
);
|
|
57230
|
+
const lockPath = [globalDaemonLock(), workspacePaths(ROOT).daemonLock].find((p) => readDaemonLock(p) !== null);
|
|
56605
57231
|
if (!lockPath) {
|
|
56606
57232
|
console.log(`no daemon lock found \u2014 nothing to stop`);
|
|
56607
57233
|
return;
|
|
@@ -56676,9 +57302,7 @@ function ensureSingletonRunning() {
|
|
|
56676
57302
|
async function cmdInstallHooks(args2) {
|
|
56677
57303
|
const harness = (args2[0] && !args2[0].startsWith("-") ? args2[0] : "claude").toLowerCase();
|
|
56678
57304
|
const portIdx = args2.indexOf("--port");
|
|
56679
|
-
const port = resolveHookPort(
|
|
56680
|
-
portIdx >= 0 && args2[portIdx + 1] ? Number(args2[portIdx + 1]) : void 0
|
|
56681
|
-
);
|
|
57305
|
+
const port = resolveHookPort(portIdx >= 0 && args2[portIdx + 1] ? Number(args2[portIdx + 1]) : void 0);
|
|
56682
57306
|
switch (harness) {
|
|
56683
57307
|
case "claude":
|
|
56684
57308
|
await installClaudeHooks(port);
|
|
@@ -56984,7 +57608,13 @@ async function cmdDash(args2) {
|
|
|
56984
57608
|
const reindexOnStart = !args2.includes("--no-reindex");
|
|
56985
57609
|
const skipEmbed = !args2.includes("--embed");
|
|
56986
57610
|
const skipWatchers = args2.includes("--no-watch");
|
|
56987
|
-
const handle2 = await startMultiDaemon({
|
|
57611
|
+
const handle2 = await startMultiDaemon({
|
|
57612
|
+
webPort: port,
|
|
57613
|
+
reindexOnStart,
|
|
57614
|
+
skipEmbed,
|
|
57615
|
+
skipWatchers,
|
|
57616
|
+
updateCheck: true
|
|
57617
|
+
});
|
|
56988
57618
|
console.log(`errata multi-daemon running`);
|
|
56989
57619
|
console.log(` endpoint: ${handle2.url} (JSON \u2014 the human view is \`errata report\`)`);
|
|
56990
57620
|
console.log(` projects: ${handle2.records.length}`);
|
|
@@ -57041,7 +57671,10 @@ async function cmdDash(args2) {
|
|
|
57041
57671
|
scheduleQuiescenceFlush();
|
|
57042
57672
|
}
|
|
57043
57673
|
if (notifyEnabled && (r.problemsResolved || r.reviewsTriggered)) {
|
|
57044
|
-
notifyTick({
|
|
57674
|
+
notifyTick({
|
|
57675
|
+
problemsResolved: r.problemsResolved,
|
|
57676
|
+
reviewsTriggered: r.reviewsTriggered
|
|
57677
|
+
});
|
|
57045
57678
|
}
|
|
57046
57679
|
}
|
|
57047
57680
|
}).finally(() => {
|
|
@@ -57067,7 +57700,10 @@ async function cmdDash(args2) {
|
|
|
57067
57700
|
}
|
|
57068
57701
|
}
|
|
57069
57702
|
if (skillsLearned > 0) {
|
|
57070
|
-
notifyEvent(
|
|
57703
|
+
notifyEvent(
|
|
57704
|
+
"skill-learned",
|
|
57705
|
+
skillsLearned === 1 ? "Distilled a new reusable skill from your work." : `Distilled ${skillsLearned} new skills from your work.`
|
|
57706
|
+
);
|
|
57071
57707
|
}
|
|
57072
57708
|
const p = await handle2.percolateAll();
|
|
57073
57709
|
const touched = /* @__PURE__ */ new Set();
|
|
@@ -57158,7 +57794,9 @@ async function cmdDash(args2) {
|
|
|
57158
57794
|
const fanIn = wStore.inEdges(anchor.to, [...CODE_REACH_EDGES]).length;
|
|
57159
57795
|
if (fanIn < HOTSPOT_FANIN) continue;
|
|
57160
57796
|
const sym = wStore.getNode(anchor.to)?.description ?? "a symbol";
|
|
57161
|
-
notifyEvent("hotspot-problem", `"${p2.description.slice(0, 60)}" touches ${sym} (${fanIn} dependents).`, {
|
|
57797
|
+
notifyEvent("hotspot-problem", `"${p2.description.slice(0, 60)}" touches ${sym} (${fanIn} dependents).`, {
|
|
57798
|
+
key: p2.id
|
|
57799
|
+
});
|
|
57162
57800
|
}
|
|
57163
57801
|
}
|
|
57164
57802
|
void handle2.syncPrinciplesPublic().then((r) => {
|
|
@@ -57188,7 +57826,9 @@ async function cmdDash(args2) {
|
|
|
57188
57826
|
try {
|
|
57189
57827
|
await materializeOverview2(r.root, r.engine.store);
|
|
57190
57828
|
} catch (err2) {
|
|
57191
|
-
console.warn(
|
|
57829
|
+
console.warn(
|
|
57830
|
+
`[errata] overview refresh failed for ${r.entry.name}: ${err2 instanceof Error ? err2.message : err2}`
|
|
57831
|
+
);
|
|
57192
57832
|
}
|
|
57193
57833
|
}
|
|
57194
57834
|
maybeFlushDigests();
|
|
@@ -57203,7 +57843,12 @@ async function cmdDash(args2) {
|
|
|
57203
57843
|
if (consolidating) return;
|
|
57204
57844
|
if (process.uptime() < CONSOLIDATE_BOOT_GRACE_S) return;
|
|
57205
57845
|
consolidationState.momentum = totalMutations() - consolidatedAtMutations;
|
|
57206
|
-
if (!shouldConsolidate({
|
|
57846
|
+
if (!shouldConsolidate({
|
|
57847
|
+
now: Date.now(),
|
|
57848
|
+
state: consolidationState,
|
|
57849
|
+
policy: consolidationPolicy,
|
|
57850
|
+
force
|
|
57851
|
+
})) {
|
|
57207
57852
|
return;
|
|
57208
57853
|
}
|
|
57209
57854
|
consolidating = true;
|
|
@@ -57301,9 +57946,7 @@ async function cmdFeedback(args2) {
|
|
|
57301
57946
|
}
|
|
57302
57947
|
async function ensureProfile() {
|
|
57303
57948
|
if (!loadProfile(ROOT)) {
|
|
57304
|
-
console.error(
|
|
57305
|
-
`no errata workspace in ${ROOT}. Run 'errata init' first.`
|
|
57306
|
-
);
|
|
57949
|
+
console.error(`no errata workspace in ${ROOT}. Run 'errata init' first.`);
|
|
57307
57950
|
process.exit(2);
|
|
57308
57951
|
}
|
|
57309
57952
|
}
|