@mevdragon/vidfarm-devcli 0.2.11 → 0.2.13

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.
Files changed (199) hide show
  1. package/.env.example +37 -1
  2. package/README.md +27 -183
  3. package/SKILL.director.md +293 -0
  4. package/SKILL.platform.md +312 -0
  5. package/auto-create-hyperframe-templates/AUTO_CREATE_HYPERFRAME_TEMPLATES.md +275 -0
  6. package/auto-create-hyperframe-templates/extractor-system-prompt.md +128 -0
  7. package/auto-create-hyperframe-templates/input-manifest.schema.json +56 -0
  8. package/auto-create-hyperframe-templates/lambda-memory-estimator.md +118 -0
  9. package/auto-create-hyperframe-templates/production-graph.schema.json +314 -0
  10. package/auto-create-hyperframe-templates/runbook.md +198 -0
  11. package/auto-create-hyperframe-templates/scripts/create-hyperframe-template.mjs +368 -0
  12. package/auto-create-hyperframe-templates/template-plan.schema.json +311 -0
  13. package/auto-create-hyperframe-templates/template-planner-prompt.md +144 -0
  14. package/auto-create-templates/AUTO_CREATE_TEMPLATES.md +124 -0
  15. package/auto-create-templates/extractor-system-prompt.md +8 -2
  16. package/auto-create-templates/template-plan.schema.json +73 -2
  17. package/auto-create-templates/template-planner-prompt.md +63 -1
  18. package/dist/src/account-pages-legacy.js +361 -89
  19. package/dist/src/account-pages-legacy.js.map +1 -0
  20. package/dist/src/account-pages.js +13 -3
  21. package/dist/src/account-pages.js.map +1 -0
  22. package/dist/src/app.js +7060 -946
  23. package/dist/src/app.js.map +1 -0
  24. package/dist/src/cli.js +111 -29
  25. package/dist/src/cli.js.map +1 -0
  26. package/dist/src/composition-runtime.js +581 -0
  27. package/dist/src/composition-runtime.js.map +1 -0
  28. package/dist/src/config.js +69 -11
  29. package/dist/src/config.js.map +1 -0
  30. package/dist/src/context.js +280 -21
  31. package/dist/src/context.js.map +1 -0
  32. package/dist/src/dev-app-legacy.js +18 -17
  33. package/dist/src/dev-app-legacy.js.map +1 -0
  34. package/dist/src/dev-app.js +1 -0
  35. package/dist/src/dev-app.js.map +1 -0
  36. package/dist/src/domain.js +2 -1
  37. package/dist/src/domain.js.map +1 -0
  38. package/dist/src/editor-chat-history.js +10 -0
  39. package/dist/src/editor-chat-history.js.map +1 -0
  40. package/dist/src/editor-chat.js +133 -24
  41. package/dist/src/editor-chat.js.map +1 -0
  42. package/dist/src/frontend/flockposter-cache-store.js +10 -2
  43. package/dist/src/frontend/flockposter-cache-store.js.map +1 -0
  44. package/dist/src/frontend/homepage-client.js +6 -3
  45. package/dist/src/frontend/homepage-client.js.map +1 -0
  46. package/dist/src/frontend/homepage-shared.js +1 -0
  47. package/dist/src/frontend/homepage-shared.js.map +1 -0
  48. package/dist/src/frontend/homepage-store.js +1 -0
  49. package/dist/src/frontend/homepage-store.js.map +1 -0
  50. package/dist/src/frontend/homepage-view.js +131 -18
  51. package/dist/src/frontend/homepage-view.js.map +1 -0
  52. package/dist/src/frontend/page-runtime-client.js +5 -2
  53. package/dist/src/frontend/page-runtime-client.js.map +1 -0
  54. package/dist/src/frontend/page-runtime-store.js +1 -0
  55. package/dist/src/frontend/page-runtime-store.js.map +1 -0
  56. package/dist/src/frontend/sentry.js +42 -0
  57. package/dist/src/frontend/sentry.js.map +1 -0
  58. package/dist/src/frontend/template-editor-chat.js +1719 -213
  59. package/dist/src/frontend/template-editor-chat.js.map +1 -0
  60. package/dist/src/help-page.js +333 -0
  61. package/dist/src/help-page.js.map +1 -0
  62. package/dist/src/homepage.js +169 -47
  63. package/dist/src/homepage.js.map +1 -0
  64. package/dist/src/hyperframes/composition.js +180 -0
  65. package/dist/src/hyperframes/composition.js.map +1 -0
  66. package/dist/src/index.js +1 -0
  67. package/dist/src/index.js.map +1 -0
  68. package/dist/src/instrument.js +30 -0
  69. package/dist/src/instrument.js.map +1 -0
  70. package/dist/src/lib/crypto.js +1 -0
  71. package/dist/src/lib/crypto.js.map +1 -0
  72. package/dist/src/lib/dev-log.js +54 -0
  73. package/dist/src/lib/dev-log.js.map +1 -0
  74. package/dist/src/lib/display-name.js +11 -0
  75. package/dist/src/lib/display-name.js.map +1 -0
  76. package/dist/src/lib/ids.js +21 -1
  77. package/dist/src/lib/ids.js.map +1 -0
  78. package/dist/src/lib/images.js +1 -0
  79. package/dist/src/lib/images.js.map +1 -0
  80. package/dist/src/lib/json.js +1 -0
  81. package/dist/src/lib/json.js.map +1 -0
  82. package/dist/src/lib/template-dna.js +1 -0
  83. package/dist/src/lib/template-dna.js.map +1 -0
  84. package/dist/src/lib/template-paths.js +1 -0
  85. package/dist/src/lib/template-paths.js.map +1 -0
  86. package/dist/src/lib/template-style-options.js +40 -2
  87. package/dist/src/lib/template-style-options.js.map +1 -0
  88. package/dist/src/lib/time.js +1 -0
  89. package/dist/src/lib/time.js.map +1 -0
  90. package/dist/src/lib/video-quality-harness.js +60 -0
  91. package/dist/src/lib/video-quality-harness.js.map +1 -0
  92. package/dist/src/page-runtime.js +1 -0
  93. package/dist/src/page-runtime.js.map +1 -0
  94. package/dist/src/page-shell.js +1501 -49
  95. package/dist/src/page-shell.js.map +1 -0
  96. package/dist/src/primitive-context.js +144 -8
  97. package/dist/src/primitive-context.js.map +1 -0
  98. package/dist/src/primitive-registry.js +1919 -78
  99. package/dist/src/primitive-registry.js.map +1 -0
  100. package/dist/src/primitive-sdk.js +1 -0
  101. package/dist/src/primitive-sdk.js.map +1 -0
  102. package/dist/src/primitives/remotion/html-image.js +3 -1
  103. package/dist/src/primitives/remotion/html-image.js.map +1 -0
  104. package/dist/src/primitives/remotion/media-slideshow.js +60 -0
  105. package/dist/src/primitives/remotion/media-slideshow.js.map +1 -0
  106. package/dist/src/react-page-shell.js +1 -0
  107. package/dist/src/react-page-shell.js.map +1 -0
  108. package/dist/src/ready-post-schedule-component.js +1 -0
  109. package/dist/src/ready-post-schedule-component.js.map +1 -0
  110. package/dist/src/registry.js +237 -24
  111. package/dist/src/registry.js.map +1 -0
  112. package/dist/src/runtime.js +3 -0
  113. package/dist/src/runtime.js.map +1 -0
  114. package/dist/src/services/api-call-history.js +4 -0
  115. package/dist/src/services/api-call-history.js.map +1 -0
  116. package/dist/src/services/auth.js +40 -50
  117. package/dist/src/services/auth.js.map +1 -0
  118. package/dist/src/services/billing.js +20 -44
  119. package/dist/src/services/billing.js.map +1 -0
  120. package/dist/src/services/chat-threads.js +10 -6
  121. package/dist/src/services/chat-threads.js.map +1 -0
  122. package/dist/src/services/fork-access.js +94 -0
  123. package/dist/src/services/fork-access.js.map +1 -0
  124. package/dist/src/services/ghostcut.js +132 -0
  125. package/dist/src/services/ghostcut.js.map +1 -0
  126. package/dist/src/services/hyperframes.js +1014 -0
  127. package/dist/src/services/hyperframes.js.map +1 -0
  128. package/dist/src/services/job-capacity.js +14 -0
  129. package/dist/src/services/job-capacity.js.map +1 -0
  130. package/dist/src/services/job-logs.js +4 -0
  131. package/dist/src/services/job-logs.js.map +1 -0
  132. package/dist/src/services/jobs.js +99 -91
  133. package/dist/src/services/jobs.js.map +1 -0
  134. package/dist/src/services/media-processing.js +743 -0
  135. package/dist/src/services/media-processing.js.map +1 -0
  136. package/dist/src/services/primitive-media-lambda.js +280 -0
  137. package/dist/src/services/primitive-media-lambda.js.map +1 -0
  138. package/dist/src/services/providers.js +1560 -178
  139. package/dist/src/services/providers.js.map +1 -0
  140. package/dist/src/services/rate-limits.js +3 -2
  141. package/dist/src/services/rate-limits.js.map +1 -0
  142. package/dist/src/services/remotion.js +495 -92
  143. package/dist/src/services/remotion.js.map +1 -0
  144. package/dist/src/services/serverless-auth.js +374 -0
  145. package/dist/src/services/serverless-auth.js.map +1 -0
  146. package/dist/src/services/serverless-jobs.js +1074 -0
  147. package/dist/src/services/serverless-jobs.js.map +1 -0
  148. package/dist/src/services/serverless-provider-keys.js +401 -0
  149. package/dist/src/services/serverless-provider-keys.js.map +1 -0
  150. package/dist/src/services/serverless-records.js +1086 -0
  151. package/dist/src/services/serverless-records.js.map +1 -0
  152. package/dist/src/services/serverless-template-configs.js +67 -0
  153. package/dist/src/services/serverless-template-configs.js.map +1 -0
  154. package/dist/src/services/storage.js +170 -35
  155. package/dist/src/services/storage.js.map +1 -0
  156. package/dist/src/services/template-certification.js +295 -3
  157. package/dist/src/services/template-certification.js.map +1 -0
  158. package/dist/src/services/template-loader.js +45 -1
  159. package/dist/src/services/template-loader.js.map +1 -0
  160. package/dist/src/services/template-runtime-bundles.js +217 -0
  161. package/dist/src/services/template-runtime-bundles.js.map +1 -0
  162. package/dist/src/services/template-sources.js +452 -87
  163. package/dist/src/services/template-sources.js.map +1 -0
  164. package/dist/src/services/video-normalization.js +2 -0
  165. package/dist/src/services/video-normalization.js.map +1 -0
  166. package/dist/src/services/webhooks.js +7 -6
  167. package/dist/src/services/webhooks.js.map +1 -0
  168. package/dist/src/template-editor-pages.js +5637 -1322
  169. package/dist/src/template-editor-pages.js.map +1 -0
  170. package/dist/src/template-editor-shell.js +1376 -170
  171. package/dist/src/template-editor-shell.js.map +1 -0
  172. package/dist/src/template-sdk.js +1 -0
  173. package/dist/src/template-sdk.js.map +1 -0
  174. package/dist/src/worker.js +11 -226
  175. package/dist/src/worker.js.map +1 -0
  176. package/package.json +47 -12
  177. package/public/assets/homepage-client-app.js +34 -8
  178. package/public/assets/page-runtime-client-app.js +44 -18
  179. package/readme.secret.md +89 -0
  180. package/templates/vidfarm_template_0000/README.md +106 -0
  181. package/templates/vidfarm_template_0000/SKILL.md +266 -0
  182. package/templates/vidfarm_template_0000/assets/Abel-Regular.ttf +0 -0
  183. package/templates/vidfarm_template_0000/assets/DMSerifDisplay-Regular.ttf +0 -0
  184. package/templates/vidfarm_template_0000/assets/Montserrat[wght].ttf +0 -0
  185. package/templates/vidfarm_template_0000/assets/SourceCodePro[wght].ttf +0 -0
  186. package/templates/vidfarm_template_0000/assets/TikTokSans-SemiBold.ttf +0 -0
  187. package/templates/vidfarm_template_0000/assets/Yesteryear-Regular.ttf +0 -0
  188. package/templates/vidfarm_template_0000/composition.json +11 -0
  189. package/templates/vidfarm_template_0000/src/lib/images.js +221 -0
  190. package/templates/vidfarm_template_0000/src/remotion/Root.js +33 -0
  191. package/templates/vidfarm_template_0000/src/remotion/index.js +3 -0
  192. package/templates/vidfarm_template_0000/src/sdk.js +3 -0
  193. package/templates/vidfarm_template_0000/src/style-options.js +200 -0
  194. package/templates/vidfarm_template_0000/src/template-dna.js +9 -0
  195. package/templates/vidfarm_template_0000/src/template.js +1566 -0
  196. package/templates/vidfarm_template_0000/template.config.json +21 -0
  197. package/GETTING_STARTED.developers.md +0 -87
  198. package/SKILL.developer.md +0 -1177
  199. package/dist/src/db.js +0 -2007
@@ -1,15 +1,16 @@
1
1
  import { createHash, createHmac } from "node:crypto";
2
- import { existsSync, mkdirSync, readFileSync, rmSync, writeFileSync } from "node:fs";
3
- import os from "node:os";
2
+ import { existsSync, mkdirSync, readdirSync, readFileSync, rmSync, statSync, symlinkSync, writeFileSync } from "node:fs";
4
3
  import path from "node:path";
5
4
  import { execFile } from "node:child_process";
6
5
  import { promisify } from "node:util";
6
+ import { gunzipSync, gzipSync } from "node:zlib";
7
7
  import { config } from "../config.js";
8
- import { database } from "../db.js";
8
+ import { serverlessRecords } from "./serverless-records.js";
9
9
  import { createId } from "../lib/ids.js";
10
10
  import { defaultSkillPathForTemplateModule, deriveTemplateRootDirFromModulePath } from "../lib/template-paths.js";
11
11
  import { nowIso } from "../lib/time.js";
12
12
  import { loadTemplateFromModule } from "./template-loader.js";
13
+ import { loadTemplateFromRuntimeRelease } from "./template-runtime-bundles.js";
13
14
  import { TemplateCertificationService } from "./template-certification.js";
14
15
  import { StorageService } from "./storage.js";
15
16
  const execFileAsync = promisify(execFile);
@@ -26,16 +27,16 @@ export class TemplateSourceAlreadyRegisteredError extends Error {
26
27
  }
27
28
  export class TemplateSourceService {
28
29
  certification = new TemplateCertificationService();
29
- listSources(input) {
30
- return database.listTemplateSources(input);
30
+ async listSources(input) {
31
+ return await serverlessRecords.listTemplateSources(input);
31
32
  }
32
- listReleases(input) {
33
- return database.listTemplateReleases(input);
33
+ async listReleases(input) {
34
+ return await serverlessRecords.listTemplateReleases(input);
34
35
  }
35
36
  async registerSource(input) {
36
37
  deriveTemplateRootDirFromModulePath(input.templateModulePath);
37
38
  const branch = input.branch ?? "production";
38
- const existingByTemplateId = database.getTemplateSourceByTemplateId(input.templateId);
39
+ const existingByTemplateId = await serverlessRecords.getTemplateSourceByTemplateId(input.templateId);
39
40
  if (existingByTemplateId && existingByTemplateId.slugId !== input.slugId) {
40
41
  throw new TemplateSourceAlreadyRegisteredError({
41
42
  message: `Template ${input.templateId} is already registered with slug ${existingByTemplateId.slugId}.`,
@@ -43,7 +44,7 @@ export class TemplateSourceService {
43
44
  conflictField: "template_id"
44
45
  });
45
46
  }
46
- const existingBySlugId = database.getTemplateSourceBySlugId(input.slugId);
47
+ const existingBySlugId = await serverlessRecords.getTemplateSourceBySlugId(input.slugId);
47
48
  if (existingBySlugId && existingBySlugId.templateId !== input.templateId) {
48
49
  throw new TemplateSourceAlreadyRegisteredError({
49
50
  message: `Template slug ${input.slugId} is already registered to template ${existingBySlugId.templateId}.`,
@@ -51,7 +52,7 @@ export class TemplateSourceService {
51
52
  conflictField: "slug_id"
52
53
  });
53
54
  }
54
- const existingByLocation = database.getTemplateSourceByLocation(input.repoUrl, branch, input.templateModulePath);
55
+ const existingByLocation = await serverlessRecords.getTemplateSourceByLocation(input.repoUrl, branch, input.templateModulePath);
55
56
  if (existingByLocation &&
56
57
  (existingByLocation.templateId !== input.templateId || existingByLocation.slugId !== input.slugId)) {
57
58
  throw new TemplateSourceAlreadyRegisteredError({
@@ -72,7 +73,7 @@ export class TemplateSourceService {
72
73
  existingSource.installCommand !== installCommand ||
73
74
  existingSource.buildCommand !== buildCommand;
74
75
  const nextSource = metadataChanged
75
- ? database.updateTemplateSource({
76
+ ? await serverlessRecords.updateTemplateSource({
76
77
  id: existingSource.id,
77
78
  repoUrl: input.repoUrl,
78
79
  branch,
@@ -90,7 +91,7 @@ export class TemplateSourceService {
90
91
  };
91
92
  }
92
93
  try {
93
- const source = database.createTemplateSource({
94
+ const source = await serverlessRecords.createTemplateSource({
94
95
  id: createId("tsrc"),
95
96
  templateId: input.templateId,
96
97
  slugId: input.slugId,
@@ -109,9 +110,9 @@ export class TemplateSourceService {
109
110
  };
110
111
  }
111
112
  catch (error) {
112
- const existingSource = database.getTemplateSourceByTemplateId(input.templateId) ??
113
- database.getTemplateSourceBySlugId(input.slugId) ??
114
- database.getTemplateSourceByLocation(input.repoUrl, branch, input.templateModulePath);
113
+ const existingSource = await serverlessRecords.getTemplateSourceByTemplateId(input.templateId) ??
114
+ await serverlessRecords.getTemplateSourceBySlugId(input.slugId) ??
115
+ await serverlessRecords.getTemplateSourceByLocation(input.repoUrl, branch, input.templateModulePath);
115
116
  if (existingSource) {
116
117
  throw new TemplateSourceAlreadyRegisteredError({
117
118
  message: `Template registration already exists with status ${existingSource.status}.`,
@@ -127,7 +128,7 @@ export class TemplateSourceService {
127
128
  }
128
129
  }
129
130
  async importRelease(input) {
130
- const source = database.getTemplateSource(input.sourceId);
131
+ const source = await serverlessRecords.getTemplateSource(input.sourceId);
131
132
  if (!source) {
132
133
  throw new Error("Template source not found.");
133
134
  }
@@ -138,26 +139,42 @@ export class TemplateSourceService {
138
139
  const checkoutPath = path.join(config.TEMPLATE_SOURCE_ROOT, source.templateId, commitSha);
139
140
  const skillPath = path.join(checkoutPath, artifactManifest?.skill_path ?? source.skillPath);
140
141
  const authRepoUrl = this.resolveGitRemoteUrl(source.repoUrl);
141
- if (!existsSync(checkoutPath)) {
142
+ const importReadyMarkerPath = path.join(checkoutPath, ".vidfarm-import-ready");
143
+ if (!existsSync(importReadyMarkerPath)) {
144
+ rmSync(checkoutPath, { recursive: true, force: true });
142
145
  mkdirSync(path.dirname(checkoutPath), { recursive: true });
143
- if (artifactManifest) {
144
- await this.extractArtifactToCheckout({
145
- checkoutPath,
146
- artifactKey: artifactManifest.artifact_key,
147
- expectedSha256: artifactManifest.artifact_sha256
148
- });
149
- }
150
- else {
151
- await this.runShell(["git", "clone", "--branch", source.branch, authRepoUrl, checkoutPath], process.cwd());
152
- await this.runShell(["git", "checkout", commitSha], checkoutPath);
153
- }
154
- if (source.installCommand.trim()) {
155
- await this.runCommandString(source.installCommand, checkoutPath);
146
+ try {
147
+ if (artifactManifest) {
148
+ await this.extractArtifactToCheckout({
149
+ checkoutPath,
150
+ artifactKey: artifactManifest.artifact_key,
151
+ expectedSha256: artifactManifest.artifact_sha256
152
+ });
153
+ }
154
+ else {
155
+ await this.runShell(["git", "clone", "--branch", source.branch, authRepoUrl, checkoutPath], process.cwd());
156
+ await this.runShell(["git", "checkout", commitSha], checkoutPath);
157
+ }
158
+ if (source.installCommand.trim()) {
159
+ await this.runCommandString(source.installCommand, checkoutPath);
160
+ }
161
+ if (source.buildCommand.trim()) {
162
+ await this.runCommandString(source.buildCommand, checkoutPath);
163
+ }
164
+ writeFileSync(importReadyMarkerPath, JSON.stringify({
165
+ source_id: source.id,
166
+ template_id: source.templateId,
167
+ commit_sha: commitSha,
168
+ artifact_manifest_key: input.artifactManifestKey ?? null,
169
+ imported_at: nowIso()
170
+ }, null, 2));
156
171
  }
157
- if (source.buildCommand.trim()) {
158
- await this.runCommandString(source.buildCommand, checkoutPath);
172
+ catch (error) {
173
+ rmSync(checkoutPath, { recursive: true, force: true });
174
+ throw error;
159
175
  }
160
176
  }
177
+ linkSharedNodeModules(checkoutPath);
161
178
  const modulePath = this.resolveImportableModulePath(checkoutPath, source.templateModulePath);
162
179
  const template = await loadTemplateFromModule(modulePath);
163
180
  if (template.id !== source.templateId) {
@@ -166,9 +183,14 @@ export class TemplateSourceService {
166
183
  if (template.slugId !== source.slugId) {
167
184
  throw new Error(`Imported template slug_id ${template.slugId} does not match source slug_id ${source.slugId}.`);
168
185
  }
169
- const certificationReport = await this.certification.certify({ template, skillPath });
186
+ const certificationReport = await this.certification.certify({ template, skillPath, modulePath });
170
187
  const status = certificationReport.passed ? "pending_approval" : "failed";
171
- return database.createTemplateRelease({
188
+ const runtimeArtifact = await this.createRuntimeArtifact({
189
+ source,
190
+ commitSha,
191
+ checkoutPath
192
+ });
193
+ const release = await serverlessRecords.createTemplateRelease({
172
194
  id: createId("trel"),
173
195
  sourceId: source.id,
174
196
  templateId: source.templateId,
@@ -177,15 +199,21 @@ export class TemplateSourceService {
177
199
  checkoutPath,
178
200
  skillPath,
179
201
  modulePath,
202
+ runtimeModulePath: path.relative(checkoutPath, modulePath).split(path.sep).join("/"),
180
203
  artifactManifestKey: input.artifactManifestKey ?? null,
181
204
  artifactKey: artifactManifest?.artifact_key ?? null,
205
+ runtimeArtifactKey: runtimeArtifact.key,
206
+ runtimeArtifactSha256: runtimeArtifact.sha256,
207
+ templateSnapshot: createTemplateReleaseSnapshot(template),
182
208
  status,
183
209
  certificationReport,
184
210
  activatedAt: null
185
211
  });
212
+ this.pruneCheckoutInstallArtifacts(checkoutPath);
213
+ return release;
186
214
  }
187
215
  async publishRelease(input) {
188
- const source = database.getTemplateSource(input.sourceId);
216
+ const source = await serverlessRecords.getTemplateSource(input.sourceId);
189
217
  if (!source) {
190
218
  throw new Error("Template source not found.");
191
219
  }
@@ -196,7 +224,7 @@ export class TemplateSourceService {
196
224
  if (!trimmedCommitSha) {
197
225
  throw new Error("commit_sha is required.");
198
226
  }
199
- const existingRelease = database.getTemplateReleaseBySourceAndCommit(source.id, trimmedCommitSha);
227
+ const existingRelease = await serverlessRecords.getTemplateReleaseBySourceAndCommit(source.id, trimmedCommitSha);
200
228
  if (existingRelease &&
201
229
  (existingRelease.status === "pending_approval"
202
230
  || existingRelease.status === "approved"
@@ -218,7 +246,7 @@ export class TemplateSourceService {
218
246
  };
219
247
  }
220
248
  async publishArtifactRelease(input) {
221
- const source = database.getTemplateSource(input.sourceId);
249
+ const source = await serverlessRecords.getTemplateSource(input.sourceId);
222
250
  if (!source) {
223
251
  throw new Error("Template source not found.");
224
252
  }
@@ -233,7 +261,7 @@ export class TemplateSourceService {
233
261
  if (!/^[0-9a-f]{64}$/i.test(trimmedArtifactSha256)) {
234
262
  throw new Error("artifact_sha256 must be a 64-character hexadecimal SHA-256 hash.");
235
263
  }
236
- const existingRelease = database.getTemplateReleaseBySourceAndCommit(source.id, trimmedCommitSha);
264
+ const existingRelease = await serverlessRecords.getTemplateReleaseBySourceAndCommit(source.id, trimmedCommitSha);
237
265
  if (existingRelease &&
238
266
  (existingRelease.status === "pending_approval"
239
267
  || existingRelease.status === "approved"
@@ -261,7 +289,7 @@ export class TemplateSourceService {
261
289
  };
262
290
  }
263
291
  async createDeveloperArtifactUpload(input) {
264
- const source = database.getTemplateSource(input.sourceId);
292
+ const source = await serverlessRecords.getTemplateSource(input.sourceId);
265
293
  if (!source) {
266
294
  throw new Error("Template source not found.");
267
295
  }
@@ -275,7 +303,6 @@ export class TemplateSourceService {
275
303
  const artifact = this.buildArtifactKeys(source, trimmedCommitSha);
276
304
  const upload = await storage.createWriteUrl(artifact.artifactKey, {
277
305
  contentType: "application/gzip",
278
- publicRead: false,
279
306
  expiresIn: 3600
280
307
  });
281
308
  return {
@@ -283,12 +310,12 @@ export class TemplateSourceService {
283
310
  upload
284
311
  };
285
312
  }
286
- approveSource(input) {
287
- const source = database.getTemplateSource(input.sourceId);
313
+ async approveSource(input) {
314
+ const source = await serverlessRecords.getTemplateSource(input.sourceId);
288
315
  if (!source) {
289
316
  throw new Error("Template source not found.");
290
317
  }
291
- return database.updateTemplateSource({
318
+ return await serverlessRecords.updateTemplateSource({
292
319
  id: source.id,
293
320
  repoUrl: source.repoUrl,
294
321
  branch: source.branch,
@@ -299,12 +326,12 @@ export class TemplateSourceService {
299
326
  status: "approved"
300
327
  });
301
328
  }
302
- rejectSource(input) {
303
- const source = database.getTemplateSource(input.sourceId);
329
+ async rejectSource(input) {
330
+ const source = await serverlessRecords.getTemplateSource(input.sourceId);
304
331
  if (!source) {
305
332
  throw new Error("Template source not found.");
306
333
  }
307
- return database.updateTemplateSource({
334
+ return await serverlessRecords.updateTemplateSource({
308
335
  id: source.id,
309
336
  repoUrl: source.repoUrl,
310
337
  branch: source.branch,
@@ -315,8 +342,8 @@ export class TemplateSourceService {
315
342
  status: "rejected"
316
343
  });
317
344
  }
318
- approveRelease(input) {
319
- const release = database.getTemplateRelease(input.releaseId);
345
+ async approveRelease(input) {
346
+ const release = await serverlessRecords.getTemplateRelease(input.releaseId);
320
347
  if (!release) {
321
348
  throw new Error("Template release not found.");
322
349
  }
@@ -326,58 +353,63 @@ export class TemplateSourceService {
326
353
  if (release.status === "active") {
327
354
  return release;
328
355
  }
329
- database.updateTemplateReleaseStatus({
330
- id: release.id,
356
+ await serverlessRecords.updateTemplateReleaseStatus({
357
+ releaseId: release.id,
331
358
  status: "approved",
332
359
  certificationReport: release.certificationReport,
333
360
  activatedAt: null
334
361
  });
335
- return database.getTemplateRelease(release.id);
362
+ return await serverlessRecords.getTemplateRelease(release.id);
336
363
  }
337
- rejectRelease(input) {
338
- const release = database.getTemplateRelease(input.releaseId);
364
+ async rejectRelease(input) {
365
+ const release = await serverlessRecords.getTemplateRelease(input.releaseId);
339
366
  if (!release) {
340
367
  throw new Error("Template release not found.");
341
368
  }
342
- database.updateTemplateReleaseStatus({
343
- id: release.id,
369
+ await serverlessRecords.updateTemplateReleaseStatus({
370
+ releaseId: release.id,
344
371
  status: "rejected",
345
372
  certificationReport: release.certificationReport,
346
373
  activatedAt: null
347
374
  });
348
- return database.getTemplateRelease(release.id);
375
+ return await serverlessRecords.getTemplateRelease(release.id);
349
376
  }
350
377
  async activateRelease(input) {
351
- const release = database.getTemplateRelease(input.releaseId);
378
+ const release = await serverlessRecords.getTemplateRelease(input.releaseId);
352
379
  if (!release) {
353
380
  throw new Error("Template release not found.");
354
381
  }
355
382
  if (release.status !== "approved" && release.status !== "active") {
356
383
  throw new Error("Only approved releases can be activated.");
357
384
  }
358
- const template = await loadTemplateFromModule(release.modulePath);
385
+ const template = release.runtimeArtifactKey
386
+ ? await loadTemplateFromRuntimeRelease(release)
387
+ : await loadTemplateFromModule(release.modulePath);
359
388
  const certificationReport = release.certificationReport ?? await this.certification.certify({
360
389
  template,
361
- skillPath: release.skillPath
390
+ skillPath: release.skillPath,
391
+ modulePath: release.modulePath
362
392
  });
363
393
  if (!certificationReport.passed) {
364
- database.updateTemplateReleaseStatus({
365
- id: release.id,
394
+ await serverlessRecords.updateTemplateReleaseStatus({
395
+ releaseId: release.id,
366
396
  status: "failed",
367
397
  certificationReport
368
398
  });
369
399
  throw new Error("Template release failed certification and cannot be activated.");
370
400
  }
371
401
  await this.syncTemplateAboutAssets({ release, template });
372
- database.clearActiveTemplateReleases(release.templateId);
373
- database.updateTemplateReleaseStatus({
374
- id: release.id,
402
+ await serverlessRecords.clearActiveTemplateReleases(release.templateId);
403
+ await serverlessRecords.updateTemplateReleaseStatus({
404
+ releaseId: release.id,
375
405
  status: "active",
376
406
  certificationReport,
377
407
  activatedAt: nowIso()
378
408
  });
409
+ const activeRelease = await serverlessRecords.getTemplateRelease(release.id);
410
+ await serverlessRecords.putActiveTemplateRelease(activeRelease);
379
411
  return {
380
- release: database.getTemplateRelease(release.id),
412
+ release: activeRelease,
381
413
  template: {
382
414
  ...template,
383
415
  skillPath: release.skillPath
@@ -385,11 +417,11 @@ export class TemplateSourceService {
385
417
  };
386
418
  }
387
419
  async getSourceSyncStatus(sourceId) {
388
- const source = database.getTemplateSource(sourceId);
420
+ const source = await serverlessRecords.getTemplateSource(sourceId);
389
421
  if (!source) {
390
422
  throw new Error("Template source not found.");
391
423
  }
392
- const latestRelease = database.getLatestTemplateReleaseForSource(source.id);
424
+ const latestRelease = await serverlessRecords.getLatestTemplateReleaseForSource(source.id);
393
425
  try {
394
426
  const headCommitSha = await this.resolveBranchHead(source.repoUrl, source.branch);
395
427
  return {
@@ -419,22 +451,64 @@ export class TemplateSourceService {
419
451
  return line.split(/\s+/)[0];
420
452
  }
421
453
  async runShell(args, cwd) {
422
- await execFileAsync(args[0], args.slice(1), { cwd });
454
+ try {
455
+ await execFileAsync(args[0], args.slice(1), { cwd });
456
+ }
457
+ catch (error) {
458
+ throw new Error(formatCommandFailure(args.join(" "), cwd, error));
459
+ }
423
460
  }
424
461
  async runCommandString(command, cwd) {
425
- await execFileAsync("/bin/sh", ["-lc", command], {
426
- cwd,
427
- env: {
428
- ...process.env,
429
- NODE_ENV: "development",
430
- npm_config_production: "false",
431
- NPM_CONFIG_PRODUCTION: "false",
432
- NPM_CONFIG_INCLUDE: "dev"
433
- }
434
- });
462
+ const scratchDir = path.join(config.TEMPLATE_SOURCE_ROOT, ".command-scratch", createId("cmd"));
463
+ const homeDir = path.join(scratchDir, "home");
464
+ const npmCacheDir = path.join(scratchDir, "npm-cache");
465
+ const tempDir = path.join(scratchDir, "tmp");
466
+ mkdirSync(homeDir, { recursive: true });
467
+ mkdirSync(npmCacheDir, { recursive: true });
468
+ mkdirSync(tempDir, { recursive: true });
469
+ rmSync(path.join(config.TEMPLATE_SOURCE_ROOT, "npm-cache"), { recursive: true, force: true });
470
+ try {
471
+ await execFileAsync("/bin/sh", ["-lc", command], {
472
+ cwd,
473
+ env: {
474
+ ...process.env,
475
+ HOME: homeDir,
476
+ TMPDIR: tempDir,
477
+ TMP: tempDir,
478
+ TEMP: tempDir,
479
+ npm_config_cache: npmCacheDir,
480
+ NPM_CONFIG_CACHE: npmCacheDir,
481
+ npm_config_tmp: tempDir,
482
+ NPM_CONFIG_TMP: tempDir,
483
+ npm_config_audit: "false",
484
+ NPM_CONFIG_AUDIT: "false",
485
+ npm_config_fund: "false",
486
+ NPM_CONFIG_FUND: "false",
487
+ npm_config_progress: "false",
488
+ NPM_CONFIG_PROGRESS: "false",
489
+ npm_config_update_notifier: "false",
490
+ NPM_CONFIG_UPDATE_NOTIFIER: "false",
491
+ npm_config_loglevel: "warn",
492
+ NPM_CONFIG_LOGLEVEL: "warn",
493
+ NODE_ENV: "development",
494
+ npm_config_production: "false",
495
+ NPM_CONFIG_PRODUCTION: "false",
496
+ NPM_CONFIG_INCLUDE: "dev"
497
+ }
498
+ });
499
+ }
500
+ catch (error) {
501
+ throw new Error(formatCommandFailure(command, cwd, error));
502
+ }
503
+ finally {
504
+ rmSync(scratchDir, { recursive: true, force: true });
505
+ }
435
506
  }
436
507
  async syncTemplateAboutAssets(input) {
437
- const entries = Array.from(new Set(input.template.about.preview_media
508
+ const entries = Array.from(new Set([
509
+ ...input.template.about.preview_media,
510
+ ...input.template.examples.flatMap((example) => example.map((entry) => entry.media_url))
511
+ ]
438
512
  .filter((entry) => !/^https?:\/\//i.test(entry))
439
513
  .filter((entry) => !entry.replace(/^\/+/, "").startsWith("templates/"))));
440
514
  for (const entry of entries) {
@@ -535,14 +609,25 @@ export class TemplateSourceService {
535
609
  throw new Error(`Template artifact hash mismatch for ${input.artifactKey}.`);
536
610
  }
537
611
  mkdirSync(input.checkoutPath, { recursive: true });
538
- const tempArchivePath = path.join(os.tmpdir(), `vidfarm-template-${createId("tmp")}.tar.gz`);
539
- writeFileSync(tempArchivePath, archive);
540
- try {
541
- await execFileAsync("tar", ["-xzf", tempArchivePath, "-C", input.checkoutPath], { cwd: process.cwd() });
542
- }
543
- finally {
544
- rmSync(tempArchivePath, { force: true });
545
- }
612
+ extractTarGzBuffer(archive, input.checkoutPath);
613
+ }
614
+ async createRuntimeArtifact(input) {
615
+ const runtimeKey = `template-runtime-artifacts/${input.source.templateId}/${input.commitSha}/runtime.tar.gz`;
616
+ const buffer = createTarGzFromDirectory(input.checkoutPath, (relativePath) => {
617
+ return hasPathSegment(relativePath, "node_modules")
618
+ || hasPathSegment(relativePath, ".git")
619
+ || hasPathSegment(relativePath, ".next")
620
+ || hasPathSegment(relativePath, ".turbo")
621
+ || relativePath === "dist/.tsbuildinfo"
622
+ || relativePath.endsWith("/dist/.tsbuildinfo")
623
+ || relativePath === ".vidfarm-import-ready";
624
+ });
625
+ const sha256 = createHash("sha256").update(buffer).digest("hex");
626
+ await storage.putBuffer(runtimeKey, buffer, "application/gzip");
627
+ return {
628
+ key: runtimeKey,
629
+ sha256
630
+ };
546
631
  }
547
632
  serializeArtifactManifestPayload(input) {
548
633
  return JSON.stringify({
@@ -626,6 +711,45 @@ export class TemplateSourceService {
626
711
  }
627
712
  throw new Error(`Template module path ${declaredModulePath} was not found in the imported release.`);
628
713
  }
714
+ pruneCheckoutInstallArtifacts(checkoutPath) {
715
+ removeNestedDirectories(checkoutPath, new Set(["node_modules"]));
716
+ linkSharedNodeModules(checkoutPath);
717
+ }
718
+ }
719
+ export function createTemplateReleaseSnapshot(template) {
720
+ return {
721
+ id: template.id,
722
+ slugId: template.slugId,
723
+ version: template.version,
724
+ about: {
725
+ title: template.about.title,
726
+ description: template.about.description,
727
+ difficulty: template.about.difficulty,
728
+ viral_dna: template.about.viral_dna,
729
+ visual_dna: template.about.visual_dna,
730
+ preview_media: [...template.about.preview_media],
731
+ link_to_original: template.about.link_to_original,
732
+ links_to_inspiration: [...template.about.links_to_inspiration],
733
+ sound: {
734
+ sound_title: template.about.sound.sound_title,
735
+ sound_url: template.about.sound.sound_url
736
+ },
737
+ proposal_generator: template.about.proposal_generator,
738
+ skeleton_prompt: template.about.skeleton_prompt,
739
+ sample_prompts: [...template.about.sample_prompts]
740
+ },
741
+ examples: template.examples.map((example) => example.map((entry) => ({
742
+ media_url: entry.media_url,
743
+ note: entry.note
744
+ }))),
745
+ operations: Object.entries(template.operations).map(([name, operation]) => ({
746
+ name,
747
+ description: operation.description,
748
+ workflow: operation.workflow,
749
+ providerHint: operation.providerHint ?? null,
750
+ webhookSupport: operation.webhookSupport ?? null
751
+ }))
752
+ };
629
753
  }
630
754
  function inferContentType(filePath) {
631
755
  switch (path.extname(filePath).toLowerCase()) {
@@ -650,3 +774,244 @@ function inferContentType(filePath) {
650
774
  return "application/octet-stream";
651
775
  }
652
776
  }
777
+ function hasPathSegment(relativePath, segment) {
778
+ return relativePath.split("/").includes(segment);
779
+ }
780
+ function removeNestedDirectories(rootDir, directoryNames) {
781
+ if (!existsSync(rootDir)) {
782
+ return;
783
+ }
784
+ for (const entryName of readdirSync(rootDir)) {
785
+ const absolutePath = path.join(rootDir, entryName);
786
+ const stat = statSync(absolutePath);
787
+ if (!stat.isDirectory()) {
788
+ continue;
789
+ }
790
+ if (directoryNames.has(entryName)) {
791
+ rmSync(absolutePath, { recursive: true, force: true });
792
+ continue;
793
+ }
794
+ removeNestedDirectories(absolutePath, directoryNames);
795
+ }
796
+ }
797
+ function linkSharedNodeModules(checkoutPath) {
798
+ const target = path.resolve(process.env.LAMBDA_TASK_ROOT ?? process.cwd(), "node_modules");
799
+ const linkPath = path.join(checkoutPath, "node_modules");
800
+ if (!existsSync(target) || existsSync(linkPath)) {
801
+ return;
802
+ }
803
+ symlinkSync(target, linkPath, "dir");
804
+ }
805
+ function formatCommandFailure(command, cwd, error) {
806
+ const details = childProcessFailureDetails(error);
807
+ const diskHint = /\bENOSPC\b|no space left on device/i.test(details)
808
+ ? " The command ran out of writable temporary storage; Vidfarm cleaned the partial checkout and npm scratch cache before returning this error."
809
+ : "";
810
+ return `Template import command failed: ${sanitizeSensitiveText(command)} (cwd: ${cwd}).${diskHint}${details ? `\n${details}` : ""}`;
811
+ }
812
+ function childProcessFailureDetails(error) {
813
+ if (!(error instanceof Error)) {
814
+ return String(error);
815
+ }
816
+ const childError = error;
817
+ const parts = [
818
+ sanitizeSensitiveText(childError.message),
819
+ childError.code == null ? "" : `exit/code: ${String(childError.code)}`,
820
+ childError.signal == null ? "" : `signal: ${childError.signal}`,
821
+ childError.stderr ? `stderr:\n${truncateCommandOutput(childError.stderr)}` : "",
822
+ childError.stdout ? `stdout:\n${truncateCommandOutput(childError.stdout)}` : ""
823
+ ].filter(Boolean);
824
+ return parts.join("\n");
825
+ }
826
+ function truncateCommandOutput(value) {
827
+ const text = Buffer.isBuffer(value) ? value.toString("utf8") : value;
828
+ const trimmed = sanitizeSensitiveText(text.trim());
829
+ return trimmed.length > 4_000 ? `${trimmed.slice(0, 4_000)}\n[output truncated]` : trimmed;
830
+ }
831
+ function sanitizeSensitiveText(value) {
832
+ return value
833
+ .replace(/https:\/\/x-access-token:[^@\s]+@github\.com\//gi, "https://x-access-token:[redacted]@github.com/")
834
+ .replace(/([?&](?:token|access_token|api_key|key|secret)=)[^&\s]+/gi, "$1[redacted]");
835
+ }
836
+ function extractTarGzBuffer(archive, outputDir) {
837
+ const tar = gunzipSync(archive);
838
+ let offset = 0;
839
+ let nextPath = null;
840
+ while (offset + 512 <= tar.length) {
841
+ const header = tar.subarray(offset, offset + 512);
842
+ offset += 512;
843
+ if (header.every((byte) => byte === 0)) {
844
+ break;
845
+ }
846
+ const rawName = readTarString(header, 0, 100);
847
+ const prefix = readTarString(header, 345, 155);
848
+ const headerPath = [prefix, rawName].filter(Boolean).join("/");
849
+ const size = readTarOctal(header, 124, 12);
850
+ const typeFlag = readTarString(header, 156, 1) || "0";
851
+ const body = tar.subarray(offset, offset + size);
852
+ offset += Math.ceil(size / 512) * 512;
853
+ if (typeFlag === "x") {
854
+ nextPath = readPaxPath(body) ?? nextPath;
855
+ continue;
856
+ }
857
+ if (typeFlag === "g") {
858
+ continue;
859
+ }
860
+ if (typeFlag === "L") {
861
+ nextPath = body.toString("utf8").replace(/\0.*$/u, "");
862
+ continue;
863
+ }
864
+ const fullName = nextPath ?? headerPath;
865
+ nextPath = null;
866
+ if (!fullName || fullName === ".") {
867
+ continue;
868
+ }
869
+ const outputPath = resolveTarOutputPath(outputDir, fullName);
870
+ if (!outputPath) {
871
+ continue;
872
+ }
873
+ if (typeFlag === "5") {
874
+ mkdirSync(outputPath, { recursive: true });
875
+ continue;
876
+ }
877
+ if (typeFlag === "2") {
878
+ continue;
879
+ }
880
+ if (typeFlag !== "0" && typeFlag !== "") {
881
+ continue;
882
+ }
883
+ mkdirSync(path.dirname(outputPath), { recursive: true });
884
+ writeFileSync(outputPath, body);
885
+ }
886
+ }
887
+ function createTarGzFromDirectory(rootDir, shouldExclude) {
888
+ const chunks = [];
889
+ for (const entry of listTarEntries(rootDir, "", shouldExclude)) {
890
+ const stat = statSync(entry.absolutePath);
891
+ const archivePath = entry.relativePath;
892
+ if (stat.isDirectory()) {
893
+ chunks.push(createTarHeader(`${archivePath.replace(/\/+$/u, "")}/`, 0, "5", stat.mtime));
894
+ continue;
895
+ }
896
+ if (!stat.isFile()) {
897
+ continue;
898
+ }
899
+ const body = readFileSync(entry.absolutePath);
900
+ chunks.push(createTarHeader(archivePath, body.length, "0", stat.mtime));
901
+ chunks.push(body);
902
+ const padding = body.length % 512;
903
+ if (padding) {
904
+ chunks.push(Buffer.alloc(512 - padding));
905
+ }
906
+ }
907
+ chunks.push(Buffer.alloc(1024));
908
+ return gzipSync(Buffer.concat(chunks));
909
+ }
910
+ function listTarEntries(rootDir, relativeDir, shouldExclude) {
911
+ const directory = path.join(rootDir, relativeDir);
912
+ const entries = [];
913
+ for (const name of readdirSync(directory).sort()) {
914
+ const relativePath = path.posix.join(relativeDir.split(path.sep).join("/"), name);
915
+ if (shouldExclude(relativePath)) {
916
+ continue;
917
+ }
918
+ const absolutePath = path.join(rootDir, relativePath);
919
+ const stat = statSync(absolutePath);
920
+ entries.push({ absolutePath, relativePath });
921
+ if (stat.isDirectory()) {
922
+ entries.push(...listTarEntries(rootDir, relativePath, shouldExclude));
923
+ }
924
+ }
925
+ return entries;
926
+ }
927
+ function createTarHeader(name, size, typeFlag, mtime) {
928
+ const header = Buffer.alloc(512);
929
+ const { namePart, prefixPart } = splitTarPath(name);
930
+ writeTarString(header, namePart, 0, 100);
931
+ writeTarOctal(header, typeFlag === "5" ? 0o755 : 0o644, 100, 8);
932
+ writeTarOctal(header, 0, 108, 8);
933
+ writeTarOctal(header, 0, 116, 8);
934
+ writeTarOctal(header, size, 124, 12);
935
+ writeTarOctal(header, Math.floor(mtime.getTime() / 1000), 136, 12);
936
+ header.fill(0x20, 148, 156);
937
+ writeTarString(header, typeFlag, 156, 1);
938
+ writeTarString(header, "ustar", 257, 6);
939
+ writeTarString(header, "00", 263, 2);
940
+ writeTarString(header, prefixPart, 345, 155);
941
+ let checksum = 0;
942
+ for (const byte of header) {
943
+ checksum += byte;
944
+ }
945
+ const checksumText = checksum.toString(8).padStart(6, "0");
946
+ writeTarString(header, `${checksumText}\0 `, 148, 8);
947
+ return header;
948
+ }
949
+ function splitTarPath(input) {
950
+ const normalized = input.replace(/^\.?\//u, "");
951
+ if (Buffer.byteLength(normalized) <= 100) {
952
+ return { namePart: normalized, prefixPart: "" };
953
+ }
954
+ const parts = normalized.split("/");
955
+ for (let index = 1; index < parts.length; index += 1) {
956
+ const prefixPart = parts.slice(0, index).join("/");
957
+ const namePart = parts.slice(index).join("/");
958
+ if (Buffer.byteLength(prefixPart) <= 155 && Buffer.byteLength(namePart) <= 100) {
959
+ return { namePart, prefixPart };
960
+ }
961
+ }
962
+ throw new Error(`Template runtime artifact path is too long for ustar: ${input}`);
963
+ }
964
+ function readPaxPath(body) {
965
+ const text = body.toString("utf8");
966
+ let offset = 0;
967
+ while (offset < text.length) {
968
+ const spaceIndex = text.indexOf(" ", offset);
969
+ if (spaceIndex === -1) {
970
+ break;
971
+ }
972
+ const length = Number.parseInt(text.slice(offset, spaceIndex), 10);
973
+ if (!Number.isFinite(length) || length <= 0) {
974
+ break;
975
+ }
976
+ const record = text.slice(spaceIndex + 1, offset + length).replace(/\n$/u, "");
977
+ const equalsIndex = record.indexOf("=");
978
+ if (equalsIndex !== -1 && record.slice(0, equalsIndex) === "path") {
979
+ return record.slice(equalsIndex + 1);
980
+ }
981
+ offset += length;
982
+ }
983
+ return null;
984
+ }
985
+ function resolveTarOutputPath(rootDir, entryPath) {
986
+ const normalized = entryPath.replace(/^\.?\//u, "");
987
+ if (!normalized || normalized.startsWith("/") || normalized.includes("..")) {
988
+ return null;
989
+ }
990
+ const outputPath = path.resolve(rootDir, normalized);
991
+ const root = path.resolve(rootDir);
992
+ if (outputPath !== root && !outputPath.startsWith(`${root}${path.sep}`)) {
993
+ return null;
994
+ }
995
+ return outputPath;
996
+ }
997
+ function readTarString(buffer, offset, length) {
998
+ return buffer
999
+ .subarray(offset, offset + length)
1000
+ .toString("utf8")
1001
+ .replace(/\0.*$/u, "")
1002
+ .trim();
1003
+ }
1004
+ function readTarOctal(buffer, offset, length) {
1005
+ return Number.parseInt(readTarString(buffer, offset, length) || "0", 8) || 0;
1006
+ }
1007
+ function writeTarString(buffer, value, offset, length) {
1008
+ const bytes = Buffer.from(value, "utf8");
1009
+ if (bytes.length > length) {
1010
+ throw new Error(`Tar header value is too long: ${value}`);
1011
+ }
1012
+ bytes.copy(buffer, offset, 0, bytes.length);
1013
+ }
1014
+ function writeTarOctal(buffer, value, offset, length) {
1015
+ writeTarString(buffer, value.toString(8).padStart(length - 1, "0"), offset, length - 1);
1016
+ }
1017
+ //# sourceMappingURL=template-sources.js.map