@crabcloud/cli 0.1.9 → 0.1.10

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
@@ -1,4 +1,18 @@
1
1
  #!/usr/bin/env node
2
+ var __defProp = Object.defineProperty;
3
+ var __returnValue = (v) => v;
4
+ function __exportSetter(name, newValue) {
5
+ this[name] = __returnValue.bind(null, newValue);
6
+ }
7
+ var __export = (target, all) => {
8
+ for (var name in all)
9
+ __defProp(target, name, {
10
+ get: all[name],
11
+ enumerable: true,
12
+ configurable: true,
13
+ set: __exportSetter.bind(all, name)
14
+ });
15
+ };
2
16
 
3
17
  // src/index.ts
4
18
  import { hostname, homedir as homedir2 } from "node:os";
@@ -980,6 +994,7 @@ function datetime(args) {
980
994
  return new RegExp(`^${dateSource}T(?:${timeRegex})$`);
981
995
  }
982
996
  var anyString = /^[\s\S]{0,}$/;
997
+ var bigint = /^-?\d+n?$/;
983
998
  var integer = /^-?\d+$/;
984
999
  var number = /^-?\d+(?:\.\d+)?$/;
985
1000
  var boolean = /^(?:true|false)$/i;
@@ -1885,6 +1900,25 @@ var $ZodBoolean = /* @__PURE__ */ $constructor("$ZodBoolean", (inst, def) => {
1885
1900
  return payload;
1886
1901
  };
1887
1902
  });
1903
+ var $ZodBigInt = /* @__PURE__ */ $constructor("$ZodBigInt", (inst, def) => {
1904
+ $ZodType.init(inst, def);
1905
+ inst._zod.pattern = bigint;
1906
+ inst._zod.parse = (payload, _ctx) => {
1907
+ if (def.coerce)
1908
+ try {
1909
+ payload.value = BigInt(payload.value);
1910
+ } catch (_) {}
1911
+ if (typeof payload.value === "bigint")
1912
+ return payload;
1913
+ payload.issues.push({
1914
+ expected: "bigint",
1915
+ code: "invalid_type",
1916
+ input: payload.value,
1917
+ inst
1918
+ });
1919
+ return payload;
1920
+ };
1921
+ });
1888
1922
  var $ZodNull = /* @__PURE__ */ $constructor("$ZodNull", (inst, def) => {
1889
1923
  $ZodType.init(inst, def);
1890
1924
  inst._zod.pattern = _null;
@@ -1918,6 +1952,29 @@ var $ZodNever = /* @__PURE__ */ $constructor("$ZodNever", (inst, def) => {
1918
1952
  return payload;
1919
1953
  };
1920
1954
  });
1955
+ var $ZodDate = /* @__PURE__ */ $constructor("$ZodDate", (inst, def) => {
1956
+ $ZodType.init(inst, def);
1957
+ inst._zod.parse = (payload, _ctx) => {
1958
+ if (def.coerce) {
1959
+ try {
1960
+ payload.value = new Date(payload.value);
1961
+ } catch (_err) {}
1962
+ }
1963
+ const input = payload.value;
1964
+ const isDate = input instanceof Date;
1965
+ const isValidDate = isDate && !Number.isNaN(input.getTime());
1966
+ if (isValidDate)
1967
+ return payload;
1968
+ payload.issues.push({
1969
+ expected: "date",
1970
+ code: "invalid_type",
1971
+ input,
1972
+ ...isDate ? { received: "Invalid Date" } : {},
1973
+ inst
1974
+ });
1975
+ return payload;
1976
+ };
1977
+ });
1921
1978
  function handleArrayResult(result, final, index) {
1922
1979
  if (result.issues.length) {
1923
1980
  final.issues.push(...prefixIssues(index, result.issues));
@@ -3323,6 +3380,9 @@ function snapshotChecks(def) {
3323
3380
  function _string(Class, params) {
3324
3381
  return new Class(snapshotChecks({ type: "string", ...normalizeParams(params) }));
3325
3382
  }
3383
+ function _coercedString(Class, params) {
3384
+ return new Class(snapshotChecks({ type: "string", coerce: true, ...normalizeParams(params) }));
3385
+ }
3326
3386
  function _email(Class, params) {
3327
3387
  return new Class({
3328
3388
  type: "string",
@@ -3563,6 +3623,9 @@ function _isoDuration(Class, params) {
3563
3623
  function _number(Class, params) {
3564
3624
  return new Class(snapshotChecks({ type: "number", checks: [], ...normalizeParams(params) }));
3565
3625
  }
3626
+ function _coercedNumber(Class, params) {
3627
+ return new Class(snapshotChecks({ type: "number", coerce: true, checks: [], ...normalizeParams(params) }));
3628
+ }
3566
3629
  function _int(Class, params) {
3567
3630
  return new Class({
3568
3631
  type: "number",
@@ -3578,6 +3641,20 @@ function _boolean(Class, params) {
3578
3641
  ...normalizeParams(params)
3579
3642
  });
3580
3643
  }
3644
+ function _coercedBoolean(Class, params) {
3645
+ return new Class({
3646
+ type: "boolean",
3647
+ coerce: true,
3648
+ ...normalizeParams(params)
3649
+ });
3650
+ }
3651
+ function _coercedBigint(Class, params) {
3652
+ return new Class({
3653
+ type: "bigint",
3654
+ coerce: true,
3655
+ ...normalizeParams(params)
3656
+ });
3657
+ }
3581
3658
  function _null2(Class, params) {
3582
3659
  return new Class({
3583
3660
  type: "null",
@@ -3595,6 +3672,13 @@ function _never(Class, params) {
3595
3672
  ...normalizeParams(params)
3596
3673
  });
3597
3674
  }
3675
+ function _coercedDate(Class, params) {
3676
+ return new Class({
3677
+ type: "date",
3678
+ coerce: true,
3679
+ ...normalizeParams(params)
3680
+ });
3681
+ }
3598
3682
  function _lt(value, params) {
3599
3683
  return new $ZodCheckLessThan({
3600
3684
  check: "less_than",
@@ -4469,6 +4553,9 @@ var numberProcessor = (schema, ctx, _json, params) => {
4469
4553
  var booleanProcessor = (_schema, _ctx, json, _params) => {
4470
4554
  json.type = "boolean";
4471
4555
  };
4556
+ var bigintProcessor = (schema, ctx, json, params) => {
4557
+ handleUnrepresentable(schema, ctx, json, params, "BigInt cannot be represented in JSON Schema");
4558
+ };
4472
4559
  var nullProcessor = (_schema, ctx, json, _params) => {
4473
4560
  if (ctx.target === "openapi-3.0") {
4474
4561
  json.type = "string";
@@ -4482,6 +4569,9 @@ var neverProcessor = (_schema, _ctx, json, _params) => {
4482
4569
  json.not = {};
4483
4570
  };
4484
4571
  var unknownProcessor = (_schema, _ctx, _json, _params) => {};
4572
+ var dateProcessor = (schema, ctx, json, params) => {
4573
+ handleUnrepresentable(schema, ctx, json, params, "Date cannot be represented in JSON Schema");
4574
+ };
4485
4575
  var enumProcessor = (schema, _ctx, json, _params) => {
4486
4576
  const def = schema._zod.def;
4487
4577
  const values = getEnumValues(def.entries);
@@ -5346,6 +5436,49 @@ var ZodBoolean = /* @__PURE__ */ $constructor("ZodBoolean", (inst, def) => {
5346
5436
  function boolean2(params) {
5347
5437
  return _boolean(ZodBoolean, params);
5348
5438
  }
5439
+ var ZodBigInt = /* @__PURE__ */ $constructor("ZodBigInt", (inst, def) => {
5440
+ $ZodBigInt.init(inst, def);
5441
+ ZodType.init(inst, def);
5442
+ inst._zod.processJSONSchema = (ctx, json, params) => bigintProcessor(inst, ctx, json, params);
5443
+ }, /* @__PURE__ */ derived({
5444
+ minValue: (inst) => aggregateChecks(inst).minimum ?? null,
5445
+ maxValue: (inst) => aggregateChecks(inst).maximum ?? null,
5446
+ format: (inst) => aggregateChecks(inst).format ?? null
5447
+ }, {
5448
+ gte(value, params) {
5449
+ return this.check(_gte(value, params));
5450
+ },
5451
+ min(value, params) {
5452
+ return this.check(_gte(value, params));
5453
+ },
5454
+ gt(value, params) {
5455
+ return this.check(_gt(value, params));
5456
+ },
5457
+ lt(value, params) {
5458
+ return this.check(_lt(value, params));
5459
+ },
5460
+ lte(value, params) {
5461
+ return this.check(_lte(value, params));
5462
+ },
5463
+ max(value, params) {
5464
+ return this.check(_lte(value, params));
5465
+ },
5466
+ positive(params) {
5467
+ return this.check(_gt(BigInt(0), params));
5468
+ },
5469
+ negative(params) {
5470
+ return this.check(_lt(BigInt(0), params));
5471
+ },
5472
+ nonpositive(params) {
5473
+ return this.check(_lte(BigInt(0), params));
5474
+ },
5475
+ nonnegative(params) {
5476
+ return this.check(_gte(BigInt(0), params));
5477
+ },
5478
+ multipleOf(value, params) {
5479
+ return this.check(_multipleOf(value, params));
5480
+ }
5481
+ }));
5349
5482
  var ZodNull = /* @__PURE__ */ $constructor("ZodNull", (inst, def) => {
5350
5483
  $ZodNull.init(inst, def);
5351
5484
  ZodType.init(inst, def);
@@ -5370,6 +5503,22 @@ var ZodNever = /* @__PURE__ */ $constructor("ZodNever", (inst, def) => {
5370
5503
  function never(params) {
5371
5504
  return _never(ZodNever, params);
5372
5505
  }
5506
+ var ZodDate = /* @__PURE__ */ $constructor("ZodDate", (inst, def) => {
5507
+ $ZodDate.init(inst, def);
5508
+ ZodType.init(inst, def);
5509
+ inst._zod.processJSONSchema = (ctx, json, params) => dateProcessor(inst, ctx, json, params);
5510
+ inst.min = (value, params) => inst.check(_gte(value, params));
5511
+ inst.max = (value, params) => inst.check(_lte(value, params));
5512
+ }, /* @__PURE__ */ derived({
5513
+ minDate: (inst) => {
5514
+ const { minimum } = aggregateChecks(inst);
5515
+ return minimum ? new Date(minimum) : null;
5516
+ },
5517
+ maxDate: (inst) => {
5518
+ const { maximum } = aggregateChecks(inst);
5519
+ return maximum ? new Date(maximum) : null;
5520
+ }
5521
+ }, {}));
5373
5522
  var ZodArray = /* @__PURE__ */ $constructor("ZodArray", (inst, def) => {
5374
5523
  _ensureDefaultMemoizer();
5375
5524
  $ZodArray.init(inst, def);
@@ -5720,6 +5869,30 @@ function refine(fn, _params = {}) {
5720
5869
  function superRefine(fn, params) {
5721
5870
  return _superRefine(fn, params);
5722
5871
  }
5872
+ // ../../node_modules/.bun/zod@4.6.5/node_modules/zod/v4/classic/coerce.js
5873
+ var exports_coerce = {};
5874
+ __export(exports_coerce, {
5875
+ bigint: () => bigint2,
5876
+ boolean: () => boolean3,
5877
+ date: () => date2,
5878
+ number: () => number3,
5879
+ string: () => string3
5880
+ });
5881
+ function string3(params) {
5882
+ return _coercedString(ZodString, params);
5883
+ }
5884
+ function number3(params) {
5885
+ return _coercedNumber(ZodNumber, params);
5886
+ }
5887
+ function boolean3(params) {
5888
+ return _coercedBoolean(ZodBoolean, params);
5889
+ }
5890
+ function bigint2(params) {
5891
+ return _coercedBigint(ZodBigInt, params);
5892
+ }
5893
+ function date2(params) {
5894
+ return _coercedDate(ZodDate, params);
5895
+ }
5723
5896
  // ../../packages/contracts/src/index.ts
5724
5897
  var accountDomain = "crabcloud.cc";
5725
5898
  var adminDomain = `admin.${accountDomain}`;
@@ -5773,6 +5946,8 @@ var apiErrorSchema = object({
5773
5946
  "expired",
5774
5947
  "insufficient_credits",
5775
5948
  "payload_too_large",
5949
+ "collab_space_not_bound",
5950
+ "collab_space_mismatch",
5776
5951
  "internal"
5777
5952
  ]),
5778
5953
  message: string2()
@@ -5827,7 +6002,8 @@ var sessionSummarySchema = object({
5827
6002
  });
5828
6003
  var deviceStartInput = object({
5829
6004
  name: string2().trim().min(1).max(80),
5830
- scopes: array(_enum(scopes)).min(1).max(scopes.length)
6005
+ scopes: array(_enum(scopes)).min(1).max(scopes.length),
6006
+ space: string2().trim().min(1).max(80).optional()
5831
6007
  });
5832
6008
  var devicePollInput = object({ deviceCode: string2().min(16).max(200) });
5833
6009
  var deviceDecisionInput = object({
@@ -5839,6 +6015,7 @@ var agentTokenSummarySchema = object({
5839
6015
  id: string2(),
5840
6016
  name: string2(),
5841
6017
  scopes: array(_enum(scopes)),
6018
+ collabSpace: string2().nullable().optional(),
5842
6019
  createdAt: number2(),
5843
6020
  lastUsedAt: number2().nullable(),
5844
6021
  expiresAt: number2().nullable(),
@@ -5886,6 +6063,13 @@ var adminPlanTierUpdateInput = object({
5886
6063
  quotaJson: record(string2(), unknown()).optional(),
5887
6064
  status: _enum(adminPlanTierStatuses).optional()
5888
6065
  });
6066
+ var subscriptionChangeInput = object({
6067
+ tierId: string2().trim().min(1).max(50)
6068
+ });
6069
+ var adminUserSubscriptionInput = object({
6070
+ tierId: string2().trim().min(1).max(50),
6071
+ reason: string2().trim().min(1).max(500)
6072
+ });
5889
6073
  var giftFulfillments = ["stock", "webhook"];
5890
6074
  var giftMerchantStatuses = ["active", "disabled"];
5891
6075
  var giftProductStatuses = ["draft", "live", "delisted"];
@@ -5990,6 +6174,49 @@ var contactQueryInput = object({
5990
6174
  limit: number2().int().min(1).max(50).default(20),
5991
6175
  offset: number2().int().min(0).default(0)
5992
6176
  });
6177
+ var collabTaskStatuses = ["open", "done"];
6178
+ var collabBoostKinds = ["tasks", "members"];
6179
+ var COLLAB_RECORD_BODY_MAX = 20000;
6180
+ var COLLAB_RECORD_FILES_MAX = 10;
6181
+ var COLLAB_RECORD_LINKS_MAX = 10;
6182
+ var COLLAB_TITLE_MAX = 200;
6183
+ var COLLAB_SPACE_NAME_MAX = 64;
6184
+ var COLLAB_SUMMARY_MAX = 2000;
6185
+ var collabCreateSpaceInput = object({
6186
+ name: string2().trim().min(1).max(COLLAB_SPACE_NAME_MAX),
6187
+ allocationBytes: number2().int().min(1).max(10 * 1024 * 1024 * 1024).optional()
6188
+ });
6189
+ var collabRenameSpaceInput = object({ name: string2().trim().min(1).max(COLLAB_SPACE_NAME_MAX) });
6190
+ var collabAllocationInput = object({
6191
+ allocationBytes: number2().int().min(1).max(10 * 1024 * 1024 * 1024)
6192
+ });
6193
+ var collabBoostInput = object({ kind: _enum(collabBoostKinds) });
6194
+ var collabInviteInput = object({ username: usernameSchema });
6195
+ var collabLinkInput = string2().trim().url().max(2000);
6196
+ var collabCreateTaskInput = object({
6197
+ title: string2().trim().min(1).max(COLLAB_TITLE_MAX),
6198
+ body: string2().max(COLLAB_RECORD_BODY_MAX).default(""),
6199
+ fileIds: array(string2().trim().min(1).max(64)).max(COLLAB_RECORD_FILES_MAX).optional(),
6200
+ links: array(collabLinkInput).max(COLLAB_RECORD_LINKS_MAX).optional()
6201
+ });
6202
+ var collabPushRecordInput = object({
6203
+ body: string2().min(1).max(COLLAB_RECORD_BODY_MAX),
6204
+ fileIds: array(string2().trim().min(1).max(64)).max(COLLAB_RECORD_FILES_MAX).optional(),
6205
+ links: array(collabLinkInput).max(COLLAB_RECORD_LINKS_MAX).optional()
6206
+ });
6207
+ var collabUpdateSummaryInput = object({
6208
+ summary: string2().trim().max(COLLAB_SUMMARY_MAX)
6209
+ });
6210
+ var collabTaskListQuery = object({
6211
+ status: _enum(collabTaskStatuses).optional(),
6212
+ q: string2().trim().max(200).optional(),
6213
+ cursor: string2().trim().max(200).optional(),
6214
+ limit: exports_coerce.number().int().min(1).max(100).optional()
6215
+ });
6216
+ var collabRecordListQuery = object({
6217
+ cursor: string2().trim().max(200).optional(),
6218
+ limit: exports_coerce.number().int().min(1).max(200).optional()
6219
+ });
5993
6220
 
5994
6221
  // ../../packages/cloudkit/src/crypto.ts
5995
6222
  var enc = new TextEncoder;
@@ -6309,6 +6536,9 @@ function requireCredentials() {
6309
6536
  function formatTime(ms) {
6310
6537
  return new Date(ms).toLocaleString("sv", { year: "numeric", month: "2-digit", day: "2-digit", hour: "2-digit", minute: "2-digit" });
6311
6538
  }
6539
+ function fmtCredits(cents) {
6540
+ return cents.toLocaleString("en-US");
6541
+ }
6312
6542
  var usage = `crab — Crab Cloud CLI(Agent-first 入口)
6313
6543
  发布物经 npm 分发:npx @crabcloud/cli <command>(全局安装后短名 crab 等价)
6314
6544
 
@@ -6371,6 +6601,10 @@ var usage = `crab — Crab Cloud CLI(Agent-first 入口)
6371
6601
  storage categories [--json] 分类列表
6372
6602
  storage mkcat <名称> 新建分类
6373
6603
  storage tags [--json] 标签汇总
6604
+ credits [--json] 积分余额(月送/充值/合计;需 credits.read scope)
6605
+ credits history [--limit N] [--offset N] [--json]
6606
+ 积分流水(变动 ±:消耗为负;需 credits.read)
6607
+ credits pricing [--json] 动作计价标准(公开:动作单价/档位/充值包)
6374
6608
  skills install|list|uninstall [--agent claude,zcode,codex,agents]
6375
6609
  skills 管理(claude/zcode/agents 复制目录,codex 写 AGENTS.md 段)
6376
6610
  logout 撤销当前 Agent Token 并清除本地凭证
@@ -6473,11 +6707,105 @@ async function whoami() {
6473
6707
  if (subscription)
6474
6708
  console.log(` 订阅: ${subscription.tier_name}`);
6475
6709
  if (credits !== undefined)
6476
- console.log(` 积分余额: $${(credits / 100).toFixed(2)} (${credits} credit_cents)`);
6710
+ console.log(` 积分余额: ${fmtCredits(credits)} 积分`);
6477
6711
  if (apps?.length)
6478
6712
  console.log(` 已启用应用: ${apps.map((a) => a.title).join("、")}`);
6479
6713
  return 0;
6480
6714
  }
6715
+ var CREDIT_REASON_LABELS = {
6716
+ subscription_grant: "月送发放",
6717
+ topup: "充值入账",
6718
+ consume: "动作消耗",
6719
+ refund: "退款",
6720
+ adjustment: "人工调整"
6721
+ };
6722
+ var CREDIT_ACTION_LABELS = {
6723
+ "mail.send_outbound": "外部出站邮件",
6724
+ "collab.space_create": "创建独立协作空间",
6725
+ "collab.boost_tasks": "协作任务扩容(+20)",
6726
+ "collab.boost_members": "协作成员扩容(+5)"
6727
+ };
6728
+ function creditMovement(entry) {
6729
+ if (entry.grantedDelta === null && entry.topupDelta === null)
6730
+ return entry.delta;
6731
+ return (entry.grantedDelta ?? 0) + (entry.topupDelta ?? 0);
6732
+ }
6733
+ async function fetchCredits(credentials, limit, offset) {
6734
+ const res = await api2(credentials.apiBase, `/v1/me/credits?limit=${limit}&offset=${offset}`, { headers: authHeaders(credentials) });
6735
+ if (res.status !== 200) {
6736
+ console.error(errorMessage(res.body, "获取积分数据失败。"));
6737
+ if (res.body?.error?.code === "scope_denied") {
6738
+ console.error("提示:积分命令需要 credits.read scope——运行 crab login --scopes account 重新授权。");
6739
+ }
6740
+ return exitCodeForApiError(res.body);
6741
+ }
6742
+ return res.body.data;
6743
+ }
6744
+ async function creditsBalance() {
6745
+ const credentials = requireCredentials();
6746
+ if (typeof credentials === "number")
6747
+ return credentials;
6748
+ const data = await fetchCredits(credentials, 1, 0);
6749
+ if (typeof data === "number")
6750
+ return data;
6751
+ if (hasFlag("--json")) {
6752
+ console.log(JSON.stringify(data.balance, null, 2));
6753
+ return 0;
6754
+ }
6755
+ console.log(`积分余额:${fmtCredits(data.balance.balance)}`);
6756
+ console.log(` 月送 ${fmtCredits(data.balance.granted)}(每周期刷新清零) · 充值 ${fmtCredits(data.balance.topup)}(长期有效)`);
6757
+ return 0;
6758
+ }
6759
+ async function creditsHistory() {
6760
+ const credentials = requireCredentials();
6761
+ if (typeof credentials === "number")
6762
+ return credentials;
6763
+ const limit = Math.min(Math.max(Number.parseInt(readFlag("--limit") ?? "", 10) || 20, 1), 100);
6764
+ const offset = Math.max(Number.parseInt(readFlag("--offset") ?? "", 10) || 0, 0);
6765
+ const data = await fetchCredits(credentials, limit, offset);
6766
+ if (typeof data === "number")
6767
+ return data;
6768
+ if (hasFlag("--json")) {
6769
+ console.log(JSON.stringify(data, null, 2));
6770
+ return 0;
6771
+ }
6772
+ if (data.ledger.length === 0) {
6773
+ console.log("还没有积分流水。");
6774
+ return 0;
6775
+ }
6776
+ for (const entry of data.ledger) {
6777
+ const movement = creditMovement(entry);
6778
+ const signed = movement > 0 ? `+${fmtCredits(movement)}` : fmtCredits(movement);
6779
+ console.log(`${formatTime(entry.createdAt)} ${CREDIT_REASON_LABELS[entry.reason] ?? entry.reason} ${signed}`);
6780
+ }
6781
+ console.log(`
6782
+ 共 ${fmtCredits(data.total)} 条(本页 ${data.ledger.length} 条,--offset 翻页)`);
6783
+ return 0;
6784
+ }
6785
+ async function creditsPricing() {
6786
+ const credentials = loadCredentials();
6787
+ const apiBase = credentials ? credentials.apiBase : process.env.CRAB_API ?? readFlag("--api") ?? DEFAULT_API;
6788
+ const res = await api2(apiBase, "/v1/pricing/standard");
6789
+ if (res.status !== 200) {
6790
+ console.error(errorMessage(res.body, "获取动作计价失败。"));
6791
+ return exitCodeForApiError(res.body);
6792
+ }
6793
+ const actions = res.body.data?.actions ?? [];
6794
+ if (hasFlag("--json")) {
6795
+ console.log(JSON.stringify(res.body.data, null, 2));
6796
+ return 0;
6797
+ }
6798
+ if (actions.length === 0) {
6799
+ console.log("暂无动作计价项。");
6800
+ return 0;
6801
+ }
6802
+ console.log("动作计价(credit_cents,100 = $1.00):");
6803
+ for (const item of actions) {
6804
+ const label = CREDIT_ACTION_LABELS[item.action] ?? item.action;
6805
+ console.log(` ${item.action.padEnd(24)} ${label} ${fmtCredits(item.creditCents)} 积分`);
6806
+ }
6807
+ return 0;
6808
+ }
6481
6809
  async function tokensList() {
6482
6810
  const credentials = requireCredentials();
6483
6811
  if (typeof credentials === "number")
@@ -6752,7 +7080,7 @@ async function mailSend() {
6752
7080
  if (attachmentIds.length > 0)
6753
7081
  console.log(` 附件 ${attachmentIds.length} 个(云盘引用,不复制)`);
6754
7082
  if (result.chargedCredits > 0)
6755
- console.log(` 消耗积分:$${(result.chargedCredits / 100).toFixed(2)}(${result.chargedCredits} credit_cents)`);
7083
+ console.log(` 消耗积分:${fmtCredits(result.chargedCredits)}`);
6756
7084
  if (result.failedExternal > 0)
6757
7085
  console.log(` ⚠ ${result.failedExternal} 个外部收件人发送失败,对应积分已退回`);
6758
7086
  return 0;
@@ -7704,6 +8032,19 @@ switch (command) {
7704
8032
  process.exit(await storageUnshare(args[2]));
7705
8033
  console.error(sub ? `未知子命令:storage ${sub}
7706
8034
  ` : `缺少子命令:storage ls | upload | download | rename | tag | category | publish | unpublish | rm | restore | purge | quota | categories | mkcat | tags | share | shares | unshare
8035
+ `);
8036
+ console.log(usage);
8037
+ process.exit(EXIT_USAGE);
8038
+ }
8039
+ case "credits": {
8040
+ const sub = args[1];
8041
+ if (!sub || sub === "balance")
8042
+ process.exit(await creditsBalance());
8043
+ if (sub === "history")
8044
+ process.exit(await creditsHistory());
8045
+ if (sub === "pricing")
8046
+ process.exit(await creditsPricing());
8047
+ console.error(`未知子命令:credits ${sub}
7707
8048
  `);
7708
8049
  console.log(usage);
7709
8050
  process.exit(EXIT_USAGE);
@@ -30,6 +30,9 @@ Crab Cloud 是用户的个人云底座:账号、订阅与积分是平台层,
30
30
  |---|---|
31
31
  | 看账号身份 / 订阅 / 积分 | `crab whoami` |
32
32
  | 程序化读取身份 | `crab whoami --json` |
33
+ | 积分余额(月送/充值/合计) | `crab credits`(需 `credits.read`,脚本消费加 `--json`) |
34
+ | 积分流水(花了多少、花在哪) | `crab credits history [--limit N] [--offset N]`(变动 ±:消耗为负;`--json` 含 total) |
35
+ | 查动作计价(如发一封外域邮件扣多少) | `crab credits pricing`(公开端点,无需登录) |
33
36
  | 列出已授权的 Agent 令牌 | `crab tokens list`(脚本消费加 `--json`) |
34
37
  | 撤销某个令牌(高危,先确认) | `crab tokens revoke <id>` |
35
38
  | 重新授权 / 调整 scope | `crab login --scopes mail`(组名整组授权;也可单 scope 如 `mail.read`。浏览器确认,你只发起) |
@@ -39,6 +42,10 @@ Crab Cloud 是用户的个人云底座:账号、订阅与积分是平台层,
39
42
  | 我的邀请码、邀请链接与受邀名单 | `crab contacts invite [--reset]` / `crab contacts invitees` |
40
43
  | 管理登录设备 / 会话、修改密码 | 网页个人中心(crabcloud.cc/account)——CLI 有意不开放 |
41
44
 
45
+ 积分命令需 `credits.read` scope(`account` 组含它):退出码 4 报缺该 scope 时,
46
+ 引导 `crab login --scopes account` 重新授权。积分额度一律以**积分数**表述
47
+ (100 积分 = $1.00,仅价格类口径用美元);充值/下单是 Web 会话能力,CLI/Agent
48
+ 令牌不动账——用户要充值时引导网页账单页(crabcloud.cc/account/billing)。
42
49
  拿不准令牌 id 时先 `crab tokens list` 核对;`revoke` 只接受列表里的 id。
43
50
 
44
51
  ## 机器可读输出
package/package.json CHANGED
@@ -1,8 +1,8 @@
1
1
  {
2
2
  "name": "@crabcloud/cli",
3
- "version": "0.1.9",
3
+ "version": "0.1.10",
4
4
  "type": "module",
5
- "description": "Crab Cloud CLI (crab) — agent-first entry to your personal cloud, with skills bundled for Claude Code / Codex / ZCode.",
5
+ "description": "Crab Cloud CLI (crab) \u2014 agent-first entry to your personal cloud, with skills bundled for Claude Code / Codex / ZCode.",
6
6
  "bin": {
7
7
  "crab": "dist/crab.js"
8
8
  },