@groundnuty/macf-channel-server 0.2.0-rc.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 (49) hide show
  1. package/dist/collision.d.ts +25 -0
  2. package/dist/collision.d.ts.map +1 -0
  3. package/dist/collision.js +94 -0
  4. package/dist/collision.js.map +1 -0
  5. package/dist/health.d.ts +3 -0
  6. package/dist/health.d.ts.map +1 -0
  7. package/dist/health.js +33 -0
  8. package/dist/health.js.map +1 -0
  9. package/dist/https.d.ts +25 -0
  10. package/dist/https.d.ts.map +1 -0
  11. package/dist/https.js +361 -0
  12. package/dist/https.js.map +1 -0
  13. package/dist/index.d.ts +29 -0
  14. package/dist/index.d.ts.map +1 -0
  15. package/dist/index.js +25 -0
  16. package/dist/index.js.map +1 -0
  17. package/dist/mcp.d.ts +6 -0
  18. package/dist/mcp.d.ts.map +1 -0
  19. package/dist/mcp.js +43 -0
  20. package/dist/mcp.js.map +1 -0
  21. package/dist/notify-formatter.d.ts +19 -0
  22. package/dist/notify-formatter.d.ts.map +1 -0
  23. package/dist/notify-formatter.js +43 -0
  24. package/dist/notify-formatter.js.map +1 -0
  25. package/dist/otel.d.ts +59 -0
  26. package/dist/otel.d.ts.map +1 -0
  27. package/dist/otel.js +122 -0
  28. package/dist/otel.js.map +1 -0
  29. package/dist/server.d.ts +2 -0
  30. package/dist/server.d.ts.map +1 -0
  31. package/dist/server.js +256 -0
  32. package/dist/server.js.map +1 -0
  33. package/dist/shutdown.d.ts +15 -0
  34. package/dist/shutdown.d.ts.map +1 -0
  35. package/dist/shutdown.js +53 -0
  36. package/dist/shutdown.js.map +1 -0
  37. package/dist/startup-issues.d.ts +24 -0
  38. package/dist/startup-issues.d.ts.map +1 -0
  39. package/dist/startup-issues.js +75 -0
  40. package/dist/startup-issues.js.map +1 -0
  41. package/dist/tmux-wake.d.ts +106 -0
  42. package/dist/tmux-wake.d.ts.map +1 -0
  43. package/dist/tmux-wake.js +196 -0
  44. package/dist/tmux-wake.js.map +1 -0
  45. package/dist/tracing.d.ts +38 -0
  46. package/dist/tracing.d.ts.map +1 -0
  47. package/dist/tracing.js +68 -0
  48. package/dist/tracing.js.map +1 -0
  49. package/package.json +46 -0
package/dist/index.js ADDED
@@ -0,0 +1,25 @@
1
+ /**
2
+ * Multi-Agent Coordination Framework (MACF)
3
+ *
4
+ * Coordinates multiple Claude Code agents via GitHub,
5
+ * using MCP channels (HTTP/mTLS) for communication.
6
+ */
7
+ export { MacfError, ConfigError, McpChannelError, HttpsServerError, PortUnavailableError, PortExhaustedError, ValidationError } from '@groundnuty/macf-core';
8
+ export { createMcpChannel } from './mcp.js';
9
+ export { createHttpsServer } from './https.js';
10
+ export { createHealthState } from './health.js';
11
+ export { createLogger } from '@groundnuty/macf-core';
12
+ export { loadConfig } from '@groundnuty/macf-core';
13
+ export { NotifyPayloadSchema, NotifyTypeSchema, HealthResponseSchema, CiCompletionPayloadSchema, CheckSuiteConclusionSchema, } from '@groundnuty/macf-core';
14
+ // P2: Registry & Discovery
15
+ export { createRegistryFromConfig, createRegistry, createGitHubClient, GitHubApiError, AgentInfoSchema, RegistryConfigSchema } from '@groundnuty/macf-core';
16
+ export { checkCollision, CollisionError } from './collision.js';
17
+ export { registerShutdownHandler } from './shutdown.js';
18
+ export { generateToken } from '@groundnuty/macf-core';
19
+ export { checkPendingIssues } from './startup-issues.js';
20
+ // P3: Certificate Management
21
+ export { createCA, backupCAKey, recoverCAKey, encryptCAKey, decryptCAKey, loadCA, CaError } from '@groundnuty/macf-core';
22
+ export { generateAgentCert, generateCSR, signCSR, AgentCertError } from '@groundnuty/macf-core';
23
+ export { createChallenge, verifyAndConsumeChallenge, ChallengeError } from '@groundnuty/macf-core';
24
+ export { SignRequestSchema, SignChallengeResponseSchema, SignCertResponseSchema } from '@groundnuty/macf-core';
25
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EAAE,SAAS,EAAE,WAAW,EAAE,eAAe,EAAE,gBAAgB,EAAE,oBAAoB,EAAE,kBAAkB,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAC;AAC7J,OAAO,EAAE,gBAAgB,EAAE,MAAM,UAAU,CAAC;AAC5C,OAAO,EAAE,iBAAiB,EAAE,MAAM,YAAY,CAAC;AAC/C,OAAO,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAC;AAChD,OAAO,EAAE,YAAY,EAAE,MAAM,uBAAuB,CAAC;AACrD,OAAO,EAAE,UAAU,EAAE,MAAM,uBAAuB,CAAC;AACnD,OAAO,EACL,mBAAmB,EAAE,gBAAgB,EAAE,oBAAoB,EAC3D,yBAAyB,EAAE,0BAA0B,GACtD,MAAM,uBAAuB,CAAC;AAO/B,2BAA2B;AAC3B,OAAO,EAAE,wBAAwB,EAAE,cAAc,EAAE,kBAAkB,EAAE,cAAc,EAAE,eAAe,EAAE,oBAAoB,EAAE,MAAM,uBAAuB,CAAC;AAE5J,OAAO,EAAE,cAAc,EAAE,cAAc,EAAE,MAAM,gBAAgB,CAAC;AAEhE,OAAO,EAAE,uBAAuB,EAAE,MAAM,eAAe,CAAC;AACxD,OAAO,EAAE,aAAa,EAAE,MAAM,uBAAuB,CAAC;AACtD,OAAO,EAAE,kBAAkB,EAAE,MAAM,qBAAqB,CAAC;AAEzD,6BAA6B;AAC7B,OAAO,EAAE,QAAQ,EAAE,WAAW,EAAE,YAAY,EAAE,YAAY,EAAE,YAAY,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,uBAAuB,CAAC;AAEzH,OAAO,EAAE,iBAAiB,EAAE,WAAW,EAAE,OAAO,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC;AAEhG,OAAO,EAAE,eAAe,EAAE,yBAAyB,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC;AACnG,OAAO,EAAE,iBAAiB,EAAE,2BAA2B,EAAE,sBAAsB,EAAE,MAAM,uBAAuB,CAAC"}
package/dist/mcp.d.ts ADDED
@@ -0,0 +1,6 @@
1
+ import type { McpChannel } from '@groundnuty/macf-core';
2
+ export declare function createMcpChannel(config: {
3
+ readonly agentName: string;
4
+ readonly instructions?: string;
5
+ }): McpChannel;
6
+ //# sourceMappingURL=mcp.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mcp.d.ts","sourceRoot":"","sources":["../src/mcp.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,uBAAuB,CAAC;AAaxD,wBAAgB,gBAAgB,CAAC,MAAM,EAAE;IACvC,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,YAAY,CAAC,EAAE,MAAM,CAAC;CAChC,GAAG,UAAU,CA4Cb"}
package/dist/mcp.js ADDED
@@ -0,0 +1,43 @@
1
+ import { Server } from '@modelcontextprotocol/sdk/server/index.js';
2
+ import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
3
+ import { McpChannelError } from '@groundnuty/macf-core';
4
+ const CHANNEL_INSTRUCTIONS = `Events arrive as <channel source="macf-agent" type="..." ...>.
5
+
6
+ type="issue_routed": A GitHub issue was routed to you by the agent-router Action.
7
+ Read the issue and work on it following your agent identity rules.
8
+
9
+ type="mention": You were @mentioned in an issue comment or PR review.
10
+ Read the context and respond.
11
+
12
+ type="startup_check": Pending issues found at session startup.
13
+ Review and pick up the most important one.`;
14
+ export function createMcpChannel(config) {
15
+ const instructions = config.instructions ?? CHANNEL_INSTRUCTIONS;
16
+ const server = new Server({ name: `macf-${config.agentName}`, version: '0.2.0-rc.0' }, {
17
+ capabilities: {
18
+ experimental: { 'claude/channel': {} },
19
+ },
20
+ instructions,
21
+ });
22
+ return {
23
+ async connect() {
24
+ const transport = new StdioServerTransport();
25
+ await server.connect(transport);
26
+ },
27
+ async pushNotification(content, meta) {
28
+ try {
29
+ // notifications/claude/channel is a Claude Code extension not in MCP's
30
+ // typed ServerNotification union, but Server.assertNotificationCapability
31
+ // has no default case so unknown methods pass through at runtime.
32
+ await server.notification({
33
+ method: 'notifications/claude/channel',
34
+ params: { content, meta },
35
+ });
36
+ }
37
+ catch (err) {
38
+ throw new McpChannelError(`Failed to push notification: ${err instanceof Error ? err.message : String(err)}`);
39
+ }
40
+ },
41
+ };
42
+ }
43
+ //# sourceMappingURL=mcp.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mcp.js","sourceRoot":"","sources":["../src/mcp.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,2CAA2C,CAAC;AACnE,OAAO,EAAE,oBAAoB,EAAE,MAAM,2CAA2C,CAAC;AACjF,OAAO,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAC;AAGxD,MAAM,oBAAoB,GAAG;;;;;;;;;6CASgB,CAAC;AAE9C,MAAM,UAAU,gBAAgB,CAAC,MAGhC;IACC,MAAM,YAAY,GAAG,MAAM,CAAC,YAAY,IAAI,oBAAoB,CAAC;IAEjE,MAAM,MAAM,GAAG,IAAI,MAAM,CACvB,EAAE,IAAI,EAAE,QAAQ,MAAM,CAAC,SAAS,EAAE,EAAE,OAAO,EAAE,YAAY,EAAE,EAC3D;QACE,YAAY,EAAE;YACZ,YAAY,EAAE,EAAE,gBAAgB,EAAE,EAAE,EAAE;SACvC;QACD,YAAY;KACb,CACF,CAAC;IAEF,OAAO;QACL,KAAK,CAAC,OAAO;YACX,MAAM,SAAS,GAAG,IAAI,oBAAoB,EAAE,CAAC;YAC7C,MAAM,MAAM,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC;QAClC,CAAC;QAED,KAAK,CAAC,gBAAgB,CACpB,OAAe,EACf,IAA4B;YAE5B,IAAI,CAAC;gBACH,uEAAuE;gBACvE,0EAA0E;gBAC1E,kEAAkE;gBAClE,MAAO,MAAM,CAAC,YAMM,CAAC;oBACnB,MAAM,EAAE,8BAA8B;oBACtC,MAAM,EAAE,EAAE,OAAO,EAAE,IAAI,EAAE;iBAC1B,CAAC,CAAC;YACL,CAAC;YAAC,OAAO,GAAG,EAAE,CAAC;gBACb,MAAM,IAAI,eAAe,CACvB,gCAAgC,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,EAAE,CACnF,CAAC;YACJ,CAAC;QACH,CAAC;KACF,CAAC;AACJ,CAAC"}
@@ -0,0 +1,19 @@
1
+ import type { NotifyPayload } from '@groundnuty/macf-core';
2
+ /**
3
+ * Render a NotifyPayload to the operator-facing content string that
4
+ * gets pushed through MCP. Pure function — extracted from `onNotify`
5
+ * in `server.ts` per ultrareview finding A6. Previously the renderer
6
+ * lived inside a 58-line closure that also did logging, health-state
7
+ * mutation, and MCP push; adding a new `NotifyPayload` variant meant
8
+ * editing that closure. Extracting makes variant dispatch testable
9
+ * in isolation.
10
+ *
11
+ * Returns the rendered content and the issue number the payload is
12
+ * associated with (for health-state updates at the call site).
13
+ */
14
+ export interface FormattedNotify {
15
+ readonly content: string;
16
+ readonly issueNumber?: number;
17
+ }
18
+ export declare function formatNotifyContent(payload: NotifyPayload): FormattedNotify;
19
+ //# sourceMappingURL=notify-formatter.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"notify-formatter.d.ts","sourceRoot":"","sources":["../src/notify-formatter.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,uBAAuB,CAAC;AAE3D;;;;;;;;;;;GAWG;AACH,MAAM,WAAW,eAAe;IAC9B,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,CAAC;CAC/B;AAED,wBAAgB,mBAAmB,CAAC,OAAO,EAAE,aAAa,GAAG,eAAe,CA4C3E"}
@@ -0,0 +1,43 @@
1
+ export function formatNotifyContent(payload) {
2
+ if (payload.type === 'issue_routed') {
3
+ if (payload.issue_number !== undefined) {
4
+ const suffix = payload.title ? `: ${payload.title}` : '';
5
+ return {
6
+ content: `Issue #${payload.issue_number} was routed to you${suffix}`,
7
+ issueNumber: payload.issue_number,
8
+ };
9
+ }
10
+ return {
11
+ content: payload.title
12
+ ? `An issue was routed to you: ${payload.title}`
13
+ : 'An issue was routed to you',
14
+ };
15
+ }
16
+ if (payload.type === 'mention') {
17
+ return { content: payload.message ?? 'You were mentioned' };
18
+ }
19
+ if (payload.type === 'ci_completion') {
20
+ // Prefer the prebuilt `message` (producer has all context) but
21
+ // fall back to a shape-derived rendering if absent. Producers
22
+ // that use CiCompletionPayloadSchema always provide `message`.
23
+ if (payload.message) {
24
+ return { content: payload.message };
25
+ }
26
+ if (payload.pr_number !== undefined && payload.conclusion !== undefined) {
27
+ const prRef = `PR #${payload.pr_number}`;
28
+ if (payload.conclusion === 'success') {
29
+ return { content: `${prRef}: CI SUCCESS` };
30
+ }
31
+ const failing = payload.failing_check_name
32
+ ? ` (first failing check: '${payload.failing_check_name}')`
33
+ : '';
34
+ return {
35
+ content: `${prRef}: CI ${payload.conclusion.toUpperCase()}${failing}`,
36
+ };
37
+ }
38
+ return { content: 'CI completed' };
39
+ }
40
+ // startup_check or any future variant falls through here
41
+ return { content: payload.message ?? 'Pending issues found at startup' };
42
+ }
43
+ //# sourceMappingURL=notify-formatter.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"notify-formatter.js","sourceRoot":"","sources":["../src/notify-formatter.ts"],"names":[],"mappings":"AAmBA,MAAM,UAAU,mBAAmB,CAAC,OAAsB;IACxD,IAAI,OAAO,CAAC,IAAI,KAAK,cAAc,EAAE,CAAC;QACpC,IAAI,OAAO,CAAC,YAAY,KAAK,SAAS,EAAE,CAAC;YACvC,MAAM,MAAM,GAAG,OAAO,CAAC,KAAK,CAAC,CAAC,CAAC,KAAK,OAAO,CAAC,KAAK,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;YACzD,OAAO;gBACL,OAAO,EAAE,UAAU,OAAO,CAAC,YAAY,qBAAqB,MAAM,EAAE;gBACpE,WAAW,EAAE,OAAO,CAAC,YAAY;aAClC,CAAC;QACJ,CAAC;QACD,OAAO;YACL,OAAO,EAAE,OAAO,CAAC,KAAK;gBACpB,CAAC,CAAC,+BAA+B,OAAO,CAAC,KAAK,EAAE;gBAChD,CAAC,CAAC,4BAA4B;SACjC,CAAC;IACJ,CAAC;IAED,IAAI,OAAO,CAAC,IAAI,KAAK,SAAS,EAAE,CAAC;QAC/B,OAAO,EAAE,OAAO,EAAE,OAAO,CAAC,OAAO,IAAI,oBAAoB,EAAE,CAAC;IAC9D,CAAC;IAED,IAAI,OAAO,CAAC,IAAI,KAAK,eAAe,EAAE,CAAC;QACrC,+DAA+D;QAC/D,8DAA8D;QAC9D,+DAA+D;QAC/D,IAAI,OAAO,CAAC,OAAO,EAAE,CAAC;YACpB,OAAO,EAAE,OAAO,EAAE,OAAO,CAAC,OAAO,EAAE,CAAC;QACtC,CAAC;QACD,IAAI,OAAO,CAAC,SAAS,KAAK,SAAS,IAAI,OAAO,CAAC,UAAU,KAAK,SAAS,EAAE,CAAC;YACxE,MAAM,KAAK,GAAG,OAAO,OAAO,CAAC,SAAS,EAAE,CAAC;YACzC,IAAI,OAAO,CAAC,UAAU,KAAK,SAAS,EAAE,CAAC;gBACrC,OAAO,EAAE,OAAO,EAAE,GAAG,KAAK,cAAc,EAAE,CAAC;YAC7C,CAAC;YACD,MAAM,OAAO,GAAG,OAAO,CAAC,kBAAkB;gBACxC,CAAC,CAAC,2BAA2B,OAAO,CAAC,kBAAkB,IAAI;gBAC3D,CAAC,CAAC,EAAE,CAAC;YACP,OAAO;gBACL,OAAO,EAAE,GAAG,KAAK,QAAQ,OAAO,CAAC,UAAU,CAAC,WAAW,EAAE,GAAG,OAAO,EAAE;aACtE,CAAC;QACJ,CAAC;QACD,OAAO,EAAE,OAAO,EAAE,cAAc,EAAE,CAAC;IACrC,CAAC;IAED,yDAAyD;IACzD,OAAO,EAAE,OAAO,EAAE,OAAO,CAAC,OAAO,IAAI,iCAAiC,EAAE,CAAC;AAC3E,CAAC"}
package/dist/otel.d.ts ADDED
@@ -0,0 +1,59 @@
1
+ /**
2
+ * OpenTelemetry bootstrap — dynamic-import gated by env.
3
+ *
4
+ * Zero-cost default (macf#196, revisiting macf#194):
5
+ *
6
+ * The original v0.1.7 shipped this module with TOP-LEVEL static
7
+ * imports of the 5 SDK packages. That violated the zero-cost
8
+ * doctrine structurally: Node resolved + loaded all of them at
9
+ * startup regardless of whether `OTEL_EXPORTER_OTLP_ENDPOINT`
10
+ * was set. Worse, when a consumer workspace didn't have the
11
+ * packages in its `node_modules/` (which was the default — they
12
+ * weren't declared in `plugin/package.json`), the server crashed
13
+ * with `ERR_MODULE_NOT_FOUND` before the env-guard ever ran.
14
+ *
15
+ * v0.1.8 fix: `bootstrapOtel()` is async; inside, we `await import()`
16
+ * the SDK packages only when the endpoint is set. Node only
17
+ * resolves the packages when the operator opts in. If they're
18
+ * missing at that point (operator set the env but forgot `npm
19
+ * install`), we fail LOUD with an actionable message — silent
20
+ * no-op would hide the opt-in attempt.
21
+ *
22
+ * `@opentelemetry/api` stays statically imported because other
23
+ * modules (`src/tracing.ts`, `src/https.ts`) import it at eval
24
+ * time for the no-op tracer path. The `api` package has zero
25
+ * deps and is already a transitive dep, so it's safe to require.
26
+ *
27
+ * Why manual-only (no `auto-instrumentations-node`):
28
+ * Auto-instrumentations monkey-patch core Node modules including
29
+ * HTTPS — and we rely on exact mTLS client-cert validation
30
+ * semantics in `src/https.ts`. Any patching layer between us and
31
+ * Node's TLS code is a correctness risk we don't need.
32
+ *
33
+ * Version pinning:
34
+ * SDK-node packages (`@opentelemetry/sdk-trace-node` etc.) are
35
+ * still pre-1.0 (0.x). Breaking changes land in minor releases.
36
+ * Pin exact versions via package.json — do NOT use caret ranges.
37
+ *
38
+ * See DR-021 for the full rationale + option analysis.
39
+ */
40
+ /**
41
+ * Opt-in OTEL bootstrap. No-op (and zero module-resolution cost) when
42
+ * `OTEL_EXPORTER_OTLP_ENDPOINT` is unset. When set, dynamic-imports
43
+ * the SDK packages, configures an OTLP-proto exporter, registers the
44
+ * provider globally, and wires SIGTERM/SIGINT span-flush.
45
+ *
46
+ * **Must be awaited before any span-emitting module's handler runs.**
47
+ * In `src/server.ts`, `await bootstrapOtel()` sits at the top of
48
+ * `main()` so the global tracer provider is live before /notify,
49
+ * /sign, etc. start accepting requests.
50
+ *
51
+ * Fail-loud policy:
52
+ * - `OTEL_EXPORTER_OTLP_ENDPOINT` unset → silent return (zero-cost default).
53
+ * - Env set + import fails → process.exit(1) with actionable
54
+ * stderr. Operator explicitly opted into observability; silent
55
+ * no-op would hide the config mistake.
56
+ * - Env set + import ok + provider setup fails → same exit(1).
57
+ */
58
+ export declare function bootstrapOtel(): Promise<void>;
59
+ //# sourceMappingURL=otel.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"otel.d.ts","sourceRoot":"","sources":["../src/otel.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAsCG;AAEH;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAsB,aAAa,IAAI,OAAO,CAAC,IAAI,CAAC,CAsEnD"}
package/dist/otel.js ADDED
@@ -0,0 +1,122 @@
1
+ /**
2
+ * OpenTelemetry bootstrap — dynamic-import gated by env.
3
+ *
4
+ * Zero-cost default (macf#196, revisiting macf#194):
5
+ *
6
+ * The original v0.1.7 shipped this module with TOP-LEVEL static
7
+ * imports of the 5 SDK packages. That violated the zero-cost
8
+ * doctrine structurally: Node resolved + loaded all of them at
9
+ * startup regardless of whether `OTEL_EXPORTER_OTLP_ENDPOINT`
10
+ * was set. Worse, when a consumer workspace didn't have the
11
+ * packages in its `node_modules/` (which was the default — they
12
+ * weren't declared in `plugin/package.json`), the server crashed
13
+ * with `ERR_MODULE_NOT_FOUND` before the env-guard ever ran.
14
+ *
15
+ * v0.1.8 fix: `bootstrapOtel()` is async; inside, we `await import()`
16
+ * the SDK packages only when the endpoint is set. Node only
17
+ * resolves the packages when the operator opts in. If they're
18
+ * missing at that point (operator set the env but forgot `npm
19
+ * install`), we fail LOUD with an actionable message — silent
20
+ * no-op would hide the opt-in attempt.
21
+ *
22
+ * `@opentelemetry/api` stays statically imported because other
23
+ * modules (`src/tracing.ts`, `src/https.ts`) import it at eval
24
+ * time for the no-op tracer path. The `api` package has zero
25
+ * deps and is already a transitive dep, so it's safe to require.
26
+ *
27
+ * Why manual-only (no `auto-instrumentations-node`):
28
+ * Auto-instrumentations monkey-patch core Node modules including
29
+ * HTTPS — and we rely on exact mTLS client-cert validation
30
+ * semantics in `src/https.ts`. Any patching layer between us and
31
+ * Node's TLS code is a correctness risk we don't need.
32
+ *
33
+ * Version pinning:
34
+ * SDK-node packages (`@opentelemetry/sdk-trace-node` etc.) are
35
+ * still pre-1.0 (0.x). Breaking changes land in minor releases.
36
+ * Pin exact versions via package.json — do NOT use caret ranges.
37
+ *
38
+ * See DR-021 for the full rationale + option analysis.
39
+ */
40
+ /**
41
+ * Opt-in OTEL bootstrap. No-op (and zero module-resolution cost) when
42
+ * `OTEL_EXPORTER_OTLP_ENDPOINT` is unset. When set, dynamic-imports
43
+ * the SDK packages, configures an OTLP-proto exporter, registers the
44
+ * provider globally, and wires SIGTERM/SIGINT span-flush.
45
+ *
46
+ * **Must be awaited before any span-emitting module's handler runs.**
47
+ * In `src/server.ts`, `await bootstrapOtel()` sits at the top of
48
+ * `main()` so the global tracer provider is live before /notify,
49
+ * /sign, etc. start accepting requests.
50
+ *
51
+ * Fail-loud policy:
52
+ * - `OTEL_EXPORTER_OTLP_ENDPOINT` unset → silent return (zero-cost default).
53
+ * - Env set + import fails → process.exit(1) with actionable
54
+ * stderr. Operator explicitly opted into observability; silent
55
+ * no-op would hide the config mistake.
56
+ * - Env set + import ok + provider setup fails → same exit(1).
57
+ */
58
+ export async function bootstrapOtel() {
59
+ const endpoint = process.env['OTEL_EXPORTER_OTLP_ENDPOINT'];
60
+ if (endpoint === undefined || endpoint === '')
61
+ return;
62
+ const serviceVersion = process.env['MACF_VERSION'] ?? '0.0.0';
63
+ const serviceName = process.env['OTEL_SERVICE_NAME'] ?? 'macf';
64
+ // Dynamic imports — only resolved when opted in. Lets a workspace
65
+ // WITHOUT `@opentelemetry/sdk-*` in node_modules start cleanly
66
+ // (as long as it doesn't opt in). See macf#196 for the bug this
67
+ // closes; consumer plugin workspaces have only a subset of deps
68
+ // available via `npm install` unless opted into observability.
69
+ let sdkNode;
70
+ let sdkBase;
71
+ let exporter;
72
+ let resources;
73
+ let semconv;
74
+ try {
75
+ [sdkNode, sdkBase, exporter, resources, semconv] = await Promise.all([
76
+ import('@opentelemetry/sdk-trace-node'),
77
+ import('@opentelemetry/sdk-trace-base'),
78
+ import('@opentelemetry/exporter-trace-otlp-proto'),
79
+ import('@opentelemetry/resources'),
80
+ import('@opentelemetry/semantic-conventions'),
81
+ ]);
82
+ }
83
+ catch (err) {
84
+ const msg = err instanceof Error ? err.message : String(err);
85
+ process.stderr.write(`FATAL: OTEL_EXPORTER_OTLP_ENDPOINT is set ("${endpoint}") but required @opentelemetry/* packages are missing.\n` +
86
+ ` Underlying error: ${msg}\n` +
87
+ ` Fix: install them in the plugin dir (e.g. run macf-agent's SessionStart npm-install hook), ` +
88
+ `or unset OTEL_EXPORTER_OTLP_ENDPOINT to disable telemetry.\n`);
89
+ process.exit(1);
90
+ }
91
+ // Resource attributes inherit from the SDK's default detectors
92
+ // (process.*, host.*, telemetry.sdk.*) via `defaultResource()`,
93
+ // merged with our explicit service.* attributes on top.
94
+ const resource = resources.defaultResource().merge(resources.resourceFromAttributes({
95
+ [semconv.ATTR_SERVICE_NAME]: serviceName,
96
+ [semconv.ATTR_SERVICE_VERSION]: serviceVersion,
97
+ }));
98
+ const provider = new sdkNode.NodeTracerProvider({
99
+ resource,
100
+ spanProcessors: [new sdkBase.BatchSpanProcessor(new exporter.OTLPTraceExporter())],
101
+ });
102
+ // register() installs the provider as global + sets W3C trace-
103
+ // context propagator as default. After this call,
104
+ // `trace.getTracer('macf')` from anywhere in the process returns a
105
+ // recording tracer bound to this provider.
106
+ provider.register();
107
+ // Clean shutdown: flush queued spans before process exits. Without
108
+ // these handlers, in-flight batches are dropped on SIGTERM/SIGINT —
109
+ // the last few seconds of coordination events never reach the
110
+ // collector.
111
+ const shutdown = async () => {
112
+ try {
113
+ await provider.shutdown();
114
+ }
115
+ catch {
116
+ // Silent — we're exiting regardless; don't spam stderr.
117
+ }
118
+ };
119
+ process.once('SIGTERM', shutdown);
120
+ process.once('SIGINT', shutdown);
121
+ }
122
+ //# sourceMappingURL=otel.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"otel.js","sourceRoot":"","sources":["../src/otel.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAsCG;AAEH;;;;;;;;;;;;;;;;;GAiBG;AACH,MAAM,CAAC,KAAK,UAAU,aAAa;IACjC,MAAM,QAAQ,GAAG,OAAO,CAAC,GAAG,CAAC,6BAA6B,CAAC,CAAC;IAC5D,IAAI,QAAQ,KAAK,SAAS,IAAI,QAAQ,KAAK,EAAE;QAAE,OAAO;IAEtD,MAAM,cAAc,GAAG,OAAO,CAAC,GAAG,CAAC,cAAc,CAAC,IAAI,OAAO,CAAC;IAC9D,MAAM,WAAW,GAAG,OAAO,CAAC,GAAG,CAAC,mBAAmB,CAAC,IAAI,MAAM,CAAC;IAE/D,kEAAkE;IAClE,+DAA+D;IAC/D,gEAAgE;IAChE,gEAAgE;IAChE,+DAA+D;IAC/D,IAAI,OAAuD,CAAC;IAC5D,IAAI,OAAuD,CAAC;IAC5D,IAAI,QAAmE,CAAC;IACxE,IAAI,SAAoD,CAAC;IACzD,IAAI,OAA6D,CAAC;IAClE,IAAI,CAAC;QACH,CAAC,OAAO,EAAE,OAAO,EAAE,QAAQ,EAAE,SAAS,EAAE,OAAO,CAAC,GAAG,MAAM,OAAO,CAAC,GAAG,CAAC;YACnE,MAAM,CAAC,+BAA+B,CAAC;YACvC,MAAM,CAAC,+BAA+B,CAAC;YACvC,MAAM,CAAC,0CAA0C,CAAC;YAClD,MAAM,CAAC,0BAA0B,CAAC;YAClC,MAAM,CAAC,qCAAqC,CAAC;SAC9C,CAAC,CAAC;IACL,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,MAAM,GAAG,GAAG,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;QAC7D,OAAO,CAAC,MAAM,CAAC,KAAK,CAClB,+CAA+C,QAAQ,0DAA0D;YAC/G,uBAAuB,GAAG,IAAI;YAC9B,+FAA+F;YAC/F,8DAA8D,CACjE,CAAC;QACF,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAClB,CAAC;IAED,+DAA+D;IAC/D,gEAAgE;IAChE,wDAAwD;IACxD,MAAM,QAAQ,GAAG,SAAS,CAAC,eAAe,EAAE,CAAC,KAAK,CAChD,SAAS,CAAC,sBAAsB,CAAC;QAC/B,CAAC,OAAO,CAAC,iBAAiB,CAAC,EAAE,WAAW;QACxC,CAAC,OAAO,CAAC,oBAAoB,CAAC,EAAE,cAAc;KAC/C,CAAC,CACH,CAAC;IAEF,MAAM,QAAQ,GAAG,IAAI,OAAO,CAAC,kBAAkB,CAAC;QAC9C,QAAQ;QACR,cAAc,EAAE,CAAC,IAAI,OAAO,CAAC,kBAAkB,CAAC,IAAI,QAAQ,CAAC,iBAAiB,EAAE,CAAC,CAAC;KACnF,CAAC,CAAC;IAEH,+DAA+D;IAC/D,kDAAkD;IAClD,mEAAmE;IACnE,2CAA2C;IAC3C,QAAQ,CAAC,QAAQ,EAAE,CAAC;IAEpB,mEAAmE;IACnE,oEAAoE;IACpE,8DAA8D;IAC9D,aAAa;IACb,MAAM,QAAQ,GAAG,KAAK,IAAmB,EAAE;QACzC,IAAI,CAAC;YACH,MAAM,QAAQ,CAAC,QAAQ,EAAE,CAAC;QAC5B,CAAC;QAAC,MAAM,CAAC;YACP,wDAAwD;QAC1D,CAAC;IACH,CAAC,CAAC;IACF,OAAO,CAAC,IAAI,CAAC,SAAS,EAAE,QAAQ,CAAC,CAAC;IAClC,OAAO,CAAC,IAAI,CAAC,QAAQ,EAAE,QAAQ,CAAC,CAAC;AACnC,CAAC"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=server.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":""}
package/dist/server.js ADDED
@@ -0,0 +1,256 @@
1
+ // macf#196: OTEL bootstrap is now async + dynamic. We still import
2
+ // the module eagerly (to get the function export), but the actual
3
+ // SDK packages are loaded only when the env is set, inside
4
+ // `bootstrapOtel()`. Calls to `trace.getTracer()` before the bootstrap
5
+ // runs return the global no-op tracer — harmless, since no spans are
6
+ // created before main() awaits the bootstrap.
7
+ import { bootstrapOtel } from './otel.js';
8
+ import { SpanKind, SpanStatusCode } from '@opentelemetry/api';
9
+ import { getTracer, SpanNames } from './tracing.js';
10
+ import { loadConfig } from '@groundnuty/macf-core';
11
+ import { createLogger } from '@groundnuty/macf-core';
12
+ import { createMcpChannel } from './mcp.js';
13
+ import { createHealthState } from './health.js';
14
+ import { createHttpsServer } from './https.js';
15
+ import { createRegistryFromConfig } from '@groundnuty/macf-core';
16
+ import { checkCollision, CollisionError } from './collision.js';
17
+ import { registerShutdownHandler } from './shutdown.js';
18
+ import { generateToken } from '@groundnuty/macf-core';
19
+ import { createChallenge, verifyAndConsumeChallenge } from '@groundnuty/macf-core';
20
+ import { createChallengeStore } from '@groundnuty/macf-core';
21
+ import { signCSR } from '@groundnuty/macf-core';
22
+ import { loadCA } from '@groundnuty/macf-core';
23
+ import { HttpError } from '@groundnuty/macf-core';
24
+ import { formatNotifyContent } from './notify-formatter.js';
25
+ import { wakeViaTmux } from './tmux-wake.js';
26
+ async function main() {
27
+ // Bootstrap OTEL BEFORE anything calls `trace.getTracer()` with
28
+ // intent to record. Function is no-op when
29
+ // OTEL_EXPORTER_OTLP_ENDPOINT is unset; when set, dynamic-imports
30
+ // the SDK packages + registers the global provider. See macf#196.
31
+ await bootstrapOtel();
32
+ const config = loadConfig();
33
+ const logger = createLogger({
34
+ logPath: config.logPath,
35
+ debug: config.debug,
36
+ });
37
+ // Partial-startup failures (MCP connected, port bound, then registry
38
+ // or collision fails) would otherwise crash with only the stderr
39
+ // message from the outer catch — channel.log would show the agent
40
+ // starting and then go silent, leaving operators with no signal.
41
+ // Wrap the startup body so post-logger failures land in the log.
42
+ // Ultrareview finding H5.
43
+ try {
44
+ await runStartup();
45
+ }
46
+ catch (err) {
47
+ logger.error('startup_failed', {
48
+ error: err instanceof Error ? err.message : String(err),
49
+ code: err.code ?? 'unknown',
50
+ });
51
+ throw err;
52
+ }
53
+ async function runStartup() {
54
+ const mcp = createMcpChannel({ agentName: config.agentName });
55
+ const health = createHealthState(config.agentName, config.agentType);
56
+ const onNotify = async (payload) => {
57
+ const meta = { type: payload.type };
58
+ if (payload.issue_number !== undefined) {
59
+ meta['issue_number'] = String(payload.issue_number);
60
+ }
61
+ if (payload.source !== undefined) {
62
+ meta['source'] = payload.source;
63
+ }
64
+ const { content, issueNumber } = formatNotifyContent(payload);
65
+ if (issueNumber !== undefined) {
66
+ health.setCurrentIssue(issueNumber);
67
+ }
68
+ logger.info('notify_received', {
69
+ type: payload.type,
70
+ issue: payload.issue_number,
71
+ });
72
+ // macf#194: wrap MCP push in an INTERNAL child span of the active
73
+ // notify span. Shows up in Langfuse as a timed hop between the
74
+ // inbound HTTP and the tmux wake.
75
+ const tracer = getTracer();
76
+ await tracer.startActiveSpan(SpanNames.McpPush, { kind: SpanKind.INTERNAL }, async (span) => {
77
+ try {
78
+ await mcp.pushNotification(content, meta);
79
+ span.setStatus({ code: SpanStatusCode.OK });
80
+ }
81
+ catch (err) {
82
+ span.recordException(err);
83
+ span.setStatus({
84
+ code: SpanStatusCode.ERROR,
85
+ message: err instanceof Error ? err.message : String(err),
86
+ });
87
+ throw err;
88
+ }
89
+ finally {
90
+ span.end();
91
+ }
92
+ });
93
+ health.recordNotification();
94
+ logger.info('mcp_pushed', {
95
+ type: payload.type,
96
+ issue: payload.issue_number,
97
+ });
98
+ // macf#185: sidecar wake via tmux-send-to-claude.sh. The MCP push
99
+ // above deposits the notification in the channel-server's
100
+ // observable state but does NOT interrupt a running Claude TUI
101
+ // with a new prompt — /notify ≠ wake without this step. Tmux
102
+ // injection surfaces the notification as the TUI's next input
103
+ // turn, so the agent actually processes it. Fail-silent on any
104
+ // path where tmux isn't available (no workspace dir, no tmux
105
+ // session, helper missing, tmux command errors).
106
+ if (config.workspaceDir !== undefined) {
107
+ // Use the formatted content as the wake prompt — same text
108
+ // Claude would see via the MCP channel, just delivered
109
+ // through the input buffer path so it becomes an actual turn.
110
+ wakeViaTmux(content, {
111
+ workspaceDir: config.workspaceDir,
112
+ session: config.tmuxSession,
113
+ window: config.tmuxWindow,
114
+ logger,
115
+ });
116
+ }
117
+ else {
118
+ logger.info('tmux_wake_skipped', {
119
+ reason: 'no_workspace_dir',
120
+ detail: 'MACF_WORKSPACE_DIR unset',
121
+ });
122
+ }
123
+ };
124
+ // P2: Generate token early — needed for /sign endpoint and registry
125
+ const token = await generateToken();
126
+ const registry = createRegistryFromConfig(config.registry, config.project, token);
127
+ const { createGitHubClient } = await import('@groundnuty/macf-core');
128
+ // Build the variables client for the /sign challenge flow
129
+ let signPathPrefix;
130
+ switch (config.registry.type) {
131
+ case 'org':
132
+ signPathPrefix = `/orgs/${config.registry.org}`;
133
+ break;
134
+ case 'profile':
135
+ signPathPrefix = `/repos/${config.registry.user}/${config.registry.user}`;
136
+ break;
137
+ case 'repo':
138
+ signPathPrefix = `/repos/${config.registry.owner}/${config.registry.repo}`;
139
+ break;
140
+ }
141
+ const varsClient = createGitHubClient(signPathPrefix, token);
142
+ // In-memory challenge store (DR-010, #80). Process-local; server restart
143
+ // between step 1 and step 2 of a flow invalidates outstanding challenges.
144
+ const challengeStore = createChallengeStore();
145
+ // /sign endpoint handler — two-step challenge-response (DR-010).
146
+ // Step 1: allocate challenge, return id + instruction (no registry write).
147
+ // Step 2: verify challenge_id + registry-observed value, sign CSR.
148
+ const onSign = async (request) => {
149
+ // Try to load CA key — if not available, this agent can't sign.
150
+ let ca;
151
+ try {
152
+ ca = loadCA(config.caCertPath, config.caKeyPath);
153
+ }
154
+ catch {
155
+ throw new HttpError(503, 'CA key not available on this agent');
156
+ }
157
+ if (!request.challenge_done) {
158
+ // Step 1: allocate in-memory challenge, return id + instruction.
159
+ const challenge = createChallenge({
160
+ project: config.project,
161
+ agentName: request.agent_name,
162
+ store: challengeStore,
163
+ });
164
+ logger.info('sign_challenge_created', {
165
+ agent_name: request.agent_name,
166
+ challenge_id: challenge.challengeId,
167
+ });
168
+ return {
169
+ challenge_id: challenge.challengeId,
170
+ instruction: challenge.instruction,
171
+ };
172
+ }
173
+ // Step 2: verify challenge + sign CSR. The refine() on SignRequestSchema
174
+ // already guarantees challenge_id is present when challenge_done is true.
175
+ const result = await verifyAndConsumeChallenge({
176
+ project: config.project,
177
+ agentName: request.agent_name,
178
+ challengeId: request.challenge_id,
179
+ store: challengeStore,
180
+ client: varsClient,
181
+ });
182
+ if (result === 'mismatch') {
183
+ // Generic error — do not leak which check failed (no oracle for
184
+ // attackers probing expired/mismatched-agent/wrong-value, etc).
185
+ logger.warn('sign_challenge_failed', { agent_name: request.agent_name });
186
+ throw new HttpError(401, 'challenge verification failed');
187
+ }
188
+ logger.info('sign_challenge_verified', { agent_name: request.agent_name });
189
+ const certPem = await signCSR({
190
+ csrPem: request.csr,
191
+ agentName: request.agent_name,
192
+ caCertPem: ca.certPem,
193
+ caKeyPem: ca.keyPem,
194
+ });
195
+ logger.info('sign_cert_issued', { agent_name: request.agent_name });
196
+ return { cert: certPem };
197
+ };
198
+ const httpsServer = createHttpsServer({
199
+ caCertPath: config.caCertPath,
200
+ agentCertPath: config.agentCertPath,
201
+ agentKeyPath: config.agentKeyPath,
202
+ onNotify,
203
+ onHealth: () => health.getHealth(),
204
+ onSign,
205
+ logger,
206
+ });
207
+ // P1: Connect MCP channel
208
+ await mcp.connect();
209
+ // P1: Bind port
210
+ const { actualPort } = await httpsServer.start(config.port, config.host);
211
+ // P2: Collision detection
212
+ const collisionResult = await checkCollision(config.agentName, registry, {
213
+ caCertPath: config.caCertPath,
214
+ agentCertPath: config.agentCertPath,
215
+ agentKeyPath: config.agentKeyPath,
216
+ }, logger);
217
+ if (collisionResult.action === 'abort') {
218
+ await httpsServer.stop();
219
+ throw new CollisionError(config.agentName, collisionResult.existing.host, collisionResult.existing.port);
220
+ }
221
+ // P2: Register in GitHub variable (use advertiseHost, not bind address)
222
+ const agentInfo = {
223
+ host: config.advertiseHost,
224
+ port: actualPort,
225
+ type: config.agentType,
226
+ instance_id: config.instanceId,
227
+ started: new Date().toISOString(),
228
+ };
229
+ await registry.register(config.agentName, agentInfo);
230
+ logger.info('registered', {
231
+ agent: config.agentName,
232
+ host: config.advertiseHost,
233
+ port: actualPort,
234
+ instance_id: config.instanceId,
235
+ });
236
+ // P2: Register shutdown handler
237
+ registerShutdownHandler({
238
+ agentName: config.agentName,
239
+ registry,
240
+ httpsServer,
241
+ logger,
242
+ });
243
+ logger.info('server_started', {
244
+ port: actualPort,
245
+ host: config.advertiseHost,
246
+ agent: config.agentName,
247
+ type: config.agentType,
248
+ instance_id: config.instanceId,
249
+ });
250
+ } // end runStartup
251
+ }
252
+ main().catch((err) => {
253
+ process.stderr.write(`Fatal: ${err instanceof Error ? err.message : String(err)}\n`);
254
+ process.exitCode = 1;
255
+ });
256
+ //# sourceMappingURL=server.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"server.js","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"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"}