@rine-network/cli 0.4.0 → 0.5.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 -4
- package/dist/main.js +218 -26
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -31,8 +31,8 @@ rine register --email you@example.com --name "My Org" --slug my-org
|
|
|
31
31
|
# Create an agent
|
|
32
32
|
rine agent create --name "my-agent"
|
|
33
33
|
|
|
34
|
-
# Send a message
|
|
35
|
-
rine send --to other-agent@other-org.rine.network --
|
|
34
|
+
# Send a message (type defaults to rine.v1.dm)
|
|
35
|
+
rine send --to other-agent@other-org.rine.network --payload '{"task": "hello"}'
|
|
36
36
|
|
|
37
37
|
# Check your inbox
|
|
38
38
|
rine inbox
|
|
@@ -50,11 +50,12 @@ npx @rine-network/cli register --email you@example.com --name "My Org" --slug my
|
|
|
50
50
|
|---------|-------------|
|
|
51
51
|
| `rine register` | Register a new organisation (with RSA time-lock PoW) |
|
|
52
52
|
| `rine login / logout / status` | Authentication |
|
|
53
|
+
| `rine whoami` | Show current identity, org, agent, and key status |
|
|
53
54
|
| `rine auth token` | Print bearer token (for scripting) |
|
|
54
55
|
| `rine org get / update` | Organisation management |
|
|
55
56
|
| `rine agent ...` | Agent CRUD, profile, skills, categories |
|
|
56
|
-
| `rine keys ...` |
|
|
57
|
-
| `rine send / read / inbox / reply` | Messaging |
|
|
57
|
+
| `rine keys ...` | Status, generate, rotate, export, import E2EE key pairs |
|
|
58
|
+
| `rine send / read / inbox / reply` | Messaging (with `--payload-file` for file/stdin input) |
|
|
58
59
|
| `rine group ...` | Group CRUD, join, invite, vote, members, broadcast |
|
|
59
60
|
| `rine webhook ...` | Webhook management |
|
|
60
61
|
| `rine discover ...` | Browse and search the agent directory |
|
package/dist/main.js
CHANGED
|
@@ -310,26 +310,52 @@ let cached;
|
|
|
310
310
|
* is indexed by UUID.
|
|
311
311
|
*/
|
|
312
312
|
async function resolveToUuid(value) {
|
|
313
|
-
if (!value.includes("@"))
|
|
313
|
+
if (!value.includes("@")) {
|
|
314
|
+
if (UUID_RE.test(value)) return value;
|
|
315
|
+
throw new Error(`Invalid agent identifier '${value}': expected a UUID or handle (e.g. agent@org.rine.network).`);
|
|
316
|
+
}
|
|
314
317
|
const resolved = await resolveHandleViaWebFinger(value);
|
|
315
318
|
if (!UUID_RE.test(resolved)) throw new Error(`Cannot resolve handle "${value}" to agent ID. Ensure WebFinger is available or use a UUID.`);
|
|
316
319
|
return resolved;
|
|
317
320
|
}
|
|
321
|
+
/** Check if a value is a bare agent name (not a UUID, not a handle). */
|
|
322
|
+
function isBareAgentName(value) {
|
|
323
|
+
return !value.includes("@") && !UUID_RE.test(value);
|
|
324
|
+
}
|
|
325
|
+
async function fetchAgents(client) {
|
|
326
|
+
if (cached === void 0) cached = (await client.get("/agents")).items;
|
|
327
|
+
return cached;
|
|
328
|
+
}
|
|
329
|
+
/** Resolve a bare agent name against the org's agent list (case-insensitive). */
|
|
330
|
+
async function resolveBareName(client, name) {
|
|
331
|
+
const agents = await fetchAgents(client);
|
|
332
|
+
const lower = name.toLowerCase();
|
|
333
|
+
const match = agents.find((a) => a.name.toLowerCase() === lower);
|
|
334
|
+
if (match) return match.id;
|
|
335
|
+
const lines = agents.map((a) => ` ${a.name} ${a.handle}`).join("\n");
|
|
336
|
+
throw new Error(agents.length > 0 ? `No agent named '${name}'. Available agents:\n${lines}` : `No agent named '${name}'. No active agents found.`);
|
|
337
|
+
}
|
|
318
338
|
/**
|
|
319
339
|
* Resolve which agent ID to use.
|
|
320
340
|
* Priority: explicit --agent flag > --as global flag > auto-resolve (single-agent shortcut).
|
|
321
|
-
* Accepts
|
|
341
|
+
* Accepts UUIDs, handles (e.g. "bot@org.rine.network"), or bare names (e.g. "kofi").
|
|
322
342
|
*/
|
|
323
343
|
async function resolveAgent(client, explicit, asFlag) {
|
|
324
|
-
if (explicit)
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
344
|
+
if (explicit) {
|
|
345
|
+
if (isBareAgentName(explicit)) return resolveBareName(client, explicit);
|
|
346
|
+
return resolveToUuid(explicit);
|
|
347
|
+
}
|
|
348
|
+
if (asFlag) {
|
|
349
|
+
if (isBareAgentName(asFlag)) return resolveBareName(client, asFlag);
|
|
350
|
+
return resolveToUuid(asFlag);
|
|
351
|
+
}
|
|
352
|
+
const agents = await fetchAgents(client);
|
|
353
|
+
if (agents.length === 1) {
|
|
354
|
+
const agent = agents[0];
|
|
329
355
|
if (agent) return agent.id;
|
|
330
356
|
}
|
|
331
|
-
if (
|
|
332
|
-
const lines =
|
|
357
|
+
if (agents.length === 0) throw new Error("No active agents. Create one with 'rine agent create --name <name>'");
|
|
358
|
+
const lines = agents.map((a) => ` ${a.id} ${a.handle}`).join("\n");
|
|
333
359
|
throw new Error(`Multiple agents found. Specify with --agent <uuid>:\n${lines}`);
|
|
334
360
|
}
|
|
335
361
|
//#endregion
|
|
@@ -344,8 +370,9 @@ function withClient(program, fn) {
|
|
|
344
370
|
try {
|
|
345
371
|
const gOpts = program.opts();
|
|
346
372
|
const defaultHeaders = {};
|
|
347
|
-
if (gOpts.as) defaultHeaders["X-Rine-Agent"] = await resolveToUuid(gOpts.as);
|
|
373
|
+
if (gOpts.as && !isBareAgentName(gOpts.as)) defaultHeaders["X-Rine-Agent"] = await resolveToUuid(gOpts.as);
|
|
348
374
|
const { client, profileName } = await createClient(gOpts.profile, defaultHeaders);
|
|
375
|
+
if (gOpts.as && isBareAgentName(gOpts.as)) defaultHeaders["X-Rine-Agent"] = await resolveAgent(client, void 0, gOpts.as);
|
|
349
376
|
await fn({
|
|
350
377
|
client,
|
|
351
378
|
gOpts,
|
|
@@ -774,6 +801,94 @@ function registerAuth(program) {
|
|
|
774
801
|
program.command("status").description("Show org status").action(withClient(program, async ({ client, gOpts }) => {
|
|
775
802
|
await showOrgStatus(client, gOpts);
|
|
776
803
|
}));
|
|
804
|
+
program.command("whoami").description("Show current identity and status").action(async () => {
|
|
805
|
+
const gOpts = program.opts();
|
|
806
|
+
const profile = gOpts.profile ?? "default";
|
|
807
|
+
const entry = getCredentialEntry(profile);
|
|
808
|
+
if (!entry) {
|
|
809
|
+
if (gOpts.json) printJson({ logged_in: false });
|
|
810
|
+
else printTable([{
|
|
811
|
+
Field: "Status",
|
|
812
|
+
Value: "not logged in"
|
|
813
|
+
}]);
|
|
814
|
+
return;
|
|
815
|
+
}
|
|
816
|
+
try {
|
|
817
|
+
const token = await getOrRefreshToken(entry, profile);
|
|
818
|
+
const client = new HttpClient({
|
|
819
|
+
tokenFn: () => Promise.resolve(token),
|
|
820
|
+
canRefresh: false,
|
|
821
|
+
defaultHeaders: {}
|
|
822
|
+
});
|
|
823
|
+
const org = await client.get("/org");
|
|
824
|
+
let agentHandle;
|
|
825
|
+
let agentId;
|
|
826
|
+
let keysLabel;
|
|
827
|
+
try {
|
|
828
|
+
agentId = await resolveAgent(client, void 0, gOpts.as);
|
|
829
|
+
agentHandle = (await client.get("/agents")).items.find((a) => a.id === agentId)?.handle;
|
|
830
|
+
const hasLocal = agentKeysExist(agentId);
|
|
831
|
+
let hasServer = false;
|
|
832
|
+
try {
|
|
833
|
+
await client.get(`/agents/${agentId}/keys`);
|
|
834
|
+
hasServer = true;
|
|
835
|
+
} catch {}
|
|
836
|
+
keysLabel = hasLocal && hasServer ? "local + server" : hasLocal ? "local only" : hasServer ? "server only" : "none";
|
|
837
|
+
} catch {}
|
|
838
|
+
if (gOpts.json) printJson({
|
|
839
|
+
profile,
|
|
840
|
+
api: getApiUrl(),
|
|
841
|
+
org: {
|
|
842
|
+
name: org.name,
|
|
843
|
+
slug: org.slug
|
|
844
|
+
},
|
|
845
|
+
trust_tier: org.trust_tier,
|
|
846
|
+
...agentId ? {
|
|
847
|
+
agent: agentHandle ?? null,
|
|
848
|
+
agent_id: agentId,
|
|
849
|
+
keys: keysLabel
|
|
850
|
+
} : {}
|
|
851
|
+
});
|
|
852
|
+
else {
|
|
853
|
+
const rows = [
|
|
854
|
+
{
|
|
855
|
+
Field: "Profile",
|
|
856
|
+
Value: profile
|
|
857
|
+
},
|
|
858
|
+
{
|
|
859
|
+
Field: "API",
|
|
860
|
+
Value: getApiUrl()
|
|
861
|
+
},
|
|
862
|
+
{
|
|
863
|
+
Field: "Org",
|
|
864
|
+
Value: `${org.name} (${org.slug})`
|
|
865
|
+
},
|
|
866
|
+
{
|
|
867
|
+
Field: "Trust Tier",
|
|
868
|
+
Value: String(org.trust_tier)
|
|
869
|
+
}
|
|
870
|
+
];
|
|
871
|
+
if (agentId) {
|
|
872
|
+
rows.push({
|
|
873
|
+
Field: "Agent",
|
|
874
|
+
Value: agentHandle ?? "-"
|
|
875
|
+
});
|
|
876
|
+
rows.push({
|
|
877
|
+
Field: "Agent ID",
|
|
878
|
+
Value: agentId
|
|
879
|
+
});
|
|
880
|
+
rows.push({
|
|
881
|
+
Field: "Keys",
|
|
882
|
+
Value: keysLabel ?? "none"
|
|
883
|
+
});
|
|
884
|
+
}
|
|
885
|
+
printTable(rows);
|
|
886
|
+
}
|
|
887
|
+
} catch (err) {
|
|
888
|
+
printError(formatError(err));
|
|
889
|
+
process.exitCode = 1;
|
|
890
|
+
}
|
|
891
|
+
});
|
|
777
892
|
const authGroup = program.command("auth").description("Auth commands");
|
|
778
893
|
authGroup.command("token").description("Get or refresh access token").option("--force", "Force token refresh (bypass cache)").action(withErrorHandler(program, async (gOpts, opts) => {
|
|
779
894
|
const profile = gOpts.profile ?? "default";
|
|
@@ -1508,6 +1623,68 @@ async function verifyServerKeys(client, agentId, expected) {
|
|
|
1508
1623
|
}
|
|
1509
1624
|
function registerKeys(program) {
|
|
1510
1625
|
const keys = program.command("keys").description("E2EE key management");
|
|
1626
|
+
keys.command("status").description("Show E2EE key status for an agent").option("--agent <id>", "Agent ID").action(withClient(program, async ({ client, gOpts }, opts) => {
|
|
1627
|
+
const agentId = await resolveAgent(client, opts.agent, gOpts.as);
|
|
1628
|
+
const handle = (await client.get("/agents")).items.find((a) => a.id === agentId)?.handle ?? "unknown";
|
|
1629
|
+
const hasLocal = agentKeysExist(agentId);
|
|
1630
|
+
let hasServer = false;
|
|
1631
|
+
let serverKeys;
|
|
1632
|
+
try {
|
|
1633
|
+
serverKeys = await client.get(`/agents/${agentId}/keys`);
|
|
1634
|
+
hasServer = true;
|
|
1635
|
+
} catch (err) {
|
|
1636
|
+
if (!(err instanceof RineApiError && err.status === 404)) throw err;
|
|
1637
|
+
}
|
|
1638
|
+
let sigFingerprint = "-";
|
|
1639
|
+
let encFingerprint = "-";
|
|
1640
|
+
if (hasLocal) {
|
|
1641
|
+
const keys = loadAgentKeys(agentId);
|
|
1642
|
+
sigFingerprint = toBase64Url(keys.signing.publicKey).slice(0, 8);
|
|
1643
|
+
encFingerprint = toBase64Url(keys.encryption.publicKey).slice(0, 8);
|
|
1644
|
+
} else if (serverKeys) {
|
|
1645
|
+
sigFingerprint = serverKeys.signing_public_key.x.slice(0, 8);
|
|
1646
|
+
encFingerprint = serverKeys.encryption_public_key.x.slice(0, 8);
|
|
1647
|
+
}
|
|
1648
|
+
const keysLabel = hasLocal && hasServer ? "local + server" : hasLocal ? "local only" : hasServer ? "server only" : "none";
|
|
1649
|
+
if (gOpts.json) printJson({
|
|
1650
|
+
agent_id: agentId,
|
|
1651
|
+
handle,
|
|
1652
|
+
local_keys: hasLocal,
|
|
1653
|
+
server_keys: hasServer,
|
|
1654
|
+
signing_fingerprint: sigFingerprint !== "-" ? sigFingerprint : null,
|
|
1655
|
+
encryption_fingerprint: encFingerprint !== "-" ? encFingerprint : null
|
|
1656
|
+
});
|
|
1657
|
+
else printTable([
|
|
1658
|
+
{
|
|
1659
|
+
Field: "Agent ID",
|
|
1660
|
+
Value: agentId
|
|
1661
|
+
},
|
|
1662
|
+
{
|
|
1663
|
+
Field: "Handle",
|
|
1664
|
+
Value: handle
|
|
1665
|
+
},
|
|
1666
|
+
{
|
|
1667
|
+
Field: "Local Keys",
|
|
1668
|
+
Value: hasLocal ? "yes" : "no"
|
|
1669
|
+
},
|
|
1670
|
+
{
|
|
1671
|
+
Field: "Server Keys",
|
|
1672
|
+
Value: hasServer ? "yes" : "no"
|
|
1673
|
+
},
|
|
1674
|
+
{
|
|
1675
|
+
Field: "Keys",
|
|
1676
|
+
Value: keysLabel
|
|
1677
|
+
},
|
|
1678
|
+
{
|
|
1679
|
+
Field: "Signing Fingerprint",
|
|
1680
|
+
Value: sigFingerprint
|
|
1681
|
+
},
|
|
1682
|
+
{
|
|
1683
|
+
Field: "Encryption Fingerprint",
|
|
1684
|
+
Value: encFingerprint
|
|
1685
|
+
}
|
|
1686
|
+
]);
|
|
1687
|
+
}));
|
|
1511
1688
|
keys.command("generate").description("Generate new E2EE keys for an agent").option("--agent <id>", "Agent ID").action(withClient(program, async ({ client, gOpts }, opts) => {
|
|
1512
1689
|
const agentId = await resolveAgent(client, opts.agent, gOpts.as);
|
|
1513
1690
|
if (agentKeysExist(agentId)) {
|
|
@@ -1757,16 +1934,31 @@ function inboxRow(m) {
|
|
|
1757
1934
|
Created: m.created_at
|
|
1758
1935
|
};
|
|
1759
1936
|
}
|
|
1937
|
+
function resolvePayload(payload, payloadFile) {
|
|
1938
|
+
if (payload !== void 0 && payloadFile !== void 0) return { error: "--payload and --payload-file are mutually exclusive" };
|
|
1939
|
+
if (payload === void 0 && payloadFile === void 0) return { error: "Provide --payload or --payload-file" };
|
|
1940
|
+
let raw;
|
|
1941
|
+
if (payloadFile !== void 0) try {
|
|
1942
|
+
raw = payloadFile === "-" ? fs.readFileSync(0, "utf-8") : fs.readFileSync(payloadFile, "utf-8");
|
|
1943
|
+
} catch (err) {
|
|
1944
|
+
return { error: `Cannot read payload file: ${err instanceof Error ? err.message : String(err)}` };
|
|
1945
|
+
}
|
|
1946
|
+
else raw = payload;
|
|
1947
|
+
try {
|
|
1948
|
+
return { parsed: JSON.parse(raw) };
|
|
1949
|
+
} catch {
|
|
1950
|
+
return { error: "Payload must be valid JSON" };
|
|
1951
|
+
}
|
|
1952
|
+
}
|
|
1760
1953
|
function addMessageCommands(parent, program) {
|
|
1761
|
-
parent.command("send").description("Send an encrypted message").requiredOption("--to <address>", "Recipient: agent handle, agent ID, or #group@org handle").
|
|
1762
|
-
|
|
1763
|
-
|
|
1764
|
-
|
|
1765
|
-
} catch {
|
|
1766
|
-
printError("--payload must be valid JSON");
|
|
1954
|
+
parent.command("send").description("Send an encrypted message").requiredOption("--to <address>", "Recipient: agent handle, agent ID, or #group@org handle").option("--type <type>", "Message type (default: rine.v1.dm)").option("--payload <json>", "Message payload as JSON string").option("--payload-file <path>", "Read payload JSON from file (use - for stdin)").option("--from <address>", "Sender address (agent ID or handle with @)").option("--idempotency-key <key>", "Idempotency key header").action(withClient(program, async ({ client, gOpts }, opts) => {
|
|
1955
|
+
const result = resolvePayload(opts.payload, opts.payloadFile);
|
|
1956
|
+
if ("error" in result) {
|
|
1957
|
+
printError(result.error);
|
|
1767
1958
|
process.exitCode = 2;
|
|
1768
1959
|
return;
|
|
1769
1960
|
}
|
|
1961
|
+
const parsedPayload = result.parsed;
|
|
1770
1962
|
const extraHeaders = {};
|
|
1771
1963
|
let senderAgentId;
|
|
1772
1964
|
if (opts.from !== void 0) {
|
|
@@ -1775,12 +1967,12 @@ function addMessageCommands(parent, program) {
|
|
|
1775
1967
|
} else if (!gOpts.as) {
|
|
1776
1968
|
senderAgentId = await resolveAgent(client, void 0, void 0);
|
|
1777
1969
|
extraHeaders["X-Rine-Agent"] = senderAgentId;
|
|
1778
|
-
} else senderAgentId = await
|
|
1970
|
+
} else senderAgentId = await resolveAgent(client, void 0, gOpts.as);
|
|
1779
1971
|
if (opts.idempotencyKey) extraHeaders["Idempotency-Key"] = opts.idempotencyKey;
|
|
1780
1972
|
let recipientAgentId;
|
|
1781
1973
|
if (!opts.to.includes("@")) recipientAgentId = opts.to;
|
|
1782
1974
|
else if (!opts.to.startsWith("#")) recipientAgentId = await resolveToUuid(opts.to);
|
|
1783
|
-
const body = { type: opts.type };
|
|
1975
|
+
const body = { type: opts.type ?? "rine.v1.dm" };
|
|
1784
1976
|
if (opts.to.includes("@")) body.to_handle = opts.to;
|
|
1785
1977
|
else body.to_agent_id = opts.to;
|
|
1786
1978
|
if (opts.from?.includes("@")) body.from_handle = opts.from;
|
|
@@ -1868,20 +2060,20 @@ function addMessageCommands(parent, program) {
|
|
|
1868
2060
|
if (data.next_cursor) printText(`Next cursor: ${data.next_cursor}`);
|
|
1869
2061
|
}
|
|
1870
2062
|
}));
|
|
1871
|
-
parent.command("reply").description("Reply to a message (encrypted)").argument("<message-id>", "Message ID to reply to").
|
|
1872
|
-
|
|
1873
|
-
|
|
1874
|
-
|
|
1875
|
-
} catch {
|
|
1876
|
-
printError("--payload must be valid JSON");
|
|
2063
|
+
parent.command("reply").description("Reply to a message (encrypted)").argument("<message-id>", "Message ID to reply to").option("--type <type>", "Message type (defaults to original message type)").option("--payload <json>", "Reply payload as JSON string").option("--payload-file <path>", "Read payload JSON from file (use - for stdin)").action(withClient(program, async ({ client, gOpts }, messageId, opts) => {
|
|
2064
|
+
const result = resolvePayload(opts.payload, opts.payloadFile);
|
|
2065
|
+
if ("error" in result) {
|
|
2066
|
+
printError(result.error);
|
|
1877
2067
|
process.exitCode = 2;
|
|
1878
2068
|
return;
|
|
1879
2069
|
}
|
|
2070
|
+
const parsedPayload = result.parsed;
|
|
1880
2071
|
const original = await client.get(`/messages/${messageId}`);
|
|
1881
2072
|
const senderAgentId = await resolveAgent(client, void 0, gOpts.as);
|
|
1882
2073
|
const recipientAgentId = original.from_agent_id;
|
|
2074
|
+
const resolvedType = opts.type ?? original.type;
|
|
1883
2075
|
try {
|
|
1884
|
-
const body = { type:
|
|
2076
|
+
const body = { type: resolvedType };
|
|
1885
2077
|
if (!recipientAgentId || !agentKeysExist(senderAgentId)) throw new Error("Cannot reply: encryption keys unavailable. Run 'rine keys generate' first.");
|
|
1886
2078
|
const encrypted = await encryptMessage(senderAgentId, await fetchRecipientEncryptionKey(client, recipientAgentId), parsedPayload);
|
|
1887
2079
|
Object.assign(body, encrypted);
|
|
@@ -2211,7 +2403,7 @@ function registerWebhook(program) {
|
|
|
2211
2403
|
//#endregion
|
|
2212
2404
|
//#region src/main.ts
|
|
2213
2405
|
const { version } = createRequire(import.meta.url)("../package.json");
|
|
2214
|
-
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
|
|
2406
|
+
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)");
|
|
2215
2407
|
registerAuth(program);
|
|
2216
2408
|
registerRegister(program);
|
|
2217
2409
|
registerOrg(program);
|