@kurtel/cli 0.1.0 → 0.1.3

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.
@@ -1,35 +1,95 @@
1
1
  import { c, symbols } from "../ui/colors.js";
2
2
  import { Spinner, sleep } from "../ui/spinner.js";
3
- import { loadConfig, saveConfig } from "../lib/config.js";
4
- import { previewNotice } from "./_shared.js";
3
+ import { loadConfig, saveSession, clearSession } from "../lib/config.js";
4
+ import { startDeviceAuth, pollDeviceAuth } from "../lib/api.js";
5
+ import { openBrowser } from "../lib/browser.js";
5
6
  export async function loginCommand() {
6
7
  const config = loadConfig();
7
- if (config.loggedIn) {
8
- console.log(`${symbols.check} Already signed in as ${c.indigo(config.account ?? "you@example.com")}.`);
8
+ if (config.loggedIn && config.token) {
9
+ console.log(`${symbols.check} Already signed in as ${c.indigo(config.account ?? "your account")}${config.organization ? c.dim(` · ${config.organization}`) : ""}.`);
10
+ console.log(`${c.dim("Run")} ${c.indigo("kurtel logout")} ${c.dim("to switch accounts.")}`);
9
11
  return;
10
12
  }
13
+ // 1. Begin the device flow.
14
+ let session;
15
+ const startSpin = new Spinner("Requesting a device code…").start();
16
+ try {
17
+ session = await startDeviceAuth();
18
+ startSpin.stop();
19
+ }
20
+ catch (e) {
21
+ startSpin.fail(`Couldn't reach Kurtel: ${e instanceof Error ? e.message : String(e)}`);
22
+ console.log(`${c.dim("Check your connection, or set")} ${c.indigo("KURTEL_API_URL")} ${c.dim("for local dev.")}`);
23
+ process.exitCode = 1;
24
+ return;
25
+ }
26
+ // 2. Show the URL + code, try to open the browser.
11
27
  console.log("");
12
- console.log(`${c.indigo(symbols.arrow)} Sign in to Kurtel — we'll open your browser to authorize.`);
13
- const fakeUrl = "https://kurtel.ai/cli/auth?code=KRTL-PREVIEW";
14
- console.log(` ${c.dim(fakeUrl)}`);
28
+ console.log(`${c.indigo(symbols.arrow)} Open this URL to authorize:`);
29
+ console.log(` ${c.bold(session.verification_url)}`);
15
30
  console.log("");
16
- const spin = new Spinner("Waiting for browser authorization…").start();
17
- await sleep(1200);
18
- spin.info("Authorization step is a preview stub.");
19
- config.loggedIn = true;
20
- config.account = "you@example.com";
21
- saveConfig(config);
22
- console.log(`${symbols.check} Signed in as ${c.indigo(config.account)} ${c.dim("(local preview session)")}`);
23
- previewNotice("Authentication");
31
+ console.log(`${c.dim("Device code:")} ${c.indigo(session.user_code)}`);
32
+ console.log("");
33
+ const opened = openBrowser(session.verification_url);
34
+ if (opened) {
35
+ console.log(c.dim("Opening your browser…"));
36
+ }
37
+ // 3. Poll until authorized / expired / timeout.
38
+ const spin = new Spinner("Waiting for you to authorize in the browser…").start();
39
+ const intervalMs = Math.max(1, session.interval) * 1000;
40
+ const deadline = Date.now() + session.expires_in * 1000;
41
+ while (Date.now() < deadline) {
42
+ await sleep(intervalMs);
43
+ let res;
44
+ try {
45
+ res = await pollDeviceAuth(session.device_code);
46
+ }
47
+ catch {
48
+ continue; // transient network error — keep polling
49
+ }
50
+ if (res.status === "authorized") {
51
+ saveSession({
52
+ token: res.token,
53
+ account: res.account,
54
+ organization: res.organization,
55
+ });
56
+ spin.succeed(`Signed in as ${c.indigo(res.account ?? "your account")}${res.organization ? c.dim(` · ${res.organization}`) : ""}`);
57
+ console.log(c.dim("You can close the browser tab."));
58
+ return;
59
+ }
60
+ if (res.status === "denied") {
61
+ spin.fail("Authorization was denied.");
62
+ process.exitCode = 1;
63
+ return;
64
+ }
65
+ if (res.status === "expired" || res.status === "not_found" || res.status === "used") {
66
+ spin.fail("This login request expired. Run `kurtel login` again.");
67
+ process.exitCode = 1;
68
+ return;
69
+ }
70
+ // status === "pending" → keep waiting
71
+ }
72
+ spin.fail("Timed out waiting for authorization. Run `kurtel login` again.");
73
+ process.exitCode = 1;
24
74
  }
25
75
  export async function logoutCommand() {
26
76
  const config = loadConfig();
27
77
  if (!config.loggedIn) {
28
- console.log(`${c.dim("You're not signed in.")}`);
78
+ console.log(c.dim("You're not signed in."));
29
79
  return;
30
80
  }
31
- config.loggedIn = false;
32
- delete config.account;
33
- saveConfig(config);
81
+ clearSession();
34
82
  console.log(`${symbols.check} Signed out.`);
35
83
  }
84
+ export function whoamiCommand() {
85
+ const config = loadConfig();
86
+ if (!config.loggedIn || !config.token) {
87
+ console.log(`${c.dim("Not signed in. Run")} ${c.indigo("kurtel login")}${c.dim(".")}`);
88
+ process.exitCode = 1;
89
+ return;
90
+ }
91
+ console.log(`${c.indigo(symbols.info)} ${c.white(config.account ?? "unknown account")}`);
92
+ if (config.organization) {
93
+ console.log(`${c.gray("org")} ${c.white(config.organization)}`);
94
+ }
95
+ }
@@ -1,27 +1,41 @@
1
1
  import { c, symbols } from "../ui/colors.js";
2
- import { Spinner, sleep } from "../ui/spinner.js";
3
- import { previewNotice } from "./_shared.js";
2
+ import { createRun, AuthError } from "../lib/api.js";
3
+ import { isEngineName } from "../lib/engines.js";
4
4
  export async function runCommand(task, opts) {
5
5
  if (!task || task.trim() === "") {
6
- console.log(`${c.red(symbols.cross)} Provide a task, e.g. ${c.indigo('kurtel run "fix flaky auth test"')}`);
6
+ console.log(`${c.red(symbols.cross)} Provide a task, e.g. ${c.indigo('kurtel run "fix the flaky auth test"')}`);
7
7
  process.exitCode = 1;
8
8
  return;
9
9
  }
10
- const id = "agent-" + Math.random().toString(16).slice(2, 6);
11
- const repo = opts.repo ?? "(current repo)";
12
- console.log("");
13
- console.log(`${c.gray("task")} ${c.white(task)}`);
14
- console.log(`${c.gray("repo")} ${c.white(repo)}`);
15
- console.log(`${c.gray("branch")} ${c.white(opts.branch ?? "main")}`);
16
- console.log("");
17
- const spin = new Spinner("Provisioning isolated sandbox…").start();
18
- await sleep(700);
19
- spin.update("Booting agent (codex + claude-code)…");
20
- await sleep(700);
21
- spin.update("Cloning repository into sandbox…");
22
- await sleep(600);
23
- spin.succeed(`Launched ${c.indigo(id)} ${c.dim(`(${opts.detach ? "detached" : "attached"})`)}`);
24
- console.log("");
25
- previewNotice("Launching agents");
26
- console.log(`${c.dim("Once connected, follow it with")} ${c.indigo(`kurtel logs ${id}`)}${c.dim(".")}`);
10
+ if (opts.engine && !isEngineName(opts.engine)) {
11
+ console.log(`${c.red(symbols.cross)} Unknown engine ${c.white(opts.engine)}. Use ${c.indigo("claude-code")} or ${c.indigo("codex")}.`);
12
+ process.exitCode = 1;
13
+ return;
14
+ }
15
+ try {
16
+ const run = await createRun({
17
+ task,
18
+ repo: opts.repo,
19
+ branch: opts.branch,
20
+ engine: opts.engine,
21
+ });
22
+ console.log("");
23
+ console.log(`${symbols.check} Launched ${c.indigo(run.id)}`);
24
+ console.log(`${c.gray("task")} ${c.white(run.task)}`);
25
+ console.log(`${c.gray("engine")} ${c.white(run.engine)}`);
26
+ if (run.repo)
27
+ console.log(`${c.gray("repo")} ${c.white(run.repo)}`);
28
+ console.log(`${c.gray("status")} ${c.indigo(run.status)}`);
29
+ console.log("");
30
+ console.log(`${c.dim("Follow it with")} ${c.indigo(`kurtel logs ${run.id} --follow`)}`);
31
+ }
32
+ catch (e) {
33
+ if (e instanceof AuthError) {
34
+ console.log(`${c.red(symbols.cross)} ${e.message}`);
35
+ }
36
+ else {
37
+ console.log(`${c.red(symbols.cross)} Failed to launch: ${e instanceof Error ? e.message : String(e)}`);
38
+ }
39
+ process.exitCode = 1;
40
+ }
27
41
  }
@@ -0,0 +1,54 @@
1
+ import { c } from "../ui/colors.js";
2
+ import { listRuns, AuthError } from "../lib/api.js";
3
+ function pad(s, width) {
4
+ // eslint-disable-next-line no-control-regex
5
+ const len = s.replace(/\x1b\[[0-9;]*m/g, "").length;
6
+ return s + " ".repeat(Math.max(0, width - len));
7
+ }
8
+ function statusLabel(status) {
9
+ switch (status) {
10
+ case "running": return c.indigo("● running");
11
+ case "queued": return c.gray("○ queued");
12
+ case "succeeded": return c.green("✔ done");
13
+ case "failed": return c.red("✖ failed");
14
+ case "canceled": return c.yellow("• canceled");
15
+ default: return status;
16
+ }
17
+ }
18
+ function ago(iso) {
19
+ const diff = Date.now() - new Date(iso).getTime();
20
+ const m = Math.floor(diff / 60000);
21
+ if (m < 1)
22
+ return "just now";
23
+ if (m < 60)
24
+ return `${m}m ago`;
25
+ const h = Math.floor(m / 60);
26
+ if (h < 24)
27
+ return `${h}h ago`;
28
+ return `${Math.floor(h / 24)}d ago`;
29
+ }
30
+ export async function runsCommand() {
31
+ try {
32
+ const { runs } = await listRuns();
33
+ if (!runs.length) {
34
+ console.log(c.dim("No runs yet. Launch one with `kurtel run \"…\"`."));
35
+ return;
36
+ }
37
+ console.log("");
38
+ console.log(pad(c.gray("ID"), 14) + pad(c.gray("STATUS"), 14) +
39
+ pad(c.gray("ENGINE"), 14) + pad(c.gray("AGE"), 10) + c.gray("TASK"));
40
+ for (const r of runs) {
41
+ console.log(pad(c.indigo(r.id), 14) + pad(statusLabel(r.status), 14) +
42
+ pad(c.dim(r.engine), 14) + pad(c.dim(ago(r.created_at)), 10) +
43
+ c.white(r.task.length > 50 ? r.task.slice(0, 49) + "…" : r.task));
44
+ }
45
+ console.log("");
46
+ }
47
+ catch (e) {
48
+ if (e instanceof AuthError)
49
+ console.log(`${c.red("✖")} ${e.message}`);
50
+ else
51
+ console.log(`${c.red("✖")} ${e instanceof Error ? e.message : String(e)}`);
52
+ process.exitCode = 1;
53
+ }
54
+ }
@@ -1,50 +1,70 @@
1
- import { c } from "../ui/colors.js";
2
- import { Spinner, sleep } from "../ui/spinner.js";
3
- import { SAMPLE_AGENTS, statusColor, levelBadge } from "../lib/agents.js";
4
- import { previewNotice } from "./_shared.js";
5
- function findAgent(id) {
6
- return SAMPLE_AGENTS.find((a) => a.id === id) ?? SAMPLE_AGENTS[0];
1
+ import { c, symbols } from "../ui/colors.js";
2
+ import { sleep } from "../ui/spinner.js";
3
+ import { getRun, getRunLogs, cancelRun, AuthError } from "../lib/api.js";
4
+ function handleErr(e) {
5
+ if (e instanceof AuthError)
6
+ console.log(`${c.red(symbols.cross)} ${e.message}`);
7
+ else
8
+ console.log(`${c.red(symbols.cross)} ${e instanceof Error ? e.message : String(e)}`);
9
+ process.exitCode = 1;
7
10
  }
8
- const FAKE_LOG_LINES = [
9
- ["plan", "Reading repository structure and conventions"],
10
- ["plan", "Drafting change set across 4 files"],
11
- ["edit", "src/webhooks/handler.ts — add idempotency guard"],
12
- ["test", "Running test suite in sandbox"],
13
- ["learn", "Captured pattern: prefer repository-scoped idempotency keys"],
14
- ["done", "Opened pull request #482"],
15
- ];
16
- export async function logsCommand(id) {
17
- const agent = findAgent(id);
18
- console.log(`\n${c.dim("Streaming logs for")} ${c.indigo(agent.id)} ${c.dim(`· ${agent.repo}`)}\n`);
19
- for (const [tag, msg] of FAKE_LOG_LINES) {
20
- const ts = c.dim(new Date().toLocaleTimeString());
21
- const label = tag === "learn"
22
- ? c.cyan(`[${tag}]`)
23
- : tag === "done"
24
- ? c.green(`[${tag}]`)
25
- : c.indigo(`[${tag}]`);
26
- console.log(`${ts} ${label} ${c.white(msg)}`);
27
- await sleep(450);
11
+ function statusLabel(status) {
12
+ switch (status) {
13
+ case "running": return c.indigo(" running");
14
+ case "queued": return c.gray(" queued");
15
+ case "succeeded": return c.green(" succeeded");
16
+ case "failed": return c.red("✖ failed");
17
+ case "canceled": return c.yellow(" canceled");
18
+ default: return status;
28
19
  }
29
- console.log("");
30
- previewNotice("Live logs");
31
20
  }
32
- export function statusCommand(id) {
33
- const a = findAgent(id);
34
- console.log("");
35
- console.log(`${c.gray("id")} ${c.indigo(a.id)}`);
36
- console.log(`${c.gray("task")} ${c.white(a.task)}`);
37
- console.log(`${c.gray("repo")} ${c.white(a.repo)}`);
38
- console.log(`${c.gray("level")} ${levelBadge(a.level)}`);
39
- console.log(`${c.gray("status")} ${statusColor(a.status)}`);
40
- console.log(`${c.gray("progress")} ${c.white(a.progress + "%")}`);
41
- console.log("");
42
- previewNotice("Agent status");
21
+ const TERMINAL = ["succeeded", "failed", "canceled"];
22
+ export async function logsCommand(id, opts = {}) {
23
+ try {
24
+ let printed = 0;
25
+ for (;;) {
26
+ const { status, logs } = await getRunLogs(id);
27
+ if (logs.length > printed) {
28
+ process.stdout.write(logs.slice(printed));
29
+ printed = logs.length;
30
+ }
31
+ if (!opts.follow || TERMINAL.includes(status)) {
32
+ console.log(`\n${statusLabel(status)}`);
33
+ return;
34
+ }
35
+ await sleep(1500);
36
+ }
37
+ }
38
+ catch (e) {
39
+ handleErr(e);
40
+ }
41
+ }
42
+ export async function statusCommand(id) {
43
+ try {
44
+ const run = await getRun(id);
45
+ console.log("");
46
+ console.log(`${c.gray("id")} ${c.indigo(run.id)}`);
47
+ console.log(`${c.gray("task")} ${c.white(run.task)}`);
48
+ if (run.repo)
49
+ console.log(`${c.gray("repo")} ${c.white(run.repo)}`);
50
+ console.log(`${c.gray("engine")} ${c.white(run.engine)}`);
51
+ console.log(`${c.gray("status")} ${statusLabel(run.status)}`);
52
+ if (run.result?.summary)
53
+ console.log(`${c.gray("summary")} ${c.white(run.result.summary)}`);
54
+ if (run.error)
55
+ console.log(`${c.gray("error")} ${c.red(run.error)}`);
56
+ console.log("");
57
+ }
58
+ catch (e) {
59
+ handleErr(e);
60
+ }
43
61
  }
44
62
  export async function stopCommand(id) {
45
- const a = findAgent(id);
46
- const spin = new Spinner(`Stopping ${c.indigo(a.id)} and tearing down sandbox…`).start();
47
- await sleep(800);
48
- spin.succeed(`Stopped ${c.indigo(a.id)} ${c.dim("· sandbox destroyed")}`);
49
- previewNotice("Stopping agents");
63
+ try {
64
+ await cancelRun(id);
65
+ console.log(`${symbols.check} Canceled ${c.indigo(id)} ${c.dim("· sandbox torn down")}`);
66
+ }
67
+ catch (e) {
68
+ handleErr(e);
69
+ }
50
70
  }
package/dist/index.js CHANGED
@@ -5,8 +5,9 @@ import { loadConfig } from "./lib/config.js";
5
5
  import { c, symbols } from "./ui/colors.js";
6
6
  import { startSession } from "./session/repl.js";
7
7
  import { runCommand } from "./commands/run.js";
8
- import { loginCommand, logoutCommand } from "./commands/auth.js";
8
+ import { loginCommand, logoutCommand, whoamiCommand } from "./commands/auth.js";
9
9
  import { agentsCommand } from "./commands/agents.js";
10
+ import { runsCommand } from "./commands/runs.js";
10
11
  import { logsCommand, statusCommand, stopCommand } from "./commands/runtime.js";
11
12
  import { configCommand } from "./commands/config.js";
12
13
  import { initCommand, doctorCommand } from "./commands/project.js";
@@ -26,12 +27,16 @@ program
26
27
  .command("run")
27
28
  .description("Launch a cloud agent on a task")
28
29
  .argument("[task...]", "What you want the agent to do")
29
- .option("-r, --repo <repo>", "Target repository")
30
+ .option("-r, --repo <repo>", "Target repository (owner/name or URL)")
30
31
  .option("-b, --branch <branch>", "Base branch", "main")
31
- .option("-d, --detach", "Launch without attaching to logs", false)
32
+ .option("-e, --engine <engine>", "Engine: claude-code | codex")
32
33
  .action(async (taskParts, opts) => {
33
34
  await runCommand((taskParts ?? []).join(" "), opts);
34
35
  });
36
+ program
37
+ .command("runs")
38
+ .description("List your recent runs")
39
+ .action(() => runsCommand());
35
40
  program
36
41
  .command("agents")
37
42
  .alias("ps")
@@ -39,13 +44,14 @@ program
39
44
  .action(() => agentsCommand());
40
45
  program
41
46
  .command("logs")
42
- .description("Stream an agent's logs")
43
- .argument("<id>", "Agent id (e.g. agent-7f3a)")
44
- .action(async (id) => logsCommand(id));
47
+ .description("Stream a run's logs")
48
+ .argument("<id>", "Run id")
49
+ .option("-f, --follow", "Keep streaming until the run finishes", false)
50
+ .action(async (id, opts) => logsCommand(id, opts));
45
51
  program
46
52
  .command("status")
47
- .description("Show an agent's status")
48
- .argument("<id>", "Agent id")
53
+ .description("Show a run's status")
54
+ .argument("<id>", "Run id")
49
55
  .action((id) => statusCommand(id));
50
56
  program
51
57
  .command("stop")
@@ -60,6 +66,10 @@ program
60
66
  .command("logout")
61
67
  .description("Sign out")
62
68
  .action(async () => logoutCommand());
69
+ program
70
+ .command("whoami")
71
+ .description("Show the signed-in account")
72
+ .action(() => whoamiCommand());
63
73
  program
64
74
  .command("config")
65
75
  .description("View or change local configuration")
@@ -0,0 +1,78 @@
1
+ import { apiUrl } from "./config.js";
2
+ async function postJSON(path, body) {
3
+ const res = await fetch(`${apiUrl()}${path}`, {
4
+ method: "POST",
5
+ headers: { "Content-Type": "application/json" },
6
+ body: JSON.stringify(body),
7
+ });
8
+ const text = await res.text();
9
+ let data = {};
10
+ try {
11
+ data = text ? JSON.parse(text) : {};
12
+ }
13
+ catch {
14
+ // non-JSON response
15
+ }
16
+ if (!res.ok && res.status >= 500) {
17
+ const msg = data?.error ?? `server error (${res.status})`;
18
+ throw new Error(msg);
19
+ }
20
+ return data;
21
+ }
22
+ export function startDeviceAuth() {
23
+ return postJSON("/api/cli/auth/start", {});
24
+ }
25
+ export function pollDeviceAuth(deviceCode) {
26
+ return postJSON("/api/cli/auth/poll", {
27
+ device_code: deviceCode,
28
+ });
29
+ }
30
+ // ── Authenticated API (uses the stored CLI token) ──────────────────────────
31
+ import { loadConfig } from "./config.js";
32
+ export class AuthError extends Error {
33
+ }
34
+ async function authed(method, path, body) {
35
+ const token = loadConfig().token;
36
+ if (!token)
37
+ throw new AuthError("Not signed in. Run `kurtel login`.");
38
+ const res = await fetch(`${apiUrl()}${path}`, {
39
+ method,
40
+ headers: {
41
+ "Content-Type": "application/json",
42
+ Authorization: `Bearer ${token}`,
43
+ },
44
+ body: body === undefined ? undefined : JSON.stringify(body),
45
+ });
46
+ const text = await res.text();
47
+ let data = {};
48
+ try {
49
+ data = text ? JSON.parse(text) : {};
50
+ }
51
+ catch { }
52
+ if (res.status === 401) {
53
+ throw new AuthError("Your session is invalid or expired. Run `kurtel login`.");
54
+ }
55
+ if (!res.ok) {
56
+ const msg = data?.error ?? `error ${res.status}`;
57
+ throw new Error(msg);
58
+ }
59
+ return data;
60
+ }
61
+ export function verifyToken() {
62
+ return authed("GET", "/api/cli/me");
63
+ }
64
+ export function createRun(input) {
65
+ return authed("POST", "/api/runs", input);
66
+ }
67
+ export function listRuns() {
68
+ return authed("GET", "/api/runs");
69
+ }
70
+ export function getRun(id) {
71
+ return authed("GET", `/api/runs/${id}`);
72
+ }
73
+ export function getRunLogs(id) {
74
+ return authed("GET", `/api/runs/${id}/logs`);
75
+ }
76
+ export function cancelRun(id) {
77
+ return authed("DELETE", `/api/runs/${id}`);
78
+ }
@@ -0,0 +1,28 @@
1
+ import { spawn } from "node:child_process";
2
+ // Best-effort: open a URL in the default browser across macOS / Windows / Linux.
3
+ export function openBrowser(url) {
4
+ try {
5
+ const platform = process.platform;
6
+ let command;
7
+ let args;
8
+ if (platform === "darwin") {
9
+ command = "open";
10
+ args = [url];
11
+ }
12
+ else if (platform === "win32") {
13
+ command = "cmd";
14
+ args = ["/c", "start", "", url];
15
+ }
16
+ else {
17
+ command = "xdg-open";
18
+ args = [url];
19
+ }
20
+ const child = spawn(command, args, { stdio: "ignore", detached: true });
21
+ child.on("error", () => { });
22
+ child.unref();
23
+ return true;
24
+ }
25
+ catch {
26
+ return false;
27
+ }
28
+ }
@@ -1,6 +1,31 @@
1
1
  import { homedir } from "node:os";
2
2
  import { join } from "node:path";
3
3
  import { existsSync, mkdirSync, readFileSync, writeFileSync, } from "node:fs";
4
+ // Where the CLI talks to. Override with KURTEL_API_URL for local dev, e.g.
5
+ // KURTEL_API_URL=http://localhost:3000 kurtel login
6
+ export function apiUrl() {
7
+ return (process.env.KURTEL_API_URL ??
8
+ loadConfig().apiUrl ??
9
+ "htt");
10
+ }
11
+ export function saveSession(session) {
12
+ const config = loadConfig();
13
+ config.loggedIn = true;
14
+ config.token = session.token;
15
+ if (session.account)
16
+ config.account = session.account;
17
+ if (session.organization)
18
+ config.organization = session.organization;
19
+ saveConfig(config);
20
+ }
21
+ export function clearSession() {
22
+ const config = loadConfig();
23
+ config.loggedIn = false;
24
+ delete config.token;
25
+ delete config.account;
26
+ delete config.organization;
27
+ saveConfig(config);
28
+ }
4
29
  const DIR = join(homedir(), ".kurtel");
5
30
  const FILE = join(DIR, "config.json");
6
31
  const DEFAULTS = {
@@ -0,0 +1,4 @@
1
+ export const ENGINE_NAMES = ["claude-code", "codex"];
2
+ export function isEngineName(v) {
3
+ return ENGINE_NAMES.includes(v);
4
+ }
package/package.json CHANGED
@@ -1,12 +1,14 @@
1
1
  {
2
2
  "name": "@kurtel/cli",
3
- "version": "0.1.0",
3
+ "version": "0.1.3",
4
4
  "description": "Launch self-improving coding agents in the cloud — the Kurtel CLI.",
5
5
  "type": "module",
6
6
  "bin": {
7
7
  "kurtel": "dist/index.js"
8
8
  },
9
- "publishConfig": { "access": "public" },
9
+ "publishConfig": {
10
+ "access": "public"
11
+ },
10
12
  "files": [
11
13
  "dist"
12
14
  ],