@kaynooo/cli-docker 0.7.0 → 0.8.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/dc-cli.js +3 -3
- package/dist/dlog-cli.js +3 -3
- package/dist/dx-cli.js +85 -14
- package/dist/sf-cli.js +103 -26
- package/package.json +3 -3
- package/src/stack.ts +2 -3
package/dist/dc-cli.js
CHANGED
|
@@ -627,7 +627,6 @@ var DASHES = /^-+/;
|
|
|
627
627
|
function jsonOption(holds) {
|
|
628
628
|
return { type: "boolean", description: holds === undefined ? "print JSON" : `print JSON: ${holds}` };
|
|
629
629
|
}
|
|
630
|
-
|
|
631
630
|
class Exit extends Error {
|
|
632
631
|
code;
|
|
633
632
|
hint;
|
|
@@ -1032,6 +1031,8 @@ function composeError({ code, stderr }) {
|
|
|
1032
1031
|
`).at(-1) ?? "";
|
|
1033
1032
|
return last === "" ? `exit ${code}` : last;
|
|
1034
1033
|
}
|
|
1034
|
+
// ../kit/src/folders.ts
|
|
1035
|
+
var SKIPPED_FOLDERS = new Set(["node_modules", "vendor", "dist", "build", "target", "coverage", "venv", "__pycache__"]);
|
|
1035
1036
|
// ../kit/src/secrets.ts
|
|
1036
1037
|
var SECRET_PART = /token|secret|passw|credential|private|dsn|apikey|accesskey|key$/i;
|
|
1037
1038
|
var SECRET_WORDS = new Set(["key", "pass", "pwd", "auth", "salt", "cookie", "session", "jwt", "ssn", "iban", "card", "cvv", "otp"]);
|
|
@@ -1404,13 +1405,12 @@ class Redactor {
|
|
|
1404
1405
|
var LABEL_FORMAT = ["project", "project.config_files", "project.environment_file", "project.working_dir"].map((label) => `{{.Label "com.docker.compose.${label}"}}`).join("\t");
|
|
1405
1406
|
var COMPOSE_FILE_VAR = /^\s*(?:export\s+)?COMPOSE_FILE\s*=\s*\S/m;
|
|
1406
1407
|
var COMPOSE_FILES = ["compose.yaml", "compose.yml", "docker-compose.yaml", "docker-compose.yml"];
|
|
1407
|
-
var SKIPPED2 = new Set(["node_modules", "vendor", "dist", "build", "target", "venv", "__pycache__"]);
|
|
1408
1408
|
var ACTIONS = ["shell", "logs", "restart", "up", "stop", "build"];
|
|
1409
1409
|
function from(dir, command) {
|
|
1410
1410
|
return dir === "." ? command : ["env", "-C", dir, ...command];
|
|
1411
1411
|
}
|
|
1412
1412
|
function children(dir = ".") {
|
|
1413
|
-
return readdirSync(dir, { withFileTypes: true }).filter((child) => child.isDirectory() && !child.name.startsWith(".") && !
|
|
1413
|
+
return readdirSync(dir, { withFileTypes: true }).filter((child) => child.isDirectory() && !child.name.startsWith(".") && !SKIPPED_FOLDERS.has(child.name)).map((child) => child.name).sort();
|
|
1414
1414
|
}
|
|
1415
1415
|
function up(dir) {
|
|
1416
1416
|
const dirs = [];
|
package/dist/dlog-cli.js
CHANGED
|
@@ -627,7 +627,6 @@ var DASHES = /^-+/;
|
|
|
627
627
|
function jsonOption(holds) {
|
|
628
628
|
return { type: "boolean", description: holds === undefined ? "print JSON" : `print JSON: ${holds}` };
|
|
629
629
|
}
|
|
630
|
-
|
|
631
630
|
class Exit extends Error {
|
|
632
631
|
code;
|
|
633
632
|
hint;
|
|
@@ -1032,6 +1031,8 @@ function composeError({ code, stderr }) {
|
|
|
1032
1031
|
`).at(-1) ?? "";
|
|
1033
1032
|
return last === "" ? `exit ${code}` : last;
|
|
1034
1033
|
}
|
|
1034
|
+
// ../kit/src/folders.ts
|
|
1035
|
+
var SKIPPED_FOLDERS = new Set(["node_modules", "vendor", "dist", "build", "target", "coverage", "venv", "__pycache__"]);
|
|
1035
1036
|
// ../kit/src/secrets.ts
|
|
1036
1037
|
var SECRET_PART = /token|secret|passw|credential|private|dsn|apikey|accesskey|key$/i;
|
|
1037
1038
|
var SECRET_WORDS = new Set(["key", "pass", "pwd", "auth", "salt", "cookie", "session", "jwt", "ssn", "iban", "card", "cvv", "otp"]);
|
|
@@ -1404,13 +1405,12 @@ class Redactor {
|
|
|
1404
1405
|
var LABEL_FORMAT = ["project", "project.config_files", "project.environment_file", "project.working_dir"].map((label) => `{{.Label "com.docker.compose.${label}"}}`).join("\t");
|
|
1405
1406
|
var COMPOSE_FILE_VAR = /^\s*(?:export\s+)?COMPOSE_FILE\s*=\s*\S/m;
|
|
1406
1407
|
var COMPOSE_FILES = ["compose.yaml", "compose.yml", "docker-compose.yaml", "docker-compose.yml"];
|
|
1407
|
-
var SKIPPED2 = new Set(["node_modules", "vendor", "dist", "build", "target", "venv", "__pycache__"]);
|
|
1408
1408
|
var ACTIONS = ["shell", "logs", "restart", "up", "stop", "build"];
|
|
1409
1409
|
function from(dir, command) {
|
|
1410
1410
|
return dir === "." ? command : ["env", "-C", dir, ...command];
|
|
1411
1411
|
}
|
|
1412
1412
|
function children(dir = ".") {
|
|
1413
|
-
return readdirSync(dir, { withFileTypes: true }).filter((child) => child.isDirectory() && !child.name.startsWith(".") && !
|
|
1413
|
+
return readdirSync(dir, { withFileTypes: true }).filter((child) => child.isDirectory() && !child.name.startsWith(".") && !SKIPPED_FOLDERS.has(child.name)).map((child) => child.name).sort();
|
|
1414
1414
|
}
|
|
1415
1415
|
function up(dir) {
|
|
1416
1416
|
const dirs = [];
|
package/dist/dx-cli.js
CHANGED
|
@@ -627,7 +627,6 @@ var DASHES = /^-+/;
|
|
|
627
627
|
function jsonOption(holds) {
|
|
628
628
|
return { type: "boolean", description: holds === undefined ? "print JSON" : `print JSON: ${holds}` };
|
|
629
629
|
}
|
|
630
|
-
|
|
631
630
|
class Exit extends Error {
|
|
632
631
|
code;
|
|
633
632
|
hint;
|
|
@@ -1032,6 +1031,8 @@ function composeError({ code, stderr }) {
|
|
|
1032
1031
|
`).at(-1) ?? "";
|
|
1033
1032
|
return last === "" ? `exit ${code}` : last;
|
|
1034
1033
|
}
|
|
1034
|
+
// ../kit/src/folders.ts
|
|
1035
|
+
var SKIPPED_FOLDERS = new Set(["node_modules", "vendor", "dist", "build", "target", "coverage", "venv", "__pycache__"]);
|
|
1035
1036
|
// ../kit/src/secrets.ts
|
|
1036
1037
|
var SECRET_PART = /token|secret|passw|credential|private|dsn|apikey|accesskey|key$/i;
|
|
1037
1038
|
var SECRET_WORDS = new Set(["key", "pass", "pwd", "auth", "salt", "cookie", "session", "jwt", "ssn", "iban", "card", "cvv", "otp"]);
|
|
@@ -1503,6 +1504,14 @@ var KEYS = {
|
|
|
1503
1504
|
"\x1B[B": "down",
|
|
1504
1505
|
"\x1BOB": "down",
|
|
1505
1506
|
"\x0E": "down",
|
|
1507
|
+
"\x1B[5~": "pageup",
|
|
1508
|
+
"\x1B[6~": "pagedown",
|
|
1509
|
+
"\x1B[H": "home",
|
|
1510
|
+
"\x1B[1~": "home",
|
|
1511
|
+
"\x1BOH": "home",
|
|
1512
|
+
"\x1B[F": "end",
|
|
1513
|
+
"\x1B[4~": "end",
|
|
1514
|
+
"\x1BOF": "end",
|
|
1506
1515
|
"\r": "enter",
|
|
1507
1516
|
"\n": "enter",
|
|
1508
1517
|
"\x03": "cancel",
|
|
@@ -1511,7 +1520,10 @@ var KEYS = {
|
|
|
1511
1520
|
"\b": "erase",
|
|
1512
1521
|
"\x15": "clear",
|
|
1513
1522
|
"\x17": "word",
|
|
1514
|
-
"\t": "tab"
|
|
1523
|
+
"\t": "tab",
|
|
1524
|
+
"\x1F": "help",
|
|
1525
|
+
"\x1BOP": "help",
|
|
1526
|
+
"\x1B[11~": "help"
|
|
1515
1527
|
};
|
|
1516
1528
|
var CTRL_KEY = /^ctrl-[abd-gkln-z]$/;
|
|
1517
1529
|
function repeat(value, count) {
|
|
@@ -1581,7 +1593,7 @@ function parseKeys(input, paste = { active: false }, ctrls = []) {
|
|
|
1581
1593
|
});
|
|
1582
1594
|
}
|
|
1583
1595
|
function createState(rows, { multi = false, prompt = "> ", query = "", header = "", expect = [], searched = Number.POSITIVE_INFINITY } = {}) {
|
|
1584
|
-
const state = { rows: [], items: [], styles: [], searchable: [], searched, header, expect, key: "", status: "", query, matches: [], cursor: 0, marked: new Set, multi, prompt, done: undefined };
|
|
1596
|
+
const state = { rows: [], items: [], styles: [], searchable: [], searched, header, expect, key: "", status: "", query, matches: [], cursor: 0, help: false, page: 10, marked: new Set, multi, prompt, done: undefined };
|
|
1585
1597
|
return withRows(state, rows);
|
|
1586
1598
|
}
|
|
1587
1599
|
function withRows(state, rows, header = state.header) {
|
|
@@ -1603,6 +1615,8 @@ function highlighted(state) {
|
|
|
1603
1615
|
function press(state, key) {
|
|
1604
1616
|
if (state.done !== undefined)
|
|
1605
1617
|
return state;
|
|
1618
|
+
if (state.help)
|
|
1619
|
+
return { ...state, help: false };
|
|
1606
1620
|
if (typeof key === "object") {
|
|
1607
1621
|
if ("text" in key)
|
|
1608
1622
|
return withQuery(state, state.query + key.text);
|
|
@@ -1613,8 +1627,18 @@ function press(state, key) {
|
|
|
1613
1627
|
return move(state, -1);
|
|
1614
1628
|
case "down":
|
|
1615
1629
|
return move(state, 1);
|
|
1630
|
+
case "pageup":
|
|
1631
|
+
return move(state, -state.page);
|
|
1632
|
+
case "pagedown":
|
|
1633
|
+
return move(state, state.page);
|
|
1634
|
+
case "home":
|
|
1635
|
+
return move(state, -state.matches.length);
|
|
1636
|
+
case "end":
|
|
1637
|
+
return move(state, state.matches.length);
|
|
1616
1638
|
case "cancel":
|
|
1617
1639
|
return { ...state, done: "cancel" };
|
|
1640
|
+
case "help":
|
|
1641
|
+
return { ...state, help: true };
|
|
1618
1642
|
case "erase":
|
|
1619
1643
|
return withQuery(state, [...state.query].slice(0, -1).join(""));
|
|
1620
1644
|
case "clear":
|
|
@@ -1794,7 +1818,18 @@ function fitStyled(line, width, plain = identity) {
|
|
|
1794
1818
|
function caretColumn(state, layout) {
|
|
1795
1819
|
return Math.min(layout.listWidth, Bun.stringWidth(state.prompt) + Bun.stringWidth(state.query) + 1);
|
|
1796
1820
|
}
|
|
1821
|
+
function helpLines(state, layout, theme) {
|
|
1822
|
+
const lines = [
|
|
1823
|
+
"type to filter, up, down, ctrl-p, ctrl-n, pgup, pgdn, home, end move",
|
|
1824
|
+
`enter picks${state.multi ? ", tab marks" : ""}, ctrl-u clears the query, ctrl-w a word, esc or ctrl-c cancels`,
|
|
1825
|
+
...state.expect.length > 0 ? [`${state.expect.join(", ")} end the pick as this command asks`] : []
|
|
1826
|
+
];
|
|
1827
|
+
const title = "keys, any key closes this help";
|
|
1828
|
+
return [{ text: theme.accent(title), width: title.length }, ...lines.map((line) => fit(` ${line}`, layout.listWidth, theme.label))];
|
|
1829
|
+
}
|
|
1797
1830
|
function listLines(state, layout, theme) {
|
|
1831
|
+
if (state.help)
|
|
1832
|
+
return helpLines(state, layout, theme);
|
|
1798
1833
|
const header = state.header === "" ? [] : state.header.split(`
|
|
1799
1834
|
`).map((line) => fitStyled(line, layout.listWidth, theme.label));
|
|
1800
1835
|
const rows = Math.max(1, layout.listHeight - 2 - header.length);
|
|
@@ -1803,7 +1838,8 @@ function listLines(state, layout, theme) {
|
|
|
1803
1838
|
const marks = (state.marked.size > 0 ? ` (${state.marked.size} marked)` : "") + (state.status === "" ? "" : ` ${state.status}`);
|
|
1804
1839
|
const promptWidth = Bun.stringWidth(state.prompt);
|
|
1805
1840
|
const query = fit(state.query, Math.max(1, layout.listWidth - promptWidth), identity);
|
|
1806
|
-
const
|
|
1841
|
+
const place = state.matches.length > rows ? ` (${state.cursor + 1} of ${state.matches.length})` : "";
|
|
1842
|
+
const count = fit(` ${state.matches.length}/${state.items.length}${place}${marks}`, layout.listWidth, theme.label);
|
|
1807
1843
|
const lines = [{ text: theme.accent(state.prompt) + query.text, width: promptWidth + query.width }, count, ...header];
|
|
1808
1844
|
if (state.matches.length === 0)
|
|
1809
1845
|
lines.push(fit(" no match", layout.listWidth, theme.label));
|
|
@@ -1830,6 +1866,7 @@ function render(state, layout, theme, preview = []) {
|
|
|
1830
1866
|
|
|
1831
1867
|
// ../kit/src/picker/terminal.ts
|
|
1832
1868
|
var PREVIEW_DELAY_MS = 120;
|
|
1869
|
+
var ESCAPE_WAIT_MS = 30;
|
|
1833
1870
|
var OPEN = { ctrl: "open" };
|
|
1834
1871
|
var HIDE_CURSOR = "\x1B[?25l";
|
|
1835
1872
|
var SHOW_CURSOR = "\x1B[?25h";
|
|
@@ -1841,14 +1878,24 @@ function up(lines) {
|
|
|
1841
1878
|
return lines > 0 ? `\x1B[${lines}A` : "";
|
|
1842
1879
|
}
|
|
1843
1880
|
function openTerminal() {
|
|
1844
|
-
let
|
|
1881
|
+
let mode;
|
|
1845
1882
|
try {
|
|
1846
|
-
|
|
1883
|
+
mode = new tty.ReadStream(openSync("/dev/tty", "r"));
|
|
1847
1884
|
} catch {
|
|
1848
1885
|
return fail("no terminal to read keys from (/dev/tty)");
|
|
1849
1886
|
}
|
|
1887
|
+
const keys = Bun.file("/dev/tty").stream().getReader();
|
|
1850
1888
|
const out = process14.stderr;
|
|
1851
1889
|
let resize;
|
|
1890
|
+
async function read(onChunk) {
|
|
1891
|
+
const decoder = new TextDecoder;
|
|
1892
|
+
try {
|
|
1893
|
+
for (let chunk = await keys.read();!chunk.done; chunk = await keys.read())
|
|
1894
|
+
onChunk(decoder.decode(chunk.value, { stream: true }));
|
|
1895
|
+
} catch {
|
|
1896
|
+
onChunk("\x03");
|
|
1897
|
+
}
|
|
1898
|
+
}
|
|
1852
1899
|
return {
|
|
1853
1900
|
get columns() {
|
|
1854
1901
|
return out.columns || 80;
|
|
@@ -1860,17 +1907,18 @@ function openTerminal() {
|
|
|
1860
1907
|
write: (text) => void out.write(text),
|
|
1861
1908
|
listen(onChunk, onResize) {
|
|
1862
1909
|
resize = onResize;
|
|
1863
|
-
|
|
1910
|
+
mode.setRawMode(true);
|
|
1864
1911
|
out.on("resize", onResize);
|
|
1865
|
-
|
|
1912
|
+
read(onChunk);
|
|
1866
1913
|
},
|
|
1867
1914
|
close() {
|
|
1868
1915
|
if (resize !== undefined)
|
|
1869
1916
|
out.off("resize", resize);
|
|
1870
1917
|
try {
|
|
1871
|
-
|
|
1918
|
+
mode.setRawMode(false);
|
|
1872
1919
|
} finally {
|
|
1873
|
-
|
|
1920
|
+
mode.destroy();
|
|
1921
|
+
keys.cancel();
|
|
1874
1922
|
}
|
|
1875
1923
|
}
|
|
1876
1924
|
};
|
|
@@ -1885,20 +1933,29 @@ async function select(rows, settings, terminal = openTerminal()) {
|
|
|
1885
1933
|
let shownLine;
|
|
1886
1934
|
let finish;
|
|
1887
1935
|
let restored = false;
|
|
1936
|
+
let escape;
|
|
1937
|
+
let caret;
|
|
1888
1938
|
function draw(clear = false) {
|
|
1889
1939
|
if (restored)
|
|
1890
1940
|
return;
|
|
1891
1941
|
const layout = layoutFor({ columns: terminal.columns, rows: terminal.rows }, settings.height, settings.preview !== undefined, settings.previewPosition);
|
|
1892
1942
|
let frame = HIDE_CURSOR;
|
|
1893
1943
|
if (reserved !== layout.height || clear) {
|
|
1944
|
+
frame += clear && caret !== undefined ? up(Math.floor((caret - 1) / terminal.columns)) : "";
|
|
1894
1945
|
frame += CLEAR_BELOW + `
|
|
1895
1946
|
`.repeat(layout.height - 1) + up(layout.height - 1);
|
|
1896
1947
|
reserved = layout.height;
|
|
1897
1948
|
}
|
|
1949
|
+
caret = caretColumn(state, layout);
|
|
1898
1950
|
frame += `\r${render(state, layout, theme, preview).map((line) => `\x1B[2K${line}`).join(`
|
|
1899
|
-
`)}${up(layout.height - 1)}\r\x1B[${
|
|
1951
|
+
`)}${up(layout.height - 1)}\r\x1B[${caret}G${SHOW_CURSOR}`;
|
|
1900
1952
|
terminal.write(frame);
|
|
1901
1953
|
}
|
|
1954
|
+
function pageSize() {
|
|
1955
|
+
const headerLines = state.header === "" ? 0 : state.header.split(`
|
|
1956
|
+
`).length;
|
|
1957
|
+
return Math.max(1, layoutFor({ columns: terminal.columns, rows: terminal.rows }, settings.height, settings.preview !== undefined, settings.previewPosition).listHeight - 2 - headerLines);
|
|
1958
|
+
}
|
|
1902
1959
|
function refreshPreview() {
|
|
1903
1960
|
const at = highlighted(state);
|
|
1904
1961
|
if (settings.preview === undefined || at === shownLine)
|
|
@@ -1928,6 +1985,7 @@ async function select(rows, settings, terminal = openTerminal()) {
|
|
|
1928
1985
|
return;
|
|
1929
1986
|
restored = true;
|
|
1930
1987
|
clearTimeout(previewTimer);
|
|
1988
|
+
clearTimeout(escape);
|
|
1931
1989
|
previewing.abort();
|
|
1932
1990
|
terminal.write(`${CLEAR_BELOW}${PASTE_OFF}${SHOW_CURSOR}`);
|
|
1933
1991
|
terminal.close();
|
|
@@ -1966,7 +2024,7 @@ async function select(rows, settings, terminal = openTerminal()) {
|
|
|
1966
2024
|
if (key === OPEN || typeof key === "object" && "ctrl" in key && key.ctrl === reload?.key)
|
|
1967
2025
|
await reloadRows();
|
|
1968
2026
|
else
|
|
1969
|
-
state = press(state, key);
|
|
2027
|
+
state = press({ ...state, page: pageSize() }, key);
|
|
1970
2028
|
}
|
|
1971
2029
|
} finally {
|
|
1972
2030
|
draining = false;
|
|
@@ -1979,6 +2037,20 @@ async function select(rows, settings, terminal = openTerminal()) {
|
|
|
1979
2037
|
draw();
|
|
1980
2038
|
}
|
|
1981
2039
|
function onChunk(chunk) {
|
|
2040
|
+
if (escape !== undefined) {
|
|
2041
|
+
clearTimeout(escape);
|
|
2042
|
+
escape = undefined;
|
|
2043
|
+
chunk = `\x1B${chunk}`;
|
|
2044
|
+
} else if (chunk === "\x1B") {
|
|
2045
|
+
escape = setTimeout(() => {
|
|
2046
|
+
escape = undefined;
|
|
2047
|
+
takeKeys("\x1B");
|
|
2048
|
+
}, ESCAPE_WAIT_MS);
|
|
2049
|
+
return;
|
|
2050
|
+
}
|
|
2051
|
+
takeKeys(chunk);
|
|
2052
|
+
}
|
|
2053
|
+
function takeKeys(chunk) {
|
|
1982
2054
|
const keys = parseKeys(chunk, paste, [...settings.expect ?? [], ...reload?.key === undefined ? [] : [reload.key]]);
|
|
1983
2055
|
if (busy && keys.includes("cancel")) {
|
|
1984
2056
|
stop();
|
|
@@ -2092,13 +2164,12 @@ import { dirname as dirname6, join as join8, relative, resolve as resolve3 } fro
|
|
|
2092
2164
|
var LABEL_FORMAT = ["project", "project.config_files", "project.environment_file", "project.working_dir"].map((label) => `{{.Label "com.docker.compose.${label}"}}`).join("\t");
|
|
2093
2165
|
var COMPOSE_FILE_VAR = /^\s*(?:export\s+)?COMPOSE_FILE\s*=\s*\S/m;
|
|
2094
2166
|
var COMPOSE_FILES = ["compose.yaml", "compose.yml", "docker-compose.yaml", "docker-compose.yml"];
|
|
2095
|
-
var SKIPPED2 = new Set(["node_modules", "vendor", "dist", "build", "target", "venv", "__pycache__"]);
|
|
2096
2167
|
var ACTIONS = ["shell", "logs", "restart", "up", "stop", "build"];
|
|
2097
2168
|
function from(dir, command) {
|
|
2098
2169
|
return dir === "." ? command : ["env", "-C", dir, ...command];
|
|
2099
2170
|
}
|
|
2100
2171
|
function children(dir = ".") {
|
|
2101
|
-
return readdirSync(dir, { withFileTypes: true }).filter((child) => child.isDirectory() && !child.name.startsWith(".") && !
|
|
2172
|
+
return readdirSync(dir, { withFileTypes: true }).filter((child) => child.isDirectory() && !child.name.startsWith(".") && !SKIPPED_FOLDERS.has(child.name)).map((child) => child.name).sort();
|
|
2102
2173
|
}
|
|
2103
2174
|
function up2(dir) {
|
|
2104
2175
|
const dirs = [];
|
package/dist/sf-cli.js
CHANGED
|
@@ -627,7 +627,6 @@ var DASHES = /^-+/;
|
|
|
627
627
|
function jsonOption(holds) {
|
|
628
628
|
return { type: "boolean", description: holds === undefined ? "print JSON" : `print JSON: ${holds}` };
|
|
629
629
|
}
|
|
630
|
-
|
|
631
630
|
class Exit extends Error {
|
|
632
631
|
code;
|
|
633
632
|
hint;
|
|
@@ -1032,6 +1031,8 @@ function composeError({ code, stderr }) {
|
|
|
1032
1031
|
`).at(-1) ?? "";
|
|
1033
1032
|
return last === "" ? `exit ${code}` : last;
|
|
1034
1033
|
}
|
|
1034
|
+
// ../kit/src/folders.ts
|
|
1035
|
+
var SKIPPED_FOLDERS = new Set(["node_modules", "vendor", "dist", "build", "target", "coverage", "venv", "__pycache__"]);
|
|
1035
1036
|
// ../kit/src/secrets.ts
|
|
1036
1037
|
var SECRET_PART = /token|secret|passw|credential|private|dsn|apikey|accesskey|key$/i;
|
|
1037
1038
|
var SECRET_WORDS = new Set(["key", "pass", "pwd", "auth", "salt", "cookie", "session", "jwt", "ssn", "iban", "card", "cvv", "otp"]);
|
|
@@ -1156,6 +1157,14 @@ var KEYS = {
|
|
|
1156
1157
|
"\x1B[B": "down",
|
|
1157
1158
|
"\x1BOB": "down",
|
|
1158
1159
|
"\x0E": "down",
|
|
1160
|
+
"\x1B[5~": "pageup",
|
|
1161
|
+
"\x1B[6~": "pagedown",
|
|
1162
|
+
"\x1B[H": "home",
|
|
1163
|
+
"\x1B[1~": "home",
|
|
1164
|
+
"\x1BOH": "home",
|
|
1165
|
+
"\x1B[F": "end",
|
|
1166
|
+
"\x1B[4~": "end",
|
|
1167
|
+
"\x1BOF": "end",
|
|
1159
1168
|
"\r": "enter",
|
|
1160
1169
|
"\n": "enter",
|
|
1161
1170
|
"\x03": "cancel",
|
|
@@ -1164,7 +1173,10 @@ var KEYS = {
|
|
|
1164
1173
|
"\b": "erase",
|
|
1165
1174
|
"\x15": "clear",
|
|
1166
1175
|
"\x17": "word",
|
|
1167
|
-
"\t": "tab"
|
|
1176
|
+
"\t": "tab",
|
|
1177
|
+
"\x1F": "help",
|
|
1178
|
+
"\x1BOP": "help",
|
|
1179
|
+
"\x1B[11~": "help"
|
|
1168
1180
|
};
|
|
1169
1181
|
var CTRL_KEY = /^ctrl-[abd-gkln-z]$/;
|
|
1170
1182
|
function repeat(value, count) {
|
|
@@ -1234,7 +1246,7 @@ function parseKeys(input, paste = { active: false }, ctrls = []) {
|
|
|
1234
1246
|
});
|
|
1235
1247
|
}
|
|
1236
1248
|
function createState(rows, { multi = false, prompt = "> ", query = "", header = "", expect = [], searched = Number.POSITIVE_INFINITY } = {}) {
|
|
1237
|
-
const state = { rows: [], items: [], styles: [], searchable: [], searched, header, expect, key: "", status: "", query, matches: [], cursor: 0, marked: new Set, multi, prompt, done: undefined };
|
|
1249
|
+
const state = { rows: [], items: [], styles: [], searchable: [], searched, header, expect, key: "", status: "", query, matches: [], cursor: 0, help: false, page: 10, marked: new Set, multi, prompt, done: undefined };
|
|
1238
1250
|
return withRows(state, rows);
|
|
1239
1251
|
}
|
|
1240
1252
|
function withRows(state, rows, header = state.header) {
|
|
@@ -1256,6 +1268,8 @@ function highlighted(state) {
|
|
|
1256
1268
|
function press(state, key) {
|
|
1257
1269
|
if (state.done !== undefined)
|
|
1258
1270
|
return state;
|
|
1271
|
+
if (state.help)
|
|
1272
|
+
return { ...state, help: false };
|
|
1259
1273
|
if (typeof key === "object") {
|
|
1260
1274
|
if ("text" in key)
|
|
1261
1275
|
return withQuery(state, state.query + key.text);
|
|
@@ -1266,8 +1280,18 @@ function press(state, key) {
|
|
|
1266
1280
|
return move(state, -1);
|
|
1267
1281
|
case "down":
|
|
1268
1282
|
return move(state, 1);
|
|
1283
|
+
case "pageup":
|
|
1284
|
+
return move(state, -state.page);
|
|
1285
|
+
case "pagedown":
|
|
1286
|
+
return move(state, state.page);
|
|
1287
|
+
case "home":
|
|
1288
|
+
return move(state, -state.matches.length);
|
|
1289
|
+
case "end":
|
|
1290
|
+
return move(state, state.matches.length);
|
|
1269
1291
|
case "cancel":
|
|
1270
1292
|
return { ...state, done: "cancel" };
|
|
1293
|
+
case "help":
|
|
1294
|
+
return { ...state, help: true };
|
|
1271
1295
|
case "erase":
|
|
1272
1296
|
return withQuery(state, [...state.query].slice(0, -1).join(""));
|
|
1273
1297
|
case "clear":
|
|
@@ -1447,7 +1471,18 @@ function fitStyled(line, width, plain = identity) {
|
|
|
1447
1471
|
function caretColumn(state, layout) {
|
|
1448
1472
|
return Math.min(layout.listWidth, Bun.stringWidth(state.prompt) + Bun.stringWidth(state.query) + 1);
|
|
1449
1473
|
}
|
|
1474
|
+
function helpLines(state, layout, theme) {
|
|
1475
|
+
const lines = [
|
|
1476
|
+
"type to filter, up, down, ctrl-p, ctrl-n, pgup, pgdn, home, end move",
|
|
1477
|
+
`enter picks${state.multi ? ", tab marks" : ""}, ctrl-u clears the query, ctrl-w a word, esc or ctrl-c cancels`,
|
|
1478
|
+
...state.expect.length > 0 ? [`${state.expect.join(", ")} end the pick as this command asks`] : []
|
|
1479
|
+
];
|
|
1480
|
+
const title = "keys, any key closes this help";
|
|
1481
|
+
return [{ text: theme.accent(title), width: title.length }, ...lines.map((line) => fit(` ${line}`, layout.listWidth, theme.label))];
|
|
1482
|
+
}
|
|
1450
1483
|
function listLines(state, layout, theme) {
|
|
1484
|
+
if (state.help)
|
|
1485
|
+
return helpLines(state, layout, theme);
|
|
1451
1486
|
const header = state.header === "" ? [] : state.header.split(`
|
|
1452
1487
|
`).map((line) => fitStyled(line, layout.listWidth, theme.label));
|
|
1453
1488
|
const rows = Math.max(1, layout.listHeight - 2 - header.length);
|
|
@@ -1456,7 +1491,8 @@ function listLines(state, layout, theme) {
|
|
|
1456
1491
|
const marks = (state.marked.size > 0 ? ` (${state.marked.size} marked)` : "") + (state.status === "" ? "" : ` ${state.status}`);
|
|
1457
1492
|
const promptWidth = Bun.stringWidth(state.prompt);
|
|
1458
1493
|
const query = fit(state.query, Math.max(1, layout.listWidth - promptWidth), identity);
|
|
1459
|
-
const
|
|
1494
|
+
const place = state.matches.length > rows ? ` (${state.cursor + 1} of ${state.matches.length})` : "";
|
|
1495
|
+
const count = fit(` ${state.matches.length}/${state.items.length}${place}${marks}`, layout.listWidth, theme.label);
|
|
1460
1496
|
const lines = [{ text: theme.accent(state.prompt) + query.text, width: promptWidth + query.width }, count, ...header];
|
|
1461
1497
|
if (state.matches.length === 0)
|
|
1462
1498
|
lines.push(fit(" no match", layout.listWidth, theme.label));
|
|
@@ -1483,6 +1519,7 @@ function render(state, layout, theme, preview = []) {
|
|
|
1483
1519
|
|
|
1484
1520
|
// ../kit/src/picker/terminal.ts
|
|
1485
1521
|
var PREVIEW_DELAY_MS = 120;
|
|
1522
|
+
var ESCAPE_WAIT_MS = 30;
|
|
1486
1523
|
var OPEN = { ctrl: "open" };
|
|
1487
1524
|
var HIDE_CURSOR = "\x1B[?25l";
|
|
1488
1525
|
var SHOW_CURSOR = "\x1B[?25h";
|
|
@@ -1494,14 +1531,24 @@ function up(lines) {
|
|
|
1494
1531
|
return lines > 0 ? `\x1B[${lines}A` : "";
|
|
1495
1532
|
}
|
|
1496
1533
|
function openTerminal() {
|
|
1497
|
-
let
|
|
1534
|
+
let mode;
|
|
1498
1535
|
try {
|
|
1499
|
-
|
|
1536
|
+
mode = new tty.ReadStream(openSync("/dev/tty", "r"));
|
|
1500
1537
|
} catch {
|
|
1501
1538
|
return fail("no terminal to read keys from (/dev/tty)");
|
|
1502
1539
|
}
|
|
1540
|
+
const keys = Bun.file("/dev/tty").stream().getReader();
|
|
1503
1541
|
const out = process14.stderr;
|
|
1504
1542
|
let resize;
|
|
1543
|
+
async function read(onChunk) {
|
|
1544
|
+
const decoder = new TextDecoder;
|
|
1545
|
+
try {
|
|
1546
|
+
for (let chunk = await keys.read();!chunk.done; chunk = await keys.read())
|
|
1547
|
+
onChunk(decoder.decode(chunk.value, { stream: true }));
|
|
1548
|
+
} catch {
|
|
1549
|
+
onChunk("\x03");
|
|
1550
|
+
}
|
|
1551
|
+
}
|
|
1505
1552
|
return {
|
|
1506
1553
|
get columns() {
|
|
1507
1554
|
return out.columns || 80;
|
|
@@ -1513,17 +1560,18 @@ function openTerminal() {
|
|
|
1513
1560
|
write: (text) => void out.write(text),
|
|
1514
1561
|
listen(onChunk, onResize) {
|
|
1515
1562
|
resize = onResize;
|
|
1516
|
-
|
|
1563
|
+
mode.setRawMode(true);
|
|
1517
1564
|
out.on("resize", onResize);
|
|
1518
|
-
|
|
1565
|
+
read(onChunk);
|
|
1519
1566
|
},
|
|
1520
1567
|
close() {
|
|
1521
1568
|
if (resize !== undefined)
|
|
1522
1569
|
out.off("resize", resize);
|
|
1523
1570
|
try {
|
|
1524
|
-
|
|
1571
|
+
mode.setRawMode(false);
|
|
1525
1572
|
} finally {
|
|
1526
|
-
|
|
1573
|
+
mode.destroy();
|
|
1574
|
+
keys.cancel();
|
|
1527
1575
|
}
|
|
1528
1576
|
}
|
|
1529
1577
|
};
|
|
@@ -1538,20 +1586,29 @@ async function select(rows, settings, terminal = openTerminal()) {
|
|
|
1538
1586
|
let shownLine;
|
|
1539
1587
|
let finish;
|
|
1540
1588
|
let restored = false;
|
|
1589
|
+
let escape;
|
|
1590
|
+
let caret;
|
|
1541
1591
|
function draw(clear = false) {
|
|
1542
1592
|
if (restored)
|
|
1543
1593
|
return;
|
|
1544
1594
|
const layout = layoutFor({ columns: terminal.columns, rows: terminal.rows }, settings.height, settings.preview !== undefined, settings.previewPosition);
|
|
1545
1595
|
let frame = HIDE_CURSOR;
|
|
1546
1596
|
if (reserved !== layout.height || clear) {
|
|
1597
|
+
frame += clear && caret !== undefined ? up(Math.floor((caret - 1) / terminal.columns)) : "";
|
|
1547
1598
|
frame += CLEAR_BELOW + `
|
|
1548
1599
|
`.repeat(layout.height - 1) + up(layout.height - 1);
|
|
1549
1600
|
reserved = layout.height;
|
|
1550
1601
|
}
|
|
1602
|
+
caret = caretColumn(state, layout);
|
|
1551
1603
|
frame += `\r${render(state, layout, theme, preview).map((line) => `\x1B[2K${line}`).join(`
|
|
1552
|
-
`)}${up(layout.height - 1)}\r\x1B[${
|
|
1604
|
+
`)}${up(layout.height - 1)}\r\x1B[${caret}G${SHOW_CURSOR}`;
|
|
1553
1605
|
terminal.write(frame);
|
|
1554
1606
|
}
|
|
1607
|
+
function pageSize() {
|
|
1608
|
+
const headerLines = state.header === "" ? 0 : state.header.split(`
|
|
1609
|
+
`).length;
|
|
1610
|
+
return Math.max(1, layoutFor({ columns: terminal.columns, rows: terminal.rows }, settings.height, settings.preview !== undefined, settings.previewPosition).listHeight - 2 - headerLines);
|
|
1611
|
+
}
|
|
1555
1612
|
function refreshPreview() {
|
|
1556
1613
|
const at = highlighted(state);
|
|
1557
1614
|
if (settings.preview === undefined || at === shownLine)
|
|
@@ -1581,6 +1638,7 @@ async function select(rows, settings, terminal = openTerminal()) {
|
|
|
1581
1638
|
return;
|
|
1582
1639
|
restored = true;
|
|
1583
1640
|
clearTimeout(previewTimer);
|
|
1641
|
+
clearTimeout(escape);
|
|
1584
1642
|
previewing.abort();
|
|
1585
1643
|
terminal.write(`${CLEAR_BELOW}${PASTE_OFF}${SHOW_CURSOR}`);
|
|
1586
1644
|
terminal.close();
|
|
@@ -1619,7 +1677,7 @@ async function select(rows, settings, terminal = openTerminal()) {
|
|
|
1619
1677
|
if (key === OPEN || typeof key === "object" && "ctrl" in key && key.ctrl === reload?.key)
|
|
1620
1678
|
await reloadRows();
|
|
1621
1679
|
else
|
|
1622
|
-
state = press(state, key);
|
|
1680
|
+
state = press({ ...state, page: pageSize() }, key);
|
|
1623
1681
|
}
|
|
1624
1682
|
} finally {
|
|
1625
1683
|
draining = false;
|
|
@@ -1632,6 +1690,20 @@ async function select(rows, settings, terminal = openTerminal()) {
|
|
|
1632
1690
|
draw();
|
|
1633
1691
|
}
|
|
1634
1692
|
function onChunk(chunk) {
|
|
1693
|
+
if (escape !== undefined) {
|
|
1694
|
+
clearTimeout(escape);
|
|
1695
|
+
escape = undefined;
|
|
1696
|
+
chunk = `\x1B${chunk}`;
|
|
1697
|
+
} else if (chunk === "\x1B") {
|
|
1698
|
+
escape = setTimeout(() => {
|
|
1699
|
+
escape = undefined;
|
|
1700
|
+
takeKeys("\x1B");
|
|
1701
|
+
}, ESCAPE_WAIT_MS);
|
|
1702
|
+
return;
|
|
1703
|
+
}
|
|
1704
|
+
takeKeys(chunk);
|
|
1705
|
+
}
|
|
1706
|
+
function takeKeys(chunk) {
|
|
1635
1707
|
const keys = parseKeys(chunk, paste, [...settings.expect ?? [], ...reload?.key === undefined ? [] : [reload.key]]);
|
|
1636
1708
|
if (busy && keys.includes("cancel")) {
|
|
1637
1709
|
stop();
|
|
@@ -1738,11 +1810,9 @@ function list(rows) {
|
|
|
1738
1810
|
|
|
1739
1811
|
// ../kit/src/prompt.ts
|
|
1740
1812
|
import process16 from "process";
|
|
1741
|
-
var
|
|
1742
|
-
|
|
1743
|
-
|
|
1744
|
-
function readKeys(input) {
|
|
1745
|
-
return (input.match(KEY_SEQUENCES2) ?? []).flatMap((sequence) => KEYS2[sequence] ?? (CONTROL3.test(sequence) ? [] : [{ text: sequence }]));
|
|
1813
|
+
var PROMPT_KEYS = new Set(["up", "down", "pageup", "pagedown", "home", "end", "enter", "cancel", "erase"]);
|
|
1814
|
+
function readKeys(input, paste = { active: false }) {
|
|
1815
|
+
return parseKeys(input, paste).filter((key) => typeof key === "object" ? "text" in key : PROMPT_KEYS.has(key));
|
|
1746
1816
|
}
|
|
1747
1817
|
async function input2(question, { default: fallback, mask = false, validate, ...settings } = {}) {
|
|
1748
1818
|
let value = "";
|
|
@@ -1781,22 +1851,29 @@ async function ask(question, { hint, terminal = process16 }, render, press) {
|
|
|
1781
1851
|
if (!isInteractive(terminal))
|
|
1782
1852
|
fail(`cannot ask "${question}" off a terminal`, 2, hint);
|
|
1783
1853
|
const colors = paint(stderr);
|
|
1784
|
-
let drawn =
|
|
1854
|
+
let drawn = [];
|
|
1855
|
+
let current = "asking";
|
|
1785
1856
|
let outcome;
|
|
1857
|
+
const columns = () => stderr.columns || 80;
|
|
1786
1858
|
const draw = (state) => {
|
|
1787
|
-
const frame = render(colors, state, { columns:
|
|
1788
|
-
|
|
1859
|
+
const frame = render(colors, state, { columns: columns(), rows: stderr.rows || 24 });
|
|
1860
|
+
const rows = drawn.reduce((sum, line) => sum + Math.max(1, Math.ceil(Bun.stringWidth(line) / columns())), 0);
|
|
1861
|
+
stderr.write(`${rows > 1 ? `\x1B[${rows - 1}A` : ""}\r\x1B[0J${frame.join(`
|
|
1789
1862
|
`)}`);
|
|
1790
|
-
drawn = frame
|
|
1863
|
+
drawn = frame;
|
|
1864
|
+
current = state;
|
|
1791
1865
|
};
|
|
1866
|
+
const redraw = () => draw(current);
|
|
1867
|
+
const paste = { active: false };
|
|
1792
1868
|
try {
|
|
1869
|
+
stderr.on?.("resize", redraw);
|
|
1793
1870
|
stdin.setRawMode(true);
|
|
1794
1871
|
stdin.resume();
|
|
1795
|
-
stderr.write("\x1B[?25l");
|
|
1872
|
+
stderr.write("\x1B[?25l\x1B[?2004h");
|
|
1796
1873
|
draw("asking");
|
|
1797
1874
|
return await new Promise((resolve) => {
|
|
1798
1875
|
function onData(data) {
|
|
1799
|
-
for (const key of readKeys(data.toString())) {
|
|
1876
|
+
for (const key of readKeys(data.toString(), paste)) {
|
|
1800
1877
|
const pressed = press(key);
|
|
1801
1878
|
if (pressed !== undefined) {
|
|
1802
1879
|
settle(pressed);
|
|
@@ -1818,9 +1895,10 @@ async function ask(question, { hint, terminal = process16 }, render, press) {
|
|
|
1818
1895
|
stdin.on("end", onEnd);
|
|
1819
1896
|
});
|
|
1820
1897
|
} finally {
|
|
1898
|
+
stderr.off?.("resize", redraw);
|
|
1821
1899
|
draw(outcome ?? "cancel");
|
|
1822
1900
|
stderr.write(`
|
|
1823
|
-
\x1B[?25h`);
|
|
1901
|
+
\x1B[?25h\x1B[?2004l`);
|
|
1824
1902
|
stdin.setRawMode(false);
|
|
1825
1903
|
stdin.pause();
|
|
1826
1904
|
}
|
|
@@ -2184,13 +2262,12 @@ class Redactor {
|
|
|
2184
2262
|
var LABEL_FORMAT = ["project", "project.config_files", "project.environment_file", "project.working_dir"].map((label) => `{{.Label "com.docker.compose.${label}"}}`).join("\t");
|
|
2185
2263
|
var COMPOSE_FILE_VAR = /^\s*(?:export\s+)?COMPOSE_FILE\s*=\s*\S/m;
|
|
2186
2264
|
var COMPOSE_FILES = ["compose.yaml", "compose.yml", "docker-compose.yaml", "docker-compose.yml"];
|
|
2187
|
-
var SKIPPED2 = new Set(["node_modules", "vendor", "dist", "build", "target", "venv", "__pycache__"]);
|
|
2188
2265
|
var ACTIONS = ["shell", "logs", "restart", "up", "stop", "build"];
|
|
2189
2266
|
function from(dir, command) {
|
|
2190
2267
|
return dir === "." ? command : ["env", "-C", dir, ...command];
|
|
2191
2268
|
}
|
|
2192
2269
|
function children(dir = ".") {
|
|
2193
|
-
return readdirSync(dir, { withFileTypes: true }).filter((child) => child.isDirectory() && !child.name.startsWith(".") && !
|
|
2270
|
+
return readdirSync(dir, { withFileTypes: true }).filter((child) => child.isDirectory() && !child.name.startsWith(".") && !SKIPPED_FOLDERS.has(child.name)).map((child) => child.name).sort();
|
|
2194
2271
|
}
|
|
2195
2272
|
function up2(dir) {
|
|
2196
2273
|
const dirs = [];
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@kaynooo/cli-docker",
|
|
3
3
|
"type": "module",
|
|
4
|
-
"version": "0.
|
|
4
|
+
"version": "0.8.1",
|
|
5
5
|
"description": "dc, dx, dlog and sf: docker compose on the stack found here, including one only its containers tell of, a compose service's shell, logs, restart, stop or rebuild, every service's health and recent errors in one call, and a Symfony console command picked and run.",
|
|
6
6
|
"author": "Kaynooo <contact@kaynooo.fr>",
|
|
7
7
|
"license": "MIT",
|
|
@@ -37,7 +37,7 @@
|
|
|
37
37
|
"sf": "$npm_execpath src/sf-cli.ts"
|
|
38
38
|
},
|
|
39
39
|
"devDependencies": {
|
|
40
|
-
"@kaynooo/cli-env": "0.
|
|
41
|
-
"@kaynooo/cli-kit": "0.
|
|
40
|
+
"@kaynooo/cli-env": "0.12.0",
|
|
41
|
+
"@kaynooo/cli-kit": "0.27.1"
|
|
42
42
|
}
|
|
43
43
|
}
|
package/src/stack.ts
CHANGED
|
@@ -3,7 +3,7 @@ import { existsSync, readdirSync, readFileSync } from 'node:fs'
|
|
|
3
3
|
import { dirname, join, relative, resolve } from 'node:path'
|
|
4
4
|
import { loadKnown } from '@kaynooo/cli-env/files'
|
|
5
5
|
import { Redactor } from '@kaynooo/cli-env/redactor'
|
|
6
|
-
import { captureOutput, composeError } from '@kaynooo/cli-kit'
|
|
6
|
+
import { captureOutput, composeError, SKIPPED_FOLDERS } from '@kaynooo/cli-kit'
|
|
7
7
|
|
|
8
8
|
/** How to reach one compose project: the command a user runs, and the one this package lists it with. */
|
|
9
9
|
export interface Stack {
|
|
@@ -54,7 +54,6 @@ export interface Container {
|
|
|
54
54
|
const LABEL_FORMAT = ['project', 'project.config_files', 'project.environment_file', 'project.working_dir'].map(label => `{{.Label "com.docker.compose.${label}"}}`).join('\t')
|
|
55
55
|
const COMPOSE_FILE_VAR = /^\s*(?:export\s+)?COMPOSE_FILE\s*=\s*\S/m
|
|
56
56
|
const COMPOSE_FILES = ['compose.yaml', 'compose.yml', 'docker-compose.yaml', 'docker-compose.yml']
|
|
57
|
-
const SKIPPED = new Set(['node_modules', 'vendor', 'dist', 'build', 'target', 'venv', '__pycache__'])
|
|
58
57
|
|
|
59
58
|
export const ACTIONS: Action[] = ['shell', 'logs', 'restart', 'up', 'stop', 'build']
|
|
60
59
|
|
|
@@ -66,7 +65,7 @@ export function from(dir: string, command: string[]): string[] {
|
|
|
66
65
|
/** The direct child folders, hidden and built ones aside. */
|
|
67
66
|
export function children(dir = '.'): string[] {
|
|
68
67
|
return readdirSync(dir, { withFileTypes: true })
|
|
69
|
-
.filter(child => child.isDirectory() && !child.name.startsWith('.') && !
|
|
68
|
+
.filter(child => child.isDirectory() && !child.name.startsWith('.') && !SKIPPED_FOLDERS.has(child.name))
|
|
70
69
|
.map(child => child.name)
|
|
71
70
|
.sort()
|
|
72
71
|
}
|