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 CHANGED
@@ -13,7 +13,7 @@ npm install -g @cloudraker/run-cli
13
13
  rakerun --help
14
14
  ```
15
15
 
16
- Node 22 or newer. No runtime dependency: `run-core` (the push lint) is bundled in.
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 and the templates to import, creates
26
- the repo, and deploys. Three steps, one terminal.
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.0";
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(() => ({ templates: [] }));
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.0",
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:*",