@rytuz/machine-access 0.1.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.
@@ -0,0 +1,263 @@
1
+ #!/usr/bin/env node
2
+ "use strict";
3
+
4
+ const crypto = require("crypto");
5
+ const fs = require("fs");
6
+ const path = require("path");
7
+ const { spawnSync } = require("child_process");
8
+
9
+ const RUNTIME_RELEASE = Object.freeze({
10
+ schemaVersion: "1.0",
11
+ version: "0.1.0-standard.1",
12
+ platform: "win32",
13
+ arch: "x64",
14
+ sha256: "036f1f0d9699b0ca06bda20afd5f702b3e62793ccf7e21c2ef4b244218583ea6",
15
+ size: 11137124,
16
+ relativePath: path.join(
17
+ "..",
18
+ "payload",
19
+ "windows-x64",
20
+ "rytuz-machine-access-standard.zip"
21
+ )
22
+ });
23
+
24
+ function fail(message) {
25
+ const error = new Error(message);
26
+ error.name = "RytuzBootstrapError";
27
+ throw error;
28
+ }
29
+
30
+ function stateRoot(env = process.env) {
31
+ const local =
32
+ typeof env.LOCALAPPDATA === "string" ? env.LOCALAPPDATA.trim() : "";
33
+ if (!local) fail("LOCALAPPDATA is required for Rytuz Standard");
34
+ return path.resolve(local, "Rytuz", "MachineAccess");
35
+ }
36
+
37
+ function validateRelease(release = RUNTIME_RELEASE) {
38
+ if (
39
+ release.schemaVersion !== "1.0" ||
40
+ release.platform !== "win32" ||
41
+ release.arch !== "x64" ||
42
+ typeof release.version !== "string" ||
43
+ !/^[0-9]+\.[0-9]+\.[0-9]+(?:-[A-Za-z0-9.-]+)?$/.test(release.version) ||
44
+ typeof release.sha256 !== "string" ||
45
+ !/^[a-f0-9]{64}$/.test(release.sha256) ||
46
+ !Number.isSafeInteger(release.size) ||
47
+ release.size <= 0 ||
48
+ release.size > 64 * 1024 * 1024
49
+ ) {
50
+ fail("bundled Rytuz runtime metadata is invalid");
51
+ }
52
+ return release;
53
+ }
54
+
55
+ function sha256File(filePath) {
56
+ const hash = crypto.createHash("sha256");
57
+ const fd = fs.openSync(filePath, "r");
58
+ const buffer = Buffer.allocUnsafe(1024 * 1024);
59
+ try {
60
+ for (;;) {
61
+ const count = fs.readSync(fd, buffer, 0, buffer.length, null);
62
+ if (count === 0) break;
63
+ hash.update(buffer.subarray(0, count));
64
+ }
65
+ } finally {
66
+ fs.closeSync(fd);
67
+ }
68
+ return hash.digest("hex");
69
+ }
70
+
71
+ function verifyPayload(filePath, release = RUNTIME_RELEASE) {
72
+ validateRelease(release);
73
+ if (!fs.existsSync(filePath) || !fs.statSync(filePath).isFile()) {
74
+ fail("bundled Rytuz runtime payload is missing");
75
+ }
76
+ const stat = fs.statSync(filePath);
77
+ if (stat.size !== release.size) {
78
+ fail("bundled Rytuz runtime payload size verification failed");
79
+ }
80
+ const digest = sha256File(filePath);
81
+ if (digest !== release.sha256) {
82
+ fail("bundled Rytuz runtime payload SHA-256 verification failed");
83
+ }
84
+ return filePath;
85
+ }
86
+
87
+ function bundledPayloadPath() {
88
+ return path.resolve(__dirname, RUNTIME_RELEASE.relativePath);
89
+ }
90
+
91
+ function powershellPath(env = process.env) {
92
+ const systemRoot = env.SystemRoot || env.SYSTEMROOT || "C:\\Windows";
93
+ return path.join(
94
+ systemRoot,
95
+ "System32",
96
+ "WindowsPowerShell",
97
+ "v1.0",
98
+ "powershell.exe"
99
+ );
100
+ }
101
+
102
+ function expandVerifiedPayload(zipPath, release, root, env = process.env) {
103
+ const runtimes = path.join(root, "runtime");
104
+ const target = path.join(runtimes, release.version);
105
+ const marker = path.join(target, ".rytuz-runtime.json");
106
+ fs.mkdirSync(runtimes, { recursive: true });
107
+
108
+ if (fs.existsSync(marker)) {
109
+ try {
110
+ const existing = JSON.parse(fs.readFileSync(marker, "utf8"));
111
+ if (
112
+ existing.version === release.version &&
113
+ existing.sha256 === release.sha256 &&
114
+ fs.existsSync(path.join(target, "python.exe")) &&
115
+ fs.existsSync(
116
+ path.join(target, "app", "oryx_access", "standard_agent.py")
117
+ )
118
+ ) {
119
+ return target;
120
+ }
121
+ } catch {
122
+ // Invalid/incomplete runtime is replaced below.
123
+ }
124
+ fs.rmSync(target, { recursive: true, force: true });
125
+ }
126
+
127
+ const staging = target + ".staging-" + process.pid;
128
+ fs.rmSync(staging, { recursive: true, force: true });
129
+ fs.mkdirSync(staging, { recursive: true });
130
+
131
+ const script = [
132
+ "$ErrorActionPreference = 'Stop'",
133
+ "Expand-Archive -LiteralPath $env:RYTUZ_ARCHIVE -DestinationPath $env:RYTUZ_DEST -Force"
134
+ ].join("\n");
135
+ const encoded = Buffer.from(script, "utf16le").toString("base64");
136
+ const child = spawnSync(
137
+ powershellPath(env),
138
+ ["-NoLogo", "-NoProfile", "-NonInteractive", "-EncodedCommand", encoded],
139
+ {
140
+ env: { ...env, RYTUZ_ARCHIVE: zipPath, RYTUZ_DEST: staging },
141
+ stdio: "pipe",
142
+ windowsHide: true
143
+ }
144
+ );
145
+ if (child.status !== 0) {
146
+ fs.rmSync(staging, { recursive: true, force: true });
147
+ fail("verified Rytuz runtime payload could not be expanded");
148
+ }
149
+
150
+ const python = path.join(staging, "python.exe");
151
+ const connectModule = path.join(
152
+ staging,
153
+ "app",
154
+ "oryx_access",
155
+ "standard_agent.py"
156
+ );
157
+ const runtimeMetadata = path.join(staging, "RYTUZ_RUNTIME.json");
158
+ if (
159
+ !fs.existsSync(python) ||
160
+ !fs.existsSync(connectModule) ||
161
+ !fs.existsSync(runtimeMetadata)
162
+ ) {
163
+ fs.rmSync(staging, { recursive: true, force: true });
164
+ fail("verified Rytuz runtime payload layout is invalid");
165
+ }
166
+
167
+ const metadata = JSON.parse(fs.readFileSync(runtimeMetadata, "utf8"));
168
+ if (
169
+ metadata.schema_version !== "1.0" ||
170
+ metadata.runtime_version !== release.version ||
171
+ metadata.platform !== "win32" ||
172
+ metadata.arch !== "x64"
173
+ ) {
174
+ fs.rmSync(staging, { recursive: true, force: true });
175
+ fail("verified Rytuz runtime identity is invalid");
176
+ }
177
+
178
+ fs.writeFileSync(
179
+ path.join(staging, ".rytuz-runtime.json"),
180
+ JSON.stringify(
181
+ {
182
+ schema_version: "1.0",
183
+ version: release.version,
184
+ sha256: release.sha256
185
+ },
186
+ null,
187
+ 2
188
+ ) + "\n",
189
+ { encoding: "utf8", flag: "wx" }
190
+ );
191
+ if (fs.existsSync(target)) {
192
+ fs.rmSync(target, { recursive: true, force: true });
193
+ }
194
+ fs.renameSync(staging, target);
195
+ return target;
196
+ }
197
+
198
+ function runConnect(runtime, root, env = process.env) {
199
+ const python = path.join(runtime, "python.exe");
200
+ const childEnv = { ...env };
201
+ delete childEnv.PYTHONPATH;
202
+ delete childEnv.PYTHONHOME;
203
+ childEnv.PYTHONNOUSERSITE = "1";
204
+ childEnv.PYTHONSAFEPATH = "1";
205
+ const child = spawnSync(
206
+ python,
207
+ ["-B", "-S", "-m", "oryx_access.standard_agent", "connect"],
208
+ {
209
+ cwd: runtime,
210
+ env: childEnv,
211
+ stdio: "inherit",
212
+ windowsHide: false
213
+ }
214
+ );
215
+ if (child.error) throw child.error;
216
+ if (child.status !== 0) {
217
+ fail("Rytuz Standard agent exited with code " + child.status);
218
+ }
219
+ }
220
+
221
+ async function main(argv = process.argv.slice(2)) {
222
+ if (argv.length !== 1 || argv[0] !== "connect") {
223
+ console.error("Usage: npx @rytuz/machine-access@latest connect");
224
+ return 2;
225
+ }
226
+ if (process.platform !== "win32" || process.arch !== "x64") {
227
+ fail("Rytuz Standard bootstrap currently supports Windows x64 only");
228
+ }
229
+
230
+ const release = validateRelease();
231
+ const root = stateRoot();
232
+ fs.mkdirSync(root, { recursive: true });
233
+
234
+ const payload = verifyPayload(bundledPayloadPath(), release);
235
+ const runtime = expandVerifiedPayload(payload, release, root);
236
+ runConnect(runtime, root);
237
+ return 0;
238
+ }
239
+
240
+ if (require.main === module) {
241
+ main().then(
242
+ (code) => {
243
+ process.exitCode = code;
244
+ },
245
+ (error) => {
246
+ console.error(
247
+ "Rytuz connect failed:",
248
+ error && error.message ? error.message : error
249
+ );
250
+ process.exitCode = 1;
251
+ }
252
+ );
253
+ }
254
+
255
+ module.exports = {
256
+ RUNTIME_RELEASE,
257
+ stateRoot,
258
+ validateRelease,
259
+ sha256File,
260
+ verifyPayload,
261
+ bundledPayloadPath,
262
+ powershellPath
263
+ };
package/package.json ADDED
@@ -0,0 +1,20 @@
1
+ {
2
+ "name": "@rytuz/machine-access",
3
+ "version": "0.1.0",
4
+ "description": "Rytuz Machine Access Standard user-space bootstrap",
5
+ "license": "UNLICENSED",
6
+ "private": false,
7
+ "bin": {
8
+ "rytuz-machine-access": "bin/rytuz-machine-access.js"
9
+ },
10
+ "files": [
11
+ "bin",
12
+ "payload"
13
+ ],
14
+ "engines": {
15
+ "node": ">=18"
16
+ },
17
+ "scripts": {
18
+ "test": "node --test"
19
+ }
20
+ }