@fungi.computer/shed 0.1.0 → 0.2.1
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 +17 -5
- package/backend/methods.ts +34 -44
- package/backend/process-page.ts +1 -1
- package/backend/runs.ts +72 -183
- package/backend/test/methods.test.ts +299 -106
- package/backend/test/runs.node.test.ts +447 -268
- package/dist/assets/index-BnXCnX-l.css +1 -0
- package/dist/assets/index-CRz0hCtN.js +1769 -0
- package/dist/index.html +2 -2
- package/package.json +35 -19
- package/dist/assets/index-2RtcU_CN.css +0 -1
- package/dist/assets/index-bW5A7RuN.js +0 -1769
package/backend/runs.ts
CHANGED
|
@@ -1,31 +1,24 @@
|
|
|
1
1
|
import * as z from "zod";
|
|
2
2
|
import { Hex } from "effect/encoding";
|
|
3
3
|
import type { HostActor } from "@fungi.computer/puffball/app-method-runtime";
|
|
4
|
-
import {
|
|
5
|
-
workflowGitPassResultSchema,
|
|
6
|
-
type WorkflowGitPassResult,
|
|
7
|
-
} from "@fungi.computer/agar/computer-contracts";
|
|
8
4
|
import { resolvedGitAccessSchema } from "@fungi.computer/braid/git-access";
|
|
9
5
|
import { pending } from "@fungi.computer/agar/backend-definition";
|
|
10
6
|
import type { WhistleJsonValue } from "@fungi.computer/whistle";
|
|
11
7
|
import { AppMethodRefusal } from "@fungi.computer/agar/backend";
|
|
12
8
|
import {
|
|
13
9
|
computerProcessSchema,
|
|
10
|
+
computerOperationResultSchema,
|
|
14
11
|
maximumProcessRecords,
|
|
15
|
-
writeOutputSchema,
|
|
16
|
-
type ComputerFileWriteInput,
|
|
17
12
|
processStartInputSchema,
|
|
18
13
|
processListOutputSchema,
|
|
19
14
|
type ComputerProcess,
|
|
20
|
-
} from "@fungi.computer/computer
|
|
15
|
+
} from "@fungi.computer/computer-contract";
|
|
21
16
|
import {
|
|
22
17
|
runCommandInputSchema,
|
|
23
18
|
runCommandResultSchema,
|
|
24
19
|
shedComputerRefusalSchema,
|
|
25
20
|
} from "../src/light-shell/run-command-contract.js";
|
|
26
21
|
|
|
27
|
-
/** What a Git command needs from a pass: its remote and any header credential. */
|
|
28
|
-
type GitCredential = Pick<WorkflowGitPassResult, "remote" | "bearer">;
|
|
29
22
|
const processInputSchema = processStartInputSchema.omit({
|
|
30
23
|
requestId: true,
|
|
31
24
|
requestScope: true,
|
|
@@ -49,12 +42,6 @@ export type ShedSql = {
|
|
|
49
42
|
): { toArray(): unknown[] };
|
|
50
43
|
};
|
|
51
44
|
export type ShedComputer = Readonly<{
|
|
52
|
-
workflowGitPass?:
|
|
53
|
-
| ((input: Record<string, never>) => Promise<z.core.util.JSONType>)
|
|
54
|
-
| undefined;
|
|
55
|
-
write?:
|
|
56
|
-
| ((input: ComputerFileWriteInput) => Promise<z.core.util.JSONType>)
|
|
57
|
-
| undefined;
|
|
58
45
|
processStart(input: {
|
|
59
46
|
command: string;
|
|
60
47
|
cwd?: string | undefined;
|
|
@@ -62,6 +49,23 @@ export type ShedComputer = Readonly<{
|
|
|
62
49
|
}): Promise<z.core.util.JSONType>;
|
|
63
50
|
processes(input: Record<string, never>): Promise<z.core.util.JSONType>;
|
|
64
51
|
}>;
|
|
52
|
+
|
|
53
|
+
const bindingNotGranted = z.object({
|
|
54
|
+
code: z.literal("binding_not_granted"),
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
/** Only refusal-aware callers translate a host's typed binding denial. */
|
|
58
|
+
export async function computerCall(
|
|
59
|
+
call: () => Promise<WhistleJsonValue>,
|
|
60
|
+
): Promise<WhistleJsonValue> {
|
|
61
|
+
try {
|
|
62
|
+
return await call();
|
|
63
|
+
} catch (cause) {
|
|
64
|
+
if (!bindingNotGranted.safeParse(cause).success) throw cause;
|
|
65
|
+
return shedComputerRefusalSchema.parse({ error: "not_granted" });
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
|
|
65
69
|
const rowSchema = z.strictObject({
|
|
66
70
|
requestKey: z.string(),
|
|
67
71
|
computerId: z.string(),
|
|
@@ -73,29 +77,7 @@ const rowSchema = z.strictObject({
|
|
|
73
77
|
exitCode: z.number().int().nullable(),
|
|
74
78
|
logPath: z.string(),
|
|
75
79
|
gitAccess: z.string().nullable(),
|
|
76
|
-
passId: z.string().nullable(),
|
|
77
|
-
remote: z.string().nullable(),
|
|
78
|
-
expiresAt: z.number().int().nullable(),
|
|
79
80
|
});
|
|
80
|
-
// A header-admitted pass reaches git only as command-local
|
|
81
|
-
// http.<remote>.extraHeader config, read from a one-shot file the command
|
|
82
|
-
// deletes; neither the command nor any repository config ever holds it.
|
|
83
|
-
function gitCredentialFile(pass: GitCredential) {
|
|
84
|
-
return pass.bearer === undefined
|
|
85
|
-
? `${pass.remote}\n`
|
|
86
|
-
: `${pass.remote}\n${pass.bearer}\n`;
|
|
87
|
-
}
|
|
88
|
-
function gitCredentialCommand(
|
|
89
|
-
remotePath: string,
|
|
90
|
-
pass: GitCredential,
|
|
91
|
-
command: string,
|
|
92
|
-
) {
|
|
93
|
-
const read = `{ IFS= read -r _fungi_git_remote && IFS= read -r _fungi_git_bearer; } < ${quote(remotePath)}; rm -f -- ${quote(remotePath)}; test -n "$_fungi_git_remote"`;
|
|
94
|
-
return pass.bearer === undefined
|
|
95
|
-
? `${read} && FUNGI_GIT_REMOTE="$_fungi_git_remote" bash -c ${quote(command)}`
|
|
96
|
-
: `${read} && test -n "$_fungi_git_bearer" && FUNGI_GIT_REMOTE="$_fungi_git_remote" GIT_CONFIG_COUNT=1 GIT_CONFIG_KEY_0="http.$_fungi_git_remote.extraHeader" GIT_CONFIG_VALUE_0="Authorization: Bearer $_fungi_git_bearer" bash -c ${quote(command)}`;
|
|
97
|
-
}
|
|
98
|
-
|
|
99
81
|
export const shedRunCommandInputSchema = runCommandInputSchema.extend({
|
|
100
82
|
gitAccess: resolvedGitAccessSchema.optional(),
|
|
101
83
|
});
|
|
@@ -103,9 +85,9 @@ export type ShedRunCommandInput = z.output<typeof shedRunCommandInputSchema>;
|
|
|
103
85
|
export type ShedRunCommandResult = z.output<typeof runCommandResultSchema>;
|
|
104
86
|
|
|
105
87
|
/**
|
|
106
|
-
* One bounded exec
|
|
107
|
-
*
|
|
108
|
-
*
|
|
88
|
+
* One bounded exec. A Workflow step's admitted `gitAccess` is identity only:
|
|
89
|
+
* the trusted host supplies `$FUNGI_GIT_REMOTE` and its credentials after
|
|
90
|
+
* native admission, so Shed sends and holds none.
|
|
109
91
|
*/
|
|
110
92
|
export async function runShedCommand(
|
|
111
93
|
input: ShedRunCommandInput,
|
|
@@ -113,39 +95,31 @@ export async function runShedCommand(
|
|
|
113
95
|
exec(
|
|
114
96
|
input: Omit<ShedRunCommandInput, "gitAccess">,
|
|
115
97
|
): Promise<WhistleJsonValue>;
|
|
116
|
-
workflowGitPass?: ShedComputer["workflowGitPass"];
|
|
117
|
-
write?: ShedComputer["write"];
|
|
118
98
|
}>,
|
|
119
|
-
now: () => number = Date.now,
|
|
120
99
|
): Promise<ShedRunCommandResult> {
|
|
121
|
-
const
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
const
|
|
127
|
-
.union([
|
|
128
|
-
.parse(await computer.
|
|
129
|
-
if ("error" in
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
const
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
await computer.exec({
|
|
145
|
-
...exec,
|
|
146
|
-
command: gitCredentialCommand(remotePath, minted, exec.command),
|
|
147
|
-
}),
|
|
148
|
-
);
|
|
100
|
+
const exec: Omit<ShedRunCommandInput, "gitAccess"> = {
|
|
101
|
+
command: input.command,
|
|
102
|
+
timeoutMs: input.timeoutMs,
|
|
103
|
+
};
|
|
104
|
+
if (input.cwd !== undefined) exec.cwd = input.cwd;
|
|
105
|
+
const reply = z
|
|
106
|
+
.union([computerOperationResultSchema, shedComputerRefusalSchema])
|
|
107
|
+
.parse(await computerCall(() => computer.exec(exec)));
|
|
108
|
+
if ("error" in reply)
|
|
109
|
+
return reply.error === "timeout" ? { error: reply.error } : reply;
|
|
110
|
+
if (reply.output === null) return { error: "output_unavailable" };
|
|
111
|
+
const { stdout, stderr, exitCode, truncated } = reply.output;
|
|
112
|
+
return runCommandResultSchema.parse({
|
|
113
|
+
stdout,
|
|
114
|
+
stderr,
|
|
115
|
+
exitCode,
|
|
116
|
+
stdoutTruncated:
|
|
117
|
+
truncated !== false &&
|
|
118
|
+
truncated.some((stream) => stream.stream === "stdout"),
|
|
119
|
+
stderrTruncated:
|
|
120
|
+
truncated !== false &&
|
|
121
|
+
truncated.some((stream) => stream.stream === "stderr"),
|
|
122
|
+
});
|
|
149
123
|
}
|
|
150
124
|
|
|
151
125
|
function claimKey(actor: HostActor, method: "start" | "run", key: string) {
|
|
@@ -166,16 +140,19 @@ function completion(exitCode: number | null, logPath: string) {
|
|
|
166
140
|
}
|
|
167
141
|
|
|
168
142
|
/** The Computer owns execution and exit state; Shed only remembers the call-to-process link. */
|
|
169
|
-
export function createShedRuns(sql: ShedSql
|
|
143
|
+
export function createShedRuns(sql: ShedSql) {
|
|
170
144
|
// Unscoped records are not current claims; do not read or import the old ledger.
|
|
145
|
+
// The superseded claim table held credential-lease columns. Preview data is
|
|
146
|
+
// disposable, so it is dropped, never read, copied or migrated.
|
|
147
|
+
sql.exec("DROP TABLE IF EXISTS shed_process_claims");
|
|
171
148
|
sql.exec(
|
|
172
|
-
"CREATE TABLE IF NOT EXISTS
|
|
149
|
+
"CREATE TABLE IF NOT EXISTS shed_native_process_claims (requestKey TEXT PRIMARY KEY, computerId TEXT NOT NULL, command TEXT NOT NULL, cwd TEXT, processId TEXT, phase TEXT NOT NULL, startedAt INTEGER, exitCode INTEGER, logPath TEXT NOT NULL, gitAccess TEXT)",
|
|
173
150
|
);
|
|
174
151
|
sql.exec(
|
|
175
152
|
"CREATE TABLE IF NOT EXISTS shed_retired_claim_keys (requestKey TEXT PRIMARY KEY)",
|
|
176
153
|
);
|
|
177
154
|
sql.exec(
|
|
178
|
-
"DELETE FROM
|
|
155
|
+
"DELETE FROM shed_native_process_claims WHERE requestKey IN (SELECT requestKey FROM shed_retired_claim_keys)",
|
|
179
156
|
);
|
|
180
157
|
const starting = new Map<string, number | null>();
|
|
181
158
|
function retired(requestKey: string) {
|
|
@@ -198,7 +175,7 @@ export function createShedRuns(sql: ShedSql, now: () => number = Date.now) {
|
|
|
198
175
|
.parse(
|
|
199
176
|
sql
|
|
200
177
|
.exec(
|
|
201
|
-
"SELECT * FROM
|
|
178
|
+
"SELECT * FROM shed_native_process_claims WHERE computerId=? AND json_extract(requestKey,'$[0]')=? AND json_extract(requestKey,'$[1]')=? ORDER BY startedAt ASC, processId ASC, rowid ASC",
|
|
202
179
|
computerId,
|
|
203
180
|
JSON.stringify(actor),
|
|
204
181
|
mode,
|
|
@@ -212,7 +189,10 @@ export function createShedRuns(sql: ShedSql, now: () => number = Date.now) {
|
|
|
212
189
|
"INSERT OR IGNORE INTO shed_retired_claim_keys (requestKey) VALUES (?)",
|
|
213
190
|
requestKey,
|
|
214
191
|
);
|
|
215
|
-
sql.exec(
|
|
192
|
+
sql.exec(
|
|
193
|
+
"DELETE FROM shed_native_process_claims WHERE requestKey=?",
|
|
194
|
+
requestKey,
|
|
195
|
+
);
|
|
216
196
|
// Replay refusal covers the latest 64 retired calls per member/method.
|
|
217
197
|
// Older opaque keys are outside the retained replay horizon, like native history.
|
|
218
198
|
sql.exec(
|
|
@@ -250,7 +230,7 @@ export function createShedRuns(sql: ShedSql, now: () => number = Date.now) {
|
|
|
250
230
|
) {
|
|
251
231
|
const rows = scopeRows(actor, mode, computerId);
|
|
252
232
|
if (rows.length < maximumProcessRecords) return null;
|
|
253
|
-
const reply = await computer.processes({});
|
|
233
|
+
const reply = await computerCall(() => computer.processes({}));
|
|
254
234
|
const refused = shedComputerRefusalSchema.safeParse(reply);
|
|
255
235
|
if (refused.success) return refused.data;
|
|
256
236
|
const inventory = processListOutputSchema.parse(reply);
|
|
@@ -266,7 +246,10 @@ export function createShedRuns(sql: ShedSql, now: () => number = Date.now) {
|
|
|
266
246
|
function read(requestKey: string) {
|
|
267
247
|
if (retired(requestKey)) return null;
|
|
268
248
|
const row = sql
|
|
269
|
-
.exec(
|
|
249
|
+
.exec(
|
|
250
|
+
"SELECT * FROM shed_native_process_claims WHERE requestKey=?",
|
|
251
|
+
requestKey,
|
|
252
|
+
)
|
|
270
253
|
.toArray()[0];
|
|
271
254
|
return row === undefined ? null : rowSchema.parse(row);
|
|
272
255
|
}
|
|
@@ -276,7 +259,7 @@ export function createShedRuns(sql: ShedSql, now: () => number = Date.now) {
|
|
|
276
259
|
logPath: string,
|
|
277
260
|
) {
|
|
278
261
|
sql.exec(
|
|
279
|
-
"UPDATE
|
|
262
|
+
"UPDATE shed_native_process_claims SET processId=?,phase=?,exitCode=?,startedAt=? WHERE requestKey=?",
|
|
280
263
|
process.id,
|
|
281
264
|
process.state === "exited" ? "exited" : "running",
|
|
282
265
|
process.exitCode,
|
|
@@ -326,15 +309,9 @@ export function createShedRuns(sql: ShedSql, now: () => number = Date.now) {
|
|
|
326
309
|
new TextEncoder().encode(requestKey),
|
|
327
310
|
);
|
|
328
311
|
const directory = `/workspace/.fungi/runs/call-${Hex.encode(new Uint8Array(digest))}`;
|
|
329
|
-
|
|
330
|
-
const credentialFile = gitCredentialFile;
|
|
331
|
-
function prepare(pass: GitCredential | null) {
|
|
312
|
+
function prepare() {
|
|
332
313
|
const logPath = `${directory}/out.log`;
|
|
333
|
-
const command
|
|
334
|
-
pass === null
|
|
335
|
-
? `bash -c ${quote(input.command)}`
|
|
336
|
-
: gitCredentialCommand(remotePath, pass, input.command);
|
|
337
|
-
const script = `mkdir -p ${quote(directory)} && ${command} 2>&1 | tee ${quote(logPath)}`;
|
|
314
|
+
const script = `mkdir -p ${quote(directory)} && bash -c ${quote(input.command)} 2>&1 | tee ${quote(logPath)}`;
|
|
338
315
|
const processInput: z.input<typeof processInputSchema> = {
|
|
339
316
|
command: `bash -o pipefail -c ${quote(script)}`,
|
|
340
317
|
};
|
|
@@ -345,48 +322,14 @@ export function createShedRuns(sql: ShedSql, now: () => number = Date.now) {
|
|
|
345
322
|
async function dispatch(
|
|
346
323
|
start: z.output<typeof processInputSchema>,
|
|
347
324
|
logPath: string,
|
|
348
|
-
pass: GitCredential | null,
|
|
349
|
-
expiresAt: number | null,
|
|
350
325
|
) {
|
|
351
|
-
const remote = pass?.remote ?? null;
|
|
352
326
|
starting.set(requestKey, input.port ?? null);
|
|
353
327
|
try {
|
|
354
|
-
|
|
355
|
-
if (remote !== null && (expiresAt === null || expiresAt <= now()))
|
|
356
|
-
throw new Error(
|
|
357
|
-
`Workflow Git pass expired before process dispatch. Log: ${logPath}`,
|
|
358
|
-
);
|
|
359
|
-
}
|
|
360
|
-
checkLease();
|
|
361
|
-
if (pass !== null) {
|
|
362
|
-
if (!computer.write)
|
|
363
|
-
throw new Error("Computer credential file access is unavailable");
|
|
364
|
-
const content = credentialFile(pass);
|
|
365
|
-
const reply = await computer.write({
|
|
366
|
-
path: remotePath,
|
|
367
|
-
content,
|
|
368
|
-
});
|
|
369
|
-
const refused = shedComputerRefusalSchema.safeParse(reply);
|
|
370
|
-
if (refused.success) {
|
|
371
|
-
sql.exec(
|
|
372
|
-
"DELETE FROM shed_process_claims WHERE requestKey=?",
|
|
373
|
-
requestKey,
|
|
374
|
-
);
|
|
375
|
-
return refused.data;
|
|
376
|
-
}
|
|
377
|
-
const written = writeOutputSchema.parse(reply);
|
|
378
|
-
if (
|
|
379
|
-
written.path !== remotePath ||
|
|
380
|
-
written.bytes !== new TextEncoder().encode(content).byteLength
|
|
381
|
-
)
|
|
382
|
-
throw new Error("Invalid Computer credential file reply");
|
|
383
|
-
}
|
|
384
|
-
checkLease();
|
|
385
|
-
const reply = await computer.processStart(start);
|
|
328
|
+
const reply = await computerCall(() => computer.processStart(start));
|
|
386
329
|
const refused = shedComputerRefusalSchema.safeParse(reply);
|
|
387
330
|
if (refused.success) {
|
|
388
331
|
sql.exec(
|
|
389
|
-
"DELETE FROM
|
|
332
|
+
"DELETE FROM shed_native_process_claims WHERE requestKey=?",
|
|
390
333
|
requestKey,
|
|
391
334
|
);
|
|
392
335
|
return refused.data;
|
|
@@ -402,16 +345,6 @@ export function createShedRuns(sql: ShedSql, now: () => number = Date.now) {
|
|
|
402
345
|
}
|
|
403
346
|
const gitAccess =
|
|
404
347
|
input.gitAccess === undefined ? null : JSON.stringify(input.gitAccess);
|
|
405
|
-
async function mint() {
|
|
406
|
-
if (!computer.workflowGitPass)
|
|
407
|
-
throw new Error("Workflow Git pass binding is unavailable");
|
|
408
|
-
const pass = workflowGitPassResultSchema.parse(
|
|
409
|
-
await computer.workflowGitPass({}),
|
|
410
|
-
);
|
|
411
|
-
if (pass.expiresAt <= now() + 5000)
|
|
412
|
-
throw new Error("Workflow Git pass expires before the next poll");
|
|
413
|
-
return pass;
|
|
414
|
-
}
|
|
415
348
|
async function resume(prior: z.output<typeof rowSchema>) {
|
|
416
349
|
verify(prior, input, computerId);
|
|
417
350
|
if (prior.phase === "starting") {
|
|
@@ -426,25 +359,8 @@ export function createShedRuns(sql: ShedSql, now: () => number = Date.now) {
|
|
|
426
359
|
}
|
|
427
360
|
// The host binding retains this call's native idempotency key across retries.
|
|
428
361
|
if (mode === "start") {
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
if (prior.remote !== null) {
|
|
432
|
-
if (prior.expiresAt === null || prior.expiresAt <= now())
|
|
433
|
-
throw new Error(
|
|
434
|
-
`Workflow Git pass expired before process dispatch. Log: ${prior.logPath}`,
|
|
435
|
-
);
|
|
436
|
-
const renewed = await mint();
|
|
437
|
-
if (
|
|
438
|
-
renewed.passId !== prior.passId ||
|
|
439
|
-
renewed.remote !== prior.remote
|
|
440
|
-
)
|
|
441
|
-
throw new Error(
|
|
442
|
-
`Workflow Git renewal changed its original selector. Log: ${prior.logPath}`,
|
|
443
|
-
);
|
|
444
|
-
pass = renewed;
|
|
445
|
-
}
|
|
446
|
-
const prepared = prepare(pass);
|
|
447
|
-
return dispatch(prepared.start, prior.logPath, pass, prior.expiresAt);
|
|
362
|
+
const prepared = prepare();
|
|
363
|
+
return dispatch(prepared.start, prior.logPath);
|
|
448
364
|
}
|
|
449
365
|
throw new Error(
|
|
450
366
|
`Process start outcome is unavailable. Log: ${prior.logPath}`,
|
|
@@ -463,48 +379,21 @@ export function createShedRuns(sql: ShedSql, now: () => number = Date.now) {
|
|
|
463
379
|
"conflict",
|
|
464
380
|
"Run identity conflicts with its declared port",
|
|
465
381
|
);
|
|
466
|
-
if (mode === "run" && process.state !== "exited" && gitAccess !== null) {
|
|
467
|
-
if (prior.expiresAt === null || prior.expiresAt <= now())
|
|
468
|
-
throw new Error(`Workflow Git pass expired. Log: ${prior.logPath}`);
|
|
469
|
-
const pass = await mint();
|
|
470
|
-
if (
|
|
471
|
-
prior.expiresAt <= now() ||
|
|
472
|
-
pass.passId !== prior.passId ||
|
|
473
|
-
pass.remote !== prior.remote ||
|
|
474
|
-
pass.expiresAt < prior.expiresAt
|
|
475
|
-
)
|
|
476
|
-
throw new Error(
|
|
477
|
-
`Workflow Git renewal changed or expired its original selector. Log: ${prior.logPath}`,
|
|
478
|
-
);
|
|
479
|
-
sql.exec(
|
|
480
|
-
"UPDATE shed_process_claims SET expiresAt=MAX(expiresAt,?) WHERE requestKey=?",
|
|
481
|
-
pass.expiresAt,
|
|
482
|
-
requestKey,
|
|
483
|
-
);
|
|
484
|
-
}
|
|
485
382
|
return observe(requestKey, process, prior.logPath);
|
|
486
383
|
}
|
|
487
384
|
const prior = read(requestKey);
|
|
488
385
|
if (prior) return resume(prior);
|
|
489
|
-
|
|
490
|
-
const pass: WorkflowGitPassResult | null =
|
|
491
|
-
gitAccess === null ? null : await mint();
|
|
492
|
-
const claimed = read(requestKey);
|
|
493
|
-
if (claimed) return resume(claimed);
|
|
494
|
-
const { start, logPath } = prepare(pass);
|
|
386
|
+
const { start, logPath } = prepare();
|
|
495
387
|
sql.exec(
|
|
496
|
-
"INSERT INTO
|
|
388
|
+
"INSERT INTO shed_native_process_claims (requestKey,computerId,command,cwd,phase,logPath,gitAccess) VALUES (?,?,?,?,'starting',?,?)",
|
|
497
389
|
requestKey,
|
|
498
390
|
computerId,
|
|
499
391
|
input.command,
|
|
500
392
|
input.cwd ?? null,
|
|
501
393
|
logPath,
|
|
502
394
|
gitAccess,
|
|
503
|
-
pass?.passId ?? null,
|
|
504
|
-
pass?.remote ?? null,
|
|
505
|
-
pass?.expiresAt ?? null,
|
|
506
395
|
);
|
|
507
|
-
return dispatch(start, logPath
|
|
396
|
+
return dispatch(start, logPath);
|
|
508
397
|
}
|
|
509
398
|
return {
|
|
510
399
|
owns(processId: string, computerId: string, actor: HostActor) {
|
|
@@ -512,7 +401,7 @@ export function createShedRuns(sql: ShedSql, now: () => number = Date.now) {
|
|
|
512
401
|
return (
|
|
513
402
|
sql
|
|
514
403
|
.exec(
|
|
515
|
-
"SELECT requestKey FROM
|
|
404
|
+
"SELECT requestKey FROM shed_native_process_claims WHERE processId=? AND computerId=? AND json_extract(requestKey,'$[0]')=?",
|
|
516
405
|
processId,
|
|
517
406
|
computerId,
|
|
518
407
|
JSON.stringify(actor),
|