@inerrata-corporation/errata 2.0.0-dev.26 → 2.0.0-dev.28

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.
Files changed (2) hide show
  1. package/errata.mjs +99 -40
  2. package/package.json +1 -1
package/errata.mjs CHANGED
@@ -19760,6 +19760,9 @@ function renderSnapshot(s) {
19760
19760
  lines.push("- `(fix:[id])` \u2014 your edit fixed a problem errata showed you (ids appear in the priors below / in file priors)");
19761
19761
  lines.push("Tag every one you state \u2014 we filter downstream, so don't self-censor or batch. This is the witness only you can give; untagged, it's lost.");
19762
19762
  lines.push("");
19763
+ lines.push(SEARCH_IMPERATIVE_HEADER);
19764
+ lines.push(SEARCH_IMPERATIVE_BODY);
19765
+ lines.push("");
19763
19766
  lines.push(`### Active workspace profile`);
19764
19767
  lines.push(`- **Name:** ${s.profile.name}`);
19765
19768
  const ctxTag = (e) => e.node && s.edgeElicitation ? `${e.name} \`[${priorHandle(e.node)}]\`` : e.name;
@@ -19809,7 +19812,7 @@ function renderSnapshot(s) {
19809
19812
  lines.push("- _none yet \u2014 errata is still building its model_");
19810
19813
  } else {
19811
19814
  if (s.recentProblems.length > 0) {
19812
- lines.push("_Open \u2014 what errata knows is currently broken here, derived from your work:_");
19815
+ lines.push("_Open \u2014 recently observed here, derived from your work (a recency slice, not an inventory \u2014 burst an id for its full neighborhood):_");
19813
19816
  for (const r of s.recentProblems) {
19814
19817
  lines.push(`- **${r.node.description}** \u2014 \`${r.node.id}\`${tagOf(r.node)}`);
19815
19818
  const hits = r.node.cumulativeHits ?? 0;
@@ -19840,7 +19843,7 @@ function renderSnapshot(s) {
19840
19843
  }
19841
19844
  lines.push("");
19842
19845
  }
19843
- lines.push("### Relevant prior knowledge from the shared graph");
19846
+ lines.push("### Prior knowledge sampled from the shared graph");
19844
19847
  if (s.motifs.length === 0) {
19845
19848
  lines.push("- _no motif matches yet \u2014 cold-start ingest pending_");
19846
19849
  } else {
@@ -19852,7 +19855,7 @@ function renderSnapshot(s) {
19852
19855
  if (s.remote && s.remote.length > 0) {
19853
19856
  lines.push("### From the Errata Network \u2014 collective priors");
19854
19857
  lines.push(
19855
- "_Knowledge from OTHER codebases facing your stack/domain, pulled live from the shared Network (not yet local). Weigh accordingly \u2014 it's corroborated across teams, but not from this repo._"
19858
+ "_A small sample of knowledge from OTHER codebases facing your stack/domain (not yet local \u2014 the Network holds far more; these ids seed deeper bursts). Weigh accordingly \u2014 it's corroborated across teams, but not from this repo._"
19856
19859
  );
19857
19860
  for (const m of s.remote) {
19858
19861
  lines.push(`- **${m.label}:** ${m.description}${tagOf(m)}`);
@@ -19968,12 +19971,14 @@ _${dropped} lower-priority item${dropped === 1 ? "" : "s"} omitted to fit the pa
19968
19971
  }
19969
19972
  return { body: body2, snapshot, dropped };
19970
19973
  }
19971
- var EVICTION_ORDER, DEFAULT_AGENT_CONTEXT_BUDGET;
19974
+ var SEARCH_IMPERATIVE_HEADER, SEARCH_IMPERATIVE_BODY, EVICTION_ORDER, DEFAULT_AGENT_CONTEXT_BUDGET;
19972
19975
  var init_render = __esm({
19973
19976
  "../../packages/context-writer/src/render.ts"() {
19974
19977
  "use strict";
19975
19978
  init_src4();
19976
19979
  init_src2();
19980
+ SEARCH_IMPERATIVE_HEADER = "### \u{1F50E} PRIORS ARE SEEDS \u2014 a sample, not the whole graph";
19981
+ SEARCH_IMPERATIVE_BODY = "Everything below is a budgeted, top-of-head slice of a much larger graph. Any prior id below is a live burst seed: `errata.burst` it (or read `.errata/g/burst/<id>`) to pull its wider neighborhood \u2014 causes, fixes, siblings. When a prior is adjacent-but-not-quite, or none fit, that gap is exactly when to search deeper before solving cold: a stuck search is itself a signal that routes help to you.";
19977
19982
  EVICTION_ORDER = [
19978
19983
  "skills",
19979
19984
  "motifs",
@@ -20628,7 +20633,7 @@ import { randomUUID as randomUUID2 } from "node:crypto";
20628
20633
  function defaultConfig() {
20629
20634
  return {
20630
20635
  version: 1,
20631
- cloudUrl: process.env["ERRATA_CLOUD_URL"] ?? "http://localhost:8787",
20636
+ cloudUrl: process.env["ERRATA_CLOUD_URL"] ?? DEFAULT_CLOUD_URL,
20632
20637
  apiKey: null,
20633
20638
  accessToken: null,
20634
20639
  refreshToken: null,
@@ -20674,10 +20679,12 @@ function machineId() {
20674
20679
  saveConfig({ ...cfg, machineId: id });
20675
20680
  return id;
20676
20681
  }
20682
+ var DEFAULT_CLOUD_URL;
20677
20683
  var init_config = __esm({
20678
20684
  "src/config.ts"() {
20679
20685
  "use strict";
20680
20686
  init_paths();
20687
+ DEFAULT_CLOUD_URL = "https://inerrata-gateway.onrender.com";
20681
20688
  }
20682
20689
  });
20683
20690
 
@@ -20751,7 +20758,7 @@ function assertDeviceCodeEndpointAllowed(url2, inspection) {
20751
20758
  allowed: false,
20752
20759
  label: "gateway device-code blocked",
20753
20760
  reason: "device-code login mints legacy v1 keys, while gateway launch uses console-issued keys/JWTs",
20754
- guidance: "Use `errata login --token <console errk_...>` or `errata login --oauth`."
20761
+ guidance: "Use `errata login` for OAuth or `errata login --token <console errk_...>`."
20755
20762
  };
20756
20763
  throw new CloudEndpointPolicyError(formatCloudEndpointBlock(url2, decision), decision);
20757
20764
  }
@@ -20895,6 +20902,7 @@ function authedCloudClient(cfg, opts = {}) {
20895
20902
  accessToken: cfg.accessToken,
20896
20903
  ...cfg.refreshToken ? { refreshToken: cfg.refreshToken } : {},
20897
20904
  fetchFn,
20905
+ ...opts.timeoutMs ? { timeoutMs: opts.timeoutMs } : {},
20898
20906
  ...tokenEndpoint && cfg.refreshToken ? {
20899
20907
  refreshFn: (rt) => refreshAccessToken(opts.fetchFn ?? fetch, tokenEndpoint, {
20900
20908
  refreshToken: rt,
@@ -20913,9 +20921,13 @@ function authedCloudClient(cfg, opts = {}) {
20913
20921
  return new CloudClient({
20914
20922
  baseUrl,
20915
20923
  ...cfg.apiKey ? { apiKey: cfg.apiKey } : {},
20916
- fetchFn
20924
+ fetchFn,
20925
+ ...opts.timeoutMs ? { timeoutMs: opts.timeoutMs } : {}
20917
20926
  });
20918
20927
  }
20928
+ function hasCloudCredential(cfg) {
20929
+ return Boolean(cfg.accessToken || cfg.apiKey);
20930
+ }
20919
20931
  var init_cloud_auth = __esm({
20920
20932
  "src/cloud-auth.ts"() {
20921
20933
  "use strict";
@@ -34482,7 +34494,7 @@ function createMcpHandler(store, ctx = {}) {
34482
34494
  function buildToolContext() {
34483
34495
  try {
34484
34496
  const cfg = loadConfig();
34485
- if (cfg.consent.sync && cfg.apiKey && cfg.cloudUrl) {
34497
+ if (cfg.consent.sync && hasCloudCredential(cfg) && cfg.cloudUrl) {
34486
34498
  return { cloud: authedCloudClient(cfg) };
34487
34499
  }
34488
34500
  } catch {
@@ -34719,7 +34731,36 @@ var init_mcp = __esm({
34719
34731
  },
34720
34732
  handler: async (args2, store, ctx) => {
34721
34733
  const id = resolveSymbol2(store, args2);
34722
- if (!id) return unresolved(args2);
34734
+ if (!id) {
34735
+ const seedId = typeof args2["nodeId"] === "string" ? args2["nodeId"] : null;
34736
+ if (seedId && ctx.cloud) {
34737
+ const res = await ctx.cloud.search({
34738
+ stack: [],
34739
+ domains: [],
34740
+ kinds: [],
34741
+ seed: [seedId],
34742
+ limit: args2["limit"] != null ? Number(args2["limit"]) : 20
34743
+ }).catch(() => null);
34744
+ if (res && res.nodes.length > 0) {
34745
+ return {
34746
+ seed: seedId,
34747
+ seedProvenance: "collective",
34748
+ collectiveReachable: true,
34749
+ collectiveStatus: "cloud-seed",
34750
+ count: res.nodes.length,
34751
+ results: res.nodes.map((n) => ({
34752
+ id: n.id,
34753
+ label: n.label,
34754
+ name: n.description,
34755
+ score: Number((n.extractionConfidence ?? 0.5).toFixed(4)),
34756
+ hops: 1,
34757
+ provenance: "collective"
34758
+ }))
34759
+ };
34760
+ }
34761
+ }
34762
+ return unresolved(args2);
34763
+ }
34723
34764
  const ranked = localBurst(store, id, {
34724
34765
  ...args2["maxHops"] != null ? { maxHops: Number(args2["maxHops"]) } : {},
34725
34766
  ...args2["limit"] != null ? { limit: Number(args2["limit"]) } : {}
@@ -40858,11 +40899,11 @@ var TelemetryRecorder = class {
40858
40899
  return out2;
40859
40900
  }
40860
40901
  /**
40861
- * Flush to the cloud iff consent + apiKey are present. Returns how many were
40902
+ * Flush to the cloud iff consent + a cloud credential are present. Returns how many were
40862
40903
  * accepted (0 when skipped). On network failure the batch is re-queued.
40863
40904
  */
40864
40905
  async flush(cloud, opts) {
40865
- if (!opts.consent || !opts.apiKey) {
40906
+ if (!opts.consent || !opts.credentialPresent) {
40866
40907
  this.drain();
40867
40908
  return { sent: 0, skipped: true };
40868
40909
  }
@@ -41156,6 +41197,7 @@ function autodetectProfile(root) {
41156
41197
 
41157
41198
  // src/engine.ts
41158
41199
  init_config();
41200
+ init_cloud_auth();
41159
41201
 
41160
41202
  // src/notify.ts
41161
41203
  init_config();
@@ -41274,7 +41316,7 @@ function maybeFlushDigests() {
41274
41316
  }
41275
41317
 
41276
41318
  // src/engine.ts
41277
- var DAEMON_VERSION = true ? "2.0.0-dev.26" : "2.0.0-alpha.0";
41319
+ var DAEMON_VERSION = true ? "2.0.0-dev.28" : "2.0.0-alpha.0";
41278
41320
  var IGNORED_PATH = /[\\/](?:\.git|node_modules|\.errata|\.claude|\.codex|\.cursor|\.turbo|\.next|dist|coverage|test-results|playwright-report|__pycache__)(?:[\\/]|$)/;
41279
41321
  var IGNORED_NOISE = /castalia\.db|eventlog\.sqlite|turn-cursors/;
41280
41322
  var GIT_OP_MUTE_MS = 4e3;
@@ -42277,7 +42319,7 @@ function createWorkspaceEngine(opts) {
42277
42319
  }
42278
42320
  const t = await telemetry.flush(cloud, {
42279
42321
  consent: freshCfg.consent.telemetry,
42280
- apiKey: opts.apiKey ?? freshCfg.apiKey
42322
+ credentialPresent: Boolean(opts.apiKey) || hasCloudCredential(freshCfg)
42281
42323
  });
42282
42324
  if (t.sent > 0) {
42283
42325
  console.log(`[errata] telemetry: ${t.sent} events sent`);
@@ -42314,6 +42356,8 @@ async function startDaemon(opts) {
42314
42356
  if (opts.apiKey) {
42315
42357
  merged.apiKey = opts.apiKey;
42316
42358
  merged.accessToken = null;
42359
+ merged.refreshToken = null;
42360
+ merged.tokenEndpoint = null;
42317
42361
  }
42318
42362
  const cloud = authedCloudClient(merged);
42319
42363
  let webUiUrl = "";
@@ -42463,7 +42507,6 @@ function pidAlive(pid) {
42463
42507
 
42464
42508
  // src/multi.ts
42465
42509
  init_dist();
42466
- init_src6();
42467
42510
  init_src4();
42468
42511
  import { readFileSync as readFileSync17, unlinkSync as unlinkSync3, writeFileSync as writeFileSync14 } from "node:fs";
42469
42512
 
@@ -43064,6 +43107,7 @@ function isDaemonAlive(lockPath) {
43064
43107
  // src/multi.ts
43065
43108
  init_config();
43066
43109
  init_cloud_endpoint_policy();
43110
+ init_cloud_auth();
43067
43111
  function normPath(p) {
43068
43112
  return p.replace(/\\/g, "/").replace(/\/+$/, "").toLowerCase();
43069
43113
  }
@@ -43098,21 +43142,25 @@ async function startMultiDaemon(opts = {}) {
43098
43142
  const cfg = loadConfig();
43099
43143
  const CLOUD_TIMEOUT_MS = 12e4;
43100
43144
  const initialCloudUrl = normalizeCloudUrl(opts.cloudUrl ?? cfg.cloudUrl);
43101
- const cloud = new CloudClient({
43102
- baseUrl: initialCloudUrl,
43103
- timeoutMs: CLOUD_TIMEOUT_MS,
43104
- fetchFn: guardCloudFetch(initialCloudUrl),
43105
- ...opts.apiKey ?? cfg.apiKey ? { apiKey: opts.apiKey ?? cfg.apiKey ?? void 0 } : {}
43106
- });
43145
+ const initialCfg = { ...cfg, cloudUrl: initialCloudUrl };
43146
+ if (opts.apiKey) {
43147
+ initialCfg.apiKey = opts.apiKey;
43148
+ initialCfg.accessToken = null;
43149
+ initialCfg.refreshToken = null;
43150
+ initialCfg.tokenEndpoint = null;
43151
+ }
43152
+ const cloud = authedCloudClient(initialCfg, { timeoutMs: CLOUD_TIMEOUT_MS });
43107
43153
  const cloudNow = () => {
43108
43154
  const c = loadConfig();
43109
43155
  const baseUrl2 = normalizeCloudUrl(opts.cloudUrl ?? c.cloudUrl);
43110
- return new CloudClient({
43111
- baseUrl: baseUrl2,
43112
- timeoutMs: CLOUD_TIMEOUT_MS,
43113
- fetchFn: guardCloudFetch(baseUrl2),
43114
- ...opts.apiKey ?? c.apiKey ? { apiKey: opts.apiKey ?? c.apiKey ?? void 0 } : {}
43115
- });
43156
+ const current = { ...c, cloudUrl: baseUrl2 };
43157
+ if (opts.apiKey) {
43158
+ current.apiKey = opts.apiKey;
43159
+ current.accessToken = null;
43160
+ current.refreshToken = null;
43161
+ current.tokenEndpoint = null;
43162
+ }
43163
+ return authedCloudClient(current, { timeoutMs: CLOUD_TIMEOUT_MS });
43116
43164
  };
43117
43165
  const sharedDbPath = sharedStorePath();
43118
43166
  ensureParent(sharedDbPath);
@@ -43813,7 +43861,7 @@ async function loginOAuthLoopback(opts) {
43813
43861
  if (opts.timeoutMs) {
43814
43862
  setTimeout(() => {
43815
43863
  server.close();
43816
- reject(new Error("login timed out \u2014 run `errata login --oauth` again"));
43864
+ reject(new Error("login timed out \u2014 run `errata login` again"));
43817
43865
  }, opts.timeoutMs).unref?.();
43818
43866
  }
43819
43867
  }
@@ -44087,8 +44135,8 @@ Commands:
44087
44135
  Flags: --port N (default 7891)
44088
44136
  mcp Run the MCP stdio server \u2014 the agent's full errata tool
44089
44137
  surface (navigation, problems, claims, burst, health)
44090
- login Register or sign in with the cloud
44091
- logout Clear local API key
44138
+ login Sign in with the cloud (OAuth by default; --device for legacy)
44139
+ logout Clear local cloud credentials
44092
44140
  sync now Flush outbox to the cloud once
44093
44141
  privacy Show what is collected/scrubbed + your consent state
44094
44142
  consent <channel> <on|off>
@@ -44100,7 +44148,7 @@ Commands:
44100
44148
  Desktop toasts on problem open/resolve + review ready
44101
44149
 
44102
44150
  Environment:
44103
- ERRATA_CLOUD_URL Cloud base URL (default http://localhost:8787)
44151
+ ERRATA_CLOUD_URL Cloud base URL (default ${DEFAULT_CLOUD_URL})
44104
44152
  ERRATA_ALLOW_DIRECT_V1_CLOUD=1 Allow non-local legacy v1 cloud testing
44105
44153
  `);
44106
44154
  }
@@ -44188,7 +44236,7 @@ async function cmdInit() {
44188
44236
  console.log("");
44189
44237
  console.log("Next:");
44190
44238
  console.log(" errata start # observe this repo");
44191
- if (!cfgNow.apiKey) {
44239
+ if (!hasCloudCredential(cfgNow)) {
44192
44240
  console.log(" errata login # connect a cloud account (or: --token <key>)");
44193
44241
  }
44194
44242
  if (!cfgNow.consent.sync) {
@@ -44288,7 +44336,7 @@ async function cmdStatus() {
44288
44336
  );
44289
44337
  console.log(` cloud:`);
44290
44338
  console.log(` url: ${cfg.cloudUrl}`);
44291
- console.log(` logged in: ${cfg.apiKey || cfg.accessToken ? `yes (${cfg.email})` : "no"}`);
44339
+ console.log(` logged in: ${hasCloudCredential(cfg) ? `yes (${cfg.email})` : "no"}`);
44292
44340
  const inspection = await inspectCloudEndpoint(cfg.cloudUrl);
44293
44341
  const decision = evaluateCloudEndpoint(cfg.cloudUrl, inspection);
44294
44342
  const service = inspection.service ? ` \xB7 service ${inspection.service}` : "";
@@ -44297,7 +44345,7 @@ async function cmdStatus() {
44297
44345
  console.log(` policy: blocked - ${decision.reason}`);
44298
44346
  if (decision.guidance) console.log(` ${decision.guidance}`);
44299
44347
  }
44300
- if (cfg.apiKey || cfg.accessToken) {
44348
+ if (hasCloudCredential(cfg)) {
44301
44349
  if (!decision.allowed) {
44302
44350
  console.log(
44303
44351
  ` reachable: ${inspection.reachable ? `yes (${inspection.probe})` : `no (${inspection.error ?? "unreachable"})`}`
@@ -44381,6 +44429,7 @@ function parseFlags(args2) {
44381
44429
  else if (a === "--token") out2.token = args2[++i2];
44382
44430
  else if (a.startsWith("--token=")) out2.token = a.slice("--token=".length);
44383
44431
  else if (a === "--oauth") out2.oauth = true;
44432
+ else if (a === "--device") out2.device = true;
44384
44433
  else if (!a.startsWith("--")) out2._.push(a);
44385
44434
  }
44386
44435
  return out2;
@@ -44406,6 +44455,9 @@ async function applyToken(cfg, token) {
44406
44455
  try {
44407
44456
  const me = await c.me();
44408
44457
  cfg.apiKey = token;
44458
+ cfg.accessToken = null;
44459
+ cfg.refreshToken = null;
44460
+ cfg.tokenEndpoint = null;
44409
44461
  cfg.userId = me.agentId;
44410
44462
  cfg.email = me.handle;
44411
44463
  saveConfig(cfg);
@@ -44442,7 +44494,7 @@ async function cmdLoginOAuth(cfg) {
44442
44494
  result = await loginOAuthLoopback({ cloudUrl: cfg.cloudUrl, timeoutMs: 5 * 6e4 });
44443
44495
  } catch (err2) {
44444
44496
  console.error(`oauth login failed: ${err2 instanceof Error ? err2.message : err2}`);
44445
- console.error(` fall back to device code: errata login`);
44497
+ console.error(` fall back to device code: errata login --device`);
44446
44498
  console.error(` or paste a key: errata login --token <key>`);
44447
44499
  process.exitCode = 1;
44448
44500
  return;
@@ -44450,6 +44502,7 @@ async function cmdLoginOAuth(cfg) {
44450
44502
  cfg.accessToken = result.tokens.accessToken;
44451
44503
  cfg.refreshToken = result.tokens.refreshToken ?? null;
44452
44504
  cfg.tokenEndpoint = result.tokenEndpoint;
44505
+ cfg.apiKey = null;
44453
44506
  saveConfig(cfg);
44454
44507
  try {
44455
44508
  const me = await authedCloudClient(cfg).me();
@@ -44469,7 +44522,7 @@ async function cmdLogin() {
44469
44522
  await applyToken(cfg, flags2.token);
44470
44523
  return;
44471
44524
  }
44472
- if (flags2.oauth) {
44525
+ if (flags2.oauth || !flags2.device) {
44473
44526
  await cmdLoginOAuth(cfg);
44474
44527
  return;
44475
44528
  }
@@ -44517,6 +44570,9 @@ async function cmdLogin() {
44517
44570
  if (poll.status === "pending") continue;
44518
44571
  if (poll.status === "authorized" && poll.apiKey) {
44519
44572
  cfg.apiKey = poll.apiKey;
44573
+ cfg.accessToken = null;
44574
+ cfg.refreshToken = null;
44575
+ cfg.tokenEndpoint = null;
44520
44576
  cfg.userId = poll.agentId ?? null;
44521
44577
  cfg.email = poll.handle ?? handle2;
44522
44578
  saveConfig(cfg);
@@ -44541,6 +44597,9 @@ async function cmdLogin() {
44541
44597
  async function cmdLogout() {
44542
44598
  const cfg = loadConfig();
44543
44599
  cfg.apiKey = null;
44600
+ cfg.accessToken = null;
44601
+ cfg.refreshToken = null;
44602
+ cfg.tokenEndpoint = null;
44544
44603
  cfg.userId = null;
44545
44604
  cfg.email = null;
44546
44605
  saveConfig(cfg);
@@ -44904,7 +44963,7 @@ async function cmdBurst(args2) {
44904
44963
  let ctx = {};
44905
44964
  if (!local) {
44906
44965
  const cfg = loadConfig();
44907
- if (cfg.apiKey || cfg.accessToken) ctx = { cloud: authedCloudClient(cfg) };
44966
+ if (hasCloudCredential(cfg)) ctx = { cloud: authedCloudClient(cfg) };
44908
44967
  }
44909
44968
  await withStore(async (store) => {
44910
44969
  const result = await runTool2(
@@ -45496,7 +45555,7 @@ async function cmdPrivacy() {
45496
45555
  sync: ${onoff(cfg.consent.sync)}
45497
45556
  telemetry: ${onoff(cfg.consent.telemetry)}
45498
45557
  packages: ${onoff(cfg.consent.contributePackages)}
45499
- logged in: ${cfg.apiKey ? `yes (${cfg.email})` : "no"}
45558
+ logged in: ${hasCloudCredential(cfg) ? `yes (${cfg.email})` : "no"}
45500
45559
 
45501
45560
  Change it: errata consent sync on | errata consent telemetry off
45502
45561
  Kill switch: errata consent all off (or errata logout to drop your key)
@@ -45519,8 +45578,8 @@ async function cmdConsent(args2) {
45519
45578
  console.log(
45520
45579
  `consent updated \u2192 sync=${cfg.consent.sync ? "on" : "off"} telemetry=${cfg.consent.telemetry ? "on" : "off"} packages=${cfg.consent.contributePackages ? "on" : "off"}`
45521
45580
  );
45522
- if ((channel === "sync" || channel === "all") && value && !cfg.apiKey) {
45523
- console.log(" note: run `errata login` to actually upload (no API key yet).");
45581
+ if ((channel === "sync" || channel === "all") && value && !hasCloudCredential(cfg)) {
45582
+ console.log(" note: run `errata login` to actually upload (no cloud credential yet).");
45524
45583
  }
45525
45584
  }
45526
45585
  async function cmdDash(args2) {
@@ -45812,7 +45871,7 @@ async function cmdNotifications(state) {
45812
45871
  }
45813
45872
  async function cmdFeedback(args2) {
45814
45873
  const cfg = loadConfig();
45815
- if (!cfg.apiKey) {
45874
+ if (!hasCloudCredential(cfg)) {
45816
45875
  console.error("not logged in \u2014 run `errata login` first.");
45817
45876
  process.exit(2);
45818
45877
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@inerrata-corporation/errata",
3
- "version": "2.0.0-dev.26",
3
+ "version": "2.0.0-dev.28",
4
4
  "description": "errata - local-first observation engine for AI coding agents",
5
5
  "type": "module",
6
6
  "bin": {