transloadit 4.7.3 → 4.7.6

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 (152) hide show
  1. package/README.md +897 -5
  2. package/dist/Transloadit.d.ts +13 -3
  3. package/dist/Transloadit.d.ts.map +1 -1
  4. package/dist/Transloadit.js +22 -2
  5. package/dist/Transloadit.js.map +1 -1
  6. package/dist/alphalib/types/assembliesGet.d.ts +5 -0
  7. package/dist/alphalib/types/assembliesGet.d.ts.map +1 -1
  8. package/dist/alphalib/types/assemblyReplay.d.ts +5 -0
  9. package/dist/alphalib/types/assemblyReplay.d.ts.map +1 -1
  10. package/dist/alphalib/types/assemblyReplayNotification.d.ts +5 -0
  11. package/dist/alphalib/types/assemblyReplayNotification.d.ts.map +1 -1
  12. package/dist/alphalib/types/assemblyStatus.d.ts +25 -25
  13. package/dist/alphalib/types/assemblyStatus.d.ts.map +1 -1
  14. package/dist/alphalib/types/assemblyStatus.js +4 -1
  15. package/dist/alphalib/types/assemblyStatus.js.map +1 -1
  16. package/dist/alphalib/types/bill.d.ts +5 -0
  17. package/dist/alphalib/types/bill.d.ts.map +1 -1
  18. package/dist/alphalib/types/builtinTemplates.d.ts +83 -0
  19. package/dist/alphalib/types/builtinTemplates.d.ts.map +1 -0
  20. package/dist/alphalib/types/builtinTemplates.js +19 -0
  21. package/dist/alphalib/types/builtinTemplates.js.map +1 -0
  22. package/dist/alphalib/types/robots/ai-chat.d.ts.map +1 -1
  23. package/dist/alphalib/types/robots/ai-chat.js +1 -0
  24. package/dist/alphalib/types/robots/ai-chat.js.map +1 -1
  25. package/dist/alphalib/types/skillFrontmatter.d.ts +29 -0
  26. package/dist/alphalib/types/skillFrontmatter.d.ts.map +1 -0
  27. package/dist/alphalib/types/skillFrontmatter.js +19 -0
  28. package/dist/alphalib/types/skillFrontmatter.js.map +1 -0
  29. package/dist/alphalib/types/template.d.ts +36 -0
  30. package/dist/alphalib/types/template.d.ts.map +1 -1
  31. package/dist/alphalib/types/template.js +10 -0
  32. package/dist/alphalib/types/template.js.map +1 -1
  33. package/dist/alphalib/types/templateCredential.d.ts +10 -0
  34. package/dist/alphalib/types/templateCredential.d.ts.map +1 -1
  35. package/dist/bearerToken.d.ts +31 -0
  36. package/dist/bearerToken.d.ts.map +1 -0
  37. package/dist/bearerToken.js +158 -0
  38. package/dist/bearerToken.js.map +1 -0
  39. package/dist/cli/commands/assemblies.d.ts +8 -2
  40. package/dist/cli/commands/assemblies.d.ts.map +1 -1
  41. package/dist/cli/commands/assemblies.js +566 -411
  42. package/dist/cli/commands/assemblies.js.map +1 -1
  43. package/dist/cli/commands/auth.d.ts +1 -4
  44. package/dist/cli/commands/auth.d.ts.map +1 -1
  45. package/dist/cli/commands/auth.js +7 -123
  46. package/dist/cli/commands/auth.js.map +1 -1
  47. package/dist/cli/commands/index.d.ts.map +1 -1
  48. package/dist/cli/commands/index.js +5 -0
  49. package/dist/cli/commands/index.js.map +1 -1
  50. package/dist/cli/commands/templates.d.ts.map +1 -1
  51. package/dist/cli/commands/templates.js +4 -14
  52. package/dist/cli/commands/templates.js.map +1 -1
  53. package/dist/cli/fileProcessingOptions.d.ts +35 -0
  54. package/dist/cli/fileProcessingOptions.d.ts.map +1 -0
  55. package/dist/cli/fileProcessingOptions.js +182 -0
  56. package/dist/cli/fileProcessingOptions.js.map +1 -0
  57. package/dist/cli/generateIntentDocs.d.ts +2 -0
  58. package/dist/cli/generateIntentDocs.d.ts.map +1 -0
  59. package/dist/cli/generateIntentDocs.js +321 -0
  60. package/dist/cli/generateIntentDocs.js.map +1 -0
  61. package/dist/cli/intentCommandSpecs.d.ts +36 -0
  62. package/dist/cli/intentCommandSpecs.d.ts.map +1 -0
  63. package/dist/cli/intentCommandSpecs.js +181 -0
  64. package/dist/cli/intentCommandSpecs.js.map +1 -0
  65. package/dist/cli/intentCommands.d.ts +13 -0
  66. package/dist/cli/intentCommands.d.ts.map +1 -0
  67. package/dist/cli/intentCommands.js +368 -0
  68. package/dist/cli/intentCommands.js.map +1 -0
  69. package/dist/cli/intentFields.d.ts +25 -0
  70. package/dist/cli/intentFields.d.ts.map +1 -0
  71. package/dist/cli/intentFields.js +298 -0
  72. package/dist/cli/intentFields.js.map +1 -0
  73. package/dist/cli/intentInputPolicy.d.ts +10 -0
  74. package/dist/cli/intentInputPolicy.d.ts.map +1 -0
  75. package/dist/cli/intentInputPolicy.js +2 -0
  76. package/dist/cli/intentInputPolicy.js.map +1 -0
  77. package/dist/cli/intentRuntime.d.ts +114 -0
  78. package/dist/cli/intentRuntime.d.ts.map +1 -0
  79. package/dist/cli/intentRuntime.js +464 -0
  80. package/dist/cli/intentRuntime.js.map +1 -0
  81. package/dist/cli/resultFiles.d.ts +19 -0
  82. package/dist/cli/resultFiles.d.ts.map +1 -0
  83. package/dist/cli/resultFiles.js +66 -0
  84. package/dist/cli/resultFiles.js.map +1 -0
  85. package/dist/cli/resultUrls.d.ts +19 -0
  86. package/dist/cli/resultUrls.d.ts.map +1 -0
  87. package/dist/cli/resultUrls.js +36 -0
  88. package/dist/cli/resultUrls.js.map +1 -0
  89. package/dist/cli/semanticIntents/imageDescribe.d.ts +43 -0
  90. package/dist/cli/semanticIntents/imageDescribe.d.ts.map +1 -0
  91. package/dist/cli/semanticIntents/imageDescribe.js +188 -0
  92. package/dist/cli/semanticIntents/imageDescribe.js.map +1 -0
  93. package/dist/cli/semanticIntents/index.d.ts +18 -0
  94. package/dist/cli/semanticIntents/index.d.ts.map +1 -0
  95. package/dist/cli/semanticIntents/index.js +18 -0
  96. package/dist/cli/semanticIntents/index.js.map +1 -0
  97. package/dist/cli/semanticIntents/markdownPdf.d.ts +4 -0
  98. package/dist/cli/semanticIntents/markdownPdf.d.ts.map +1 -0
  99. package/dist/cli/semanticIntents/markdownPdf.js +93 -0
  100. package/dist/cli/semanticIntents/markdownPdf.js.map +1 -0
  101. package/dist/cli/semanticIntents/parsing.d.ts +11 -0
  102. package/dist/cli/semanticIntents/parsing.d.ts.map +1 -0
  103. package/dist/cli/semanticIntents/parsing.js +29 -0
  104. package/dist/cli/semanticIntents/parsing.js.map +1 -0
  105. package/dist/cli/stepsInput.d.ts +4 -0
  106. package/dist/cli/stepsInput.d.ts.map +1 -0
  107. package/dist/cli/stepsInput.js +23 -0
  108. package/dist/cli/stepsInput.js.map +1 -0
  109. package/dist/cli.d.ts +1 -1
  110. package/dist/cli.d.ts.map +1 -1
  111. package/dist/cli.js +5 -4
  112. package/dist/cli.js.map +1 -1
  113. package/dist/ensureUniqueCounter.d.ts +8 -0
  114. package/dist/ensureUniqueCounter.d.ts.map +1 -0
  115. package/dist/ensureUniqueCounter.js +48 -0
  116. package/dist/ensureUniqueCounter.js.map +1 -0
  117. package/dist/inputFiles.d.ts +9 -0
  118. package/dist/inputFiles.d.ts.map +1 -1
  119. package/dist/inputFiles.js +177 -26
  120. package/dist/inputFiles.js.map +1 -1
  121. package/dist/robots.js +1 -1
  122. package/dist/robots.js.map +1 -1
  123. package/package.json +9 -7
  124. package/src/Transloadit.ts +35 -3
  125. package/src/alphalib/types/assemblyStatus.ts +4 -1
  126. package/src/alphalib/types/builtinTemplates.ts +24 -0
  127. package/src/alphalib/types/robots/ai-chat.ts +1 -0
  128. package/src/alphalib/types/skillFrontmatter.ts +24 -0
  129. package/src/alphalib/types/template.ts +14 -0
  130. package/src/bearerToken.ts +208 -0
  131. package/src/cli/commands/assemblies.ts +825 -505
  132. package/src/cli/commands/auth.ts +9 -151
  133. package/src/cli/commands/index.ts +6 -3
  134. package/src/cli/commands/templates.ts +6 -17
  135. package/src/cli/fileProcessingOptions.ts +294 -0
  136. package/src/cli/generateIntentDocs.ts +419 -0
  137. package/src/cli/intentCommandSpecs.ts +282 -0
  138. package/src/cli/intentCommands.ts +525 -0
  139. package/src/cli/intentFields.ts +403 -0
  140. package/src/cli/intentInputPolicy.ts +11 -0
  141. package/src/cli/intentRuntime.ts +734 -0
  142. package/src/cli/resultFiles.ts +105 -0
  143. package/src/cli/resultUrls.ts +72 -0
  144. package/src/cli/semanticIntents/imageDescribe.ts +254 -0
  145. package/src/cli/semanticIntents/index.ts +48 -0
  146. package/src/cli/semanticIntents/markdownPdf.ts +120 -0
  147. package/src/cli/semanticIntents/parsing.ts +56 -0
  148. package/src/cli/stepsInput.ts +32 -0
  149. package/src/cli.ts +5 -4
  150. package/src/ensureUniqueCounter.ts +75 -0
  151. package/src/inputFiles.ts +277 -26
  152. package/src/robots.ts +1 -1
@@ -7,6 +7,7 @@ import {
7
7
  assemblyInstructionsSchema,
8
8
  } from '../../alphalib/types/template.ts'
9
9
  import type { OptionalAuthParams } from '../../apiTypes.ts'
10
+ import { mintBearerTokenWithCredentials } from '../../bearerToken.ts'
10
11
  import { Transloadit } from '../../Transloadit.ts'
11
12
  import { readCliInput, requireEnvCredentials } from '../helpers.ts'
12
13
  import { UnauthenticatedCommand } from './BaseCommand.ts'
@@ -194,155 +195,6 @@ export interface RunSmartSigOptions {
194
195
  providedInput?: string
195
196
  }
196
197
 
197
- export interface RequestTokenOptions {
198
- endpoint?: string
199
- aud?: string
200
- }
201
-
202
- const tokenErrorSchema = z
203
- .object({
204
- error: z.string(),
205
- message: z.string().optional(),
206
- })
207
- .passthrough()
208
-
209
- const tokenSuccessSchema = z
210
- .object({
211
- access_token: z.string().min(1),
212
- })
213
- .passthrough()
214
-
215
- const buildBasicAuthHeaderValue = (credentials: { authKey: string; authSecret: string }): string =>
216
- `Basic ${Buffer.from(`${credentials.authKey}:${credentials.authSecret}`, 'utf8').toString('base64')}`
217
-
218
- const isLoopbackHost = (hostname: string): boolean =>
219
- hostname === 'localhost' || hostname === '::1' || hostname.startsWith('127.')
220
-
221
- type TokenBaseResult = { ok: true; baseUrl: URL } | { ok: false; error: string }
222
-
223
- const normalizeTokenBaseEndpoint = (raw?: string): TokenBaseResult => {
224
- const baseRaw = (raw || process.env.TRANSLOADIT_ENDPOINT || 'https://api2.transloadit.com').trim()
225
-
226
- let url: URL
227
- try {
228
- url = new URL(baseRaw)
229
- } catch {
230
- return {
231
- ok: false,
232
- error:
233
- 'Invalid endpoint URL. Use --endpoint https://api2.transloadit.com (or set TRANSLOADIT_ENDPOINT).',
234
- }
235
- }
236
-
237
- if (url.username || url.password) {
238
- return { ok: false, error: 'Endpoint must not include username/password.' }
239
- }
240
- if (url.search || url.hash) {
241
- return { ok: false, error: 'Endpoint must not include query string or hash.' }
242
- }
243
-
244
- if (url.protocol !== 'https:') {
245
- if (url.protocol === 'http:' && isLoopbackHost(url.hostname)) {
246
- // Allowed for local development only.
247
- } else {
248
- return {
249
- ok: false,
250
- error:
251
- 'Refusing to send credentials to a non-HTTPS endpoint. Use https://... (or http://localhost for local development).',
252
- }
253
- }
254
- }
255
-
256
- // If someone pasted the token URL, normalize it back to the API base to avoid /token/token.
257
- const pathLower = url.pathname.toLowerCase()
258
- if (pathLower === '/token' || pathLower === '/token/') {
259
- url.pathname = '/'
260
- }
261
-
262
- if (!url.pathname.endsWith('/')) {
263
- url.pathname = `${url.pathname}/`
264
- }
265
-
266
- return { ok: true, baseUrl: url }
267
- }
268
-
269
- async function requestTokenWithCredentials(
270
- credentials: { authKey: string; authSecret: string },
271
- options: RequestTokenOptions = {},
272
- ): Promise<OutputResult> {
273
- const endpointResult = normalizeTokenBaseEndpoint(options.endpoint)
274
- if (!endpointResult.ok) {
275
- return { ok: false, error: endpointResult.error }
276
- }
277
-
278
- const url = new URL('token', endpointResult.baseUrl).toString()
279
- const aud = (options.aud ?? 'mcp').trim() || 'mcp'
280
-
281
- const body = new URLSearchParams({ grant_type: 'client_credentials', aud }).toString()
282
-
283
- let res: Response
284
- const controller = new AbortController()
285
- const timeout = setTimeout(() => controller.abort(), 15_000)
286
- try {
287
- res = await fetch(url, {
288
- method: 'POST',
289
- // Never follow redirects with Basic Auth credentials.
290
- redirect: 'error',
291
- signal: controller.signal,
292
- headers: {
293
- Authorization: buildBasicAuthHeaderValue(credentials),
294
- Accept: 'application/json',
295
- 'Content-Type': 'application/x-www-form-urlencoded',
296
- },
297
- body,
298
- })
299
- } catch (err) {
300
- clearTimeout(timeout)
301
- if (err instanceof Error && err.name === 'AbortError') {
302
- return { ok: false, error: 'Failed to mint bearer token: request timed out after 15s.' }
303
- }
304
- const message = err instanceof Error ? err.message : String(err)
305
- return { ok: false, error: `Failed to mint bearer token: ${message}` }
306
- } finally {
307
- clearTimeout(timeout)
308
- }
309
-
310
- const text = await res.text()
311
- const trimmed = text.trim()
312
- let parsedJson: unknown = null
313
- try {
314
- parsedJson = trimmed ? JSON.parse(trimmed) : null
315
- } catch {
316
- parsedJson = null
317
- }
318
-
319
- if (res.ok) {
320
- if (parsedJson == null) {
321
- return { ok: false, error: 'Token response was not valid JSON.' }
322
- }
323
- const parsed = tokenSuccessSchema.safeParse(parsedJson)
324
- if (!parsed.success) {
325
- return { ok: false, error: 'Token response did not include an access_token.' }
326
- }
327
- return { ok: true, output: trimmed }
328
- }
329
-
330
- const parsedError = tokenErrorSchema.safeParse(parsedJson)
331
- if (parsedError.success) {
332
- return {
333
- ok: false,
334
- error: parsedError.data.message
335
- ? `${parsedError.data.error}: ${parsedError.data.message}`
336
- : parsedError.data.error,
337
- }
338
- }
339
-
340
- return {
341
- ok: false,
342
- error: `Token request failed (${res.status}): ${trimmed || res.statusText}`,
343
- }
344
- }
345
-
346
198
  export async function runSig(options: RunSigOptions = {}): Promise<void> {
347
199
  const credentialsResult = requireEnvCredentials()
348
200
  if (!credentialsResult.ok) {
@@ -520,6 +372,11 @@ export class TokenCommand extends UnauthenticatedCommand {
520
372
  description: 'Token audience (default: mcp).',
521
373
  })
522
374
 
375
+ scope = Option.String('--scope', {
376
+ description:
377
+ 'Comma-separated list of scopes to request (defaults to auth key scopes). Example: assemblies:write,templates:read',
378
+ })
379
+
523
380
  protected override async run(): Promise<number | undefined> {
524
381
  const credentialsResult = requireEnvCredentials()
525
382
  if (!credentialsResult.ok) {
@@ -527,13 +384,14 @@ export class TokenCommand extends UnauthenticatedCommand {
527
384
  return 1
528
385
  }
529
386
 
530
- const result = await requestTokenWithCredentials(credentialsResult.credentials, {
387
+ const result = await mintBearerTokenWithCredentials(credentialsResult.credentials, {
531
388
  endpoint: this.endpoint,
532
389
  aud: this.aud,
390
+ scope: this.scope,
533
391
  })
534
392
 
535
393
  if (result.ok) {
536
- process.stdout.write(`${result.output}\n`)
394
+ process.stdout.write(`${result.raw}\n`)
537
395
  return undefined
538
396
  }
539
397
 
@@ -1,7 +1,7 @@
1
1
  import { Builtins, Cli } from 'clipanion'
2
2
 
3
3
  import packageJson from '../../../package.json' with { type: 'json' }
4
-
4
+ import { intentCommands } from '../intentCommands.ts'
5
5
  import {
6
6
  AssembliesCreateCommand,
7
7
  AssembliesDeleteCommand,
@@ -10,9 +10,7 @@ import {
10
10
  AssembliesListCommand,
11
11
  AssembliesReplayCommand,
12
12
  } from './assemblies.ts'
13
-
14
13
  import { SignatureCommand, SmartCdnSignatureCommand, TokenCommand } from './auth.ts'
15
-
16
14
  import { BillsGetCommand } from './bills.ts'
17
15
  import { DocsRobotsGetCommand, DocsRobotsListCommand } from './docs.ts'
18
16
  import { NotificationsReplayCommand } from './notifications.ts'
@@ -71,5 +69,10 @@ export function createCli(): Cli {
71
69
  cli.register(DocsRobotsListCommand)
72
70
  cli.register(DocsRobotsGetCommand)
73
71
 
72
+ // Intent-first commands
73
+ for (const command of intentCommands) {
74
+ cli.register(command)
75
+ }
76
+
74
77
  return cli
75
78
  }
@@ -5,12 +5,11 @@ import { Command, Option } from 'clipanion'
5
5
  import rreaddir from 'recursive-readdir'
6
6
  import { z } from 'zod'
7
7
  import { tryCatch } from '../../alphalib/tryCatch.ts'
8
- import type { Steps } from '../../alphalib/types/template.ts'
9
- import { stepsSchema } from '../../alphalib/types/template.ts'
10
8
  import type { TemplateContent } from '../../apiTypes.ts'
11
9
  import type { Transloadit } from '../../Transloadit.ts'
12
10
  import { createReadStream, formatAPIError, streamToBuffer } from '../helpers.ts'
13
11
  import type { IOutputCtl } from '../OutputCtl.ts'
12
+ import { parseStepsInputJson } from '../stepsInput.ts'
14
13
  import ModifiedLookup from '../template-last-modified.ts'
15
14
  import type { TemplateFile } from '../types.ts'
16
15
  import { ensureError, isTransloaditAPIError, TemplateFileDataSchema } from '../types.ts'
@@ -60,16 +59,11 @@ export async function create(
60
59
  try {
61
60
  const buf = await streamToBuffer(createReadStream(file))
62
61
 
63
- const parsed: unknown = JSON.parse(buf.toString())
64
- const validated = stepsSchema.safeParse(parsed)
65
- if (!validated.success) {
66
- throw new Error(`Invalid template steps format: ${validated.error.message}`)
67
- }
62
+ const steps = parseStepsInputJson(buf.toString())
68
63
 
69
64
  const result = await client.createTemplate({
70
65
  name,
71
- // Steps (validated) is assignable to StepsInput at runtime; cast for TS
72
- template: { steps: validated.data } as TemplateContent,
66
+ template: { steps } as TemplateContent,
73
67
  })
74
68
  output.print(result.id, result)
75
69
  return result
@@ -106,23 +100,18 @@ export async function modify(
106
100
  try {
107
101
  const buf = await streamToBuffer(createReadStream(file))
108
102
 
109
- let steps: Steps | null = null
103
+ let steps: TemplateContent['steps'] | null = null
110
104
  let newName = name
111
105
 
112
106
  if (buf.length > 0) {
113
- const parsed: unknown = JSON.parse(buf.toString())
114
- const validated = stepsSchema.safeParse(parsed)
115
- if (!validated.success) {
116
- throw new Error(`Invalid template steps format: ${validated.error.message}`)
117
- }
118
- steps = validated.data
107
+ steps = parseStepsInputJson(buf.toString()) as TemplateContent['steps']
119
108
  }
120
109
 
121
110
  if (!name || buf.length === 0) {
122
111
  const tpl = await client.getTemplate(template)
123
112
  if (!name) newName = tpl.name
124
113
  if (buf.length === 0 && tpl.content.steps) {
125
- steps = tpl.content.steps
114
+ steps = tpl.content.steps as TemplateContent['steps']
126
115
  }
127
116
  }
128
117
 
@@ -0,0 +1,294 @@
1
+ import { Option } from 'clipanion'
2
+ import * as t from 'typanion'
3
+
4
+ export interface SharedCliOptionDocumentation {
5
+ description: string
6
+ example: string
7
+ flags: string
8
+ required: string
9
+ type: string
10
+ }
11
+
12
+ interface SharedCliOptionDefinition {
13
+ docs: SharedCliOptionDocumentation
14
+ optionFlags: string
15
+ }
16
+
17
+ interface SharedCliOptionExports<T> {
18
+ docs: (description?: string) => SharedCliOptionDocumentation
19
+ option: (description?: string) => T
20
+ }
21
+
22
+ export interface SharedFileProcessingValidationInput {
23
+ explicitInputCount: number
24
+ singleAssembly: boolean
25
+ watch: boolean
26
+ watchRequiresInputsMessage: string
27
+ }
28
+
29
+ const inputPathsOptionDefinition = {
30
+ docs: {
31
+ flags: '--input, -i',
32
+ type: 'path | dir | url | -',
33
+ required: 'varies',
34
+ example: 'input.file',
35
+ description: 'Provide an input path, directory, URL, or - for stdin',
36
+ },
37
+ optionFlags: '--input,-i',
38
+ } as const satisfies SharedCliOptionDefinition
39
+
40
+ const recursiveOptionDefinition = {
41
+ docs: {
42
+ flags: '--recursive, -r',
43
+ type: 'boolean',
44
+ required: 'no',
45
+ example: 'false',
46
+ description: 'Enumerate input directories recursively',
47
+ },
48
+ optionFlags: '--recursive,-r',
49
+ } as const satisfies SharedCliOptionDefinition
50
+
51
+ const deleteAfterProcessingOptionDefinition = {
52
+ docs: {
53
+ flags: '--delete-after-processing, -d',
54
+ type: 'boolean',
55
+ required: 'no',
56
+ example: 'false',
57
+ description: 'Delete input files after they are processed',
58
+ },
59
+ optionFlags: '--delete-after-processing,-d',
60
+ } as const satisfies SharedCliOptionDefinition
61
+
62
+ const reprocessStaleOptionDefinition = {
63
+ docs: {
64
+ flags: '--reprocess-stale',
65
+ type: 'boolean',
66
+ required: 'no',
67
+ example: 'false',
68
+ description: 'Process inputs even if output is newer',
69
+ },
70
+ optionFlags: '--reprocess-stale',
71
+ } as const satisfies SharedCliOptionDefinition
72
+
73
+ const watchOptionDefinition = {
74
+ docs: {
75
+ flags: '--watch, -w',
76
+ type: 'boolean',
77
+ required: 'no',
78
+ example: 'false',
79
+ description: 'Watch inputs for changes',
80
+ },
81
+ optionFlags: '--watch,-w',
82
+ } as const satisfies SharedCliOptionDefinition
83
+
84
+ const singleAssemblyOptionDefinition = {
85
+ docs: {
86
+ flags: '--single-assembly',
87
+ type: 'boolean',
88
+ required: 'no',
89
+ example: 'false',
90
+ description: 'Pass all input files to a single assembly instead of one assembly per file',
91
+ },
92
+ optionFlags: '--single-assembly',
93
+ } as const satisfies SharedCliOptionDefinition
94
+
95
+ const concurrencyOptionDefinition = {
96
+ docs: {
97
+ flags: '--concurrency, -c',
98
+ type: 'number',
99
+ required: 'no',
100
+ example: '5',
101
+ description: 'Maximum number of concurrent assemblies (default: 5)',
102
+ },
103
+ optionFlags: '--concurrency,-c',
104
+ } as const satisfies SharedCliOptionDefinition
105
+
106
+ const printUrlsOptionDefinition = {
107
+ docs: {
108
+ flags: '--print-urls',
109
+ type: 'boolean',
110
+ required: 'no',
111
+ example: 'false',
112
+ description: 'Print temporary result URLs after completion',
113
+ },
114
+ optionFlags: '--print-urls',
115
+ } as const satisfies SharedCliOptionDefinition
116
+
117
+ function getSharedCliOptionDocumentation(
118
+ definition: SharedCliOptionDefinition,
119
+ description = definition.docs.description,
120
+ ): SharedCliOptionDocumentation {
121
+ return {
122
+ ...definition.docs,
123
+ description,
124
+ }
125
+ }
126
+
127
+ function arrayOption(
128
+ definition: SharedCliOptionDefinition,
129
+ description = definition.docs.description,
130
+ ): string[] {
131
+ return Option.Array(definition.optionFlags, {
132
+ description,
133
+ }) as unknown as string[]
134
+ }
135
+
136
+ function booleanOption(
137
+ definition: SharedCliOptionDefinition,
138
+ description = definition.docs.description,
139
+ ): boolean {
140
+ return Option.Boolean(definition.optionFlags, false, {
141
+ description,
142
+ }) as unknown as boolean
143
+ }
144
+
145
+ function createArrayOptionExports(
146
+ definition: SharedCliOptionDefinition,
147
+ ): SharedCliOptionExports<string[]> {
148
+ return {
149
+ docs: (description = definition.docs.description) =>
150
+ getSharedCliOptionDocumentation(definition, description),
151
+ option: (description = definition.docs.description) => arrayOption(definition, description),
152
+ }
153
+ }
154
+
155
+ function createBooleanOptionExports(
156
+ definition: SharedCliOptionDefinition,
157
+ ): SharedCliOptionExports<boolean> {
158
+ return {
159
+ docs: (description = definition.docs.description) =>
160
+ getSharedCliOptionDocumentation(definition, description),
161
+ option: (description = definition.docs.description) => booleanOption(definition, description),
162
+ }
163
+ }
164
+
165
+ const inputPathsOptionExports = createArrayOptionExports(inputPathsOptionDefinition)
166
+ const recursiveOptionExports = createBooleanOptionExports(recursiveOptionDefinition)
167
+ const deleteAfterProcessingOptionExports = createBooleanOptionExports(
168
+ deleteAfterProcessingOptionDefinition,
169
+ )
170
+ const reprocessStaleOptionExports = createBooleanOptionExports(reprocessStaleOptionDefinition)
171
+ const watchOptionExports = createBooleanOptionExports(watchOptionDefinition)
172
+ const singleAssemblyOptionExports = createBooleanOptionExports(singleAssemblyOptionDefinition)
173
+
174
+ export function getInputPathsOptionDocumentation(
175
+ description = inputPathsOptionDefinition.docs.description,
176
+ ): SharedCliOptionDocumentation {
177
+ return inputPathsOptionExports.docs(description)
178
+ }
179
+
180
+ export function inputPathsOption(
181
+ description = inputPathsOptionDefinition.docs.description,
182
+ ): string[] {
183
+ return inputPathsOptionExports.option(description)
184
+ }
185
+
186
+ export function getRecursiveOptionDocumentation(
187
+ description = recursiveOptionDefinition.docs.description,
188
+ ): SharedCliOptionDocumentation {
189
+ return recursiveOptionExports.docs(description)
190
+ }
191
+
192
+ export function recursiveOption(description = recursiveOptionDefinition.docs.description): boolean {
193
+ return recursiveOptionExports.option(description)
194
+ }
195
+
196
+ export function getDeleteAfterProcessingOptionDocumentation(
197
+ description = deleteAfterProcessingOptionDefinition.docs.description,
198
+ ): SharedCliOptionDocumentation {
199
+ return deleteAfterProcessingOptionExports.docs(description)
200
+ }
201
+
202
+ export function deleteAfterProcessingOption(
203
+ description = deleteAfterProcessingOptionDefinition.docs.description,
204
+ ): boolean {
205
+ return deleteAfterProcessingOptionExports.option(description)
206
+ }
207
+
208
+ export function getReprocessStaleOptionDocumentation(
209
+ description = reprocessStaleOptionDefinition.docs.description,
210
+ ): SharedCliOptionDocumentation {
211
+ return reprocessStaleOptionExports.docs(description)
212
+ }
213
+
214
+ export function reprocessStaleOption(
215
+ description = reprocessStaleOptionDefinition.docs.description,
216
+ ): boolean {
217
+ return reprocessStaleOptionExports.option(description)
218
+ }
219
+
220
+ export function getWatchOptionDocumentation(
221
+ description = watchOptionDefinition.docs.description,
222
+ ): SharedCliOptionDocumentation {
223
+ return watchOptionExports.docs(description)
224
+ }
225
+
226
+ export function watchOption(description = watchOptionDefinition.docs.description): boolean {
227
+ return watchOptionExports.option(description)
228
+ }
229
+
230
+ export function getSingleAssemblyOptionDocumentation(
231
+ description = singleAssemblyOptionDefinition.docs.description,
232
+ ): SharedCliOptionDocumentation {
233
+ return singleAssemblyOptionExports.docs(description)
234
+ }
235
+
236
+ export function singleAssemblyOption(
237
+ description = singleAssemblyOptionDefinition.docs.description,
238
+ ): boolean {
239
+ return singleAssemblyOptionExports.option(description)
240
+ }
241
+
242
+ export function getConcurrencyOptionDocumentation(
243
+ description = concurrencyOptionDefinition.docs.description,
244
+ ): SharedCliOptionDocumentation {
245
+ return getSharedCliOptionDocumentation(concurrencyOptionDefinition, description)
246
+ }
247
+
248
+ export function concurrencyOption(
249
+ description = concurrencyOptionDefinition.docs.description,
250
+ ): number | undefined {
251
+ return Option.String(concurrencyOptionDefinition.optionFlags, {
252
+ description,
253
+ validator: t.applyCascade(t.isNumber(), [t.isAtLeast(1)]),
254
+ }) as unknown as number | undefined
255
+ }
256
+
257
+ export function getPrintUrlsOptionDocumentation(
258
+ description = printUrlsOptionDefinition.docs.description,
259
+ ): SharedCliOptionDocumentation {
260
+ return getSharedCliOptionDocumentation(printUrlsOptionDefinition, description)
261
+ }
262
+
263
+ export function printUrlsOption(description = printUrlsOptionDefinition.docs.description): boolean {
264
+ return Option.Boolean(printUrlsOptionDefinition.optionFlags, {
265
+ description,
266
+ }) as unknown as boolean
267
+ }
268
+
269
+ export function countProvidedInputs({
270
+ inputBase64,
271
+ inputs,
272
+ }: {
273
+ inputBase64?: string[]
274
+ inputs?: string[]
275
+ }): number {
276
+ return (inputs ?? []).length + (inputBase64 ?? []).length
277
+ }
278
+
279
+ export function validateSharedFileProcessingOptions({
280
+ explicitInputCount,
281
+ singleAssembly,
282
+ watch,
283
+ watchRequiresInputsMessage,
284
+ }: SharedFileProcessingValidationInput): string | undefined {
285
+ if (watch && explicitInputCount === 0) {
286
+ return watchRequiresInputsMessage
287
+ }
288
+
289
+ if (watch && singleAssembly) {
290
+ return '--single-assembly cannot be used with --watch'
291
+ }
292
+
293
+ return undefined
294
+ }