@interactive-inc/claude-funnel 0.58.1 → 0.59.1

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.
@@ -1,3 +1,3 @@
1
- import { C as profileSpecSchema, S as localConfigSchema, _ as LOCAL_CONFIG_FILENAME, b as channelSpecSchema, g as ConnectorSpec, h as ChannelSpec, i as FunnelTokenPrompter, m as FunnelLocalConfig, n as FunnelLocalConfigSync, r as LocalConfigSyncResult, t as ConnectorSyncOutcome, v as LocalConfig, x as connectorSpecSchema, y as ProfileSpec } from "./local-config-sync--f739oCJ.js";
2
- import { i as funnelJsonSchema, n as NodeFunnelTokenPrompter, r as FunnelLocalConfigWriter, t as MemoryFunnelTokenPrompter } from "./memory-token-prompter-BlFwK9k7.js";
1
+ import { C as profileSpecSchema, S as localConfigSchema, _ as LOCAL_CONFIG_FILENAME, b as channelSpecSchema, g as ConnectorSpec, h as ChannelSpec, i as FunnelTokenPrompter, m as FunnelLocalConfig, n as FunnelLocalConfigSync, r as LocalConfigSyncResult, t as ConnectorSyncOutcome, v as LocalConfig, x as connectorSpecSchema, y as ProfileSpec } from "./local-config-sync-BGPAS9Be.js";
2
+ import { i as funnelJsonSchema, n as NodeFunnelTokenPrompter, r as FunnelLocalConfigWriter, t as MemoryFunnelTokenPrompter } from "./memory-token-prompter-CcShtF8B.js";
3
3
  export { ChannelSpec, ConnectorSpec, ConnectorSyncOutcome, FunnelLocalConfig, FunnelLocalConfigSync, FunnelLocalConfigWriter, FunnelTokenPrompter, LOCAL_CONFIG_FILENAME, LocalConfig, LocalConfigSyncResult, MemoryFunnelTokenPrompter, NodeFunnelTokenPrompter, ProfileSpec, channelSpecSchema, connectorSpecSchema, funnelJsonSchema, localConfigSchema, profileSpecSchema };
@@ -1,6 +1,6 @@
1
- import { n as NodeFunnelProcessRunner } from "./gh-connector-schema-DUcZgN2Q.js";
1
+ import { n as NodeFunnelProcessRunner } from "./gh-connector-schema-ClPLSYD9.js";
2
2
  import { t as NodeFunnelFileSystem } from "./node-file-system-BcrmWN9I.js";
3
- import { r as FUNNEL_DIR, s as resolveFunnelPort, t as gatewayLoopbackUrl } from "./gateway-base-url-6foMXfFf.js";
3
+ import { r as FUNNEL_DIR, s as resolveFunnelPort, t as gatewayLoopbackUrl } from "./gateway-base-url-DxVjjDoW.js";
4
4
  import { t as ConnectorDiagnosticSqlReader } from "./diagnostic-sql-reader-CzYgZpq2.js";
5
5
  import { t as FunnelLogSqliteSink } from "./funnel-log-sqlite-sink-B_5_4ybn.js";
6
6
  import { dirname, join } from "node:path";
@@ -1,5 +1,5 @@
1
1
  import { n as FunnelFileSystem } from "./file-system-DxpnnUVb.js";
2
- import { i as FunnelTokenPrompter } from "./local-config-sync--f739oCJ.js";
2
+ import { i as FunnelTokenPrompter } from "./local-config-sync-BGPAS9Be.js";
3
3
 
4
4
  //#region lib/services/local-config/local-config-json-schema.d.ts
5
5
  /**
@@ -34,7 +34,9 @@ type ProcessSnapshot = {
34
34
  * callers do not branch on `process.platform`. `isAlive` checks whether a PID
35
35
  * names a live (non-zombie) process; `listProcessesContaining` enumerates
36
36
  * processes whose command line includes `marker`, used for funnel-gateway tag
37
- * matching across daemons that share a home dir.
37
+ * matching across daemons that share a home dir. `getStartTime` returns a
38
+ * stable string identifying when a PID was started, used to detect PID reuse
39
+ * after the original process died abnormally (no exit hook fired).
38
40
  */
39
41
  declare abstract class FunnelProcessRunner {
40
42
  abstract run(command: string[], options?: RunOptions): Promise<RunResult>;
@@ -44,6 +46,7 @@ declare abstract class FunnelProcessRunner {
44
46
  abstract kill(pid: number, signal?: string): void;
45
47
  abstract isAlive(pid: number): boolean;
46
48
  abstract listProcessesContaining(marker: string): ProcessSnapshot[];
49
+ abstract getStartTime(pid: number): string | null;
47
50
  }
48
51
  //#endregion
49
52
  export { RunOptions as a, ProcessSnapshot as i, DetachOptions as n, RunResult as o, FunnelProcessRunner as r, AttachOptions as t };
@@ -1,6 +1,6 @@
1
- import { n as NodeFunnelProcessRunner } from "./gh-connector-schema-DUcZgN2Q.js";
1
+ import { n as NodeFunnelProcessRunner } from "./gh-connector-schema-ClPLSYD9.js";
2
2
  import { t as NodeFunnelFileSystem } from "./node-file-system-BcrmWN9I.js";
3
- import { m as NodeFunnelIdGenerator, r as FUNNEL_DIR, s as resolveFunnelPort } from "./gateway-base-url-6foMXfFf.js";
3
+ import { m as NodeFunnelIdGenerator, r as FUNNEL_DIR, s as resolveFunnelPort } from "./gateway-base-url-DxVjjDoW.js";
4
4
  import { join } from "node:path";
5
5
  import { stringify } from "yaml";
6
6
  //#region lib/engine/claude/claude.ts
@@ -133,13 +133,33 @@ var FileProcessGuard = class {
133
133
  Object.freeze(this);
134
134
  }
135
135
  isRunning(profileId) {
136
- const pid = this.readPid(profileId);
137
- if (!pid) return false;
138
- return this.process.isAlive(pid);
136
+ const record = this.readRecord(profileId);
137
+ if (!record) return false;
138
+ if (!this.process.isAlive(record.pid)) {
139
+ this.release(profileId);
140
+ return false;
141
+ }
142
+ if (record.startTime !== null) {
143
+ const currentStartTime = this.process.getStartTime(record.pid);
144
+ if (currentStartTime === null) {
145
+ this.release(profileId);
146
+ return false;
147
+ }
148
+ if (currentStartTime !== record.startTime) {
149
+ this.release(profileId);
150
+ return false;
151
+ }
152
+ }
153
+ return true;
139
154
  }
140
155
  acquire(profileId) {
141
156
  this.fs.mkdirSync(this.pidDir, { recursive: true });
142
- this.fs.writeFileSync(this.pidPath(profileId), String(globalThis.process.pid));
157
+ const pid = globalThis.process.pid;
158
+ const record = {
159
+ pid,
160
+ startTime: this.process.getStartTime(pid)
161
+ };
162
+ this.fs.writeFileSync(this.pidPath(profileId), JSON.stringify(record));
143
163
  globalThis.process.once("exit", () => this.release(profileId));
144
164
  }
145
165
  release(profileId) {
@@ -149,14 +169,27 @@ var FileProcessGuard = class {
149
169
  pidPath(profileId) {
150
170
  return join(this.pidDir, `${profileId}.pid`);
151
171
  }
152
- readPid(profileId) {
172
+ readRecord(profileId) {
153
173
  const path = this.pidPath(profileId);
154
174
  if (!this.fs.existsSync(path)) return null;
155
175
  try {
156
176
  const content = this.fs.readFileSync(path).trim();
177
+ if (!content) return null;
178
+ if (content.startsWith("{")) {
179
+ const parsed = JSON.parse(content);
180
+ const pid = typeof parsed.pid === "number" ? parsed.pid : Number(parsed.pid);
181
+ if (!Number.isInteger(pid) || pid <= 0) return null;
182
+ return {
183
+ pid,
184
+ startTime: typeof parsed.startTime === "string" ? parsed.startTime : null
185
+ };
186
+ }
157
187
  const pid = Number(content);
158
188
  if (!pid || pid <= 0) return null;
159
- return pid;
189
+ return {
190
+ pid,
191
+ startTime: null
192
+ };
160
193
  } catch {
161
194
  return null;
162
195
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@interactive-inc/claude-funnel",
3
- "version": "0.58.1",
3
+ "version": "0.59.1",
4
4
  "description": "Hub CLI that routes external events (Slack / GitHub / Discord) to Claude Code agents through subscription channels over MCP.",
5
5
  "keywords": [
6
6
  "bun",
@@ -117,7 +117,7 @@
117
117
  "prepack": "make build"
118
118
  },
119
119
  "dependencies": {
120
- "@hono/zod-validator": "^0.8.0",
120
+ "@hono/zod-validator": "0.8.0",
121
121
  "@modelcontextprotocol/sdk": "^1.29.0",
122
122
  "@slack/bolt": "^4.7.2",
123
123
  "@slack/web-api": "^7.16.0",