@h-rig/cli 0.0.6-alpha.74 → 0.0.6-alpha.76
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/rig.js +4657 -4206
- package/dist/src/commands/_help-catalog.js +32 -14
- package/dist/src/commands/_operator-board.js +730 -0
- package/dist/src/commands/_operator-view.js +98 -92
- package/dist/src/commands/_pi-frontend.js +134 -4
- package/dist/src/commands/_server-client.js +14 -0
- package/dist/src/commands/_tui-theme.js +135 -0
- package/dist/src/commands/run.js +98 -92
- package/dist/src/commands/stats.js +32 -14
- package/dist/src/commands/task.js +40 -18
- package/dist/src/commands.js +4667 -4216
- package/dist/src/index.js +4667 -4216
- package/package.json +9 -8
|
@@ -574,98 +574,6 @@ import { tmpdir } from "os";
|
|
|
574
574
|
import { join } from "path";
|
|
575
575
|
import { main as runPiMain } from "@earendil-works/pi-coding-agent";
|
|
576
576
|
import createPiRigExtension from "@rig/pi-rig";
|
|
577
|
-
function setTemporaryEnv(updates) {
|
|
578
|
-
const previous = new Map;
|
|
579
|
-
for (const [key, value] of Object.entries(updates)) {
|
|
580
|
-
previous.set(key, process.env[key]);
|
|
581
|
-
process.env[key] = value;
|
|
582
|
-
}
|
|
583
|
-
return () => {
|
|
584
|
-
for (const [key, value] of previous) {
|
|
585
|
-
if (value === undefined)
|
|
586
|
-
delete process.env[key];
|
|
587
|
-
else
|
|
588
|
-
process.env[key] = value;
|
|
589
|
-
}
|
|
590
|
-
};
|
|
591
|
-
}
|
|
592
|
-
function buildOperatorPiEnv(input) {
|
|
593
|
-
return {
|
|
594
|
-
PI_CODING_AGENT_SESSION_DIR: input.sessionDir,
|
|
595
|
-
PI_SKIP_VERSION_CHECK: "1",
|
|
596
|
-
RIG_PI_OPERATOR_SESSION: "1",
|
|
597
|
-
RIG_RUN_ID: input.runId,
|
|
598
|
-
RIG_SERVER_URL: input.serverUrl,
|
|
599
|
-
...input.authToken ? { RIG_AUTH_TOKEN: input.authToken } : {},
|
|
600
|
-
...input.serverProjectRoot ? { RIG_PROJECT_ROOT: input.serverProjectRoot } : {}
|
|
601
|
-
};
|
|
602
|
-
}
|
|
603
|
-
async function attachRunBundledPiFrontend(context, input) {
|
|
604
|
-
const tempSessionDir = mkdtempSync(join(tmpdir(), "rig-pi-frontend-sessions-"));
|
|
605
|
-
const server = await ensureServerForCli(context.projectRoot);
|
|
606
|
-
let sessionFileArg = [];
|
|
607
|
-
try {
|
|
608
|
-
const payload = await requestServerJson(context, `/api/runs/${encodeURIComponent(input.runId)}/session-file`);
|
|
609
|
-
if (payload?.ok && typeof payload.content === "string" && payload.content.trim()) {
|
|
610
|
-
const fileName = typeof payload.fileName === "string" && payload.fileName.trim() ? payload.fileName.replace(/[^A-Za-z0-9._-]/g, "_") : `rig-run-${input.runId}.jsonl`;
|
|
611
|
-
const localSessionPath = join(tempSessionDir, fileName);
|
|
612
|
-
const content = payload.content.split(`
|
|
613
|
-
`).map((line, index) => {
|
|
614
|
-
if (index > 0 || !line.trim())
|
|
615
|
-
return line;
|
|
616
|
-
try {
|
|
617
|
-
const header = JSON.parse(line);
|
|
618
|
-
if (header.type === "session" && typeof header.cwd === "string") {
|
|
619
|
-
return JSON.stringify({ ...header, cwd: process.cwd() });
|
|
620
|
-
}
|
|
621
|
-
} catch {}
|
|
622
|
-
return line;
|
|
623
|
-
}).join(`
|
|
624
|
-
`);
|
|
625
|
-
writeFileSync2(localSessionPath, content);
|
|
626
|
-
sessionFileArg = ["--session", localSessionPath];
|
|
627
|
-
}
|
|
628
|
-
} catch {}
|
|
629
|
-
const restoreEnv = setTemporaryEnv(buildOperatorPiEnv({
|
|
630
|
-
runId: input.runId,
|
|
631
|
-
serverUrl: server.baseUrl,
|
|
632
|
-
authToken: server.authToken,
|
|
633
|
-
serverProjectRoot: server.serverProjectRoot,
|
|
634
|
-
sessionDir: tempSessionDir
|
|
635
|
-
}));
|
|
636
|
-
const piRigExtensionFactory = (pi) => {
|
|
637
|
-
createPiRigExtension(pi);
|
|
638
|
-
};
|
|
639
|
-
let detached = false;
|
|
640
|
-
try {
|
|
641
|
-
await runPiMain([
|
|
642
|
-
"--no-extensions",
|
|
643
|
-
"--no-skills",
|
|
644
|
-
"--no-prompt-templates",
|
|
645
|
-
"--no-context-files",
|
|
646
|
-
...sessionFileArg
|
|
647
|
-
], {
|
|
648
|
-
extensionFactories: [piRigExtensionFactory]
|
|
649
|
-
});
|
|
650
|
-
detached = true;
|
|
651
|
-
} finally {
|
|
652
|
-
restoreEnv();
|
|
653
|
-
rmSync(tempSessionDir, { recursive: true, force: true });
|
|
654
|
-
}
|
|
655
|
-
let run = { runId: input.runId, status: "unknown" };
|
|
656
|
-
try {
|
|
657
|
-
run = await getRunDetailsViaServer(context, input.runId);
|
|
658
|
-
} catch {}
|
|
659
|
-
return {
|
|
660
|
-
run,
|
|
661
|
-
logs: [],
|
|
662
|
-
timeline: [],
|
|
663
|
-
timelineCursor: null,
|
|
664
|
-
steered: input.steered === true,
|
|
665
|
-
detached,
|
|
666
|
-
rendered: "stock Pi operator console with the pi-rig extension"
|
|
667
|
-
};
|
|
668
|
-
}
|
|
669
577
|
|
|
670
578
|
// packages/cli/src/commands/_async-ui.ts
|
|
671
579
|
import pc from "picocolors";
|
|
@@ -788,6 +696,104 @@ async function withSpinner(label, work, options = {}) {
|
|
|
788
696
|
}
|
|
789
697
|
}
|
|
790
698
|
|
|
699
|
+
// packages/cli/src/commands/_pi-frontend.ts
|
|
700
|
+
function setTemporaryEnv(updates) {
|
|
701
|
+
const previous = new Map;
|
|
702
|
+
for (const [key, value] of Object.entries(updates)) {
|
|
703
|
+
previous.set(key, process.env[key]);
|
|
704
|
+
process.env[key] = value;
|
|
705
|
+
}
|
|
706
|
+
return () => {
|
|
707
|
+
for (const [key, value] of previous) {
|
|
708
|
+
if (value === undefined)
|
|
709
|
+
delete process.env[key];
|
|
710
|
+
else
|
|
711
|
+
process.env[key] = value;
|
|
712
|
+
}
|
|
713
|
+
};
|
|
714
|
+
}
|
|
715
|
+
function buildOperatorPiEnv(input) {
|
|
716
|
+
return {
|
|
717
|
+
PI_CODING_AGENT_SESSION_DIR: input.sessionDir,
|
|
718
|
+
PI_SKIP_VERSION_CHECK: "1",
|
|
719
|
+
RIG_PI_OPERATOR_SESSION: "1",
|
|
720
|
+
RIG_RUN_ID: input.runId,
|
|
721
|
+
RIG_SERVER_URL: input.serverUrl,
|
|
722
|
+
...input.authToken ? { RIG_AUTH_TOKEN: input.authToken } : {},
|
|
723
|
+
...input.serverProjectRoot ? { RIG_PROJECT_ROOT: input.serverProjectRoot } : {}
|
|
724
|
+
};
|
|
725
|
+
}
|
|
726
|
+
async function prepareOperatorConsole(context, runId, tempSessionDir) {
|
|
727
|
+
const server = await ensureServerForCli(context.projectRoot);
|
|
728
|
+
let sessionFileArg = [];
|
|
729
|
+
try {
|
|
730
|
+
const payload = await requestServerJson(context, `/api/runs/${encodeURIComponent(runId)}/session-file`);
|
|
731
|
+
if (payload?.ok && typeof payload.content === "string" && payload.content.trim()) {
|
|
732
|
+
const fileName = typeof payload.fileName === "string" && payload.fileName.trim() ? payload.fileName.replace(/[^A-Za-z0-9._-]/g, "_") : `rig-run-${runId}.jsonl`;
|
|
733
|
+
const localSessionPath = join(tempSessionDir, fileName);
|
|
734
|
+
const content = payload.content.split(`
|
|
735
|
+
`).map((line, index) => {
|
|
736
|
+
if (index > 0 || !line.trim())
|
|
737
|
+
return line;
|
|
738
|
+
try {
|
|
739
|
+
const header = JSON.parse(line);
|
|
740
|
+
if (header.type === "session" && typeof header.cwd === "string") {
|
|
741
|
+
return JSON.stringify({ ...header, cwd: process.cwd() });
|
|
742
|
+
}
|
|
743
|
+
} catch {}
|
|
744
|
+
return line;
|
|
745
|
+
}).join(`
|
|
746
|
+
`);
|
|
747
|
+
writeFileSync2(localSessionPath, content);
|
|
748
|
+
sessionFileArg = ["--session", localSessionPath];
|
|
749
|
+
}
|
|
750
|
+
} catch {}
|
|
751
|
+
return { server, sessionFileArg };
|
|
752
|
+
}
|
|
753
|
+
async function attachRunBundledPiFrontend(context, input) {
|
|
754
|
+
const tempSessionDir = mkdtempSync(join(tmpdir(), "rig-pi-frontend-sessions-"));
|
|
755
|
+
const { server, sessionFileArg } = await withSpinner(`Opening Pi console for run ${input.runId}\u2026`, () => prepareOperatorConsole(context, input.runId, tempSessionDir), { outputMode: context.outputMode });
|
|
756
|
+
const restoreEnv = setTemporaryEnv(buildOperatorPiEnv({
|
|
757
|
+
runId: input.runId,
|
|
758
|
+
serverUrl: server.baseUrl,
|
|
759
|
+
authToken: server.authToken,
|
|
760
|
+
serverProjectRoot: server.serverProjectRoot,
|
|
761
|
+
sessionDir: tempSessionDir
|
|
762
|
+
}));
|
|
763
|
+
const piRigExtensionFactory = (pi) => {
|
|
764
|
+
createPiRigExtension(pi);
|
|
765
|
+
};
|
|
766
|
+
let detached = false;
|
|
767
|
+
try {
|
|
768
|
+
await runPiMain([
|
|
769
|
+
"--no-extensions",
|
|
770
|
+
"--no-skills",
|
|
771
|
+
"--no-prompt-templates",
|
|
772
|
+
"--no-context-files",
|
|
773
|
+
...sessionFileArg
|
|
774
|
+
], {
|
|
775
|
+
extensionFactories: [piRigExtensionFactory]
|
|
776
|
+
});
|
|
777
|
+
detached = true;
|
|
778
|
+
} finally {
|
|
779
|
+
restoreEnv();
|
|
780
|
+
rmSync(tempSessionDir, { recursive: true, force: true });
|
|
781
|
+
}
|
|
782
|
+
let run = { runId: input.runId, status: "unknown" };
|
|
783
|
+
try {
|
|
784
|
+
run = await getRunDetailsViaServer(context, input.runId);
|
|
785
|
+
} catch {}
|
|
786
|
+
return {
|
|
787
|
+
run,
|
|
788
|
+
logs: [],
|
|
789
|
+
timeline: [],
|
|
790
|
+
timelineCursor: null,
|
|
791
|
+
steered: input.steered === true,
|
|
792
|
+
detached,
|
|
793
|
+
rendered: "stock Pi operator console with the pi-rig extension"
|
|
794
|
+
};
|
|
795
|
+
}
|
|
796
|
+
|
|
791
797
|
// packages/cli/src/commands/_operator-view.ts
|
|
792
798
|
var TERMINAL_RUN_STATUSES = new Set(["completed", "failed", "stopped", "cancelled", "canceled", "closed", "merged", "needs_attention", "needs-attention"]);
|
|
793
799
|
function runStatusFromPayload(payload) {
|
|
@@ -129,6 +129,11 @@ function writeRepoServerProjectRoot(projectRoot, serverProjectRoot) {
|
|
|
129
129
|
// packages/cli/src/commands/_server-client.ts
|
|
130
130
|
var scopedGitHubBearerTokens = new Map;
|
|
131
131
|
var serverPhaseListener = null;
|
|
132
|
+
function setServerPhaseListener(listener) {
|
|
133
|
+
const previous = serverPhaseListener;
|
|
134
|
+
serverPhaseListener = listener;
|
|
135
|
+
return previous;
|
|
136
|
+
}
|
|
132
137
|
function reportServerPhase(label) {
|
|
133
138
|
serverPhaseListener?.(label);
|
|
134
139
|
}
|
|
@@ -336,6 +341,127 @@ var RESUMABLE_RUN_STATUSES = new Set([
|
|
|
336
341
|
"needs_attention"
|
|
337
342
|
]);
|
|
338
343
|
|
|
344
|
+
// packages/cli/src/commands/_async-ui.ts
|
|
345
|
+
import pc from "picocolors";
|
|
346
|
+
|
|
347
|
+
// packages/cli/src/commands/_spinner.ts
|
|
348
|
+
var SPINNER_FRAMES = ["\u280B", "\u2819", "\u2839", "\u2838", "\u283C", "\u2834", "\u2826", "\u2827", "\u2807", "\u280F"];
|
|
349
|
+
function createTtySpinner(input) {
|
|
350
|
+
const output = input.output ?? process.stdout;
|
|
351
|
+
const isTty = output.isTTY === true;
|
|
352
|
+
const frames = input.frames && input.frames.length > 0 ? input.frames : SPINNER_FRAMES;
|
|
353
|
+
let label = input.label;
|
|
354
|
+
let frame = 0;
|
|
355
|
+
let paused = false;
|
|
356
|
+
let stopped = false;
|
|
357
|
+
let lastPrintedLabel = "";
|
|
358
|
+
const render = () => {
|
|
359
|
+
if (stopped || paused)
|
|
360
|
+
return;
|
|
361
|
+
if (!isTty) {
|
|
362
|
+
if (label !== lastPrintedLabel) {
|
|
363
|
+
output.write(`${label}
|
|
364
|
+
`);
|
|
365
|
+
lastPrintedLabel = label;
|
|
366
|
+
}
|
|
367
|
+
return;
|
|
368
|
+
}
|
|
369
|
+
frame = (frame + 1) % frames.length;
|
|
370
|
+
const glyph = frames[frame] ?? frames[0] ?? "";
|
|
371
|
+
output.write(`\r\x1B[2K${input.styleFrame ? input.styleFrame(glyph) : glyph} ${label}`);
|
|
372
|
+
};
|
|
373
|
+
const clearLine = () => {
|
|
374
|
+
if (isTty)
|
|
375
|
+
output.write("\r\x1B[2K");
|
|
376
|
+
};
|
|
377
|
+
render();
|
|
378
|
+
const timer = isTty ? setInterval(render, input.intervalMs ?? 120) : null;
|
|
379
|
+
return {
|
|
380
|
+
setLabel(next) {
|
|
381
|
+
label = next;
|
|
382
|
+
render();
|
|
383
|
+
},
|
|
384
|
+
pause() {
|
|
385
|
+
paused = true;
|
|
386
|
+
clearLine();
|
|
387
|
+
},
|
|
388
|
+
resume() {
|
|
389
|
+
if (stopped)
|
|
390
|
+
return;
|
|
391
|
+
paused = false;
|
|
392
|
+
render();
|
|
393
|
+
},
|
|
394
|
+
stop(finalLine) {
|
|
395
|
+
if (stopped)
|
|
396
|
+
return;
|
|
397
|
+
stopped = true;
|
|
398
|
+
if (timer)
|
|
399
|
+
clearInterval(timer);
|
|
400
|
+
clearLine();
|
|
401
|
+
if (finalLine)
|
|
402
|
+
output.write(`${finalLine}
|
|
403
|
+
`);
|
|
404
|
+
}
|
|
405
|
+
};
|
|
406
|
+
}
|
|
407
|
+
|
|
408
|
+
// packages/cli/src/commands/_async-ui.ts
|
|
409
|
+
var CLACK_SPINNER_FRAMES = ["\u25D2", "\u25D0", "\u25D3", "\u25D1"];
|
|
410
|
+
var DONE_SYMBOL = pc.green("\u25C7");
|
|
411
|
+
var FAIL_SYMBOL = pc.red("\u25A0");
|
|
412
|
+
var activeUpdate = null;
|
|
413
|
+
async function withSpinner(label, work, options = {}) {
|
|
414
|
+
if (options.outputMode === "json") {
|
|
415
|
+
return work(() => {});
|
|
416
|
+
}
|
|
417
|
+
if (activeUpdate) {
|
|
418
|
+
const outer = activeUpdate;
|
|
419
|
+
outer(label);
|
|
420
|
+
return work(outer);
|
|
421
|
+
}
|
|
422
|
+
const output = options.output ?? process.stderr;
|
|
423
|
+
const isTty = output.isTTY === true;
|
|
424
|
+
let lastLabel = label;
|
|
425
|
+
if (!isTty) {
|
|
426
|
+
output.write(`${label}
|
|
427
|
+
`);
|
|
428
|
+
const update2 = (next) => {
|
|
429
|
+
lastLabel = next;
|
|
430
|
+
};
|
|
431
|
+
activeUpdate = update2;
|
|
432
|
+
const previousListener2 = setServerPhaseListener(update2);
|
|
433
|
+
try {
|
|
434
|
+
return await work(update2);
|
|
435
|
+
} finally {
|
|
436
|
+
activeUpdate = null;
|
|
437
|
+
setServerPhaseListener(previousListener2);
|
|
438
|
+
}
|
|
439
|
+
}
|
|
440
|
+
const spinner = createTtySpinner({
|
|
441
|
+
label,
|
|
442
|
+
output,
|
|
443
|
+
frames: CLACK_SPINNER_FRAMES,
|
|
444
|
+
styleFrame: (frame) => pc.magenta(frame)
|
|
445
|
+
});
|
|
446
|
+
const update = (next) => {
|
|
447
|
+
lastLabel = next;
|
|
448
|
+
spinner.setLabel(next);
|
|
449
|
+
};
|
|
450
|
+
activeUpdate = update;
|
|
451
|
+
const previousListener = setServerPhaseListener(update);
|
|
452
|
+
try {
|
|
453
|
+
const result = await work(update);
|
|
454
|
+
spinner.stop(options.doneLabel ? `${DONE_SYMBOL} ${options.doneLabel}` : undefined);
|
|
455
|
+
return result;
|
|
456
|
+
} catch (error) {
|
|
457
|
+
spinner.stop(`${FAIL_SYMBOL} ${lastLabel}`);
|
|
458
|
+
throw error;
|
|
459
|
+
} finally {
|
|
460
|
+
activeUpdate = null;
|
|
461
|
+
setServerPhaseListener(previousListener);
|
|
462
|
+
}
|
|
463
|
+
}
|
|
464
|
+
|
|
339
465
|
// packages/cli/src/commands/_pi-frontend.ts
|
|
340
466
|
function setTemporaryEnv(updates) {
|
|
341
467
|
const previous = new Map;
|
|
@@ -363,14 +489,13 @@ function buildOperatorPiEnv(input) {
|
|
|
363
489
|
...input.serverProjectRoot ? { RIG_PROJECT_ROOT: input.serverProjectRoot } : {}
|
|
364
490
|
};
|
|
365
491
|
}
|
|
366
|
-
async function
|
|
367
|
-
const tempSessionDir = mkdtempSync(join(tmpdir(), "rig-pi-frontend-sessions-"));
|
|
492
|
+
async function prepareOperatorConsole(context, runId, tempSessionDir) {
|
|
368
493
|
const server = await ensureServerForCli(context.projectRoot);
|
|
369
494
|
let sessionFileArg = [];
|
|
370
495
|
try {
|
|
371
|
-
const payload = await requestServerJson(context, `/api/runs/${encodeURIComponent(
|
|
496
|
+
const payload = await requestServerJson(context, `/api/runs/${encodeURIComponent(runId)}/session-file`);
|
|
372
497
|
if (payload?.ok && typeof payload.content === "string" && payload.content.trim()) {
|
|
373
|
-
const fileName = typeof payload.fileName === "string" && payload.fileName.trim() ? payload.fileName.replace(/[^A-Za-z0-9._-]/g, "_") : `rig-run-${
|
|
498
|
+
const fileName = typeof payload.fileName === "string" && payload.fileName.trim() ? payload.fileName.replace(/[^A-Za-z0-9._-]/g, "_") : `rig-run-${runId}.jsonl`;
|
|
374
499
|
const localSessionPath = join(tempSessionDir, fileName);
|
|
375
500
|
const content = payload.content.split(`
|
|
376
501
|
`).map((line, index) => {
|
|
@@ -389,6 +514,11 @@ async function attachRunBundledPiFrontend(context, input) {
|
|
|
389
514
|
sessionFileArg = ["--session", localSessionPath];
|
|
390
515
|
}
|
|
391
516
|
} catch {}
|
|
517
|
+
return { server, sessionFileArg };
|
|
518
|
+
}
|
|
519
|
+
async function attachRunBundledPiFrontend(context, input) {
|
|
520
|
+
const tempSessionDir = mkdtempSync(join(tmpdir(), "rig-pi-frontend-sessions-"));
|
|
521
|
+
const { server, sessionFileArg } = await withSpinner(`Opening Pi console for run ${input.runId}\u2026`, () => prepareOperatorConsole(context, input.runId, tempSessionDir), { outputMode: context.outputMode });
|
|
392
522
|
const restoreEnv = setTemporaryEnv(buildOperatorPiEnv({
|
|
393
523
|
runId: input.runId,
|
|
394
524
|
serverUrl: server.baseUrl,
|
|
@@ -513,6 +513,19 @@ async function resumeRunViaServer(context, runId, options) {
|
|
|
513
513
|
}
|
|
514
514
|
return { ok: true, runId: targetRunId, ...record };
|
|
515
515
|
}
|
|
516
|
+
async function resolveServerConnectionLabel(projectRoot) {
|
|
517
|
+
try {
|
|
518
|
+
const selected = resolveSelectedConnection(projectRoot);
|
|
519
|
+
if (!selected)
|
|
520
|
+
return "no server selected";
|
|
521
|
+
if (selected.connection.kind === "remote") {
|
|
522
|
+
return selected.connection.baseUrl.replace(/^https?:\/\//, "");
|
|
523
|
+
}
|
|
524
|
+
return `local (${selected.alias})`;
|
|
525
|
+
} catch {
|
|
526
|
+
return "no server selected";
|
|
527
|
+
}
|
|
528
|
+
}
|
|
516
529
|
async function stopRunViaServer(context, runId) {
|
|
517
530
|
const payload = await requestServerJson(context, "/api/runs/stop", {
|
|
518
531
|
method: "POST",
|
|
@@ -640,6 +653,7 @@ export {
|
|
|
640
653
|
runRunPiCommandViaServer,
|
|
641
654
|
resumeRunViaServer,
|
|
642
655
|
respondRunPiExtensionUiViaServer,
|
|
656
|
+
resolveServerConnectionLabel,
|
|
643
657
|
requestServerJson,
|
|
644
658
|
registerProjectViaServer,
|
|
645
659
|
prepareRemoteCheckoutViaServer,
|
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
// @bun
|
|
2
|
+
// packages/cli/src/commands/_tui-theme.ts
|
|
3
|
+
var RIG_PALETTE = {
|
|
4
|
+
ink: "#f2f3f6",
|
|
5
|
+
ink2: "#aeb0ba",
|
|
6
|
+
ink3: "#6c6e79",
|
|
7
|
+
ink4: "#44464f",
|
|
8
|
+
accent: "#ccff4d",
|
|
9
|
+
accentDim: "#a9d63f",
|
|
10
|
+
cyan: "#56d8ff",
|
|
11
|
+
red: "#ff5d5d",
|
|
12
|
+
yellow: "#ffd24d"
|
|
13
|
+
};
|
|
14
|
+
function hexToRgb(hex) {
|
|
15
|
+
const value = hex.replace("#", "");
|
|
16
|
+
return [
|
|
17
|
+
Number.parseInt(value.slice(0, 2), 16),
|
|
18
|
+
Number.parseInt(value.slice(2, 4), 16),
|
|
19
|
+
Number.parseInt(value.slice(4, 6), 16)
|
|
20
|
+
];
|
|
21
|
+
}
|
|
22
|
+
function fg(hex) {
|
|
23
|
+
const [r, g, b] = hexToRgb(hex);
|
|
24
|
+
return (text) => `\x1B[38;2;${r};${g};${b}m${text}\x1B[39m`;
|
|
25
|
+
}
|
|
26
|
+
var ink = fg(RIG_PALETTE.ink);
|
|
27
|
+
var ink2 = fg(RIG_PALETTE.ink2);
|
|
28
|
+
var ink3 = fg(RIG_PALETTE.ink3);
|
|
29
|
+
var ink4 = fg(RIG_PALETTE.ink4);
|
|
30
|
+
var accent = fg(RIG_PALETTE.accent);
|
|
31
|
+
var accentDim = fg(RIG_PALETTE.accentDim);
|
|
32
|
+
var cyan = fg(RIG_PALETTE.cyan);
|
|
33
|
+
var red = fg(RIG_PALETTE.red);
|
|
34
|
+
var yellow = fg(RIG_PALETTE.yellow);
|
|
35
|
+
function bold(text) {
|
|
36
|
+
return `\x1B[1m${text}\x1B[22m`;
|
|
37
|
+
}
|
|
38
|
+
function statusColor(status) {
|
|
39
|
+
switch (status) {
|
|
40
|
+
case "running":
|
|
41
|
+
return accent;
|
|
42
|
+
case "preparing":
|
|
43
|
+
case "created":
|
|
44
|
+
case "validating":
|
|
45
|
+
case "reviewing":
|
|
46
|
+
case "closing-out":
|
|
47
|
+
return cyan;
|
|
48
|
+
case "needs-attention":
|
|
49
|
+
case "needs_attention":
|
|
50
|
+
return yellow;
|
|
51
|
+
case "failed":
|
|
52
|
+
return red;
|
|
53
|
+
default:
|
|
54
|
+
return ink3;
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
var RIG_SPINNER_FRAMES = ["\u280B", "\u2819", "\u2839", "\u2838", "\u283C", "\u2834", "\u2826", "\u2827", "\u2807", "\u280F"];
|
|
58
|
+
var DRONE_ART = [
|
|
59
|
+
" .-=-. .-=-. ",
|
|
60
|
+
" ( !!! ) ( !!! ) ",
|
|
61
|
+
" '-=-'._ _.'-=-' ",
|
|
62
|
+
" '._ _.' ",
|
|
63
|
+
" '=$$$$$$$=.' ",
|
|
64
|
+
" =$$$$$$$$$$$= ",
|
|
65
|
+
" $$$@@@@@@@@@@$$$ ",
|
|
66
|
+
" $$$@@ @@$$$ ",
|
|
67
|
+
" $$@ ? @$$$ ",
|
|
68
|
+
" $$$@ '-' @$$$ ",
|
|
69
|
+
" $$$@@ @@$$$ ",
|
|
70
|
+
" $$$@@@@@@@@@@$$$ ",
|
|
71
|
+
" =$$$$$$$$$$$= ",
|
|
72
|
+
" '=$$$$$$$=.' ",
|
|
73
|
+
" _.' '._ ",
|
|
74
|
+
" .-=-.' '.-=-. ",
|
|
75
|
+
" ( !!! ) ( !!! ) ",
|
|
76
|
+
" '-=-' '-=-' "
|
|
77
|
+
];
|
|
78
|
+
var BLADE_FRAMES = ["---", "\\\\\\", "|||", "///"];
|
|
79
|
+
var EYE_FRAMES = ["@", "o", "."];
|
|
80
|
+
function droneCharColor(char) {
|
|
81
|
+
if (char === "$" || char === "@")
|
|
82
|
+
return accent;
|
|
83
|
+
if (char === "=" || char === "%")
|
|
84
|
+
return accentDim;
|
|
85
|
+
if (char === "\\" || char === "/")
|
|
86
|
+
return ink3;
|
|
87
|
+
return ink4;
|
|
88
|
+
}
|
|
89
|
+
function renderDroneFrame(tick) {
|
|
90
|
+
const blade = BLADE_FRAMES[Math.floor(tick / 4) % BLADE_FRAMES.length];
|
|
91
|
+
const pulse = Math.sin(tick * 0.07);
|
|
92
|
+
const eye = pulse > 0.45 ? EYE_FRAMES[0] : pulse > -0.1 ? EYE_FRAMES[1] : EYE_FRAMES[2];
|
|
93
|
+
return DRONE_ART.map((line) => {
|
|
94
|
+
let out = "";
|
|
95
|
+
let index = 0;
|
|
96
|
+
while (index < line.length) {
|
|
97
|
+
if (line.startsWith("!!!", index)) {
|
|
98
|
+
out += cyan(blade);
|
|
99
|
+
index += 3;
|
|
100
|
+
continue;
|
|
101
|
+
}
|
|
102
|
+
const char = line[index];
|
|
103
|
+
if (char === "?") {
|
|
104
|
+
out += bold(cyan(eye));
|
|
105
|
+
} else if (char !== " ") {
|
|
106
|
+
out += droneCharColor(char)(char);
|
|
107
|
+
} else {
|
|
108
|
+
out += " ";
|
|
109
|
+
}
|
|
110
|
+
index += 1;
|
|
111
|
+
}
|
|
112
|
+
return out;
|
|
113
|
+
});
|
|
114
|
+
}
|
|
115
|
+
var DRONE_WIDTH = DRONE_ART[0].length;
|
|
116
|
+
var DRONE_HEIGHT = DRONE_ART.length;
|
|
117
|
+
export {
|
|
118
|
+
yellow,
|
|
119
|
+
statusColor,
|
|
120
|
+
renderDroneFrame,
|
|
121
|
+
red,
|
|
122
|
+
ink4,
|
|
123
|
+
ink3,
|
|
124
|
+
ink2,
|
|
125
|
+
ink,
|
|
126
|
+
fg,
|
|
127
|
+
cyan,
|
|
128
|
+
bold,
|
|
129
|
+
accentDim,
|
|
130
|
+
accent,
|
|
131
|
+
RIG_SPINNER_FRAMES,
|
|
132
|
+
RIG_PALETTE,
|
|
133
|
+
DRONE_WIDTH,
|
|
134
|
+
DRONE_HEIGHT
|
|
135
|
+
};
|