@koda-sl/baker-cli 0.353.0-dev.8bdd8f915 → 0.353.0-dev.df03f88fe
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/README.md +10 -5
- package/dist/{chunk-YBLNCFK4.js → chunk-B7MLH3K6.js} +1 -2
- package/dist/chunk-B7MLH3K6.js.map +1 -0
- package/dist/{chunk-4QXKWIFC.js → chunk-MZQC4LII.js} +4 -1
- package/dist/chunk-MZQC4LII.js.map +1 -0
- package/dist/{chunk-NA5CPMUA.js → chunk-SR56QQTD.js} +4 -4
- package/dist/chunk-SR56QQTD.js.map +1 -0
- package/dist/{chunk-2IC3CGQO.js → chunk-VVQLRFCI.js} +2 -2
- package/dist/cli.js +36 -25
- package/dist/cli.js.map +1 -1
- package/dist/{client-ODCNYCP4.js → client-3DGBQ2H3.js} +2 -2
- package/dist/engine/index.js +1 -1
- package/dist/{output-2XBCYF7L.js → output-OSSBUGT7.js} +3 -3
- package/dist/{shared-RYAF7Y7H.js → shared-MSI4UMZ3.js} +4 -4
- package/package.json +1 -1
- package/dist/chunk-4QXKWIFC.js.map +0 -1
- package/dist/chunk-NA5CPMUA.js.map +0 -1
- package/dist/chunk-YBLNCFK4.js.map +0 -1
- /package/dist/{chunk-2IC3CGQO.js.map → chunk-VVQLRFCI.js.map} +0 -0
- /package/dist/{client-ODCNYCP4.js.map → client-3DGBQ2H3.js.map} +0 -0
- /package/dist/{output-2XBCYF7L.js.map → output-OSSBUGT7.js.map} +0 -0
- /package/dist/{shared-RYAF7Y7H.js.map → shared-MSI4UMZ3.js.map} +0 -0
|
@@ -85,6 +85,9 @@ function mapHttpError(status) {
|
|
|
85
85
|
if (status === 429) {
|
|
86
86
|
return "RATE_LIMITED";
|
|
87
87
|
}
|
|
88
|
+
if (status === 503) {
|
|
89
|
+
return "UNAVAILABLE";
|
|
90
|
+
}
|
|
88
91
|
return "INTERNAL_ERROR";
|
|
89
92
|
}
|
|
90
93
|
async function handleResponse(response) {
|
|
@@ -205,4 +208,4 @@ export {
|
|
|
205
208
|
apiGet,
|
|
206
209
|
apiPost
|
|
207
210
|
};
|
|
208
|
-
//# sourceMappingURL=chunk-
|
|
211
|
+
//# sourceMappingURL=chunk-MZQC4LII.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/client.ts"],"sourcesContent":["import { debugLogHttp, readBodyForLog } from \"./debugLog.ts\";\nimport { getEnv } from \"./env.ts\";\n\nconst MAX_RATE_LIMIT_RETRIES = 3;\nconst MAX_TOTAL_WAIT_MS = 2 * 60 * 1000;\n\nasync function fetchWithRateLimitRetry(url: string, init: RequestInit): Promise<Response> {\n let totalWaited = 0;\n\n for (let attempt = 0; attempt <= MAX_RATE_LIMIT_RETRIES; attempt++) {\n const response = await fetch(url, init);\n\n if (response.status !== 429 || attempt >= MAX_RATE_LIMIT_RETRIES) {\n return response;\n }\n\n const retryAfterHeader = response.headers.get(\"Retry-After\");\n const waitMs = retryAfterHeader ? Number(retryAfterHeader) * 1000 : 2000 * 2 ** attempt;\n\n if (totalWaited + waitMs > MAX_TOTAL_WAIT_MS) {\n return response;\n }\n\n totalWaited += waitMs;\n await new Promise((resolve) => setTimeout(resolve, waitMs));\n }\n\n return fetch(url, init);\n}\n\nexport type ErrorCode =\n | \"UNAUTHORIZED\"\n | \"FORBIDDEN\"\n | \"NOT_FOUND\"\n | \"CONFLICT\"\n | \"VALIDATION_ERROR\"\n | \"RATE_LIMITED\"\n | \"UNAVAILABLE\"\n | \"INTERNAL_ERROR\"\n | \"NETWORK_ERROR\"\n | \"TIMEOUT\"\n | \"IMAGE_PROCESSING_ERROR\";\n\nexport class ApiError extends Error {\n code: ErrorCode;\n\n constructor(code: ErrorCode, message: string) {\n super(message);\n this.name = \"ApiError\";\n this.code = code;\n }\n}\n\nconst CONVEX_ID_RE = /^[a-zA-Z0-9_]+$/;\n\nfunction hasControlCharacters(value: string): boolean {\n for (let i = 0; i < value.length; i++) {\n const code = value.charCodeAt(i);\n // Allow tab (9), newline (10), carriage return (13)\n if (code < 32 && code !== 9 && code !== 10 && code !== 13) {\n return true;\n }\n }\n return false;\n}\n\nfunction validateStringValue(value: string): void {\n if (hasControlCharacters(value)) {\n throw new ApiError(\"VALIDATION_ERROR\", \"String value contains invalid control characters\");\n }\n}\n\nexport function validateConvexId(id: string): void {\n if (!CONVEX_ID_RE.test(id)) {\n throw new ApiError(\"VALIDATION_ERROR\", `Invalid ID format: \"${id}\". Expected alphanumeric string.`);\n }\n}\n\nfunction sanitizeParams(params: Record<string, string>): Record<string, string> {\n const sanitized: Record<string, string> = {};\n for (const [key, value] of Object.entries(params)) {\n validateStringValue(value);\n sanitized[key] = value;\n }\n return sanitized;\n}\n\n/**\n * Sent on every call so the backend can attribute immediately-applied writes\n * (library images and videos, followed advertisers) to the Session they were\n * made from. Absent outside a chat-attached environment.\n */\nfunction chatHeader(): Record<string, string> {\n const chatId = getEnv().BAKER_CHAT_ID;\n return chatId ? { \"x-baker-chat-id\": chatId } : {};\n}\n\nfunction mapHttpError(status: number): ErrorCode {\n if (status === 401) {\n return \"UNAUTHORIZED\";\n }\n // 403 is never \"not connected\" — every backend `FORBIDDEN` means connected but\n // not allowed (an asset outside the granted scope, a capability the account\n // lacks). Folding it into UNAUTHORIZED sent agents down the reconnect path,\n // which cannot fix a permission gap and wastes the user's time.\n if (status === 403) {\n return \"FORBIDDEN\";\n }\n if (status === 404) {\n return \"NOT_FOUND\";\n }\n if (status === 422 || status === 400) {\n return \"VALIDATION_ERROR\";\n }\n // 409 is a state the caller has to change, never a fault to sit out. It is\n // what the backend returns for \"this workspace has not switched analytics on\n // yet\" and its siblings — folded into INTERNAL_ERROR, that reaches an agent\n // as a transient failure worth retrying, which it never is.\n if (status === 409) {\n return \"CONFLICT\";\n }\n if (status === 429) {\n return \"RATE_LIMITED\";\n }\n // 503 is the backend's `UNAVAILABLE`: a system it depends on did not answer\n // just now, and asking again is the fix. Folded into INTERNAL_ERROR, it reads\n // exactly like a fault of ours, and the agent cannot tell which one waits out.\n if (status === 503) {\n return \"UNAVAILABLE\";\n }\n return \"INTERNAL_ERROR\";\n}\n\nasync function handleResponse<T>(response: Response): Promise<T> {\n const body = await response.text();\n\n if (!response.ok) {\n let message = `HTTP ${response.status}: ${response.statusText}`;\n try {\n const parsed = JSON.parse(body) as { error?: string | { message?: string }; message?: string };\n if (typeof parsed.error === \"string\") {\n message = parsed.error;\n } else if (parsed.error?.message) {\n message = parsed.error.message;\n } else if (parsed.message) {\n message = parsed.message;\n }\n } catch {\n // Use default message\n }\n throw new ApiError(mapHttpError(response.status), message);\n }\n\n try {\n return JSON.parse(body) as T;\n } catch {\n throw new ApiError(\"INTERNAL_ERROR\", \"Failed to parse API response as JSON\");\n }\n}\n\n/**\n * A GET that also hands back the response headers.\n *\n * Some endpoints have to say something *about* the answer that the answer has\n * no room for — `GET /api/testimonials` returns a bare array and sets\n * `X-Baker-Next-Cursor` when rows past this page remain unexamined, so the rows\n * alone cannot tell one page of a library from the whole of it.\n */\nexport async function apiGetWithHeaders<T>(\n path: string,\n params?: Record<string, string>,\n): Promise<{ data: T; headers: Headers }> {\n const env = getEnv();\n const url = new URL(path, env.BAKER_API_URL);\n if (params) {\n const clean = sanitizeParams(params);\n for (const [key, value] of Object.entries(clean)) {\n url.searchParams.set(key, value);\n }\n }\n\n const urlStr = url.toString();\n const startedAt = Date.now();\n let response: Response;\n try {\n response = await fetchWithRateLimitRetry(urlStr, {\n method: \"GET\",\n headers: {\n Authorization: `Bearer ${env.BAKER_API_KEY}`,\n Accept: \"application/json\",\n ...chatHeader(),\n },\n signal: AbortSignal.timeout(60_000),\n });\n } catch (err) {\n debugLogHttp({ source: \"cli\", method: \"GET\", url: urlStr, durationMs: Date.now() - startedAt, error: err });\n if (err instanceof Error && (err.name === \"TimeoutError\" || err.name === \"AbortError\")) {\n throw new ApiError(\"TIMEOUT\", \"Request timed out after 60 seconds\");\n }\n throw new ApiError(\"NETWORK_ERROR\", `Request failed: ${err instanceof Error ? err.message : \"Unknown error\"}`);\n }\n\n debugLogHttp({\n source: \"cli\",\n method: \"GET\",\n url: urlStr,\n status: response.status,\n ok: response.ok,\n responseBody: await readBodyForLog(response),\n durationMs: Date.now() - startedAt,\n });\n return { data: await handleResponse<T>(response), headers: response.headers };\n}\n\nexport async function apiGet<T>(path: string, params?: Record<string, string>): Promise<T> {\n return (await apiGetWithHeaders<T>(path, params)).data;\n}\n\nexport async function apiPost<T>(path: string, body: unknown, opts?: { timeoutMs?: number }): Promise<T> {\n const env = getEnv();\n const timeoutMs = opts?.timeoutMs ?? 60_000;\n const urlStr = new URL(path, env.BAKER_API_URL).toString();\n const startedAt = Date.now();\n let response: Response;\n try {\n response = await fetchWithRateLimitRetry(urlStr, {\n method: \"POST\",\n headers: {\n Authorization: `Bearer ${env.BAKER_API_KEY}`,\n \"Content-Type\": \"application/json\",\n Accept: \"application/json\",\n ...chatHeader(),\n },\n body: JSON.stringify(body),\n signal: AbortSignal.timeout(timeoutMs),\n });\n } catch (err) {\n debugLogHttp({\n source: \"cli\",\n method: \"POST\",\n url: urlStr,\n requestBody: body,\n durationMs: Date.now() - startedAt,\n error: err,\n });\n if (err instanceof Error && (err.name === \"TimeoutError\" || err.name === \"AbortError\")) {\n throw new ApiError(\"TIMEOUT\", `Request timed out after ${Math.round(timeoutMs / 1000)} seconds`);\n }\n throw new ApiError(\"NETWORK_ERROR\", `Request failed: ${err instanceof Error ? err.message : \"Unknown error\"}`);\n }\n\n debugLogHttp({\n source: \"cli\",\n method: \"POST\",\n url: urlStr,\n requestBody: body,\n status: response.status,\n ok: response.ok,\n responseBody: await readBodyForLog(response),\n durationMs: Date.now() - startedAt,\n });\n return handleResponse<T>(response);\n}\n"],"mappings":"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","names":[]}
|
|
@@ -2,10 +2,10 @@ import {
|
|
|
2
2
|
handleConnectionError,
|
|
3
3
|
needsConnectionFix,
|
|
4
4
|
writeAdsJson
|
|
5
|
-
} from "./chunk-
|
|
5
|
+
} from "./chunk-VVQLRFCI.js";
|
|
6
6
|
import {
|
|
7
7
|
ApiError
|
|
8
|
-
} from "./chunk-
|
|
8
|
+
} from "./chunk-MZQC4LII.js";
|
|
9
9
|
import {
|
|
10
10
|
getEnv
|
|
11
11
|
} from "./chunk-2OKSJNLA.js";
|
|
@@ -35,7 +35,7 @@ function handleMetaError(err) {
|
|
|
35
35
|
code: err.code,
|
|
36
36
|
message: err.message,
|
|
37
37
|
fix: err.code === "FORBIDDEN" ? metaForbiddenFix(err.message) : { action: "reject", explanation: err.message },
|
|
38
|
-
retryable: err.code === "RATE_LIMITED" || err.code === "INTERNAL_ERROR"
|
|
38
|
+
retryable: err.code === "RATE_LIMITED" || err.code === "UNAVAILABLE" || err.code === "INTERNAL_ERROR"
|
|
39
39
|
}
|
|
40
40
|
};
|
|
41
41
|
writeAdsJson(envelope);
|
|
@@ -108,4 +108,4 @@ export {
|
|
|
108
108
|
csvOrJson,
|
|
109
109
|
resolveEffectiveStatus
|
|
110
110
|
};
|
|
111
|
-
//# sourceMappingURL=chunk-
|
|
111
|
+
//# sourceMappingURL=chunk-SR56QQTD.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/commands/ads/meta/shared.ts"],"sourcesContent":["import { ApiError } from \"../../../client.ts\";\nimport { getEnv } from \"../../../env.ts\";\nimport { handleConnectionError, needsConnectionFix } from \"../../../error-handler.ts\";\nimport { writeAdsJson } from \"../output.ts\";\nimport type { AdsErrorEnvelope, QueryFix } from \"../types.ts\";\n\nconst DAY_MS = 86_400_000;\n\n/**\n * Meta answers a permission refusal (Graph codes 200/10/294) with a bare\n * \"Permissions error\" that covers two causes needing opposite remedies: a\n * consent grant the connection never got — nothing validates Meta's granted\n * scopes after connect, unlike HubSpot's `requireCapability` — or a role the\n * user lacks on one asset, since a Pixel/Dataset, Page or catalog lives in a\n * Business and is assigned separately from the ad account. Only the blast radius\n * separates them, so hand the agent that test rather than a verdict: guessing\n * \"reconnect\" wastes the user's time on an unassigned asset, and ruling it out\n * strands a connection that really is short a scope.\n */\nexport function metaForbiddenFix(message: string): QueryFix {\n return {\n action: \"reject\",\n explanation: [\n message,\n \"This is a permission refusal, not a dead connection, and Meta returns the same code for two causes with opposite remedies. Tell them apart by how much else works before you answer.\",\n \"If other reads on this account are ALSO refused (campaigns, ad sets, insights), the connection is short a permission it was never granted. Only the user can widen it, by reconnecting Meta from the dashboard.\",\n \"If those reads work and only this object is refused, the asset (pixel/dataset, Page, or catalog) belongs to a Business the connected user holds no role in. Ad-account access does not carry asset access and reconnecting cannot grant it — the client assigns that asset to the connected user or partner in Meta Business settings, under the asset's own section (Data sources → Datasets for a pixel).\",\n \"Either way do not retry the same call, and do not abort the wider job. Continue with what you can read, and state in your answer that this specific check could not run and why — never present the task as verified when this step was skipped.\",\n ].join(\"\\n\\n\"),\n };\n}\n\nexport function handleMetaError(err: unknown): never {\n if (err instanceof ApiError) {\n if (needsConnectionFix(err.code, err.message)) {\n handleConnectionError(\"meta_ads\", err.message);\n }\n const envelope: AdsErrorEnvelope = {\n ok: false,\n error: {\n code: err.code,\n message: err.message,\n fix: err.code === \"FORBIDDEN\" ? metaForbiddenFix(err.message) : { action: \"reject\", explanation: err.message },\n retryable: err.code === \"RATE_LIMITED\" || err.code === \"UNAVAILABLE\" || err.code === \"INTERNAL_ERROR\",\n },\n };\n writeAdsJson(envelope);\n process.exit(1);\n }\n const message = err instanceof Error ? err.message : \"Unexpected error\";\n writeAdsJson({ ok: false, error: { code: \"NETWORK_ERROR\", message } });\n process.exit(1);\n}\n\nconst ACCOUNT_ID_RE = /^(act_)?[0-9]+$/;\n\n/** Pull the default Meta ad account ID from `--account-id` arg or `BAKER_META_AD_ACCOUNT_ID` env. */\nexport function resolveAccountIdArg(args: Record<string, unknown>): string {\n const fromArgs = args[\"account-id\"] as string | undefined;\n const id = fromArgs || getEnv().BAKER_META_AD_ACCOUNT_ID;\n if (!id) {\n writeAdsJson({\n ok: false,\n error: {\n code: \"MISSING_ACCOUNT_ID\",\n message: \"Pass --account-id or set BAKER_META_AD_ACCOUNT_ID. Run `baker ads meta accounts` to find IDs.\",\n },\n });\n process.exit(1);\n }\n if (!ACCOUNT_ID_RE.test(id)) {\n writeAdsJson({\n ok: false,\n error: {\n code: \"INVALID_ACCOUNT_ID\",\n message: `Invalid account ID \"${id}\". Expected numeric ID, optionally prefixed with \"act_\".`,\n },\n });\n process.exit(1);\n }\n return id.startsWith(\"act_\") ? id : `act_${id}`;\n}\n\nexport function getDateRange(args: Record<string, unknown>): { datePreset?: string; since?: string; until?: string } {\n const datePreset = args[\"date-preset\"] as string | undefined;\n const since = args.since as string | undefined;\n const until = args.until as string | undefined;\n if (since && until) return { since, until };\n return { datePreset: datePreset ?? \"last_7d\" };\n}\n\nexport function todayIso(): string {\n return new Date().toISOString().slice(0, 10);\n}\n\nexport function daysAgoIso(days: number): string {\n return new Date(Date.now() - days * DAY_MS).toISOString().slice(0, 10);\n}\n\nexport function csvOrJson(args: Record<string, unknown>): string {\n return (args.output as string | undefined) ?? \"json\";\n}\n\n/**\n * Default to ACTIVE-only on listings — that's almost always what an AI agent or\n * a human glance wants. Pass `--effective-status ACTIVE,PAUSED` to override, or\n * `--all-statuses` to drop the filter entirely.\n */\nexport function resolveEffectiveStatus(args: Record<string, unknown>): string | undefined {\n if (args[\"all-statuses\"]) {\n return undefined;\n }\n const explicit = args[\"effective-status\"] as string | undefined;\n if (explicit) {\n return explicit;\n }\n return \"ACTIVE\";\n}\n"],"mappings":";;;;;;;;;;;;;AAMA,IAAM,SAAS;AAaR,SAAS,iBAAiB,SAA2B;AAC1D,SAAO;AAAA,IACL,QAAQ;AAAA,IACR,aAAa;AAAA,MACX;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,IACF,EAAE,KAAK,MAAM;AAAA,EACf;AACF;AAEO,SAAS,gBAAgB,KAAqB;AACnD,MAAI,eAAe,UAAU;AAC3B,QAAI,mBAAmB,IAAI,MAAM,IAAI,OAAO,GAAG;AAC7C,4BAAsB,YAAY,IAAI,OAAO;AAAA,IAC/C;AACA,UAAM,WAA6B;AAAA,MACjC,IAAI;AAAA,MACJ,OAAO;AAAA,QACL,MAAM,IAAI;AAAA,QACV,SAAS,IAAI;AAAA,QACb,KAAK,IAAI,SAAS,cAAc,iBAAiB,IAAI,OAAO,IAAI,EAAE,QAAQ,UAAU,aAAa,IAAI,QAAQ;AAAA,QAC7G,WAAW,IAAI,SAAS,kBAAkB,IAAI,SAAS,iBAAiB,IAAI,SAAS;AAAA,MACvF;AAAA,IACF;AACA,iBAAa,QAAQ;AACrB,YAAQ,KAAK,CAAC;AAAA,EAChB;AACA,QAAM,UAAU,eAAe,QAAQ,IAAI,UAAU;AACrD,eAAa,EAAE,IAAI,OAAO,OAAO,EAAE,MAAM,iBAAiB,QAAQ,EAAE,CAAC;AACrE,UAAQ,KAAK,CAAC;AAChB;AAEA,IAAM,gBAAgB;AAGf,SAAS,oBAAoB,MAAuC;AACzE,QAAM,WAAW,KAAK,YAAY;AAClC,QAAM,KAAK,YAAY,OAAO,EAAE;AAChC,MAAI,CAAC,IAAI;AACP,iBAAa;AAAA,MACX,IAAI;AAAA,MACJ,OAAO;AAAA,QACL,MAAM;AAAA,QACN,SAAS;AAAA,MACX;AAAA,IACF,CAAC;AACD,YAAQ,KAAK,CAAC;AAAA,EAChB;AACA,MAAI,CAAC,cAAc,KAAK,EAAE,GAAG;AAC3B,iBAAa;AAAA,MACX,IAAI;AAAA,MACJ,OAAO;AAAA,QACL,MAAM;AAAA,QACN,SAAS,uBAAuB,EAAE;AAAA,MACpC;AAAA,IACF,CAAC;AACD,YAAQ,KAAK,CAAC;AAAA,EAChB;AACA,SAAO,GAAG,WAAW,MAAM,IAAI,KAAK,OAAO,EAAE;AAC/C;AAEO,SAAS,aAAa,MAAwF;AACnH,QAAM,aAAa,KAAK,aAAa;AACrC,QAAM,QAAQ,KAAK;AACnB,QAAM,QAAQ,KAAK;AACnB,MAAI,SAAS,MAAO,QAAO,EAAE,OAAO,MAAM;AAC1C,SAAO,EAAE,YAAY,cAAc,UAAU;AAC/C;AAEO,SAAS,WAAmB;AACjC,UAAO,oBAAI,KAAK,GAAE,YAAY,EAAE,MAAM,GAAG,EAAE;AAC7C;AAEO,SAAS,WAAW,MAAsB;AAC/C,SAAO,IAAI,KAAK,KAAK,IAAI,IAAI,OAAO,MAAM,EAAE,YAAY,EAAE,MAAM,GAAG,EAAE;AACvE;AAEO,SAAS,UAAU,MAAuC;AAC/D,SAAQ,KAAK,UAAiC;AAChD;AAOO,SAAS,uBAAuB,MAAmD;AACxF,MAAI,KAAK,cAAc,GAAG;AACxB,WAAO;AAAA,EACT;AACA,QAAM,WAAW,KAAK,kBAAkB;AACxC,MAAI,UAAU;AACZ,WAAO;AAAA,EACT;AACA,SAAO;AACT;","names":[]}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import {
|
|
2
2
|
ApiError,
|
|
3
3
|
apiGet
|
|
4
|
-
} from "./chunk-
|
|
4
|
+
} from "./chunk-MZQC4LII.js";
|
|
5
5
|
import {
|
|
6
6
|
getEnv
|
|
7
7
|
} from "./chunk-2OKSJNLA.js";
|
|
@@ -294,4 +294,4 @@ export {
|
|
|
294
294
|
writeAdsOutput,
|
|
295
295
|
resolveCustomerId
|
|
296
296
|
};
|
|
297
|
-
//# sourceMappingURL=chunk-
|
|
297
|
+
//# sourceMappingURL=chunk-VVQLRFCI.js.map
|
package/dist/cli.js
CHANGED
|
@@ -28,7 +28,6 @@ import {
|
|
|
28
28
|
clipDurationsFor,
|
|
29
29
|
clipParamRecipe,
|
|
30
30
|
clipProfileFor,
|
|
31
|
-
coercePathUrlToUrl,
|
|
32
31
|
collectAssetRefLikes,
|
|
33
32
|
createEngineFromEnv,
|
|
34
33
|
defaultRegistry,
|
|
@@ -69,7 +68,7 @@ import {
|
|
|
69
68
|
validateCanvasDeep,
|
|
70
69
|
withoutCastName,
|
|
71
70
|
ytDlpBlockSignal
|
|
72
|
-
} from "./chunk-
|
|
71
|
+
} from "./chunk-B7MLH3K6.js";
|
|
73
72
|
import {
|
|
74
73
|
csvOrJson,
|
|
75
74
|
daysAgoIso,
|
|
@@ -78,7 +77,7 @@ import {
|
|
|
78
77
|
resolveAccountIdArg,
|
|
79
78
|
resolveEffectiveStatus,
|
|
80
79
|
todayIso
|
|
81
|
-
} from "./chunk-
|
|
80
|
+
} from "./chunk-SR56QQTD.js";
|
|
82
81
|
import {
|
|
83
82
|
buildQueryCacheKey,
|
|
84
83
|
cacheGet,
|
|
@@ -94,14 +93,14 @@ import {
|
|
|
94
93
|
writeAdsJson,
|
|
95
94
|
writeAdsOutput,
|
|
96
95
|
writeJsonEnvelope
|
|
97
|
-
} from "./chunk-
|
|
96
|
+
} from "./chunk-VVQLRFCI.js";
|
|
98
97
|
import {
|
|
99
98
|
ApiError,
|
|
100
99
|
apiGet,
|
|
101
100
|
apiGetWithHeaders,
|
|
102
101
|
apiPost,
|
|
103
102
|
validateConvexId
|
|
104
|
-
} from "./chunk-
|
|
103
|
+
} from "./chunk-MZQC4LII.js";
|
|
105
104
|
import {
|
|
106
105
|
installExitRecord,
|
|
107
106
|
installStreamTaps,
|
|
@@ -18925,7 +18924,7 @@ function handleLinkedinError(err) {
|
|
|
18925
18924
|
explanation: explanation.message,
|
|
18926
18925
|
correctedCommand: explanation.correctedCommand
|
|
18927
18926
|
},
|
|
18928
|
-
retryable: err.code === "RATE_LIMITED" || err.code === "INTERNAL_ERROR"
|
|
18927
|
+
retryable: err.code === "RATE_LIMITED" || err.code === "UNAVAILABLE" || err.code === "INTERNAL_ERROR"
|
|
18929
18928
|
}
|
|
18930
18929
|
};
|
|
18931
18930
|
writeAdsJson(envelope);
|
|
@@ -25464,10 +25463,10 @@ Example: baker ads meta ads retag 120252158038400247 --url-tags "utm_source={{si
|
|
|
25464
25463
|
}
|
|
25465
25464
|
},
|
|
25466
25465
|
run: async ({ args: args5 }) => {
|
|
25467
|
-
const { apiPost: apiPost2 } = await import("./client-
|
|
25466
|
+
const { apiPost: apiPost2 } = await import("./client-3DGBQ2H3.js");
|
|
25468
25467
|
const { requireChatId: requireChatId2 } = await import("./env-BSG7TY5Y.js");
|
|
25469
|
-
const { writeJsonEnvelope: writeJsonEnvelope2 } = await import("./output-
|
|
25470
|
-
const { handleMetaError: handleMetaError2 } = await import("./shared-
|
|
25468
|
+
const { writeJsonEnvelope: writeJsonEnvelope2 } = await import("./output-OSSBUGT7.js");
|
|
25469
|
+
const { handleMetaError: handleMetaError2 } = await import("./shared-MSI4UMZ3.js");
|
|
25471
25470
|
try {
|
|
25472
25471
|
const accountId = bareAccountId2(args5);
|
|
25473
25472
|
const urlTags = typeof args5["url-tags"] === "string" ? args5["url-tags"] : void 0;
|
|
@@ -25612,10 +25611,10 @@ function duplicateCommand2(entity, label) {
|
|
|
25612
25611
|
replace: { type: "boolean", description: "Pause the original once the copy publishes" }
|
|
25613
25612
|
},
|
|
25614
25613
|
run: async ({ args: args5 }) => {
|
|
25615
|
-
const { apiPost: apiPost2 } = await import("./client-
|
|
25614
|
+
const { apiPost: apiPost2 } = await import("./client-3DGBQ2H3.js");
|
|
25616
25615
|
const { requireChatId: requireChatId2 } = await import("./env-BSG7TY5Y.js");
|
|
25617
|
-
const { writeJsonEnvelope: writeJsonEnvelope2 } = await import("./output-
|
|
25618
|
-
const { handleMetaError: handleMetaError2 } = await import("./shared-
|
|
25616
|
+
const { writeJsonEnvelope: writeJsonEnvelope2 } = await import("./output-OSSBUGT7.js");
|
|
25617
|
+
const { handleMetaError: handleMetaError2 } = await import("./shared-MSI4UMZ3.js");
|
|
25619
25618
|
try {
|
|
25620
25619
|
const accountId = bareAccountId2(args5);
|
|
25621
25620
|
const chatId = requireChatId2();
|
|
@@ -35896,7 +35895,7 @@ function rewriteTarget(value, workspaceRoot) {
|
|
|
35896
35895
|
}
|
|
35897
35896
|
return null;
|
|
35898
35897
|
}
|
|
35899
|
-
function
|
|
35898
|
+
function normalizeAbsoluteCanvasPaths(canvas, canvasDir, workspaceRoot) {
|
|
35900
35899
|
if (!canvas || typeof canvas !== "object") return { canvas, rewrites: [] };
|
|
35901
35900
|
const c = canvas;
|
|
35902
35901
|
if (!Array.isArray(c.nodes)) return { canvas, rewrites: [] };
|
|
@@ -35909,10 +35908,6 @@ function normalizeCanvasPaths(canvas, canvasDir, workspaceRoot) {
|
|
|
35909
35908
|
const params = n.params;
|
|
35910
35909
|
if (!params || typeof params !== "object") return node;
|
|
35911
35910
|
const nodeId = typeof n.id === "string" ? n.id : "?";
|
|
35912
|
-
if (n.type === "ingest" && params.source === "path" && looksLikeHttpUrl(params.path)) {
|
|
35913
|
-
rewrites.push({ nodeId, param: "source", from: "path", to: "url" });
|
|
35914
|
-
return { ...node, params: coercePathUrlToUrl(params) };
|
|
35915
|
-
}
|
|
35916
35911
|
const key = n.type === "ingest" && params.source === "path" ? "path" : n.type === "hyperframe_render" ? "composition" : null;
|
|
35917
35912
|
if (!key) return node;
|
|
35918
35913
|
const target = rewriteTarget(params[key], root);
|
|
@@ -35923,11 +35918,11 @@ function normalizeCanvasPaths(canvas, canvasDir, workspaceRoot) {
|
|
|
35923
35918
|
});
|
|
35924
35919
|
return rewrites.length === 0 ? { canvas, rewrites } : { canvas: { ...canvas, nodes }, rewrites };
|
|
35925
35920
|
}
|
|
35926
|
-
async function
|
|
35921
|
+
async function healAbsoluteCanvasPaths(filePath, canvas) {
|
|
35927
35922
|
const canvasDir = path5.dirname(filePath);
|
|
35928
35923
|
const workspaceRoot = findWorkspaceRoot(canvasDir);
|
|
35929
35924
|
if (!workspaceRoot) return { canvas, rewrites: [], text: null };
|
|
35930
|
-
const normalized =
|
|
35925
|
+
const normalized = normalizeAbsoluteCanvasPaths(canvas, canvasDir, workspaceRoot);
|
|
35931
35926
|
if (normalized.rewrites.length === 0) return { ...normalized, text: null };
|
|
35932
35927
|
const text2 = `${JSON.stringify(normalized.canvas, null, 2)}
|
|
35933
35928
|
`;
|
|
@@ -40869,12 +40864,12 @@ async function executeCanvasRun(opts) {
|
|
|
40869
40864
|
}
|
|
40870
40865
|
const attemptedSlug = creativeSlugFromCanvasPath(filePath);
|
|
40871
40866
|
if (attemptedSlug) await clearCreativeDirty(attemptedSlug);
|
|
40872
|
-
const healed = await
|
|
40867
|
+
const healed = await healAbsoluteCanvasPaths(filePath, parsed);
|
|
40873
40868
|
parsed = healed.canvas;
|
|
40874
40869
|
const canvasText = healed.text ?? raw;
|
|
40875
40870
|
if (healed.rewrites.length > 0) {
|
|
40876
40871
|
process.stderr.write(
|
|
40877
|
-
`[canvas] rewrote ${healed.rewrites.length} asset
|
|
40872
|
+
`[canvas] rewrote ${healed.rewrites.length} absolute asset path(s) to canvas-relative \u2014 they resolve only in this workspace:
|
|
40878
40873
|
${describeRewrites(healed.rewrites)}
|
|
40879
40874
|
`
|
|
40880
40875
|
);
|
|
@@ -44467,7 +44462,7 @@ var validateCommand = defineCommand118({
|
|
|
44467
44462
|
`);
|
|
44468
44463
|
process.exit(2);
|
|
44469
44464
|
}
|
|
44470
|
-
const healed = await
|
|
44465
|
+
const healed = await healAbsoluteCanvasPaths(filePath, parsed);
|
|
44471
44466
|
parsed = healed.canvas;
|
|
44472
44467
|
parsed = resolveRelativeCanvasPaths(parsed, path28.dirname(filePath));
|
|
44473
44468
|
let styleProjection = "not_applicable";
|
|
@@ -49602,7 +49597,7 @@ function loadJsonPayloads(args5) {
|
|
|
49602
49597
|
}
|
|
49603
49598
|
return parsed.map((entry, index) => asObject(entry, "json", index));
|
|
49604
49599
|
}
|
|
49605
|
-
var RETRYABLE_CODES = /* @__PURE__ */ new Set(["RATE_LIMITED", "INTERNAL_ERROR", "NETWORK_ERROR", "TIMEOUT"]);
|
|
49600
|
+
var RETRYABLE_CODES = /* @__PURE__ */ new Set(["RATE_LIMITED", "UNAVAILABLE", "INTERNAL_ERROR", "NETWORK_ERROR", "TIMEOUT"]);
|
|
49606
49601
|
function handleError5(err) {
|
|
49607
49602
|
if (err instanceof ApiError) {
|
|
49608
49603
|
const readOnly = err.code === "FORBIDDEN" && /read the property but not change it/i.test(err.message);
|
|
@@ -52854,6 +52849,22 @@ function parseAutoIngest(raw, max) {
|
|
|
52854
52849
|
return { ok: true, value };
|
|
52855
52850
|
}
|
|
52856
52851
|
|
|
52852
|
+
// src/commands/images/extractErrors.ts
|
|
52853
|
+
var RETRYABLE = /* @__PURE__ */ new Set(["UNAVAILABLE", "RATE_LIMITED", "TIMEOUT", "NETWORK_ERROR"]);
|
|
52854
|
+
function extractErrorEnvelope(err, url) {
|
|
52855
|
+
const retryable = RETRYABLE.has(err.code);
|
|
52856
|
+
const fallback = `use \`baker images screenshot ${url}\` to capture the page as one image, or \`baker images logo <domain>\` for the brand's mark, and carry on with the rest of the job.`;
|
|
52857
|
+
return {
|
|
52858
|
+
ok: false,
|
|
52859
|
+
error: {
|
|
52860
|
+
code: err.code,
|
|
52861
|
+
message: err.message,
|
|
52862
|
+
retryable,
|
|
52863
|
+
fix: retryable ? { action: "retry_once", explanation: `Run the same command once more. If it fails again, ${fallback}` } : { action: "use_fallback", explanation: `Retrying the same address will fail the same way \u2014 ${fallback}` }
|
|
52864
|
+
}
|
|
52865
|
+
};
|
|
52866
|
+
}
|
|
52867
|
+
|
|
52857
52868
|
// src/commands/images/extract.ts
|
|
52858
52869
|
registerSchema({
|
|
52859
52870
|
command: "images.extract",
|
|
@@ -52920,7 +52931,7 @@ var extractCommand = defineCommand149({
|
|
|
52920
52931
|
writeJson({ ok: true, data });
|
|
52921
52932
|
} catch (err) {
|
|
52922
52933
|
if (err instanceof ApiError) {
|
|
52923
|
-
writeJson(
|
|
52934
|
+
writeJson(extractErrorEnvelope(err, String(args5.url)));
|
|
52924
52935
|
process.exit(1);
|
|
52925
52936
|
}
|
|
52926
52937
|
writeJson({ ok: false, error: { code: "INTERNAL_ERROR", message: "Unexpected error" } });
|
|
@@ -64288,7 +64299,7 @@ function loadJsonArg2(args5, flag = "json") {
|
|
|
64288
64299
|
return failValidation5(`--${flag} is not valid JSON`);
|
|
64289
64300
|
}
|
|
64290
64301
|
}
|
|
64291
|
-
var RETRYABLE_CODES2 = /* @__PURE__ */ new Set(["RATE_LIMITED", "INTERNAL_ERROR", "NETWORK_ERROR", "TIMEOUT"]);
|
|
64302
|
+
var RETRYABLE_CODES2 = /* @__PURE__ */ new Set(["RATE_LIMITED", "UNAVAILABLE", "INTERNAL_ERROR", "NETWORK_ERROR", "TIMEOUT"]);
|
|
64292
64303
|
function handleError8(err) {
|
|
64293
64304
|
if (err instanceof ApiError) {
|
|
64294
64305
|
writeJsonEnvelope({
|