@sys9/chord-cli 0.1.159-linux-x64 → 0.1.159

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/bin/chord ADDED
@@ -0,0 +1,610 @@
1
+ #!/usr/bin/env node
2
+
3
+ "use strict";
4
+
5
+ const { spawn } = require("node:child_process");
6
+ const fs = require("node:fs");
7
+ const path = require("node:path");
8
+
9
+ const packageJSON = require("../package.json");
10
+ const {
11
+ resolvePackagedBinaryPath,
12
+ resolvePlatformPackageName,
13
+ } = require("../lib/platform");
14
+
15
+ function detectPackageManager() {
16
+ const userAgent = process.env.npm_config_user_agent || "";
17
+ if (/\bbun\//.test(userAgent)) {
18
+ return "bun";
19
+ }
20
+
21
+ const execPath = process.env.npm_execpath || "";
22
+ if (execPath.includes("bun")) {
23
+ return "bun";
24
+ }
25
+
26
+ if (
27
+ __dirname.includes(".bun/install/global") ||
28
+ __dirname.includes(".bun\\install\\global")
29
+ ) {
30
+ return "bun";
31
+ }
32
+
33
+ return "npm";
34
+ }
35
+
36
+ function resolveBinaryPath() {
37
+ const platformPackage = resolvePlatformPackageName(packageJSON.name);
38
+ try {
39
+ const packageJSONPath = require.resolve(platformPackage + "/package.json");
40
+ return {
41
+ binaryPath: resolvePackagedBinaryPath(path.dirname(packageJSONPath)),
42
+ platformPackage,
43
+ };
44
+ } catch {
45
+ return {
46
+ binaryPath: resolvePackagedBinaryPath(path.join(__dirname, "..")),
47
+ platformPackage,
48
+ };
49
+ }
50
+ }
51
+
52
+ const updateRequestedExitCode = 75;
53
+ const updateFDEnv = "CHORD_CLI_UPDATE_FD";
54
+ const updateAttemptEnv = "CHORD_CLI_UPDATE_ATTEMPT_ID";
55
+ const updateOutcomeEnv = "CHORD_CLI_UPDATE_OUTCOME";
56
+ const updateErrorEnv = "CHORD_CLI_UPDATE_ERROR";
57
+ const updateReadyFileEnv = "CHORD_CLI_UPDATE_READY_FILE";
58
+ const updateReadyTimeoutEnv = "CHORD_CLI_UPDATE_READY_TIMEOUT_MS";
59
+ const updateRetryInitialEnv = "CHORD_CLI_UPDATE_RETRY_INITIAL_MS";
60
+ const updateRetryMaxEnv = "CHORD_CLI_UPDATE_RETRY_MAX_MS";
61
+ const maxUpdateInstructionBytes = 4096;
62
+ const defaultUpdateReadyTimeoutMs = 60_000;
63
+ const defaultUpdateRetryInitialMs = 1_000;
64
+ const defaultUpdateRetryMaxMs = 300_000;
65
+ const activeChildren = new Set();
66
+ let receivedSignal = null;
67
+
68
+ const forwardSignal = (signal) => {
69
+ receivedSignal = signal;
70
+ for (const child of activeChildren) {
71
+ if (child.killed) {
72
+ continue;
73
+ }
74
+ try {
75
+ child.kill(signal);
76
+ } catch {
77
+ // Ignore failures while the child is already exiting.
78
+ }
79
+ }
80
+ };
81
+
82
+ ["SIGINT", "SIGTERM", "SIGHUP"].forEach((signal) => {
83
+ process.on(signal, () => forwardSignal(signal));
84
+ });
85
+
86
+ function runChild(command, args, options) {
87
+ let spawned;
88
+ const completion = new Promise((resolve) => {
89
+ let updateInstruction = "";
90
+ let settled = false;
91
+ let childResult = null;
92
+ let updatePipeClosed = true;
93
+ let updateInstructionComplete = false;
94
+ const finish = (result) => {
95
+ if (settled) {
96
+ return;
97
+ }
98
+ settled = true;
99
+ activeChildren.delete(spawned);
100
+ resolve(result);
101
+ };
102
+ const finishAfterUpdatePipe = () => {
103
+ if (childResult && (updatePipeClosed || updateInstructionComplete)) {
104
+ finish({ ...childResult, updateInstruction });
105
+ }
106
+ };
107
+ spawned = spawn(command, args, options);
108
+ activeChildren.add(spawned);
109
+ if (spawned.stdio[3]) {
110
+ updatePipeClosed = false;
111
+ spawned.stdio[3].setEncoding("utf8");
112
+ spawned.stdio[3].on("data", (chunk) => {
113
+ if (updateInstruction.length <= maxUpdateInstructionBytes) {
114
+ updateInstruction += chunk;
115
+ }
116
+ updateInstructionComplete = updateInstruction.includes("\n");
117
+ finishAfterUpdatePipe();
118
+ });
119
+ spawned.stdio[3].on("end", () => {
120
+ updatePipeClosed = true;
121
+ finishAfterUpdatePipe();
122
+ });
123
+ }
124
+ spawned.on("error", (error) => {
125
+ finish({ code: 1, signal: null, updateInstruction: "", error });
126
+ });
127
+ spawned.on("exit", (code, signal) => {
128
+ childResult = { code: code ?? 1, signal, error: null };
129
+ // A daemon descendant may temporarily retain the update pipe. Once the
130
+ // direct child supplies the complete newline-delimited update instruction,
131
+ // waiting for every inherited file descriptor would keep replacement stuck
132
+ // for that descendant's lifetime.
133
+ if (signal || receivedSignal) {
134
+ finish({ code: code ?? 1, signal, updateInstruction, error: null });
135
+ return;
136
+ }
137
+ finishAfterUpdatePipe();
138
+ });
139
+ spawned.on("close", (code, signal) => {
140
+ childResult = { code: code ?? 1, signal, error: null };
141
+ finishAfterUpdatePipe();
142
+ });
143
+ });
144
+ return { process: spawned, completion };
145
+ }
146
+
147
+ function parseUpdateInstruction(raw) {
148
+ if (!raw || raw.length > maxUpdateInstructionBytes) {
149
+ return null;
150
+ }
151
+ try {
152
+ const instruction = JSON.parse(raw);
153
+ const version = instruction && instruction.cli_version;
154
+ if (typeof version !== "string" || !/^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)$/.test(version)) {
155
+ return null;
156
+ }
157
+ const attemptID = instruction.attempt_id;
158
+ const dataDir = instruction.data_dir;
159
+ const fromVersion = instruction.from_version;
160
+ const hostID = instruction.host_id;
161
+ if (
162
+ typeof attemptID !== "string" ||
163
+ !/^[A-Za-z0-9_-]{1,128}$/.test(attemptID) ||
164
+ typeof dataDir !== "string" ||
165
+ !path.isAbsolute(dataDir) ||
166
+ typeof fromVersion !== "string" ||
167
+ !/^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)$/.test(fromVersion) ||
168
+ typeof hostID !== "string" ||
169
+ !hostID ||
170
+ hostID.length > 128
171
+ ) {
172
+ return null;
173
+ }
174
+ return { cliVersion: version, attemptID, dataDir, fromVersion, hostID };
175
+ } catch {
176
+ return null;
177
+ }
178
+ }
179
+
180
+ function updateCommand(packageManager, cliVersion, daemonArgs) {
181
+ const packageSpec = packageJSON.name + "@" + cliVersion;
182
+ if (packageManager === "bun") {
183
+ return { command: "bunx", args: [packageSpec, ...daemonArgs] };
184
+ }
185
+ return { command: "npx", args: ["--yes", packageSpec, ...daemonArgs] };
186
+ }
187
+
188
+ function updateReadyTimeoutMs() {
189
+ const configured = Number(process.env[updateReadyTimeoutEnv]);
190
+ if (Number.isInteger(configured) && configured >= 100 && configured <= 300_000) {
191
+ return configured;
192
+ }
193
+ return defaultUpdateReadyTimeoutMs;
194
+ }
195
+
196
+ function configuredMilliseconds(name, fallback) {
197
+ const configured = Number(process.env[name]);
198
+ if (Number.isInteger(configured) && configured >= 10 && configured <= 300_000) {
199
+ return configured;
200
+ }
201
+ return fallback;
202
+ }
203
+
204
+ function updateRetryInitialMs() {
205
+ return configuredMilliseconds(updateRetryInitialEnv, defaultUpdateRetryInitialMs);
206
+ }
207
+
208
+ function updateRetryMaxMs() {
209
+ return Math.max(
210
+ updateRetryInitialMs(),
211
+ configuredMilliseconds(updateRetryMaxEnv, defaultUpdateRetryMaxMs),
212
+ );
213
+ }
214
+
215
+ function monitorReadiness(readyFile, instruction, outcome) {
216
+ let timer = null;
217
+ let interval = null;
218
+ let settled = false;
219
+ let resolvePromise;
220
+ const finish = (result) => {
221
+ if (settled) {
222
+ return;
223
+ }
224
+ settled = true;
225
+ clearTimeout(timer);
226
+ clearInterval(interval);
227
+ resolvePromise(result);
228
+ };
229
+ const promise = new Promise((resolve) => {
230
+ resolvePromise = resolve;
231
+ const inspect = () => {
232
+ let record;
233
+ try {
234
+ record = JSON.parse(fs.readFileSync(readyFile, "utf8"));
235
+ } catch {
236
+ return;
237
+ }
238
+ if (
239
+ record.attempt_id === instruction.attemptID &&
240
+ record.version === (outcome === "target" ? instruction.cliVersion : instruction.fromVersion) &&
241
+ record.outcome === outcome &&
242
+ record.host_id === instruction.hostID
243
+ ) {
244
+ finish({ kind: "ready" });
245
+ }
246
+ };
247
+ interval = setInterval(inspect, 25);
248
+ timer = setTimeout(() => finish({ kind: "timeout" }), updateReadyTimeoutMs());
249
+ inspect();
250
+ });
251
+ return { promise, cancel: () => finish({ kind: "cancelled" }) };
252
+ }
253
+
254
+ function snapshotDaemonState(instruction) {
255
+ const updatesRoot = path.join(instruction.dataDir, ".updates");
256
+ const attemptDir = path.join(updatesRoot, instruction.attemptID);
257
+ fs.mkdirSync(updatesRoot, { recursive: true, mode: 0o700 });
258
+ fs.mkdirSync(attemptDir, { mode: 0o700 });
259
+ const database = path.join(instruction.dataDir, "queue.db");
260
+ const backup = path.join(attemptDir, "queue.db");
261
+ const databaseExisted = fs.existsSync(database);
262
+ if (databaseExisted) {
263
+ fs.copyFileSync(database, backup);
264
+ const backupFile = fs.openSync(backup, "r");
265
+ try {
266
+ fs.fsyncSync(backupFile);
267
+ } finally {
268
+ fs.closeSync(backupFile);
269
+ }
270
+ }
271
+ return {
272
+ attemptDir,
273
+ readyFile: path.join(attemptDir, "ready.json"),
274
+ database,
275
+ backup,
276
+ databaseExisted,
277
+ };
278
+ }
279
+
280
+ function restoreDaemonState(snapshot) {
281
+ for (const suffix of ["-wal", "-shm"]) {
282
+ fs.rmSync(snapshot.database + suffix, { force: true });
283
+ }
284
+ if (snapshot.databaseExisted) {
285
+ const restore = snapshot.database + ".restore";
286
+ fs.copyFileSync(snapshot.backup, restore);
287
+ fs.renameSync(restore, snapshot.database);
288
+ } else {
289
+ fs.rmSync(snapshot.database, { force: true });
290
+ }
291
+ }
292
+
293
+ function managedUpdateEnvironment(instruction, snapshot, outcome, error) {
294
+ return {
295
+ ...process.env,
296
+ [updateFDEnv]: "3",
297
+ [updateAttemptEnv]: instruction.attemptID,
298
+ [updateOutcomeEnv]: outcome,
299
+ [updateErrorEnv]: error || "",
300
+ [updateReadyFileEnv]: snapshot.readyFile,
301
+ };
302
+ }
303
+
304
+ function removeUpdateSnapshot(snapshot) {
305
+ try {
306
+ fs.rmSync(snapshot.attemptDir, { recursive: true, force: true });
307
+ } catch (error) {
308
+ console.error("Chord could not remove the completed daemon update snapshot: " + error.message);
309
+ }
310
+ }
311
+
312
+ async function stopChild(running) {
313
+ const child = running.process;
314
+ if (child && child.exitCode === null && child.signalCode === null) {
315
+ try {
316
+ child.kill("SIGTERM");
317
+ } catch {
318
+ // The child may have exited between the check and signal.
319
+ }
320
+ }
321
+ const stopped = await Promise.race([
322
+ running.completion.then(() => true),
323
+ new Promise((resolve) => setTimeout(() => resolve(false), 5_000)),
324
+ ]);
325
+ if (!stopped && child && child.exitCode === null && child.signalCode === null) {
326
+ try {
327
+ child.kill("SIGKILL");
328
+ } catch {
329
+ // The child may have exited between the check and signal.
330
+ }
331
+ }
332
+ return running.completion;
333
+ }
334
+
335
+ function candidateFailure(outcome) {
336
+ if (outcome.kind === "timeout") {
337
+ return "the updated daemon did not become ready within " + updateReadyTimeoutMs() + "ms";
338
+ }
339
+ const result = outcome.result;
340
+ if (result.error) {
341
+ return "the updated daemon could not start: " + result.error.message;
342
+ }
343
+ if (result.signal) {
344
+ return "the updated daemon exited before readiness with signal " + result.signal;
345
+ }
346
+ return "the updated daemon exited before readiness with code " + result.code;
347
+ }
348
+
349
+ function checkUpdatePackage(packageManager, instruction) {
350
+ const check = updateCommand(packageManager, instruction.cliVersion, ["version"]);
351
+ const running = runChild(check.command, check.args, {
352
+ stdio: ["ignore", "pipe", "inherit"],
353
+ env: process.env,
354
+ });
355
+ let stdout = "";
356
+ running.process.stdout.setEncoding("utf8");
357
+ running.process.stdout.on("data", (chunk) => {
358
+ if (stdout.length <= 1024) {
359
+ stdout += chunk;
360
+ }
361
+ });
362
+ const completion = running.completion.then((result) => {
363
+ if (result.error) {
364
+ return { kind: "fatal", error: result.error.message };
365
+ }
366
+ if (result.signal || receivedSignal) {
367
+ return { kind: "stopped", result };
368
+ }
369
+ if (result.code !== 0) {
370
+ return { kind: "retry", error: "package command exited with code " + result.code };
371
+ }
372
+ if (stdout.trim() !== "chord version " + instruction.cliVersion) {
373
+ return { kind: "fatal", error: "package version check returned an unexpected version" };
374
+ }
375
+ return { kind: "ready" };
376
+ });
377
+ return { running, completion };
378
+ }
379
+
380
+ function startPreviousDaemon(binaryPath, daemonArgs, instruction, snapshot, outcome, error) {
381
+ fs.rmSync(snapshot.readyFile, { force: true });
382
+ const running = runChild(binaryPath, daemonArgs, {
383
+ stdio: ["inherit", "inherit", "inherit", "pipe"],
384
+ env: managedUpdateEnvironment(instruction, snapshot, outcome, error),
385
+ });
386
+ const readiness = monitorReadiness(snapshot.readyFile, instruction, outcome);
387
+ void Promise.race([
388
+ running.completion.then((childResult) => ({ kind: "exit", result: childResult })),
389
+ readiness.promise,
390
+ ]).then((result) => {
391
+ readiness.cancel();
392
+ if (result.kind === "ready" && outcome === "rolled_back") {
393
+ removeUpdateSnapshot(snapshot);
394
+ }
395
+ });
396
+ return running;
397
+ }
398
+
399
+ async function waitForUpdatePackage(binaryPath, packageManager, instruction, daemonArgs, snapshot) {
400
+ let packageCheck = checkUpdatePackage(packageManager, instruction);
401
+ let check = await packageCheck.completion;
402
+ if (check.kind !== "retry") {
403
+ return check;
404
+ }
405
+
406
+ console.error(
407
+ "Chord could not fetch @sys9/chord-cli@" + instruction.cliVersion +
408
+ "; the previous daemon will stay online while Chord retries. " + check.error,
409
+ );
410
+ const previous = startPreviousDaemon(
411
+ binaryPath,
412
+ daemonArgs,
413
+ instruction,
414
+ snapshot,
415
+ "retrying",
416
+ check.error,
417
+ );
418
+
419
+ let delay = updateRetryInitialMs();
420
+ for (;;) {
421
+ console.error(
422
+ "Retrying Chord daemon package in " + delay + "ms (maximum interval " +
423
+ updateRetryMaxMs() + "ms).",
424
+ );
425
+ const wait = await Promise.race([
426
+ previous.completion.then((result) => ({ kind: "previous_exit", result })),
427
+ new Promise((resolve) => setTimeout(() => resolve({ kind: "retry" }), delay)),
428
+ ]);
429
+ if (wait.kind === "previous_exit") {
430
+ return wait;
431
+ }
432
+
433
+ packageCheck = checkUpdatePackage(packageManager, instruction);
434
+ const checked = await Promise.race([
435
+ previous.completion.then((result) => ({ kind: "previous_exit", result })),
436
+ packageCheck.completion,
437
+ ]);
438
+ if (checked.kind === "previous_exit") {
439
+ await stopChild(packageCheck.running);
440
+ return checked;
441
+ }
442
+ if (checked.kind === "ready") {
443
+ const previousResult = await stopChild(previous);
444
+ if (previousResult.code === updateRequestedExitCode && previousResult.updateInstruction) {
445
+ return { kind: "previous_exit", result: previousResult };
446
+ }
447
+ return checked;
448
+ }
449
+ if (checked.kind === "fatal" || checked.kind === "stopped") {
450
+ await stopChild(previous);
451
+ return checked;
452
+ }
453
+ delay = Math.min(delay * 2, updateRetryMaxMs());
454
+ }
455
+ }
456
+
457
+ async function runManagedUpdate(binaryPath, packageManager, instruction, daemonArgs) {
458
+ let snapshot;
459
+ try {
460
+ snapshot = snapshotDaemonState(instruction);
461
+ } catch (error) {
462
+ console.error("Chord could not prepare the daemon update: " + error.message);
463
+ return runChild(binaryPath, daemonArgs, {
464
+ stdio: ["inherit", "inherit", "inherit", "pipe"],
465
+ env: { ...process.env, [updateFDEnv]: "3" },
466
+ }).completion;
467
+ }
468
+
469
+ const packageResult = await waitForUpdatePackage(
470
+ binaryPath,
471
+ packageManager,
472
+ instruction,
473
+ daemonArgs,
474
+ snapshot,
475
+ );
476
+ if (packageResult.kind === "previous_exit") {
477
+ return packageResult.result;
478
+ }
479
+ if (packageResult.kind === "stopped") {
480
+ return packageResult.result;
481
+ }
482
+ if (packageResult.kind === "fatal") {
483
+ const failure = packageResult.error.slice(0, 1024);
484
+ console.error("Chord daemon update failed; restoring " + instruction.fromVersion + ". " + failure);
485
+ const restored = startPreviousDaemon(
486
+ binaryPath,
487
+ daemonArgs,
488
+ instruction,
489
+ snapshot,
490
+ "rolled_back",
491
+ failure,
492
+ );
493
+ return restored.completion;
494
+ }
495
+
496
+ // The retrying daemon may have changed local durable state while npm was
497
+ // unavailable. Snapshot only after it has stopped and immediately before the
498
+ // candidate can mutate that state.
499
+ removeUpdateSnapshot(snapshot);
500
+ try {
501
+ snapshot = snapshotDaemonState(instruction);
502
+ } catch (error) {
503
+ console.error("Chord could not prepare the daemon update: " + error.message);
504
+ return runChild(binaryPath, daemonArgs, {
505
+ stdio: ["inherit", "inherit", "inherit", "pipe"],
506
+ env: { ...process.env, [updateFDEnv]: "3" },
507
+ }).completion;
508
+ }
509
+
510
+ const update = updateCommand(packageManager, instruction.cliVersion, daemonArgs);
511
+ console.error("Updating Chord daemon to @sys9/chord-cli@" + instruction.cliVersion + ".");
512
+ const candidate = runChild(update.command, update.args, {
513
+ stdio: "inherit",
514
+ env: managedUpdateEnvironment(instruction, snapshot, "target", ""),
515
+ });
516
+ const readiness = monitorReadiness(snapshot.readyFile, instruction, "target");
517
+ const outcome = await Promise.race([
518
+ candidate.completion.then((result) => ({ kind: "exit", result })),
519
+ readiness.promise,
520
+ ]);
521
+ readiness.cancel();
522
+
523
+ if (receivedSignal) {
524
+ if (outcome.kind !== "ready") {
525
+ try {
526
+ restoreDaemonState(snapshot);
527
+ } catch (error) {
528
+ console.error("Chord could not restore daemon state during shutdown: " + error.message);
529
+ }
530
+ }
531
+ return candidate.completion;
532
+ }
533
+ if (outcome.kind === "ready") {
534
+ removeUpdateSnapshot(snapshot);
535
+ return candidate.completion;
536
+ }
537
+ if (outcome.kind === "timeout") {
538
+ await stopChild(candidate);
539
+ }
540
+ const failure = candidateFailure(outcome).slice(0, 1024);
541
+ console.error("Chord daemon update failed; restoring " + instruction.fromVersion + ". " + failure);
542
+ try {
543
+ restoreDaemonState(snapshot);
544
+ } catch (error) {
545
+ console.error("Chord could not restore the previous daemon state: " + error.message);
546
+ return { code: 1, signal: null, updateInstruction: "", error: null };
547
+ }
548
+
549
+ const restored = startPreviousDaemon(
550
+ binaryPath,
551
+ daemonArgs,
552
+ instruction,
553
+ snapshot,
554
+ "rolled_back",
555
+ failure,
556
+ );
557
+ return restored.completion;
558
+ }
559
+
560
+ async function main() {
561
+ const { binaryPath, platformPackage } = resolveBinaryPath();
562
+ if (!fs.existsSync(binaryPath)) {
563
+ const packageManager = detectPackageManager();
564
+ const update =
565
+ packageManager === "bun"
566
+ ? "bun add -g @sys9/chord-cli@latest"
567
+ : "npm install -g @sys9/chord-cli@latest";
568
+ throw new Error(
569
+ "Missing optional dependency " + platformPackage + ". Reinstall chord: " + update,
570
+ );
571
+ }
572
+
573
+ const daemonArgs = process.argv.slice(2);
574
+ let result = await runChild(binaryPath, daemonArgs, {
575
+ stdio: ["inherit", "inherit", "inherit", "pipe"],
576
+ env: { ...process.env, [updateFDEnv]: "3" },
577
+ }).completion;
578
+ const packageManager = detectPackageManager();
579
+ for (;;) {
580
+ if (result.error) {
581
+ console.error(result.error);
582
+ return result;
583
+ }
584
+ if (result.signal || receivedSignal) {
585
+ return { ...result, signal: result.signal || receivedSignal };
586
+ }
587
+ if (result.code !== updateRequestedExitCode) {
588
+ return result;
589
+ }
590
+
591
+ const instruction = parseUpdateInstruction(result.updateInstruction);
592
+ if (!instruction) {
593
+ console.error("Chord daemon exited for an update, but did not provide a valid managed update instruction.");
594
+ return result;
595
+ }
596
+ result = await runManagedUpdate(binaryPath, packageManager, instruction, daemonArgs);
597
+ }
598
+ }
599
+
600
+ main().then((result) => {
601
+ if (result.signal) {
602
+ process.removeAllListeners(result.signal);
603
+ process.kill(process.pid, result.signal);
604
+ return;
605
+ }
606
+ process.exit(result.code);
607
+ }).catch((error) => {
608
+ console.error(error);
609
+ process.exit(1);
610
+ });
@@ -0,0 +1,60 @@
1
+ "use strict";
2
+
3
+ const path = require("node:path");
4
+
5
+ const SUPPORTED_TARGETS = [
6
+ {
7
+ slug: "linux-x64",
8
+ os: "linux",
9
+ cpu: "x64",
10
+ vendorDir: "linux_amd64",
11
+ targetTriple: "x86_64-unknown-linux-musl",
12
+ },
13
+ {
14
+ slug: "linux-arm64",
15
+ os: "linux",
16
+ cpu: "arm64",
17
+ vendorDir: "linux_arm64",
18
+ targetTriple: "aarch64-unknown-linux-musl",
19
+ },
20
+ {
21
+ slug: "darwin-x64",
22
+ os: "darwin",
23
+ cpu: "x64",
24
+ vendorDir: "darwin_amd64",
25
+ targetTriple: "x86_64-apple-darwin",
26
+ },
27
+ {
28
+ slug: "darwin-arm64",
29
+ os: "darwin",
30
+ cpu: "arm64",
31
+ vendorDir: "darwin_arm64",
32
+ targetTriple: "aarch64-apple-darwin",
33
+ }
34
+ ];
35
+
36
+ function resolveSupportedTarget(platform = process.platform, arch = process.arch) {
37
+ for (const target of SUPPORTED_TARGETS) {
38
+ if (target.os === platform && target.cpu === arch) {
39
+ return target;
40
+ }
41
+ }
42
+ throw new Error("unsupported platform: " + platform + " (" + arch + ")");
43
+ }
44
+
45
+ function resolvePackagedBinaryPath(rootDir, platform = process.platform, arch = process.arch) {
46
+ const target = resolveSupportedTarget(platform, arch);
47
+ return path.join(rootDir, "vendor", target.vendorDir, "chord");
48
+ }
49
+
50
+ function resolvePlatformPackageName(rootPackageName, platform = process.platform, arch = process.arch) {
51
+ const target = resolveSupportedTarget(platform, arch);
52
+ return rootPackageName + "-" + target.slug;
53
+ }
54
+
55
+ module.exports = {
56
+ SUPPORTED_TARGETS,
57
+ resolvePackagedBinaryPath,
58
+ resolvePlatformPackageName,
59
+ resolveSupportedTarget,
60
+ };
package/package.json CHANGED
@@ -1,22 +1,30 @@
1
1
  {
2
2
  "name": "@sys9/chord-cli",
3
- "version": "0.1.159-linux-x64",
3
+ "version": "0.1.159",
4
4
  "description": "Chord CLI package used by @sys9/cli",
5
5
  "license": "UNLICENSED",
6
- "os": [
7
- "linux"
8
- ],
9
- "cpu": [
10
- "x64"
11
- ],
12
- "files": [
13
- "vendor"
14
- ],
15
6
  "repository": {
16
7
  "type": "git",
17
8
  "url": "git+https://github.com/sys9-ai/chord.git"
18
9
  },
10
+ "publishConfig": {
11
+ "access": "public"
12
+ },
13
+ "bin": {
14
+ "chord": "bin/chord"
15
+ },
16
+ "files": [
17
+ "README.md",
18
+ "bin",
19
+ "lib"
20
+ ],
19
21
  "engines": {
20
22
  "node": ">=16"
23
+ },
24
+ "optionalDependencies": {
25
+ "@sys9/chord-cli-linux-x64": "npm:@sys9/chord-cli@0.1.159-linux-x64",
26
+ "@sys9/chord-cli-linux-arm64": "npm:@sys9/chord-cli@0.1.159-linux-arm64",
27
+ "@sys9/chord-cli-darwin-x64": "npm:@sys9/chord-cli@0.1.159-darwin-x64",
28
+ "@sys9/chord-cli-darwin-arm64": "npm:@sys9/chord-cli@0.1.159-darwin-arm64"
21
29
  }
22
30
  }
Binary file