@alfe.ai/gateway 0.2.4 → 0.3.0
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/dist/bin/gateway.js +1 -1
- package/dist/health.js +206 -2
- package/dist/src/index.d.ts +66 -1
- package/dist/src/index.js +2 -2
- package/package.json +4 -3
package/dist/bin/gateway.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import { a as installService, c as uninstallService,
|
|
2
|
+
import { a as installService, c as uninstallService, i as checkExistingDaemon, n as queryDaemonHealth, r as startDaemon, s as stopExistingDaemon, t as formatHealthReport, v as SOCKET_PATH } from "../health.js";
|
|
3
3
|
import { t as LOG_FILE } from "../logger.js";
|
|
4
4
|
import { spawn } from "node:child_process";
|
|
5
5
|
//#region bin/gateway.ts
|
package/dist/health.js
CHANGED
|
@@ -7,7 +7,7 @@ import { dirname, join } from "node:path";
|
|
|
7
7
|
import { homedir } from "node:os";
|
|
8
8
|
import pino from "pino";
|
|
9
9
|
import { chmodSync, existsSync, mkdirSync, readFileSync, unlinkSync, writeFileSync } from "node:fs";
|
|
10
|
-
import { getEndpointFromToken, readConfig } from "@alfe.ai/config";
|
|
10
|
+
import { getEndpointFromToken, readConfig, resolveConfig } from "@alfe.ai/config";
|
|
11
11
|
import crypto from "crypto";
|
|
12
12
|
import { parse } from "smol-toml";
|
|
13
13
|
import WebSocket from "ws";
|
|
@@ -5330,6 +5330,195 @@ var NoopRuntimeGate = class {
|
|
|
5330
5330
|
requestRestart() {}
|
|
5331
5331
|
};
|
|
5332
5332
|
//#endregion
|
|
5333
|
+
//#region src/sentry.ts
|
|
5334
|
+
/**
|
|
5335
|
+
* Agent-side error reporting (Sentry) for the Alfe CLI + gateway daemon.
|
|
5336
|
+
*
|
|
5337
|
+
* This module is the single Sentry bootstrap shared by both published
|
|
5338
|
+
* agent-side packages — `@alfe.ai/gateway` (the daemon) and `@alfe.ai/cli`
|
|
5339
|
+
* (which re-imports these helpers from `@alfe.ai/gateway`). It exists so
|
|
5340
|
+
* integration-installation failures on customer/managed VMs surface centrally
|
|
5341
|
+
* instead of silently rotting until someone SSHes into the box.
|
|
5342
|
+
*
|
|
5343
|
+
* Design constraints (see packages/gateway/DEVELOPING.md → "Error reporting"):
|
|
5344
|
+
* - Errors only. No tracing/profiling (`tracesSampleRate: 0`, `sampleRate: 1`).
|
|
5345
|
+
* - `@sentry/node` is lazy-imported so `alfe --version` doesn't pay for it.
|
|
5346
|
+
* - Sentry can NEVER break the CLI/daemon — every entry point is try/catch'd.
|
|
5347
|
+
* - The DSNs are baked as constants: published tarballs run on agent VMs and
|
|
5348
|
+
* cannot read the repo's `config/config.ts`. That file's `SENTRY_DSNS`
|
|
5349
|
+
* registry (keys `agentDaemon` + `cli`) mirrors them as the human reference.
|
|
5350
|
+
* - Secrets are scrubbed in `beforeSend`; request/env payloads are dropped.
|
|
5351
|
+
* - Opt-out via `ALFE_ERROR_REPORTING=0|false` or `errorReporting = false`
|
|
5352
|
+
* in `~/.alfe/config.toml`. Default ON.
|
|
5353
|
+
*/
|
|
5354
|
+
/**
|
|
5355
|
+
* Baked per-surface Sentry DSNs (org `alfe-ai`). DSNs are public by design
|
|
5356
|
+
* (they only permit event ingestion). Mirrors of `config/config.ts` →
|
|
5357
|
+
* `SENTRY_DSNS.agentDaemon` / `SENTRY_DSNS.cli`. If you rotate a DSN, update
|
|
5358
|
+
* both places.
|
|
5359
|
+
*/
|
|
5360
|
+
const AGENT_DAEMON_SENTRY_DSN = "https://a47f010d8d39fb4350f913492189f283@o4511008239452160.ingest.us.sentry.io/4511679448547328";
|
|
5361
|
+
/** Surface → Sentry project: `cli` and `agent-daemon` respectively. */
|
|
5362
|
+
const SURFACE_DSNS = {
|
|
5363
|
+
cli: "https://82dde4631336561f2fcc89d7531623de@o4511008239452160.ingest.us.sentry.io/4511679411191808",
|
|
5364
|
+
daemon: AGENT_DAEMON_SENTRY_DSN
|
|
5365
|
+
};
|
|
5366
|
+
/** Held after a successful init so the capture helpers can run synchronously. */
|
|
5367
|
+
let sentry = null;
|
|
5368
|
+
/**
|
|
5369
|
+
* Map the configured API URL to a coarse environment tag. Best-effort — returns
|
|
5370
|
+
* `"unknown"` when no config is present yet (e.g. `alfe login` pre-setup).
|
|
5371
|
+
*/
|
|
5372
|
+
function deriveEnvironment() {
|
|
5373
|
+
try {
|
|
5374
|
+
const { apiUrl } = resolveConfig();
|
|
5375
|
+
if (apiUrl.includes("api.dev.alfe.ai") || apiUrl.includes("dev.alfe.ai")) return "dev";
|
|
5376
|
+
if (apiUrl.includes("api.test.alfe.ai")) return "test";
|
|
5377
|
+
if (apiUrl.includes("api.demo.alfe.ai")) return "demo";
|
|
5378
|
+
if (apiUrl.includes("api.alfe.ai")) return "prod";
|
|
5379
|
+
} catch {}
|
|
5380
|
+
return "unknown";
|
|
5381
|
+
}
|
|
5382
|
+
/** Active runtime (`openclaw`/`hermes`) from config, or undefined if unknown. */
|
|
5383
|
+
function deriveRuntime() {
|
|
5384
|
+
try {
|
|
5385
|
+
return resolveConfig().runtime;
|
|
5386
|
+
} catch {
|
|
5387
|
+
return;
|
|
5388
|
+
}
|
|
5389
|
+
}
|
|
5390
|
+
/** True when the operator has opted out of error reporting. */
|
|
5391
|
+
function errorReportingDisabled() {
|
|
5392
|
+
const env = process.env.ALFE_ERROR_REPORTING?.trim().toLowerCase();
|
|
5393
|
+
if (env === "0" || env === "false") return true;
|
|
5394
|
+
try {
|
|
5395
|
+
if (readConfig().errorReporting === false) return true;
|
|
5396
|
+
} catch {}
|
|
5397
|
+
return false;
|
|
5398
|
+
}
|
|
5399
|
+
const KV_SECRET_RE = /\b(authorization|api[_-]?key|apikey|token|secret|password|passwd|bearer)\b(\s*[:=]\s*|\s+)("?)([^\s"']+)\3/gi;
|
|
5400
|
+
const MIN_BARE_SECRET_LEN = 16;
|
|
5401
|
+
const ALFE_TOKEN_RE = /alfe_(?:dev|test|demo|live)_[A-Za-z0-9._-]+/g;
|
|
5402
|
+
const BEARER_RE = /\bbearer\s+[A-Za-z0-9._\-+/=]+/gi;
|
|
5403
|
+
function scrubString(value) {
|
|
5404
|
+
return value.replace(BEARER_RE, "Bearer [REDACTED]").replace(KV_SECRET_RE, (match, label, sep, _q, secret) => {
|
|
5405
|
+
if (!/[:=]/.test(sep) && secret.length < MIN_BARE_SECRET_LEN) return match;
|
|
5406
|
+
return `${label}${sep}[REDACTED]`;
|
|
5407
|
+
}).replace(ALFE_TOKEN_RE, "alfe_[REDACTED]");
|
|
5408
|
+
}
|
|
5409
|
+
/** Scrub secrets from a breadcrumb's message + string data values in place. */
|
|
5410
|
+
function scrubBreadcrumb(b) {
|
|
5411
|
+
if (typeof b.message === "string") b.message = scrubString(b.message);
|
|
5412
|
+
const data = b.data;
|
|
5413
|
+
if (data) for (const k of Object.keys(data)) {
|
|
5414
|
+
const dv = data[k];
|
|
5415
|
+
if (typeof dv === "string") data[k] = scrubString(dv);
|
|
5416
|
+
}
|
|
5417
|
+
return b;
|
|
5418
|
+
}
|
|
5419
|
+
/**
|
|
5420
|
+
* Scrub secrets and drop request/env payloads before an event is sent.
|
|
5421
|
+
* Generic so it preserves the caller's exact event subtype (`beforeSend`
|
|
5422
|
+
* receives — and must return — an `ErrorEvent`, not a widened `Event`).
|
|
5423
|
+
*/
|
|
5424
|
+
function scrubEvent(event) {
|
|
5425
|
+
delete event.request;
|
|
5426
|
+
if (event.contexts) delete event.contexts.runtime;
|
|
5427
|
+
delete event.extra;
|
|
5428
|
+
delete event.server_name;
|
|
5429
|
+
if (typeof event.message === "string") event.message = scrubString(event.message);
|
|
5430
|
+
const values = event.exception?.values;
|
|
5431
|
+
if (values) {
|
|
5432
|
+
for (const v of values) if (typeof v.value === "string") v.value = scrubString(v.value);
|
|
5433
|
+
}
|
|
5434
|
+
if (event.breadcrumbs) for (const b of event.breadcrumbs) scrubBreadcrumb(b);
|
|
5435
|
+
return event;
|
|
5436
|
+
}
|
|
5437
|
+
/**
|
|
5438
|
+
* Initialise Sentry for the agent-side CLI/daemon. Idempotent, best-effort, and
|
|
5439
|
+
* a no-op when opted out or when the baked DSN is empty. Never throws.
|
|
5440
|
+
*/
|
|
5441
|
+
async function initAgentSentry(options) {
|
|
5442
|
+
try {
|
|
5443
|
+
if (sentry) return;
|
|
5444
|
+
if (errorReportingDisabled()) return;
|
|
5445
|
+
const dsn = SURFACE_DSNS[options.surface].trim();
|
|
5446
|
+
if (!dsn) return;
|
|
5447
|
+
const mod = await import("@sentry/node");
|
|
5448
|
+
if (mod.getClient()) {
|
|
5449
|
+
sentry = mod;
|
|
5450
|
+
return;
|
|
5451
|
+
}
|
|
5452
|
+
mod.init({
|
|
5453
|
+
dsn,
|
|
5454
|
+
environment: deriveEnvironment(),
|
|
5455
|
+
...options.release ? { release: options.release } : {},
|
|
5456
|
+
sampleRate: 1,
|
|
5457
|
+
tracesSampleRate: 0,
|
|
5458
|
+
sendDefaultPii: false,
|
|
5459
|
+
beforeSend: (event) => scrubEvent(event),
|
|
5460
|
+
beforeBreadcrumb: (breadcrumb) => scrubBreadcrumb(breadcrumb)
|
|
5461
|
+
});
|
|
5462
|
+
sentry = mod;
|
|
5463
|
+
mod.setTag("surface", options.surface);
|
|
5464
|
+
const runtime = deriveRuntime();
|
|
5465
|
+
if (runtime) mod.setTag("runtime", runtime);
|
|
5466
|
+
} catch {
|
|
5467
|
+
sentry = null;
|
|
5468
|
+
}
|
|
5469
|
+
}
|
|
5470
|
+
/**
|
|
5471
|
+
* Attach the resolved agent identity to all subsequent events. Called by the
|
|
5472
|
+
* daemon once `loadDaemonConfig()` has resolved `agentId`/`runtime`.
|
|
5473
|
+
*/
|
|
5474
|
+
function setAgentContext(context) {
|
|
5475
|
+
if (!sentry) return;
|
|
5476
|
+
try {
|
|
5477
|
+
if (context.agentId) sentry.setTag("agentId", context.agentId);
|
|
5478
|
+
if (context.runtime) sentry.setTag("runtime", context.runtime);
|
|
5479
|
+
} catch {}
|
|
5480
|
+
}
|
|
5481
|
+
/**
|
|
5482
|
+
* Capture an integration lifecycle failure (install/activate/reinstall/remove)
|
|
5483
|
+
* with `{ integration, phase }` tags. Accepts an `Error` (captured with stack)
|
|
5484
|
+
* or a plain message string (captured as an error-level message). No-op when
|
|
5485
|
+
* Sentry is not initialised.
|
|
5486
|
+
*/
|
|
5487
|
+
function captureIntegrationFailure(integration, phase, cause) {
|
|
5488
|
+
if (!sentry) return;
|
|
5489
|
+
try {
|
|
5490
|
+
if (cause instanceof Error) sentry.captureException(cause, { tags: {
|
|
5491
|
+
integration,
|
|
5492
|
+
phase
|
|
5493
|
+
} });
|
|
5494
|
+
else sentry.captureMessage(String(cause), {
|
|
5495
|
+
level: "error",
|
|
5496
|
+
tags: {
|
|
5497
|
+
integration,
|
|
5498
|
+
phase
|
|
5499
|
+
}
|
|
5500
|
+
});
|
|
5501
|
+
} catch {}
|
|
5502
|
+
}
|
|
5503
|
+
/** Flush queued events (small timeout) so short-lived commands deliver them. */
|
|
5504
|
+
async function flushSentry(timeoutMs = 2e3) {
|
|
5505
|
+
if (!sentry) return;
|
|
5506
|
+
try {
|
|
5507
|
+
await sentry.flush(timeoutMs);
|
|
5508
|
+
} catch {}
|
|
5509
|
+
}
|
|
5510
|
+
/**
|
|
5511
|
+
* Capture a fatal error — for the CLI's top-level catch. Does NOT flush: the
|
|
5512
|
+
* caller's `finally { await flushSentry() }` delivers it (flushing here too
|
|
5513
|
+
* doubled the worst-case exit delay on a failing command).
|
|
5514
|
+
*/
|
|
5515
|
+
function captureFatal(cause) {
|
|
5516
|
+
if (!sentry) return;
|
|
5517
|
+
try {
|
|
5518
|
+
sentry.captureException(cause);
|
|
5519
|
+
} catch {}
|
|
5520
|
+
}
|
|
5521
|
+
//#endregion
|
|
5333
5522
|
//#region src/reconciliation.ts
|
|
5334
5523
|
const log$2 = logger$1.child({ component: "Reconciliation" });
|
|
5335
5524
|
/**
|
|
@@ -5466,6 +5655,7 @@ var ReconciliationEngine = class {
|
|
|
5466
5655
|
const attempts = this.manager.getReinstallAttempts(id);
|
|
5467
5656
|
if (attempts >= 3) {
|
|
5468
5657
|
log$2.error(`Auto-reinstall blocked for ${id} — ${String(attempts)} consecutive failures. Manual reinstall required.`);
|
|
5658
|
+
captureIntegrationFailure(id, "reinstall", `Max auto-reinstall attempts (${String(attempts)}) reached — manual reinstall required`);
|
|
5469
5659
|
report.errors.push({
|
|
5470
5660
|
integrationId: id,
|
|
5471
5661
|
error: `Max auto-reinstall attempts (${String(attempts)}) reached`
|
|
@@ -5495,6 +5685,7 @@ var ReconciliationEngine = class {
|
|
|
5495
5685
|
} catch (reinstallErr) {
|
|
5496
5686
|
const reinstallMsg = reinstallErr instanceof Error ? reinstallErr.message : String(reinstallErr);
|
|
5497
5687
|
log$2.error({ err: reinstallErr }, `Auto-reinstall failed for ${id}`);
|
|
5688
|
+
captureIntegrationFailure(id, "reinstall", reinstallErr);
|
|
5498
5689
|
report.errors.push({
|
|
5499
5690
|
integrationId: id,
|
|
5500
5691
|
error: reinstallMsg
|
|
@@ -5526,6 +5717,7 @@ var ReconciliationEngine = class {
|
|
|
5526
5717
|
} catch (reactivateErr) {
|
|
5527
5718
|
const reactivateMsg = reactivateErr instanceof Error ? reactivateErr.message : String(reactivateErr);
|
|
5528
5719
|
log$2.warn(`Re-activation of ${id} from error state failed (attempt ${String(activateAttempts + 1)}/${String(MAX_ERROR_ACTIVATE_ATTEMPTS)}): ${reactivateMsg}`);
|
|
5720
|
+
captureIntegrationFailure(id, "reactivate", reactivateErr);
|
|
5529
5721
|
report.errors.push({
|
|
5530
5722
|
integrationId: id,
|
|
5531
5723
|
error: reactivateMsg
|
|
@@ -5540,6 +5732,7 @@ var ReconciliationEngine = class {
|
|
|
5540
5732
|
}
|
|
5541
5733
|
}
|
|
5542
5734
|
log$2.warn(`Integration ${id} is in error state — re-activation exhausted, waiting for reinstall request`);
|
|
5735
|
+
captureIntegrationFailure(id, "reactivate", `Integration ${id} stuck in error state — re-activation attempts exhausted`);
|
|
5543
5736
|
report.errors.push({
|
|
5544
5737
|
integrationId: id,
|
|
5545
5738
|
error: "Integration is in error state"
|
|
@@ -5588,6 +5781,7 @@ var ReconciliationEngine = class {
|
|
|
5588
5781
|
} catch (err) {
|
|
5589
5782
|
const message = err instanceof Error ? err.message : String(err);
|
|
5590
5783
|
log$2.error({ err }, `Error reconciling ${id}`);
|
|
5784
|
+
captureIntegrationFailure(id, "reconcile_active", err);
|
|
5591
5785
|
report.errors.push({
|
|
5592
5786
|
integrationId: id,
|
|
5593
5787
|
error: message
|
|
@@ -5626,6 +5820,7 @@ var ReconciliationEngine = class {
|
|
|
5626
5820
|
} catch (err) {
|
|
5627
5821
|
const message = err instanceof Error ? err.message : String(err);
|
|
5628
5822
|
log$2.error({ err }, `Error removing ${id}`);
|
|
5823
|
+
captureIntegrationFailure(id, "reconcile_removed", err);
|
|
5629
5824
|
report.errors.push({
|
|
5630
5825
|
integrationId: id,
|
|
5631
5826
|
error: message
|
|
@@ -22530,6 +22725,7 @@ async function getRuntimeVersion(runtime) {
|
|
|
22530
22725
|
* process.exit() can drop buffered log lines — this ensures they're written first.
|
|
22531
22726
|
*/
|
|
22532
22727
|
async function flushAndExit(code) {
|
|
22728
|
+
await flushSentry();
|
|
22533
22729
|
await new Promise((resolve) => {
|
|
22534
22730
|
logger$1.flush();
|
|
22535
22731
|
setTimeout(resolve, 500);
|
|
@@ -22592,6 +22788,10 @@ async function startDaemon() {
|
|
|
22592
22788
|
managed,
|
|
22593
22789
|
pid: process.pid
|
|
22594
22790
|
}, "Starting Alfe Gateway Daemon...");
|
|
22791
|
+
await initAgentSentry({
|
|
22792
|
+
surface: "daemon",
|
|
22793
|
+
release: await getCliVersion()
|
|
22794
|
+
});
|
|
22595
22795
|
if (!managed) {
|
|
22596
22796
|
await mkdir(join(homedir(), ".alfe"), { recursive: true });
|
|
22597
22797
|
const existingPid = await checkExistingDaemon();
|
|
@@ -22608,6 +22808,10 @@ async function startDaemon() {
|
|
|
22608
22808
|
orgId: config.orgId,
|
|
22609
22809
|
wsUrl: config.gatewayWsUrl
|
|
22610
22810
|
}, "Config loaded, identity resolved");
|
|
22811
|
+
setAgentContext({
|
|
22812
|
+
agentId: config.agentId,
|
|
22813
|
+
runtime: config.runtime
|
|
22814
|
+
});
|
|
22611
22815
|
} catch (err) {
|
|
22612
22816
|
const message = err instanceof Error ? err.message : String(err);
|
|
22613
22817
|
const stack = err instanceof Error ? err.stack : void 0;
|
|
@@ -23575,4 +23779,4 @@ function formatDuration(ms) {
|
|
|
23575
23779
|
return `${String(Math.round(seconds / 3600))}h`;
|
|
23576
23780
|
}
|
|
23577
23781
|
//#endregion
|
|
23578
|
-
export { installService as a, uninstallService as c,
|
|
23782
|
+
export { PINNED_OPENCLAW_VERSION as S, PID_PATH as _, installService as a, loadDaemonConfig as b, uninstallService as c, captureIntegrationFailure as d, flushSentry as f, ALFE_DIR as g, PROTOCOL_VERSION as h, checkExistingDaemon as i, AGENT_DAEMON_SENTRY_DSN as l, setAgentContext as m, queryDaemonHealth as n, startService as o, initAgentSentry as p, startDaemon as r, stopExistingDaemon as s, formatHealthReport as t, captureFatal as u, SOCKET_PATH as v, resolveAgentIdentity as x, fetchAgentConfig as y };
|
package/dist/src/index.d.ts
CHANGED
|
@@ -46,6 +46,71 @@ declare function startDaemon(): Promise<void>;
|
|
|
46
46
|
*/
|
|
47
47
|
declare const PINNED_OPENCLAW_VERSION = "2026.6.8";
|
|
48
48
|
//#endregion
|
|
49
|
+
//#region src/sentry.d.ts
|
|
50
|
+
/**
|
|
51
|
+
* Agent-side error reporting (Sentry) for the Alfe CLI + gateway daemon.
|
|
52
|
+
*
|
|
53
|
+
* This module is the single Sentry bootstrap shared by both published
|
|
54
|
+
* agent-side packages — `@alfe.ai/gateway` (the daemon) and `@alfe.ai/cli`
|
|
55
|
+
* (which re-imports these helpers from `@alfe.ai/gateway`). It exists so
|
|
56
|
+
* integration-installation failures on customer/managed VMs surface centrally
|
|
57
|
+
* instead of silently rotting until someone SSHes into the box.
|
|
58
|
+
*
|
|
59
|
+
* Design constraints (see packages/gateway/DEVELOPING.md → "Error reporting"):
|
|
60
|
+
* - Errors only. No tracing/profiling (`tracesSampleRate: 0`, `sampleRate: 1`).
|
|
61
|
+
* - `@sentry/node` is lazy-imported so `alfe --version` doesn't pay for it.
|
|
62
|
+
* - Sentry can NEVER break the CLI/daemon — every entry point is try/catch'd.
|
|
63
|
+
* - The DSNs are baked as constants: published tarballs run on agent VMs and
|
|
64
|
+
* cannot read the repo's `config/config.ts`. That file's `SENTRY_DSNS`
|
|
65
|
+
* registry (keys `agentDaemon` + `cli`) mirrors them as the human reference.
|
|
66
|
+
* - Secrets are scrubbed in `beforeSend`; request/env payloads are dropped.
|
|
67
|
+
* - Opt-out via `ALFE_ERROR_REPORTING=0|false` or `errorReporting = false`
|
|
68
|
+
* in `~/.alfe/config.toml`. Default ON.
|
|
69
|
+
*/
|
|
70
|
+
/**
|
|
71
|
+
* Baked per-surface Sentry DSNs (org `alfe-ai`). DSNs are public by design
|
|
72
|
+
* (they only permit event ingestion). Mirrors of `config/config.ts` →
|
|
73
|
+
* `SENTRY_DSNS.agentDaemon` / `SENTRY_DSNS.cli`. If you rotate a DSN, update
|
|
74
|
+
* both places.
|
|
75
|
+
*/
|
|
76
|
+
declare const AGENT_DAEMON_SENTRY_DSN = "https://a47f010d8d39fb4350f913492189f283@o4511008239452160.ingest.us.sentry.io/4511679448547328";
|
|
77
|
+
/** Which surface initialised Sentry — tagged on every event. */
|
|
78
|
+
type SentrySurface = "cli" | "daemon";
|
|
79
|
+
interface InitAgentSentryOptions {
|
|
80
|
+
/** `"cli"` or `"daemon"` — tagged as `surface`. */
|
|
81
|
+
surface: SentrySurface;
|
|
82
|
+
/** Release identifier — the package version. */
|
|
83
|
+
release?: string;
|
|
84
|
+
}
|
|
85
|
+
/**
|
|
86
|
+
* Initialise Sentry for the agent-side CLI/daemon. Idempotent, best-effort, and
|
|
87
|
+
* a no-op when opted out or when the baked DSN is empty. Never throws.
|
|
88
|
+
*/
|
|
89
|
+
declare function initAgentSentry(options: InitAgentSentryOptions): Promise<void>;
|
|
90
|
+
/**
|
|
91
|
+
* Attach the resolved agent identity to all subsequent events. Called by the
|
|
92
|
+
* daemon once `loadDaemonConfig()` has resolved `agentId`/`runtime`.
|
|
93
|
+
*/
|
|
94
|
+
declare function setAgentContext(context: {
|
|
95
|
+
agentId?: string;
|
|
96
|
+
runtime?: string;
|
|
97
|
+
}): void;
|
|
98
|
+
/**
|
|
99
|
+
* Capture an integration lifecycle failure (install/activate/reinstall/remove)
|
|
100
|
+
* with `{ integration, phase }` tags. Accepts an `Error` (captured with stack)
|
|
101
|
+
* or a plain message string (captured as an error-level message). No-op when
|
|
102
|
+
* Sentry is not initialised.
|
|
103
|
+
*/
|
|
104
|
+
declare function captureIntegrationFailure(integration: string, phase: string, cause: unknown): void;
|
|
105
|
+
/** Flush queued events (small timeout) so short-lived commands deliver them. */
|
|
106
|
+
declare function flushSentry(timeoutMs?: number): Promise<void>;
|
|
107
|
+
/**
|
|
108
|
+
* Capture a fatal error — for the CLI's top-level catch. Does NOT flush: the
|
|
109
|
+
* caller's `finally { await flushSentry() }` delivers it (flushing here too
|
|
110
|
+
* doubled the worst-case exit delay on a failing command).
|
|
111
|
+
*/
|
|
112
|
+
declare function captureFatal(cause: unknown): void;
|
|
113
|
+
//#endregion
|
|
49
114
|
//#region src/config.d.ts
|
|
50
115
|
/**
|
|
51
116
|
* Daemon configuration — reads ~/.alfe/config.toml and resolves agent identity.
|
|
@@ -218,4 +283,4 @@ declare function checkExistingDaemon(): Promise<number | null>;
|
|
|
218
283
|
*/
|
|
219
284
|
declare function stopExistingDaemon(): Promise<boolean>;
|
|
220
285
|
//#endregion
|
|
221
|
-
export { ALFE_DIR, type AgentIdentity, type AgentWorkspaceConfig, type DaemonConfig, type DaemonHealth, type IPCEvent, type IPCRequest, type IPCResponse, PID_PATH, PINNED_OPENCLAW_VERSION, PROTOCOL_VERSION, SOCKET_PATH, checkExistingDaemon, fetchAgentConfig, formatHealthReport, installService, loadDaemonConfig, logger, queryDaemonHealth, resolveAgentIdentity, startDaemon, startService, stopExistingDaemon, uninstallService };
|
|
286
|
+
export { AGENT_DAEMON_SENTRY_DSN, ALFE_DIR, type AgentIdentity, type AgentWorkspaceConfig, type DaemonConfig, type DaemonHealth, type IPCEvent, type IPCRequest, type IPCResponse, type InitAgentSentryOptions, PID_PATH, PINNED_OPENCLAW_VERSION, PROTOCOL_VERSION, SOCKET_PATH, type SentrySurface, captureFatal, captureIntegrationFailure, checkExistingDaemon, fetchAgentConfig, flushSentry, formatHealthReport, initAgentSentry, installService, loadDaemonConfig, logger, queryDaemonHealth, resolveAgentIdentity, setAgentContext, startDaemon, startService, stopExistingDaemon, uninstallService };
|
package/dist/src/index.js
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
import { a as installService, c as uninstallService, d as
|
|
1
|
+
import { S as PINNED_OPENCLAW_VERSION, _ as PID_PATH, a as installService, b as loadDaemonConfig, c as uninstallService, d as captureIntegrationFailure, f as flushSentry, g as ALFE_DIR, h as PROTOCOL_VERSION, i as checkExistingDaemon, l as AGENT_DAEMON_SENTRY_DSN, m as setAgentContext, n as queryDaemonHealth, o as startService, p as initAgentSentry, r as startDaemon, s as stopExistingDaemon, t as formatHealthReport, u as captureFatal, v as SOCKET_PATH, x as resolveAgentIdentity, y as fetchAgentConfig } from "../health.js";
|
|
2
2
|
import { n as logger } from "../logger.js";
|
|
3
|
-
export { ALFE_DIR, PID_PATH, PINNED_OPENCLAW_VERSION, PROTOCOL_VERSION, SOCKET_PATH, checkExistingDaemon, fetchAgentConfig, formatHealthReport, installService, loadDaemonConfig, logger, queryDaemonHealth, resolveAgentIdentity, startDaemon, startService, stopExistingDaemon, uninstallService };
|
|
3
|
+
export { AGENT_DAEMON_SENTRY_DSN, ALFE_DIR, PID_PATH, PINNED_OPENCLAW_VERSION, PROTOCOL_VERSION, SOCKET_PATH, captureFatal, captureIntegrationFailure, checkExistingDaemon, fetchAgentConfig, flushSentry, formatHealthReport, initAgentSentry, installService, loadDaemonConfig, logger, queryDaemonHealth, resolveAgentIdentity, setAgentContext, startDaemon, startService, stopExistingDaemon, uninstallService };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@alfe.ai/gateway",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.3.0",
|
|
4
4
|
"description": "Alfe local gateway daemon — persistent control plane for agent integrations",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -18,15 +18,16 @@
|
|
|
18
18
|
"dist"
|
|
19
19
|
],
|
|
20
20
|
"dependencies": {
|
|
21
|
+
"@sentry/node": "^10.42.0",
|
|
21
22
|
"pino": "^9.6.0",
|
|
22
23
|
"pino-roll": "^1.2.0",
|
|
23
24
|
"smol-toml": ">=1.6.1",
|
|
24
25
|
"ws": "^8.18.0",
|
|
25
|
-
"@alfe.ai/agent-api-client": "^0.
|
|
26
|
+
"@alfe.ai/agent-api-client": "^0.5.0",
|
|
26
27
|
"@alfe.ai/ai-proxy-local": "^0.0.13",
|
|
27
28
|
"@alfe.ai/config": "^0.3.0",
|
|
28
29
|
"@alfe.ai/integration-manifest": "^0.3.1",
|
|
29
|
-
"@alfe.ai/integrations": "^0.2.
|
|
30
|
+
"@alfe.ai/integrations": "^0.2.7",
|
|
30
31
|
"@alfe.ai/mcp-bundler": "^0.2.2"
|
|
31
32
|
},
|
|
32
33
|
"license": "UNLICENSED",
|