@lark-apaas/miaoda-cli 0.1.0-alpha.08508f4
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/LICENSE +13 -0
- package/README.md +66 -0
- package/bin/miaoda.js +2 -0
- package/dist/api/db/api.js +200 -0
- package/dist/api/db/client.js +166 -0
- package/dist/api/db/index.js +16 -0
- package/dist/api/db/parsers.js +161 -0
- package/dist/api/db/types.js +10 -0
- package/dist/api/file/api.js +425 -0
- package/dist/api/file/client.js +199 -0
- package/dist/api/file/detect.js +56 -0
- package/dist/api/file/index.js +17 -0
- package/dist/api/file/parsers.js +72 -0
- package/dist/api/file/types.js +3 -0
- package/dist/api/index.js +42 -0
- package/dist/api/plugin/api.js +243 -0
- package/dist/api/plugin/index.js +12 -0
- package/dist/api/plugin/types.js +3 -0
- package/dist/cli/commands/db/index.js +69 -0
- package/dist/cli/commands/file/index.js +75 -0
- package/dist/cli/commands/index.js +11 -0
- package/dist/cli/commands/plugin/index.js +204 -0
- package/dist/cli/commands/shared.js +52 -0
- package/dist/cli/handlers/db/data.js +168 -0
- package/dist/cli/handlers/db/index.js +11 -0
- package/dist/cli/handlers/db/schema.js +163 -0
- package/dist/cli/handlers/db/sql.js +252 -0
- package/dist/cli/handlers/file/cp.js +220 -0
- package/dist/cli/handlers/file/index.js +13 -0
- package/dist/cli/handlers/file/ls.js +110 -0
- package/dist/cli/handlers/file/rm.js +263 -0
- package/dist/cli/handlers/file/sign.js +96 -0
- package/dist/cli/handlers/file/stat.js +97 -0
- package/dist/cli/handlers/index.js +19 -0
- package/dist/cli/handlers/plugin/index.js +10 -0
- package/dist/cli/handlers/plugin/plugin-local.js +382 -0
- package/dist/cli/handlers/plugin/plugin.js +308 -0
- package/dist/main.js +31 -0
- package/dist/utils/config.js +31 -0
- package/dist/utils/error.js +38 -0
- package/dist/utils/http.js +67 -0
- package/dist/utils/index.js +27 -0
- package/dist/utils/log_id.js +13 -0
- package/dist/utils/logger.js +15 -0
- package/dist/utils/output.js +72 -0
- package/dist/utils/render.js +187 -0
- package/package.json +53 -0
|
@@ -0,0 +1,252 @@
|
|
|
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.handleDbSql = handleDbSql;
|
|
37
|
+
const api = __importStar(require("../../../api/index"));
|
|
38
|
+
const error_1 = require("../../../utils/error");
|
|
39
|
+
const output_1 = require("../../../utils/output");
|
|
40
|
+
const config_1 = require("../../../utils/config");
|
|
41
|
+
const shared_1 = require("../../../cli/commands/shared");
|
|
42
|
+
const render_1 = require("../../../utils/render");
|
|
43
|
+
const index_1 = require("../../../api/db/index");
|
|
44
|
+
/**
|
|
45
|
+
* miaoda db sql <query> — 执行任意 SQL。
|
|
46
|
+
*
|
|
47
|
+
* - query 为空时读 stdin
|
|
48
|
+
* - --env 透传给后端 admin-inner(dbBranch 参数),不传时由后端按 workspace
|
|
49
|
+
* 多环境状态兜底(多环境 → dev / 单环境 → main);后端检测到环境不存在
|
|
50
|
+
* 会返 k_dl_1600000 + "Invalid DB Branch:...",CLI 侧映射为 MULTI_ENV_NOT_INITIALIZED
|
|
51
|
+
* - 多语句行为对齐 PRD:每条 statement 一个独立结果元素,pretty 逐条 +
|
|
52
|
+
* 末尾汇总,--json 输出 data 数组。
|
|
53
|
+
*/
|
|
54
|
+
async function handleDbSql(query, opts) {
|
|
55
|
+
const appId = (0, shared_1.resolveAppId)(opts);
|
|
56
|
+
const sql = await readSql(query);
|
|
57
|
+
if (!sql.trim()) {
|
|
58
|
+
throw new error_1.AppError("ARGS_INVALID", "Empty SQL (no inline query and stdin is empty)");
|
|
59
|
+
}
|
|
60
|
+
const results = await api.db.execSql({ appId, sql, dbBranch: opts.env });
|
|
61
|
+
if (results.length === 0) {
|
|
62
|
+
// 后端未返回任何结果,通常不会发生
|
|
63
|
+
if ((0, output_1.isJsonMode)()) {
|
|
64
|
+
(0, output_1.emit)({ data: [] });
|
|
65
|
+
return;
|
|
66
|
+
}
|
|
67
|
+
(0, output_1.emit)("✓ No results");
|
|
68
|
+
return;
|
|
69
|
+
}
|
|
70
|
+
if (results.length === 1) {
|
|
71
|
+
renderSingle(results[0]);
|
|
72
|
+
return;
|
|
73
|
+
}
|
|
74
|
+
// 多语句:每条 statement 独立结果
|
|
75
|
+
if ((0, output_1.isJsonMode)()) {
|
|
76
|
+
(0, output_1.emit)({ data: results.map((r) => toMultiElement((0, index_1.parseSqlResult)(r))) });
|
|
77
|
+
return;
|
|
78
|
+
}
|
|
79
|
+
renderMultiPretty(results);
|
|
80
|
+
}
|
|
81
|
+
/** 读取 stdin 并返回完整 SQL 文本(stdin 不是 TTY 即认为被 pipe)。 */
|
|
82
|
+
async function readSql(inline) {
|
|
83
|
+
if (inline !== undefined && inline.length > 0)
|
|
84
|
+
return inline;
|
|
85
|
+
const stdin = process.stdin;
|
|
86
|
+
if (stdin.isTTY)
|
|
87
|
+
return "";
|
|
88
|
+
return new Promise((resolve, reject) => {
|
|
89
|
+
let data = "";
|
|
90
|
+
process.stdin.setEncoding("utf8");
|
|
91
|
+
process.stdin.on("data", (chunk) => {
|
|
92
|
+
data += chunk;
|
|
93
|
+
});
|
|
94
|
+
process.stdin.on("end", () => {
|
|
95
|
+
resolve(data);
|
|
96
|
+
});
|
|
97
|
+
process.stdin.on("error", reject);
|
|
98
|
+
});
|
|
99
|
+
}
|
|
100
|
+
function renderSingle(raw) {
|
|
101
|
+
const parsed = (0, index_1.parseSqlResult)(raw);
|
|
102
|
+
const tty = (0, render_1.isStdoutTty)();
|
|
103
|
+
if ((0, output_1.isJsonMode)()) {
|
|
104
|
+
(0, output_1.emit)(toJson(parsed));
|
|
105
|
+
return;
|
|
106
|
+
}
|
|
107
|
+
if (parsed.kind === "select") {
|
|
108
|
+
if (parsed.rows.length === 0) {
|
|
109
|
+
(0, output_1.emit)("(0 rows)");
|
|
110
|
+
return;
|
|
111
|
+
}
|
|
112
|
+
const cols = collectColumns(parsed.rows);
|
|
113
|
+
const rows = parsed.rows.map((r) => cols.map((c) => formatCell(r[c], tty)));
|
|
114
|
+
(0, output_1.emit)(tty ? (0, render_1.renderAlignedTable)(cols, rows) : (0, render_1.renderTsv)(cols, rows));
|
|
115
|
+
return;
|
|
116
|
+
}
|
|
117
|
+
if (parsed.kind === "dml") {
|
|
118
|
+
const verb = dmlVerb(parsed.sqlType);
|
|
119
|
+
(0, output_1.emit)(tty
|
|
120
|
+
? `✓ ${String(parsed.affectedRows)} row${parsed.affectedRows === 1 ? "" : "s"} ${verb}`
|
|
121
|
+
: `OK ${String(parsed.affectedRows)} rows ${verb}`);
|
|
122
|
+
return;
|
|
123
|
+
}
|
|
124
|
+
// DDL
|
|
125
|
+
(0, output_1.emit)(tty ? "✓ DDL executed" : "OK DDL executed");
|
|
126
|
+
}
|
|
127
|
+
function toJson(parsed) {
|
|
128
|
+
if (parsed.kind === "select") {
|
|
129
|
+
// PRD 单 SELECT:data 直接是行数组(按 --json 字段投影裁剪)
|
|
130
|
+
return { data: projectRows(parsed.rows) };
|
|
131
|
+
}
|
|
132
|
+
if (parsed.kind === "dml") {
|
|
133
|
+
// PRD 单 DML:data = {command, rows_affected}
|
|
134
|
+
return {
|
|
135
|
+
data: {
|
|
136
|
+
command: parsed.sqlType,
|
|
137
|
+
rows_affected: parsed.affectedRows,
|
|
138
|
+
},
|
|
139
|
+
};
|
|
140
|
+
}
|
|
141
|
+
// PRD 单 DDL:data = {command, target?}。command 直接用后端给的细粒度
|
|
142
|
+
// (CREATE_TABLE / DROP_TABLE / ...),target 待后端给对象名后再加。
|
|
143
|
+
return { data: { command: parsed.sqlType } };
|
|
144
|
+
}
|
|
145
|
+
/**
|
|
146
|
+
* 多语句 --json 元素:与单 DDL/DML 形状一致,但 SELECT 包成
|
|
147
|
+
* {command:"SELECT", rows:[...]}(PRD 约定,避免数组里嵌套数组造成歧义)。
|
|
148
|
+
*/
|
|
149
|
+
function toMultiElement(parsed) {
|
|
150
|
+
if (parsed.kind === "select") {
|
|
151
|
+
return { command: "SELECT", rows: projectRows(parsed.rows) };
|
|
152
|
+
}
|
|
153
|
+
if (parsed.kind === "dml") {
|
|
154
|
+
return { command: parsed.sqlType, rows_affected: parsed.affectedRows };
|
|
155
|
+
}
|
|
156
|
+
// DDL:用后端给的细粒度 command
|
|
157
|
+
return { command: parsed.sqlType };
|
|
158
|
+
}
|
|
159
|
+
/**
|
|
160
|
+
* PRD:`--json id,name` 字段投影。--json 不带值(boolean true)等价于不裁剪。
|
|
161
|
+
* 字段不存在时按 undefined 处理(JSON.stringify 会忽略 undefined value 的 key),
|
|
162
|
+
* 这样 Agent 拿到的 row 永远只含请求过的列。
|
|
163
|
+
*/
|
|
164
|
+
function projectRows(rows) {
|
|
165
|
+
const fields = parseJsonFields();
|
|
166
|
+
if (!fields)
|
|
167
|
+
return rows;
|
|
168
|
+
return rows.map((r) => {
|
|
169
|
+
const out = {};
|
|
170
|
+
for (const f of fields) {
|
|
171
|
+
out[f] = r[f];
|
|
172
|
+
}
|
|
173
|
+
return out;
|
|
174
|
+
});
|
|
175
|
+
}
|
|
176
|
+
/** 读取 --json [fields]:返回字段列表;boolean true 或 undefined 返回 null(不裁剪)。 */
|
|
177
|
+
function parseJsonFields() {
|
|
178
|
+
const v = (0, config_1.getConfig)().json;
|
|
179
|
+
if (typeof v !== "string" || v === "")
|
|
180
|
+
return null;
|
|
181
|
+
return v.split(",").map((s) => s.trim()).filter((s) => s.length > 0);
|
|
182
|
+
}
|
|
183
|
+
/** 多语句 pretty:逐条 `Statement N: ...` + 末尾 `✓ N statements executed`。 */
|
|
184
|
+
function renderMultiPretty(results) {
|
|
185
|
+
const tty = (0, render_1.isStdoutTty)();
|
|
186
|
+
const lines = [];
|
|
187
|
+
for (let i = 0; i < results.length; i++) {
|
|
188
|
+
const parsed = (0, index_1.parseSqlResult)(results[i]);
|
|
189
|
+
const idx = i + 1;
|
|
190
|
+
if (parsed.kind === "select") {
|
|
191
|
+
const n = parsed.rows.length;
|
|
192
|
+
lines.push(`Statement ${String(idx)}: SELECT (${String(n)} row${n === 1 ? "" : "s"})`);
|
|
193
|
+
if (n > 0) {
|
|
194
|
+
const cols = collectColumns(parsed.rows);
|
|
195
|
+
const tbl = parsed.rows.map((r) => cols.map((c) => formatCell(r[c], tty)));
|
|
196
|
+
lines.push(tty ? (0, render_1.renderAlignedTable)(cols, tbl) : (0, render_1.renderTsv)(cols, tbl));
|
|
197
|
+
}
|
|
198
|
+
// 块间空行(最后一条不留)
|
|
199
|
+
if (i < results.length - 1)
|
|
200
|
+
lines.push("");
|
|
201
|
+
continue;
|
|
202
|
+
}
|
|
203
|
+
if (parsed.kind === "dml") {
|
|
204
|
+
const verb = dmlVerb(parsed.sqlType);
|
|
205
|
+
const n = parsed.affectedRows;
|
|
206
|
+
lines.push(`Statement ${String(idx)}: ${tty ? "✓ " : ""}${String(n)} row${n === 1 ? "" : "s"} ${verb}`);
|
|
207
|
+
continue;
|
|
208
|
+
}
|
|
209
|
+
// DDL
|
|
210
|
+
lines.push(`Statement ${String(idx)}: ${tty ? "✓ " : ""}DDL executed`);
|
|
211
|
+
}
|
|
212
|
+
// 汇总行:所有 statement 都跑完了
|
|
213
|
+
lines.push(tty
|
|
214
|
+
? `✓ ${String(results.length)} statements executed`
|
|
215
|
+
: `OK ${String(results.length)} statements executed`);
|
|
216
|
+
(0, output_1.emit)(lines.join("\n"));
|
|
217
|
+
}
|
|
218
|
+
function dmlVerb(type) {
|
|
219
|
+
switch (type) {
|
|
220
|
+
case "INSERT": return "inserted";
|
|
221
|
+
case "UPDATE": return "updated";
|
|
222
|
+
case "DELETE": return "deleted";
|
|
223
|
+
case "MERGE": return "merged";
|
|
224
|
+
case "DML": return "affected"; // 未识别子类的兜底
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
/** 从第一行收集列顺序;缺失列保留空白(兼容稀疏行)。 */
|
|
228
|
+
function collectColumns(rows) {
|
|
229
|
+
if (rows.length === 0)
|
|
230
|
+
return [];
|
|
231
|
+
return Object.keys(rows[0]);
|
|
232
|
+
}
|
|
233
|
+
/**
|
|
234
|
+
* SQL 单元格 → 字符串:
|
|
235
|
+
* - null / undefined → "NULL"(TTY 下用 dim+gray ANSI 弱化,对齐 mysql/DuckDB;
|
|
236
|
+
* non-TTY 用裸字面量,避免被 cat / 管道吞掉样式;JSON 模式不会走到这里,
|
|
237
|
+
* 走 toJson 直接用 parsed.rows,JSON.stringify 输出 `null` 字面)
|
|
238
|
+
* - string → 原样
|
|
239
|
+
* - number / boolean → toString
|
|
240
|
+
* - object / array → JSON.stringify
|
|
241
|
+
*/
|
|
242
|
+
function formatCell(v, tty) {
|
|
243
|
+
if (v === null || v === undefined) {
|
|
244
|
+
return tty ? "\u001b[2;90mNULL\u001b[0m" : "NULL";
|
|
245
|
+
}
|
|
246
|
+
if (typeof v === "string")
|
|
247
|
+
return v;
|
|
248
|
+
if (typeof v === "number" || typeof v === "boolean")
|
|
249
|
+
return String(v);
|
|
250
|
+
// object / array → JSON
|
|
251
|
+
return JSON.stringify(v);
|
|
252
|
+
}
|
|
@@ -0,0 +1,220 @@
|
|
|
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.handleFileCp = handleFileCp;
|
|
40
|
+
const node_fs_1 = __importDefault(require("node:fs"));
|
|
41
|
+
const node_path_1 = __importDefault(require("node:path"));
|
|
42
|
+
const node_os_1 = __importDefault(require("node:os"));
|
|
43
|
+
const api = __importStar(require("../../../api/index"));
|
|
44
|
+
const output_1 = require("../../../utils/output");
|
|
45
|
+
const render_1 = require("../../../utils/render");
|
|
46
|
+
const error_1 = require("../../../utils/error");
|
|
47
|
+
const shared_1 = require("../../../cli/commands/shared");
|
|
48
|
+
const MAX_UPLOAD_BYTES = 100 * 1024 * 1024;
|
|
49
|
+
/**
|
|
50
|
+
* 判断 src 是本地文件还是远程引用:
|
|
51
|
+
* - src 本地 fs 可访问 → upload(dst 当 remote)
|
|
52
|
+
* - 其他 → download(src 是 /path 或 file_name,由 resolveRemotePath 解析)
|
|
53
|
+
*
|
|
54
|
+
* `cp` 语义要求 src 必须存在;本地不存在就认为用户指向远程。不需要给 dst 猜方向。
|
|
55
|
+
*/
|
|
56
|
+
function isLocalSrc(src) {
|
|
57
|
+
const expanded = expandHome(src);
|
|
58
|
+
try {
|
|
59
|
+
return node_fs_1.default.existsSync(expanded);
|
|
60
|
+
}
|
|
61
|
+
catch {
|
|
62
|
+
return false;
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
function expandHome(p) {
|
|
66
|
+
if (p.startsWith("~/"))
|
|
67
|
+
return node_path_1.default.join(node_os_1.default.homedir(), p.slice(2));
|
|
68
|
+
return p;
|
|
69
|
+
}
|
|
70
|
+
function detectMime(filePath) {
|
|
71
|
+
const ext = node_path_1.default.extname(filePath).toLowerCase();
|
|
72
|
+
const map = {
|
|
73
|
+
".png": "image/png",
|
|
74
|
+
".jpg": "image/jpeg",
|
|
75
|
+
".jpeg": "image/jpeg",
|
|
76
|
+
".gif": "image/gif",
|
|
77
|
+
".webp": "image/webp",
|
|
78
|
+
".svg": "image/svg+xml",
|
|
79
|
+
".pdf": "application/pdf",
|
|
80
|
+
".json": "application/json",
|
|
81
|
+
".csv": "text/csv",
|
|
82
|
+
".txt": "text/plain",
|
|
83
|
+
".html": "text/html",
|
|
84
|
+
".zip": "application/zip",
|
|
85
|
+
".tar": "application/x-tar",
|
|
86
|
+
".gz": "application/gzip",
|
|
87
|
+
".mp4": "video/mp4",
|
|
88
|
+
".mp3": "audio/mpeg",
|
|
89
|
+
};
|
|
90
|
+
return map[ext] ?? "application/octet-stream";
|
|
91
|
+
}
|
|
92
|
+
/** 把 src 的远程形式(`/path` / `file_name`)解析成后端 sign 可用的 remote path。 */
|
|
93
|
+
async function resolveRemotePath(appId, input) {
|
|
94
|
+
if (input.startsWith("/"))
|
|
95
|
+
return input;
|
|
96
|
+
const [resolved] = await api.file.resolveInputs({ appId, inputs: [input] });
|
|
97
|
+
if (resolved.status === "error") {
|
|
98
|
+
throw new error_1.AppError(resolved.error.code, resolved.error.message, {
|
|
99
|
+
next_actions: resolved.error.hint ? [resolved.error.hint] : undefined,
|
|
100
|
+
});
|
|
101
|
+
}
|
|
102
|
+
return resolved.file.path;
|
|
103
|
+
}
|
|
104
|
+
async function handleUpload(appId, localRaw, remoteRaw, rename) {
|
|
105
|
+
const localPath = expandHome(localRaw);
|
|
106
|
+
if (!node_fs_1.default.existsSync(localPath) || !node_fs_1.default.statSync(localPath).isFile()) {
|
|
107
|
+
throw new error_1.AppError("FILE_SRC_NOT_FOUND", `Local file '${localRaw}' does not exist`);
|
|
108
|
+
}
|
|
109
|
+
const stat = node_fs_1.default.statSync(localPath);
|
|
110
|
+
if (stat.size > MAX_UPLOAD_BYTES) {
|
|
111
|
+
throw new error_1.AppError("FILE_SIZE_EXCEEDED", `File size ${(0, render_1.formatSize)(stat.size)} exceeds the 100 MB upload limit`, {
|
|
112
|
+
next_actions: [
|
|
113
|
+
"Split the file, or use the web console for large uploads.",
|
|
114
|
+
],
|
|
115
|
+
});
|
|
116
|
+
}
|
|
117
|
+
const fileName = rename ?? node_path_1.default.basename(localPath);
|
|
118
|
+
let remotePath = remoteRaw;
|
|
119
|
+
if (remoteRaw.endsWith("/")) {
|
|
120
|
+
remotePath = remoteRaw + fileName;
|
|
121
|
+
}
|
|
122
|
+
else if (remoteRaw === "/") {
|
|
123
|
+
remotePath = "/" + fileName;
|
|
124
|
+
}
|
|
125
|
+
const contentType = detectMime(localPath);
|
|
126
|
+
const result = await api.file.uploadFile({
|
|
127
|
+
appId,
|
|
128
|
+
localPath,
|
|
129
|
+
remotePath,
|
|
130
|
+
fileName,
|
|
131
|
+
fileSize: stat.size,
|
|
132
|
+
contentType,
|
|
133
|
+
readFile: async () => node_fs_1.default.promises.readFile(localPath),
|
|
134
|
+
});
|
|
135
|
+
if ((0, output_1.isJsonMode)()) {
|
|
136
|
+
const payload = {
|
|
137
|
+
file_name: result.file_name,
|
|
138
|
+
path: result.path,
|
|
139
|
+
size_bytes: result.size,
|
|
140
|
+
type: result.type,
|
|
141
|
+
};
|
|
142
|
+
if (result.download_url)
|
|
143
|
+
payload.download_url = result.download_url;
|
|
144
|
+
(0, output_1.emitOk)(payload);
|
|
145
|
+
return;
|
|
146
|
+
}
|
|
147
|
+
const tty = (0, render_1.isStdoutTty)();
|
|
148
|
+
const lines = [];
|
|
149
|
+
if (tty) {
|
|
150
|
+
lines.push(`✓ Uploaded ${node_path_1.default.basename(localPath)} → ${result.path}`);
|
|
151
|
+
lines.push(` file_name: ${result.file_name}`);
|
|
152
|
+
lines.push(` path: ${result.path}`);
|
|
153
|
+
lines.push(` size: ${(0, render_1.formatSize)(result.size)} (${String(result.size)} bytes)`);
|
|
154
|
+
lines.push(` type: ${result.type}`);
|
|
155
|
+
if (result.download_url) {
|
|
156
|
+
lines.push(` download_url: ${result.download_url}`);
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
else {
|
|
160
|
+
lines.push(`OK Uploaded ${node_path_1.default.basename(localPath)} -> ${result.path}`);
|
|
161
|
+
lines.push(`file_name\t${result.file_name}`);
|
|
162
|
+
lines.push(`path\t${result.path}`);
|
|
163
|
+
lines.push(`size\t${String(result.size)}`);
|
|
164
|
+
lines.push(`type\t${result.type}`);
|
|
165
|
+
if (result.download_url) {
|
|
166
|
+
lines.push(`download_url\t${result.download_url}`);
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
(0, output_1.emit)(lines.join("\n"));
|
|
170
|
+
}
|
|
171
|
+
async function handleDownload(appId, remoteRaw, localRaw) {
|
|
172
|
+
const filePath = await resolveRemotePath(appId, remoteRaw);
|
|
173
|
+
const sign = await api.file.signDownload({
|
|
174
|
+
appId,
|
|
175
|
+
filePath,
|
|
176
|
+
expiresIn: 300,
|
|
177
|
+
});
|
|
178
|
+
const baseName = node_path_1.default.basename(filePath);
|
|
179
|
+
let localTarget = expandHome(localRaw);
|
|
180
|
+
if (localTarget === "./" || localTarget === "." || localTarget.endsWith("/")) {
|
|
181
|
+
localTarget = node_path_1.default.join(localTarget, baseName);
|
|
182
|
+
}
|
|
183
|
+
else if (node_fs_1.default.existsSync(localTarget) && node_fs_1.default.statSync(localTarget).isDirectory()) {
|
|
184
|
+
localTarget = node_path_1.default.join(localTarget, baseName);
|
|
185
|
+
}
|
|
186
|
+
let writtenBytes = 0;
|
|
187
|
+
await api.file.downloadFile({
|
|
188
|
+
signedURL: sign.signed_url,
|
|
189
|
+
writeFile: async (buf) => {
|
|
190
|
+
await node_fs_1.default.promises.mkdir(node_path_1.default.dirname(node_path_1.default.resolve(localTarget)), {
|
|
191
|
+
recursive: true,
|
|
192
|
+
});
|
|
193
|
+
await node_fs_1.default.promises.writeFile(localTarget, buf);
|
|
194
|
+
writtenBytes = buf.length;
|
|
195
|
+
},
|
|
196
|
+
});
|
|
197
|
+
if ((0, output_1.isJsonMode)()) {
|
|
198
|
+
(0, output_1.emitOk)({
|
|
199
|
+
path: filePath,
|
|
200
|
+
local_path: localTarget,
|
|
201
|
+
size: writtenBytes,
|
|
202
|
+
});
|
|
203
|
+
return;
|
|
204
|
+
}
|
|
205
|
+
const tty = (0, render_1.isStdoutTty)();
|
|
206
|
+
if (tty) {
|
|
207
|
+
(0, output_1.emit)(`✓ Downloaded ${baseName} → ${localTarget} (${(0, render_1.formatSize)(writtenBytes)})`);
|
|
208
|
+
}
|
|
209
|
+
else {
|
|
210
|
+
(0, output_1.emit)(`OK Downloaded ${baseName} -> ${localTarget} (${String(writtenBytes)} bytes)`);
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
async function handleFileCp(src, dst, opts) {
|
|
214
|
+
const appId = (0, shared_1.resolveAppId)(opts);
|
|
215
|
+
// src 本地存在 → upload;其他情况(file_ref / `/path` / 裸文件名)→ download
|
|
216
|
+
if (isLocalSrc(src)) {
|
|
217
|
+
return handleUpload(appId, src, dst, opts.rename);
|
|
218
|
+
}
|
|
219
|
+
return handleDownload(appId, src, dst);
|
|
220
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.handleFileSign = exports.handleFileRm = exports.handleFileCp = exports.handleFileStat = exports.handleFileLs = void 0;
|
|
4
|
+
var ls_1 = require("./ls");
|
|
5
|
+
Object.defineProperty(exports, "handleFileLs", { enumerable: true, get: function () { return ls_1.handleFileLs; } });
|
|
6
|
+
var stat_1 = require("./stat");
|
|
7
|
+
Object.defineProperty(exports, "handleFileStat", { enumerable: true, get: function () { return stat_1.handleFileStat; } });
|
|
8
|
+
var cp_1 = require("./cp");
|
|
9
|
+
Object.defineProperty(exports, "handleFileCp", { enumerable: true, get: function () { return cp_1.handleFileCp; } });
|
|
10
|
+
var rm_1 = require("./rm");
|
|
11
|
+
Object.defineProperty(exports, "handleFileRm", { enumerable: true, get: function () { return rm_1.handleFileRm; } });
|
|
12
|
+
var sign_1 = require("./sign");
|
|
13
|
+
Object.defineProperty(exports, "handleFileSign", { enumerable: true, get: function () { return sign_1.handleFileSign; } });
|
|
@@ -0,0 +1,110 @@
|
|
|
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.handleFileLs = handleFileLs;
|
|
37
|
+
const api = __importStar(require("../../../api/index"));
|
|
38
|
+
const output_1 = require("../../../utils/output");
|
|
39
|
+
const render_1 = require("../../../utils/render");
|
|
40
|
+
const error_1 = require("../../../utils/error");
|
|
41
|
+
const shared_1 = require("../../../cli/commands/shared");
|
|
42
|
+
const index_1 = require("../../../api/file/index");
|
|
43
|
+
/**
|
|
44
|
+
* 把位置参数 `query` 路由到 `--path` 或 `--name`:
|
|
45
|
+
* - `looksLikePath(query)` → path 精确匹配
|
|
46
|
+
* - 否则 → file_name 精确匹配
|
|
47
|
+
*
|
|
48
|
+
* 显式 `--path` / `--name` 传入时跳过 query 自动识别。
|
|
49
|
+
* 同时传 query 又传 --path/--name 视为歧义,直接报错。
|
|
50
|
+
*/
|
|
51
|
+
function resolveQueryRouting(opts) {
|
|
52
|
+
const explicit = {
|
|
53
|
+
path: opts.path,
|
|
54
|
+
name: opts.name,
|
|
55
|
+
};
|
|
56
|
+
if (!opts.query)
|
|
57
|
+
return explicit;
|
|
58
|
+
if (explicit.path || explicit.name) {
|
|
59
|
+
throw new error_1.AppError("ARGS_INVALID", "Do not mix positional <query> with --path / --name; pick one.");
|
|
60
|
+
}
|
|
61
|
+
if ((0, index_1.looksLikePath)(opts.query)) {
|
|
62
|
+
// 补齐前导 `/`,对齐 ls sidecar 比较逻辑(info.path 永远带前导 `/`)
|
|
63
|
+
return { path: (0, index_1.toAbsolutePath)(opts.query) };
|
|
64
|
+
}
|
|
65
|
+
return { name: opts.query };
|
|
66
|
+
}
|
|
67
|
+
async function handleFileLs(opts) {
|
|
68
|
+
const appId = (0, shared_1.resolveAppId)(opts);
|
|
69
|
+
// commander 已经把 --limit 解析为 number;保留 ?? undefined 兼容老调用
|
|
70
|
+
const limit = opts.limit;
|
|
71
|
+
const sizeGt = opts.sizeGt ? (0, render_1.parseSize)(opts.sizeGt) : undefined;
|
|
72
|
+
const sizeLt = opts.sizeLt ? (0, render_1.parseSize)(opts.sizeLt) : undefined;
|
|
73
|
+
const { path, name } = resolveQueryRouting(opts);
|
|
74
|
+
const result = await api.file.listFiles({
|
|
75
|
+
appId,
|
|
76
|
+
limit,
|
|
77
|
+
cursor: opts.cursor,
|
|
78
|
+
all: opts.all,
|
|
79
|
+
path,
|
|
80
|
+
name,
|
|
81
|
+
type: opts.type,
|
|
82
|
+
sizeGt,
|
|
83
|
+
sizeLt,
|
|
84
|
+
uploadedSince: opts.uploadedSince,
|
|
85
|
+
});
|
|
86
|
+
if ((0, output_1.isJsonMode)()) {
|
|
87
|
+
(0, output_1.emitPaged)(result.items, result.next_cursor, result.has_more);
|
|
88
|
+
return;
|
|
89
|
+
}
|
|
90
|
+
if (result.items.length === 0) {
|
|
91
|
+
(0, output_1.emit)("No files found.");
|
|
92
|
+
return;
|
|
93
|
+
}
|
|
94
|
+
const tty = (0, render_1.isStdoutTty)();
|
|
95
|
+
const headers = ["file_name", "path", "size", "type", "uploaded_at"];
|
|
96
|
+
const rows = result.items.map((info) => [
|
|
97
|
+
info.file_name || "—",
|
|
98
|
+
info.path,
|
|
99
|
+
tty ? (0, render_1.formatSize)(info.size_bytes) : String(info.size_bytes),
|
|
100
|
+
info.type,
|
|
101
|
+
(0, render_1.formatTime)(info.uploaded_at, tty),
|
|
102
|
+
]);
|
|
103
|
+
const table = tty
|
|
104
|
+
? (0, render_1.renderAlignedTable)(headers, rows)
|
|
105
|
+
: (0, render_1.renderTsv)(headers, rows);
|
|
106
|
+
const hint = result.has_more && result.next_cursor
|
|
107
|
+
? `\n— ${String(result.items.length)} results. Next: --cursor ${result.next_cursor}`
|
|
108
|
+
: "";
|
|
109
|
+
(0, output_1.emit)(table + hint);
|
|
110
|
+
}
|