@kaynooo/cli-docker 0.2.0 → 0.4.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.
package/dist/dx-cli.js CHANGED
@@ -349,6 +349,9 @@ function optionRows(options, colors = paint(process3.stdout)) {
349
349
 
350
350
  // ../kit/src/text.ts
351
351
  var ANSI = new RegExp(`${String.fromCharCode(27)}\\[[\\d;?]*[a-z]`, "gi");
352
+ function stripAnsi(text) {
353
+ return text.replace(ANSI, "");
354
+ }
352
355
  function distance(a, b) {
353
356
  let previous = Array.from({ length: b.length + 1 }, (_, index) => index);
354
357
  for (const [i, left] of [...a].entries()) {
@@ -745,34 +748,86 @@ var {Glob } = globalThis.Bun;
745
748
  var SESSION_FILES = new Glob("*.json");
746
749
  var ACCOUNT_DIRS = new Glob("*/sessions");
747
750
  // package.json
748
- var version = "0.2.0";
751
+ var version = "0.4.0";
749
752
 
750
753
  // src/dx.ts
751
754
  import process10 from "process";
752
755
 
753
- // src/history.ts
756
+ // ../kit/src/fzf.ts
754
757
  import { existsSync, mkdirSync as mkdirSync2, readFileSync, writeFileSync } from "fs";
755
758
  import { homedir as homedir2 } from "os";
756
759
  import { dirname as dirname2, join as join2 } from "path";
757
760
  import process8 from "process";
758
761
  var REMEMBERED = 20;
759
- function historyFile() {
760
- return join2(process8.env.XDG_CACHE_HOME ?? join2(homedir2(), ".cache"), "dx", "history.json");
762
+ function table(rows) {
763
+ const widths = rows[0]?.cells.map((_, index) => Math.max(...rows.map((row) => Bun.stringWidth(row.cells[index])))) ?? [];
764
+ return rows.map((row) => [
765
+ ...row.hidden,
766
+ ...row.cells.map((cell, index) => index === row.cells.length - 1 ? cell.trimEnd() : `${cell}${" ".repeat(widths[index] + 1 - Bun.stringWidth(cell))}`)
767
+ ].join("\t"));
768
+ }
769
+ function fzf(rows, options) {
770
+ const hidden = options.hidden ?? rows[0]?.hidden.length ?? 0;
771
+ const proc = Bun.spawnSync([
772
+ "fzf",
773
+ "--prompt",
774
+ options.prompt,
775
+ ...options.header === undefined ? [] : ["--header", options.header],
776
+ ...(options.expect ?? []).length > 0 ? ["--expect", (options.expect ?? []).join(",")] : [],
777
+ "--ansi",
778
+ "--reverse",
779
+ "--height",
780
+ options.height ?? "100%",
781
+ "--delimiter",
782
+ "\t",
783
+ "--with-nth",
784
+ `${hidden + 1}..`,
785
+ "--nth",
786
+ `1..${options.searched}`,
787
+ "--tabstop",
788
+ "1",
789
+ "--tiebreak",
790
+ "chunk,index",
791
+ ...options.preview === undefined ? [] : ["--preview", options.preview, "--preview-window", "down,40%"],
792
+ ...(options.bind ?? []).flatMap((bind) => ["--bind", bind]),
793
+ ...options.query === true ? ["--print-query"] : []
794
+ ], {
795
+ stdin: new TextEncoder().encode(table(rows).join(`
796
+ `)),
797
+ stdout: "pipe",
798
+ stderr: "inherit"
799
+ });
800
+ return picked(proc.stdout.toString(), hidden, options);
801
+ }
802
+ function picked(output, hidden, options) {
803
+ const lines = output.split(`
804
+ `);
805
+ const query = options.query === true ? lines.shift() : "";
806
+ const [key, line] = (options.expect ?? []).length > 0 ? [lines[0], lines[1]] : ["", lines[0]];
807
+ if (line !== undefined && line !== "")
808
+ return { key, hidden: line.split("\t").slice(0, hidden), query };
809
+ return options.query === true && key !== "" ? { key, hidden: [], query } : undefined;
810
+ }
811
+ function heightFor(count) {
812
+ return String(Math.max(15, Math.ceil((count + 4) / 0.6)));
813
+ }
814
+ function historyFile(name) {
815
+ return join2(process8.env.XDG_CACHE_HOME ?? join2(homedir2(), ".cache"), name, "history.json");
761
816
  }
762
- function history() {
817
+ function history(name) {
763
818
  try {
764
- const file = historyFile();
819
+ const file = historyFile(name);
765
820
  return existsSync(file) ? JSON.parse(readFileSync(file, "utf-8")) : {};
766
821
  } catch {
767
822
  return {};
768
823
  }
769
824
  }
770
- function runs() {
771
- return history()[process8.cwd()] ?? [];
825
+ function runs(name) {
826
+ return history(name)[process8.cwd()] ?? [];
772
827
  }
773
- function record(run) {
774
- const all = history();
775
- const file = historyFile();
828
+ function record(name, run) {
829
+ const all = history(name);
830
+ const file = historyFile(name);
776
831
  all[process8.cwd()] = [run, ...(all[process8.cwd()] ?? []).filter((earlier) => earlier.id !== run.id)].slice(0, REMEMBERED);
777
832
  mkdirSync2(dirname2(file), { recursive: true });
778
833
  writeFileSync(file, JSON.stringify(all));
@@ -782,6 +837,19 @@ function byRecency(items, previous, idOf) {
782
837
  const recent = items.filter((item) => rank.has(idOf(item))).sort((a, b) => rank.get(idOf(a)) - rank.get(idOf(b)));
783
838
  return [...recent, ...items.filter((item) => !rank.has(idOf(item)))];
784
839
  }
840
+ function word(arg) {
841
+ if (typeof arg === "string")
842
+ return `=${arg}`;
843
+ return "given" in arg ? `+${arg.given}` : `?${arg.ask}`;
844
+ }
845
+ function emit(mode, typed, recorded, command) {
846
+ const words = command.map(word);
847
+ console.log([mode, typed, recorded, ...words].join(`
848
+ `));
849
+ }
850
+ function list(rows) {
851
+ console.log(JSON.stringify(rows));
852
+ }
785
853
 
786
854
  // src/picker.ts
787
855
  import process9 from "process";
@@ -863,9 +931,17 @@ async function recordedNearby() {
863
931
  const dirs = new Set([...up("."), ...children()].map((dir) => resolve(dir)));
864
932
  return (await labels()).filter((stack) => dirs.has(stack.workdir));
865
933
  }
866
- async function output(stack, args) {
934
+ async function attempt(stack, args) {
867
935
  const proc = Bun.spawn([...stack.list.argv, ...args], { cwd: stack.list.cwd, env: stack.list.env, stdout: "pipe", stderr: "pipe" });
868
- return await proc.exited === 0 ? new Response(proc.stdout).text() : "";
936
+ const [text, stderr, code] = await Promise.all([new Response(proc.stdout).text(), new Response(proc.stderr).text(), proc.exited]);
937
+ if (code === 0)
938
+ return { text, error: undefined };
939
+ const last = stderr.trim().split(`
940
+ `).at(-1) ?? "";
941
+ return { text: "", error: last === "" ? `exit ${code}` : last };
942
+ }
943
+ async function output(stack, args) {
944
+ return (await attempt(stack, args)).text;
869
945
  }
870
946
  function containers(text) {
871
947
  const trimmed = text.trim();
@@ -955,47 +1031,17 @@ function row(service) {
955
1031
  cells: [dir === "." ? name : `${colors.dim(`${dir}/`)}${name}`, colors[stateColor(service.state)](service.state), colors.cyan(service.ports), colors.dim(service.image)]
956
1032
  };
957
1033
  }
958
- function table(rows) {
959
- const widths = rows[0]?.cells.map((_, index) => Math.max(...rows.map((each) => Bun.stringWidth(each.cells[index])))) ?? [];
960
- return rows.map((each) => [
961
- ...each.hidden,
962
- ...each.cells.map((cell, index) => index === each.cells.length - 1 ? cell.trimEnd() : `${cell}${" ".repeat(widths[index] + 1 - Bun.stringWidth(cell))}`)
963
- ].join("\t")).join(`
964
- `);
965
- }
966
1034
  function pickService(services) {
967
- const proc = Bun.spawnSync([
968
- "fzf",
969
- "--prompt",
970
- "dx> ",
971
- "--header",
972
- "enter shell or up, ctrl-l logs, ctrl-r restart, ctrl-x stop, ctrl-b rebuild",
973
- "--expect",
974
- Object.keys(KEYS).join(","),
975
- "--ansi",
976
- "--reverse",
977
- "--height",
978
- String(Math.max(15, Math.ceil((services.length + 4) / 0.6))),
979
- "--delimiter",
980
- "\t",
981
- "--with-nth",
982
- "5..",
983
- "--nth",
984
- "1",
985
- "--tabstop",
986
- "1",
987
- "--tiebreak",
988
- "chunk,index",
989
- "--preview",
990
- `${process9.execPath} ${process9.argv[1]} --show {1} {2} {4}`,
991
- "--preview-window",
992
- "down,40%"
993
- ], { stdin: new TextEncoder().encode(table(services.map(row))), stdout: "pipe", stderr: "inherit" });
994
- const [pressed = "", line] = proc.stdout.toString().split(`
995
- `);
996
- const keyed = line?.split("\t")[2];
997
- const service = services.find((candidate) => key(candidate) === keyed);
998
- return service === undefined ? undefined : { service, key: pressed };
1035
+ const picked = fzf(services.map(row), {
1036
+ prompt: "dx> ",
1037
+ header: "enter shell or up, ctrl-l logs, ctrl-r restart, ctrl-x stop, ctrl-b rebuild",
1038
+ expect: Object.keys(KEYS),
1039
+ searched: 1,
1040
+ height: heightFor(services.length),
1041
+ preview: `${process9.execPath} ${process9.argv[1]} --show {1} {2} {4}`
1042
+ });
1043
+ const service = services.find((candidate) => key(candidate) === picked?.hidden[2]);
1044
+ return picked === undefined || service === undefined ? undefined : { service, key: picked.key };
999
1045
  }
1000
1046
 
1001
1047
  // src/dx.ts
@@ -1031,7 +1077,7 @@ function find(services, named) {
1031
1077
  return fail(`no service named ${named}${nearest === undefined ? "" : `, did you mean ${nearest}?`}`, 1);
1032
1078
  }
1033
1079
  function listed2(services) {
1034
- return byRecency(services, runs(), key).map((service) => ({
1080
+ return byRecency(services, runs("dx"), key).map((service) => ({
1035
1081
  label: key(service),
1036
1082
  hint: [service.state, service.ports, service.image].filter(Boolean).join(" \xB7 "),
1037
1083
  typed: key(service),
@@ -1091,13 +1137,13 @@ async function dx(args) {
1091
1137
  console.log(table2(services));
1092
1138
  return 0;
1093
1139
  }
1094
- const picked = pickService(byRecency(services, runs(), key));
1140
+ const picked = pickService(byRecency(services, runs("dx"), key));
1095
1141
  if (picked === undefined)
1096
1142
  return 1;
1097
1143
  service = picked.service;
1098
1144
  action = KEYS[picked.key];
1099
1145
  } else if (named === "-") {
1100
- const last = runs()[0];
1146
+ const last = runs("dx")[0];
1101
1147
  service = last && services.find((candidate) => key(candidate) === last.id);
1102
1148
  if (service === undefined)
1103
1149
  fail(last ? `${last.id} is gone` : "nothing done here yet", 1);
@@ -1110,7 +1156,7 @@ async function dx(args) {
1110
1156
  }
1111
1157
  const chosen = action ?? defaultAction(service);
1112
1158
  const command = await commandFor(service, chosen);
1113
- record({ id: key(service), args: [chosen] });
1159
+ record("dx", { id: key(service), args: [chosen] });
1114
1160
  if (emit) {
1115
1161
  console.log(["run", action === undefined ? key(service) : `${key(service)} ${action}`, "", ...command.map((word) => `=${word}`)].join(`
1116
1162
  `));