@commandgarden/cli 0.1.0 → 1.0.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.
Files changed (2) hide show
  1. package/dist/main.js +346 -41
  2. package/package.json +3 -3
package/dist/main.js CHANGED
@@ -7,7 +7,7 @@ var __export = (target, all) => {
7
7
 
8
8
  // src/main.ts
9
9
  import { Command } from "commander";
10
- import { join as join2, dirname as dirname2 } from "path";
10
+ import { join as join3, dirname } from "path";
11
11
  import { homedir } from "os";
12
12
  import { fileURLToPath } from "url";
13
13
 
@@ -58,6 +58,54 @@ var DaemonClient = class {
58
58
  }
59
59
  return data;
60
60
  }
61
+ async connectSSE(path, onEvent, signal) {
62
+ const resp = await this.rawFetch(path, {
63
+ method: "GET",
64
+ headers: {
65
+ "Authorization": `Bearer ${this.token}`,
66
+ "X-CommandGarden": "1",
67
+ "Accept": "text/event-stream"
68
+ },
69
+ signal
70
+ });
71
+ if (!resp.ok || !resp.body) {
72
+ throw new Error(`SSE connection failed: HTTP ${resp.status}`);
73
+ }
74
+ const reader = resp.body.getReader();
75
+ const decoder = new TextDecoder();
76
+ let buffer = "";
77
+ try {
78
+ while (true) {
79
+ const { done, value } = await reader.read();
80
+ if (done) break;
81
+ buffer += decoder.decode(value, { stream: true });
82
+ const lines = buffer.split("\n");
83
+ buffer = lines.pop() ?? "";
84
+ let currentEvent = "message";
85
+ let currentData = "";
86
+ for (const line of lines) {
87
+ if (line.startsWith("event: ")) {
88
+ currentEvent = line.slice(7).trim();
89
+ } else if (line.startsWith("data: ")) {
90
+ currentData = line.slice(6);
91
+ } else if (line === "") {
92
+ if (currentData) {
93
+ try {
94
+ onEvent(currentEvent, JSON.parse(currentData));
95
+ } catch {
96
+ onEvent(currentEvent, currentData);
97
+ }
98
+ currentData = "";
99
+ currentEvent = "message";
100
+ }
101
+ }
102
+ }
103
+ }
104
+ } catch (err) {
105
+ if (signal?.aborted) return;
106
+ throw err;
107
+ }
108
+ }
61
109
  rawFetch(path, init) {
62
110
  return fetch(`${this.baseUrl}${path}`, init);
63
111
  }
@@ -79,6 +127,9 @@ function parseDuration(input) {
79
127
  return new Date(Date.now() - value * UNITS[unit]);
80
128
  }
81
129
 
130
+ // src/commands/run.ts
131
+ import { createInterface } from "readline";
132
+
82
133
  // src/formatters.ts
83
134
  import Table from "cli-table3";
84
135
  function formatTable(data, columns) {
@@ -138,6 +189,30 @@ function parseConnectorArgs(rawArgs) {
138
189
  }
139
190
  return result;
140
191
  }
192
+ function promptApproval(request) {
193
+ const rl = createInterface({ input: process.stdin, output: process.stderr });
194
+ let cancelled = false;
195
+ const cancel = () => {
196
+ cancelled = true;
197
+ rl.close();
198
+ };
199
+ const prompt = `
200
+ \u26A0 Step ${request.stepIndex + 1} [${request.stepType}] in ${request.connectorKey} requires approval.
201
+ Capability: ${request.capability}
202
+ ${request.description}
203
+ Approve? (y/n): `;
204
+ const promise = new Promise((resolve) => {
205
+ rl.question(prompt, (answer) => {
206
+ rl.close();
207
+ if (cancelled) return;
208
+ resolve(answer.trim().toLowerCase() === "y" || answer.trim().toLowerCase() === "yes");
209
+ });
210
+ rl.on("close", () => {
211
+ if (cancelled) resolve(true);
212
+ });
213
+ });
214
+ return { promise, cancel };
215
+ }
141
216
  async function executeRun(client, connector, args, outputFormat) {
142
217
  try {
143
218
  const resp = await client.post("/api/run", {
@@ -145,10 +220,57 @@ async function executeRun(client, connector, args, outputFormat) {
145
220
  args,
146
221
  format: outputFormat
147
222
  });
148
- if (!resp.ok) {
149
- return `Error: ${resp.error ?? "Unknown error from extension"} (${resp.durationMs}ms)`;
223
+ if (!resp.requiresApproval) {
224
+ if (!resp.ok) {
225
+ return `Error: ${resp.error ?? "Unknown error from extension"} (${resp.durationMs}ms)`;
226
+ }
227
+ return format(resp.data, resp.columns, connector, outputFormat);
150
228
  }
151
- return format(resp.data, resp.columns, connector, outputFormat);
229
+ const requestId = resp.requestId;
230
+ process.stderr.write(`Pipeline requires approval. Waiting for steps...
231
+ `);
232
+ return new Promise((resolve) => {
233
+ const abortController = new AbortController();
234
+ let activePrompt = null;
235
+ client.connectSSE(
236
+ `/api/run/events/${requestId}`,
237
+ (event, data) => {
238
+ if (event === "approval") {
239
+ const approvalReq = data;
240
+ activePrompt = promptApproval(approvalReq);
241
+ activePrompt.promise.then(async (approved) => {
242
+ activePrompt = null;
243
+ try {
244
+ await client.post("/api/approval", {
245
+ approvalId: approvalReq.approvalId,
246
+ approved
247
+ });
248
+ } catch {
249
+ }
250
+ if (!approved) {
251
+ abortController.abort();
252
+ resolve(`Aborted: Step ${approvalReq.stepIndex + 1} [${approvalReq.stepType}] was rejected.`);
253
+ }
254
+ });
255
+ } else if (event === "result") {
256
+ if (activePrompt) {
257
+ activePrompt.cancel();
258
+ activePrompt = null;
259
+ process.stderr.write("\n (approved via extension)\n");
260
+ }
261
+ abortController.abort();
262
+ const result = data;
263
+ if (!result.ok) {
264
+ resolve(`Error: ${result.error ?? "Unknown error"} (${result.durationMs}ms)`);
265
+ } else {
266
+ resolve(format(result.data, result.columns ?? [], connector, outputFormat));
267
+ }
268
+ }
269
+ },
270
+ abortController.signal
271
+ ).catch(() => {
272
+ });
273
+ });
152
274
  } catch (err) {
153
275
  return `Error: ${err instanceof Error ? err.message : String(err)}`;
154
276
  }
@@ -4603,6 +4725,7 @@ function buildQueryString(filter) {
4603
4725
  const params = new URLSearchParams();
4604
4726
  if (filter.since) params.set("since", filter.since);
4605
4727
  if (filter.connector) params.set("connector", filter.connector);
4728
+ if (filter.type) params.set("type", filter.type);
4606
4729
  if (filter.limit) params.set("limit", String(filter.limit));
4607
4730
  const qs = params.toString();
4608
4731
  return qs ? `?${qs}` : "";
@@ -4614,7 +4737,7 @@ async function executeAuditList(client, filter) {
4614
4737
  );
4615
4738
  if (resp.events.length === 0) return "No audit events found.";
4616
4739
  const table = new Table3({
4617
- head: ["Timestamp", "Type", "Connector", "User", "Duration", "Rows", "Error"],
4740
+ head: ["Timestamp", "Type", "Connector", "User", "Duration", "Rows", "Source", "Error"],
4618
4741
  style: { head: ["cyan"] }
4619
4742
  });
4620
4743
  for (const e of resp.events) {
@@ -4625,6 +4748,7 @@ async function executeAuditList(client, filter) {
4625
4748
  e.user,
4626
4749
  `${e.durationMs}ms`,
4627
4750
  e.rowCount?.toString() ?? "-",
4751
+ e.source ?? "-",
4628
4752
  e.error ?? e.denialReason ?? ""
4629
4753
  ]);
4630
4754
  }
@@ -4633,7 +4757,24 @@ async function executeAuditList(client, filter) {
4633
4757
  return `Error: ${err instanceof Error ? err.message : String(err)}`;
4634
4758
  }
4635
4759
  }
4636
- var AUDIT_COLUMNS = ["id", "timestamp", "type", "user", "connector", "durationMs", "rowCount", "error", "denialReason"];
4760
+ var AUDIT_COLUMNS = [
4761
+ "id",
4762
+ "timestamp",
4763
+ "type",
4764
+ "user",
4765
+ "connector",
4766
+ "durationMs",
4767
+ "rowCount",
4768
+ "error",
4769
+ "denialReason",
4770
+ "correlationId",
4771
+ "connectorHash",
4772
+ "source",
4773
+ "args",
4774
+ "domains",
4775
+ "capabilities",
4776
+ "steps"
4777
+ ];
4637
4778
  async function executeAuditExport(client, filter, format2) {
4638
4779
  try {
4639
4780
  const resp = await client.get(
@@ -4646,8 +4787,15 @@ async function executeAuditExport(client, filter, format2) {
4646
4787
  const rows = resp.events.map(
4647
4788
  (e) => AUDIT_COLUMNS.map((col) => {
4648
4789
  const val = e[col];
4649
- const str = String(val ?? "");
4650
- return str.includes(",") || str.includes('"') ? `"${str.replace(/"/g, '""')}"` : str;
4790
+ let str;
4791
+ if (val === null || val === void 0) {
4792
+ str = "";
4793
+ } else if (typeof val === "object") {
4794
+ str = JSON.stringify(val);
4795
+ } else {
4796
+ str = String(val);
4797
+ }
4798
+ return str.includes(",") || str.includes('"') || str.includes("\n") ? `"${str.replace(/"/g, '""')}"` : str;
4651
4799
  }).join(",")
4652
4800
  );
4653
4801
  return [header, ...rows].join("\n") + "\n";
@@ -4655,12 +4803,54 @@ async function executeAuditExport(client, filter, format2) {
4655
4803
  return `Error: ${err instanceof Error ? err.message : String(err)}`;
4656
4804
  }
4657
4805
  }
4806
+ async function executeAuditShow(client, id) {
4807
+ try {
4808
+ const resp = await client.get(`/api/audit/${id}`);
4809
+ const e = resp.event;
4810
+ const lines = [
4811
+ `Event: ${e.id}`,
4812
+ `Type: ${e.type}`,
4813
+ `Connector: ${e.connector}${e.connectorHash ? ` (hash: ${e.connectorHash})` : ""}`,
4814
+ `Correlation: ${e.correlationId ?? "-"}`,
4815
+ `User: ${e.user}`,
4816
+ `Timestamp: ${e.timestamp.replace("T", " ").slice(0, 19)}`,
4817
+ `Duration: ${e.durationMs}ms`
4818
+ ];
4819
+ if (e.rowCount !== void 0) lines.push(`Rows: ${e.rowCount}`);
4820
+ if (e.error) lines.push(`Error: ${e.error}`);
4821
+ if (e.denialReason) lines.push(`Denial: ${e.denialReason}`);
4822
+ if (e.source) lines.push(`Source: ${e.source}`);
4823
+ if (e.previousValue !== void 0) lines.push(`Previous: ${e.previousValue}`);
4824
+ if (e.newValue !== void 0) lines.push(`New: ${e.newValue}`);
4825
+ if (Object.keys(e.args).length > 0) {
4826
+ lines.push(`Args: ${JSON.stringify(e.args)}`);
4827
+ }
4828
+ if (e.steps && e.steps.length > 0) {
4829
+ lines.push("");
4830
+ lines.push("Pipeline Steps:");
4831
+ const stepTable = new Table3({
4832
+ head: ["#", "Step", "Capability", "Duration", "Error"],
4833
+ style: { head: ["cyan"] }
4834
+ });
4835
+ for (const s of e.steps) {
4836
+ stepTable.push([
4837
+ s.index + 1,
4838
+ s.step,
4839
+ s.capability ?? "-",
4840
+ `${s.durationMs}ms`,
4841
+ s.error ?? ""
4842
+ ]);
4843
+ }
4844
+ lines.push(stepTable.toString());
4845
+ }
4846
+ return lines.join("\n");
4847
+ } catch (err) {
4848
+ return `Error: ${err instanceof Error ? err.message : String(err)}`;
4849
+ }
4850
+ }
4658
4851
 
4659
4852
  // src/commands/config-cmd.ts
4660
- import { readFileSync as readFileSync4, writeFileSync as writeFileSync2, existsSync as existsSync2, mkdirSync as mkdirSync2 } from "fs";
4661
- import { dirname } from "path";
4662
- import { parse as parseYaml2, stringify as stringifyYaml } from "yaml";
4663
- var VALID_SECTIONS = ["daemon", "security", "connectors", "audit", "output"];
4853
+ import { readFileSync as readFileSync4, existsSync as existsSync2 } from "fs";
4664
4854
  function executeConfigShow(configPath) {
4665
4855
  if (!existsSync2(configPath)) {
4666
4856
  return [
@@ -4671,35 +4861,123 @@ function executeConfigShow(configPath) {
4671
4861
  }
4672
4862
  return readFileSync4(configPath, "utf-8");
4673
4863
  }
4674
- function executeConfigSet(configPath, key, value) {
4675
- const parts = key.split(".");
4676
- if (parts.length !== 2 || !VALID_SECTIONS.includes(parts[0])) {
4677
- return `Unknown config section: "${parts[0]}". Valid sections: ${VALID_SECTIONS.join(", ")}`;
4678
- }
4679
- let config2 = {};
4680
- if (existsSync2(configPath)) {
4681
- const raw = readFileSync4(configPath, "utf-8");
4682
- config2 = parseYaml2(raw) ?? {};
4683
- }
4684
- const [section, prop] = parts;
4685
- if (!config2[section]) config2[section] = {};
4686
- let parsed = value;
4687
- if (value === "true") parsed = true;
4688
- else if (value === "false") parsed = false;
4689
- else if (!isNaN(Number(value)) && value !== "") parsed = Number(value);
4690
- config2[section][prop] = parsed;
4691
- mkdirSync2(dirname(configPath), { recursive: true });
4692
- writeFileSync2(configPath, stringifyYaml(config2), "utf-8");
4693
- return `Set ${key} = ${value}`;
4864
+ async function executeConfigSet(client, key, value) {
4865
+ try {
4866
+ await client.post("/api/config", { key, value });
4867
+ return `Set ${key} = ${value}`;
4868
+ } catch (err) {
4869
+ return `Error: ${err instanceof Error ? err.message : String(err)}`;
4870
+ }
4871
+ }
4872
+
4873
+ // src/commands/gui-cmd.ts
4874
+ import { spawn as spawn2 } from "child_process";
4875
+ import { readFileSync as readFileSync5, writeFileSync as writeFileSync2, existsSync as existsSync3, unlinkSync as unlinkSync2, mkdirSync as mkdirSync2 } from "fs";
4876
+ import { join as join2 } from "path";
4877
+ import { exec } from "child_process";
4878
+ import { parse as parseYaml2 } from "yaml";
4879
+ function readAppPort(configPath) {
4880
+ try {
4881
+ if (existsSync3(configPath)) {
4882
+ const config2 = parseYaml2(readFileSync5(configPath, "utf-8"));
4883
+ const port = config2?.app?.port;
4884
+ if (typeof port === "number") return port;
4885
+ }
4886
+ } catch {
4887
+ }
4888
+ return 19826;
4889
+ }
4890
+ async function executeGuiStart(baseUrl, cgHome, appScript, opts) {
4891
+ const appPort = opts.configPath ? readAppPort(opts.configPath) : 19826;
4892
+ try {
4893
+ const resp = await fetch(`${baseUrl}/api/status`);
4894
+ if (!resp.ok) throw new Error();
4895
+ } catch {
4896
+ return "Daemon is not running. Start it with: cg daemon start (or use cg up)";
4897
+ }
4898
+ const pidPath = join2(cgHome, "app.pid");
4899
+ if (existsSync3(pidPath)) {
4900
+ const pid = parseInt(readFileSync5(pidPath, "utf-8").trim(), 10);
4901
+ try {
4902
+ process.kill(pid, 0);
4903
+ return "GUI is already running.";
4904
+ } catch {
4905
+ unlinkSync2(pidPath);
4906
+ }
4907
+ }
4908
+ mkdirSync2(cgHome, { recursive: true });
4909
+ if (opts.background) {
4910
+ const child2 = spawn2("node", [appScript], { detached: true, stdio: "ignore" });
4911
+ if (child2.pid) writeFileSync2(pidPath, String(child2.pid));
4912
+ child2.unref();
4913
+ if (!opts.noOpen) openBrowser(`http://127.0.0.1:${appPort}`);
4914
+ return `GUI started (PID: ${child2.pid ?? "unknown"}).`;
4915
+ }
4916
+ if (!opts.noOpen) openBrowser(`http://127.0.0.1:${appPort}`);
4917
+ const child = spawn2("node", [appScript], { stdio: "inherit" });
4918
+ await new Promise((resolve) => child.on("exit", () => resolve()));
4919
+ return "GUI stopped.";
4920
+ }
4921
+ function executeGuiStop(cgHome) {
4922
+ const pidPath = join2(cgHome, "app.pid");
4923
+ if (!existsSync3(pidPath)) return "GUI is not running (no PID file found).";
4924
+ const pid = parseInt(readFileSync5(pidPath, "utf-8").trim(), 10);
4925
+ try {
4926
+ process.kill(pid, "SIGTERM");
4927
+ unlinkSync2(pidPath);
4928
+ return `GUI stopped (PID: ${pid}).`;
4929
+ } catch {
4930
+ unlinkSync2(pidPath);
4931
+ return `GUI process ${pid} not found (stale PID file cleaned up).`;
4932
+ }
4933
+ }
4934
+ function executeGuiStatus(cgHome) {
4935
+ const pidPath = join2(cgHome, "app.pid");
4936
+ if (!existsSync3(pidPath)) return "GUI: not running (no PID file found).";
4937
+ const pid = parseInt(readFileSync5(pidPath, "utf-8").trim(), 10);
4938
+ try {
4939
+ process.kill(pid, 0);
4940
+ return `GUI: running (PID: ${pid}).`;
4941
+ } catch {
4942
+ return "GUI: not running (stale PID file).";
4943
+ }
4944
+ }
4945
+ function openBrowser(url) {
4946
+ const cmd = process.platform === "darwin" ? "open" : process.platform === "win32" ? "start" : "xdg-open";
4947
+ exec(`${cmd} ${url}`);
4948
+ }
4949
+
4950
+ // src/commands/up-down.ts
4951
+ async function executeUp(baseUrl, cgHome, daemonScript, appScript, configPath) {
4952
+ const lines = [];
4953
+ lines.push(await executeDaemonStart(baseUrl, cgHome, daemonScript));
4954
+ for (let i = 0; i < 10; i++) {
4955
+ try {
4956
+ const resp = await fetch(`${baseUrl}/api/status`);
4957
+ if (resp.ok) break;
4958
+ } catch {
4959
+ }
4960
+ await new Promise((r) => setTimeout(r, 500));
4961
+ }
4962
+ lines.push(await executeGuiStart(baseUrl, cgHome, appScript, { background: true, configPath }));
4963
+ return lines.join("\n");
4964
+ }
4965
+ async function executeDown(cgHome) {
4966
+ const lines = [];
4967
+ lines.push(executeGuiStop(cgHome));
4968
+ lines.push(await executeDaemonStop(cgHome));
4969
+ lines.push("All services stopped.");
4970
+ return lines.join("\n");
4694
4971
  }
4695
4972
 
4696
4973
  // src/main.ts
4697
4974
  var __filename = fileURLToPath(import.meta.url);
4698
- var __dirname = dirname2(__filename);
4699
- var DAEMON_SCRIPT = join2(__dirname, "..", "..", "daemon", "dist", "main.js");
4700
- var CG_HOME = join2(homedir(), ".commandgarden");
4701
- var TOKEN_PATH = join2(CG_HOME, "session-token");
4702
- var CONFIG_PATH = join2(CG_HOME, "config.yaml");
4975
+ var __dirname = dirname(__filename);
4976
+ var DAEMON_SCRIPT = join3(__dirname, "..", "..", "daemon", "dist", "main.js");
4977
+ var APP_SCRIPT = join3(__dirname, "..", "..", "app", "dist", "server", "main.js");
4978
+ var CG_HOME = join3(homedir(), ".commandgarden");
4979
+ var TOKEN_PATH = join3(CG_HOME, "session-token");
4980
+ var CONFIG_PATH = join3(CG_HOME, "config.yaml");
4703
4981
  var BASE_URL = `http://127.0.0.1:19825`;
4704
4982
  function createClient() {
4705
4983
  const token = readToken(TOKEN_PATH);
@@ -4739,26 +5017,53 @@ daemon.command("stop").description("Stop the daemon").action(async () => {
4739
5017
  console.log(await executeDaemonStop(CG_HOME));
4740
5018
  });
4741
5019
  var audit = program.command("audit").description("View audit event log");
4742
- audit.command("list").description("List recent audit events").option("--since <duration>", "time window, e.g. 7d, 2w, 12h").option("--connector <pattern>", "filter by connector pattern, e.g. test/*").option("--limit <n>", "max events to return", "100").action(async (opts) => {
5020
+ audit.command("list").description("List recent audit events").option("--since <duration>", "time window, e.g. 7d, 2w, 12h").option("--connector <pattern>", "filter by connector pattern, e.g. test/*").option("--type <pattern>", "filter by event type, e.g. auth.failed, command.*").option("--limit <n>", "max events to return", "100").action(async (opts) => {
4743
5021
  const client = createClient();
4744
5022
  const filter = {};
4745
5023
  if (opts.since) filter.since = parseDuration(opts.since).toISOString();
4746
5024
  if (opts.connector) filter.connector = opts.connector;
5025
+ if (opts.type) filter.type = opts.type;
4747
5026
  filter.limit = parseInt(opts.limit, 10);
4748
5027
  console.log(await executeAuditList(client, filter));
4749
5028
  });
4750
- audit.command("export").description("Export audit events").option("--format <format>", "export format: json, csv", "json").option("--since <duration>", "time window, e.g. 7d, 30d").option("--connector <pattern>", "filter by connector pattern").action(async (opts) => {
5029
+ audit.command("export").description("Export audit events").option("--format <format>", "export format: json, csv", "json").option("--since <duration>", "time window, e.g. 7d, 30d").option("--connector <pattern>", "filter by connector pattern").option("--type <pattern>", "filter by event type").action(async (opts) => {
4751
5030
  const client = createClient();
4752
5031
  const filter = {};
4753
5032
  if (opts.since) filter.since = parseDuration(opts.since).toISOString();
4754
5033
  if (opts.connector) filter.connector = opts.connector;
5034
+ if (opts.type) filter.type = opts.type;
4755
5035
  console.log(await executeAuditExport(client, filter, opts.format));
4756
5036
  });
5037
+ audit.command("show <id>").description("Show full details of an audit event").action(async (id) => {
5038
+ const client = createClient();
5039
+ console.log(await executeAuditShow(client, id));
5040
+ });
4757
5041
  var config = program.command("config").description("Manage daemon configuration");
4758
5042
  config.command("show").description("Show current configuration").action(() => {
4759
5043
  console.log(executeConfigShow(CONFIG_PATH));
4760
5044
  });
4761
- config.command("set <key> <value>").description("Set a configuration value (e.g. daemon.port 9999)").action((key, value) => {
4762
- console.log(executeConfigSet(CONFIG_PATH, key, value));
5045
+ config.command("set <key> <value>").description("Set a configuration value (e.g. daemon.port 9999)").action(async (key, value) => {
5046
+ const client = createClient();
5047
+ console.log(await executeConfigSet(client, key, value));
5048
+ });
5049
+ var gui = program.command("gui").description("Manage the GUI app server");
5050
+ gui.command("start", { isDefault: true }).description("Start the GUI (foreground by default)").option("-b, --background", "Run in background").option("--no-open", "Do not open browser").action(async (opts) => {
5051
+ console.log(await executeGuiStart(BASE_URL, CG_HOME, APP_SCRIPT, {
5052
+ background: opts.background,
5053
+ noOpen: opts.open === false,
5054
+ configPath: CONFIG_PATH
5055
+ }));
5056
+ });
5057
+ gui.command("stop").description("Stop the GUI").action(() => {
5058
+ console.log(executeGuiStop(CG_HOME));
5059
+ });
5060
+ gui.command("status").description("Check GUI status").action(() => {
5061
+ console.log(executeGuiStatus(CG_HOME));
5062
+ });
5063
+ program.command("up").description("Start daemon + GUI, open browser").action(async () => {
5064
+ console.log(await executeUp(BASE_URL, CG_HOME, DAEMON_SCRIPT, APP_SCRIPT, CONFIG_PATH));
5065
+ });
5066
+ program.command("down").description("Stop GUI + daemon").action(async () => {
5067
+ console.log(await executeDown(CG_HOME));
4763
5068
  });
4764
5069
  program.parse();
package/package.json CHANGED
@@ -1,13 +1,13 @@
1
1
  {
2
2
  "name": "@commandgarden/cli",
3
- "version": "0.1.0",
3
+ "version": "1.0.0",
4
4
  "description": "Enterprise browser automation CLI",
5
5
  "type": "module",
6
6
  "license": "MIT",
7
7
  "main": "./dist/main.js",
8
8
  "bin": {
9
- "commandgarden": "./dist/main.js",
10
- "cg": "./dist/main.js"
9
+ "commandgarden": "dist/main.js",
10
+ "cg": "dist/main.js"
11
11
  },
12
12
  "files": [
13
13
  "dist"