@qoder-ai/qmind-cli 2.0.0 → 3.1.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/CHANGELOG.md +37 -1
- package/CHANGELOG.zh-CN.md +80 -0
- package/README.md +43 -63
- package/README.zh-CN.md +90 -0
- package/dist/bot-gateway-Hzjl50zu.js +373 -0
- package/dist/cards-DsMbd-g1.js +132 -0
- package/dist/imports-B01ERZgD.js +126 -0
- package/dist/management-BIYQa9Al.js +313 -0
- package/dist/notes-BvucEfdi.js +128 -0
- package/dist/qmind.js +861 -1377
- package/dist/shared-BkQJzIZF.js +1155 -0
- package/dist/source-upload-DBm1Hyki.js +17 -0
- package/dist/transfers-F1ZLLBdU.js +237 -0
- package/package.json +7 -13
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { H as sourceUploadCapabilitiesSchema, m as requiredString, n as call } from "./shared-BkQJzIZF.js";
|
|
2
|
+
//#region ../sdk/src/operations/source-upload.ts
|
|
3
|
+
/** Notebook-scoped edit preflight, not the deployment-level capabilities lookup. */
|
|
4
|
+
async function preflightSourceUpload(context, notebookId, options) {
|
|
5
|
+
const notebook = requiredString(notebookId, "notebookId");
|
|
6
|
+
return call(context, {
|
|
7
|
+
callOptions: options,
|
|
8
|
+
capability: "transfers",
|
|
9
|
+
idempotent: true,
|
|
10
|
+
method: "GET",
|
|
11
|
+
operation: "sources.uploadPreflight",
|
|
12
|
+
path: context.profile.apiPath("notebooks", notebook, "sources", "upload", "capabilities"),
|
|
13
|
+
schema: sourceUploadCapabilitiesSchema
|
|
14
|
+
});
|
|
15
|
+
}
|
|
16
|
+
//#endregion
|
|
17
|
+
export { preflightSourceUpload as t };
|
|
@@ -0,0 +1,237 @@
|
|
|
1
|
+
import { $ as QMIND_SOURCE_UPLOAD_MAX_BYTES, B as sourceSchema, E as imageUploadTicketSchema, Q as QMIND_MULTIPART_UPLOAD_MAX_BYTES, U as sourceUploadTicketSchema, X as voidResponseSchema, a as invalidArgument, l as optionalString, m as requiredString, nt as executeUnary, p as queryPath, r as compactUndefined, rt as QMindError, u as optionalTrimmedString } from "./shared-BkQJzIZF.js";
|
|
2
|
+
import { t as preflightSourceUpload } from "./source-upload-DBm1Hyki.js";
|
|
3
|
+
//#region ../sdk/src/operations/transfers.ts
|
|
4
|
+
function isRecord(value) {
|
|
5
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
6
|
+
}
|
|
7
|
+
function binarySource(value) {
|
|
8
|
+
if (!isRecord(value)) throw invalidArgument("source must be a QMindBinarySource", "source");
|
|
9
|
+
const filename = requiredString(value.filename, "source.filename");
|
|
10
|
+
const mimeType = requiredString(value.mimeType, "source.mimeType");
|
|
11
|
+
const size = value.size;
|
|
12
|
+
if (typeof size !== "number" || !Number.isSafeInteger(size) || size <= 0) throw invalidArgument("source.size must be a positive safe integer", "source.size");
|
|
13
|
+
if (typeof value.open !== "function") throw invalidArgument("source.open must be a function", "source.open");
|
|
14
|
+
const source = value;
|
|
15
|
+
return {
|
|
16
|
+
filename,
|
|
17
|
+
mimeType,
|
|
18
|
+
open: () => source.open(),
|
|
19
|
+
size
|
|
20
|
+
};
|
|
21
|
+
}
|
|
22
|
+
function transferOptions(options) {
|
|
23
|
+
if (options === void 0) return {};
|
|
24
|
+
if (!isRecord(options)) throw invalidArgument("options must be an object", "options");
|
|
25
|
+
if (options.onProgress !== void 0 && typeof options.onProgress !== "function") throw invalidArgument("onProgress must be a function", "onProgress");
|
|
26
|
+
return options;
|
|
27
|
+
}
|
|
28
|
+
function uploadMetadata(value) {
|
|
29
|
+
if (value === void 0) return {};
|
|
30
|
+
if (!isRecord(value)) throw invalidArgument("metadata must be an object", "metadata");
|
|
31
|
+
return {
|
|
32
|
+
parentSourceId: optionalString(value.parentSourceId, "parentSourceId"),
|
|
33
|
+
path: optionalString(value.path, "path"),
|
|
34
|
+
sha256: optionalTrimmedString(value.sha256, "sha256"),
|
|
35
|
+
sourceType: optionalTrimmedString(value.sourceType, "sourceType"),
|
|
36
|
+
title: optionalTrimmedString(value.title, "title")
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
function progressReporter(context, operation, strategy, totalBytes, options) {
|
|
40
|
+
let lastUploadedBytes = 0;
|
|
41
|
+
const notify = (phase, uploadedBytes) => {
|
|
42
|
+
if (!Number.isSafeInteger(uploadedBytes) || uploadedBytes < lastUploadedBytes) throw new QMindError("HOST_POLICY_ERROR", "upload progress must be a monotonic integer", {
|
|
43
|
+
access: context.profile.access,
|
|
44
|
+
details: {
|
|
45
|
+
lastUploadedBytes,
|
|
46
|
+
totalBytes,
|
|
47
|
+
uploadedBytes
|
|
48
|
+
},
|
|
49
|
+
operation
|
|
50
|
+
});
|
|
51
|
+
if (uploadedBytes > totalBytes) throw new QMindError("HOST_POLICY_ERROR", "upload progress exceeds source size", {
|
|
52
|
+
access: context.profile.access,
|
|
53
|
+
details: {
|
|
54
|
+
totalBytes,
|
|
55
|
+
uploadedBytes
|
|
56
|
+
},
|
|
57
|
+
operation
|
|
58
|
+
});
|
|
59
|
+
lastUploadedBytes = uploadedBytes;
|
|
60
|
+
const progress = {
|
|
61
|
+
phase,
|
|
62
|
+
strategy,
|
|
63
|
+
totalBytes,
|
|
64
|
+
uploadedBytes
|
|
65
|
+
};
|
|
66
|
+
options.onProgress?.(progress);
|
|
67
|
+
};
|
|
68
|
+
return {
|
|
69
|
+
complete: () => notify("uploading", totalBytes),
|
|
70
|
+
registering: () => notify("registering", totalBytes),
|
|
71
|
+
start: () => notify("uploading", 0),
|
|
72
|
+
update: (uploadedBytes) => notify("uploading", uploadedBytes)
|
|
73
|
+
};
|
|
74
|
+
}
|
|
75
|
+
function requestOptions(options) {
|
|
76
|
+
return {
|
|
77
|
+
...options.signal === void 0 ? {} : { signal: options.signal },
|
|
78
|
+
...options.timeoutMs === void 0 ? {} : { timeoutMs: options.timeoutMs }
|
|
79
|
+
};
|
|
80
|
+
}
|
|
81
|
+
function transferCall(context, operation, request, schema) {
|
|
82
|
+
return executeUnary({
|
|
83
|
+
capability: operation.startsWith("images.") ? "images.presignUpload" : "transfers",
|
|
84
|
+
operation,
|
|
85
|
+
profile: context.profile,
|
|
86
|
+
request,
|
|
87
|
+
schema,
|
|
88
|
+
transport: context.transport
|
|
89
|
+
});
|
|
90
|
+
}
|
|
91
|
+
function hostRequest(path, method, idempotent, options, body) {
|
|
92
|
+
const authorization = options.authorization?.trim();
|
|
93
|
+
return {
|
|
94
|
+
body,
|
|
95
|
+
destination: {
|
|
96
|
+
kind: "host",
|
|
97
|
+
path
|
|
98
|
+
},
|
|
99
|
+
...authorization === void 0 || authorization.length === 0 ? {} : { headers: { authorization: `Bearer ${authorization}` } },
|
|
100
|
+
idempotent,
|
|
101
|
+
method,
|
|
102
|
+
...requestOptions(options)
|
|
103
|
+
};
|
|
104
|
+
}
|
|
105
|
+
function signedUploadRequest(ticket, source, progress, options) {
|
|
106
|
+
return {
|
|
107
|
+
body: {
|
|
108
|
+
kind: "binary",
|
|
109
|
+
onProgress: progress.update,
|
|
110
|
+
source
|
|
111
|
+
},
|
|
112
|
+
destination: {
|
|
113
|
+
kind: "signed",
|
|
114
|
+
url: ticket.uploadUrl
|
|
115
|
+
},
|
|
116
|
+
headers: ticket.headers,
|
|
117
|
+
idempotent: true,
|
|
118
|
+
method: "PUT",
|
|
119
|
+
...requestOptions(options)
|
|
120
|
+
};
|
|
121
|
+
}
|
|
122
|
+
function selectSourceUploadStrategy(source, capabilities) {
|
|
123
|
+
const methods = capabilities.uploadMethods.filter(({ available, method, maxSizeBytes }) => available && maxSizeBytes > 0 && (method === "multipart" || method === "presign"));
|
|
124
|
+
if (methods.length === 0) throw new QMindError("UNSUPPORTED_OPERATION", "source upload is unavailable", {
|
|
125
|
+
details: { reason: "upload-unavailable" },
|
|
126
|
+
operation: "sources.uploadPreflight"
|
|
127
|
+
});
|
|
128
|
+
const filename = source.filename.toLowerCase();
|
|
129
|
+
const matching = methods.filter(({ supportedExtensions }) => supportedExtensions.some((extension) => filename.endsWith(extension.toLowerCase())));
|
|
130
|
+
if (matching.length === 0) throw new QMindError("INVALID_ARGUMENT", "file extension is not supported for upload", {
|
|
131
|
+
details: { reason: "unsupported-extension" },
|
|
132
|
+
operation: "sources.uploadPreflight"
|
|
133
|
+
});
|
|
134
|
+
const limit = (method) => Math.min(method.maxSizeBytes, method.method === "multipart" ? QMIND_MULTIPART_UPLOAD_MAX_BYTES : QMIND_SOURCE_UPLOAD_MAX_BYTES);
|
|
135
|
+
if (matching.find((method) => method.method === "multipart" && source.size <= limit(method))) return "multipart";
|
|
136
|
+
if (matching.some((method) => method.method === "presign" && source.size <= limit(method))) return "presigned";
|
|
137
|
+
throw new QMindError("FILE_TOO_LARGE", "file exceeds the available upload size limit", {
|
|
138
|
+
details: {
|
|
139
|
+
limitBytes: Math.max(...matching.map(limit)),
|
|
140
|
+
size: source.size
|
|
141
|
+
},
|
|
142
|
+
operation: "sources.uploadPreflight"
|
|
143
|
+
});
|
|
144
|
+
}
|
|
145
|
+
function createTransferOperations(context) {
|
|
146
|
+
return {
|
|
147
|
+
async uploadImage(notebookId, sourceId, rawSource, rawOptions) {
|
|
148
|
+
const notebook = requiredString(notebookId, "notebookId");
|
|
149
|
+
const sourceIdValue = requiredString(sourceId, "sourceId");
|
|
150
|
+
const source = binarySource(rawSource);
|
|
151
|
+
const options = transferOptions(rawOptions);
|
|
152
|
+
if (source.size > 52428800) throw new QMindError("FILE_TOO_LARGE", "image exceeds the 50 MiB upload limit", {
|
|
153
|
+
access: context.profile.access,
|
|
154
|
+
details: {
|
|
155
|
+
limitBytes: QMIND_MULTIPART_UPLOAD_MAX_BYTES,
|
|
156
|
+
size: source.size
|
|
157
|
+
},
|
|
158
|
+
operation: "images.upload"
|
|
159
|
+
});
|
|
160
|
+
const ticket = await transferCall(context, "images.presignUpload", hostRequest(context.profile.apiPath("notebooks", notebook, "sources", sourceIdValue, "images", "presign"), "POST", false, options, {
|
|
161
|
+
kind: "json",
|
|
162
|
+
value: {
|
|
163
|
+
filename: source.filename,
|
|
164
|
+
mimeType: source.mimeType,
|
|
165
|
+
size: source.size
|
|
166
|
+
}
|
|
167
|
+
}), imageUploadTicketSchema);
|
|
168
|
+
const progress = progressReporter(context, "images.upload", "presigned", source.size, options);
|
|
169
|
+
progress.start();
|
|
170
|
+
await transferCall(context, "images.upload", signedUploadRequest(ticket, source, progress, options), voidResponseSchema);
|
|
171
|
+
progress.complete();
|
|
172
|
+
return ticket;
|
|
173
|
+
},
|
|
174
|
+
async uploadSource(notebookId, rawSource, rawMetadata, rawOptions) {
|
|
175
|
+
const notebook = requiredString(notebookId, "notebookId");
|
|
176
|
+
const source = binarySource(rawSource);
|
|
177
|
+
const metadata = uploadMetadata(rawMetadata);
|
|
178
|
+
const options = transferOptions(rawOptions);
|
|
179
|
+
if (source.size > 524288e3) throw new QMindError("FILE_TOO_LARGE", "file exceeds the 500 MiB upload limit", {
|
|
180
|
+
access: context.profile.access,
|
|
181
|
+
details: {
|
|
182
|
+
limitBytes: QMIND_SOURCE_UPLOAD_MAX_BYTES,
|
|
183
|
+
size: source.size
|
|
184
|
+
},
|
|
185
|
+
operation: "transfers.source.multipart"
|
|
186
|
+
});
|
|
187
|
+
if (selectSourceUploadStrategy(source, await preflightSourceUpload(context, notebook, options)) === "multipart") {
|
|
188
|
+
const progress = progressReporter(context, "transfers.source.multipart", "multipart", source.size, options);
|
|
189
|
+
progress.start();
|
|
190
|
+
const fields = compactUndefined({
|
|
191
|
+
parent_source_id: metadata.parentSourceId,
|
|
192
|
+
path: metadata.path,
|
|
193
|
+
retain_original_file: "true",
|
|
194
|
+
source_type: metadata.sourceType,
|
|
195
|
+
title: metadata.title
|
|
196
|
+
});
|
|
197
|
+
const result = await transferCall(context, "transfers.source.multipart", hostRequest(queryPath(context.profile.apiPath("notebooks", notebook, "sources", "upload"), [["client", context.client]]), "POST", false, options, {
|
|
198
|
+
file: source,
|
|
199
|
+
fields,
|
|
200
|
+
kind: "multipart",
|
|
201
|
+
onProgress: progress.update
|
|
202
|
+
}), sourceSchema);
|
|
203
|
+
progress.complete();
|
|
204
|
+
return result;
|
|
205
|
+
}
|
|
206
|
+
const ticket = await transferCall(context, "transfers.source.presign", hostRequest(context.profile.apiPath("notebooks", notebook, "sources", "presign"), "POST", false, options, {
|
|
207
|
+
kind: "json",
|
|
208
|
+
value: {
|
|
209
|
+
filename: source.filename,
|
|
210
|
+
mimeType: source.mimeType,
|
|
211
|
+
size: source.size
|
|
212
|
+
}
|
|
213
|
+
}), sourceUploadTicketSchema);
|
|
214
|
+
const progress = progressReporter(context, "transfers.source.upload", "presigned", source.size, options);
|
|
215
|
+
progress.start();
|
|
216
|
+
await transferCall(context, "transfers.source.upload", signedUploadRequest(ticket, source, progress, options), voidResponseSchema);
|
|
217
|
+
progress.complete();
|
|
218
|
+
progress.registering();
|
|
219
|
+
return transferCall(context, "transfers.source.register", hostRequest(context.profile.apiPath("notebooks", notebook, "sources", "register"), "POST", false, options, {
|
|
220
|
+
kind: "json",
|
|
221
|
+
value: compactUndefined({
|
|
222
|
+
filename: source.filename,
|
|
223
|
+
mimeType: source.mimeType,
|
|
224
|
+
parentSourceId: metadata.parentSourceId,
|
|
225
|
+
path: metadata.path,
|
|
226
|
+
sha256: metadata.sha256,
|
|
227
|
+
size: source.size,
|
|
228
|
+
sourceType: metadata.sourceType,
|
|
229
|
+
title: metadata.title,
|
|
230
|
+
uri: ticket.uri
|
|
231
|
+
})
|
|
232
|
+
}), sourceSchema);
|
|
233
|
+
}
|
|
234
|
+
};
|
|
235
|
+
}
|
|
236
|
+
//#endregion
|
|
237
|
+
export { createTransferOperations };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@qoder-ai/qmind-cli",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "3.1.0",
|
|
4
4
|
"description": "QMind command line interface",
|
|
5
5
|
"bin": {
|
|
6
6
|
"qmind": "bin/cli.js"
|
|
@@ -11,7 +11,9 @@
|
|
|
11
11
|
"bin",
|
|
12
12
|
"dist",
|
|
13
13
|
"CHANGELOG.md",
|
|
14
|
-
"
|
|
14
|
+
"CHANGELOG.zh-CN.md",
|
|
15
|
+
"README.md",
|
|
16
|
+
"README.zh-CN.md"
|
|
15
17
|
],
|
|
16
18
|
"engines": {
|
|
17
19
|
"node": ">=20.18.1"
|
|
@@ -31,20 +33,12 @@
|
|
|
31
33
|
"undici": "7.29.0",
|
|
32
34
|
"zod": "4.3.6"
|
|
33
35
|
},
|
|
34
|
-
"optionalDependencies": {
|
|
35
|
-
"@napi-rs/keyring": "1.3.0"
|
|
36
|
-
},
|
|
37
36
|
"devDependencies": {
|
|
38
37
|
"@types/node": "24.13.3",
|
|
39
38
|
"@types/picomatch": "4.0.3",
|
|
40
39
|
"@types/proper-lockfile": "4.1.4",
|
|
41
|
-
"@ali/qmind-node-host": "^2.
|
|
42
|
-
"@ali/qmind-sdk": "^0.
|
|
40
|
+
"@ali/qmind-node-host": "^2.2.8",
|
|
41
|
+
"@ali/qmind-sdk": "^0.14.0"
|
|
43
42
|
},
|
|
44
|
-
"license": "UNLICENSED"
|
|
45
|
-
"scripts": {
|
|
46
|
-
"build": "tsdown",
|
|
47
|
-
"check:package": "publint",
|
|
48
|
-
"typecheck": "tsc --noEmit -p tsconfig.json"
|
|
49
|
-
}
|
|
43
|
+
"license": "UNLICENSED"
|
|
50
44
|
}
|