@mevdragon/vidfarm-devcli 0.2.6 → 0.2.8
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/GETTING_STARTED.developers.md +25 -0
- package/README.md +44 -3
- package/SKILL.developer.md +147 -11
- package/dist/src/account-pages.js +1 -1
- package/dist/src/app.js +380 -13
- package/dist/src/cli.js +246 -59
- package/dist/src/config.js +4 -5
- package/dist/src/context.js +16 -0
- package/dist/src/db.js +67 -13
- package/dist/src/homepage.js +33 -1
- package/dist/src/lib/template-paths.js +10 -4
- package/dist/src/registry.js +9 -2
- package/dist/src/services/auth.js +2 -6
- package/dist/src/services/job-logs.js +4 -4
- package/dist/src/services/providers.js +470 -0
- package/dist/src/services/storage.js +13 -0
- package/dist/src/services/template-certification.js +16 -2
- package/dist/src/services/template-loader.js +17 -1
- package/dist/src/services/template-sources.js +338 -17
- package/{templates/template_0000 → dist/templates/vidfarm_template_0000}/src/remotion/Root.js +1 -0
- package/dist/templates/{template_0000 → vidfarm_template_0000}/src/template.js +9 -3
- package/package.json +4 -3
- package/templates/{template_0000 → vidfarm_template_0000}/README.md +23 -0
- package/templates/{template_0000 → vidfarm_template_0000}/package.json +0 -1
- package/templates/{template_0000 → vidfarm_template_0000}/src/sdk.ts +18 -0
- package/templates/{template_0000 → vidfarm_template_0000}/src/template.js +9 -3
- package/templates/{template_0000 → vidfarm_template_0000}/src/template.ts +10 -3
- package/templates/vidfarm_template_0000/tmp/solobacterium-moorei-slideshow.request.json +31 -0
- /package/dist/templates/{template_0000 → vidfarm_template_0000}/src/lib/images.js +0 -0
- /package/dist/templates/{template_0000 → vidfarm_template_0000}/src/remotion/index.js +0 -0
- /package/dist/templates/{template_0000 → vidfarm_template_0000}/src/sdk.js +0 -0
- /package/dist/templates/{template_0000 → vidfarm_template_0000}/src/style-options.js +0 -0
- /package/dist/templates/{template_0000 → vidfarm_template_0000}/src/template-dna.js +0 -0
- /package/templates/{template_0000 → vidfarm_template_0000}/SKILL.md +0 -0
- /package/templates/{template_0000 → vidfarm_template_0000}/assets/Abel-Regular.ttf +0 -0
- /package/templates/{template_0000 → vidfarm_template_0000}/assets/DMSerifDisplay-Regular.ttf +0 -0
- /package/templates/{template_0000 → vidfarm_template_0000}/assets/Montserrat[wght].ttf +0 -0
- /package/templates/{template_0000 → vidfarm_template_0000}/assets/SourceCodePro[wght].ttf +0 -0
- /package/templates/{template_0000 → vidfarm_template_0000}/assets/TikTokSans-SemiBold.ttf +0 -0
- /package/templates/{template_0000 → vidfarm_template_0000}/assets/Yesteryear-Regular.ttf +0 -0
- /package/templates/{template_0000 → vidfarm_template_0000}/composition.json +0 -0
- /package/templates/{template_0000 → vidfarm_template_0000}/research/preview/.gitkeep +0 -0
- /package/templates/{template_0000 → vidfarm_template_0000}/research/source_notes.md +0 -0
- /package/templates/{template_0000 → vidfarm_template_0000}/src/lib/images.js +0 -0
- /package/templates/{template_0000 → vidfarm_template_0000}/src/lib/images.ts +0 -0
- /package/{dist/templates/template_0000 → templates/vidfarm_template_0000}/src/remotion/Root.js +0 -0
- /package/templates/{template_0000 → vidfarm_template_0000}/src/remotion/Root.tsx +0 -0
- /package/templates/{template_0000 → vidfarm_template_0000}/src/remotion/index.js +0 -0
- /package/templates/{template_0000 → vidfarm_template_0000}/src/remotion/index.tsx +0 -0
- /package/templates/{template_0000 → vidfarm_template_0000}/src/sdk.js +0 -0
- /package/templates/{template_0000 → vidfarm_template_0000}/src/style-options.js +0 -0
- /package/templates/{template_0000 → vidfarm_template_0000}/src/style-options.ts +0 -0
- /package/templates/{template_0000 → vidfarm_template_0000}/src/template-dna.js +0 -0
- /package/templates/{template_0000 → vidfarm_template_0000}/src/template-dna.ts +0 -0
- /package/templates/{template_0000 → vidfarm_template_0000}/template.config.json +0 -0
- /package/templates/{template_0000 → vidfarm_template_0000}/tsconfig.json +0 -0
package/dist/src/app.js
CHANGED
|
@@ -23,6 +23,8 @@ const templateSources = new TemplateSourceService();
|
|
|
23
23
|
const API_PREFIX = "/api/v1";
|
|
24
24
|
const USER_PREFIX = `${API_PREFIX}/user`;
|
|
25
25
|
const TEMPLATES_PREFIX = `${API_PREFIX}/templates`;
|
|
26
|
+
const ADMIN_TEMPLATES_PREFIX = `${API_PREFIX}/admin/templates`;
|
|
27
|
+
const ADMIN_CUSTOMERS_PREFIX = `${API_PREFIX}/admin/customers`;
|
|
26
28
|
const SESSION_COOKIE = "vidfarm_session";
|
|
27
29
|
const app = new Hono();
|
|
28
30
|
const otpRequestSchema = z.object({
|
|
@@ -45,6 +47,20 @@ const adminCreateUserSchema = z.object({
|
|
|
45
47
|
password: z.string().min(8),
|
|
46
48
|
name: z.string().optional()
|
|
47
49
|
});
|
|
50
|
+
const adminProvisionUserSchema = z.object({
|
|
51
|
+
email: z.string().email().optional(),
|
|
52
|
+
username: z.string().email().optional(),
|
|
53
|
+
password: z.string().min(8),
|
|
54
|
+
name: z.string().optional()
|
|
55
|
+
}).refine((value) => Boolean(value.email || value.username), {
|
|
56
|
+
message: "Provide email or username."
|
|
57
|
+
});
|
|
58
|
+
const adminCustomerAccessSchema = z.object({
|
|
59
|
+
email: z.string().email(),
|
|
60
|
+
is_developer: z.boolean().optional(),
|
|
61
|
+
is_paid_plan: z.boolean().optional(),
|
|
62
|
+
name: z.string().optional()
|
|
63
|
+
}).refine((value) => value.is_developer !== undefined || value.is_paid_plan !== undefined || value.name !== undefined, { message: "Provide at least one of is_developer, is_paid_plan, or name." });
|
|
48
64
|
const providerKeySchema = z.object({
|
|
49
65
|
provider: z.enum(["openai", "gemini", "openrouter", "perplexity"]),
|
|
50
66
|
label: z.string().optional(),
|
|
@@ -67,6 +83,15 @@ const developerPreviewPresignSchema = z.object({
|
|
|
67
83
|
content_type: z.string().trim().min(1).max(255).optional(),
|
|
68
84
|
directory: z.string().trim().max(500).optional()
|
|
69
85
|
});
|
|
86
|
+
const developerTemplateArtifactPresignSchema = z.object({
|
|
87
|
+
commit_sha: z.string().trim().min(7).max(255)
|
|
88
|
+
});
|
|
89
|
+
const developerTemplateArtifactPublishSchema = z.object({
|
|
90
|
+
commit_sha: z.string().trim().min(7).max(255),
|
|
91
|
+
artifact_sha256: z.string().trim().regex(/^[0-9a-f]{64}$/i, "artifact_sha256 must be a SHA-256 hex digest.")
|
|
92
|
+
});
|
|
93
|
+
const templateSourceStatusSchema = z.enum(["pending_review", "approved", "rejected", "disabled"]);
|
|
94
|
+
const templateReleaseStatusSchema = z.enum(["imported", "pending_approval", "approved", "rejected", "failed", "active"]);
|
|
70
95
|
const listJobsQuerySchema = z.object({
|
|
71
96
|
tracer: z.string().min(1).optional(),
|
|
72
97
|
start_time: z.string().min(1).optional(),
|
|
@@ -288,7 +313,7 @@ function getReleaseTimestamp(release) {
|
|
|
288
313
|
function getApprovedHomepageTemplates(c) {
|
|
289
314
|
const approvedReleaseByTemplateId = new Map();
|
|
290
315
|
for (const release of database.listTemplateReleases()) {
|
|
291
|
-
if (release.status !== "active" && release.status !== "
|
|
316
|
+
if (release.status !== "active" && release.status !== "approved") {
|
|
292
317
|
continue;
|
|
293
318
|
}
|
|
294
319
|
const current = approvedReleaseByTemplateId.get(release.templateId);
|
|
@@ -583,9 +608,7 @@ app.post(`${API_PREFIX}/admin/auth/whitelist`, async (c) => {
|
|
|
583
608
|
email: normalizedEmail,
|
|
584
609
|
name: existing?.name ?? null,
|
|
585
610
|
defaultWebhookUrl: existing?.defaultWebhookUrl ?? null,
|
|
586
|
-
isDeveloper: existing?.isDeveloper
|
|
587
|
-
|| config.adminEmails.includes(normalizedEmail)
|
|
588
|
-
|| config.developerEmails.includes(normalizedEmail),
|
|
611
|
+
isDeveloper: existing?.isDeveloper || config.adminEmails.includes(normalizedEmail),
|
|
589
612
|
isPaidPlan: true
|
|
590
613
|
});
|
|
591
614
|
});
|
|
@@ -606,6 +629,213 @@ app.post(`${API_PREFIX}/admin/auth/users`, async (c) => {
|
|
|
606
629
|
});
|
|
607
630
|
return c.json({ customer }, 201);
|
|
608
631
|
});
|
|
632
|
+
app.post(`${API_PREFIX}/admin/auth/provision-user-account`, async (c) => {
|
|
633
|
+
try {
|
|
634
|
+
requireSuperagency(c);
|
|
635
|
+
}
|
|
636
|
+
catch (error) {
|
|
637
|
+
return c.json({ error: error instanceof Error ? error.message : "Forbidden" }, 403);
|
|
638
|
+
}
|
|
639
|
+
const parsed = adminProvisionUserSchema.parse(await c.req.json());
|
|
640
|
+
const email = (parsed.email ?? parsed.username ?? "").trim().toLowerCase();
|
|
641
|
+
const existing = database.getCustomerByEmail(email);
|
|
642
|
+
const customer = auth.createPasswordUser({
|
|
643
|
+
email,
|
|
644
|
+
password: parsed.password,
|
|
645
|
+
name: parsed.name ?? null
|
|
646
|
+
});
|
|
647
|
+
const apiKey = getVisibleApiKey(customer.id);
|
|
648
|
+
return c.json({
|
|
649
|
+
customer,
|
|
650
|
+
credentials: {
|
|
651
|
+
email,
|
|
652
|
+
password: parsed.password,
|
|
653
|
+
api_key: apiKey
|
|
654
|
+
},
|
|
655
|
+
action: existing ? "updated" : "created"
|
|
656
|
+
}, existing ? 200 : 201);
|
|
657
|
+
});
|
|
658
|
+
app.post(`${ADMIN_CUSTOMERS_PREFIX}/access`, async (c) => {
|
|
659
|
+
try {
|
|
660
|
+
requireSuperagency(c);
|
|
661
|
+
}
|
|
662
|
+
catch (error) {
|
|
663
|
+
return c.json({ error: error instanceof Error ? error.message : "Forbidden" }, 403);
|
|
664
|
+
}
|
|
665
|
+
const parsed = adminCustomerAccessSchema.parse(await c.req.json());
|
|
666
|
+
const existing = database.getCustomerByEmail(parsed.email);
|
|
667
|
+
const customer = database.setCustomerAccessByEmail({
|
|
668
|
+
email: parsed.email,
|
|
669
|
+
isDeveloper: parsed.is_developer,
|
|
670
|
+
isPaidPlan: parsed.is_paid_plan,
|
|
671
|
+
name: parsed.name
|
|
672
|
+
});
|
|
673
|
+
return c.json({
|
|
674
|
+
customer,
|
|
675
|
+
action: existing ? "updated" : "created"
|
|
676
|
+
}, existing ? 200 : 201);
|
|
677
|
+
});
|
|
678
|
+
app.get(`${ADMIN_TEMPLATES_PREFIX}/sources`, (c) => {
|
|
679
|
+
try {
|
|
680
|
+
requireSuperagency(c);
|
|
681
|
+
}
|
|
682
|
+
catch (error) {
|
|
683
|
+
return c.json({ error: error instanceof Error ? error.message : "Forbidden" }, 403);
|
|
684
|
+
}
|
|
685
|
+
const status = c.req.query("status");
|
|
686
|
+
const parsedStatus = status ? templateSourceStatusSchema.parse(status) : undefined;
|
|
687
|
+
return c.json({ sources: templateSources.listSources(parsedStatus) });
|
|
688
|
+
});
|
|
689
|
+
app.get(`${ADMIN_TEMPLATES_PREFIX}/releases`, (c) => {
|
|
690
|
+
try {
|
|
691
|
+
requireSuperagency(c);
|
|
692
|
+
}
|
|
693
|
+
catch (error) {
|
|
694
|
+
return c.json({ error: error instanceof Error ? error.message : "Forbidden" }, 403);
|
|
695
|
+
}
|
|
696
|
+
const status = c.req.query("status");
|
|
697
|
+
const parsedStatus = status ? templateReleaseStatusSchema.parse(status) : undefined;
|
|
698
|
+
return c.json({
|
|
699
|
+
releases: templateSources.listReleases({
|
|
700
|
+
templateId: c.req.query("template_id") || undefined,
|
|
701
|
+
status: parsedStatus
|
|
702
|
+
})
|
|
703
|
+
});
|
|
704
|
+
});
|
|
705
|
+
app.get(`${ADMIN_TEMPLATES_PREFIX}/review-queue`, (c) => {
|
|
706
|
+
try {
|
|
707
|
+
requireSuperagency(c);
|
|
708
|
+
}
|
|
709
|
+
catch (error) {
|
|
710
|
+
return c.json({ error: error instanceof Error ? error.message : "Forbidden" }, 403);
|
|
711
|
+
}
|
|
712
|
+
return c.json({
|
|
713
|
+
pending_sources: templateSources.listSources("pending_review"),
|
|
714
|
+
pending_releases: templateSources.listReleases({ status: "pending_approval" })
|
|
715
|
+
});
|
|
716
|
+
});
|
|
717
|
+
app.post(`${ADMIN_TEMPLATES_PREFIX}/sources/register`, async (c) => {
|
|
718
|
+
try {
|
|
719
|
+
requireSuperagency(c);
|
|
720
|
+
}
|
|
721
|
+
catch (error) {
|
|
722
|
+
return c.json({ error: error instanceof Error ? error.message : "Forbidden" }, 403);
|
|
723
|
+
}
|
|
724
|
+
try {
|
|
725
|
+
const body = z.object({
|
|
726
|
+
template_id: z.string().regex(/^[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i, "template_id must be a UUIDv4."),
|
|
727
|
+
slug_id: z.string().min(3).regex(/^[a-z0-9_]+$/i, "slug_id must contain only letters, numbers, and underscores."),
|
|
728
|
+
repo_url: z.string().url(),
|
|
729
|
+
branch: z.string().min(1).default("production"),
|
|
730
|
+
template_module_path: z.string().regex(/(^src\/template\.ts$|(^|\/)vidfarm_template_[^/]+\/src\/template\.ts$)/i, "template_module_path must point at src/template.ts for a standalone template repo, or at a template entrypoint inside a folder that starts with vidfarm_template_, for example templates/vidfarm_template_example/src/template.ts."),
|
|
731
|
+
skill_path: z.string().min(1).optional(),
|
|
732
|
+
install_command: z.string().min(1).default("npm install"),
|
|
733
|
+
build_command: z.string().min(1).default("npm run build")
|
|
734
|
+
}).parse(await c.req.json());
|
|
735
|
+
const registration = await templateSources.registerSource({
|
|
736
|
+
templateId: body.template_id,
|
|
737
|
+
slugId: body.slug_id,
|
|
738
|
+
repoUrl: body.repo_url,
|
|
739
|
+
branch: body.branch,
|
|
740
|
+
templateModulePath: body.template_module_path,
|
|
741
|
+
skillPath: body.skill_path,
|
|
742
|
+
installCommand: body.install_command,
|
|
743
|
+
buildCommand: body.build_command
|
|
744
|
+
});
|
|
745
|
+
return c.json({
|
|
746
|
+
source: registration.source,
|
|
747
|
+
registration_action: registration.action,
|
|
748
|
+
sync: registration.sync
|
|
749
|
+
}, registration.action === "created" ? 201 : 200);
|
|
750
|
+
}
|
|
751
|
+
catch (error) {
|
|
752
|
+
if (error instanceof TemplateSourceAlreadyRegisteredError) {
|
|
753
|
+
return c.json({
|
|
754
|
+
error: error.message,
|
|
755
|
+
registration_status: error.existingSource.status,
|
|
756
|
+
existing_source: error.existingSource,
|
|
757
|
+
conflict_field: error.conflictField
|
|
758
|
+
}, 409);
|
|
759
|
+
}
|
|
760
|
+
const message = error instanceof Error ? error.message : "Unable to register template source.";
|
|
761
|
+
const status = /already exists/i.test(message) ? 409 : 400;
|
|
762
|
+
return c.json({ error: message }, status);
|
|
763
|
+
}
|
|
764
|
+
});
|
|
765
|
+
app.post(`${ADMIN_TEMPLATES_PREFIX}/sources/:sourceId/approve`, (c) => {
|
|
766
|
+
try {
|
|
767
|
+
requireSuperagency(c);
|
|
768
|
+
}
|
|
769
|
+
catch (error) {
|
|
770
|
+
return c.json({ error: error instanceof Error ? error.message : "Forbidden" }, 403);
|
|
771
|
+
}
|
|
772
|
+
return c.json({ source: templateSources.approveSource({ sourceId: c.req.param("sourceId") }) });
|
|
773
|
+
});
|
|
774
|
+
app.post(`${ADMIN_TEMPLATES_PREFIX}/sources/:sourceId/reject`, (c) => {
|
|
775
|
+
try {
|
|
776
|
+
requireSuperagency(c);
|
|
777
|
+
}
|
|
778
|
+
catch (error) {
|
|
779
|
+
return c.json({ error: error instanceof Error ? error.message : "Forbidden" }, 403);
|
|
780
|
+
}
|
|
781
|
+
return c.json({ source: templateSources.rejectSource({ sourceId: c.req.param("sourceId") }) });
|
|
782
|
+
});
|
|
783
|
+
app.post(`${ADMIN_TEMPLATES_PREFIX}/sources/:sourceId/import`, async (c) => {
|
|
784
|
+
try {
|
|
785
|
+
requireSuperagency(c);
|
|
786
|
+
}
|
|
787
|
+
catch (error) {
|
|
788
|
+
return c.json({ error: error instanceof Error ? error.message : "Forbidden" }, 403);
|
|
789
|
+
}
|
|
790
|
+
const body = z.object({
|
|
791
|
+
commit_sha: z.string().min(7).optional(),
|
|
792
|
+
artifact_manifest_key: z.string().min(1).optional()
|
|
793
|
+
}).parse(await c.req.json().catch(() => ({})));
|
|
794
|
+
const release = await templateSources.importRelease({
|
|
795
|
+
sourceId: c.req.param("sourceId"),
|
|
796
|
+
commitSha: body.commit_sha ?? null,
|
|
797
|
+
artifactManifestKey: body.artifact_manifest_key ?? null
|
|
798
|
+
});
|
|
799
|
+
return c.json({ release }, 201);
|
|
800
|
+
});
|
|
801
|
+
app.post(`${ADMIN_TEMPLATES_PREFIX}/releases/:releaseId/approve`, (c) => {
|
|
802
|
+
try {
|
|
803
|
+
requireSuperagency(c);
|
|
804
|
+
}
|
|
805
|
+
catch (error) {
|
|
806
|
+
return c.json({ error: error instanceof Error ? error.message : "Forbidden" }, 403);
|
|
807
|
+
}
|
|
808
|
+
return c.json({ release: templateSources.approveRelease({ releaseId: c.req.param("releaseId") }) });
|
|
809
|
+
});
|
|
810
|
+
app.post(`${ADMIN_TEMPLATES_PREFIX}/releases/:releaseId/reject`, (c) => {
|
|
811
|
+
try {
|
|
812
|
+
requireSuperagency(c);
|
|
813
|
+
}
|
|
814
|
+
catch (error) {
|
|
815
|
+
return c.json({ error: error instanceof Error ? error.message : "Forbidden" }, 403);
|
|
816
|
+
}
|
|
817
|
+
return c.json({ release: templateSources.rejectRelease({ releaseId: c.req.param("releaseId") }) });
|
|
818
|
+
});
|
|
819
|
+
app.post(`${ADMIN_TEMPLATES_PREFIX}/releases/:releaseId/activate`, async (c) => {
|
|
820
|
+
try {
|
|
821
|
+
requireSuperagency(c);
|
|
822
|
+
}
|
|
823
|
+
catch (error) {
|
|
824
|
+
return c.json({ error: error instanceof Error ? error.message : "Forbidden" }, 403);
|
|
825
|
+
}
|
|
826
|
+
const { release, template } = await templateSources.activateRelease({
|
|
827
|
+
releaseId: c.req.param("releaseId")
|
|
828
|
+
});
|
|
829
|
+
templateRegistry.registerRuntimeTemplate(template);
|
|
830
|
+
return c.json({
|
|
831
|
+
release,
|
|
832
|
+
template: {
|
|
833
|
+
id: template.id,
|
|
834
|
+
version: template.version,
|
|
835
|
+
description: template.about.description
|
|
836
|
+
}
|
|
837
|
+
});
|
|
838
|
+
});
|
|
609
839
|
app.post(`${USER_PREFIX}/request-otp`, async (c) => {
|
|
610
840
|
const body = otpRequestSchema.parse(await c.req.json());
|
|
611
841
|
await auth.requestOtp(body.email);
|
|
@@ -645,6 +875,12 @@ function requireDeveloper(c) {
|
|
|
645
875
|
}
|
|
646
876
|
return customer;
|
|
647
877
|
}
|
|
878
|
+
function templatePublishingAdminError() {
|
|
879
|
+
return {
|
|
880
|
+
error: "Admin access required.",
|
|
881
|
+
detail: "Reviewing, importing, approving, or activating a registered template source is platform-managed. Third-party developers can register/update source metadata and publish an exact GitHub commit for review, but only platform admins can approve or activate releases."
|
|
882
|
+
};
|
|
883
|
+
}
|
|
648
884
|
function requireSuperagency(c) {
|
|
649
885
|
const provided = c.req.header("x-superagency-key");
|
|
650
886
|
if (!config.SUPERAGENCY_KEY || provided !== config.SUPERAGENCY_KEY) {
|
|
@@ -791,7 +1027,9 @@ function serializeTemplateAbout(c, template) {
|
|
|
791
1027
|
viral_dna: template.about.viral_dna,
|
|
792
1028
|
visual_dna: template.about.visual_dna,
|
|
793
1029
|
preview_media: template.about.preview_media.map((entry) => resolveTemplateAboutMediaUrl(c, template.id, entry)),
|
|
794
|
-
link_to_original: template.about.link_to_original
|
|
1030
|
+
link_to_original: template.about.link_to_original,
|
|
1031
|
+
proposal_generator: template.about.proposal_generator,
|
|
1032
|
+
sample_prompts: template.about.sample_prompts
|
|
795
1033
|
};
|
|
796
1034
|
}
|
|
797
1035
|
function resolveTemplateAboutMediaUrl(c, templateId, entry) {
|
|
@@ -1006,6 +1244,36 @@ app.post(`${USER_PREFIX}/me/developer/preview-media/presign`, async (c) => {
|
|
|
1006
1244
|
return c.json({ error: error instanceof Error ? error.message : "Unable to create preview upload URL." }, 400);
|
|
1007
1245
|
}
|
|
1008
1246
|
});
|
|
1247
|
+
app.post(`${TEMPLATES_PREFIX}/sources/:sourceId/artifacts/presign`, async (c) => {
|
|
1248
|
+
try {
|
|
1249
|
+
requireDeveloper(c);
|
|
1250
|
+
}
|
|
1251
|
+
catch (error) {
|
|
1252
|
+
return c.json({ error: error instanceof Error ? error.message : "Forbidden" }, 403);
|
|
1253
|
+
}
|
|
1254
|
+
try {
|
|
1255
|
+
const body = developerTemplateArtifactPresignSchema.parse(await c.req.json());
|
|
1256
|
+
const artifact = await templateSources.createDeveloperArtifactUpload({
|
|
1257
|
+
sourceId: c.req.param("sourceId"),
|
|
1258
|
+
commitSha: body.commit_sha
|
|
1259
|
+
});
|
|
1260
|
+
return c.json({
|
|
1261
|
+
artifact_key: artifact.artifactKey,
|
|
1262
|
+
artifact_manifest_key: artifact.manifestKey,
|
|
1263
|
+
upload: {
|
|
1264
|
+
method: artifact.upload.method,
|
|
1265
|
+
url: artifact.upload.url,
|
|
1266
|
+
headers: artifact.upload.headers,
|
|
1267
|
+
expires_in_seconds: artifact.upload.expiresIn
|
|
1268
|
+
}
|
|
1269
|
+
});
|
|
1270
|
+
}
|
|
1271
|
+
catch (error) {
|
|
1272
|
+
const message = error instanceof Error ? error.message : "Unable to prepare template artifact upload.";
|
|
1273
|
+
const status = /not found/i.test(message) ? 404 : 400;
|
|
1274
|
+
return c.json({ error: message }, status);
|
|
1275
|
+
}
|
|
1276
|
+
});
|
|
1009
1277
|
app.get(`${USER_PREFIX}/me/jobs`, (c) => {
|
|
1010
1278
|
const query = listJobsQuerySchema.parse({
|
|
1011
1279
|
template_id: c.req.query("template_id"),
|
|
@@ -1046,7 +1314,9 @@ app.get(`${TEMPLATES_PREFIX}/sources`, (c) => {
|
|
|
1046
1314
|
catch (error) {
|
|
1047
1315
|
return c.json({ error: error instanceof Error ? error.message : "Forbidden" }, 403);
|
|
1048
1316
|
}
|
|
1049
|
-
|
|
1317
|
+
const status = c.req.query("status");
|
|
1318
|
+
const parsedStatus = status ? templateSourceStatusSchema.parse(status) : undefined;
|
|
1319
|
+
return c.json({ sources: templateSources.listSources(parsedStatus) });
|
|
1050
1320
|
});
|
|
1051
1321
|
app.post(`${TEMPLATES_PREFIX}/sources`, async (c) => {
|
|
1052
1322
|
try {
|
|
@@ -1061,7 +1331,7 @@ app.post(`${TEMPLATES_PREFIX}/sources`, async (c) => {
|
|
|
1061
1331
|
slug_id: z.string().min(3).regex(/^[a-z0-9_]+$/i, "slug_id must contain only letters, numbers, and underscores."),
|
|
1062
1332
|
repo_url: z.string().url(),
|
|
1063
1333
|
branch: z.string().min(1).default("production"),
|
|
1064
|
-
template_module_path: z.string().regex(/(^|\/)vidfarm_template_[^/]+\/src\/template\.ts
|
|
1334
|
+
template_module_path: z.string().regex(/(^src\/template\.ts$|(^|\/)vidfarm_template_[^/]+\/src\/template\.ts$)/i, "template_module_path must point at src/template.ts for a standalone template repo, or at a template entrypoint inside a folder that starts with vidfarm_template_, for example templates/vidfarm_template_example/src/template.ts."),
|
|
1065
1335
|
skill_path: z.string().min(1).optional(),
|
|
1066
1336
|
install_command: z.string().min(1).default("npm install"),
|
|
1067
1337
|
build_command: z.string().min(1).default("npm run build")
|
|
@@ -1079,7 +1349,13 @@ app.post(`${TEMPLATES_PREFIX}/sources`, async (c) => {
|
|
|
1079
1349
|
return c.json({
|
|
1080
1350
|
source: registration.source,
|
|
1081
1351
|
registration_action: registration.action,
|
|
1082
|
-
sync: registration.sync
|
|
1352
|
+
sync: registration.sync,
|
|
1353
|
+
developer_handoff: {
|
|
1354
|
+
completed: true,
|
|
1355
|
+
responsibility_boundary: "third_party_developer_done",
|
|
1356
|
+
message: "Template source registration is complete. Your responsibility stops at GitHub push plus source registration metadata. Importing, activating, and making the template live/public are platform-admin steps.",
|
|
1357
|
+
next_step_for_platform: "A Vidfarm admin can now import this source from the registered branch head or a chosen commit SHA, or the developer can publish an exact commit SHA for review."
|
|
1358
|
+
}
|
|
1083
1359
|
}, registration.action === "created" ? 201 : 200);
|
|
1084
1360
|
}
|
|
1085
1361
|
catch (error) {
|
|
@@ -1096,6 +1372,40 @@ app.post(`${TEMPLATES_PREFIX}/sources`, async (c) => {
|
|
|
1096
1372
|
return c.json({ error: message }, status);
|
|
1097
1373
|
}
|
|
1098
1374
|
});
|
|
1375
|
+
app.post(`${TEMPLATES_PREFIX}/sources/:sourceId/publish`, async (c) => {
|
|
1376
|
+
let customer;
|
|
1377
|
+
try {
|
|
1378
|
+
customer = requireDeveloper(c);
|
|
1379
|
+
}
|
|
1380
|
+
catch (error) {
|
|
1381
|
+
return c.json({ error: error instanceof Error ? error.message : "Forbidden" }, 403);
|
|
1382
|
+
}
|
|
1383
|
+
try {
|
|
1384
|
+
const body = developerTemplateArtifactPublishSchema.parse(await c.req.json());
|
|
1385
|
+
const result = await templateSources.publishArtifactRelease({
|
|
1386
|
+
sourceId: c.req.param("sourceId"),
|
|
1387
|
+
commitSha: body.commit_sha,
|
|
1388
|
+
artifactSha256: body.artifact_sha256,
|
|
1389
|
+
signedBy: customer.email
|
|
1390
|
+
});
|
|
1391
|
+
return c.json({
|
|
1392
|
+
source: result.source,
|
|
1393
|
+
release: result.release,
|
|
1394
|
+
publish_action: result.publishAction,
|
|
1395
|
+
developer_handoff: {
|
|
1396
|
+
completed: true,
|
|
1397
|
+
responsibility_boundary: "third_party_developer_done",
|
|
1398
|
+
message: "The exact GitHub commit has been submitted for platform review.",
|
|
1399
|
+
next_step_for_platform: "A Vidfarm admin reviews the certification result, approves the release, and activates it."
|
|
1400
|
+
}
|
|
1401
|
+
}, result.publishAction === "created" ? 201 : 200);
|
|
1402
|
+
}
|
|
1403
|
+
catch (error) {
|
|
1404
|
+
const message = error instanceof Error ? error.message : "Unable to publish template update.";
|
|
1405
|
+
const status = /not found/i.test(message) ? 404 : 400;
|
|
1406
|
+
return c.json({ error: message }, status);
|
|
1407
|
+
}
|
|
1408
|
+
});
|
|
1099
1409
|
app.get(`${TEMPLATES_PREFIX}/releases`, (c) => {
|
|
1100
1410
|
try {
|
|
1101
1411
|
requireAdmin(c);
|
|
@@ -1103,30 +1413,87 @@ app.get(`${TEMPLATES_PREFIX}/releases`, (c) => {
|
|
|
1103
1413
|
catch (error) {
|
|
1104
1414
|
return c.json({ error: error instanceof Error ? error.message : "Forbidden" }, 403);
|
|
1105
1415
|
}
|
|
1106
|
-
|
|
1416
|
+
const status = c.req.query("status");
|
|
1417
|
+
const parsedStatus = status ? templateReleaseStatusSchema.parse(status) : undefined;
|
|
1418
|
+
return c.json({
|
|
1419
|
+
releases: templateSources.listReleases({
|
|
1420
|
+
templateId: c.req.query("template_id") || undefined,
|
|
1421
|
+
status: parsedStatus
|
|
1422
|
+
})
|
|
1423
|
+
});
|
|
1107
1424
|
});
|
|
1108
|
-
app.
|
|
1425
|
+
app.get(`${TEMPLATES_PREFIX}/review-queue`, (c) => {
|
|
1109
1426
|
try {
|
|
1110
1427
|
requireAdmin(c);
|
|
1111
1428
|
}
|
|
1112
1429
|
catch (error) {
|
|
1113
1430
|
return c.json({ error: error instanceof Error ? error.message : "Forbidden" }, 403);
|
|
1114
1431
|
}
|
|
1432
|
+
return c.json({
|
|
1433
|
+
pending_sources: templateSources.listSources("pending_review"),
|
|
1434
|
+
pending_releases: templateSources.listReleases({ status: "pending_approval" })
|
|
1435
|
+
});
|
|
1436
|
+
});
|
|
1437
|
+
app.post(`${TEMPLATES_PREFIX}/sources/:sourceId/approve`, (c) => {
|
|
1438
|
+
try {
|
|
1439
|
+
requireAdmin(c);
|
|
1440
|
+
}
|
|
1441
|
+
catch (error) {
|
|
1442
|
+
return c.json(templatePublishingAdminError(), 403);
|
|
1443
|
+
}
|
|
1444
|
+
return c.json({ source: templateSources.approveSource({ sourceId: c.req.param("sourceId") }) });
|
|
1445
|
+
});
|
|
1446
|
+
app.post(`${TEMPLATES_PREFIX}/sources/:sourceId/reject`, (c) => {
|
|
1447
|
+
try {
|
|
1448
|
+
requireAdmin(c);
|
|
1449
|
+
}
|
|
1450
|
+
catch (error) {
|
|
1451
|
+
return c.json(templatePublishingAdminError(), 403);
|
|
1452
|
+
}
|
|
1453
|
+
return c.json({ source: templateSources.rejectSource({ sourceId: c.req.param("sourceId") }) });
|
|
1454
|
+
});
|
|
1455
|
+
app.post(`${TEMPLATES_PREFIX}/sources/:sourceId/import`, async (c) => {
|
|
1456
|
+
try {
|
|
1457
|
+
requireAdmin(c);
|
|
1458
|
+
}
|
|
1459
|
+
catch (error) {
|
|
1460
|
+
return c.json(templatePublishingAdminError(), 403);
|
|
1461
|
+
}
|
|
1115
1462
|
const body = z.object({
|
|
1116
|
-
commit_sha: z.string().min(7).optional()
|
|
1463
|
+
commit_sha: z.string().min(7).optional(),
|
|
1464
|
+
artifact_manifest_key: z.string().min(1).optional()
|
|
1117
1465
|
}).parse(await c.req.json().catch(() => ({})));
|
|
1118
1466
|
const release = await templateSources.importRelease({
|
|
1119
1467
|
sourceId: c.req.param("sourceId"),
|
|
1120
|
-
commitSha: body.commit_sha ?? null
|
|
1468
|
+
commitSha: body.commit_sha ?? null,
|
|
1469
|
+
artifactManifestKey: body.artifact_manifest_key ?? null
|
|
1121
1470
|
});
|
|
1122
1471
|
return c.json({ release }, 201);
|
|
1123
1472
|
});
|
|
1473
|
+
app.post(`${TEMPLATES_PREFIX}/releases/:releaseId/approve`, (c) => {
|
|
1474
|
+
try {
|
|
1475
|
+
requireAdmin(c);
|
|
1476
|
+
}
|
|
1477
|
+
catch (error) {
|
|
1478
|
+
return c.json(templatePublishingAdminError(), 403);
|
|
1479
|
+
}
|
|
1480
|
+
return c.json({ release: templateSources.approveRelease({ releaseId: c.req.param("releaseId") }) });
|
|
1481
|
+
});
|
|
1482
|
+
app.post(`${TEMPLATES_PREFIX}/releases/:releaseId/reject`, (c) => {
|
|
1483
|
+
try {
|
|
1484
|
+
requireAdmin(c);
|
|
1485
|
+
}
|
|
1486
|
+
catch (error) {
|
|
1487
|
+
return c.json(templatePublishingAdminError(), 403);
|
|
1488
|
+
}
|
|
1489
|
+
return c.json({ release: templateSources.rejectRelease({ releaseId: c.req.param("releaseId") }) });
|
|
1490
|
+
});
|
|
1124
1491
|
app.post(`${TEMPLATES_PREFIX}/releases/:releaseId/activate`, async (c) => {
|
|
1125
1492
|
try {
|
|
1126
1493
|
requireAdmin(c);
|
|
1127
1494
|
}
|
|
1128
1495
|
catch (error) {
|
|
1129
|
-
return c.json(
|
|
1496
|
+
return c.json(templatePublishingAdminError(), 403);
|
|
1130
1497
|
}
|
|
1131
1498
|
const { release, template } = await templateSources.activateRelease({
|
|
1132
1499
|
releaseId: c.req.param("releaseId")
|