@interactive-inc/claude-funnel 0.33.0 → 0.35.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.d.ts CHANGED
@@ -1516,11 +1516,11 @@ declare class FunnelSettingsStore extends FunnelSettingsReader {
1516
1516
  read(): Settings;
1517
1517
  private looksLikeLegacy;
1518
1518
  /**
1519
- * Non-destructive migration for profiles written before `id` existed. The id
1520
- * is a later addition to an otherwise-compatible schema, so rather than
1521
- * rejecting the file we mint a uuid for each profile that lacks one; the next
1522
- * `write` persists it. Mutates `parsed` in place (it is freshly JSON-parsed
1523
- * and discarded after the schema parse, so no shared state is touched).
1519
+ * Non-destructive migration for profiles written before `id` existed. Mints a
1520
+ * uuid for each profile lacking one and returns whether anything was minted, so
1521
+ * `read` can persist it immediately a profile id must be STABLE across reads,
1522
+ * otherwise `setSessionId` (a second read) sees a different id and can't match
1523
+ * the one the launch used. Mutates `parsed` in place (freshly JSON-parsed).
1524
1524
  */
1525
1525
  private backfillProfileIds;
1526
1526
  write(settings: Settings): void;
package/dist/index.js CHANGED
@@ -148,9 +148,10 @@ var FunnelSettingsStore = class extends FunnelSettingsReader {
148
148
  const parsed = JSON.parse(content);
149
149
  if (this.looksLikeLegacy(parsed)) throw new Error(`legacy settings.json detected at ${this.path}. The schema changed (channel.connectors are now nested objects with ids; profile fields renamed). Migration is intentionally not provided. Back up and remove the old file:\n mv ${this.path} ${this.path}.bak`);
150
150
  if (parsed && typeof parsed === "object" && "version" in parsed && parsed.version !== 1) throw new Error(`unsupported settings.json version (${this.path}): expected 1, got ${String(parsed.version)}`);
151
- this.backfillProfileIds(parsed);
151
+ const minted = this.backfillProfileIds(parsed);
152
152
  const result = settingsSchema.safeParse(parsed);
153
153
  if (!result.success) throw new Error(`invalid settings.json (${this.path}): ${result.error.issues.map((i) => `${i.path.join(".")}: ${i.message}`).join(", ")}`);
154
+ if (minted) this.write(result.data);
154
155
  return result.data;
155
156
  }
156
157
  looksLikeLegacy(parsed) {
@@ -172,21 +173,26 @@ var FunnelSettingsStore = class extends FunnelSettingsReader {
172
173
  return false;
173
174
  }
174
175
  /**
175
- * Non-destructive migration for profiles written before `id` existed. The id
176
- * is a later addition to an otherwise-compatible schema, so rather than
177
- * rejecting the file we mint a uuid for each profile that lacks one; the next
178
- * `write` persists it. Mutates `parsed` in place (it is freshly JSON-parsed
179
- * and discarded after the schema parse, so no shared state is touched).
176
+ * Non-destructive migration for profiles written before `id` existed. Mints a
177
+ * uuid for each profile lacking one and returns whether anything was minted, so
178
+ * `read` can persist it immediately a profile id must be STABLE across reads,
179
+ * otherwise `setSessionId` (a second read) sees a different id and can't match
180
+ * the one the launch used. Mutates `parsed` in place (freshly JSON-parsed).
180
181
  */
181
182
  backfillProfileIds(parsed) {
182
- if (!parsed || typeof parsed !== "object") return;
183
+ if (!parsed || typeof parsed !== "object") return false;
183
184
  const obj = parsed;
184
- if (!Array.isArray(obj.profiles)) return;
185
+ if (!Array.isArray(obj.profiles)) return false;
186
+ let minted = false;
185
187
  for (const profile of obj.profiles) {
186
188
  if (!profile || typeof profile !== "object") continue;
187
189
  const p = profile;
188
- if (typeof p.id !== "string") p.id = this.idGenerator.generate();
190
+ if (typeof p.id !== "string") {
191
+ p.id = this.idGenerator.generate();
192
+ minted = true;
193
+ }
189
194
  }
195
+ return minted;
190
196
  }
191
197
  write(settings) {
192
198
  this.fs.mkdirSync(dirname(this.path), { recursive: true });
@@ -795,6 +801,7 @@ var FunnelClaude = class {
795
801
  for (const [key, value] of Object.entries(recipeEnv)) env[key] = value;
796
802
  for (const [key, value] of Object.entries(globalThis.process.env)) if (typeof value === "string") env[key] = value;
797
803
  env.FUNNEL_CHANNEL_ID = channelId;
804
+ env.FUNNEL_PORT = String(resolveFunnelPort());
798
805
  return env;
799
806
  }
800
807
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@interactive-inc/claude-funnel",
3
- "version": "0.33.0",
3
+ "version": "0.35.0",
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",