videobook 0.2.4 → 0.3.0
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 +38 -0
- package/README.md +6 -2
- package/dist/archive-b2.js +1 -1
- package/dist/cli/args.js +42 -3
- package/dist/cli/config-command.js +17 -1
- package/dist/cli/doctor-command.js +15 -0
- package/dist/cli/har-command.js +86 -0
- package/dist/cli/main.js +13 -1
- package/dist/command-reference.js +5 -1
- package/dist/content-store.js +2 -1
- package/dist/diagnostics.js +1 -1
- package/dist/env-secrets.js +1 -0
- package/dist/generation-har.js +106 -0
- package/dist/har-capture.js +142 -0
- package/dist/har-export.js +238 -0
- package/dist/har-index.js +98 -0
- package/dist/har-retention.js +73 -0
- package/dist/har-settings.js +27 -0
- package/dist/jobs/_alibaba-upload.js +2 -4
- package/dist/jobs/_fal-upload.js +2 -4
- package/dist/jobs/_image-edit-adapter.js +1 -1
- package/dist/jobs/_kie-upload.js +2 -4
- package/dist/jobs/complete-provider-task.js +5 -2
- package/dist/jobs/generate-media.js +9 -1
- package/dist/jobs/image-subjects.js +1 -1
- package/dist/jobs/kie-paygate.js +10 -3
- package/dist/jobs/run-notebook.js +4 -1
- package/dist/jobs/xai-paygate.js +5 -2
- package/dist/media/adapters/kie.js +2 -1
- package/dist/media/catalog.js +20 -0
- package/dist/media/cost.js +1 -1
- package/dist/package-version.js +18 -0
- package/dist/posts/dropbox-auth.js +1 -1
- package/dist/posts/post-metrics.js +2 -1
- package/dist/provider-usage.js +1 -18
- package/dist/queue.js +12 -1
- package/dist/serve.js +2 -0
- package/dist/tools/api-status.js +1 -1
- package/dist/tools/generate-media.js +3 -0
- package/dist/tools/generation-payload.js +17 -2
- package/dist/tools/get-generation-har.js +43 -0
- package/dist/tools/index.js +4 -0
- package/dist/tools/list-cell-generations.js +19 -0
- package/dist/tools/reconcile-project-work.js +18 -12
- package/dist/tools/set-har-capture.js +30 -0
- package/dist/upload.js +2 -1
- package/dist/usage-providers.js +48 -10
- package/dist/workspace-action-groups.js +2 -2
- package/dist/x/account.js +1 -1
- package/dist/x/authenticated-handle.js +1 -1
- package/dist/x/oauth-flow.js +1 -1
- package/dist/x/post-to-x.js +2 -1
- package/dist/x/x-tokens.js +2 -1
- package/package.json +15 -14
- package/ui/assets/{alive-captions-Bp6dxosy.js → alive-captions-Cj8C1tCN.js} +1 -1
- package/ui/assets/{confirm-bodies-Ds1HtaGc.js → confirm-bodies-BxWXwDf0.js} +1 -1
- package/ui/assets/{fullscreen-BCeVHjvY.js → fullscreen-DyEncSdm.js} +1 -1
- package/ui/assets/{goal-ui-DbAGwgMN.js → goal-ui-BdFeoSWh.js} +2 -2
- package/ui/assets/{index-D1d-F1y9.js → index-QZ8lbemN.js} +9 -9
- package/ui/assets/inspector-BC6xWkJU.js +1 -0
- package/ui/assets/{jobs-ui-C1cbw-xG.js → jobs-ui-BudI4w0r.js} +1 -1
- package/ui/assets/{music-video-CWtxPlxq.js → music-video-CT4C55ok.js} +1 -1
- package/ui/assets/ui-mq0uIS5P.css +1 -0
- package/ui/assets/ui-z5r-p0K9.js +2 -0
- package/ui/assets/{vendor-BFupLz37.js → vendor-D8XcMav4.js} +2 -2
- package/ui/assets/{vendor-flow-CKiuzaeM.js → vendor-flow-nqTFQmTh.js} +1 -1
- package/ui/assets/vendor-mcp-D_C3EZPv.js +60 -0
- package/ui/assets/videobook-CsW453sb.js +21 -0
- package/ui/index.html +12 -12
- package/ui/assets/inspector-BJTC0luH.js +0 -1
- package/ui/assets/ui-CXDi5Omq.js +0 -2
- package/ui/assets/ui-Oirr9VOf.css +0 -1
- package/ui/assets/vendor-mcp-DQEeVZH0.js +0 -67
- package/ui/assets/videobook-J-j9bSjM.js +0 -20
package/dist/media/catalog.js
CHANGED
|
@@ -203,6 +203,26 @@ export const MODEL_ENTRIES = [
|
|
|
203
203
|
{ id: "fal:alibaba/wan-3.0-prime/text-to-video", media: "video", operations: ["text_to_video"], hidden: true },
|
|
204
204
|
{ id: "fal:alibaba/wan-3.0-prime/image-to-video", media: "video", operations: ["image_to_video"], hidden: true },
|
|
205
205
|
{ id: "fal:alibaba/wan-3.0-prime/reference-to-video", media: "video", operations: ["reference_to_video"], hidden: true },
|
|
206
|
+
{ id: "fal:kling/v4/text-to-video", media: "video", operations: ["text_to_video"], dialect: "seedance" },
|
|
207
|
+
{ id: "fal:kling/v4/image-to-video", media: "video", operations: ["image_to_video"], dialect: "seedance", modes: FRAME_MODES },
|
|
208
|
+
{ id: "fal:kling/v4/reference-to-video", media: "video", operations: ["reference_to_video"], dialect: "seedance" },
|
|
209
|
+
{ id: "fal:kling/v4/video-to-video/edit", media: "video", operations: ["edit_video"], dialect: "seedance" },
|
|
210
|
+
{ id: "fal:heygen/heygen-video/text-to-video", media: "video", operations: ["text_to_video"], dialect: "natural" },
|
|
211
|
+
{ id: "fal:heygen/heygen-video/image-to-video", media: "video", operations: ["image_to_video"], dialect: "strip_frames", refs: { first_frame: "image_url" } },
|
|
212
|
+
{ id: "fal:heygen/heygen-video/reference-to-video", media: "video", operations: ["reference_to_video"], dialect: "natural", modes: [{ id: "r2v", label: "References", roles: ["image", "video", "audio"], requireAny: ["image", "video"] }] },
|
|
213
|
+
...[3, 4].map((version) => ({
|
|
214
|
+
id: `fal:fal-ai/heygen/avatar${version}/digital-twin`,
|
|
215
|
+
media: "video",
|
|
216
|
+
operations: ["text_to_video"],
|
|
217
|
+
dialect: "plain",
|
|
218
|
+
prompt: "voice.prompt",
|
|
219
|
+
defaults: { character: { avatar: "Abigail Office Front" }, voice: { voice: "Ivy" } },
|
|
220
|
+
aliases: { avatar: "character.avatar", avatar_style: "character.avatar_style", voice: "voice.voice", voice_id: "voice.voice_id", speed: "voice.speed" },
|
|
221
|
+
})),
|
|
222
|
+
{ id: "fal:fal-ai/heygen/avatar5/digital-twin", media: "video", operations: ["text_to_video"], dialect: "plain" },
|
|
223
|
+
{ id: "fal:fal-ai/heygen/avatar4/image-to-video", media: "video", operations: ["image_to_video"], dialect: "plain", defaults: { voice: "Ivy" } },
|
|
224
|
+
{ id: "fal:fal-ai/heygen/v2/video-agent", media: "video", operations: ["text_to_video"], dialect: "plain" },
|
|
225
|
+
{ id: "fal:fal-ai/heygen/v3/video-agent", media: "video", operations: ["text_to_video"], dialect: "plain" },
|
|
206
226
|
{ id: "fal:blackforestlabs/flux-3-max/text-to-video", media: "video", operations: ["text_to_video"], title: "FLUX 3 Max", defaults: FLUX_3_MAX_DEFAULTS },
|
|
207
227
|
{ id: "fal:blackforestlabs/flux-3-max/image-to-video", media: "video", operations: ["image_to_video"], title: "FLUX 3 Max Image", refs: { first_frame: "image_url" }, modes: FRAME_MODES, defaults: FLUX_3_MAX_DEFAULTS },
|
|
208
228
|
{ id: "fal:blackforestlabs/flux-3-max/keyframes-to-video", media: "video", operations: ["image_to_video"], title: "FLUX 3 Max Keyframes", refs: { image: "keyframes" }, keyframeFps: 24, defaults: FLUX_3_MAX_DEFAULTS },
|
package/dist/media/cost.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { createFal } from "
|
|
1
|
+
import { createFal } from "../usage-providers.js";
|
|
2
2
|
import { computeEstimate, createFalEstimateCache, resolveFalDynamicEstimate, } from "@apicity/cost";
|
|
3
3
|
import { buildWireRequest } from "./request.js";
|
|
4
4
|
const FAL_ESTIMATE_TTL_MS = 5 * 60 * 1000;
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { readFileSync } from "node:fs";
|
|
2
|
+
import { dirname, join } from "node:path";
|
|
3
|
+
import { fileURLToPath } from "node:url";
|
|
4
|
+
export function installedPackageVersion(name) {
|
|
5
|
+
let dir = dirname(fileURLToPath(import.meta.resolve(name)));
|
|
6
|
+
while (dir !== dirname(dir)) {
|
|
7
|
+
try {
|
|
8
|
+
const manifest = JSON.parse(readFileSync(join(dir, "package.json"), "utf8"));
|
|
9
|
+
if (manifest.name === name && manifest.version)
|
|
10
|
+
return manifest.version;
|
|
11
|
+
}
|
|
12
|
+
catch {
|
|
13
|
+
void 0;
|
|
14
|
+
}
|
|
15
|
+
dir = dirname(dir);
|
|
16
|
+
}
|
|
17
|
+
return "unknown";
|
|
18
|
+
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { createHash, randomBytes } from "node:crypto";
|
|
2
2
|
import { createServer } from "node:http";
|
|
3
|
-
import { createDropbox } from "
|
|
3
|
+
import { createDropbox } from "../usage-providers.js";
|
|
4
4
|
import { appState } from "../app-state.js";
|
|
5
5
|
const KEY = "dropbox.oauth.tokens";
|
|
6
6
|
const REDIRECT = "http://127.0.0.1:8766/callback";
|
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import { createX
|
|
1
|
+
import { createX } from "../usage-providers.js";
|
|
2
|
+
import { XError } from "@apicity/x";
|
|
2
3
|
import { v7 as uuidv7 } from "uuid";
|
|
3
4
|
import { getValidAccessToken } from "../x/x-tokens.js";
|
|
4
5
|
import { listBookEngines, queue } from "../queue.js";
|
package/dist/provider-usage.js
CHANGED
|
@@ -1,26 +1,9 @@
|
|
|
1
1
|
import { randomUUID } from "node:crypto";
|
|
2
|
-
import { readFileSync } from "node:fs";
|
|
3
|
-
import { dirname, join } from "node:path";
|
|
4
|
-
import { fileURLToPath } from "node:url";
|
|
5
2
|
import { createCost, PRICING, PRICING_AS_OF } from "@apicity/cost";
|
|
6
3
|
import { modelEntry } from "./media/catalog.js";
|
|
7
4
|
import { estimateFalRequest } from "./media/cost.js";
|
|
8
5
|
import { currentBookContext } from "./book-context.js";
|
|
9
|
-
|
|
10
|
-
let dir = dirname(fileURLToPath(import.meta.resolve(name)));
|
|
11
|
-
while (dir !== dirname(dir)) {
|
|
12
|
-
try {
|
|
13
|
-
const manifest = JSON.parse(readFileSync(join(dir, "package.json"), "utf8"));
|
|
14
|
-
if (manifest.name === name && manifest.version)
|
|
15
|
-
return manifest.version;
|
|
16
|
-
}
|
|
17
|
-
catch {
|
|
18
|
-
void 0;
|
|
19
|
-
}
|
|
20
|
-
dir = dirname(dir);
|
|
21
|
-
}
|
|
22
|
-
return "unknown";
|
|
23
|
-
}
|
|
6
|
+
import { installedPackageVersion } from "./package-version.js";
|
|
24
7
|
const COST_PACKAGE_VERSION = installedPackageVersion("@apicity/cost");
|
|
25
8
|
const incomplete = new WeakSet();
|
|
26
9
|
const cost = createCost();
|
package/dist/queue.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { sweepHarCaptures } from "./har-retention.js";
|
|
1
2
|
import { appendDiagnostic, registerDiagnosticName } from "./diagnostics.js";
|
|
2
3
|
import { createEngine, } from "videobook-engine";
|
|
3
4
|
import { activeBookEngine, withBookContext } from "./book-context.js";
|
|
@@ -54,11 +55,13 @@ export function isProviderPollDriverActive(bookId) {
|
|
|
54
55
|
const info = runnerByBook.get(bookId);
|
|
55
56
|
return info?.providerPollTimer != null;
|
|
56
57
|
}
|
|
58
|
+
let harRetentionTimer = null;
|
|
57
59
|
async function tickProviderPoll(info) {
|
|
58
60
|
if (info.providerPollInFlight || info.pollAbort.signal.aborted)
|
|
59
61
|
return;
|
|
60
62
|
info.providerPollInFlight = true;
|
|
61
63
|
try {
|
|
64
|
+
await sweepHarCaptures();
|
|
62
65
|
if (providerPollWorkHook) {
|
|
63
66
|
await providerPollWorkHook(info.pollAbort.signal);
|
|
64
67
|
}
|
|
@@ -258,7 +261,10 @@ function makeWrappedResolver(bookId, engine) {
|
|
|
258
261
|
const handler = handlers.get(type);
|
|
259
262
|
if (!handler)
|
|
260
263
|
return null;
|
|
261
|
-
return async (job, queueSignal) => withBookContext({ bookId, engine, operation: type, ...(job.artifactId ? { artifactId: job.artifactId } : {})
|
|
264
|
+
return async (job, queueSignal) => withBookContext({ bookId, engine, operation: type, ...(job.artifactId ? { artifactId: job.artifactId } : {}),
|
|
265
|
+
...(typeof job.payload.runId === "string" ? { runId: job.payload.runId } : {}),
|
|
266
|
+
...(typeof job.payload.generationId === "string" ? { attemptId: job.payload.generationId } : {}),
|
|
267
|
+
...(typeof job.payload.model === "string" ? { model: job.payload.model } : {}) }, () => runWithJobDeadline(type, async (signal) => {
|
|
262
268
|
const jobSignal = queueSignal
|
|
263
269
|
? AbortSignal.any([signal, queueSignal])
|
|
264
270
|
: signal;
|
|
@@ -579,6 +585,8 @@ export const queue = {
|
|
|
579
585
|
};
|
|
580
586
|
},
|
|
581
587
|
async start() {
|
|
588
|
+
await sweepHarCaptures(true);
|
|
589
|
+
harRetentionTimer ??= setInterval(() => { void sweepHarCaptures(); }, 60_000).unref();
|
|
582
590
|
await ensureJobHandlersLoaded();
|
|
583
591
|
await reconcileQueueOnStartup();
|
|
584
592
|
},
|
|
@@ -586,6 +594,9 @@ export const queue = {
|
|
|
586
594
|
await ensureRunner(bookId);
|
|
587
595
|
},
|
|
588
596
|
async stop() {
|
|
597
|
+
if (harRetentionTimer)
|
|
598
|
+
clearInterval(harRetentionTimer);
|
|
599
|
+
harRetentionTimer = null;
|
|
589
600
|
if (stoppingQueue)
|
|
590
601
|
return stoppingQueue;
|
|
591
602
|
const pending = stopQueueRunners();
|
package/dist/serve.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { flushHarCaptures } from "./har-capture.js";
|
|
1
2
|
import { once } from "node:events";
|
|
2
3
|
import { setTimeout as delay } from "node:timers/promises";
|
|
3
4
|
import { stopAceServer } from "./acestep/process.js";
|
|
@@ -22,6 +23,7 @@ async function shutdownServices(services) {
|
|
|
22
23
|
await services.voiceSessions.close();
|
|
23
24
|
await services.mcpHandler.close();
|
|
24
25
|
await queue.stop();
|
|
26
|
+
await flushHarCaptures();
|
|
25
27
|
await stopAceServer();
|
|
26
28
|
await bookManager.close();
|
|
27
29
|
}
|
package/dist/tools/api-status.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { currentBookContext } from "../book-context.js";
|
|
1
2
|
import { z } from "zod";
|
|
2
3
|
import { queue } from "../queue.js";
|
|
3
4
|
import { generationLabel } from "../artifact-label.js";
|
|
@@ -92,6 +93,8 @@ async function enqueueGeneration(engine, model, args, refs) {
|
|
|
92
93
|
const jobId = `generate-${model.media}-${artifactId}-${Date.now()}`;
|
|
93
94
|
const job = {
|
|
94
95
|
type: "generate_media",
|
|
96
|
+
runId: currentBookContext()?.runId,
|
|
97
|
+
generationId: currentBookContext()?.attemptId,
|
|
95
98
|
model: model.id,
|
|
96
99
|
bookId: String(args.book_id),
|
|
97
100
|
assetId: artifactId,
|
|
@@ -1,20 +1,34 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
|
+
import { captureKeys, generationHarStatus } from "../generation-har.js";
|
|
3
|
+
import { readHarSettings } from "../har-settings.js";
|
|
4
|
+
import { logger } from "../logger.js";
|
|
2
5
|
import { readProviderTaskStatus } from "../provider-task-status.js";
|
|
6
|
+
async function inspectionHar(engine, assetId) {
|
|
7
|
+
try {
|
|
8
|
+
const { mode } = await readHarSettings();
|
|
9
|
+
return await generationHarStatus(await captureKeys(engine, [assetId]), mode);
|
|
10
|
+
}
|
|
11
|
+
catch (error) {
|
|
12
|
+
logger.warn("har-capture-failed", { error });
|
|
13
|
+
return null;
|
|
14
|
+
}
|
|
15
|
+
}
|
|
3
16
|
export function generationPayloadDef(engine) {
|
|
4
17
|
return {
|
|
5
18
|
name: "inspect_generation_request",
|
|
6
|
-
description: "Read the persisted provider request payload, task details, status-page information,
|
|
19
|
+
description: "Read the persisted provider request payload, task details, status-page information, failure info and HAR capture status for a single asset. Used by the generating-cell inspector to show what was sent to the provider.",
|
|
7
20
|
schema: {
|
|
8
21
|
book_id: z.string().describe("Book id"),
|
|
9
22
|
asset_id: z.string().describe("Artifact id"),
|
|
10
23
|
},
|
|
11
24
|
handler: async (args) => {
|
|
12
25
|
const { book_id: _book_id, asset_id } = args;
|
|
13
|
-
const [payloadRes, taskRes, errorRes, providerTask] = await Promise.all([
|
|
26
|
+
const [payloadRes, taskRes, errorRes, providerTask, har] = await Promise.all([
|
|
14
27
|
engine.metadata.artifacts.read(asset_id, "generation-payload"),
|
|
15
28
|
engine.jobs.pending.read(asset_id),
|
|
16
29
|
engine.jobs.failures.read(asset_id),
|
|
17
30
|
readProviderTaskStatus(engine, asset_id),
|
|
31
|
+
inspectionHar(engine, asset_id),
|
|
18
32
|
]);
|
|
19
33
|
const task = taskRes.ok ? taskRes.value : null;
|
|
20
34
|
const errorRecord = errorRes.ok ? errorRes.value : null;
|
|
@@ -32,6 +46,7 @@ export function generationPayloadDef(engine) {
|
|
|
32
46
|
error,
|
|
33
47
|
taskType: task ? task.taskType : null,
|
|
34
48
|
providerTask,
|
|
49
|
+
har,
|
|
35
50
|
task: task
|
|
36
51
|
? {
|
|
37
52
|
taskId: task.taskId,
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import { generationCaptureKeys, generationHar } from "../generation-har.js";
|
|
3
|
+
import { readHarSettings } from "../har-settings.js";
|
|
4
|
+
function errorResult(message) {
|
|
5
|
+
return { isError: true, content: [{ type: "text", text: message }] };
|
|
6
|
+
}
|
|
7
|
+
export function getGenerationHarDef(engine) {
|
|
8
|
+
return {
|
|
9
|
+
name: "get_generation_har",
|
|
10
|
+
description: "Return the scrubbed provider HAR capture of one generation attempt, by attempt id or asset id. Several captured calls are combined into one HAR with a page per call. Needs provider capture (set_har_capture).",
|
|
11
|
+
schema: {
|
|
12
|
+
book_id: z.string().describe("Book id"),
|
|
13
|
+
generation_id: z.string().min(1).describe("Generation attempt id or its asset id"),
|
|
14
|
+
},
|
|
15
|
+
annotations: { readOnlyHint: true, idempotentHint: true, openWorldHint: false },
|
|
16
|
+
handler: async (args) => {
|
|
17
|
+
const { generation_id } = args;
|
|
18
|
+
try {
|
|
19
|
+
const result = await generationHar(await generationCaptureKeys(engine, generation_id), generation_id);
|
|
20
|
+
if (!result) {
|
|
21
|
+
const { mode } = await readHarSettings();
|
|
22
|
+
return errorResult(mode === "off"
|
|
23
|
+
? "No HAR capture for this generation. Provider capture is off; turn it on in Settings > Diagnostics or with set_har_capture."
|
|
24
|
+
: "No HAR capture for this generation.");
|
|
25
|
+
}
|
|
26
|
+
return {
|
|
27
|
+
content: [{
|
|
28
|
+
type: "text",
|
|
29
|
+
text: JSON.stringify({
|
|
30
|
+
generationId: generation_id,
|
|
31
|
+
filename: `videobook-generation-${generation_id.replace(/[^A-Za-z0-9._-]+/gu, "-")}.har`,
|
|
32
|
+
captures: result.captures,
|
|
33
|
+
har: result.har,
|
|
34
|
+
}),
|
|
35
|
+
}],
|
|
36
|
+
};
|
|
37
|
+
}
|
|
38
|
+
catch (error) {
|
|
39
|
+
return errorResult(error instanceof Error ? error.message : String(error));
|
|
40
|
+
}
|
|
41
|
+
},
|
|
42
|
+
};
|
|
43
|
+
}
|
package/dist/tools/index.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { setHarCaptureDef } from "./set-har-capture.js";
|
|
1
2
|
import { coldOpenDefs } from "./cold-open.js";
|
|
2
3
|
import { withBookContext } from "../book-context.js";
|
|
3
4
|
import { bookManager } from "../engine.js";
|
|
@@ -78,6 +79,7 @@ import { listAssetsDef } from "./list-assets.js";
|
|
|
78
79
|
import { listCellGenerationsDef } from "./list-cell-generations.js";
|
|
79
80
|
import { waitForAssetReadyDef } from "./wait-for-asset-ready.js";
|
|
80
81
|
import { generationPayloadDef } from "./generation-payload.js";
|
|
82
|
+
import { getGenerationHarDef } from "./get-generation-har.js";
|
|
81
83
|
import { concatImagesDef } from "./concat-images.js";
|
|
82
84
|
import { chatDef } from "./chat.js";
|
|
83
85
|
import { chainDef } from "./chain.js";
|
|
@@ -250,6 +252,7 @@ export function getAllToolDefs(engine, books = bookManager) {
|
|
|
250
252
|
listChatModelsDef(),
|
|
251
253
|
providerKeyStatusDef(),
|
|
252
254
|
setEnvSecretDef(),
|
|
255
|
+
setHarCaptureDef(),
|
|
253
256
|
listElevenLabsVoicesDef(),
|
|
254
257
|
estimateCostDef(engine, books),
|
|
255
258
|
bookUsageDef(engine),
|
|
@@ -285,6 +288,7 @@ export function getAllToolDefs(engine, books = bookManager) {
|
|
|
285
288
|
listCellGenerationsDef(engine),
|
|
286
289
|
waitForAssetReadyDef(engine),
|
|
287
290
|
generationPayloadDef(engine),
|
|
291
|
+
getGenerationHarDef(engine),
|
|
288
292
|
concatImagesDef(engine),
|
|
289
293
|
chatDef(engine),
|
|
290
294
|
chainDef(engine),
|
|
@@ -2,6 +2,20 @@ import { z } from "zod";
|
|
|
2
2
|
import { findGenerateAudioOutput, findGenerateImageOutput, findGenerateVideoOutput, listGenerateImageOutputs, listGenerateVideoOutputs, } from "../notebook-cell-outputs.js";
|
|
3
3
|
import { buildAssetList } from "./reconcile-project-work.js";
|
|
4
4
|
import { readProviderTaskStatus } from "../provider-task-status.js";
|
|
5
|
+
import { captureKeys, generationHarStatus } from "../generation-har.js";
|
|
6
|
+
import { readHarIndex } from "../har-index.js";
|
|
7
|
+
import { readHarSettings } from "../har-settings.js";
|
|
8
|
+
import { logger } from "../logger.js";
|
|
9
|
+
async function attemptHarLookup(engine) {
|
|
10
|
+
try {
|
|
11
|
+
const [{ mode }, rows] = await Promise.all([readHarSettings(), readHarIndex()]);
|
|
12
|
+
return { mode, status: async (ids) => generationHarStatus(await captureKeys(engine, ids), mode, rows) };
|
|
13
|
+
}
|
|
14
|
+
catch (error) {
|
|
15
|
+
logger.warn("har-capture-failed", { error });
|
|
16
|
+
return null;
|
|
17
|
+
}
|
|
18
|
+
}
|
|
5
19
|
function generationArtifactIds(generations) {
|
|
6
20
|
return [...new Set(generations.flatMap((row) => [
|
|
7
21
|
...(row.outputArtifactId ? [row.outputArtifactId] : []),
|
|
@@ -132,6 +146,7 @@ export function listCellGenerationsDef(engine) {
|
|
|
132
146
|
? generationsResult.value.filter((row) => !row.outputCellId || row.outputCellId === requestedResult.id)
|
|
133
147
|
: generationsResult.value;
|
|
134
148
|
const transitions = generationTransitions(engine, rows);
|
|
149
|
+
const harLookup = await attemptHarLookup(engine);
|
|
135
150
|
const assets = await buildAssetList(engine, book_id);
|
|
136
151
|
const assetById = new Map(assets.map((asset) => [asset.artifact_id, asset]));
|
|
137
152
|
const attempts = [];
|
|
@@ -180,6 +195,9 @@ export function listCellGenerationsDef(engine) {
|
|
|
180
195
|
...(include_payloads ? { payload } : {}),
|
|
181
196
|
commandPlan,
|
|
182
197
|
transitions: transitions.get(row.generationId) ?? [],
|
|
198
|
+
har: harLookup
|
|
199
|
+
? await harLookup.status([row.generationId, row.providerArtifactId, row.outputArtifactId])
|
|
200
|
+
: null,
|
|
183
201
|
createdAt: row.createdAt,
|
|
184
202
|
updatedAt: row.updatedAt,
|
|
185
203
|
});
|
|
@@ -205,6 +223,7 @@ export function listCellGenerationsDef(engine) {
|
|
|
205
223
|
latestStatus: rows[0]?.status ?? null,
|
|
206
224
|
artifactIds: generationArtifactIds(rows),
|
|
207
225
|
},
|
|
226
|
+
harMode: harLookup?.mode ?? null,
|
|
208
227
|
};
|
|
209
228
|
return {
|
|
210
229
|
content: [{
|
|
@@ -791,12 +791,18 @@ async function processMachGenTaskStatus(engine, task, status, result, bookId) {
|
|
|
791
791
|
});
|
|
792
792
|
result.completed.push({ assetId: task.artifactId, taskType: task.taskType, jobId });
|
|
793
793
|
}
|
|
794
|
+
function withProviderTaskContext(engine, bookId, task, operation) {
|
|
795
|
+
return withBookContext({ bookId, engine, operation: "poll_pending_provider_tasks", artifactId: task.artifactId,
|
|
796
|
+
providerTaskId: task.taskId, attemptId: typeof task.meta.attemptId === "string" ? task.meta.attemptId : task.artifactId,
|
|
797
|
+
runId: typeof task.meta.runId === "string" ? task.meta.runId : undefined,
|
|
798
|
+
model: typeof task.meta.model === "string" ? task.meta.model : undefined }, operation);
|
|
799
|
+
}
|
|
794
800
|
async function pollMachGenTasks(engine, tasks, result, bookId, baseURL) {
|
|
795
801
|
const machgenTasks = tasks.filter((task) => task.taskType === MEDIA_TASK_TYPES.machgen);
|
|
796
802
|
if (!getApiKey("MACHGEN_API_KEY") || machgenTasks.length === 0)
|
|
797
803
|
return;
|
|
798
804
|
const provider = machgenProvider(30_000, baseURL);
|
|
799
|
-
await Promise.all(machgenTasks.map(async (task) => {
|
|
805
|
+
await Promise.all(machgenTasks.map(async (task) => withProviderTaskContext(engine, bookId, task, async () => {
|
|
800
806
|
try {
|
|
801
807
|
await processMachGenTaskStatus(engine, task, await provider.api.v0.tasks(task.taskId), result, bookId);
|
|
802
808
|
}
|
|
@@ -807,7 +813,7 @@ async function pollMachGenTasks(engine, tasks, result, bookId, baseURL) {
|
|
|
807
813
|
if (machgenPollFailureIsFinal(error))
|
|
808
814
|
await failPendingTaskAndLog(engine, task.artifactId, task.taskId, { message: failMsg, prompt });
|
|
809
815
|
}
|
|
810
|
-
}));
|
|
816
|
+
})));
|
|
811
817
|
}
|
|
812
818
|
/**
|
|
813
819
|
* Poll provider-pending tasks: submit queued KIE rows, age out stale tasks,
|
|
@@ -887,7 +893,7 @@ export async function pollPendingProviderTasks(engine, book_id, options = {}) {
|
|
|
887
893
|
timeout: 30_000,
|
|
888
894
|
...(kieBaseURL ? { baseURL: kieBaseURL } : {}),
|
|
889
895
|
});
|
|
890
|
-
await Promise.all(kieTasks.map(async (task) => {
|
|
896
|
+
await Promise.all(kieTasks.map(async (task) => withProviderTaskContext(engine, book_id, task, async () => {
|
|
891
897
|
try {
|
|
892
898
|
const taskInfo = await provider.get.api.v1.jobs.recordInfo(task.taskId);
|
|
893
899
|
await processKieTaskResult(engine, task, taskInfo, result, book_id);
|
|
@@ -904,7 +910,7 @@ export async function pollPendingProviderTasks(engine, book_id, options = {}) {
|
|
|
904
910
|
}
|
|
905
911
|
}
|
|
906
912
|
}
|
|
907
|
-
}));
|
|
913
|
+
})));
|
|
908
914
|
}
|
|
909
915
|
// Poll KIE Veo tasks (separate namespace: kie.veo.get.api.v1.veo.recordInfo)
|
|
910
916
|
const veoTasks = pollableTasks.filter(isVeoPendingTask);
|
|
@@ -914,7 +920,7 @@ export async function pollPendingProviderTasks(engine, book_id, options = {}) {
|
|
|
914
920
|
timeout: 30_000,
|
|
915
921
|
...(kieBaseURL ? { baseURL: kieBaseURL } : {}),
|
|
916
922
|
});
|
|
917
|
-
await Promise.all(veoTasks.map(async (task) => {
|
|
923
|
+
await Promise.all(veoTasks.map(async (task) => withProviderTaskContext(engine, book_id, task, async () => {
|
|
918
924
|
const prompt = metaPrompt(task.meta);
|
|
919
925
|
try {
|
|
920
926
|
const recordInfo = await provider.veo.get.api.v1.veo.recordInfo(task.taskId);
|
|
@@ -973,13 +979,13 @@ export async function pollPendingProviderTasks(engine, book_id, options = {}) {
|
|
|
973
979
|
}
|
|
974
980
|
}
|
|
975
981
|
}
|
|
976
|
-
}));
|
|
982
|
+
})));
|
|
977
983
|
}
|
|
978
984
|
// Poll xAI tasks (only if API key is set)
|
|
979
985
|
const xaiTasks = pollableTasks.filter((t) => t.taskType.startsWith("xai_"));
|
|
980
986
|
if (process.env.XAI_API_KEY && xaiTasks.length > 0) {
|
|
981
987
|
const provider = createXai({ apiKey: process.env.XAI_API_KEY, timeout: 30_000 });
|
|
982
|
-
await Promise.all(xaiTasks.map(async (task) => {
|
|
988
|
+
await Promise.all(xaiTasks.map(async (task) => withProviderTaskContext(engine, book_id, task, async () => {
|
|
983
989
|
const prompt = metaPrompt(task.meta);
|
|
984
990
|
try {
|
|
985
991
|
const videoResult = await provider.get.v1.videos(task.taskId);
|
|
@@ -1029,13 +1035,13 @@ export async function pollPendingProviderTasks(engine, book_id, options = {}) {
|
|
|
1029
1035
|
}
|
|
1030
1036
|
}
|
|
1031
1037
|
}
|
|
1032
|
-
}));
|
|
1038
|
+
})));
|
|
1033
1039
|
}
|
|
1034
1040
|
// Poll Alibaba tasks (only if API key is set)
|
|
1035
1041
|
const alibabaTasks = pollableTasks.filter((t) => t.taskType.startsWith("alibaba_"));
|
|
1036
1042
|
if (process.env.DASHSCOPE_API_KEY && alibabaTasks.length > 0) {
|
|
1037
1043
|
const provider = createAlibaba({ apiKey: process.env.DASHSCOPE_API_KEY, timeout: 30_000 });
|
|
1038
|
-
await Promise.all(alibabaTasks.map(async (task) => {
|
|
1044
|
+
await Promise.all(alibabaTasks.map(async (task) => withProviderTaskContext(engine, book_id, task, async () => {
|
|
1039
1045
|
const prompt = metaPrompt(task.meta);
|
|
1040
1046
|
try {
|
|
1041
1047
|
const taskStatus = await provider.get.api.v1.tasks(task.taskId);
|
|
@@ -1105,7 +1111,7 @@ export async function pollPendingProviderTasks(engine, book_id, options = {}) {
|
|
|
1105
1111
|
}
|
|
1106
1112
|
}
|
|
1107
1113
|
}
|
|
1108
|
-
}));
|
|
1114
|
+
})));
|
|
1109
1115
|
}
|
|
1110
1116
|
// Poll FAL tasks (only if API key is set)
|
|
1111
1117
|
const falTasks = pollableTasks.filter((t) => t.taskType.startsWith("fal_"));
|
|
@@ -1116,7 +1122,7 @@ export async function pollPendingProviderTasks(engine, book_id, options = {}) {
|
|
|
1116
1122
|
// — e.g. `fal-ai/elevenlabs/speech-to-text/scribe-v2` — 405s. Strip back
|
|
1117
1123
|
// to `fal-ai/elevenlabs` for polling.
|
|
1118
1124
|
const falNamespace = (endpointId) => endpointId.split("/").slice(0, 2).join("/");
|
|
1119
|
-
await Promise.all(falTasks.map(async (task) => {
|
|
1125
|
+
await Promise.all(falTasks.map(async (task) => withProviderTaskContext(engine, book_id, task, async () => {
|
|
1120
1126
|
const prompt = metaPrompt(task.meta);
|
|
1121
1127
|
const endpointId = typeof task.meta?.falEndpointId === "string" ? task.meta.falEndpointId : null;
|
|
1122
1128
|
if (!endpointId) {
|
|
@@ -1179,7 +1185,7 @@ export async function pollPendingProviderTasks(engine, book_id, options = {}) {
|
|
|
1179
1185
|
}
|
|
1180
1186
|
}
|
|
1181
1187
|
}
|
|
1182
|
-
}));
|
|
1188
|
+
})));
|
|
1183
1189
|
}
|
|
1184
1190
|
await pollMachGenTasks(engine, pollableTasks, result, book_id, options.machgenBaseURL ?? providerEndpoints().machgenBaseURL);
|
|
1185
1191
|
for (const task of expiredTasks) {
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import { harSettingsSchema, readHarSettings, writeHarSettings, harDirectory } from "../har-settings.js";
|
|
3
|
+
import { appState } from "../app-state.js";
|
|
4
|
+
export function setHarCaptureDef(store = appState) {
|
|
5
|
+
return {
|
|
6
|
+
name: "set_har_capture",
|
|
7
|
+
description: "Read or change opt-in local provider HAR diagnostics. Captures retain prompts, media URLs, parameters, responses and errors; secrets, cookies and oversized media bytes are scrubbed. Omit mode to read settings. Environment override wins.",
|
|
8
|
+
schema: {
|
|
9
|
+
mode: harSettingsSchema.shape.mode.removeDefault().optional(),
|
|
10
|
+
maxAgeDays: harSettingsSchema.shape.maxAgeDays.removeDefault().optional(),
|
|
11
|
+
maxTotalMB: harSettingsSchema.shape.maxTotalMB.removeDefault().optional(),
|
|
12
|
+
},
|
|
13
|
+
handler: async (args) => {
|
|
14
|
+
try {
|
|
15
|
+
const changes = z.object({
|
|
16
|
+
mode: harSettingsSchema.shape.mode.removeDefault().optional(),
|
|
17
|
+
maxAgeDays: harSettingsSchema.shape.maxAgeDays.removeDefault().optional(),
|
|
18
|
+
maxTotalMB: harSettingsSchema.shape.maxTotalMB.removeDefault().optional(),
|
|
19
|
+
}).parse(args);
|
|
20
|
+
const settings = Object.values(changes).some((value) => value !== undefined)
|
|
21
|
+
? await writeHarSettings({ ...await readHarSettings(store, {}), ...changes }, store)
|
|
22
|
+
: await readHarSettings(store);
|
|
23
|
+
return { content: [{ type: "text", text: JSON.stringify({ ...settings, dir: harDirectory(), overridden: Boolean(process.env.VIDEOBOOK_HAR_CAPTURE?.trim()) }) }] };
|
|
24
|
+
}
|
|
25
|
+
catch (error) {
|
|
26
|
+
return { isError: true, content: [{ type: "text", text: error instanceof Error ? error.message : String(error) }] };
|
|
27
|
+
}
|
|
28
|
+
},
|
|
29
|
+
};
|
|
30
|
+
}
|
package/dist/upload.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import { createFreeMediaUpload
|
|
1
|
+
import { createFreeMediaUpload } from "./usage-providers.js";
|
|
2
|
+
import { uploadToAnyHost } from "@apicity/free-media-upload";
|
|
2
3
|
import { createHash } from "node:crypto";
|
|
3
4
|
import { maybeReencodeImage, maybeUpscaleSmallImage } from "./image-compress.js";
|
|
4
5
|
export function contentHash(data) {
|
package/dist/usage-providers.js
CHANGED
|
@@ -1,29 +1,67 @@
|
|
|
1
|
+
import { createFreeMediaUpload as freemediaupload } from "@apicity/free-media-upload";
|
|
2
|
+
import { createKimiCoding as kimicoding } from "@apicity/kimicoding";
|
|
3
|
+
import { createXOAuth as xoauth } from "@apicity/x";
|
|
4
|
+
import { createX as x } from "@apicity/x";
|
|
5
|
+
import { createDropbox as dropbox } from "@apicity/dropbox";
|
|
6
|
+
import { createB2 as b2 } from "@apicity/b2";
|
|
1
7
|
import { createOpenAi as openai } from "@apicity/openai";
|
|
2
8
|
import { createXai as xai } from "@apicity/xai";
|
|
3
|
-
import { createKie as kie } from "@apicity/kie";
|
|
4
|
-
import { createFal as fal } from "@apicity/fal";
|
|
5
|
-
import { createAlibaba as alibaba } from "@apicity/alibaba";
|
|
9
|
+
import { createKie as kie, uploadFile as kieUpload, submitMediaJob as submitKieMediaJob } from "@apicity/kie";
|
|
10
|
+
import { createFal as fal, uploadFile as falUpload } from "@apicity/fal";
|
|
11
|
+
import { createAlibaba as alibaba, uploadFile as alibabaUpload } from "@apicity/alibaba";
|
|
6
12
|
import { createElevenLabs as elevenlabs } from "@apicity/elevenlabs";
|
|
7
13
|
import { createMachGen as machgen } from "@apicity/machgen";
|
|
14
|
+
import { capturedProvider, withHarCapture, withProviderCall } from "./har-capture.js";
|
|
8
15
|
import { providerUsageFetch } from "./provider-usage.js";
|
|
9
16
|
export function createOpenAi(options) {
|
|
10
|
-
return openai({ ...options, fetch
|
|
17
|
+
return capturedProvider("openai", (fetch) => openai({ ...options, fetch }), providerUsageFetch("openai", options.fetch));
|
|
11
18
|
}
|
|
12
19
|
export function createXai(options) {
|
|
13
|
-
return xai({ ...options, fetch
|
|
20
|
+
return capturedProvider("xai", (fetch) => xai({ ...options, fetch }), providerUsageFetch("xai", options.fetch));
|
|
14
21
|
}
|
|
15
22
|
export function createKie(options) {
|
|
16
|
-
return kie({ ...options, fetch
|
|
23
|
+
return capturedProvider("kie", (fetch) => kie({ ...options, fetch }), providerUsageFetch("kie", options.fetch));
|
|
17
24
|
}
|
|
18
25
|
export function createFal(options) {
|
|
19
|
-
return fal({ ...options, fetch
|
|
26
|
+
return capturedProvider("fal", (fetch) => fal({ ...options, fetch }), providerUsageFetch("fal", options.fetch));
|
|
20
27
|
}
|
|
21
28
|
export function createAlibaba(options) {
|
|
22
|
-
return alibaba({ ...options, fetch
|
|
29
|
+
return capturedProvider("alibaba", (fetch) => alibaba({ ...options, fetch }), providerUsageFetch("alibaba", options.fetch));
|
|
23
30
|
}
|
|
24
31
|
export function createElevenLabs(options) {
|
|
25
|
-
return elevenlabs({ ...options, fetch
|
|
32
|
+
return capturedProvider("elevenlabs", (fetch) => elevenlabs({ ...options, fetch }), providerUsageFetch("elevenlabs", options.fetch));
|
|
26
33
|
}
|
|
27
34
|
export function createMachGen(options) {
|
|
28
|
-
return machgen({ ...options, fetch
|
|
35
|
+
return capturedProvider("machgen", (fetch) => machgen({ ...options, fetch }), providerUsageFetch("machgen", options.fetch));
|
|
36
|
+
}
|
|
37
|
+
export function createB2(options) {
|
|
38
|
+
return capturedProvider("b2", (fetch) => b2({ ...options, fetch }), options?.fetch);
|
|
39
|
+
}
|
|
40
|
+
export function createDropbox(options) {
|
|
41
|
+
return capturedProvider("dropbox", (fetch) => dropbox({ ...options, fetch }), options?.fetch);
|
|
42
|
+
}
|
|
43
|
+
export function createX(options) {
|
|
44
|
+
return capturedProvider("x", (fetch) => x({ ...options, fetch }), options?.fetch);
|
|
45
|
+
}
|
|
46
|
+
export function createXOAuth(options) {
|
|
47
|
+
return capturedProvider("x", (fetch) => xoauth({ ...options, fetch }), options?.fetch);
|
|
48
|
+
}
|
|
49
|
+
export function createKimiCoding(options) {
|
|
50
|
+
return capturedProvider("kimicoding", (fetch) => kimicoding({ ...options, fetch }), options?.fetch);
|
|
51
|
+
}
|
|
52
|
+
export function createFreeMediaUpload(options = {}) {
|
|
53
|
+
return capturedProvider("free-media-upload", (fetch) => freemediaupload({ ...options, fetch }), options?.fetch);
|
|
54
|
+
}
|
|
55
|
+
export function uploadFalFile(options, input) {
|
|
56
|
+
return withHarCapture({ provider: "fal", endpoint: "uploadFile", input }, (fetch) => falUpload(fal({ ...options, fetch }), input), providerUsageFetch("fal", options.fetch));
|
|
57
|
+
}
|
|
58
|
+
export function uploadAlibabaFile(options, input) {
|
|
59
|
+
return withHarCapture({ provider: "alibaba", endpoint: "uploadFile", input }, (fetch) => alibabaUpload(alibaba({ ...options, fetch }), input), providerUsageFetch("alibaba", options.fetch));
|
|
60
|
+
}
|
|
61
|
+
export function uploadKieFile(options, file, filename) {
|
|
62
|
+
return withHarCapture({ provider: "kie", endpoint: "uploadFile", input: { file, filename } }, (fetch) => kieUpload(kie({ ...options, fetch }), file, filename), providerUsageFetch("kie", options.fetch));
|
|
63
|
+
}
|
|
64
|
+
export function submitMediaJob(...args) {
|
|
65
|
+
const [provider, request, approval] = args;
|
|
66
|
+
return withProviderCall(provider, { provider: "kie", endpoint: "api.v1.jobs.createTask", input: request }, (original) => submitKieMediaJob(original, request, approval));
|
|
29
67
|
}
|
|
@@ -79,7 +79,7 @@ export const WORKSPACE_TOOL_GROUPS = {
|
|
|
79
79
|
],
|
|
80
80
|
History: [
|
|
81
81
|
"asset_history", "restore_asset_version", "read_file_at_commit",
|
|
82
|
-
"list_cell_generations", "inspect_generation_request",
|
|
82
|
+
"list_cell_generations", "inspect_generation_request", "get_generation_har",
|
|
83
83
|
"record_prompt", "list_prompt_history", "list_book_actions",
|
|
84
84
|
"get_history_action",
|
|
85
85
|
],
|
|
@@ -92,7 +92,7 @@ export const WORKSPACE_TOOL_GROUPS = {
|
|
|
92
92
|
],
|
|
93
93
|
Publishing: ["create_post", "update_post", "get_post", "list_posts", "link_post", "upload_post_to_dropbox", "save_post_to_icloud", "refresh_post_metrics", "record_post_metrics", "connect_service", "service_status", "post_icloud_sync_status", "draft_post_copy", "post", "verify_x_mentions"],
|
|
94
94
|
Providers: [
|
|
95
|
-
"check_kimi_coding_api", "list_chat_models", "provider_key_status", "set_env_secret",
|
|
95
|
+
"check_kimi_coding_api", "list_chat_models", "provider_key_status", "set_env_secret", "set_har_capture",
|
|
96
96
|
"list_elevenlabs_voices",
|
|
97
97
|
],
|
|
98
98
|
};
|
package/dist/x/account.js
CHANGED
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
* store next to the OAuth tokens, so posting never spends an extra API call.
|
|
8
8
|
* Callers refresh it after a new authorization or on explicit request.
|
|
9
9
|
*/
|
|
10
|
-
import { createX } from "
|
|
10
|
+
import { createX } from "../usage-providers.js";
|
|
11
11
|
import { appState } from "../app-state.js";
|
|
12
12
|
import { getValidAccessToken } from "./x-tokens.js";
|
|
13
13
|
export const X_POST_MAX_CHARACTERS = 280;
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
* API rejects, or the response is malformed. Callers should treat null as
|
|
6
6
|
* "unknown" and fall back to a cached value or prompt the user.
|
|
7
7
|
*/
|
|
8
|
-
import { createX } from "
|
|
8
|
+
import { createX } from "../usage-providers.js";
|
|
9
9
|
import { getValidAccessToken } from "./x-tokens.js";
|
|
10
10
|
import { normalizeHandle } from "../watermark-config.js";
|
|
11
11
|
export async function fetchAuthenticatedXHandle() {
|
package/dist/x/oauth-flow.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import http from "node:http";
|
|
2
2
|
import crypto from "node:crypto";
|
|
3
|
-
import { createXOAuth } from "
|
|
3
|
+
import { createXOAuth } from "../usage-providers.js";
|
|
4
4
|
import { appState } from "../app-state.js";
|
|
5
5
|
import { readTokens, writeTokens } from "./x-tokens.js";
|
|
6
6
|
import { clearXAccount, fetchXAccount, resolveXAccount, xPostMaxCharacters, } from "./account.js";
|
package/dist/x/post-to-x.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { findPrimaryMediaFile } from "videobook-engine";
|
|
2
|
-
import { createX
|
|
2
|
+
import { createX } from "../usage-providers.js";
|
|
3
|
+
import { XError } from "@apicity/x";
|
|
3
4
|
import { extname, join } from "node:path";
|
|
4
5
|
import { tmpdir } from "node:os";
|
|
5
6
|
import { readFile, unlink } from "node:fs/promises";
|