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