@agent-play/cli 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli.js +353 -0
- package/package.json +30 -0
package/dist/cli.js
ADDED
|
@@ -0,0 +1,353 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
// src/cli.ts
|
|
4
|
+
import { mkdir, readFile, unlink, writeFile } from "fs/promises";
|
|
5
|
+
import { homedir } from "os";
|
|
6
|
+
import { join } from "path";
|
|
7
|
+
import { createInterface } from "readline/promises";
|
|
8
|
+
import { stdin as input, stdout as output } from "process";
|
|
9
|
+
function credentialsPath() {
|
|
10
|
+
return join(homedir(), ".agent-play", "credentials.json");
|
|
11
|
+
}
|
|
12
|
+
async function loadCredentials() {
|
|
13
|
+
try {
|
|
14
|
+
const raw = await readFile(credentialsPath(), "utf8");
|
|
15
|
+
const json = JSON.parse(raw);
|
|
16
|
+
if (typeof json !== "object" || json === null) return null;
|
|
17
|
+
const o = json;
|
|
18
|
+
if (typeof o.serverUrl !== "string" || typeof o.token !== "string") {
|
|
19
|
+
return null;
|
|
20
|
+
}
|
|
21
|
+
return { serverUrl: o.serverUrl.replace(/\/$/, ""), token: o.token };
|
|
22
|
+
} catch {
|
|
23
|
+
return null;
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
async function saveCredentials(c) {
|
|
27
|
+
const dir = join(homedir(), ".agent-play");
|
|
28
|
+
await mkdir(dir, { recursive: true });
|
|
29
|
+
await writeFile(
|
|
30
|
+
credentialsPath(),
|
|
31
|
+
JSON.stringify({ serverUrl: c.serverUrl, token: c.token }, null, 2),
|
|
32
|
+
"utf8"
|
|
33
|
+
);
|
|
34
|
+
}
|
|
35
|
+
function defaultServerUrl() {
|
|
36
|
+
return process.env.AGENT_PLAY_SERVER_URL ?? "http://127.0.0.1:3000";
|
|
37
|
+
}
|
|
38
|
+
async function cmdLogin() {
|
|
39
|
+
const rl = createInterface({ input, output });
|
|
40
|
+
const serverUrl = ((await rl.question(
|
|
41
|
+
`Server URL [${defaultServerUrl()}]: `
|
|
42
|
+
)).trim() || defaultServerUrl()).replace(/\/$/, "");
|
|
43
|
+
const email = (await rl.question("Email: ")).trim();
|
|
44
|
+
if (email.length === 0) {
|
|
45
|
+
rl.close();
|
|
46
|
+
console.error("Email is required.");
|
|
47
|
+
process.exitCode = 1;
|
|
48
|
+
return;
|
|
49
|
+
}
|
|
50
|
+
const lookupRes = await fetch(`${serverUrl}/api/auth/lookup`, {
|
|
51
|
+
method: "POST",
|
|
52
|
+
headers: { "content-type": "application/json" },
|
|
53
|
+
body: JSON.stringify({ email })
|
|
54
|
+
});
|
|
55
|
+
const lookupText = await lookupRes.text();
|
|
56
|
+
if (!lookupRes.ok) {
|
|
57
|
+
rl.close();
|
|
58
|
+
console.error(`Lookup failed (${lookupRes.status}): ${lookupText}`);
|
|
59
|
+
process.exitCode = 1;
|
|
60
|
+
return;
|
|
61
|
+
}
|
|
62
|
+
let lookupJson;
|
|
63
|
+
try {
|
|
64
|
+
lookupJson = JSON.parse(lookupText);
|
|
65
|
+
} catch {
|
|
66
|
+
rl.close();
|
|
67
|
+
console.error("Invalid JSON from server.");
|
|
68
|
+
process.exitCode = 1;
|
|
69
|
+
return;
|
|
70
|
+
}
|
|
71
|
+
const exists = typeof lookupJson === "object" && lookupJson !== null && lookupJson.exists === true;
|
|
72
|
+
let token;
|
|
73
|
+
if (exists) {
|
|
74
|
+
const password = (await rl.question("Password: ")).trim();
|
|
75
|
+
rl.close();
|
|
76
|
+
const loginRes = await fetch(`${serverUrl}/api/auth/login`, {
|
|
77
|
+
method: "POST",
|
|
78
|
+
headers: { "content-type": "application/json" },
|
|
79
|
+
body: JSON.stringify({ email, password })
|
|
80
|
+
});
|
|
81
|
+
const loginText = await loginRes.text();
|
|
82
|
+
if (!loginRes.ok) {
|
|
83
|
+
console.error(`Login failed (${loginRes.status}): ${loginText}`);
|
|
84
|
+
process.exitCode = 1;
|
|
85
|
+
return;
|
|
86
|
+
}
|
|
87
|
+
const loginJson = JSON.parse(loginText);
|
|
88
|
+
if (typeof loginJson.token !== "string") {
|
|
89
|
+
console.error("Missing token in response.");
|
|
90
|
+
process.exitCode = 1;
|
|
91
|
+
return;
|
|
92
|
+
}
|
|
93
|
+
token = loginJson.token;
|
|
94
|
+
} else {
|
|
95
|
+
const name = (await rl.question("Your name: ")).trim() || "User";
|
|
96
|
+
const password = (await rl.question("Choose a password (min 8 chars): ")).trim();
|
|
97
|
+
if (password.length < 8) {
|
|
98
|
+
rl.close();
|
|
99
|
+
console.error("Password must be at least 8 characters.");
|
|
100
|
+
process.exitCode = 1;
|
|
101
|
+
return;
|
|
102
|
+
}
|
|
103
|
+
rl.close();
|
|
104
|
+
const regRes = await fetch(`${serverUrl}/api/auth/register`, {
|
|
105
|
+
method: "POST",
|
|
106
|
+
headers: { "content-type": "application/json" },
|
|
107
|
+
body: JSON.stringify({ email, name, password })
|
|
108
|
+
});
|
|
109
|
+
const regText = await regRes.text();
|
|
110
|
+
if (!regRes.ok) {
|
|
111
|
+
console.error(`Sign up failed (${regRes.status}): ${regText}`);
|
|
112
|
+
process.exitCode = 1;
|
|
113
|
+
return;
|
|
114
|
+
}
|
|
115
|
+
const regJson = JSON.parse(regText);
|
|
116
|
+
if (typeof regJson.token !== "string") {
|
|
117
|
+
console.error("Missing token in response.");
|
|
118
|
+
process.exitCode = 1;
|
|
119
|
+
return;
|
|
120
|
+
}
|
|
121
|
+
token = regJson.token;
|
|
122
|
+
}
|
|
123
|
+
await saveCredentials({ serverUrl, token });
|
|
124
|
+
console.log(`Signed in. Credentials saved to ${credentialsPath()}`);
|
|
125
|
+
}
|
|
126
|
+
async function cmdLogout() {
|
|
127
|
+
try {
|
|
128
|
+
await unlink(credentialsPath());
|
|
129
|
+
console.log("Logged out.");
|
|
130
|
+
} catch {
|
|
131
|
+
console.log("No saved session.");
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
function printAgentPlayIntegrationGuide() {
|
|
135
|
+
console.log("");
|
|
136
|
+
console.log("How your agent appears on the play world");
|
|
137
|
+
console.log("\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500");
|
|
138
|
+
console.log(
|
|
139
|
+
" \u2022 One account API key: run `agent-play create-key` (after login) if you do not have one yet."
|
|
140
|
+
);
|
|
141
|
+
console.log(
|
|
142
|
+
" \u2022 LangChain: use langchainRegistration(agent) and pass agent.toolNames to addPlayer."
|
|
143
|
+
);
|
|
144
|
+
console.log(
|
|
145
|
+
' \u2022 The tool contract requires a tool named "chat_tool" in that list (add it if missing).'
|
|
146
|
+
);
|
|
147
|
+
console.log(
|
|
148
|
+
" \u2022 Structures on the map are derived from those tool names \u2014 keep them aligned with your real tools."
|
|
149
|
+
);
|
|
150
|
+
console.log(
|
|
151
|
+
" \u2022 RemotePlayWorld({ apiKey: <account key> }) and addPlayer({ ..., agentId: <id below> })."
|
|
152
|
+
);
|
|
153
|
+
console.log("");
|
|
154
|
+
}
|
|
155
|
+
async function cmdCreateKey() {
|
|
156
|
+
const cred = await loadCredentials();
|
|
157
|
+
if (cred === null) {
|
|
158
|
+
console.error("Run `agent-play login` first.");
|
|
159
|
+
process.exitCode = 1;
|
|
160
|
+
return;
|
|
161
|
+
}
|
|
162
|
+
const res = await fetch(`${cred.serverUrl}/api/agents/api-key`, {
|
|
163
|
+
method: "POST",
|
|
164
|
+
headers: {
|
|
165
|
+
authorization: `Bearer ${cred.token}`
|
|
166
|
+
}
|
|
167
|
+
});
|
|
168
|
+
const text = await res.text();
|
|
169
|
+
if (!res.ok) {
|
|
170
|
+
let msg = text;
|
|
171
|
+
try {
|
|
172
|
+
const err = JSON.parse(text);
|
|
173
|
+
if (typeof err.error === "string") msg = err.error;
|
|
174
|
+
} catch {
|
|
175
|
+
}
|
|
176
|
+
console.error(`create-key failed (${res.status}): ${msg}`);
|
|
177
|
+
process.exitCode = 1;
|
|
178
|
+
return;
|
|
179
|
+
}
|
|
180
|
+
const json = JSON.parse(text);
|
|
181
|
+
if (typeof json.plainApiKey !== "string") {
|
|
182
|
+
console.error("Invalid response from server.");
|
|
183
|
+
process.exitCode = 1;
|
|
184
|
+
return;
|
|
185
|
+
}
|
|
186
|
+
console.log("API key (store securely; shown once):");
|
|
187
|
+
console.log(json.plainApiKey);
|
|
188
|
+
console.log("");
|
|
189
|
+
}
|
|
190
|
+
async function cmdViewKeys() {
|
|
191
|
+
const cred = await loadCredentials();
|
|
192
|
+
if (cred === null) {
|
|
193
|
+
console.error("Run `agent-play login` first.");
|
|
194
|
+
process.exitCode = 1;
|
|
195
|
+
return;
|
|
196
|
+
}
|
|
197
|
+
const res = await fetch(`${cred.serverUrl}/api/agents/api-key`, {
|
|
198
|
+
headers: { authorization: `Bearer ${cred.token}` }
|
|
199
|
+
});
|
|
200
|
+
const text = await res.text();
|
|
201
|
+
if (!res.ok) {
|
|
202
|
+
console.error(`view-keys failed (${res.status}): ${text}`);
|
|
203
|
+
process.exitCode = 1;
|
|
204
|
+
return;
|
|
205
|
+
}
|
|
206
|
+
const json = JSON.parse(text);
|
|
207
|
+
if (json.hasKey === true) {
|
|
208
|
+
const when = typeof json.createdAt === "string" ? json.createdAt : "unknown time";
|
|
209
|
+
console.log(`Account API key: active (created ${when}).`);
|
|
210
|
+
console.log(
|
|
211
|
+
"The secret value cannot be shown again. Use the key you saved when you ran `agent-play create-key`."
|
|
212
|
+
);
|
|
213
|
+
} else {
|
|
214
|
+
console.log("No API key for this account.");
|
|
215
|
+
console.log("Run `agent-play create-key` to generate one (shown once).");
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
async function cmdCreate() {
|
|
219
|
+
const cred = await loadCredentials();
|
|
220
|
+
if (cred === null) {
|
|
221
|
+
console.error("Run `agent-play login` first.");
|
|
222
|
+
process.exitCode = 1;
|
|
223
|
+
return;
|
|
224
|
+
}
|
|
225
|
+
const rl = createInterface({ input, output });
|
|
226
|
+
const name = (await rl.question("Agent name: ")).trim() || "agent";
|
|
227
|
+
rl.close();
|
|
228
|
+
const res = await fetch(`${cred.serverUrl}/api/agents`, {
|
|
229
|
+
method: "POST",
|
|
230
|
+
headers: {
|
|
231
|
+
"content-type": "application/json",
|
|
232
|
+
authorization: `Bearer ${cred.token}`
|
|
233
|
+
},
|
|
234
|
+
body: JSON.stringify({ name })
|
|
235
|
+
});
|
|
236
|
+
const text = await res.text();
|
|
237
|
+
if (!res.ok) {
|
|
238
|
+
let msg = text;
|
|
239
|
+
try {
|
|
240
|
+
const err = JSON.parse(text);
|
|
241
|
+
if (typeof err.error === "string") msg = err.error;
|
|
242
|
+
} catch {
|
|
243
|
+
}
|
|
244
|
+
console.error(`Create failed (${res.status}): ${msg}`);
|
|
245
|
+
process.exitCode = 1;
|
|
246
|
+
return;
|
|
247
|
+
}
|
|
248
|
+
const json = JSON.parse(text);
|
|
249
|
+
if (typeof json.agentId !== "string") {
|
|
250
|
+
console.error("Invalid response from server.");
|
|
251
|
+
process.exitCode = 1;
|
|
252
|
+
return;
|
|
253
|
+
}
|
|
254
|
+
printAgentPlayIntegrationGuide();
|
|
255
|
+
console.log(`Created agent id: ${json.agentId}`);
|
|
256
|
+
console.log("");
|
|
257
|
+
}
|
|
258
|
+
async function cmdDelete() {
|
|
259
|
+
const cred = await loadCredentials();
|
|
260
|
+
if (cred === null) {
|
|
261
|
+
console.error("Run `agent-play login` first.");
|
|
262
|
+
process.exitCode = 1;
|
|
263
|
+
return;
|
|
264
|
+
}
|
|
265
|
+
const listRes = await fetch(`${cred.serverUrl}/api/agents`, {
|
|
266
|
+
headers: { authorization: `Bearer ${cred.token}` }
|
|
267
|
+
});
|
|
268
|
+
const listText = await listRes.text();
|
|
269
|
+
if (!listRes.ok) {
|
|
270
|
+
console.error(`List failed (${listRes.status}): ${listText}`);
|
|
271
|
+
process.exitCode = 1;
|
|
272
|
+
return;
|
|
273
|
+
}
|
|
274
|
+
const listJson = JSON.parse(listText);
|
|
275
|
+
const agentsRaw = listJson.agents;
|
|
276
|
+
if (!Array.isArray(agentsRaw)) {
|
|
277
|
+
console.error("Invalid list response.");
|
|
278
|
+
process.exitCode = 1;
|
|
279
|
+
return;
|
|
280
|
+
}
|
|
281
|
+
const agents = [];
|
|
282
|
+
for (const a of agentsRaw) {
|
|
283
|
+
if (typeof a !== "object" || a === null) continue;
|
|
284
|
+
const o = a;
|
|
285
|
+
if (typeof o.agentId === "string" && typeof o.name === "string") {
|
|
286
|
+
agents.push({ agentId: o.agentId, name: o.name });
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
if (agents.length === 0) {
|
|
290
|
+
console.log("No agents.");
|
|
291
|
+
return;
|
|
292
|
+
}
|
|
293
|
+
agents.forEach((a, i) => {
|
|
294
|
+
console.log(`${i + 1}. ${a.agentId} (${a.name})`);
|
|
295
|
+
});
|
|
296
|
+
const rl = createInterface({ input, output });
|
|
297
|
+
const pick = (await rl.question("Agent id to delete (empty = cancel): ")).trim();
|
|
298
|
+
rl.close();
|
|
299
|
+
if (pick.length === 0) {
|
|
300
|
+
console.log("Cancelled.");
|
|
301
|
+
return;
|
|
302
|
+
}
|
|
303
|
+
const delRes = await fetch(
|
|
304
|
+
`${cred.serverUrl}/api/agents?id=${encodeURIComponent(pick)}`,
|
|
305
|
+
{
|
|
306
|
+
method: "DELETE",
|
|
307
|
+
headers: { authorization: `Bearer ${cred.token}` }
|
|
308
|
+
}
|
|
309
|
+
);
|
|
310
|
+
const delText = await delRes.text();
|
|
311
|
+
if (!delRes.ok) {
|
|
312
|
+
console.error(`Delete failed (${delRes.status}): ${delText}`);
|
|
313
|
+
process.exitCode = 1;
|
|
314
|
+
return;
|
|
315
|
+
}
|
|
316
|
+
const delJson = JSON.parse(delText);
|
|
317
|
+
console.log(delJson.ok === true ? "Deleted." : "Not found.");
|
|
318
|
+
}
|
|
319
|
+
async function main() {
|
|
320
|
+
const cmd = process.argv[2];
|
|
321
|
+
if (cmd === "login") {
|
|
322
|
+
await cmdLogin();
|
|
323
|
+
return;
|
|
324
|
+
}
|
|
325
|
+
if (cmd === "logout") {
|
|
326
|
+
await cmdLogout();
|
|
327
|
+
return;
|
|
328
|
+
}
|
|
329
|
+
if (cmd === "create") {
|
|
330
|
+
await cmdCreate();
|
|
331
|
+
return;
|
|
332
|
+
}
|
|
333
|
+
if (cmd === "create-key" || cmd === "generate-key") {
|
|
334
|
+
await cmdCreateKey();
|
|
335
|
+
return;
|
|
336
|
+
}
|
|
337
|
+
if (cmd === "view-keys") {
|
|
338
|
+
await cmdViewKeys();
|
|
339
|
+
return;
|
|
340
|
+
}
|
|
341
|
+
if (cmd === "delete" || cmd === "remove") {
|
|
342
|
+
await cmdDelete();
|
|
343
|
+
return;
|
|
344
|
+
}
|
|
345
|
+
console.error(
|
|
346
|
+
"Usage: agent-play login | logout | create-key | view-keys | create | delete"
|
|
347
|
+
);
|
|
348
|
+
process.exitCode = 1;
|
|
349
|
+
}
|
|
350
|
+
void main().catch((e) => {
|
|
351
|
+
console.error(e);
|
|
352
|
+
process.exitCode = 1;
|
|
353
|
+
});
|
package/package.json
ADDED
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@agent-play/cli",
|
|
3
|
+
"version": "0.0.1",
|
|
4
|
+
"description": "Command-line tool for Agent Play: login, API keys, and agent registration against the web UI.",
|
|
5
|
+
"private": false,
|
|
6
|
+
"type": "module",
|
|
7
|
+
"bin": {
|
|
8
|
+
"agent-play": "./dist/cli.js"
|
|
9
|
+
},
|
|
10
|
+
"files": [
|
|
11
|
+
"dist"
|
|
12
|
+
],
|
|
13
|
+
"repository": {
|
|
14
|
+
"type": "git",
|
|
15
|
+
"url": "https://github.com/wilforlan/agent-play.git",
|
|
16
|
+
"directory": "packages/cli"
|
|
17
|
+
},
|
|
18
|
+
"publishConfig": {
|
|
19
|
+
"access": "public"
|
|
20
|
+
},
|
|
21
|
+
"scripts": {
|
|
22
|
+
"build": "tsup src/cli.ts --format esm --platform node --target node20 --out-dir dist --clean"
|
|
23
|
+
},
|
|
24
|
+
"dependencies": {},
|
|
25
|
+
"devDependencies": {
|
|
26
|
+
"@types/node": "^22.10.0",
|
|
27
|
+
"tsup": "^8.5.1",
|
|
28
|
+
"typescript": "~5.9.3"
|
|
29
|
+
}
|
|
30
|
+
}
|