opencara 0.100.0 → 0.100.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/dist/bin.js +176 -172
- package/package.json +1 -1
package/dist/bin.js
CHANGED
|
@@ -1,23 +1,76 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
|
+
// src/commands/run.ts
|
|
4
|
+
import {
|
|
5
|
+
arch,
|
|
6
|
+
cpus,
|
|
7
|
+
freemem,
|
|
8
|
+
hostname,
|
|
9
|
+
networkInterfaces,
|
|
10
|
+
platform,
|
|
11
|
+
release,
|
|
12
|
+
totalmem,
|
|
13
|
+
uptime
|
|
14
|
+
} from "node:os";
|
|
15
|
+
import { readFileSync as readFileSync2, statfsSync } from "node:fs";
|
|
16
|
+
import { dirname, join as join2 } from "node:path";
|
|
17
|
+
import { fileURLToPath } from "node:url";
|
|
18
|
+
|
|
19
|
+
// src/config/store.ts
|
|
20
|
+
import { readFileSync, writeFileSync, mkdirSync, existsSync, unlinkSync } from "node:fs";
|
|
21
|
+
import { z } from "zod";
|
|
22
|
+
|
|
23
|
+
// src/config/paths.ts
|
|
24
|
+
import { homedir } from "node:os";
|
|
25
|
+
import { join } from "node:path";
|
|
26
|
+
var CONFIG_DIR = join(homedir(), ".opencara");
|
|
27
|
+
var CONFIG_FILE = join(CONFIG_DIR, "config.json");
|
|
28
|
+
var DEFAULT_ORCHESTRATOR_URL = "https://opencara.com";
|
|
29
|
+
|
|
30
|
+
// src/config/store.ts
|
|
31
|
+
var ConfigSchema = z.object({
|
|
32
|
+
orchestratorUrl: z.string().url(),
|
|
33
|
+
token: z.string(),
|
|
34
|
+
agentHostId: z.string(),
|
|
35
|
+
deviceName: z.string()
|
|
36
|
+
});
|
|
37
|
+
function readConfig() {
|
|
38
|
+
if (!existsSync(CONFIG_FILE)) return null;
|
|
39
|
+
try {
|
|
40
|
+
return ConfigSchema.parse(JSON.parse(readFileSync(CONFIG_FILE, "utf8")));
|
|
41
|
+
} catch {
|
|
42
|
+
return null;
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
function writeConfig(cfg) {
|
|
46
|
+
if (!existsSync(CONFIG_DIR)) mkdirSync(CONFIG_DIR, { recursive: true, mode: 448 });
|
|
47
|
+
writeFileSync(CONFIG_FILE, JSON.stringify(cfg, null, 2), { mode: 384 });
|
|
48
|
+
}
|
|
49
|
+
function clearConfig() {
|
|
50
|
+
if (existsSync(CONFIG_FILE)) unlinkSync(CONFIG_FILE);
|
|
51
|
+
}
|
|
52
|
+
function defaultOrchestratorUrl() {
|
|
53
|
+
return process.env["OPENCARA_URL"] ?? DEFAULT_ORCHESTRATOR_URL;
|
|
54
|
+
}
|
|
55
|
+
|
|
3
56
|
// src/commands/register.ts
|
|
4
57
|
import { createHash, randomBytes } from "node:crypto";
|
|
5
58
|
import { spawn } from "node:child_process";
|
|
6
59
|
|
|
7
60
|
// ../shared/dist/events.js
|
|
8
|
-
import { z } from "zod";
|
|
9
|
-
var PlatformSchema =
|
|
10
|
-
var PlatformEventSchema =
|
|
11
|
-
id:
|
|
61
|
+
import { z as z2 } from "zod";
|
|
62
|
+
var PlatformSchema = z2.enum(["github"]);
|
|
63
|
+
var PlatformEventSchema = z2.object({
|
|
64
|
+
id: z2.string(),
|
|
12
65
|
platform: PlatformSchema,
|
|
13
|
-
type:
|
|
14
|
-
receivedAt:
|
|
15
|
-
payload:
|
|
66
|
+
type: z2.string(),
|
|
67
|
+
receivedAt: z2.string().datetime(),
|
|
68
|
+
payload: z2.unknown()
|
|
16
69
|
});
|
|
17
70
|
|
|
18
71
|
// ../shared/dist/agent.js
|
|
19
|
-
import { z as
|
|
20
|
-
var AgentRunStatusSchema =
|
|
72
|
+
import { z as z3 } from "zod";
|
|
73
|
+
var AgentRunStatusSchema = z3.enum([
|
|
21
74
|
"queued",
|
|
22
75
|
"assigned",
|
|
23
76
|
"running",
|
|
@@ -25,171 +78,134 @@ var AgentRunStatusSchema = z2.enum([
|
|
|
25
78
|
"failed",
|
|
26
79
|
"cancelled"
|
|
27
80
|
]);
|
|
28
|
-
var AgentSpecSchema =
|
|
29
|
-
kind:
|
|
30
|
-
command:
|
|
31
|
-
args:
|
|
32
|
-
env:
|
|
33
|
-
cwd:
|
|
81
|
+
var AgentSpecSchema = z3.object({
|
|
82
|
+
kind: z3.string(),
|
|
83
|
+
command: z3.string(),
|
|
84
|
+
args: z3.array(z3.string()).default([]),
|
|
85
|
+
env: z3.record(z3.string()).default({}),
|
|
86
|
+
cwd: z3.string().optional()
|
|
34
87
|
});
|
|
35
|
-
var AgentRunSchema =
|
|
36
|
-
id:
|
|
88
|
+
var AgentRunSchema = z3.object({
|
|
89
|
+
id: z3.string(),
|
|
37
90
|
spec: AgentSpecSchema,
|
|
38
|
-
triggerEventId:
|
|
91
|
+
triggerEventId: z3.string().optional(),
|
|
39
92
|
status: AgentRunStatusSchema,
|
|
40
|
-
hostId:
|
|
41
|
-
createdAt:
|
|
42
|
-
startedAt:
|
|
43
|
-
finishedAt:
|
|
44
|
-
exitCode:
|
|
93
|
+
hostId: z3.string().nullable(),
|
|
94
|
+
createdAt: z3.string().datetime(),
|
|
95
|
+
startedAt: z3.string().datetime().nullable(),
|
|
96
|
+
finishedAt: z3.string().datetime().nullable(),
|
|
97
|
+
exitCode: z3.number().int().nullable()
|
|
45
98
|
});
|
|
46
99
|
|
|
47
100
|
// ../shared/dist/host-protocol.js
|
|
48
|
-
import { z as
|
|
49
|
-
var PairingCreateRequestSchema =
|
|
50
|
-
device_secret_hash:
|
|
101
|
+
import { z as z4 } from "zod";
|
|
102
|
+
var PairingCreateRequestSchema = z4.object({
|
|
103
|
+
device_secret_hash: z4.string()
|
|
51
104
|
});
|
|
52
|
-
var PairingCreateResponseSchema =
|
|
53
|
-
code:
|
|
54
|
-
expires_at:
|
|
105
|
+
var PairingCreateResponseSchema = z4.object({
|
|
106
|
+
code: z4.string(),
|
|
107
|
+
expires_at: z4.string().datetime()
|
|
55
108
|
});
|
|
56
|
-
var PairingStatusResponseSchema =
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
status:
|
|
60
|
-
token:
|
|
61
|
-
agent_host_id:
|
|
62
|
-
device_name:
|
|
109
|
+
var PairingStatusResponseSchema = z4.union([
|
|
110
|
+
z4.object({ status: z4.literal("pending") }),
|
|
111
|
+
z4.object({
|
|
112
|
+
status: z4.literal("confirmed"),
|
|
113
|
+
token: z4.string(),
|
|
114
|
+
agent_host_id: z4.string(),
|
|
115
|
+
device_name: z4.string()
|
|
63
116
|
}),
|
|
64
|
-
|
|
117
|
+
z4.object({ status: z4.literal("expired") })
|
|
65
118
|
]);
|
|
66
|
-
var PairingConfirmRequestSchema =
|
|
67
|
-
device_name:
|
|
119
|
+
var PairingConfirmRequestSchema = z4.object({
|
|
120
|
+
device_name: z4.string().min(1)
|
|
68
121
|
});
|
|
69
|
-
var SystemInfoSchema =
|
|
70
|
-
os:
|
|
122
|
+
var SystemInfoSchema = z4.object({
|
|
123
|
+
os: z4.string(),
|
|
71
124
|
// os.platform()
|
|
72
|
-
release:
|
|
125
|
+
release: z4.string(),
|
|
73
126
|
// os.release()
|
|
74
|
-
arch:
|
|
127
|
+
arch: z4.string(),
|
|
75
128
|
// os.arch()
|
|
76
|
-
hostname:
|
|
77
|
-
cpu:
|
|
78
|
-
model:
|
|
79
|
-
cores:
|
|
80
|
-
speedMhz:
|
|
129
|
+
hostname: z4.string(),
|
|
130
|
+
cpu: z4.object({
|
|
131
|
+
model: z4.string(),
|
|
132
|
+
cores: z4.number().int().nonnegative(),
|
|
133
|
+
speedMhz: z4.number().int().nonnegative()
|
|
81
134
|
}),
|
|
82
|
-
memory:
|
|
83
|
-
totalBytes:
|
|
84
|
-
freeBytes:
|
|
135
|
+
memory: z4.object({
|
|
136
|
+
totalBytes: z4.number().nonnegative(),
|
|
137
|
+
freeBytes: z4.number().nonnegative()
|
|
85
138
|
}),
|
|
86
|
-
disk:
|
|
87
|
-
path:
|
|
88
|
-
totalBytes:
|
|
89
|
-
freeBytes:
|
|
139
|
+
disk: z4.object({
|
|
140
|
+
path: z4.string(),
|
|
141
|
+
totalBytes: z4.number().nonnegative(),
|
|
142
|
+
freeBytes: z4.number().nonnegative()
|
|
90
143
|
}).optional(),
|
|
91
|
-
ipAddrs:
|
|
92
|
-
uptimeSec:
|
|
144
|
+
ipAddrs: z4.array(z4.string()).default([]),
|
|
145
|
+
uptimeSec: z4.number().nonnegative()
|
|
93
146
|
});
|
|
94
|
-
var HelloMessageSchema =
|
|
95
|
-
type:
|
|
96
|
-
platform:
|
|
97
|
-
version:
|
|
98
|
-
capabilities:
|
|
147
|
+
var HelloMessageSchema = z4.object({
|
|
148
|
+
type: z4.literal("hello"),
|
|
149
|
+
platform: z4.string(),
|
|
150
|
+
version: z4.string(),
|
|
151
|
+
capabilities: z4.array(z4.string()).default([]),
|
|
99
152
|
systemInfo: SystemInfoSchema.optional()
|
|
100
153
|
});
|
|
101
|
-
var JobAssignmentSchema =
|
|
102
|
-
type:
|
|
154
|
+
var JobAssignmentSchema = z4.object({
|
|
155
|
+
type: z4.literal("job"),
|
|
103
156
|
run: AgentRunSchema,
|
|
104
157
|
spec: AgentSpecSchema,
|
|
105
|
-
stdinJson:
|
|
158
|
+
stdinJson: z4.unknown().optional()
|
|
106
159
|
});
|
|
107
|
-
var LogFrameSchema =
|
|
108
|
-
type:
|
|
109
|
-
runId:
|
|
110
|
-
seq:
|
|
111
|
-
stream:
|
|
112
|
-
chunk:
|
|
160
|
+
var LogFrameSchema = z4.object({
|
|
161
|
+
type: z4.literal("log"),
|
|
162
|
+
runId: z4.string(),
|
|
163
|
+
seq: z4.number().int().min(0),
|
|
164
|
+
stream: z4.enum(["stdout", "stderr"]),
|
|
165
|
+
chunk: z4.string()
|
|
113
166
|
});
|
|
114
|
-
var RunDoneSchema =
|
|
115
|
-
type:
|
|
116
|
-
runId:
|
|
117
|
-
status:
|
|
118
|
-
exitCode:
|
|
119
|
-
errorMessage:
|
|
167
|
+
var RunDoneSchema = z4.object({
|
|
168
|
+
type: z4.literal("done"),
|
|
169
|
+
runId: z4.string(),
|
|
170
|
+
status: z4.enum(["succeeded", "failed", "cancelled"]),
|
|
171
|
+
exitCode: z4.number().int().nullable().optional(),
|
|
172
|
+
errorMessage: z4.string().optional()
|
|
120
173
|
});
|
|
121
|
-
var HelloAckSchema =
|
|
122
|
-
type:
|
|
123
|
-
agentHostId:
|
|
124
|
-
deviceName:
|
|
174
|
+
var HelloAckSchema = z4.object({
|
|
175
|
+
type: z4.literal("hello-ack"),
|
|
176
|
+
agentHostId: z4.string(),
|
|
177
|
+
deviceName: z4.string()
|
|
125
178
|
});
|
|
126
|
-
var PingSchema =
|
|
127
|
-
var PongSchema =
|
|
128
|
-
var ServerToDeviceMessageSchema =
|
|
179
|
+
var PingSchema = z4.object({ type: z4.literal("ping") });
|
|
180
|
+
var PongSchema = z4.object({ type: z4.literal("pong") });
|
|
181
|
+
var ServerToDeviceMessageSchema = z4.discriminatedUnion("type", [
|
|
129
182
|
JobAssignmentSchema,
|
|
130
183
|
HelloAckSchema,
|
|
131
184
|
PingSchema
|
|
132
185
|
]);
|
|
133
|
-
var DeviceToServerMessageSchema =
|
|
186
|
+
var DeviceToServerMessageSchema = z4.discriminatedUnion("type", [
|
|
134
187
|
HelloMessageSchema,
|
|
135
188
|
LogFrameSchema,
|
|
136
189
|
RunDoneSchema,
|
|
137
190
|
PongSchema
|
|
138
191
|
]);
|
|
139
|
-
var HostRegisterRequestSchema =
|
|
140
|
-
hostId:
|
|
141
|
-
hostName:
|
|
142
|
-
capabilities:
|
|
143
|
-
token:
|
|
192
|
+
var HostRegisterRequestSchema = z4.object({
|
|
193
|
+
hostId: z4.string(),
|
|
194
|
+
hostName: z4.string(),
|
|
195
|
+
capabilities: z4.array(z4.string()).default([]),
|
|
196
|
+
token: z4.string()
|
|
144
197
|
});
|
|
145
|
-
var HostRegisterResponseSchema =
|
|
146
|
-
ok:
|
|
147
|
-
pollIntervalMs:
|
|
198
|
+
var HostRegisterResponseSchema = z4.object({
|
|
199
|
+
ok: z4.literal(true),
|
|
200
|
+
pollIntervalMs: z4.number().int().positive()
|
|
148
201
|
});
|
|
149
202
|
|
|
150
|
-
// src/config/store.ts
|
|
151
|
-
import { readFileSync, writeFileSync, mkdirSync, existsSync, unlinkSync } from "node:fs";
|
|
152
|
-
import { z as z4 } from "zod";
|
|
153
|
-
|
|
154
|
-
// src/config/paths.ts
|
|
155
|
-
import { homedir } from "node:os";
|
|
156
|
-
import { join } from "node:path";
|
|
157
|
-
var CONFIG_DIR = join(homedir(), ".opencara");
|
|
158
|
-
var CONFIG_FILE = join(CONFIG_DIR, "config.json");
|
|
159
|
-
var DEFAULT_ORCHESTRATOR_URL = "https://opencara.com";
|
|
160
|
-
|
|
161
|
-
// src/config/store.ts
|
|
162
|
-
var ConfigSchema = z4.object({
|
|
163
|
-
orchestratorUrl: z4.string().url(),
|
|
164
|
-
token: z4.string(),
|
|
165
|
-
agentHostId: z4.string(),
|
|
166
|
-
deviceName: z4.string()
|
|
167
|
-
});
|
|
168
|
-
function readConfig() {
|
|
169
|
-
if (!existsSync(CONFIG_FILE)) return null;
|
|
170
|
-
try {
|
|
171
|
-
return ConfigSchema.parse(JSON.parse(readFileSync(CONFIG_FILE, "utf8")));
|
|
172
|
-
} catch {
|
|
173
|
-
return null;
|
|
174
|
-
}
|
|
175
|
-
}
|
|
176
|
-
function writeConfig(cfg) {
|
|
177
|
-
if (!existsSync(CONFIG_DIR)) mkdirSync(CONFIG_DIR, { recursive: true, mode: 448 });
|
|
178
|
-
writeFileSync(CONFIG_FILE, JSON.stringify(cfg, null, 2), { mode: 384 });
|
|
179
|
-
}
|
|
180
|
-
function clearConfig() {
|
|
181
|
-
if (existsSync(CONFIG_FILE)) unlinkSync(CONFIG_FILE);
|
|
182
|
-
}
|
|
183
|
-
function defaultOrchestratorUrl() {
|
|
184
|
-
return process.env["OPENCARA_URL"] ?? DEFAULT_ORCHESTRATOR_URL;
|
|
185
|
-
}
|
|
186
|
-
|
|
187
203
|
// src/commands/register.ts
|
|
188
204
|
var POLL_INTERVAL_MS = 2e3;
|
|
189
205
|
async function register(opts = {}) {
|
|
190
206
|
const orchestratorUrl = opts.url ?? defaultOrchestratorUrl();
|
|
191
|
-
if (!opts.
|
|
192
|
-
console.log("Already paired. Use --force to re-pair.");
|
|
207
|
+
if (!opts.forcePair && readConfig()) {
|
|
208
|
+
console.log("Already paired. Use --force-pair to re-pair.");
|
|
193
209
|
return;
|
|
194
210
|
}
|
|
195
211
|
const deviceSecret = randomBytes(32).toString("base64url");
|
|
@@ -237,7 +253,6 @@ async function register(opts = {}) {
|
|
|
237
253
|
console.log(`
|
|
238
254
|
|
|
239
255
|
\u2713 Paired as "${result.device_name}".`);
|
|
240
|
-
console.log(` Run 'opencara run' to start accepting jobs.`);
|
|
241
256
|
return;
|
|
242
257
|
}
|
|
243
258
|
throw new Error("pairing timed out");
|
|
@@ -255,22 +270,6 @@ function sleep(ms) {
|
|
|
255
270
|
return new Promise((r) => setTimeout(r, ms));
|
|
256
271
|
}
|
|
257
272
|
|
|
258
|
-
// src/commands/run.ts
|
|
259
|
-
import {
|
|
260
|
-
arch,
|
|
261
|
-
cpus,
|
|
262
|
-
freemem,
|
|
263
|
-
hostname,
|
|
264
|
-
networkInterfaces,
|
|
265
|
-
platform,
|
|
266
|
-
release,
|
|
267
|
-
totalmem,
|
|
268
|
-
uptime
|
|
269
|
-
} from "node:os";
|
|
270
|
-
import { readFileSync as readFileSync2, statfsSync } from "node:fs";
|
|
271
|
-
import { dirname, join as join2 } from "node:path";
|
|
272
|
-
import { fileURLToPath } from "node:url";
|
|
273
|
-
|
|
274
273
|
// src/transport/ws-client.ts
|
|
275
274
|
import WebSocket from "ws";
|
|
276
275
|
var HEARTBEAT_MS = 3e4;
|
|
@@ -373,10 +372,12 @@ var __dirname = dirname(fileURLToPath(import.meta.url));
|
|
|
373
372
|
var PKG_VERSION = readPkgVersion();
|
|
374
373
|
var LOG_FLUSH_MS = 800;
|
|
375
374
|
var MAX_CHUNK_SIZE = 4 * 1024;
|
|
376
|
-
async function run() {
|
|
377
|
-
|
|
378
|
-
if (!cfg) {
|
|
379
|
-
|
|
375
|
+
async function run(opts = {}) {
|
|
376
|
+
let cfg = readConfig();
|
|
377
|
+
if (!cfg || opts.forcePair) {
|
|
378
|
+
await register({ url: opts.url, forcePair: opts.forcePair });
|
|
379
|
+
cfg = readConfig();
|
|
380
|
+
if (!cfg) throw new Error("pairing did not save a config");
|
|
380
381
|
}
|
|
381
382
|
const wsUrl = cfg.orchestratorUrl.replace(/^http/, "ws") + "/api/devices/ws";
|
|
382
383
|
const client = new WsClient({
|
|
@@ -512,7 +513,7 @@ function collectSystemInfo() {
|
|
|
512
513
|
async function status() {
|
|
513
514
|
const cfg = readConfig();
|
|
514
515
|
if (!cfg) {
|
|
515
|
-
console.log("Not paired. Run 'opencara
|
|
516
|
+
console.log("Not paired. Run 'opencara' to pair and start accepting jobs.");
|
|
516
517
|
return;
|
|
517
518
|
}
|
|
518
519
|
console.log(`Paired:`);
|
|
@@ -551,17 +552,11 @@ async function logout() {
|
|
|
551
552
|
|
|
552
553
|
// src/bin.ts
|
|
553
554
|
async function main() {
|
|
554
|
-
const
|
|
555
|
+
const argv = process.argv.slice(2);
|
|
556
|
+
const cmd = argv[0];
|
|
555
557
|
switch (cmd) {
|
|
556
|
-
case
|
|
557
|
-
|
|
558
|
-
force: rest.includes("--force"),
|
|
559
|
-
url: pickFlag(rest, "--url")
|
|
560
|
-
});
|
|
561
|
-
return;
|
|
562
|
-
case "run":
|
|
563
|
-
await run();
|
|
564
|
-
return;
|
|
558
|
+
case void 0:
|
|
559
|
+
break;
|
|
565
560
|
case "status":
|
|
566
561
|
await status();
|
|
567
562
|
return;
|
|
@@ -570,14 +565,19 @@ async function main() {
|
|
|
570
565
|
return;
|
|
571
566
|
case "--help":
|
|
572
567
|
case "-h":
|
|
573
|
-
case void 0:
|
|
574
568
|
printHelp();
|
|
575
569
|
return;
|
|
576
570
|
default:
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
571
|
+
if (!cmd.startsWith("-")) {
|
|
572
|
+
console.error(`unknown command: ${cmd}`);
|
|
573
|
+
printHelp();
|
|
574
|
+
process.exit(1);
|
|
575
|
+
}
|
|
580
576
|
}
|
|
577
|
+
await run({
|
|
578
|
+
forcePair: argv.includes("--force-pair"),
|
|
579
|
+
url: pickFlag(argv, "--url")
|
|
580
|
+
});
|
|
581
581
|
}
|
|
582
582
|
function pickFlag(argv, name) {
|
|
583
583
|
const i = argv.indexOf(name);
|
|
@@ -588,10 +588,14 @@ function printHelp() {
|
|
|
588
588
|
console.log(`opencara \u2014 agent host CLI
|
|
589
589
|
|
|
590
590
|
Usage:
|
|
591
|
-
opencara
|
|
592
|
-
opencara
|
|
593
|
-
opencara
|
|
594
|
-
|
|
591
|
+
opencara [--url URL] [--force-pair] Pair (if needed) and start accepting jobs.
|
|
592
|
+
opencara status Show pairing state.
|
|
593
|
+
opencara logout Forget the saved pairing.
|
|
594
|
+
|
|
595
|
+
Options:
|
|
596
|
+
--url URL Orchestrator URL (default: https://opencara.com,
|
|
597
|
+
or $OPENCARA_URL).
|
|
598
|
+
--force-pair Re-pair even if already paired.
|
|
595
599
|
`);
|
|
596
600
|
}
|
|
597
601
|
main().catch((err) => {
|