@arbidocs/cli 0.3.41 → 0.3.43
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 +11 -3
- package/SKILL.md +22 -8
- package/dist/index.js +382 -75
- package/dist/index.js.map +1 -1
- package/package.json +4 -4
package/dist/index.js
CHANGED
|
@@ -3641,7 +3641,7 @@ function getLatestVersion(skipCache = false) {
|
|
|
3641
3641
|
}
|
|
3642
3642
|
}
|
|
3643
3643
|
function getCurrentVersion() {
|
|
3644
|
-
return "0.3.
|
|
3644
|
+
return "0.3.43";
|
|
3645
3645
|
}
|
|
3646
3646
|
function readChangelog(fromVersion, toVersion) {
|
|
3647
3647
|
try {
|
|
@@ -3694,17 +3694,17 @@ function showChangelog(fromVersion, toVersion) {
|
|
|
3694
3694
|
async function checkForUpdates(autoUpdate) {
|
|
3695
3695
|
try {
|
|
3696
3696
|
const latest = getLatestVersion();
|
|
3697
|
-
if (!latest || latest === "0.3.
|
|
3697
|
+
if (!latest || latest === "0.3.43") return;
|
|
3698
3698
|
if (autoUpdate) {
|
|
3699
3699
|
warn(`
|
|
3700
|
-
Your arbi version is out of date (${"0.3.
|
|
3700
|
+
Your arbi version is out of date (${"0.3.43"} \u2192 ${latest}). Updating...`);
|
|
3701
3701
|
child_process.execSync("npm install -g @arbidocs/cli@latest", { stdio: "inherit" });
|
|
3702
|
-
showChangelog("0.3.
|
|
3702
|
+
showChangelog("0.3.43", latest);
|
|
3703
3703
|
console.log(`Updated to ${latest}.`);
|
|
3704
3704
|
} else {
|
|
3705
3705
|
warn(
|
|
3706
3706
|
`
|
|
3707
|
-
Your arbi version is out of date (${"0.3.
|
|
3707
|
+
Your arbi version is out of date (${"0.3.43"} \u2192 ${latest}).
|
|
3708
3708
|
Run "arbi update" to upgrade, or "arbi update auto" to always stay up to date.`
|
|
3709
3709
|
);
|
|
3710
3710
|
}
|
|
@@ -3714,9 +3714,9 @@ Run "arbi update" to upgrade, or "arbi update auto" to always stay up to date.`
|
|
|
3714
3714
|
function hintUpdateOnError() {
|
|
3715
3715
|
try {
|
|
3716
3716
|
const cached = readCache();
|
|
3717
|
-
if (cached && cached.latest !== "0.3.
|
|
3717
|
+
if (cached && cached.latest !== "0.3.43") {
|
|
3718
3718
|
warn(
|
|
3719
|
-
`Your arbi version is out of date (${"0.3.
|
|
3719
|
+
`Your arbi version is out of date (${"0.3.43"} \u2192 ${cached.latest}). Run "arbi update".`
|
|
3720
3720
|
);
|
|
3721
3721
|
}
|
|
3722
3722
|
} catch {
|
|
@@ -4101,14 +4101,29 @@ function registerListenCommand(program2) {
|
|
|
4101
4101
|
|
|
4102
4102
|
// src/commands/login.ts
|
|
4103
4103
|
function registerLoginCommand(program2) {
|
|
4104
|
-
program2.command("login").description("Log in to ARBI").option("-e, --email <email>", "Email address (or ARBI_EMAIL env var)").option("-p, --password <password>", "Password (or ARBI_PASSWORD env var)").option("-k, --signing-key <key>", "Signing key (base64) for agent recovery login").option("-w, --workspace <id>", "Workspace ID to select after login").option("--sso", "Log in with Auth0 SSO (device flow)").option("--agent <name>", "Agent backend to configure (claude, openclaw)").option("--listen", "Start DM listener after login").action(
|
|
4104
|
+
program2.command("login").description("Log in to ARBI").option("-e, --email <email>", "Email address (or ARBI_EMAIL env var)").option("-p, --password <password>", "Password (or ARBI_PASSWORD env var)").option("-k, --signing-key <key>", "Signing key (base64) for agent recovery login").option("-w, --workspace <id>", "Workspace ID to select after login").option("--sso", "Log in with Auth0 SSO (device flow)").option("--agent <name>", "Agent backend to configure (claude, openclaw)").option("--listen", "Start DM listener after login").option("--json", "Emit a single JSON object on success (scripting)").action(
|
|
4105
4105
|
(opts) => runAction(async () => {
|
|
4106
4106
|
const config = store.requireConfig();
|
|
4107
|
-
const
|
|
4107
|
+
const isTty = process.stdin.isTTY;
|
|
4108
|
+
const emailFromFlagOrEnv = opts.email || process.env.ARBI_EMAIL;
|
|
4109
|
+
if (!isTty && !emailFromFlagOrEnv) {
|
|
4110
|
+
const msg = "Email is required when stdin is not a TTY. Use --email <email> or set ARBI_EMAIL.";
|
|
4111
|
+
if (opts.json) console.log(JSON.stringify({ ok: false, error: msg }));
|
|
4112
|
+
else error(msg);
|
|
4113
|
+
process.exit(1);
|
|
4114
|
+
}
|
|
4115
|
+
const passwordFromFlagOrEnv = opts.password || process.env.ARBI_PASSWORD;
|
|
4116
|
+
if (!isTty && !passwordFromFlagOrEnv && !opts.signingKey) {
|
|
4117
|
+
const msg = "Password is required when stdin is not a TTY. Use --password <password> or set ARBI_PASSWORD.";
|
|
4118
|
+
if (opts.json) console.log(JSON.stringify({ ok: false, error: msg }));
|
|
4119
|
+
else error(msg);
|
|
4120
|
+
process.exit(1);
|
|
4121
|
+
}
|
|
4122
|
+
const email = emailFromFlagOrEnv || await promptInput("Email");
|
|
4108
4123
|
try {
|
|
4109
4124
|
let ssoPolling = false;
|
|
4110
4125
|
const { arbi } = opts.signingKey ? await sdk.performSigningKeyLogin(config, email, opts.signingKey, store) : opts.sso ? await (async () => {
|
|
4111
|
-
const pw =
|
|
4126
|
+
const pw = passwordFromFlagOrEnv || await promptPassword("Password");
|
|
4112
4127
|
const result = await sdk.performSsoDeviceFlowLogin(config, email, pw, store, {
|
|
4113
4128
|
onUserCode: (userCode, verificationUri) => {
|
|
4114
4129
|
console.log(`
|
|
@@ -4127,9 +4142,10 @@ Open this URL in your browser:
|
|
|
4127
4142
|
if (ssoPolling) console.log("\n");
|
|
4128
4143
|
return result;
|
|
4129
4144
|
})() : await (async () => {
|
|
4130
|
-
const pw =
|
|
4145
|
+
const pw = passwordFromFlagOrEnv || await promptPassword("Password");
|
|
4131
4146
|
return sdk.performPasswordLogin(config, email, pw, store);
|
|
4132
4147
|
})();
|
|
4148
|
+
if (!opts.json) success(`Logged in as ${email}`);
|
|
4133
4149
|
clearChatSession();
|
|
4134
4150
|
const { data: workspaces3 } = await arbi.fetch.GET("/v1/user/workspaces");
|
|
4135
4151
|
const wsList = workspaces3 || [];
|
|
@@ -4137,26 +4153,39 @@ Open this URL in your browser:
|
|
|
4137
4153
|
const memberWorkspaces = wsList.filter(
|
|
4138
4154
|
(ws) => ws.users?.some((u) => u.user.email === email)
|
|
4139
4155
|
);
|
|
4156
|
+
let selectedWorkspace;
|
|
4140
4157
|
if (memberWorkspaces.length === 0) {
|
|
4141
|
-
|
|
4158
|
+
if (!opts.json) {
|
|
4159
|
+
console.log("No workspaces found. Create one with: arbi workspace create <name>");
|
|
4160
|
+
}
|
|
4142
4161
|
} else if (opts.workspace) {
|
|
4143
4162
|
const ws = memberWorkspaces.find((w) => w.external_id === opts.workspace);
|
|
4144
4163
|
if (!ws) {
|
|
4145
|
-
|
|
4164
|
+
const msg = `Workspace ${opts.workspace} not found or you don't have access.`;
|
|
4165
|
+
if (opts.json) console.log(JSON.stringify({ ok: false, error: msg }));
|
|
4166
|
+
else error(msg);
|
|
4146
4167
|
process.exit(1);
|
|
4147
4168
|
}
|
|
4148
4169
|
updateConfig({ selectedWorkspaceId: ws.external_id });
|
|
4149
|
-
|
|
4170
|
+
selectedWorkspace = { external_id: ws.external_id, name: ws.name };
|
|
4171
|
+
if (!opts.json) success(`Workspace: ${ws.name} (${ref(ws.external_id)})`);
|
|
4150
4172
|
} else if (memberWorkspaces.length === 1) {
|
|
4151
4173
|
updateConfig({ selectedWorkspaceId: memberWorkspaces[0].external_id });
|
|
4152
|
-
|
|
4153
|
-
|
|
4154
|
-
|
|
4174
|
+
selectedWorkspace = {
|
|
4175
|
+
external_id: memberWorkspaces[0].external_id,
|
|
4176
|
+
name: memberWorkspaces[0].name
|
|
4177
|
+
};
|
|
4178
|
+
if (!opts.json)
|
|
4179
|
+
success(
|
|
4180
|
+
`Workspace: ${memberWorkspaces[0].name} (${ref(memberWorkspaces[0].external_id)})`
|
|
4181
|
+
);
|
|
4182
|
+
} else if (opts.json) {
|
|
4155
4183
|
} else {
|
|
4156
4184
|
const choices = sdk.formatWorkspaceChoices(memberWorkspaces);
|
|
4157
4185
|
const selected = await promptSelect("Select workspace", choices);
|
|
4158
4186
|
updateConfig({ selectedWorkspaceId: selected });
|
|
4159
4187
|
const ws = memberWorkspaces.find((w) => w.external_id === selected);
|
|
4188
|
+
selectedWorkspace = { external_id: selected, name: ws.name };
|
|
4160
4189
|
success(`Workspace: ${ws.name} (${ref(selected)})`);
|
|
4161
4190
|
dim('\nTip: Run "arbi config alias" to use A as a shortcut for "arbi ask"');
|
|
4162
4191
|
}
|
|
@@ -4166,8 +4195,21 @@ Open this URL in your browser:
|
|
|
4166
4195
|
await startListening(opts.agent, config);
|
|
4167
4196
|
}
|
|
4168
4197
|
}
|
|
4198
|
+
if (opts.json) {
|
|
4199
|
+
console.log(
|
|
4200
|
+
JSON.stringify({
|
|
4201
|
+
ok: true,
|
|
4202
|
+
email,
|
|
4203
|
+
server: config.baseUrl,
|
|
4204
|
+
workspace: selectedWorkspace ?? null,
|
|
4205
|
+
workspaces_available: memberWorkspaces.length
|
|
4206
|
+
})
|
|
4207
|
+
);
|
|
4208
|
+
}
|
|
4169
4209
|
} catch (err) {
|
|
4170
|
-
|
|
4210
|
+
const msg = formatCliError(err);
|
|
4211
|
+
if (opts.json) console.log(JSON.stringify({ ok: false, error: msg }));
|
|
4212
|
+
else error(`Login failed: ${msg}`);
|
|
4171
4213
|
process.exit(1);
|
|
4172
4214
|
} finally {
|
|
4173
4215
|
await checkForUpdates(getConfig()?.autoUpdate);
|
|
@@ -4176,7 +4218,19 @@ Open this URL in your browser:
|
|
|
4176
4218
|
);
|
|
4177
4219
|
}
|
|
4178
4220
|
var CENTRAL_API_URL = "https://central.arbi.work";
|
|
4179
|
-
|
|
4221
|
+
var RegisterHintError = class extends Error {
|
|
4222
|
+
constructor(message, kind) {
|
|
4223
|
+
super(message);
|
|
4224
|
+
this.kind = kind;
|
|
4225
|
+
this.name = "RegisterHintError";
|
|
4226
|
+
}
|
|
4227
|
+
kind;
|
|
4228
|
+
};
|
|
4229
|
+
function looksLikeAgentEmail(email) {
|
|
4230
|
+
const local = email.split("@")[0] ?? "";
|
|
4231
|
+
return local.toLowerCase().startsWith("agent-");
|
|
4232
|
+
}
|
|
4233
|
+
async function getVerificationCode(email, apiKey, deploymentDomain) {
|
|
4180
4234
|
const params = new URLSearchParams({ email });
|
|
4181
4235
|
const res = await fetch(`${CENTRAL_API_URL}/license-management/verify-ci?${params.toString()}`, {
|
|
4182
4236
|
method: "GET",
|
|
@@ -4184,6 +4238,24 @@ async function getVerificationCode(email, apiKey) {
|
|
|
4184
4238
|
});
|
|
4185
4239
|
if (!res.ok) {
|
|
4186
4240
|
const body = await res.text().catch(() => "");
|
|
4241
|
+
if (res.status === 403 && /agent-\*/i.test(body)) {
|
|
4242
|
+
throw new RegisterHintError(
|
|
4243
|
+
`This SUPPORT_API_KEY is a deployment key that can only register agent-*@${deploymentDomain} emails. Prefix your email with "agent-" (e.g. agent-<id>@${deploymentDomain}) or use a superuser key.`,
|
|
4244
|
+
"agent-email-required"
|
|
4245
|
+
);
|
|
4246
|
+
}
|
|
4247
|
+
if (res.status === 403 && /Invalid API Key/i.test(body)) {
|
|
4248
|
+
throw new RegisterHintError(
|
|
4249
|
+
"SUPPORT_API_KEY was rejected by central. Check the key value.",
|
|
4250
|
+
"bad-api-key"
|
|
4251
|
+
);
|
|
4252
|
+
}
|
|
4253
|
+
if (res.status === 404 && /No verification record/i.test(body)) {
|
|
4254
|
+
throw new RegisterHintError(
|
|
4255
|
+
`No pending verification for ${email}. The email may already be registered \u2014 try: arbi login --email ${email}`,
|
|
4256
|
+
"already-registered"
|
|
4257
|
+
);
|
|
4258
|
+
}
|
|
4187
4259
|
throw new Error(`Failed to get verification code: ${res.status} ${body}`);
|
|
4188
4260
|
}
|
|
4189
4261
|
const data = await res.json();
|
|
@@ -4192,7 +4264,7 @@ async function getVerificationCode(email, apiKey) {
|
|
|
4192
4264
|
return Array.isArray(words) ? words.join(" ") : String(words);
|
|
4193
4265
|
}
|
|
4194
4266
|
function registerRegisterCommand(program2) {
|
|
4195
|
-
program2.command("register").description("Register a new ARBI account").option("--non-interactive", "CI/automation mode (requires SUPPORT_API_KEY env var)").option("-e, --email <email>", "Email address (or ARBI_EMAIL env var)").option("-p, --password <password>", "Password (or ARBI_PASSWORD env var)").option("-c, --verification-code <code>", "Verification code (skip prompt)").option("--first-name <name>", "First name").option("--last-name <name>", "Last name").action(
|
|
4267
|
+
program2.command("register").description("Register a new ARBI account").option("--non-interactive", "CI/automation mode (requires SUPPORT_API_KEY env var)").option("-e, --email <email>", "Email address (or ARBI_EMAIL env var)").option("-p, --password <password>", "Password (or ARBI_PASSWORD env var)").option("-c, --verification-code <code>", "Verification code (skip prompt)").option("--first-name <name>", "First name").option("--last-name <name>", "Last name").option("-y, --yes", 'Skip the post-register "Log in now?" confirmation').option("--json", "Emit a single JSON object on success (scripting)").action(
|
|
4196
4268
|
(opts) => runAction(async () => {
|
|
4197
4269
|
const config = requireConfig();
|
|
4198
4270
|
if (opts.nonInteractive) {
|
|
@@ -4207,7 +4279,7 @@ async function smartRegister(config, opts) {
|
|
|
4207
4279
|
let email = opts.email || process.env.ARBI_EMAIL || await promptInput("Email");
|
|
4208
4280
|
if ((opts.email || process.env.ARBI_EMAIL) && !email.includes("@")) {
|
|
4209
4281
|
email = `${email}@${config.deploymentDomain}`;
|
|
4210
|
-
console.log(`Using email: ${email}`);
|
|
4282
|
+
if (!opts.json) console.log(`Using email: ${email}`);
|
|
4211
4283
|
}
|
|
4212
4284
|
const arbi = client.createArbiClient({
|
|
4213
4285
|
baseUrl: config.baseUrl,
|
|
@@ -4261,16 +4333,22 @@ async function smartRegister(config, opts) {
|
|
|
4261
4333
|
firstName,
|
|
4262
4334
|
lastName
|
|
4263
4335
|
});
|
|
4264
|
-
success(`
|
|
4336
|
+
if (!opts.json) success(`
|
|
4265
4337
|
Registered successfully as ${email}`);
|
|
4266
4338
|
} catch (err) {
|
|
4267
|
-
|
|
4339
|
+
if (opts.json) {
|
|
4340
|
+
console.log(JSON.stringify({ ok: false, stage: "register", error: formatCliError(err) }));
|
|
4341
|
+
} else {
|
|
4342
|
+
error(`Registration failed: ${formatCliError(err)}`);
|
|
4343
|
+
}
|
|
4268
4344
|
process.exit(1);
|
|
4269
4345
|
}
|
|
4270
4346
|
const allFlagsProvided = !!(opts.email || process.env.ARBI_EMAIL) && !!(opts.password || process.env.ARBI_PASSWORD) && !!opts.verificationCode;
|
|
4271
|
-
const doLogin = allFlagsProvided || await promptConfirm("Log in now?");
|
|
4347
|
+
const doLogin = allFlagsProvided || opts.yes || await promptConfirm("Log in now?");
|
|
4272
4348
|
if (doLogin) {
|
|
4273
|
-
await loginAfterRegister(config, email, pw);
|
|
4349
|
+
await loginAfterRegister(config, email, pw, { json: opts.json });
|
|
4350
|
+
} else if (opts.json) {
|
|
4351
|
+
console.log(JSON.stringify({ ok: true, email, server: config.baseUrl, logged_in: false }));
|
|
4274
4352
|
}
|
|
4275
4353
|
}
|
|
4276
4354
|
async function nonInteractiveRegister(config, opts) {
|
|
@@ -4283,12 +4361,18 @@ async function nonInteractiveRegister(config, opts) {
|
|
|
4283
4361
|
}
|
|
4284
4362
|
if (!email.includes("@")) {
|
|
4285
4363
|
email = `${email}@${config.deploymentDomain}`;
|
|
4286
|
-
console.log(`Using email: ${email}`);
|
|
4364
|
+
if (!opts.json) console.log(`Using email: ${email}`);
|
|
4287
4365
|
}
|
|
4288
4366
|
if (!password2) {
|
|
4289
4367
|
error("Password required. Use --password <password> or set ARBI_PASSWORD");
|
|
4290
4368
|
process.exit(1);
|
|
4291
4369
|
}
|
|
4370
|
+
if (supportApiKey && !opts.verificationCode && !looksLikeAgentEmail(email)) {
|
|
4371
|
+
const hint = `Email "${email}" does not start with "agent-". SUPPORT_API_KEY deployment keys only accept agent-*@${config.deploymentDomain} emails. If your key is a superuser key this may still succeed \u2014 proceeding...`;
|
|
4372
|
+
if (!opts.json) {
|
|
4373
|
+
console.error(`Warning: ${hint}`);
|
|
4374
|
+
}
|
|
4375
|
+
}
|
|
4292
4376
|
const arbi = client.createArbiClient({
|
|
4293
4377
|
baseUrl: config.baseUrl,
|
|
4294
4378
|
deploymentDomain: config.deploymentDomain,
|
|
@@ -4309,10 +4393,37 @@ async function nonInteractiveRegister(config, opts) {
|
|
|
4309
4393
|
body: { email }
|
|
4310
4394
|
});
|
|
4311
4395
|
if (verifyResponse.error) {
|
|
4312
|
-
|
|
4396
|
+
if (opts.json) {
|
|
4397
|
+
console.log(
|
|
4398
|
+
JSON.stringify({
|
|
4399
|
+
ok: false,
|
|
4400
|
+
stage: "verify-email",
|
|
4401
|
+
error: JSON.stringify(verifyResponse.error)
|
|
4402
|
+
})
|
|
4403
|
+
);
|
|
4404
|
+
} else {
|
|
4405
|
+
error(`verify-email failed: ${JSON.stringify(verifyResponse.error)}`);
|
|
4406
|
+
}
|
|
4407
|
+
process.exit(1);
|
|
4408
|
+
}
|
|
4409
|
+
try {
|
|
4410
|
+
verificationCode = await getVerificationCode(email, supportApiKey, config.deploymentDomain);
|
|
4411
|
+
} catch (err) {
|
|
4412
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
4413
|
+
if (opts.json) {
|
|
4414
|
+
console.log(
|
|
4415
|
+
JSON.stringify({
|
|
4416
|
+
ok: false,
|
|
4417
|
+
stage: "verify-ci",
|
|
4418
|
+
error: msg,
|
|
4419
|
+
kind: err instanceof RegisterHintError ? err.kind : void 0
|
|
4420
|
+
})
|
|
4421
|
+
);
|
|
4422
|
+
} else {
|
|
4423
|
+
error(`Error: ${msg}`);
|
|
4424
|
+
}
|
|
4313
4425
|
process.exit(1);
|
|
4314
4426
|
}
|
|
4315
|
-
verificationCode = await getVerificationCode(email, supportApiKey);
|
|
4316
4427
|
}
|
|
4317
4428
|
try {
|
|
4318
4429
|
await arbi.auth.register({
|
|
@@ -4322,60 +4433,93 @@ async function nonInteractiveRegister(config, opts) {
|
|
|
4322
4433
|
firstName: opts.firstName ?? "Test",
|
|
4323
4434
|
lastName: opts.lastName ?? "User"
|
|
4324
4435
|
});
|
|
4325
|
-
success(`Registered: ${email}`);
|
|
4436
|
+
if (!opts.json) success(`Registered: ${email}`);
|
|
4326
4437
|
} catch (err) {
|
|
4327
|
-
|
|
4438
|
+
if (opts.json) {
|
|
4439
|
+
console.log(JSON.stringify({ ok: false, stage: "register", error: formatCliError(err) }));
|
|
4440
|
+
} else {
|
|
4441
|
+
error(`Registration failed: ${formatCliError(err)}`);
|
|
4442
|
+
}
|
|
4328
4443
|
process.exit(1);
|
|
4329
4444
|
}
|
|
4330
|
-
await loginAfterRegister(config, email, password2);
|
|
4445
|
+
await loginAfterRegister(config, email, password2, { json: opts.json });
|
|
4331
4446
|
}
|
|
4332
|
-
async function loginAfterRegister(config, email, password2) {
|
|
4447
|
+
async function loginAfterRegister(config, email, password2, { json = false } = {}) {
|
|
4333
4448
|
try {
|
|
4334
4449
|
const { arbi, loginResult } = await sdk.performPasswordLogin(config, email, password2, store);
|
|
4335
|
-
success(`Logged in as ${email}`);
|
|
4450
|
+
if (!json) success(`Logged in as ${email}`);
|
|
4336
4451
|
const { data: workspaces3 } = await arbi.fetch.GET("/v1/user/workspaces");
|
|
4337
4452
|
const wsList = workspaces3 || [];
|
|
4338
4453
|
updateCompletionCache(wsList);
|
|
4339
|
-
const memberWorkspaces = wsList.filter((
|
|
4454
|
+
const memberWorkspaces = wsList.filter((ws) => ws.users?.some((u) => u.user.email === email));
|
|
4455
|
+
let selectedWorkspace;
|
|
4340
4456
|
if (memberWorkspaces.length === 0) {
|
|
4341
|
-
console.log("Creating your first workspace...");
|
|
4457
|
+
if (!json) console.log("Creating your first workspace...");
|
|
4342
4458
|
const userProjects = await sdk.projects.listProjects(arbi);
|
|
4343
4459
|
const defaultProjectExtId = userProjects[0]?.external_id;
|
|
4344
4460
|
if (!defaultProjectExtId) throw new Error("No projects found for user");
|
|
4345
4461
|
const encryptedKey = await sdk.generateNewWorkspaceKey(arbi, loginResult.serverSessionKey);
|
|
4346
|
-
const
|
|
4462
|
+
const ws = await sdk.workspaces.createWorkspace(
|
|
4347
4463
|
arbi,
|
|
4348
4464
|
"My First Workspace",
|
|
4349
4465
|
encryptedKey,
|
|
4350
4466
|
defaultProjectExtId
|
|
4351
4467
|
);
|
|
4352
|
-
updateConfig({ selectedWorkspaceId:
|
|
4353
|
-
|
|
4354
|
-
|
|
4355
|
-
}
|
|
4356
|
-
if (memberWorkspaces.length === 1) {
|
|
4468
|
+
updateConfig({ selectedWorkspaceId: ws.external_id });
|
|
4469
|
+
selectedWorkspace = { external_id: ws.external_id, name: ws.name };
|
|
4470
|
+
if (!json) success(`Workspace: ${ws.name} (${ref(ws.external_id)})`);
|
|
4471
|
+
} else if (memberWorkspaces.length === 1) {
|
|
4357
4472
|
updateConfig({ selectedWorkspaceId: memberWorkspaces[0].external_id });
|
|
4358
|
-
|
|
4359
|
-
|
|
4473
|
+
selectedWorkspace = {
|
|
4474
|
+
external_id: memberWorkspaces[0].external_id,
|
|
4475
|
+
name: memberWorkspaces[0].name
|
|
4476
|
+
};
|
|
4477
|
+
if (!json)
|
|
4478
|
+
success(`Workspace: ${memberWorkspaces[0].name} (${ref(memberWorkspaces[0].external_id)})`);
|
|
4479
|
+
} else if (json) {
|
|
4480
|
+
} else {
|
|
4481
|
+
const choices = sdk.formatWorkspaceChoices(memberWorkspaces);
|
|
4482
|
+
const selected = await promptSelect("Select workspace", choices);
|
|
4483
|
+
updateConfig({ selectedWorkspaceId: selected });
|
|
4484
|
+
const ws = memberWorkspaces.find((w) => w.external_id === selected);
|
|
4485
|
+
selectedWorkspace = { external_id: selected, name: ws.name };
|
|
4486
|
+
success(`Workspace: ${ws.name} (${ref(selected)})`);
|
|
4487
|
+
}
|
|
4488
|
+
if (json) {
|
|
4489
|
+
console.log(
|
|
4490
|
+
JSON.stringify({
|
|
4491
|
+
ok: true,
|
|
4492
|
+
email,
|
|
4493
|
+
server: config.baseUrl,
|
|
4494
|
+
logged_in: true,
|
|
4495
|
+
workspace: selectedWorkspace ?? null,
|
|
4496
|
+
workspaces_available: memberWorkspaces.length
|
|
4497
|
+
})
|
|
4498
|
+
);
|
|
4360
4499
|
}
|
|
4361
|
-
const choices = sdk.formatWorkspaceChoices(memberWorkspaces);
|
|
4362
|
-
const selected = await promptSelect("Select workspace", choices);
|
|
4363
|
-
updateConfig({ selectedWorkspaceId: selected });
|
|
4364
|
-
const ws = memberWorkspaces.find((w) => w.external_id === selected);
|
|
4365
|
-
success(`Workspace: ${ws.name} (${ref(selected)})`);
|
|
4366
4500
|
} catch (err) {
|
|
4367
|
-
|
|
4368
|
-
|
|
4501
|
+
if (json) {
|
|
4502
|
+
console.log(
|
|
4503
|
+
JSON.stringify({ ok: false, stage: "post-register-login", error: formatCliError(err) })
|
|
4504
|
+
);
|
|
4505
|
+
} else {
|
|
4506
|
+
error(`Login failed: ${formatCliError(err)}`);
|
|
4507
|
+
error("You can log in later with: arbi login");
|
|
4508
|
+
}
|
|
4369
4509
|
}
|
|
4370
4510
|
}
|
|
4371
4511
|
|
|
4372
4512
|
// src/commands/logout.ts
|
|
4373
4513
|
function registerLogoutCommand(program2) {
|
|
4374
|
-
program2.command("logout").description("Log out of ARBI").action(() => {
|
|
4514
|
+
program2.command("logout").description("Log out of ARBI").option("--json", "Emit a single JSON object on success (scripting)").action((opts) => {
|
|
4375
4515
|
deleteCredentials();
|
|
4376
4516
|
clearChatSession();
|
|
4377
4517
|
updateConfig({ selectedWorkspaceId: void 0 });
|
|
4378
|
-
|
|
4518
|
+
if (opts.json) {
|
|
4519
|
+
console.log(JSON.stringify({ ok: true }));
|
|
4520
|
+
} else {
|
|
4521
|
+
success("Logged out.");
|
|
4522
|
+
}
|
|
4379
4523
|
});
|
|
4380
4524
|
}
|
|
4381
4525
|
|
|
@@ -4417,12 +4561,49 @@ function registerStatusCommand(program2) {
|
|
|
4417
4561
|
}
|
|
4418
4562
|
});
|
|
4419
4563
|
}
|
|
4564
|
+
function resolveWorkspaceSelector(list, selector) {
|
|
4565
|
+
const byId = list.find((w) => w.external_id === selector);
|
|
4566
|
+
if (byId) return { ok: true, id: byId.external_id, ws: byId };
|
|
4567
|
+
const lower = selector.toLowerCase();
|
|
4568
|
+
const byName = list.filter((w) => (w.name ?? "").toLowerCase() === lower);
|
|
4569
|
+
if (byName.length === 1) return { ok: true, id: byName[0].external_id, ws: byName[0] };
|
|
4570
|
+
if (byName.length > 1) return { ok: false, reason: "ambiguous", matches: byName };
|
|
4571
|
+
return { ok: false, reason: "not-found" };
|
|
4572
|
+
}
|
|
4573
|
+
function printResolveError(list, selector, res) {
|
|
4574
|
+
if (res.ok) return;
|
|
4575
|
+
if (res.reason === "ambiguous") {
|
|
4576
|
+
error(`Workspace name "${selector}" is ambiguous \u2014 multiple matches:`);
|
|
4577
|
+
for (const w of res.matches ?? []) error(` ${w.external_id} ${w.name}`);
|
|
4578
|
+
error("Use the workspace ID instead.");
|
|
4579
|
+
return;
|
|
4580
|
+
}
|
|
4581
|
+
error(`Workspace ${selector} not found.`);
|
|
4582
|
+
error("Available workspaces:");
|
|
4583
|
+
for (const w of list) error(` ${w.external_id} ${w.name}`);
|
|
4584
|
+
}
|
|
4420
4585
|
function registerWorkspacesCommand(program2) {
|
|
4421
|
-
program2.command("workspaces").description("List workspaces").action(
|
|
4422
|
-
runAction(async () => {
|
|
4586
|
+
program2.command("workspaces").description("List workspaces").option("--json", "Output as JSON").option("--ids", "Output only workspace IDs (one per line)").action(
|
|
4587
|
+
(opts) => runAction(async () => {
|
|
4423
4588
|
const { arbi } = await resolveAuth();
|
|
4424
4589
|
const data = await sdk.workspaces.listWorkspaces(arbi);
|
|
4425
4590
|
updateCompletionCache(data);
|
|
4591
|
+
if (opts.ids) {
|
|
4592
|
+
for (const w of data) console.log(w.external_id);
|
|
4593
|
+
return;
|
|
4594
|
+
}
|
|
4595
|
+
if (opts.json) {
|
|
4596
|
+
const selectedId = getConfig()?.selectedWorkspaceId ?? null;
|
|
4597
|
+
const out = data.map((w) => ({
|
|
4598
|
+
id: w.external_id,
|
|
4599
|
+
name: w.name,
|
|
4600
|
+
docs: w.shared_document_count + w.private_document_count,
|
|
4601
|
+
role: w.users?.[0]?.role ?? null,
|
|
4602
|
+
is_selected: w.external_id === selectedId
|
|
4603
|
+
}));
|
|
4604
|
+
console.log(JSON.stringify(out, null, 2));
|
|
4605
|
+
return;
|
|
4606
|
+
}
|
|
4426
4607
|
if (data.length === 0) {
|
|
4427
4608
|
console.log("No workspaces found.");
|
|
4428
4609
|
return;
|
|
@@ -4444,11 +4625,51 @@ function registerWorkspacesCommand(program2) {
|
|
|
4444
4625
|
],
|
|
4445
4626
|
data
|
|
4446
4627
|
);
|
|
4447
|
-
})
|
|
4628
|
+
})()
|
|
4448
4629
|
);
|
|
4449
4630
|
const workspace = program2.command("workspace").description("Workspace management");
|
|
4450
|
-
workspace.command("
|
|
4451
|
-
(
|
|
4631
|
+
workspace.command("current").description("Print the currently selected workspace ID (empty string if none)").option("--json", "Output full details as JSON").action(
|
|
4632
|
+
(opts) => runAction(async () => {
|
|
4633
|
+
const selectedId = getConfig()?.selectedWorkspaceId ?? "";
|
|
4634
|
+
if (!opts.json) {
|
|
4635
|
+
console.log(selectedId);
|
|
4636
|
+
return;
|
|
4637
|
+
}
|
|
4638
|
+
if (!selectedId) {
|
|
4639
|
+
console.log(JSON.stringify({ id: null, name: null, docs: 0, role: null }, null, 2));
|
|
4640
|
+
return;
|
|
4641
|
+
}
|
|
4642
|
+
const { arbi } = await resolveAuth();
|
|
4643
|
+
const data = await sdk.workspaces.listWorkspaces(arbi);
|
|
4644
|
+
const ws = data.find((w) => w.external_id === selectedId);
|
|
4645
|
+
if (!ws) {
|
|
4646
|
+
console.log(
|
|
4647
|
+
JSON.stringify(
|
|
4648
|
+
{ id: selectedId, name: null, docs: 0, role: null, stale: true },
|
|
4649
|
+
null,
|
|
4650
|
+
2
|
|
4651
|
+
)
|
|
4652
|
+
);
|
|
4653
|
+
return;
|
|
4654
|
+
}
|
|
4655
|
+
console.log(
|
|
4656
|
+
JSON.stringify(
|
|
4657
|
+
{
|
|
4658
|
+
id: ws.external_id,
|
|
4659
|
+
name: ws.name,
|
|
4660
|
+
docs: ws.shared_document_count + ws.private_document_count,
|
|
4661
|
+
role: ws.users?.[0]?.role ?? null
|
|
4662
|
+
},
|
|
4663
|
+
null,
|
|
4664
|
+
2
|
|
4665
|
+
)
|
|
4666
|
+
);
|
|
4667
|
+
})()
|
|
4668
|
+
);
|
|
4669
|
+
workspace.command("select [id-or-name]").description(
|
|
4670
|
+
"Select active workspace (accepts ID or unique name; interactive picker if nothing given)"
|
|
4671
|
+
).action(
|
|
4672
|
+
(idOrName) => runAction(async () => {
|
|
4452
4673
|
const { arbi } = await resolveAuth();
|
|
4453
4674
|
const data = await sdk.workspaces.listWorkspaces(arbi);
|
|
4454
4675
|
updateCompletionCache(data);
|
|
@@ -4457,15 +4678,13 @@ function registerWorkspacesCommand(program2) {
|
|
|
4457
4678
|
return;
|
|
4458
4679
|
}
|
|
4459
4680
|
let selectedId;
|
|
4460
|
-
if (
|
|
4461
|
-
const
|
|
4462
|
-
if (!
|
|
4463
|
-
|
|
4464
|
-
error("Available workspaces:");
|
|
4465
|
-
for (const w of data) error(` ${w.external_id} ${w.name}`);
|
|
4681
|
+
if (idOrName) {
|
|
4682
|
+
const res = resolveWorkspaceSelector(data, idOrName);
|
|
4683
|
+
if (!res.ok) {
|
|
4684
|
+
printResolveError(data, idOrName, res);
|
|
4466
4685
|
process.exit(1);
|
|
4467
4686
|
}
|
|
4468
|
-
selectedId = id;
|
|
4687
|
+
selectedId = res.id;
|
|
4469
4688
|
} else {
|
|
4470
4689
|
const choices = sdk.formatWorkspaceChoices(data);
|
|
4471
4690
|
selectedId = await promptSelect("Select workspace", choices);
|
|
@@ -4476,7 +4695,7 @@ function registerWorkspacesCommand(program2) {
|
|
|
4476
4695
|
success(`Selected: ${ws.name} (${ref(selectedId)})`);
|
|
4477
4696
|
})()
|
|
4478
4697
|
);
|
|
4479
|
-
workspace.command("create <name>").description("Create a new workspace").option("-d, --description <text>", "Workspace description").option("--public", "Make workspace public", false).action(
|
|
4698
|
+
workspace.command("create <name>").description("Create a new workspace").option("-d, --description <text>", "Workspace description").option("--public", "Make workspace public", false).option("--select", "Set the new workspace as the active selection", false).option("--json", "Output the new workspace as JSON").action(
|
|
4480
4699
|
(name, opts) => runAction(async () => {
|
|
4481
4700
|
const { arbi, loginResult } = await resolveAuth();
|
|
4482
4701
|
const userProjects = await sdk.projects.listProjects(arbi);
|
|
@@ -4491,37 +4710,114 @@ function registerWorkspacesCommand(program2) {
|
|
|
4491
4710
|
opts.description,
|
|
4492
4711
|
opts.public ?? false
|
|
4493
4712
|
);
|
|
4713
|
+
if (opts.select) {
|
|
4714
|
+
updateConfig({ selectedWorkspaceId: data.external_id });
|
|
4715
|
+
clearChatSession();
|
|
4716
|
+
}
|
|
4717
|
+
if (opts.json) {
|
|
4718
|
+
console.log(
|
|
4719
|
+
JSON.stringify(
|
|
4720
|
+
{
|
|
4721
|
+
id: data.external_id,
|
|
4722
|
+
name: data.name,
|
|
4723
|
+
selected: opts.select ?? false
|
|
4724
|
+
},
|
|
4725
|
+
null,
|
|
4726
|
+
2
|
|
4727
|
+
)
|
|
4728
|
+
);
|
|
4729
|
+
return;
|
|
4730
|
+
}
|
|
4494
4731
|
success(`Created: ${data.name} (${ref(data.external_id)})`);
|
|
4732
|
+
if (opts.select) success(`Selected: ${data.name} (${ref(data.external_id)})`);
|
|
4495
4733
|
})()
|
|
4496
4734
|
);
|
|
4497
|
-
workspace.command("delete [id]").description("Delete a workspace (defaults to selected workspace)").action(
|
|
4498
|
-
(
|
|
4735
|
+
workspace.command("delete [id-or-name]").description("Delete a workspace (defaults to selected workspace)").option("-y, --yes", "Skip confirmation prompt (required in non-interactive shells)", false).option("--json", "Output the result as JSON").action(
|
|
4736
|
+
(idOrName, opts) => runAction(async () => {
|
|
4499
4737
|
const { arbi } = await resolveAuth();
|
|
4500
|
-
const
|
|
4501
|
-
|
|
4502
|
-
|
|
4503
|
-
|
|
4738
|
+
const config = getConfig();
|
|
4739
|
+
let targetId;
|
|
4740
|
+
let targetName;
|
|
4741
|
+
if (idOrName) {
|
|
4742
|
+
const data = await sdk.workspaces.listWorkspaces(arbi);
|
|
4743
|
+
const res = resolveWorkspaceSelector(data, idOrName);
|
|
4744
|
+
if (!res.ok) {
|
|
4745
|
+
printResolveError(data, idOrName, res);
|
|
4746
|
+
process.exit(1);
|
|
4747
|
+
}
|
|
4748
|
+
targetId = res.id;
|
|
4749
|
+
targetName = res.ws.name;
|
|
4750
|
+
} else {
|
|
4751
|
+
targetId = config?.selectedWorkspaceId;
|
|
4752
|
+
if (!targetId) {
|
|
4753
|
+
error("No workspace ID given and no workspace selected. Run: arbi workspace select");
|
|
4754
|
+
process.exit(1);
|
|
4755
|
+
}
|
|
4756
|
+
}
|
|
4757
|
+
const isInteractive = process.stdin.isTTY === true && process.stdout.isTTY === true;
|
|
4758
|
+
if (!opts.yes) {
|
|
4759
|
+
if (!isInteractive) {
|
|
4760
|
+
error(
|
|
4761
|
+
`Refusing to delete ${targetId} without confirmation. Re-run with --yes (non-interactive shell).`
|
|
4762
|
+
);
|
|
4763
|
+
process.exit(1);
|
|
4764
|
+
}
|
|
4765
|
+
const label2 = targetName ? `"${targetName}" (${targetId})` : targetId;
|
|
4766
|
+
const confirmed = await promptConfirm(
|
|
4767
|
+
`Delete workspace ${label2}? This removes all documents, conversations, and tags in it.`,
|
|
4768
|
+
false
|
|
4769
|
+
);
|
|
4770
|
+
if (!confirmed) {
|
|
4771
|
+
console.log("Cancelled.");
|
|
4772
|
+
return;
|
|
4773
|
+
}
|
|
4504
4774
|
}
|
|
4505
4775
|
await sdk.workspaces.deleteWorkspaces(arbi, [targetId]);
|
|
4776
|
+
if (config?.selectedWorkspaceId === targetId) {
|
|
4777
|
+
updateConfig({ selectedWorkspaceId: void 0 });
|
|
4778
|
+
}
|
|
4506
4779
|
const session = getChatSession();
|
|
4507
4780
|
if (session.workspaceId === targetId) {
|
|
4508
4781
|
clearChatSession();
|
|
4509
4782
|
}
|
|
4783
|
+
if (opts.json) {
|
|
4784
|
+
console.log(JSON.stringify({ id: targetId, deleted: true }, null, 2));
|
|
4785
|
+
return;
|
|
4786
|
+
}
|
|
4510
4787
|
success(`Deleted workspace ${targetId}`);
|
|
4511
4788
|
})()
|
|
4512
4789
|
);
|
|
4513
|
-
workspace.command("update <json>").description("Update workspace properties (pass JSON)").option("-w, --workspace <id>", "Workspace ID (defaults to selected workspace)").action(
|
|
4790
|
+
workspace.command("update <json>").description("Update workspace properties (pass JSON)").option("-w, --workspace <id>", "Workspace ID (defaults to selected workspace)").option("--json", "Output the updated workspace as JSON").action(
|
|
4514
4791
|
(json, opts) => runAction(async () => {
|
|
4515
4792
|
const body = parseJsonArg(json, `arbi workspace update '{"name": "New Name"}'`);
|
|
4516
4793
|
const { arbi } = await resolveWorkspace(opts.workspace);
|
|
4517
4794
|
const data = await sdk.workspaces.updateWorkspace(arbi, body);
|
|
4795
|
+
if (opts.json) {
|
|
4796
|
+
console.log(JSON.stringify({ id: data.external_id, name: data.name }, null, 2));
|
|
4797
|
+
return;
|
|
4798
|
+
}
|
|
4518
4799
|
success(`Updated: ${data.name} (${ref(data.external_id)})`);
|
|
4519
4800
|
})()
|
|
4520
4801
|
);
|
|
4521
|
-
workspace.command("users").description("List users in the active workspace").option("-w, --workspace <id>", "Workspace ID (defaults to selected workspace)").action(
|
|
4802
|
+
workspace.command("users").description("List users in the active workspace").option("-w, --workspace <id>", "Workspace ID (defaults to selected workspace)").option("--json", "Output as JSON").action(
|
|
4522
4803
|
(opts) => runAction(async () => {
|
|
4523
4804
|
const { arbi } = await resolveWorkspace(opts.workspace);
|
|
4524
4805
|
const data = await sdk.workspaces.listWorkspaceUsers(arbi);
|
|
4806
|
+
if (opts.json) {
|
|
4807
|
+
const out = data.map((r) => {
|
|
4808
|
+
const u = r.user ?? {};
|
|
4809
|
+
return {
|
|
4810
|
+
user_id: u.external_id ?? null,
|
|
4811
|
+
email: u.email ?? null,
|
|
4812
|
+
name: [u.given_name, u.family_name].filter(Boolean).join(" ") || null,
|
|
4813
|
+
role: r.role,
|
|
4814
|
+
document_count: r.document_count,
|
|
4815
|
+
conversation_count: r.conversation_count
|
|
4816
|
+
};
|
|
4817
|
+
});
|
|
4818
|
+
console.log(JSON.stringify(out, null, 2));
|
|
4819
|
+
return;
|
|
4820
|
+
}
|
|
4525
4821
|
if (data.length === 0) {
|
|
4526
4822
|
console.log("No users found.");
|
|
4527
4823
|
return;
|
|
@@ -4598,7 +4894,18 @@ function registerWorkspacesCommand(program2) {
|
|
|
4598
4894
|
signingPrivateKeyBase64
|
|
4599
4895
|
);
|
|
4600
4896
|
const data = await sdk.workspaces.copyDocuments(arbi, targetId, docIds, targetKey);
|
|
4601
|
-
|
|
4897
|
+
const copied = data.documents_copied ?? 0;
|
|
4898
|
+
const requested = docIds.length;
|
|
4899
|
+
if (copied === 0) {
|
|
4900
|
+
error(`Copied 0 of ${requested} document(s). ${data.detail ?? ""}`.trim());
|
|
4901
|
+
error("Hint: documents must be fully indexed before they can be copied.");
|
|
4902
|
+
process.exit(1);
|
|
4903
|
+
}
|
|
4904
|
+
if (copied < requested) {
|
|
4905
|
+
error(`Copied ${copied} of ${requested} document(s). ${data.detail ?? ""}`.trim());
|
|
4906
|
+
return;
|
|
4907
|
+
}
|
|
4908
|
+
success(`Copied ${copied} document(s) to ${targetId}`);
|
|
4602
4909
|
})()
|
|
4603
4910
|
);
|
|
4604
4911
|
}
|
|
@@ -8299,7 +8606,7 @@ console.info = (...args) => {
|
|
|
8299
8606
|
_origInfo(...args);
|
|
8300
8607
|
};
|
|
8301
8608
|
var program = new commander.Command();
|
|
8302
|
-
program.name("arbi").description("ARBI CLI \u2014 interact with ARBI from the terminal").version("0.3.
|
|
8609
|
+
program.name("arbi").description("ARBI CLI \u2014 interact with ARBI from the terminal").version("0.3.43");
|
|
8303
8610
|
registerConfigCommand(program);
|
|
8304
8611
|
registerLoginCommand(program);
|
|
8305
8612
|
registerRegisterCommand(program);
|