@crabcloud/cli 0.2.4 → 0.3.0

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/crab.js CHANGED
@@ -6034,6 +6034,10 @@ var oauthBindLoginInput = object({
6034
6034
  password: string2().min(1).max(256)
6035
6035
  });
6036
6036
  var adminSettingsInput = object({ registrationOpen: boolean2() });
6037
+ var collabContentViewInput = object({
6038
+ reason: string2().trim().min(1).max(200),
6039
+ cursor: string2().trim().max(200).optional()
6040
+ });
6037
6041
  var creditBuckets = ["granted", "topup"];
6038
6042
  var creditPackStatuses = ["draft", "live", "delisted"];
6039
6043
  var adminCreditAdjustInput = object({
@@ -6140,6 +6144,7 @@ var mailSendInput = object({
6140
6144
  cc: mailRecipientList.default([]),
6141
6145
  subject: string2().trim().min(1).max(200),
6142
6146
  body: string2().min(1).max(1e5),
6147
+ bodyHtml: string2().max(300000).optional(),
6143
6148
  inReplyTo: string2().max(998).optional(),
6144
6149
  attachmentIds: array(string2().min(1).max(64)).max(10).optional()
6145
6150
  });
@@ -6147,7 +6152,8 @@ var mailDraftInput = object({
6147
6152
  to: mailRecipientList.default([]),
6148
6153
  cc: mailRecipientList.default([]),
6149
6154
  subject: string2().trim().max(200).default(""),
6150
- body: string2().max(1e5).default("")
6155
+ body: string2().max(1e5).default(""),
6156
+ bodyHtml: string2().max(1e6).optional()
6151
6157
  });
6152
6158
  var mailReadInput = object({ unread: boolean2() });
6153
6159
  var mailDevInboundInput = object({
@@ -6155,6 +6161,7 @@ var mailDevInboundInput = object({
6155
6161
  to: mailAddressSchema,
6156
6162
  subject: string2().trim().min(1).max(200),
6157
6163
  body: string2().max(1e5),
6164
+ html: string2().max(1e6).optional(),
6158
6165
  attachments: array(object({
6159
6166
  filename: string2().min(1).max(120),
6160
6167
  mimeType: string2().regex(/^[\w.+-]+\/[\w.+-]+$/).max(100),
@@ -6548,9 +6555,6 @@ function formatTime(ms) {
6548
6555
  function fmtCredits(cents) {
6549
6556
  return (cents / 100).toLocaleString("en-US");
6550
6557
  }
6551
- function fmtUsd(cents) {
6552
- return `$${cents / 100}`;
6553
- }
6554
6558
  var usage = `crab — Crab Cloud CLI(Agent-first 入口)
6555
6559
  发布物经 npm 分发:npx @crabcloud/cli <command>(全局安装后短名 crab 等价)
6556
6560
 
@@ -6574,7 +6578,10 @@ var usage = `crab — Crab Cloud CLI(Agent-first 入口)
6574
6578
  mail list [--folder inbox|sent|drafts|archive|trash] [--unread] [--limit N] [--json]
6575
6579
  列出邮件(cursor 分页契约与 Web 工作台一致)
6576
6580
  mail read <id> [--json] 读取邮件正文(自动置已读)
6581
+ mail unread <id> 标记为未读
6577
6582
  mail search <query> [--json] 全文搜索(主题/发件人/收件人/正文)
6583
+ mail folders [--json] 邮箱概览(各夹总数/未读 + 本月收发统计)
6584
+ mail activity [--json] 邮箱操作流(谁读过/谁代发,最近 50 条)
6578
6585
  mail send --to a@x.cc|联系人名,b@y.cn [--cc c@z.io] --subject "..." --body "..." | --body-file <path>
6579
6586
  [--attach <云盘id|文件名>,...] [--in-reply-to <message-id>]
6580
6587
  回信线程锚点(原信 RFC Message-ID,crab mail read 可见)
@@ -6582,7 +6589,7 @@ var usage = `crab — Crab Cloud CLI(Agent-first 入口)
6582
6589
  命中采用,多候选报错列出);--attach 引用云盘已有对象
6583
6590
  (id 或文件名精确唯一命中,未绑信对象,≤10 个,大文件
6584
6591
  自动转下载链接);平台内(@crabcloud.cc)免费,外部出站
6585
- 2 积分/封
6592
+ 0.02 积分/封
6586
6593
  mail archive|trash|delete <id> 移夹 / 废纸篓 / 彻底删除(delete 不可恢复)
6587
6594
  contacts list [query] [--offset N] [--json] 列出联系人(地址前缀/显示名匹配)
6588
6595
  contacts add <address> [--name 名] [--note 备注] [--json]
@@ -6625,10 +6632,11 @@ var usage = `crab — Crab Cloud CLI(Agent-first 入口)
6625
6632
  分类重命名 / 改色
6626
6633
  storage rmcat <id|分类名> 删除分类(其中文件回落未分类)
6627
6634
  storage tags [--json] 标签汇总
6635
+ storage activity [--json] 云盘操作流(读取/上传/删除/建分享,最近 50 条)
6628
6636
  collab spaces [--json] 列出我所在的协作空间(任务/成员计数与限额)
6629
6637
  collab space create <名称> [--allocation-mb N] [--json]
6630
6638
  创建独立协作空间(消耗 5 积分;默认自分配 500MB
6631
- 存储占订阅总池,可随时调)
6639
+ 存储占订阅总池;后续调整在网页端空间设置)
6632
6640
  collab tasks [--space <arg>] [--status open|done] [--q 关键字]
6633
6641
  [--limit N] [--cursor <cursor>] [--json]
6634
6642
  列出任务(含摘要/记录数;--q 搜标题与记录正文)
@@ -6859,10 +6867,10 @@ async function creditsPricing() {
6859
6867
  console.log("暂无动作计价项。");
6860
6868
  return 0;
6861
6869
  }
6862
- console.log("动作计价(1 积分 = $1,价目为 $ 口径):");
6870
+ console.log("动作计价(1 积分 = $1,价目为积分数):");
6863
6871
  for (const item of actions) {
6864
6872
  const label = CREDIT_ACTION_LABELS[item.action] ?? item.action;
6865
- console.log(` ${item.action.padEnd(24)} ${label} ${fmtUsd(item.creditCents)}`);
6873
+ console.log(` ${item.action.padEnd(24)} ${label} ${fmtCredits(item.creditCents)} 积分`);
6866
6874
  }
6867
6875
  return 0;
6868
6876
  }
@@ -7140,7 +7148,7 @@ async function mailSend() {
7140
7148
  if (attachmentIds.length > 0)
7141
7149
  console.log(` 附件 ${attachmentIds.length} 个(云盘引用,不复制)`);
7142
7150
  if (result.chargedCredits > 0)
7143
- console.log(` 消耗:${fmtUsd(result.chargedCredits)}`);
7151
+ console.log(` 消耗:${fmtCredits(result.chargedCredits)} 积分`);
7144
7152
  if (result.failedExternal > 0)
7145
7153
  console.log(` ⚠ ${result.failedExternal} 个外部收件人发送失败,对应积分已退回`);
7146
7154
  return 0;
@@ -7161,6 +7169,72 @@ async function mailMove(action, id) {
7161
7169
  console.log(`✓ ${action === "archive" ? "已归档" : action === "trash" ? "已移入废纸篓" : "已彻底删除"}(${id})`);
7162
7170
  return 0;
7163
7171
  }
7172
+ async function mailUnread(id) {
7173
+ if (!id) {
7174
+ console.error("用法:crab mail unread <id>(id 见 crab mail list)");
7175
+ return EXIT_USAGE;
7176
+ }
7177
+ const credentials = requireCredentials();
7178
+ if (typeof credentials === "number")
7179
+ return credentials;
7180
+ const res = await api2(credentials.apiBase, `/v1/mail/messages/${encodeURIComponent(id)}/read`, {
7181
+ method: "POST",
7182
+ headers: authHeaders(credentials),
7183
+ body: JSON.stringify({ unread: true })
7184
+ });
7185
+ if (res.status !== 200) {
7186
+ console.error(errorMessage(res.body, "标记未读失败。"));
7187
+ return exitCodeForApiError(res.body);
7188
+ }
7189
+ console.log(`✓ 已标为未读(${id})。`);
7190
+ return 0;
7191
+ }
7192
+ var MAILBOX_FOLDER_LABELS = { inbox: "收件箱", sent: "已发送", drafts: "草稿", archive: "归档", trash: "废纸篓" };
7193
+ async function mailFoldersOverview() {
7194
+ const credentials = requireCredentials();
7195
+ if (typeof credentials === "number")
7196
+ return credentials;
7197
+ const res = await api2(credentials.apiBase, "/v1/mail/folders", { headers: authHeaders(credentials) });
7198
+ if (res.status !== 200) {
7199
+ console.error(errorMessage(res.body, "获取邮箱概览失败。"));
7200
+ return exitCodeForApiError(res.body);
7201
+ }
7202
+ const overview = res.body.data;
7203
+ if (hasFlag("--json")) {
7204
+ console.log(JSON.stringify(overview, null, 2));
7205
+ return 0;
7206
+ }
7207
+ console.log(overview.address);
7208
+ for (const folder of overview.folders) {
7209
+ console.log(` ${MAILBOX_FOLDER_LABELS[folder.folder] ?? folder.folder} ${folder.total} 封${folder.unread > 0 ? `(未读 ${folder.unread})` : ""}`);
7210
+ }
7211
+ console.log(`本月:收 ${overview.month.received} · 发 ${overview.month.sent}(外部出站 ${overview.month.outbound})`);
7212
+ return 0;
7213
+ }
7214
+ var MAIL_ACTIVITY_LABELS = { read: "读取", drafted: "撰写", sent: "发送" };
7215
+ async function mailActivity() {
7216
+ const credentials = requireCredentials();
7217
+ if (typeof credentials === "number")
7218
+ return credentials;
7219
+ const res = await api2(credentials.apiBase, "/v1/mail/agent-activity", { headers: authHeaders(credentials) });
7220
+ if (res.status !== 200) {
7221
+ console.error(errorMessage(res.body, "获取邮箱活动失败。"));
7222
+ return exitCodeForApiError(res.body);
7223
+ }
7224
+ const activity = res.body.data.activity;
7225
+ if (hasFlag("--json")) {
7226
+ console.log(JSON.stringify(activity, null, 2));
7227
+ return 0;
7228
+ }
7229
+ if (activity.length === 0) {
7230
+ console.log("还没有邮箱操作记录。");
7231
+ return 0;
7232
+ }
7233
+ for (const entry of activity) {
7234
+ console.log(`${formatTime(entry.createdAt)} ${(MAIL_ACTIVITY_LABELS[entry.action] ?? entry.action).padEnd(4)} ${entry.actorType === "agent" ? "agent" : "user "} ${entry.actorLabel ?? "—"} ${entry.subject || "(无主题)"}`);
7235
+ }
7236
+ return 0;
7237
+ }
7164
7238
  function contactDisplayName(contact) {
7165
7239
  return contact.displayName ?? contact.resolvedName ?? contact.address;
7166
7240
  }
@@ -7272,10 +7346,7 @@ async function contactsInvite(reset) {
7272
7346
  const credentials = requireCredentials();
7273
7347
  if (typeof credentials === "number")
7274
7348
  return credentials;
7275
- const res = await api2(credentials.apiBase, "/v1/contacts/invite/reset", {
7276
- method: reset ? "POST" : "GET",
7277
- headers: authHeaders(credentials)
7278
- });
7349
+ const res = await api2(credentials.apiBase, reset ? "/v1/contacts/invite/reset" : "/v1/contacts/invite", { method: reset ? "POST" : "GET", headers: authHeaders(credentials) });
7279
7350
  if (res.status !== 200) {
7280
7351
  console.error(errorMessage(res.body, "获取邀请链接失败。"));
7281
7352
  return exitCodeForApiError(res.body);
@@ -8034,7 +8105,8 @@ async function storageShares(token) {
8034
8105
  }
8035
8106
  for (const s of rows) {
8036
8107
  const state = s.revokedAt ? "已撤销" : s.expiresAt !== null && s.expiresAt <= Date.now() ? "已过期" : "生效中";
8037
- console.log(`${s.id} …${s.token.slice(-12)} ${state} 下载 ${s.downloadCount} 次 ${formatTime(s.createdAt)}${s.hasPasscode ? " [提取码]" : ""}`);
8108
+ const srcLabel = { drive: "云盘", mail: "邮件引用", collab: "协作引用", api: "API" }[s.source ?? "drive"];
8109
+ console.log(`${s.id} …${s.token.slice(-12)} ${state} ${srcLabel} 下载 ${s.downloadCount} 次 ${formatTime(s.createdAt)}${s.hasPasscode ? " [提取码]" : ""}`);
8038
8110
  }
8039
8111
  return 0;
8040
8112
  }
@@ -8230,6 +8302,35 @@ async function storageTags() {
8230
8302
  console.log(`${row.tag} (${row.count})`);
8231
8303
  return 0;
8232
8304
  }
8305
+ var STORAGE_ACTIVITY_LABELS = {
8306
+ "object.read": "读取",
8307
+ "object.uploaded": "上传",
8308
+ "object.deleted": "删除",
8309
+ "share.created": "建分享"
8310
+ };
8311
+ async function storageActivity() {
8312
+ const credentials = requireCredentials();
8313
+ if (typeof credentials === "number")
8314
+ return credentials;
8315
+ const res = await api2(credentials.apiBase, "/v1/storage/agent-activity", { headers: authHeaders(credentials) });
8316
+ if (res.status !== 200) {
8317
+ console.error(errorMessage(res.body, "获取云盘活动失败。"));
8318
+ return exitCodeForApiError(res.body);
8319
+ }
8320
+ const activity = res.body.data.activity;
8321
+ if (hasFlag("--json")) {
8322
+ console.log(JSON.stringify(activity, null, 2));
8323
+ return 0;
8324
+ }
8325
+ if (activity.length === 0) {
8326
+ console.log("还没有云盘操作记录。");
8327
+ return 0;
8328
+ }
8329
+ for (const entry of activity) {
8330
+ console.log(`${formatTime(entry.createdAt)} ${(STORAGE_ACTIVITY_LABELS[entry.action] ?? entry.action).padEnd(4)} ${entry.actorType === "agent" ? "agent" : "user "} ${entry.actorLabel ?? "—"} ${entry.objectName || entry.objectId}`);
8331
+ }
8332
+ return 0;
8333
+ }
8233
8334
  var COLLAB_TASK_STATUSES = ["open", "done"];
8234
8335
  function collabSpaceToken(space) {
8235
8336
  return space.kind === "default" ? "default" : `space:${space.name}`;
@@ -8339,7 +8440,7 @@ async function collabSpaceCreate(name) {
8339
8440
  return 0;
8340
8441
  }
8341
8442
  console.log(`✓ 独立协作空间已创建(id ${data.spaceId})`);
8342
- console.log(` 消耗:${fmtUsd(data.chargedCredits)} · --space space:${name} 即可指向它`);
8443
+ console.log(` 消耗:${fmtCredits(data.chargedCredits)} 积分 · --space space:${name} 即可指向它`);
8343
8444
  return 0;
8344
8445
  }
8345
8446
  async function collabTasksList() {
@@ -8782,14 +8883,20 @@ switch (command) {
8782
8883
  process.exit(await mailList());
8783
8884
  if (sub === "read")
8784
8885
  process.exit(await mailRead(args[2]));
8886
+ if (sub === "unread")
8887
+ process.exit(await mailUnread(args[2]));
8785
8888
  if (sub === "search")
8786
8889
  process.exit(await mailSearch(args.slice(2).join(" ") || undefined));
8787
8890
  if (sub === "send")
8788
8891
  process.exit(await mailSend());
8789
8892
  if (sub === "archive" || sub === "trash" || sub === "delete")
8790
8893
  process.exit(await mailMove(sub, args[2]));
8894
+ if (sub === "folders")
8895
+ process.exit(await mailFoldersOverview());
8896
+ if (sub === "activity")
8897
+ process.exit(await mailActivity());
8791
8898
  console.error(sub ? `未知子命令:mail ${sub}
8792
- ` : `缺少子命令:mail list | read | search | send | archive | trash | delete
8899
+ ` : `缺少子命令:mail list | read | unread | search | send | archive | trash | delete | folders | activity
8793
8900
  `);
8794
8901
  console.log(usage);
8795
8902
  process.exit(EXIT_USAGE);
@@ -8852,8 +8959,10 @@ switch (command) {
8852
8959
  process.exit(await storageUnshare(args[2]));
8853
8960
  if (sub === "key")
8854
8961
  process.exit(await storageKeyCmd());
8962
+ if (sub === "activity")
8963
+ process.exit(await storageActivity());
8855
8964
  console.error(sub ? `未知子命令:storage ${sub}
8856
- ` : `缺少子命令:storage ls | upload | download | rename | tag | category | publish | unpublish | rm | restore | purge | quota | categories | mkcat | rencat | rmcat | tags | share | shares | unshare | key
8965
+ ` : `缺少子命令:storage ls | upload | download | rename | tag | category | publish | unpublish | rm | restore | purge | quota | categories | mkcat | rencat | rmcat | tags | share | shares | unshare | key | activity
8857
8966
  `);
8858
8967
  console.log(usage);
8859
8968
  process.exit(EXIT_USAGE);
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: crabcloud
3
- description: Manage the user's Crab Cloud personal cloud account and agent tokens through the `crab` CLI — check identity/subscription/credits, list and revoke authorized agent tokens, and re-authorize with specific scopes or scope groups. Use this skill whenever the user asks about their Crab Cloud account ("我的账号信息", "who am I on crabcloud"), wants to see or clean up authorized agents/tokens ("看看我授权了哪些 agent", "revoke that old token", "撤销那个旧令牌"), manages their contacts ("我的联系人", "把 zhangsan 加到联系人", "我的邀请码"), or asks how to connect their coding agent to Crab Cloud — even if they never say "crab" or "Crab Cloud" explicitly. Contacts are managed with `crab contacts …` in this skill; email lives in the crabcloud-mail skill (crab mail …) and the Drive in crabcloud-storage (crab storage …); collab ships in a later phase.
3
+ description: Manage the user's Crab Cloud personal cloud account and agent tokens through the `crab` CLI — check identity/subscription/credits, list and revoke authorized agent tokens, and re-authorize with specific scopes or scope groups. Use this skill whenever the user asks about their Crab Cloud account ("我的账号信息", "who am I on crabcloud"), wants to see or clean up authorized agents/tokens ("看看我授权了哪些 agent", "revoke that old token", "撤销那个旧令牌"), manages their contacts ("我的联系人", "把 zhangsan 加到联系人", "我的邀请码"), or asks how to connect their coding agent to Crab Cloud — even if they never say "crab" or "Crab Cloud" explicitly. Contacts are managed with `crab contacts …` in this skill; email lives in the crabcloud-mail skill (crab mail …), the Drive in crabcloud-storage (crab storage …), and collab in crabcloud-collab (crab collab …).
4
4
  ---
5
5
 
6
6
  # Crab Cloud(平台 / 账号域)
@@ -10,8 +10,8 @@ Crab Cloud 是用户的个人云底座:账号、订阅与积分是平台层,
10
10
  (`~/.config/crabcloud/credentials.json`,0600),服务端只存令牌哈希、全程审计。
11
11
  本 skill 覆盖**平台、账号域与联系人**(`crab contacts …`);邮箱见
12
12
  `crabcloud-mail` skill(`crab mail …`),
13
- 云盘见 `crabcloud-storage` skill(`crab storage …`);协作随后续阶段
14
- 提供——用户问到时如实说明,**不要猜命令**。
13
+ 云盘见 `crabcloud-storage` skill(`crab storage …`);协作见
14
+ `crabcloud-collab` skill(`crab collab …`)。
15
15
 
16
16
  ## 第一步:确认 CLI 可用
17
17
 
@@ -45,7 +45,7 @@ Crab Cloud 是用户的个人云底座:账号、订阅与积分是平台层,
45
45
 
46
46
  积分命令需 `credits.read` scope(`account` 组含它):退出码 4 报缺该 scope 时,
47
47
  引导 `crab login --scopes account` 重新授权。积分额度一律以**积分数**表述
48
- (100 积分 = $1.00,仅价格类口径用美元);充值/下单是 Web 会话能力,CLI/Agent
48
+ (**1 积分 = $1**,仅价格类口径用美元);充值/下单是 Web 会话能力,CLI/Agent
49
49
  令牌不动账——用户要充值时引导网页账单页(crabcloud.cc/account/billing)。
50
50
  拿不准令牌 id 时先 `crab tokens list` 核对;`revoke` 只接受列表里的 id。
51
51
 
@@ -76,11 +76,11 @@ Crab Cloud 是用户的个人云底座:账号、订阅与积分是平台层,
76
76
  不要怂恿用户一次性给全量 scope。`--scopes` 支持组名(`mail` = 读写搜删四项
77
77
  整组)与单个 scope 混用;组目录见 `crab help`,可用组:account / mail /
78
78
  storage(云盘,含素材与附件)/ collab / contacts。
79
- - 如实转述能力边界:协作尚未上线——相关请求如实说明,引导用户
80
- 用网页(crabcloud.cc),不要编造命令或输出;联系人已上线,用 `crab contacts`
79
+ - 如实转述能力边界,不要编造命令或输出:联系人用 `crab contacts`
81
80
  (list / add / rm / invite / invitees;scope contacts.read/write,程序消费加
82
- `--json`)处理;邮箱请求转交 `crabcloud-mail`
83
- skill,云盘请求转交 `crabcloud-storage` skill。
81
+ `--json`)处理;邮箱请求转交 `crabcloud-mail` skill,云盘请求转交
82
+ `crabcloud-storage` skill,协作请求转交 `crabcloud-collab` skill
83
+ (`crab collab …`,已上线)。
84
84
  - 账号安全操作(修改密码、登录设备/会话的查看与吊销)**有意不开放给 agent
85
85
  通道**:改密对令牌持有者是账号接管面,会话属于"人的浏览器会话"。用户让
86
86
  agent 做这些时,如实说明并引导到网页个人中心(crabcloud.cc/account)操作,
@@ -83,6 +83,10 @@ skill 覆盖协作域的 agent 通道;账号/令牌管理见 `crabcloud` skill
83
83
  补一条更正记录,不要试图「撤回」。
84
84
  - **摘要是可变当前态**:表达「现在进展到哪」,不是流水账;每次推送记录后若
85
85
  摘要已过时,顺手 `crab collab task summary` 刷新,让不看全程的人一眼看懂。
86
+ - **空间级管理走网页端**:改名/归档/调整自分配存储/扩容(任务+20、成员+5)/
87
+ 移除成员是 owner 的网页操作,Agent 令牌一律被服务端拒绝——用户要这些时如实
88
+ 说明并引导网页端,**不要换姿势重试**;建独立空间(`collab space create`)与
89
+ 邀请成员(`collab invite`)例外,Agent 可执行。
86
90
  - **建独立空间是付费动作(5 积分)**:先向用户复述名称与自分配存储(默认
87
91
  500MB,占订阅总池),确认后再执行;`collab space create` 不要当作顺手操作。
88
92
  - **邀请有前置**:目标必须是联系人簿里已关联的平台用户(`crab contacts add`
@@ -7,8 +7,8 @@ description: Read, search and send the user's Crab Cloud platform email (usernam
7
7
 
8
8
  用户的 Crab Cloud 账号自带平台邮箱 `username@crabcloud.cc`(用户名即地址本地部分)。
9
9
  本 skill 覆盖邮箱域的 agent 通道;账号/令牌管理见 `crabcloud` skill。云盘见
10
- `crabcloud-storage` skill(`crab storage …`),协作尚未上线——用户问到时如实说明,
11
- **不要猜命令**。
10
+ `crabcloud-storage` skill(`crab storage …`),协作见 `crabcloud-collab` skill
11
+ (`crab collab …`)。
12
12
 
13
13
  ## 第一步:确认可用
14
14
 
@@ -32,6 +32,9 @@ description: Read, search and send the user's Crab Cloud platform email (usernam
32
32
  | 只看未读 | `crab mail list --unread` |
33
33
  | 翻页 | `crab mail list --cursor <nextCursor>`(列表末尾会提示) |
34
34
  | 读某封邮件 | `crab mail read <id>`(自动置已读;正文 + 操作记录) |
35
+ | 标回未读 | `crab mail unread <id>` |
36
+ | 看各文件夹总量/未读、本月收发 | `crab mail folders`(判断「有没有新邮件」先看这个,别翻列表) |
37
+ | 看邮箱操作流(谁读过/谁代发) | `crab mail activity`(最近 50 条,审计源) |
35
38
  | 找邮件 | `crab mail search <关键词>`(主题/发件人/收件人/正文,不含废纸篓) |
36
39
  | 发邮件 | `crab mail send --to a@x.cc --subject "..." --body "..."`(长正文用 `--body-file`) |
37
40
  | 发邮件带附件 | `crab mail send … --attach <云盘id\|文件名>[,…]`(引用云盘已有对象,不复制,≤10 个;先 `crab storage ls` 核对对象,文件名需精确唯一命中) |
@@ -44,7 +47,7 @@ description: Read, search and send the user's Crab Cloud platform email (usernam
44
47
  ## 计费口径(必须如实转述)
45
48
 
46
49
  - **平台内互发免费**:收件人也是 `@crabcloud.cc` 地址时不消耗积分。
47
- - **外部出站 2 积分/封**:发送前向用户复述收件人与计费;退出码 7 = 积分不足,
50
+ - **外部出站 0.02 积分/封($0.02)**:发送前向用户复述收件人与计费;退出码 7 = 积分不足,
48
51
  告知用户不要自动重试。
49
52
  - 发送结果会报告 `消耗积分` 与失败数;部分失败时失败部分的积分自动退回
50
53
  (`发送:partial` 标记)。
@@ -57,7 +60,9 @@ description: Read, search and send the user's Crab Cloud platform email (usernam
57
60
  `id/folder/fromAddr/to/subject/snippet/isUnread/hasAttachments/createdAt`;
58
61
  - `mail read --json` → 完整正文 + `attachments`(id/filename/mimeType/sizeBytes)
59
62
  + `actors`(谁读过/谁代发,审计源);
60
- - `mail send --json` → `{ message, chargedCredits, failedExternal }`。
63
+ - `mail send --json` → `{ message, chargedCredits, failedExternal }`;
64
+ - `mail folders --json` → `{ address, folders: [{ folder, total, unread }], month: { received, sent, outbound } }`;
65
+ - `mail activity --json` → 条目含 `action(read/drafted/sent)/ actorType / actorLabel / subject / messageId / createdAt`。
61
66
 
62
67
  ## 纪律
63
68
 
@@ -47,6 +47,7 @@ skill。
47
47
  | 云盘钥匙管理 | `crab storage key`(查状态)/ `--remember`(解锁一次并记住,之后免口令)/ `--forget`(清除)——见下节 |
48
48
  | 删除 | `crab storage rm <id\|文件名>`(进回收站,7 天后自动清理)→ `restore` 恢复 / `purge` 彻底删除 |
49
49
  | 看水位 | `crab storage quota`(三来源分项 + 回收站占用 + 对象计数) |
50
+ | 看云盘操作流 | `crab storage activity`(读取/上传/删除/建分享,最近 50 条) |
50
51
 
51
52
  `<id|文件名>`:文件名全库精确唯一命中时自动解析为 id(回收站内同样可解析,
52
53
  restore/purge 直接用文件名即可);命中多个或查不到时按字面当 id 用——拿不准
@@ -60,6 +61,7 @@ restore/purge 直接用文件名即可);命中多个或查不到时按字面
60
61
  - `storage ls --json` → `{ objects: [...], nextOffset, total }`,条目含
61
62
  `id / source / filename / mimeType / sizeBytes / categoryId / tags / isPublic / createdAt`;
62
63
  - `storage quota --json` → `{ usedBytes, quotaBytes, bySource: { files, media, mail }, trashBytes, counts }`;
64
+ - `storage activity --json` → 条目含 `action(object.read/object.uploaded/object.deleted/share.created)/ actorType / actorLabel / objectId / objectName / createdAt`;
63
65
  - `upload` / `categories` / `tags` / `shares` 的 `--json` 同构(单对象 / 分类数组 /
64
66
  标签数组 / 分享数组)。
65
67
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@crabcloud/cli",
3
- "version": "0.2.4",
3
+ "version": "0.3.0",
4
4
  "type": "module",
5
5
  "description": "Crab Cloud CLI (crab) — agent-first entry to your personal cloud, with skills bundled for Claude Code / Codex / ZCode.",
6
6
  "bin": {
@@ -24,9 +24,9 @@
24
24
  "prepublishOnly": "bun run typecheck && bun run build"
25
25
  },
26
26
  "devDependencies": {
27
- "@crabcloud/cloudkit": "workspace:*",
28
- "@crabcloud/contracts": "workspace:*",
29
- "@crabcloud/skills": "workspace:*",
27
+ "@crabcloud/cloudkit": "0.1.0",
28
+ "@crabcloud/contracts": "0.1.0",
29
+ "@crabcloud/skills": "0.1.5",
30
30
  "@types/bun": "latest",
31
31
  "typescript": "^5.9.3"
32
32
  }