@rine-network/cli 0.7.3 → 0.8.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/README.md +5 -7
- package/dist/main.js +87 -95
- package/package.json +59 -59
package/README.md
CHANGED
|
@@ -25,11 +25,8 @@ curl -fsSL https://rine.network/install.sh | sh
|
|
|
25
25
|
## Quick start
|
|
26
26
|
|
|
27
27
|
```sh
|
|
28
|
-
# Register
|
|
29
|
-
rine
|
|
30
|
-
|
|
31
|
-
# Create an agent
|
|
32
|
-
rine agent create --name "my-agent"
|
|
28
|
+
# Register and create your first agent in one step
|
|
29
|
+
rine onboard --email you@example.com --name "My Org" --slug my-org --agent my-agent
|
|
33
30
|
|
|
34
31
|
# Send a message (type defaults to rine.v1.dm)
|
|
35
32
|
rine send --to other-agent@other-org.rine.network --payload '{"task": "hello"}'
|
|
@@ -41,13 +38,14 @@ rine inbox
|
|
|
41
38
|
Or with npx (no install needed):
|
|
42
39
|
|
|
43
40
|
```sh
|
|
44
|
-
npx @rine-network/cli
|
|
41
|
+
npx @rine-network/cli onboard --email you@example.com --name "My Org" --slug my-org --agent my-agent
|
|
45
42
|
```
|
|
46
43
|
|
|
47
44
|
## Commands
|
|
48
45
|
|
|
49
46
|
| Command | Description |
|
|
50
47
|
|---------|-------------|
|
|
48
|
+
| `rine onboard` | Register + create first agent in one step (PoW + key generation) |
|
|
51
49
|
| `rine register` | Register a new organisation (with RSA time-lock PoW) |
|
|
52
50
|
| `rine login / logout / status` | Authentication |
|
|
53
51
|
| `rine whoami` | Show current identity, org, agent, and key status |
|
|
@@ -67,7 +65,7 @@ Use `rine --help` or `rine <command> --help` for full usage.
|
|
|
67
65
|
|
|
68
66
|
## Configuration
|
|
69
67
|
|
|
70
|
-
The CLI resolves its config directory in order: `$RINE_CONFIG_DIR` > `.rine/` in the current directory
|
|
68
|
+
The CLI resolves its config directory in order: `$RINE_CONFIG_DIR` > `~/.config/rine` > `.rine/` in the current directory.
|
|
71
69
|
|
|
72
70
|
| Variable | Description |
|
|
73
71
|
|----------|-------------|
|
package/dist/main.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { createRequire } from "node:module";
|
|
2
2
|
import { Command } from "commander";
|
|
3
|
-
import { HttpClient, RineApiError, UUID_RE, agentKeysExist, cacheToken, decryptGroupMessage, decryptMessage, encryptGroupMessage, encryptMessage,
|
|
3
|
+
import { HttpClient, RineApiError, UUID_RE, agentKeysExist, cacheToken, decryptGroupMessage, decryptMessage, encryptGroupMessage, encryptMessage, fetchAgents, fetchOAuthToken, fetchRecipientEncryptionKey, formatError, fromBase64Url, generateAgentKeys, getAgentPublicKeys, getCredentialEntry, getOrCreateSenderKey, getOrRefreshToken, ingestSenderKeyDistribution, isBareAgentName, loadAgentKeys, loadCredentials, loadTokenCache, performAgentCreation, performRegistration, resolveAgent, resolveApiUrl, resolveConfigDir, resolveHandleViaWebFinger, resolveToUuid, saveAgentKeys, saveCredentials, saveTokenCache, toBase64Url, validateEncryptionKey, validateSigningKey, validateSlug } from "@rine-network/core";
|
|
4
4
|
import readline from "node:readline";
|
|
5
5
|
import * as fs from "node:fs";
|
|
6
6
|
import { join } from "node:path";
|
|
@@ -216,6 +216,21 @@ function registerAgentProfile(program) {
|
|
|
216
216
|
}));
|
|
217
217
|
}
|
|
218
218
|
//#endregion
|
|
219
|
+
//#region src/agent-format.ts
|
|
220
|
+
function toAgentRow(a, opts = {}) {
|
|
221
|
+
const row = {
|
|
222
|
+
ID: a.id,
|
|
223
|
+
Name: a.name,
|
|
224
|
+
Handle: a.handle,
|
|
225
|
+
"Human Oversight": String(a.human_oversight),
|
|
226
|
+
Created: a.created_at
|
|
227
|
+
};
|
|
228
|
+
if (opts.includeVerificationWords && a.verification_words) row["Verification Words"] = a.verification_words;
|
|
229
|
+
if (opts.includeCard) row.Card = a.unlisted ? "unlisted" : "public";
|
|
230
|
+
if (opts.includeRevoked) row.Revoked = a.revoked_at ? String(a.revoked_at) : "—";
|
|
231
|
+
return row;
|
|
232
|
+
}
|
|
233
|
+
//#endregion
|
|
219
234
|
//#region src/prompt.ts
|
|
220
235
|
/** Prompt for a plain-text value on stdout/stdin. */
|
|
221
236
|
async function promptText(question) {
|
|
@@ -261,19 +276,6 @@ async function promptConfirm(question) {
|
|
|
261
276
|
}
|
|
262
277
|
//#endregion
|
|
263
278
|
//#region src/commands/agent.ts
|
|
264
|
-
function toAgentRow(a, opts = {}) {
|
|
265
|
-
const row = {
|
|
266
|
-
ID: a.id,
|
|
267
|
-
Name: a.name,
|
|
268
|
-
Handle: a.handle,
|
|
269
|
-
"Human Oversight": String(a.human_oversight),
|
|
270
|
-
Created: a.created_at
|
|
271
|
-
};
|
|
272
|
-
if (opts.includeVerificationWords && a.verification_words) row["Verification Words"] = a.verification_words;
|
|
273
|
-
if (opts.includeCard) row.Card = a.unlisted ? "unlisted" : "public";
|
|
274
|
-
if (opts.includeRevoked) row.Revoked = a.revoked_at ? String(a.revoked_at) : "—";
|
|
275
|
-
return row;
|
|
276
|
-
}
|
|
277
279
|
function registerAgent(program) {
|
|
278
280
|
const agent = program.command("agent").description("Agent management");
|
|
279
281
|
agent.command("create").description("Create a new agent").option("--name <name>", "Agent name").option("--[no-]human-oversight", "Require human oversight (default: true)").option("--unlisted", "Mark agent as unlisted (not in public directory)").action(withClient(program, async ({ client, gOpts, configDir }, opts) => {
|
|
@@ -286,24 +288,11 @@ function registerAgent(program) {
|
|
|
286
288
|
return;
|
|
287
289
|
}
|
|
288
290
|
}
|
|
289
|
-
const
|
|
290
|
-
const body = {
|
|
291
|
+
const data = await performAgentCreation(client, configDir, gOpts.profile ?? "default", {
|
|
291
292
|
name,
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
};
|
|
295
|
-
if (opts.humanOversight !== void 0) body.human_oversight = opts.humanOversight;
|
|
296
|
-
if (opts.unlisted) body.unlisted = true;
|
|
297
|
-
const data = await client.post("/agents", body);
|
|
298
|
-
saveAgentKeys(configDir, data.id, agentKeys);
|
|
299
|
-
if (data.poll_url) {
|
|
300
|
-
const profile = gOpts.profile ?? "default";
|
|
301
|
-
const creds = loadCredentials(configDir);
|
|
302
|
-
if (creds[profile]) {
|
|
303
|
-
creds[profile].poll_url = data.poll_url;
|
|
304
|
-
saveCredentials(configDir, creds);
|
|
305
|
-
}
|
|
306
|
-
}
|
|
293
|
+
humanOversight: opts.humanOversight,
|
|
294
|
+
unlisted: opts.unlisted
|
|
295
|
+
});
|
|
307
296
|
if (gOpts.json) printJson(data);
|
|
308
297
|
else {
|
|
309
298
|
printTable([toAgentRow(data, {
|
|
@@ -1273,85 +1262,87 @@ function registerOrg(program) {
|
|
|
1273
1262
|
}));
|
|
1274
1263
|
}
|
|
1275
1264
|
//#endregion
|
|
1276
|
-
//#region src/commands/
|
|
1277
|
-
function
|
|
1278
|
-
program.command("
|
|
1265
|
+
//#region src/commands/onboard.ts
|
|
1266
|
+
function registerOnboard(program) {
|
|
1267
|
+
program.command("onboard").description("Register a new organization and create your first agent").requiredOption("--email <email>", "Email address").requiredOption("--name <name>", "Organization name").requiredOption("--slug <slug>", "Organization slug").requiredOption("--agent <agent-name>", "Name for the first agent").option("--[no-]human-oversight", "Require human oversight (default: true)").option("--unlisted", "Mark agent as unlisted (not in public directory)").action(withErrorHandler(program, async (gOpts, opts) => {
|
|
1279
1268
|
const profile = gOpts.profile ?? "default";
|
|
1280
1269
|
const configDir = resolveConfigDir();
|
|
1281
1270
|
const apiUrl = resolveApiUrl();
|
|
1282
|
-
|
|
1283
|
-
|
|
1284
|
-
headers: { "Content-Type": "application/json" },
|
|
1285
|
-
body: JSON.stringify({
|
|
1286
|
-
email: opts.email,
|
|
1287
|
-
org_slug: opts.slug
|
|
1288
|
-
})
|
|
1289
|
-
});
|
|
1290
|
-
if (challengeRes.status === 409) {
|
|
1291
|
-
printError("Email or slug already registered");
|
|
1292
|
-
process.exitCode = 1;
|
|
1293
|
-
return;
|
|
1294
|
-
}
|
|
1295
|
-
if (challengeRes.status === 429) {
|
|
1296
|
-
printError("Rate limited — please wait before retrying");
|
|
1271
|
+
if (!validateSlug(opts.slug)) {
|
|
1272
|
+
printError("Invalid slug: must be 2-32 lowercase alphanumeric characters or hyphens, no leading/trailing hyphen");
|
|
1297
1273
|
process.exitCode = 1;
|
|
1298
1274
|
return;
|
|
1299
1275
|
}
|
|
1300
|
-
if (
|
|
1301
|
-
|
|
1302
|
-
|
|
1303
|
-
|
|
1304
|
-
|
|
1305
|
-
|
|
1306
|
-
|
|
1307
|
-
|
|
1308
|
-
|
|
1276
|
+
if (getCredentialEntry(configDir, profile)) {
|
|
1277
|
+
const { client } = await createClient(configDir, apiUrl, gOpts.profile);
|
|
1278
|
+
const org = await client.get("/org");
|
|
1279
|
+
const agents = await client.get("/agents");
|
|
1280
|
+
if (gOpts.json) printJson({
|
|
1281
|
+
status: "already_registered",
|
|
1282
|
+
org,
|
|
1283
|
+
agents
|
|
1284
|
+
});
|
|
1285
|
+
else {
|
|
1286
|
+
console.log(`Already registered as '${org.name}' (${org.slug})`);
|
|
1287
|
+
if (agents.items.length > 0) printTable(agents.items.map((a) => ({
|
|
1288
|
+
ID: a.id,
|
|
1289
|
+
Name: a.name,
|
|
1290
|
+
Handle: a.handle
|
|
1291
|
+
})));
|
|
1292
|
+
}
|
|
1309
1293
|
return;
|
|
1310
1294
|
}
|
|
1311
1295
|
process.stderr.write("By registering, you accept the rine terms of service and privacy policy:\n https://rine.network/terms.html\n https://rine.network/privacy.html\n");
|
|
1312
1296
|
const progressStream = gOpts.json ? process.stderr : process.stdout;
|
|
1313
|
-
const
|
|
1297
|
+
const regResult = await performRegistration(apiUrl, configDir, profile, {
|
|
1298
|
+
email: opts.email,
|
|
1299
|
+
slug: opts.slug,
|
|
1300
|
+
name: opts.name
|
|
1301
|
+
}, (pct) => {
|
|
1314
1302
|
progressStream.write(`\rSolving PoW challenge: ${pct}%`);
|
|
1315
1303
|
});
|
|
1316
1304
|
progressStream.write("\rSolving PoW challenge: 100%\n");
|
|
1317
|
-
const
|
|
1318
|
-
|
|
1319
|
-
|
|
1320
|
-
|
|
1321
|
-
|
|
1322
|
-
nonce,
|
|
1323
|
-
org_name: opts.name,
|
|
1324
|
-
org_slug: opts.slug,
|
|
1325
|
-
consent: true
|
|
1326
|
-
})
|
|
1305
|
+
const { client } = await createClient(configDir, apiUrl, gOpts.profile);
|
|
1306
|
+
const agent = await performAgentCreation(client, configDir, profile, {
|
|
1307
|
+
name: opts.agent,
|
|
1308
|
+
humanOversight: opts.humanOversight,
|
|
1309
|
+
unlisted: opts.unlisted
|
|
1327
1310
|
});
|
|
1328
|
-
if (
|
|
1329
|
-
|
|
1330
|
-
|
|
1331
|
-
|
|
1332
|
-
|
|
1333
|
-
|
|
1334
|
-
|
|
1335
|
-
|
|
1336
|
-
|
|
1337
|
-
|
|
1338
|
-
|
|
1339
|
-
|
|
1340
|
-
|
|
1341
|
-
|
|
1342
|
-
|
|
1343
|
-
const data = await solveRes.json();
|
|
1344
|
-
const creds = loadCredentials(configDir);
|
|
1345
|
-
creds[profile] = {
|
|
1346
|
-
client_id: data.client_id,
|
|
1347
|
-
client_secret: data.client_secret
|
|
1348
|
-
};
|
|
1349
|
-
saveCredentials(configDir, creds);
|
|
1350
|
-
try {
|
|
1351
|
-
cacheToken(configDir, profile, await fetchOAuthToken(apiUrl, data.client_id, data.client_secret));
|
|
1352
|
-
} catch {
|
|
1353
|
-
process.stderr.write("Warning: initial token cache failed\n");
|
|
1311
|
+
if (gOpts.json) printJson({
|
|
1312
|
+
org_id: regResult.org_id,
|
|
1313
|
+
client_id: regResult.client_id,
|
|
1314
|
+
agent,
|
|
1315
|
+
poll_url: agent.poll_url,
|
|
1316
|
+
config_dir: configDir
|
|
1317
|
+
});
|
|
1318
|
+
else {
|
|
1319
|
+
printTable([toAgentRow(agent, {
|
|
1320
|
+
includeCard: true,
|
|
1321
|
+
includeVerificationWords: true
|
|
1322
|
+
})]);
|
|
1323
|
+
console.log(`\nAgent '${agent.name}' created — reachable at ${agent.handle}`);
|
|
1324
|
+
if (agent.verification_words) console.log(`Verification words: ${agent.verification_words}`);
|
|
1325
|
+
if (agent.poll_url) console.log(`Poll URL: ${agent.poll_url}`);
|
|
1354
1326
|
}
|
|
1327
|
+
}));
|
|
1328
|
+
}
|
|
1329
|
+
//#endregion
|
|
1330
|
+
//#region src/commands/register.ts
|
|
1331
|
+
function registerRegister(program) {
|
|
1332
|
+
program.command("register").description("Register a new organization (two-step PoW flow)").requiredOption("--email <email>", "Email address").requiredOption("--name <name>", "Organization name").requiredOption("--slug <slug>", "Organization slug").action(withErrorHandler(program, async (gOpts, opts) => {
|
|
1333
|
+
const profile = gOpts.profile ?? "default";
|
|
1334
|
+
const configDir = resolveConfigDir();
|
|
1335
|
+
const apiUrl = resolveApiUrl();
|
|
1336
|
+
process.stderr.write("By registering, you accept the rine terms of service and privacy policy:\n https://rine.network/terms.html\n https://rine.network/privacy.html\n");
|
|
1337
|
+
const progressStream = gOpts.json ? process.stderr : process.stdout;
|
|
1338
|
+
const data = await performRegistration(apiUrl, configDir, profile, {
|
|
1339
|
+
email: opts.email,
|
|
1340
|
+
slug: opts.slug,
|
|
1341
|
+
name: opts.name
|
|
1342
|
+
}, (pct) => {
|
|
1343
|
+
progressStream.write(`\rSolving PoW challenge: ${pct}%`);
|
|
1344
|
+
});
|
|
1345
|
+
progressStream.write("\rSolving PoW challenge: 100%\n");
|
|
1355
1346
|
if (gOpts.json) printJson(data);
|
|
1356
1347
|
else {
|
|
1357
1348
|
printSuccess(`Registered '${opts.name}' (${opts.slug})`);
|
|
@@ -1545,6 +1536,7 @@ const { version } = createRequire(import.meta.url)("../package.json");
|
|
|
1545
1536
|
const program = new Command("rine").version(version).description("rine.network CLI — messaging infrastructure for AI agents").option("--profile <name>", "credential profile to use", "default").option("--json", "output as JSON").option("--table", "output as table").option("--as <agent>", "act as a specific agent (UUID, handle, or bare name e.g. kofi)");
|
|
1546
1537
|
registerAuth(program);
|
|
1547
1538
|
registerRegister(program);
|
|
1539
|
+
registerOnboard(program);
|
|
1548
1540
|
registerOrg(program);
|
|
1549
1541
|
registerAgent(program);
|
|
1550
1542
|
registerAgentProfile(program);
|
package/package.json
CHANGED
|
@@ -1,61 +1,61 @@
|
|
|
1
1
|
{
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
2
|
+
"name": "@rine-network/cli",
|
|
3
|
+
"version": "0.8.0",
|
|
4
|
+
"description": "CLI client for rine.network \u2014 EU-first messaging infrastructure for AI agents",
|
|
5
|
+
"author": "mmmbs <mmmbs@proton.me>",
|
|
6
|
+
"license": "EUPL-1.2",
|
|
7
|
+
"type": "module",
|
|
8
|
+
"engines": {
|
|
9
|
+
"node": ">=22"
|
|
10
|
+
},
|
|
11
|
+
"bin": {
|
|
12
|
+
"rine": "bin/rine.js"
|
|
13
|
+
},
|
|
14
|
+
"files": [
|
|
15
|
+
"bin/",
|
|
16
|
+
"dist/"
|
|
17
|
+
],
|
|
18
|
+
"exports": {
|
|
19
|
+
".": "./dist/main.js"
|
|
20
|
+
},
|
|
21
|
+
"scripts": {
|
|
22
|
+
"build": "tsdown src/main.ts --format esm --outDir dist --clean",
|
|
23
|
+
"prepublishOnly": "npm run build",
|
|
24
|
+
"typecheck": "tsc --noEmit",
|
|
25
|
+
"lint": "biome check .",
|
|
26
|
+
"test": "vitest run",
|
|
27
|
+
"test:coverage": "vitest --coverage",
|
|
28
|
+
"dev": "tsx src/main.ts"
|
|
29
|
+
},
|
|
30
|
+
"dependencies": {
|
|
31
|
+
"@rine-network/core": "^0.3.1",
|
|
32
|
+
"commander": "^12.0.0",
|
|
33
|
+
"eventsource-client": "^1.1.0"
|
|
34
|
+
},
|
|
35
|
+
"devDependencies": {
|
|
36
|
+
"@biomejs/biome": "^1.9.0",
|
|
37
|
+
"@types/node": "^22.0.0",
|
|
38
|
+
"tsdown": "^0.12.0",
|
|
39
|
+
"tsx": "^4.19.0",
|
|
40
|
+
"typescript": "^5.8.0",
|
|
41
|
+
"vitest": "^3.0.0"
|
|
42
|
+
},
|
|
43
|
+
"homepage": "https://rine.network",
|
|
44
|
+
"repository": {
|
|
45
|
+
"type": "git",
|
|
46
|
+
"url": "https://codeberg.org/rine/rine-cli"
|
|
47
|
+
},
|
|
48
|
+
"bugs": {
|
|
49
|
+
"url": "https://codeberg.org/rine/rine-cli/issues"
|
|
50
|
+
},
|
|
51
|
+
"publishConfig": {
|
|
52
|
+
"access": "public"
|
|
53
|
+
},
|
|
54
|
+
"keywords": [
|
|
55
|
+
"messaging",
|
|
56
|
+
"ai-agents",
|
|
57
|
+
"cli",
|
|
58
|
+
"a2a",
|
|
59
|
+
"eu"
|
|
60
|
+
]
|
|
61
61
|
}
|