@honest-pitches/pitch-sdk 0.4.6 → 0.5.2

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.mjs CHANGED
@@ -1,3 +1,5 @@
1
+ import { createRequire as topLevelCreateRequire } from "node:module";
2
+ const require = topLevelCreateRequire(import.meta.url);
1
3
  var __create = Object.create;
2
4
  var __defProp = Object.defineProperty;
3
5
  var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
@@ -30,168 +32,8 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
30
32
  mod
31
33
  ));
32
34
 
33
- // vendor/platform-libs/pitcher-api-key-core/dist/index.cjs
34
- var require_dist = __commonJS({
35
- "vendor/platform-libs/pitcher-api-key-core/dist/index.cjs"(exports, module) {
36
- "use strict";
37
- var __defProp2 = Object.defineProperty;
38
- var __getOwnPropDesc2 = Object.getOwnPropertyDescriptor;
39
- var __getOwnPropNames2 = Object.getOwnPropertyNames;
40
- var __hasOwnProp2 = Object.prototype.hasOwnProperty;
41
- var __export = (target, all) => {
42
- for (var name in all)
43
- __defProp2(target, name, { get: all[name], enumerable: true });
44
- };
45
- var __copyProps2 = (to, from, except, desc) => {
46
- if (from && typeof from === "object" || typeof from === "function") {
47
- for (let key of __getOwnPropNames2(from))
48
- if (!__hasOwnProp2.call(to, key) && key !== except)
49
- __defProp2(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc2(from, key)) || desc.enumerable });
50
- }
51
- return to;
52
- };
53
- var __toCommonJS = (mod) => __copyProps2(__defProp2({}, "__esModule", { value: true }), mod);
54
- var index_exports = {};
55
- __export(index_exports, {
56
- ALL_API_KEY_SCOPES: () => ALL_API_KEY_SCOPES,
57
- API_KEY_PREFIX: () => API_KEY_PREFIX,
58
- API_KEY_PREFIX_LEN: () => API_KEY_PREFIX_LEN,
59
- API_KEY_RANDOM_LEN: () => API_KEY_RANDOM_LEN,
60
- HASH_ALGO: () => HASH_ALGO,
61
- extractKeyPrefix: () => extractKeyPrefix2,
62
- generateApiKeyRandomSegment: () => generateApiKeyRandomSegment,
63
- hashApiKey: () => hashApiKey,
64
- isApiKeyScope: () => isApiKeyScope,
65
- isApiKeyUsable: () => isApiKeyUsable2,
66
- isWellFormedApiKey: () => isWellFormedApiKey2,
67
- mintApiKey: () => mintApiKey,
68
- parseApiKeyScopes: () => parseApiKeyScopes,
69
- parseBearerHeader: () => parseBearerHeader,
70
- verifyApiKey: () => verifyApiKey2
71
- });
72
- module.exports = __toCommonJS(index_exports);
73
- var import_node_crypto = __require("crypto");
74
- var API_KEY_PREFIX = "hp_pk_";
75
- var API_KEY_PREFIX_LEN = 8;
76
- var API_KEY_RANDOM_LEN = 48;
77
- var HASH_ALGO = "sha256";
78
- var BASE62 = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
79
- function generateApiKeyRandomSegment(len) {
80
- if (!Number.isInteger(len) || len <= 0) {
81
- throw new Error("len must be a positive integer");
82
- }
83
- const out = [];
84
- while (out.length < len) {
85
- const buf = (0, import_node_crypto.randomBytes)(len + 4);
86
- for (let i = 0; i < buf.length && out.length < len; i++) {
87
- const byte = buf[i];
88
- if (byte < 248) {
89
- out.push(BASE62[byte % 62]);
90
- }
91
- }
92
- }
93
- return out.join("");
94
- }
95
- function hashApiKey(rawToken) {
96
- if (typeof rawToken !== "string" || rawToken.length === 0) {
97
- throw new Error("hashApiKey: rawToken must be a non-empty string");
98
- }
99
- return (0, import_node_crypto.createHash)(HASH_ALGO).update(rawToken, "utf8").digest("hex");
100
- }
101
- function isWellFormedApiKey2(rawToken) {
102
- if (typeof rawToken !== "string") return false;
103
- if (!rawToken.startsWith(API_KEY_PREFIX)) return false;
104
- const rest = rawToken.slice(API_KEY_PREFIX.length);
105
- if (rest.length !== API_KEY_RANDOM_LEN) return false;
106
- for (let i = 0; i < rest.length; i++) {
107
- const code = rest.charCodeAt(i);
108
- const isDigit = code >= 48 && code <= 57;
109
- const isUpper = code >= 65 && code <= 90;
110
- const isLower = code >= 97 && code <= 122;
111
- if (!isDigit && !isUpper && !isLower) return false;
112
- }
113
- return true;
114
- }
115
- function extractKeyPrefix2(rawToken) {
116
- if (!isWellFormedApiKey2(rawToken)) return null;
117
- return rawToken.slice(API_KEY_PREFIX.length, API_KEY_PREFIX.length + API_KEY_PREFIX_LEN);
118
- }
119
- var ALL_API_KEY_SCOPES = ["mcp", "sdk"];
120
- function isApiKeyScope(value) {
121
- return value === "mcp" || value === "sdk";
122
- }
123
- function parseApiKeyScopes(input) {
124
- if (!Array.isArray(input)) {
125
- throw new Error("scopes must be an array of strings");
126
- }
127
- const seen = /* @__PURE__ */ new Set();
128
- for (const raw of input) {
129
- if (!isApiKeyScope(raw)) {
130
- throw new Error(
131
- `unknown scope: ${JSON.stringify(raw)}. Valid scopes: ${ALL_API_KEY_SCOPES.join(", ")}`
132
- );
133
- }
134
- seen.add(raw);
135
- }
136
- return Array.from(seen);
137
- }
138
- function mintApiKey(input) {
139
- if (typeof input.label !== "string" || input.label.length === 0) {
140
- throw new Error("mintApiKey: label must be a non-empty string");
141
- }
142
- if (input.label.length > 128) {
143
- throw new Error("mintApiKey: label must be <= 128 chars");
144
- }
145
- if (!Array.isArray(input.scopes) || input.scopes.length === 0) {
146
- throw new Error("mintApiKey: scopes must be a non-empty array");
147
- }
148
- if (input.expiresAt != null && typeof input.expiresAt !== "number") {
149
- throw new Error("mintApiKey: expiresAt must be a number (epoch ms) or null");
150
- }
151
- const randomSegment = generateApiKeyRandomSegment(API_KEY_RANDOM_LEN);
152
- const rawToken = `${API_KEY_PREFIX}${randomSegment}`;
153
- const keyHash = hashApiKey(rawToken);
154
- const keyPrefix = extractKeyPrefix2(rawToken);
155
- if (keyPrefix === null) {
156
- throw new Error("mintApiKey: generated key is not well-formed");
157
- }
158
- return { rawToken, keyHash, keyPrefix };
159
- }
160
- function constantTimeEqualHex(a, b) {
161
- if (a.length !== b.length) return false;
162
- let diff = 0;
163
- for (let i = 0; i < a.length; i++) {
164
- diff |= a.charCodeAt(i) ^ b.charCodeAt(i);
165
- }
166
- return diff === 0;
167
- }
168
- function verifyApiKey2(presented, storedHash) {
169
- if (typeof presented !== "string" || typeof storedHash !== "string") return false;
170
- if (presented.length === 0 || storedHash.length === 0) return false;
171
- let presentedHash;
172
- try {
173
- presentedHash = hashApiKey(presented);
174
- } catch {
175
- return false;
176
- }
177
- return constantTimeEqualHex(presentedHash, storedHash);
178
- }
179
- function isApiKeyUsable2(row, now = Date.now()) {
180
- if (row.revokedAt != null) return false;
181
- if (row.expiresAt != null && row.expiresAt <= now) return false;
182
- return true;
183
- }
184
- function parseBearerHeader(header) {
185
- if (!header || typeof header !== "string") return null;
186
- const match = /^Bearer\s+(\S+)\s*$/i.exec(header.trim());
187
- if (!match) return null;
188
- return { token: match[1] };
189
- }
190
- }
191
- });
192
-
193
35
  // vendor/platform-libs/hp-codes/dist/index.cjs
194
- var require_dist2 = __commonJS({
36
+ var require_dist = __commonJS({
195
37
  "vendor/platform-libs/hp-codes/dist/index.cjs"(exports, module) {
196
38
  "use strict";
197
39
  var __defProp2 = Object.defineProperty;
@@ -332,7 +174,7 @@ var require_dist2 = __commonJS({
332
174
  });
333
175
 
334
176
  // vendor/platform-libs/pitch-core/dist/index.cjs
335
- var require_dist3 = __commonJS({
177
+ var require_dist2 = __commonJS({
336
178
  "vendor/platform-libs/pitch-core/dist/index.cjs"(exports, module) {
337
179
  "use strict";
338
180
  var __create2 = Object.create;
@@ -12341,7 +12183,7 @@ and ensure you are accounting for this risk.
12341
12183
  TEST_CONCLUSIONS: () => TEST_CONCLUSIONS,
12342
12184
  TEST_PRIMARY_METRICS: () => TEST_PRIMARY_METRICS,
12343
12185
  TEST_STATUSES: () => TEST_STATUSES,
12344
- assertFrameworkSwitchable: () => assertFrameworkSwitchable2,
12186
+ assertFrameworkSwitchable: () => assertFrameworkSwitchable,
12345
12187
  assertSameTestBase: () => assertSameTestBase,
12346
12188
  baseKey: () => baseKey,
12347
12189
  buildPitchVariantIndexes: () => buildPitchVariantIndexes,
@@ -12373,13 +12215,13 @@ and ensure you are accounting for this risk.
12373
12215
  lifecycleFromRow: () => lifecycleFromRow2,
12374
12216
  loadFrameworkGuidance: () => loadFrameworkGuidance,
12375
12217
  loadFrameworks: () => loadFrameworks3,
12376
- loadPitchSections: () => loadPitchSections2,
12218
+ loadPitchSections: () => loadPitchSections,
12377
12219
  loadSegments: () => loadSegments2,
12378
12220
  mergeSectionsForFramework: () => mergeSectionsForFramework,
12379
12221
  mismatchedSectionKinds: () => mismatchedSectionKinds,
12380
12222
  normalizeClips: () => normalizeClips,
12381
12223
  normalizeCreatePitchInput: () => normalizeCreatePitchInput,
12382
- normalizePitchContent: () => normalizePitchContent2,
12224
+ normalizePitchContent: () => normalizePitchContent,
12383
12225
  normalizePitchLayout: () => normalizePitchLayout,
12384
12226
  parseClipsJson: () => parseClipsJson,
12385
12227
  parsePitchSectionsColumn: () => parsePitchSectionsColumn,
@@ -14247,7 +14089,7 @@ Please report this to https://github.com/markedjs/marked.`, e) {
14247
14089
  function sectionsFromDescription(description) {
14248
14090
  return [{ kind: "problem", body: description, mediaIds: [] }];
14249
14091
  }
14250
- function normalizePitchContent2(row) {
14092
+ function normalizePitchContent(row) {
14251
14093
  const frameworkRaw = String(row.framework ?? "pas");
14252
14094
  const framework = isFrameworkId3(frameworkRaw) ? frameworkRaw : "pas";
14253
14095
  const parsed = parseSectionsJson2(
@@ -15597,7 +15439,7 @@ ${raw.slice(0, 200)}`
15597
15439
  }
15598
15440
  return { ok: true };
15599
15441
  }
15600
- var import_hp_codes = require_dist2();
15442
+ var import_hp_codes = require_dist();
15601
15443
  function baseKey(pitch) {
15602
15444
  const result = (0, import_hp_codes.validateCode)(pitch.hpCode);
15603
15445
  if (!result.valid || !result.payload || !result.checkDigit) {
@@ -15797,7 +15639,7 @@ ${raw.slice(0, 200)}`
15797
15639
  "expired",
15798
15640
  "archived"
15799
15641
  ];
15800
- function assertFrameworkSwitchable2(input) {
15642
+ function assertFrameworkSwitchable(input) {
15801
15643
  if (input.status === "draft") return { ok: true };
15802
15644
  if (input.status === "live" || input.status === "expired" || input.status === "archived") {
15803
15645
  return { ok: false, reason: "immutable" };
@@ -15809,7 +15651,7 @@ ${raw.slice(0, 200)}`
15809
15651
  if (from === "draft" && to !== "draft") {
15810
15652
  return { ok: false, reason: "locked" };
15811
15653
  }
15812
- return assertFrameworkSwitchable2({ status: from });
15654
+ return assertFrameworkSwitchable({ status: from });
15813
15655
  }
15814
15656
  function isPitchLifecycleState(value) {
15815
15657
  return PITCH_LIFECYCLE_STATES.includes(value);
@@ -16644,7 +16486,7 @@ ${raw.slice(0, 200)}`
16644
16486
  };
16645
16487
  });
16646
16488
  }
16647
- function loadPitchSections2({
16489
+ function loadPitchSections({
16648
16490
  pitchId,
16649
16491
  doc,
16650
16492
  rows,
@@ -16731,9 +16573,6 @@ ${raw.slice(0, 200)}`
16731
16573
  }
16732
16574
  });
16733
16575
 
16734
- // src/config.ts
16735
- import { Client } from "node-appwrite";
16736
-
16737
16576
  // src/errors.ts
16738
16577
  var PitcherSdkError = class extends Error {
16739
16578
  code;
@@ -16773,71 +16612,50 @@ var HpCodeImmutableError = class extends PitcherSdkError {
16773
16612
  this.name = "HpCodeImmutableError";
16774
16613
  }
16775
16614
  };
16776
- var AppwriteRequestError = class extends PitcherSdkError {
16615
+ var PitcherRequestError = class extends PitcherSdkError {
16777
16616
  status;
16778
- constructor(status, message) {
16779
- super("APPWRITE_REQUEST", message);
16780
- this.name = "AppwriteRequestError";
16617
+ constructor(status, code, message) {
16618
+ super(code || "PITCHER_REQUEST", message);
16619
+ this.name = "PitcherRequestError";
16781
16620
  this.status = status;
16782
16621
  }
16783
16622
  };
16784
16623
 
16785
16624
  // src/config.ts
16625
+ var DEFAULT_PITCHER_BASE_URL = "https://mcp.honestpitches.com";
16786
16626
  function resolvePitcherConfig(input) {
16787
- if (input.initialJwt && input.apiKey) {
16627
+ if (input.jwt && input.apiKey) {
16788
16628
  throw new PitcherSdkError(
16789
16629
  "PITCHER_AUTH_AMBIGUOUS",
16790
- "Set either initialJwt or apiKey, not both \u2014 the SDK can only act as one principal per client"
16630
+ "Set either jwt or apiKey, not both \u2014 the SDK can only act as one principal per client"
16791
16631
  );
16792
16632
  }
16793
- const endpoint = input.endpoint?.replace(/\/$/, "");
16794
- if (!endpoint || !input.projectId) {
16795
- throw new Error("PitcherClientConfig requires endpoint and projectId");
16796
- }
16797
- const client = input.client ?? (() => {
16798
- const c = new Client().setEndpoint(endpoint).setProject(input.projectId);
16799
- return c;
16800
- })();
16801
- if (input.initialJwt) {
16802
- try {
16803
- client.setJWT(input.initialJwt);
16804
- } catch {
16805
- }
16806
- }
16807
- let jwt = input.initialJwt ?? null;
16808
- let jwtExpiresAt = input.initialJwtExpiresAt ?? null;
16633
+ const baseUrl = (input.baseUrl ?? DEFAULT_PITCHER_BASE_URL).replace(/\/$/, "");
16634
+ const state = {
16635
+ jwt: input.jwt ?? null,
16636
+ jwtExpiresAt: input.jwtExpiresAt ?? null
16637
+ };
16809
16638
  const apiKey = input.apiKey ?? null;
16639
+ const fetchImpl = input.fetchImpl ?? globalThis.fetch;
16640
+ if (!fetchImpl) {
16641
+ throw new PitcherSdkError(
16642
+ "PITCHER_NO_FETCH",
16643
+ "PitcherClient requires a fetch implementation \u2014 Node 20+ provides globalThis.fetch; older runtimes must pass { fetchImpl }"
16644
+ );
16645
+ }
16810
16646
  return {
16811
- endpoint,
16812
- projectId: input.projectId,
16813
- databaseId: input.databaseId ?? "honest-pitches",
16814
- pitchesTableId: input.pitchesTableId ?? "pitches",
16815
- creatorsTableId: input.creatorsTableId ?? "creators",
16816
- pitchSectionsTableId: input.pitchSectionsTableId ?? "pitch_sections",
16817
- pitchCreateFunctionId: input.pitchCreateFunctionId ?? "pitch-write",
16818
- pitchAssetUploadFunctionId: input.pitchAssetUploadFunctionId ?? "pitch-asset-upload",
16819
- pitcherApiKeysTableId: input.pitcherApiKeysTableId ?? "pitcher_api_keys",
16820
- client,
16821
- getJwt: () => jwt,
16822
- getJwtExpiresAt: () => jwtExpiresAt,
16647
+ baseUrl,
16648
+ getJwt: () => state.jwt,
16649
+ getJwtExpiresAt: () => state.jwtExpiresAt,
16823
16650
  setJwt: (next, expiresAt) => {
16824
- jwt = next;
16825
- jwtExpiresAt = expiresAt ?? null;
16826
- if (!apiKey) {
16827
- try {
16828
- client.setJWT(next);
16829
- } catch {
16830
- }
16831
- }
16651
+ state.jwt = next;
16652
+ state.jwtExpiresAt = expiresAt ?? null;
16832
16653
  },
16833
- getApiKey: () => apiKey
16654
+ getApiKey: () => apiKey,
16655
+ fetchImpl
16834
16656
  };
16835
16657
  }
16836
16658
 
16837
- // src/creators.ts
16838
- var import_pitcher_api_key_core = __toESM(require_dist(), 1);
16839
- import { Query } from "node-appwrite";
16840
-
16841
16659
  // src/jwt.ts
16842
16660
  function decodeJwtSub(jwt) {
16843
16661
  const parts = jwt.split(".");
@@ -16860,214 +16678,92 @@ function isJwtExpired(expiresAt) {
16860
16678
  return Date.now() >= expiresAt;
16861
16679
  }
16862
16680
 
16863
- // src/http.ts
16864
- import { Functions, TablesDB, AppwriteException } from "node-appwrite";
16865
-
16866
- // src/resolve-consolidated-route.ts
16867
- var PUBLIC_API_FUNCTION_ID = "honest-pitches-public-api";
16868
- var STRIPE_API_FUNCTION_ID = "stripe-api";
16869
- var STUDIO_API_FUNCTION_ID = "honest-pitches-studio-api";
16870
- var MEDIA_API_FUNCTION_ID = "honest-pitches-media";
16871
- var FUNCTION_BUNDLE_MAP = {
16872
- "pitch-write": STUDIO_API_FUNCTION_ID,
16873
- "pitch-transition": STUDIO_API_FUNCTION_ID,
16874
- "pitch-asset-upload": MEDIA_API_FUNCTION_ID,
16875
- "agent-magic-link-issue": STUDIO_API_FUNCTION_ID
16876
- };
16877
- function resolveConsolidatedRoute(functionId, path) {
16878
- const bundle = FUNCTION_BUNDLE_MAP[functionId];
16879
- if (!bundle) return { functionId, path };
16880
- const sub = path.startsWith("/") ? path : `/${path}`;
16881
- const prefixed = sub === "/" ? `/${functionId}` : `/${functionId}${sub}`;
16882
- return { functionId: bundle, path: prefixed };
16883
- }
16884
-
16885
- // src/http.ts
16886
- function requireJwt(config) {
16681
+ // src/fetch-pitcher.ts
16682
+ function requireAuthHeader(config) {
16683
+ const apiKey = config.getApiKey();
16684
+ if (apiKey) return `Bearer ${apiKey}`;
16887
16685
  const jwt = config.getJwt();
16888
16686
  if (!jwt) {
16889
- throw new PitcherNotAuthenticatedError();
16687
+ throw new PitcherNotAuthenticatedError(
16688
+ "PitcherClient requires { apiKey: 'hp_pk_\u2026' } or { jwt: '<jwt>' }"
16689
+ );
16890
16690
  }
16891
16691
  if (isJwtExpired(config.getJwtExpiresAt())) {
16892
- throw new PitcherNotAuthenticatedError("JWT expired \u2014 refresh via pitcher-mcp OAuth");
16692
+ throw new PitcherNotAuthenticatedError(
16693
+ "JWT expired \u2014 refresh via pitcher-mcp OAuth"
16694
+ );
16893
16695
  }
16894
- return jwt;
16696
+ return `Bearer ${jwt}`;
16895
16697
  }
16896
- function mapAppwriteException(err, fallbackMessage) {
16897
- if (err instanceof AppwriteRequestError) {
16898
- throw err;
16899
- }
16900
- if (err instanceof AppwriteException) {
16901
- const status = typeof err.code === "number" && err.code > 0 ? err.code : 500;
16902
- if (status === 401) {
16903
- throw new PitcherNotAuthenticatedError(err.message || "Unauthorized");
16904
- }
16905
- throw new AppwriteRequestError(status, err.message || fallbackMessage);
16906
- }
16907
- if (typeof err === "object" && err !== null && err.name === "AppwriteException") {
16908
- const code = err.code;
16909
- const message = err.message;
16910
- const status = typeof code === "number" && code > 0 ? code : 500;
16911
- if (status === 401) {
16912
- throw new PitcherNotAuthenticatedError(message || "Unauthorized");
16913
- }
16914
- throw new AppwriteRequestError(status, message || fallbackMessage);
16698
+ async function fetchPitches(config, opts) {
16699
+ const auth = requireAuthHeader(config);
16700
+ const url = `${config.baseUrl}/pitcher${opts.path.startsWith("/") ? opts.path : `/${opts.path}`}`;
16701
+ const headers = {
16702
+ Authorization: auth,
16703
+ Accept: "application/json",
16704
+ "User-Agent": "@honest-pitches/pitch-sdk/0.5.0"
16705
+ };
16706
+ let body;
16707
+ if (opts.body !== void 0 && (opts.method === "POST" || opts.method === "PATCH")) {
16708
+ headers["Content-Type"] = "application/json";
16709
+ body = JSON.stringify(opts.body);
16915
16710
  }
16916
- throw new AppwriteRequestError(
16917
- 500,
16918
- err instanceof Error ? err.message : fallbackMessage
16919
- );
16920
- }
16921
- function tablesDB(config) {
16922
- return new TablesDB(config.client);
16923
- }
16924
- function functions(config) {
16925
- return new Functions(config.client);
16926
- }
16927
- function applyApiKeyHeader(config) {
16928
- const apiKey = config.getApiKey();
16929
- if (!apiKey) return;
16930
- config.client.addHeader("Authorization", `Bearer ${apiKey}`);
16931
- }
16932
- async function executePitchFunction(config, opts) {
16933
- if (!config.getApiKey()) requireJwt(config);
16934
- applyApiKeyHeader(config);
16935
- const fn = functions(config);
16936
- const legacyFn = opts.functionId ?? config.pitchCreateFunctionId;
16937
- const route = resolveConsolidatedRoute(legacyFn, opts.path ?? "/");
16711
+ let res;
16938
16712
  try {
16939
- const execution = await fn.createExecution({
16940
- functionId: route.functionId,
16941
- body: JSON.stringify(opts.body),
16942
- async: false,
16713
+ res = await config.fetchImpl(url, {
16943
16714
  method: opts.method,
16944
- xpath: route.path
16945
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
16715
+ headers,
16716
+ ...body !== void 0 ? { body } : {},
16717
+ ...opts.signal ? { signal: opts.signal } : {}
16946
16718
  });
16947
- const status = execution.responseStatusCode ?? 201;
16948
- const responseBody = execution.responseBody ?? "";
16949
- if (status >= 400) {
16950
- let message = responseBody;
16951
- try {
16952
- const parsed = JSON.parse(responseBody);
16953
- message = parsed.error ?? responseBody;
16954
- } catch {
16955
- }
16956
- throw new AppwriteRequestError(status, message);
16957
- }
16958
- if (!responseBody) {
16959
- throw new AppwriteRequestError(status, "Empty response from pitch-write");
16960
- }
16961
- return JSON.parse(responseBody);
16962
- } catch (err) {
16963
- if (err instanceof AppwriteRequestError) throw err;
16964
- if (err instanceof PitcherNotAuthenticatedError) throw err;
16965
- mapAppwriteException(err, "pitch-write execution failed");
16966
- }
16967
- }
16968
- async function tablesListRows(config, tableId, queries) {
16969
- if (!config.getApiKey()) requireJwt(config);
16970
- applyApiKeyHeader(config);
16971
- try {
16972
- const result = await tablesDB(config).listRows({
16973
- databaseId: config.databaseId,
16974
- tableId,
16975
- queries
16976
- });
16977
- const total = result.total ?? 0;
16978
- const rows = result.rows ?? [];
16979
- return { total, documents: rows };
16980
16719
  } catch (err) {
16981
- mapAppwriteException(err, `listRows(${tableId}) failed`);
16720
+ const message = err instanceof Error ? err.message : String(err);
16721
+ throw new PitcherRequestError(0, "PITCHER_NETWORK", `fetchPitches failed: ${message}`);
16982
16722
  }
16983
- }
16984
- async function tablesGetRow(config, tableId, rowId) {
16985
- if (!config.getApiKey()) requireJwt(config);
16986
- applyApiKeyHeader(config);
16723
+ const text = await res.text();
16724
+ let envelope = null;
16987
16725
  try {
16988
- const row = await tablesDB(config).getRow({
16989
- databaseId: config.databaseId,
16990
- tableId,
16991
- rowId
16992
- });
16993
- return row;
16994
- } catch (err) {
16995
- mapAppwriteException(err, `getRow(${tableId}/${rowId}) failed`);
16996
- }
16997
- }
16998
-
16999
- // src/creators.ts
17000
- async function findCreatorIdForJwtUser(config) {
17001
- const jwt = config.getJwt();
17002
- if (!jwt) throw new PitcherNotAuthenticatedError();
17003
- const userId = decodeJwtSub(jwt);
17004
- if (!userId) {
17005
- throw new PitcherNotAuthenticatedError(
17006
- "JWT missing userId/sub \u2014 use account.createJWT().jwt from studio"
16726
+ envelope = JSON.parse(text);
16727
+ } catch {
16728
+ throw new PitcherRequestError(
16729
+ res.status,
16730
+ "PITCHER_NON_JSON",
16731
+ `MCP server returned non-JSON body (status ${res.status}): ${text.slice(0, 256)}`
17007
16732
  );
17008
16733
  }
17009
- const json = await tablesListRows(
17010
- config,
17011
- config.creatorsTableId,
17012
- [Query.equal("userId", userId), Query.limit(1)]
17013
- );
17014
- const doc = json.documents?.[0];
17015
- return doc?.$id ? String(doc.$id) : null;
17016
- }
17017
- async function findCreatorIdForApiKey(config) {
17018
- const apiKey = config.getApiKey();
17019
- if (!apiKey) {
17020
- throw new PitcherNotAuthenticatedError(
17021
- "findCreatorIdForApiKey requires the SDK to be configured with { apiKey: 'hp_pk_\u2026' }; for JWT auth use findCreatorIdForJwtUser instead"
17022
- );
16734
+ if (envelope && envelope.ok === true) {
16735
+ return envelope.data;
17023
16736
  }
17024
- if (!(0, import_pitcher_api_key_core.isWellFormedApiKey)(apiKey)) {
17025
- return null;
17026
- }
17027
- const keyPrefix = (0, import_pitcher_api_key_core.extractKeyPrefix)(apiKey);
17028
- if (!keyPrefix) {
17029
- return null;
16737
+ if (envelope && envelope.ok === false) {
16738
+ throw new PitcherRequestError(
16739
+ res.status,
16740
+ envelope.error.code,
16741
+ envelope.error.message
16742
+ );
17030
16743
  }
17031
- const apiKeyRows = await tablesListRows(
17032
- config,
17033
- config.pitcherApiKeysTableId,
17034
- [Query.equal("keyPrefix", keyPrefix), Query.limit(1)]
16744
+ throw new PitcherRequestError(
16745
+ res.status,
16746
+ "PITCHER_MALFORMED",
16747
+ `MCP server returned malformed envelope: ${text.slice(0, 256)}`
17035
16748
  );
17036
- const keyDoc = apiKeyRows.documents?.[0];
17037
- if (!keyDoc) return null;
17038
- const keyHash = String(keyDoc["keyHash"] ?? "");
17039
- if (!(0, import_pitcher_api_key_core.verifyApiKey)(apiKey, keyHash)) return null;
17040
- const rowShape = {
17041
- revokedAt: typeof keyDoc["revokedAt"] === "string" ? Date.parse(String(keyDoc["revokedAt"])) : null,
17042
- expiresAt: typeof keyDoc["expiresAt"] === "string" ? Date.parse(String(keyDoc["expiresAt"])) : null
17043
- };
17044
- if (!(0, import_pitcher_api_key_core.isApiKeyUsable)(rowShape)) return null;
17045
- const userId = String(keyDoc["userId"] ?? "");
17046
- if (!userId) return null;
17047
- const creatorRows = await tablesListRows(
17048
- config,
17049
- config.creatorsTableId,
17050
- [Query.equal("userId", userId), Query.limit(1)]
17051
- );
17052
- const creatorDoc = creatorRows.documents?.[0];
17053
- return creatorDoc?.$id ? String(creatorDoc.$id) : null;
17054
16749
  }
17055
16750
 
17056
- // src/pitches.ts
17057
- var import_pitch_core = __toESM(require_dist3(), 1);
17058
-
17059
- // src/query.ts
17060
- import { Query as Query2 } from "node-appwrite";
17061
- function appwriteQueryEqual(attribute, value) {
17062
- return Query2.equal(attribute, value);
16751
+ // src/creators.ts
16752
+ async function findCreatorIdForJwtUser(config) {
16753
+ const result = await fetchPitches(config, {
16754
+ method: "GET",
16755
+ path: "/creators/me"
16756
+ });
16757
+ return result.creatorId;
17063
16758
  }
17064
- function appwriteQueryLimit(limit) {
17065
- return Query2.limit(limit);
16759
+ async function findCreatorIdForApiKey(config) {
16760
+ return findCreatorIdForJwtUser(config);
17066
16761
  }
17067
16762
 
17068
16763
  // src/pitches.ts
17069
- function rowToPitch(doc, precomputedSections) {
17070
- const sections = precomputedSections ?? (0, import_pitch_core.normalizePitchContent)(doc).sections;
16764
+ var import_pitch_core = __toESM(require_dist2(), 1);
16765
+ function rowToPitch(doc) {
16766
+ const sections = Array.isArray(doc.sections) ? doc.sections : [];
17071
16767
  const lifecycle = (0, import_pitch_core.lifecycleFromRow)({
17072
16768
  status: String(doc.status ?? "draft"),
17073
16769
  previewToken: doc.previewToken
@@ -17079,14 +16775,6 @@ function rowToPitch(doc, precomputedSections) {
17079
16775
  status: String(doc.status ?? "draft")
17080
16776
  };
17081
16777
  }
17082
- async function loadPitchSectionsFor(config, pitchId) {
17083
- const json = await tablesListRows(
17084
- config,
17085
- config.pitchSectionsTableId,
17086
- [appwriteQueryEqual("pitchId", pitchId)]
17087
- );
17088
- return json.documents ?? [];
17089
- }
17090
16778
  function validatePitchContent(framework, sections) {
17091
16779
  if (!(0, import_pitch_core.isFrameworkId)(framework)) {
17092
16780
  throw new InvalidSectionKindError(`Unknown framework: ${framework}`);
@@ -17099,7 +16787,9 @@ function validatePitchContent(framework, sections) {
17099
16787
  }
17100
16788
  }
17101
16789
  async function assertCreatorOwnedByAuthContext(config, creatorId) {
17102
- if (!config.getJwt()) throw new PitcherNotAuthenticatedError();
16790
+ if (!config.getJwt() && !config.getApiKey()) {
16791
+ throw new PitcherNotAuthenticatedError();
16792
+ }
17103
16793
  const owningCreatorId = await findCreatorIdForJwtUser(config);
17104
16794
  if (!owningCreatorId) {
17105
16795
  throw new PitcherNotAuthenticatedError(
@@ -17116,10 +16806,8 @@ var PitchesApi = class {
17116
16806
  }
17117
16807
  config;
17118
16808
  async create(input) {
17119
- await assertCreatorOwnedByAuthContext(this.config, input.creatorId);
17120
16809
  validatePitchContent(input.framework, input.sections);
17121
- const switchCheck = (0, import_pitch_core.assertFrameworkSwitchable)({ status: "draft" });
17122
- if (!switchCheck.ok) throw new FrameworkLockedError();
16810
+ await assertCreatorOwnedByAuthContext(this.config, input.creatorId);
17123
16811
  const payload = {
17124
16812
  creatorId: input.creatorId,
17125
16813
  slug: input.slug,
@@ -17134,151 +16822,198 @@ var PitchesApi = class {
17134
16822
  ctaLabel: input.ctaLabel ?? "Get it now",
17135
16823
  intendedAudience: input.intendedAudience ?? "",
17136
16824
  visibility: input.visibility ?? "private"
17137
- // 2026-06-21: `listingMode` removed (clean-break).
17138
16825
  };
16826
+ if (input.honestyDisclosure != null && input.honestyDisclosure !== "") {
16827
+ payload.honestyDisclosure = String(input.honestyDisclosure).slice(0, 2e3);
16828
+ }
17139
16829
  if (input.categorySlug != null) payload.categorySlug = input.categorySlug;
17140
16830
  if (input.priceNote != null && input.priceNote !== "") {
17141
16831
  payload.priceNote = String(input.priceNote).slice(0, 64);
17142
16832
  }
17143
- const doc = await executePitchFunction(this.config, {
16833
+ if (input.videoSource != null && input.videoSource !== "") {
16834
+ payload.videoSource = input.videoSource;
16835
+ }
16836
+ if (input.videoExternalId != null && input.videoExternalId !== "") {
16837
+ payload.videoExternalId = String(input.videoExternalId);
16838
+ }
16839
+ if (input.videoExternalUrl != null && input.videoExternalUrl !== "") {
16840
+ payload.videoExternalUrl = String(input.videoExternalUrl);
16841
+ }
16842
+ if (input.bunnyVideoId != null && input.bunnyVideoId !== "") {
16843
+ payload.bunnyVideoId = String(input.bunnyVideoId);
16844
+ }
16845
+ if (input.primaryVideo != null && input.primaryVideo !== "") {
16846
+ payload.primaryVideo = String(input.primaryVideo);
16847
+ }
16848
+ if (input.primaryVideoId != null && input.primaryVideoId !== "") {
16849
+ payload.primaryVideoId = String(input.primaryVideoId);
16850
+ }
16851
+ if (input.honestyDisclosure != null && input.honestyDisclosure !== "") {
16852
+ payload.honestyDisclosure = String(input.honestyDisclosure).slice(0, 2e3);
16853
+ }
16854
+ const doc = await fetchPitches(this.config, {
17144
16855
  method: "POST",
17145
- path: "/",
16856
+ path: "/pitches",
17146
16857
  body: payload
17147
16858
  });
17148
16859
  return rowToPitch(doc);
17149
16860
  }
17150
16861
  async get(id) {
17151
- const doc = await tablesGetRow(
17152
- this.config,
17153
- this.config.pitchesTableId,
17154
- id
17155
- );
17156
- const rows = await loadPitchSectionsFor(this.config, id);
17157
- const resolved = (0, import_pitch_core.loadPitchSections)({
17158
- pitchId: id,
17159
- doc,
17160
- rows,
17161
- // The SDK is silent on the self-heal path; the build log
17162
- // catches disagreement via its own `loadPitchSections`
17163
- // invocation. Keeping the SDK quiet preserves the
17164
- // "read-only" contract of `pitches.get`.
17165
- log: () => {
17166
- }
16862
+ const doc = await fetchPitches(this.config, {
16863
+ method: "GET",
16864
+ path: `/pitches/${encodeURIComponent(id)}`
17167
16865
  });
17168
- return rowToPitch(doc, resolved.sections);
16866
+ return rowToPitch(doc);
17169
16867
  }
17170
16868
  async find(opts = {}) {
17171
- const queries = [appwriteQueryLimit(opts.limit ?? 25)];
17172
- if (opts.status) {
17173
- queries.push(appwriteQueryEqual("status", opts.status));
17174
- }
17175
- const json = await tablesListRows(
17176
- this.config,
17177
- this.config.pitchesTableId,
17178
- queries
17179
- );
17180
- const docs = json.documents ?? [];
17181
- if (docs.length === 0) return [];
17182
- const pitchIds = docs.map((d) => String(d["$id"] ?? ""));
17183
- const sectionRowsByPitch = /* @__PURE__ */ new Map();
17184
- for (const pitchId of pitchIds) {
17185
- if (!pitchId) continue;
17186
- if (sectionRowsByPitch.has(pitchId)) continue;
17187
- const rows = await loadPitchSectionsFor(this.config, pitchId);
17188
- sectionRowsByPitch.set(pitchId, rows);
17189
- }
17190
- return docs.map((doc) => {
17191
- const pitchId = String(doc["$id"] ?? "");
17192
- const rows = sectionRowsByPitch.get(pitchId) ?? [];
17193
- const resolved = (0, import_pitch_core.loadPitchSections)({
17194
- pitchId,
17195
- doc,
17196
- rows,
17197
- log: () => {
17198
- }
17199
- });
17200
- return rowToPitch(doc, resolved.sections);
16869
+ const params = new URLSearchParams();
16870
+ if (opts.status) params.set("status", opts.status);
16871
+ if (opts.creatorId) params.set("creatorId", opts.creatorId);
16872
+ if (typeof opts.limit === "number") params.set("limit", String(opts.limit));
16873
+ if (opts.cursor) params.set("cursor", opts.cursor);
16874
+ const qs = params.toString();
16875
+ const docs = await fetchPitches(this.config, {
16876
+ method: "GET",
16877
+ path: `/pitches${qs ? `?${qs}` : ""}`
17201
16878
  });
16879
+ return docs.map((d) => rowToPitch(d));
16880
+ }
16881
+ /**
16882
+ * Public alias for {@link PitchesApi.find}. Stable on the public
16883
+ * npm registry since `@honest-pitches/pitch-sdk@0.5.2` (2026-07-28).
16884
+ * If you see "client.pitches.list is not a function" you are on
16885
+ * an older SDK — bump to `>=0.5.2`. (The 0.5.1 tarball on npmjs
16886
+ * never shipped this alias even though the source landed under
16887
+ * that version number locally; 0.5.2 is the first published cut.)
16888
+ *
16889
+ * (Added 2026-07-27; published 2026-07-28 after the agent bug
16890
+ * report against CLI 0.5.1 / hosted MCP `list_pitches`.)
16891
+ */
16892
+ async list(opts = {}) {
16893
+ return this.find(opts);
17202
16894
  }
17203
16895
  async update(id, input) {
17204
- const existing = await this.get(id);
17205
- await assertCreatorOwnedByAuthContext(this.config, String(existing.creatorId));
17206
- const nextFramework = input.framework ?? existing.framework;
17207
- const nextSections = input.sections ?? (0, import_pitch_core.parseSectionsJson)(existing.sections);
17208
- if (input.framework != null && input.framework !== existing.framework) {
17209
- const from = existing.lifecycle;
17210
- const check = (0, import_pitch_core.canChangeFramework)(from, from);
17211
- if (!check.ok) throw new FrameworkLockedError(check.reason);
17212
- }
17213
- validatePitchContent(nextFramework, nextSections);
17214
- const body = { ...input };
17215
- const doc = await executePitchFunction(this.config, {
16896
+ const doc = await fetchPitches(this.config, {
17216
16897
  method: "PATCH",
17217
- path: `/${id}`,
17218
- body
16898
+ path: `/pitches/${encodeURIComponent(id)}`,
16899
+ body: { ...input }
17219
16900
  });
17220
16901
  return rowToPitch(doc);
17221
16902
  }
16903
+ /**
16904
+ * Move a pitch through its lifecycle. The returned row carries BOTH
16905
+ * `status` (row-level persisted) and `lifecycle` (computed from
16906
+ * `status + previewToken` server-side).
16907
+ *
16908
+ * Status vs lifecycle (2026-07-28 FlexTape retest doc bundle):
16909
+ * `lifecycle` is the canonical, computed view of where the row sits
16910
+ * (`draft | preview | live | expired | archived`). It is derived
16911
+ * from `status + previewToken` server-side in
16912
+ * `honest-pitches-studio-api/legacy/pitch-write/src/handler.ts`.
16913
+ * Branch on `lifecycle` for terminal-state checks.
16914
+ *
16915
+ * The mapping is:
16916
+ * - `lifecycle: "draft"` ⟷ `status: "draft"` (no previewToken)
16917
+ * - `lifecycle: "preview"` ⟷ `status: "draft"` (previewToken set)
16918
+ * - `lifecycle: "live"` ⟷ `status: "live"`
16919
+ * - `lifecycle: "expired"` ⟷ `status: "expired"`
16920
+ * - `lifecycle: "archived"` ⟷ `status: "removed"` (intentional vocab split)
16921
+ *
16922
+ * Token gate (Finding #17, FlexTape retest):
16923
+ * `to: "preview"` requires a `previewToken` that matches the row's
16924
+ * stored token. The server returns
16925
+ * `400 previewToken is required to transition to preview`
16926
+ * when the token is missing or wrong.
16927
+ *
16928
+ * `to: "draft"` from an `archived` row ALSO requires the same
16929
+ * `previewToken` (the token is the only proof the caller owns the
16930
+ * unarchive). There is NO `archived → preview` single-step path —
16931
+ * the supported path is `archived → draft` (token required) and
16932
+ * then `draft → preview` (which may issue a NEW token).
16933
+ *
16934
+ * Visibility on archive (Finding #17, FlexTape retest):
16935
+ * `to: "archived"` does NOT touch `visibility` — the row keeps
16936
+ * whatever visibility it had at the time of the archive (usually
16937
+ * `"public"`, but `"unlisted"` is preserved too). Only `to: "live"`
16938
+ * assigns `visibility: "public"`.
16939
+ */
17222
16940
  async transition(id, input) {
17223
- const existing = await this.get(id);
17224
- await assertCreatorOwnedByAuthContext(this.config, String(existing.creatorId));
17225
- const doc = await executePitchFunction(this.config, {
16941
+ const doc = await fetchPitches(this.config, {
17226
16942
  method: "POST",
17227
- path: `/${id}/transition`,
17228
- body: {
17229
- to: input.to,
17230
- previewToken: input.previewToken
17231
- }
16943
+ path: `/pitches/${encodeURIComponent(id)}/transition`,
16944
+ body: { to: input.to, previewToken: input.previewToken }
17232
16945
  });
17233
16946
  return rowToPitch(doc);
17234
16947
  }
17235
16948
  /**
17236
16949
  * Upload a media file (thumbnail, og-image, video-poster, etc.)
17237
- * to Bunny Storage via the `pitch-asset-upload` Appwrite Function
17238
- * and wire the resulting `pitch_assets` row onto the draft pitch.
17239
- *
17240
- * Added 2026-06-23 to address Finding #4 of
17241
- * `docs-new/hp-cli-cycle-finds-2026-06-23.md`. The CLI's
17242
- * `hp pitches media upload` command calls this method.
17243
- *
17244
- * The `kind` must be one of the `AssetKind` values exported by
17245
- * `@honest-pitches/pitch-asset-core`. The server reads the bytes
17246
- * from `bytesBase64` (the base64 of `bytes`), uploads them to
17247
- * Bunny Storage, and writes a `pitch_assets` row. The returned
17248
- * record carries the wired `bunnyPath` and `cdnUrl`.
16950
+ * via the MCP server's `POST /pitcher/pitches/:id/media` route.
16951
+ * The MCP server handles the Bunny Storage upload + the
16952
+ * `pitch_assets` row insert.
17249
16953
  */
17250
16954
  async uploadMedia(input) {
17251
- const doc = await executePitchFunction(this.config, {
17252
- functionId: this.config.pitchAssetUploadFunctionId,
16955
+ const doc = await fetchPitches(this.config, {
17253
16956
  method: "POST",
17254
- path: "/",
16957
+ path: `/pitches/${encodeURIComponent(input.pitchId)}/media`,
17255
16958
  body: {
17256
- pitchId: input.pitchId,
17257
16959
  kind: input.kind,
17258
16960
  mimeType: input.mimeType,
17259
- sizeBytes: input.bytes.byteLength,
17260
- isPublic: input.isPublic ?? true,
17261
- // Buffer.from(Uint8Array) is the canonical Node 22 path to
17262
- // get the raw bytes. In browsers this is also covered by the
17263
- // same Buffer.from(uint8) shim, but the SDK is Node-only
17264
- // today so we don't need a cross-runtime polyfill.
17265
- bytesBase64: Buffer.from(input.bytes).toString("base64")
16961
+ bytesBase64: Buffer.from(input.bytes).toString("base64"),
16962
+ isPublic: input.isPublic ?? true
17266
16963
  }
17267
16964
  });
17268
16965
  return doc;
17269
16966
  }
17270
16967
  /**
17271
- * Restore an archived pitch to `draft` so the operator can edit
17272
- * and re-publish. Routes through the standard `transition` path
17273
- * with `to: "draft"` + a `previewToken` (required by the
17274
- * `pitch-write` server handler's "to=preview" pre-condition;
17275
- * the unarchive path reuses the same token so subsequent
17276
- * `draft → preview` moves don't require a re-mint).
16968
+ * List every media asset for a pitch, joining two server-side sources:
17277
16969
  *
17278
- * Added 2026-06-23 to address Finding #5 of
17279
- * `docs-new/hp-cli-cycle-finds-2026-06-23.md`. The CLI's
17280
- * `hp pitches transition <id> --to draft` command routes through
17281
- * this method when the current lifecycle is `archived`.
16970
+ * - `pitch_assets` rows with `pitchId === id` (the explicit
16971
+ * uploads via `POST /pitcher/pitches/:id/media`).
16972
+ * - The auto-generated `thumbnailFileId` and `ogImageFileId` fields
16973
+ * on the pitch row itself (synthesised by `pitch-write`'s
16974
+ * `to=live` path; they live on the row, NOT in `pitch_assets`).
16975
+ *
16976
+ * Each result carries a `source: "pitch_assets" | "pitch_row"`
16977
+ * discriminator so consumers can tell the two apart. The server
16978
+ * may synthesise `pitch_row` rows with `sizeBytes: 0` (the auto
16979
+ * images don't carry an upload byte count — the `cdnUrl` is the
16980
+ * canonical handle).
16981
+ *
16982
+ * Added 2026-07-28 to address Bug #12 of the FlexTape retest brief
16983
+ * (`testing-feedback/2026-07-27-flextape-rerun-3surface-retest.md`).
16984
+ * Prior to this, `hp pitches media list <id>` returned `[]` for
16985
+ * every pitch whose only assets were auto-generated.
16986
+ */
16987
+ async listMedia(id) {
16988
+ return fetchPitches(this.config, {
16989
+ method: "GET",
16990
+ path: `/pitches/${encodeURIComponent(id)}/media`
16991
+ });
16992
+ }
16993
+ /**
16994
+ * Restore an archived pitch to `draft`. Same as `transition(id, {
16995
+ * to: "draft", previewToken })` but with a placeholder previewToken
16996
+ * if the caller didn't pass one. The server's "to=draft" path
16997
+ * mirrors the existing "to=preview" pre-condition.
16998
+ *
16999
+ * Status vs lifecycle (2026-07-28 FlexTape retest doc bundle):
17000
+ * `pitches.lifecycle` is the canonical, computed view of where
17001
+ * the row sits (`draft, preview, live, expired, archived`). It is
17002
+ * derived from `status + previewToken` server-side
17003
+ * (`honest-pitches-studio-api/legacy/pitch-write/src/handler.ts`).
17004
+ * Branch on `lifecycle` for terminal-state checks.
17005
+ *
17006
+ * The mapping is:
17007
+ * - `lifecycle: "preview"` ⟷ `status: "draft"` (previewToken set)
17008
+ * - `lifecycle: "live"` ⟷ `status: "live"`
17009
+ * - `lifecycle: "archived"` ⟷ `status: "removed"` (intentional vocab split)
17010
+ *
17011
+ * There is NO direct `archived → preview` path — the server only
17012
+ * exposes `archived → draft`. To resume preview after a draft,
17013
+ * transition twice (`archived → draft` then `draft → preview` with
17014
+ * a new preview token), or use `transition(id, { to: "preview",
17015
+ * previewToken })` directly if your `lifecycle` is `draft` (NOT
17016
+ * `archived`).
17282
17017
  */
17283
17018
  async unarchive(id, opts = {}) {
17284
17019
  return this.transition(id, {
@@ -17295,25 +17030,16 @@ function createPitcherClient(config) {
17295
17030
  pitches: new PitchesApi(resolved),
17296
17031
  setJwt: (jwt, expiresAt) => resolved.setJwt(jwt, expiresAt),
17297
17032
  getJwt: () => resolved.getJwt(),
17298
- resolveCreatorId: async () => {
17299
- const id = await findCreatorIdForJwtUser(resolved);
17300
- if (!id) {
17301
- throw new Error(
17302
- "No creators row for this user \u2014 sign in via studio once so user-onboard runs"
17303
- );
17304
- }
17305
- return id;
17306
- },
17307
17033
  findCreatorIdForJwtUser: () => findCreatorIdForJwtUser(resolved),
17308
17034
  findCreatorIdForApiKey: () => findCreatorIdForApiKey(resolved)
17309
17035
  };
17310
17036
  }
17311
17037
 
17312
17038
  // src/index.ts
17313
- var import_pitch_core3 = __toESM(require_dist3(), 1);
17039
+ var import_pitch_core3 = __toESM(require_dist2(), 1);
17314
17040
 
17315
17041
  // src/scaffold.ts
17316
- var import_pitch_core2 = __toESM(require_dist3(), 1);
17042
+ var import_pitch_core2 = __toESM(require_dist2(), 1);
17317
17043
  function scaffoldPitchFromFramework(input) {
17318
17044
  if (!(0, import_pitch_core2.isFrameworkId)(input.framework)) {
17319
17045
  throw new Error(`Unknown framework id: ${input.framework}`);
@@ -17334,25 +17060,37 @@ function scaffoldPitchFromFramework(input) {
17334
17060
  ctaLabel: input.ctaLabel
17335
17061
  };
17336
17062
  }
17063
+
17064
+ // src/resolve-consolidated-route.ts
17065
+ var STUDIO_API_FUNCTION_ID = "honest-pitches-studio-api";
17066
+ var MEDIA_API_FUNCTION_ID = "honest-pitches-media";
17067
+ var FUNCTION_BUNDLE_MAP = {
17068
+ "pitch-write": STUDIO_API_FUNCTION_ID,
17069
+ "pitch-transition": STUDIO_API_FUNCTION_ID,
17070
+ "pitch-asset-upload": MEDIA_API_FUNCTION_ID,
17071
+ "agent-magic-link-issue": STUDIO_API_FUNCTION_ID
17072
+ };
17073
+ function resolveConsolidatedRoute(functionId, path) {
17074
+ const bundle = FUNCTION_BUNDLE_MAP[functionId];
17075
+ if (!bundle) return { functionId, path };
17076
+ const sub = path.startsWith("/") ? path : `/${path}`;
17077
+ const prefixed = sub === "/" ? `/${functionId}` : `/${functionId}${sub}`;
17078
+ return { functionId: bundle, path: prefixed };
17079
+ }
17337
17080
  var export_getSegmentDefinition = import_pitch_core3.getSegmentDefinition;
17338
17081
  var export_loadFrameworks = import_pitch_core3.loadFrameworks;
17339
17082
  var export_loadSegments = import_pitch_core3.loadSegments;
17340
17083
  var export_suggestedSectionsForFramework = import_pitch_core3.suggestedSectionsForFramework;
17341
17084
  export {
17342
- AppwriteRequestError,
17085
+ DEFAULT_PITCHER_BASE_URL,
17343
17086
  FrameworkLockedError,
17344
17087
  HpCodeImmutableError,
17345
17088
  InvalidSectionKindError,
17346
- MEDIA_API_FUNCTION_ID,
17347
- PUBLIC_API_FUNCTION_ID,
17348
17089
  PitcherNotAuthenticatedError,
17349
17090
  PitcherNotOwnedCreatorError,
17091
+ PitcherRequestError,
17350
17092
  PitcherSdkError,
17351
17093
  PitchesApi,
17352
- STRIPE_API_FUNCTION_ID,
17353
- STUDIO_API_FUNCTION_ID,
17354
- appwriteQueryEqual,
17355
- appwriteQueryLimit,
17356
17094
  createPitcherClient,
17357
17095
  decodeJwtSub,
17358
17096
  findCreatorIdForApiKey,