@oisincoveney/pipeline 2.8.0 → 2.8.2
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/argo-submit.d.ts +2 -4
- package/dist/argo-submit.js +80 -80
- package/dist/cluster-doctor.js +89 -101
- package/dist/config/defaults.js +9 -19
- package/dist/config/load.js +32 -39
- package/dist/config/schemas.d.ts +1 -1
- package/dist/gates.js +6 -225
- package/dist/mcp/gateway-error.js +15 -0
- package/dist/mcp/gateway.js +119 -220
- package/dist/moka-global-config.js +20 -20
- package/dist/moka-submit.d.ts +1 -1
- package/dist/pipeline-runtime.js +580 -371
- package/dist/run-state/git-refs.js +124 -94
- package/dist/runner-command-contract.d.ts +2 -2
- package/dist/runner-event-sink.js +37 -69
- package/dist/runtime/agent-node/agent-node.js +214 -173
- package/dist/runtime/builtins/builtins.js +344 -57
- package/dist/runtime/changed-files/changed-files.js +15 -27
- package/dist/runtime/changed-files/index.js +2 -0
- package/dist/runtime/drain-merge/drain-merge.js +124 -82
- package/dist/runtime/gates/gates.js +46 -28
- package/dist/runtime/hooks/hooks.js +74 -29
- package/dist/runtime/opencode-server.js +27 -21
- package/dist/runtime/opencode-session-executor.js +101 -44
- package/dist/runtime/parallel-node/parallel-node.js +24 -5
- package/dist/runtime/select-candidate/select-candidate.js +45 -29
- package/dist/runtime/services/agent-node-runtime-service.js +15 -0
- package/dist/runtime/services/command-executor-service.js +8 -0
- package/dist/runtime/services/config-io-service.js +42 -0
- package/dist/runtime/services/drain-merge-git-service.js +10 -0
- package/dist/runtime/services/git-porcelain-service.js +38 -0
- package/dist/runtime/services/kubernetes-argo-service.d.ts +13 -0
- package/dist/runtime/services/kubernetes-argo-service.js +81 -0
- package/dist/runtime/services/mcp-gateway-service.js +184 -0
- package/dist/runtime/services/opencode-sdk-service.js +27 -0
- package/dist/runtime/services/runner-event-sink-http-service.js +80 -0
- package/dist/runtime/services/select-candidate-service.js +13 -0
- package/package.json +1 -1
|
@@ -1,14 +1,13 @@
|
|
|
1
|
+
import { GitPorcelainService, GitPorcelainServiceLive } from "../runtime/services/git-porcelain-service.js";
|
|
2
|
+
import { Effect } from "effect";
|
|
1
3
|
import { chmodSync, existsSync, mkdirSync, readFileSync, statSync, writeFileSync } from "node:fs";
|
|
2
4
|
import { dirname, resolve } from "node:path";
|
|
3
5
|
import { tmpdir } from "node:os";
|
|
4
|
-
import { execFile } from "node:child_process";
|
|
5
|
-
import { promisify } from "node:util";
|
|
6
6
|
//#region src/run-state/git-refs.ts
|
|
7
7
|
const DEFAULT_WORKSPACE_PATH = "/workspace";
|
|
8
8
|
const DEFAULT_GIT_CREDENTIALS_DIR = "/etc/pipeline/git-credentials";
|
|
9
9
|
const WRITABLE_GIT_CREDENTIAL_STORE = resolve(tmpdir(), "pipeline-git-credentials");
|
|
10
10
|
const SCP_LIKE_SSH_REMOTE_RE = /^[^@\s]+@[^:\s]+:.+/u;
|
|
11
|
-
const execGit = promisify(execFile);
|
|
12
11
|
let preparedBasicAuthCredentialStore;
|
|
13
12
|
function runnerGitRefs(payload, nodeId) {
|
|
14
13
|
const prefix = `refs/heads/pipeline/runs/${payload.run.id}/${payload.workflow.id}`;
|
|
@@ -19,101 +18,132 @@ function runnerGitRefs(payload, nodeId) {
|
|
|
19
18
|
};
|
|
20
19
|
}
|
|
21
20
|
async function prepareRunnerGitWorkspace(payload, options = {}) {
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
worktreePath
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
21
|
+
return await Effect.runPromise(Effect.provide(prepareRunnerGitWorkspaceEffect(payload, options), GitPorcelainServiceLive));
|
|
22
|
+
}
|
|
23
|
+
function prepareRunnerGitWorkspaceEffect(payload, options) {
|
|
24
|
+
return Effect.gen(function* () {
|
|
25
|
+
if (options.cwd) return resolve(options.cwd);
|
|
26
|
+
const worktreePath = options.workspacePath ?? DEFAULT_WORKSPACE_PATH;
|
|
27
|
+
yield* Effect.sync(() => mkdirSync(dirname(worktreePath), { recursive: true }));
|
|
28
|
+
yield* runGit(dirname(worktreePath), [
|
|
29
|
+
"clone",
|
|
30
|
+
"--no-tags",
|
|
31
|
+
payload.repository.url,
|
|
32
|
+
worktreePath
|
|
33
|
+
]);
|
|
34
|
+
yield* runGit(worktreePath, ["checkout", payload.repository.sha ?? `origin/${payload.repository.baseBranch}`]);
|
|
35
|
+
return worktreePath;
|
|
36
|
+
});
|
|
33
37
|
}
|
|
34
38
|
async function mergeDependencyRefs(input) {
|
|
35
|
-
await
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
+
return await Effect.runPromise(Effect.provide(mergeDependencyRefsEffect(input), GitPorcelainServiceLive));
|
|
40
|
+
}
|
|
41
|
+
function mergeDependencyRefsEffect(input) {
|
|
42
|
+
return Effect.gen(function* () {
|
|
43
|
+
yield* configureGitCommitter(input.worktreePath, input.committer);
|
|
44
|
+
yield* Effect.forEach(input.dependencyNodeIds, (nodeId) => mergeDependencyRef(input.worktreePath, runnerGitRefs(input.payload, nodeId).nodeRef));
|
|
45
|
+
});
|
|
46
|
+
}
|
|
47
|
+
function mergeDependencyRef(worktreePath, ref) {
|
|
48
|
+
return Effect.gen(function* () {
|
|
49
|
+
yield* runGit(worktreePath, [
|
|
39
50
|
"fetch",
|
|
40
51
|
"origin",
|
|
41
52
|
ref
|
|
42
53
|
]);
|
|
43
|
-
|
|
54
|
+
yield* runGit(worktreePath, [
|
|
44
55
|
"merge",
|
|
45
56
|
"--no-ff",
|
|
46
57
|
"--no-edit",
|
|
47
58
|
"FETCH_HEAD"
|
|
48
59
|
]);
|
|
49
|
-
}
|
|
60
|
+
});
|
|
50
61
|
}
|
|
51
62
|
async function commitAndPushNodeRef(input) {
|
|
52
|
-
await
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
63
|
+
return await Effect.runPromise(Effect.provide(commitAndPushNodeRefEffect(input), GitPorcelainServiceLive));
|
|
64
|
+
}
|
|
65
|
+
function commitAndPushNodeRefEffect(input) {
|
|
66
|
+
return Effect.gen(function* () {
|
|
67
|
+
yield* commitChangesIfNeeded(input.worktreePath, input.nodeId, input.committer);
|
|
68
|
+
const sha = yield* headSha(input.worktreePath);
|
|
69
|
+
yield* runGit(input.worktreePath, [
|
|
70
|
+
"push",
|
|
71
|
+
"origin",
|
|
72
|
+
`HEAD:${runnerGitRefs(input.payload, input.nodeId).nodeRef}`
|
|
73
|
+
]);
|
|
74
|
+
return sha;
|
|
75
|
+
});
|
|
60
76
|
}
|
|
61
77
|
async function promoteFinalRef(input) {
|
|
62
|
-
await
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
78
|
+
return await Effect.runPromise(Effect.provide(promoteFinalRefEffect(input), GitPorcelainServiceLive));
|
|
79
|
+
}
|
|
80
|
+
function promoteFinalRefEffect(input) {
|
|
81
|
+
return Effect.gen(function* () {
|
|
82
|
+
yield* mergeDependencyRefsEffect({
|
|
83
|
+
committer: input.committer,
|
|
84
|
+
dependencyNodeIds: input.sourceNodeIds,
|
|
85
|
+
payload: input.payload,
|
|
86
|
+
worktreePath: input.worktreePath
|
|
87
|
+
});
|
|
88
|
+
yield* commitChangesIfNeeded(input.worktreePath, "final", input.committer);
|
|
89
|
+
const sha = yield* headSha(input.worktreePath);
|
|
90
|
+
yield* runGit(input.worktreePath, [
|
|
91
|
+
"push",
|
|
92
|
+
"origin",
|
|
93
|
+
`HEAD:${runnerGitRefs(input.payload, "final").finalRef}`
|
|
94
|
+
]);
|
|
95
|
+
return sha;
|
|
96
|
+
});
|
|
97
|
+
}
|
|
98
|
+
function headSha(worktreePath) {
|
|
99
|
+
return Effect.map(runGit(worktreePath, ["rev-parse", "HEAD"]), (sha) => sha.trim());
|
|
100
|
+
}
|
|
101
|
+
function commitChangesIfNeeded(worktreePath, nodeId, committer) {
|
|
102
|
+
return Effect.gen(function* () {
|
|
103
|
+
if ((yield* runGit(worktreePath, [
|
|
104
|
+
"status",
|
|
105
|
+
"--porcelain",
|
|
106
|
+
"--untracked-files=all"
|
|
107
|
+
])).trim().length === 0) return;
|
|
108
|
+
yield* runGit(worktreePath, ["add", "--all"]);
|
|
109
|
+
yield* configureGitCommitter(worktreePath, committer);
|
|
110
|
+
yield* runGit(worktreePath, [
|
|
111
|
+
"commit",
|
|
112
|
+
"-m",
|
|
113
|
+
`pipeline: ${nodeId}`
|
|
114
|
+
]);
|
|
115
|
+
});
|
|
116
|
+
}
|
|
117
|
+
function configureGitCommitter(worktreePath, committer) {
|
|
118
|
+
return Effect.gen(function* () {
|
|
119
|
+
yield* runGit(worktreePath, [
|
|
120
|
+
"config",
|
|
121
|
+
"--local",
|
|
122
|
+
"user.name",
|
|
123
|
+
committer.name
|
|
124
|
+
]);
|
|
125
|
+
yield* runGit(worktreePath, [
|
|
126
|
+
"config",
|
|
127
|
+
"--local",
|
|
128
|
+
"user.email",
|
|
129
|
+
committer.email
|
|
130
|
+
]);
|
|
67
131
|
});
|
|
68
|
-
await commitChangesIfNeeded(input.worktreePath, "final", input.committer);
|
|
69
|
-
const sha = (await runGit(input.worktreePath, ["rev-parse", "HEAD"])).trim();
|
|
70
|
-
await runGit(input.worktreePath, [
|
|
71
|
-
"push",
|
|
72
|
-
"origin",
|
|
73
|
-
`HEAD:${runnerGitRefs(input.payload, "final").finalRef}`
|
|
74
|
-
]);
|
|
75
|
-
return sha;
|
|
76
|
-
}
|
|
77
|
-
async function commitChangesIfNeeded(worktreePath, nodeId, committer) {
|
|
78
|
-
if ((await runGit(worktreePath, [
|
|
79
|
-
"status",
|
|
80
|
-
"--porcelain",
|
|
81
|
-
"--untracked-files=all"
|
|
82
|
-
])).trim().length === 0) return;
|
|
83
|
-
await runGit(worktreePath, ["add", "--all"]);
|
|
84
|
-
await configureGitCommitter(worktreePath, committer);
|
|
85
|
-
await runGit(worktreePath, [
|
|
86
|
-
"commit",
|
|
87
|
-
"-m",
|
|
88
|
-
`pipeline: ${nodeId}`
|
|
89
|
-
]);
|
|
90
|
-
}
|
|
91
|
-
async function configureGitCommitter(worktreePath, committer) {
|
|
92
|
-
await runGit(worktreePath, [
|
|
93
|
-
"config",
|
|
94
|
-
"--local",
|
|
95
|
-
"user.name",
|
|
96
|
-
committer.name
|
|
97
|
-
]);
|
|
98
|
-
await runGit(worktreePath, [
|
|
99
|
-
"config",
|
|
100
|
-
"--local",
|
|
101
|
-
"user.email",
|
|
102
|
-
committer.email
|
|
103
|
-
]);
|
|
104
132
|
}
|
|
105
133
|
function runnerGitCommandArgs(args, remoteUrl) {
|
|
106
134
|
return [...gitCredentialConfigArgs(remoteUrl), ...args];
|
|
107
135
|
}
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
136
|
+
function runGit(cwd, args) {
|
|
137
|
+
return Effect.gen(function* () {
|
|
138
|
+
const remoteUrl = yield* remoteUrlFromGitArgs(cwd, args);
|
|
139
|
+
yield* Effect.try({
|
|
140
|
+
try: () => assertSshCredentialsAvailable(remoteUrl),
|
|
141
|
+
catch: (error) => error
|
|
142
|
+
});
|
|
143
|
+
const git = yield* GitPorcelainService;
|
|
144
|
+
const commandArgs = runnerGitCommandArgs(args, remoteUrl);
|
|
145
|
+
return yield* git.run(cwd, commandArgs, runnerGitEnv(remoteUrl));
|
|
115
146
|
});
|
|
116
|
-
return stdout;
|
|
117
147
|
}
|
|
118
148
|
function assertSshCredentialsAvailable(remoteUrl) {
|
|
119
149
|
if (!(remoteUrl && isSshRemote(remoteUrl))) return;
|
|
@@ -227,12 +257,15 @@ function isOwnerReadOnly(path) {
|
|
|
227
257
|
function readCredentialFile(path) {
|
|
228
258
|
return readFileSync(path, "utf8").trim();
|
|
229
259
|
}
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
260
|
+
function remoteUrlFromGitArgs(cwd, args) {
|
|
261
|
+
return Effect.gen(function* () {
|
|
262
|
+
const literalRemoteUrl = literalRemoteUrlFromGitArgs(args);
|
|
263
|
+
if (literalRemoteUrl) return literalRemoteUrl;
|
|
264
|
+
return yield* remoteUrlForName(cwd, remoteNameFromGitArgs(args));
|
|
265
|
+
});
|
|
266
|
+
}
|
|
267
|
+
function remoteUrlForName(cwd, remoteName) {
|
|
268
|
+
return remoteName ? gitRemoteUrl(cwd, remoteName) : Effect.succeed(void 0);
|
|
236
269
|
}
|
|
237
270
|
function literalRemoteUrlFromGitArgs(args) {
|
|
238
271
|
if (args[0] === "clone") return args.find((arg) => isRemoteUrl(arg));
|
|
@@ -247,20 +280,17 @@ function remoteNameFromGitArgs(args) {
|
|
|
247
280
|
if (remoteArg && !remoteArg.startsWith("-") && !isRemoteUrl(remoteArg)) return remoteArg;
|
|
248
281
|
}
|
|
249
282
|
}
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
encoding: "utf8",
|
|
258
|
-
env: {
|
|
283
|
+
function gitRemoteUrl(cwd, remoteName) {
|
|
284
|
+
return Effect.gen(function* () {
|
|
285
|
+
return (yield* (yield* GitPorcelainService).run(cwd, [
|
|
286
|
+
"remote",
|
|
287
|
+
"get-url",
|
|
288
|
+
remoteName
|
|
289
|
+
], {
|
|
259
290
|
...process.env,
|
|
260
291
|
GIT_TERMINAL_PROMPT: "0"
|
|
261
|
-
}
|
|
292
|
+
})).trim() || void 0;
|
|
262
293
|
});
|
|
263
|
-
return stdout.trim() || void 0;
|
|
264
294
|
}
|
|
265
295
|
function isRemoteUrl(value) {
|
|
266
296
|
return value.startsWith("http://") || value.startsWith("https://") || value.startsWith("ssh://") || isScpLikeSshRemote(value);
|
|
@@ -43,8 +43,8 @@ declare const runnerDeliverySchema: z.ZodObject<{
|
|
|
43
43
|
declare const mokaSubmissionSchema: z.ZodDiscriminatedUnion<[z.ZodObject<{
|
|
44
44
|
kind: z.ZodLiteral<"graph">;
|
|
45
45
|
mode: z.ZodEnum<{
|
|
46
|
-
quick: "quick";
|
|
47
46
|
full: "full";
|
|
47
|
+
quick: "quick";
|
|
48
48
|
}>;
|
|
49
49
|
}, z.core.$strict>, z.ZodObject<{
|
|
50
50
|
argv: z.ZodArray<z.ZodString>;
|
|
@@ -104,8 +104,8 @@ declare const runnerCommandPayloadSchema: z.ZodObject<{
|
|
|
104
104
|
submission: z.ZodDefault<z.ZodDiscriminatedUnion<[z.ZodObject<{
|
|
105
105
|
kind: z.ZodLiteral<"graph">;
|
|
106
106
|
mode: z.ZodEnum<{
|
|
107
|
-
quick: "quick";
|
|
108
107
|
full: "full";
|
|
108
|
+
quick: "quick";
|
|
109
109
|
}>;
|
|
110
110
|
}, z.core.$strict>, z.ZodObject<{
|
|
111
111
|
argv: z.ZodArray<z.ZodString>;
|
|
@@ -1,39 +1,14 @@
|
|
|
1
1
|
import { mapRuntimeEventToRunnerEventRecords } from "./runner-command-contract.js";
|
|
2
|
-
import {
|
|
3
|
-
import
|
|
2
|
+
import { RunnerEventSinkHttpService, RunnerEventSinkHttpServiceLive } from "./runtime/services/runner-event-sink-http-service.js";
|
|
3
|
+
import { Effect } from "effect";
|
|
4
4
|
//#region src/runner-event-sink.ts
|
|
5
5
|
const DEFAULT_BATCH_SIZE = 50;
|
|
6
6
|
const DEFAULT_MAX_RETRIES = 0;
|
|
7
7
|
const DEFAULT_RETRY_DELAY_MS = 250;
|
|
8
|
-
const RETRYABLE_STATUS_CODES = [
|
|
9
|
-
408,
|
|
10
|
-
429,
|
|
11
|
-
500,
|
|
12
|
-
501,
|
|
13
|
-
502,
|
|
14
|
-
503,
|
|
15
|
-
504,
|
|
16
|
-
505,
|
|
17
|
-
506,
|
|
18
|
-
507,
|
|
19
|
-
508,
|
|
20
|
-
509,
|
|
21
|
-
510,
|
|
22
|
-
511
|
|
23
|
-
];
|
|
24
|
-
var EventSinkHttpError = class extends Data.TaggedError("EventSinkHttpError") {
|
|
25
|
-
constructor(status, message) {
|
|
26
|
-
super({
|
|
27
|
-
status,
|
|
28
|
-
message
|
|
29
|
-
});
|
|
30
|
-
}
|
|
31
|
-
};
|
|
32
8
|
function createRunnerEventSink(options) {
|
|
33
|
-
const batchSize =
|
|
34
|
-
const fetchImpl = options.fetch
|
|
35
|
-
|
|
36
|
-
if (!options.authToken.trim()) throw new Error("Runner event sink requires an auth token");
|
|
9
|
+
const batchSize = positiveOrDefault(options.batchSize, DEFAULT_BATCH_SIZE);
|
|
10
|
+
const fetchImpl = resolveFetch(options.fetch);
|
|
11
|
+
assertAuthToken(options.authToken);
|
|
37
12
|
const queue = [];
|
|
38
13
|
let flushChain = Promise.resolve();
|
|
39
14
|
let nextSequence = 1;
|
|
@@ -46,15 +21,17 @@ function createRunnerEventSink(options) {
|
|
|
46
21
|
sequence
|
|
47
22
|
};
|
|
48
23
|
};
|
|
49
|
-
const
|
|
50
|
-
|
|
24
|
+
const flushQueueEffect = () => Effect.gen(function* () {
|
|
25
|
+
const service = yield* RunnerEventSinkHttpService;
|
|
26
|
+
while (hasQueuedEvents(queue)) {
|
|
51
27
|
const batch = queue.slice(0, batchSize);
|
|
52
|
-
|
|
28
|
+
yield* service.postBatch(postBatchRequest(options, fetchImpl, batch));
|
|
53
29
|
queue.splice(0, batch.length);
|
|
54
30
|
}
|
|
55
|
-
};
|
|
31
|
+
});
|
|
32
|
+
const runFlush = () => Effect.runPromise(Effect.provide(flushQueueEffect(), RunnerEventSinkHttpServiceLive));
|
|
56
33
|
const runSerializedFlush = () => {
|
|
57
|
-
const nextFlush = flushChain.then(
|
|
34
|
+
const nextFlush = flushChain.then(runFlush, runFlush);
|
|
58
35
|
flushChain = nextFlush.catch(() => void 0);
|
|
59
36
|
return nextFlush;
|
|
60
37
|
};
|
|
@@ -145,41 +122,32 @@ function createRunnerEventSink(options) {
|
|
|
145
122
|
}
|
|
146
123
|
};
|
|
147
124
|
}
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
const retryDelayMs = Math.max(0, options.retryDelayMs ?? DEFAULT_RETRY_DELAY_MS);
|
|
151
|
-
try {
|
|
152
|
-
await ky.post(options.url, {
|
|
153
|
-
fetch: kyFetchAdapter(fetchImpl),
|
|
154
|
-
headers: { [options.authHeader ?? "Authorization"]: `Bearer ${options.authToken}` },
|
|
155
|
-
json: { events },
|
|
156
|
-
retry: {
|
|
157
|
-
delay: () => retryDelayMs,
|
|
158
|
-
limit: maxRetries,
|
|
159
|
-
methods: ["post"],
|
|
160
|
-
retryOnTimeout: true,
|
|
161
|
-
statusCodes: RETRYABLE_STATUS_CODES
|
|
162
|
-
}
|
|
163
|
-
});
|
|
164
|
-
} catch (err) {
|
|
165
|
-
if (isHTTPError(err)) {
|
|
166
|
-
let data = "";
|
|
167
|
-
if (typeof err.data === "string") data = err.data;
|
|
168
|
-
else if (err.data !== void 0) data = JSON.stringify(err.data);
|
|
169
|
-
throw new EventSinkHttpError(err.response.status, `Event sink responded with ${err.response.status}${data ? `: ${data}` : ""}`);
|
|
170
|
-
}
|
|
171
|
-
throw err instanceof Error ? err : new Error(String(err));
|
|
172
|
-
}
|
|
125
|
+
function hasQueuedEvents(queue) {
|
|
126
|
+
return queue.length > 0;
|
|
173
127
|
}
|
|
174
|
-
function
|
|
175
|
-
return
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
128
|
+
function positiveOrDefault(value, fallback) {
|
|
129
|
+
return Math.max(1, value ?? fallback);
|
|
130
|
+
}
|
|
131
|
+
function nonNegativeOrDefault(value, fallback) {
|
|
132
|
+
return Math.max(0, value ?? fallback);
|
|
133
|
+
}
|
|
134
|
+
function resolveFetch(fetchImpl) {
|
|
135
|
+
const resolved = fetchImpl ?? globalThis.fetch?.bind(globalThis);
|
|
136
|
+
if (!resolved) throw new Error("Runner event sink requires fetch support");
|
|
137
|
+
return resolved;
|
|
138
|
+
}
|
|
139
|
+
function assertAuthToken(authToken) {
|
|
140
|
+
if (!authToken.trim()) throw new Error("Runner event sink requires an auth token");
|
|
141
|
+
}
|
|
142
|
+
function postBatchRequest(options, fetchImpl, events) {
|
|
143
|
+
return {
|
|
144
|
+
authHeader: options.authHeader,
|
|
145
|
+
authToken: options.authToken,
|
|
146
|
+
events,
|
|
147
|
+
fetch: fetchImpl,
|
|
148
|
+
maxRetries: nonNegativeOrDefault(options.maxRetries, DEFAULT_MAX_RETRIES),
|
|
149
|
+
retryDelayMs: nonNegativeOrDefault(options.retryDelayMs, DEFAULT_RETRY_DELAY_MS),
|
|
150
|
+
url: options.url
|
|
183
151
|
};
|
|
184
152
|
}
|
|
185
153
|
function timestamp(now) {
|