@honest-pitches/pitch-sdk 0.4.6 → 0.5.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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
16979
- });
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
16741
+ headers,
16742
+ ...body !== void 0 ? { body } : {},
16743
+ ...opts.signal ? { signal: opts.signal } : {}
17009
16744
  });
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"
17040
- );
17041
- }
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"
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)}`
17055
16758
  );
17056
16759
  }
17057
- if (!(0, import_pitcher_api_key_core.isWellFormedApiKey)(apiKey)) {
17058
- return null;
16760
+ if (envelope && envelope.ok === true) {
16761
+ return envelope.data;
17059
16762
  }
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)]
17068
- );
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)]
16770
+ throw new PitcherRequestError(
16771
+ res.status,
16772
+ "PITCHER_MALFORMED",
16773
+ `MCP server returned malformed envelope: ${text.slice(0, 256)}`
17084
16774
  );
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,76 @@ 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
  };
17172
16852
  if (input.categorySlug != null) payload.categorySlug = input.categorySlug;
17173
16853
  if (input.priceNote != null && input.priceNote !== "") {
17174
16854
  payload.priceNote = String(input.priceNote).slice(0, 64);
17175
16855
  }
17176
- const doc = await executePitchFunction(this.config, {
16856
+ const doc = await fetchPitches(this.config, {
17177
16857
  method: "POST",
17178
- path: "/",
16858
+ path: "/pitches",
17179
16859
  body: payload
17180
16860
  });
17181
16861
  return rowToPitch(doc);
17182
16862
  }
17183
16863
  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
- }
16864
+ const doc = await fetchPitches(this.config, {
16865
+ method: "GET",
16866
+ path: `/pitches/${encodeURIComponent(id)}`
17200
16867
  });
17201
- return rowToPitch(doc, resolved.sections);
16868
+ return rowToPitch(doc);
17202
16869
  }
17203
16870
  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);
16871
+ const params = new URLSearchParams();
16872
+ if (opts.status) params.set("status", opts.status);
16873
+ if (typeof opts.limit === "number") params.set("limit", String(opts.limit));
16874
+ const qs = params.toString();
16875
+ const docs = await fetchPitches(this.config, {
16876
+ method: "GET",
16877
+ path: `/pitches${qs ? `?${qs}` : ""}`
17234
16878
  });
16879
+ return docs.map((d) => rowToPitch(d));
17235
16880
  }
17236
16881
  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, {
16882
+ const doc = await fetchPitches(this.config, {
17249
16883
  method: "PATCH",
17250
- path: `/${id}`,
17251
- body
16884
+ path: `/pitches/${encodeURIComponent(id)}`,
16885
+ body: { ...input }
17252
16886
  });
17253
16887
  return rowToPitch(doc);
17254
16888
  }
17255
16889
  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, {
16890
+ const doc = await fetchPitches(this.config, {
17259
16891
  method: "POST",
17260
- path: `/${id}/transition`,
17261
- body: {
17262
- to: input.to,
17263
- previewToken: input.previewToken
17264
- }
16892
+ path: `/pitches/${encodeURIComponent(id)}/transition`,
16893
+ body: { to: input.to, previewToken: input.previewToken }
17265
16894
  });
17266
16895
  return rowToPitch(doc);
17267
16896
  }
17268
16897
  /**
17269
16898
  * 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`.
16899
+ * via the MCP server's `POST /pitcher/pitches/:id/media` route.
16900
+ * The MCP server handles the Bunny Storage upload + the
16901
+ * `pitch_assets` row insert.
17282
16902
  */
17283
16903
  async uploadMedia(input) {
17284
- const doc = await executePitchFunction(this.config, {
17285
- functionId: this.config.pitchAssetUploadFunctionId,
16904
+ const doc = await fetchPitches(this.config, {
17286
16905
  method: "POST",
17287
- path: "/",
16906
+ path: `/pitches/${encodeURIComponent(input.pitchId)}/media`,
17288
16907
  body: {
17289
- pitchId: input.pitchId,
17290
16908
  kind: input.kind,
17291
16909
  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")
16910
+ bytesBase64: Buffer.from(input.bytes).toString("base64"),
16911
+ isPublic: input.isPublic ?? true
17299
16912
  }
17300
16913
  });
17301
16914
  return doc;
17302
16915
  }
17303
16916
  /**
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).
17310
- *
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`.
16917
+ * Restore an archived pitch to `draft`. Same as `transition(id, {
16918
+ * to: "draft", previewToken })` but with a placeholder previewToken
16919
+ * if the caller didn't pass one. The server's "to=draft" path
16920
+ * mirrors the existing "to=preview" pre-condition.
17315
16921
  */
17316
16922
  async unarchive(id, opts = {}) {
17317
16923
  return this.transition(id, {
@@ -17328,25 +16934,16 @@ function createPitcherClient(config) {
17328
16934
  pitches: new PitchesApi(resolved),
17329
16935
  setJwt: (jwt, expiresAt) => resolved.setJwt(jwt, expiresAt),
17330
16936
  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
16937
  findCreatorIdForJwtUser: () => findCreatorIdForJwtUser(resolved),
17341
16938
  findCreatorIdForApiKey: () => findCreatorIdForApiKey(resolved)
17342
16939
  };
17343
16940
  }
17344
16941
 
17345
16942
  // src/index.ts
17346
- var import_pitch_core3 = __toESM(require_dist3(), 1);
16943
+ var import_pitch_core3 = __toESM(require_dist2(), 1);
17347
16944
 
17348
16945
  // src/scaffold.ts
17349
- var import_pitch_core2 = __toESM(require_dist3(), 1);
16946
+ var import_pitch_core2 = __toESM(require_dist2(), 1);
17350
16947
  function scaffoldPitchFromFramework(input) {
17351
16948
  if (!(0, import_pitch_core2.isFrameworkId)(input.framework)) {
17352
16949
  throw new Error(`Unknown framework id: ${input.framework}`);
@@ -17367,22 +16964,34 @@ function scaffoldPitchFromFramework(input) {
17367
16964
  ctaLabel: input.ctaLabel
17368
16965
  };
17369
16966
  }
16967
+
16968
+ // src/resolve-consolidated-route.ts
16969
+ var STUDIO_API_FUNCTION_ID = "honest-pitches-studio-api";
16970
+ var MEDIA_API_FUNCTION_ID = "honest-pitches-media";
16971
+ var FUNCTION_BUNDLE_MAP = {
16972
+ "pitch-write": STUDIO_API_FUNCTION_ID,
16973
+ "pitch-transition": STUDIO_API_FUNCTION_ID,
16974
+ "pitch-asset-upload": MEDIA_API_FUNCTION_ID,
16975
+ "agent-magic-link-issue": STUDIO_API_FUNCTION_ID
16976
+ };
16977
+ function resolveConsolidatedRoute(functionId, path) {
16978
+ const bundle = FUNCTION_BUNDLE_MAP[functionId];
16979
+ if (!bundle) return { functionId, path };
16980
+ const sub = path.startsWith("/") ? path : `/${path}`;
16981
+ const prefixed = sub === "/" ? `/${functionId}` : `/${functionId}${sub}`;
16982
+ return { functionId: bundle, path: prefixed };
16983
+ }
17370
16984
  // Annotate the CommonJS export names for ESM import in node:
17371
16985
  0 && (module.exports = {
17372
- AppwriteRequestError,
16986
+ DEFAULT_PITCHER_BASE_URL,
17373
16987
  FrameworkLockedError,
17374
16988
  HpCodeImmutableError,
17375
16989
  InvalidSectionKindError,
17376
- MEDIA_API_FUNCTION_ID,
17377
- PUBLIC_API_FUNCTION_ID,
17378
16990
  PitcherNotAuthenticatedError,
17379
16991
  PitcherNotOwnedCreatorError,
16992
+ PitcherRequestError,
17380
16993
  PitcherSdkError,
17381
16994
  PitchesApi,
17382
- STRIPE_API_FUNCTION_ID,
17383
- STUDIO_API_FUNCTION_ID,
17384
- appwriteQueryEqual,
17385
- appwriteQueryLimit,
17386
16995
  createPitcherClient,
17387
16996
  decodeJwtSub,
17388
16997
  findCreatorIdForApiKey,