@transloadit/node 4.5.1 → 4.6.0
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/dist/Transloadit.d.ts +1 -1
- package/dist/Transloadit.d.ts.map +1 -1
- package/dist/Transloadit.js +1 -1
- package/dist/Transloadit.js.map +1 -1
- package/dist/alphalib/mcache.d.ts.map +1 -1
- package/dist/alphalib/mcache.js +4 -2
- package/dist/alphalib/mcache.js.map +1 -1
- package/dist/alphalib/types/assemblyReplay.d.ts +2 -0
- package/dist/alphalib/types/assemblyReplay.d.ts.map +1 -1
- package/dist/alphalib/types/assemblyReplayNotification.d.ts +2 -0
- package/dist/alphalib/types/assemblyReplayNotification.d.ts.map +1 -1
- package/dist/alphalib/types/assemblyStatus.d.ts +20 -20
- package/dist/alphalib/types/assemblyStatus.d.ts.map +1 -1
- package/dist/alphalib/types/assemblyStatus.js +13 -1
- package/dist/alphalib/types/assemblyStatus.js.map +1 -1
- package/dist/alphalib/types/robots/_index.d.ts +20 -0
- package/dist/alphalib/types/robots/_index.d.ts.map +1 -1
- package/dist/alphalib/types/robots/ai-chat.d.ts +20 -0
- package/dist/alphalib/types/robots/ai-chat.d.ts.map +1 -1
- package/dist/alphalib/types/robots/ai-chat.js +5 -3
- package/dist/alphalib/types/robots/ai-chat.js.map +1 -1
- package/dist/alphalib/types/template.d.ts +36 -0
- package/dist/alphalib/types/template.d.ts.map +1 -1
- package/dist/cli/commands/assemblies.d.ts.map +1 -1
- package/dist/cli/commands/assemblies.js +109 -25
- package/dist/cli/commands/assemblies.js.map +1 -1
- package/dist/cli/commands/docs.d.ts +15 -0
- package/dist/cli/commands/docs.d.ts.map +1 -0
- package/dist/cli/commands/docs.js +58 -0
- package/dist/cli/commands/docs.js.map +1 -0
- package/dist/cli/commands/index.d.ts.map +1 -1
- package/dist/cli/commands/index.js +4 -0
- package/dist/cli/commands/index.js.map +1 -1
- package/dist/robots.d.ts +2 -1
- package/dist/robots.d.ts.map +1 -1
- package/dist/robots.js +9 -3
- package/dist/robots.js.map +1 -1
- package/package.json +2 -2
- package/src/Transloadit.ts +1 -1
- package/src/alphalib/mcache.ts +5 -3
- package/src/alphalib/types/assemblyStatus.ts +13 -1
- package/src/alphalib/types/robots/ai-chat.ts +11 -3
- package/src/cli/commands/assemblies.ts +131 -25
- package/src/cli/commands/docs.ts +68 -0
- package/src/cli/commands/index.ts +5 -2
- package/src/robots.ts +12 -4
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@transloadit/node",
|
|
3
|
-
"version": "4.
|
|
3
|
+
"version": "4.6.0",
|
|
4
4
|
"description": "Node.js SDK for Transloadit",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"keywords": [
|
|
@@ -76,7 +76,7 @@
|
|
|
76
76
|
"fix": "npm-run-all --serial 'fix:js'",
|
|
77
77
|
"lint:deps": "knip --dependencies --no-progress",
|
|
78
78
|
"fix:deps": "knip --dependencies --no-progress --fix",
|
|
79
|
-
"prepack": "rm -f tsconfig.tsbuildinfo tsconfig.build.tsbuildinfo && yarn --cwd ../.. tsc:node",
|
|
79
|
+
"prepack": "node -e \"require('node:fs').rmSync('dist',{recursive:true,force:true})\" && rm -f tsconfig.tsbuildinfo tsconfig.build.tsbuildinfo && yarn --cwd ../.. tsc:node",
|
|
80
80
|
"test:unit": "yarn --cwd ../.. tsc:utils && ../../node_modules/.bin/vitest run --coverage ./test/unit",
|
|
81
81
|
"test:e2e": "yarn --cwd ../.. tsc:utils && ../../node_modules/.bin/vitest run ./test/e2e",
|
|
82
82
|
"test": "yarn --cwd ../.. tsc:utils && ../../node_modules/.bin/vitest run --coverage"
|
package/src/Transloadit.ts
CHANGED
|
@@ -90,7 +90,7 @@ export type {
|
|
|
90
90
|
RobotListResult,
|
|
91
91
|
RobotParamHelp,
|
|
92
92
|
} from './robots.ts'
|
|
93
|
-
export { getRobotHelp, listRobots } from './robots.ts'
|
|
93
|
+
export { getRobotHelp, isKnownRobot, listRobots } from './robots.ts'
|
|
94
94
|
|
|
95
95
|
const log = debug('transloadit')
|
|
96
96
|
const logWarn = debug('transloadit:warn')
|
package/src/alphalib/mcache.ts
CHANGED
|
@@ -98,9 +98,11 @@ export class Mcache<T> {
|
|
|
98
98
|
})
|
|
99
99
|
|
|
100
100
|
this.#pending.set(key, promise)
|
|
101
|
-
void promise
|
|
102
|
-
|
|
103
|
-
|
|
101
|
+
void promise
|
|
102
|
+
.finally(() => {
|
|
103
|
+
this.#pending.delete(key)
|
|
104
|
+
})
|
|
105
|
+
.catch(() => undefined)
|
|
104
106
|
return promise
|
|
105
107
|
}
|
|
106
108
|
|
|
@@ -10,7 +10,6 @@ export const assemblyStatusOkCodeSchema = z.enum([
|
|
|
10
10
|
'ASSEMBLY_CANCELED',
|
|
11
11
|
'ASSEMBLY_COMPLETED',
|
|
12
12
|
'ASSEMBLY_EXECUTING',
|
|
13
|
-
'ASSEMBLY_EXPIRED',
|
|
14
13
|
'ASSEMBLY_REPLAYING',
|
|
15
14
|
'ASSEMBLY_UPLOADING',
|
|
16
15
|
'REQUEST_ABORTED',
|
|
@@ -27,6 +26,7 @@ export const assemblyStatusErrCodeSchema = z.enum([
|
|
|
27
26
|
'ASSEMBLY_CRASHED',
|
|
28
27
|
'ASSEMBLY_DISALLOWED_ROBOTS_USED',
|
|
29
28
|
'ASSEMBLY_EMPTY_STEPS',
|
|
29
|
+
'ASSEMBLY_EXECUTION_PROGRESS_NOT_ENABLED',
|
|
30
30
|
'ASSEMBLY_EXPIRED',
|
|
31
31
|
'ASSEMBLY_FILE_NOT_RESERVED',
|
|
32
32
|
'ASSEMBLY_INFINITE',
|
|
@@ -43,9 +43,17 @@ export const assemblyStatusErrCodeSchema = z.enum([
|
|
|
43
43
|
'ASSEMBLY_NOT_FINISHED',
|
|
44
44
|
'ASSEMBLY_NOT_FOUND',
|
|
45
45
|
'ASSEMBLY_NOT_REPLAYED',
|
|
46
|
+
'ASSEMBLY_NOTIFICATION_LIST_ERROR',
|
|
46
47
|
'ASSEMBLY_NOTIFICATION_NOT_PERSISTED',
|
|
48
|
+
'ASSEMBLY_NOTIFICATION_NOT_REPLAYED',
|
|
49
|
+
'ASSEMBLY_NOTIFICATIONS_LIST_ERROR',
|
|
50
|
+
'ASSEMBLY_NO_NOTIFY_URL',
|
|
47
51
|
'ASSEMBLY_ROBOT_MISSING',
|
|
48
52
|
'ASSEMBLY_SATURATED',
|
|
53
|
+
'ASSEMBLY_STATS_ERROR',
|
|
54
|
+
'ASSEMBLY_STATS_INVALID_TIME',
|
|
55
|
+
'ASSEMBLY_STATS_MISSING_REGION',
|
|
56
|
+
'ASSEMBLY_STATUS_FETCHING_RATE_LIMIT_REACHED',
|
|
49
57
|
'ASSEMBLY_STATUS_NOT_FOUND',
|
|
50
58
|
'ASSEMBLY_STATUS_PARSE_ERROR',
|
|
51
59
|
'ASSEMBLY_STEP_INVALID_ROBOT',
|
|
@@ -61,6 +69,9 @@ export const assemblyStatusErrCodeSchema = z.enum([
|
|
|
61
69
|
'AUTH_KEYS_NOT_FOUND',
|
|
62
70
|
'AUTH_SECRET_NOT_RETRIEVED',
|
|
63
71
|
'AZURE_STORE_ACCESS_DENIED',
|
|
72
|
+
'BEARER_TOKEN_AUTH_KEY_MISMATCH',
|
|
73
|
+
'BEARER_TOKEN_EXPIRED',
|
|
74
|
+
'BEARER_TOKEN_INVALID',
|
|
64
75
|
'BACKBLAZE_IMPORT_ACCESS_DENIED',
|
|
65
76
|
'BACKBLAZE_IMPORT_NOT_FOUND',
|
|
66
77
|
'BACKBLAZE_STORE_ACCESS_DENIED',
|
|
@@ -88,6 +99,7 @@ export const assemblyStatusErrCodeSchema = z.enum([
|
|
|
88
99
|
'FILE_META_DATA_ERROR',
|
|
89
100
|
'FILE_PREVIEW_VALIDATION',
|
|
90
101
|
'FILE_READ_VALIDATION_ERROR',
|
|
102
|
+
'FILE_SERVE_NO_RESULT',
|
|
91
103
|
'FILE_VERIFY_INVALID_FILE',
|
|
92
104
|
'FILE_VIRUSSCAN_DECLINED_FILE',
|
|
93
105
|
'GET_ACCOUNT_DB_ERROR',
|
|
@@ -150,6 +150,7 @@ export const MODEL_CAPABILITIES: Record<string, { pdf: boolean; image: boolean }
|
|
|
150
150
|
'anthropic/claude-4-opus-20250514': { pdf: true, image: true },
|
|
151
151
|
'anthropic/claude-sonnet-4-5': { pdf: true, image: true },
|
|
152
152
|
'anthropic/claude-opus-4-5': { pdf: true, image: true },
|
|
153
|
+
'anthropic/claude-opus-4-6': { pdf: true, image: true },
|
|
153
154
|
'openai/gpt-4.1-2025-04-14': { pdf: false, image: true },
|
|
154
155
|
'openai/chatgpt-4o-latest': { pdf: false, image: true },
|
|
155
156
|
'openai/o3-2025-04-16': { pdf: false, image: true },
|
|
@@ -197,21 +198,28 @@ export const robotAiChatInstructionsSchema = robotBase
|
|
|
197
198
|
credentials: z
|
|
198
199
|
.union([z.string(), z.array(z.string())])
|
|
199
200
|
.optional()
|
|
200
|
-
.describe(
|
|
201
|
+
.describe(
|
|
202
|
+
'Names of template credentials to make available to the robot. When using your own AI provider keys, Transloadit charges a 10% markup (minimum $0.0005 per request).',
|
|
203
|
+
),
|
|
201
204
|
test_credentials: z
|
|
202
205
|
.boolean()
|
|
203
206
|
.optional()
|
|
204
|
-
.describe(
|
|
207
|
+
.describe(
|
|
208
|
+
'Use Transloadit-provided credentials for testing. Usage is billed at provider cost plus a 10% markup (minimum $0.0005 per request).',
|
|
209
|
+
),
|
|
205
210
|
mcp_servers: z
|
|
206
211
|
.array(
|
|
207
212
|
z.object({
|
|
208
213
|
type: z.enum(['sse', 'http']),
|
|
209
214
|
url: z.string(),
|
|
210
215
|
headers: z.record(z.string()).optional(),
|
|
216
|
+
auth: z.enum(['transloadit']).optional(),
|
|
211
217
|
}),
|
|
212
218
|
)
|
|
213
219
|
.optional()
|
|
214
|
-
.describe(
|
|
220
|
+
.describe(
|
|
221
|
+
'The MCP servers to use. This is used to call tools from the LLM. Use `headers` to pass `Authorization: Bearer <token>` when needed. You can use any MCP server reachable from your environment. For Transloadit\'s own MCP server, you can set `auth: "transloadit"` to let API2 auto-auth and inject an Authorization header for you (only for Transloadit-hosted MCP servers).',
|
|
222
|
+
),
|
|
215
223
|
})
|
|
216
224
|
.strict()
|
|
217
225
|
|
|
@@ -356,7 +356,12 @@ async function myStat(
|
|
|
356
356
|
|
|
357
357
|
function dirProvider(output: string): OutstreamProvider {
|
|
358
358
|
return async (inpath, indir = process.cwd()) => {
|
|
359
|
-
|
|
359
|
+
// Inputless assemblies can still write into a directory, but output paths are derived from
|
|
360
|
+
// assembly results rather than an input file path (handled later).
|
|
361
|
+
if (inpath == null) {
|
|
362
|
+
return null
|
|
363
|
+
}
|
|
364
|
+
if (inpath === '-') {
|
|
360
365
|
throw new Error('You must provide an input to output to a directory')
|
|
361
366
|
}
|
|
362
367
|
|
|
@@ -937,13 +942,12 @@ export async function create(
|
|
|
937
942
|
// Create fresh streams for this job
|
|
938
943
|
const inStream = inPath ? fs.createReadStream(inPath) : null
|
|
939
944
|
inStream?.on('error', () => {})
|
|
940
|
-
const outStream = outPath ? (fs.createWriteStream(outPath) as OutStream) : null
|
|
941
|
-
outStream?.on('error', () => {})
|
|
942
|
-
if (outStream) outStream.mtime = outMtime
|
|
943
945
|
|
|
944
946
|
let superceded = false
|
|
945
|
-
|
|
946
|
-
|
|
947
|
+
// When writing to a file path (non-directory output), we treat finish as a supersede signal.
|
|
948
|
+
// Directory-output multi-download mode does not use a single shared outstream.
|
|
949
|
+
const markSupersededOnFinish = (stream: OutStream) => {
|
|
950
|
+
stream.on('finish', () => {
|
|
947
951
|
superceded = true
|
|
948
952
|
})
|
|
949
953
|
}
|
|
@@ -982,23 +986,124 @@ export async function create(
|
|
|
982
986
|
}
|
|
983
987
|
|
|
984
988
|
if (!assembly.results) throw new Error('No results in assembly')
|
|
985
|
-
|
|
986
|
-
const
|
|
987
|
-
|
|
988
|
-
const
|
|
989
|
-
|
|
990
|
-
|
|
991
|
-
|
|
992
|
-
|
|
993
|
-
|
|
994
|
-
|
|
995
|
-
|
|
996
|
-
|
|
997
|
-
|
|
998
|
-
|
|
999
|
-
|
|
1000
|
-
|
|
1001
|
-
|
|
989
|
+
|
|
990
|
+
const outIsDirectory = Boolean(resolvedOutput != null && outstat?.isDirectory())
|
|
991
|
+
const entries = Object.entries(assembly.results)
|
|
992
|
+
const allFiles: Array<{
|
|
993
|
+
stepName: string
|
|
994
|
+
file: { name?: string; basename?: string; ext?: string; ssl_url?: string; url?: string }
|
|
995
|
+
}> = []
|
|
996
|
+
for (const [stepName, stepResults] of entries) {
|
|
997
|
+
for (const file of stepResults as Array<{
|
|
998
|
+
name?: string
|
|
999
|
+
basename?: string
|
|
1000
|
+
ext?: string
|
|
1001
|
+
ssl_url?: string
|
|
1002
|
+
url?: string
|
|
1003
|
+
}>) {
|
|
1004
|
+
allFiles.push({ stepName, file })
|
|
1005
|
+
}
|
|
1006
|
+
}
|
|
1007
|
+
|
|
1008
|
+
const getFileUrl = (file: { ssl_url?: string; url?: string }): string | null =>
|
|
1009
|
+
file.ssl_url ?? file.url ?? null
|
|
1010
|
+
|
|
1011
|
+
const sanitizeName = (value: string): string => {
|
|
1012
|
+
const base = path.basename(value)
|
|
1013
|
+
return base.replaceAll('\\', '_').replaceAll('/', '_').replaceAll('\u0000', '')
|
|
1014
|
+
}
|
|
1015
|
+
|
|
1016
|
+
const ensureUniquePath = async (targetPath: string): Promise<string> => {
|
|
1017
|
+
const parsed = path.parse(targetPath)
|
|
1018
|
+
let candidate = targetPath
|
|
1019
|
+
let counter = 1
|
|
1020
|
+
while (true) {
|
|
1021
|
+
const [statErr] = await tryCatch(fsp.stat(candidate))
|
|
1022
|
+
if (statErr) return candidate
|
|
1023
|
+
candidate = path.join(parsed.dir, `${parsed.name}__${counter}${parsed.ext}`)
|
|
1024
|
+
counter += 1
|
|
1025
|
+
}
|
|
1026
|
+
}
|
|
1027
|
+
|
|
1028
|
+
if (resolvedOutput != null && !superceded) {
|
|
1029
|
+
// Directory output:
|
|
1030
|
+
// - For single-result, input-backed jobs, preserve existing behavior (write to mapped file path).
|
|
1031
|
+
// - Otherwise (multi-result or inputless), download all results into a directory structure.
|
|
1032
|
+
if (outIsDirectory && (inPath == null || allFiles.length !== 1 || outPath == null)) {
|
|
1033
|
+
let baseDir = resolvedOutput
|
|
1034
|
+
if (inPath != null) {
|
|
1035
|
+
let relpath = path.relative(process.cwd(), inPath)
|
|
1036
|
+
relpath = relpath.replace(/^(\.\.\/)+/, '')
|
|
1037
|
+
baseDir = path.join(resolvedOutput, path.dirname(relpath), path.parse(relpath).name)
|
|
1038
|
+
}
|
|
1039
|
+
await fsp.mkdir(baseDir, { recursive: true })
|
|
1040
|
+
|
|
1041
|
+
for (const { stepName, file } of allFiles) {
|
|
1042
|
+
const resultUrl = getFileUrl(file)
|
|
1043
|
+
if (!resultUrl) continue
|
|
1044
|
+
|
|
1045
|
+
const stepDir = path.join(baseDir, stepName)
|
|
1046
|
+
await fsp.mkdir(stepDir, { recursive: true })
|
|
1047
|
+
|
|
1048
|
+
const rawName =
|
|
1049
|
+
file.name ??
|
|
1050
|
+
(file.basename && file.ext ? `${file.basename}.${file.ext}` : undefined) ??
|
|
1051
|
+
`${stepName}_result`
|
|
1052
|
+
const safeName = sanitizeName(rawName)
|
|
1053
|
+
const targetPath = await ensureUniquePath(path.join(stepDir, safeName))
|
|
1054
|
+
|
|
1055
|
+
outputctl.debug('DOWNLOADING')
|
|
1056
|
+
const outStream = fs.createWriteStream(targetPath) as OutStream
|
|
1057
|
+
outStream.on('error', () => {})
|
|
1058
|
+
const [dlErr] = await tryCatch(
|
|
1059
|
+
pipeline(got.stream(resultUrl, { signal: abortController.signal }), outStream),
|
|
1060
|
+
)
|
|
1061
|
+
if (dlErr) {
|
|
1062
|
+
if (dlErr.name === 'AbortError') continue
|
|
1063
|
+
outputctl.error(dlErr.message)
|
|
1064
|
+
throw dlErr
|
|
1065
|
+
}
|
|
1066
|
+
}
|
|
1067
|
+
} else if (!outIsDirectory && outPath != null) {
|
|
1068
|
+
const first = allFiles[0]
|
|
1069
|
+
const resultUrl = first ? getFileUrl(first.file) : null
|
|
1070
|
+
if (resultUrl) {
|
|
1071
|
+
outputctl.debug('DOWNLOADING')
|
|
1072
|
+
const outStream = fs.createWriteStream(outPath) as OutStream
|
|
1073
|
+
outStream.on('error', () => {})
|
|
1074
|
+
outStream.mtime = outMtime
|
|
1075
|
+
markSupersededOnFinish(outStream)
|
|
1076
|
+
|
|
1077
|
+
const [dlErr] = await tryCatch(
|
|
1078
|
+
pipeline(got.stream(resultUrl, { signal: abortController.signal }), outStream),
|
|
1079
|
+
)
|
|
1080
|
+
if (dlErr) {
|
|
1081
|
+
if (dlErr.name !== 'AbortError') {
|
|
1082
|
+
outputctl.error(dlErr.message)
|
|
1083
|
+
throw dlErr
|
|
1084
|
+
}
|
|
1085
|
+
}
|
|
1086
|
+
}
|
|
1087
|
+
} else if (outIsDirectory && outPath != null) {
|
|
1088
|
+
// Single-result, input-backed job: preserve existing file mapping in outdir.
|
|
1089
|
+
const first = allFiles[0]
|
|
1090
|
+
const resultUrl = first ? getFileUrl(first.file) : null
|
|
1091
|
+
if (resultUrl) {
|
|
1092
|
+
outputctl.debug('DOWNLOADING')
|
|
1093
|
+
const outStream = fs.createWriteStream(outPath) as OutStream
|
|
1094
|
+
outStream.on('error', () => {})
|
|
1095
|
+
outStream.mtime = outMtime
|
|
1096
|
+
markSupersededOnFinish(outStream)
|
|
1097
|
+
|
|
1098
|
+
const [dlErr] = await tryCatch(
|
|
1099
|
+
pipeline(got.stream(resultUrl, { signal: abortController.signal }), outStream),
|
|
1100
|
+
)
|
|
1101
|
+
if (dlErr) {
|
|
1102
|
+
if (dlErr.name !== 'AbortError') {
|
|
1103
|
+
outputctl.error(dlErr.message)
|
|
1104
|
+
throw dlErr
|
|
1105
|
+
}
|
|
1106
|
+
}
|
|
1002
1107
|
}
|
|
1003
1108
|
}
|
|
1004
1109
|
}
|
|
@@ -1272,8 +1377,9 @@ export class AssembliesCreateCommand extends AuthenticatedCommand {
|
|
|
1272
1377
|
return 1
|
|
1273
1378
|
}
|
|
1274
1379
|
|
|
1275
|
-
// Default to stdin
|
|
1276
|
-
|
|
1380
|
+
// Default to stdin only for `--steps` mode (common "pipe a file into a one-off assembly" use case).
|
|
1381
|
+
// For `--template` mode, templates may be inputless or use /http/import, so stdin should be explicit (`--input -`).
|
|
1382
|
+
if (this.steps && inputList.length === 0 && !process.stdin.isTTY) {
|
|
1277
1383
|
inputList.push('-')
|
|
1278
1384
|
}
|
|
1279
1385
|
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import { Option } from 'clipanion'
|
|
2
|
+
import { getRobotHelp, isKnownRobot, listRobots } from '../../robots.ts'
|
|
3
|
+
import { UnauthenticatedCommand } from './BaseCommand.ts'
|
|
4
|
+
|
|
5
|
+
const splitRobotArgs = (values: string[]): string[] => {
|
|
6
|
+
const out: string[] = []
|
|
7
|
+
for (const value of values) {
|
|
8
|
+
for (const part of value.split(',')) {
|
|
9
|
+
const trimmed = part.trim()
|
|
10
|
+
if (trimmed) out.push(trimmed)
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
return out
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export class DocsRobotsListCommand extends UnauthenticatedCommand {
|
|
17
|
+
static override paths = [['docs', 'robots', 'list']]
|
|
18
|
+
|
|
19
|
+
search = Option.String('--search', { description: 'Filter by substring match.' })
|
|
20
|
+
category = Option.String('--category', { description: 'Filter by category (service slug).' })
|
|
21
|
+
limit = Option.String('--limit', { description: 'Max results (default: 20).' })
|
|
22
|
+
cursor = Option.String('--cursor', { description: 'Pagination cursor.' })
|
|
23
|
+
|
|
24
|
+
protected override run(): Promise<number | undefined> {
|
|
25
|
+
const limitNum = this.limit ? Number.parseInt(this.limit, 10) : undefined
|
|
26
|
+
const result = listRobots({
|
|
27
|
+
search: this.search,
|
|
28
|
+
category: this.category,
|
|
29
|
+
limit: Number.isFinite(limitNum as number) ? (limitNum as number) : undefined,
|
|
30
|
+
cursor: this.cursor,
|
|
31
|
+
})
|
|
32
|
+
|
|
33
|
+
this.output.print(result.robots.map((r) => `${r.name} ${r.summary}`).join('\n'), {
|
|
34
|
+
robots: result.robots,
|
|
35
|
+
nextCursor: result.nextCursor,
|
|
36
|
+
})
|
|
37
|
+
|
|
38
|
+
return Promise.resolve(0)
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
export class DocsRobotsGetCommand extends UnauthenticatedCommand {
|
|
43
|
+
static override paths = [['docs', 'robots', 'get']]
|
|
44
|
+
|
|
45
|
+
robots = Option.Rest({ required: 1 })
|
|
46
|
+
|
|
47
|
+
protected override run(): Promise<number | undefined> {
|
|
48
|
+
const requested = splitRobotArgs(this.robots)
|
|
49
|
+
const robots = []
|
|
50
|
+
const notFound: string[] = []
|
|
51
|
+
|
|
52
|
+
for (const robotName of requested) {
|
|
53
|
+
if (!isKnownRobot(robotName)) {
|
|
54
|
+
notFound.push(robotName)
|
|
55
|
+
continue
|
|
56
|
+
}
|
|
57
|
+
const help = getRobotHelp({ robotName, detailLevel: 'full' })
|
|
58
|
+
robots.push(help)
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
this.output.print(robots.length === 1 ? robots[0] : robots, {
|
|
62
|
+
robots,
|
|
63
|
+
notFound,
|
|
64
|
+
})
|
|
65
|
+
|
|
66
|
+
return Promise.resolve(notFound.length > 0 ? 1 : 0)
|
|
67
|
+
}
|
|
68
|
+
}
|
|
@@ -14,9 +14,8 @@ import {
|
|
|
14
14
|
import { SignatureCommand, SmartCdnSignatureCommand } from './auth.ts'
|
|
15
15
|
|
|
16
16
|
import { BillsGetCommand } from './bills.ts'
|
|
17
|
-
|
|
17
|
+
import { DocsRobotsGetCommand, DocsRobotsListCommand } from './docs.ts'
|
|
18
18
|
import { NotificationsReplayCommand } from './notifications.ts'
|
|
19
|
-
|
|
20
19
|
import {
|
|
21
20
|
TemplatesCreateCommand,
|
|
22
21
|
TemplatesDeleteCommand,
|
|
@@ -67,5 +66,9 @@ export function createCli(): Cli {
|
|
|
67
66
|
// Uploads commands
|
|
68
67
|
cli.register(UploadCommand)
|
|
69
68
|
|
|
69
|
+
// Documentation commands (offline metadata)
|
|
70
|
+
cli.register(DocsRobotsListCommand)
|
|
71
|
+
cli.register(DocsRobotsGetCommand)
|
|
72
|
+
|
|
70
73
|
return cli
|
|
71
74
|
}
|
package/src/robots.ts
CHANGED
|
@@ -36,7 +36,7 @@ export type RobotHelp = {
|
|
|
36
36
|
|
|
37
37
|
export type RobotHelpOptions = {
|
|
38
38
|
robotName: string
|
|
39
|
-
detailLevel?: 'summary' | 'params' | 'examples'
|
|
39
|
+
detailLevel?: 'summary' | 'params' | 'examples' | 'full'
|
|
40
40
|
}
|
|
41
41
|
|
|
42
42
|
type RobotsMetaMap = typeof robotsMeta
|
|
@@ -299,11 +299,11 @@ export const getRobotHelp = (options: RobotHelpOptions): RobotHelp => {
|
|
|
299
299
|
const help: RobotHelp = {
|
|
300
300
|
name: path,
|
|
301
301
|
summary,
|
|
302
|
-
requiredParams: detailLevel === 'params' ? params.required : [],
|
|
303
|
-
optionalParams: detailLevel === 'params' ? params.optional : [],
|
|
302
|
+
requiredParams: detailLevel === 'params' || detailLevel === 'full' ? params.required : [],
|
|
303
|
+
optionalParams: detailLevel === 'params' || detailLevel === 'full' ? params.optional : [],
|
|
304
304
|
}
|
|
305
305
|
|
|
306
|
-
if (detailLevel === 'examples' && meta?.example_code) {
|
|
306
|
+
if ((detailLevel === 'examples' || detailLevel === 'full') && meta?.example_code) {
|
|
307
307
|
const snippet = isRecord(meta.example_code) ? meta.example_code : {}
|
|
308
308
|
help.examples = [
|
|
309
309
|
{
|
|
@@ -315,3 +315,11 @@ export const getRobotHelp = (options: RobotHelpOptions): RobotHelp => {
|
|
|
315
315
|
|
|
316
316
|
return help
|
|
317
317
|
}
|
|
318
|
+
|
|
319
|
+
export const isKnownRobot = (robotName: string): boolean => {
|
|
320
|
+
const { byPath, byName } = getMetaIndex()
|
|
321
|
+
const schemaIndex = getSchemaIndex()
|
|
322
|
+
|
|
323
|
+
const path = resolveRobotPath(robotName)
|
|
324
|
+
return byPath.has(path) || byName.has(robotName) || schemaIndex.has(path)
|
|
325
|
+
}
|