@hooklink/cli 0.1.69 → 0.1.70

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 (109) hide show
  1. package/dist/__tests__/agent/lease.test.d.ts +2 -0
  2. package/dist/__tests__/agent/lease.test.d.ts.map +1 -0
  3. package/dist/__tests__/agent/lease.test.js +31 -0
  4. package/dist/__tests__/agent/lease.test.js.map +1 -0
  5. package/dist/__tests__/agent/local-server.test.d.ts +9 -0
  6. package/dist/__tests__/agent/local-server.test.d.ts.map +1 -0
  7. package/dist/__tests__/agent/local-server.test.js +218 -0
  8. package/dist/__tests__/agent/local-server.test.js.map +1 -0
  9. package/dist/__tests__/agent/local-token.test.d.ts +2 -0
  10. package/dist/__tests__/agent/local-token.test.d.ts.map +1 -0
  11. package/dist/__tests__/agent/local-token.test.js +42 -0
  12. package/dist/__tests__/agent/local-token.test.js.map +1 -0
  13. package/dist/__tests__/agent/state.test.d.ts +2 -0
  14. package/dist/__tests__/agent/state.test.d.ts.map +1 -0
  15. package/dist/__tests__/agent/state.test.js +63 -0
  16. package/dist/__tests__/agent/state.test.js.map +1 -0
  17. package/dist/__tests__/agent/supervisor.test.d.ts +2 -0
  18. package/dist/__tests__/agent/supervisor.test.d.ts.map +1 -0
  19. package/dist/__tests__/agent/supervisor.test.js +126 -0
  20. package/dist/__tests__/agent/supervisor.test.js.map +1 -0
  21. package/dist/__tests__/json-events.test.d.ts +2 -0
  22. package/dist/__tests__/json-events.test.d.ts.map +1 -0
  23. package/dist/__tests__/json-events.test.js +101 -0
  24. package/dist/__tests__/json-events.test.js.map +1 -0
  25. package/dist/agent/endpoint-resolver.d.ts +35 -0
  26. package/dist/agent/endpoint-resolver.d.ts.map +1 -0
  27. package/dist/agent/endpoint-resolver.js +181 -0
  28. package/dist/agent/endpoint-resolver.js.map +1 -0
  29. package/dist/agent/lease.d.ts +18 -0
  30. package/dist/agent/lease.d.ts.map +1 -0
  31. package/dist/agent/lease.js +40 -0
  32. package/dist/agent/lease.js.map +1 -0
  33. package/dist/agent/local-server.d.ts +69 -0
  34. package/dist/agent/local-server.d.ts.map +1 -0
  35. package/dist/agent/local-server.js +426 -0
  36. package/dist/agent/local-server.js.map +1 -0
  37. package/dist/agent/local-token.d.ts +18 -0
  38. package/dist/agent/local-token.d.ts.map +1 -0
  39. package/dist/agent/local-token.js +65 -0
  40. package/dist/agent/local-token.js.map +1 -0
  41. package/dist/agent/paths.d.ts +12 -0
  42. package/dist/agent/paths.d.ts.map +1 -0
  43. package/dist/agent/paths.js +37 -0
  44. package/dist/agent/paths.js.map +1 -0
  45. package/dist/agent/preflight.d.ts +18 -0
  46. package/dist/agent/preflight.d.ts.map +1 -0
  47. package/dist/agent/preflight.js +87 -0
  48. package/dist/agent/preflight.js.map +1 -0
  49. package/dist/agent/resolve-bin.d.ts +26 -0
  50. package/dist/agent/resolve-bin.d.ts.map +1 -0
  51. package/dist/agent/resolve-bin.js +55 -0
  52. package/dist/agent/resolve-bin.js.map +1 -0
  53. package/dist/agent/service/darwin.d.ts +9 -0
  54. package/dist/agent/service/darwin.d.ts.map +1 -0
  55. package/dist/agent/service/darwin.js +85 -0
  56. package/dist/agent/service/darwin.js.map +1 -0
  57. package/dist/agent/service/index.d.ts +24 -0
  58. package/dist/agent/service/index.d.ts.map +1 -0
  59. package/dist/agent/service/index.js +71 -0
  60. package/dist/agent/service/index.js.map +1 -0
  61. package/dist/agent/service/linux.d.ts +10 -0
  62. package/dist/agent/service/linux.d.ts.map +1 -0
  63. package/dist/agent/service/linux.js +75 -0
  64. package/dist/agent/service/linux.js.map +1 -0
  65. package/dist/agent/service/win32.d.ts +9 -0
  66. package/dist/agent/service/win32.d.ts.map +1 -0
  67. package/dist/agent/service/win32.js +109 -0
  68. package/dist/agent/service/win32.js.map +1 -0
  69. package/dist/agent/state.d.ts +25 -0
  70. package/dist/agent/state.d.ts.map +1 -0
  71. package/dist/agent/state.js +63 -0
  72. package/dist/agent/state.js.map +1 -0
  73. package/dist/agent/supervisor.d.ts +116 -0
  74. package/dist/agent/supervisor.d.ts.map +1 -0
  75. package/dist/agent/supervisor.js +353 -0
  76. package/dist/agent/supervisor.js.map +1 -0
  77. package/dist/agent/zombie-cleanup.d.ts +20 -0
  78. package/dist/agent/zombie-cleanup.d.ts.map +1 -0
  79. package/dist/agent/zombie-cleanup.js +124 -0
  80. package/dist/agent/zombie-cleanup.js.map +1 -0
  81. package/dist/commands/agent.d.ts +12 -0
  82. package/dist/commands/agent.d.ts.map +1 -0
  83. package/dist/commands/agent.js +173 -0
  84. package/dist/commands/agent.js.map +1 -0
  85. package/dist/commands/connect.d.ts.map +1 -1
  86. package/dist/commands/connect.js +106 -8
  87. package/dist/commands/connect.js.map +1 -1
  88. package/dist/commands/tunnel.d.ts.map +1 -1
  89. package/dist/commands/tunnel.js +103 -4
  90. package/dist/commands/tunnel.js.map +1 -1
  91. package/dist/core/websocket-client.d.ts +2 -0
  92. package/dist/core/websocket-client.d.ts.map +1 -1
  93. package/dist/core/websocket-client.js +6 -0
  94. package/dist/core/websocket-client.js.map +1 -1
  95. package/dist/core/websocket-pool.d.ts +1 -0
  96. package/dist/core/websocket-pool.d.ts.map +1 -1
  97. package/dist/core/websocket-pool.js +3 -0
  98. package/dist/core/websocket-pool.js.map +1 -1
  99. package/dist/index.js +7 -1
  100. package/dist/index.js.map +1 -1
  101. package/dist/utils/json-events.d.ts +32 -0
  102. package/dist/utils/json-events.d.ts.map +1 -0
  103. package/dist/utils/json-events.js +71 -0
  104. package/dist/utils/json-events.js.map +1 -0
  105. package/dist/utils/logger.d.ts +6 -0
  106. package/dist/utils/logger.d.ts.map +1 -1
  107. package/dist/utils/logger.js +30 -0
  108. package/dist/utils/logger.js.map +1 -1
  109. package/package.json +6 -2
@@ -0,0 +1,63 @@
1
+ "use strict";
2
+ /**
3
+ * On-disk state for zombie cleanup across agent restarts.
4
+ *
5
+ * Stored at `~/.hooklink/agent.state` (0600) with a minimal schema:
6
+ * [{ tunnel_id, pid, api_key_hash, local_target, started_at }]
7
+ *
8
+ * We hash the API key before writing so a stolen state file doesn't
9
+ * leak credentials. `api_key_hash === null` identifies trial tunnels.
10
+ */
11
+ var __importDefault = (this && this.__importDefault) || function (mod) {
12
+ return (mod && mod.__esModule) ? mod : { "default": mod };
13
+ };
14
+ Object.defineProperty(exports, "__esModule", { value: true });
15
+ exports.hashApiKey = hashApiKey;
16
+ exports.readState = readState;
17
+ exports.writeState = writeState;
18
+ const crypto_1 = __importDefault(require("crypto"));
19
+ const fs_1 = __importDefault(require("fs"));
20
+ const path_1 = __importDefault(require("path"));
21
+ const paths_1 = require("./paths");
22
+ function hashApiKey(apiKey) {
23
+ return crypto_1.default.createHash('sha256').update(apiKey).digest('hex');
24
+ }
25
+ function readState() {
26
+ try {
27
+ const raw = fs_1.default.readFileSync((0, paths_1.getStatePath)(), 'utf8');
28
+ const parsed = JSON.parse(raw);
29
+ if (!Array.isArray(parsed))
30
+ return [];
31
+ return parsed.filter((entry) => entry &&
32
+ typeof entry.tunnel_id === 'string' &&
33
+ typeof entry.pid === 'number' &&
34
+ typeof entry.local_target === 'string' &&
35
+ typeof entry.started_at === 'string');
36
+ }
37
+ catch (err) {
38
+ // Corrupt or missing. Rename a corrupt file so we can investigate
39
+ // later and start fresh.
40
+ const statePath = (0, paths_1.getStatePath)();
41
+ if (err.code !== 'ENOENT') {
42
+ try {
43
+ fs_1.default.renameSync(statePath, `${statePath}.corrupted.${Date.now()}`);
44
+ }
45
+ catch {
46
+ // If we can't rename, just drop it.
47
+ }
48
+ }
49
+ return [];
50
+ }
51
+ }
52
+ /**
53
+ * Atomic write via tmpfile + rename. Guarantees readers never see a
54
+ * half-written file even if the agent crashes mid-write.
55
+ */
56
+ function writeState(tunnels) {
57
+ const statePath = (0, paths_1.getStatePath)();
58
+ fs_1.default.mkdirSync(path_1.default.dirname(statePath), { recursive: true, mode: 0o700 });
59
+ const tmp = `${statePath}.${process.pid}.${Date.now()}.tmp`;
60
+ fs_1.default.writeFileSync(tmp, JSON.stringify(tunnels, null, 2), { mode: 0o600 });
61
+ fs_1.default.renameSync(tmp, statePath);
62
+ }
63
+ //# sourceMappingURL=state.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"state.js","sourceRoot":"","sources":["../../src/agent/state.ts"],"names":[],"mappings":";AAAA;;;;;;;;GAQG;;;;;AAgBH,gCAEC;AAED,8BA0BC;AAMD,gCAMC;AAxDD,oDAA4B;AAC5B,4CAAoB;AACpB,gDAAwB;AACxB,mCAAuC;AAWvC,SAAgB,UAAU,CAAC,MAAc;IACvC,OAAO,gBAAM,CAAC,UAAU,CAAC,QAAQ,CAAC,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;AAClE,CAAC;AAED,SAAgB,SAAS;IACvB,IAAI,CAAC;QACH,MAAM,GAAG,GAAG,YAAE,CAAC,YAAY,CAAC,IAAA,oBAAY,GAAE,EAAE,MAAM,CAAC,CAAC;QACpD,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;QAC/B,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,MAAM,CAAC;YAAE,OAAO,EAAE,CAAC;QACtC,OAAO,MAAM,CAAC,MAAM,CAClB,CAAC,KAAK,EAA4B,EAAE,CAClC,KAAK;YACL,OAAO,KAAK,CAAC,SAAS,KAAK,QAAQ;YACnC,OAAO,KAAK,CAAC,GAAG,KAAK,QAAQ;YAC7B,OAAO,KAAK,CAAC,YAAY,KAAK,QAAQ;YACtC,OAAO,KAAK,CAAC,UAAU,KAAK,QAAQ,CACvC,CAAC;IACJ,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,kEAAkE;QAClE,yBAAyB;QACzB,MAAM,SAAS,GAAG,IAAA,oBAAY,GAAE,CAAC;QACjC,IAAK,GAA6B,CAAC,IAAI,KAAK,QAAQ,EAAE,CAAC;YACrD,IAAI,CAAC;gBACH,YAAE,CAAC,UAAU,CAAC,SAAS,EAAE,GAAG,SAAS,cAAc,IAAI,CAAC,GAAG,EAAE,EAAE,CAAC,CAAC;YACnE,CAAC;YAAC,MAAM,CAAC;gBACP,oCAAoC;YACtC,CAAC;QACH,CAAC;QACD,OAAO,EAAE,CAAC;IACZ,CAAC;AACH,CAAC;AAED;;;GAGG;AACH,SAAgB,UAAU,CAAC,OAA0B;IACnD,MAAM,SAAS,GAAG,IAAA,oBAAY,GAAE,CAAC;IACjC,YAAE,CAAC,SAAS,CAAC,cAAI,CAAC,OAAO,CAAC,SAAS,CAAC,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC,CAAC;IACxE,MAAM,GAAG,GAAG,GAAG,SAAS,IAAI,OAAO,CAAC,GAAG,IAAI,IAAI,CAAC,GAAG,EAAE,MAAM,CAAC;IAC5D,YAAE,CAAC,aAAa,CAAC,GAAG,EAAE,IAAI,CAAC,SAAS,CAAC,OAAO,EAAE,IAAI,EAAE,CAAC,CAAC,EAAE,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC,CAAC;IACzE,YAAE,CAAC,UAAU,CAAC,GAAG,EAAE,SAAS,CAAC,CAAC;AAChC,CAAC"}
@@ -0,0 +1,116 @@
1
+ /**
2
+ * Subprocess supervisor. Spawns one `hooklink connect` (authenticated)
3
+ * or `hooklink tunnel` (trial) child per tunnel, parses its `--json`
4
+ * event stream, and exposes a minimal imperative surface
5
+ * (`startTunnel`, `stopTunnel`, `getTunnel`, `listTunnels`) for the
6
+ * HTTP server to call.
7
+ *
8
+ * The supervisor is storage-less by design: agent.state persistence
9
+ * lives in state.ts so restart-time zombie cleanup has a single
10
+ * source of truth.
11
+ */
12
+ import { ChildProcess } from 'child_process';
13
+ import { EventEmitter } from 'events';
14
+ import fs from 'fs';
15
+ import { getTunnelLogPath } from './paths';
16
+ export type TunnelState = 'starting' | 'live' | 'stopping' | 'stopped' | 'crashed';
17
+ export interface TunnelInfo {
18
+ tunnel_id: string;
19
+ public_url: string | null;
20
+ local_target: string;
21
+ pid: number | null;
22
+ started_at: string;
23
+ state: TunnelState;
24
+ /** Identity of the plugin/CLI that requested the tunnel.
25
+ * - `api_key_hash` for authenticated tunnels
26
+ * - `owner_id` (the plugin's X-Hooklink-Owner-Id) for trial tunnels */
27
+ api_key_hash: string | null;
28
+ owner_id: string | null;
29
+ trial: boolean;
30
+ expires_at?: string;
31
+ session_id?: string;
32
+ limits?: {
33
+ max_requests: number;
34
+ max_bandwidth_bytes: number;
35
+ session_duration_seconds: number;
36
+ };
37
+ usage?: {
38
+ requests: number;
39
+ bytes: number;
40
+ remaining_seconds: number;
41
+ };
42
+ last_error?: {
43
+ code: string | number;
44
+ message: string;
45
+ };
46
+ }
47
+ export interface StartTunnelAuthenticated {
48
+ mode: 'authenticated';
49
+ tunnel_id: string;
50
+ keyword: string;
51
+ api_key: string;
52
+ api_key_hash: string;
53
+ local_target: string;
54
+ public_url: string;
55
+ }
56
+ export interface StartTunnelTrial {
57
+ mode: 'trial';
58
+ owner_id: string;
59
+ local_target: string;
60
+ }
61
+ export type StartTunnelOptions = StartTunnelAuthenticated | StartTunnelTrial;
62
+ export interface SupervisorOptions {
63
+ /** Absolute path to the `hooklink` CLI JS entry (e.g. `dist/index.js`).
64
+ * The supervisor always invokes it with `process.execPath` so we
65
+ * never depend on a shebang + +x bit being preserved through
66
+ * packaging, containers, or Windows filesystems. */
67
+ binaryPath: string;
68
+ /** Override the node binary. Defaults to `process.execPath`. Useful
69
+ * when the service was installed against a specific node (e.g. the
70
+ * bundled Node 20 under ~/.hooklink/node/). */
71
+ nodePath?: string;
72
+ /** Max seconds to wait for a tunnel_ready event on spawn. */
73
+ spawnTimeoutMs?: number;
74
+ /** Max tunnels allowed concurrently; protects the agent from runaway plugins. */
75
+ maxConcurrent?: number;
76
+ }
77
+ export declare class Supervisor extends EventEmitter {
78
+ private tunnels;
79
+ private binaryPath;
80
+ private nodePath;
81
+ private spawnTimeoutMs;
82
+ private maxConcurrent;
83
+ constructor(opts: SupervisorOptions);
84
+ /**
85
+ * Spawn the subprocess and resolve once the child emits
86
+ * `tunnel_ready` (or rejects on timeout / fatal / early exit).
87
+ */
88
+ startTunnel(opts: StartTunnelOptions): Promise<TunnelInfo>;
89
+ /** Resolve when the tunnel reaches `live`; reject on spawn timeout
90
+ * or terminal state. */
91
+ private waitForReady;
92
+ private spawnChild;
93
+ private handleEventLine;
94
+ stopTunnel(tunnelId: string, graceMs?: number): Promise<boolean>;
95
+ private cleanupTunnelLog;
96
+ getTunnel(tunnelId: string): TunnelInfo | null;
97
+ listTunnels(filter?: (info: TunnelInfo) => boolean): TunnelInfo[];
98
+ getRecentRequests(tunnelId: string, limit?: number): InternalTunnel['recentRequests'];
99
+ shutdown(graceMs?: number): Promise<void>;
100
+ /** Test-only. Accessor used by zombie cleanup to know which PIDs are active. */
101
+ getTrackedPids(): number[];
102
+ }
103
+ interface InternalTunnel {
104
+ info: TunnelInfo;
105
+ child: ChildProcess;
106
+ logStream: fs.WriteStream;
107
+ recentRequests: Array<{
108
+ timestamp: string;
109
+ method: string;
110
+ path: string;
111
+ status: number;
112
+ ms: number;
113
+ }>;
114
+ }
115
+ export { getTunnelLogPath };
116
+ //# sourceMappingURL=supervisor.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"supervisor.d.ts","sourceRoot":"","sources":["../../src/agent/supervisor.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,353 @@
1
+ "use strict";
2
+ /**
3
+ * Subprocess supervisor. Spawns one `hooklink connect` (authenticated)
4
+ * or `hooklink tunnel` (trial) child per tunnel, parses its `--json`
5
+ * event stream, and exposes a minimal imperative surface
6
+ * (`startTunnel`, `stopTunnel`, `getTunnel`, `listTunnels`) for the
7
+ * HTTP server to call.
8
+ *
9
+ * The supervisor is storage-less by design: agent.state persistence
10
+ * lives in state.ts so restart-time zombie cleanup has a single
11
+ * source of truth.
12
+ */
13
+ var __importDefault = (this && this.__importDefault) || function (mod) {
14
+ return (mod && mod.__esModule) ? mod : { "default": mod };
15
+ };
16
+ Object.defineProperty(exports, "__esModule", { value: true });
17
+ exports.getTunnelLogPath = exports.Supervisor = void 0;
18
+ const child_process_1 = require("child_process");
19
+ const events_1 = require("events");
20
+ const fs_1 = __importDefault(require("fs"));
21
+ const readline_1 = __importDefault(require("readline"));
22
+ const paths_1 = require("./paths");
23
+ Object.defineProperty(exports, "getTunnelLogPath", { enumerable: true, get: function () { return paths_1.getTunnelLogPath; } });
24
+ class Supervisor extends events_1.EventEmitter {
25
+ constructor(opts) {
26
+ super();
27
+ this.tunnels = new Map();
28
+ this.binaryPath = opts.binaryPath;
29
+ this.nodePath = opts.nodePath ?? process.execPath;
30
+ this.spawnTimeoutMs = opts.spawnTimeoutMs ?? 8000;
31
+ this.maxConcurrent = opts.maxConcurrent ?? 20;
32
+ fs_1.default.mkdirSync((0, paths_1.getTunnelsDir)(), { recursive: true, mode: 0o700 });
33
+ }
34
+ /**
35
+ * Spawn the subprocess and resolve once the child emits
36
+ * `tunnel_ready` (or rejects on timeout / fatal / early exit).
37
+ */
38
+ async startTunnel(opts) {
39
+ if (this.tunnels.size >= this.maxConcurrent) {
40
+ const err = new Error('too_many_local_tunnels');
41
+ err.code = 'too_many_local_tunnels';
42
+ throw err;
43
+ }
44
+ const { child, args, env } = this.spawnChild(opts);
45
+ const tunnelId = opts.mode === 'authenticated' ? opts.tunnel_id : `trial-pending-${child.pid}`;
46
+ const now = new Date().toISOString();
47
+ const info = {
48
+ info: {
49
+ tunnel_id: tunnelId,
50
+ public_url: opts.mode === 'authenticated' ? opts.public_url : null,
51
+ local_target: opts.local_target,
52
+ pid: child.pid ?? null,
53
+ started_at: now,
54
+ state: 'starting',
55
+ api_key_hash: opts.mode === 'authenticated' ? opts.api_key_hash : null,
56
+ owner_id: opts.mode === 'trial' ? opts.owner_id : null,
57
+ trial: opts.mode === 'trial',
58
+ },
59
+ child,
60
+ logStream: fs_1.default.createWriteStream((0, paths_1.getTunnelLogPath)(tunnelId), {
61
+ flags: 'a',
62
+ mode: 0o600,
63
+ }),
64
+ recentRequests: [],
65
+ };
66
+ this.tunnels.set(tunnelId, info);
67
+ // Wire stdout → readline → event parser, with the raw lines also
68
+ // appended to the per-tunnel log. We keep a reference to `info`
69
+ // rather than looking it up by id each time — trial tunnels are
70
+ // re-keyed by `trial_started.session_id`, so a captured id goes
71
+ // stale after the re-key.
72
+ const rl = readline_1.default.createInterface({ input: child.stdout });
73
+ rl.on('line', (line) => {
74
+ info.logStream.write(line + '\n');
75
+ this.handleEventLine(info, line);
76
+ });
77
+ child.stderr?.on('data', (chunk) => {
78
+ info.logStream.write(`stderr: ${chunk.toString()}`);
79
+ });
80
+ child.on('exit', (code, signal) => {
81
+ const entry = info;
82
+ if (!entry)
83
+ return;
84
+ if (signal === 'SIGTERM' || signal === 'SIGINT' || code === 0) {
85
+ entry.info.state = 'stopped';
86
+ }
87
+ else if (entry.info.state !== 'crashed') {
88
+ entry.info.state = 'crashed';
89
+ }
90
+ entry.info.pid = null;
91
+ entry.logStream.end();
92
+ this.emit('tunnel_exited', entry.info);
93
+ });
94
+ child.on('error', (err) => {
95
+ info.info.state = 'crashed';
96
+ info.info.last_error = {
97
+ code: 'spawn_error',
98
+ message: err.message,
99
+ };
100
+ this.emit('tunnel_exited', info.info);
101
+ });
102
+ // Wait for tunnel_ready (or rejection).
103
+ try {
104
+ await this.waitForReady(info);
105
+ }
106
+ catch (err) {
107
+ // Tear down on failure; don't leak subprocesses. Remove the
108
+ // entry from the map eagerly so callers observe the failure
109
+ // immediately, then SIGTERM in the background. Trial mode may
110
+ // have re-keyed the tunnel under its session id between spawn
111
+ // and timeout, so we use the live id.
112
+ const liveId = info.info.tunnel_id;
113
+ this.tunnels.delete(liveId);
114
+ try {
115
+ info.child.kill('SIGTERM');
116
+ }
117
+ catch {
118
+ // Child may already be gone.
119
+ }
120
+ throw err;
121
+ }
122
+ this.emit('tunnel_started', info.info, { args, env });
123
+ return { ...info.info };
124
+ }
125
+ /** Resolve when the tunnel reaches `live`; reject on spawn timeout
126
+ * or terminal state. */
127
+ waitForReady(entry) {
128
+ return new Promise((resolve, reject) => {
129
+ const timer = setTimeout(() => {
130
+ const detail = entry.info.last_error?.message ||
131
+ `No tunnel_ready within ${this.spawnTimeoutMs}ms`;
132
+ const err = new Error(detail);
133
+ err.code = 'spawn_timeout';
134
+ reject(err);
135
+ }, this.spawnTimeoutMs);
136
+ const onEvent = (id, event) => {
137
+ if (id !== entry.info.tunnel_id)
138
+ return;
139
+ if (event === 'ready') {
140
+ cleanup();
141
+ resolve();
142
+ }
143
+ else if (event === 'fatal' || event === 'exited') {
144
+ cleanup();
145
+ const err = new Error(entry.info.last_error?.message || 'Subprocess terminated before ready');
146
+ err.code = String(entry.info.last_error?.code || 'spawn_failed');
147
+ reject(err);
148
+ }
149
+ };
150
+ const cleanup = () => {
151
+ clearTimeout(timer);
152
+ this.off('internal_event', onEvent);
153
+ };
154
+ this.on('internal_event', onEvent);
155
+ });
156
+ }
157
+ spawnChild(opts) {
158
+ let args;
159
+ let env = { ...process.env };
160
+ if (opts.mode === 'authenticated') {
161
+ args = ['connect', opts.keyword, '--target', opts.local_target, '--json'];
162
+ env = { ...env, HOOKLINK_API_KEY: opts.api_key };
163
+ }
164
+ else {
165
+ args = ['tunnel', opts.local_target, '--json'];
166
+ // Scrub any ambient API key so the trial subprocess genuinely
167
+ // hits the unauthenticated trial endpoint.
168
+ delete env.HOOKLINK_API_KEY;
169
+ }
170
+ // Always invoke as `node <entry> ...args` so we don't depend on a
171
+ // shebang or +x bit surviving packaging/filesystems.
172
+ const child = (0, child_process_1.spawn)(this.nodePath, [this.binaryPath, ...args], {
173
+ env,
174
+ stdio: ['ignore', 'pipe', 'pipe'],
175
+ detached: false,
176
+ });
177
+ return { child, args, env };
178
+ }
179
+ handleEventLine(entry, line) {
180
+ if (!line.trim().startsWith('{'))
181
+ return;
182
+ let event;
183
+ try {
184
+ event = JSON.parse(line);
185
+ }
186
+ catch {
187
+ return;
188
+ }
189
+ const name = event.event;
190
+ switch (name) {
191
+ case 'tunnel_ready': {
192
+ if (typeof event.public_url === 'string') {
193
+ entry.info.public_url = event.public_url;
194
+ }
195
+ if (typeof event.local_target === 'string' && entry.info.local_target === '') {
196
+ entry.info.local_target = event.local_target;
197
+ }
198
+ entry.info.state = 'live';
199
+ this.emit('internal_event', entry.info.tunnel_id, 'ready');
200
+ break;
201
+ }
202
+ case 'authenticated':
203
+ // Waiting on `tunnel_ready` for the live transition; this is
204
+ // just a breadcrumb.
205
+ break;
206
+ case 'trial_started': {
207
+ const sessionId = event.session_id;
208
+ if (sessionId) {
209
+ entry.info.session_id = sessionId;
210
+ // Re-key the tunnel under its real session id now that we have one.
211
+ if (entry.info.tunnel_id !== sessionId) {
212
+ this.tunnels.delete(entry.info.tunnel_id);
213
+ entry.info.tunnel_id = sessionId;
214
+ this.tunnels.set(sessionId, entry);
215
+ }
216
+ }
217
+ if (typeof event.expires_at === 'string') {
218
+ entry.info.expires_at = event.expires_at;
219
+ }
220
+ if (event.limits && typeof event.limits === 'object') {
221
+ entry.info.limits = event.limits;
222
+ }
223
+ if (typeof event.public_url === 'string') {
224
+ entry.info.public_url = event.public_url;
225
+ }
226
+ break;
227
+ }
228
+ case 'usage': {
229
+ const requests = typeof event.requests === 'number' ? event.requests : 0;
230
+ const bytes = typeof event.bytes === 'number' ? event.bytes : 0;
231
+ const remaining = typeof event.remaining_seconds === 'number'
232
+ ? event.remaining_seconds
233
+ : 0;
234
+ entry.info.usage = {
235
+ requests,
236
+ bytes,
237
+ remaining_seconds: remaining,
238
+ };
239
+ break;
240
+ }
241
+ case 'request': {
242
+ const record = {
243
+ timestamp: typeof event.timestamp === 'string'
244
+ ? event.timestamp
245
+ : new Date().toISOString(),
246
+ method: String(event.method ?? ''),
247
+ path: String(event.path ?? ''),
248
+ status: typeof event.status === 'number' ? event.status : 0,
249
+ ms: typeof event.ms === 'number' ? event.ms : 0,
250
+ };
251
+ entry.recentRequests.push(record);
252
+ if (entry.recentRequests.length > 100) {
253
+ entry.recentRequests.shift();
254
+ }
255
+ break;
256
+ }
257
+ case 'trial_expired': {
258
+ entry.info.state = 'crashed';
259
+ entry.info.last_error = {
260
+ code: 'trial_expired',
261
+ message: `trial_expired_${event.reason ?? 'unknown'}`,
262
+ };
263
+ break;
264
+ }
265
+ case 'fatal': {
266
+ entry.info.state = 'crashed';
267
+ entry.info.last_error = {
268
+ code: String(event.code ?? 'fatal'),
269
+ message: String(event.message ?? 'fatal'),
270
+ };
271
+ this.emit('internal_event', entry.info.tunnel_id, 'fatal');
272
+ break;
273
+ }
274
+ case 'disconnected':
275
+ // Reconnect may follow; don't change state yet.
276
+ break;
277
+ }
278
+ }
279
+ async stopTunnel(tunnelId, graceMs = 5000) {
280
+ const entry = this.tunnels.get(tunnelId);
281
+ if (!entry)
282
+ return false;
283
+ entry.info.state = 'stopping';
284
+ const child = entry.child;
285
+ if (!child.pid || child.exitCode !== null) {
286
+ this.tunnels.delete(tunnelId);
287
+ return true;
288
+ }
289
+ child.kill('SIGTERM');
290
+ const exited = await waitForExit(child, graceMs);
291
+ if (!exited) {
292
+ try {
293
+ child.kill('SIGKILL');
294
+ }
295
+ catch {
296
+ // Already gone.
297
+ }
298
+ }
299
+ this.tunnels.delete(tunnelId);
300
+ this.emit('internal_event', tunnelId, 'exited');
301
+ this.cleanupTunnelLog(tunnelId);
302
+ return true;
303
+ }
304
+ cleanupTunnelLog(tunnelId) {
305
+ try {
306
+ fs_1.default.unlinkSync((0, paths_1.getTunnelLogPath)(tunnelId));
307
+ }
308
+ catch {
309
+ // Log may be retained for crashed tunnels; ignore.
310
+ }
311
+ }
312
+ getTunnel(tunnelId) {
313
+ const entry = this.tunnels.get(tunnelId);
314
+ return entry ? { ...entry.info } : null;
315
+ }
316
+ listTunnels(filter) {
317
+ const out = [];
318
+ for (const entry of this.tunnels.values()) {
319
+ if (!filter || filter(entry.info))
320
+ out.push({ ...entry.info });
321
+ }
322
+ return out;
323
+ }
324
+ getRecentRequests(tunnelId, limit = 20) {
325
+ const entry = this.tunnels.get(tunnelId);
326
+ if (!entry)
327
+ return [];
328
+ return entry.recentRequests.slice(-Math.min(limit, 100));
329
+ }
330
+ async shutdown(graceMs = 5000) {
331
+ const ids = Array.from(this.tunnels.keys());
332
+ await Promise.all(ids.map((id) => this.stopTunnel(id, graceMs)));
333
+ }
334
+ /** Test-only. Accessor used by zombie cleanup to know which PIDs are active. */
335
+ getTrackedPids() {
336
+ return Array.from(this.tunnels.values())
337
+ .map((e) => e.info.pid)
338
+ .filter((pid) => pid !== null);
339
+ }
340
+ }
341
+ exports.Supervisor = Supervisor;
342
+ function waitForExit(child, ms) {
343
+ if (child.exitCode !== null)
344
+ return Promise.resolve(true);
345
+ return new Promise((resolve) => {
346
+ const timer = setTimeout(() => resolve(false), ms);
347
+ child.once('exit', () => {
348
+ clearTimeout(timer);
349
+ resolve(true);
350
+ });
351
+ });
352
+ }
353
+ //# sourceMappingURL=supervisor.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"supervisor.js","sourceRoot":"","sources":["../../src/agent/supervisor.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,20 @@
1
+ /**
2
+ * On agent startup, reap any subprocesses left behind by a previous
3
+ * agent run. We read `agent.state` for PIDs we know about; for a
4
+ * belt-and-braces pass we also scan `ps` output for processes whose
5
+ * argv looks like one of ours.
6
+ *
7
+ * This is best-effort: failures to kill an orphan don't stop startup.
8
+ */
9
+ import { PersistedTunnel } from './state';
10
+ export interface CleanupResult {
11
+ reapedFromState: number[];
12
+ reapedFromScan: number[];
13
+ }
14
+ export declare function cleanupZombies(cliEntryAbs: string): CleanupResult;
15
+ declare function scanForHooklinkChildren(cliEntryAbs: string): number[];
16
+ export declare const _internal: {
17
+ scanForHooklinkChildren: typeof scanForHooklinkChildren;
18
+ };
19
+ export type { PersistedTunnel };
20
+ //# sourceMappingURL=zombie-cleanup.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"zombie-cleanup.d.ts","sourceRoot":"","sources":["../../src/agent/zombie-cleanup.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAIH,OAAO,EAAyB,eAAe,EAAE,MAAM,SAAS,CAAC;AAEjE,MAAM,WAAW,aAAa;IAC5B,eAAe,EAAE,MAAM,EAAE,CAAC;IAC1B,cAAc,EAAE,MAAM,EAAE,CAAC;CAC1B;AAED,wBAAgB,cAAc,CAAC,WAAW,EAAE,MAAM,GAAG,aAAa,CAMjE;AAqBD,iBAAS,uBAAuB,CAAC,WAAW,EAAE,MAAM,GAAG,MAAM,EAAE,CAK9D;AAgED,eAAO,MAAM,SAAS;;CAA8B,CAAC;AAGrD,YAAY,EAAE,eAAe,EAAE,CAAC"}