@pipelex/sdk 0.33.0 → 0.34.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/README.md +20 -1
- package/dist/cli/args.d.ts +41 -0
- package/dist/cli/args.js +96 -0
- package/dist/cli/args.js.map +1 -0
- package/dist/cli/bundle.d.ts +45 -0
- package/dist/cli/bundle.js +148 -0
- package/dist/cli/bundle.js.map +1 -0
- package/dist/cli/environment.d.ts +36 -0
- package/dist/cli/environment.js +72 -0
- package/dist/cli/environment.js.map +1 -0
- package/dist/cli/help.d.ts +4 -0
- package/dist/cli/help.js +56 -0
- package/dist/cli/help.js.map +1 -0
- package/dist/cli/inputs.d.ts +13 -0
- package/dist/cli/inputs.js +70 -0
- package/dist/cli/inputs.js.map +1 -0
- package/dist/cli/io.d.ts +79 -0
- package/dist/cli/io.js +96 -0
- package/dist/cli/io.js.map +1 -0
- package/dist/cli/main.d.ts +18 -0
- package/dist/cli/main.js +58 -0
- package/dist/cli/main.js.map +1 -0
- package/dist/cli/method.d.ts +56 -0
- package/dist/cli/method.js +108 -0
- package/dist/cli/method.js.map +1 -0
- package/dist/cli/present.d.ts +43 -0
- package/dist/cli/present.js +191 -0
- package/dist/cli/present.js.map +1 -0
- package/dist/cli/run.d.ts +17 -0
- package/dist/cli/run.js +290 -0
- package/dist/cli/run.js.map +1 -0
- package/dist/cli/script.d.ts +23 -0
- package/dist/cli/script.js +212 -0
- package/dist/cli/script.js.map +1 -0
- package/dist/cli/source.d.ts +49 -0
- package/dist/cli/source.js +71 -0
- package/dist/cli/source.js.map +1 -0
- package/dist/cli.d.ts +10 -0
- package/dist/cli.js +65 -0
- package/dist/cli.js.map +1 -0
- package/dist/client.d.ts +49 -7
- package/dist/client.js +152 -26
- package/dist/client.js.map +1 -1
- package/dist/errors.d.ts +42 -4
- package/dist/errors.js +43 -4
- package/dist/errors.js.map +1 -1
- package/dist/hooks/validate-client.d.ts +3 -3
- package/dist/hooks/validate-client.js +3 -3
- package/dist/index.d.ts +3 -3
- package/dist/index.js +2 -2
- package/dist/index.js.map +1 -1
- package/dist/prepare-inputs.d.ts +6 -0
- package/dist/prepare-inputs.js +21 -10
- package/dist/prepare-inputs.js.map +1 -1
- package/dist/runs.d.ts +28 -1
- package/dist/runs.js +2 -1
- package/dist/runs.js.map +1 -1
- package/dist/upload.d.ts +6 -0
- package/dist/upload.js +9 -4
- package/dist/upload.js.map +1 -1
- package/dist/user-agent.d.ts +3 -2
- package/dist/user-agent.js +3 -2
- package/dist/user-agent.js.map +1 -1
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/docs/architecture.md +8 -3
- package/docs/cli.md +123 -0
- package/docs/client-identification.md +1 -1
- package/docs/errors.md +5 -3
- package/docs/input-preparation.md +6 -2
- package/docs/run-results.md +20 -0
- package/package.json +5 -1
|
@@ -0,0 +1,191 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* What the command prints: a finished run's result on stdout, and every failure on stderr.
|
|
3
|
+
*
|
|
4
|
+
* A failure is worded by the command from the error's typed fields, never from an SDK message
|
|
5
|
+
* whose wording is the SDK's own, wherever the fields carry what a person needs: the API's reason,
|
|
6
|
+
* the next step it advises and the request id support asks for. That keeps the two commands, this
|
|
7
|
+
* one and its Python twin, saying the same thing for the same answer, which is what the recorded
|
|
8
|
+
* case table holds them to.
|
|
9
|
+
*/
|
|
10
|
+
import { ApiResponseError, ApiUnreachableError, InputPreparationError, InvalidInputValueError, InvalidLocalSourceError, MethodLoadError, PipelineRequestError, RejectedAssetError, RunFailedError, UnsupportedUploadCapabilityError, UploadAuthenticationError, UploadTransportError, } from "../index.js";
|
|
11
|
+
import { CommandError, EXIT_FAILED, EXIT_USAGE } from "./io.js";
|
|
12
|
+
/** Word any failure for stderr and choose its exit code. */
|
|
13
|
+
export function presentError(error) {
|
|
14
|
+
if (error instanceof CommandError) {
|
|
15
|
+
const first = error.bare ? error.message : `Error: ${error.message}`;
|
|
16
|
+
return { lines: [first, ...error.details], exitCode: error.exitCode };
|
|
17
|
+
}
|
|
18
|
+
if (error instanceof RunFailedError) {
|
|
19
|
+
const report = error.error;
|
|
20
|
+
const lines = [`Error: run ${error.runId} ended with status ${error.status}.`];
|
|
21
|
+
lines.push(`Reason: ${report?.message ?? error.message}`);
|
|
22
|
+
if (report?.user_action?.detail)
|
|
23
|
+
lines.push(`Next step: ${report.user_action.detail}`);
|
|
24
|
+
return { lines, exitCode: EXIT_FAILED };
|
|
25
|
+
}
|
|
26
|
+
if (error instanceof InvalidLocalSourceError) {
|
|
27
|
+
// A file the inputs name that cannot be read is the inputs' fault, as an unreadable inputs
|
|
28
|
+
// file is, so it is a usage error.
|
|
29
|
+
return {
|
|
30
|
+
lines: [
|
|
31
|
+
`Error: ${error.message}`,
|
|
32
|
+
"A relative path in the inputs resolves against the current directory.",
|
|
33
|
+
],
|
|
34
|
+
exitCode: EXIT_USAGE,
|
|
35
|
+
};
|
|
36
|
+
}
|
|
37
|
+
if (error instanceof InvalidInputValueError) {
|
|
38
|
+
// A value at a file input that is no file, such as a data: URL that does not decode or a
|
|
39
|
+
// number, is the inputs' fault too.
|
|
40
|
+
return {
|
|
41
|
+
lines: [
|
|
42
|
+
"Error: the inputs hold a value at a file input that cannot be read as a file.",
|
|
43
|
+
`Reason: ${error.message}`,
|
|
44
|
+
FILE_INPUT_FORMS,
|
|
45
|
+
],
|
|
46
|
+
exitCode: EXIT_USAGE,
|
|
47
|
+
};
|
|
48
|
+
}
|
|
49
|
+
if (error instanceof MethodLoadError) {
|
|
50
|
+
// The same lines whichever route said so: the input preparation here, the pipe I/O call of
|
|
51
|
+
// `--inputs-template` and `script` in `source.ts`.
|
|
52
|
+
return {
|
|
53
|
+
lines: [
|
|
54
|
+
METHOD_DOES_NOT_LOAD,
|
|
55
|
+
...loadFailureLines(error.validationErrors, error.serverMessage),
|
|
56
|
+
],
|
|
57
|
+
exitCode: EXIT_FAILED,
|
|
58
|
+
};
|
|
59
|
+
}
|
|
60
|
+
if (error instanceof RejectedAssetError ||
|
|
61
|
+
error instanceof UploadAuthenticationError ||
|
|
62
|
+
error instanceof UnsupportedUploadCapabilityError ||
|
|
63
|
+
error instanceof UploadTransportError) {
|
|
64
|
+
return presentUploadFailure(error);
|
|
65
|
+
}
|
|
66
|
+
if (error instanceof InputPreparationError && error.cause instanceof ApiResponseError) {
|
|
67
|
+
return presentRefusal(error.cause);
|
|
68
|
+
}
|
|
69
|
+
if (error instanceof ApiResponseError) {
|
|
70
|
+
return presentRefusal(error);
|
|
71
|
+
}
|
|
72
|
+
if (error instanceof ApiUnreachableError) {
|
|
73
|
+
return {
|
|
74
|
+
lines: [
|
|
75
|
+
`Error: could not reach the Pipelex API at ${error.apiUrl} (${error.code ?? "network error"}).`,
|
|
76
|
+
],
|
|
77
|
+
exitCode: EXIT_FAILED,
|
|
78
|
+
};
|
|
79
|
+
}
|
|
80
|
+
if (error instanceof PipelineRequestError) {
|
|
81
|
+
return { lines: [`Error: ${error.message}`], exitCode: EXIT_FAILED };
|
|
82
|
+
}
|
|
83
|
+
const detail = error instanceof Error ? `${error.name}: ${error.message}` : String(error);
|
|
84
|
+
return { lines: [`Error: unexpected failure, ${detail}`], exitCode: EXIT_FAILED };
|
|
85
|
+
}
|
|
86
|
+
/**
|
|
87
|
+
* A file the inputs name that could not be uploaded: which file, the status the upload route
|
|
88
|
+
* answered when it answered, and the SDK's own sentence, which says what the status means for an
|
|
89
|
+
* upload (a file past the size limit, a deployment without upload), followed by the advice and the
|
|
90
|
+
* request id of the API's answer when it carries them.
|
|
91
|
+
*/
|
|
92
|
+
function presentUploadFailure(error) {
|
|
93
|
+
const cause = error.cause instanceof ApiResponseError ? error.cause : undefined;
|
|
94
|
+
const status = "status" in error && typeof error.status === "number" ? error.status : cause?.status;
|
|
95
|
+
const what = error.filename === undefined ? "an upload failed" : `the upload of "${error.filename}" failed`;
|
|
96
|
+
const lines = [`Error: ${what}${status === undefined ? "" : ` (status ${status})`}.`];
|
|
97
|
+
lines.push(`Reason: ${error.message}`);
|
|
98
|
+
if (cause?.userAction?.detail)
|
|
99
|
+
lines.push(`Next step: ${cause.userAction.detail}`);
|
|
100
|
+
if (cause?.requestId)
|
|
101
|
+
lines.push(`Request id: ${cause.requestId}`);
|
|
102
|
+
return { lines, exitCode: EXIT_FAILED };
|
|
103
|
+
}
|
|
104
|
+
/** An answer the API gave instead of a result: its status, its reason and its advice. */
|
|
105
|
+
function presentRefusal(error) {
|
|
106
|
+
const lines = error.status >= 200 && error.status < 300
|
|
107
|
+
? [`Error: the API's answer could not be read (status ${error.status}).`]
|
|
108
|
+
: [`Error: the API refused the request (status ${error.status}).`];
|
|
109
|
+
lines.push(`Reason: ${error.serverMessage ?? error.title ?? "the answer gives no reason"}`);
|
|
110
|
+
for (const item of error.validationErrors ?? [])
|
|
111
|
+
lines.push(validationLine(item));
|
|
112
|
+
if (error.userAction?.detail)
|
|
113
|
+
lines.push(`Next step: ${error.userAction.detail}`);
|
|
114
|
+
if (error.requestId)
|
|
115
|
+
lines.push(`Request id: ${error.requestId}`);
|
|
116
|
+
return { lines, exitCode: EXIT_FAILED };
|
|
117
|
+
}
|
|
118
|
+
/** What a file input takes, the hint under a value it cannot take. */
|
|
119
|
+
const FILE_INPUT_FORMS = 'A file input takes a local path, an http(s) or pipelex-storage:// URL, a data: URL, or an object whose "url" is one of these.';
|
|
120
|
+
/** The first line of a method that does not load, after `Error: `. */
|
|
121
|
+
export const METHOD_DOES_NOT_LOAD_SENTENCE = "the method does not load.";
|
|
122
|
+
const METHOD_DOES_NOT_LOAD = `Error: ${METHOD_DOES_NOT_LOAD_SENTENCE}`;
|
|
123
|
+
/**
|
|
124
|
+
* The lines under "the method does not load": one per validation item that carries a string
|
|
125
|
+
* `category` and `message`, the rule the SDK reads a failed load's items by, and when there is
|
|
126
|
+
* none, the answer's own message.
|
|
127
|
+
*/
|
|
128
|
+
export function loadFailureLines(items, message) {
|
|
129
|
+
const lines = (Array.isArray(items) ? items : []).filter(isLoadItem).map(validationLine);
|
|
130
|
+
if (lines.length === 0) {
|
|
131
|
+
lines.push(` - ${typeof message === "string" ? message : "the answer gives no reason"}`);
|
|
132
|
+
}
|
|
133
|
+
return lines;
|
|
134
|
+
}
|
|
135
|
+
function isLoadItem(value) {
|
|
136
|
+
return (isPlainObject(value) && typeof value.category === "string" && typeof value.message === "string");
|
|
137
|
+
}
|
|
138
|
+
/** One validation error, as an indented line naming its file when it has one. */
|
|
139
|
+
export function validationLine(item) {
|
|
140
|
+
const source = typeof item.source === "string" && item.source !== "" ? `${item.source}: ` : "";
|
|
141
|
+
return ` - ${source}${item.message}`;
|
|
142
|
+
}
|
|
143
|
+
/** The keys of the runtime's absence document, and no other. */
|
|
144
|
+
const ABSENCE_KEYS = ["absent", "variable_name", "kind", "reason", "producing_pipe", "upstream"];
|
|
145
|
+
const ABSENCE_KINDS = ["declared_absent", "skipped", "not_provided"];
|
|
146
|
+
function isPlainObject(value) {
|
|
147
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
148
|
+
}
|
|
149
|
+
/**
|
|
150
|
+
* Whether a run's main output is the absence of one: nothing at all, or the runtime's absence
|
|
151
|
+
* document, matched by its whole shape (exactly its keys, each of the type the runtime writes) and
|
|
152
|
+
* never by its `absent` key alone, since a method's own output may carry such a field. The same
|
|
153
|
+
* rule `cli-python` applies.
|
|
154
|
+
*/
|
|
155
|
+
export function isAbsence(mainStuff) {
|
|
156
|
+
if (mainStuff === null || mainStuff === undefined)
|
|
157
|
+
return true;
|
|
158
|
+
if (!isPlainObject(mainStuff))
|
|
159
|
+
return false;
|
|
160
|
+
const keys = Object.keys(mainStuff);
|
|
161
|
+
if (keys.length !== ABSENCE_KEYS.length || !ABSENCE_KEYS.every((key) => keys.includes(key))) {
|
|
162
|
+
return false;
|
|
163
|
+
}
|
|
164
|
+
const { absent, variable_name, kind, reason, producing_pipe, upstream } = mainStuff;
|
|
165
|
+
return (absent === true &&
|
|
166
|
+
typeof variable_name === "string" &&
|
|
167
|
+
typeof kind === "string" &&
|
|
168
|
+
ABSENCE_KINDS.includes(kind) &&
|
|
169
|
+
typeof reason === "string" &&
|
|
170
|
+
(producing_pipe === null || typeof producing_pipe === "string") &&
|
|
171
|
+
(upstream === null || isPlainObject(upstream)));
|
|
172
|
+
}
|
|
173
|
+
/**
|
|
174
|
+
* Print a finished run's main output on stdout, as the method produced it: indented JSON,
|
|
175
|
+
* unvalidated and unrepaired, a list included whichever shape it arrived in. An output the method
|
|
176
|
+
* left absent prints `null`, and a line on stderr says so.
|
|
177
|
+
*/
|
|
178
|
+
export function printResult(mainStuff, io) {
|
|
179
|
+
if (isAbsence(mainStuff)) {
|
|
180
|
+
io.writeStdout("null\n");
|
|
181
|
+
if (isPlainObject(mainStuff)) {
|
|
182
|
+
io.writeStderr(`The method left its output "${String(mainStuff.variable_name)}" absent (${String(mainStuff.reason)}), so the result is null.\n`);
|
|
183
|
+
}
|
|
184
|
+
else {
|
|
185
|
+
io.writeStderr("The method produced no output, so the result is null.\n");
|
|
186
|
+
}
|
|
187
|
+
return;
|
|
188
|
+
}
|
|
189
|
+
io.writeStdout(`${JSON.stringify(mainStuff, null, 2)}\n`);
|
|
190
|
+
}
|
|
191
|
+
//# sourceMappingURL=present.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"present.js","sourceRoot":"","sources":["../../src/cli/present.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `pipelex-sdk run`: run one method and print its main output.
|
|
3
|
+
*
|
|
4
|
+
* Every check that needs no request comes first, in this order, so that a mistake costs nothing:
|
|
5
|
+
* the flags, `--pipe`'s form, the method (a path is read from disk), the inputs, the poll interval
|
|
6
|
+
* and the key. Then the inputs are prepared, which uploads each local file at a file input; the
|
|
7
|
+
* run is started through the SDK's start-and-wait, whose `onStarted` gives the run id to stderr
|
|
8
|
+
* as soon as the run exists; and the main output reaches stdout once the run completes, however
|
|
9
|
+
* long that takes.
|
|
10
|
+
*/
|
|
11
|
+
import type { CommandIO } from "./io.js";
|
|
12
|
+
/** Run `pipelex-sdk run` and return its exit code. */
|
|
13
|
+
export declare function runCommandRun(args: readonly string[], io: CommandIO): Promise<number>;
|
|
14
|
+
/** The line under a failure that leaves it unknown whether a run was created. */
|
|
15
|
+
export declare const RUN_MAY_HAVE_STARTED_LINE = "A run may have started on the server without the command learning of it, so check before starting it again.";
|
|
16
|
+
/** The line under a failure met while waiting on a run that exists. */
|
|
17
|
+
export declare function runStillGoingLine(runId: string): string;
|
package/dist/cli/run.js
ADDED
|
@@ -0,0 +1,290 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `pipelex-sdk run`: run one method and print its main output.
|
|
3
|
+
*
|
|
4
|
+
* Every check that needs no request comes first, in this order, so that a mistake costs nothing:
|
|
5
|
+
* the flags, `--pipe`'s form, the method (a path is read from disk), the inputs, the poll interval
|
|
6
|
+
* and the key. Then the inputs are prepared, which uploads each local file at a file input; the
|
|
7
|
+
* run is started through the SDK's start-and-wait, whose `onStarted` gives the run id to stderr
|
|
8
|
+
* as soon as the run exists; and the main output reaches stdout once the run completes, however
|
|
9
|
+
* long that takes.
|
|
10
|
+
*/
|
|
11
|
+
import { ApiResponseError, ApiUnreachableError, MissingMainStuffError, PipelineExecuteTimeoutError, RunFailedError, isGatewayCutOff, renderInputsTemplate, } from "../index.js";
|
|
12
|
+
import { parseFlags } from "./args.js";
|
|
13
|
+
import { readBundle } from "./bundle.js";
|
|
14
|
+
import { makeClient, readPollInterval } from "./environment.js";
|
|
15
|
+
import { RUN_HELP } from "./help.js";
|
|
16
|
+
import { readInputs } from "./inputs.js";
|
|
17
|
+
import { CommandError, EXIT_INTERRUPTED, EXIT_OK, Interrupted, untilInterrupted, usageError, writeLines, } from "./io.js";
|
|
18
|
+
import { checkPipeRef, classifyMethod } from "./method.js";
|
|
19
|
+
import { presentError, printResult } from "./present.js";
|
|
20
|
+
import { crateSelector, describePipe, runSelector } from "./source.js";
|
|
21
|
+
const RUN_FLAGS = {
|
|
22
|
+
method: { type: "string" },
|
|
23
|
+
pipe: { type: "string" },
|
|
24
|
+
inputs: { type: "string" },
|
|
25
|
+
"inputs-template": { type: "boolean" },
|
|
26
|
+
};
|
|
27
|
+
/** Run `pipelex-sdk run` and return its exit code. */
|
|
28
|
+
export async function runCommandRun(args, io) {
|
|
29
|
+
const flags = parseFlags("run", args, RUN_FLAGS);
|
|
30
|
+
if (flags.help) {
|
|
31
|
+
io.writeStdout(RUN_HELP);
|
|
32
|
+
return EXIT_OK;
|
|
33
|
+
}
|
|
34
|
+
const method = flags.strings.get("method");
|
|
35
|
+
if (method === undefined) {
|
|
36
|
+
throw usageError("--method is required.", ["Run 'pipelex-sdk run --help' to see its flags."]);
|
|
37
|
+
}
|
|
38
|
+
const inputsSource = flags.strings.get("inputs");
|
|
39
|
+
const templateOnly = flags.booleans.has("inputs-template");
|
|
40
|
+
if (templateOnly && inputsSource !== undefined) {
|
|
41
|
+
throw usageError("--inputs-template prints the inputs a run takes, so it takes no --inputs.");
|
|
42
|
+
}
|
|
43
|
+
const pipe = flags.strings.get("pipe");
|
|
44
|
+
if (pipe !== undefined)
|
|
45
|
+
checkPipeRef(pipe);
|
|
46
|
+
let stage = { kind: "local" };
|
|
47
|
+
try {
|
|
48
|
+
// Raced like the inputs read: a bundle on a stalled mount must not hold the command.
|
|
49
|
+
const source = await untilInterrupted(() => methodSource(method), io.interrupt);
|
|
50
|
+
const inputs = inputsSource === undefined ? undefined : await readInputs(inputsSource, io);
|
|
51
|
+
const intervalMs = readPollInterval(io);
|
|
52
|
+
const client = makeClient(io);
|
|
53
|
+
if (templateOnly) {
|
|
54
|
+
const described = await describePipe(client, source, pipe, io.interrupt);
|
|
55
|
+
const template = renderInputsTemplate(described.descriptor, {
|
|
56
|
+
explicit: false,
|
|
57
|
+
format: "json",
|
|
58
|
+
});
|
|
59
|
+
io.writeStdout(`${template}\n`);
|
|
60
|
+
return EXIT_OK;
|
|
61
|
+
}
|
|
62
|
+
const prepared = await prepare(client, source, pipe, inputs, io);
|
|
63
|
+
const results = await untilInterrupted(() => {
|
|
64
|
+
return client.startAndWaitForResult({
|
|
65
|
+
...runSelector(source),
|
|
66
|
+
...(pipe === undefined ? {} : { pipe_code: pipe }),
|
|
67
|
+
...(prepared === undefined ? {} : { inputs: prepared }),
|
|
68
|
+
}, {
|
|
69
|
+
// A run takes as long as it takes: the command waits for it until it ends or the
|
|
70
|
+
// person interrupts.
|
|
71
|
+
timeoutMs: Number.POSITIVE_INFINITY,
|
|
72
|
+
...(intervalMs === undefined ? {} : { intervalMs }),
|
|
73
|
+
signal: io.interrupt,
|
|
74
|
+
artifacts: ["main_stuff"],
|
|
75
|
+
onStarting: () => {
|
|
76
|
+
// Only once a request that may create a run is about to leave: an interrupt before
|
|
77
|
+
// it, during the version handshake included, starts nothing, and the command says so.
|
|
78
|
+
stage = { kind: "starting", since: performance.now() };
|
|
79
|
+
},
|
|
80
|
+
onStarted: (ack) => {
|
|
81
|
+
if (io.interrupt.aborted)
|
|
82
|
+
return;
|
|
83
|
+
stage = { kind: "started", runId: ack.pipeline_run_id };
|
|
84
|
+
io.writeStderr(`Run started: ${ack.pipeline_run_id}\n`);
|
|
85
|
+
},
|
|
86
|
+
});
|
|
87
|
+
}, io.interrupt);
|
|
88
|
+
printResult(results.main_stuff, io);
|
|
89
|
+
return EXIT_OK;
|
|
90
|
+
}
|
|
91
|
+
catch (error) {
|
|
92
|
+
if (!(error instanceof Interrupted)) {
|
|
93
|
+
const runId = waitedOnRun(stage, error);
|
|
94
|
+
const warning = runId !== undefined
|
|
95
|
+
? runStillGoingLine(runId)
|
|
96
|
+
: mayHaveStarted(stage, error)
|
|
97
|
+
? RUN_MAY_HAVE_STARTED_LINE
|
|
98
|
+
: undefined;
|
|
99
|
+
if (warning === undefined)
|
|
100
|
+
throw error;
|
|
101
|
+
// A failure that says nothing of a run that exists, or may: a wrapper must not read it as a
|
|
102
|
+
// failed run and pay for another.
|
|
103
|
+
const presented = presentError(error);
|
|
104
|
+
writeLines(io, [...presented.lines, warning]);
|
|
105
|
+
return presented.exitCode;
|
|
106
|
+
}
|
|
107
|
+
throw new CommandError(interruptMessage(stage, templateOnly), {
|
|
108
|
+
exitCode: EXIT_INTERRUPTED,
|
|
109
|
+
bare: true,
|
|
110
|
+
});
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
/**
|
|
114
|
+
* The run a failure leaves going on the server: the run that exists when the failure is not its
|
|
115
|
+
* own outcome, a failed run or a completed one without its output, but an unreachable API or a
|
|
116
|
+
* refused poll.
|
|
117
|
+
*/
|
|
118
|
+
function waitedOnRun(stage, error) {
|
|
119
|
+
if (stage.kind !== "started")
|
|
120
|
+
return undefined;
|
|
121
|
+
if (error instanceof RunFailedError || error instanceof MissingMainStuffError)
|
|
122
|
+
return undefined;
|
|
123
|
+
return stage.runId;
|
|
124
|
+
}
|
|
125
|
+
/**
|
|
126
|
+
* Whether a transport failure proves the request never left, so that no run was created by it: it
|
|
127
|
+
* names a step of the connection's set-up, which no request follows. That is the name lookup or
|
|
128
|
+
* the connection itself failing (the system calls `getaddrinfo` and `connect`: an unknown host, a
|
|
129
|
+
* refused connection, no route to the host or the network, the system's connect time limit),
|
|
130
|
+
* undici's own connect time limit, which covers the TLS handshake, and a server certificate the
|
|
131
|
+
* TLS handshake refused. Several addresses each failing so, which Node reports together as an
|
|
132
|
+
* `AggregateError`, prove it too. A host that drops packets is a connection that never comes:
|
|
133
|
+
* undici gives it up after ten seconds (`UND_ERR_CONNECT_TIMEOUT`), before the time limit of any
|
|
134
|
+
* request the command sends, as the Python command meets httpx's connect timeout first and reads
|
|
135
|
+
* it the same way.
|
|
136
|
+
*
|
|
137
|
+
* Anything else may have come once the request had left, the Python command reading the same
|
|
138
|
+
* failures the same way. A reset (`ECONNRESET`) comes from a `read` whether the connection dropped
|
|
139
|
+
* during the TLS handshake or after the request, and so does an `EHOSTUNREACH` met once connected,
|
|
140
|
+
* so neither proves anything; nor does another TLS failure, which may come after the handshake;
|
|
141
|
+
* nor does the SDK's own time limit (`ABORT_TIMEOUT`), which fetch cannot place before or after
|
|
142
|
+
* the connection was made. Every request the command sends has a limit longer than undici's
|
|
143
|
+
* connect limit, so with Node's own dispatcher it runs out only once the request has left.
|
|
144
|
+
*/
|
|
145
|
+
function provesNothingSent(error) {
|
|
146
|
+
// The SDK keeps fetch's own failure as `cause`, and undici gives the transport's under it.
|
|
147
|
+
const fetchFailure = error.cause;
|
|
148
|
+
return fetchFailure instanceof Error && isConnectionSetupFailure(fetchFailure.cause);
|
|
149
|
+
}
|
|
150
|
+
function isConnectionSetupFailure(failure) {
|
|
151
|
+
if (failure instanceof AggregateError) {
|
|
152
|
+
return failure.errors.length > 0 && failure.errors.every(isConnectionSetupFailure);
|
|
153
|
+
}
|
|
154
|
+
if (typeof failure !== "object" || failure === null)
|
|
155
|
+
return false;
|
|
156
|
+
const { code, syscall } = failure;
|
|
157
|
+
if (typeof code === "string" && CONNECTION_SETUP_CODES.has(code))
|
|
158
|
+
return true;
|
|
159
|
+
return typeof syscall === "string" && CONNECTION_SETUP_CALLS.has(syscall);
|
|
160
|
+
}
|
|
161
|
+
/** The system calls that resolve the host's name and open the connection. */
|
|
162
|
+
const CONNECTION_SETUP_CALLS = new Set(["getaddrinfo", "connect"]);
|
|
163
|
+
/**
|
|
164
|
+
* The codes only a step of the connection's set-up gives: the name lookup's and the connect call's
|
|
165
|
+
* own (set even where Node reports several addresses together, without a system call), undici's
|
|
166
|
+
* connect time limit, Node's limit on one attempt among several addresses, Node's own check of the
|
|
167
|
+
* host's name, and every code Node gives a server certificate the TLS handshake refused.
|
|
168
|
+
*/
|
|
169
|
+
const CONNECTION_SETUP_CODES = new Set([
|
|
170
|
+
"ECONNREFUSED",
|
|
171
|
+
"ENOTFOUND",
|
|
172
|
+
"EAI_AGAIN",
|
|
173
|
+
"UND_ERR_CONNECT_TIMEOUT",
|
|
174
|
+
"ERR_SOCKET_CONNECTION_TIMEOUT",
|
|
175
|
+
"ERR_TLS_CERT_ALTNAME_INVALID",
|
|
176
|
+
"ERR_TLS_CERT_ALTNAME_FORMAT",
|
|
177
|
+
// Node names the verdicts of OpenSSL's certificate check from a fixed table, the "X509 certificate
|
|
178
|
+
// error codes" of its `tls` docs, and gives every other verdict, such as an unhandled critical
|
|
179
|
+
// extension or a CA certificate that may not sign certificates, the code `UNSPECIFIED`. The
|
|
180
|
+
// error carries nothing else that marks it, so these codes are the mark: the table as Node 24
|
|
181
|
+
// compiles it, in its order, then its default. The Python command reads every
|
|
182
|
+
// `ssl.SSLCertVerificationError` the same way.
|
|
183
|
+
"UNABLE_TO_GET_ISSUER_CERT",
|
|
184
|
+
"UNABLE_TO_GET_CRL",
|
|
185
|
+
"UNABLE_TO_DECRYPT_CERT_SIGNATURE",
|
|
186
|
+
"UNABLE_TO_DECRYPT_CRL_SIGNATURE",
|
|
187
|
+
"UNABLE_TO_DECODE_ISSUER_PUBLIC_KEY",
|
|
188
|
+
"CERT_SIGNATURE_FAILURE",
|
|
189
|
+
"CRL_SIGNATURE_FAILURE",
|
|
190
|
+
"CERT_NOT_YET_VALID",
|
|
191
|
+
"CERT_HAS_EXPIRED",
|
|
192
|
+
"CRL_NOT_YET_VALID",
|
|
193
|
+
"CRL_HAS_EXPIRED",
|
|
194
|
+
"ERROR_IN_CERT_NOT_BEFORE_FIELD",
|
|
195
|
+
"ERROR_IN_CERT_NOT_AFTER_FIELD",
|
|
196
|
+
"ERROR_IN_CRL_LAST_UPDATE_FIELD",
|
|
197
|
+
"ERROR_IN_CRL_NEXT_UPDATE_FIELD",
|
|
198
|
+
"OUT_OF_MEM",
|
|
199
|
+
"DEPTH_ZERO_SELF_SIGNED_CERT",
|
|
200
|
+
"SELF_SIGNED_CERT_IN_CHAIN",
|
|
201
|
+
"UNABLE_TO_GET_ISSUER_CERT_LOCALLY",
|
|
202
|
+
"UNABLE_TO_VERIFY_LEAF_SIGNATURE",
|
|
203
|
+
"CERT_CHAIN_TOO_LONG",
|
|
204
|
+
"CERT_REVOKED",
|
|
205
|
+
"INVALID_CA",
|
|
206
|
+
"PATH_LENGTH_EXCEEDED",
|
|
207
|
+
"INVALID_PURPOSE",
|
|
208
|
+
"CERT_UNTRUSTED",
|
|
209
|
+
"CERT_REJECTED",
|
|
210
|
+
"HOSTNAME_MISMATCH",
|
|
211
|
+
"UNSPECIFIED",
|
|
212
|
+
]);
|
|
213
|
+
/**
|
|
214
|
+
* Whether a failure met once a request that may create a run was sent, and before the API named
|
|
215
|
+
* the run, leaves it unknown whether one was created: its answer was lost to a time limit, a
|
|
216
|
+
* connection that closed once the request had left or a gateway that cut the request off, or it
|
|
217
|
+
* came back unreadable, or a gateway answered that it lost or never got the server's answer
|
|
218
|
+
* (`502`, `504`, RFC 9110). A gateway cuts a request off at ~30 seconds, whether a blocking
|
|
219
|
+
* execute or a start the server is still handling, such as one fetching a `method_ref`'s package:
|
|
220
|
+
* the SDK's `isGatewayCutOff` tells it, past its threshold, from the time since `onStarting`. Any
|
|
221
|
+
* other answer from the API, a `503` that came back before that saying the request was not handled
|
|
222
|
+
* included, and a failure that proves nothing was sent, say no run was created.
|
|
223
|
+
*/
|
|
224
|
+
function mayHaveStarted(stage, error) {
|
|
225
|
+
if (stage.kind !== "starting")
|
|
226
|
+
return false;
|
|
227
|
+
if (error instanceof PipelineExecuteTimeoutError)
|
|
228
|
+
return true;
|
|
229
|
+
if (error instanceof ApiUnreachableError)
|
|
230
|
+
return !provesNothingSent(error);
|
|
231
|
+
if (!(error instanceof ApiResponseError))
|
|
232
|
+
return false;
|
|
233
|
+
if (error.status >= 200 && error.status < 300)
|
|
234
|
+
return true;
|
|
235
|
+
return (GATEWAY_LOST_ANSWER.has(error.status) || isGatewayCutOff(error, performance.now() - stage.since));
|
|
236
|
+
}
|
|
237
|
+
/** The gateway statuses that say the server's answer was lost or never came (RFC 9110). */
|
|
238
|
+
const GATEWAY_LOST_ANSWER = new Set([502, 504]);
|
|
239
|
+
/** The line under a failure that leaves it unknown whether a run was created. */
|
|
240
|
+
export const RUN_MAY_HAVE_STARTED_LINE = "A run may have started on the server without the command learning of it, so check before starting it again.";
|
|
241
|
+
/** The line under a failure met while waiting on a run that exists. */
|
|
242
|
+
export function runStillGoingLine(runId) {
|
|
243
|
+
return `Run ${runId} may still be going on the server, so do not start it again.`;
|
|
244
|
+
}
|
|
245
|
+
/** The method `--method` names, a path being read from disk. */
|
|
246
|
+
async function methodSource(method) {
|
|
247
|
+
const selector = classifyMethod(method);
|
|
248
|
+
switch (selector.kind) {
|
|
249
|
+
case "address":
|
|
250
|
+
return { kind: "address", methodRef: selector.methodRef };
|
|
251
|
+
case "catalog":
|
|
252
|
+
return { kind: "catalog", methodId: selector.methodId };
|
|
253
|
+
case "path":
|
|
254
|
+
return { kind: "bundle", files: await readBundle(selector.path) };
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
/**
|
|
258
|
+
* Upload the local files the inputs name at the method's file inputs, through the SDK's input
|
|
259
|
+
* preparation, and report each upload on stderr. Inputs that are absent or empty are sent as they
|
|
260
|
+
* are, with no request: there is nothing to upload.
|
|
261
|
+
*/
|
|
262
|
+
async function prepare(client, source, pipe, inputs, io) {
|
|
263
|
+
if (inputs === undefined || Object.keys(inputs).length === 0)
|
|
264
|
+
return inputs;
|
|
265
|
+
const prepared = await untilInterrupted(() => client.prepareInputs({
|
|
266
|
+
...crateSelector(source),
|
|
267
|
+
...(pipe === undefined ? {} : { pipe_ref: pipe }),
|
|
268
|
+
inputs,
|
|
269
|
+
// Once interrupted, no upload starts after the pipe I/O answer arrives.
|
|
270
|
+
signal: io.interrupt,
|
|
271
|
+
}), io.interrupt);
|
|
272
|
+
for (const upload of prepared.uploads) {
|
|
273
|
+
io.writeStderr(`Uploaded ${upload.filename} as ${upload.uri}\n`);
|
|
274
|
+
}
|
|
275
|
+
return prepared.inputs;
|
|
276
|
+
}
|
|
277
|
+
/** What an interrupt says, by where the run stood. */
|
|
278
|
+
function interruptMessage(stage, templateOnly) {
|
|
279
|
+
if (templateOnly)
|
|
280
|
+
return "Interrupted.";
|
|
281
|
+
switch (stage.kind) {
|
|
282
|
+
case "local":
|
|
283
|
+
return "Interrupted. No run was started.";
|
|
284
|
+
case "starting":
|
|
285
|
+
return "Interrupted before the API answered with a run id. A run may or may not have started on the server.";
|
|
286
|
+
case "started":
|
|
287
|
+
return `Interrupted. Run ${stage.runId} keeps going on the server.`;
|
|
288
|
+
}
|
|
289
|
+
}
|
|
290
|
+
//# sourceMappingURL=run.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"run.js","sourceRoot":"","sources":["../../src/cli/run.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `pipelex-sdk script`: write the per-method command, a short shell script that runs one method
|
|
3
|
+
* through this SDK's `run`, its version pinned.
|
|
4
|
+
*
|
|
5
|
+
* The checks that need no request come first, in this order: the flags, `--pipe`'s form, a
|
|
6
|
+
* control character in any value the script would carry, the method's form (a local bundle is
|
|
7
|
+
* refused), `--name`, `--dir`, the target file when its name is already known, and the key. Then
|
|
8
|
+
* one pipe I/O call checks the method and the pipe, which spends no inference; a catalog id with
|
|
9
|
+
* no `--name` is named from its catalog entry; and the file is written, never over an existing
|
|
10
|
+
* one, with the permissions of an executable, an interrupt meanwhile waiting until it is whole.
|
|
11
|
+
*/
|
|
12
|
+
import type { CommandIO } from "./io.js";
|
|
13
|
+
/** The package a written script runs, and what its runner needs. */
|
|
14
|
+
export declare const SCRIPT_RUNNER = "npx --yes @pipelex/sdk@0.34.0";
|
|
15
|
+
export declare const SCRIPT_WRITER = "@pipelex/sdk";
|
|
16
|
+
export declare const SCRIPT_NEEDS = "Node 22.12 or later";
|
|
17
|
+
/** Run `pipelex-sdk script` and return its exit code. */
|
|
18
|
+
export declare function runCommandScript(args: readonly string[], io: CommandIO): Promise<number>;
|
|
19
|
+
/**
|
|
20
|
+
* The script's text: the shebang, a header saying what it runs, which SDK wrote it, what it needs
|
|
21
|
+
* and how to use it, and the one line that runs it, every value single-quoted.
|
|
22
|
+
*/
|
|
23
|
+
export declare function scriptBody(name: string, method: string, pipe: string | undefined): string;
|