@arbidocs/cli 0.3.39 โ†’ 0.3.40

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 CHANGED
@@ -1,5 +1,13 @@
1
1
  # Changelog
2
2
 
3
+ ## v0.3.40
4
+
5
+ [compare changes](https://github.com/arbicity/ARBI-frontend/compare/v0.3.39...HEAD)
6
+
7
+ ### ๐Ÿš€ Enhancements
8
+
9
+ - **cli:** Add --json to list commands + agent-aware not-logged-in hint ([#629](https://github.com/arbicity/ARBI-frontend/pull/629))
10
+
3
11
  ## v0.3.39
4
12
 
5
13
  [compare changes](https://github.com/arbicity/ARBI-frontend/compare/v0.3.38...HEAD)
package/SKILL.md CHANGED
@@ -6,15 +6,18 @@ ARBI is an enterprise document intelligence platform with E2E encrypted communic
6
6
 
7
7
  1. Install: `npm install -g @arbidocs/cli`
8
8
  2. Configure server: `arbi config set-url https://<deployment>.arbibox.com`
9
- 3. Get a claim code from your parent user (via ARBI web UI โ†’ Settings โ†’ Agents โ†’ Authorize, or `arbi authorize agent`)
10
- 4. Connect: `arbi connect --code <claim-code>`
11
- 5. Verify: `arbi status`
9
+ 3. Log in (pick one):
10
+ - Interactive: `arbi quickstart` (wizard) or `arbi login -e <email> -p <password>`
11
+ - Scripted (humans with API access): `arbi register -e <email> -p <password> --verification-code <code>`
12
+ - Scripted (AI agents / bots, requires `SUPPORT_API_KEY`): `arbi agent-create --email agent-<name>@<domain> --workspace-name "<name>"`
13
+ - The email MUST start with `agent-`. This path creates the account, logs in, and selects the new workspace in one call.
14
+ 4. Verify: `arbi status` (add `--json` for machine-readable output)
12
15
 
13
- For multiple agents on the same machine, set `ARBI_ID=<name>` to isolate config in `~/.arbi/<name>/`.
16
+ For multiple agents on the same machine, set `ARBI_ID=<name>` (or `ARBI_CONFIG_DIR=<path>`) to isolate config in `~/.arbi/<name>/`.
14
17
 
15
18
  ## When to Use
16
19
 
17
- - **Document questions**: `arbi ask <question>` โ€” performs RAG retrieval across workspace documents
20
+ - **Document questions**: `arbi ask <question>` โ€” performs RAG retrieval across workspace documents (no quotes needed around the question)
18
21
  - **Document management**: `arbi docs`, `arbi upload`, `arbi download`, `arbi find`
19
22
  - **Local files**: `arbi local ls`, `arbi local find "**/*.pdf"`, `arbi local cat <file>`, `arbi local tree`
20
23
  - **Secure messaging**: `arbi dm send <recipient> <message>` โ€” E2E encrypted
@@ -22,9 +25,54 @@ For multiple agents on the same machine, set `ARBI_ID=<name>` to isolate config
22
25
 
23
26
  Every command supports `--help`. Explore with `arbi --help` to discover all capabilities.
24
27
 
28
+ ## Workspaces
29
+
30
+ Documents, conversations, and tags are scoped per workspace. An active workspace is stored in the CLI config and used by `arbi docs`, `arbi upload`, `arbi ask`, etc.
31
+
32
+ Common moves:
33
+
34
+ - List: `arbi workspaces` (human) ยท `arbi workspaces --json` (script) ยท `arbi workspaces --ids`
35
+ - Active workspace ID: `arbi workspace current` (prints the ID, empty string if none)
36
+ - Pick/switch: `arbi workspace select <id>` โ€” clears the chat session; uploaded docs in other workspaces are unaffected
37
+ - Create: `arbi workspace create "My WS"` โ€” does NOT auto-select, follow with `workspace select`
38
+ - Rename / edit: `arbi workspace update '{"name":"New Name"}'`
39
+ - Delete: `arbi workspace delete [id]` โ€” deletes immediately (no confirmation prompt); if you delete the active workspace, the selection is cleared
40
+ - Share: `arbi workspace add-user <email> -r <role>` where role is `owner|collaborator|guest` (may be plan-limited)
41
+ - List members: `arbi workspace users [--json]`
42
+ - Remove member: `arbi workspace remove-user <user-id>`
43
+ - Copy docs between workspaces: `arbi workspace copy <target-ws-id> <doc-id>...` โ€” only copies documents that have finished indexing; partial/failed copies exit non-zero
44
+
45
+ Scripting notes:
46
+
47
+ - The table output of `arbi workspaces` is NOT reliably parseable (names contain spaces, status cells may contain glyphs). Use `--json` or `--ids`.
48
+ - Sessions persist between CLI invocations via the config file (default `~/.arbi/`, override with `ARBI_CONFIG_DIR`).
49
+ - Per-agent isolation: run agents with their own `ARBI_CONFIG_DIR=<path>` (or `ARBI_ID=<name>`) to avoid stomping each other's active workspace.
50
+
25
51
  ## Tips
26
52
 
27
- - If not logged in, run `arbi connect --code <code>` with a new claim code from your parent user.
53
+ - Not logged in: run `arbi login` (interactive) or, with `SUPPORT_API_KEY` set, `arbi agent-create --email agent-<name>@<domain>`.
28
54
  - If a command fails with "fetch failed", check connectivity with `arbi health`.
29
55
  - Use `arbi ask -n` to start a fresh conversation (forget prior context).
30
56
  - Use `arbi ask -v` for verbose output showing retrieval steps.
57
+ - Use `arbi status --json` and `arbi workspace current` to read CLI state from scripts.
58
+
59
+ ## For Automations / AI Agents
60
+
61
+ - **Structured output (`--json`)**: supported on `status`, `config show`, `health`, `models`, `workspaces`, `workspace users`, `docs`, `conversations` (or `conversations list`), `tags` (or `tags list`), `contacts` (or `contacts list`), `find`, `settings get`, `task list`, `ask -b`, `upload`, `watch`. Use it whenever parsing output. `docs` also supports `--csv`, `--ids`, and `--count`.
62
+ - **Human table output is lossy**: table rows may contain status glyphs (`โ—`, `๐ŸŸฉ`, etc.) and emoji, and column widths truncate content. Never parse tables โ€” always pass `--json`.
63
+ - **Exit codes**:
64
+ - `0` success
65
+ - `1` failure (bad args / auth / API / network / server error)
66
+ - `2` `arbi health --json` when the server reports unhealthy (the JSON body is still printed so you can introspect)
67
+ - `130` user-cancelled interactive prompt (Ctrl+C in `quickstart`)
68
+ - Always check `$?` โ€” `arbi ask` while logged-out, typo commands, and network failures all return non-zero.
69
+ - **Avoid interactive prompts** โ€” pass everything as flags/env vars:
70
+ - `arbi login -e $ARBI_EMAIL -p $ARBI_PASSWORD -w <workspace-id>` (or set `ARBI_EMAIL`/`ARBI_PASSWORD`).
71
+ - `arbi agent-create --email agent-<...> --workspace-name "<name>"` (requires `SUPPORT_API_KEY`).
72
+ - `arbi workspace select <id>` (ID directly, no picker).
73
+ - `arbi docs` auto-uses the selected workspace; pass `-w <id>` to override.
74
+ - Do **not** run `arbi quickstart` in non-interactive contexts โ€” it is interactive-only.
75
+ - **Health probe**: `arbi health --json` returns the raw payload and exits `2` if the server is not healthy โ€” useful for pre-flight checks.
76
+ - **No color leakage**: chalk auto-disables ANSI codes when stdout is not a TTY, so `arbi docs --json | jq`, `arbi workspaces --json | jq`, etc. work cleanly. Status messages and spinners go to stderr; stdout is safe to pipe.
77
+ - **Typo suggestions**: `arbi updat` โ†’ `error: unknown command 'updat' (Did you mean update?)` (exit 1). Works for top-level commands and nested subcommands.
78
+ - **Completion**: `arbi completion install` (no `bash`/`zsh` arg โ€” auto-detected from `$SHELL`).
package/dist/index.js CHANGED
@@ -216,8 +216,12 @@ function registerConfigCommand(program2) {
216
216
  process.exit(1);
217
217
  }
218
218
  });
219
- config.command("show").description("Show current configuration").action(() => {
219
+ config.command("show").description("Show current configuration").option("--json", "Output as JSON").action((opts) => {
220
220
  const cfg = getConfig();
221
+ if (opts.json) {
222
+ console.log(JSON.stringify(cfg ?? {}, null, 2));
223
+ return;
224
+ }
221
225
  if (!cfg) {
222
226
  dim("Not configured. Run: arbi config set-url <url>");
223
227
  return;
@@ -3637,7 +3641,7 @@ function getLatestVersion(skipCache = false) {
3637
3641
  }
3638
3642
  }
3639
3643
  function getCurrentVersion() {
3640
- return "0.3.39";
3644
+ return "0.3.40";
3641
3645
  }
3642
3646
  function readChangelog(fromVersion, toVersion) {
3643
3647
  try {
@@ -3690,17 +3694,17 @@ function showChangelog(fromVersion, toVersion) {
3690
3694
  async function checkForUpdates(autoUpdate) {
3691
3695
  try {
3692
3696
  const latest = getLatestVersion();
3693
- if (!latest || latest === "0.3.39") return;
3697
+ if (!latest || latest === "0.3.40") return;
3694
3698
  if (autoUpdate) {
3695
3699
  warn(`
3696
- Your arbi version is out of date (${"0.3.39"} \u2192 ${latest}). Updating...`);
3700
+ Your arbi version is out of date (${"0.3.40"} \u2192 ${latest}). Updating...`);
3697
3701
  child_process.execSync("npm install -g @arbidocs/cli@latest", { stdio: "inherit" });
3698
- showChangelog("0.3.39", latest);
3702
+ showChangelog("0.3.40", latest);
3699
3703
  console.log(`Updated to ${latest}.`);
3700
3704
  } else {
3701
3705
  warn(
3702
3706
  `
3703
- Your arbi version is out of date (${"0.3.39"} \u2192 ${latest}).
3707
+ Your arbi version is out of date (${"0.3.40"} \u2192 ${latest}).
3704
3708
  Run "arbi update" to upgrade, or "arbi update auto" to always stay up to date.`
3705
3709
  );
3706
3710
  }
@@ -3710,9 +3714,9 @@ Run "arbi update" to upgrade, or "arbi update auto" to always stay up to date.`
3710
3714
  function hintUpdateOnError() {
3711
3715
  try {
3712
3716
  const cached = readCache();
3713
- if (cached && cached.latest !== "0.3.39") {
3717
+ if (cached && cached.latest !== "0.3.40") {
3714
3718
  warn(
3715
- `Your arbi version is out of date (${"0.3.39"} \u2192 ${cached.latest}). Run "arbi update".`
3719
+ `Your arbi version is out of date (${"0.3.40"} \u2192 ${cached.latest}). Run "arbi update".`
3716
3720
  );
3717
3721
  }
3718
3722
  } catch {
@@ -4377,9 +4381,25 @@ function registerLogoutCommand(program2) {
4377
4381
 
4378
4382
  // src/commands/status.ts
4379
4383
  function registerStatusCommand(program2) {
4380
- program2.command("status").description("Show current configuration and login status").action(() => {
4384
+ program2.command("status").description("Show current configuration and login status").option("--json", "Output as JSON for scripting").action((opts) => {
4381
4385
  const config = getConfig();
4382
4386
  const creds = getCredentials();
4387
+ if (opts.json) {
4388
+ console.log(
4389
+ JSON.stringify(
4390
+ {
4391
+ configured: !!config?.baseUrl,
4392
+ server: config?.baseUrl ?? null,
4393
+ logged_in: !!creds,
4394
+ user: creds?.email ?? null,
4395
+ workspace: config?.selectedWorkspaceId ?? null
4396
+ },
4397
+ null,
4398
+ 2
4399
+ )
4400
+ );
4401
+ return;
4402
+ }
4383
4403
  if (!config?.baseUrl) {
4384
4404
  dim("Not configured. Run: arbi config set-url <url>");
4385
4405
  return;
@@ -6615,10 +6635,14 @@ Timeout (${timeoutSec}s), closing.`));
6615
6635
  }
6616
6636
  function registerContactsCommand(program2) {
6617
6637
  const contacts = program2.command("contacts").description("Manage contacts");
6618
- contacts.command("list").description("List all contacts").action(
6619
- runAction(async () => {
6638
+ contacts.command("list").description("List all contacts").option("--json", "Output as JSON").action(
6639
+ (opts) => runAction(async () => {
6620
6640
  const { arbi } = await resolveAuth();
6621
6641
  const data = await sdk.contacts.listContacts(arbi);
6642
+ if (opts.json) {
6643
+ console.log(JSON.stringify(data, null, 2));
6644
+ return;
6645
+ }
6622
6646
  if (data.length === 0) {
6623
6647
  console.log("No contacts found.");
6624
6648
  return;
@@ -6636,7 +6660,7 @@ function registerContactsCommand(program2) {
6636
6660
  ],
6637
6661
  data
6638
6662
  );
6639
- })
6663
+ })()
6640
6664
  );
6641
6665
  contacts.command("add [emails...]").description("Add contacts by email (prompt if no emails given)").action(
6642
6666
  (emails) => runAction(async () => {
@@ -6678,8 +6702,10 @@ function registerContactsCommand(program2) {
6678
6702
  success(`Removed ${contactIds.length} contact(s).`);
6679
6703
  })()
6680
6704
  );
6681
- contacts.action(async () => {
6682
- await contacts.commands.find((c) => c.name() === "list").parseAsync([], { from: "user" });
6705
+ contacts.option("--json", "Output as JSON").action(async (opts) => {
6706
+ const args = [];
6707
+ if (opts.json) args.push("--json");
6708
+ await contacts.commands.find((c) => c.name() === "list").parseAsync(args, { from: "user" });
6683
6709
  });
6684
6710
  }
6685
6711
  function registerDmCommand(program2) {
@@ -6901,10 +6927,14 @@ async function fetchTagChoices(arbi) {
6901
6927
  }
6902
6928
  function registerTagsCommand(program2) {
6903
6929
  const tags = program2.command("tags").description("Manage tags");
6904
- tags.command("list").description("List tags in the active workspace").option("-w, --workspace <id>", "Workspace ID (defaults to selected workspace)").action(
6930
+ tags.command("list").description("List tags in the active workspace").option("-w, --workspace <id>", "Workspace ID (defaults to selected workspace)").option("--json", "Output as JSON").action(
6905
6931
  (opts) => runAction(async () => {
6906
6932
  const { arbi } = await resolveWorkspace(opts.workspace);
6907
6933
  const data = await sdk.tags.listTags(arbi);
6934
+ if (opts.json) {
6935
+ console.log(JSON.stringify(data, null, 2));
6936
+ return;
6937
+ }
6908
6938
  if (data.length === 0) {
6909
6939
  console.log("No tags found.");
6910
6940
  return;
@@ -6997,8 +7027,11 @@ function registerTagsCommand(program2) {
6997
7027
  success(`Updated: ${data.name} (${ref(data.external_id)})`);
6998
7028
  })()
6999
7029
  );
7000
- tags.action(async (_opts, cmd) => {
7001
- await cmd.commands.find((c) => c.name() === "list").parseAsync([], { from: "user" });
7030
+ tags.option("--json", "Output as JSON").option("-w, --workspace <id>", "Workspace ID (defaults to selected workspace)").action(async (opts, cmd) => {
7031
+ const args = [];
7032
+ if (opts.json) args.push("--json");
7033
+ if (opts.workspace) args.push("-w", opts.workspace);
7034
+ await cmd.commands.find((c) => c.name() === "list").parseAsync(args, { from: "user" });
7002
7035
  });
7003
7036
  }
7004
7037
  async function pickTag(arbi, workspaceId, message = "Select tag") {
@@ -7106,10 +7139,14 @@ async function pickConversation(arbi, workspaceId, message = "Select conversatio
7106
7139
  }
7107
7140
  function registerConversationsCommand(program2) {
7108
7141
  const conv = program2.command("conversations").description("Manage conversations");
7109
- conv.command("list").description("List conversations in the active workspace").option("-w, --workspace <id>", "Workspace ID (defaults to selected workspace)").action(
7142
+ conv.command("list").description("List conversations in the active workspace").option("-w, --workspace <id>", "Workspace ID (defaults to selected workspace)").option("--json", "Output as JSON").action(
7110
7143
  (opts) => runAction(async () => {
7111
7144
  const { arbi } = await resolveWorkspace(opts.workspace);
7112
7145
  const data = await sdk.conversations.listConversations(arbi);
7146
+ if (opts.json) {
7147
+ console.log(JSON.stringify(data, null, 2));
7148
+ return;
7149
+ }
7113
7150
  if (data.length === 0) {
7114
7151
  console.log("No conversations found.");
7115
7152
  return;
@@ -7178,18 +7215,21 @@ function registerConversationsCommand(program2) {
7178
7215
  success(data?.detail ?? `Deleted message ${messageId}`);
7179
7216
  })()
7180
7217
  );
7181
- conv.action(async (_opts, cmd) => {
7182
- await cmd.commands.find((c) => c.name() === "list").parseAsync([], { from: "user" });
7218
+ conv.option("--json", "Output as JSON").option("-w, --workspace <id>", "Workspace ID (defaults to selected workspace)").action(async (opts, cmd) => {
7219
+ const args = [];
7220
+ if (opts.json) args.push("--json");
7221
+ if (opts.workspace) args.push("-w", opts.workspace);
7222
+ await cmd.commands.find((c) => c.name() === "list").parseAsync(args, { from: "user" });
7183
7223
  });
7184
7224
  }
7185
7225
  function registerSettingsCommand(program2) {
7186
7226
  const settings = program2.command("settings").description("Manage user settings");
7187
- settings.command("get").description("Show current user settings").action(
7188
- runAction(async () => {
7227
+ settings.command("get").description("Show current user settings (always JSON; --json accepted for consistency)").option("--json", "Output as JSON (default \u2014 always JSON)").action(
7228
+ () => runAction(async () => {
7189
7229
  const { arbi } = await resolveAuth();
7190
7230
  const data = await sdk.settings.getSettings(arbi);
7191
7231
  console.log(JSON.stringify(data, null, 2));
7192
- })
7232
+ })()
7193
7233
  );
7194
7234
  settings.command("set <json>").description("Update user settings (pass JSON object)").action(
7195
7235
  (json) => runAction(async () => {
@@ -7424,10 +7464,17 @@ function registerAgentconfigCommand(program2) {
7424
7464
  });
7425
7465
  }
7426
7466
  function registerHealthCommand(program2) {
7427
- program2.command("health").description("Show server health status").action(
7428
- runAction(async () => {
7467
+ program2.command("health").description("Show server health status").option("--json", "Output raw JSON for scripting").action(
7468
+ (opts) => runAction(async () => {
7429
7469
  const { arbi } = await resolveAuth();
7430
7470
  const data = await sdk.health.getHealth(arbi);
7471
+ if (opts.json) {
7472
+ console.log(JSON.stringify(data, null, 2));
7473
+ if (data.status && data.status.toLowerCase() !== "healthy") {
7474
+ process.exit(2);
7475
+ }
7476
+ return;
7477
+ }
7431
7478
  label("Status:", status(data.status));
7432
7479
  if (data.backend_git_hash) label("Backend:", data.backend_git_hash);
7433
7480
  if (data.frontend_docker_version) label("Frontend:", data.frontend_docker_version);
@@ -7444,12 +7491,16 @@ Models (${data.models_health.application}):`);
7444
7491
  console.log(` ${m.model}: ${status(m.status)}${m.detail ? ` \u2014 ${m.detail}` : ""}`);
7445
7492
  }
7446
7493
  }
7447
- })
7494
+ })()
7448
7495
  );
7449
- program2.command("models").description("List available AI models").action(
7450
- runAction(async () => {
7496
+ program2.command("models").description("List available AI models").option("--json", "Output as JSON").action(
7497
+ (opts) => runAction(async () => {
7451
7498
  const { arbi } = await resolveAuth();
7452
7499
  const data = await sdk.health.getHealthModels(arbi);
7500
+ if (opts.json) {
7501
+ console.log(JSON.stringify(data.models, null, 2));
7502
+ return;
7503
+ }
7453
7504
  if (data.models.length === 0) {
7454
7505
  console.log("No models available.");
7455
7506
  return;
@@ -7462,7 +7513,7 @@ Models (${data.models_health.application}):`);
7462
7513
  ],
7463
7514
  data.models
7464
7515
  );
7465
- })
7516
+ })()
7466
7517
  );
7467
7518
  }
7468
7519
  function registerTuiCommand(program2) {
@@ -8031,7 +8082,7 @@ function removeCompletionBlock(content) {
8031
8082
  }
8032
8083
  function registerCompletionCommand(program2) {
8033
8084
  const completion = program2.command("completion").description("Manage shell tab completion");
8034
- completion.command("install").description("Install shell tab completion (bash/zsh)").action(() => {
8085
+ completion.command("install").description("Install shell tab completion (auto-detects bash or zsh \u2014 no shell arg needed)").action(() => {
8035
8086
  const rcPath = getShellRcPath2();
8036
8087
  if (isCompletionInstalled(rcPath)) {
8037
8088
  success(`Completion already installed in ${rcPath}`);
@@ -8207,7 +8258,7 @@ console.info = (...args) => {
8207
8258
  _origInfo(...args);
8208
8259
  };
8209
8260
  var program = new commander.Command();
8210
- program.name("arbi").description("ARBI CLI \u2014 interact with ARBI from the terminal").version("0.3.39");
8261
+ program.name("arbi").description("ARBI CLI \u2014 interact with ARBI from the terminal").version("0.3.40");
8211
8262
  registerConfigCommand(program);
8212
8263
  registerLoginCommand(program);
8213
8264
  registerRegisterCommand(program);