@mevdragon/vidfarm-devcli 0.20.1 → 0.20.3
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/.agents/skills/editor-capabilities/SKILL.md +2 -1
- package/SKILL.director.md +96 -10
- package/SKILL.platform.md +3 -3
- package/dist/src/app.js +326 -24
- package/dist/src/cli.js +253 -26
- package/dist/src/devcli/sync.js +66 -9
- package/dist/src/editor-chat.js +2 -2
- package/dist/src/services/hyperframes.js +162 -28
- package/package.json +1 -1
package/dist/src/app.js
CHANGED
|
@@ -24,6 +24,7 @@ import { writeForkManifest } from "./services/fork-manifest.js";
|
|
|
24
24
|
import { renderHelpPage } from "./help-page.js";
|
|
25
25
|
import { renderHomepage } from "./homepage.js";
|
|
26
26
|
import { renderLandingPage } from "./landing-page.js";
|
|
27
|
+
import { createTemplateJobContext } from "./context.js";
|
|
27
28
|
import { renderReskinIndex } from "./reskin/index-page.js";
|
|
28
29
|
import { renderReskinSettings } from "./reskin/settings-page.js";
|
|
29
30
|
import { renderReskinLibrary } from "./reskin/library-page.js";
|
|
@@ -76,7 +77,7 @@ import { clipRecords, makeUserPreset } from "./services/clip-records.js";
|
|
|
76
77
|
import { matchScenesToClips, searchUserClips, vectorIndex } from "./services/clip-search.js";
|
|
77
78
|
import { ATTACHMENTS_FOLDER_SCOPE, buildDirectoryPath, DIRECTORY_ROOTS, DIRECTORY_ROOT_KEYS, folderItem, immediateChildFolders, normalizeFolderPath, parseDirectoryPath } from "./services/file-directory.js";
|
|
78
79
|
import { applyHuntSpecDefaults, BUILTIN_PRESETS, buildClipEmbeddingText, buildEffectiveGuidance, ClipModelClient, cosineSimilarity, detectLocalAgent, effectiveHuntDurationSec, ACTIVE_TAXONOMY_VERSION, estimateScanCostFromDuration, extractAudioClip, extractClip, extractThumbnail, extractWaveformThumbnail, hasFfmpeg, LocalAgentClipClient, normalizeCropFocus, normalizeHuntSpec, parseClipHuntPrompt, pickBestVideoMedia, probeVideo, resolveDurationBand, resolveTargetClipCount, scanVideo } from "./services/clip-curation/index.js";
|
|
79
|
-
import { analyzeCastMembers, annotateScenesFromSource, buildBlankCompositionHtml, buildSmartDecomposedHtml, DEFAULT_EDITOR_HARNESS, HyperframesService, smartDecomposeVideo, stampCompositionIdentity } from "./services/hyperframes.js";
|
|
80
|
+
import { analyzeCastMembers, annotateScenesFromSource, buildBlankCompositionHtml, buildSmartDecomposedData, buildSmartDecomposedHtml, DEFAULT_EDITOR_HARNESS, DEFAULT_VIRAL_DNA, HyperframesService, smartDecomposeVideo, stampCompositionIdentity } from "./services/hyperframes.js";
|
|
80
81
|
import { buildPendingSceneAnnotationsState } from "./services/scene-annotations.js";
|
|
81
82
|
import { createPrimitiveJobContext } from "./primitive-context.js";
|
|
82
83
|
import { buildPendingCastState, castMemberFromRaw, extractCastStill, isolateCastMember } from "./services/cast.js";
|
|
@@ -7805,7 +7806,7 @@ async function publishCompositionToRenderer(input) {
|
|
|
7805
7806
|
fps: 30,
|
|
7806
7807
|
chunk_size: 240,
|
|
7807
7808
|
max_parallel_chunks: 8,
|
|
7808
|
-
output_key:
|
|
7809
|
+
output_key: "output.mp4",
|
|
7809
7810
|
project_files: projectFiles,
|
|
7810
7811
|
composition_data: compositionData ?? undefined
|
|
7811
7812
|
});
|
|
@@ -7894,6 +7895,48 @@ async function runPrimitiveJobInProcess(jobId) {
|
|
|
7894
7895
|
throw error;
|
|
7895
7896
|
}
|
|
7896
7897
|
}
|
|
7898
|
+
async function runTemplateJobInProcess(jobId) {
|
|
7899
|
+
const job = await jobs.getJobAsync(jobId);
|
|
7900
|
+
if (!job)
|
|
7901
|
+
throw new Error(`Job not found: ${jobId}`);
|
|
7902
|
+
const failContract = async (message) => {
|
|
7903
|
+
await jobs.failJob(job.id, { message, type: "local_runner_contract_error" });
|
|
7904
|
+
throw new Error(message);
|
|
7905
|
+
};
|
|
7906
|
+
const customer = await serverlessRecords.getCustomerById(job.customerId);
|
|
7907
|
+
if (!customer)
|
|
7908
|
+
return failContract(`Customer not found: ${job.customerId}`);
|
|
7909
|
+
const template = await templateRegistry.getExecutable(job.templateId);
|
|
7910
|
+
if (!template)
|
|
7911
|
+
return failContract(`Template not found: ${job.templateId}`);
|
|
7912
|
+
const workflow = template.jobs[job.workflowName];
|
|
7913
|
+
if (!workflow)
|
|
7914
|
+
return failContract(`Template workflow not found: ${job.workflowName}`);
|
|
7915
|
+
const workerId = createId("local_worker");
|
|
7916
|
+
try {
|
|
7917
|
+
await jobs.markRunning(job.id);
|
|
7918
|
+
const ctx = createTemplateJobContext({
|
|
7919
|
+
customer,
|
|
7920
|
+
job,
|
|
7921
|
+
template,
|
|
7922
|
+
workerId,
|
|
7923
|
+
storage,
|
|
7924
|
+
jobs,
|
|
7925
|
+
billing,
|
|
7926
|
+
providers,
|
|
7927
|
+
hyperframes
|
|
7928
|
+
});
|
|
7929
|
+
const result = await workflow(ctx, job.payload);
|
|
7930
|
+
await jobs.succeedJob(job.id, result.output ?? {}, result.progress ?? 1);
|
|
7931
|
+
}
|
|
7932
|
+
catch (error) {
|
|
7933
|
+
await jobs.failJob(job.id, {
|
|
7934
|
+
message: error instanceof Error ? error.message : "Local template job failed",
|
|
7935
|
+
type: "local_render_failed"
|
|
7936
|
+
});
|
|
7937
|
+
throw error;
|
|
7938
|
+
}
|
|
7939
|
+
}
|
|
7897
7940
|
function mapJobStatusToPublishPhase(status) {
|
|
7898
7941
|
const normalized = String(status ?? "").toLowerCase();
|
|
7899
7942
|
if (normalized === "completed" || normalized === "succeeded")
|
|
@@ -7977,6 +8020,7 @@ function adaptJobToPublishStatus(job, fallbackTitle) {
|
|
|
7977
8020
|
cost,
|
|
7978
8021
|
outputS3Uri: readNonEmptyString(asRecord(result.output)?.s3_uri) ?? readNonEmptyString(renderBlock.output_s3_uri),
|
|
7979
8022
|
outputUrl,
|
|
8023
|
+
expectedOutputPublicUrl: readNonEmptyString(record.expected_output_public_url) ?? expectedHyperframesRenderOutputPublicUrl(job),
|
|
7980
8024
|
title: readNonEmptyString(asRecord(record.payload)?.title) ?? fallbackTitle,
|
|
7981
8025
|
fatalErrorEncountered: Boolean(publishErrorMessage),
|
|
7982
8026
|
errors: publishErrorMessage ? [{ error: publishErrorMessage }] : []
|
|
@@ -8807,6 +8851,7 @@ app.post(`${INSPIRATIONS_PREFIX}/:inspirationId/decompose`, async (c) => {
|
|
|
8807
8851
|
result: smart
|
|
8808
8852
|
});
|
|
8809
8853
|
await storage.putText(storage.compositionForkWorkingKey(canonicalFork.id, "composition.html"), nextHtml, "text/html; charset=utf-8");
|
|
8854
|
+
await storage.putJson(storage.compositionForkWorkingKey(canonicalFork.id, "composition.json"), buildSmartDecomposedData({ sourceUrl, result: smart }));
|
|
8810
8855
|
// Persist placement surfaces on the canonical fork too, before the
|
|
8811
8856
|
// snapshot, so they travel with v1 and seed every downstream fork.
|
|
8812
8857
|
try {
|
|
@@ -9296,6 +9341,7 @@ app.post(`${COMPOSITIONS_PREFIX}/:forkId/auto-decompose`, async (c) => {
|
|
|
9296
9341
|
}
|
|
9297
9342
|
const nextHtml = buildSmartDecomposedHtml({ compositionId, sourceUrl, title, result: smart });
|
|
9298
9343
|
await storage.putText(storage.compositionForkWorkingKey(fork.id, "composition.html"), nextHtml, "text/html; charset=utf-8");
|
|
9344
|
+
await storage.putJson(storage.compositionForkWorkingKey(fork.id, "composition.json"), buildSmartDecomposedData({ sourceUrl, result: smart }));
|
|
9299
9345
|
// Persist the raw smart-decompose result so the async /remove-video-captions-poll
|
|
9300
9346
|
// handler can rebuild the composition HTML pointing at the cleaned
|
|
9301
9347
|
// (ghostcut-mirrored) video URL — with the ORIGINAL captions, positions,
|
|
@@ -9685,10 +9731,7 @@ async function rebuildDecomposedCompositionAgainstCleanVideo(input) {
|
|
|
9685
9731
|
title: persisted.title ?? fork.title ?? fork.id,
|
|
9686
9732
|
result: {
|
|
9687
9733
|
summary: persisted.result.summary ?? "",
|
|
9688
|
-
viralDna: persisted.result.viralDna ??
|
|
9689
|
-
trend_tagline: "", hook: "", retention: "", payoff: "",
|
|
9690
|
-
preserve: [], avoid: [], promotions: [], keywords: []
|
|
9691
|
-
},
|
|
9734
|
+
viralDna: persisted.result.viralDna ?? DEFAULT_VIRAL_DNA,
|
|
9692
9735
|
transcript: persisted.result.transcript ?? null,
|
|
9693
9736
|
durationSeconds: Number(persisted.result.durationSeconds ?? 0),
|
|
9694
9737
|
width: Number(persisted.result.width ?? 720),
|
|
@@ -9704,6 +9747,21 @@ async function rebuildDecomposedCompositionAgainstCleanVideo(input) {
|
|
|
9704
9747
|
}
|
|
9705
9748
|
});
|
|
9706
9749
|
await storage.putText(storage.compositionForkWorkingKey(fork.id, "composition.html"), rebuilt, "text/html; charset=utf-8");
|
|
9750
|
+
await storage.putJson(storage.compositionForkWorkingKey(fork.id, "composition.json"), buildSmartDecomposedData({
|
|
9751
|
+
sourceUrl: cleanedSourceUrl,
|
|
9752
|
+
result: {
|
|
9753
|
+
summary: persisted.result.summary ?? "",
|
|
9754
|
+
viralDna: persisted.result.viralDna ?? DEFAULT_VIRAL_DNA,
|
|
9755
|
+
transcript: persisted.result.transcript ?? null,
|
|
9756
|
+
durationSeconds: Number(persisted.result.durationSeconds ?? 0),
|
|
9757
|
+
width: Number(persisted.result.width ?? 720),
|
|
9758
|
+
height: Number(persisted.result.height ?? 1280),
|
|
9759
|
+
scenes: persisted.result.scenes,
|
|
9760
|
+
captions: Array.isArray(persisted.result.captions) ? persisted.result.captions : [],
|
|
9761
|
+
placementSurfaces: [],
|
|
9762
|
+
editorHarness: persisted.result.editorHarness ?? DEFAULT_EDITOR_HARNESS
|
|
9763
|
+
}
|
|
9764
|
+
}));
|
|
9707
9765
|
await snapshotForkVersion({
|
|
9708
9766
|
fork,
|
|
9709
9767
|
callerCustomerId,
|
|
@@ -10238,7 +10296,7 @@ app.post(`${COMPOSITIONS_PREFIX}/:forkId/scene-annotations-poll`, async (c) => {
|
|
|
10238
10296
|
sourceUrl: state.sourceUrl,
|
|
10239
10297
|
keys,
|
|
10240
10298
|
scenes,
|
|
10241
|
-
viralDna: viralDna ??
|
|
10299
|
+
viralDna: viralDna ?? DEFAULT_VIRAL_DNA,
|
|
10242
10300
|
summary,
|
|
10243
10301
|
transcript,
|
|
10244
10302
|
userPrompt
|
|
@@ -16449,8 +16507,10 @@ async function serializeJob(job) {
|
|
|
16449
16507
|
customerId: job.customerId,
|
|
16450
16508
|
templateId: job.templateId
|
|
16451
16509
|
});
|
|
16510
|
+
const expectedOutputPublicUrl = expectedHyperframesRenderOutputPublicUrl(job);
|
|
16452
16511
|
return {
|
|
16453
16512
|
job_id: job.id,
|
|
16513
|
+
customer_id: job.customerId,
|
|
16454
16514
|
template_id: job.templateId,
|
|
16455
16515
|
operation_name: job.operationName,
|
|
16456
16516
|
workflow_name: job.workflowName,
|
|
@@ -16466,6 +16526,7 @@ async function serializeJob(job) {
|
|
|
16466
16526
|
event_count: jobBilling.eventCount,
|
|
16467
16527
|
consumed_usd: jobBilling.totalChargeUsd
|
|
16468
16528
|
},
|
|
16529
|
+
expected_output_public_url: expectedOutputPublicUrl,
|
|
16469
16530
|
parent_job_id: job.parentJobId,
|
|
16470
16531
|
created_at: job.createdAt,
|
|
16471
16532
|
updated_at: job.updatedAt,
|
|
@@ -16485,6 +16546,19 @@ function serializeArtifact(artifact) {
|
|
|
16485
16546
|
created_at: artifact.createdAt
|
|
16486
16547
|
};
|
|
16487
16548
|
}
|
|
16549
|
+
function expectedHyperframesRenderOutputPublicUrl(job) {
|
|
16550
|
+
const record = asRecord(job);
|
|
16551
|
+
if (!record)
|
|
16552
|
+
return null;
|
|
16553
|
+
const templateId = readNonEmptyString(record.templateId) ?? readNonEmptyString(record.template_id);
|
|
16554
|
+
const customerId = readNonEmptyString(record.customerId) ?? readNonEmptyString(record.customer_id);
|
|
16555
|
+
const jobId = readNonEmptyString(record.id) ?? readNonEmptyString(record.job_id);
|
|
16556
|
+
if (templateId !== "primitive:hyperframes_render" || !customerId || !jobId)
|
|
16557
|
+
return null;
|
|
16558
|
+
const outputKey = readNonEmptyString(asRecord(record.payload)?.output_key) ?? "output.mp4";
|
|
16559
|
+
const storageKey = storage.primitiveJobKey(templateId, customerId, jobId, outputKey);
|
|
16560
|
+
return storage.getPublicUrl(storageKey);
|
|
16561
|
+
}
|
|
16488
16562
|
async function buildJobDebugBundle(input) {
|
|
16489
16563
|
const job = input.job;
|
|
16490
16564
|
const startTime = input.startTime ?? offsetIso(job.createdAt, -60_000);
|
|
@@ -17020,7 +17094,7 @@ async function deriveProviderHint(input) {
|
|
|
17020
17094
|
];
|
|
17021
17095
|
return useHintIfAvailable(configCandidates.find(isProviderType));
|
|
17022
17096
|
}
|
|
17023
|
-
async function
|
|
17097
|
+
async function listJobsForCustomerByTemplateId(input) {
|
|
17024
17098
|
const target = clampPageLimit(input.limit, 100) + 1;
|
|
17025
17099
|
const collected = [];
|
|
17026
17100
|
let cursor = input.cursor ?? null;
|
|
@@ -17037,7 +17111,8 @@ async function listPrimitiveJobsForCustomer(input) {
|
|
|
17037
17111
|
break;
|
|
17038
17112
|
}
|
|
17039
17113
|
for (const job of batch) {
|
|
17040
|
-
if (
|
|
17114
|
+
if ((!input.templateId || job.templateId === input.templateId) &&
|
|
17115
|
+
(!input.templateIdPrefix || job.templateId.startsWith(input.templateIdPrefix))) {
|
|
17041
17116
|
collected.push(job);
|
|
17042
17117
|
if (collected.length >= target) {
|
|
17043
17118
|
break;
|
|
@@ -17058,6 +17133,16 @@ async function listPrimitiveJobsForCustomer(input) {
|
|
|
17058
17133
|
}
|
|
17059
17134
|
return collected;
|
|
17060
17135
|
}
|
|
17136
|
+
async function getJobForCustomerByTemplateId(input) {
|
|
17137
|
+
const job = await jobs.getJobAsync(input.jobId);
|
|
17138
|
+
if (!job || job.customerId !== input.customerId) {
|
|
17139
|
+
return null;
|
|
17140
|
+
}
|
|
17141
|
+
if (input.templateId && job.templateId !== input.templateId) {
|
|
17142
|
+
return null;
|
|
17143
|
+
}
|
|
17144
|
+
return job;
|
|
17145
|
+
}
|
|
17061
17146
|
async function createPrimitiveJob(c, input) {
|
|
17062
17147
|
const customer = requireCustomer(c);
|
|
17063
17148
|
const primitive = primitiveRegistry.get(input.primitiveId);
|
|
@@ -17145,6 +17230,90 @@ async function createPrimitiveJob(c, input) {
|
|
|
17145
17230
|
operation_name: input.operationName
|
|
17146
17231
|
}, 202);
|
|
17147
17232
|
}
|
|
17233
|
+
async function createTemplateJob(c, input) {
|
|
17234
|
+
const customer = requireCustomer(c);
|
|
17235
|
+
const template = await templateRegistry.getExecutable(input.templateId);
|
|
17236
|
+
if (!template) {
|
|
17237
|
+
return c.json({ error: "Template not found" }, 404);
|
|
17238
|
+
}
|
|
17239
|
+
const operation = template.operations[input.operationName];
|
|
17240
|
+
if (!operation) {
|
|
17241
|
+
return c.json({ error: "Template operation not found" }, 404);
|
|
17242
|
+
}
|
|
17243
|
+
let body;
|
|
17244
|
+
let payload;
|
|
17245
|
+
try {
|
|
17246
|
+
body = primitiveEnvelopeSchema.parse(await c.req.json());
|
|
17247
|
+
payload = operation.inputSchema.parse(body.payload);
|
|
17248
|
+
}
|
|
17249
|
+
catch (error) {
|
|
17250
|
+
if (error instanceof z.ZodError) {
|
|
17251
|
+
return c.json({
|
|
17252
|
+
error: "Invalid template request.",
|
|
17253
|
+
issues: error.issues
|
|
17254
|
+
}, 400);
|
|
17255
|
+
}
|
|
17256
|
+
if (isJsonBodyParseError(error)) {
|
|
17257
|
+
return c.json({
|
|
17258
|
+
error: "Invalid template request.",
|
|
17259
|
+
detail: "Request body must be valid JSON with top-level tracer and payload fields."
|
|
17260
|
+
}, 400);
|
|
17261
|
+
}
|
|
17262
|
+
throw error;
|
|
17263
|
+
}
|
|
17264
|
+
const providerPreflight = await preflightAiProviderKeys({
|
|
17265
|
+
customerId: customer.id,
|
|
17266
|
+
templateId: template.id,
|
|
17267
|
+
operationName: input.operationName,
|
|
17268
|
+
operationHint: operation.providerHint,
|
|
17269
|
+
payload
|
|
17270
|
+
});
|
|
17271
|
+
if (providerPreflight) {
|
|
17272
|
+
return c.json(providerPreflight, 412);
|
|
17273
|
+
}
|
|
17274
|
+
let job;
|
|
17275
|
+
try {
|
|
17276
|
+
await assertWalletCanQueueJobs(customer.id);
|
|
17277
|
+
job = await jobs.createRootJob({
|
|
17278
|
+
templateId: template.id,
|
|
17279
|
+
operationName: input.operationName,
|
|
17280
|
+
workflowName: operation.workflow,
|
|
17281
|
+
tracer: body.tracer,
|
|
17282
|
+
payload,
|
|
17283
|
+
webhookUrl: body.webhook_url,
|
|
17284
|
+
customer,
|
|
17285
|
+
providerHint: await deriveProviderHint({
|
|
17286
|
+
customerId: customer.id,
|
|
17287
|
+
templateId: template.id,
|
|
17288
|
+
operationName: input.operationName,
|
|
17289
|
+
operationHint: operation.providerHint,
|
|
17290
|
+
payload
|
|
17291
|
+
})
|
|
17292
|
+
});
|
|
17293
|
+
}
|
|
17294
|
+
catch (error) {
|
|
17295
|
+
if (error instanceof InsufficientWalletFundsError) {
|
|
17296
|
+
return insufficientFundsResponse(c, error);
|
|
17297
|
+
}
|
|
17298
|
+
if (error instanceof ActiveJobLimitExceededError) {
|
|
17299
|
+
return activeJobLimitExceededResponse(c, error);
|
|
17300
|
+
}
|
|
17301
|
+
throw error;
|
|
17302
|
+
}
|
|
17303
|
+
if (!config.VIDFARM_JOB_STATE_MACHINE_ARN) {
|
|
17304
|
+
void runTemplateJobInProcess(job.id).catch((error) => {
|
|
17305
|
+
console.error("local template job failed", job.id, error instanceof Error ? error.message : error);
|
|
17306
|
+
});
|
|
17307
|
+
}
|
|
17308
|
+
return c.json({
|
|
17309
|
+
job_id: job.id,
|
|
17310
|
+
tracer: job.tracer,
|
|
17311
|
+
status: job.status,
|
|
17312
|
+
template_id: template.id,
|
|
17313
|
+
template_type: "template",
|
|
17314
|
+
operation_name: input.operationName
|
|
17315
|
+
}, 202);
|
|
17316
|
+
}
|
|
17148
17317
|
function resolvePrimitiveRouteTarget(primitiveParam) {
|
|
17149
17318
|
const normalized = primitiveParam.trim();
|
|
17150
17319
|
if (!normalized) {
|
|
@@ -17185,6 +17354,12 @@ app.post(`${PRIMITIVES_PREFIX}/:primitiveKind/operations/:operationName`, async
|
|
|
17185
17354
|
operationName: c.req.param("operationName")
|
|
17186
17355
|
});
|
|
17187
17356
|
});
|
|
17357
|
+
app.post(`${TEMPLATES_PREFIX}/:templateId/operations/:operationName`, async (c) => {
|
|
17358
|
+
return createTemplateJob(c, {
|
|
17359
|
+
templateId: c.req.param("templateId"),
|
|
17360
|
+
operationName: c.req.param("operationName")
|
|
17361
|
+
});
|
|
17362
|
+
});
|
|
17188
17363
|
app.get(`${PRIMITIVES_PREFIX}/jobs`, async (c) => {
|
|
17189
17364
|
const customer = requireCustomer(c);
|
|
17190
17365
|
const query = listJobsQuerySchema.parse({
|
|
@@ -17195,8 +17370,9 @@ app.get(`${PRIMITIVES_PREFIX}/jobs`, async (c) => {
|
|
|
17195
17370
|
limit: c.req.query("limit")
|
|
17196
17371
|
});
|
|
17197
17372
|
const limit = clampPageLimit(query.limit, 100);
|
|
17198
|
-
const listedJobs = await
|
|
17373
|
+
const listedJobs = await listJobsForCustomerByTemplateId({
|
|
17199
17374
|
customerId: customer.id,
|
|
17375
|
+
templateIdPrefix: "primitive:",
|
|
17200
17376
|
tracer: query.tracer,
|
|
17201
17377
|
startTime: query.start_time,
|
|
17202
17378
|
endTime: query.end_time,
|
|
@@ -17206,9 +17382,40 @@ app.get(`${PRIMITIVES_PREFIX}/jobs`, async (c) => {
|
|
|
17206
17382
|
const page = paginateDescendingByCreatedAt(listedJobs, limit);
|
|
17207
17383
|
return c.json({ primitive_jobs: await Promise.all(page.items.map(serializePrimitiveJob)), next_cursor: page.nextCursor });
|
|
17208
17384
|
});
|
|
17385
|
+
app.get(`${TEMPLATES_PREFIX}/:templateId/jobs`, async (c) => {
|
|
17386
|
+
const customer = requireCustomer(c);
|
|
17387
|
+
const template = await templateRegistry.getExecutable(c.req.param("templateId"));
|
|
17388
|
+
if (!template) {
|
|
17389
|
+
return c.json({ error: "Template not found" }, 404);
|
|
17390
|
+
}
|
|
17391
|
+
const query = listJobsQuerySchema.parse({
|
|
17392
|
+
tracer: c.req.query("tracer"),
|
|
17393
|
+
start_time: c.req.query("start_time"),
|
|
17394
|
+
end_time: c.req.query("end_time"),
|
|
17395
|
+
cursor: c.req.query("cursor"),
|
|
17396
|
+
limit: c.req.query("limit")
|
|
17397
|
+
});
|
|
17398
|
+
const limit = clampPageLimit(query.limit, 100);
|
|
17399
|
+
const listedJobs = await listJobsForCustomerByTemplateId({
|
|
17400
|
+
customerId: customer.id,
|
|
17401
|
+
templateId: template.id,
|
|
17402
|
+
tracer: query.tracer,
|
|
17403
|
+
startTime: query.start_time,
|
|
17404
|
+
endTime: query.end_time,
|
|
17405
|
+
limit,
|
|
17406
|
+
cursor: parseCreatedAtIdCursor(query.cursor)
|
|
17407
|
+
});
|
|
17408
|
+
const page = paginateDescendingByCreatedAt(listedJobs, limit);
|
|
17409
|
+
return c.json({
|
|
17410
|
+
template_id: template.id,
|
|
17411
|
+
template_type: "template",
|
|
17412
|
+
jobs: await Promise.all(page.items.map(serializeJob)),
|
|
17413
|
+
next_cursor: page.nextCursor
|
|
17414
|
+
});
|
|
17415
|
+
});
|
|
17209
17416
|
app.get(`${PRIMITIVES_PREFIX}/jobs/:jobId`, async (c) => {
|
|
17210
17417
|
const customer = requireCustomer(c);
|
|
17211
|
-
const job = await
|
|
17418
|
+
const job = await getJobForCustomerByTemplateId({
|
|
17212
17419
|
jobId: c.req.param("jobId"),
|
|
17213
17420
|
customerId: customer.id
|
|
17214
17421
|
});
|
|
@@ -17217,9 +17424,25 @@ app.get(`${PRIMITIVES_PREFIX}/jobs/:jobId`, async (c) => {
|
|
|
17217
17424
|
}
|
|
17218
17425
|
return c.json(await serializePrimitiveJob(job));
|
|
17219
17426
|
});
|
|
17427
|
+
app.get(`${TEMPLATES_PREFIX}/:templateId/jobs/:jobId`, async (c) => {
|
|
17428
|
+
const customer = requireCustomer(c);
|
|
17429
|
+
const template = await templateRegistry.getExecutable(c.req.param("templateId"));
|
|
17430
|
+
if (!template) {
|
|
17431
|
+
return c.json({ error: "Template not found" }, 404);
|
|
17432
|
+
}
|
|
17433
|
+
const job = await getJobForCustomerByTemplateId({
|
|
17434
|
+
jobId: c.req.param("jobId"),
|
|
17435
|
+
customerId: customer.id,
|
|
17436
|
+
templateId: template.id
|
|
17437
|
+
});
|
|
17438
|
+
if (!job) {
|
|
17439
|
+
return c.json({ error: "Template job not found" }, 404);
|
|
17440
|
+
}
|
|
17441
|
+
return c.json(await serializeJob(job));
|
|
17442
|
+
});
|
|
17220
17443
|
app.get(`${PRIMITIVES_PREFIX}/jobs/:jobId/artifacts`, async (c) => {
|
|
17221
17444
|
const customer = requireCustomer(c);
|
|
17222
|
-
const job = await
|
|
17445
|
+
const job = await getJobForCustomerByTemplateId({
|
|
17223
17446
|
jobId: c.req.param("jobId"),
|
|
17224
17447
|
customerId: customer.id
|
|
17225
17448
|
});
|
|
@@ -17242,9 +17465,40 @@ app.get(`${PRIMITIVES_PREFIX}/jobs/:jobId/artifacts`, async (c) => {
|
|
|
17242
17465
|
next_cursor: page.nextCursor
|
|
17243
17466
|
});
|
|
17244
17467
|
});
|
|
17468
|
+
app.get(`${TEMPLATES_PREFIX}/:templateId/jobs/:jobId/artifacts`, async (c) => {
|
|
17469
|
+
const customer = requireCustomer(c);
|
|
17470
|
+
const template = await templateRegistry.getExecutable(c.req.param("templateId"));
|
|
17471
|
+
if (!template) {
|
|
17472
|
+
return c.json({ error: "Template not found" }, 404);
|
|
17473
|
+
}
|
|
17474
|
+
const job = await getJobForCustomerByTemplateId({
|
|
17475
|
+
jobId: c.req.param("jobId"),
|
|
17476
|
+
customerId: customer.id,
|
|
17477
|
+
templateId: template.id
|
|
17478
|
+
});
|
|
17479
|
+
if (!job) {
|
|
17480
|
+
return c.json({ error: "Template job not found" }, 404);
|
|
17481
|
+
}
|
|
17482
|
+
const limit = clampPageLimit(Number(c.req.query("limit") || "100"), 100);
|
|
17483
|
+
const page = await listArtifactsForJobPage({
|
|
17484
|
+
jobId: job.id,
|
|
17485
|
+
customerId: customer.id,
|
|
17486
|
+
templateId: job.templateId,
|
|
17487
|
+
limit,
|
|
17488
|
+
cursor: parseCreatedAtIdCursor(c.req.query("cursor"))
|
|
17489
|
+
});
|
|
17490
|
+
return c.json({
|
|
17491
|
+
job_id: job.id,
|
|
17492
|
+
tracer: job.tracer,
|
|
17493
|
+
template_id: job.templateId,
|
|
17494
|
+
template_type: "template",
|
|
17495
|
+
artifacts: page.items.map(serializeArtifact),
|
|
17496
|
+
next_cursor: page.nextCursor
|
|
17497
|
+
});
|
|
17498
|
+
});
|
|
17245
17499
|
app.get(`${PRIMITIVES_PREFIX}/jobs/:jobId/logs`, async (c) => {
|
|
17246
17500
|
const customer = requireCustomer(c);
|
|
17247
|
-
const job = await
|
|
17501
|
+
const job = await getJobForCustomerByTemplateId({
|
|
17248
17502
|
jobId: c.req.param("jobId"),
|
|
17249
17503
|
customerId: customer.id
|
|
17250
17504
|
});
|
|
@@ -17268,9 +17522,40 @@ app.get(`${PRIMITIVES_PREFIX}/jobs/:jobId/logs`, async (c) => {
|
|
|
17268
17522
|
const page = paginateDescendingByCreatedAt(logs, limit);
|
|
17269
17523
|
return c.json({ job_id: job.id, tracer: job.tracer, logs: page.items, next_cursor: page.nextCursor });
|
|
17270
17524
|
});
|
|
17525
|
+
app.get(`${TEMPLATES_PREFIX}/:templateId/jobs/:jobId/logs`, async (c) => {
|
|
17526
|
+
const customer = requireCustomer(c);
|
|
17527
|
+
const template = await templateRegistry.getExecutable(c.req.param("templateId"));
|
|
17528
|
+
if (!template) {
|
|
17529
|
+
return c.json({ error: "Template not found" }, 404);
|
|
17530
|
+
}
|
|
17531
|
+
const job = await getJobForCustomerByTemplateId({
|
|
17532
|
+
jobId: c.req.param("jobId"),
|
|
17533
|
+
customerId: customer.id,
|
|
17534
|
+
templateId: template.id
|
|
17535
|
+
});
|
|
17536
|
+
if (!job) {
|
|
17537
|
+
return c.json({ error: "Template job not found" }, 404);
|
|
17538
|
+
}
|
|
17539
|
+
const query = listJobsQuerySchema.parse({
|
|
17540
|
+
start_time: c.req.query("start_time"),
|
|
17541
|
+
end_time: c.req.query("end_time"),
|
|
17542
|
+
cursor: c.req.query("cursor"),
|
|
17543
|
+
limit: c.req.query("limit")
|
|
17544
|
+
});
|
|
17545
|
+
const limit = clampPageLimit(query.limit, 100);
|
|
17546
|
+
const logs = await jobs.listLogs({
|
|
17547
|
+
jobId: job.id,
|
|
17548
|
+
startTime: query.start_time,
|
|
17549
|
+
endTime: query.end_time,
|
|
17550
|
+
limit: limit + 1,
|
|
17551
|
+
cursor: parseCreatedAtIdCursor(query.cursor)
|
|
17552
|
+
});
|
|
17553
|
+
const page = paginateDescendingByCreatedAt(logs, limit);
|
|
17554
|
+
return c.json({ job_id: job.id, tracer: job.tracer, logs: page.items, next_cursor: page.nextCursor });
|
|
17555
|
+
});
|
|
17271
17556
|
app.post(`${PRIMITIVES_PREFIX}/jobs/:jobId/cancel`, async (c) => {
|
|
17272
17557
|
const customer = requireCustomer(c);
|
|
17273
|
-
const job = await
|
|
17558
|
+
const job = await getJobForCustomerByTemplateId({
|
|
17274
17559
|
jobId: c.req.param("jobId"),
|
|
17275
17560
|
customerId: customer.id
|
|
17276
17561
|
});
|
|
@@ -17280,6 +17565,23 @@ app.post(`${PRIMITIVES_PREFIX}/jobs/:jobId/cancel`, async (c) => {
|
|
|
17280
17565
|
await jobs.cancelJobAsync(job.id);
|
|
17281
17566
|
return c.json({ ok: true, job_id: job.id, status: "cancelled" });
|
|
17282
17567
|
});
|
|
17568
|
+
app.post(`${TEMPLATES_PREFIX}/:templateId/jobs/:jobId/cancel`, async (c) => {
|
|
17569
|
+
const customer = requireCustomer(c);
|
|
17570
|
+
const template = await templateRegistry.getExecutable(c.req.param("templateId"));
|
|
17571
|
+
if (!template) {
|
|
17572
|
+
return c.json({ error: "Template not found" }, 404);
|
|
17573
|
+
}
|
|
17574
|
+
const job = await getJobForCustomerByTemplateId({
|
|
17575
|
+
jobId: c.req.param("jobId"),
|
|
17576
|
+
customerId: customer.id,
|
|
17577
|
+
templateId: template.id
|
|
17578
|
+
});
|
|
17579
|
+
if (!job) {
|
|
17580
|
+
return c.json({ error: "Template job not found" }, 404);
|
|
17581
|
+
}
|
|
17582
|
+
await jobs.cancelJobAsync(job.id);
|
|
17583
|
+
return c.json({ ok: true, job_id: job.id, status: "cancelled" });
|
|
17584
|
+
});
|
|
17283
17585
|
app.get(`${PRIMITIVES_PREFIX}/:primitiveKind/jobs`, async (c) => {
|
|
17284
17586
|
const customer = requireCustomer(c);
|
|
17285
17587
|
const primitive = resolvePrimitiveRouteTarget(c.req.param("primitiveKind"));
|
|
@@ -17294,9 +17596,9 @@ app.get(`${PRIMITIVES_PREFIX}/:primitiveKind/jobs`, async (c) => {
|
|
|
17294
17596
|
limit: c.req.query("limit")
|
|
17295
17597
|
});
|
|
17296
17598
|
const limit = clampPageLimit(query.limit, 100);
|
|
17297
|
-
const listedJobs = await
|
|
17599
|
+
const listedJobs = await listJobsForCustomerByTemplateId({
|
|
17298
17600
|
customerId: customer.id,
|
|
17299
|
-
|
|
17601
|
+
templateId: primitive.id,
|
|
17300
17602
|
tracer: query.tracer,
|
|
17301
17603
|
startTime: query.start_time,
|
|
17302
17604
|
endTime: query.end_time,
|
|
@@ -17317,10 +17619,10 @@ app.get(`${PRIMITIVES_PREFIX}/:primitiveKind/jobs/:jobId`, async (c) => {
|
|
|
17317
17619
|
if (!primitive) {
|
|
17318
17620
|
return c.json({ error: "Primitive not found" }, 404);
|
|
17319
17621
|
}
|
|
17320
|
-
const job = await
|
|
17622
|
+
const job = await getJobForCustomerByTemplateId({
|
|
17321
17623
|
jobId: c.req.param("jobId"),
|
|
17322
17624
|
customerId: customer.id,
|
|
17323
|
-
|
|
17625
|
+
templateId: primitive.id
|
|
17324
17626
|
});
|
|
17325
17627
|
if (!job) {
|
|
17326
17628
|
return c.json({ error: "Primitive job not found" }, 404);
|
|
@@ -17333,10 +17635,10 @@ app.get(`${PRIMITIVES_PREFIX}/:primitiveKind/jobs/:jobId/artifacts`, async (c) =
|
|
|
17333
17635
|
if (!primitive) {
|
|
17334
17636
|
return c.json({ error: "Primitive not found" }, 404);
|
|
17335
17637
|
}
|
|
17336
|
-
const job = await
|
|
17638
|
+
const job = await getJobForCustomerByTemplateId({
|
|
17337
17639
|
jobId: c.req.param("jobId"),
|
|
17338
17640
|
customerId: customer.id,
|
|
17339
|
-
|
|
17641
|
+
templateId: primitive.id
|
|
17340
17642
|
});
|
|
17341
17643
|
if (!job) {
|
|
17342
17644
|
return c.json({ error: "Primitive job not found" }, 404);
|
|
@@ -17364,10 +17666,10 @@ app.get(`${PRIMITIVES_PREFIX}/:primitiveKind/jobs/:jobId/logs`, async (c) => {
|
|
|
17364
17666
|
if (!primitive) {
|
|
17365
17667
|
return c.json({ error: "Primitive not found" }, 404);
|
|
17366
17668
|
}
|
|
17367
|
-
const job = await
|
|
17669
|
+
const job = await getJobForCustomerByTemplateId({
|
|
17368
17670
|
jobId: c.req.param("jobId"),
|
|
17369
17671
|
customerId: customer.id,
|
|
17370
|
-
|
|
17672
|
+
templateId: primitive.id
|
|
17371
17673
|
});
|
|
17372
17674
|
if (!job) {
|
|
17373
17675
|
return c.json({ error: "Primitive job not found" }, 404);
|
|
@@ -17402,10 +17704,10 @@ app.post(`${PRIMITIVES_PREFIX}/:primitiveKind/jobs/:jobId/cancel`, async (c) =>
|
|
|
17402
17704
|
if (!primitive) {
|
|
17403
17705
|
return c.json({ error: "Primitive not found" }, 404);
|
|
17404
17706
|
}
|
|
17405
|
-
const job = await
|
|
17707
|
+
const job = await getJobForCustomerByTemplateId({
|
|
17406
17708
|
jobId: c.req.param("jobId"),
|
|
17407
17709
|
customerId: customer.id,
|
|
17408
|
-
|
|
17710
|
+
templateId: primitive.id
|
|
17409
17711
|
});
|
|
17410
17712
|
if (!job) {
|
|
17411
17713
|
return c.json({ error: "Primitive job not found" }, 404);
|