@rine-network/cli 0.7.4 → 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.
Files changed (3) hide show
  1. package/README.md +5 -7
  2. package/dist/main.js +87 -95
  3. 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 an organisation
29
- rine register --email you@example.com --name "My Org" --slug my-org
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 register --email you@example.com --name "My Org" --slug my-org
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 > `~/.config/rine`.
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, encryptionPublicKeyToJWK, fetchAgents, fetchOAuthToken, fetchRecipientEncryptionKey, formatError, fromBase64Url, generateAgentKeys, getAgentPublicKeys, getCredentialEntry, getOrCreateSenderKey, getOrRefreshToken, ingestSenderKeyDistribution, isBareAgentName, loadAgentKeys, loadCredentials, loadTokenCache, resolveAgent, resolveApiUrl, resolveConfigDir, resolveHandleViaWebFinger, resolveToUuid, saveAgentKeys, saveCredentials, saveTokenCache, signingPublicKeyToJWK, solveTimeLockWithProgress, toBase64Url, validateEncryptionKey, validateSigningKey } from "@rine-network/core";
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 agentKeys = generateAgentKeys();
290
- const body = {
291
+ const data = await performAgentCreation(client, configDir, gOpts.profile ?? "default", {
291
292
  name,
292
- signing_public_key: signingPublicKeyToJWK(agentKeys.signing.publicKey),
293
- encryption_public_key: encryptionPublicKeyToJWK(agentKeys.encryption.publicKey)
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/register.ts
1277
- function registerRegister(program) {
1278
- 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) => {
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
- const challengeRes = await fetch(`${apiUrl}/auth/register`, {
1283
- method: "POST",
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 (!challengeRes.ok) {
1301
- printError(`Registration failed: ${(await challengeRes.json().catch(() => ({}))).detail ?? challengeRes.statusText}`);
1302
- process.exitCode = 1;
1303
- return;
1304
- }
1305
- const challenge = await challengeRes.json();
1306
- if (challenge.algorithm !== "rsa-timelock-v1") {
1307
- printError(`Unsupported algorithm: ${challenge.algorithm}. Please upgrade rine-cli.`);
1308
- process.exitCode = 1;
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 nonce = await solveTimeLockWithProgress(challenge.prefix, challenge.modulus, challenge.difficulty, (pct) => {
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 solveRes = await fetch(`${apiUrl}/auth/register/solve`, {
1318
- method: "POST",
1319
- headers: { "Content-Type": "application/json" },
1320
- body: JSON.stringify({
1321
- challenge_id: challenge.challenge_id,
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 (solveRes.status === 409) {
1329
- printError("Conflict: email or slug already registered");
1330
- process.exitCode = 1;
1331
- return;
1332
- }
1333
- if (solveRes.status === 410) {
1334
- printError("Challenge expired — please register again");
1335
- process.exitCode = 1;
1336
- return;
1337
- }
1338
- if (!solveRes.ok) {
1339
- printError(`Solve failed: ${(await solveRes.json().catch(() => ({}))).detail ?? solveRes.statusText}`);
1340
- process.exitCode = 1;
1341
- return;
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
- "name": "@rine-network/cli",
3
- "version": "0.7.4",
4
- "description": "CLI client for rine.network 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.2.0",
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
- ]
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
  }