@arbidocs/cli 0.4.48 → 0.4.50

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
@@ -3,8 +3,8 @@
3
3
 
4
4
  var prompts = require('@inquirer/prompts');
5
5
  var commander = require('commander');
6
- var fs5 = require('fs');
7
- var path5 = require('path');
6
+ var fs7 = require('fs');
7
+ var path6 = require('path');
8
8
  var os2 = require('os');
9
9
  var chalk2 = require('chalk');
10
10
  var sdk = require('@arbidocs/sdk');
@@ -17,8 +17,8 @@ var module$1 = require('module');
17
17
  var _documentCurrentScript = typeof document !== 'undefined' ? document.currentScript : null;
18
18
  function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
19
19
 
20
- var fs5__default = /*#__PURE__*/_interopDefault(fs5);
21
- var path5__default = /*#__PURE__*/_interopDefault(path5);
20
+ var fs7__default = /*#__PURE__*/_interopDefault(fs7);
21
+ var path6__default = /*#__PURE__*/_interopDefault(path6);
22
22
  var os2__default = /*#__PURE__*/_interopDefault(os2);
23
23
  var chalk2__default = /*#__PURE__*/_interopDefault(chalk2);
24
24
 
@@ -85,18 +85,18 @@ var init_prompts = __esm({
85
85
  }
86
86
  });
87
87
  function getCacheFile() {
88
- const configDir = process.env.ARBI_CONFIG_DIR ?? path5__default.default.join(os2__default.default.homedir(), ".arbi");
89
- return path5__default.default.join(configDir, "completions.json");
88
+ const configDir = process.env.ARBI_CONFIG_DIR ?? path6__default.default.join(os2__default.default.homedir(), ".arbi");
89
+ return path6__default.default.join(configDir, "completions.json");
90
90
  }
91
91
  function ensureDir(filePath) {
92
- const dir = path5__default.default.dirname(filePath);
93
- if (!fs5__default.default.existsSync(dir)) {
94
- fs5__default.default.mkdirSync(dir, { recursive: true, mode: 448 });
92
+ const dir = path6__default.default.dirname(filePath);
93
+ if (!fs7__default.default.existsSync(dir)) {
94
+ fs7__default.default.mkdirSync(dir, { recursive: true, mode: 448 });
95
95
  }
96
96
  }
97
97
  function getCachedWorkspaceIds() {
98
98
  try {
99
- const content = fs5__default.default.readFileSync(getCacheFile(), "utf-8");
99
+ const content = fs7__default.default.readFileSync(getCacheFile(), "utf-8");
100
100
  const cache = JSON.parse(content);
101
101
  return cache.workspaces.map((w) => w.id);
102
102
  } catch {
@@ -105,7 +105,7 @@ function getCachedWorkspaceIds() {
105
105
  }
106
106
  function getCachedWorkspaceName(id) {
107
107
  try {
108
- const content = fs5__default.default.readFileSync(getCacheFile(), "utf-8");
108
+ const content = fs7__default.default.readFileSync(getCacheFile(), "utf-8");
109
109
  const cache = JSON.parse(content);
110
110
  const hit = cache.workspaces.find((w) => w.id === id);
111
111
  return hit?.name?.trim() ? hit.name : null;
@@ -120,7 +120,7 @@ function updateCompletionCache(workspaces4) {
120
120
  };
121
121
  const filePath = getCacheFile();
122
122
  ensureDir(filePath);
123
- fs5__default.default.writeFileSync(filePath, JSON.stringify(cache, null, 2) + "\n", { mode: 384 });
123
+ fs7__default.default.writeFileSync(filePath, JSON.stringify(cache, null, 2) + "\n", { mode: 384 });
124
124
  }
125
125
 
126
126
  // src/completion.ts
@@ -267,12 +267,12 @@ var ALIAS_LINE = 'alias A="arbi ask"';
267
267
  var ALIAS_MARKER = "# arbi-cli alias";
268
268
  function getShellRcPath() {
269
269
  const shell = process.env.SHELL || "";
270
- if (shell.includes("zsh")) return path5.join(os2.homedir(), ".zshrc");
271
- return path5.join(os2.homedir(), ".bashrc");
270
+ if (shell.includes("zsh")) return path6.join(os2.homedir(), ".zshrc");
271
+ return path6.join(os2.homedir(), ".bashrc");
272
272
  }
273
273
  function isAliasInstalled(rcPath) {
274
- if (!fs5.existsSync(rcPath)) return false;
275
- const content = fs5.readFileSync(rcPath, "utf-8");
274
+ if (!fs7.existsSync(rcPath)) return false;
275
+ const content = fs7.readFileSync(rcPath, "utf-8");
276
276
  return content.includes(ALIAS_LINE) || content.includes(ALIAS_MARKER);
277
277
  }
278
278
  function registerConfigCommand(program2) {
@@ -366,7 +366,7 @@ function registerConfigCommand(program2) {
366
366
  dim("Usage: A what is the meaning of life");
367
367
  return;
368
368
  }
369
- fs5.appendFileSync(rcPath, `
369
+ fs7.appendFileSync(rcPath, `
370
370
  ${ALIAS_MARKER}
371
371
  ${ALIAS_LINE}
372
372
  `);
@@ -3657,11 +3657,11 @@ Object.defineProperties(globalVar, {
3657
3657
  IDBVersionChangeEvent: createPropertyDescriptor(FDBVersionChangeEvent_default)
3658
3658
  });
3659
3659
  init_prompts();
3660
- var CACHE_FILE = path5__default.default.join(os2__default.default.homedir(), ".arbi", "version-cache.json");
3660
+ var CACHE_FILE = path6__default.default.join(os2__default.default.homedir(), ".arbi", "version-cache.json");
3661
3661
  var CACHE_TTL_MS = 24 * 60 * 60 * 1e3;
3662
3662
  function readCache() {
3663
3663
  try {
3664
- const data = JSON.parse(fs5__default.default.readFileSync(CACHE_FILE, "utf8"));
3664
+ const data = JSON.parse(fs7__default.default.readFileSync(CACHE_FILE, "utf8"));
3665
3665
  if (data.latest && data.checkedAt && Date.now() - data.checkedAt < CACHE_TTL_MS) {
3666
3666
  return data;
3667
3667
  }
@@ -3671,9 +3671,9 @@ function readCache() {
3671
3671
  }
3672
3672
  function writeCache(latest) {
3673
3673
  try {
3674
- const dir = path5__default.default.dirname(CACHE_FILE);
3675
- if (!fs5__default.default.existsSync(dir)) fs5__default.default.mkdirSync(dir, { recursive: true, mode: 448 });
3676
- fs5__default.default.writeFileSync(CACHE_FILE, JSON.stringify({ latest, checkedAt: Date.now() }) + "\n");
3674
+ const dir = path6__default.default.dirname(CACHE_FILE);
3675
+ if (!fs7__default.default.existsSync(dir)) fs7__default.default.mkdirSync(dir, { recursive: true, mode: 448 });
3676
+ fs7__default.default.writeFileSync(CACHE_FILE, JSON.stringify({ latest, checkedAt: Date.now() }) + "\n");
3677
3677
  } catch {
3678
3678
  }
3679
3679
  }
@@ -3694,13 +3694,13 @@ function getLatestVersion(skipCache = false) {
3694
3694
  }
3695
3695
  }
3696
3696
  function getCurrentVersion() {
3697
- return "0.4.48";
3697
+ return "0.4.50";
3698
3698
  }
3699
3699
  function readChangelog(fromVersion, toVersion) {
3700
3700
  try {
3701
3701
  const globalRoot = child_process.execSync("npm root -g", { encoding: "utf8", timeout: 5e3 }).trim();
3702
- const changelogPath = path5__default.default.join(globalRoot, "@arbidocs", "cli", "CHANGELOG.md");
3703
- const text = fs5__default.default.readFileSync(changelogPath, "utf8");
3702
+ const changelogPath = path6__default.default.join(globalRoot, "@arbidocs", "cli", "CHANGELOG.md");
3703
+ const text = fs7__default.default.readFileSync(changelogPath, "utf8");
3704
3704
  return extractSections(text, fromVersion, toVersion);
3705
3705
  } catch {
3706
3706
  return null;
@@ -3747,30 +3747,30 @@ function showChangelog(fromVersion, toVersion) {
3747
3747
  async function checkForUpdates(autoUpdate) {
3748
3748
  try {
3749
3749
  const latest = getLatestVersion();
3750
- if (!latest || latest === "0.4.48") return;
3750
+ if (!latest || latest === "0.4.50") return;
3751
3751
  if (autoUpdate) {
3752
3752
  warn(`
3753
- Your arbi version is out of date (${"0.4.48"} \u2192 ${latest}). Updating...`);
3753
+ Your arbi version is out of date (${"0.4.50"} \u2192 ${latest}). Updating...`);
3754
3754
  child_process.execSync("npm install -g @arbidocs/cli@latest", { stdio: "inherit" });
3755
- showChangelog("0.4.48", latest);
3755
+ showChangelog("0.4.50", latest);
3756
3756
  console.log(`Updated to ${latest}.`);
3757
3757
  } else {
3758
3758
  warn(
3759
3759
  `
3760
- Your arbi version is out of date (${"0.4.48"} \u2192 ${latest}).
3760
+ Your arbi version is out of date (${"0.4.50"} \u2192 ${latest}).
3761
3761
  Run "arbi update" to upgrade, or "arbi update auto" to always stay up to date.`
3762
3762
  );
3763
3763
  }
3764
3764
  } catch {
3765
3765
  }
3766
3766
  }
3767
- var NAG_FILE = path5__default.default.join(os2__default.default.homedir(), ".arbi", "last-update-hint.json");
3767
+ var NAG_FILE = path6__default.default.join(os2__default.default.homedir(), ".arbi", "last-update-hint.json");
3768
3768
  var NAG_INTERVAL_MS = 24 * 60 * 60 * 1e3;
3769
3769
  function shouldShowNag(latest) {
3770
3770
  if (process.env.ARBI_JSON === "1") return false;
3771
3771
  if (process.argv.includes("--json")) return false;
3772
3772
  try {
3773
- const data = JSON.parse(fs5__default.default.readFileSync(NAG_FILE, "utf8"));
3773
+ const data = JSON.parse(fs7__default.default.readFileSync(NAG_FILE, "utf8"));
3774
3774
  if (data.latest === latest && typeof data.shownAt === "number" && Date.now() - data.shownAt < NAG_INTERVAL_MS) {
3775
3775
  return false;
3776
3776
  }
@@ -3780,19 +3780,19 @@ function shouldShowNag(latest) {
3780
3780
  }
3781
3781
  function markNagShown(latest) {
3782
3782
  try {
3783
- const dir = path5__default.default.dirname(NAG_FILE);
3784
- if (!fs5__default.default.existsSync(dir)) fs5__default.default.mkdirSync(dir, { recursive: true, mode: 448 });
3785
- fs5__default.default.writeFileSync(NAG_FILE, JSON.stringify({ latest, shownAt: Date.now() }) + "\n");
3783
+ const dir = path6__default.default.dirname(NAG_FILE);
3784
+ if (!fs7__default.default.existsSync(dir)) fs7__default.default.mkdirSync(dir, { recursive: true, mode: 448 });
3785
+ fs7__default.default.writeFileSync(NAG_FILE, JSON.stringify({ latest, shownAt: Date.now() }) + "\n");
3786
3786
  } catch {
3787
3787
  }
3788
3788
  }
3789
3789
  function hintUpdateOnError() {
3790
3790
  try {
3791
3791
  const cached = readCache();
3792
- if (!cached || cached.latest === "0.4.48") return;
3792
+ if (!cached || cached.latest === "0.4.50") return;
3793
3793
  if (!shouldShowNag(cached.latest)) return;
3794
3794
  warn(
3795
- `Your arbi version is out of date (${"0.4.48"} \u2192 ${cached.latest}). Run "arbi update".`
3795
+ `Your arbi version is out of date (${"0.4.50"} \u2192 ${cached.latest}). Run "arbi update".`
3796
3796
  );
3797
3797
  markNagShown(cached.latest);
3798
3798
  } catch {
@@ -3801,18 +3801,18 @@ function hintUpdateOnError() {
3801
3801
  var MAX_TASKS = 50;
3802
3802
  var MAX_AGE_MS = 7 * 24 * 60 * 60 * 1e3;
3803
3803
  function getTasksFile() {
3804
- const configDir = process.env.ARBI_CONFIG_DIR ?? path5__default.default.join(os2__default.default.homedir(), ".arbi");
3805
- return path5__default.default.join(configDir, "tasks.json");
3804
+ const configDir = process.env.ARBI_CONFIG_DIR ?? path6__default.default.join(os2__default.default.homedir(), ".arbi");
3805
+ return path6__default.default.join(configDir, "tasks.json");
3806
3806
  }
3807
3807
  function ensureDir2(filePath) {
3808
- const dir = path5__default.default.dirname(filePath);
3809
- if (!fs5__default.default.existsSync(dir)) {
3810
- fs5__default.default.mkdirSync(dir, { recursive: true, mode: 448 });
3808
+ const dir = path6__default.default.dirname(filePath);
3809
+ if (!fs7__default.default.existsSync(dir)) {
3810
+ fs7__default.default.mkdirSync(dir, { recursive: true, mode: 448 });
3811
3811
  }
3812
3812
  }
3813
3813
  function readTasks() {
3814
3814
  try {
3815
- const content = fs5__default.default.readFileSync(getTasksFile(), "utf-8");
3815
+ const content = fs7__default.default.readFileSync(getTasksFile(), "utf-8");
3816
3816
  return JSON.parse(content);
3817
3817
  } catch {
3818
3818
  return [];
@@ -3821,7 +3821,7 @@ function readTasks() {
3821
3821
  function writeTasks(tasks) {
3822
3822
  const filePath = getTasksFile();
3823
3823
  ensureDir2(filePath);
3824
- fs5__default.default.writeFileSync(filePath, JSON.stringify(tasks, null, 2) + "\n", { mode: 384 });
3824
+ fs7__default.default.writeFileSync(filePath, JSON.stringify(tasks, null, 2) + "\n", { mode: 384 });
3825
3825
  }
3826
3826
  function getTasks() {
3827
3827
  const now = Date.now();
@@ -4178,39 +4178,39 @@ function checkAgentDependency(backend) {
4178
4178
  dim(`${binary} ${match?.[1] ?? version} \u2713`);
4179
4179
  }
4180
4180
  function loadSkillContent() {
4181
- const cliRoot = path5.resolve(path5.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)))), "..");
4181
+ const cliRoot = path6.resolve(path6.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)))), "..");
4182
4182
  try {
4183
- return fs5.readFileSync(path5.join(cliRoot, "SKILL.md"), "utf-8");
4183
+ return fs7.readFileSync(path6.join(cliRoot, "SKILL.md"), "utf-8");
4184
4184
  } catch {
4185
4185
  return void 0;
4186
4186
  }
4187
4187
  }
4188
4188
  function installSkill(backend) {
4189
- const cliRoot = path5.resolve(path5.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)))), "..");
4189
+ const cliRoot = path6.resolve(path6.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)))), "..");
4190
4190
  let skillContent;
4191
4191
  try {
4192
- skillContent = fs5.readFileSync(path5.join(cliRoot, "SKILL.md"), "utf-8");
4192
+ skillContent = fs7.readFileSync(path6.join(cliRoot, "SKILL.md"), "utf-8");
4193
4193
  } catch {
4194
4194
  return;
4195
4195
  }
4196
4196
  if (backend === "claude") {
4197
- const dir = path5.join(os2.homedir(), ".claude", "commands", "arbi");
4198
- const target = path5.join(dir, "SKILL.md");
4197
+ const dir = path6.join(os2.homedir(), ".claude", "commands", "arbi");
4198
+ const target = path6.join(dir, "SKILL.md");
4199
4199
  try {
4200
- fs5.mkdirSync(dir, { recursive: true });
4201
- if (!fs5.existsSync(target) || fs5.readFileSync(target, "utf-8") !== skillContent) {
4202
- fs5.writeFileSync(target, skillContent, "utf-8");
4200
+ fs7.mkdirSync(dir, { recursive: true });
4201
+ if (!fs7.existsSync(target) || fs7.readFileSync(target, "utf-8") !== skillContent) {
4202
+ fs7.writeFileSync(target, skillContent, "utf-8");
4203
4203
  dim(`Installed ARBI skill \u2192 ${target}`);
4204
4204
  }
4205
4205
  } catch {
4206
4206
  }
4207
4207
  } else if (backend === "openclaw") {
4208
- const workspace = path5.join(os2.homedir(), ".arbi", "openclaw-workspace");
4209
- const bootstrap = path5.join(workspace, "BOOTSTRAP.md");
4208
+ const workspace = path6.join(os2.homedir(), ".arbi", "openclaw-workspace");
4209
+ const bootstrap = path6.join(workspace, "BOOTSTRAP.md");
4210
4210
  try {
4211
- fs5.mkdirSync(workspace, { recursive: true });
4212
- if (!fs5.existsSync(bootstrap) || fs5.readFileSync(bootstrap, "utf-8") !== skillContent) {
4213
- fs5.writeFileSync(bootstrap, skillContent, "utf-8");
4211
+ fs7.mkdirSync(workspace, { recursive: true });
4212
+ if (!fs7.existsSync(bootstrap) || fs7.readFileSync(bootstrap, "utf-8") !== skillContent) {
4213
+ fs7.writeFileSync(bootstrap, skillContent, "utf-8");
4214
4214
  dim(`Installed ARBI skill \u2192 ${bootstrap}`);
4215
4215
  }
4216
4216
  } catch {
@@ -4850,6 +4850,53 @@ function registerStatusCommand(program2) {
4850
4850
  });
4851
4851
  }
4852
4852
  init_prompts();
4853
+ var IMAGE_TYPES = {
4854
+ ".png": "image/png",
4855
+ ".jpg": "image/jpeg",
4856
+ ".jpeg": "image/jpeg",
4857
+ ".gif": "image/gif",
4858
+ ".webp": "image/webp"
4859
+ };
4860
+ function typeFromName(name) {
4861
+ return IMAGE_TYPES[path6__default.default.extname(name).toLowerCase()] ?? sdk.documents.mimeFromName(name) ?? "application/octet-stream";
4862
+ }
4863
+ function collectPaths(value, previous) {
4864
+ return [...previous ?? [], value];
4865
+ }
4866
+ function readAttachmentFiles(paths) {
4867
+ const stats = paths.map((p) => {
4868
+ const resolved = path6__default.default.resolve(p);
4869
+ if (!fs7__default.default.existsSync(resolved) || !fs7__default.default.statSync(resolved).isFile()) {
4870
+ throw new Error(`File not found: ${p}`);
4871
+ }
4872
+ return { resolved, name: path6__default.default.basename(resolved), size: fs7__default.default.statSync(resolved).size };
4873
+ });
4874
+ sdk.dm.checkDmAttachmentLimits(stats);
4875
+ return stats.map(({ resolved, name }) => ({
4876
+ name,
4877
+ type: typeFromName(name),
4878
+ bytes: new Uint8Array(fs7__default.default.readFileSync(resolved))
4879
+ }));
4880
+ }
4881
+ function findAttachment(row, attachmentId) {
4882
+ const found = row.attachmentFiles?.find((f) => f.id === attachmentId);
4883
+ if (!found) throw new Error(`No attachment ${attachmentId} on message ${String(row.external_id)}`);
4884
+ return found;
4885
+ }
4886
+ function attachmentOutputPath(file, output) {
4887
+ if (output) return output;
4888
+ const base = path6__default.default.basename(file.name);
4889
+ return base && base !== "." && base !== ".." ? base : file.id;
4890
+ }
4891
+ function publicAttachmentFiles(files) {
4892
+ return (files ?? []).map(({ id, name, type, size }) => ({ id, name, type, size }));
4893
+ }
4894
+ function attachmentLabel(files) {
4895
+ const n = files?.length ?? 0;
4896
+ return n ? `[${n} file${n === 1 ? "" : "s"}] ` : "";
4897
+ }
4898
+
4899
+ // src/commands/dm.ts
4853
4900
  async function resolveRecipient(arbi, ref2) {
4854
4901
  return sdk.dm.resolveRecipient(arbi, ref2);
4855
4902
  }
@@ -4899,7 +4946,17 @@ function registerDmCommand(program2) {
4899
4946
  const filtered = dms.filter((r) => rowMatchesFilters(r, filterOpts));
4900
4947
  const limited = typeof opts.limit === "number" && opts.limit > 0 ? filtered.slice(0, opts.limit) : filtered;
4901
4948
  if (opts.json) {
4902
- printJson(limited.map((r) => ({ ...r, encrypted_in_transit: true })));
4949
+ printJson(
4950
+ limited.map(({ attachmentFiles, ...r }) => ({
4951
+ ...r,
4952
+ ...attachmentFiles ? {
4953
+ attachment_files: publicAttachmentFiles(
4954
+ attachmentFiles
4955
+ )
4956
+ } : {},
4957
+ encrypted_in_transit: true
4958
+ }))
4959
+ );
4903
4960
  return;
4904
4961
  }
4905
4962
  if (limited.length === 0) {
@@ -4931,15 +4988,23 @@ function registerDmCommand(program2) {
4931
4988
  {
4932
4989
  header: "CONTENT",
4933
4990
  width: 50,
4934
- value: (r) => truncate(r.content ?? "", 49)
4991
+ value: (r) => truncate(
4992
+ attachmentLabel(r.attachmentFiles) + (r.content ?? ""),
4993
+ 49
4994
+ )
4935
4995
  }
4936
4996
  ],
4937
4997
  limited
4938
4998
  );
4939
4999
  })()
4940
5000
  );
4941
- dm.command("send [recipient] [content...]").description("Send an E2E encrypted DM (interactive if no args)").action(
4942
- (recipient, contentParts) => runAction(async () => {
5001
+ dm.command("send [recipient] [content...]").description("Send an E2E encrypted DM (interactive if no args)").option(
5002
+ "-a, --attach <path>",
5003
+ "Attach a file, encrypted end to end (repeatable; up to 10 files of 15 MB each)",
5004
+ collectPaths
5005
+ ).action(
5006
+ (recipient, contentParts, opts) => runAction(async () => {
5007
+ const attachmentInputs = readAttachmentFiles(opts?.attach ?? []);
4943
5008
  const { arbi, crypto } = await resolveDmCrypto();
4944
5009
  if (!recipient) {
4945
5010
  requireInteractive(
@@ -4964,7 +5029,8 @@ function registerDmCommand(program2) {
4964
5029
  );
4965
5030
  }
4966
5031
  let content = contentParts?.length ? contentParts.join(" ") : void 0;
4967
- if (!content) {
5032
+ if (!content && attachmentInputs.length) content = "";
5033
+ if (content === void 0) {
4968
5034
  requireInteractive(
4969
5035
  'Pass the message as a positional argument: arbi dm send <email> "your message"'
4970
5036
  );
@@ -4986,6 +5052,7 @@ Try: arbi contacts add ${recipient.includes("@") ? recipient : "<their-email>"}`
4986
5052
  );
4987
5053
  process.exit(3);
4988
5054
  }
5055
+ const attachments = await sdk.dm.uploadDmAttachments(arbi, attachmentInputs);
4989
5056
  const data = await sdk.dm.sendEncryptedDM(
4990
5057
  arbi,
4991
5058
  [
@@ -4995,17 +5062,44 @@ Try: arbi contacts add ${recipient.includes("@") ? recipient : "<their-email>"}`
4995
5062
  recipient_encryption_public_key: recipientPubKey
4996
5063
  }
4997
5064
  ],
4998
- crypto
5065
+ crypto,
5066
+ { attachments }
4999
5067
  );
5000
5068
  for (const n of data) {
5001
5069
  success(`Sent (encrypted): ${n.external_id} \u2192 ${n.recipient.email}`);
5002
5070
  }
5071
+ for (const f of attachments) {
5072
+ success(`Attached (encrypted): ${f.id} ${f.name} (${f.size} bytes)`);
5073
+ }
5003
5074
  })()
5004
5075
  );
5005
- dm.command("read [ids...]").description("Mark messages as read (interactive picker if no IDs given)").option("--all", "Mark every unread message as read (no picker)").action(
5076
+ dm.command("read [ids...]").description(
5077
+ "Mark messages as read (interactive picker if no IDs given), or save an attachment with --attachment"
5078
+ ).option("--all", "Mark every unread message as read (no picker)").option(
5079
+ "--attachment <att-id>",
5080
+ "Save this attachment of the message (ids from `arbi dm list --json`) instead"
5081
+ ).option("-o, --output <path>", "Where to save the attachment (default: its file name)").action(
5006
5082
  (ids, opts) => runAction(async () => {
5007
5083
  const { arbi, crypto } = await resolveDmCrypto();
5008
5084
  let msgIds = ids && ids.length > 0 ? ids : void 0;
5085
+ if (typeof opts?.attachment === "string") {
5086
+ if (msgIds?.length !== 1) {
5087
+ throw new Error("Name exactly one message: arbi dm read <ntf-id> --attachment <att-id>");
5088
+ }
5089
+ const rows = await sdk.dm.listDecryptedDMs(arbi, crypto);
5090
+ const row = rows.find((m) => m.external_id === msgIds[0]);
5091
+ if (!row) throw new Error(`No message ${msgIds[0]}`);
5092
+ if (row.attachmentsError) throw new Error(row.attachmentsError);
5093
+ const file = findAttachment(row, opts.attachment);
5094
+ const bytes = await sdk.dm.downloadDmAttachment(arbi, file);
5095
+ const target = attachmentOutputPath(
5096
+ file,
5097
+ typeof opts.output === "string" ? opts.output : void 0
5098
+ );
5099
+ fs7__default.default.writeFileSync(target, bytes);
5100
+ success(`Saved ${target} (${bytes.length} bytes, decrypted)`);
5101
+ return;
5102
+ }
5009
5103
  if (!msgIds && opts?.all) {
5010
5104
  const data2 = await sdk.dm.listDecryptedDMs(arbi, crypto);
5011
5105
  msgIds = data2.filter((m) => !m.read).map((m) => m.external_id);
@@ -5758,12 +5852,12 @@ function registerDocsCommand(program2) {
5758
5852
  null,
5759
5853
  opts.output ? 2 : 0
5760
5854
  );
5761
- if (opts.output) fs5.writeFileSync(opts.output, emptyPayload + "\n");
5855
+ if (opts.output) fs7.writeFileSync(opts.output, emptyPayload + "\n");
5762
5856
  else console.log(emptyPayload);
5763
5857
  } else if (opts.ids) {
5764
- if (opts.output) fs5.writeFileSync(opts.output, "");
5858
+ if (opts.output) fs7.writeFileSync(opts.output, "");
5765
5859
  } else if (opts.csv) {
5766
- if (opts.output) fs5.writeFileSync(opts.output, csvHeader + "\n");
5860
+ if (opts.output) fs7.writeFileSync(opts.output, csvHeader + "\n");
5767
5861
  } else if (opts.count) {
5768
5862
  const wsLabel2 = workspaceName ? `${workspaceName} (${workspaceId})` : workspaceId;
5769
5863
  console.log(chalk2__default.default.dim(`Workspace: ${wsLabel2}`));
@@ -5828,7 +5922,7 @@ function registerDocsCommand(program2) {
5828
5922
  }
5829
5923
  const writeOut = (payload, defaultExt) => {
5830
5924
  if (opts.output) {
5831
- fs5.writeFileSync(opts.output, payload.endsWith("\n") ? payload : payload + "\n");
5925
+ fs7.writeFileSync(opts.output, payload.endsWith("\n") ? payload : payload + "\n");
5832
5926
  success(`Wrote ${data.length} rows to ${opts.output} (${defaultExt})`);
5833
5927
  } else {
5834
5928
  console.log(payload.endsWith("\n") ? payload.slice(0, -1) : payload);
@@ -6427,12 +6521,12 @@ function printSummary(summary) {
6427
6521
  }
6428
6522
  function countSupportedFiles(dirPath) {
6429
6523
  let count = 0;
6430
- for (const entry of fs5__default.default.readdirSync(dirPath, { withFileTypes: true })) {
6524
+ for (const entry of fs7__default.default.readdirSync(dirPath, { withFileTypes: true })) {
6431
6525
  const full = `${dirPath}/${entry.name}`;
6432
6526
  if (entry.isDirectory()) {
6433
6527
  count += countSupportedFiles(full);
6434
6528
  } else if (entry.isFile()) {
6435
- const ext = path5__default.default.extname(entry.name).toLowerCase();
6529
+ const ext = path6__default.default.extname(entry.name).toLowerCase();
6436
6530
  if (sdk.documents.SUPPORTED_EXTENSIONS.has(ext)) count++;
6437
6531
  }
6438
6532
  }
@@ -6460,17 +6554,17 @@ function mergeBatchResult(summary, uploadedDocs, result, sourceLabel, opts) {
6460
6554
  function classifyDirectInputs(inputPaths) {
6461
6555
  const out = [];
6462
6556
  for (const p of inputPaths) {
6463
- if (!fs5__default.default.existsSync(p)) {
6557
+ if (!fs7__default.default.existsSync(p)) {
6464
6558
  error(`Path not found: ${p}`);
6465
6559
  process.exit(1);
6466
6560
  }
6467
- const resolved = path5__default.default.resolve(p);
6468
- const stat = fs5__default.default.statSync(resolved);
6561
+ const resolved = path6__default.default.resolve(p);
6562
+ const stat = fs7__default.default.statSync(resolved);
6469
6563
  if (stat.isDirectory()) {
6470
6564
  out.push({ kind: "dir", absolutePath: resolved, originalPath: p });
6471
6565
  continue;
6472
6566
  }
6473
- const ext = path5__default.default.extname(resolved).toLowerCase();
6567
+ const ext = path6__default.default.extname(resolved).toLowerCase();
6474
6568
  if (ext === ".zip") {
6475
6569
  out.push({ kind: "zip", absolutePath: resolved, originalPath: p });
6476
6570
  continue;
@@ -6508,7 +6602,7 @@ async function runDirectUploadMode(inputPaths, opts) {
6508
6602
  console.log(` ${inp.originalPath}: zip archive (contents would be extracted + uploaded)`);
6509
6603
  } else {
6510
6604
  console.log(
6511
- ` ${inp.originalPath}: ${path5__default.default.extname(inp.absolutePath).slice(1)} archive (contents would be extracted + uploaded)`
6605
+ ` ${inp.originalPath}: ${path6__default.default.extname(inp.absolutePath).slice(1)} archive (contents would be extracted + uploaded)`
6512
6606
  );
6513
6607
  }
6514
6608
  }
@@ -6617,7 +6711,7 @@ async function runDirectUploadMode(inputPaths, opts) {
6617
6711
  summary.docIds.push(...result.doc_ext_ids);
6618
6712
  summary.skippedDetails.push(...result.skipped);
6619
6713
  for (let i = 0; i < result.doc_ext_ids.length && i < plainFiles.length; i++) {
6620
- uploadedDocs.set(result.doc_ext_ids[i], path5__default.default.basename(plainFiles[i]));
6714
+ uploadedDocs.set(result.doc_ext_ids[i], path6__default.default.basename(plainFiles[i]));
6621
6715
  }
6622
6716
  if (!opts.json) {
6623
6717
  success(
@@ -6717,7 +6811,7 @@ function registerUploadCommand(program2) {
6717
6811
  process.exit(1);
6718
6812
  }
6719
6813
  for (const p of inputPaths) {
6720
- if (!fs5__default.default.existsSync(p)) {
6814
+ if (!fs7__default.default.existsSync(p)) {
6721
6815
  error(`Path not found: ${p}`);
6722
6816
  process.exit(1);
6723
6817
  }
@@ -6759,7 +6853,7 @@ function registerUploadCommand(program2) {
6759
6853
  }
6760
6854
  };
6761
6855
  for (const filePath of inputPaths) {
6762
- const stat = fs5__default.default.statSync(filePath);
6856
+ const stat = fs7__default.default.statSync(filePath);
6763
6857
  if (stat.isDirectory()) {
6764
6858
  if (opts.dryRun) {
6765
6859
  const count = countSupportedFiles2(filePath);
@@ -6953,7 +7047,7 @@ function updateSummary(summary, result) {
6953
7047
  }
6954
7048
  function countSupportedFiles2(dirPath) {
6955
7049
  let count = 0;
6956
- for (const entry of fs5__default.default.readdirSync(dirPath, { withFileTypes: true })) {
7050
+ for (const entry of fs7__default.default.readdirSync(dirPath, { withFileTypes: true })) {
6957
7051
  const fullPath = `${dirPath}/${entry.name}`;
6958
7052
  if (entry.isDirectory()) {
6959
7053
  count += countSupportedFiles2(fullPath);
@@ -6971,8 +7065,8 @@ function countSupportedFiles2(dirPath) {
6971
7065
  }
6972
7066
  function readLogState(logPath) {
6973
7067
  const state = /* @__PURE__ */ new Map();
6974
- if (!fs5__default.default.existsSync(logPath)) return state;
6975
- const text = fs5__default.default.readFileSync(logPath, "utf8");
7068
+ if (!fs7__default.default.existsSync(logPath)) return state;
7069
+ const text = fs7__default.default.readFileSync(logPath, "utf8");
6976
7070
  for (const line of text.split("\n")) {
6977
7071
  const trimmed = line.trim();
6978
7072
  if (!trimmed || trimmed.startsWith("#")) continue;
@@ -6988,7 +7082,7 @@ function readLogState(logPath) {
6988
7082
  return state;
6989
7083
  }
6990
7084
  function readTextManifest(manifestPath) {
6991
- const text = fs5__default.default.readFileSync(manifestPath, "utf8");
7085
+ const text = fs7__default.default.readFileSync(manifestPath, "utf8");
6992
7086
  const out = [];
6993
7087
  for (const raw of text.split("\n")) {
6994
7088
  const line = raw.replace(/\r$/, "").trim();
@@ -7060,8 +7154,8 @@ function writeCsvAtomic(filePath, header, rows) {
7060
7154
  lines.push(padded.map(csvField).join(","));
7061
7155
  }
7062
7156
  const tmp = filePath + ".tmp";
7063
- fs5__default.default.writeFileSync(tmp, lines.join("\n") + "\n");
7064
- fs5__default.default.renameSync(tmp, filePath);
7157
+ fs7__default.default.writeFileSync(tmp, lines.join("\n") + "\n");
7158
+ fs7__default.default.renameSync(tmp, filePath);
7065
7159
  }
7066
7160
  function findColumn(header, candidates) {
7067
7161
  const normalized = header.map((h) => h.trim().toLowerCase());
@@ -7072,7 +7166,7 @@ function findColumn(header, candidates) {
7072
7166
  return -1;
7073
7167
  }
7074
7168
  function loadCsvManifest(csvPath, rootDir) {
7075
- const text = fs5__default.default.readFileSync(csvPath, "utf8");
7169
+ const text = fs7__default.default.readFileSync(csvPath, "utf8");
7076
7170
  const all = parseCsv(text);
7077
7171
  if (all.length === 0) {
7078
7172
  error(`Empty CSV: ${csvPath}`);
@@ -7103,7 +7197,7 @@ function loadCsvManifest(csvPath, rootDir) {
7103
7197
  for (const row of rows) {
7104
7198
  while (row.length < header.length) row.push("");
7105
7199
  }
7106
- const root = rootDir ? path5__default.default.resolve(rootDir) : process.cwd();
7200
+ const root = rootDir ? path6__default.default.resolve(rootDir) : process.cwd();
7107
7201
  const paths = [];
7108
7202
  const pathToRow = /* @__PURE__ */ new Map();
7109
7203
  for (let i = 0; i < rows.length; i++) {
@@ -7111,8 +7205,8 @@ function loadCsvManifest(csvPath, rootDir) {
7111
7205
  const name = (row[nameCol] ?? "").trim();
7112
7206
  if (!name) continue;
7113
7207
  const folder = (row[folderCol] ?? "").trim();
7114
- const rel = folder ? path5__default.default.join(folder, name) : name;
7115
- const abs = path5__default.default.resolve(root, rel);
7208
+ const rel = folder ? path6__default.default.join(folder, name) : name;
7209
+ const abs = path6__default.default.resolve(root, rel);
7116
7210
  paths.push(abs);
7117
7211
  pathToRow.set(abs, i);
7118
7212
  }
@@ -7136,11 +7230,11 @@ async function runManifestMode(inputPaths, opts) {
7136
7230
  const rawPaths = [];
7137
7231
  let csv = null;
7138
7232
  if (opts.manifest) {
7139
- if (!fs5__default.default.existsSync(opts.manifest)) {
7233
+ if (!fs7__default.default.existsSync(opts.manifest)) {
7140
7234
  error(`Manifest not found: ${opts.manifest}`);
7141
7235
  process.exit(1);
7142
7236
  }
7143
- const ext = path5__default.default.extname(opts.manifest).toLowerCase();
7237
+ const ext = path6__default.default.extname(opts.manifest).toLowerCase();
7144
7238
  if (ext === ".csv" || ext === ".tsv") {
7145
7239
  csv = loadCsvManifest(opts.manifest, opts.root);
7146
7240
  rawPaths.push(...csv.paths);
@@ -7149,15 +7243,15 @@ async function runManifestMode(inputPaths, opts) {
7149
7243
  }
7150
7244
  }
7151
7245
  for (const p of inputPaths) {
7152
- if (!fs5__default.default.existsSync(p)) {
7246
+ if (!fs7__default.default.existsSync(p)) {
7153
7247
  error(`Path not found: ${p}`);
7154
7248
  process.exit(1);
7155
7249
  }
7156
- const stat = fs5__default.default.statSync(p);
7250
+ const stat = fs7__default.default.statSync(p);
7157
7251
  if (stat.isDirectory()) {
7158
7252
  rawPaths.push(...sdk.documentsNode.walkSupportedFiles(p));
7159
7253
  } else {
7160
- rawPaths.push(path5__default.default.resolve(p));
7254
+ rawPaths.push(path6__default.default.resolve(p));
7161
7255
  }
7162
7256
  }
7163
7257
  if (rawPaths.length === 0) {
@@ -7195,7 +7289,7 @@ async function runManifestMode(inputPaths, opts) {
7195
7289
  const humanOutput = !opts.json;
7196
7290
  if (humanOutput) {
7197
7291
  console.log(
7198
- `Manifest: ${rawPaths.length} file(s)` + (csv ? ` from ${path5__default.default.basename(csv.filePath)}` : "") + (resumedCount ? ` (${resumedCount} already done, skipping)` : "") + (limit !== null && limitedOut > 0 ? ` (limit=${limit}, ${limitedOut} deferred to next run)` : "")
7292
+ `Manifest: ${rawPaths.length} file(s)` + (csv ? ` from ${path6__default.default.basename(csv.filePath)}` : "") + (resumedCount ? ` (${resumedCount} already done, skipping)` : "") + (limit !== null && limitedOut > 0 ? ` (limit=${limit}, ${limitedOut} deferred to next run)` : "")
7199
7293
  );
7200
7294
  }
7201
7295
  if (opts.dryRun) {
@@ -7224,9 +7318,9 @@ async function runManifestMode(inputPaths, opts) {
7224
7318
  const auth = { baseUrl: config.baseUrl, accessToken };
7225
7319
  let logFd = null;
7226
7320
  if (opts.log) {
7227
- const dir = path5__default.default.dirname(path5__default.default.resolve(opts.log));
7228
- fs5__default.default.mkdirSync(dir, { recursive: true });
7229
- logFd = fs5__default.default.openSync(opts.log, "a");
7321
+ const dir = path6__default.default.dirname(path6__default.default.resolve(opts.log));
7322
+ fs7__default.default.mkdirSync(dir, { recursive: true });
7323
+ logFd = fs7__default.default.openSync(opts.log, "a");
7230
7324
  const header = JSON.stringify({
7231
7325
  ts: (/* @__PURE__ */ new Date()).toISOString(),
7232
7326
  event: "run_start",
@@ -7236,8 +7330,8 @@ async function runManifestMode(inputPaths, opts) {
7236
7330
  toUpload: pathsToUpload.length,
7237
7331
  resumed: resumedCount
7238
7332
  });
7239
- fs5__default.default.writeSync(logFd, header + "\n");
7240
- fs5__default.default.fsyncSync(logFd);
7333
+ fs7__default.default.writeSync(logFd, header + "\n");
7334
+ fs7__default.default.fsyncSync(logFd);
7241
7335
  }
7242
7336
  const uploadedDocs = /* @__PURE__ */ new Map();
7243
7337
  const runStart = Date.now();
@@ -7254,8 +7348,8 @@ async function runManifestMode(inputPaths, opts) {
7254
7348
  attempt: o.attempt,
7255
7349
  ...o.batchId ? { batchId: o.batchId } : {}
7256
7350
  };
7257
- fs5__default.default.writeSync(logFd, JSON.stringify(rec) + "\n");
7258
- fs5__default.default.fsyncSync(logFd);
7351
+ fs7__default.default.writeSync(logFd, JSON.stringify(rec) + "\n");
7352
+ fs7__default.default.fsyncSync(logFd);
7259
7353
  };
7260
7354
  const total = pathsToUpload.length;
7261
7355
  let done = 0;
@@ -7275,7 +7369,7 @@ async function runManifestMode(inputPaths, opts) {
7275
7369
  };
7276
7370
  const printInlineOutcome = (o) => {
7277
7371
  if (!humanOutput) return;
7278
- const name = path5__default.default.basename(o.path);
7372
+ const name = path6__default.default.basename(o.path);
7279
7373
  if (o.status === "rejected") {
7280
7374
  clearProgress();
7281
7375
  warn(` \u2717 ${name}: ${o.reason}`);
@@ -7307,7 +7401,7 @@ async function runManifestMode(inputPaths, opts) {
7307
7401
  switch (outcome.status) {
7308
7402
  case "uploaded":
7309
7403
  uploadedCount++;
7310
- if (outcome.docId) uploadedDocs.set(outcome.docId, path5__default.default.basename(outcome.path));
7404
+ if (outcome.docId) uploadedDocs.set(outcome.docId, path6__default.default.basename(outcome.path));
7311
7405
  break;
7312
7406
  case "duplicate":
7313
7407
  duplicateCount++;
@@ -7346,9 +7440,9 @@ async function runManifestMode(inputPaths, opts) {
7346
7440
  ...result.summary,
7347
7441
  elapsedMs: Date.now() - runStart
7348
7442
  });
7349
- fs5__default.default.writeSync(logFd, footer + "\n");
7350
- fs5__default.default.fsyncSync(logFd);
7351
- fs5__default.default.closeSync(logFd);
7443
+ fs7__default.default.writeSync(logFd, footer + "\n");
7444
+ fs7__default.default.fsyncSync(logFd);
7445
+ fs7__default.default.closeSync(logFd);
7352
7446
  }
7353
7447
  clearProgress();
7354
7448
  if (opts.json) {
@@ -7473,21 +7567,21 @@ function registerDownloadCommand(program2) {
7473
7567
  process.stdout.write(buffer);
7474
7568
  return;
7475
7569
  }
7476
- const outputPath = opts.output || path5__default.default.join(process.cwd(), filename);
7477
- fs5__default.default.writeFileSync(outputPath, buffer);
7570
+ const outputPath = opts.output || path6__default.default.join(process.cwd(), filename);
7571
+ fs7__default.default.writeFileSync(outputPath, buffer);
7478
7572
  if (opts.json) {
7479
7573
  console.log(
7480
7574
  JSON.stringify({
7481
7575
  doc_id: docId,
7482
7576
  path: outputPath,
7483
7577
  bytes: buffer.length,
7484
- filename: path5__default.default.basename(outputPath)
7578
+ filename: path6__default.default.basename(outputPath)
7485
7579
  })
7486
7580
  );
7487
7581
  return;
7488
7582
  }
7489
7583
  success(
7490
- `Downloaded: ${path5__default.default.basename(outputPath)} (${(buffer.length / (1024 * 1024)).toFixed(1)} MB)`
7584
+ `Downloaded: ${path6__default.default.basename(outputPath)} (${(buffer.length / (1024 * 1024)).toFixed(1)} MB)`
7491
7585
  );
7492
7586
  })()
7493
7587
  );
@@ -8650,12 +8744,12 @@ function registerConversationsCommand(program2) {
8650
8744
  conv.command("import <path>").description(
8651
8745
  "Import a conversation export (ChatGPT, OpenClaw, or Claude Code) into the active workspace"
8652
8746
  ).option("-w, --workspace <id>", "Workspace ID (defaults to selected workspace)").option("-s, --source <source>", "Export format: auto, chatgpt, openclaw, claude_code", "auto").option("--json", "Output as JSON").action(
8653
- (path7, opts) => runAction(async () => {
8747
+ (path8, opts) => runAction(async () => {
8654
8748
  const { loginResult, config } = await resolveWorkspace(opts.workspace);
8655
- const filePath = path5.resolve(path7);
8749
+ const filePath = path6.resolve(path8);
8656
8750
  let bytes;
8657
8751
  try {
8658
- bytes = fs5.readFileSync(filePath);
8752
+ bytes = fs7.readFileSync(filePath);
8659
8753
  } catch {
8660
8754
  error(`Cannot read file: ${filePath}`);
8661
8755
  process.exit(3);
@@ -8667,7 +8761,7 @@ function registerConversationsCommand(program2) {
8667
8761
  accessToken: loginResult.accessToken
8668
8762
  },
8669
8763
  blob,
8670
- path5.basename(filePath),
8764
+ path6.basename(filePath),
8671
8765
  opts.source ?? "auto"
8672
8766
  );
8673
8767
  if (opts.json) {
@@ -9806,7 +9900,7 @@ function registerWorkflowCommand(program2) {
9806
9900
  workflow.command("update <id>").description("Update any saved automation settings from a JSON file").requiredOption("--file <path>", "JSON UpdateWorkflowRequest file").option("--json", "Output as JSON").action(
9807
9901
  (id, opts) => runAction(async () => {
9808
9902
  const { arbi } = await resolveAuth();
9809
- const body = JSON.parse(fs5.readFileSync(opts.file, "utf8"));
9903
+ const body = JSON.parse(fs7.readFileSync(opts.file, "utf8"));
9810
9904
  const data = await sdk.workflows.updateWorkflow(arbi, id, body);
9811
9905
  if (opts.json) printJson(data);
9812
9906
  else success(`Updated automation ${ref(id)}`);
@@ -9843,15 +9937,15 @@ compdef _arbi_completions arbi
9843
9937
  ${MARKER_END}`;
9844
9938
  function getShellRcPath2() {
9845
9939
  const shell = process.env.SHELL || "";
9846
- if (shell.includes("zsh")) return path5.join(os2.homedir(), ".zshrc");
9847
- return path5.join(os2.homedir(), ".bashrc");
9940
+ if (shell.includes("zsh")) return path6.join(os2.homedir(), ".zshrc");
9941
+ return path6.join(os2.homedir(), ".bashrc");
9848
9942
  }
9849
9943
  function isZsh() {
9850
9944
  return (process.env.SHELL || "").includes("zsh");
9851
9945
  }
9852
9946
  function isCompletionInstalled(rcPath) {
9853
- if (!fs5.existsSync(rcPath)) return false;
9854
- const content = fs5.readFileSync(rcPath, "utf-8");
9947
+ if (!fs7.existsSync(rcPath)) return false;
9948
+ const content = fs7.readFileSync(rcPath, "utf-8");
9855
9949
  return content.includes(MARKER_START);
9856
9950
  }
9857
9951
  function removeCompletionBlock(content) {
@@ -9872,7 +9966,7 @@ function registerCompletionCommand(program2) {
9872
9966
  return;
9873
9967
  }
9874
9968
  const snippet = isZsh() ? ZSH_COMPLETION : BASH_COMPLETION;
9875
- fs5.appendFileSync(rcPath, snippet + "\n");
9969
+ fs7.appendFileSync(rcPath, snippet + "\n");
9876
9970
  success(`Installed tab completion in ${rcPath}`);
9877
9971
  dim("");
9878
9972
  dim(`Run: source ${rcPath}`);
@@ -9884,9 +9978,9 @@ function registerCompletionCommand(program2) {
9884
9978
  dim("Completion is not installed.");
9885
9979
  return;
9886
9980
  }
9887
- const content = fs5.readFileSync(rcPath, "utf-8");
9981
+ const content = fs7.readFileSync(rcPath, "utf-8");
9888
9982
  const cleaned = removeCompletionBlock(content);
9889
- fs5.writeFileSync(rcPath, cleaned);
9983
+ fs7.writeFileSync(rcPath, cleaned);
9890
9984
  success(`Removed tab completion from ${rcPath}`);
9891
9985
  dim(`Run: source ${rcPath}`);
9892
9986
  });
@@ -9902,7 +9996,7 @@ function registerCompletionCommand(program2) {
9902
9996
  function resolvePath(p) {
9903
9997
  const raw = p ?? ".";
9904
9998
  const expanded = raw.startsWith("~") ? raw.replace("~", os2.homedir()) : raw;
9905
- return path5.resolve(expanded);
9999
+ return path6.resolve(expanded);
9906
10000
  }
9907
10001
  function formatSize2(bytes) {
9908
10002
  if (bytes < 1024) return `${bytes}B`;
@@ -9912,7 +10006,7 @@ function formatSize2(bytes) {
9912
10006
  }
9913
10007
  function entryToRow(dir, name) {
9914
10008
  try {
9915
- const stat = fs5.statSync(path5.join(dir, name));
10009
+ const stat = fs7.statSync(path6.join(dir, name));
9916
10010
  if (stat.isDirectory()) return { name, type: "dir", size: null };
9917
10011
  if (stat.isSymbolicLink()) return { name, type: "symlink", size: stat.size };
9918
10012
  if (stat.isFile()) return { name, type: "file", size: stat.size };
@@ -9923,11 +10017,11 @@ function entryToRow(dir, name) {
9923
10017
  }
9924
10018
  function registerLocalCommand(program2) {
9925
10019
  const local = program2.command("local").description("Local filesystem operations");
9926
- local.command("ls [path]").description("List files in a directory").option("-l, --long", "Show file sizes and types (ignored when --json is set)").option("-a, --all", "Include dotfiles").option("--json", "Output as JSON array of {name, type, size}").action((path7, opts) => {
9927
- const dir = resolvePath(path7);
10020
+ local.command("ls [path]").description("List files in a directory").option("-l, --long", "Show file sizes and types (ignored when --json is set)").option("-a, --all", "Include dotfiles").option("--json", "Output as JSON array of {name, type, size}").action((path8, opts) => {
10021
+ const dir = resolvePath(path8);
9928
10022
  let entries;
9929
10023
  try {
9930
- entries = fs5.readdirSync(dir);
10024
+ entries = fs7.readdirSync(dir);
9931
10025
  } catch {
9932
10026
  console.error(`Cannot read directory: ${dir}`);
9933
10027
  process.exit(1);
@@ -9950,7 +10044,7 @@ function registerLocalCommand(program2) {
9950
10044
  });
9951
10045
  local.command("find <pattern>").description('Find files matching a glob (e.g. "**/*.pdf")').option("-d, --dir <path>", "Directory to search in", ".").option("--json", "Output as JSON array of matched paths").action((pattern, opts) => {
9952
10046
  const dir = resolvePath(opts.dir);
9953
- const results = fs5.globSync(pattern, { cwd: dir });
10047
+ const results = fs7.globSync(pattern, { cwd: dir });
9954
10048
  if (opts.json) {
9955
10049
  if (results.length === 0) {
9956
10050
  process.stderr.write(`No files matching "${pattern}" in ${dir}
@@ -9975,7 +10069,7 @@ function registerLocalCommand(program2) {
9975
10069
  const filePath = resolvePath(file);
9976
10070
  let content;
9977
10071
  try {
9978
- content = fs5.readFileSync(filePath, "utf-8");
10072
+ content = fs7.readFileSync(filePath, "utf-8");
9979
10073
  } catch {
9980
10074
  console.error(`Cannot read file: ${filePath}`);
9981
10075
  process.exit(1);
@@ -9990,31 +10084,31 @@ function registerLocalCommand(program2) {
9990
10084
  }
9991
10085
  console.log(content);
9992
10086
  });
9993
- local.command("tree [path]").description("Show directory tree").option("-d, --depth <n>", "Maximum depth", "3").option("--json", "Output as nested {name, type, children} tree").action((path7, opts) => {
9994
- const dir = resolvePath(path7);
10087
+ local.command("tree [path]").description("Show directory tree").option("-d, --depth <n>", "Maximum depth", "3").option("--json", "Output as nested {name, type, children} tree").action((path8, opts) => {
10088
+ const dir = resolvePath(path8);
9995
10089
  const maxDepth = parseInt(opts?.depth ?? "3", 10);
9996
10090
  if (opts?.json) {
9997
10091
  printJson(buildTree(dir, maxDepth, 0));
9998
10092
  return;
9999
10093
  }
10000
- console.log(path5.basename(dir) + "/");
10094
+ console.log(path6.basename(dir) + "/");
10001
10095
  printTree(dir, "", maxDepth, 0);
10002
10096
  });
10003
10097
  }
10004
10098
  function buildTree(dir, maxDepth, depth) {
10005
- const node = { name: path5.basename(dir), type: "dir", children: [] };
10099
+ const node = { name: path6.basename(dir), type: "dir", children: [] };
10006
10100
  if (depth >= maxDepth) return node;
10007
10101
  let entries;
10008
10102
  try {
10009
- entries = fs5.readdirSync(dir).filter((e) => !e.startsWith(".")).sort();
10103
+ entries = fs7.readdirSync(dir).filter((e) => !e.startsWith(".")).sort();
10010
10104
  } catch {
10011
10105
  return node;
10012
10106
  }
10013
10107
  for (const entry of entries) {
10014
- const full = path5.join(dir, entry);
10108
+ const full = path6.join(dir, entry);
10015
10109
  let isDir = false;
10016
10110
  try {
10017
- isDir = fs5.statSync(full).isDirectory();
10111
+ isDir = fs7.statSync(full).isDirectory();
10018
10112
  } catch {
10019
10113
  node.children.push({ name: entry, type: "other" });
10020
10114
  continue;
@@ -10031,7 +10125,7 @@ function printTree(dir, prefix, maxDepth, depth) {
10031
10125
  if (depth >= maxDepth) return;
10032
10126
  let entries;
10033
10127
  try {
10034
- entries = fs5.readdirSync(dir).filter((e) => !e.startsWith(".")).sort();
10128
+ entries = fs7.readdirSync(dir).filter((e) => !e.startsWith(".")).sort();
10035
10129
  } catch {
10036
10130
  return;
10037
10131
  }
@@ -10039,10 +10133,10 @@ function printTree(dir, prefix, maxDepth, depth) {
10039
10133
  const entry = entries[i];
10040
10134
  const isLast = i === entries.length - 1;
10041
10135
  const connector = isLast ? "\u2514\u2500\u2500 " : "\u251C\u2500\u2500 ";
10042
- const full = path5.join(dir, entry);
10136
+ const full = path6.join(dir, entry);
10043
10137
  let isDir = false;
10044
10138
  try {
10045
- isDir = fs5.statSync(full).isDirectory();
10139
+ isDir = fs7.statSync(full).isDirectory();
10046
10140
  } catch {
10047
10141
  continue;
10048
10142
  }
@@ -10123,7 +10217,7 @@ function parseAddress(value) {
10123
10217
  function collect(value, previous = []) {
10124
10218
  return [...previous, ...value.split(",").filter((v) => v.trim())];
10125
10219
  }
10126
- var readTextFile = (path7) => fs5__default.default.readFileSync(path7 === "-" ? 0 : path7, "utf8");
10220
+ var readTextFile = (path8) => fs7__default.default.readFileSync(path8 === "-" ? 0 : path8, "utf8");
10127
10221
  function buildCompose(options) {
10128
10222
  const read = options.readText ?? readTextFile;
10129
10223
  if (options.text !== void 0 && options.textFile) {
@@ -10245,7 +10339,7 @@ function registerMailCommand(program2) {
10245
10339
  if (!meta) throw new Error(`No attachment ${opts.attachment} on this message`);
10246
10340
  const bytes = await sdk.mail.downloadAttachment(fetch2, id, meta.id);
10247
10341
  const target = opts.output ?? meta.name;
10248
- fs5__default.default.writeFileSync(target, bytes);
10342
+ fs7__default.default.writeFileSync(target, bytes);
10249
10343
  success(`Saved ${target} (${bytes.length} bytes)`);
10250
10344
  return;
10251
10345
  }
@@ -10529,12 +10623,12 @@ function registerFilesCommand(program2) {
10529
10623
  })()
10530
10624
  );
10531
10625
  files.command("upload <path>").description("Upload a file (file ID is returned for use with arbi ask -b --attach)").option("--purpose <name>", "OpenAI purpose tag (default: assistants)", "assistants").action(
10532
- (path7, opts) => runAction(async () => {
10626
+ (path8, opts) => runAction(async () => {
10533
10627
  const { loginResult, config } = await resolveWorkspace();
10534
- const filePath = path5.resolve(path7);
10628
+ const filePath = path6.resolve(path8);
10535
10629
  let bytes;
10536
10630
  try {
10537
- bytes = fs5.readFileSync(filePath);
10631
+ bytes = fs7.readFileSync(filePath);
10538
10632
  } catch {
10539
10633
  error(`Cannot read file: ${filePath}`);
10540
10634
  process.exit(3);
@@ -10546,7 +10640,7 @@ function registerFilesCommand(program2) {
10546
10640
  accessToken: loginResult.accessToken
10547
10641
  },
10548
10642
  blob,
10549
- path5.basename(filePath),
10643
+ path6.basename(filePath),
10550
10644
  opts.purpose ?? "assistants"
10551
10645
  );
10552
10646
  success(`Uploaded: ${ref(data.id)} (${formatBytes(data.bytes ?? 0)})`);
@@ -10585,7 +10679,7 @@ function registerFilesCommand(program2) {
10585
10679
  process.stdout.write(buf);
10586
10680
  return;
10587
10681
  }
10588
- fs5.writeFileSync(path5.resolve(opts.output), buf);
10682
+ fs7.writeFileSync(path6.resolve(opts.output), buf);
10589
10683
  success(`Wrote ${formatBytes(buf.length)} \u2192 ${opts.output}`);
10590
10684
  })()
10591
10685
  );
@@ -10664,7 +10758,7 @@ function registerAuthCommand(program2) {
10664
10758
  let picture = opts.picture;
10665
10759
  if (picture && !/^https?:|^data:/.test(picture)) {
10666
10760
  try {
10667
- const bytes = fs5.readFileSync(path5.resolve(picture));
10761
+ const bytes = fs7.readFileSync(path6.resolve(picture));
10668
10762
  const ext = picture.toLowerCase().split(".").pop();
10669
10763
  const mime = ext === "png" ? "image/png" : ext === "gif" ? "image/gif" : "image/jpeg";
10670
10764
  picture = `data:${mime};base64,${bytes.toString("base64")}`;
@@ -10754,13 +10848,13 @@ async function loadSkillSource(source) {
10754
10848
  }
10755
10849
  return await res.text();
10756
10850
  }
10757
- const stat = fs5.statSync(source);
10851
+ const stat = fs7.statSync(source);
10758
10852
  if (stat.isDirectory()) {
10759
10853
  throw new Error(
10760
10854
  `${source} is a directory. Pass a single SKILL.md file, or use \`\`arbi upload --folder skills/<slug> --wp-type skill\`\` for multi-file skills.`
10761
10855
  );
10762
10856
  }
10763
- return fs5.readFileSync(source, "utf8");
10857
+ return fs7.readFileSync(source, "utf8");
10764
10858
  }
10765
10859
  function registerSkillsCommand(program2) {
10766
10860
  const skills = program2.command("skills").description("Skills: list, show");
@@ -10878,7 +10972,7 @@ console.info = (...args) => {
10878
10972
  _origInfo(...args);
10879
10973
  };
10880
10974
  var program = new commander.Command();
10881
- program.name("arbi").description("ARBI CLI \u2014 interact with ARBI from the terminal").version("0.4.48").showHelpAfterError(true).showSuggestionAfterError(true);
10975
+ program.name("arbi").description("ARBI CLI \u2014 interact with ARBI from the terminal").version("0.4.50").showHelpAfterError(true).showSuggestionAfterError(true);
10882
10976
  registerConfigCommand(program);
10883
10977
  registerLoginCommand(program);
10884
10978
  registerRegisterCommand(program);