@lark-apaas/miaoda-cli 0.1.2 → 0.1.3-alpha.09899c4

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.
Files changed (85) hide show
  1. package/README.md +8 -7
  2. package/dist/api/app/api.js +25 -0
  3. package/dist/api/app/index.js +15 -0
  4. package/dist/api/app/schemas.js +79 -0
  5. package/dist/api/app/types.js +58 -0
  6. package/dist/api/db/api.js +390 -55
  7. package/dist/api/db/client.js +65 -25
  8. package/dist/api/db/index.js +12 -1
  9. package/dist/api/db/parsers.js +20 -20
  10. package/dist/api/db/sql-keywords.js +87 -87
  11. package/dist/api/deploy/api.js +60 -0
  12. package/dist/api/deploy/index.js +16 -0
  13. package/dist/api/deploy/schemas.js +105 -0
  14. package/dist/api/deploy/types.js +22 -0
  15. package/dist/api/file/api.js +89 -87
  16. package/dist/api/file/client.js +62 -22
  17. package/dist/api/file/detect.js +3 -3
  18. package/dist/api/file/index.js +2 -1
  19. package/dist/api/file/parsers.js +18 -7
  20. package/dist/api/index.js +7 -1
  21. package/dist/api/observability/api.js +52 -0
  22. package/dist/api/observability/index.js +16 -0
  23. package/dist/api/observability/schemas.js +60 -0
  24. package/dist/api/observability/types.js +27 -0
  25. package/dist/api/plugin/api.js +31 -31
  26. package/dist/cli/commands/app/index.js +62 -0
  27. package/dist/cli/commands/db/index.js +600 -59
  28. package/dist/cli/commands/deploy/index.js +155 -0
  29. package/dist/cli/commands/file/index.js +91 -63
  30. package/dist/cli/commands/index.js +10 -6
  31. package/dist/cli/commands/observability/index.js +240 -0
  32. package/dist/cli/commands/plugin/index.js +27 -27
  33. package/dist/cli/commands/shared.js +86 -10
  34. package/dist/cli/handlers/app/get.js +47 -0
  35. package/dist/cli/handlers/app/index.js +7 -0
  36. package/dist/cli/handlers/app/update.js +59 -0
  37. package/dist/cli/handlers/db/_operator.js +35 -0
  38. package/dist/cli/handlers/db/audit.js +383 -0
  39. package/dist/cli/handlers/db/changelog.js +160 -0
  40. package/dist/cli/handlers/db/data.js +34 -34
  41. package/dist/cli/handlers/db/index.js +17 -1
  42. package/dist/cli/handlers/db/migration.js +245 -0
  43. package/dist/cli/handlers/db/quota.js +68 -0
  44. package/dist/cli/handlers/db/recovery.js +387 -0
  45. package/dist/cli/handlers/db/schema.js +35 -36
  46. package/dist/cli/handlers/db/sql.js +70 -71
  47. package/dist/cli/handlers/deploy/deploy.js +84 -0
  48. package/dist/cli/handlers/deploy/error-log.js +60 -0
  49. package/dist/cli/handlers/deploy/format.js +39 -0
  50. package/dist/cli/handlers/deploy/get.js +71 -0
  51. package/dist/cli/handlers/deploy/helpers.js +41 -0
  52. package/dist/cli/handlers/deploy/history.js +70 -0
  53. package/dist/cli/handlers/deploy/index.js +14 -0
  54. package/dist/cli/handlers/deploy/polling.js +162 -0
  55. package/dist/cli/handlers/file/cp.js +31 -32
  56. package/dist/cli/handlers/file/index.js +3 -1
  57. package/dist/cli/handlers/file/ls.js +6 -7
  58. package/dist/cli/handlers/file/quota.js +66 -0
  59. package/dist/cli/handlers/file/rm.js +33 -32
  60. package/dist/cli/handlers/file/sign.js +4 -5
  61. package/dist/cli/handlers/file/stat.js +11 -11
  62. package/dist/cli/handlers/observability/analytics.js +212 -0
  63. package/dist/cli/handlers/observability/helpers.js +66 -0
  64. package/dist/cli/handlers/observability/index.js +12 -0
  65. package/dist/cli/handlers/observability/log.js +94 -0
  66. package/dist/cli/handlers/observability/metric.js +208 -0
  67. package/dist/cli/handlers/observability/trace.js +102 -0
  68. package/dist/cli/handlers/plugin/plugin-local.js +53 -53
  69. package/dist/cli/handlers/plugin/plugin.js +15 -15
  70. package/dist/cli/help.js +16 -16
  71. package/dist/main.js +13 -9
  72. package/dist/utils/args.js +8 -0
  73. package/dist/utils/colors.js +2 -2
  74. package/dist/utils/config.js +2 -2
  75. package/dist/utils/devops-error.js +28 -0
  76. package/dist/utils/error.js +2 -2
  77. package/dist/utils/git.js +29 -0
  78. package/dist/utils/http.js +119 -1
  79. package/dist/utils/index.js +15 -1
  80. package/dist/utils/output.js +373 -20
  81. package/dist/utils/poll.js +35 -0
  82. package/dist/utils/render.js +27 -27
  83. package/dist/utils/spinner.js +46 -0
  84. package/dist/utils/time.js +208 -0
  85. package/package.json +7 -5
@@ -0,0 +1,41 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.parseStatusFlag = parseStatusFlag;
4
+ exports.validateLimit = validateLimit;
5
+ exports.parseDeployId = parseDeployId;
6
+ const args_1 = require("../../../utils/args");
7
+ const index_1 = require("../../../api/deploy/index");
8
+ /**
9
+ * 把 CLI --status 字符串映射成 BAM NodeStatus 数字;非法值抛 ARGS_INVALID。
10
+ *
11
+ * 历史接口(CliListPipelineInstances)返回 SimpleInstance,状态语义是 pipeline 节点状态,
12
+ * 取值 todo / running / success / failed / canceled / hold_on。
13
+ */
14
+ function parseStatusFlag(text) {
15
+ const value = (0, index_1.nodeStatusFromText)(text);
16
+ if (value === undefined) {
17
+ (0, args_1.failArgs)(`--status 无效:'${text}'。可选值:todo | running | success | failed | canceled | hold_on`);
18
+ }
19
+ return value;
20
+ }
21
+ /** limit/pageSize 校验(API 限制 1~100) */
22
+ function validateLimit(limit, defaultLimit = 50) {
23
+ const n = Number.isFinite(limit) ? limit : defaultLimit;
24
+ if (n <= 0 || n > 100) {
25
+ (0, args_1.failArgs)(`--limit 必须在 1~100 之间,收到 ${String(n)}`);
26
+ }
27
+ return n;
28
+ }
29
+ /**
30
+ * 把 CLI <deploy-id> 字符串转成 i64 number。
31
+ *
32
+ * 语义上 deploy-id 即 pipelineTaskID(pipeline 实例 ID);BAM CLIGetReleaseLogs
33
+ * IDL 历史命名为 releaseID,但传入的就是这同一个值。
34
+ */
35
+ function parseDeployId(text) {
36
+ const n = Number(text);
37
+ if (!Number.isFinite(n) || !/^\d+$/.test(text)) {
38
+ (0, args_1.failArgs)(`<deploy-id> 必须是数字 ID,收到 '${text}'`);
39
+ }
40
+ return n;
41
+ }
@@ -0,0 +1,70 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ Object.defineProperty(exports, "__esModule", { value: true });
36
+ exports.handleDeployHistory = handleDeployHistory;
37
+ const api = __importStar(require("../../../api/index"));
38
+ const output_1 = require("../../../utils/output");
39
+ const time_1 = require("../../../utils/time");
40
+ const index_1 = require("../../../api/deploy/index");
41
+ const helpers_1 = require("./helpers");
42
+ const format_1 = require("./format");
43
+ /** miaoda deploy history */
44
+ async function handleDeployHistory(opts) {
45
+ const appID = opts.appId;
46
+ const limit = (0, helpers_1.validateLimit)(opts.limit ?? 50);
47
+ const req = {
48
+ appID,
49
+ limit,
50
+ pageToken: opts.cursor,
51
+ };
52
+ if (opts.status)
53
+ req.status = [(0, helpers_1.parseStatusFlag)(opts.status)];
54
+ // BAM startTimestamp / endTimestamp 单位是秒(IDL desc:单位:秒)
55
+ if (opts.since !== undefined)
56
+ req.startTimestamp = msToSec((0, time_1.parseToMs)(opts.since));
57
+ if (opts.until !== undefined)
58
+ req.endTimestamp = msToSec((0, time_1.parseToMs)(opts.until));
59
+ const resp = await api.deploy.listPipelineInstances(req);
60
+ (0, output_1.emit)({
61
+ data: (resp.instances ?? []).map(format_1.formatSimpleInstance),
62
+ next_cursor: resp.hasMore ? (resp.nextPageToken ?? null) : null,
63
+ has_more: resp.hasMore ?? false,
64
+ }, index_1.deployHistorySchema);
65
+ }
66
+ function msToSec(ms) {
67
+ if (ms === undefined)
68
+ return undefined;
69
+ return Math.floor(ms / 1000);
70
+ }
@@ -0,0 +1,14 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.DEFAULT_POLL_INTERVAL_MS = exports.waitForPipeline = exports.handleDeployErrorLog = exports.handleDeployHistory = exports.handleDeployGet = exports.handleDeploy = void 0;
4
+ var deploy_1 = require("./deploy");
5
+ Object.defineProperty(exports, "handleDeploy", { enumerable: true, get: function () { return deploy_1.handleDeploy; } });
6
+ var get_1 = require("./get");
7
+ Object.defineProperty(exports, "handleDeployGet", { enumerable: true, get: function () { return get_1.handleDeployGet; } });
8
+ var history_1 = require("./history");
9
+ Object.defineProperty(exports, "handleDeployHistory", { enumerable: true, get: function () { return history_1.handleDeployHistory; } });
10
+ var error_log_1 = require("./error-log");
11
+ Object.defineProperty(exports, "handleDeployErrorLog", { enumerable: true, get: function () { return error_log_1.handleDeployErrorLog; } });
12
+ var polling_1 = require("./polling");
13
+ Object.defineProperty(exports, "waitForPipeline", { enumerable: true, get: function () { return polling_1.waitForPipeline; } });
14
+ Object.defineProperty(exports, "DEFAULT_POLL_INTERVAL_MS", { enumerable: true, get: function () { return polling_1.DEFAULT_POLL_INTERVAL_MS; } });
@@ -0,0 +1,162 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ var __importDefault = (this && this.__importDefault) || function (mod) {
36
+ return (mod && mod.__esModule) ? mod : { "default": mod };
37
+ };
38
+ Object.defineProperty(exports, "__esModule", { value: true });
39
+ exports.DEFAULT_POLL_INTERVAL_MS = void 0;
40
+ exports.waitForPipeline = waitForPipeline;
41
+ const ora_1 = __importDefault(require("ora"));
42
+ const api = __importStar(require("../../../api/index"));
43
+ const error_1 = require("../../../utils/error");
44
+ const output_1 = require("../../../utils/output");
45
+ const index_1 = require("../../../api/deploy/index");
46
+ /** 默认轮询间隔(毫秒) */
47
+ exports.DEFAULT_POLL_INTERVAL_MS = 2_000;
48
+ const TERMINAL_STATUSES = [
49
+ index_1.NodeStatus.SUCCESS,
50
+ index_1.NodeStatus.FAILED,
51
+ index_1.NodeStatus.CANCELED,
52
+ ];
53
+ function defaultSleep(ms) {
54
+ return new Promise((res) => setTimeout(res, ms));
55
+ }
56
+ /**
57
+ * 轮询 pipeline 状态直到终态或超时。
58
+ *
59
+ * - JSON 模式:轮询期间静默;终态后由 caller 输出最终信封。
60
+ * - Pretty 非 TTY:同 JSON。
61
+ * - Pretty TTY:底部行用 braille spinner 显示当前正在跑的 stage(每 80ms 跳一帧),
62
+ * stage 转终态时把 ✓ stage(12.3s) 打到 spinner 上方然后清掉 spinner 行。
63
+ *
64
+ * 终态返回最终 PipelineInstance,调用方再决定怎么 emit。
65
+ */
66
+ async function waitForPipeline(opts) {
67
+ const interval = opts.intervalMs ?? exports.DEFAULT_POLL_INTERVAL_MS;
68
+ const timeoutMs = opts.timeoutSec * 1000;
69
+ const sleep = opts.sleep ?? defaultSleep;
70
+ const now = opts.now ?? Date.now;
71
+ const start = now();
72
+ // ora 自身在非 TTY 下会降级为不动画,但我们额外加 isJsonMode 闸门:JSON / 字段选择
73
+ // 模式下完全静默。
74
+ const showProgress = !(0, output_1.isJsonMode)() && process.stdout.isTTY;
75
+ const printedStages = new Set();
76
+ const spinner = showProgress
77
+ ? (0, ora_1.default)({ text: '等待调度', spinner: 'dots', stream: process.stdout }).start()
78
+ : null;
79
+ try {
80
+ for (;;) {
81
+ const resp = await api.deploy.queryPipelineInstance({
82
+ appID: opts.appID,
83
+ instanceID: opts.pipelineTaskID,
84
+ });
85
+ const detail = normalizePipelineInstance(resp);
86
+ if (spinner)
87
+ advanceProgress(detail, printedStages, spinner);
88
+ if (TERMINAL_STATUSES.includes(detail.status)) {
89
+ return detail;
90
+ }
91
+ if (now() - start >= timeoutMs) {
92
+ throw new error_1.AppError('DEPLOY_TIMEOUT', `轮询发布状态超时(${String(opts.timeoutSec)}s)`, {
93
+ retryable: true,
94
+ next_actions: [
95
+ `Run \`miaoda deploy get <deploy-id>\` 查看最新状态`,
96
+ `加大 --timeout 后重试`,
97
+ ],
98
+ });
99
+ }
100
+ await sleep(interval);
101
+ }
102
+ }
103
+ finally {
104
+ spinner?.stop();
105
+ }
106
+ }
107
+ /**
108
+ * 每轮 poll 的进度推进:刚转终态的 stage 用 stopAndPersist 落定为永久行,
109
+ * 再把 spinner 文本切到当前 running stage;没有 running 时显示"等待调度"。
110
+ */
111
+ function advanceProgress(detail, printed, spinner) {
112
+ const stages = Array.isArray(detail.stages) ? detail.stages : [];
113
+ for (const stage of stages) {
114
+ if (printed.has(stage.id))
115
+ continue;
116
+ if (!TERMINAL_STATUSES.includes(stage.status))
117
+ continue;
118
+ spinner.stopAndPersist({
119
+ symbol: stageSymbol(stage),
120
+ text: formatStageBody(stage),
121
+ });
122
+ printed.add(stage.id);
123
+ }
124
+ const running = stages.find((s) => s.status === index_1.NodeStatus.RUNNING);
125
+ const text = running ? `${running.name} 执行中` : '等待调度';
126
+ if (spinner.isSpinning) {
127
+ spinner.text = text;
128
+ }
129
+ else {
130
+ spinner.start(text);
131
+ }
132
+ }
133
+ function normalizePipelineInstance(resp) {
134
+ const keyedDetail = readPipelineDetail(resp, 'pipelineInstanceDetail') ?? readPipelineDetail(resp, 'detail');
135
+ if (keyedDetail)
136
+ return keyedDetail;
137
+ if (isPipelineInstance(resp))
138
+ return resp;
139
+ throw new error_1.AppError('DEPLOY_RESPONSE_INVALID', '发布状态响应缺少 pipeline detail', {
140
+ next_actions: ['使用 --verbose 重试以查看请求 logid,并联系平台排查响应结构'],
141
+ });
142
+ }
143
+ function readPipelineDetail(resp, key) {
144
+ if (!(key in resp))
145
+ return undefined;
146
+ const detail = resp[key];
147
+ return isPipelineInstance(detail) ? detail : undefined;
148
+ }
149
+ function isPipelineInstance(value) {
150
+ if (typeof value !== 'object' || value === null)
151
+ return false;
152
+ const item = value;
153
+ return typeof item.status === 'number';
154
+ }
155
+ function stageSymbol(stage) {
156
+ return stage.status === index_1.NodeStatus.SUCCESS ? '✓' : stage.status === index_1.NodeStatus.FAILED ? '✗' : '•';
157
+ }
158
+ function formatStageBody(stage) {
159
+ const cost = stage.costTime >= 0 ? `(${(stage.costTime / 1000).toFixed(1)}s)` : '';
160
+ const status = (0, index_1.nodeStatusText)(stage.status) ?? String(stage.status);
161
+ return `${stage.name}${cost ? ` ${cost}` : ''} ${status}`.trim();
162
+ }
@@ -44,7 +44,6 @@ const api = __importStar(require("../../../api/index"));
44
44
  const output_1 = require("../../../utils/output");
45
45
  const render_1 = require("../../../utils/render");
46
46
  const error_1 = require("../../../utils/error");
47
- const shared_1 = require("../../../cli/commands/shared");
48
47
  const colors_1 = require("../../../utils/colors");
49
48
  const MAX_UPLOAD_BYTES = 100 * 1024 * 1024;
50
49
  /**
@@ -57,9 +56,9 @@ const MAX_UPLOAD_BYTES = 100 * 1024 * 1024;
57
56
  * 不存在即远程 path(download,由 resolveRemotePath 处理)。
58
57
  */
59
58
  function isLocalSrc(src) {
60
- if (src.startsWith("./") || src.startsWith("../") || src.startsWith("~/"))
59
+ if (src.startsWith('./') || src.startsWith('../') || src.startsWith('~/'))
61
60
  return true;
62
- if (!src.includes("/"))
61
+ if (!src.includes('/'))
63
62
  return true;
64
63
  // `/` 开头:可能是本地绝对路径,也可能是远程 path;交给 fs 探测。
65
64
  const expanded = expandHome(src);
@@ -71,38 +70,38 @@ function isLocalSrc(src) {
71
70
  }
72
71
  }
73
72
  function expandHome(p) {
74
- if (p.startsWith("~/"))
73
+ if (p.startsWith('~/'))
75
74
  return node_path_1.default.join(node_os_1.default.homedir(), p.slice(2));
76
75
  return p;
77
76
  }
78
77
  function detectMime(filePath) {
79
78
  const ext = node_path_1.default.extname(filePath).toLowerCase();
80
79
  const map = {
81
- ".png": "image/png",
82
- ".jpg": "image/jpeg",
83
- ".jpeg": "image/jpeg",
84
- ".gif": "image/gif",
85
- ".webp": "image/webp",
86
- ".svg": "image/svg+xml",
87
- ".pdf": "application/pdf",
88
- ".json": "application/json",
89
- ".csv": "text/csv",
90
- ".txt": "text/plain",
91
- ".html": "text/html",
92
- ".zip": "application/zip",
93
- ".tar": "application/x-tar",
94
- ".gz": "application/gzip",
95
- ".mp4": "video/mp4",
96
- ".mp3": "audio/mpeg",
80
+ '.png': 'image/png',
81
+ '.jpg': 'image/jpeg',
82
+ '.jpeg': 'image/jpeg',
83
+ '.gif': 'image/gif',
84
+ '.webp': 'image/webp',
85
+ '.svg': 'image/svg+xml',
86
+ '.pdf': 'application/pdf',
87
+ '.json': 'application/json',
88
+ '.csv': 'text/csv',
89
+ '.txt': 'text/plain',
90
+ '.html': 'text/html',
91
+ '.zip': 'application/zip',
92
+ '.tar': 'application/x-tar',
93
+ '.gz': 'application/gzip',
94
+ '.mp4': 'video/mp4',
95
+ '.mp3': 'audio/mpeg',
97
96
  };
98
- return map[ext] ?? "application/octet-stream";
97
+ return map[ext] ?? 'application/octet-stream';
99
98
  }
100
99
  /** 把 src 的远程形式(`/path` / `file_name`)解析成后端 sign 可用的 remote path。 */
101
100
  async function resolveRemotePath(appId, input) {
102
- if (input.startsWith("/"))
101
+ if (input.startsWith('/'))
103
102
  return input;
104
103
  const [resolved] = await api.file.resolveInputs({ appId, inputs: [input] });
105
- if (resolved.status === "error") {
104
+ if (resolved.status === 'error') {
106
105
  throw new error_1.AppError(resolved.error.code, resolved.error.message, {
107
106
  next_actions: resolved.error.hint ? [resolved.error.hint] : undefined,
108
107
  });
@@ -112,15 +111,15 @@ async function resolveRemotePath(appId, input) {
112
111
  async function handleUpload(appId, localRaw, remoteRaw, rename) {
113
112
  const localPath = expandHome(localRaw);
114
113
  if (!node_fs_1.default.existsSync(localPath) || !node_fs_1.default.statSync(localPath).isFile()) {
115
- throw new error_1.AppError("FILE_SRC_NOT_FOUND", `Local file '${localRaw}' does not exist`, {
114
+ throw new error_1.AppError('FILE_SRC_NOT_FOUND', `Local file '${localRaw}' does not exist`, {
116
115
  // 引导本地路径自检;远程下载请用 `/path` 形态,避免裸名/相对路径误入上传分支
117
- next_actions: ["Check the local file path; use `/path` prefix for remote download."],
116
+ next_actions: ['Check the local file path; use `/path` prefix for remote download.'],
118
117
  });
119
118
  }
120
119
  const stat = node_fs_1.default.statSync(localPath);
121
120
  if (stat.size > MAX_UPLOAD_BYTES) {
122
- throw new error_1.AppError("FILE_SIZE_EXCEEDED", `File size ${(0, render_1.formatSize)(stat.size)} exceeds the 100 MB upload limit`, {
123
- next_actions: ["Split the file, or use the web console for large uploads."],
121
+ throw new error_1.AppError('FILE_SIZE_EXCEEDED', `File size ${(0, render_1.formatSize)(stat.size)} exceeds the 100 MB upload limit`, {
122
+ next_actions: ['Split the file, or use the web console for large uploads.'],
124
123
  });
125
124
  }
126
125
  // PRD:path 末段**始终**由平台生成 16 位 ID 确保唯一,不受 dst 形态或
@@ -135,12 +134,12 @@ async function handleUpload(appId, localRaw, remoteRaw, rename) {
135
134
  // 完整对象 key 模式。--rename 始终覆盖 file_name 推断结果(PRD 优先级)。
136
135
  let fileName;
137
136
  let remotePath;
138
- if (remoteRaw === "" || remoteRaw.endsWith("/")) {
137
+ if (remoteRaw === '' || remoteRaw.endsWith('/')) {
139
138
  fileName = rename ?? node_path_1.default.basename(localPath);
140
139
  remotePath = remoteRaw;
141
140
  }
142
141
  else {
143
- const lastSlash = remoteRaw.lastIndexOf("/");
142
+ const lastSlash = remoteRaw.lastIndexOf('/');
144
143
  fileName = rename ?? remoteRaw.slice(lastSlash + 1);
145
144
  remotePath = remoteRaw.slice(0, lastSlash + 1);
146
145
  }
@@ -188,7 +187,7 @@ async function handleUpload(appId, localRaw, remoteRaw, rename) {
188
187
  lines.push(`download_url\t${result.download_url}`);
189
188
  }
190
189
  }
191
- (0, output_1.emit)(lines.join("\n"));
190
+ (0, output_1.emit)(lines.join('\n'));
192
191
  }
193
192
  async function handleDownload(appId, remoteRaw, localRaw) {
194
193
  const filePath = await resolveRemotePath(appId, remoteRaw);
@@ -199,7 +198,7 @@ async function handleDownload(appId, remoteRaw, localRaw) {
199
198
  });
200
199
  const baseName = node_path_1.default.basename(filePath);
201
200
  let localTarget = expandHome(localRaw);
202
- if (localTarget === "./" || localTarget === "." || localTarget.endsWith("/")) {
201
+ if (localTarget === './' || localTarget === '.' || localTarget.endsWith('/')) {
203
202
  localTarget = node_path_1.default.join(localTarget, baseName);
204
203
  }
205
204
  else if (node_fs_1.default.existsSync(localTarget) && node_fs_1.default.statSync(localTarget).isDirectory()) {
@@ -233,7 +232,7 @@ async function handleDownload(appId, remoteRaw, localRaw) {
233
232
  }
234
233
  }
235
234
  async function handleFileCp(src, dst, opts) {
236
- const appId = (0, shared_1.resolveAppId)(opts);
235
+ const appId = opts.appId;
237
236
  // src 本地存在 → upload;其他情况(file_ref / `/path` / 裸文件名)→ download
238
237
  if (isLocalSrc(src)) {
239
238
  return handleUpload(appId, src, dst, opts.rename);
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.handleFileSign = exports.handleFileRm = exports.handleFileCp = exports.handleFileStat = exports.handleFileLs = void 0;
3
+ exports.handleFileQuota = exports.handleFileSign = exports.handleFileRm = exports.handleFileCp = exports.handleFileStat = exports.handleFileLs = void 0;
4
4
  var ls_1 = require("./ls");
5
5
  Object.defineProperty(exports, "handleFileLs", { enumerable: true, get: function () { return ls_1.handleFileLs; } });
6
6
  var stat_1 = require("./stat");
@@ -11,3 +11,5 @@ var rm_1 = require("./rm");
11
11
  Object.defineProperty(exports, "handleFileRm", { enumerable: true, get: function () { return rm_1.handleFileRm; } });
12
12
  var sign_1 = require("./sign");
13
13
  Object.defineProperty(exports, "handleFileSign", { enumerable: true, get: function () { return sign_1.handleFileSign; } });
14
+ var quota_1 = require("./quota");
15
+ Object.defineProperty(exports, "handleFileQuota", { enumerable: true, get: function () { return quota_1.handleFileQuota; } });
@@ -38,7 +38,6 @@ const api = __importStar(require("../../../api/index"));
38
38
  const output_1 = require("../../../utils/output");
39
39
  const render_1 = require("../../../utils/render");
40
40
  const error_1 = require("../../../utils/error");
41
- const shared_1 = require("../../../cli/commands/shared");
42
41
  const index_1 = require("../../../api/file/index");
43
42
  /**
44
43
  * 把位置参数 `query` 路由到 `--path` 或 `--name`:
@@ -56,7 +55,7 @@ function resolveQueryRouting(opts) {
56
55
  if (!opts.query)
57
56
  return explicit;
58
57
  if (explicit.path || explicit.name) {
59
- throw new error_1.AppError("ARGS_INVALID", "Do not mix positional <query> with --path / --name; pick one.");
58
+ throw new error_1.AppError('ARGS_INVALID', 'Do not mix positional <query> with --path / --name; pick one.');
60
59
  }
61
60
  if ((0, index_1.looksLikePath)(opts.query)) {
62
61
  // 补齐前导 `/`,对齐 ls sidecar 比较逻辑(info.path 永远带前导 `/`)
@@ -65,7 +64,7 @@ function resolveQueryRouting(opts) {
65
64
  return { name: opts.query };
66
65
  }
67
66
  async function handleFileLs(opts) {
68
- const appId = (0, shared_1.resolveAppId)(opts);
67
+ const appId = opts.appId;
69
68
  // commander 已经把 --limit 解析为 number;保留 ?? undefined 兼容老调用
70
69
  const limit = opts.limit;
71
70
  const sizeGt = opts.sizeGt ? (0, render_1.parseSize)(opts.sizeGt) : undefined;
@@ -89,13 +88,13 @@ async function handleFileLs(opts) {
89
88
  return;
90
89
  }
91
90
  if (result.items.length === 0) {
92
- (0, output_1.emit)("No files found.");
91
+ (0, output_1.emit)('No files found.');
93
92
  return;
94
93
  }
95
94
  const tty = (0, render_1.isStdoutTty)();
96
- const headers = ["file_name", "path", "size", "type", "uploaded_at"];
95
+ const headers = ['file_name', 'path', 'size', 'type', 'uploaded_at'];
97
96
  const rows = result.items.map((info) => [
98
- info.file_name || "",
97
+ info.file_name || '',
99
98
  info.path,
100
99
  tty ? (0, render_1.formatSize)(info.size_bytes) : String(info.size_bytes),
101
100
  info.type,
@@ -104,6 +103,6 @@ async function handleFileLs(opts) {
104
103
  const table = tty ? (0, render_1.renderAlignedTable)(headers, rows) : (0, render_1.renderTsv)(headers, rows);
105
104
  const hint = result.has_more && result.next_cursor
106
105
  ? `\n— ${String(result.items.length)} results. Next: --cursor ${result.next_cursor}`
107
- : "";
106
+ : '';
108
107
  (0, output_1.emit)(table + hint);
109
108
  }
@@ -0,0 +1,66 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ Object.defineProperty(exports, "__esModule", { value: true });
36
+ exports.handleFileQuota = handleFileQuota;
37
+ const api = __importStar(require("../../../api/index"));
38
+ const shared_1 = require("../../../cli/commands/shared");
39
+ const output_1 = require("../../../utils/output");
40
+ const render_1 = require("../../../utils/render");
41
+ async function handleFileQuota(opts) {
42
+ const appId = (0, shared_1.resolveAppId)(opts);
43
+ const data = await api.file.getStorageQuota({ appId });
44
+ if ((0, output_1.isJsonMode)()) {
45
+ // 配额未对接(storageQuotaBytes=0)时,quota / usage_percent 字段都不输出
46
+ const out = {
47
+ storageUsedBytes: data.storageUsedBytes,
48
+ files: data.files,
49
+ };
50
+ if (data.storageQuotaBytes > 0) {
51
+ out.storageQuotaBytes = data.storageQuotaBytes;
52
+ out.usagePercent = data.usagePercent;
53
+ }
54
+ (0, output_1.emitOk)((0, output_1.snakeCaseKeys)(out));
55
+ return;
56
+ }
57
+ // PRD:单行 "Storage: 150 MB / 1 GB (15%)";配额未对接时只显示 used
58
+ const tty = (0, render_1.isStdoutTty)();
59
+ const storageLine = data.storageQuotaBytes > 0
60
+ ? `${(0, render_1.formatSize)(data.storageUsedBytes)} / ${(0, render_1.formatSize)(data.storageQuotaBytes)} (${data.usagePercent.toFixed(1)}%)`
61
+ : (0, render_1.formatSize)(data.storageUsedBytes);
62
+ (0, output_1.emit)((0, render_1.renderKeyValue)([
63
+ ['Storage', storageLine],
64
+ ['Files', String(data.files)],
65
+ ], tty));
66
+ }