@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/CHANGELOG.md +72 -0
- package/dist/index.js +159 -314
- package/dist/index.js.map +1 -1
- package/package.json +5 -4
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
3713
|
+
if (cached && cached.latest !== "0.3.27") {
|
|
3714
3714
|
warn(
|
|
3715
|
-
`Your arbi version is out of date (${"0.3.
|
|
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
|
|
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
|
-
|
|
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
|
|
3963
|
-
if (
|
|
3964
|
-
|
|
3965
|
-
|
|
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} ${
|
|
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(
|
|
3989
|
+
skillContent = fs2.readFileSync(path.join(cliRoot, "SKILL.md"), "utf-8");
|
|
4011
3990
|
} catch {
|
|
4012
3991
|
return;
|
|
4013
3992
|
}
|
|
4014
|
-
|
|
4015
|
-
|
|
4016
|
-
|
|
4017
|
-
|
|
4018
|
-
|
|
4019
|
-
|
|
4020
|
-
|
|
4021
|
-
|
|
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
|
-
|
|
4002
|
+
} catch {
|
|
4029
4003
|
}
|
|
4030
|
-
|
|
4031
|
-
|
|
4032
|
-
|
|
4033
|
-
|
|
4034
|
-
|
|
4035
|
-
|
|
4036
|
-
|
|
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
|
-
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
4140
|
-
program2.command("
|
|
4141
|
-
|
|
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
|
-
|
|
4239
|
-
|
|
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,
|
|
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
|
-
|
|
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,
|
|
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,
|
|
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.
|
|
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
|
-
|
|
7064
|
-
registerAuthorizeCommand(program);
|
|
6909
|
+
registerListenCommand(program);
|
|
7065
6910
|
registerSessionCommand(program);
|
|
7066
6911
|
registerLocalCommand(program);
|
|
7067
6912
|
var completionIdx = process.argv.indexOf("--get-completions");
|