@qodercn-ai/qmind-cli 3.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.
@@ -566,6 +566,7 @@ const sourceListSchema = z.object({
566
566
  });
567
567
  const sourceTreeNodeSchema = z.lazy(() => z.object({
568
568
  files: z.array(sourceTreeNodeSchema).default([]),
569
+ hasChildren: z.boolean().optional(),
569
570
  id: z.string().min(1),
570
571
  name: z.string().default(""),
571
572
  path: z.string().default(""),
@@ -666,11 +667,12 @@ const sourcePreviewSchema = z.preprocess((value) => isRecord(value) ? {
666
667
  url: httpsUrlSchema
667
668
  }));
668
669
  /** proto3 JSON serializes the token timeout and version as int64, so both arrive either way. */
670
+ const officeTokenIntegerSchema = z.union([z.number(), z.string().regex(/^\d+$/).transform(Number)]).pipe(z.number().int().nonnegative().max(Number.MAX_SAFE_INTEGER));
669
671
  const webOfficeTokenFields = {
670
672
  accessToken: z.string().min(1),
671
673
  accessTokenExpiresAt: timestampSchema.optional(),
672
- tokenTimeoutMs: wireInt64Schema,
673
- tokenVersion: wireInt64Schema
674
+ tokenTimeoutMs: officeTokenIntegerSchema.refine((value) => value > 0),
675
+ tokenVersion: officeTokenIntegerSchema
674
676
  };
675
677
  function webOfficeTokenAliases(value) {
676
678
  return isRecord(value) ? {
@@ -693,11 +695,11 @@ const sourceWebOfficeSessionSchema = z.preprocess((value) => {
693
695
  } : aliased;
694
696
  }, z.object({
695
697
  ...webOfficeTokenFields,
696
- readOnly: z.boolean().default(true),
698
+ readOnly: z.literal(true),
697
699
  reused: z.boolean().default(false),
698
700
  sessionId: z.string().min(1),
699
- sourceFormat: z.string().default(""),
700
- sourceId: z.string().default(""),
701
+ sourceFormat: z.string().min(1).transform((value) => value.trim().toLowerCase().replace(/^\./, "")).pipe(z.string().min(1)),
702
+ sourceId: z.string().min(1),
701
703
  webOfficeUrl: httpsUrlSchema
702
704
  }));
703
705
  const sourceWebOfficeTokenSchema = z.preprocess(webOfficeTokenAliases, z.object(webOfficeTokenFields));
@@ -1038,6 +1040,12 @@ function optionalString(value, field) {
1038
1040
  if (typeof value !== "string") throw invalidArgument(`${field} must be a string`, field);
1039
1041
  return value;
1040
1042
  }
1043
+ /** Validate body text without trimming meaningful Markdown indentation or trailing newlines. */
1044
+ function requiredText(value, field) {
1045
+ const text = optionalString(value, field);
1046
+ if (text === void 0 || text.trim().length === 0) throw invalidArgument(`${field} must be a non-empty string`, field);
1047
+ return text;
1048
+ }
1041
1049
  function optionalTrimmedString(value, field) {
1042
1050
  const string = optionalString(value, field)?.trim();
1043
1051
  return string === "" ? void 0 : string;
@@ -1144,4 +1152,4 @@ function call(context, options) {
1144
1152
  });
1145
1153
  }
1146
1154
  //#endregion
1147
- export { executeStream as $, retrieveResultSchema as A, sourceTreeSchema as B, chatSessionListSchema as C, importProvidersSchema as D, imageUrlsSchema as E, sourceImportResultsSchema as F, streamWireEventSchema as G, sourceUploadTicketSchema as H, sourceListSchema as I, voidResponseSchema as J, taskRunListSchema as K, sourcePreviewSchema as L, sourceActionAccessSchema as M, sourceChildrenCountsSchema as N, notebookListSchema as O, sourceContentSchema as P, assertCapability as Q, sourceRawContentSchema as R, chatMessageListSchema as S, imageUploadTicketSchema as T, sourceWebOfficeSessionSchema as U, sourceUploadCapabilitiesSchema as V, sourceWebOfficeTokenSchema as W, QMIND_MULTIPART_UPLOAD_MAX_BYTES as X, wikiBranchListSchema as Y, QMIND_SOURCE_UPLOAD_MAX_BYTES as Z, aliDingImportRunSchema as _, invalidArgument as a, cardListSchema as b, optionalRecord as c, pagination as d, executeUnary as et, positiveInteger as f, aliDingImportRunItemListSchema as g, stringList as h, httpUrl as i, sharedNotebookListSchema as j, notebookSchema as k, optionalString as l, requiredString as m, call as n, nonNegativeInteger as o, queryPath as p, taskRunSchema as q, compactUndefined as r, optionalBoolean as s, assertNonEmptyPatch as t, QMindError as tt, optionalTrimmedString as u, batchMutationResultSchema as v, chatSessionSchema as w, cardSchema as x, cancelQuestionResultSchema as y, sourceSchema as z };
1155
+ export { QMIND_SOURCE_UPLOAD_MAX_BYTES as $, notebookSchema as A, sourceSchema as B, chatMessageListSchema as C, imageUrlsSchema as D, imageUploadTicketSchema as E, sourceContentSchema as F, sourceWebOfficeTokenSchema as G, sourceUploadCapabilitiesSchema as H, sourceImportResultsSchema as I, taskRunSchema as J, streamWireEventSchema as K, sourceListSchema as L, sharedNotebookListSchema as M, sourceActionAccessSchema as N, importProvidersSchema as O, sourceChildrenCountsSchema as P, QMIND_MULTIPART_UPLOAD_MAX_BYTES as Q, sourcePreviewSchema as R, cardSchema as S, chatSessionSchema as T, sourceUploadTicketSchema as U, sourceTreeSchema as V, sourceWebOfficeSessionSchema as W, voidResponseSchema as X, timestampSchema as Y, wikiBranchListSchema as Z, aliDingImportRunItemListSchema as _, invalidArgument as a, cancelQuestionResultSchema as b, optionalRecord as c, pagination as d, assertCapability as et, positiveInteger as f, stringList as g, requiredText as h, httpUrl as i, retrieveResultSchema as j, notebookListSchema as k, optionalString as l, requiredString as m, call as n, executeUnary as nt, nonNegativeInteger as o, queryPath as p, taskRunListSchema as q, compactUndefined as r, QMindError as rt, optionalBoolean as s, assertNonEmptyPatch as t, executeStream as tt, optionalTrimmedString as u, aliDingImportRunSchema as v, chatSessionListSchema as w, cardListSchema as x, batchMutationResultSchema as y, sourceRawContentSchema as z };
@@ -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": "@qodercn-ai/qmind-cli",
3
- "version": "3.0.0",
3
+ "version": "3.1.0",
4
4
  "description": "QMind command line interface",
5
5
  "bin": {
6
6
  "qmind-cn": "bin/cli.js"
@@ -37,8 +37,8 @@
37
37
  "@types/node": "24.13.3",
38
38
  "@types/picomatch": "4.0.3",
39
39
  "@types/proper-lockfile": "4.1.4",
40
- "@ali/qmind-node-host": "^2.2.7",
41
- "@ali/qmind-sdk": "^0.13.0"
40
+ "@ali/qmind-node-host": "^2.2.8",
41
+ "@ali/qmind-sdk": "^0.14.0"
42
42
  },
43
43
  "license": "UNLICENSED"
44
44
  }
@@ -1,75 +0,0 @@
1
- import { b as cardListSchema, d as pagination, h as stringList, m as requiredString, n as call, p as queryPath, u as optionalTrimmedString, v as batchMutationResultSchema, x as cardSchema } from "./shared-DwycozMA.js";
2
- //#region ../sdk/src/operations/cards.ts
3
- function createCardOperations(context) {
4
- const listCards = async (notebookId, input = {}, options, featured = false) => {
5
- const notebook = requiredString(notebookId, "notebookId");
6
- const normalizedPage = pagination({
7
- page: input.page ?? 1,
8
- pageSize: input.pageSize ?? 20
9
- });
10
- const category = optionalTrimmedString(input.category, "category");
11
- const query = optionalTrimmedString(input.query, "query");
12
- const root = context.profile.apiPath(featured ? "featured-notebooks" : "notebooks", notebook, "cards");
13
- const path = queryPath(root, [
14
- ["category", category],
15
- ["query", query],
16
- ["page", normalizedPage.page],
17
- ["page_size", normalizedPage.pageSize]
18
- ]);
19
- return call(context, {
20
- callOptions: options,
21
- capability: featured ? "featuredNotebooks.read" : "cards.read",
22
- idempotent: true,
23
- method: "GET",
24
- operation: featured ? "featuredCards.list" : "cards.list",
25
- path,
26
- schema: cardListSchema
27
- });
28
- };
29
- return {
30
- async batchDeleteCards(notebookId, cardIds, options) {
31
- const notebook = requiredString(notebookId, "notebookId");
32
- const ids = stringList(cardIds, "cardIds", { min: 1 });
33
- return call(context, {
34
- body: { cardIds: ids },
35
- callOptions: options,
36
- capability: "cards.write",
37
- idempotent: false,
38
- method: "POST",
39
- operation: "cards.batchDelete",
40
- path: context.profile.apiPath("notebooks", notebook, "cards", "batch-delete"),
41
- schema: batchMutationResultSchema
42
- });
43
- },
44
- async deleteCard(notebookId, cardId, options) {
45
- const notebook = requiredString(notebookId, "notebookId");
46
- const card = requiredString(cardId, "cardId");
47
- return call(context, {
48
- callOptions: options,
49
- capability: "cards.write",
50
- idempotent: false,
51
- method: "DELETE",
52
- operation: "cards.delete",
53
- path: context.profile.apiPath("notebooks", notebook, "cards", card),
54
- schema: batchMutationResultSchema
55
- });
56
- },
57
- async getCard(notebookId, cardId, options) {
58
- const notebook = requiredString(notebookId, "notebookId");
59
- const card = requiredString(cardId, "cardId");
60
- return call(context, {
61
- callOptions: options,
62
- capability: "cards.read",
63
- idempotent: true,
64
- method: "GET",
65
- operation: "cards.get",
66
- path: context.profile.apiPath("notebooks", notebook, "cards", card),
67
- schema: cardSchema
68
- });
69
- },
70
- listCards: (notebookId, input, options) => listCards(notebookId, input, options),
71
- listFeaturedCards: (notebookId, input, options) => listCards(notebookId, input, options, true)
72
- };
73
- }
74
- //#endregion
75
- export { createCardOperations };