@naia-team/cli 0.1.0 → 0.1.2
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 +14 -8
- package/package.json +1 -1
- package/src/commands/auth-login.js +14 -2
- package/src/commands/install.js +239 -114
- package/src/commands/session-run.js +1 -2
- package/src/commands/uninstall.js +127 -0
- package/src/commands/utils.js +6 -0
- package/src/lib/auth-store.js +28 -0
- package/src/lib/config-store.js +2 -2
- package/src/lib/mcp-config.js +4 -5
- package/src/main.js +13 -3
package/README.md
CHANGED
|
@@ -1,36 +1,42 @@
|
|
|
1
|
-
# @naia/cli
|
|
1
|
+
# @naia-team/cli
|
|
2
2
|
|
|
3
3
|
Naia platform CLI for local agents (Codex, Claude Code, Claude Desktop via MCP).
|
|
4
4
|
|
|
5
5
|
## Install
|
|
6
6
|
|
|
7
7
|
```bash
|
|
8
|
-
npx @naia/cli install
|
|
8
|
+
npx @naia-team/cli install
|
|
9
9
|
```
|
|
10
10
|
|
|
11
11
|
## Login
|
|
12
12
|
|
|
13
13
|
```bash
|
|
14
|
-
npx @naia/cli auth login --org-slug <org-slug> --webapp-url <webapp-url>
|
|
14
|
+
npx @naia-team/cli auth login --org-slug <org-slug> --webapp-url <webapp-url>
|
|
15
|
+
npx @naia-team/cli auth logout --profile default
|
|
15
16
|
```
|
|
16
17
|
|
|
17
18
|
## Verify
|
|
18
19
|
|
|
19
20
|
```bash
|
|
20
|
-
npx @naia/cli doctor --runtime-url <runtime-url>
|
|
21
|
+
npx @naia-team/cli doctor --runtime-url <runtime-url>
|
|
21
22
|
```
|
|
22
23
|
|
|
23
24
|
## Platform commands
|
|
24
25
|
|
|
25
26
|
```bash
|
|
26
|
-
npx @naia/cli platform invoices list
|
|
27
|
-
npx @naia/cli platform invoices create-draft --issue-date 2026-04-06 --description "CLI draft" --amount 100 --entity-id <id>
|
|
28
|
-
npx @naia/cli platform invoices issue --invoice-id <id>
|
|
27
|
+
npx @naia-team/cli platform invoices list
|
|
28
|
+
npx @naia-team/cli platform invoices create-draft --issue-date 2026-04-06 --description "CLI draft" --amount 100 --entity-id <id>
|
|
29
|
+
npx @naia-team/cli platform invoices issue --invoice-id <id>
|
|
29
30
|
```
|
|
30
31
|
|
|
31
32
|
## MCP server
|
|
32
33
|
|
|
33
34
|
```bash
|
|
34
|
-
npx @naia/cli mcp serve
|
|
35
|
+
npx @naia-team/cli mcp serve
|
|
35
36
|
```
|
|
36
37
|
|
|
38
|
+
## Uninstall
|
|
39
|
+
|
|
40
|
+
```bash
|
|
41
|
+
npx @naia-team/cli uninstall
|
|
42
|
+
```
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { randomUUID } from "node:crypto";
|
|
2
2
|
import { createServer } from "node:http";
|
|
3
3
|
import { URL } from "node:url";
|
|
4
|
-
import { writeAuthProfile } from "../lib/auth-store.js";
|
|
4
|
+
import { removeAuthProfile, writeAuthProfile } from "../lib/auth-store.js";
|
|
5
5
|
import { parseArgVector } from "../lib/args.js";
|
|
6
6
|
import { readCliConfig } from "../lib/config-store.js";
|
|
7
7
|
|
|
@@ -27,6 +27,18 @@ export async function runAuthLogin(args) {
|
|
|
27
27
|
console.log(`memberId: ${callback.memberId}`);
|
|
28
28
|
}
|
|
29
29
|
|
|
30
|
+
export async function runAuthLogout(args) {
|
|
31
|
+
const { options } = parseArgVector(args);
|
|
32
|
+
const defaults = await readCliConfig();
|
|
33
|
+
const profile = options.profile ?? defaults.defaultProfile ?? "default";
|
|
34
|
+
const removed = await removeAuthProfile(profile);
|
|
35
|
+
if (!removed) {
|
|
36
|
+
console.log(`No auth profile found for '${profile}'.`);
|
|
37
|
+
return;
|
|
38
|
+
}
|
|
39
|
+
console.log(`Logged out profile '${profile}'.`);
|
|
40
|
+
}
|
|
41
|
+
|
|
30
42
|
async function waitForCallback(config, state) {
|
|
31
43
|
return new Promise((resolve, reject) => {
|
|
32
44
|
const server = createServer((req, res) => {
|
|
@@ -94,7 +106,7 @@ async function waitForCallback(config, state) {
|
|
|
94
106
|
async function parseConfig(args) {
|
|
95
107
|
const { options } = parseArgVector(args);
|
|
96
108
|
const defaults = await readCliConfig();
|
|
97
|
-
const webappBaseUrl = options.webappUrl ?? process.env.NAIA_WEBAPP_URL ?? defaults.webappUrl ?? "
|
|
109
|
+
const webappBaseUrl = options.webappUrl ?? process.env.NAIA_WEBAPP_URL ?? defaults.webappUrl ?? "https://app.naia.team";
|
|
98
110
|
const orgSlug = options.orgSlug ?? "";
|
|
99
111
|
const profile = options.profile ?? defaults.defaultProfile ?? "default";
|
|
100
112
|
const timeoutMs = Number.parseInt(options.timeoutMs ?? "180000", 10);
|
package/src/commands/install.js
CHANGED
|
@@ -6,14 +6,31 @@ import { homedir } from "node:os";
|
|
|
6
6
|
import { spawnSync } from "node:child_process";
|
|
7
7
|
import { parseArgVector } from "../lib/args.js";
|
|
8
8
|
import { defaultCliConfig, getConfigPath, readCliConfig, writeCliConfig } from "../lib/config-store.js";
|
|
9
|
-
import { getAuthStorePath } from "../lib/auth-store.js";
|
|
9
|
+
import { getAuthStorePath, readAuthProfile } from "../lib/auth-store.js";
|
|
10
10
|
import { buildClaudeDesktopJsonSnippet, buildCodexTomlSnippet, buildMcpServerDefinition } from "../lib/mcp-config.js";
|
|
11
|
+
import { runAuthLogin } from "./auth-login.js";
|
|
11
12
|
|
|
12
|
-
const
|
|
13
|
-
{
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
13
|
+
const TARGETS = [
|
|
14
|
+
{
|
|
15
|
+
id: "codex",
|
|
16
|
+
label: "Codex CLI",
|
|
17
|
+
executor: { id: "codex-cli", name: "Codex", type: "agent" },
|
|
18
|
+
},
|
|
19
|
+
{
|
|
20
|
+
id: "claude-code",
|
|
21
|
+
label: "Claude Code",
|
|
22
|
+
executor: { id: "claude-code", name: "Claude Code", type: "agent" },
|
|
23
|
+
},
|
|
24
|
+
{
|
|
25
|
+
id: "claude-desktop",
|
|
26
|
+
label: "Claude Desktop",
|
|
27
|
+
executor: { id: "claude-desktop", name: "Claude Desktop", type: "agent" },
|
|
28
|
+
},
|
|
29
|
+
{
|
|
30
|
+
id: "manual",
|
|
31
|
+
label: "Manual config only",
|
|
32
|
+
executor: { id: "naia-cli", name: "Naia CLI", type: "agent" },
|
|
33
|
+
},
|
|
17
34
|
];
|
|
18
35
|
|
|
19
36
|
export async function runInstall(args) {
|
|
@@ -22,53 +39,54 @@ export async function runInstall(args) {
|
|
|
22
39
|
const current = await readCliConfig();
|
|
23
40
|
const base = defaultCliConfig();
|
|
24
41
|
|
|
25
|
-
|
|
26
|
-
let
|
|
42
|
+
const localMode = options.local === "true";
|
|
43
|
+
let runtimeUrl = options.runtimeUrl
|
|
44
|
+
?? (localMode ? "http://localhost:4310" : base.runtimeUrl);
|
|
45
|
+
let webappUrl = options.webappUrl
|
|
46
|
+
?? (localMode ? "http://localhost:5173" : base.webappUrl);
|
|
27
47
|
let profile = options.profile ?? current.defaultProfile ?? base.defaultProfile;
|
|
28
|
-
let executorName = options.executorName ?? current.executor.name;
|
|
29
|
-
let executorId = options.executorId ?? current.executor.id;
|
|
30
|
-
let executorType = options.executorType === "user" ? "user" : current.executor.type;
|
|
31
48
|
let targets = parseTargets(options.targets);
|
|
49
|
+
const nonInteractive = options.nonInteractive === "true";
|
|
32
50
|
|
|
33
|
-
if (
|
|
34
|
-
const interactive = await
|
|
35
|
-
runtimeUrl,
|
|
36
|
-
webappUrl,
|
|
37
|
-
profile,
|
|
38
|
-
executorName,
|
|
39
|
-
executorId,
|
|
40
|
-
executorType,
|
|
41
|
-
targets,
|
|
42
|
-
});
|
|
51
|
+
if (!nonInteractive && targets.length === 0) {
|
|
52
|
+
const interactive = await runWizard({ profile });
|
|
43
53
|
runtimeUrl = interactive.runtimeUrl;
|
|
44
54
|
webappUrl = interactive.webappUrl;
|
|
45
55
|
profile = interactive.profile;
|
|
46
|
-
executorName = interactive.executorName;
|
|
47
|
-
executorId = interactive.executorId;
|
|
48
|
-
executorType = interactive.executorType;
|
|
49
56
|
targets = interactive.targets;
|
|
50
57
|
}
|
|
58
|
+
if (targets.length === 0) {
|
|
59
|
+
targets = ["manual"];
|
|
60
|
+
}
|
|
51
61
|
|
|
62
|
+
const defaultExecutor = resolveExecutor(targets[0]);
|
|
52
63
|
const config = {
|
|
53
64
|
...current,
|
|
54
65
|
runtimeUrl,
|
|
55
66
|
webappUrl,
|
|
56
67
|
defaultProfile: profile,
|
|
57
68
|
executor: {
|
|
58
|
-
id: executorId,
|
|
59
|
-
name: executorName,
|
|
60
|
-
type: executorType === "user" ? "user" :
|
|
69
|
+
id: options.executorId ?? defaultExecutor.id,
|
|
70
|
+
name: options.executorName ?? defaultExecutor.name,
|
|
71
|
+
type: options.executorType === "user" ? "user" : defaultExecutor.type,
|
|
61
72
|
},
|
|
62
73
|
};
|
|
63
74
|
await writeCliConfig(config);
|
|
64
75
|
|
|
65
|
-
const
|
|
66
|
-
|
|
67
|
-
|
|
76
|
+
const authResult = await ensureAuthProfile({
|
|
77
|
+
profile,
|
|
78
|
+
webappUrl,
|
|
79
|
+
orgSlug: options.orgSlug,
|
|
80
|
+
nonInteractive,
|
|
81
|
+
});
|
|
68
82
|
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
83
|
+
const installResult = await applyTargets(targets, config);
|
|
84
|
+
printSummary({
|
|
85
|
+
config,
|
|
86
|
+
targets,
|
|
87
|
+
installResult,
|
|
88
|
+
authResult,
|
|
89
|
+
});
|
|
72
90
|
}
|
|
73
91
|
|
|
74
92
|
function parseTargets(raw) {
|
|
@@ -76,37 +94,43 @@ function parseTargets(raw) {
|
|
|
76
94
|
return raw
|
|
77
95
|
.split(",")
|
|
78
96
|
.map((item) => item.trim().toLowerCase())
|
|
79
|
-
.filter((item) =>
|
|
97
|
+
.filter((item) => TARGETS.some((target) => target.id === item));
|
|
80
98
|
}
|
|
81
99
|
|
|
82
|
-
async function
|
|
100
|
+
async function runWizard(defaults) {
|
|
83
101
|
const rl = createInterface({ input: process.stdin, output: process.stdout });
|
|
84
102
|
try {
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
const
|
|
88
|
-
const
|
|
89
|
-
const
|
|
90
|
-
const
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
103
|
+
printHeader();
|
|
104
|
+
const mode = (await rl.question("Mode [1=production, 2=local] (1): ")).trim();
|
|
105
|
+
const production = mode !== "2";
|
|
106
|
+
const runtimeUrl = production ? "https://naia-agent.fly.dev" : "http://localhost:4310";
|
|
107
|
+
const webappUrl = production ? "https://app.naia.team" : "http://localhost:5173";
|
|
108
|
+
const profile = (await rl.question(`Profile name (${defaults.profile}): `)).trim() || defaults.profile;
|
|
109
|
+
|
|
110
|
+
console.log(`Runtime: ${runtimeUrl}`);
|
|
111
|
+
console.log(`Webapp: ${webappUrl}`);
|
|
94
112
|
|
|
95
113
|
console.log("");
|
|
96
|
-
console.log("Select
|
|
97
|
-
|
|
98
|
-
|
|
114
|
+
console.log("Select agent clients (comma-separated numbers):");
|
|
115
|
+
TARGETS.forEach((target, index) => {
|
|
116
|
+
const marker = index < 2 ? "[x]" : "[ ]";
|
|
117
|
+
console.log(` ${marker} ${index + 1}. ${target.label}`);
|
|
99
118
|
});
|
|
100
|
-
const
|
|
101
|
-
const targets = parseTargetSelection(
|
|
119
|
+
const rawSelection = (await rl.question("Targets (default 1,2): ")).trim();
|
|
120
|
+
const targets = parseTargetSelection(rawSelection || "1,2");
|
|
121
|
+
|
|
122
|
+
console.log("");
|
|
123
|
+
console.log("Selected:");
|
|
124
|
+
for (const target of targets) {
|
|
125
|
+
const meta = TARGETS.find((item) => item.id === target);
|
|
126
|
+
if (!meta) continue;
|
|
127
|
+
console.log(` [x] ${meta.label} -> ${meta.executor.name} (${meta.executor.id})`);
|
|
128
|
+
}
|
|
102
129
|
|
|
103
130
|
return {
|
|
104
131
|
runtimeUrl,
|
|
105
132
|
webappUrl,
|
|
106
133
|
profile,
|
|
107
|
-
executorName,
|
|
108
|
-
executorId,
|
|
109
|
-
executorType,
|
|
110
134
|
targets: targets.length > 0 ? targets : ["manual"],
|
|
111
135
|
};
|
|
112
136
|
} finally {
|
|
@@ -115,47 +139,104 @@ async function runInteractiveWizard(defaults) {
|
|
|
115
139
|
}
|
|
116
140
|
|
|
117
141
|
function parseTargetSelection(input) {
|
|
118
|
-
if (!input) return [];
|
|
119
142
|
const picked = new Set();
|
|
120
143
|
for (const token of input.split(",")) {
|
|
121
144
|
const index = Number.parseInt(token.trim(), 10);
|
|
122
|
-
if (!Number.isFinite(index) || index < 1 || index >
|
|
123
|
-
picked.add(
|
|
145
|
+
if (!Number.isFinite(index) || index < 1 || index > TARGETS.length) continue;
|
|
146
|
+
picked.add(TARGETS[index - 1].id);
|
|
124
147
|
}
|
|
125
148
|
return [...picked];
|
|
126
149
|
}
|
|
127
150
|
|
|
128
|
-
|
|
151
|
+
function printHeader() {
|
|
152
|
+
console.log("╔═══════════════════════════════════════╗");
|
|
153
|
+
console.log("║ Naia CLI Installer ║");
|
|
154
|
+
console.log("║ Connect Codex/Claude to Naia MCP ║");
|
|
155
|
+
console.log("╚═══════════════════════════════════════╝");
|
|
156
|
+
console.log("");
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
function resolveExecutor(target) {
|
|
160
|
+
return TARGETS.find((item) => item.id === target)?.executor ?? TARGETS[0].executor;
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
async function ensureAuthProfile(input) {
|
|
164
|
+
const existing = await readAuthProfile(input.profile);
|
|
165
|
+
if (existing) {
|
|
166
|
+
return {
|
|
167
|
+
status: "existing",
|
|
168
|
+
profile: input.profile,
|
|
169
|
+
organizationId: existing.organizationId,
|
|
170
|
+
memberId: existing.memberId,
|
|
171
|
+
};
|
|
172
|
+
}
|
|
173
|
+
if (input.nonInteractive) {
|
|
174
|
+
return {
|
|
175
|
+
status: "missing",
|
|
176
|
+
profile: input.profile,
|
|
177
|
+
};
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
const rl = createInterface({ input: process.stdin, output: process.stdout });
|
|
181
|
+
try {
|
|
182
|
+
const answer = (await rl.question("No login for this profile. Login now? [Y/n]: ")).trim().toLowerCase();
|
|
183
|
+
if (answer === "n" || answer === "no") {
|
|
184
|
+
return {
|
|
185
|
+
status: "missing",
|
|
186
|
+
profile: input.profile,
|
|
187
|
+
};
|
|
188
|
+
}
|
|
189
|
+
const orgSlug = (input.orgSlug ?? (await rl.question("Organization slug: ")).trim());
|
|
190
|
+
if (!orgSlug) {
|
|
191
|
+
return {
|
|
192
|
+
status: "missing",
|
|
193
|
+
profile: input.profile,
|
|
194
|
+
};
|
|
195
|
+
}
|
|
196
|
+
await runAuthLogin(["--profile", input.profile, "--org-slug", orgSlug, "--webapp-url", input.webappUrl]);
|
|
197
|
+
const fresh = await readAuthProfile(input.profile);
|
|
198
|
+
if (!fresh) {
|
|
199
|
+
return { status: "missing", profile: input.profile };
|
|
200
|
+
}
|
|
201
|
+
return {
|
|
202
|
+
status: "created",
|
|
203
|
+
profile: input.profile,
|
|
204
|
+
organizationId: fresh.organizationId,
|
|
205
|
+
memberId: fresh.memberId,
|
|
206
|
+
};
|
|
207
|
+
} finally {
|
|
208
|
+
rl.close();
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
async function applyTargets(targets, config) {
|
|
129
213
|
const result = [];
|
|
130
|
-
const
|
|
131
|
-
|
|
214
|
+
for (const target of targets) {
|
|
215
|
+
const executor = resolveExecutor(target);
|
|
132
216
|
if (target === "codex") {
|
|
133
|
-
result.push(await installCodex(config));
|
|
217
|
+
result.push(await installCodex(config.runtimeUrl, executor));
|
|
134
218
|
continue;
|
|
135
219
|
}
|
|
136
220
|
if (target === "claude-code") {
|
|
137
|
-
result.push(await installClaudeCode(config));
|
|
221
|
+
result.push(await installClaudeCode(config.runtimeUrl, executor));
|
|
138
222
|
continue;
|
|
139
223
|
}
|
|
140
224
|
if (target === "claude-desktop") {
|
|
141
|
-
result.push(await installClaudeDesktop(config));
|
|
142
|
-
continue;
|
|
143
|
-
}
|
|
144
|
-
if (target === "manual") {
|
|
145
|
-
result.push({ target, status: "manual", detail: "No file changes applied." });
|
|
225
|
+
result.push(await installClaudeDesktop(config.runtimeUrl, executor));
|
|
146
226
|
continue;
|
|
147
227
|
}
|
|
228
|
+
result.push({ target: "manual", status: "manual", detail: "No file changes applied.", executor });
|
|
148
229
|
}
|
|
149
230
|
return result;
|
|
150
231
|
}
|
|
151
232
|
|
|
152
|
-
async function installCodex(
|
|
233
|
+
async function installCodex(runtimeUrl, executor) {
|
|
153
234
|
const filePath = join(homedir(), ".codex", "config.toml");
|
|
154
235
|
const snippet = buildCodexTomlSnippet({
|
|
155
|
-
runtimeUrl
|
|
156
|
-
executorName:
|
|
157
|
-
executorId:
|
|
158
|
-
executorType:
|
|
236
|
+
runtimeUrl,
|
|
237
|
+
executorName: executor.name,
|
|
238
|
+
executorId: executor.id,
|
|
239
|
+
executorType: executor.type,
|
|
159
240
|
});
|
|
160
241
|
await ensureDir(dirname(filePath));
|
|
161
242
|
const existing = existsSync(filePath) ? await readFile(filePath, "utf8") : "";
|
|
@@ -165,10 +246,10 @@ async function installCodex(config) {
|
|
|
165
246
|
await backupFile(filePath);
|
|
166
247
|
}
|
|
167
248
|
await writeFile(filePath, merged, "utf8");
|
|
168
|
-
return { target: "codex", status: "updated", detail: filePath };
|
|
249
|
+
return { target: "codex", status: "updated", detail: filePath, executor };
|
|
169
250
|
}
|
|
170
251
|
|
|
171
|
-
async function installClaudeDesktop(
|
|
252
|
+
async function installClaudeDesktop(runtimeUrl, executor) {
|
|
172
253
|
const filePath = resolveClaudeDesktopPath();
|
|
173
254
|
await ensureDir(dirname(filePath));
|
|
174
255
|
const existing = existsSync(filePath) ? JSON.parse(await readFile(filePath, "utf8")) : {};
|
|
@@ -176,38 +257,64 @@ async function installClaudeDesktop(config) {
|
|
|
176
257
|
existing.mcpServers = {};
|
|
177
258
|
}
|
|
178
259
|
existing.mcpServers.naia = buildMcpServerDefinition({
|
|
179
|
-
runtimeUrl
|
|
180
|
-
executorName:
|
|
181
|
-
executorId:
|
|
182
|
-
executorType:
|
|
260
|
+
runtimeUrl,
|
|
261
|
+
executorName: executor.name,
|
|
262
|
+
executorId: executor.id,
|
|
263
|
+
executorType: executor.type,
|
|
183
264
|
});
|
|
184
265
|
if (existsSync(filePath)) {
|
|
185
266
|
await backupFile(filePath);
|
|
186
267
|
}
|
|
187
268
|
await writeFile(filePath, JSON.stringify(existing, null, 2), "utf8");
|
|
188
|
-
return { target: "claude-desktop", status: "updated", detail: filePath };
|
|
269
|
+
return { target: "claude-desktop", status: "updated", detail: filePath, executor };
|
|
189
270
|
}
|
|
190
271
|
|
|
191
|
-
async function installClaudeCode(
|
|
192
|
-
const
|
|
193
|
-
...process.env,
|
|
194
|
-
NAIA_RUNTIME_URL: config.runtimeUrl,
|
|
195
|
-
NAIA_EXECUTOR_NAME: config.executor.name,
|
|
196
|
-
NAIA_EXECUTOR_ID: config.executor.id,
|
|
197
|
-
NAIA_EXECUTOR_TYPE: config.executor.type,
|
|
198
|
-
};
|
|
199
|
-
const result = spawnSync("claude", ["mcp", "add", "naia", "--", "naia", "mcp", "serve"], {
|
|
272
|
+
async function installClaudeCode(runtimeUrl, executor) {
|
|
273
|
+
const remove = spawnSync("claude", ["mcp", "remove", "-s", "user", "naia"], {
|
|
200
274
|
stdio: "pipe",
|
|
201
275
|
encoding: "utf8",
|
|
202
|
-
env,
|
|
203
276
|
});
|
|
204
|
-
|
|
205
|
-
|
|
277
|
+
const definition = {
|
|
278
|
+
type: "stdio",
|
|
279
|
+
command: "npx",
|
|
280
|
+
args: ["-y", "@naia-team/cli", "mcp", "serve"],
|
|
281
|
+
env: {
|
|
282
|
+
NAIA_RUNTIME_URL: runtimeUrl,
|
|
283
|
+
NAIA_EXECUTOR_NAME: executor.name,
|
|
284
|
+
NAIA_EXECUTOR_ID: executor.id,
|
|
285
|
+
NAIA_EXECUTOR_TYPE: executor.type,
|
|
286
|
+
},
|
|
287
|
+
};
|
|
288
|
+
const add = spawnSync(
|
|
289
|
+
"claude",
|
|
290
|
+
[
|
|
291
|
+
"mcp",
|
|
292
|
+
"add-json",
|
|
293
|
+
"-s",
|
|
294
|
+
"user",
|
|
295
|
+
"naia",
|
|
296
|
+
JSON.stringify(definition),
|
|
297
|
+
],
|
|
298
|
+
{
|
|
299
|
+
stdio: "pipe",
|
|
300
|
+
encoding: "utf8",
|
|
301
|
+
env: process.env,
|
|
302
|
+
}
|
|
303
|
+
);
|
|
304
|
+
if (add.status === 0) {
|
|
305
|
+
return {
|
|
306
|
+
target: "claude-code",
|
|
307
|
+
status: "updated",
|
|
308
|
+
detail: "Registered with `claude mcp add -s user`.",
|
|
309
|
+
executor,
|
|
310
|
+
notes: remove.status === 0 ? "Replaced existing naia MCP server." : undefined,
|
|
311
|
+
};
|
|
206
312
|
}
|
|
207
313
|
return {
|
|
208
314
|
target: "claude-code",
|
|
209
315
|
status: "manual",
|
|
210
|
-
detail: "Could not run `claude mcp add`. Run manually:\nclaude mcp add naia -- naia mcp serve",
|
|
316
|
+
detail: "Could not run `claude mcp add`. Run manually:\nclaude mcp add -s user -e NAIA_RUNTIME_URL=... -e NAIA_EXECUTOR_NAME=... -e NAIA_EXECUTOR_ID=... -e NAIA_EXECUTOR_TYPE=agent naia -- npx -y @naia-team/cli mcp serve",
|
|
317
|
+
executor,
|
|
211
318
|
};
|
|
212
319
|
}
|
|
213
320
|
|
|
@@ -251,46 +358,64 @@ async function backupFile(path) {
|
|
|
251
358
|
await copyFile(path, `${path}.${timestamp}.bak`);
|
|
252
359
|
}
|
|
253
360
|
|
|
254
|
-
function printSummary(
|
|
361
|
+
function printSummary(input) {
|
|
255
362
|
const configPath = getConfigPath();
|
|
256
363
|
const authPath = getAuthStorePath();
|
|
257
364
|
|
|
258
|
-
console.log("Naia CLI install completed.");
|
|
259
|
-
console.log(`config: ${configPath}`);
|
|
260
|
-
console.log(`auth store: ${authPath}`);
|
|
261
365
|
console.log("");
|
|
262
|
-
console.log("
|
|
263
|
-
console.log(`-
|
|
264
|
-
console.log(`-
|
|
265
|
-
console.log(
|
|
266
|
-
console.log(
|
|
366
|
+
console.log("Install completed.");
|
|
367
|
+
console.log(`- config: ${configPath}`);
|
|
368
|
+
console.log(`- auth store: ${authPath}`);
|
|
369
|
+
console.log("");
|
|
370
|
+
console.log("Runtime profile:");
|
|
371
|
+
console.log(`- runtime: ${input.config.runtimeUrl}`);
|
|
372
|
+
console.log(`- webapp: ${input.config.webappUrl}`);
|
|
373
|
+
console.log(`- profile: ${input.config.defaultProfile}`);
|
|
374
|
+
console.log(`- default executor: ${input.config.executor.name} (${input.config.executor.id}, ${input.config.executor.type})`);
|
|
375
|
+
|
|
376
|
+
if (input.authResult.status === "existing" || input.authResult.status === "created") {
|
|
377
|
+
console.log("");
|
|
378
|
+
console.log("Auth:");
|
|
379
|
+
console.log(`- status: ${input.authResult.status}`);
|
|
380
|
+
console.log(`- organizationId: ${input.authResult.organizationId}`);
|
|
381
|
+
console.log(`- memberId: ${input.authResult.memberId}`);
|
|
382
|
+
} else {
|
|
383
|
+
console.log("");
|
|
384
|
+
console.log("Auth:");
|
|
385
|
+
console.log("- status: missing (run auth login before using platform commands)");
|
|
386
|
+
}
|
|
387
|
+
|
|
267
388
|
console.log("");
|
|
268
|
-
console.log(
|
|
269
|
-
for (const item of installResult) {
|
|
270
|
-
console.log(`- ${item.target}: ${item.status}
|
|
389
|
+
console.log("Targets:");
|
|
390
|
+
for (const item of input.installResult) {
|
|
391
|
+
console.log(`- ${item.target}: ${item.status} -> ${item.detail}`);
|
|
392
|
+
console.log(` executor: ${item.executor.name} (${item.executor.id}, ${item.executor.type})`);
|
|
393
|
+
if (item.notes) {
|
|
394
|
+
console.log(` note: ${item.notes}`);
|
|
395
|
+
}
|
|
271
396
|
}
|
|
397
|
+
|
|
272
398
|
console.log("");
|
|
273
399
|
console.log("Next steps:");
|
|
274
|
-
console.log(
|
|
275
|
-
console.log(
|
|
276
|
-
console.log(
|
|
400
|
+
console.log("1) npx @naia-team/cli doctor");
|
|
401
|
+
console.log("2) npx @naia-team/cli platform invoices list");
|
|
402
|
+
console.log("3) In Claude Code run: /mcp (verify server `naia` is enabled)");
|
|
277
403
|
console.log("");
|
|
278
404
|
console.log("Manual snippets:");
|
|
279
405
|
console.log("");
|
|
280
406
|
console.log("Codex (~/.codex/config.toml):");
|
|
281
407
|
console.log(buildCodexTomlSnippet({
|
|
282
|
-
runtimeUrl: config.runtimeUrl,
|
|
283
|
-
executorName:
|
|
284
|
-
executorId:
|
|
285
|
-
executorType:
|
|
408
|
+
runtimeUrl: input.config.runtimeUrl,
|
|
409
|
+
executorName: resolveExecutor("codex").name,
|
|
410
|
+
executorId: resolveExecutor("codex").id,
|
|
411
|
+
executorType: resolveExecutor("codex").type,
|
|
286
412
|
}));
|
|
287
413
|
console.log("");
|
|
288
414
|
console.log("Claude Desktop (claude_desktop_config.json):");
|
|
289
415
|
console.log(buildClaudeDesktopJsonSnippet({
|
|
290
|
-
runtimeUrl: config.runtimeUrl,
|
|
291
|
-
executorName:
|
|
292
|
-
executorId:
|
|
293
|
-
executorType:
|
|
416
|
+
runtimeUrl: input.config.runtimeUrl,
|
|
417
|
+
executorName: resolveExecutor("claude-desktop").name,
|
|
418
|
+
executorId: resolveExecutor("claude-desktop").id,
|
|
419
|
+
executorType: resolveExecutor("claude-desktop").type,
|
|
294
420
|
}));
|
|
295
421
|
}
|
|
296
|
-
|
|
@@ -61,7 +61,7 @@ async function parseConfig(args) {
|
|
|
61
61
|
const profile = options.profile ?? defaults.defaultProfile ?? "default";
|
|
62
62
|
const stored = await readAuthProfile(profile);
|
|
63
63
|
|
|
64
|
-
const runtimeUrl = (options.runtimeUrl ?? process.env.NAIA_RUNTIME_URL ?? defaults.runtimeUrl ?? "
|
|
64
|
+
const runtimeUrl = (options.runtimeUrl ?? process.env.NAIA_RUNTIME_URL ?? defaults.runtimeUrl ?? "https://naia-agent.fly.dev").replace(/\/+$/, "");
|
|
65
65
|
const organizationId = options.organizationId ?? process.env.NAIA_ORGANIZATION_ID ?? stored?.organizationId ?? "";
|
|
66
66
|
const memberId = options.memberId ?? process.env.NAIA_MEMBER_ID ?? stored?.memberId ?? "";
|
|
67
67
|
const bearerToken = options.token ?? process.env.NAIA_BEARER_TOKEN ?? stored?.token;
|
|
@@ -247,4 +247,3 @@ function log(message) {
|
|
|
247
247
|
function sleep(ms) {
|
|
248
248
|
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
249
249
|
}
|
|
250
|
-
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
import { existsSync } from "node:fs";
|
|
2
|
+
import { readFile, writeFile, copyFile } from "node:fs/promises";
|
|
3
|
+
import { join, dirname } from "node:path";
|
|
4
|
+
import { homedir } from "node:os";
|
|
5
|
+
import { parseArgVector } from "../lib/args.js";
|
|
6
|
+
import { ensureDir } from "./utils.js";
|
|
7
|
+
|
|
8
|
+
const ALL_TARGETS = ["codex", "claude-code", "claude-desktop"];
|
|
9
|
+
|
|
10
|
+
export async function runUninstall(args) {
|
|
11
|
+
const { options } = parseArgVector(args);
|
|
12
|
+
const targets = parseTargets(options.targets);
|
|
13
|
+
const selected = targets.length > 0 ? targets : ALL_TARGETS;
|
|
14
|
+
const results = [];
|
|
15
|
+
|
|
16
|
+
for (const target of selected) {
|
|
17
|
+
if (target === "codex") {
|
|
18
|
+
results.push(await uninstallCodex());
|
|
19
|
+
continue;
|
|
20
|
+
}
|
|
21
|
+
if (target === "claude-code") {
|
|
22
|
+
results.push(await uninstallClaudeCode());
|
|
23
|
+
continue;
|
|
24
|
+
}
|
|
25
|
+
if (target === "claude-desktop") {
|
|
26
|
+
results.push(await uninstallClaudeDesktop());
|
|
27
|
+
continue;
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
console.log("Naia CLI uninstall completed.");
|
|
32
|
+
for (const item of results) {
|
|
33
|
+
console.log(`- ${item.target}: ${item.status}${item.detail ? ` -> ${item.detail}` : ""}`);
|
|
34
|
+
}
|
|
35
|
+
console.log("");
|
|
36
|
+
console.log("Optional cleanup:");
|
|
37
|
+
console.log("- npx @naia-team/cli auth logout --profile default");
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
function parseTargets(raw) {
|
|
41
|
+
if (!raw) return [];
|
|
42
|
+
return raw
|
|
43
|
+
.split(",")
|
|
44
|
+
.map((item) => item.trim().toLowerCase())
|
|
45
|
+
.filter((item) => ALL_TARGETS.includes(item));
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
async function uninstallCodex() {
|
|
49
|
+
const filePath = join(homedir(), ".codex", "config.toml");
|
|
50
|
+
if (!existsSync(filePath)) {
|
|
51
|
+
return { target: "codex", status: "skipped", detail: "No ~/.codex/config.toml" };
|
|
52
|
+
}
|
|
53
|
+
const existing = await readFile(filePath, "utf8");
|
|
54
|
+
const cleaned = stripCodexNaiaBlock(existing);
|
|
55
|
+
if (cleaned === existing) {
|
|
56
|
+
return { target: "codex", status: "skipped", detail: "No naia MCP block found." };
|
|
57
|
+
}
|
|
58
|
+
await backupFile(filePath);
|
|
59
|
+
await writeFile(filePath, cleaned.trimEnd() + "\n", "utf8");
|
|
60
|
+
return { target: "codex", status: "removed", detail: filePath };
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
async function uninstallClaudeCode() {
|
|
64
|
+
const remove = await import("node:child_process").then(({ spawnSync }) =>
|
|
65
|
+
spawnSync("claude", ["mcp", "remove", "-s", "user", "naia"], {
|
|
66
|
+
stdio: "pipe",
|
|
67
|
+
encoding: "utf8",
|
|
68
|
+
})
|
|
69
|
+
);
|
|
70
|
+
if (remove.status === 0) {
|
|
71
|
+
return { target: "claude-code", status: "removed", detail: "Removed user-scope MCP server 'naia'." };
|
|
72
|
+
}
|
|
73
|
+
return { target: "claude-code", status: "skipped", detail: "Could not remove (not configured or CLI unavailable)." };
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
async function uninstallClaudeDesktop() {
|
|
77
|
+
const filePath = resolveClaudeDesktopPath();
|
|
78
|
+
if (!existsSync(filePath)) {
|
|
79
|
+
return { target: "claude-desktop", status: "skipped", detail: "No desktop config file found." };
|
|
80
|
+
}
|
|
81
|
+
const existing = JSON.parse(await readFile(filePath, "utf8"));
|
|
82
|
+
if (!existing?.mcpServers?.naia) {
|
|
83
|
+
return { target: "claude-desktop", status: "skipped", detail: "No naia MCP server found." };
|
|
84
|
+
}
|
|
85
|
+
delete existing.mcpServers.naia;
|
|
86
|
+
await ensureDir(dirname(filePath));
|
|
87
|
+
await backupFile(filePath);
|
|
88
|
+
await writeFile(filePath, JSON.stringify(existing, null, 2), "utf8");
|
|
89
|
+
return { target: "claude-desktop", status: "removed", detail: filePath };
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
function resolveClaudeDesktopPath() {
|
|
93
|
+
if (process.platform === "darwin") {
|
|
94
|
+
return join(homedir(), "Library", "Application Support", "Claude", "claude_desktop_config.json");
|
|
95
|
+
}
|
|
96
|
+
if (process.platform === "win32") {
|
|
97
|
+
const appData = process.env.APPDATA ?? join(homedir(), "AppData", "Roaming");
|
|
98
|
+
return join(appData, "Claude", "claude_desktop_config.json");
|
|
99
|
+
}
|
|
100
|
+
return join(homedir(), ".config", "Claude", "claude_desktop_config.json");
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
function stripCodexNaiaBlock(source) {
|
|
104
|
+
const lines = source.split("\n");
|
|
105
|
+
const result = [];
|
|
106
|
+
let skipping = false;
|
|
107
|
+
for (const line of lines) {
|
|
108
|
+
const trimmed = line.trim();
|
|
109
|
+
if (trimmed === "[mcp_servers.naia]" || trimmed === "[mcp_servers.naia.env]") {
|
|
110
|
+
skipping = true;
|
|
111
|
+
continue;
|
|
112
|
+
}
|
|
113
|
+
if (skipping && trimmed.startsWith("[") && trimmed.endsWith("]")) {
|
|
114
|
+
skipping = false;
|
|
115
|
+
}
|
|
116
|
+
if (!skipping) {
|
|
117
|
+
result.push(line);
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
return result.join("\n");
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
async function backupFile(path) {
|
|
124
|
+
const timestamp = new Date().toISOString().replace(/[:.]/g, "-");
|
|
125
|
+
await copyFile(path, `${path}.${timestamp}.bak`);
|
|
126
|
+
}
|
|
127
|
+
|
package/src/lib/auth-store.js
CHANGED
|
@@ -39,6 +39,34 @@ export async function writeAuthProfile(input) {
|
|
|
39
39
|
await writeFile(filePath, JSON.stringify(next, null, 2), { mode: 0o600 });
|
|
40
40
|
}
|
|
41
41
|
|
|
42
|
+
export async function removeAuthProfile(profile = "default") {
|
|
43
|
+
const filePath = getAuthStorePath();
|
|
44
|
+
const existing = await readStore(filePath);
|
|
45
|
+
if (!existing?.profiles || typeof existing.profiles !== "object") {
|
|
46
|
+
return false;
|
|
47
|
+
}
|
|
48
|
+
const resolvedProfile = profile === "default"
|
|
49
|
+
? (existing.defaultProfile || "default")
|
|
50
|
+
: profile;
|
|
51
|
+
if (!existing.profiles[resolvedProfile]) {
|
|
52
|
+
return false;
|
|
53
|
+
}
|
|
54
|
+
const nextProfiles = { ...existing.profiles };
|
|
55
|
+
delete nextProfiles[resolvedProfile];
|
|
56
|
+
|
|
57
|
+
const nextDefault = existing.defaultProfile === resolvedProfile
|
|
58
|
+
? (Object.keys(nextProfiles)[0] ?? "default")
|
|
59
|
+
: (existing.defaultProfile || "default");
|
|
60
|
+
|
|
61
|
+
const next = {
|
|
62
|
+
defaultProfile: nextDefault,
|
|
63
|
+
profiles: nextProfiles,
|
|
64
|
+
};
|
|
65
|
+
await mkdir(dirname(filePath), { recursive: true });
|
|
66
|
+
await writeFile(filePath, JSON.stringify(next, null, 2), { mode: 0o600 });
|
|
67
|
+
return true;
|
|
68
|
+
}
|
|
69
|
+
|
|
42
70
|
async function readStore(filePath) {
|
|
43
71
|
try {
|
|
44
72
|
const raw = await readFile(filePath, "utf8");
|
package/src/lib/config-store.js
CHANGED
|
@@ -4,8 +4,8 @@ import { homedir } from "node:os";
|
|
|
4
4
|
|
|
5
5
|
const DEFAULT_CONFIG = {
|
|
6
6
|
defaultProfile: "default",
|
|
7
|
-
runtimeUrl: "
|
|
8
|
-
webappUrl: "
|
|
7
|
+
runtimeUrl: "https://naia-agent.fly.dev",
|
|
8
|
+
webappUrl: "https://app.naia.team",
|
|
9
9
|
executor: {
|
|
10
10
|
id: "naia-cli",
|
|
11
11
|
name: "CLI",
|
package/src/lib/mcp-config.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
export function buildMcpServerDefinition(input) {
|
|
2
2
|
return {
|
|
3
|
-
command: "
|
|
4
|
-
args: ["mcp", "serve"],
|
|
3
|
+
command: "npx",
|
|
4
|
+
args: ["-y", "@naia-team/cli", "mcp", "serve"],
|
|
5
5
|
env: {
|
|
6
6
|
NAIA_RUNTIME_URL: input.runtimeUrl,
|
|
7
7
|
NAIA_EXECUTOR_NAME: input.executorName,
|
|
@@ -14,8 +14,8 @@ export function buildMcpServerDefinition(input) {
|
|
|
14
14
|
export function buildCodexTomlSnippet(input) {
|
|
15
15
|
return [
|
|
16
16
|
`[mcp_servers.naia]`,
|
|
17
|
-
`command = "
|
|
18
|
-
`args = ["mcp", "serve"]`,
|
|
17
|
+
`command = "npx"`,
|
|
18
|
+
`args = ["-y", "@naia-team/cli", "mcp", "serve"]`,
|
|
19
19
|
`[mcp_servers.naia.env]`,
|
|
20
20
|
`NAIA_RUNTIME_URL = "${input.runtimeUrl}"`,
|
|
21
21
|
`NAIA_EXECUTOR_NAME = "${input.executorName}"`,
|
|
@@ -31,4 +31,3 @@ export function buildClaudeDesktopJsonSnippet(input) {
|
|
|
31
31
|
},
|
|
32
32
|
}, null, 2);
|
|
33
33
|
}
|
|
34
|
-
|
package/src/main.js
CHANGED
|
@@ -1,9 +1,10 @@
|
|
|
1
|
-
import { runAuthLogin } from "./commands/auth-login.js";
|
|
1
|
+
import { runAuthLogin, runAuthLogout } from "./commands/auth-login.js";
|
|
2
2
|
import { runInstall } from "./commands/install.js";
|
|
3
3
|
import { runDoctor } from "./commands/doctor.js";
|
|
4
4
|
import { runPlatform } from "./commands/platform.js";
|
|
5
5
|
import { runMcpServe } from "./commands/mcp-serve.js";
|
|
6
6
|
import { runSession } from "./commands/session-run.js";
|
|
7
|
+
import { runUninstall } from "./commands/uninstall.js";
|
|
7
8
|
|
|
8
9
|
export async function main() {
|
|
9
10
|
const args = process.argv.slice(2);
|
|
@@ -17,10 +18,18 @@ export async function main() {
|
|
|
17
18
|
await runAuthLogin(rest);
|
|
18
19
|
return;
|
|
19
20
|
}
|
|
21
|
+
if (group === "auth" && action === "logout") {
|
|
22
|
+
await runAuthLogout(rest);
|
|
23
|
+
return;
|
|
24
|
+
}
|
|
20
25
|
if (group === "install") {
|
|
21
26
|
await runInstall([action, ...rest].filter(Boolean));
|
|
22
27
|
return;
|
|
23
28
|
}
|
|
29
|
+
if (group === "uninstall") {
|
|
30
|
+
await runUninstall([action, ...rest].filter(Boolean));
|
|
31
|
+
return;
|
|
32
|
+
}
|
|
24
33
|
if (group === "doctor") {
|
|
25
34
|
await runDoctor([action, ...rest].filter(Boolean));
|
|
26
35
|
return;
|
|
@@ -48,12 +57,13 @@ export async function main() {
|
|
|
48
57
|
|
|
49
58
|
function printUsage() {
|
|
50
59
|
console.log(`Usage:
|
|
51
|
-
naia install [--
|
|
60
|
+
naia install [--targets codex,claude-code,claude-desktop,manual] [--local true]
|
|
61
|
+
naia uninstall [--targets codex,claude-code,claude-desktop]
|
|
52
62
|
naia doctor [--runtime-url <url>] [--profile default]
|
|
53
63
|
naia auth login --org-slug <slug> [--webapp-url <url>] [--profile default]
|
|
64
|
+
naia auth logout [--profile default]
|
|
54
65
|
naia session run --message "hola" [--runtime-url <url>]
|
|
55
66
|
naia platform invoices list [--runtime-url <url>]
|
|
56
67
|
naia mcp serve
|
|
57
68
|
`);
|
|
58
69
|
}
|
|
59
|
-
|