@ory/argus 0.14.0 → 1.0.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.
Files changed (150) hide show
  1. package/README.md +31 -46
  2. package/assets/commands/temporal-up.md +1 -1
  3. package/assets/skills/auth-setup/SKILL.md +1 -1
  4. package/assets/skills/local-dev/SKILL.md +17 -7
  5. package/assets/skills/ory-build-agent/SKILL.md +39 -91
  6. package/assets/skills/ory-e2b-sandbox/SKILL.md +18 -17
  7. package/assets/skills/ory-temporal-worker/SKILL.md +31 -33
  8. package/assets/skills/permissions-onboarding/SKILL.md +131 -104
  9. package/dist/adapters.d.ts +88 -32
  10. package/dist/adapters.js +443 -164
  11. package/dist/agent-auth.d.ts +226 -67
  12. package/dist/agent-auth.js +951 -205
  13. package/dist/auth-store.d.ts +37 -2
  14. package/dist/auth-store.js +37 -3
  15. package/dist/auth.d.ts +33 -4
  16. package/dist/auth.js +163 -20
  17. package/dist/bash-parser.d.ts +98 -0
  18. package/dist/bash-parser.js +396 -0
  19. package/dist/branding.d.ts +77 -16
  20. package/dist/branding.js +93 -23
  21. package/dist/build-info.json +4 -4
  22. package/dist/cli-invocation.d.ts +1 -1
  23. package/dist/cli-invocation.js +2 -1
  24. package/dist/cli.d.ts +20 -29
  25. package/dist/cli.js +271 -239
  26. package/dist/client.d.ts +175 -138
  27. package/dist/client.js +672 -391
  28. package/dist/config.d.ts +217 -54
  29. package/dist/config.js +461 -42
  30. package/dist/context.d.ts +10 -0
  31. package/dist/context.js +21 -0
  32. package/dist/contract-suite.d.ts +3 -5
  33. package/dist/contract-suite.js +75 -47
  34. package/dist/denial.d.ts +36 -3
  35. package/dist/denial.js +79 -10
  36. package/dist/event-reporter.d.ts +77 -0
  37. package/dist/event-reporter.js +776 -0
  38. package/dist/external-registrations-main.d.ts +10 -0
  39. package/dist/external-registrations-main.js +38 -0
  40. package/dist/external-registrations.d.ts +79 -0
  41. package/dist/external-registrations.js +188 -0
  42. package/dist/help-cli.d.ts +39 -0
  43. package/dist/help-cli.js +55 -0
  44. package/dist/hook-timeout.d.ts +64 -0
  45. package/dist/hook-timeout.js +88 -0
  46. package/dist/index.d.ts +28 -23
  47. package/dist/index.js +173 -58
  48. package/dist/lifecycle.d.ts +3 -3
  49. package/dist/lifecycle.js +38 -6
  50. package/dist/local/cli.js +11 -6
  51. package/dist/local/configs.d.ts +74 -18
  52. package/dist/local/configs.js +291 -84
  53. package/dist/local/health.js +7 -7
  54. package/dist/local/index.d.ts +2 -2
  55. package/dist/local/index.js +24 -10
  56. package/dist/local/manager.d.ts +20 -1
  57. package/dist/local/manager.js +159 -36
  58. package/dist/local/ports.d.ts +158 -0
  59. package/dist/local/ports.js +443 -0
  60. package/dist/local/seed.d.ts +13 -5
  61. package/dist/local/seed.js +62 -38
  62. package/dist/logger.d.ts +54 -25
  63. package/dist/logger.js +329 -63
  64. package/dist/mcp.d.ts +2 -2
  65. package/dist/mcp.js +10 -5
  66. package/dist/mirror-bootstrap.d.ts +48 -0
  67. package/dist/mirror-bootstrap.js +254 -0
  68. package/dist/opl.d.ts +289 -0
  69. package/dist/opl.js +446 -0
  70. package/dist/permission-mode.d.ts +87 -0
  71. package/dist/permission-mode.js +307 -0
  72. package/dist/permissions-cli.d.ts +13 -49
  73. package/dist/permissions-cli.js +154 -348
  74. package/dist/permissions.d.ts +148 -38
  75. package/dist/permissions.js +591 -45
  76. package/dist/post-install.d.ts +33 -0
  77. package/dist/post-install.js +127 -0
  78. package/dist/read-credential.d.ts +65 -0
  79. package/dist/read-credential.js +86 -0
  80. package/dist/registry/cli.js +5 -2
  81. package/dist/registry/config.d.ts +0 -17
  82. package/dist/registry/config.js +0 -23
  83. package/dist/registry/index.d.ts +1 -1
  84. package/dist/registry/index.js +2 -2
  85. package/dist/registry/manager.d.ts +4 -21
  86. package/dist/registry/manager.js +21 -37
  87. package/dist/runtime-credential.d.ts +140 -0
  88. package/dist/runtime-credential.js +572 -0
  89. package/dist/runtime.d.ts +408 -0
  90. package/dist/runtime.js +748 -0
  91. package/dist/setup.d.ts +23 -47
  92. package/dist/setup.js +59 -116
  93. package/dist/skills.js +0 -7
  94. package/dist/status-cli.d.ts +27 -11
  95. package/dist/status-cli.js +121 -115
  96. package/dist/status-data.d.ts +116 -17
  97. package/dist/status-data.js +121 -38
  98. package/dist/subject.d.ts +126 -20
  99. package/dist/subject.js +215 -30
  100. package/dist/testing.d.ts +74 -38
  101. package/dist/testing.js +185 -68
  102. package/dist/tool-catalog.d.ts +53 -11
  103. package/dist/tool-catalog.js +164 -13
  104. package/dist/tool-metadata.d.ts +7 -6
  105. package/dist/tool-metadata.js +6 -5
  106. package/dist/types.d.ts +11 -1
  107. package/dist/uninstall.d.ts +71 -34
  108. package/dist/uninstall.js +217 -85
  109. package/dist/user-login.d.ts +9 -10
  110. package/dist/user-login.js +56 -75
  111. package/dist/watch-cli.d.ts +6 -0
  112. package/dist/watch-cli.js +217 -0
  113. package/package.json +4 -27
  114. package/assets/commands/dashboard.md +0 -34
  115. package/dist/dashboard-cli.d.ts +0 -8
  116. package/dist/dashboard-cli.js +0 -70
  117. package/dist/dev.d.ts +0 -103
  118. package/dist/dev.js +0 -583
  119. package/dist/interactive-setup.d.ts +0 -286
  120. package/dist/interactive-setup.js +0 -1734
  121. package/dist/local/jaeger-main.d.ts +0 -13
  122. package/dist/local/jaeger-main.js +0 -85
  123. package/dist/local/jaeger.d.ts +0 -50
  124. package/dist/local/jaeger.js +0 -162
  125. package/dist/otel/exporter.d.ts +0 -17
  126. package/dist/otel/exporter.js +0 -12
  127. package/dist/otel/index.d.ts +0 -2
  128. package/dist/otel/index.js +0 -8
  129. package/dist/otel/otlp.d.ts +0 -103
  130. package/dist/otel/otlp.js +0 -385
  131. package/dist/project-api-key.d.ts +0 -69
  132. package/dist/project-api-key.js +0 -147
  133. package/dist/setup-actions.d.ts +0 -232
  134. package/dist/setup-actions.js +0 -507
  135. package/dist/tracer.d.ts +0 -190
  136. package/dist/tracer.js +0 -481
  137. package/dist/watch-sandbox.d.ts +0 -9
  138. package/dist/watch-sandbox.js +0 -81
  139. package/dist/web/api.d.ts +0 -33
  140. package/dist/web/api.js +0 -294
  141. package/dist/web/launch.d.ts +0 -11
  142. package/dist/web/launch.js +0 -96
  143. package/dist/web/server.d.ts +0 -20
  144. package/dist/web/server.js +0 -233
  145. package/dist/web/types.d.ts +0 -65
  146. package/dist/web/types.js +0 -2
  147. package/dist/webapp/assets/index-Wucl4SZs.css +0 -1
  148. package/dist/webapp/assets/index-m-GtEdq0.js +0 -49
  149. package/dist/webapp/favicon.ico +0 -0
  150. package/dist/webapp/index.html +0 -15
package/dist/tracer.d.ts DELETED
@@ -1,190 +0,0 @@
1
- /**
2
- * Distributed tracing for Ory agent plugin hooks.
3
- *
4
- * Replaces simple audit logging with structured, correlated trace spans.
5
- * Every plugin resolves to the same unified TraceEvent classification —
6
- * there is no plugin-specific output.
7
- *
8
- * Key concepts:
9
- * - **TraceEvent**: A canonical event type (e.g. "tool.invoke", "permission.check").
10
- * - **TraceSpan**: A completed unit of work with timing and status.
11
- * - **ActiveSpan**: An in-flight span that must be `.end()`-ed.
12
- * - **Tracer**: EventEmitter that collects spans, writes NDJSON, and
13
- * supports live watching via the "span" event.
14
- *
15
- * Trace correlation: call `tracer.setContext()` with a traceId derived
16
- * from the session ID so all spans within the same agent session share
17
- * a single trace, even across separate hook process invocations.
18
- */
19
- import { EventEmitter } from "node:events";
20
- import type { SpanExporter } from "./otel/exporter.js";
21
- export type TraceEvent = "session.start" | "session.end" | "session.verify" | "user.auth" | "user.prompt" | "user.interaction" | "agent.auth" | "oauth2.introspect" | "oauth2.login" | "oauth2.refresh" | "permission.check" | "permission.batch_check" | "permission.observe_deny" | "tool.invoke" | "tool.complete" | "tool.block" | "tool.fail" | "turn.stop" | "subagent.start" | "subagent.stop" | "notification" | "compaction" | "relationship.create" | "relationship.delete" | "relationship.patch" | "hook.receive" | "hook.passthrough" | "config.resolve";
22
- export type SpanStatus = "ok" | "error" | "denied" | "skipped";
23
- export interface TraceSpan {
24
- traceId: string;
25
- spanId: string;
26
- parentSpanId?: string;
27
- event: TraceEvent;
28
- status: SpanStatus;
29
- startedAt: string;
30
- endedAt: string;
31
- durationMs: number;
32
- harness: string;
33
- hostname: string;
34
- user: string;
35
- pid: number;
36
- cwd: string;
37
- pluginVersion: string;
38
- nodeVersion: string;
39
- /** Git branch of `cwd` if it is a git working tree. */
40
- gitBranch?: string;
41
- /** Short git SHA of HEAD in `cwd` if it is a git working tree. */
42
- gitCommit?: string;
43
- sessionId?: string;
44
- attributes?: Record<string, unknown>;
45
- }
46
- export interface SpanOptions {
47
- traceId?: string;
48
- parentSpanId?: string;
49
- sessionId?: string;
50
- attributes?: Record<string, unknown>;
51
- }
52
- export declare class ActiveSpan {
53
- readonly traceId: string;
54
- readonly spanId: string;
55
- readonly parentSpanId: string | undefined;
56
- readonly event: TraceEvent;
57
- readonly startedAt: string;
58
- readonly sessionId: string | undefined;
59
- private readonly startMs;
60
- private _ended;
61
- private readonly tracer;
62
- private readonly baseAttributes;
63
- /** @internal — use `tracer.startSpan()` instead. */
64
- constructor(tracer: Tracer, event: TraceEvent, opts: SpanOptions & {
65
- resolvedTraceId: string;
66
- });
67
- get ended(): boolean;
68
- /**
69
- * Complete the span. Returns the recorded TraceSpan.
70
- */
71
- end(status: SpanStatus, attributes?: Record<string, unknown>): TraceSpan;
72
- }
73
- export interface TracerOptions {
74
- harness: string;
75
- traceFile?: string;
76
- /**
77
- * Optional span exporter (e.g. OTLP/HTTP). Each completed span is
78
- * dispatched fire-and-forget; the tracer never awaits the export and
79
- * exporter failures must not throw.
80
- */
81
- exporter?: SpanExporter;
82
- }
83
- export interface TracerContext {
84
- traceId?: string;
85
- spanId?: string;
86
- sessionId?: string;
87
- }
88
- /**
89
- * Process-level metadata captured once per Tracer and emitted on every span.
90
- * These values are constant for the lifetime of the process.
91
- */
92
- export interface ProcessMetadata {
93
- hostname: string;
94
- user: string;
95
- pid: number;
96
- cwd: string;
97
- pluginVersion: string;
98
- nodeVersion: string;
99
- gitBranch?: string;
100
- gitCommit?: string;
101
- }
102
- /**
103
- * Returns a record of attributes to merge into every span's attribute
104
- * bag at span-end. Used by `OryAgentClient` to inject the current
105
- * `userSubject` / `agentSubject` onto every span without each handler
106
- * having to remember to attach them. Must not throw and should be cheap;
107
- * it is invoked on every span.
108
- */
109
- export type SpanAttributeEnricher = () => Record<string, unknown> | undefined;
110
- export declare class Tracer extends EventEmitter {
111
- readonly harness: string;
112
- readonly processMetadata: ProcessMetadata;
113
- private readonly traceFile;
114
- private readonly _spans;
115
- private _context;
116
- private _exporter;
117
- private _enricher;
118
- constructor(opts: TracerOptions);
119
- /** Backwards-compatible accessors for the most common metadata fields. */
120
- get hostname(): string;
121
- get user(): string;
122
- /**
123
- * Set the ambient context. Subsequent spans inherit traceId,
124
- * parentSpanId, and sessionId from the context unless overridden.
125
- */
126
- setContext(ctx: TracerContext): void;
127
- clearContext(): void;
128
- get context(): Readonly<TracerContext>;
129
- /**
130
- * Start a span that must be completed with `.end(status)`.
131
- * Use this for operations with meaningful duration (API calls, etc.).
132
- */
133
- startSpan(event: TraceEvent, opts?: SpanOptions): ActiveSpan;
134
- /**
135
- * Record a completed span in one call. Use this for instantaneous
136
- * events (tool invoked, tool blocked, etc.).
137
- */
138
- record(event: TraceEvent, status: SpanStatus, opts?: SpanOptions): TraceSpan;
139
- /** All recorded spans in this process. */
140
- spans(): ReadonlyArray<TraceSpan>;
141
- /**
142
- * Attach (or replace) the span exporter. Useful when constructing
143
- * the tracer before env-driven configuration has resolved.
144
- */
145
- setExporter(exporter: SpanExporter | null): void;
146
- get exporter(): SpanExporter | null;
147
- /**
148
- * Install (or replace) an attribute enricher. The enricher is invoked
149
- * on every span at end-time and its return value is merged into the
150
- * span's attributes (with caller-supplied attributes taking precedence).
151
- * Pass `null` to clear.
152
- */
153
- setAttributeEnricher(enricher: SpanAttributeEnricher | null): void;
154
- /** @internal — invoked by `ActiveSpan.end()`. Never throws. */
155
- _callEnricher(): Record<string, unknown> | undefined;
156
- /**
157
- * Flush any pending exports. Call before the host process exits so
158
- * the OTLP HTTP request has a chance to complete.
159
- */
160
- shutdown(): Promise<void>;
161
- /** @internal — called by ActiveSpan.end() */
162
- _record(span: TraceSpan): void;
163
- private dispatchExport;
164
- private writeToFile;
165
- }
166
- /**
167
- * Derive a stable trace ID from a session ID so all hook invocations
168
- * within the same agent session correlate automatically.
169
- */
170
- export declare function deriveTraceId(sessionId: string): string;
171
- /**
172
- * Parse a W3C traceparent header. Format:
173
- * `<version>-<traceId 32hex>-<parentSpanId 16hex>-<flags 2hex>`
174
- * Returns undefined for malformed or "all-zero" traceparents.
175
- */
176
- export declare function parseTraceparent(raw: string | undefined): {
177
- traceId: string;
178
- parentSpanId: string;
179
- } | undefined;
180
- /**
181
- * Format a single TraceSpan for terminal display.
182
- */
183
- export declare function formatSpan(span: TraceSpan): string;
184
- /**
185
- * Watch a trace file for new spans and invoke `callback` for each one.
186
- * Returns a cleanup function that stops watching.
187
- *
188
- * If the file does not yet exist, polling waits for it to appear.
189
- */
190
- export declare function watchTraceFile(filePath: string, callback: (span: TraceSpan) => void): () => void;
package/dist/tracer.js DELETED
@@ -1,481 +0,0 @@
1
- "use strict";
2
- /**
3
- * Distributed tracing for Ory agent plugin hooks.
4
- *
5
- * Replaces simple audit logging with structured, correlated trace spans.
6
- * Every plugin resolves to the same unified TraceEvent classification —
7
- * there is no plugin-specific output.
8
- *
9
- * Key concepts:
10
- * - **TraceEvent**: A canonical event type (e.g. "tool.invoke", "permission.check").
11
- * - **TraceSpan**: A completed unit of work with timing and status.
12
- * - **ActiveSpan**: An in-flight span that must be `.end()`-ed.
13
- * - **Tracer**: EventEmitter that collects spans, writes NDJSON, and
14
- * supports live watching via the "span" event.
15
- *
16
- * Trace correlation: call `tracer.setContext()` with a traceId derived
17
- * from the session ID so all spans within the same agent session share
18
- * a single trace, even across separate hook process invocations.
19
- */
20
- var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
21
- if (k2 === undefined) k2 = k;
22
- var desc = Object.getOwnPropertyDescriptor(m, k);
23
- if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
24
- desc = { enumerable: true, get: function() { return m[k]; } };
25
- }
26
- Object.defineProperty(o, k2, desc);
27
- }) : (function(o, m, k, k2) {
28
- if (k2 === undefined) k2 = k;
29
- o[k2] = m[k];
30
- }));
31
- var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
32
- Object.defineProperty(o, "default", { enumerable: true, value: v });
33
- }) : function(o, v) {
34
- o["default"] = v;
35
- });
36
- var __importStar = (this && this.__importStar) || (function () {
37
- var ownKeys = function(o) {
38
- ownKeys = Object.getOwnPropertyNames || function (o) {
39
- var ar = [];
40
- for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
41
- return ar;
42
- };
43
- return ownKeys(o);
44
- };
45
- return function (mod) {
46
- if (mod && mod.__esModule) return mod;
47
- var result = {};
48
- if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
49
- __setModuleDefault(result, mod);
50
- return result;
51
- };
52
- })();
53
- Object.defineProperty(exports, "__esModule", { value: true });
54
- exports.Tracer = exports.ActiveSpan = void 0;
55
- exports.deriveTraceId = deriveTraceId;
56
- exports.parseTraceparent = parseTraceparent;
57
- exports.formatSpan = formatSpan;
58
- exports.watchTraceFile = watchTraceFile;
59
- const node_events_1 = require("node:events");
60
- const node_child_process_1 = require("node:child_process");
61
- const node_crypto_1 = require("node:crypto");
62
- const fs = __importStar(require("node:fs"));
63
- const os = __importStar(require("node:os"));
64
- const path = __importStar(require("node:path"));
65
- // ─── ActiveSpan ────────────────────────────────────────────────────
66
- class ActiveSpan {
67
- traceId;
68
- spanId;
69
- parentSpanId;
70
- event;
71
- startedAt;
72
- sessionId;
73
- startMs;
74
- _ended = false;
75
- tracer;
76
- baseAttributes;
77
- /** @internal — use `tracer.startSpan()` instead. */
78
- constructor(tracer, event, opts) {
79
- this.tracer = tracer;
80
- this.event = event;
81
- this.traceId = opts.resolvedTraceId;
82
- this.spanId = generateSpanId();
83
- this.parentSpanId = opts.parentSpanId;
84
- this.sessionId = opts.sessionId;
85
- this.startedAt = new Date().toISOString();
86
- this.startMs = performance.now();
87
- this.baseAttributes = opts.attributes;
88
- }
89
- get ended() {
90
- return this._ended;
91
- }
92
- /**
93
- * Complete the span. Returns the recorded TraceSpan.
94
- */
95
- end(status, attributes) {
96
- if (this._ended) {
97
- throw new Error(`Span ${this.spanId} (${this.event}) already ended`);
98
- }
99
- this._ended = true;
100
- const durationMs = Math.round(performance.now() - this.startMs);
101
- // Pull enricher attributes (e.g. userSubject / agentSubject from the
102
- // client's principals) at span-end so every span gets them, including
103
- // direct `tracer.record(...)` calls from plugin handlers.
104
- const enriched = this.tracer._callEnricher();
105
- const merged = enriched || this.baseAttributes || attributes
106
- ? { ...enriched, ...this.baseAttributes, ...attributes }
107
- : undefined;
108
- const meta = this.tracer.processMetadata;
109
- const span = {
110
- traceId: this.traceId,
111
- spanId: this.spanId,
112
- ...(this.parentSpanId ? { parentSpanId: this.parentSpanId } : {}),
113
- event: this.event,
114
- status,
115
- startedAt: this.startedAt,
116
- endedAt: new Date().toISOString(),
117
- durationMs,
118
- harness: this.tracer.harness,
119
- hostname: meta.hostname,
120
- user: meta.user,
121
- pid: meta.pid,
122
- cwd: meta.cwd,
123
- pluginVersion: meta.pluginVersion,
124
- nodeVersion: meta.nodeVersion,
125
- ...(meta.gitBranch ? { gitBranch: meta.gitBranch } : {}),
126
- ...(meta.gitCommit ? { gitCommit: meta.gitCommit } : {}),
127
- ...(this.sessionId ? { sessionId: this.sessionId } : {}),
128
- ...(merged && Object.keys(merged).length > 0
129
- ? { attributes: merged }
130
- : {}),
131
- };
132
- this.tracer._record(span);
133
- return span;
134
- }
135
- }
136
- exports.ActiveSpan = ActiveSpan;
137
- class Tracer extends node_events_1.EventEmitter {
138
- harness;
139
- processMetadata;
140
- traceFile;
141
- _spans = [];
142
- _context = {};
143
- _exporter;
144
- _enricher = null;
145
- constructor(opts) {
146
- super();
147
- this.harness = opts.harness;
148
- this.processMetadata = resolveProcessMetadata();
149
- this.traceFile = opts.traceFile ?? null;
150
- this._exporter = opts.exporter ?? null;
151
- // Seed default context from W3C traceparent if present so spans
152
- // stitch into upstream OTel traces (e.g. CI, sidecar collectors).
153
- const upstream = parseTraceparent(process.env.TRACEPARENT);
154
- if (upstream) {
155
- this._context = {
156
- traceId: upstream.traceId,
157
- spanId: upstream.parentSpanId,
158
- };
159
- }
160
- }
161
- /** Backwards-compatible accessors for the most common metadata fields. */
162
- get hostname() {
163
- return this.processMetadata.hostname;
164
- }
165
- get user() {
166
- return this.processMetadata.user;
167
- }
168
- // ─── Context ───────────────────────────────────────────────────
169
- /**
170
- * Set the ambient context. Subsequent spans inherit traceId,
171
- * parentSpanId, and sessionId from the context unless overridden.
172
- */
173
- setContext(ctx) {
174
- this._context = { ...ctx };
175
- }
176
- clearContext() {
177
- this._context = {};
178
- }
179
- get context() {
180
- return this._context;
181
- }
182
- // ─── Span Creation ─────────────────────────────────────────────
183
- /**
184
- * Start a span that must be completed with `.end(status)`.
185
- * Use this for operations with meaningful duration (API calls, etc.).
186
- */
187
- startSpan(event, opts) {
188
- const resolvedTraceId = opts?.traceId ?? this._context.traceId ?? generateTraceId();
189
- return new ActiveSpan(this, event, {
190
- resolvedTraceId,
191
- parentSpanId: opts?.parentSpanId ?? this._context.spanId,
192
- sessionId: opts?.sessionId ?? this._context.sessionId,
193
- attributes: opts?.attributes,
194
- });
195
- }
196
- /**
197
- * Record a completed span in one call. Use this for instantaneous
198
- * events (tool invoked, tool blocked, etc.).
199
- */
200
- record(event, status, opts) {
201
- const span = this.startSpan(event, opts);
202
- return span.end(status);
203
- }
204
- // ─── Query ─────────────────────────────────────────────────────
205
- /** All recorded spans in this process. */
206
- spans() {
207
- return this._spans;
208
- }
209
- // ─── Internal ──────────────────────────────────────────────────
210
- /**
211
- * Attach (or replace) the span exporter. Useful when constructing
212
- * the tracer before env-driven configuration has resolved.
213
- */
214
- setExporter(exporter) {
215
- this._exporter = exporter;
216
- }
217
- get exporter() {
218
- return this._exporter;
219
- }
220
- /**
221
- * Install (or replace) an attribute enricher. The enricher is invoked
222
- * on every span at end-time and its return value is merged into the
223
- * span's attributes (with caller-supplied attributes taking precedence).
224
- * Pass `null` to clear.
225
- */
226
- setAttributeEnricher(enricher) {
227
- this._enricher = enricher;
228
- }
229
- /** @internal — invoked by `ActiveSpan.end()`. Never throws. */
230
- _callEnricher() {
231
- if (!this._enricher)
232
- return undefined;
233
- try {
234
- const out = this._enricher();
235
- if (!out || Object.keys(out).length === 0)
236
- return undefined;
237
- return out;
238
- }
239
- catch {
240
- // Enricher contract: never throw. Swallow defensively.
241
- return undefined;
242
- }
243
- }
244
- /**
245
- * Flush any pending exports. Call before the host process exits so
246
- * the OTLP HTTP request has a chance to complete.
247
- */
248
- async shutdown() {
249
- if (!this._exporter)
250
- return;
251
- try {
252
- await this._exporter.shutdown();
253
- }
254
- catch {
255
- /* exporter contract: never throw */
256
- }
257
- }
258
- /** @internal — called by ActiveSpan.end() */
259
- _record(span) {
260
- this._spans.push(span);
261
- this.emit("span", span);
262
- this.writeToFile(span);
263
- this.dispatchExport(span);
264
- }
265
- dispatchExport(span) {
266
- if (!this._exporter)
267
- return;
268
- // Fire-and-forget: never await, never throw. The exporter contract
269
- // guarantees its returned Promise resolves even on transport error.
270
- void this._exporter.export([span]).catch(() => undefined);
271
- }
272
- writeToFile(span) {
273
- if (!this.traceFile)
274
- return;
275
- const dir = path.dirname(this.traceFile);
276
- if (!fs.existsSync(dir)) {
277
- fs.mkdirSync(dir, { recursive: true });
278
- }
279
- fs.appendFileSync(this.traceFile, JSON.stringify(span) + "\n");
280
- }
281
- }
282
- exports.Tracer = Tracer;
283
- // ─── Trace ID Helpers ──────────────────────────────────────────────
284
- /**
285
- * Derive a stable trace ID from a session ID so all hook invocations
286
- * within the same agent session correlate automatically.
287
- */
288
- function deriveTraceId(sessionId) {
289
- return (0, node_crypto_1.createHash)("sha256").update(sessionId).digest("hex").slice(0, 16);
290
- }
291
- function generateSpanId() {
292
- return (0, node_crypto_1.randomBytes)(8).toString("hex");
293
- }
294
- function generateTraceId() {
295
- return (0, node_crypto_1.randomBytes)(8).toString("hex");
296
- }
297
- function resolveHostname() {
298
- try {
299
- return os.hostname();
300
- }
301
- catch {
302
- return "unknown";
303
- }
304
- }
305
- function resolveUser() {
306
- try {
307
- return os.userInfo().username;
308
- }
309
- catch {
310
- return process.env.USER ?? process.env.USERNAME ?? "unknown";
311
- }
312
- }
313
- function resolveCwd() {
314
- try {
315
- return process.cwd();
316
- }
317
- catch {
318
- return "unknown";
319
- }
320
- }
321
- function resolvePluginVersion() {
322
- // Walks up from this file's directory to the nearest package.json. The
323
- // tracer ships with @ory/argus, so the closest package.json
324
- // is always the core package.
325
- try {
326
- let dir = __dirname;
327
- for (let i = 0; i < 5; i++) {
328
- const candidate = path.join(dir, "package.json");
329
- if (fs.existsSync(candidate)) {
330
- const pkg = JSON.parse(fs.readFileSync(candidate, "utf8"));
331
- if (pkg.version)
332
- return pkg.version;
333
- }
334
- const parent = path.dirname(dir);
335
- if (parent === dir)
336
- break;
337
- dir = parent;
338
- }
339
- }
340
- catch {
341
- /* fall through */
342
- }
343
- return "unknown";
344
- }
345
- function resolveGitInfo(cwd) {
346
- // Best-effort: short-timeout git calls. Skipped silently when git is
347
- // unavailable or `cwd` is not a working tree.
348
- const exec = (args) => {
349
- try {
350
- return (0, node_child_process_1.execFileSync)("git", args, {
351
- cwd,
352
- timeout: 250,
353
- stdio: ["ignore", "pipe", "ignore"],
354
- encoding: "utf8",
355
- }).trim() || undefined;
356
- }
357
- catch {
358
- return undefined;
359
- }
360
- };
361
- const gitBranch = exec(["rev-parse", "--abbrev-ref", "HEAD"]);
362
- const gitCommit = exec(["rev-parse", "--short", "HEAD"]);
363
- return {
364
- ...(gitBranch ? { gitBranch } : {}),
365
- ...(gitCommit ? { gitCommit } : {}),
366
- };
367
- }
368
- function resolveProcessMetadata() {
369
- const cwd = resolveCwd();
370
- return {
371
- hostname: resolveHostname(),
372
- user: resolveUser(),
373
- pid: process.pid,
374
- cwd,
375
- pluginVersion: resolvePluginVersion(),
376
- nodeVersion: process.version,
377
- ...resolveGitInfo(cwd),
378
- };
379
- }
380
- /**
381
- * Parse a W3C traceparent header. Format:
382
- * `<version>-<traceId 32hex>-<parentSpanId 16hex>-<flags 2hex>`
383
- * Returns undefined for malformed or "all-zero" traceparents.
384
- */
385
- function parseTraceparent(raw) {
386
- if (!raw)
387
- return undefined;
388
- const match = /^([0-9a-f]{2})-([0-9a-f]{32})-([0-9a-f]{16})-([0-9a-f]{2})$/i.exec(raw.trim());
389
- if (!match)
390
- return undefined;
391
- const traceId = match[2].toLowerCase();
392
- const parentSpanId = match[3].toLowerCase();
393
- if (/^0+$/.test(traceId) || /^0+$/.test(parentSpanId))
394
- return undefined;
395
- return { traceId, parentSpanId };
396
- }
397
- // ─── Live Watch ────────────────────────────────────────────────────
398
- const STATUS_ICONS = {
399
- ok: "\x1b[32m✓\x1b[0m",
400
- error: "\x1b[31m✗\x1b[0m",
401
- denied: "\x1b[31m⊘\x1b[0m",
402
- skipped: "\x1b[33m─\x1b[0m",
403
- };
404
- const EVENT_PAD = 22;
405
- /**
406
- * Format a single TraceSpan for terminal display.
407
- */
408
- function formatSpan(span) {
409
- const time = span.startedAt.slice(11, 23); // HH:MM:SS.mmm
410
- const icon = STATUS_ICONS[span.status];
411
- const event = span.event.padEnd(EVENT_PAD);
412
- const traceShort = `\x1b[2m[${span.traceId.slice(0, 8)}]\x1b[0m`;
413
- const parts = [];
414
- // Duration (skip for instant spans)
415
- if (span.durationMs > 0) {
416
- parts.push(`\x1b[2m${span.durationMs}ms\x1b[0m`);
417
- }
418
- // Key attributes
419
- if (span.attributes) {
420
- const a = span.attributes;
421
- if (a.toolName)
422
- parts.push(`tool=${a.toolName}`);
423
- if (a.allowed !== undefined)
424
- parts.push(`allowed=${a.allowed}`);
425
- if (a.sessionId)
426
- parts.push(`session=${a.sessionId}`);
427
- if (a.identityId)
428
- parts.push(`identity=${a.identityId}`);
429
- if (a.clientId)
430
- parts.push(`client=${a.clientId}`);
431
- if (a.error)
432
- parts.push(`\x1b[31merror=${a.error}\x1b[0m`);
433
- }
434
- if (span.sessionId && !parts.some((p) => p.startsWith("session="))) {
435
- parts.push(`session=${span.sessionId}`);
436
- }
437
- const detail = parts.length > 0 ? " " + parts.join(" ") : "";
438
- return `\x1b[2m${time}\x1b[0m ${event} ${traceShort} ${icon}${detail}`;
439
- }
440
- /**
441
- * Watch a trace file for new spans and invoke `callback` for each one.
442
- * Returns a cleanup function that stops watching.
443
- *
444
- * If the file does not yet exist, polling waits for it to appear.
445
- */
446
- function watchTraceFile(filePath, callback) {
447
- let offset = 0;
448
- function poll() {
449
- try {
450
- if (!fs.existsSync(filePath))
451
- return;
452
- const stat = fs.statSync(filePath);
453
- if (stat.size <= offset)
454
- return;
455
- const fd = fs.openSync(filePath, "r");
456
- try {
457
- const buf = Buffer.alloc(stat.size - offset);
458
- fs.readSync(fd, buf, 0, buf.length, offset);
459
- offset = stat.size;
460
- const lines = buf.toString().split("\n").filter(Boolean);
461
- for (const line of lines) {
462
- try {
463
- callback(JSON.parse(line));
464
- }
465
- catch {
466
- /* skip malformed lines */
467
- }
468
- }
469
- }
470
- finally {
471
- fs.closeSync(fd);
472
- }
473
- }
474
- catch {
475
- /* file may be mid-write */
476
- }
477
- }
478
- poll(); // read existing content
479
- const timer = setInterval(poll, 200);
480
- return () => clearInterval(timer);
481
- }
@@ -1,9 +0,0 @@
1
- #!/usr/bin/env node
2
- /**
3
- * Live trace watcher for the dev sandbox.
4
- *
5
- * Tails the NDJSON trace file produced by `pnpm dev:<harness>` and prints
6
- * a colorized line per span as they arrive. Run in a second terminal
7
- * alongside the dev launcher.
8
- */
9
- export {};