@rine-network/cli 0.9.6 → 0.10.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 (2) hide show
  1. package/dist/main.js +134 -16
  2. package/package.json +2 -2
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, fetchAgents, fetchAndIngestPendingSKDistributions, fetchOAuthToken, fetchRecipientEncryptionKey, formatError, fromBase64Url, generateAgentKeys, getAgentPublicKeys, getCredentialEntry, getOrCreateSenderKey, getOrRefreshToken, ingestSenderKeyDistribution, isBareAgentName, loadAgentKeys, loadCredentials, loadTokenCache, normalizeHandle, performAgentCreation, performRegistration, resolveAgent, resolveApiUrl, resolveConfigDir, resolveHandleViaWebFinger, resolveToUuid, saveAgentKeys, saveCredentials, saveTokenCache, toBase64Url, validateEncryptionKey, validateSigningKey, validateSlug } from "@rine-network/core";
3
+ import { HttpClient, RineApiError, UUID_RE, addMlsGroupMember, agentKeysExist, cacheToken, decryptGroupMessage, decryptMessage, encryptGroupMessage, encryptMessage, encryptMlsGroupMessage, externalJoinMlsGroup, fetchAgents, fetchAndIngestPendingSKDistributions, fetchOAuthToken, fetchRecipientKeys, formatError, fromBase64Url, generateAgentKeys, generatePqKeyPair, getAgentPublicKeys, getCredentialEntry, getOrCreateSenderKey, getOrRefreshToken, ingestSenderKeyDistribution, initMlsGroup, isBareAgentName, loadAgentKeys, loadCredentials, loadMlsState, loadTokenCache, normalizeHandle, performAgentCreation, performRegistration, processMlsWelcomes, resolveAgent, resolveApiUrl, resolveConfigDir, resolveHandleViaWebFinger, resolveToUuid, saveAgentKeys, saveCredentials, savePqEncryptionKey, saveTokenCache, syncMlsGroup, 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";
@@ -686,6 +686,31 @@ function registerDiscover(program) {
686
686
  }
687
687
  //#endregion
688
688
  //#region src/commands/group.ts
689
+ async function installWelcomes(client, configDir, agentId, json) {
690
+ try {
691
+ await processMlsWelcomes(configDir, agentId, client);
692
+ } catch (err) {
693
+ if (!json) printText(`Warning: MLS welcome install failed: ${err instanceof Error ? err.message : String(err)}`);
694
+ }
695
+ }
696
+ async function selfJoinViaExternalCommit(client, configDir, groupId, agentId, json) {
697
+ try {
698
+ if (!(await client.get(`/groups/${groupId}`)).mls_group_id) return;
699
+ if (loadMlsState(configDir, agentId, groupId)) return;
700
+ await externalJoinMlsGroup(configDir, agentId, groupId, client, { "X-Rine-Agent": agentId });
701
+ } catch (err) {
702
+ if (!json) printText(`Warning: MLS self-join failed: ${err instanceof Error ? err.message : String(err)}`);
703
+ }
704
+ }
705
+ async function addMemberToMls(client, configDir, apiUrl, groupId, memberAgentId, as, json) {
706
+ try {
707
+ if (!(await client.get(`/groups/${groupId}`)).mls_group_id) return;
708
+ const adminId = await resolveAgent(apiUrl, await fetchAgents(client), void 0, as);
709
+ await addMlsGroupMember(configDir, adminId, groupId, memberAgentId, client, { "X-Rine-Agent": adminId });
710
+ } catch (err) {
711
+ if (!json) printText(`Warning: MLS add-member failed: ${err instanceof Error ? err.message : String(err)}`);
712
+ }
713
+ }
689
714
  function groupRow(g) {
690
715
  return {
691
716
  ID: g.id,
@@ -731,7 +756,7 @@ function requestRow(r) {
731
756
  }
732
757
  function registerGroup(program) {
733
758
  const group = program.command("group").description("Group management");
734
- group.command("create").description("Create a new group").requiredOption("--name <name>", "Group name (DNS-safe slug)").option("--enrollment <policy>", "Enrollment policy (open|closed|majority|unanimity)").option("--visibility <vis>", "Visibility (public|private)").option("--isolated", "Isolate group communication").option("--vote-duration <hours>", "Vote duration in hours (1-72)").action(withClient(program, async ({ client, gOpts }, opts) => {
759
+ group.command("create").description("Create a new group").requiredOption("--name <name>", "Group name (DNS-safe slug)").option("--enrollment <policy>", "Enrollment policy (open|closed|majority|unanimity)").option("--visibility <vis>", "Visibility (public|private)").option("--isolated", "Isolate group communication").option("--vote-duration <hours>", "Vote duration in hours (1-72)").action(withClient(program, async ({ client, gOpts, configDir, apiUrl }, opts) => {
735
760
  const body = { name: opts.name };
736
761
  if (opts.enrollment) body.enrollment_policy = opts.enrollment;
737
762
  if (opts.visibility) body.visibility = opts.visibility;
@@ -742,6 +767,12 @@ function registerGroup(program) {
742
767
  if (opts.isolated) body.isolated = true;
743
768
  if (opts.voteDuration) body.vote_duration_hours = Number(opts.voteDuration);
744
769
  const data = await client.post("/groups", body);
770
+ try {
771
+ const actingAgentId = await resolveAgent(apiUrl, await fetchAgents(client), void 0, gOpts.as);
772
+ await initMlsGroup(configDir, actingAgentId, data.id, client, { "X-Rine-Agent": actingAgentId });
773
+ } catch (err) {
774
+ if (!gOpts.json) printText(`Warning: MLS init failed (group still created): ${err instanceof Error ? err.message : String(err)}`);
775
+ }
745
776
  if (gOpts.json) printJson(data);
746
777
  else {
747
778
  printTable([groupDetailRow(data)]);
@@ -788,10 +819,14 @@ function registerGroup(program) {
788
819
  if (gOpts.json) printJson(data);
789
820
  else printTable(data.items.map(memberRow));
790
821
  }));
791
- group.command("join").description("Join a group").argument("<group-id>", "Group ID").option("--message <msg>", "Join request message").action(withClient(program, async ({ client, gOpts }, groupId, opts) => {
822
+ group.command("join").description("Join a group").argument("<group-id>", "Group ID").option("--message <msg>", "Join request message").action(withClient(program, async ({ client, gOpts, configDir }, groupId, opts) => {
792
823
  const body = {};
793
824
  if (opts.message) body.message = opts.message;
794
825
  const data = await client.post(`/groups/${groupId}/join`, body);
826
+ if ("role" in data) {
827
+ await installWelcomes(client, configDir, data.agent_id, gOpts.json);
828
+ await selfJoinViaExternalCommit(client, configDir, groupId, data.agent_id, gOpts.json);
829
+ }
795
830
  if (gOpts.json) printJson(data);
796
831
  else if ("role" in data) {
797
832
  printTable([memberRow(data)]);
@@ -811,10 +846,12 @@ function registerGroup(program) {
811
846
  await client.delete(`/groups/${groupId}/members/${resolved}`);
812
847
  printMutationOk("Member removed", gOpts.json);
813
848
  }));
814
- group.command("invite").description("Invite an agent to the group").argument("<group-id>", "Group ID").requiredOption("--agent <id>", "Agent ID to invite").option("--message <msg>", "Invitation message").action(withClient(program, async ({ client, gOpts, apiUrl }, groupId, opts) => {
815
- const body = { agent_id: await resolveToUuid(apiUrl, opts.agent) };
849
+ group.command("invite").description("Invite an agent to the group").argument("<group-id>", "Group ID").requiredOption("--agent <id>", "Agent ID to invite").option("--message <msg>", "Invitation message").action(withClient(program, async ({ client, gOpts, configDir, apiUrl }, groupId, opts) => {
850
+ const agentId = await resolveToUuid(apiUrl, opts.agent);
851
+ const body = { agent_id: agentId };
816
852
  if (opts.message) body.message = opts.message;
817
853
  const data = await client.post(`/groups/${groupId}/invite`, body);
854
+ await addMemberToMls(client, configDir, apiUrl, groupId, agentId, gOpts.as, gOpts.json);
818
855
  if (gOpts.json) printJson(data);
819
856
  else {
820
857
  printText("Invitation sent");
@@ -826,13 +863,16 @@ function registerGroup(program) {
826
863
  if (gOpts.json) printJson(data);
827
864
  else printTable(data.items.map(requestRow));
828
865
  }));
829
- group.command("vote").description("Vote on a join request").argument("<group-id>", "Group ID").argument("<request-id>", "Join request ID").requiredOption("--vote <vote>", "Vote: approve or deny").action(withClient(program, async ({ client, gOpts }, groupId, requestId, opts) => {
866
+ group.command("vote").description("Vote on a join request").argument("<group-id>", "Group ID").argument("<request-id>", "Join request ID").requiredOption("--vote <vote>", "Vote: approve or deny").action(withClient(program, async ({ client, gOpts, configDir, apiUrl }, groupId, requestId, opts) => {
830
867
  if (opts.vote !== "approve" && opts.vote !== "deny") {
831
868
  printError("--vote must be 'approve' or 'deny'");
832
869
  process.exitCode = 2;
833
870
  return;
834
871
  }
872
+ let applicantId;
873
+ if (opts.vote === "approve") applicantId = (await client.get(`/groups/${groupId}/requests`)).items.find((r) => r.id === requestId)?.agent_id;
835
874
  const data = await client.post(`/groups/${groupId}/requests/${requestId}/vote`, { vote: opts.vote });
875
+ if (data.status === "approved" && applicantId) await addMemberToMls(client, configDir, apiUrl, groupId, applicantId, gOpts.as, gOpts.json);
836
876
  if (gOpts.json) printJson(data);
837
877
  else printText(`Vote recorded. Request status: ${data.status}`);
838
878
  }));
@@ -845,7 +885,11 @@ function getKeysDir(configDir, agentId) {
845
885
  function backupKeys(configDir, agentId) {
846
886
  const dir = getKeysDir(configDir, agentId);
847
887
  const ts = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
848
- for (const name of ["signing.key", "encryption.key"]) {
888
+ for (const name of [
889
+ "signing.key",
890
+ "encryption.key",
891
+ "pq_encryption.key"
892
+ ]) {
849
893
  const src = join(dir, name);
850
894
  if (!fs.existsSync(src)) continue;
851
895
  for (const f of fs.readdirSync(dir)) if (f.startsWith(`${name}.bak.`)) fs.unlinkSync(join(dir, f));
@@ -855,6 +899,7 @@ function backupKeys(configDir, agentId) {
855
899
  async function verifyServerKeys(client, agentId, expected) {
856
900
  const server = await client.get(`/agents/${agentId}/keys`);
857
901
  if (server.signing_public_key.x !== expected.signing_public_key.x || server.encryption_public_key.x !== expected.encryption_public_key.x) throw new Error("Server key verification failed: uploaded keys do not match. Old keys preserved locally.");
902
+ if (expected.pq_encryption_public_key && server.pq_encryption_public_key?.x !== expected.pq_encryption_public_key.x) throw new Error("Server PQ key verification failed: uploaded PQ key does not match. Old keys preserved locally.");
858
903
  }
859
904
  function registerKeys(program) {
860
905
  const keys = program.command("keys").description("E2EE key management");
@@ -873,13 +918,16 @@ function registerKeys(program) {
873
918
  }
874
919
  let sigFingerprint = "-";
875
920
  let encFingerprint = "-";
921
+ let pqFingerprint = "-";
876
922
  if (hasLocal) {
877
923
  const agentKeys = loadAgentKeys(configDir, agentId);
878
924
  sigFingerprint = toBase64Url(agentKeys.signing.publicKey).slice(0, 8);
879
925
  encFingerprint = toBase64Url(agentKeys.encryption.publicKey).slice(0, 8);
926
+ if (agentKeys.pqEncryption) pqFingerprint = toBase64Url(agentKeys.pqEncryption.publicKey).slice(0, 8);
880
927
  } else if (serverKeys) {
881
928
  sigFingerprint = serverKeys.signing_public_key.x.slice(0, 8);
882
929
  encFingerprint = serverKeys.encryption_public_key.x.slice(0, 8);
930
+ if (serverKeys.pq_encryption_public_key) pqFingerprint = serverKeys.pq_encryption_public_key.x.slice(0, 8);
883
931
  }
884
932
  const keysLabel = hasLocal && hasServer ? "local + server" : hasLocal ? "local only" : hasServer ? "server only" : "none";
885
933
  if (gOpts.json) printJson({
@@ -888,7 +936,8 @@ function registerKeys(program) {
888
936
  local_keys: hasLocal,
889
937
  server_keys: hasServer,
890
938
  signing_fingerprint: sigFingerprint !== "-" ? sigFingerprint : null,
891
- encryption_fingerprint: encFingerprint !== "-" ? encFingerprint : null
939
+ encryption_fingerprint: encFingerprint !== "-" ? encFingerprint : null,
940
+ pq_encryption_fingerprint: pqFingerprint !== "-" ? pqFingerprint : null
892
941
  });
893
942
  else printTable([
894
943
  {
@@ -918,6 +967,10 @@ function registerKeys(program) {
918
967
  {
919
968
  Field: "Encryption Fingerprint",
920
969
  Value: encFingerprint
970
+ },
971
+ {
972
+ Field: "PQ Encryption Fingerprint",
973
+ Value: pqFingerprint
921
974
  }
922
975
  ]);
923
976
  }));
@@ -945,6 +998,30 @@ function registerKeys(program) {
945
998
  saveAgentKeys(configDir, agentId, agentKeys);
946
999
  printMutationOk(`E2EE keys rotated for agent ${agentId}`, gOpts.json);
947
1000
  }));
1001
+ keys.command("add-pq").description("Add a post-quantum encryption key without rotating other keys").option("--agent <id>", "Agent ID").action(withClient(program, async ({ client, gOpts, configDir, apiUrl }, opts) => {
1002
+ const agentId = await resolveAgent(apiUrl, await fetchAgents(client), opts.agent, gOpts.as);
1003
+ if (!agentKeysExist(configDir, agentId)) {
1004
+ printError(`No keys found for agent ${agentId}. Use 'rine keys generate' first.`);
1005
+ process.exitCode = 1;
1006
+ return;
1007
+ }
1008
+ const existingKeys = loadAgentKeys(configDir, agentId);
1009
+ if (existingKeys.pqEncryption) {
1010
+ printError(`Agent ${agentId} already has a post-quantum key. Use 'rine keys rotate' to replace all keys.`);
1011
+ process.exitCode = 1;
1012
+ return;
1013
+ }
1014
+ backupKeys(configDir, agentId);
1015
+ const pqKeyPair = generatePqKeyPair();
1016
+ const pubKeys = getAgentPublicKeys(configDir, agentId, {
1017
+ ...existingKeys,
1018
+ pqEncryption: pqKeyPair
1019
+ });
1020
+ await client.post(`/agents/${agentId}/keys`, pubKeys);
1021
+ await verifyServerKeys(client, agentId, pubKeys);
1022
+ savePqEncryptionKey(configDir, agentId, pqKeyPair);
1023
+ printMutationOk(`Post-quantum encryption key added for agent ${agentId}`, gOpts.json);
1024
+ }));
948
1025
  keys.command("export").description("Export private keys to a file").option("--agent <id>", "Agent ID").requiredOption("--output <file>", "Output file path").action(withClient(program, async ({ client, gOpts, configDir, apiUrl }, opts) => {
949
1026
  const agentId = await resolveAgent(apiUrl, await fetchAgents(client), opts.agent, gOpts.as);
950
1027
  if (!agentKeysExist(configDir, agentId)) {
@@ -958,6 +1035,7 @@ function registerKeys(program) {
958
1035
  signing_private_key: toBase64Url(agentKeys.signing.privateKey),
959
1036
  encryption_private_key: toBase64Url(agentKeys.encryption.privateKey)
960
1037
  };
1038
+ if (agentKeys.pqEncryption) exported.pq_encryption_private_key = toBase64Url(agentKeys.pqEncryption.privateKey);
961
1039
  fs.writeFileSync(opts.output, JSON.stringify(exported, null, 2));
962
1040
  fs.chmodSync(opts.output, 384);
963
1041
  if (gOpts.json) printJson({
@@ -988,6 +1066,14 @@ function registerKeys(program) {
988
1066
  process.exitCode = 2;
989
1067
  return;
990
1068
  }
1069
+ if (data.pq_encryption_private_key) {
1070
+ const pqBytes = fromBase64Url(data.pq_encryption_private_key);
1071
+ if (pqBytes.length !== 2400) {
1072
+ printError(`Invalid PQ encryption key: expected 2400 bytes, got ${pqBytes.length}`);
1073
+ process.exitCode = 2;
1074
+ return;
1075
+ }
1076
+ }
991
1077
  const dir = join(resolveConfigDir(), "keys", data.agent_id);
992
1078
  fs.mkdirSync(dir, {
993
1079
  recursive: true,
@@ -999,6 +1085,11 @@ function registerKeys(program) {
999
1085
  const encPath = join(dir, "encryption.key");
1000
1086
  fs.writeFileSync(encPath, data.encryption_private_key, "utf-8");
1001
1087
  fs.chmodSync(encPath, 384);
1088
+ if (data.pq_encryption_private_key) {
1089
+ const pqPath = join(dir, "pq_encryption.key");
1090
+ fs.writeFileSync(pqPath, data.pq_encryption_private_key, "utf-8");
1091
+ fs.chmodSync(pqPath, 384);
1092
+ }
1002
1093
  printMutationOk(`Keys imported for agent ${data.agent_id}`, gOpts.json);
1003
1094
  }));
1004
1095
  }
@@ -1050,6 +1141,26 @@ function resolvePayload(payload, payloadFile) {
1050
1141
  return { error: "Payload must be valid JSON" };
1051
1142
  }
1052
1143
  }
1144
+ async function resolveGroup(client, handle) {
1145
+ const group = (await client.get("/groups", { handle })).items?.[0];
1146
+ if (!group) throw new Error(`Group not found: ${handle}`);
1147
+ return group;
1148
+ }
1149
+ async function encryptMlsWithRecovery(client, configDir, senderAgentId, groupId, payload) {
1150
+ const hdrs = { "X-Rine-Agent": senderAgentId };
1151
+ await processMlsWelcomes(configDir, senderAgentId, client).catch(() => {});
1152
+ await syncMlsGroup(configDir, senderAgentId, groupId, client, hdrs).catch(() => {});
1153
+ try {
1154
+ return await encryptMlsGroupMessage(configDir, senderAgentId, groupId, payload);
1155
+ } catch (err) {
1156
+ if (err instanceof Error && err.message.startsWith("No MLS state for group")) {
1157
+ await processMlsWelcomes(configDir, senderAgentId, client);
1158
+ await syncMlsGroup(configDir, senderAgentId, groupId, client, hdrs);
1159
+ return await encryptMlsGroupMessage(configDir, senderAgentId, groupId, payload);
1160
+ }
1161
+ throw err;
1162
+ }
1163
+ }
1053
1164
  function addMessageCommands(parent, program) {
1054
1165
  parent.command("send").description("Send an encrypted message").requiredOption("--to <address>", "Recipient: agent handle (agent@org), agent ID, or group handle (#group@org)").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: agent handle (agent@org) or agent ID").option("--idempotency-key <key>", "Idempotency key header").action(withClient(program, async ({ client, gOpts, configDir, apiUrl }, opts) => {
1055
1166
  const result = resolvePayload(opts.payload, opts.payloadFile);
@@ -1079,12 +1190,18 @@ function addMessageCommands(parent, program) {
1079
1190
  else body.to_agent_id = to;
1080
1191
  if (from?.includes("@")) body.from_handle = from;
1081
1192
  if (to.startsWith("#") && senderAgentId) {
1082
- const { state, groupId } = await getOrCreateSenderKey(client, configDir, senderAgentId, to, Object.keys(extraHeaders).length > 0 ? extraHeaders : void 0);
1083
- const { result: encResult } = await encryptGroupMessage(configDir, senderAgentId, groupId, state, parsedPayload);
1084
- Object.assign(body, encResult);
1193
+ const group = await resolveGroup(client, to);
1194
+ if (group.mls_group_id) {
1195
+ const encResult = await encryptMlsWithRecovery(client, configDir, senderAgentId, group.id, parsedPayload);
1196
+ Object.assign(body, encResult);
1197
+ } else {
1198
+ const { state, groupId } = await getOrCreateSenderKey(client, configDir, senderAgentId, to, Object.keys(extraHeaders).length > 0 ? extraHeaders : void 0);
1199
+ const { result: encResult } = await encryptGroupMessage(configDir, senderAgentId, groupId, state, parsedPayload);
1200
+ Object.assign(body, encResult);
1201
+ }
1085
1202
  } else if (senderAgentId && recipientAgentId) {
1086
- const recipientPk = await fetchRecipientEncryptionKey(client, recipientAgentId);
1087
- const encrypted = await encryptMessage(configDir, senderAgentId, recipientPk, parsedPayload);
1203
+ const recipientKeys = await fetchRecipientKeys(client, recipientAgentId);
1204
+ const encrypted = await encryptMessage(configDir, senderAgentId, recipientKeys.encryption, parsedPayload, recipientKeys.pqEncryption);
1088
1205
  Object.assign(body, encrypted);
1089
1206
  } else throw new Error("Cannot encrypt: unable to resolve sender or recipient. Use UUIDs or ensure WebFinger is available.");
1090
1207
  const data = await client.post("/messages", body, Object.keys(extraHeaders).length > 0 ? extraHeaders : void 0);
@@ -1094,9 +1211,9 @@ function addMessageCommands(parent, program) {
1094
1211
  parent.command("read").description("Read and decrypt a message by ID").argument("<message-id>", "Message ID").option("--agent <id>", "Agent ID (for decryption key)").action(withClient(program, async ({ client, gOpts, configDir, apiUrl }, messageId, opts) => {
1095
1212
  const data = await client.get(`/messages/${messageId}`);
1096
1213
  const agentId = await resolveAgent(apiUrl, await fetchAgents(client), opts.agent, gOpts.as);
1097
- if ((data.encryption_version === "hpke-v1" || data.encryption_version === "sender-key-v1") && agentKeysExist(configDir, agentId)) {
1214
+ if ((data.encryption_version === "hpke-v1" || data.encryption_version === "hpke-hybrid-v1" || data.encryption_version === "sender-key-v1" || data.encryption_version === "mls-v1") && agentKeysExist(configDir, agentId)) {
1098
1215
  let decResult;
1099
- if (data.encryption_version === "sender-key-v1" && data.group_id) try {
1216
+ if ((data.encryption_version === "sender-key-v1" || data.encryption_version === "mls-v1") && data.group_id) try {
1100
1217
  decResult = await decryptGroupMessage(configDir, agentId, data.group_id, data.encrypted_payload, client);
1101
1218
  } catch (err) {
1102
1219
  const match = err instanceof Error && err.message.match(/^unknown sender key id: (.+)$/);
@@ -1190,7 +1307,8 @@ function addMessageCommands(parent, program) {
1190
1307
  try {
1191
1308
  const body = { type: resolvedType };
1192
1309
  if (!recipientAgentId || !agentKeysExist(configDir, senderAgentId)) throw new Error("Cannot reply: encryption keys unavailable. Run 'rine keys generate' first.");
1193
- const encrypted = await encryptMessage(configDir, senderAgentId, await fetchRecipientEncryptionKey(client, recipientAgentId), parsedPayload);
1310
+ const recipientKeys = await fetchRecipientKeys(client, recipientAgentId);
1311
+ const encrypted = await encryptMessage(configDir, senderAgentId, recipientKeys.encryption, parsedPayload, recipientKeys.pqEncryption);
1194
1312
  Object.assign(body, encrypted);
1195
1313
  const data = await client.post(`/messages/${messageId}/reply`, body);
1196
1314
  if (gOpts.json) printJson(data);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rine-network/cli",
3
- "version": "0.9.6",
3
+ "version": "0.10.0",
4
4
  "description": "CLI client for rine.network \u2014 EU-first messaging infrastructure for AI agents",
5
5
  "author": "mmmbs <mmmbs@proton.me>",
6
6
  "license": "EUPL-1.2",
@@ -28,7 +28,7 @@
28
28
  "dev": "tsx src/main.ts"
29
29
  },
30
30
  "dependencies": {
31
- "@rine-network/core": "^0.4.4",
31
+ "@rine-network/core": "^0.5.0",
32
32
  "commander": "^12.0.0",
33
33
  "eventsource-client": "^1.1.0"
34
34
  },