@arbidocs/cli 0.3.25 → 0.3.27

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/index.js CHANGED
@@ -3637,7 +3637,7 @@ function getLatestVersion(skipCache = false) {
3637
3637
  }
3638
3638
  }
3639
3639
  function getCurrentVersion() {
3640
- return "0.3.25";
3640
+ return "0.3.27";
3641
3641
  }
3642
3642
  function readChangelog(fromVersion, toVersion) {
3643
3643
  try {
@@ -3690,17 +3690,17 @@ function showChangelog(fromVersion, toVersion) {
3690
3690
  async function checkForUpdates(autoUpdate) {
3691
3691
  try {
3692
3692
  const latest = getLatestVersion();
3693
- if (!latest || latest === "0.3.25") return;
3693
+ if (!latest || latest === "0.3.27") return;
3694
3694
  if (autoUpdate) {
3695
3695
  warn(`
3696
- Your arbi version is out of date (${"0.3.25"} \u2192 ${latest}). Updating...`);
3696
+ Your arbi version is out of date (${"0.3.27"} \u2192 ${latest}). Updating...`);
3697
3697
  child_process.execSync("npm install -g @arbidocs/cli@latest", { stdio: "inherit" });
3698
- showChangelog("0.3.25", latest);
3698
+ showChangelog("0.3.27", latest);
3699
3699
  console.log(`Updated to ${latest}.`);
3700
3700
  } else {
3701
3701
  warn(
3702
3702
  `
3703
- Your arbi version is out of date (${"0.3.25"} \u2192 ${latest}).
3703
+ Your arbi version is out of date (${"0.3.27"} \u2192 ${latest}).
3704
3704
  Run "arbi update" to upgrade, or "arbi update auto" to always stay up to date.`
3705
3705
  );
3706
3706
  }
@@ -3710,9 +3710,9 @@ Run "arbi update" to upgrade, or "arbi update auto" to always stay up to date.`
3710
3710
  function hintUpdateOnError() {
3711
3711
  try {
3712
3712
  const cached = readCache();
3713
- if (cached && cached.latest !== "0.3.25") {
3713
+ if (cached && cached.latest !== "0.3.27") {
3714
3714
  warn(
3715
- `Your arbi version is out of date (${"0.3.25"} \u2192 ${cached.latest}). Run "arbi update".`
3715
+ `Your arbi version is out of date (${"0.3.27"} \u2192 ${cached.latest}). Run "arbi update".`
3716
3716
  );
3717
3717
  }
3718
3718
  } catch {
@@ -3911,8 +3911,8 @@ async function resolveDmCrypto() {
3911
3911
  error("No user ID in session \u2014 cannot set up DM encryption.");
3912
3912
  process.exit(1);
3913
3913
  }
3914
- const crypto = sdk.dm.createDmCryptoContext(arbi, loginResult.signingPrivateKey, userExtId);
3915
- return { ...authCtx, crypto };
3914
+ const crypto2 = sdk.dm.createDmCryptoContext(arbi, loginResult.signingPrivateKey, userExtId);
3915
+ return { ...authCtx, crypto: crypto2 };
3916
3916
  }
3917
3917
  function printTable(columns, rows) {
3918
3918
  console.log(chalk2__default.default.bold(columns.map((c) => c.header.padEnd(c.width)).join("")));
@@ -3946,43 +3946,33 @@ var AGENT_MIN_VERSIONS = {
3946
3946
  installUrl: "https://docs.openclaw.ai/install"
3947
3947
  }
3948
3948
  };
3949
+ function compareVersions(a, b) {
3950
+ const pa = a.split(".").map(Number);
3951
+ const pb = b.split(".").map(Number);
3952
+ const len = Math.max(pa.length, pb.length);
3953
+ for (let i = 0; i < len; i++) {
3954
+ const diff = (pa[i] ?? 0) - (pb[i] ?? 0);
3955
+ if (diff !== 0) return diff;
3956
+ }
3957
+ return 0;
3958
+ }
3949
3959
  function checkAgentDependency(backend) {
3950
3960
  const { binary, minVersion, installUrl } = AGENT_MIN_VERSIONS[backend];
3951
3961
  let version;
3952
3962
  try {
3953
3963
  version = child_process.execFileSync(binary, ["--version"], { encoding: "utf-8", timeout: 5e3 }).trim();
3954
3964
  } catch {
3955
- error(
3956
- `"${binary}" is not installed or not in PATH.
3957
- The "${backend}" agent backend requires ${binary} >= ${minVersion}.
3958
- Install: ${installUrl}`
3959
- );
3965
+ error(`"${binary}" is not installed.
3966
+ Install: ${installUrl}`);
3960
3967
  process.exit(1);
3961
3968
  }
3962
- const versionMatch = version.match(/(\d+\.\d+[\d.]*)/);
3963
- if (!versionMatch) {
3964
- dim(`Could not parse ${binary} version from: ${version}`);
3965
- return;
3966
- }
3967
- const current = versionMatch[1];
3968
- if (compareVersions(current, minVersion) < 0) {
3969
- error(
3970
- `"${binary}" version ${current} is too old (need >= ${minVersion}).
3971
- Update: ${installUrl}`
3972
- );
3969
+ const match = version.match(/(\d+\.\d+[\d.]*)/);
3970
+ if (match && compareVersions(match[1], minVersion) < 0) {
3971
+ error(`"${binary}" ${match[1]} is too old (need >= ${minVersion}).
3972
+ Update: ${installUrl}`);
3973
3973
  process.exit(1);
3974
3974
  }
3975
- dim(`${binary} ${current} \u2713`);
3976
- }
3977
- function compareVersions(a, b) {
3978
- const pa = a.split(".").map(Number);
3979
- const pb = b.split(".").map(Number);
3980
- const len = Math.max(pa.length, pb.length);
3981
- for (let i = 0; i < len; i++) {
3982
- const diff = (pa[i] ?? 0) - (pb[i] ?? 0);
3983
- if (diff !== 0) return diff;
3984
- }
3985
- return 0;
3975
+ dim(`${binary} ${match?.[1] ?? version} \u2713`);
3986
3976
  }
3987
3977
  function loadSkillContent() {
3988
3978
  const cliRoot = path.resolve(path.dirname(url.fileURLToPath((typeof document === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : (_documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === 'SCRIPT' && _documentCurrentScript.src || new URL('index.js', document.baseURI).href)))), "..");
@@ -3992,91 +3982,49 @@ function loadSkillContent() {
3992
3982
  return void 0;
3993
3983
  }
3994
3984
  }
3995
- function createAgent(backend, sessionId) {
3996
- switch (backend) {
3997
- case "claude":
3998
- return new sdk.ClaudeOrchestrator({ sessionId, skillPrompt: loadSkillContent() });
3999
- case "openclaw":
4000
- return new sdk.OpenClawOrchestrator({ sessionId });
4001
- default:
4002
- throw new Error(`Unknown agent backend: ${backend}`);
4003
- }
4004
- }
4005
3985
  function installSkill(backend) {
4006
3986
  const cliRoot = path.resolve(path.dirname(url.fileURLToPath((typeof document === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : (_documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === 'SCRIPT' && _documentCurrentScript.src || new URL('index.js', document.baseURI).href)))), "..");
4007
- const skillPath = path.join(cliRoot, "SKILL.md");
4008
3987
  let skillContent;
4009
3988
  try {
4010
- skillContent = fs2.readFileSync(skillPath, "utf-8");
3989
+ skillContent = fs2.readFileSync(path.join(cliRoot, "SKILL.md"), "utf-8");
4011
3990
  } catch {
4012
3991
  return;
4013
3992
  }
4014
- switch (backend) {
4015
- case "claude": {
4016
- const commandsDir = path.join(os.homedir(), ".claude", "commands", "arbi");
4017
- const targetPath = path.join(commandsDir, "SKILL.md");
4018
- try {
4019
- fs2.mkdirSync(commandsDir, { recursive: true });
4020
- if (fs2.existsSync(targetPath)) {
4021
- const existing = fs2.readFileSync(targetPath, "utf-8");
4022
- if (existing === skillContent) return;
4023
- }
4024
- fs2.writeFileSync(targetPath, skillContent, "utf-8");
4025
- dim(`Installed ARBI skill \u2192 ${targetPath}`);
4026
- } catch {
3993
+ if (backend === "claude") {
3994
+ const dir = path.join(os.homedir(), ".claude", "commands", "arbi");
3995
+ const target = path.join(dir, "SKILL.md");
3996
+ try {
3997
+ fs2.mkdirSync(dir, { recursive: true });
3998
+ if (!fs2.existsSync(target) || fs2.readFileSync(target, "utf-8") !== skillContent) {
3999
+ fs2.writeFileSync(target, skillContent, "utf-8");
4000
+ dim(`Installed ARBI skill \u2192 ${target}`);
4027
4001
  }
4028
- break;
4002
+ } catch {
4029
4003
  }
4030
- case "openclaw": {
4031
- const ocAgentName = "arbi";
4032
- const ocWorkspace = path.join(os.homedir(), ".arbi", "openclaw-workspace");
4033
- const bootstrapPath = path.join(ocWorkspace, "BOOTSTRAP.md");
4034
- try {
4035
- const list = child_process.execFileSync("openclaw", ["agents", "list", "--json"], {
4036
- encoding: "utf-8"
4037
- });
4038
- const agents = JSON.parse(list);
4039
- const exists = Array.isArray(agents) ? agents.some((a) => a.id === ocAgentName) : false;
4040
- if (!exists) {
4041
- fs2.mkdirSync(ocWorkspace, { recursive: true });
4042
- child_process.execFileSync(
4043
- "openclaw",
4044
- ["agents", "add", ocAgentName, "--workspace", ocWorkspace, "--non-interactive"],
4045
- { encoding: "utf-8" }
4046
- );
4047
- dim(`Created OpenClaw agent "${ocAgentName}" \u2192 ${ocWorkspace}`);
4048
- }
4049
- } catch {
4050
- fs2.mkdirSync(ocWorkspace, { recursive: true });
4051
- try {
4052
- child_process.execFileSync(
4053
- "openclaw",
4054
- ["agents", "add", ocAgentName, "--workspace", ocWorkspace, "--non-interactive"],
4055
- { encoding: "utf-8" }
4056
- );
4057
- dim(`Created OpenClaw agent "${ocAgentName}" \u2192 ${ocWorkspace}`);
4058
- } catch {
4059
- }
4060
- }
4061
- try {
4062
- fs2.mkdirSync(ocWorkspace, { recursive: true });
4063
- if (fs2.existsSync(bootstrapPath)) {
4064
- const existing = fs2.readFileSync(bootstrapPath, "utf-8");
4065
- if (existing === skillContent) break;
4066
- }
4067
- fs2.writeFileSync(bootstrapPath, skillContent, "utf-8");
4068
- dim(`Installed ARBI skill \u2192 ${bootstrapPath}`);
4069
- } catch {
4004
+ } else if (backend === "openclaw") {
4005
+ const workspace = path.join(os.homedir(), ".arbi", "openclaw-workspace");
4006
+ const bootstrap = path.join(workspace, "BOOTSTRAP.md");
4007
+ try {
4008
+ fs2.mkdirSync(workspace, { recursive: true });
4009
+ if (!fs2.existsSync(bootstrap) || fs2.readFileSync(bootstrap, "utf-8") !== skillContent) {
4010
+ fs2.writeFileSync(bootstrap, skillContent, "utf-8");
4011
+ dim(`Installed ARBI skill \u2192 ${bootstrap}`);
4070
4012
  }
4071
- break;
4013
+ } catch {
4072
4014
  }
4073
4015
  }
4074
4016
  }
4017
+ function createOrchestrator(backend, sessionId) {
4018
+ if (backend === "claude")
4019
+ return new sdk.ClaudeOrchestrator({ sessionId, skillPrompt: loadSkillContent() });
4020
+ if (backend === "openclaw") return new sdk.OpenClawOrchestrator({ sessionId });
4021
+ throw new Error(`Unknown backend: ${backend}`);
4022
+ }
4075
4023
  async function setupAgent(agentName, config) {
4076
4024
  const backend = agentName ?? config.orchestrator;
4077
4025
  if (!backend) return void 0;
4078
4026
  if (!AGENT_BACKENDS.includes(backend)) {
4079
- error(`Unknown agent backend: "${backend}". Choose from: ${AGENT_BACKENDS.join(", ")}`);
4027
+ error(`Unknown backend: "${backend}". Choose from: ${AGENT_BACKENDS.join(", ")}`);
4080
4028
  process.exit(1);
4081
4029
  }
4082
4030
  checkAgentDependency(backend);
@@ -4092,32 +4040,30 @@ async function startListening(agentName, config) {
4092
4040
  if (!backend) {
4093
4041
  error(
4094
4042
  `No agent backend configured. Use --agent to specify one:
4095
- arbi connect --agent ${AGENT_BACKENDS[0]} --listen`
4043
+ arbi listen --agent claude`
4096
4044
  );
4097
4045
  process.exit(1);
4098
4046
  }
4099
4047
  const creds = store.getCredentials();
4100
4048
  if (!creds?.parentExtId) {
4101
- error(
4102
- "DM listener requires a persistent agent identity (parentExtId).\n This session was not claimed as a delegated agent."
4103
- );
4049
+ error("DM listener requires a persistent agent identity (parentExtId).");
4104
4050
  process.exit(1);
4105
4051
  }
4106
4052
  dim("Starting DM listener...");
4107
- const { arbi, crypto } = await resolveDmCrypto();
4108
- const sessionId = arbi.session.getState().userExtId ?? "default";
4109
- const orchestrator = createAgent(backend, sessionId);
4053
+ const { arbi, crypto: crypto2 } = await resolveDmCrypto();
4110
4054
  const fullConfig = resolveConfig();
4055
+ const sessionId = arbi.session.getState().userExtId ?? "default";
4056
+ const orchestrator = createOrchestrator(backend, sessionId);
4111
4057
  const accessToken = arbi.session.getState().accessToken;
4112
4058
  if (!accessToken) {
4113
- error("No access token \u2014 authentication may have failed.");
4059
+ error("No access token \u2014 run `arbi login` first.");
4114
4060
  process.exit(1);
4115
4061
  }
4116
4062
  const listener = await sdk.startDmListener({
4117
4063
  arbi,
4118
4064
  accessToken,
4119
4065
  baseUrl: fullConfig.baseUrl,
4120
- crypto,
4066
+ crypto: crypto2,
4121
4067
  orchestrator,
4122
4068
  parentExtId: creds.parentExtId,
4123
4069
  onLog: (msg) => dim(msg),
@@ -4126,7 +4072,7 @@ async function startListening(agentName, config) {
4126
4072
  success(`Listening for DMs as ${chalk2__default.default.cyan(creds.email)} via ${chalk2__default.default.cyan(backend)}`);
4127
4073
  dim("Press Ctrl+C to stop.");
4128
4074
  const shutdown = () => {
4129
- dim("\nShutting down listener...");
4075
+ dim("\nShutting down...");
4130
4076
  listener.close();
4131
4077
  orchestrator.close?.();
4132
4078
  process.exit(0);
@@ -4136,116 +4082,17 @@ async function startListening(agentName, config) {
4136
4082
  await new Promise(() => {
4137
4083
  });
4138
4084
  }
4139
- function registerConnectCommand(program2) {
4140
- program2.command("connect").description("Claim a session and/or connect to an agent backend").option("--code <claim-code>", "Claim code from parent (required on first run)").option("--agent <name>", `Agent backend (${AGENT_BACKENDS.join(", ")})`).option("--listen", "Start DM listener (agent receives prompts via encrypted DMs)").action((opts) => {
4141
- const run = async () => {
4085
+ function registerListenCommand(program2) {
4086
+ program2.command("listen").description("Start DM listener (connect agent backend to ARBI messaging)").option("--agent <name>", `Agent backend: ${AGENT_BACKENDS.join(", ")}`).action(
4087
+ (opts) => (async () => {
4142
4088
  const config = resolveConfig();
4143
- const creds = store.getCredentials();
4144
- const hasIdentity = creds?.signingPrivateKeyBase64;
4145
- if (!hasIdentity) {
4146
- const claimCode = opts.code?.trim() ?? "";
4147
- if (!claimCode) {
4148
- const authorizeUrl = `${config.baseUrl}#/action/authorize-agent`;
4149
- error(
4150
- `No saved identity. Provide a claim code:
4151
- arbi connect --code purple-mountain-river
4152
-
4153
- Get a claim code from the ARBI web UI:
4154
- ${chalk2__default.default.underline(authorizeUrl)}`
4155
- );
4156
- process.exit(1);
4157
- }
4158
- dim("Claiming session...");
4159
- const arbi = client.createArbiClient({
4160
- baseUrl: config.baseUrl,
4161
- deploymentDomain: config.deploymentDomain,
4162
- credentials: "omit"
4163
- });
4164
- await arbi.crypto.initSodium();
4165
- const keypair = arbi.crypto.generateRandomSigningKeypair();
4166
- const signingPubKeyBase64 = client.bytesToBase64(keypair.publicKey);
4167
- const signingPrivateKeyBase64 = client.bytesToBase64(keypair.secretKey);
4168
- const claimResponse = await sdk.sessions.claimSession(arbi, claimCode, signingPubKeyBase64);
4169
- const parentExtId = claimResponse.user.parent_ext_id ?? void 0;
4170
- const agentEmail = claimResponse.user.email ?? claimResponse.user.external_id;
4171
- store.saveCredentials({
4172
- email: agentEmail,
4173
- signingPrivateKeyBase64,
4174
- serverSessionKeyBase64: claimResponse.session_key,
4175
- accessToken: claimResponse.access_token,
4176
- parentExtId
4177
- });
4178
- const workspaces3 = claimResponse.workspaces ?? [];
4179
- if (workspaces3.length > 0) {
4180
- const firstWs = workspaces3[0];
4181
- const wsId = firstWs.external_id;
4182
- if (wsId) {
4183
- updateConfig({ selectedWorkspaceId: wsId });
4184
- }
4185
- }
4186
- success("Session claimed!");
4187
- console.log(` Identity: ${chalk2__default.default.cyan(agentEmail)}`);
4188
- if (parentExtId) {
4189
- console.log(` Parent: ${chalk2__default.default.cyan(parentExtId)}`);
4190
- console.log(` Type: Persistent agent`);
4191
- } else {
4192
- console.log(` Type: Ephemeral session`);
4193
- }
4194
- console.log("");
4195
- if (parentExtId) {
4196
- try {
4197
- arbi.session.setAccessToken(claimResponse.access_token);
4198
- arbi.session.setUser(agentEmail, claimResponse.user.external_id);
4199
- const contacts = await sdk.contacts.listContacts(arbi);
4200
- const parentContact = contacts.find((c) => {
4201
- const u = c.user;
4202
- return u?.external_id === parentExtId;
4203
- });
4204
- const parentPubKey = parentContact?.user?.encryption_public_key;
4205
- if (parentPubKey) {
4206
- const userExtId = claimResponse.user.external_id ?? agentEmail;
4207
- const crypto = sdk.dm.createDmCryptoContext(
4208
- arbi,
4209
- client.base64ToBytes(signingPrivateKeyBase64),
4210
- userExtId
4211
- );
4212
- await sdk.dm.sendEncryptedDM(
4213
- arbi,
4214
- [
4215
- {
4216
- recipient_ext_id: parentExtId,
4217
- content: `\u{1F511} Agent recovery key for ${agentEmail}
4218
- signing_key: ${signingPrivateKeyBase64}
4219
-
4220
- Save this key \u2014 it is the only way to recover this agent session.`,
4221
- recipient_encryption_public_key: parentPubKey
4222
- }
4223
- ],
4224
- crypto
4225
- );
4226
- dim("Recovery key sent to parent via encrypted DM.");
4227
- } else {
4228
- dim("Could not find parent encryption key \u2014 recovery key not sent.");
4229
- dim(`Backup manually: ${signingPrivateKeyBase64}`);
4230
- }
4231
- } catch {
4232
- dim("Failed to send recovery key DM \u2014 save it manually:");
4233
- dim(` ${signingPrivateKeyBase64}`);
4234
- }
4235
- }
4236
- }
4237
4089
  await setupAgent(opts.agent, config);
4238
- if (opts.listen) {
4239
- await startListening(opts.agent, config);
4240
- } else {
4241
- success("Connected. Use `arbi --help` to explore.");
4242
- }
4243
- };
4244
- run().catch((err) => {
4090
+ await startListening(opts.agent, config);
4091
+ })().catch((err) => {
4245
4092
  error(`Error: ${err instanceof Error ? err.message : String(err)}`);
4246
4093
  process.exit(1);
4247
- });
4248
- });
4094
+ })
4095
+ );
4249
4096
  }
4250
4097
 
4251
4098
  // src/commands/login.ts
@@ -5525,8 +5372,8 @@ function registerDmCommand(program2) {
5525
5372
  const dm = program2.command("dm").description("Direct messages (E2E encrypted)");
5526
5373
  dm.command("list").description("List all DMs and notifications (decrypted)").action(
5527
5374
  runAction(async () => {
5528
- const { arbi, crypto } = await resolveDmCrypto();
5529
- const data = await sdk.dm.listDecryptedDMs(arbi, crypto);
5375
+ const { arbi, crypto: crypto2 } = await resolveDmCrypto();
5376
+ const data = await sdk.dm.listDecryptedDMs(arbi, crypto2);
5530
5377
  if (data.length === 0) {
5531
5378
  console.log("No messages found.");
5532
5379
  return;
@@ -5552,7 +5399,7 @@ function registerDmCommand(program2) {
5552
5399
  );
5553
5400
  dm.command("send [recipient] [content...]").description("Send an E2E encrypted DM (interactive if no args)").action(
5554
5401
  (recipient, contentParts) => runAction(async () => {
5555
- const { arbi, crypto } = await resolveDmCrypto();
5402
+ const { arbi, crypto: crypto2 } = await resolveDmCrypto();
5556
5403
  if (!recipient) {
5557
5404
  const contacts2 = await sdk.contacts.listContacts(arbi);
5558
5405
  if (contacts2.length === 0) {
@@ -5657,7 +5504,7 @@ function registerDmCommand(program2) {
5657
5504
  recipient_encryption_public_key: recipientPubKey
5658
5505
  }
5659
5506
  ],
5660
- crypto
5507
+ crypto2
5661
5508
  );
5662
5509
  for (const n of data) {
5663
5510
  success(`Sent: ${n.external_id} \u2192 ${n.recipient.email}`);
@@ -5666,10 +5513,10 @@ function registerDmCommand(program2) {
5666
5513
  );
5667
5514
  dm.command("read [ids...]").description("Mark messages as read (interactive picker if no IDs given)").action(
5668
5515
  (ids) => runAction(async () => {
5669
- const { arbi, crypto } = await resolveDmCrypto();
5516
+ const { arbi, crypto: crypto2 } = await resolveDmCrypto();
5670
5517
  let msgIds = ids && ids.length > 0 ? ids : void 0;
5671
5518
  if (!msgIds) {
5672
- const data2 = await sdk.dm.listDecryptedDMs(arbi, crypto);
5519
+ const data2 = await sdk.dm.listDecryptedDMs(arbi, crypto2);
5673
5520
  const unread = data2.filter((m) => !m.read);
5674
5521
  if (unread.length === 0) {
5675
5522
  console.log("No unread messages.");
@@ -5694,10 +5541,10 @@ function registerDmCommand(program2) {
5694
5541
  );
5695
5542
  dm.command("delete [ids...]").description("Delete messages (interactive picker if no IDs given)").action(
5696
5543
  (ids) => runAction(async () => {
5697
- const { arbi, crypto } = await resolveDmCrypto();
5544
+ const { arbi, crypto: crypto2 } = await resolveDmCrypto();
5698
5545
  let msgIds = ids && ids.length > 0 ? ids : void 0;
5699
5546
  if (!msgIds) {
5700
- const data = await sdk.dm.listDecryptedDMs(arbi, crypto);
5547
+ const data = await sdk.dm.listDecryptedDMs(arbi, crypto2);
5701
5548
  if (data.length === 0) {
5702
5549
  console.log("No messages found.");
5703
5550
  return;
@@ -6583,8 +6430,90 @@ function registerAgentCreateCommand(program2) {
6583
6430
  }
6584
6431
  );
6585
6432
  }
6433
+ function generatePassword() {
6434
+ const chars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-_";
6435
+ const bytes = new Uint8Array(20);
6436
+ crypto.getRandomValues(bytes);
6437
+ return Array.from(bytes, (b) => chars[b % chars.length]).join("");
6438
+ }
6586
6439
  function registerAgentCommand(program2) {
6587
6440
  const agent = program2.command("agent").description("Manage persistent agents");
6441
+ agent.command("create").description("Create a persistent agent with its own login credentials").option("--name <name>", "Agent name (letters, digits, hyphens, underscores)").option("-w, --workspace <ids...>", "Workspace IDs to share with the agent").option("--role <role>", "Workspace role: collaborator or guest", "collaborator").action(
6442
+ (opts) => runAction(async () => {
6443
+ const { arbi, loginResult } = await resolveAuth();
6444
+ const config = resolveConfig();
6445
+ const creds = store.requireCredentials();
6446
+ let name = opts.name;
6447
+ if (!name) name = await promptInput("Agent name (letters, digits, hyphens, underscores)");
6448
+ if (!name?.trim() || !/^[a-zA-Z0-9][a-zA-Z0-9_-]*$/.test(name.trim())) {
6449
+ throw new Error("Invalid agent name \u2014 use letters, digits, hyphens, underscores");
6450
+ }
6451
+ let workspaceIds = opts.workspace ?? [];
6452
+ if (workspaceIds.length === 0) {
6453
+ const { data: allWorkspaces } = await arbi.fetch.GET("/v1/user/workspaces");
6454
+ if (allWorkspaces && allWorkspaces.length > 0) {
6455
+ workspaceIds = await promptCheckbox(
6456
+ "Share with workspaces (optional \u2014 press Enter to skip)",
6457
+ allWorkspaces.map((ws) => ({
6458
+ name: `${ws.name} (${ws.external_id})`,
6459
+ value: ws.external_id
6460
+ }))
6461
+ );
6462
+ }
6463
+ }
6464
+ await client.initSodium();
6465
+ const parentExtId = arbi.session.getState().userExtId ?? "";
6466
+ if (!parentExtId) throw new Error("Could not determine current user ext_id");
6467
+ const agentEmail = `${name.trim()}.${parentExtId}@${config.deploymentDomain}`;
6468
+ const password2 = generatePassword();
6469
+ const { signingPrivateKey } = await client.generateLoginCredentials(
6470
+ { email: agentEmail },
6471
+ password2,
6472
+ config.deploymentDomain
6473
+ );
6474
+ const signingPubKey = client.bytesToBase64(signingPrivateKey.slice(32));
6475
+ const { data: agent2, error: error2 } = await arbi.fetch.POST("/v1/user/agent", {
6476
+ body: {
6477
+ name: name.trim(),
6478
+ signing_key: signingPubKey,
6479
+ recovery_key: client.bytesToBase64(signingPrivateKey)
6480
+ }
6481
+ });
6482
+ if (!agent2) throw new Error(`Failed to create agent: ${JSON.stringify(error2)}`);
6483
+ if (workspaceIds.length > 0) {
6484
+ const { data: allWs } = await arbi.fetch.GET("/v1/user/workspaces");
6485
+ for (const wsId of workspaceIds) {
6486
+ const ws = allWs?.find((w) => w.external_id === wsId);
6487
+ if (!ws?.wrapped_key) {
6488
+ dim(`Skipping ${wsId} \u2014 no key found`);
6489
+ continue;
6490
+ }
6491
+ const encryptedKey = await sdk.generateEncryptedWorkspaceKey(
6492
+ arbi,
6493
+ ws.wrapped_key,
6494
+ loginResult.serverSessionKey,
6495
+ creds.signingPrivateKeyBase64
6496
+ );
6497
+ await arbi.fetch.POST("/v1/workspace/users", {
6498
+ body: {
6499
+ emails: [agent2.email],
6500
+ role: opts.role ?? "collaborator",
6501
+ workspace_key: encryptedKey,
6502
+ workspace_ext_id: wsId
6503
+ }
6504
+ });
6505
+ }
6506
+ }
6507
+ console.log("");
6508
+ success("Agent created!");
6509
+ console.log("");
6510
+ console.log(` Email: ${ref(agent2.email)}`);
6511
+ console.log(` Password: ${ref(password2)}`);
6512
+ if (workspaceIds.length > 0) console.log(` Workspaces: ${workspaceIds.join(", ")}`);
6513
+ console.log("");
6514
+ dim(`On the agent machine: arbi login --email ${agent2.email} --password <password>`);
6515
+ })()
6516
+ );
6588
6517
  agent.command("list").description("List your persistent agents").action(
6589
6518
  runAction(async () => {
6590
6519
  const { arbi } = await resolveAuth();
@@ -6904,89 +6833,6 @@ function printTree(dir, prefix, maxDepth, depth) {
6904
6833
  }
6905
6834
  }
6906
6835
  }
6907
- async function authorizeShared(opts) {
6908
- const { arbi, loginResult } = await resolveAuth();
6909
- let name = opts.name;
6910
- if (!name) {
6911
- name = await promptInput(opts.persist ? "Agent name" : "Session name");
6912
- }
6913
- if (!name?.trim()) {
6914
- error("Name is required");
6915
- process.exit(1);
6916
- }
6917
- const { data: allWorkspaces, error: wsErr } = await arbi.fetch.GET("/v1/user/workspaces");
6918
- if (wsErr || !allWorkspaces || allWorkspaces.length === 0) {
6919
- error("No workspaces found");
6920
- process.exit(1);
6921
- }
6922
- let workspaceIds = opts.workspace;
6923
- if (!workspaceIds || workspaceIds.length === 0) {
6924
- workspaceIds = await promptCheckbox(
6925
- "Select workspaces to grant access",
6926
- allWorkspaces.map((ws) => ({
6927
- name: `${ws.name} (${ws.external_id})`,
6928
- value: ws.external_id
6929
- }))
6930
- );
6931
- }
6932
- if (workspaceIds.length === 0) {
6933
- error("At least one workspace is required");
6934
- process.exit(1);
6935
- }
6936
- const creds = store.requireCredentials();
6937
- const workspaceKeysMap = {};
6938
- for (const wsId of workspaceIds) {
6939
- const ws = allWorkspaces.find((w) => w.external_id === wsId);
6940
- if (!ws?.wrapped_key) {
6941
- error(`Workspace ${wsId} not found or has no encryption key`);
6942
- process.exit(1);
6943
- }
6944
- const encryptedKey = await sdk.generateEncryptedWorkspaceKey(
6945
- arbi,
6946
- ws.wrapped_key,
6947
- loginResult.serverSessionKey,
6948
- creds.signingPrivateKeyBase64
6949
- );
6950
- workspaceKeysMap[wsId] = encryptedKey;
6951
- }
6952
- const ttl = parseInt(opts.ttl || "3600", 10);
6953
- const response = await sdk.sessions.authorizeSession(arbi, workspaceKeysMap, {
6954
- activeWorkspace: workspaceIds[0],
6955
- ttl,
6956
- persistIdentity: opts.persist,
6957
- name: name.trim()
6958
- });
6959
- return { response, ttl };
6960
- }
6961
- function registerAuthorizeCommand(program2) {
6962
- const authorize = program2.command("authorize").description("Create a claim code for a session or agent");
6963
- authorize.command("agent").description("Create a persistent agent identity with a claim code").option("--name <name>", "Agent name").option("-w, --workspace <ids...>", "Workspace IDs to grant access to (interactive if omitted)").action(
6964
- (opts) => runAction(async () => {
6965
- const { response } = await authorizeShared({ persist: true, ...opts });
6966
- console.log("");
6967
- success("Agent authorized!");
6968
- console.log("");
6969
- console.log(` Claim code: ${chalk2__default.default.cyan(chalk2__default.default.bold(response.claim_code))}`);
6970
- console.log("");
6971
- dim("On the agent machine: arbi connect --code <claim-code> --agent claude");
6972
- })()
6973
- );
6974
- authorize.command("session").description("Create an ephemeral session with a claim code").option("--name <name>", "Session name").option("--ttl <seconds>", "Session TTL in seconds (default: 3600)", "3600").option("-w, --workspace <ids...>", "Workspace IDs to grant access to (interactive if omitted)").action(
6975
- (opts) => runAction(async () => {
6976
- const { response, ttl } = await authorizeShared({ persist: false, ...opts });
6977
- console.log("");
6978
- success("Session authorized!");
6979
- console.log("");
6980
- console.log(` Claim code: ${chalk2__default.default.cyan(chalk2__default.default.bold(response.claim_code))}`);
6981
- console.log(` Expires in: ${ttl}s`);
6982
- console.log("");
6983
- dim("Claim with: arbi connect --code <claim-code>");
6984
- })()
6985
- );
6986
- authorize.action(() => {
6987
- authorize.help();
6988
- });
6989
- }
6990
6836
  function registerSessionCommand(program2) {
6991
6837
  const session = program2.command("session").description("List and manage sessions");
6992
6838
  session.command("list").description("List active sessions").action(
@@ -7031,7 +6877,7 @@ console.info = (...args) => {
7031
6877
  _origInfo(...args);
7032
6878
  };
7033
6879
  var program = new commander.Command();
7034
- program.name("arbi").description("ARBI CLI \u2014 interact with ARBI from the terminal").version("0.3.25");
6880
+ program.name("arbi").description("ARBI CLI \u2014 interact with ARBI from the terminal").version("0.3.27");
7035
6881
  registerConfigCommand(program);
7036
6882
  registerLoginCommand(program);
7037
6883
  registerRegisterCommand(program);
@@ -7060,8 +6906,7 @@ registerAgentCreateCommand(program);
7060
6906
  registerAgentCommand(program);
7061
6907
  registerTaskCommand(program);
7062
6908
  registerCompletionCommand(program);
7063
- registerConnectCommand(program);
7064
- registerAuthorizeCommand(program);
6909
+ registerListenCommand(program);
7065
6910
  registerSessionCommand(program);
7066
6911
  registerLocalCommand(program);
7067
6912
  var completionIdx = process.argv.indexOf("--get-completions");