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.
- package/README.md +897 -5
- package/dist/Transloadit.d.ts +13 -3
- package/dist/Transloadit.d.ts.map +1 -1
- package/dist/Transloadit.js +22 -2
- package/dist/Transloadit.js.map +1 -1
- package/dist/alphalib/types/assembliesGet.d.ts +5 -0
- package/dist/alphalib/types/assembliesGet.d.ts.map +1 -1
- package/dist/alphalib/types/assemblyReplay.d.ts +5 -0
- package/dist/alphalib/types/assemblyReplay.d.ts.map +1 -1
- package/dist/alphalib/types/assemblyReplayNotification.d.ts +5 -0
- package/dist/alphalib/types/assemblyReplayNotification.d.ts.map +1 -1
- package/dist/alphalib/types/assemblyStatus.d.ts +25 -25
- package/dist/alphalib/types/assemblyStatus.d.ts.map +1 -1
- package/dist/alphalib/types/assemblyStatus.js +4 -1
- package/dist/alphalib/types/assemblyStatus.js.map +1 -1
- package/dist/alphalib/types/bill.d.ts +5 -0
- package/dist/alphalib/types/bill.d.ts.map +1 -1
- package/dist/alphalib/types/builtinTemplates.d.ts +83 -0
- package/dist/alphalib/types/builtinTemplates.d.ts.map +1 -0
- package/dist/alphalib/types/builtinTemplates.js +19 -0
- package/dist/alphalib/types/builtinTemplates.js.map +1 -0
- package/dist/alphalib/types/robots/ai-chat.d.ts.map +1 -1
- package/dist/alphalib/types/robots/ai-chat.js +1 -0
- package/dist/alphalib/types/robots/ai-chat.js.map +1 -1
- package/dist/alphalib/types/skillFrontmatter.d.ts +29 -0
- package/dist/alphalib/types/skillFrontmatter.d.ts.map +1 -0
- package/dist/alphalib/types/skillFrontmatter.js +19 -0
- package/dist/alphalib/types/skillFrontmatter.js.map +1 -0
- package/dist/alphalib/types/template.d.ts +36 -0
- package/dist/alphalib/types/template.d.ts.map +1 -1
- package/dist/alphalib/types/template.js +10 -0
- package/dist/alphalib/types/template.js.map +1 -1
- package/dist/alphalib/types/templateCredential.d.ts +10 -0
- package/dist/alphalib/types/templateCredential.d.ts.map +1 -1
- package/dist/bearerToken.d.ts +31 -0
- package/dist/bearerToken.d.ts.map +1 -0
- package/dist/bearerToken.js +158 -0
- package/dist/bearerToken.js.map +1 -0
- package/dist/cli/commands/assemblies.d.ts +8 -2
- package/dist/cli/commands/assemblies.d.ts.map +1 -1
- package/dist/cli/commands/assemblies.js +566 -411
- package/dist/cli/commands/assemblies.js.map +1 -1
- package/dist/cli/commands/auth.d.ts +1 -4
- package/dist/cli/commands/auth.d.ts.map +1 -1
- package/dist/cli/commands/auth.js +7 -123
- package/dist/cli/commands/auth.js.map +1 -1
- package/dist/cli/commands/index.d.ts.map +1 -1
- package/dist/cli/commands/index.js +5 -0
- package/dist/cli/commands/index.js.map +1 -1
- package/dist/cli/commands/templates.d.ts.map +1 -1
- package/dist/cli/commands/templates.js +4 -14
- package/dist/cli/commands/templates.js.map +1 -1
- package/dist/cli/fileProcessingOptions.d.ts +35 -0
- package/dist/cli/fileProcessingOptions.d.ts.map +1 -0
- package/dist/cli/fileProcessingOptions.js +182 -0
- package/dist/cli/fileProcessingOptions.js.map +1 -0
- package/dist/cli/generateIntentDocs.d.ts +2 -0
- package/dist/cli/generateIntentDocs.d.ts.map +1 -0
- package/dist/cli/generateIntentDocs.js +321 -0
- package/dist/cli/generateIntentDocs.js.map +1 -0
- package/dist/cli/intentCommandSpecs.d.ts +36 -0
- package/dist/cli/intentCommandSpecs.d.ts.map +1 -0
- package/dist/cli/intentCommandSpecs.js +181 -0
- package/dist/cli/intentCommandSpecs.js.map +1 -0
- package/dist/cli/intentCommands.d.ts +13 -0
- package/dist/cli/intentCommands.d.ts.map +1 -0
- package/dist/cli/intentCommands.js +368 -0
- package/dist/cli/intentCommands.js.map +1 -0
- package/dist/cli/intentFields.d.ts +25 -0
- package/dist/cli/intentFields.d.ts.map +1 -0
- package/dist/cli/intentFields.js +298 -0
- package/dist/cli/intentFields.js.map +1 -0
- package/dist/cli/intentInputPolicy.d.ts +10 -0
- package/dist/cli/intentInputPolicy.d.ts.map +1 -0
- package/dist/cli/intentInputPolicy.js +2 -0
- package/dist/cli/intentInputPolicy.js.map +1 -0
- package/dist/cli/intentRuntime.d.ts +114 -0
- package/dist/cli/intentRuntime.d.ts.map +1 -0
- package/dist/cli/intentRuntime.js +464 -0
- package/dist/cli/intentRuntime.js.map +1 -0
- package/dist/cli/resultFiles.d.ts +19 -0
- package/dist/cli/resultFiles.d.ts.map +1 -0
- package/dist/cli/resultFiles.js +66 -0
- package/dist/cli/resultFiles.js.map +1 -0
- package/dist/cli/resultUrls.d.ts +19 -0
- package/dist/cli/resultUrls.d.ts.map +1 -0
- package/dist/cli/resultUrls.js +36 -0
- package/dist/cli/resultUrls.js.map +1 -0
- package/dist/cli/semanticIntents/imageDescribe.d.ts +43 -0
- package/dist/cli/semanticIntents/imageDescribe.d.ts.map +1 -0
- package/dist/cli/semanticIntents/imageDescribe.js +188 -0
- package/dist/cli/semanticIntents/imageDescribe.js.map +1 -0
- package/dist/cli/semanticIntents/index.d.ts +18 -0
- package/dist/cli/semanticIntents/index.d.ts.map +1 -0
- package/dist/cli/semanticIntents/index.js +18 -0
- package/dist/cli/semanticIntents/index.js.map +1 -0
- package/dist/cli/semanticIntents/markdownPdf.d.ts +4 -0
- package/dist/cli/semanticIntents/markdownPdf.d.ts.map +1 -0
- package/dist/cli/semanticIntents/markdownPdf.js +93 -0
- package/dist/cli/semanticIntents/markdownPdf.js.map +1 -0
- package/dist/cli/semanticIntents/parsing.d.ts +11 -0
- package/dist/cli/semanticIntents/parsing.d.ts.map +1 -0
- package/dist/cli/semanticIntents/parsing.js +29 -0
- package/dist/cli/semanticIntents/parsing.js.map +1 -0
- package/dist/cli/stepsInput.d.ts +4 -0
- package/dist/cli/stepsInput.d.ts.map +1 -0
- package/dist/cli/stepsInput.js +23 -0
- package/dist/cli/stepsInput.js.map +1 -0
- package/dist/cli.d.ts +1 -1
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +5 -4
- package/dist/cli.js.map +1 -1
- package/dist/ensureUniqueCounter.d.ts +8 -0
- package/dist/ensureUniqueCounter.d.ts.map +1 -0
- package/dist/ensureUniqueCounter.js +48 -0
- package/dist/ensureUniqueCounter.js.map +1 -0
- package/dist/inputFiles.d.ts +9 -0
- package/dist/inputFiles.d.ts.map +1 -1
- package/dist/inputFiles.js +177 -26
- package/dist/inputFiles.js.map +1 -1
- package/dist/robots.js +1 -1
- package/dist/robots.js.map +1 -1
- package/package.json +9 -7
- package/src/Transloadit.ts +35 -3
- package/src/alphalib/types/assemblyStatus.ts +4 -1
- package/src/alphalib/types/builtinTemplates.ts +24 -0
- package/src/alphalib/types/robots/ai-chat.ts +1 -0
- package/src/alphalib/types/skillFrontmatter.ts +24 -0
- package/src/alphalib/types/template.ts +14 -0
- package/src/bearerToken.ts +208 -0
- package/src/cli/commands/assemblies.ts +825 -505
- package/src/cli/commands/auth.ts +9 -151
- package/src/cli/commands/index.ts +6 -3
- package/src/cli/commands/templates.ts +6 -17
- package/src/cli/fileProcessingOptions.ts +294 -0
- package/src/cli/generateIntentDocs.ts +419 -0
- package/src/cli/intentCommandSpecs.ts +282 -0
- package/src/cli/intentCommands.ts +525 -0
- package/src/cli/intentFields.ts +403 -0
- package/src/cli/intentInputPolicy.ts +11 -0
- package/src/cli/intentRuntime.ts +734 -0
- package/src/cli/resultFiles.ts +105 -0
- package/src/cli/resultUrls.ts +72 -0
- package/src/cli/semanticIntents/imageDescribe.ts +254 -0
- package/src/cli/semanticIntents/index.ts +48 -0
- package/src/cli/semanticIntents/markdownPdf.ts +120 -0
- package/src/cli/semanticIntents/parsing.ts +56 -0
- package/src/cli/stepsInput.ts +32 -0
- package/src/cli.ts +5 -4
- package/src/ensureUniqueCounter.ts +75 -0
- package/src/inputFiles.ts +277 -26
- package/src/robots.ts +1 -1
|
@@ -0,0 +1,734 @@
|
|
|
1
|
+
import { statSync } from 'node:fs'
|
|
2
|
+
import { basename } from 'node:path'
|
|
3
|
+
import { Option } from 'clipanion'
|
|
4
|
+
import type { z } from 'zod'
|
|
5
|
+
|
|
6
|
+
import { prepareInputFiles } from '../inputFiles.ts'
|
|
7
|
+
import type { AssembliesCreateOptions } from './commands/assemblies.ts'
|
|
8
|
+
import * as assembliesCommands from './commands/assemblies.ts'
|
|
9
|
+
import { AuthenticatedCommand } from './commands/BaseCommand.ts'
|
|
10
|
+
import type { SharedCliOptionDocumentation } from './fileProcessingOptions.ts'
|
|
11
|
+
import {
|
|
12
|
+
concurrencyOption,
|
|
13
|
+
countProvidedInputs,
|
|
14
|
+
deleteAfterProcessingOption,
|
|
15
|
+
inputPathsOption,
|
|
16
|
+
printUrlsOption,
|
|
17
|
+
recursiveOption,
|
|
18
|
+
reprocessStaleOption,
|
|
19
|
+
singleAssemblyOption,
|
|
20
|
+
validateSharedFileProcessingOptions,
|
|
21
|
+
watchOption,
|
|
22
|
+
} from './fileProcessingOptions.ts'
|
|
23
|
+
import type { IntentFieldSpec } from './intentFields.ts'
|
|
24
|
+
import { coerceIntentFieldValue } from './intentFields.ts'
|
|
25
|
+
import type { IntentInputPolicy } from './intentInputPolicy.ts'
|
|
26
|
+
import { printResultUrls } from './resultUrls.ts'
|
|
27
|
+
import { getSemanticIntentDescriptor } from './semanticIntents/index.ts'
|
|
28
|
+
|
|
29
|
+
export interface PreparedIntentInputs {
|
|
30
|
+
cleanup: Array<() => Promise<void>>
|
|
31
|
+
hasTransientInputs: boolean
|
|
32
|
+
inputs: string[]
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export interface IntentSingleStepExecutionDefinition {
|
|
36
|
+
fields: readonly IntentOptionDefinition[]
|
|
37
|
+
fixedValues: Record<string, unknown>
|
|
38
|
+
kind: 'single-step'
|
|
39
|
+
resultStepName: string
|
|
40
|
+
schema: z.AnyZodObject
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
export interface IntentDynamicStepExecutionDefinition {
|
|
44
|
+
fields: readonly IntentOptionDefinition[]
|
|
45
|
+
handler: string
|
|
46
|
+
kind: 'dynamic-step'
|
|
47
|
+
resultStepName: string
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
export interface IntentTemplateExecutionDefinition {
|
|
51
|
+
kind: 'template'
|
|
52
|
+
templateId: string
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
export type IntentFileExecutionDefinition =
|
|
56
|
+
| IntentDynamicStepExecutionDefinition
|
|
57
|
+
| IntentSingleStepExecutionDefinition
|
|
58
|
+
| IntentTemplateExecutionDefinition
|
|
59
|
+
|
|
60
|
+
export interface IntentFileCommandDefinition {
|
|
61
|
+
commandLabel: string
|
|
62
|
+
execution: IntentFileExecutionDefinition
|
|
63
|
+
inputPolicy: IntentInputPolicy
|
|
64
|
+
outputDescription: string
|
|
65
|
+
outputMode?: 'directory' | 'file'
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
export interface IntentNoInputCommandDefinition {
|
|
69
|
+
execution: IntentSingleStepExecutionDefinition
|
|
70
|
+
outputDescription: string
|
|
71
|
+
outputMode?: 'directory' | 'file'
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
export type IntentRunnerKind = 'bundled' | 'no-input' | 'standard' | 'watchable'
|
|
75
|
+
|
|
76
|
+
export interface IntentCommandDefinition {
|
|
77
|
+
className: string
|
|
78
|
+
description: string
|
|
79
|
+
details: string
|
|
80
|
+
examples: Array<[string, string]>
|
|
81
|
+
intentDefinition: IntentFileCommandDefinition | IntentNoInputCommandDefinition
|
|
82
|
+
paths: string[]
|
|
83
|
+
runnerKind: IntentRunnerKind
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
export interface IntentOptionDefinition extends IntentFieldSpec {
|
|
87
|
+
description?: string
|
|
88
|
+
exampleValue?: unknown
|
|
89
|
+
optionFlags: string
|
|
90
|
+
propertyName: string
|
|
91
|
+
required?: boolean
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
const inputBase64OptionDocumentation = {
|
|
95
|
+
flags: '--input-base64',
|
|
96
|
+
type: 'base64 | data URL',
|
|
97
|
+
required: 'no',
|
|
98
|
+
example: 'data:text/plain;base64,SGVsbG8=',
|
|
99
|
+
description: 'Provide base64-encoded input content directly',
|
|
100
|
+
} as const satisfies SharedCliOptionDocumentation
|
|
101
|
+
|
|
102
|
+
export function getInputBase64OptionDocumentation(): SharedCliOptionDocumentation {
|
|
103
|
+
return inputBase64OptionDocumentation
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
function inputBase64Option(): string[] {
|
|
107
|
+
return Option.Array(inputBase64OptionDocumentation.flags, {
|
|
108
|
+
description: inputBase64OptionDocumentation.description,
|
|
109
|
+
}) as unknown as string[]
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
function isHttpUrl(value: string): boolean {
|
|
113
|
+
try {
|
|
114
|
+
const url = new URL(value)
|
|
115
|
+
return url.protocol === 'http:' || url.protocol === 'https:'
|
|
116
|
+
} catch {
|
|
117
|
+
return false
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
function parseBase64DataUrl(
|
|
122
|
+
value: string,
|
|
123
|
+
): { mediaType: string | null; payload: string; trimmed: string } | null {
|
|
124
|
+
const trimmed = value.trim()
|
|
125
|
+
const marker = ';base64,'
|
|
126
|
+
const markerIndex = trimmed.indexOf(marker)
|
|
127
|
+
if (!trimmed.startsWith('data:') || markerIndex === -1) {
|
|
128
|
+
return null
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
return {
|
|
132
|
+
trimmed,
|
|
133
|
+
mediaType: trimmed.slice('data:'.length, markerIndex).split(';')[0]?.toLowerCase() ?? null,
|
|
134
|
+
payload: trimmed.slice(markerIndex + marker.length),
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
function normalizeBase64Value(value: string): string {
|
|
139
|
+
const parsed = parseBase64DataUrl(value)
|
|
140
|
+
return parsed?.payload ?? value.trim()
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
function inferFilenameFromBase64Value(value: string, index: number): string {
|
|
144
|
+
const parsed = parseBase64DataUrl(value)
|
|
145
|
+
if (parsed == null) {
|
|
146
|
+
return `input-base64-${index}.bin`
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
const extensionByMediaType = {
|
|
150
|
+
'text/plain': 'txt',
|
|
151
|
+
'text/markdown': 'md',
|
|
152
|
+
'application/pdf': 'pdf',
|
|
153
|
+
'image/png': 'png',
|
|
154
|
+
'image/jpeg': 'jpg',
|
|
155
|
+
'image/webp': 'webp',
|
|
156
|
+
'application/json': 'json',
|
|
157
|
+
} as const satisfies Record<string, string>
|
|
158
|
+
const extension =
|
|
159
|
+
(extensionByMediaType as Record<string, string>)[parsed.mediaType ?? ''] ?? 'bin'
|
|
160
|
+
|
|
161
|
+
return `input-base64-${index}.${extension}`
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
export async function prepareIntentInputs({
|
|
165
|
+
inputBase64Values,
|
|
166
|
+
inputValues,
|
|
167
|
+
}: {
|
|
168
|
+
inputBase64Values: string[]
|
|
169
|
+
inputValues: string[]
|
|
170
|
+
}): Promise<PreparedIntentInputs> {
|
|
171
|
+
const preparedOrder: string[] = []
|
|
172
|
+
const syntheticInputs: Array<
|
|
173
|
+
| {
|
|
174
|
+
base64: string
|
|
175
|
+
field: string
|
|
176
|
+
filename: string
|
|
177
|
+
kind: 'base64'
|
|
178
|
+
}
|
|
179
|
+
| {
|
|
180
|
+
field: string
|
|
181
|
+
kind: 'url'
|
|
182
|
+
url: string
|
|
183
|
+
}
|
|
184
|
+
> = []
|
|
185
|
+
|
|
186
|
+
for (const value of inputValues) {
|
|
187
|
+
if (!isHttpUrl(value)) {
|
|
188
|
+
preparedOrder.push(value)
|
|
189
|
+
continue
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
const field = `input_url_${syntheticInputs.length + 1}`
|
|
193
|
+
syntheticInputs.push({
|
|
194
|
+
kind: 'url',
|
|
195
|
+
field,
|
|
196
|
+
url: value,
|
|
197
|
+
})
|
|
198
|
+
preparedOrder.push(field)
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
for (const [index, value] of inputBase64Values.entries()) {
|
|
202
|
+
const field = `input_base64_${index + 1}`
|
|
203
|
+
const filename = inferFilenameFromBase64Value(value, index + 1)
|
|
204
|
+
syntheticInputs.push({
|
|
205
|
+
kind: 'base64',
|
|
206
|
+
field,
|
|
207
|
+
filename,
|
|
208
|
+
base64: normalizeBase64Value(value),
|
|
209
|
+
})
|
|
210
|
+
preparedOrder.push(field)
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
if (syntheticInputs.length === 0) {
|
|
214
|
+
return {
|
|
215
|
+
cleanup: [],
|
|
216
|
+
hasTransientInputs: false,
|
|
217
|
+
inputs: preparedOrder,
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
const prepared = await prepareInputFiles({
|
|
222
|
+
inputFiles: syntheticInputs.map((input) => {
|
|
223
|
+
if (input.kind === 'url') {
|
|
224
|
+
return {
|
|
225
|
+
kind: 'url' as const,
|
|
226
|
+
field: input.field,
|
|
227
|
+
url: input.url,
|
|
228
|
+
filename: basename(new URL(input.url).pathname) || undefined,
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
return {
|
|
233
|
+
kind: 'base64' as const,
|
|
234
|
+
field: input.field,
|
|
235
|
+
base64: input.base64,
|
|
236
|
+
filename: input.filename,
|
|
237
|
+
}
|
|
238
|
+
}),
|
|
239
|
+
base64Strategy: 'tempfile',
|
|
240
|
+
allowPrivateUrls: false,
|
|
241
|
+
urlStrategy: 'download',
|
|
242
|
+
})
|
|
243
|
+
|
|
244
|
+
const inputs = preparedOrder.map((value) => prepared.files[value] ?? value)
|
|
245
|
+
|
|
246
|
+
return {
|
|
247
|
+
cleanup: prepared.cleanup,
|
|
248
|
+
hasTransientInputs: true,
|
|
249
|
+
inputs,
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
function parseIntentStep<TSchema extends z.AnyZodObject>({
|
|
254
|
+
fields,
|
|
255
|
+
fixedValues,
|
|
256
|
+
rawValues,
|
|
257
|
+
schema,
|
|
258
|
+
}: {
|
|
259
|
+
fields: readonly IntentFieldSpec[]
|
|
260
|
+
fixedValues: Record<string, unknown>
|
|
261
|
+
rawValues: Record<string, unknown>
|
|
262
|
+
schema: TSchema
|
|
263
|
+
}): z.input<TSchema> {
|
|
264
|
+
const input: Record<string, unknown> = { ...fixedValues }
|
|
265
|
+
|
|
266
|
+
for (const fieldSpec of fields) {
|
|
267
|
+
const rawValue = rawValues[fieldSpec.name]
|
|
268
|
+
if (rawValue == null) continue
|
|
269
|
+
const fieldSchema = schema.shape[fieldSpec.name]
|
|
270
|
+
input[fieldSpec.name] = coerceIntentFieldValue(fieldSpec.kind, rawValue, fieldSchema)
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
const parsed = schema.parse(input) as Record<string, unknown>
|
|
274
|
+
const normalizedInput: Record<string, unknown> = { ...fixedValues }
|
|
275
|
+
|
|
276
|
+
for (const fieldSpec of fields) {
|
|
277
|
+
const rawValue = rawValues[fieldSpec.name]
|
|
278
|
+
if (rawValue == null) continue
|
|
279
|
+
normalizedInput[fieldSpec.name] = parsed[fieldSpec.name]
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
return normalizedInput as z.input<TSchema>
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
function resolveSingleStepFixedValues(
|
|
286
|
+
execution: IntentSingleStepExecutionDefinition,
|
|
287
|
+
inputPolicy: IntentInputPolicy,
|
|
288
|
+
hasInputs: boolean,
|
|
289
|
+
): Record<string, unknown> {
|
|
290
|
+
if (!hasInputs) {
|
|
291
|
+
return execution.fixedValues
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
if (inputPolicy.kind !== 'optional' || inputPolicy.attachUseWhenInputsProvided !== true) {
|
|
295
|
+
return execution.fixedValues
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
return {
|
|
299
|
+
...execution.fixedValues,
|
|
300
|
+
use: ':original',
|
|
301
|
+
}
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
function createSingleStep(
|
|
305
|
+
execution: IntentSingleStepExecutionDefinition,
|
|
306
|
+
inputPolicy: IntentInputPolicy,
|
|
307
|
+
rawValues: Record<string, unknown>,
|
|
308
|
+
hasInputs: boolean,
|
|
309
|
+
): z.input<typeof execution.schema> {
|
|
310
|
+
return parseIntentStep({
|
|
311
|
+
schema: execution.schema,
|
|
312
|
+
fixedValues: resolveSingleStepFixedValues(execution, inputPolicy, hasInputs),
|
|
313
|
+
fields: execution.fields,
|
|
314
|
+
rawValues,
|
|
315
|
+
})
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
function createDynamicIntentStep(
|
|
319
|
+
execution: IntentDynamicStepExecutionDefinition,
|
|
320
|
+
rawValues: Record<string, unknown>,
|
|
321
|
+
): Record<string, unknown> {
|
|
322
|
+
return getSemanticIntentDescriptor(execution.handler).createStep(rawValues)
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
function requiresLocalInput(
|
|
326
|
+
inputPolicy: IntentInputPolicy,
|
|
327
|
+
rawValues: Record<string, unknown>,
|
|
328
|
+
): boolean {
|
|
329
|
+
if (inputPolicy.kind === 'required') {
|
|
330
|
+
return true
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
return rawValues[inputPolicy.field] == null
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
async function executeIntentCommand({
|
|
337
|
+
client,
|
|
338
|
+
definition,
|
|
339
|
+
output,
|
|
340
|
+
outputPath,
|
|
341
|
+
printUrls,
|
|
342
|
+
rawValues,
|
|
343
|
+
createOptions,
|
|
344
|
+
}: {
|
|
345
|
+
client: AuthenticatedCommand['client']
|
|
346
|
+
createOptions: Omit<AssembliesCreateOptions, 'output' | 'steps' | 'stepsData' | 'template'>
|
|
347
|
+
definition: IntentFileCommandDefinition | IntentNoInputCommandDefinition
|
|
348
|
+
output: AuthenticatedCommand['output']
|
|
349
|
+
outputPath?: string
|
|
350
|
+
printUrls: boolean
|
|
351
|
+
rawValues: Record<string, unknown>
|
|
352
|
+
}): Promise<number | undefined> {
|
|
353
|
+
const inputPolicy: IntentInputPolicy =
|
|
354
|
+
'inputPolicy' in definition ? definition.inputPolicy : { kind: 'required' }
|
|
355
|
+
const executionOptions =
|
|
356
|
+
definition.execution.kind === 'template'
|
|
357
|
+
? {
|
|
358
|
+
template: definition.execution.templateId,
|
|
359
|
+
}
|
|
360
|
+
: {
|
|
361
|
+
stepsData: {
|
|
362
|
+
[definition.execution.resultStepName]:
|
|
363
|
+
definition.execution.kind === 'single-step'
|
|
364
|
+
? createSingleStep(
|
|
365
|
+
definition.execution,
|
|
366
|
+
inputPolicy,
|
|
367
|
+
rawValues,
|
|
368
|
+
createOptions.inputs.length > 0,
|
|
369
|
+
)
|
|
370
|
+
: createDynamicIntentStep(definition.execution, rawValues),
|
|
371
|
+
} as AssembliesCreateOptions['stepsData'],
|
|
372
|
+
}
|
|
373
|
+
|
|
374
|
+
const { hasFailures, resultUrls } = await assembliesCommands.create(output, client, {
|
|
375
|
+
...createOptions,
|
|
376
|
+
output: outputPath ?? null,
|
|
377
|
+
outputMode: definition.outputMode,
|
|
378
|
+
...executionOptions,
|
|
379
|
+
})
|
|
380
|
+
if (printUrls) {
|
|
381
|
+
printResultUrls(output, resultUrls)
|
|
382
|
+
}
|
|
383
|
+
return hasFailures ? 1 : undefined
|
|
384
|
+
}
|
|
385
|
+
|
|
386
|
+
abstract class GeneratedIntentCommandBase extends AuthenticatedCommand {
|
|
387
|
+
declare static intentDefinition: IntentFileCommandDefinition | IntentNoInputCommandDefinition
|
|
388
|
+
|
|
389
|
+
outputPath = Option.String('--out,-o', {
|
|
390
|
+
description: this.getOutputDescription(),
|
|
391
|
+
})
|
|
392
|
+
|
|
393
|
+
printUrls = printUrlsOption()
|
|
394
|
+
|
|
395
|
+
protected getIntentDefinition(): IntentFileCommandDefinition | IntentNoInputCommandDefinition {
|
|
396
|
+
const commandClass = this.constructor as unknown as typeof GeneratedIntentCommandBase
|
|
397
|
+
return commandClass.intentDefinition
|
|
398
|
+
}
|
|
399
|
+
|
|
400
|
+
protected getIntentRawValues(): Record<string, unknown> {
|
|
401
|
+
return readIntentRawValues(this, getIntentOptionDefinitions(this.getIntentDefinition()))
|
|
402
|
+
}
|
|
403
|
+
|
|
404
|
+
private getOutputDescription(): string {
|
|
405
|
+
return this.getIntentDefinition().outputDescription
|
|
406
|
+
}
|
|
407
|
+
|
|
408
|
+
protected validateOutputChoice(): number | undefined {
|
|
409
|
+
if (this.outputPath == null && !this.printUrls) {
|
|
410
|
+
this.output.error('Specify at least one of --out or --print-urls')
|
|
411
|
+
return 1
|
|
412
|
+
}
|
|
413
|
+
|
|
414
|
+
return undefined
|
|
415
|
+
}
|
|
416
|
+
}
|
|
417
|
+
|
|
418
|
+
export abstract class GeneratedNoInputIntentCommand extends GeneratedIntentCommandBase {
|
|
419
|
+
protected override async run(): Promise<number | undefined> {
|
|
420
|
+
const outputValidationError = this.validateOutputChoice()
|
|
421
|
+
if (outputValidationError != null) {
|
|
422
|
+
return outputValidationError
|
|
423
|
+
}
|
|
424
|
+
|
|
425
|
+
return await executeIntentCommand({
|
|
426
|
+
client: this.client,
|
|
427
|
+
createOptions: {
|
|
428
|
+
inputs: [],
|
|
429
|
+
},
|
|
430
|
+
definition: this.getIntentDefinition() as IntentNoInputCommandDefinition,
|
|
431
|
+
output: this.output,
|
|
432
|
+
outputPath: this.outputPath,
|
|
433
|
+
printUrls: this.printUrls ?? false,
|
|
434
|
+
rawValues: this.getIntentRawValues(),
|
|
435
|
+
})
|
|
436
|
+
}
|
|
437
|
+
}
|
|
438
|
+
|
|
439
|
+
export function getIntentOptionDefinitions(
|
|
440
|
+
definition: IntentFileCommandDefinition | IntentNoInputCommandDefinition,
|
|
441
|
+
): readonly IntentOptionDefinition[] {
|
|
442
|
+
if (definition.execution.kind !== 'single-step' && definition.execution.kind !== 'dynamic-step') {
|
|
443
|
+
return []
|
|
444
|
+
}
|
|
445
|
+
|
|
446
|
+
return definition.execution.fields
|
|
447
|
+
}
|
|
448
|
+
|
|
449
|
+
function readIntentRawValues(
|
|
450
|
+
command: object,
|
|
451
|
+
fieldDefinitions: readonly IntentOptionDefinition[],
|
|
452
|
+
): Record<string, unknown> {
|
|
453
|
+
const rawValues: Record<string, unknown> = {}
|
|
454
|
+
|
|
455
|
+
for (const fieldDefinition of fieldDefinitions) {
|
|
456
|
+
rawValues[fieldDefinition.name] = (command as Record<string, unknown>)[
|
|
457
|
+
fieldDefinition.propertyName
|
|
458
|
+
]
|
|
459
|
+
}
|
|
460
|
+
|
|
461
|
+
return rawValues
|
|
462
|
+
}
|
|
463
|
+
|
|
464
|
+
abstract class GeneratedFileIntentCommandBase extends GeneratedIntentCommandBase {
|
|
465
|
+
inputs = inputPathsOption('Provide an input path, directory, URL, or - for stdin')
|
|
466
|
+
|
|
467
|
+
inputBase64 = inputBase64Option()
|
|
468
|
+
|
|
469
|
+
recursive = recursiveOption()
|
|
470
|
+
|
|
471
|
+
deleteAfterProcessing = deleteAfterProcessingOption()
|
|
472
|
+
|
|
473
|
+
reprocessStale = reprocessStaleOption()
|
|
474
|
+
|
|
475
|
+
protected override getIntentDefinition(): IntentFileCommandDefinition {
|
|
476
|
+
return super.getIntentDefinition() as IntentFileCommandDefinition
|
|
477
|
+
}
|
|
478
|
+
|
|
479
|
+
protected async prepareInputs(): Promise<PreparedIntentInputs> {
|
|
480
|
+
return await prepareIntentInputs({
|
|
481
|
+
inputValues: this.inputs ?? [],
|
|
482
|
+
inputBase64Values: this.inputBase64 ?? [],
|
|
483
|
+
})
|
|
484
|
+
}
|
|
485
|
+
|
|
486
|
+
protected getCreateOptions(
|
|
487
|
+
inputs: string[],
|
|
488
|
+
): Omit<AssembliesCreateOptions, 'output' | 'steps' | 'stepsData' | 'template'> {
|
|
489
|
+
return {
|
|
490
|
+
del: this.deleteAfterProcessing,
|
|
491
|
+
inputs,
|
|
492
|
+
reprocessStale: this.reprocessStale,
|
|
493
|
+
recursive: this.recursive,
|
|
494
|
+
}
|
|
495
|
+
}
|
|
496
|
+
|
|
497
|
+
protected getProvidedInputCount(): number {
|
|
498
|
+
return countProvidedInputs({
|
|
499
|
+
inputs: this.inputs,
|
|
500
|
+
inputBase64: this.inputBase64,
|
|
501
|
+
})
|
|
502
|
+
}
|
|
503
|
+
|
|
504
|
+
protected hasTransientInputSources(): boolean {
|
|
505
|
+
return (
|
|
506
|
+
(this.inputs?.some((input) => isHttpUrl(input)) ?? false) ||
|
|
507
|
+
(this.inputBase64?.length ?? 0) > 0
|
|
508
|
+
)
|
|
509
|
+
}
|
|
510
|
+
|
|
511
|
+
protected resolveOutputMode(): 'directory' | 'file' | undefined {
|
|
512
|
+
if (this.getIntentDefinition().outputMode != null) {
|
|
513
|
+
return this.getIntentDefinition().outputMode
|
|
514
|
+
}
|
|
515
|
+
|
|
516
|
+
if (this.outputPath == null) {
|
|
517
|
+
return undefined
|
|
518
|
+
}
|
|
519
|
+
|
|
520
|
+
try {
|
|
521
|
+
return statSync(this.outputPath).isDirectory() ? 'directory' : 'file'
|
|
522
|
+
} catch {
|
|
523
|
+
return 'file'
|
|
524
|
+
}
|
|
525
|
+
}
|
|
526
|
+
|
|
527
|
+
protected isDirectoryOutputTarget(): boolean {
|
|
528
|
+
return this.resolveOutputMode() === 'directory'
|
|
529
|
+
}
|
|
530
|
+
|
|
531
|
+
protected validateInputPresence(rawValues: Record<string, unknown>): number | undefined {
|
|
532
|
+
const intentDefinition = this.getIntentDefinition()
|
|
533
|
+
const inputCount = this.getProvidedInputCount()
|
|
534
|
+
if (inputCount !== 0) {
|
|
535
|
+
return undefined
|
|
536
|
+
}
|
|
537
|
+
|
|
538
|
+
if (!requiresLocalInput(intentDefinition.inputPolicy, rawValues)) {
|
|
539
|
+
return undefined
|
|
540
|
+
}
|
|
541
|
+
|
|
542
|
+
if (intentDefinition.inputPolicy.kind === 'required') {
|
|
543
|
+
this.output.error(`${intentDefinition.commandLabel} requires --input or --input-base64`)
|
|
544
|
+
return 1
|
|
545
|
+
}
|
|
546
|
+
|
|
547
|
+
this.output.error(
|
|
548
|
+
`${intentDefinition.commandLabel} requires --input or --${intentDefinition.inputPolicy.field.replaceAll('_', '-')}`,
|
|
549
|
+
)
|
|
550
|
+
return 1
|
|
551
|
+
}
|
|
552
|
+
|
|
553
|
+
protected validateBeforePreparingInputs(rawValues: Record<string, unknown>): number | undefined {
|
|
554
|
+
const outputValidationError = this.validateOutputChoice()
|
|
555
|
+
if (outputValidationError != null) {
|
|
556
|
+
return outputValidationError
|
|
557
|
+
}
|
|
558
|
+
|
|
559
|
+
const validationError = this.validateInputPresence(rawValues)
|
|
560
|
+
if (validationError != null) {
|
|
561
|
+
return validationError
|
|
562
|
+
}
|
|
563
|
+
|
|
564
|
+
const execution = this.getIntentDefinition().execution
|
|
565
|
+
if (execution.kind === 'dynamic-step') {
|
|
566
|
+
createDynamicIntentStep(execution, rawValues)
|
|
567
|
+
}
|
|
568
|
+
|
|
569
|
+
return undefined
|
|
570
|
+
}
|
|
571
|
+
|
|
572
|
+
protected validatePreparedInputs(_preparedInputs: PreparedIntentInputs): number | undefined {
|
|
573
|
+
return undefined
|
|
574
|
+
}
|
|
575
|
+
|
|
576
|
+
protected async executePreparedInputs(
|
|
577
|
+
rawValues: Record<string, unknown>,
|
|
578
|
+
preparedInputs: PreparedIntentInputs,
|
|
579
|
+
): Promise<number | undefined> {
|
|
580
|
+
let effectivePreparedInputs = preparedInputs
|
|
581
|
+
const execution = this.getIntentDefinition().execution
|
|
582
|
+
if (execution.kind === 'dynamic-step') {
|
|
583
|
+
const descriptor = getSemanticIntentDescriptor(execution.handler)
|
|
584
|
+
if (descriptor.prepareInputs != null) {
|
|
585
|
+
effectivePreparedInputs = await descriptor.prepareInputs(preparedInputs, rawValues)
|
|
586
|
+
}
|
|
587
|
+
}
|
|
588
|
+
|
|
589
|
+
return await executeIntentCommand({
|
|
590
|
+
client: this.client,
|
|
591
|
+
createOptions: this.getCreateOptions(effectivePreparedInputs.inputs),
|
|
592
|
+
definition: this.getIntentDefinition(),
|
|
593
|
+
output: this.output,
|
|
594
|
+
outputPath: this.outputPath,
|
|
595
|
+
printUrls: this.printUrls ?? false,
|
|
596
|
+
rawValues,
|
|
597
|
+
})
|
|
598
|
+
}
|
|
599
|
+
|
|
600
|
+
protected override async run(): Promise<number | undefined> {
|
|
601
|
+
const rawValues = this.getIntentRawValues()
|
|
602
|
+
const validationError = this.validateBeforePreparingInputs(rawValues)
|
|
603
|
+
if (validationError != null) {
|
|
604
|
+
return validationError
|
|
605
|
+
}
|
|
606
|
+
|
|
607
|
+
const preparedInputs = await this.prepareInputs()
|
|
608
|
+
try {
|
|
609
|
+
const preparedInputError = this.validatePreparedInputs(preparedInputs)
|
|
610
|
+
if (preparedInputError != null) {
|
|
611
|
+
return preparedInputError
|
|
612
|
+
}
|
|
613
|
+
|
|
614
|
+
return await this.executePreparedInputs(rawValues, preparedInputs)
|
|
615
|
+
} finally {
|
|
616
|
+
await Promise.all(preparedInputs.cleanup.map((cleanup) => cleanup()))
|
|
617
|
+
}
|
|
618
|
+
}
|
|
619
|
+
}
|
|
620
|
+
|
|
621
|
+
export abstract class GeneratedWatchableFileIntentCommand extends GeneratedFileIntentCommandBase {
|
|
622
|
+
watch = watchOption()
|
|
623
|
+
|
|
624
|
+
concurrency = concurrencyOption()
|
|
625
|
+
|
|
626
|
+
protected override getCreateOptions(
|
|
627
|
+
inputs: string[],
|
|
628
|
+
): Omit<AssembliesCreateOptions, 'output' | 'steps' | 'stepsData' | 'template'> {
|
|
629
|
+
return {
|
|
630
|
+
...super.getCreateOptions(inputs),
|
|
631
|
+
concurrency: this.concurrency,
|
|
632
|
+
watch: this.watch,
|
|
633
|
+
}
|
|
634
|
+
}
|
|
635
|
+
|
|
636
|
+
protected override validateBeforePreparingInputs(
|
|
637
|
+
rawValues: Record<string, unknown>,
|
|
638
|
+
): number | undefined {
|
|
639
|
+
const validationError = super.validateBeforePreparingInputs(rawValues)
|
|
640
|
+
if (validationError != null) {
|
|
641
|
+
return validationError
|
|
642
|
+
}
|
|
643
|
+
|
|
644
|
+
const sharedValidationError = validateSharedFileProcessingOptions({
|
|
645
|
+
explicitInputCount: this.getProvidedInputCount(),
|
|
646
|
+
singleAssembly: this.getSingleAssemblyEnabled(),
|
|
647
|
+
watch: this.watch,
|
|
648
|
+
watchRequiresInputsMessage: `${this.getIntentDefinition().commandLabel} --watch requires --input or --input-base64`,
|
|
649
|
+
})
|
|
650
|
+
if (sharedValidationError != null) {
|
|
651
|
+
this.output.error(sharedValidationError)
|
|
652
|
+
return 1
|
|
653
|
+
}
|
|
654
|
+
|
|
655
|
+
if (this.watch && this.hasTransientInputSources()) {
|
|
656
|
+
this.output.error('--watch is only supported for filesystem inputs')
|
|
657
|
+
return 1
|
|
658
|
+
}
|
|
659
|
+
|
|
660
|
+
return undefined
|
|
661
|
+
}
|
|
662
|
+
|
|
663
|
+
protected getSingleAssemblyEnabled(): boolean {
|
|
664
|
+
return false
|
|
665
|
+
}
|
|
666
|
+
|
|
667
|
+
protected override validatePreparedInputs(
|
|
668
|
+
preparedInputs: PreparedIntentInputs,
|
|
669
|
+
): number | undefined {
|
|
670
|
+
if (this.watch && preparedInputs.hasTransientInputs) {
|
|
671
|
+
this.output.error('--watch is only supported for filesystem inputs')
|
|
672
|
+
return 1
|
|
673
|
+
}
|
|
674
|
+
return undefined
|
|
675
|
+
}
|
|
676
|
+
}
|
|
677
|
+
|
|
678
|
+
export abstract class GeneratedStandardFileIntentCommand extends GeneratedWatchableFileIntentCommand {
|
|
679
|
+
singleAssembly = singleAssemblyOption()
|
|
680
|
+
|
|
681
|
+
protected override getSingleAssemblyEnabled(): boolean {
|
|
682
|
+
return this.singleAssembly
|
|
683
|
+
}
|
|
684
|
+
|
|
685
|
+
protected override getCreateOptions(
|
|
686
|
+
inputs: string[],
|
|
687
|
+
): Omit<AssembliesCreateOptions, 'output' | 'steps' | 'stepsData' | 'template'> {
|
|
688
|
+
return {
|
|
689
|
+
...super.getCreateOptions(inputs),
|
|
690
|
+
singleAssembly: this.singleAssembly,
|
|
691
|
+
}
|
|
692
|
+
}
|
|
693
|
+
|
|
694
|
+
protected override validateBeforePreparingInputs(
|
|
695
|
+
rawValues: Record<string, unknown>,
|
|
696
|
+
): number | undefined {
|
|
697
|
+
const validationError = super.validateBeforePreparingInputs(rawValues)
|
|
698
|
+
if (validationError != null) {
|
|
699
|
+
return validationError
|
|
700
|
+
}
|
|
701
|
+
|
|
702
|
+
if (
|
|
703
|
+
this.singleAssembly &&
|
|
704
|
+
(this.getProvidedInputCount() > 1 ||
|
|
705
|
+
this.inputs.some((inputPath) => {
|
|
706
|
+
try {
|
|
707
|
+
return statSync(inputPath).isDirectory()
|
|
708
|
+
} catch {
|
|
709
|
+
return false
|
|
710
|
+
}
|
|
711
|
+
})) &&
|
|
712
|
+
this.outputPath != null &&
|
|
713
|
+
!this.isDirectoryOutputTarget()
|
|
714
|
+
) {
|
|
715
|
+
this.output.error(
|
|
716
|
+
'Output must be a directory when using --single-assembly with multiple inputs',
|
|
717
|
+
)
|
|
718
|
+
return 1
|
|
719
|
+
}
|
|
720
|
+
|
|
721
|
+
return undefined
|
|
722
|
+
}
|
|
723
|
+
}
|
|
724
|
+
|
|
725
|
+
export abstract class GeneratedBundledFileIntentCommand extends GeneratedFileIntentCommandBase {
|
|
726
|
+
protected override getCreateOptions(
|
|
727
|
+
inputs: string[],
|
|
728
|
+
): Omit<AssembliesCreateOptions, 'output' | 'steps' | 'stepsData' | 'template'> {
|
|
729
|
+
return {
|
|
730
|
+
...super.getCreateOptions(inputs),
|
|
731
|
+
singleAssembly: true,
|
|
732
|
+
}
|
|
733
|
+
}
|
|
734
|
+
}
|