@ricsam/r5d-worker 0.0.125 → 0.0.127
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 +4 -0
- package/dist/cjs/browser-upload.cjs +73 -0
- package/dist/cjs/main.cjs +29 -7
- package/dist/cjs/package.json +1 -1
- package/dist/mjs/browser-upload.mjs +42 -0
- package/dist/mjs/main.mjs +29 -7
- package/dist/mjs/package.json +1 -1
- package/dist/types/browser-upload.d.ts +7 -0
- package/dist/types/main.d.ts +20 -2
- package/package.json +2 -1
package/README.md
CHANGED
|
@@ -94,3 +94,7 @@ Cancellation is checked again immediately before spawn. A `cancel` carrying `sco
|
|
|
94
94
|
When the connected `r5d-browser` requests a port forward, the worker opens each relayed connection only to `127.0.0.1` on the requested worker port. Browser-side and worker-side ports may differ. The worker never opens a public listener, and a disconnected worker leaves the browser's long-lived mapping unavailable until the same worker label reconnects.
|
|
95
95
|
|
|
96
96
|
Control execution requires an initialized launcher that explicitly declares its control capacity (or direct execution without an external launcher), an updated worker, and r5dctl installed on the worker's own PATH. Missing control support is an update error; a control request never falls back to a general shell. Deploy the app and worker support assets, update the CLI/worker, and reconnect before resuming paused work.
|
|
97
|
+
|
|
98
|
+
## Uploading files to the signed-in browser
|
|
99
|
+
|
|
100
|
+
`browser_upload_files` transfers regular files from this worker to the connected r5d-browser and attaches them to the selected file input in one agent operation. Source paths may be anywhere this worker can read, including outside the project; relative paths resolve from the session working directory. `$R5D_ROOT`, `$R5D_SESSION_ID`, and `~/` are expanded without executing a shell. File reads are bounded to 1 MiB per RPC, and source metadata is checked throughout the transfer. These transient reads and binary responses bypass recovery journals. Uploads interrupted by a disconnect must be retried as a new operation.
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __create = Object.create;
|
|
3
|
+
var __defProp = Object.defineProperty;
|
|
4
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
5
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
6
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
7
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
8
|
+
var __export = (target, all) => {
|
|
9
|
+
for (var name in all)
|
|
10
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
11
|
+
};
|
|
12
|
+
var __copyProps = (to, from, except, desc) => {
|
|
13
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
14
|
+
for (let key of __getOwnPropNames(from))
|
|
15
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
16
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
17
|
+
}
|
|
18
|
+
return to;
|
|
19
|
+
};
|
|
20
|
+
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
|
21
|
+
// If the importer is in node compatibility mode or this is not an ESM
|
|
22
|
+
// file that has been converted to a CommonJS file using a Babel-
|
|
23
|
+
// compatible transform (i.e. "__esModule" has not been set), then set
|
|
24
|
+
// "default" to the CommonJS "module.exports" for node compatibility.
|
|
25
|
+
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
|
26
|
+
mod
|
|
27
|
+
));
|
|
28
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
29
|
+
var browser_upload_exports = {};
|
|
30
|
+
__export(browser_upload_exports, {
|
|
31
|
+
readBrowserUploadChunk: () => readBrowserUploadChunk
|
|
32
|
+
});
|
|
33
|
+
module.exports = __toCommonJS(browser_upload_exports);
|
|
34
|
+
var import_promises = __toESM(require("node:fs/promises"), 1);
|
|
35
|
+
var import_node_fs = require("node:fs");
|
|
36
|
+
var import_node_os = __toESM(require("node:os"), 1);
|
|
37
|
+
var import_node_path = __toESM(require("node:path"), 1);
|
|
38
|
+
var import_browser_upload = require("@ricsam/r5d-api/browser-upload");
|
|
39
|
+
async function readBrowserUploadChunk(input, context) {
|
|
40
|
+
if (typeof input.filePath !== "string" || !input.filePath.trim() || input.filePath.includes("\0"))
|
|
41
|
+
throw new Error("Upload requires a file path.");
|
|
42
|
+
if (!Number.isSafeInteger(input.offset) || input.offset < 0) throw new Error("Invalid upload read offset.");
|
|
43
|
+
if (input.offset > 0 && !input.version) throw new Error("Upload continuation requires a file version.");
|
|
44
|
+
const expanded = input.filePath.replace(/\$\{R5D_ROOT\}|\$R5D_ROOT\b/g, () => context.rootDir).replace(/\$\{R5D_SESSION_ID\}|\$R5D_SESSION_ID\b/g, () => context.sessionId).replace(/^~\//, () => `${import_node_os.default.homedir()}/`);
|
|
45
|
+
const filePath = import_node_path.default.resolve(context.cwd, expanded);
|
|
46
|
+
const handle = await import_promises.default.open(filePath, import_node_fs.constants.O_RDONLY | import_node_fs.constants.O_NONBLOCK);
|
|
47
|
+
try {
|
|
48
|
+
const stat = await handle.stat({ bigint: true });
|
|
49
|
+
if (!stat.isFile()) throw new Error(`Upload source is not a regular file: ${input.filePath}`);
|
|
50
|
+
const metadata = (0, import_browser_upload.validateUploadFile)({ filename: import_node_path.default.basename(filePath), size: Number(stat.size) });
|
|
51
|
+
const versionOf = (s) => `${s.dev}:${s.ino}:${s.size}:${s.mtimeNs}:${s.ctimeNs}`;
|
|
52
|
+
const version = versionOf(stat);
|
|
53
|
+
if (input.version !== void 0 && input.version !== version)
|
|
54
|
+
throw new Error(`Upload source changed while being transferred: ${input.filePath}`);
|
|
55
|
+
if (input.offset > metadata.size) throw new Error("Upload offset is past the end of the file.");
|
|
56
|
+
const bytes = Buffer.alloc(input.metadataOnly === true ? 0 : Math.min(import_browser_upload.BROWSER_UPLOAD_CHUNK_BYTES, metadata.size - input.offset));
|
|
57
|
+
let read = 0;
|
|
58
|
+
while (read < bytes.length) {
|
|
59
|
+
const { bytesRead } = await handle.read(bytes, read, bytes.length - read, input.offset + read);
|
|
60
|
+
if (!bytesRead) throw new Error(`Upload source was truncated: ${input.filePath}`);
|
|
61
|
+
read += bytesRead;
|
|
62
|
+
}
|
|
63
|
+
if (versionOf(await handle.stat({ bigint: true })) !== version)
|
|
64
|
+
throw new Error(`Upload source changed while being read: ${input.filePath}`);
|
|
65
|
+
return { type: "browser_upload_read", ...metadata, version, offset: input.offset, base64: bytes.toString("base64") };
|
|
66
|
+
} finally {
|
|
67
|
+
await handle.close();
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
71
|
+
0 && (module.exports = {
|
|
72
|
+
readBrowserUploadChunk
|
|
73
|
+
});
|
package/dist/cjs/main.cjs
CHANGED
|
@@ -56,6 +56,7 @@ __export(main_exports, {
|
|
|
56
56
|
writeWorkerTextFile: () => writeWorkerTextFile
|
|
57
57
|
});
|
|
58
58
|
module.exports = __toCommonJS(main_exports);
|
|
59
|
+
var import_browser_upload = require("./browser-upload.cjs");
|
|
59
60
|
var import_runtime_version = require("./runtime-version.cjs");
|
|
60
61
|
var import_plan_paths = require("./plan-paths.cjs");
|
|
61
62
|
var import_plan_parser = require("./plan-parser.cjs");
|
|
@@ -2819,6 +2820,13 @@ async function executeOperation(input) {
|
|
|
2819
2820
|
return executeViewFileBytesOperation({ ...input, message: input.message });
|
|
2820
2821
|
case "code_list":
|
|
2821
2822
|
return executeCodeListOperation({ ...input, message: input.message });
|
|
2823
|
+
case "browser_upload_read":
|
|
2824
|
+
input.assertAdmission();
|
|
2825
|
+
return (0, import_browser_upload.readBrowserUploadChunk)(input.message.input, {
|
|
2826
|
+
cwd: input.resolvedTarget.rootPath,
|
|
2827
|
+
rootDir: input.rootDir,
|
|
2828
|
+
sessionId: input.message.sessionId
|
|
2829
|
+
});
|
|
2822
2830
|
case "code_read":
|
|
2823
2831
|
return executeCodeReadOperation({ ...input, message: input.message });
|
|
2824
2832
|
}
|
|
@@ -3006,6 +3014,7 @@ async function executeStreamingCommand(input) {
|
|
|
3006
3014
|
let timeout;
|
|
3007
3015
|
let spawnedProcess;
|
|
3008
3016
|
let preparationSettled = false;
|
|
3017
|
+
let stagedEnvFiles;
|
|
3009
3018
|
const settlePreparation = () => {
|
|
3010
3019
|
if (preparationSettled) return;
|
|
3011
3020
|
preparationSettled = true;
|
|
@@ -3034,7 +3043,13 @@ async function executeStreamingCommand(input) {
|
|
|
3034
3043
|
const interactive = input.message.interactive === true;
|
|
3035
3044
|
const control = input.message.commandClass === "control";
|
|
3036
3045
|
let argv = input.message.argv;
|
|
3037
|
-
|
|
3046
|
+
if (control && input.message.envFiles?.length) throw new Error("Control commands cannot receive secret files");
|
|
3047
|
+
stagedEnvFiles = stagePtyEnvFiles(input.message.envFiles);
|
|
3048
|
+
let environment = workerChildProcessEnvironment([
|
|
3049
|
+
githubProcessEnv(),
|
|
3050
|
+
resolvePtyEnvFileReferences(input.message.env ?? {}, stagedEnvFiles),
|
|
3051
|
+
targetProcessEnv2
|
|
3052
|
+
]);
|
|
3038
3053
|
if (control) {
|
|
3039
3054
|
(0, import_control_command_policy.assertControlCommandArgv)(argv);
|
|
3040
3055
|
if (interactive) throw new Error("Control commands cannot be interactive");
|
|
@@ -3169,6 +3184,7 @@ async function executeStreamingCommand(input) {
|
|
|
3169
3184
|
if (spawnedProcess) {
|
|
3170
3185
|
await reapCompletedCredentialBearingProcessGroup(input.message.runId, spawnedProcess);
|
|
3171
3186
|
}
|
|
3187
|
+
if (stagedEnvFiles) removePtyEnvFiles(stagedEnvFiles.paths);
|
|
3172
3188
|
await releaseWorkerCommandLaunch(input.resources);
|
|
3173
3189
|
activeProcesses.delete(input.message.runId);
|
|
3174
3190
|
cancelledProcessRuns.delete(input.message.runId);
|
|
@@ -3250,6 +3266,10 @@ function sendReplayWorkerMessage(ws, message) {
|
|
|
3250
3266
|
}
|
|
3251
3267
|
function sendWorkerMessage(ws, message) {
|
|
3252
3268
|
if (launcherFailureInProgress) return;
|
|
3269
|
+
if (message.type === "browser_upload_read_result") {
|
|
3270
|
+
if (currentWorkerSocket === ws && ws.readyState === WebSocket.OPEN) ws.send(JSON.stringify(message));
|
|
3271
|
+
return;
|
|
3272
|
+
}
|
|
3253
3273
|
const transportError = "error" in message && typeof message.error === "string" ? message.error : message.type === "workspace_sync_result" && message.result.outcome === "failed" ? message.result.error : void 0;
|
|
3254
3274
|
if (recoveryJournal && transportError && isWorkerCommunicationFailure(transportError) && "requestId" in message && typeof message.requestId === "string") {
|
|
3255
3275
|
recoveryJournal.unknown(message.requestId);
|
|
@@ -6036,8 +6056,10 @@ async function startWorker(options, projectRuntime = {
|
|
|
6036
6056
|
capabilities: {
|
|
6037
6057
|
updateClis: true,
|
|
6038
6058
|
browserPortForwarding: true,
|
|
6059
|
+
browserFileUploads: true,
|
|
6039
6060
|
execStdinV1: true,
|
|
6040
6061
|
ptyEnvFilesV1: true,
|
|
6062
|
+
execEnvFilesV1: true,
|
|
6041
6063
|
workspaceRemediationAncestorGuardV1: true,
|
|
6042
6064
|
workspaceIncidentConfigDeferralV1: true,
|
|
6043
6065
|
workspaceConfigResetToCanonicalV1: true,
|
|
@@ -6172,7 +6194,7 @@ async function startWorker(options, projectRuntime = {
|
|
|
6172
6194
|
if (!startupRecoveryComplete && message.type !== "ping") await startupRecoveryPromise;
|
|
6173
6195
|
if (!startupRecoveryComplete && message.type !== "ping") return;
|
|
6174
6196
|
if (currentWorkerSocket !== ws) return;
|
|
6175
|
-
const operationRequestId = "requestId" in message && typeof message.requestId === "string" && message.type !== "workspace_config" ? message.requestId : void 0;
|
|
6197
|
+
const operationRequestId = "requestId" in message && typeof message.requestId === "string" && message.type !== "workspace_config" && message.type !== "browser_upload_read" ? message.requestId : void 0;
|
|
6176
6198
|
if (message.type === "cancel") {
|
|
6177
6199
|
const admission = recoveryJournal.admit({ ...message, requestId: message.requestId });
|
|
6178
6200
|
const cancelled = await cancelProcessRun(message.runId, message.scope);
|
|
@@ -7078,10 +7100,10 @@ async function startWorker(options, projectRuntime = {
|
|
|
7078
7100
|
});
|
|
7079
7101
|
return;
|
|
7080
7102
|
}
|
|
7081
|
-
if (message.type === "read_plan" || message.type === "lint_plans" || message.type === "update_task_status" || message.type === "read" || message.type === "write" || message.type === "edit" || message.type === "grep" || message.type === "find" || message.type === "ls" || message.type === "view_file_bytes" || message.type === "code_list" || message.type === "code_read") {
|
|
7103
|
+
if (message.type === "read_plan" || message.type === "lint_plans" || message.type === "update_task_status" || message.type === "read" || message.type === "write" || message.type === "edit" || message.type === "grep" || message.type === "find" || message.type === "ls" || message.type === "view_file_bytes" || message.type === "code_list" || message.type === "code_read" || message.type === "browser_upload_read") {
|
|
7082
7104
|
if (workspaceOperationsAreFenced(deferredWorkspaceConfiguration, activeWorkspaceIncidentId)) {
|
|
7083
7105
|
sendWorkerMessage(ws, {
|
|
7084
|
-
type: "operation_result",
|
|
7106
|
+
type: message.type === "browser_upload_read" ? "browser_upload_read_result" : "operation_result",
|
|
7085
7107
|
requestId: message.requestId,
|
|
7086
7108
|
error: `Workspace operations are deferred for incident ${deferredWorkspaceConfiguration?.incidentId ?? activeWorkspaceIncidentId}; use a canonical remediation shell`
|
|
7087
7109
|
});
|
|
@@ -7109,7 +7131,7 @@ async function startWorker(options, projectRuntime = {
|
|
|
7109
7131
|
sendSerializedWorkerMessage(
|
|
7110
7132
|
ws,
|
|
7111
7133
|
JSON.stringify({
|
|
7112
|
-
type: "operation_result",
|
|
7134
|
+
type: message.type === "browser_upload_read" ? "browser_upload_read_result" : "operation_result",
|
|
7113
7135
|
requestId: message.requestId,
|
|
7114
7136
|
error: error instanceof Error ? error.message : String(error)
|
|
7115
7137
|
})
|
|
@@ -7137,7 +7159,7 @@ async function startWorker(options, projectRuntime = {
|
|
|
7137
7159
|
sendSerializedWorkerMessage(
|
|
7138
7160
|
ws,
|
|
7139
7161
|
JSON.stringify({
|
|
7140
|
-
type: "operation_result",
|
|
7162
|
+
type: message.type === "browser_upload_read" ? "browser_upload_read_result" : "operation_result",
|
|
7141
7163
|
requestId: message.requestId,
|
|
7142
7164
|
result
|
|
7143
7165
|
})
|
|
@@ -7155,7 +7177,7 @@ async function startWorker(options, projectRuntime = {
|
|
|
7155
7177
|
sendSerializedWorkerMessage(
|
|
7156
7178
|
ws,
|
|
7157
7179
|
JSON.stringify({
|
|
7158
|
-
type: "operation_result",
|
|
7180
|
+
type: message.type === "browser_upload_read" ? "browser_upload_read_result" : "operation_result",
|
|
7159
7181
|
requestId: message.requestId,
|
|
7160
7182
|
error: error instanceof Error ? error.message : String(error)
|
|
7161
7183
|
})
|
package/dist/cjs/package.json
CHANGED
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import fs from "node:fs/promises";
|
|
2
|
+
import { constants } from "node:fs";
|
|
3
|
+
import os from "node:os";
|
|
4
|
+
import path from "node:path";
|
|
5
|
+
import {
|
|
6
|
+
BROWSER_UPLOAD_CHUNK_BYTES,
|
|
7
|
+
validateUploadFile
|
|
8
|
+
} from "@ricsam/r5d-api/browser-upload";
|
|
9
|
+
async function readBrowserUploadChunk(input, context) {
|
|
10
|
+
if (typeof input.filePath !== "string" || !input.filePath.trim() || input.filePath.includes("\0"))
|
|
11
|
+
throw new Error("Upload requires a file path.");
|
|
12
|
+
if (!Number.isSafeInteger(input.offset) || input.offset < 0) throw new Error("Invalid upload read offset.");
|
|
13
|
+
if (input.offset > 0 && !input.version) throw new Error("Upload continuation requires a file version.");
|
|
14
|
+
const expanded = input.filePath.replace(/\$\{R5D_ROOT\}|\$R5D_ROOT\b/g, () => context.rootDir).replace(/\$\{R5D_SESSION_ID\}|\$R5D_SESSION_ID\b/g, () => context.sessionId).replace(/^~\//, () => `${os.homedir()}/`);
|
|
15
|
+
const filePath = path.resolve(context.cwd, expanded);
|
|
16
|
+
const handle = await fs.open(filePath, constants.O_RDONLY | constants.O_NONBLOCK);
|
|
17
|
+
try {
|
|
18
|
+
const stat = await handle.stat({ bigint: true });
|
|
19
|
+
if (!stat.isFile()) throw new Error(`Upload source is not a regular file: ${input.filePath}`);
|
|
20
|
+
const metadata = validateUploadFile({ filename: path.basename(filePath), size: Number(stat.size) });
|
|
21
|
+
const versionOf = (s) => `${s.dev}:${s.ino}:${s.size}:${s.mtimeNs}:${s.ctimeNs}`;
|
|
22
|
+
const version = versionOf(stat);
|
|
23
|
+
if (input.version !== void 0 && input.version !== version)
|
|
24
|
+
throw new Error(`Upload source changed while being transferred: ${input.filePath}`);
|
|
25
|
+
if (input.offset > metadata.size) throw new Error("Upload offset is past the end of the file.");
|
|
26
|
+
const bytes = Buffer.alloc(input.metadataOnly === true ? 0 : Math.min(BROWSER_UPLOAD_CHUNK_BYTES, metadata.size - input.offset));
|
|
27
|
+
let read = 0;
|
|
28
|
+
while (read < bytes.length) {
|
|
29
|
+
const { bytesRead } = await handle.read(bytes, read, bytes.length - read, input.offset + read);
|
|
30
|
+
if (!bytesRead) throw new Error(`Upload source was truncated: ${input.filePath}`);
|
|
31
|
+
read += bytesRead;
|
|
32
|
+
}
|
|
33
|
+
if (versionOf(await handle.stat({ bigint: true })) !== version)
|
|
34
|
+
throw new Error(`Upload source changed while being read: ${input.filePath}`);
|
|
35
|
+
return { type: "browser_upload_read", ...metadata, version, offset: input.offset, base64: bytes.toString("base64") };
|
|
36
|
+
} finally {
|
|
37
|
+
await handle.close();
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
export {
|
|
41
|
+
readBrowserUploadChunk
|
|
42
|
+
};
|
package/dist/mjs/main.mjs
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
#!/usr/bin/env bun
|
|
2
|
+
import { readBrowserUploadChunk } from "./browser-upload.mjs";
|
|
2
3
|
import { WORKER_RUNTIME_VERSION } from "./runtime-version.mjs";
|
|
3
4
|
import { getWorkerPlanPath, getWorkerPlansPath, validatePlanId } from "./plan-paths.mjs";
|
|
4
5
|
import { createPlanMarkdown, updateTaskCheckbox } from "./plan-parser.mjs";
|
|
@@ -2840,6 +2841,13 @@ async function executeOperation(input) {
|
|
|
2840
2841
|
return executeViewFileBytesOperation({ ...input, message: input.message });
|
|
2841
2842
|
case "code_list":
|
|
2842
2843
|
return executeCodeListOperation({ ...input, message: input.message });
|
|
2844
|
+
case "browser_upload_read":
|
|
2845
|
+
input.assertAdmission();
|
|
2846
|
+
return readBrowserUploadChunk(input.message.input, {
|
|
2847
|
+
cwd: input.resolvedTarget.rootPath,
|
|
2848
|
+
rootDir: input.rootDir,
|
|
2849
|
+
sessionId: input.message.sessionId
|
|
2850
|
+
});
|
|
2843
2851
|
case "code_read":
|
|
2844
2852
|
return executeCodeReadOperation({ ...input, message: input.message });
|
|
2845
2853
|
}
|
|
@@ -3027,6 +3035,7 @@ async function executeStreamingCommand(input) {
|
|
|
3027
3035
|
let timeout;
|
|
3028
3036
|
let spawnedProcess;
|
|
3029
3037
|
let preparationSettled = false;
|
|
3038
|
+
let stagedEnvFiles;
|
|
3030
3039
|
const settlePreparation = () => {
|
|
3031
3040
|
if (preparationSettled) return;
|
|
3032
3041
|
preparationSettled = true;
|
|
@@ -3055,7 +3064,13 @@ async function executeStreamingCommand(input) {
|
|
|
3055
3064
|
const interactive = input.message.interactive === true;
|
|
3056
3065
|
const control = input.message.commandClass === "control";
|
|
3057
3066
|
let argv = input.message.argv;
|
|
3058
|
-
|
|
3067
|
+
if (control && input.message.envFiles?.length) throw new Error("Control commands cannot receive secret files");
|
|
3068
|
+
stagedEnvFiles = stagePtyEnvFiles(input.message.envFiles);
|
|
3069
|
+
let environment = workerChildProcessEnvironment([
|
|
3070
|
+
githubProcessEnv(),
|
|
3071
|
+
resolvePtyEnvFileReferences(input.message.env ?? {}, stagedEnvFiles),
|
|
3072
|
+
targetProcessEnv2
|
|
3073
|
+
]);
|
|
3059
3074
|
if (control) {
|
|
3060
3075
|
assertControlCommandArgv(argv);
|
|
3061
3076
|
if (interactive) throw new Error("Control commands cannot be interactive");
|
|
@@ -3190,6 +3205,7 @@ async function executeStreamingCommand(input) {
|
|
|
3190
3205
|
if (spawnedProcess) {
|
|
3191
3206
|
await reapCompletedCredentialBearingProcessGroup(input.message.runId, spawnedProcess);
|
|
3192
3207
|
}
|
|
3208
|
+
if (stagedEnvFiles) removePtyEnvFiles(stagedEnvFiles.paths);
|
|
3193
3209
|
await releaseWorkerCommandLaunch(input.resources);
|
|
3194
3210
|
activeProcesses.delete(input.message.runId);
|
|
3195
3211
|
cancelledProcessRuns.delete(input.message.runId);
|
|
@@ -3271,6 +3287,10 @@ function sendReplayWorkerMessage(ws, message) {
|
|
|
3271
3287
|
}
|
|
3272
3288
|
function sendWorkerMessage(ws, message) {
|
|
3273
3289
|
if (launcherFailureInProgress) return;
|
|
3290
|
+
if (message.type === "browser_upload_read_result") {
|
|
3291
|
+
if (currentWorkerSocket === ws && ws.readyState === WebSocket.OPEN) ws.send(JSON.stringify(message));
|
|
3292
|
+
return;
|
|
3293
|
+
}
|
|
3274
3294
|
const transportError = "error" in message && typeof message.error === "string" ? message.error : message.type === "workspace_sync_result" && message.result.outcome === "failed" ? message.result.error : void 0;
|
|
3275
3295
|
if (recoveryJournal && transportError && isWorkerCommunicationFailure(transportError) && "requestId" in message && typeof message.requestId === "string") {
|
|
3276
3296
|
recoveryJournal.unknown(message.requestId);
|
|
@@ -6057,8 +6077,10 @@ async function startWorker(options, projectRuntime = {
|
|
|
6057
6077
|
capabilities: {
|
|
6058
6078
|
updateClis: true,
|
|
6059
6079
|
browserPortForwarding: true,
|
|
6080
|
+
browserFileUploads: true,
|
|
6060
6081
|
execStdinV1: true,
|
|
6061
6082
|
ptyEnvFilesV1: true,
|
|
6083
|
+
execEnvFilesV1: true,
|
|
6062
6084
|
workspaceRemediationAncestorGuardV1: true,
|
|
6063
6085
|
workspaceIncidentConfigDeferralV1: true,
|
|
6064
6086
|
workspaceConfigResetToCanonicalV1: true,
|
|
@@ -6193,7 +6215,7 @@ async function startWorker(options, projectRuntime = {
|
|
|
6193
6215
|
if (!startupRecoveryComplete && message.type !== "ping") await startupRecoveryPromise;
|
|
6194
6216
|
if (!startupRecoveryComplete && message.type !== "ping") return;
|
|
6195
6217
|
if (currentWorkerSocket !== ws) return;
|
|
6196
|
-
const operationRequestId = "requestId" in message && typeof message.requestId === "string" && message.type !== "workspace_config" ? message.requestId : void 0;
|
|
6218
|
+
const operationRequestId = "requestId" in message && typeof message.requestId === "string" && message.type !== "workspace_config" && message.type !== "browser_upload_read" ? message.requestId : void 0;
|
|
6197
6219
|
if (message.type === "cancel") {
|
|
6198
6220
|
const admission = recoveryJournal.admit({ ...message, requestId: message.requestId });
|
|
6199
6221
|
const cancelled = await cancelProcessRun(message.runId, message.scope);
|
|
@@ -7099,10 +7121,10 @@ async function startWorker(options, projectRuntime = {
|
|
|
7099
7121
|
});
|
|
7100
7122
|
return;
|
|
7101
7123
|
}
|
|
7102
|
-
if (message.type === "read_plan" || message.type === "lint_plans" || message.type === "update_task_status" || message.type === "read" || message.type === "write" || message.type === "edit" || message.type === "grep" || message.type === "find" || message.type === "ls" || message.type === "view_file_bytes" || message.type === "code_list" || message.type === "code_read") {
|
|
7124
|
+
if (message.type === "read_plan" || message.type === "lint_plans" || message.type === "update_task_status" || message.type === "read" || message.type === "write" || message.type === "edit" || message.type === "grep" || message.type === "find" || message.type === "ls" || message.type === "view_file_bytes" || message.type === "code_list" || message.type === "code_read" || message.type === "browser_upload_read") {
|
|
7103
7125
|
if (workspaceOperationsAreFenced(deferredWorkspaceConfiguration, activeWorkspaceIncidentId)) {
|
|
7104
7126
|
sendWorkerMessage(ws, {
|
|
7105
|
-
type: "operation_result",
|
|
7127
|
+
type: message.type === "browser_upload_read" ? "browser_upload_read_result" : "operation_result",
|
|
7106
7128
|
requestId: message.requestId,
|
|
7107
7129
|
error: `Workspace operations are deferred for incident ${deferredWorkspaceConfiguration?.incidentId ?? activeWorkspaceIncidentId}; use a canonical remediation shell`
|
|
7108
7130
|
});
|
|
@@ -7130,7 +7152,7 @@ async function startWorker(options, projectRuntime = {
|
|
|
7130
7152
|
sendSerializedWorkerMessage(
|
|
7131
7153
|
ws,
|
|
7132
7154
|
JSON.stringify({
|
|
7133
|
-
type: "operation_result",
|
|
7155
|
+
type: message.type === "browser_upload_read" ? "browser_upload_read_result" : "operation_result",
|
|
7134
7156
|
requestId: message.requestId,
|
|
7135
7157
|
error: error instanceof Error ? error.message : String(error)
|
|
7136
7158
|
})
|
|
@@ -7158,7 +7180,7 @@ async function startWorker(options, projectRuntime = {
|
|
|
7158
7180
|
sendSerializedWorkerMessage(
|
|
7159
7181
|
ws,
|
|
7160
7182
|
JSON.stringify({
|
|
7161
|
-
type: "operation_result",
|
|
7183
|
+
type: message.type === "browser_upload_read" ? "browser_upload_read_result" : "operation_result",
|
|
7162
7184
|
requestId: message.requestId,
|
|
7163
7185
|
result
|
|
7164
7186
|
})
|
|
@@ -7176,7 +7198,7 @@ async function startWorker(options, projectRuntime = {
|
|
|
7176
7198
|
sendSerializedWorkerMessage(
|
|
7177
7199
|
ws,
|
|
7178
7200
|
JSON.stringify({
|
|
7179
|
-
type: "operation_result",
|
|
7201
|
+
type: message.type === "browser_upload_read" ? "browser_upload_read_result" : "operation_result",
|
|
7180
7202
|
requestId: message.requestId,
|
|
7181
7203
|
error: error instanceof Error ? error.message : String(error)
|
|
7182
7204
|
})
|
package/dist/mjs/package.json
CHANGED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { type BrowserUploadChunk, type BrowserUploadReadInput } from "@ricsam/r5d-api/browser-upload";
|
|
2
|
+
/** Unlike code-view paths, uploads may read any regular file accessible to this worker. */
|
|
3
|
+
export declare function readBrowserUploadChunk(input: BrowserUploadReadInput, context: {
|
|
4
|
+
cwd: string;
|
|
5
|
+
rootDir: string;
|
|
6
|
+
sessionId: string;
|
|
7
|
+
}): Promise<BrowserUploadChunk>;
|
package/dist/types/main.d.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
#!/usr/bin/env bun
|
|
2
|
+
import type { BrowserUploadChunk, BrowserUploadReadInput } from "@ricsam/r5d-api/browser-upload";
|
|
2
3
|
import type { WorkerLintPlansResult, WorkerUpdatePlanTaskResult } from "./plan-paths";
|
|
3
4
|
import { WORKER_RESUMABLE_PROTOCOL, type WorkerRecoveryClientMessage, type WorkerRecoveryServerMessage } from "./recovery-protocol";
|
|
4
5
|
import { Database } from "bun:sqlite";
|
|
@@ -204,6 +205,11 @@ type WorkerClientMessage = WorkerRecoveryClientMessage | {
|
|
|
204
205
|
ptyId: string;
|
|
205
206
|
requestId?: string;
|
|
206
207
|
error: string;
|
|
208
|
+
} | {
|
|
209
|
+
type: "browser_upload_read_result";
|
|
210
|
+
requestId: string;
|
|
211
|
+
result?: WorkerOperationResult;
|
|
212
|
+
error?: string;
|
|
207
213
|
} | {
|
|
208
214
|
type: "operation_result";
|
|
209
215
|
requestId: string;
|
|
@@ -355,6 +361,12 @@ type WorkerServerMessage = WorkerRecoveryServerMessage | {
|
|
|
355
361
|
type: "project";
|
|
356
362
|
}>;
|
|
357
363
|
path: string;
|
|
364
|
+
} | {
|
|
365
|
+
type: "browser_upload_read";
|
|
366
|
+
requestId: string;
|
|
367
|
+
target: WorkerSessionTarget;
|
|
368
|
+
sessionId: string;
|
|
369
|
+
input: BrowserUploadReadInput;
|
|
358
370
|
} | {
|
|
359
371
|
type: "code_read";
|
|
360
372
|
requestId: string;
|
|
@@ -409,6 +421,12 @@ type WorkerServerMessage = WorkerRecoveryServerMessage | {
|
|
|
409
421
|
credentialId?: string;
|
|
410
422
|
cwd?: string;
|
|
411
423
|
env?: Record<string, string>;
|
|
424
|
+
envFiles?: Array<{
|
|
425
|
+
path: string;
|
|
426
|
+
content: string;
|
|
427
|
+
mode?: number;
|
|
428
|
+
}>;
|
|
429
|
+
accountEnvironmentWorkerId?: string;
|
|
412
430
|
timeoutMs?: number;
|
|
413
431
|
interactive?: boolean;
|
|
414
432
|
workspaceEffect?: "none";
|
|
@@ -639,9 +657,9 @@ type WorkerCodeReadResult = {
|
|
|
639
657
|
mtime: string;
|
|
640
658
|
base64: string;
|
|
641
659
|
};
|
|
642
|
-
type WorkerOperationResult = WorkerReadFileResult | WorkerReadPlanResult | WorkerLintPlansResult | WorkerUpdatePlanTaskResult | WorkerWriteFileResult | WorkerEditFileResult | WorkerGrepResult | WorkerFindResult | WorkerLsResult | WorkerViewFileBytesResult | WorkerExecStdinAck | WorkerCreateProjectBranchResult | WorkerDeleteProjectBranchResult | WorkerCodeListResult | WorkerCodeReadResult;
|
|
660
|
+
type WorkerOperationResult = WorkerReadFileResult | WorkerReadPlanResult | WorkerLintPlansResult | WorkerUpdatePlanTaskResult | WorkerWriteFileResult | WorkerEditFileResult | WorkerGrepResult | WorkerFindResult | WorkerLsResult | WorkerViewFileBytesResult | WorkerExecStdinAck | WorkerCreateProjectBranchResult | WorkerDeleteProjectBranchResult | WorkerCodeListResult | WorkerCodeReadResult | BrowserUploadChunk;
|
|
643
661
|
type WorkerOperationServerMessage = Extract<WorkerServerMessage, {
|
|
644
|
-
type: "read_plan" | "lint_plans" | "update_task_status" | "read" | "write" | "edit" | "grep" | "find" | "ls" | "view_file_bytes" | "code_list" | "code_read";
|
|
662
|
+
type: "read_plan" | "lint_plans" | "update_task_status" | "read" | "write" | "edit" | "grep" | "find" | "ls" | "view_file_bytes" | "code_list" | "code_read" | "browser_upload_read";
|
|
645
663
|
}>;
|
|
646
664
|
declare class WorkspaceSyncAdmissionChangedError extends Error {
|
|
647
665
|
readonly name = "WorkspaceSyncAdmissionChangedError";
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ricsam/r5d-worker",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.127",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"main": "./dist/cjs/main.cjs",
|
|
6
6
|
"module": "./dist/mjs/main.mjs",
|
|
@@ -21,6 +21,7 @@
|
|
|
21
21
|
"r5d-worker": "dist/cjs/main.cjs"
|
|
22
22
|
},
|
|
23
23
|
"dependencies": {
|
|
24
|
+
"@ricsam/r5d-api": "^0.0.127",
|
|
24
25
|
"node-pty": "^1.1.0",
|
|
25
26
|
"picomatch": "^4.0.3"
|
|
26
27
|
},
|