@agentprojectcontext/apx 1.3.1 → 1.4.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.
- package/package.json +1 -1
- package/src/cli/commands/status.js +127 -0
- package/src/cli/index.js +154 -122
package/package.json
CHANGED
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
// apx status — show full system status at a glance.
|
|
2
|
+
// Daemon, super-agent, engines, telegram, projects.
|
|
3
|
+
|
|
4
|
+
import { readConfig } from "../../core/config.js";
|
|
5
|
+
import { http } from "../http.js";
|
|
6
|
+
|
|
7
|
+
const R = "\x1b[0m";
|
|
8
|
+
const B = "\x1b[1m";
|
|
9
|
+
const DI = "\x1b[2m";
|
|
10
|
+
const CY = "\x1b[36m";
|
|
11
|
+
const GR = "\x1b[32m";
|
|
12
|
+
const YE = "\x1b[33m";
|
|
13
|
+
const RE = "\x1b[31m";
|
|
14
|
+
const WH = "\x1b[97m";
|
|
15
|
+
|
|
16
|
+
const ok = (s) => `${GR}✓${R} ${s}`;
|
|
17
|
+
const off = (s) => `${DI}–${R} ${s}`;
|
|
18
|
+
const err = (s) => `${RE}✗${R} ${s}`;
|
|
19
|
+
const key = (s) => ` ${DI}${s.padEnd(16)}${R}`;
|
|
20
|
+
const val = (s) => `${WH}${s}${R}`;
|
|
21
|
+
const sec = (s) => `\n${B}${CY} ${s}${R}\n`;
|
|
22
|
+
|
|
23
|
+
export async function cmdStatus() {
|
|
24
|
+
const cfg = readConfig();
|
|
25
|
+
|
|
26
|
+
// ── Daemon ─────────────────────────────────────────────────────────────────
|
|
27
|
+
console.log(sec("Daemon"));
|
|
28
|
+
let daemonOk = false;
|
|
29
|
+
let daemonInfo = {};
|
|
30
|
+
try {
|
|
31
|
+
daemonInfo = await http.get("/health");
|
|
32
|
+
daemonOk = true;
|
|
33
|
+
} catch {}
|
|
34
|
+
|
|
35
|
+
if (daemonOk) {
|
|
36
|
+
console.log(` ${ok("running")} port ${val(daemonInfo.port || cfg.port)} v${val(daemonInfo.version || "?")} uptime ${val(formatUptime(daemonInfo.uptime_s))}`);
|
|
37
|
+
} else {
|
|
38
|
+
console.log(` ${err("stopped")} run: ${CY}apx daemon start${R}`);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
// ── Super-agent ────────────────────────────────────────────────────────────
|
|
42
|
+
console.log(sec("Super-agent"));
|
|
43
|
+
const sa = cfg.super_agent || {};
|
|
44
|
+
if (sa.enabled) {
|
|
45
|
+
console.log(` ${ok("enabled")}`);
|
|
46
|
+
console.log(`${key("model")}${val(sa.model || "(not set)")}`);
|
|
47
|
+
if (sa.system) {
|
|
48
|
+
const preview = sa.system.length > 60 ? sa.system.slice(0, 60) + "…" : sa.system;
|
|
49
|
+
console.log(`${key("system")}${DI}${preview}${R}`);
|
|
50
|
+
}
|
|
51
|
+
if (cfg.identity?.agent_name) {
|
|
52
|
+
console.log(`${key("identity")}${val(cfg.identity.agent_name)}${cfg.identity.owner_name ? DI + " for " + cfg.identity.owner_name + R : ""}`);
|
|
53
|
+
}
|
|
54
|
+
} else {
|
|
55
|
+
console.log(` ${off("disabled")} run: ${CY}apx setup${R}`);
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
// ── Engines ────────────────────────────────────────────────────────────────
|
|
59
|
+
console.log(sec("Engines"));
|
|
60
|
+
const engines = cfg.engines || {};
|
|
61
|
+
const engineList = [
|
|
62
|
+
{ id: "anthropic", label: "Anthropic", check: () => !!engines.anthropic?.api_key },
|
|
63
|
+
{ id: "openai", label: "OpenAI", check: () => !!engines.openai?.api_key },
|
|
64
|
+
{ id: "gemini", label: "Gemini", check: () => !!engines.gemini?.api_key },
|
|
65
|
+
{ id: "ollama", label: "Ollama", check: () => true,
|
|
66
|
+
detail: () => engines.ollama?.base_url || "http://localhost:11434" },
|
|
67
|
+
];
|
|
68
|
+
|
|
69
|
+
for (const e of engineList) {
|
|
70
|
+
const active = sa.model?.startsWith(e.id + ":");
|
|
71
|
+
const configured = e.check();
|
|
72
|
+
const detail = e.detail ? ` ${DI}${e.detail()}${R}` : "";
|
|
73
|
+
const marker = active ? `${YE}← active${R}` : "";
|
|
74
|
+
if (configured) {
|
|
75
|
+
console.log(` ${ok(e.label.padEnd(12))}${detail} ${marker}`);
|
|
76
|
+
} else {
|
|
77
|
+
console.log(` ${off(e.label.padEnd(12))}${DI}(no key)${R} ${marker}`);
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
// ── Telegram ───────────────────────────────────────────────────────────────
|
|
82
|
+
console.log(sec("Telegram"));
|
|
83
|
+
const tg = cfg.telegram || {};
|
|
84
|
+
if (tg.enabled && tg.bot_token) {
|
|
85
|
+
console.log(` ${ok("enabled")}`);
|
|
86
|
+
console.log(`${key("chat_id")}${val(tg.chat_id || "(not set)")}`);
|
|
87
|
+
console.log(`${key("bot_token")}${DI}${maskToken(tg.bot_token)}${R}`);
|
|
88
|
+
if (tg.channels?.length) {
|
|
89
|
+
console.log(`${key("channels")}${val(tg.channels.length + " configured")}`);
|
|
90
|
+
}
|
|
91
|
+
} else {
|
|
92
|
+
console.log(` ${off("disabled")} run: ${CY}apx setup${R}`);
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
// ── Projects ───────────────────────────────────────────────────────────────
|
|
96
|
+
console.log(sec("Projects"));
|
|
97
|
+
let projects = [];
|
|
98
|
+
if (daemonOk) {
|
|
99
|
+
try { projects = await http.get("/projects"); } catch {}
|
|
100
|
+
} else {
|
|
101
|
+
projects = (cfg.projects || []).map((p, i) => ({ id: i + 1, path: p.path, name: p.path?.split("/").pop() || "?", agents: "?" }));
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
const registered = projects.filter((p) => p.id !== 0);
|
|
105
|
+
if (registered.length === 0) {
|
|
106
|
+
console.log(` ${off("none registered")} run: ${CY}apx project add .${R}`);
|
|
107
|
+
} else {
|
|
108
|
+
for (const p of registered) {
|
|
109
|
+
const agents = p.agents !== "?" ? ` ${DI}${p.agents} agent${p.agents !== 1 ? "s" : ""}${R}` : "";
|
|
110
|
+
console.log(` ${DI}#${p.id}${R} ${val(p.name)}${agents} ${DI}${p.path}${R}`);
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
console.log();
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
function maskToken(token) {
|
|
118
|
+
if (!token || token.length < 12) return "***";
|
|
119
|
+
return token.slice(0, 6) + "…" + token.slice(-4);
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
function formatUptime(s) {
|
|
123
|
+
if (!s) return "?";
|
|
124
|
+
if (s < 60) return `${s}s`;
|
|
125
|
+
if (s < 3600) return `${Math.floor(s / 60)}m`;
|
|
126
|
+
return `${Math.floor(s / 3600)}h ${Math.floor((s % 3600) / 60)}m`;
|
|
127
|
+
}
|
package/src/cli/index.js
CHANGED
|
@@ -73,6 +73,7 @@ import { cmdIdentity } from "./commands/identity.js";
|
|
|
73
73
|
import { cmdCommandList, cmdCommandShow } from "./commands/command.js";
|
|
74
74
|
import { cmdUpdate } from "./commands/update.js";
|
|
75
75
|
import { cmdSetup } from "./commands/setup.js";
|
|
76
|
+
import { cmdStatus } from "./commands/status.js";
|
|
76
77
|
import { checkForUpdate } from "../core/update-check.js";
|
|
77
78
|
import { mascot } from "../core/mascot.js";
|
|
78
79
|
import {
|
|
@@ -91,127 +92,144 @@ const VERSION = JSON.parse(
|
|
|
91
92
|
fs.readFileSync(path.join(__dirname, "..", "..", "package.json"), "utf8")
|
|
92
93
|
).version;
|
|
93
94
|
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
apx
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
95
|
+
// ── ANSI helpers (help only) ─────────────────────────────────────────────────
|
|
96
|
+
const H = {
|
|
97
|
+
R: "\x1b[0m",
|
|
98
|
+
B: "\x1b[1m",
|
|
99
|
+
DI: "\x1b[2m",
|
|
100
|
+
CY: "\x1b[36m",
|
|
101
|
+
GR: "\x1b[32m",
|
|
102
|
+
YE: "\x1b[33m",
|
|
103
|
+
WH: "\x1b[97m",
|
|
104
|
+
MA: "\x1b[35m",
|
|
105
|
+
};
|
|
106
|
+
const hSec = (s) => `\n${H.B}${H.CY} ${s}${H.R}\n`;
|
|
107
|
+
const hCmd = (cmd, pad, desc) => ` ${H.WH}${cmd.padEnd(pad)}${H.R} ${H.DI}${desc}${H.R}`;
|
|
108
|
+
const hSub = (sub, pad, desc) => ` ${H.CY}${sub.padEnd(pad)}${H.R} ${H.DI}${desc}${H.R}`;
|
|
109
|
+
const hFlag = (f, pad, desc) => ` ${H.YE}${f.padEnd(pad)}${H.R} ${H.DI}${desc}${H.R}`;
|
|
110
|
+
|
|
111
|
+
function buildHelp(version) {
|
|
112
|
+
return [
|
|
113
|
+
"",
|
|
114
|
+
` ${H.B}${H.WH}apx${H.R} ${H.DI}Agent Project Context v${version}${H.R}`,
|
|
115
|
+
` ${H.DI}Docs: https://agentprojectcontext.com${H.R}`,
|
|
116
|
+
"",
|
|
117
|
+
` ${H.DI}Usage:${H.R} ${H.WH}apx${H.R} ${H.CY}<command>${H.R} ${H.DI}[subcommand] [args] [--flags]${H.R}`,
|
|
118
|
+
"",
|
|
119
|
+
|
|
120
|
+
hSec("Bootstrap"),
|
|
121
|
+
hCmd("apx init [path]", 36, "--name \"<name>\" initialize a new APC project"),
|
|
122
|
+
hCmd("apx setup", 36, "interactive wizard: provider → model → channels → daemon (alias: install)"),
|
|
123
|
+
hCmd("apx status", 36, "full system status: daemon, super-agent, engines, telegram, projects"),
|
|
124
|
+
hCmd("apx update", 36, "check for updates and upgrade (alias: upgrade)"),
|
|
125
|
+
hCmd("apx project add [path]", 36, "register a project with the daemon"),
|
|
126
|
+
hCmd("apx project list", 36, ""),
|
|
127
|
+
hCmd("apx project remove <id>", 36, ""),
|
|
128
|
+
hCmd("apx project rebuild [id]", 36, "rebuild project index from filesystem"),
|
|
129
|
+
hCmd("apx add project [path]", 36, "alias for: apx project add"),
|
|
130
|
+
|
|
131
|
+
hSec("Agents"),
|
|
132
|
+
hCmd("apx agent add <slug>", 36, "--role R --model M --skills a,b --language es-AR --description D"),
|
|
133
|
+
hCmd("apx agent list", 36, ""),
|
|
134
|
+
hCmd("apx agent get <slug>", 36, ""),
|
|
135
|
+
hCmd("apx agent import", 36, ""),
|
|
136
|
+
hCmd("apx agent vault list", 36, ""),
|
|
137
|
+
hCmd("apx agent vault add", 36, ""),
|
|
138
|
+
|
|
139
|
+
hSec("Identity & Config"),
|
|
140
|
+
hCmd("apx identity show", 36, "print current agent identity (name, owner, personality)"),
|
|
141
|
+
hCmd("apx identity set <k> <v>", 36, "set identity field"),
|
|
142
|
+
hCmd("apx identity wizard", 36, "interactive identity setup"),
|
|
143
|
+
hCmd("apx config show", 36, "--effective --only-overrides"),
|
|
144
|
+
hCmd("apx config set <key> <val>", 36, "set key in .apc/config.json (JSON-aware)"),
|
|
145
|
+
hCmd("apx config unset <key>", 36, "remove key from .apc/config.json"),
|
|
146
|
+
|
|
147
|
+
hSec("Memory"),
|
|
148
|
+
hCmd("apx memory <slug>", 36, "print memory.md"),
|
|
149
|
+
hCmd("apx memory <slug> --append", 36, "\"<note>\" append under '## Recent context'"),
|
|
150
|
+
hCmd("apx memory <slug> --replace",36, "replace from stdin"),
|
|
151
|
+
|
|
152
|
+
hSec("Sessions"),
|
|
153
|
+
hCmd("apx session new <slug>", 36, "--title \"T\" [--task-ref REF] [--body -|\"text\"]"),
|
|
154
|
+
hCmd("apx session list [slug]", 36, "--last N"),
|
|
155
|
+
hCmd("apx session get <id>", 36, "--body"),
|
|
156
|
+
hCmd("apx session update <id>", 36, "--status S --result R --title T"),
|
|
157
|
+
hCmd("apx session close <id>", 36, "--result \"text\""),
|
|
158
|
+
hCmd("apx session check", 36, "anti-collision: exit 1 if active session"),
|
|
159
|
+
hCmd("apx session close-stale", 36, "auto-close sessions older than 1h"),
|
|
160
|
+
hCmd("apx session resume <id>", 36, "--summary --full (APC + Claude Code transcript)"),
|
|
161
|
+
hCmd("apx session compact <slug>", 36, "--conversation <id> collapse history into summary"),
|
|
162
|
+
|
|
163
|
+
hSec("MCPs"),
|
|
164
|
+
hCmd("apx mcp list", 36, ""),
|
|
165
|
+
hCmd("apx mcp add <name>", 36, "--command <cmd> [args] --env KEY=VAL"),
|
|
166
|
+
hCmd("apx mcp remove <name>", 36, ""),
|
|
167
|
+
hCmd("apx mcp enable/disable", 36, "<name>"),
|
|
168
|
+
hCmd("apx mcp run <name> <tool>", 36, "[<json-args>] call a tool through the daemon"),
|
|
169
|
+
hCmd("apx mcp tools <name>", 36, "list available tools"),
|
|
170
|
+
hCmd("apx mcp check", 36, "audit multi-source merge"),
|
|
171
|
+
|
|
172
|
+
hSec("Daemon"),
|
|
173
|
+
hCmd("apx daemon start", 36, ""),
|
|
174
|
+
hCmd("apx daemon stop", 36, ""),
|
|
175
|
+
hCmd("apx daemon status", 36, ""),
|
|
176
|
+
hCmd("apx daemon logs", 36, "--tail N"),
|
|
177
|
+
|
|
178
|
+
hSec("Telegram"),
|
|
179
|
+
hCmd("apx telegram send \"text\"", 36, "--chat <id>"),
|
|
180
|
+
hCmd("apx telegram status", 36, ""),
|
|
181
|
+
hCmd("apx telegram setup", 36, ""),
|
|
182
|
+
|
|
183
|
+
hSec("Messages"),
|
|
184
|
+
hCmd("apx messages tail", 36, "--agent <slug> --channel <ch> -n 50 --global"),
|
|
185
|
+
hCmd("apx messages search \"q\"", 36, ""),
|
|
186
|
+
|
|
187
|
+
hSec("LLM / Chat"),
|
|
188
|
+
hCmd("apx exec <agent> \"prompt\"",36, "--model <id> --max-tokens N --temperature T"),
|
|
189
|
+
hCmd("apx chat <agent>", 36, "--conversation <id> --model <id> (interactive REPL)"),
|
|
190
|
+
hCmd("apx conversations list", 36, "<agent>"),
|
|
191
|
+
hCmd("apx conversations get", 36, "<agent> <id>"),
|
|
192
|
+
|
|
193
|
+
hSec("Runtimes"),
|
|
194
|
+
hCmd("apx run <agent>", 36, "--runtime <id> \"prompt\" --timeout <s>"),
|
|
195
|
+
` ${H.DI}runtimes: claude-code | codex | opencode | aider${H.R}`,
|
|
196
|
+
hCmd("apx env detect", 36, "which agent CLIs are installed"),
|
|
197
|
+
|
|
198
|
+
hSec("Agent-to-Agent"),
|
|
199
|
+
hCmd("apx send <from> <to> \"msg\"",36, "--deliver log A2A message; --deliver runs target engine"),
|
|
200
|
+
hCmd("apx connections <agent>", 36, "mental map: who/how/when this agent talked"),
|
|
201
|
+
hCmd("apx graph <agent>", 36, "alias for connections"),
|
|
202
|
+
|
|
203
|
+
hSec("Routines"),
|
|
204
|
+
hCmd("apx routine list", 36, "list routines + next/last run"),
|
|
205
|
+
hCmd("apx routine add <name>", 36, "--kind K --schedule S [--spec '{...}']"),
|
|
206
|
+
` ${H.DI}kinds: heartbeat | exec_agent | telegram | shell${H.R}`,
|
|
207
|
+
` ${H.DI}schedule: every:60s | every:5m | every:1h | once:<iso>${H.R}`,
|
|
208
|
+
hCmd("apx routine get <name>", 36, ""),
|
|
209
|
+
hCmd("apx routine run <name>", 36, "manual trigger (ignores schedule)"),
|
|
210
|
+
hCmd("apx routine enable <name>", 36, ""),
|
|
211
|
+
hCmd("apx routine disable <name>", 36, ""),
|
|
212
|
+
hCmd("apx routine remove <name>", 36, ""),
|
|
213
|
+
|
|
214
|
+
hSec("Commands & Skills"),
|
|
215
|
+
hCmd("apx command list", 36, "list workflow commands (.apc/commands/)"),
|
|
216
|
+
hCmd("apx command show <name>", 36, "print command content"),
|
|
217
|
+
hCmd("apx skills add [targets]", 36, "install skill into IDE rule files (claude-code cursor windsurf copilot trae)"),
|
|
218
|
+
hCmd("apx skills add --global", 36, "install to ~/.claude/skills/ etc."),
|
|
219
|
+
hCmd("apx skills list", 36, ""),
|
|
220
|
+
hCmd("apx skills status", 36, "show which IDE targets are installed"),
|
|
221
|
+
|
|
222
|
+
hSec("Plugins"),
|
|
223
|
+
hCmd("apx plugins list", 36, "show loaded plugins and their status"),
|
|
224
|
+
hCmd("apx plugins status <id>", 36, "detailed status of one plugin (e.g. telegram)"),
|
|
225
|
+
|
|
226
|
+
hSec("Flags"),
|
|
227
|
+
hFlag("--project <name|id|path>", 36, "pin commands to a specific project"),
|
|
228
|
+
hFlag("--help", 36, "show this help"),
|
|
229
|
+
hFlag("--version", 36, "print version"),
|
|
230
|
+
"",
|
|
231
|
+
].join("\n") + "\n";
|
|
232
|
+
}
|
|
215
233
|
|
|
216
234
|
function parseArgs(argv) {
|
|
217
235
|
const args = { _: [], flags: {} };
|
|
@@ -255,8 +273,18 @@ function die(msg, code = 1) {
|
|
|
255
273
|
|
|
256
274
|
const argv = process.argv.slice(2);
|
|
257
275
|
|
|
276
|
+
// ── Global error safety net ──────────────────────────────────────────────────
|
|
277
|
+
// Catches any unhandled promise rejection or sync exception that escapes
|
|
278
|
+
// the main try/catch — shows the panda instead of a raw Node.js stack trace.
|
|
279
|
+
process.on("uncaughtException", (err) => {
|
|
280
|
+
die(err && err.message ? err.message : String(err));
|
|
281
|
+
});
|
|
282
|
+
process.on("unhandledRejection", (reason) => {
|
|
283
|
+
die(reason instanceof Error ? reason.message : String(reason));
|
|
284
|
+
});
|
|
285
|
+
|
|
258
286
|
if (argv.length === 0 || argv[0] === "--help" || argv[0] === "-h" || argv[0] === "help") {
|
|
259
|
-
process.stdout.write(
|
|
287
|
+
process.stdout.write(buildHelp(VERSION));
|
|
260
288
|
process.exit(0);
|
|
261
289
|
}
|
|
262
290
|
|
|
@@ -483,6 +511,10 @@ async function dispatch(cmd, rest) {
|
|
|
483
511
|
break;
|
|
484
512
|
}
|
|
485
513
|
|
|
514
|
+
case "status":
|
|
515
|
+
await cmdStatus();
|
|
516
|
+
return; // skip checkForUpdate after status (avoid noise)
|
|
517
|
+
|
|
486
518
|
case "setup":
|
|
487
519
|
case "install":
|
|
488
520
|
await cmdSetup();
|