@arbidocs/cli 0.4.109 → 0.4.111
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 +28 -0
- package/SKILL.md +5 -4
- package/dist/index.js +347 -114
- package/dist/index.js.map +1 -1
- package/package.json +4 -4
package/dist/index.js
CHANGED
|
@@ -3695,7 +3695,7 @@ function getLatestVersion(skipCache = false) {
|
|
|
3695
3695
|
}
|
|
3696
3696
|
}
|
|
3697
3697
|
function getCurrentVersion() {
|
|
3698
|
-
return "0.4.
|
|
3698
|
+
return "0.4.111";
|
|
3699
3699
|
}
|
|
3700
3700
|
function readChangelog(fromVersion, toVersion) {
|
|
3701
3701
|
try {
|
|
@@ -3748,17 +3748,17 @@ function showChangelog(fromVersion, toVersion) {
|
|
|
3748
3748
|
async function checkForUpdates(autoUpdate) {
|
|
3749
3749
|
try {
|
|
3750
3750
|
const latest = getLatestVersion();
|
|
3751
|
-
if (!latest || latest === "0.4.
|
|
3751
|
+
if (!latest || latest === "0.4.111") return;
|
|
3752
3752
|
if (autoUpdate) {
|
|
3753
3753
|
warn(`
|
|
3754
|
-
Your arbi version is out of date (${"0.4.
|
|
3754
|
+
Your arbi version is out of date (${"0.4.111"} \u2192 ${latest}). Updating...`);
|
|
3755
3755
|
child_process.execSync("npm install -g @arbidocs/cli@latest", { stdio: "inherit" });
|
|
3756
|
-
showChangelog("0.4.
|
|
3756
|
+
showChangelog("0.4.111", latest);
|
|
3757
3757
|
console.log(`Updated to ${latest}.`);
|
|
3758
3758
|
} else {
|
|
3759
3759
|
warn(
|
|
3760
3760
|
`
|
|
3761
|
-
Your arbi version is out of date (${"0.4.
|
|
3761
|
+
Your arbi version is out of date (${"0.4.111"} \u2192 ${latest}).
|
|
3762
3762
|
Run "arbi update" to upgrade, or "arbi update auto" to always stay up to date.`
|
|
3763
3763
|
);
|
|
3764
3764
|
}
|
|
@@ -3790,10 +3790,10 @@ function markNagShown(latest) {
|
|
|
3790
3790
|
function hintUpdateOnError() {
|
|
3791
3791
|
try {
|
|
3792
3792
|
const cached = readCache();
|
|
3793
|
-
if (!cached || cached.latest === "0.4.
|
|
3793
|
+
if (!cached || cached.latest === "0.4.111") return;
|
|
3794
3794
|
if (!shouldShowNag(cached.latest)) return;
|
|
3795
3795
|
warn(
|
|
3796
|
-
`Your arbi version is out of date (${"0.4.
|
|
3796
|
+
`Your arbi version is out of date (${"0.4.111"} \u2192 ${cached.latest}). Run "arbi update".`
|
|
3797
3797
|
);
|
|
3798
3798
|
markNagShown(cached.latest);
|
|
3799
3799
|
} catch {
|
|
@@ -4020,15 +4020,15 @@ async function resolveWorkspace(workspaceOpt, { skipNotifications = false } = {}
|
|
|
4020
4020
|
throw err;
|
|
4021
4021
|
}
|
|
4022
4022
|
}
|
|
4023
|
-
async function
|
|
4023
|
+
async function resolveChatCrypto() {
|
|
4024
4024
|
const authCtx = await resolveAuth();
|
|
4025
4025
|
const { arbi, loginResult } = authCtx;
|
|
4026
4026
|
const userExtId = arbi.session.getState().userExtId;
|
|
4027
4027
|
if (!userExtId) {
|
|
4028
|
-
error("No user ID in session \u2014 cannot set up
|
|
4028
|
+
error("No user ID in session \u2014 cannot set up chat encryption.");
|
|
4029
4029
|
process.exit(1);
|
|
4030
4030
|
}
|
|
4031
|
-
const crypto = sdk.
|
|
4031
|
+
const crypto = sdk.chats.createChatCryptoContext(loginResult.signingPrivateKey, userExtId);
|
|
4032
4032
|
return { ...authCtx, crypto };
|
|
4033
4033
|
}
|
|
4034
4034
|
function truncate(value, width) {
|
|
@@ -4261,7 +4261,7 @@ async function startListening(agentName, config) {
|
|
|
4261
4261
|
process.exit(1);
|
|
4262
4262
|
}
|
|
4263
4263
|
dim("Starting DM listener...");
|
|
4264
|
-
const { arbi, crypto } = await
|
|
4264
|
+
const { arbi, crypto } = await resolveChatCrypto();
|
|
4265
4265
|
const fullConfig = resolveConfig();
|
|
4266
4266
|
const sessionId = arbi.session.getState().userExtId ?? "default";
|
|
4267
4267
|
const agentId = backend === "openclaw" ? sessionId.toLowerCase() : void 0;
|
|
@@ -4876,7 +4876,7 @@ function readAttachmentFiles(paths) {
|
|
|
4876
4876
|
}
|
|
4877
4877
|
return { resolved, name: path6__default.default.basename(resolved), size: fs7__default.default.statSync(resolved).size };
|
|
4878
4878
|
});
|
|
4879
|
-
sdk.
|
|
4879
|
+
sdk.chats.checkChatAttachmentLimits(stats);
|
|
4880
4880
|
return stats.map(({ resolved, name }) => ({
|
|
4881
4881
|
name,
|
|
4882
4882
|
type: typeFromName(name),
|
|
@@ -4902,8 +4902,36 @@ function attachmentLabel(files) {
|
|
|
4902
4902
|
}
|
|
4903
4903
|
|
|
4904
4904
|
// src/commands/dm.ts
|
|
4905
|
+
var PER_CHAT = 100;
|
|
4905
4906
|
async function resolveRecipient(arbi, ref2) {
|
|
4906
|
-
return sdk.
|
|
4907
|
+
return sdk.chats.resolveRecipient(arbi, ref2);
|
|
4908
|
+
}
|
|
4909
|
+
async function listDmRows(arbi, crypto, options) {
|
|
4910
|
+
const direct = await sdk.chats.listChats(arbi, "direct");
|
|
4911
|
+
const rows = [];
|
|
4912
|
+
for (const chat of direct) {
|
|
4913
|
+
if (!chat.last_message) continue;
|
|
4914
|
+
const ring = await sdk.chats.openChatKeyRing(chat, crypto);
|
|
4915
|
+
const raw = await sdk.chats.listChatMessages(arbi, chat.external_id, {
|
|
4916
|
+
limit: PER_CHAT
|
|
4917
|
+
});
|
|
4918
|
+
const byId = new Map(chat.members.map((m) => [m.user.external_id, m]));
|
|
4919
|
+
const me = byId.get(crypto.userExtId);
|
|
4920
|
+
const peer = sdk.chats.directPeer(chat, crypto.userExtId);
|
|
4921
|
+
const peerMember = peer ? byId.get(peer.external_id) : me;
|
|
4922
|
+
for (const m of await sdk.chats.decryptChatMessages(ring, raw)) {
|
|
4923
|
+
const mine = m.sender_ext_id === crypto.userExtId;
|
|
4924
|
+
const sender = (mine ? me?.user : peer) ?? null;
|
|
4925
|
+
const recipient = (mine ? peer ?? me?.user : me?.user) ?? null;
|
|
4926
|
+
rows.push({
|
|
4927
|
+
...m,
|
|
4928
|
+
sender,
|
|
4929
|
+
recipient,
|
|
4930
|
+
read: sdk.chats.hasRead(mine ? peerMember : me, m)
|
|
4931
|
+
});
|
|
4932
|
+
}
|
|
4933
|
+
}
|
|
4934
|
+
return rows.sort((a, b) => Date.parse(b.created_at) - Date.parse(a.created_at));
|
|
4907
4935
|
}
|
|
4908
4936
|
function rowMatchesFilters(row, opts) {
|
|
4909
4937
|
if (opts.unread && row.read) return false;
|
|
@@ -4930,17 +4958,17 @@ function rowMatchesFilters(row, opts) {
|
|
|
4930
4958
|
}
|
|
4931
4959
|
return true;
|
|
4932
4960
|
}
|
|
4961
|
+
var unreadForMe = (rows, me) => rows.filter((r) => !r.read && r.sender_ext_id !== me);
|
|
4933
4962
|
function registerDmCommand(program2) {
|
|
4934
|
-
const dm = program2.command("dm").description("Direct messages (E2E encrypted)");
|
|
4963
|
+
const dm = program2.command("dm").description("Direct messages (E2E encrypted 1:1 chats)");
|
|
4935
4964
|
dm.command("list").description("List direct messages (decrypted, newest first)").option("--unread", "Only show unread messages").option("--from <email-or-extid>", "Filter by sender OR recipient (email or usr- id)").option(
|
|
4936
4965
|
"--thread <email-or-extid>",
|
|
4937
4966
|
"Show the full conversation with one peer (both directions)"
|
|
4938
4967
|
).option("--since <iso>", "Only messages newer than this ISO timestamp").option("-l, --limit <n>", "Cap to N most-recent messages", (v) => parseInt(v, 10)).option("--json", "Output as JSON (recommended for scripting / agents)").action(
|
|
4939
4968
|
(opts) => runAction(async () => {
|
|
4940
|
-
const { arbi, crypto } = await
|
|
4941
|
-
const
|
|
4942
|
-
const
|
|
4943
|
-
const dms = all.filter((r) => r.type === "user_message");
|
|
4969
|
+
const { arbi, crypto } = await resolveChatCrypto();
|
|
4970
|
+
const myExtId = crypto.userExtId;
|
|
4971
|
+
const all = await listDmRows(arbi, crypto);
|
|
4944
4972
|
const filterOpts = {
|
|
4945
4973
|
unread: Boolean(opts.unread),
|
|
4946
4974
|
from: typeof opts.from === "string" ? opts.from : void 0,
|
|
@@ -4948,17 +4976,14 @@ function registerDmCommand(program2) {
|
|
|
4948
4976
|
since: typeof opts.since === "string" ? opts.since : void 0,
|
|
4949
4977
|
myExtId
|
|
4950
4978
|
};
|
|
4951
|
-
const filtered =
|
|
4979
|
+
const filtered = all.filter((r) => rowMatchesFilters(r, filterOpts)).filter((r) => !filterOpts.unread || r.sender_ext_id !== myExtId);
|
|
4952
4980
|
const limited = typeof opts.limit === "number" && opts.limit > 0 ? filtered.slice(0, opts.limit) : filtered;
|
|
4953
4981
|
if (opts.json) {
|
|
4954
4982
|
printJson(
|
|
4955
|
-
limited.map(({ attachmentFiles, ...r }) => ({
|
|
4983
|
+
limited.map(({ attachmentFiles, decryptFailed, ...r }) => ({
|
|
4956
4984
|
...r,
|
|
4957
|
-
...attachmentFiles ? {
|
|
4958
|
-
|
|
4959
|
-
attachmentFiles
|
|
4960
|
-
)
|
|
4961
|
-
} : {},
|
|
4985
|
+
...attachmentFiles ? { attachment_files: publicAttachmentFiles(attachmentFiles) } : {},
|
|
4986
|
+
...decryptFailed ? { decrypt_failed: true } : {},
|
|
4962
4987
|
encrypted_in_transit: true
|
|
4963
4988
|
}))
|
|
4964
4989
|
);
|
|
@@ -4979,7 +5004,7 @@ function registerDmCommand(program2) {
|
|
|
4979
5004
|
{
|
|
4980
5005
|
header: "DIR",
|
|
4981
5006
|
width: 4,
|
|
4982
|
-
value: (r) => r.
|
|
5007
|
+
value: (r) => r.sender_ext_id === myExtId ? "\u2192" : "\u2190"
|
|
4983
5008
|
},
|
|
4984
5009
|
{
|
|
4985
5010
|
header: "PEER",
|
|
@@ -5003,14 +5028,14 @@ function registerDmCommand(program2) {
|
|
|
5003
5028
|
);
|
|
5004
5029
|
})()
|
|
5005
5030
|
);
|
|
5006
|
-
dm.command("send [recipient] [content...]").description("Send an E2E encrypted
|
|
5031
|
+
dm.command("send [recipient] [content...]").description("Send an E2E encrypted direct message (interactive if no args)").option(
|
|
5007
5032
|
"-a, --attach <path>",
|
|
5008
5033
|
"Attach a file, encrypted end to end (repeatable; up to 10 files of 15 MB each)",
|
|
5009
5034
|
collectPaths
|
|
5010
5035
|
).action(
|
|
5011
5036
|
(recipient, contentParts, opts) => runAction(async () => {
|
|
5012
5037
|
const attachmentInputs = readAttachmentFiles(opts?.attach ?? []);
|
|
5013
|
-
const { arbi, crypto } = await
|
|
5038
|
+
const { arbi, crypto } = await resolveChatCrypto();
|
|
5014
5039
|
if (!recipient) {
|
|
5015
5040
|
requireInteractive(
|
|
5016
5041
|
'Pass recipient + message as arguments: arbi dm send <email-or-id> "<message>"'
|
|
@@ -5049,30 +5074,11 @@ Try: arbi contacts add ${recipient.includes("@") ? recipient : "<their-email>"}`
|
|
|
5049
5074
|
);
|
|
5050
5075
|
process.exit(3);
|
|
5051
5076
|
}
|
|
5052
|
-
const
|
|
5053
|
-
const
|
|
5054
|
-
|
|
5055
|
-
|
|
5056
|
-
|
|
5057
|
-
);
|
|
5058
|
-
process.exit(3);
|
|
5059
|
-
}
|
|
5060
|
-
const attachments = await sdk.dm.uploadDmAttachments(arbi, attachmentInputs);
|
|
5061
|
-
const data = await sdk.dm.sendEncryptedDM(
|
|
5062
|
-
arbi,
|
|
5063
|
-
[
|
|
5064
|
-
{
|
|
5065
|
-
recipient_ext_id: recipientExtId,
|
|
5066
|
-
content,
|
|
5067
|
-
recipient_encryption_public_key: recipientPubKey
|
|
5068
|
-
}
|
|
5069
|
-
],
|
|
5070
|
-
crypto,
|
|
5071
|
-
{ attachments }
|
|
5072
|
-
);
|
|
5073
|
-
for (const n of data) {
|
|
5074
|
-
success(`Sent (encrypted): ${n.external_id} \u2192 ${n.recipient.email}`);
|
|
5075
|
-
}
|
|
5077
|
+
const attachments = await sdk.chats.uploadChatAttachments(arbi, attachmentInputs);
|
|
5078
|
+
const sent = await sdk.chats.sendDirectMessage(arbi, crypto, resolved.extId, content, {
|
|
5079
|
+
attachments
|
|
5080
|
+
});
|
|
5081
|
+
success(`Sent (encrypted): ${sent.external_id} \u2192 ${recipient} (chat ${sent.chat_ext_id})`);
|
|
5076
5082
|
for (const f of attachments) {
|
|
5077
5083
|
success(`Attached (encrypted): ${f.id} ${f.name} (${f.size} bytes)`);
|
|
5078
5084
|
}
|
|
@@ -5085,18 +5091,19 @@ Try: arbi contacts add ${recipient.includes("@") ? recipient : "<their-email>"}`
|
|
|
5085
5091
|
"Save this attachment of the message (ids from `arbi dm list --json`) instead"
|
|
5086
5092
|
).option("-o, --output <path>", "Where to save the attachment (default: its file name)").action(
|
|
5087
5093
|
(ids, opts) => runAction(async () => {
|
|
5088
|
-
const { arbi, crypto } = await
|
|
5094
|
+
const { arbi, crypto } = await resolveChatCrypto();
|
|
5095
|
+
const me = crypto.userExtId;
|
|
5089
5096
|
let msgIds = ids && ids.length > 0 ? ids : void 0;
|
|
5097
|
+
const rows = await listDmRows(arbi, crypto);
|
|
5090
5098
|
if (typeof opts?.attachment === "string") {
|
|
5091
5099
|
if (msgIds?.length !== 1) {
|
|
5092
|
-
throw new Error("Name exactly one message: arbi dm read <
|
|
5100
|
+
throw new Error("Name exactly one message: arbi dm read <chm-id> --attachment <att-id>");
|
|
5093
5101
|
}
|
|
5094
|
-
const rows = await sdk.dm.listDecryptedDMs(arbi, crypto);
|
|
5095
5102
|
const row = rows.find((m) => m.external_id === msgIds[0]);
|
|
5096
5103
|
if (!row) throw new Error(`No message ${msgIds[0]}`);
|
|
5097
5104
|
if (row.attachmentsError) throw new Error(row.attachmentsError);
|
|
5098
5105
|
const file = findAttachment(row, opts.attachment);
|
|
5099
|
-
const bytes = await sdk.
|
|
5106
|
+
const bytes = await sdk.chats.downloadChatAttachment(arbi, file);
|
|
5100
5107
|
const target = attachmentOutputPath(
|
|
5101
5108
|
file,
|
|
5102
5109
|
typeof opts.output === "string" ? opts.output : void 0
|
|
@@ -5105,13 +5112,9 @@ Try: arbi contacts add ${recipient.includes("@") ? recipient : "<their-email>"}`
|
|
|
5105
5112
|
success(`Saved ${target} (${bytes.length} bytes, decrypted)`);
|
|
5106
5113
|
return;
|
|
5107
5114
|
}
|
|
5108
|
-
|
|
5109
|
-
|
|
5110
|
-
msgIds = data2.filter((m) => !m.read).map((m) => m.external_id);
|
|
5111
|
-
}
|
|
5115
|
+
const unread = unreadForMe(rows, me);
|
|
5116
|
+
if (!msgIds && opts?.all) msgIds = unread.map((m) => m.external_id);
|
|
5112
5117
|
if (!msgIds) {
|
|
5113
|
-
const data2 = await sdk.dm.listDecryptedDMs(arbi, crypto);
|
|
5114
|
-
const unread = data2.filter((m) => !m.read);
|
|
5115
5118
|
if (unread.length === 0) {
|
|
5116
5119
|
process.stderr.write("No unread messages.\n");
|
|
5117
5120
|
return;
|
|
@@ -5120,8 +5123,7 @@ Try: arbi contacts add ${recipient.includes("@") ? recipient : "<their-email>"}`
|
|
|
5120
5123
|
msgIds = await promptCheckbox(
|
|
5121
5124
|
"Select messages to mark as read",
|
|
5122
5125
|
unread.map((m) => {
|
|
5123
|
-
const
|
|
5124
|
-
const from = sdk.formatUserName(s) || s?.email || "";
|
|
5126
|
+
const from = sdk.formatUserName(m.sender) || m.sender?.email || "";
|
|
5125
5127
|
return {
|
|
5126
5128
|
name: `${from}: ${(m.content ?? "").slice(0, 50)}`,
|
|
5127
5129
|
value: m.external_id
|
|
@@ -5134,32 +5136,42 @@ Try: arbi contacts add ${recipient.includes("@") ? recipient : "<their-email>"}`
|
|
|
5134
5136
|
process.stderr.write("No unread messages.\n");
|
|
5135
5137
|
return;
|
|
5136
5138
|
}
|
|
5137
|
-
const
|
|
5138
|
-
|
|
5139
|
+
const wanted = new Set(msgIds);
|
|
5140
|
+
const targets = rows.filter((r) => wanted.has(r.external_id));
|
|
5141
|
+
const missing = msgIds.filter((id) => !targets.some((r) => r.external_id === id));
|
|
5142
|
+
if (missing.length) throw new Error(`No message ${missing.join(", ")}`);
|
|
5143
|
+
const newestPerChat = /* @__PURE__ */ new Map();
|
|
5144
|
+
for (const r of targets) {
|
|
5145
|
+
const cur = newestPerChat.get(r.chat_ext_id);
|
|
5146
|
+
if (!cur || Date.parse(r.created_at) > Date.parse(cur.created_at)) {
|
|
5147
|
+
newestPerChat.set(r.chat_ext_id, r);
|
|
5148
|
+
}
|
|
5149
|
+
}
|
|
5150
|
+
for (const [chatExtId, r] of newestPerChat) {
|
|
5151
|
+
await sdk.chats.markChatRead(arbi, chatExtId, r.external_id);
|
|
5152
|
+
}
|
|
5153
|
+
success(`Marked ${targets.length} message(s) as read.`);
|
|
5139
5154
|
})()
|
|
5140
5155
|
);
|
|
5141
|
-
dm.command("delete [ids...]").description("Delete messages (interactive picker if no IDs given)").option("--all", "Delete every message in
|
|
5156
|
+
dm.command("delete [ids...]").description("Delete your own messages (interactive picker if no IDs given)").option("--all", "Delete every message you sent in direct chats (no picker, no prompt)").option("--dry-run", "Preview which messages would be deleted (no SDK call)").action(
|
|
5142
5157
|
(ids, opts) => runAction(async () => {
|
|
5143
|
-
const { arbi, crypto } = await
|
|
5158
|
+
const { arbi, crypto } = await resolveChatCrypto();
|
|
5159
|
+
const rows = await listDmRows(arbi, crypto);
|
|
5160
|
+
const own = rows.filter((r) => r.sender_ext_id === crypto.userExtId);
|
|
5144
5161
|
let msgIds = ids && ids.length > 0 ? ids : void 0;
|
|
5145
|
-
if (!msgIds && opts?.all)
|
|
5146
|
-
const data = await sdk.dm.listDecryptedDMs(arbi, crypto);
|
|
5147
|
-
msgIds = data.map((m) => m.external_id);
|
|
5148
|
-
}
|
|
5162
|
+
if (!msgIds && opts?.all) msgIds = own.map((m) => m.external_id);
|
|
5149
5163
|
if (!msgIds) {
|
|
5150
|
-
|
|
5151
|
-
if (data.length === 0) {
|
|
5164
|
+
if (own.length === 0) {
|
|
5152
5165
|
process.stderr.write("No messages found.\n");
|
|
5153
5166
|
return;
|
|
5154
5167
|
}
|
|
5155
|
-
requireInteractive("Pass IDs directly or use --all to delete
|
|
5168
|
+
requireInteractive("Pass IDs directly or use --all to delete all your messages.");
|
|
5156
5169
|
msgIds = await promptCheckbox(
|
|
5157
5170
|
"Select messages to delete",
|
|
5158
|
-
|
|
5159
|
-
const
|
|
5160
|
-
const from = sdk.formatUserName(s) || s?.email || "";
|
|
5171
|
+
own.map((m) => {
|
|
5172
|
+
const to = sdk.formatUserName(m.recipient) || m.recipient?.email || "";
|
|
5161
5173
|
return {
|
|
5162
|
-
name:
|
|
5174
|
+
name: `\u2192 ${to}: ${(m.content ?? "").slice(0, 50)}`,
|
|
5163
5175
|
value: m.external_id
|
|
5164
5176
|
};
|
|
5165
5177
|
})
|
|
@@ -5170,11 +5182,16 @@ Try: arbi contacts add ${recipient.includes("@") ? recipient : "<their-email>"}`
|
|
|
5170
5182
|
process.stderr.write("No messages to delete.\n");
|
|
5171
5183
|
return;
|
|
5172
5184
|
}
|
|
5185
|
+
const chatOf = new Map(rows.map((r) => [r.external_id, r.chat_ext_id]));
|
|
5186
|
+
const unknown = msgIds.filter((id) => !chatOf.has(id));
|
|
5187
|
+
if (unknown.length) throw new Error(`No message ${unknown.join(", ")}`);
|
|
5173
5188
|
if (opts?.dryRun) {
|
|
5174
5189
|
dryRun(`delete ${msgIds.length} message(s)`, msgIds);
|
|
5175
5190
|
return;
|
|
5176
5191
|
}
|
|
5177
|
-
|
|
5192
|
+
for (const id of msgIds) {
|
|
5193
|
+
await sdk.chats.deleteChatMessage(arbi, chatOf.get(id), id);
|
|
5194
|
+
}
|
|
5178
5195
|
success(`Deleted ${msgIds.length} message(s).`);
|
|
5179
5196
|
})()
|
|
5180
5197
|
);
|
|
@@ -5538,7 +5555,7 @@ function registerWorkspacesCommand(program2) {
|
|
|
5538
5555
|
"Target a specific workspace id (advanced \u2014 usually omitted; the owner picks)"
|
|
5539
5556
|
).action(
|
|
5540
5557
|
(owner, opts) => runAction(async () => {
|
|
5541
|
-
const { arbi, crypto, loginResult } = await
|
|
5558
|
+
const { arbi, crypto, loginResult } = await resolveChatCrypto();
|
|
5542
5559
|
const resolved = await resolveRecipient(arbi, owner);
|
|
5543
5560
|
if (!resolved || !resolved.pubKey) {
|
|
5544
5561
|
error(
|
|
@@ -5547,7 +5564,7 @@ Try: arbi contacts add ${owner.includes("@") ? owner : "<their-email>"}`
|
|
|
5547
5564
|
);
|
|
5548
5565
|
process.exit(3);
|
|
5549
5566
|
}
|
|
5550
|
-
await sdk.
|
|
5567
|
+
await sdk.notifications.requestWorkspaceAccess(
|
|
5551
5568
|
arbi,
|
|
5552
5569
|
{
|
|
5553
5570
|
recipientExtId: resolved.extId,
|
|
@@ -8302,6 +8319,10 @@ Timeout (${timeoutSec}s), closing.`));
|
|
|
8302
8319
|
);
|
|
8303
8320
|
}
|
|
8304
8321
|
init_prompts();
|
|
8322
|
+
var asContact = (row) => row;
|
|
8323
|
+
function contactLabel(c) {
|
|
8324
|
+
return c.card?.name || sdk.formatUserName(c.user) || "";
|
|
8325
|
+
}
|
|
8305
8326
|
function redactPictures(rows) {
|
|
8306
8327
|
return rows.map((row) => {
|
|
8307
8328
|
const user = row.user;
|
|
@@ -8317,7 +8338,7 @@ function redactPictures(rows) {
|
|
|
8317
8338
|
});
|
|
8318
8339
|
}
|
|
8319
8340
|
function registerContactsCommand(program2) {
|
|
8320
|
-
const contacts = program2.command("contacts").description("Contacts: list, add, remove");
|
|
8341
|
+
const contacts = program2.command("contacts").description("Contacts: list, add, search, remove");
|
|
8321
8342
|
contacts.command("list").description("List all contacts").option("--json", "Output as JSON").option("--include-pictures", "Keep base64 profile-picture bytes in --json output").action(
|
|
8322
8343
|
(opts) => runAction(async () => {
|
|
8323
8344
|
const { arbi } = await resolveAuth();
|
|
@@ -8332,20 +8353,20 @@ function registerContactsCommand(program2) {
|
|
|
8332
8353
|
}
|
|
8333
8354
|
printTable(
|
|
8334
8355
|
[
|
|
8335
|
-
{ header: "ID", width: 16, value: (r) => r.external_id },
|
|
8356
|
+
{ header: "ID", width: 16, value: (r) => asContact(r).external_id ?? "" },
|
|
8357
|
+
{ header: "NAME", width: 20, value: (r) => contactLabel(asContact(r)) },
|
|
8336
8358
|
{
|
|
8337
|
-
header: "
|
|
8338
|
-
width:
|
|
8339
|
-
value: (r) =>
|
|
8359
|
+
header: "EMAIL",
|
|
8360
|
+
width: 30,
|
|
8361
|
+
value: (r) => asContact(r).email ?? asContact(r).card?.emails?.[0]?.email ?? ""
|
|
8340
8362
|
},
|
|
8341
|
-
{ header: "
|
|
8342
|
-
{ header: "STATUS", width: 18, value: (r) => r.status }
|
|
8363
|
+
{ header: "STATUS", width: 18, value: (r) => asContact(r).status }
|
|
8343
8364
|
],
|
|
8344
8365
|
data
|
|
8345
8366
|
);
|
|
8346
8367
|
})()
|
|
8347
8368
|
);
|
|
8348
|
-
contacts.command("add [emails...]").description("
|
|
8369
|
+
contacts.command("add [emails...]").description("Invite or link people by email (prompt if no emails given)").action(
|
|
8349
8370
|
(emails) => runAction(async () => {
|
|
8350
8371
|
const { arbi } = await resolveAuth();
|
|
8351
8372
|
if (!emails || emails.length === 0) {
|
|
@@ -8354,12 +8375,39 @@ function registerContactsCommand(program2) {
|
|
|
8354
8375
|
emails = input2.split(",").map((e) => e.trim()).filter(Boolean);
|
|
8355
8376
|
if (emails.length === 0) return;
|
|
8356
8377
|
}
|
|
8357
|
-
const data = await sdk.contacts.
|
|
8378
|
+
const data = await sdk.contacts.inviteContacts(arbi, emails);
|
|
8358
8379
|
for (const c of data) {
|
|
8359
|
-
|
|
8380
|
+
if (c.status === "failed") {
|
|
8381
|
+
process.stderr.write(`Failed: ${c.email} \u2014 ${c.outcome_detail ?? "not invited"}
|
|
8382
|
+
`);
|
|
8383
|
+
} else {
|
|
8384
|
+
success(`Added: ${c.email} (${c.external_id}) \u2014 ${c.outcome ?? c.status}`);
|
|
8385
|
+
}
|
|
8360
8386
|
}
|
|
8361
8387
|
})()
|
|
8362
8388
|
);
|
|
8389
|
+
contacts.command("search <query>").description("Search your contacts and people who share a workspace with you").option("--json", "Output as JSON").action(
|
|
8390
|
+
(query, opts) => runAction(async () => {
|
|
8391
|
+
const { arbi } = await resolveAuth();
|
|
8392
|
+
const data = await sdk.contacts.searchContacts(arbi, query);
|
|
8393
|
+
if (opts.json) {
|
|
8394
|
+
printJson(data);
|
|
8395
|
+
return;
|
|
8396
|
+
}
|
|
8397
|
+
if (data.length === 0) {
|
|
8398
|
+
process.stderr.write("No matches.\n");
|
|
8399
|
+
return;
|
|
8400
|
+
}
|
|
8401
|
+
printTable(
|
|
8402
|
+
[
|
|
8403
|
+
{ header: "NAME", width: 24, value: (r) => r.name ?? "" },
|
|
8404
|
+
{ header: "EMAIL", width: 30, value: (r) => r.email },
|
|
8405
|
+
{ header: "SOURCE", width: 10, value: (r) => r.source }
|
|
8406
|
+
],
|
|
8407
|
+
data
|
|
8408
|
+
);
|
|
8409
|
+
})()
|
|
8410
|
+
);
|
|
8363
8411
|
contacts.command("remove [ids...]").description("Remove contacts (interactive picker if no IDs given)").option("--dry-run", "Preview which contacts would be removed (no SDK call)").action(
|
|
8364
8412
|
(ids, opts) => runAction(async () => {
|
|
8365
8413
|
const { arbi } = await resolveAuth();
|
|
@@ -8373,10 +8421,11 @@ function registerContactsCommand(program2) {
|
|
|
8373
8421
|
requireInteractive("Pass contact IDs directly: arbi contacts remove cnt-\u2026");
|
|
8374
8422
|
contactIds = await promptCheckbox(
|
|
8375
8423
|
"Select contacts to remove",
|
|
8376
|
-
data.map((c) => {
|
|
8377
|
-
const name =
|
|
8424
|
+
data.filter((c) => c.external_id).map((c) => {
|
|
8425
|
+
const name = contactLabel(c);
|
|
8426
|
+
const email = c.email ?? "";
|
|
8378
8427
|
return {
|
|
8379
|
-
name: name ? `${name} (${
|
|
8428
|
+
name: name && email ? `${name} (${email})` : name || email || "(no name)",
|
|
8380
8429
|
value: c.external_id
|
|
8381
8430
|
};
|
|
8382
8431
|
})
|
|
@@ -8387,7 +8436,7 @@ function registerContactsCommand(program2) {
|
|
|
8387
8436
|
dryRun(`remove ${contactIds.length} contact(s)`, contactIds);
|
|
8388
8437
|
return;
|
|
8389
8438
|
}
|
|
8390
|
-
await sdk.contacts.
|
|
8439
|
+
for (const id of contactIds) await sdk.contacts.deleteContact(arbi, id);
|
|
8391
8440
|
success(`Removed ${contactIds.length} contact(s).`);
|
|
8392
8441
|
})()
|
|
8393
8442
|
);
|
|
@@ -8396,6 +8445,190 @@ function registerContactsCommand(program2) {
|
|
|
8396
8445
|
await contacts.commands.find((c) => c.name() === "list").parseAsync(tail, { from: "user" });
|
|
8397
8446
|
});
|
|
8398
8447
|
}
|
|
8448
|
+
async function resolveMembers(arbi, refs) {
|
|
8449
|
+
const out = [];
|
|
8450
|
+
for (const ref2 of refs) {
|
|
8451
|
+
const r = await resolveRecipient(arbi, ref2);
|
|
8452
|
+
if (!r) {
|
|
8453
|
+
error(`No contact or workspace member found for: ${ref2}`);
|
|
8454
|
+
process.exit(3);
|
|
8455
|
+
}
|
|
8456
|
+
if (sdk.chats.isAgentExtId(r.extId)) {
|
|
8457
|
+
error(`${ref2} is a personal agent: agents cannot join a group chat`);
|
|
8458
|
+
process.exit(3);
|
|
8459
|
+
}
|
|
8460
|
+
out.push({ external_id: r.extId, encryption_public_key: r.pubKey });
|
|
8461
|
+
}
|
|
8462
|
+
return out;
|
|
8463
|
+
}
|
|
8464
|
+
async function readyChat(arbi, crypto, chatExtId) {
|
|
8465
|
+
return sdk.chats.ensureChatKeyCurrent(arbi, await sdk.chats.getChat(arbi, chatExtId), crypto);
|
|
8466
|
+
}
|
|
8467
|
+
function registerChatCommand(program2) {
|
|
8468
|
+
const chat = program2.command("chat").description("Chats: direct and group (E2E encrypted)");
|
|
8469
|
+
chat.command("list").description("List your chats, most recently active first").option("--kind <kind>", "direct | group | meeting").option("--json", "Output as JSON").action(
|
|
8470
|
+
(opts) => runAction(async () => {
|
|
8471
|
+
const { arbi, crypto } = await resolveChatCrypto();
|
|
8472
|
+
const list = await sdk.chats.listChats(arbi, opts.kind);
|
|
8473
|
+
const rows = await Promise.all(
|
|
8474
|
+
list.map(async (c) => {
|
|
8475
|
+
const ring = await sdk.chats.openChatKeyRing(c, crypto);
|
|
8476
|
+
const peer = sdk.chats.directPeer(c, crypto.userExtId);
|
|
8477
|
+
const name = c.kind === "direct" ? sdk.formatUserName(peer) || peer?.email || "(yourself)" : await sdk.chats.decryptChatTitle(ring, c) ?? c.members.filter((m) => m.user.external_id !== crypto.userExtId).map((m) => sdk.formatUserName(m.user) || m.user.email).join(", ");
|
|
8478
|
+
return {
|
|
8479
|
+
external_id: c.external_id,
|
|
8480
|
+
kind: c.kind,
|
|
8481
|
+
name,
|
|
8482
|
+
members: c.members.length,
|
|
8483
|
+
unread: c.unread_count,
|
|
8484
|
+
last_message_at: c.last_message_at ?? null
|
|
8485
|
+
};
|
|
8486
|
+
})
|
|
8487
|
+
);
|
|
8488
|
+
if (opts.json) {
|
|
8489
|
+
printJson(rows);
|
|
8490
|
+
return;
|
|
8491
|
+
}
|
|
8492
|
+
if (rows.length === 0) {
|
|
8493
|
+
process.stderr.write("No chats.\n");
|
|
8494
|
+
return;
|
|
8495
|
+
}
|
|
8496
|
+
printTable(
|
|
8497
|
+
[
|
|
8498
|
+
{ header: "ID", width: 14, value: (r) => r.external_id },
|
|
8499
|
+
{ header: "KIND", width: 8, value: (r) => r.kind },
|
|
8500
|
+
{ header: "NAME", width: 36, value: (r) => truncate(r.name, 35) },
|
|
8501
|
+
{ header: "MEMBERS", width: 8, value: (r) => String(r.members) },
|
|
8502
|
+
{ header: "UNREAD", width: 7, value: (r) => String(r.unread) }
|
|
8503
|
+
],
|
|
8504
|
+
rows
|
|
8505
|
+
);
|
|
8506
|
+
})()
|
|
8507
|
+
);
|
|
8508
|
+
chat.command("create").description("Create a group chat with at least one other person (no agents)").requiredOption(
|
|
8509
|
+
"-m, --member <email-or-id>",
|
|
8510
|
+
"A member (repeatable)",
|
|
8511
|
+
(v, prev = []) => [...prev, v]
|
|
8512
|
+
).option("-t, --title <title>", "Group title (encrypted)").action(
|
|
8513
|
+
(opts) => runAction(async () => {
|
|
8514
|
+
const { arbi, crypto } = await resolveChatCrypto();
|
|
8515
|
+
const members = await resolveMembers(arbi, opts.member);
|
|
8516
|
+
const created = await sdk.chats.createGroupChat(arbi, crypto, {
|
|
8517
|
+
title: opts.title,
|
|
8518
|
+
members
|
|
8519
|
+
});
|
|
8520
|
+
success(
|
|
8521
|
+
`Created group (encrypted): ${created.external_id} (${created.members.length} members)`
|
|
8522
|
+
);
|
|
8523
|
+
})()
|
|
8524
|
+
);
|
|
8525
|
+
chat.command("send <chatId> <text...>").description("Post a message to a chat").action(
|
|
8526
|
+
(chatId, textParts) => runAction(async () => {
|
|
8527
|
+
const { arbi, crypto } = await resolveChatCrypto();
|
|
8528
|
+
const c = await readyChat(arbi, crypto, chatId);
|
|
8529
|
+
const sent = await sdk.chats.sendChatMessage(
|
|
8530
|
+
arbi,
|
|
8531
|
+
await sdk.chats.openChatKeyRing(c, crypto),
|
|
8532
|
+
textParts.join(" ")
|
|
8533
|
+
);
|
|
8534
|
+
success(`Sent (encrypted): ${sent.external_id} \u2192 ${chatId}`);
|
|
8535
|
+
})()
|
|
8536
|
+
);
|
|
8537
|
+
chat.command("show <chatId>").description("Show the newest messages of a chat, decrypted").option("-l, --limit <n>", "How many messages", (v) => parseInt(v, 10)).option("--json", "Output as JSON").action(
|
|
8538
|
+
(chatId, opts) => runAction(async () => {
|
|
8539
|
+
const { arbi, crypto } = await resolveChatCrypto();
|
|
8540
|
+
const loaded = await sdk.chats.loadDecryptedChat(
|
|
8541
|
+
arbi,
|
|
8542
|
+
crypto,
|
|
8543
|
+
await sdk.chats.getChat(arbi, chatId),
|
|
8544
|
+
{ limit: opts.limit }
|
|
8545
|
+
);
|
|
8546
|
+
const names = new Map(
|
|
8547
|
+
loaded.chat.members.map((m) => [
|
|
8548
|
+
m.user.external_id,
|
|
8549
|
+
sdk.formatUserName(m.user) || m.user.email
|
|
8550
|
+
])
|
|
8551
|
+
);
|
|
8552
|
+
const rows = loaded.messages.map(({ attachmentFiles, ...m }) => ({
|
|
8553
|
+
...m,
|
|
8554
|
+
...attachmentFiles ? { attachment_files: publicAttachmentFiles(attachmentFiles) } : {},
|
|
8555
|
+
sender: m.sender_ext_id ? names.get(m.sender_ext_id) ?? m.sender_ext_id : ""
|
|
8556
|
+
}));
|
|
8557
|
+
if (opts.json) {
|
|
8558
|
+
printJson({ ...loaded.chat, title: loaded.title, messages: rows });
|
|
8559
|
+
return;
|
|
8560
|
+
}
|
|
8561
|
+
printTable(
|
|
8562
|
+
[
|
|
8563
|
+
{ header: "ID", width: 14, value: (r) => r.external_id },
|
|
8564
|
+
{ header: "FROM", width: 24, value: (r) => truncate(r.sender, 23) },
|
|
8565
|
+
{
|
|
8566
|
+
header: "CONTENT",
|
|
8567
|
+
width: 60,
|
|
8568
|
+
value: (r) => truncate(r.content ?? "", 59)
|
|
8569
|
+
}
|
|
8570
|
+
],
|
|
8571
|
+
rows
|
|
8572
|
+
);
|
|
8573
|
+
})()
|
|
8574
|
+
);
|
|
8575
|
+
chat.command("members <chatId>").description("List the members of a chat").action(
|
|
8576
|
+
(chatId) => runAction(async () => {
|
|
8577
|
+
const { arbi } = await resolveChatCrypto();
|
|
8578
|
+
const c = await sdk.chats.getChat(arbi, chatId);
|
|
8579
|
+
printTable(
|
|
8580
|
+
[
|
|
8581
|
+
{ header: "ID", width: 14, value: (r) => r.id },
|
|
8582
|
+
{ header: "NAME", width: 28, value: (r) => r.name },
|
|
8583
|
+
{ header: "ROLE", width: 8, value: (r) => r.role }
|
|
8584
|
+
],
|
|
8585
|
+
c.members.map((m) => ({
|
|
8586
|
+
id: m.user.external_id,
|
|
8587
|
+
name: sdk.formatUserName(m.user) || m.user.email,
|
|
8588
|
+
role: m.role
|
|
8589
|
+
}))
|
|
8590
|
+
);
|
|
8591
|
+
})()
|
|
8592
|
+
);
|
|
8593
|
+
chat.command("add <chatId> <refs...>").description("Add people to a group (they can read the current epoch)").action(
|
|
8594
|
+
(chatId, refs) => runAction(async () => {
|
|
8595
|
+
const { arbi, crypto } = await resolveChatCrypto();
|
|
8596
|
+
const members = await resolveMembers(arbi, refs);
|
|
8597
|
+
const c = await readyChat(arbi, crypto, chatId);
|
|
8598
|
+
const updated = await sdk.chats.addChatMembers(
|
|
8599
|
+
arbi,
|
|
8600
|
+
await sdk.chats.openChatKeyRing(c, crypto),
|
|
8601
|
+
members
|
|
8602
|
+
);
|
|
8603
|
+
success(`Added ${members.length} member(s) to ${chatId} (${updated.members.length} now)`);
|
|
8604
|
+
})()
|
|
8605
|
+
);
|
|
8606
|
+
chat.command("remove <chatId> <userId>").description("Remove a member (owners only); a new epoch key is sealed to the rest").action(
|
|
8607
|
+
(chatId, userId) => runAction(async () => {
|
|
8608
|
+
const { arbi, crypto } = await resolveChatCrypto();
|
|
8609
|
+
await sdk.chats.removeChatMember(arbi, chatId, userId);
|
|
8610
|
+
await readyChat(arbi, crypto, chatId);
|
|
8611
|
+
success(`Removed ${userId} from ${chatId}; rotated the chat key`);
|
|
8612
|
+
})()
|
|
8613
|
+
);
|
|
8614
|
+
chat.command("leave <chatId>").description("Leave a group chat").action(
|
|
8615
|
+
(chatId) => runAction(async () => {
|
|
8616
|
+
const { arbi, crypto } = await resolveChatCrypto();
|
|
8617
|
+
await sdk.chats.removeChatMember(arbi, chatId, crypto.userExtId);
|
|
8618
|
+
success(`Left ${chatId}`);
|
|
8619
|
+
})()
|
|
8620
|
+
);
|
|
8621
|
+
chat.arguments("[maybeSubcommand]").action(async (maybe) => {
|
|
8622
|
+
if (maybe) {
|
|
8623
|
+
suggestSubcommandAndExit(
|
|
8624
|
+
"chat",
|
|
8625
|
+
maybe,
|
|
8626
|
+
chat.commands.map((c) => c.name())
|
|
8627
|
+
);
|
|
8628
|
+
}
|
|
8629
|
+
await chat.commands.find((c) => c.name() === "list").parseAsync([], { from: "user" });
|
|
8630
|
+
});
|
|
8631
|
+
}
|
|
8399
8632
|
init_prompts();
|
|
8400
8633
|
function formatSpan(startIso, endIso) {
|
|
8401
8634
|
const start = new Date(startIso);
|
|
@@ -11359,16 +11592,15 @@ ${message.text}`);
|
|
|
11359
11592
|
});
|
|
11360
11593
|
}
|
|
11361
11594
|
function registerNotificationsCommand(program2) {
|
|
11362
|
-
const notif = program2.command("notifications").enablePositionalOptions().description("
|
|
11363
|
-
notif.command("list").description("List
|
|
11595
|
+
const notif = program2.command("notifications").enablePositionalOptions().description("Notifications (workspace invites, contact accepts, \u2026)");
|
|
11596
|
+
notif.command("list").description("List notifications (workspace invites, contact accepts, \u2026)").option("--unread", "Only show unread notifications").option("--type <comma-list>", "Filter to specific type(s), e.g. workspaceuser_invited").option("-l, --limit <n>", "Cap to N most-recent", (v) => parseInt(v, 10)).option("--json", "Output as JSON (recommended for scripting / agents)").action(
|
|
11364
11597
|
(opts) => runAction(async () => {
|
|
11365
|
-
const { arbi
|
|
11366
|
-
const all = await sdk.
|
|
11367
|
-
const nonDm = all.filter((r) => r.type !== "user_message");
|
|
11598
|
+
const { arbi } = await resolveAuth();
|
|
11599
|
+
const all = await sdk.notifications.listNotifications(arbi);
|
|
11368
11600
|
const typeFilter = opts.type ? new Set(
|
|
11369
11601
|
opts.type.split(",").map((t) => t.trim()).filter(Boolean)
|
|
11370
11602
|
) : null;
|
|
11371
|
-
const filtered =
|
|
11603
|
+
const filtered = all.filter((r) => {
|
|
11372
11604
|
if (opts.unread && r.read) return false;
|
|
11373
11605
|
if (typeFilter && !typeFilter.has(r.type ?? "")) return false;
|
|
11374
11606
|
return true;
|
|
@@ -11390,7 +11622,7 @@ function registerNotificationsCommand(program2) {
|
|
|
11390
11622
|
{
|
|
11391
11623
|
header: "CONTENT",
|
|
11392
11624
|
width: 50,
|
|
11393
|
-
//
|
|
11625
|
+
// Rows usually carry a JSON-encoded payload in
|
|
11394
11626
|
// `content`. Show the first slice and let the JSON path
|
|
11395
11627
|
// expose the full thing for agents that want to introspect.
|
|
11396
11628
|
value: (r) => truncate(r.content ?? "", 49)
|
|
@@ -11400,30 +11632,30 @@ function registerNotificationsCommand(program2) {
|
|
|
11400
11632
|
);
|
|
11401
11633
|
})()
|
|
11402
11634
|
);
|
|
11403
|
-
notif.command("mark-read [ids...]").description("Mark notifications as read (use --all to drain the unread queue)").option("--all", "Mark every unread
|
|
11635
|
+
notif.command("mark-read [ids...]").description("Mark notifications as read (use --all to drain the unread queue)").option("--all", "Mark every unread notification as read").action(
|
|
11404
11636
|
(ids, opts) => runAction(async () => {
|
|
11405
|
-
const { arbi
|
|
11637
|
+
const { arbi } = await resolveAuth();
|
|
11406
11638
|
let toMark = ids && ids.length > 0 ? ids : void 0;
|
|
11407
11639
|
if (!toMark && opts.all) {
|
|
11408
|
-
const all = await sdk.
|
|
11409
|
-
toMark = all.filter((r) =>
|
|
11640
|
+
const all = await sdk.notifications.listNotifications(arbi);
|
|
11641
|
+
toMark = all.filter((r) => !r.read).map((r) => r.external_id);
|
|
11410
11642
|
}
|
|
11411
11643
|
if (!toMark || toMark.length === 0) {
|
|
11412
11644
|
process.stderr.write("Nothing to mark read. (Pass IDs or --all.)\n");
|
|
11413
11645
|
return;
|
|
11414
11646
|
}
|
|
11415
|
-
const data = await sdk.
|
|
11647
|
+
const data = await sdk.notifications.markNotificationsRead(arbi, toMark);
|
|
11416
11648
|
process.stdout.write(`Marked ${data.length} notification(s) as read.
|
|
11417
11649
|
`);
|
|
11418
11650
|
})()
|
|
11419
11651
|
);
|
|
11420
|
-
notif.command("delete [ids...]").description("Delete notifications by ID (or use --all to clear
|
|
11652
|
+
notif.command("delete [ids...]").description("Delete notifications by ID (or use --all to clear them all)").option("--all", "Delete every notification (workspace invites, contact accepts, ...)").option("--dry-run", "Preview which notifications would be deleted (no SDK call)").action(
|
|
11421
11653
|
(ids, opts) => runAction(async () => {
|
|
11422
|
-
const { arbi
|
|
11654
|
+
const { arbi } = await resolveAuth();
|
|
11423
11655
|
let toDelete = ids && ids.length > 0 ? ids : void 0;
|
|
11424
11656
|
if (!toDelete && opts.all) {
|
|
11425
|
-
const all = await sdk.
|
|
11426
|
-
toDelete = all.
|
|
11657
|
+
const all = await sdk.notifications.listNotifications(arbi);
|
|
11658
|
+
toDelete = all.map((r) => r.external_id);
|
|
11427
11659
|
}
|
|
11428
11660
|
if (!toDelete || toDelete.length === 0) {
|
|
11429
11661
|
process.stderr.write("Nothing to delete. (Pass IDs or --all.)\n");
|
|
@@ -11433,7 +11665,7 @@ function registerNotificationsCommand(program2) {
|
|
|
11433
11665
|
dryRun(`delete ${toDelete.length} notification(s)`, toDelete);
|
|
11434
11666
|
return;
|
|
11435
11667
|
}
|
|
11436
|
-
await sdk.
|
|
11668
|
+
await sdk.notifications.deleteNotifications(arbi, toDelete);
|
|
11437
11669
|
process.stdout.write(`Deleted ${toDelete.length} notification(s).
|
|
11438
11670
|
`);
|
|
11439
11671
|
})()
|
|
@@ -12272,7 +12504,7 @@ console.info = (...args) => {
|
|
|
12272
12504
|
_origInfo(...args);
|
|
12273
12505
|
};
|
|
12274
12506
|
var program = new commander.Command();
|
|
12275
|
-
program.name("arbi").description("ARBI CLI \u2014 interact with ARBI from the terminal").version("0.4.
|
|
12507
|
+
program.name("arbi").description("ARBI CLI \u2014 interact with ARBI from the terminal").version("0.4.111").showHelpAfterError(true).showSuggestionAfterError(true);
|
|
12276
12508
|
registerConfigCommand(program);
|
|
12277
12509
|
registerLoginCommand(program);
|
|
12278
12510
|
registerRegisterCommand(program);
|
|
@@ -12289,6 +12521,7 @@ registerFindCommand(program);
|
|
|
12289
12521
|
registerWatchCommand(program);
|
|
12290
12522
|
registerContactsCommand(program);
|
|
12291
12523
|
registerDmCommand(program);
|
|
12524
|
+
registerChatCommand(program);
|
|
12292
12525
|
registerTagsCommand(program);
|
|
12293
12526
|
registerTagKindsCommand(program);
|
|
12294
12527
|
registerTimelineCommand(program);
|