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