@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/CHANGELOG.md +146 -0
- package/README.md +38 -69
- package/dist/index.cjs +277 -546
- package/dist/index.cjs.map +4 -4
- package/dist/index.d.mts +3 -4
- package/dist/index.mjs +277 -539
- package/dist/index.mjs.map +4 -4
- package/package.json +7 -6
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
|
|
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
|
|
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: () =>
|
|
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: () =>
|
|
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: () =>
|
|
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
|
|
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 =
|
|
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
|
|
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
|
|
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
|
|
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
|
|
16615
|
+
var PitcherRequestError = class extends PitcherSdkError {
|
|
16777
16616
|
status;
|
|
16778
|
-
constructor(status, message) {
|
|
16779
|
-
super("
|
|
16780
|
-
this.name = "
|
|
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.
|
|
16627
|
+
if (input.jwt && input.apiKey) {
|
|
16788
16628
|
throw new PitcherSdkError(
|
|
16789
16629
|
"PITCHER_AUTH_AMBIGUOUS",
|
|
16790
|
-
"Set either
|
|
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
|
|
16794
|
-
|
|
16795
|
-
|
|
16796
|
-
|
|
16797
|
-
|
|
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
|
-
|
|
16812
|
-
|
|
16813
|
-
|
|
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/
|
|
16864
|
-
|
|
16865
|
-
|
|
16866
|
-
|
|
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(
|
|
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
|
|
16897
|
-
|
|
16898
|
-
|
|
16899
|
-
|
|
16900
|
-
|
|
16901
|
-
|
|
16902
|
-
|
|
16903
|
-
|
|
16904
|
-
|
|
16905
|
-
|
|
16906
|
-
|
|
16907
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
16945
|
-
|
|
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
|
-
|
|
16720
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
16721
|
+
throw new PitcherRequestError(0, "PITCHER_NETWORK", `fetchPitches failed: ${message}`);
|
|
16982
16722
|
}
|
|
16983
|
-
|
|
16984
|
-
|
|
16985
|
-
if (!config.getApiKey()) requireJwt(config);
|
|
16986
|
-
applyApiKeyHeader(config);
|
|
16723
|
+
const text = await res.text();
|
|
16724
|
+
let envelope = null;
|
|
16987
16725
|
try {
|
|
16988
|
-
|
|
16989
|
-
|
|
16990
|
-
|
|
16991
|
-
|
|
16992
|
-
|
|
16993
|
-
|
|
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
|
-
|
|
17010
|
-
|
|
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 (
|
|
17025
|
-
|
|
17026
|
-
|
|
17027
|
-
|
|
17028
|
-
|
|
17029
|
-
|
|
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
|
-
|
|
17032
|
-
|
|
17033
|
-
|
|
17034
|
-
|
|
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/
|
|
17057
|
-
|
|
17058
|
-
|
|
17059
|
-
|
|
17060
|
-
|
|
17061
|
-
|
|
17062
|
-
return
|
|
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
|
|
17065
|
-
return
|
|
16759
|
+
async function findCreatorIdForApiKey(config) {
|
|
16760
|
+
return findCreatorIdForJwtUser(config);
|
|
17066
16761
|
}
|
|
17067
16762
|
|
|
17068
16763
|
// src/pitches.ts
|
|
17069
|
-
|
|
17070
|
-
|
|
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()
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
17152
|
-
|
|
17153
|
-
|
|
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
|
|
16866
|
+
return rowToPitch(doc);
|
|
17169
16867
|
}
|
|
17170
16868
|
async find(opts = {}) {
|
|
17171
|
-
const
|
|
17172
|
-
if (opts.status)
|
|
17173
|
-
|
|
17174
|
-
|
|
17175
|
-
|
|
17176
|
-
|
|
17177
|
-
|
|
17178
|
-
|
|
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
|
|
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:
|
|
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
|
|
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:
|
|
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
|
-
*
|
|
17238
|
-
*
|
|
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
|
|
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
|
-
|
|
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
|
-
*
|
|
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
|
-
*
|
|
17279
|
-
* `
|
|
17280
|
-
*
|
|
17281
|
-
*
|
|
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(
|
|
17039
|
+
var import_pitch_core3 = __toESM(require_dist2(), 1);
|
|
17314
17040
|
|
|
17315
17041
|
// src/scaffold.ts
|
|
17316
|
-
var import_pitch_core2 = __toESM(
|
|
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
|
-
|
|
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,
|