rakerun 0.1.0 → 0.1.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/README.md +4 -3
- package/dist/rakerun.js +147 -5
- package/package.json +6 -1
package/README.md
CHANGED
|
@@ -13,7 +13,7 @@ npm install -g @cloudraker/run-cli
|
|
|
13
13
|
rakerun --help
|
|
14
14
|
```
|
|
15
15
|
|
|
16
|
-
Node 22 or newer.
|
|
16
|
+
Node 22 or newer. Two runtime dependencies, Ink and React, for the template picker. The push lint of `run-core` is bundled in.
|
|
17
17
|
|
|
18
18
|
## First contact
|
|
19
19
|
|
|
@@ -22,8 +22,9 @@ rakerun start
|
|
|
22
22
|
```
|
|
23
23
|
|
|
24
24
|
`rakerun start` opens the console sign-up, creates the organization and a CLI token (the browser
|
|
25
|
-
hands it to the terminal), then asks for a project name
|
|
26
|
-
|
|
25
|
+
hands it to the terminal), then asks for a project name, opens the template picker (grouped by
|
|
26
|
+
category; arrow keys, space to select, type to filter, enter), creates the repo, and deploys.
|
|
27
|
+
Three steps, one terminal.
|
|
27
28
|
|
|
28
29
|
## Log in
|
|
29
30
|
|
package/dist/rakerun.js
CHANGED
|
@@ -9,6 +9,9 @@ import { createInterface } from "node:readline/promises";
|
|
|
9
9
|
import { chmodSync, existsSync, mkdirSync, readFileSync, readdirSync, statSync, writeFileSync } from "node:fs";
|
|
10
10
|
import { dirname, join, relative, resolve, sep } from "node:path";
|
|
11
11
|
import { parseArgs } from "node:util";
|
|
12
|
+
import { Box, Text, render, useApp, useInput } from "ink";
|
|
13
|
+
import { useMemo, useState } from "react";
|
|
14
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
12
15
|
//#region \0rolldown/runtime.js
|
|
13
16
|
var __commonJSMin = (cb, mod) => () => (mod || (cb((mod = { exports: {} }).exports, mod), cb = null), mod.exports);
|
|
14
17
|
var __require = /* #__PURE__ */ (() => createRequire(import.meta.url))();
|
|
@@ -15550,7 +15553,7 @@ function normalizeBaseUrl(input) {
|
|
|
15550
15553
|
}
|
|
15551
15554
|
//#endregion
|
|
15552
15555
|
//#region src/help.ts
|
|
15553
|
-
const VERSION = "0.1.
|
|
15556
|
+
const VERSION = "0.1.1";
|
|
15554
15557
|
const HELP = `rakerun ${VERSION}: the command line of cloudraker.run
|
|
15555
15558
|
|
|
15556
15559
|
Usage: rakerun <command> [subcommand] [options]
|
|
@@ -16605,7 +16608,7 @@ async function deploy(ctx) {
|
|
|
16605
16608
|
}
|
|
16606
16609
|
const PLATFORM = `https://${DEFAULT_DOMAIN}`;
|
|
16607
16610
|
/** Picks template ids from a numbered list: `1,3` or ids. Empty: none. */
|
|
16608
|
-
function pickTemplates(answer, templates) {
|
|
16611
|
+
function pickTemplates$1(answer, templates) {
|
|
16609
16612
|
const picks = answer.split(/[\s,]+/).filter(Boolean);
|
|
16610
16613
|
const chosen = [];
|
|
16611
16614
|
for (const pick of picks) {
|
|
@@ -16645,15 +16648,19 @@ async function start(initial) {
|
|
|
16645
16648
|
io.err(REPO_NAME.test(name) ? `${name} exists here and is not empty.` : "Letters, digits, dots, dashes and underscores only.");
|
|
16646
16649
|
}
|
|
16647
16650
|
const dir = resolve(io.cwd, name);
|
|
16648
|
-
const catalog = await call(ctx, "/_console/templates").catch(() => ({
|
|
16651
|
+
const catalog = await call(ctx, "/_console/templates").catch(() => ({
|
|
16652
|
+
templates: [],
|
|
16653
|
+
categories: []
|
|
16654
|
+
}));
|
|
16649
16655
|
let chosen = [];
|
|
16650
|
-
if (catalog.templates.length)
|
|
16656
|
+
if (catalog.templates.length && io.pickTemplates) chosen = await io.pickTemplates(catalog.templates, catalog.categories);
|
|
16657
|
+
else if (catalog.templates.length) {
|
|
16651
16658
|
io.err("\nTemplates:");
|
|
16652
16659
|
catalog.templates.forEach((t, i) => io.err(` ${String(i + 1).padStart(2)}. ${t.id.padEnd(28)} ${t.description}`));
|
|
16653
16660
|
for (;;) {
|
|
16654
16661
|
const answer = await io.ask("Pick templates (numbers, comma separated; empty for a starter agent)", "");
|
|
16655
16662
|
try {
|
|
16656
|
-
chosen = pickTemplates(answer, catalog.templates);
|
|
16663
|
+
chosen = pickTemplates$1(answer, catalog.templates);
|
|
16657
16664
|
break;
|
|
16658
16665
|
} catch (error) {
|
|
16659
16666
|
io.err(error.message);
|
|
@@ -16799,6 +16806,140 @@ async function main(io) {
|
|
|
16799
16806
|
}
|
|
16800
16807
|
}
|
|
16801
16808
|
//#endregion
|
|
16809
|
+
//#region src/ui/template-picker.tsx
|
|
16810
|
+
/** Rows shown at once. The list scrolls around the cursor. */
|
|
16811
|
+
const WINDOW = 14;
|
|
16812
|
+
function rowsOf(templates, categories, filter) {
|
|
16813
|
+
const q = filter.trim().toLowerCase();
|
|
16814
|
+
const shown = q ? templates.filter((t) => `${t.id} ${t.name} ${t.description}`.toLowerCase().includes(q)) : templates;
|
|
16815
|
+
const names = new Map(categories.map((c) => [c.id, c.name]));
|
|
16816
|
+
const rows = [];
|
|
16817
|
+
for (const category of new Set(shown.map((t) => t.category))) {
|
|
16818
|
+
rows.push({
|
|
16819
|
+
kind: "header",
|
|
16820
|
+
name: names.get(category) ?? category
|
|
16821
|
+
});
|
|
16822
|
+
for (const template of shown.filter((t) => t.category === category)) rows.push({
|
|
16823
|
+
kind: "item",
|
|
16824
|
+
template
|
|
16825
|
+
});
|
|
16826
|
+
}
|
|
16827
|
+
return rows;
|
|
16828
|
+
}
|
|
16829
|
+
function Picker({ templates, categories, onDone }) {
|
|
16830
|
+
const { exit } = useApp();
|
|
16831
|
+
const [filter, setFilter] = useState("");
|
|
16832
|
+
const [cursor, setCursor] = useState(0);
|
|
16833
|
+
const [chosen, setChosen] = useState(/* @__PURE__ */ new Set());
|
|
16834
|
+
const rows = useMemo(() => rowsOf(templates, categories, filter), [
|
|
16835
|
+
templates,
|
|
16836
|
+
categories,
|
|
16837
|
+
filter
|
|
16838
|
+
]);
|
|
16839
|
+
const items = rows.map((r, i) => r.kind === "item" ? i : -1).filter((i) => i >= 0);
|
|
16840
|
+
const at = items[Math.min(cursor, Math.max(items.length - 1, 0))];
|
|
16841
|
+
const current = at === void 0 ? null : rows[at].template;
|
|
16842
|
+
useInput((input, key) => {
|
|
16843
|
+
if (key.return) {
|
|
16844
|
+
onDone(templates.filter((t) => chosen.has(t.id)));
|
|
16845
|
+
exit();
|
|
16846
|
+
} else if (key.escape) {
|
|
16847
|
+
onDone([]);
|
|
16848
|
+
exit();
|
|
16849
|
+
} else if (key.upArrow) setCursor((c) => Math.max(0, c - 1));
|
|
16850
|
+
else if (key.downArrow) setCursor((c) => Math.min(items.length - 1, c + 1));
|
|
16851
|
+
else if (input === " " && current) setChosen((s) => {
|
|
16852
|
+
const next = new Set(s);
|
|
16853
|
+
if (next.has(current.id)) next.delete(current.id);
|
|
16854
|
+
else next.add(current.id);
|
|
16855
|
+
return next;
|
|
16856
|
+
});
|
|
16857
|
+
else if (key.backspace || key.delete) {
|
|
16858
|
+
setFilter((f) => f.slice(0, -1));
|
|
16859
|
+
setCursor(0);
|
|
16860
|
+
} else if (input && !key.ctrl && !key.meta) {
|
|
16861
|
+
setFilter((f) => f + input);
|
|
16862
|
+
setCursor(0);
|
|
16863
|
+
}
|
|
16864
|
+
});
|
|
16865
|
+
const start = Math.max(0, Math.min((at ?? 0) - Math.floor(WINDOW / 2), rows.length - WINDOW));
|
|
16866
|
+
const visible = rows.slice(start, start + WINDOW);
|
|
16867
|
+
return /* @__PURE__ */ jsxs(Box, {
|
|
16868
|
+
flexDirection: "column",
|
|
16869
|
+
children: [
|
|
16870
|
+
/* @__PURE__ */ jsxs(Text, { children: [/* @__PURE__ */ jsx(Text, {
|
|
16871
|
+
bold: true,
|
|
16872
|
+
children: "Templates"
|
|
16873
|
+
}), /* @__PURE__ */ jsxs(Text, {
|
|
16874
|
+
dimColor: true,
|
|
16875
|
+
children: [" ", "↑↓ move · space select · type to filter · enter confirm · esc for a starter agent"]
|
|
16876
|
+
})] }),
|
|
16877
|
+
/* @__PURE__ */ jsxs(Text, { children: [
|
|
16878
|
+
/* @__PURE__ */ jsx(Text, {
|
|
16879
|
+
dimColor: true,
|
|
16880
|
+
children: "Filter: "
|
|
16881
|
+
}),
|
|
16882
|
+
/* @__PURE__ */ jsx(Text, { children: filter || " " }),
|
|
16883
|
+
/* @__PURE__ */ jsxs(Text, {
|
|
16884
|
+
dimColor: true,
|
|
16885
|
+
children: [
|
|
16886
|
+
" ",
|
|
16887
|
+
chosen.size,
|
|
16888
|
+
" selected"
|
|
16889
|
+
]
|
|
16890
|
+
})
|
|
16891
|
+
] }),
|
|
16892
|
+
visible.map((row, i) => row.kind === "header" ? /* @__PURE__ */ jsxs(Text, {
|
|
16893
|
+
bold: true,
|
|
16894
|
+
color: "cyan",
|
|
16895
|
+
children: [start + i > 0 ? "\n" : "", row.name]
|
|
16896
|
+
}, `h-${row.name}`) : /* @__PURE__ */ jsxs(Text, {
|
|
16897
|
+
inverse: row.template === current,
|
|
16898
|
+
children: [
|
|
16899
|
+
chosen.has(row.template.id) ? " ◉ " : " ○ ",
|
|
16900
|
+
row.template.id.padEnd(28),
|
|
16901
|
+
/* @__PURE__ */ jsx(Text, {
|
|
16902
|
+
dimColor: true,
|
|
16903
|
+
children: row.template.name
|
|
16904
|
+
})
|
|
16905
|
+
]
|
|
16906
|
+
}, row.template.id)),
|
|
16907
|
+
rows.length > WINDOW ? /* @__PURE__ */ jsxs(Text, {
|
|
16908
|
+
dimColor: true,
|
|
16909
|
+
children: [
|
|
16910
|
+
start + 1,
|
|
16911
|
+
"-",
|
|
16912
|
+
Math.min(start + WINDOW, rows.length),
|
|
16913
|
+
" of ",
|
|
16914
|
+
rows.length
|
|
16915
|
+
]
|
|
16916
|
+
}) : null,
|
|
16917
|
+
current ? /* @__PURE__ */ jsxs(Box, {
|
|
16918
|
+
marginTop: 1,
|
|
16919
|
+
flexDirection: "column",
|
|
16920
|
+
children: [/* @__PURE__ */ jsx(Text, { children: current.description }), /* @__PURE__ */ jsxs(Text, {
|
|
16921
|
+
dimColor: true,
|
|
16922
|
+
children: [current.mode, current.connectors.length ? ` · connectors: ${current.connectors.map((c) => c.required ? `${c.id}*` : c.id).join(", ")}` : ""]
|
|
16923
|
+
})]
|
|
16924
|
+
}) : /* @__PURE__ */ jsx(Text, {
|
|
16925
|
+
dimColor: true,
|
|
16926
|
+
children: "No template matches."
|
|
16927
|
+
})
|
|
16928
|
+
]
|
|
16929
|
+
});
|
|
16930
|
+
}
|
|
16931
|
+
/** Renders the picker on the terminal and resolves with the chosen templates. */
|
|
16932
|
+
function pickTemplates(templates, categories) {
|
|
16933
|
+
return new Promise((resolve) => {
|
|
16934
|
+
let chosen = [];
|
|
16935
|
+
render(/* @__PURE__ */ jsx(Picker, {
|
|
16936
|
+
templates,
|
|
16937
|
+
categories,
|
|
16938
|
+
onDone: (c) => chosen = c
|
|
16939
|
+
}), { exitOnCtrlC: true }).waitUntilExit().then(() => resolve(chosen));
|
|
16940
|
+
});
|
|
16941
|
+
}
|
|
16942
|
+
//#endregion
|
|
16802
16943
|
//#region src/index.ts
|
|
16803
16944
|
/** All of stdin. A pipe ends it; a terminal ends it on Ctrl-D. */
|
|
16804
16945
|
async function stdin() {
|
|
@@ -16971,6 +17112,7 @@ process.exitCode = await main({
|
|
|
16971
17112
|
stdinIsTty: process.stdin.isTTY === true,
|
|
16972
17113
|
readSecret,
|
|
16973
17114
|
ask,
|
|
17115
|
+
pickTemplates: process.stdin.isTTY && process.stdout.isTTY ? pickTemplates : null,
|
|
16974
17116
|
callback,
|
|
16975
17117
|
open,
|
|
16976
17118
|
exec,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "rakerun",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.1",
|
|
4
4
|
"description": "rakerun: the command line of cloudraker.run. Sign up, create projects from templates, deploy agents, run them, and manage secrets, connectors, tokens and Structured Knowledge from the terminal.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"agents",
|
|
@@ -39,8 +39,13 @@
|
|
|
39
39
|
"format:check": "oxfmt --check .",
|
|
40
40
|
"prepublishOnly": "pnpm build"
|
|
41
41
|
},
|
|
42
|
+
"dependencies": {
|
|
43
|
+
"ink": "8.0.0",
|
|
44
|
+
"react": "19.3.1"
|
|
45
|
+
},
|
|
42
46
|
"devDependencies": {
|
|
43
47
|
"@types/node": "26.6.1",
|
|
48
|
+
"@types/react": "^19.3.0",
|
|
44
49
|
"oxfmt": "catalog:",
|
|
45
50
|
"oxlint": "catalog:",
|
|
46
51
|
"run-core": "workspace:*",
|