@qodercn-ai/qmind-cli 3.0.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/dist/qmind.js ADDED
@@ -0,0 +1,4696 @@
1
+ import { $ as executeStream, A as retrieveResultSchema, B as sourceTreeSchema, C as chatSessionListSchema, E as imageUrlsSchema, F as sourceImportResultsSchema, G as streamWireEventSchema, H as sourceUploadTicketSchema, I as sourceListSchema, J as voidResponseSchema, K as taskRunListSchema, L as sourcePreviewSchema, M as sourceActionAccessSchema, N as sourceChildrenCountsSchema, O as notebookListSchema, P as sourceContentSchema, Q as assertCapability, R as sourceRawContentSchema, S as chatMessageListSchema, T as imageUploadTicketSchema, U as sourceWebOfficeSessionSchema, V as sourceUploadCapabilitiesSchema, W as sourceWebOfficeTokenSchema, X as QMIND_MULTIPART_UPLOAD_MAX_BYTES, Y as wikiBranchListSchema, Z as QMIND_SOURCE_UPLOAD_MAX_BYTES, a as invalidArgument$1, c as optionalRecord, d as pagination$1, et as executeUnary, f as positiveInteger$1, h as stringList, i as httpUrl, j as sharedNotebookListSchema, k as notebookSchema, l as optionalString$1, m as requiredString$2, n as call, o as nonNegativeInteger$1, p as queryPath, q as taskRunSchema, r as compactUndefined, s as optionalBoolean, t as assertNonEmptyPatch, tt as QMindError, u as optionalTrimmedString, v as batchMutationResultSchema, w as chatSessionSchema, y as cancelQuestionResultSchema, z as sourceSchema } from "./shared-DwycozMA.js";
2
+ import { createInterface } from "node:readline/promises";
3
+ import { basename, dirname, extname, join, relative, resolve, sep } from "node:path";
4
+ import { z } from "zod";
5
+ import { createHash, randomBytes } from "node:crypto";
6
+ import { chmod, copyFile, link, lstat, mkdir, readFile, readdir, rename, rm, stat, writeFile } from "node:fs/promises";
7
+ import { homedir } from "node:os";
8
+ import lockfile from "proper-lockfile";
9
+ import open from "open";
10
+ import { createReadStream, createWriteStream } from "node:fs";
11
+ import { Readable } from "node:stream";
12
+ import { pipeline } from "node:stream/promises";
13
+ import { createParser } from "eventsource-parser";
14
+ import { Agent, EnvHttpProxyAgent, request } from "undici";
15
+ import { Command, CommanderError, Option } from "commander";
16
+ import pLimit from "p-limit";
17
+ import picomatch from "picomatch";
18
+ import { decodeHTML } from "entities";
19
+ //#region ../sdk/src/operations/agents.ts
20
+ const workflowPrompt = "Follow the system prompt strictly: use the provided tools to discover, process and prepare the result, then emit the final report. Do NOT greet, do NOT ask for clarification — act immediately.";
21
+ function isRecord$3(value) {
22
+ return typeof value === "object" && value !== null && !Array.isArray(value);
23
+ }
24
+ function finiteNumber(value, field) {
25
+ if (value === void 0) return void 0;
26
+ if (typeof value !== "number" || !Number.isFinite(value)) throw invalidArgument$1(`${field} must be a finite number`, field);
27
+ return value;
28
+ }
29
+ function queryHints(value) {
30
+ const input = optionalRecord(value, "queryHints");
31
+ if (input === void 0) return void 0;
32
+ return compactUndefined({
33
+ intent: optionalTrimmedString(input.intent, "queryHints.intent"),
34
+ keywords: input.keywords === void 0 ? void 0 : stringList(input.keywords, "queryHints.keywords"),
35
+ language: optionalTrimmedString(input.language, "queryHints.language"),
36
+ queryVariants: input.queryVariants === void 0 ? void 0 : stringList(input.queryVariants, "queryHints.queryVariants"),
37
+ rewrittenQuery: optionalTrimmedString(input.rewrittenQuery, "queryHints.rewrittenQuery")
38
+ });
39
+ }
40
+ function businessContext(value) {
41
+ const input = optionalRecord(value, "billingContext.business");
42
+ if (input === void 0) return void 0;
43
+ const beginAt = input.beginAt;
44
+ if (beginAt !== void 0 && (typeof beginAt !== "number" || !Number.isSafeInteger(beginAt))) throw invalidArgument$1("billingContext.business.beginAt must be a safe integer", "beginAt");
45
+ return compactUndefined({
46
+ beginAt,
47
+ id: optionalTrimmedString(input.id, "billingContext.business.id"),
48
+ name: optionalTrimmedString(input.name, "billingContext.business.name"),
49
+ product: optionalTrimmedString(input.product, "billingContext.business.product"),
50
+ stage: optionalTrimmedString(input.stage, "billingContext.business.stage"),
51
+ type: optionalTrimmedString(input.type, "billingContext.business.type"),
52
+ version: optionalTrimmedString(input.version, "billingContext.business.version")
53
+ });
54
+ }
55
+ function billingContext(value) {
56
+ const input = optionalRecord(value, "billingContext");
57
+ if (input === void 0) return void 0;
58
+ return compactUndefined({
59
+ business: businessContext(input.business),
60
+ modelKey: optionalTrimmedString(input.modelKey, "billingContext.modelKey"),
61
+ sessionId: optionalTrimmedString(input.sessionId, "billingContext.sessionId")
62
+ });
63
+ }
64
+ function retrieveBody(input) {
65
+ if (!isRecord$3(input)) throw invalidArgument$1("input must be an object", "input");
66
+ return compactUndefined({
67
+ billingContext: billingContext(input.billingContext),
68
+ maxResults: nonNegativeInteger$1(input.maxResults, "maxResults"),
69
+ query: requiredString$2(input.query, "query"),
70
+ queryHints: queryHints(input.queryHints),
71
+ scoreThreshold: finiteNumber(input.scoreThreshold, "scoreThreshold"),
72
+ sourceIds: stringList(input.sourceIds ?? [], "sourceIds"),
73
+ topK: nonNegativeInteger$1(input.topK, "topK")
74
+ });
75
+ }
76
+ function chatSessionBody(input = {}) {
77
+ if (!isRecord$3(input)) throw invalidArgument$1("input must be an object", "input");
78
+ return compactUndefined({
79
+ metadata: optionalRecord(input.metadata, "metadata"),
80
+ modelOverride: optionalTrimmedString(input.modelOverride, "modelOverride"),
81
+ sceneType: optionalTrimmedString(input.sceneType, "sceneType") ?? "rag",
82
+ status: optionalTrimmedString(input.status, "status"),
83
+ title: optionalString$1(input.title, "title")
84
+ });
85
+ }
86
+ function streamBody(input) {
87
+ if (!isRecord$3(input)) throw invalidArgument$1("input must be an object", "input");
88
+ return compactUndefined({
89
+ content: requiredString$2(input.content, "content"),
90
+ maxResults: nonNegativeInteger$1(input.maxResults, "maxResults"),
91
+ modelOverride: optionalTrimmedString(input.modelOverride, "modelOverride"),
92
+ sceneType: optionalTrimmedString(input.sceneType, "sceneType"),
93
+ sourceIds: stringList(input.sourceIds ?? [], "sourceIds"),
94
+ topK: nonNegativeInteger$1(input.topK, "topK")
95
+ });
96
+ }
97
+ function taskRunBody(input) {
98
+ if (!isRecord$3(input)) throw invalidArgument$1("input must be an object", "input");
99
+ return compactUndefined({
100
+ chatSessionId: optionalTrimmedString(input.chatSessionId, "chatSessionId"),
101
+ config: optionalRecord(input.config, "config"),
102
+ parentRunId: optionalTrimmedString(input.parentRunId, "parentRunId"),
103
+ sceneType: requiredString$2(input.sceneType, "sceneType"),
104
+ settings: optionalRecord(input.settings, "settings"),
105
+ sourceId: optionalTrimmedString(input.sourceId, "sourceId"),
106
+ scheduledTaskId: optionalTrimmedString(input.scheduledTaskId, "scheduledTaskId"),
107
+ trigger: optionalTrimmedString(input.trigger, "trigger")
108
+ });
109
+ }
110
+ function taskRunListPath(context, notebookId, input = {}) {
111
+ if (!isRecord$3(input)) throw invalidArgument$1("input must be an object", "input");
112
+ const normalizedPage = pagination$1(input);
113
+ const status = optionalTrimmedString(input.status, "status");
114
+ if (status !== void 0 && ![
115
+ "pending",
116
+ "running",
117
+ "success",
118
+ "partial",
119
+ "failed",
120
+ "canceled"
121
+ ].includes(status)) throw invalidArgument$1("status must be a known task run status", "status");
122
+ return queryPath(context.profile.apiPath("notebooks", notebookId, "task-runs"), [
123
+ ["scheduled_task_id", optionalTrimmedString(input.scheduledTaskId, "scheduledTaskId")],
124
+ ["parent_run_id", optionalTrimmedString(input.parentRunId, "parentRunId")],
125
+ ["scene_type", optionalTrimmedString(input.sceneType, "sceneType")],
126
+ ["source_id", optionalTrimmedString(input.sourceId, "sourceId")],
127
+ ["trigger", optionalTrimmedString(input.trigger, "trigger")],
128
+ ["status", status],
129
+ ["page", normalizedPage.page],
130
+ ["page_size", normalizedPage.pageSize]
131
+ ]);
132
+ }
133
+ function agentContent(input) {
134
+ if (input.scene === "rag") return {
135
+ content: requiredString$2(input.question, "question"),
136
+ maxResults: input.maxResults,
137
+ modelOverride: input.modelOverride,
138
+ sceneType: "rag",
139
+ sourceIds: input.sourceIds,
140
+ topK: input.topK
141
+ };
142
+ return {
143
+ content: `Begin the ${input.scene} workflow now for this notebook. ${workflowPrompt}`,
144
+ modelOverride: input.modelOverride,
145
+ sceneType: input.scene
146
+ };
147
+ }
148
+ function callOptions(options) {
149
+ return {
150
+ ...options?.signal === void 0 ? {} : { signal: options.signal },
151
+ ...options?.timeoutMs === void 0 ? {} : { timeoutMs: options.timeoutMs }
152
+ };
153
+ }
154
+ function streamCallOptions(options) {
155
+ return {
156
+ ...callOptions(options),
157
+ ...options?.connectTimeoutMs === void 0 ? {} : { connectTimeoutMs: options.connectTimeoutMs },
158
+ ...options?.idleTimeoutMs === void 0 ? {} : { idleTimeoutMs: options.idleTimeoutMs }
159
+ };
160
+ }
161
+ function createAgentOperations(context) {
162
+ const createChatSessionFor = (notebookId, input, options, featured) => {
163
+ const notebook = requiredString$2(notebookId, "notebookId");
164
+ return call(context, {
165
+ body: chatSessionBody(input),
166
+ callOptions: options,
167
+ capability: featured ? "featuredNotebooks.read" : "chat",
168
+ idempotent: false,
169
+ method: "POST",
170
+ operation: featured ? "featuredChatSessions.create" : "chatSessions.create",
171
+ path: context.profile.apiPath(featured ? "featured-notebooks" : "notebooks", notebook, "chat-sessions"),
172
+ schema: chatSessionSchema
173
+ });
174
+ };
175
+ const createChatSession = (notebookId, input, options) => createChatSessionFor(notebookId, input, options, false);
176
+ const createFeaturedChatSession = (notebookId, input, options) => createChatSessionFor(notebookId, input, options, true);
177
+ const streamQuestionFor = (notebookId, sessionId, input, options, featured) => {
178
+ const notebook = requiredString$2(notebookId, "notebookId");
179
+ const session = requiredString$2(sessionId, "sessionId");
180
+ return executeStream({
181
+ capability: featured ? "featuredNotebooks.read" : "chat",
182
+ operation: featured ? "featuredChat.stream" : "chat.stream",
183
+ profile: context.profile,
184
+ request: {
185
+ body: {
186
+ kind: "json",
187
+ value: streamBody(input)
188
+ },
189
+ destination: {
190
+ kind: "host",
191
+ path: queryPath(context.profile.apiPath(featured ? "featured-notebooks" : "notebooks", notebook, "chat-sessions", session, "messages", "stream"), [["client", context.client]])
192
+ },
193
+ idempotent: false,
194
+ method: "POST",
195
+ ...streamCallOptions(options)
196
+ },
197
+ transport: context.transport
198
+ });
199
+ };
200
+ const streamQuestion = (notebookId, sessionId, input, options) => streamQuestionFor(notebookId, sessionId, input, options, false);
201
+ const streamFeaturedQuestion = (notebookId, sessionId, input, options) => streamQuestionFor(notebookId, sessionId, input, options, true);
202
+ const cancelQuestionFor = (notebookId, sessionId, input = {}, options, featured = false) => {
203
+ const notebook = requiredString$2(notebookId, "notebookId");
204
+ const session = requiredString$2(sessionId, "sessionId");
205
+ if (!isRecord$3(input)) throw invalidArgument$1("input must be an object", "input");
206
+ return call(context, {
207
+ body: compactUndefined({ reason: optionalTrimmedString(input.reason, "reason") }),
208
+ callOptions: options,
209
+ capability: featured ? "featuredNotebooks.read" : "chat",
210
+ idempotent: false,
211
+ method: "POST",
212
+ operation: featured ? "featuredChat.cancel" : "chat.cancel",
213
+ path: context.profile.apiPath(featured ? "featured-notebooks" : "notebooks", notebook, "chat-sessions", session, "messages", "cancel"),
214
+ schema: cancelQuestionResultSchema
215
+ });
216
+ };
217
+ const listChatSessionsFor = (notebookId, input = {}, options, featured = false) => {
218
+ const notebook = requiredString$2(notebookId, "notebookId");
219
+ const normalizedPage = pagination$1({
220
+ page: input.page ?? 1,
221
+ pageSize: input.pageSize ?? 20
222
+ });
223
+ return call(context, {
224
+ callOptions: options,
225
+ capability: featured ? "featuredNotebooks.read" : "chat",
226
+ idempotent: true,
227
+ method: "GET",
228
+ operation: featured ? "featuredChatSessions.list" : "chatSessions.list",
229
+ path: queryPath(context.profile.apiPath(featured ? "featured-notebooks" : "notebooks", notebook, "chat-sessions"), [["page", normalizedPage.page], ["page_size", normalizedPage.pageSize]]),
230
+ schema: chatSessionListSchema
231
+ });
232
+ };
233
+ const listChatMessagesFor = (notebookId, sessionId, input = {}, options, featured = false) => {
234
+ const notebook = requiredString$2(notebookId, "notebookId");
235
+ const session = requiredString$2(sessionId, "sessionId");
236
+ const normalizedPage = pagination$1({
237
+ page: input.page ?? 1,
238
+ pageSize: input.pageSize ?? 100
239
+ });
240
+ return call(context, {
241
+ callOptions: options,
242
+ capability: featured ? "featuredNotebooks.read" : "chat",
243
+ idempotent: true,
244
+ method: "GET",
245
+ operation: featured ? "featuredChatMessages.list" : "chatMessages.list",
246
+ path: queryPath(context.profile.apiPath(featured ? "featured-notebooks" : "notebooks", notebook, "chat-sessions", session, "messages"), [["page", normalizedPage.page], ["page_size", normalizedPage.pageSize]]),
247
+ schema: chatMessageListSchema
248
+ });
249
+ };
250
+ const deleteChatSessionFor = (notebookId, sessionId, options, featured = false) => {
251
+ const notebook = requiredString$2(notebookId, "notebookId");
252
+ const session = requiredString$2(sessionId, "sessionId");
253
+ return call(context, {
254
+ callOptions: options,
255
+ capability: featured ? "featuredNotebooks.read" : "chat",
256
+ idempotent: false,
257
+ method: "DELETE",
258
+ operation: featured ? "featuredChatSessions.delete" : "chatSessions.delete",
259
+ path: context.profile.apiPath(featured ? "featured-notebooks" : "notebooks", notebook, "chat-sessions", session),
260
+ schema: voidResponseSchema
261
+ });
262
+ };
263
+ const startAgentRunFor = async (notebookId, input, options, featured) => {
264
+ if (!isRecord$3(input)) throw invalidArgument$1("input must be an object", "input");
265
+ if (input.scene !== "rag" && input.scene !== "compilation" && input.scene !== "lint") throw invalidArgument$1("scene must be rag, compilation or lint", "scene");
266
+ const notebook = requiredString$2(notebookId, "notebookId");
267
+ const scene = input.scene;
268
+ const modelOverride = optionalTrimmedString(input.modelOverride, "modelOverride");
269
+ const title = optionalString$1(input.title, "title");
270
+ const session = await (featured ? createFeaturedChatSession : createChatSession)(notebook, {
271
+ ...modelOverride === void 0 ? {} : { modelOverride },
272
+ sceneType: scene,
273
+ ...title === void 0 ? {} : { title }
274
+ }, options);
275
+ return {
276
+ events: (featured ? streamFeaturedQuestion : streamQuestion)(notebook, session.id, agentContent(input), options),
277
+ scene,
278
+ session
279
+ };
280
+ };
281
+ return {
282
+ cancelFeaturedQuestion: (notebookId, sessionId, input, options) => cancelQuestionFor(notebookId, sessionId, input, options, true),
283
+ cancelQuestion: (notebookId, sessionId, input, options) => cancelQuestionFor(notebookId, sessionId, input, options),
284
+ createChatSession,
285
+ createFeaturedChatSession,
286
+ deleteChatSession: (notebookId, sessionId, options) => deleteChatSessionFor(notebookId, sessionId, options),
287
+ deleteFeaturedChatSession: (notebookId, sessionId, options) => deleteChatSessionFor(notebookId, sessionId, options, true),
288
+ async createTaskRun(notebookId, input, options) {
289
+ const notebook = requiredString$2(notebookId, "notebookId");
290
+ return call(context, {
291
+ body: taskRunBody(input),
292
+ callOptions: options,
293
+ capability: "taskRuns.write",
294
+ idempotent: false,
295
+ method: "POST",
296
+ operation: "taskRuns.create",
297
+ path: context.profile.apiPath("notebooks", notebook, "task-runs"),
298
+ schema: taskRunSchema
299
+ });
300
+ },
301
+ async getTaskRun(notebookId, runId, options) {
302
+ const notebook = requiredString$2(notebookId, "notebookId");
303
+ const run = requiredString$2(runId, "runId");
304
+ return call(context, {
305
+ callOptions: options,
306
+ capability: "taskRuns.read",
307
+ idempotent: true,
308
+ method: "GET",
309
+ operation: "taskRuns.get",
310
+ path: context.profile.apiPath("notebooks", notebook, "task-runs", run),
311
+ schema: taskRunSchema
312
+ });
313
+ },
314
+ async listTaskRuns(notebookId, input, options) {
315
+ const notebook = requiredString$2(notebookId, "notebookId");
316
+ return call(context, {
317
+ callOptions: options,
318
+ capability: "taskRuns.read",
319
+ idempotent: true,
320
+ method: "GET",
321
+ operation: "taskRuns.list",
322
+ path: taskRunListPath(context, notebook, input),
323
+ schema: taskRunListSchema
324
+ });
325
+ },
326
+ listChatMessages: (notebookId, sessionId, input, options) => listChatMessagesFor(notebookId, sessionId, input, options),
327
+ listChatSessions: (notebookId, input, options) => listChatSessionsFor(notebookId, input, options),
328
+ listFeaturedChatMessages: (notebookId, sessionId, input, options) => listChatMessagesFor(notebookId, sessionId, input, options, true),
329
+ listFeaturedChatSessions: (notebookId, input, options) => listChatSessionsFor(notebookId, input, options, true),
330
+ async retrieve(notebookId, input, options) {
331
+ const notebook = requiredString$2(notebookId, "notebookId");
332
+ return call(context, {
333
+ body: retrieveBody(input),
334
+ callOptions: options,
335
+ capability: "retrieval",
336
+ idempotent: false,
337
+ method: "POST",
338
+ operation: "retrieval.retrieve",
339
+ path: context.profile.apiPath("notebooks", notebook, "retrieve"),
340
+ schema: retrieveResultSchema
341
+ });
342
+ },
343
+ startAgentRun: (notebookId, input, options) => startAgentRunFor(notebookId, input, options, false),
344
+ startFeaturedAgentRun: (notebookId, input, options) => startAgentRunFor(notebookId, input, options, true),
345
+ streamFeaturedQuestion,
346
+ streamQuestion
347
+ };
348
+ }
349
+ //#endregion
350
+ //#region ../sdk/src/operations/notebooks.ts
351
+ function createNotebookOperations(context) {
352
+ return {
353
+ async createNotebook(input, options) {
354
+ const title = requiredString$2(input?.title, "title");
355
+ const description = optionalString$1(input?.description, "description");
356
+ const settings = optionalRecord(input?.settings, "settings");
357
+ return call(context, {
358
+ body: compactUndefined({
359
+ client: context.client,
360
+ description,
361
+ settings,
362
+ title
363
+ }),
364
+ callOptions: options,
365
+ capability: "notebooks.write",
366
+ idempotent: false,
367
+ method: "POST",
368
+ operation: "notebooks.create",
369
+ path: context.profile.apiPath("notebooks"),
370
+ schema: notebookSchema
371
+ });
372
+ },
373
+ async deleteNotebook(notebookId, options) {
374
+ const id = requiredString$2(notebookId, "notebookId");
375
+ return call(context, {
376
+ callOptions: options,
377
+ capability: "notebooks.write",
378
+ idempotent: false,
379
+ method: "DELETE",
380
+ operation: "notebooks.delete",
381
+ path: context.profile.apiPath("notebooks", id),
382
+ schema: voidResponseSchema
383
+ });
384
+ },
385
+ async getNotebook(notebookId, options) {
386
+ const id = requiredString$2(notebookId, "notebookId");
387
+ return call(context, {
388
+ callOptions: options,
389
+ capability: "notebooks.read",
390
+ idempotent: true,
391
+ method: "GET",
392
+ operation: "notebooks.get",
393
+ path: context.profile.apiPath("notebooks", id),
394
+ schema: notebookSchema
395
+ });
396
+ },
397
+ async getFeaturedNotebook(notebookId, options) {
398
+ const id = requiredString$2(notebookId, "notebookId");
399
+ return call(context, {
400
+ callOptions: options,
401
+ capability: "featuredNotebooks.read",
402
+ idempotent: true,
403
+ method: "GET",
404
+ operation: "featuredNotebooks.get",
405
+ path: context.profile.featuredNotebookPath(id),
406
+ schema: notebookSchema
407
+ });
408
+ },
409
+ async listFeaturedNotebooks(input = {}, options) {
410
+ const normalized = pagination$1(input);
411
+ return call(context, {
412
+ callOptions: options,
413
+ capability: "featuredNotebooks.read",
414
+ idempotent: true,
415
+ method: "GET",
416
+ operation: "featuredNotebooks.list",
417
+ path: context.profile.listFeaturedNotebooksPath(normalized),
418
+ schema: notebookListSchema
419
+ });
420
+ },
421
+ async listNotebooks(input = {}, options) {
422
+ const normalizedPage = pagination$1(input);
423
+ const client = optionalTrimmedString(input.client, "client");
424
+ if (client !== void 0 && !context.profile.capabilities["notebooks.filterByClient"]) assertCapability(context.profile, "notebooks.filterByClient", "notebooks.list");
425
+ const normalized = {
426
+ ...normalizedPage,
427
+ ...client === void 0 ? {} : { client }
428
+ };
429
+ return call(context, {
430
+ callOptions: options,
431
+ capability: "notebooks.read",
432
+ idempotent: true,
433
+ method: "GET",
434
+ operation: "notebooks.list",
435
+ path: context.profile.listNotebooksPath(normalized),
436
+ schema: notebookListSchema
437
+ });
438
+ },
439
+ async listSharedNotebooks(input = {}, options) {
440
+ const normalized = pagination$1(input);
441
+ return call(context, {
442
+ callOptions: options,
443
+ capability: "sharedNotebooks.read",
444
+ idempotent: true,
445
+ method: "GET",
446
+ operation: "sharedNotebooks.list",
447
+ path: context.profile.listSharedNotebooksPath(normalized),
448
+ schema: sharedNotebookListSchema
449
+ });
450
+ },
451
+ async listWikiBranches(notebookId, options) {
452
+ const id = requiredString$2(notebookId, "notebookId");
453
+ return (await call(context, {
454
+ callOptions: options,
455
+ capability: "wikiBranches.read",
456
+ idempotent: true,
457
+ method: "GET",
458
+ operation: "wikiBranches.list",
459
+ path: context.profile.apiPath("notebooks", id, "wiki-branches"),
460
+ schema: wikiBranchListSchema
461
+ })).branches;
462
+ },
463
+ async updateNotebook(notebookId, input, options) {
464
+ const id = requiredString$2(notebookId, "notebookId");
465
+ const body = compactUndefined({
466
+ description: optionalString$1(input?.description, "description"),
467
+ settings: optionalRecord(input?.settings, "settings"),
468
+ status: optionalString$1(input?.status, "status"),
469
+ title: input?.title === void 0 ? void 0 : requiredString$2(input.title, "title")
470
+ });
471
+ assertNonEmptyPatch(body, "input");
472
+ return call(context, {
473
+ body,
474
+ callOptions: options,
475
+ capability: "notebooks.write",
476
+ idempotent: false,
477
+ method: "PUT",
478
+ operation: "notebooks.update",
479
+ path: context.profile.apiPath("notebooks", id),
480
+ schema: notebookSchema
481
+ });
482
+ }
483
+ };
484
+ }
485
+ //#endregion
486
+ //#region ../sdk/src/operations/sources.ts
487
+ const maxImageBytes = 52428800;
488
+ function sourceTreeNodeOrder(left, right) {
489
+ if (left.type !== right.type) return left.type === "directory" ? -1 : 1;
490
+ if (left.name !== right.name) return left.name < right.name ? -1 : 1;
491
+ return left.id < right.id ? -1 : left.id === right.id ? 0 : 1;
492
+ }
493
+ function sortSourceTree(nodes) {
494
+ nodes.sort(sourceTreeNodeOrder);
495
+ for (const node of nodes) sortSourceTree(node.files);
496
+ }
497
+ function createsSourceCycle(source, sourcesById) {
498
+ const seen = /* @__PURE__ */ new Set([source.id]);
499
+ let parentId = source.parentSourceId;
500
+ while (parentId.length > 0) {
501
+ if (seen.has(parentId)) return true;
502
+ seen.add(parentId);
503
+ const parent = sourcesById.get(parentId);
504
+ if (parent === void 0) return false;
505
+ parentId = parent.parentSourceId;
506
+ }
507
+ return false;
508
+ }
509
+ function buildSourceTree(sources) {
510
+ const sourcesById = new Map(sources.map((source) => [source.id, source]));
511
+ const parentIds = new Set(sources.filter((source) => sourcesById.has(source.parentSourceId) && !createsSourceCycle(source, sourcesById)).map((source) => source.parentSourceId));
512
+ const nodesById = /* @__PURE__ */ new Map();
513
+ for (const source of sources) nodesById.set(source.id, {
514
+ files: [],
515
+ id: source.id,
516
+ name: source.title,
517
+ path: source.path,
518
+ sourceType: source.sourceType,
519
+ status: source.status,
520
+ type: source.isDir || parentIds.has(source.id) ? "directory" : "file"
521
+ });
522
+ const tree = [];
523
+ for (const source of sources) {
524
+ const node = nodesById.get(source.id);
525
+ if (node === void 0) continue;
526
+ const parent = nodesById.get(source.parentSourceId);
527
+ if (parent === void 0 || createsSourceCycle(source, sourcesById)) tree.push(node);
528
+ else parent.files.push(node);
529
+ }
530
+ sortSourceTree(tree);
531
+ return {
532
+ totalSize: sources.length,
533
+ tree
534
+ };
535
+ }
536
+ function sourceBody(input) {
537
+ return compactUndefined({
538
+ isDir: optionalBoolean(input?.isDir, "isDir"),
539
+ metadata: optionalRecord(input?.metadata, "metadata"),
540
+ parentSourceId: optionalString$1(input?.parentSourceId, "parentSourceId"),
541
+ path: optionalString$1(input?.path, "path"),
542
+ sourceType: optionalTrimmedString(input?.sourceType, "sourceType"),
543
+ status: optionalTrimmedString(input?.status, "status"),
544
+ title: requiredString$2(input?.title, "title"),
545
+ uri: optionalString$1(input?.uri, "uri")
546
+ });
547
+ }
548
+ function sourcePatch(input) {
549
+ const patch = compactUndefined({
550
+ content: optionalString$1(input?.content, "content"),
551
+ errorMessage: optionalString$1(input?.errorMessage, "errorMessage"),
552
+ isDir: optionalBoolean(input?.isDir, "isDir"),
553
+ metadata: optionalRecord(input?.metadata, "metadata"),
554
+ parentSourceId: optionalString$1(input?.parentSourceId, "parentSourceId"),
555
+ path: optionalString$1(input?.path, "path"),
556
+ sourceType: optionalString$1(input?.sourceType, "sourceType"),
557
+ status: optionalString$1(input?.status, "status"),
558
+ title: optionalString$1(input?.title, "title"),
559
+ uri: optionalString$1(input?.uri, "uri")
560
+ });
561
+ assertNonEmptyPatch(patch, "input");
562
+ return patch;
563
+ }
564
+ function siteBody(input) {
565
+ const maxPages = positiveInteger$1(input?.maxPages, "maxPages") ?? 50;
566
+ if (maxPages > 200) throw invalidArgument$1("maxPages must not exceed 200", "maxPages");
567
+ const includePaths = stringList(input?.includePaths ?? [], "includePaths");
568
+ const excludePaths = stringList(input?.excludePaths ?? [], "excludePaths");
569
+ const sitemap = optionalTrimmedString(input?.sitemapUrl, "sitemapUrl");
570
+ const compileAfterRefresh = optionalBoolean(input?.compileAfterRefresh, "compileAfterRefresh");
571
+ return compactUndefined({
572
+ config: compactUndefined({
573
+ compileAfterRefresh,
574
+ excludePaths,
575
+ includePaths,
576
+ maxPages,
577
+ rootUrl: httpUrl(input?.rootUrl, "rootUrl"),
578
+ sitemapUrl: sitemap === void 0 ? void 0 : httpUrl(sitemap, "sitemapUrl")
579
+ }),
580
+ parentSourceId: optionalString$1(input?.parentSourceId, "parentSourceId"),
581
+ path: optionalString$1(input?.pathPrefix, "pathPrefix"),
582
+ sourceType: "site_import",
583
+ title: optionalTrimmedString(input?.title, "title")
584
+ });
585
+ }
586
+ function createSourceOperations(context) {
587
+ const createSource = async (notebookId, input, options) => {
588
+ const notebook = requiredString$2(notebookId, "notebookId");
589
+ return call(context, {
590
+ body: sourceBody(input),
591
+ callOptions: options,
592
+ capability: "sources.write",
593
+ idempotent: false,
594
+ method: "POST",
595
+ operation: "sources.create",
596
+ path: context.profile.apiPath("notebooks", notebook, "sources"),
597
+ schema: sourceSchema
598
+ });
599
+ };
600
+ const updateSource = async (notebookId, sourceId, input, options) => {
601
+ const notebook = requiredString$2(notebookId, "notebookId");
602
+ const source = requiredString$2(sourceId, "sourceId");
603
+ return call(context, {
604
+ body: sourcePatch(input),
605
+ callOptions: options,
606
+ capability: "sources.write",
607
+ idempotent: false,
608
+ method: "PUT",
609
+ operation: "sources.update",
610
+ path: context.profile.apiPath("notebooks", notebook, "sources", source),
611
+ schema: sourceSchema
612
+ });
613
+ };
614
+ const readSourcePage = async (notebookId, input = {}, options, operation = "sources.list") => {
615
+ const notebook = requiredString$2(notebookId, "notebookId");
616
+ const normalizedPage = pagination$1(input);
617
+ const isDir = optionalBoolean(input.isDir, "isDir");
618
+ const view = input.view ?? "list";
619
+ if (view !== "list" && view !== "children") throw invalidArgument$1("view must be either \"list\" or \"children\"", "view");
620
+ const parentSourceId = optionalString$1(input.parentSourceId, "parentSourceId");
621
+ const query = optionalTrimmedString(input.query, "query");
622
+ if (view === "children") assertCapability(context.profile, "sources.children", "sources.list");
623
+ if (query !== void 0) assertCapability(context.profile, "sources.search", "sources.list");
624
+ if (parentSourceId !== void 0 && view !== "children") throw invalidArgument$1("parentSourceId requires view=\"children\"", "parentSourceId");
625
+ const path = queryPath(context.profile.apiPath("notebooks", notebook, "sources"), [
626
+ ["view", view === "list" ? void 0 : view],
627
+ ["is_dir", isDir === void 0 ? void 0 : String(isDir)],
628
+ ["parent_source_id", parentSourceId],
629
+ ["page", normalizedPage.page],
630
+ ["page_size", normalizedPage.pageSize],
631
+ ["query", query]
632
+ ]);
633
+ return call(context, {
634
+ callOptions: options,
635
+ capability: "sources.read",
636
+ idempotent: true,
637
+ method: "GET",
638
+ operation,
639
+ path,
640
+ schema: sourceListSchema
641
+ });
642
+ };
643
+ const listSources = (notebookId, input, options) => readSourcePage(notebookId, input, options);
644
+ const listFeaturedSources = async (notebookId, input = {}, options) => {
645
+ const notebook = requiredString$2(notebookId, "notebookId");
646
+ const normalizedPage = pagination$1({
647
+ page: input.page ?? 1,
648
+ pageSize: input.pageSize ?? 200
649
+ });
650
+ const parentSourceId = optionalString$1(input.parentSourceId, "parentSourceId");
651
+ const view = input.view ?? "list";
652
+ if (view !== "list" && view !== "children") throw invalidArgument$1("view must be either \"list\" or \"children\"", "view");
653
+ return call(context, {
654
+ callOptions: options,
655
+ capability: "featuredNotebooks.read",
656
+ idempotent: true,
657
+ method: "GET",
658
+ operation: "featuredSources.list",
659
+ path: queryPath(context.profile.apiPath("featured-notebooks", notebook, "sources"), [
660
+ ["view", view === "list" ? void 0 : view],
661
+ ["parent_source_id", parentSourceId],
662
+ ["page", normalizedPage.page],
663
+ ["page_size", normalizedPage.pageSize]
664
+ ]),
665
+ schema: sourceListSchema
666
+ });
667
+ };
668
+ const collectAllSources = async (notebookId, input = {}, options, operation) => {
669
+ const notebook = requiredString$2(notebookId, "notebookId");
670
+ const pageSize = positiveInteger$1(input.pageSize, "pageSize") ?? 200;
671
+ const maxPages = positiveInteger$1(input.maxPages, "maxPages") ?? 1e4;
672
+ const sources = [];
673
+ for (let page = 1; page <= maxPages; page += 1) {
674
+ const result = await readSourcePage(notebook, {
675
+ page,
676
+ pageSize,
677
+ view: "list"
678
+ }, options, operation);
679
+ sources.push(...result.sources);
680
+ const lastPage = result.pages?.lastPage;
681
+ if (result.sources.length < pageSize || lastPage !== void 0 && page >= lastPage) return sources;
682
+ }
683
+ throw new QMindError("PROTOCOL_ERROR", `source pagination exceeded ${maxPages} pages`, {
684
+ access: context.profile.access,
685
+ details: {
686
+ maxPages,
687
+ pageSize
688
+ },
689
+ operation
690
+ });
691
+ };
692
+ const listAllSources = (notebookId, input, options) => collectAllSources(notebookId, input, options, "sources.list");
693
+ const getSourceTree = async (notebookId, options) => {
694
+ const notebook = requiredString$2(notebookId, "notebookId");
695
+ assertCapability(context.profile, "sources.tree", "sources.tree");
696
+ if (context.profile.access === "sash") return buildSourceTree(await collectAllSources(notebook, {}, options, "sources.tree"));
697
+ return call(context, {
698
+ callOptions: options,
699
+ capability: "sources.tree",
700
+ idempotent: true,
701
+ method: "GET",
702
+ operation: "sources.tree",
703
+ path: queryPath(context.profile.apiPath("notebooks", notebook, "sources"), [["view", "filetree"]]),
704
+ schema: sourceTreeSchema
705
+ });
706
+ };
707
+ return {
708
+ async batchDeleteSources(notebookId, sourceIds, options) {
709
+ const notebook = requiredString$2(notebookId, "notebookId");
710
+ const ids = stringList(sourceIds, "sourceIds", { min: 1 });
711
+ return call(context, {
712
+ body: { sourceIds: ids },
713
+ callOptions: options,
714
+ capability: "sources.write",
715
+ idempotent: false,
716
+ method: "POST",
717
+ operation: "sources.batchDelete",
718
+ path: context.profile.apiPath("notebooks", notebook, "sources", "batch-delete"),
719
+ schema: batchMutationResultSchema
720
+ });
721
+ },
722
+ async countSourceChildren(notebookId, parentIds, options) {
723
+ const notebook = requiredString$2(notebookId, "notebookId");
724
+ const ids = stringList(parentIds, "parentIds", {
725
+ max: 200,
726
+ min: 1
727
+ });
728
+ assertCapability(context.profile, "sources.childrenCount", "sources.childrenCount");
729
+ const path = context.profile.mindPath("notebooks", notebook, "sources", "children-count");
730
+ const query = new URLSearchParams();
731
+ for (const id of ids) query.append("parent_ids", id);
732
+ return call(context, {
733
+ callOptions: options,
734
+ capability: "sources.childrenCount",
735
+ idempotent: true,
736
+ method: "GET",
737
+ operation: "sources.childrenCount",
738
+ path: `${path}?${query.toString()}`,
739
+ schema: sourceChildrenCountsSchema
740
+ });
741
+ },
742
+ async createDirectory(notebookId, input, options) {
743
+ const title = requiredString$2(input?.title, "title");
744
+ return createSource(notebookId, {
745
+ isDir: true,
746
+ parentSourceId: input?.parentSourceId,
747
+ path: input?.path,
748
+ status: "ready",
749
+ title
750
+ }, options);
751
+ },
752
+ createSource,
753
+ async createSourceWebOfficeSession(notebookId, sourceId, options) {
754
+ const notebook = requiredString$2(notebookId, "notebookId");
755
+ const source = requiredString$2(sourceId, "sourceId");
756
+ assertCapability(context.profile, "sources.webOffice", "sources.webOffice.create");
757
+ return call(context, {
758
+ callOptions: options,
759
+ capability: "sources.webOffice",
760
+ idempotent: false,
761
+ method: "POST",
762
+ operation: "sources.webOffice.create",
763
+ path: context.profile.apiPath("notebooks", notebook, "sources", source, "weboffice", "session"),
764
+ schema: sourceWebOfficeSessionSchema
765
+ });
766
+ },
767
+ async deleteSource(notebookId, sourceId, options) {
768
+ const notebook = requiredString$2(notebookId, "notebookId");
769
+ const source = requiredString$2(sourceId, "sourceId");
770
+ return call(context, {
771
+ callOptions: options,
772
+ capability: "sources.write",
773
+ idempotent: false,
774
+ method: "DELETE",
775
+ operation: "sources.delete",
776
+ path: context.profile.apiPath("notebooks", notebook, "sources", source),
777
+ schema: voidResponseSchema
778
+ });
779
+ },
780
+ async getFeaturedSource(notebookId, sourceId, options) {
781
+ const notebook = requiredString$2(notebookId, "notebookId");
782
+ const source = requiredString$2(sourceId, "sourceId");
783
+ return call(context, {
784
+ callOptions: options,
785
+ capability: "featuredNotebooks.read",
786
+ idempotent: true,
787
+ method: "GET",
788
+ operation: "featuredSources.get",
789
+ path: context.profile.apiPath("featured-notebooks", notebook, "sources", source),
790
+ schema: sourceSchema
791
+ });
792
+ },
793
+ async getFeaturedSourceContent(notebookId, sourceId, options) {
794
+ const notebook = requiredString$2(notebookId, "notebookId");
795
+ const source = requiredString$2(sourceId, "sourceId");
796
+ return call(context, {
797
+ callOptions: options,
798
+ capability: "featuredNotebooks.read",
799
+ idempotent: true,
800
+ method: "GET",
801
+ operation: "featuredSources.content",
802
+ path: context.profile.apiPath("featured-notebooks", notebook, "sources", source, "content"),
803
+ schema: sourceContentSchema
804
+ });
805
+ },
806
+ async getFeaturedSourcePreview(notebookId, sourceId, options) {
807
+ const notebook = requiredString$2(notebookId, "notebookId");
808
+ const source = requiredString$2(sourceId, "sourceId");
809
+ return call(context, {
810
+ callOptions: options,
811
+ capability: "featuredNotebooks.read",
812
+ idempotent: true,
813
+ method: "GET",
814
+ operation: "featuredSources.preview",
815
+ path: context.profile.apiPath("featured-notebooks", notebook, "sources", source, "preview"),
816
+ schema: sourcePreviewSchema
817
+ });
818
+ },
819
+ async getFeaturedSourceRawContent(notebookId, sourceId, options) {
820
+ const notebook = requiredString$2(notebookId, "notebookId");
821
+ const source = requiredString$2(sourceId, "sourceId");
822
+ return call(context, {
823
+ callOptions: options,
824
+ capability: "featuredNotebooks.read",
825
+ idempotent: true,
826
+ method: "GET",
827
+ operation: "featuredSources.rawContent",
828
+ path: context.profile.apiPath("featured-notebooks", notebook, "sources", source, "raw-content"),
829
+ schema: sourceRawContentSchema
830
+ });
831
+ },
832
+ async getSource(notebookId, sourceId, options) {
833
+ const notebook = requiredString$2(notebookId, "notebookId");
834
+ const source = requiredString$2(sourceId, "sourceId");
835
+ return call(context, {
836
+ callOptions: options,
837
+ capability: "sources.read",
838
+ idempotent: true,
839
+ method: "GET",
840
+ operation: "sources.get",
841
+ path: context.profile.apiPath("notebooks", notebook, "sources", source),
842
+ schema: sourceSchema
843
+ });
844
+ },
845
+ async getSourceContent(notebookId, sourceId, options) {
846
+ const notebook = requiredString$2(notebookId, "notebookId");
847
+ const source = requiredString$2(sourceId, "sourceId");
848
+ return call(context, {
849
+ callOptions: options,
850
+ capability: "sources.read",
851
+ idempotent: true,
852
+ method: "GET",
853
+ operation: "sources.content",
854
+ path: context.profile.apiPath("notebooks", notebook, "sources", source, "content"),
855
+ schema: sourceContentSchema
856
+ });
857
+ },
858
+ async getSourceActionAccess(options) {
859
+ return call(context, {
860
+ callOptions: options,
861
+ capability: "sources.actionAccess",
862
+ idempotent: true,
863
+ method: "GET",
864
+ operation: "sources.actionAccess",
865
+ path: context.profile.mindPath("source-actions", "access"),
866
+ schema: sourceActionAccessSchema
867
+ });
868
+ },
869
+ async getSourcePreview(notebookId, sourceId, options) {
870
+ const notebook = requiredString$2(notebookId, "notebookId");
871
+ const source = requiredString$2(sourceId, "sourceId");
872
+ return call(context, {
873
+ callOptions: options,
874
+ capability: "sources.preview",
875
+ idempotent: true,
876
+ method: "GET",
877
+ operation: "sources.preview",
878
+ path: context.profile.apiPath("notebooks", notebook, "sources", source, "preview"),
879
+ schema: sourcePreviewSchema
880
+ });
881
+ },
882
+ async getSourceRawContent(notebookId, sourceId, options) {
883
+ const notebook = requiredString$2(notebookId, "notebookId");
884
+ const source = requiredString$2(sourceId, "sourceId");
885
+ return call(context, {
886
+ callOptions: options,
887
+ capability: "sources.rawContent",
888
+ idempotent: true,
889
+ method: "GET",
890
+ operation: "sources.rawContent",
891
+ path: context.profile.apiPath("notebooks", notebook, "sources", source, "raw-content"),
892
+ schema: sourceRawContentSchema
893
+ });
894
+ },
895
+ async getSourceUploadCapabilities(options) {
896
+ return call(context, {
897
+ callOptions: options,
898
+ capability: "sources.write",
899
+ idempotent: true,
900
+ method: "GET",
901
+ operation: "sources.uploadCapabilities",
902
+ path: context.profile.mindPath("source-upload", "capabilities"),
903
+ schema: sourceUploadCapabilitiesSchema
904
+ });
905
+ },
906
+ async getFeaturedSourceTree(notebookId, options) {
907
+ return buildSourceTree((await listFeaturedSources(notebookId, {
908
+ page: 1,
909
+ pageSize: 200
910
+ }, options)).sources);
911
+ },
912
+ getSourceTree,
913
+ async importSite(notebookId, input, options) {
914
+ const notebook = requiredString$2(notebookId, "notebookId");
915
+ return call(context, {
916
+ body: siteBody(input),
917
+ callOptions: options,
918
+ capability: "sources.write",
919
+ idempotent: false,
920
+ method: "POST",
921
+ operation: "sources.importSite",
922
+ path: context.profile.apiPath("notebooks", notebook, "sources"),
923
+ schema: sourceSchema
924
+ });
925
+ },
926
+ async importWebLinks(notebookId, input, options) {
927
+ const notebook = requiredString$2(notebookId, "notebookId");
928
+ const urls = stringList(input?.urls ?? [], "urls", {
929
+ max: 5,
930
+ min: 1
931
+ }).map((url) => httpUrl(url, "urls"));
932
+ return call(context, {
933
+ body: compactUndefined({
934
+ parentSourceId: optionalString$1(input?.parentSourceId, "parentSourceId"),
935
+ path: optionalString$1(input?.path, "path"),
936
+ sourceType: "url",
937
+ urls
938
+ }),
939
+ callOptions: options,
940
+ capability: "sources.importWeb",
941
+ idempotent: false,
942
+ method: "POST",
943
+ operation: "sources.importWeb",
944
+ path: context.profile.apiPath("notebooks", notebook, "sources"),
945
+ schema: sourceImportResultsSchema
946
+ });
947
+ },
948
+ async importWikiDocuments(notebookId, input, options) {
949
+ const notebook = requiredString$2(notebookId, "notebookId");
950
+ const urls = stringList(input?.urls ?? [], "urls", {
951
+ max: 5,
952
+ min: 1
953
+ });
954
+ return call(context, {
955
+ body: compactUndefined({
956
+ parentSourceId: optionalString$1(input?.parentSourceId, "parentSourceId"),
957
+ path: "/repowiki",
958
+ sourceType: "repowiki",
959
+ urls
960
+ }),
961
+ callOptions: options,
962
+ capability: "sources.write",
963
+ idempotent: false,
964
+ method: "POST",
965
+ operation: "sources.importWiki",
966
+ path: context.profile.apiPath("notebooks", notebook, "sources"),
967
+ schema: sourceImportResultsSchema
968
+ });
969
+ },
970
+ listAllSources,
971
+ listFeaturedSources,
972
+ listSources,
973
+ async moveSource(notebookId, sourceId, input, options) {
974
+ const parentSourceId = optionalString$1(input?.parentSourceId, "parentSourceId");
975
+ const path = optionalString$1(input?.path, "path");
976
+ const status = optionalString$1(input?.status, "status");
977
+ const title = optionalString$1(input?.title, "title");
978
+ const patch = {
979
+ ...parentSourceId === void 0 ? {} : { parentSourceId },
980
+ ...path === void 0 ? {} : { path },
981
+ ...status === void 0 ? {} : { status },
982
+ ...title === void 0 ? {} : { title }
983
+ };
984
+ assertNonEmptyPatch(patch, "input");
985
+ return updateSource(notebookId, sourceId, patch, options);
986
+ },
987
+ async presignImageUpload(notebookId, sourceId, input, options) {
988
+ const notebook = requiredString$2(notebookId, "notebookId");
989
+ const source = requiredString$2(sourceId, "sourceId");
990
+ const filename = requiredString$2(input?.filename, "filename");
991
+ const mimeType = requiredString$2(input?.mimeType, "mimeType");
992
+ const size = nonNegativeInteger$1(input?.size, "size");
993
+ if (size === void 0) throw invalidArgument$1("size must be a non-negative integer", "size");
994
+ if (size > maxImageBytes) throw invalidArgument$1(`size must not exceed ${maxImageBytes} bytes`, "size");
995
+ return call(context, {
996
+ body: compactUndefined({
997
+ filename,
998
+ mimeType,
999
+ size
1000
+ }),
1001
+ callOptions: options,
1002
+ capability: "images.presignUpload",
1003
+ idempotent: false,
1004
+ method: "POST",
1005
+ operation: "images.presignUpload",
1006
+ path: context.profile.apiPath("notebooks", notebook, "sources", source, "images", "presign"),
1007
+ schema: imageUploadTicketSchema
1008
+ });
1009
+ },
1010
+ async refreshSourceWebOfficeSession(notebookId, sourceId, input, options) {
1011
+ const notebook = requiredString$2(notebookId, "notebookId");
1012
+ const source = requiredString$2(sourceId, "sourceId");
1013
+ const sessionId = requiredString$2(input?.sessionId, "sessionId");
1014
+ const tokenVersion = nonNegativeInteger$1(input?.tokenVersion, "tokenVersion") ?? 0;
1015
+ assertCapability(context.profile, "sources.webOffice", "sources.webOffice.refresh");
1016
+ return call(context, {
1017
+ body: { tokenVersion },
1018
+ callOptions: options,
1019
+ capability: "sources.webOffice",
1020
+ idempotent: false,
1021
+ method: "POST",
1022
+ operation: "sources.webOffice.refresh",
1023
+ path: context.profile.apiPath("notebooks", notebook, "sources", source, "weboffice", "session", sessionId, "refresh"),
1024
+ schema: sourceWebOfficeTokenSchema
1025
+ });
1026
+ },
1027
+ async presignImageUrls(notebookId, ossKeys, options) {
1028
+ const notebook = requiredString$2(notebookId, "notebookId");
1029
+ const keys = stringList(ossKeys, "ossKeys", {
1030
+ max: 100,
1031
+ min: 1
1032
+ });
1033
+ return call(context, {
1034
+ body: { ossKeys: keys },
1035
+ callOptions: options,
1036
+ capability: "images.presignUrls",
1037
+ idempotent: false,
1038
+ method: "POST",
1039
+ operation: "images.presignUrls",
1040
+ path: context.profile.apiPath("notebooks", notebook, "images", "presign-urls"),
1041
+ schema: imageUrlsSchema
1042
+ });
1043
+ },
1044
+ updateSource
1045
+ };
1046
+ }
1047
+ //#endregion
1048
+ //#region ../sdk/src/operations/transfers.ts
1049
+ function isRecord$2(value) {
1050
+ return typeof value === "object" && value !== null && !Array.isArray(value);
1051
+ }
1052
+ function binarySource(value) {
1053
+ if (!isRecord$2(value)) throw invalidArgument$1("source must be a QMindBinarySource", "source");
1054
+ const filename = requiredString$2(value.filename, "source.filename");
1055
+ const mimeType = requiredString$2(value.mimeType, "source.mimeType");
1056
+ const size = value.size;
1057
+ if (typeof size !== "number" || !Number.isSafeInteger(size) || size <= 0) throw invalidArgument$1("source.size must be a positive safe integer", "source.size");
1058
+ if (typeof value.open !== "function") throw invalidArgument$1("source.open must be a function", "source.open");
1059
+ const source = value;
1060
+ return {
1061
+ filename,
1062
+ mimeType,
1063
+ open: () => source.open(),
1064
+ size
1065
+ };
1066
+ }
1067
+ function transferOptions(options) {
1068
+ if (options === void 0) return {};
1069
+ if (!isRecord$2(options)) throw invalidArgument$1("options must be an object", "options");
1070
+ if (options.onProgress !== void 0 && typeof options.onProgress !== "function") throw invalidArgument$1("onProgress must be a function", "onProgress");
1071
+ return options;
1072
+ }
1073
+ function uploadMetadata(value) {
1074
+ if (value === void 0) return {};
1075
+ if (!isRecord$2(value)) throw invalidArgument$1("metadata must be an object", "metadata");
1076
+ return {
1077
+ parentSourceId: optionalString$1(value.parentSourceId, "parentSourceId"),
1078
+ path: optionalString$1(value.path, "path"),
1079
+ sha256: optionalTrimmedString(value.sha256, "sha256"),
1080
+ sourceType: optionalTrimmedString(value.sourceType, "sourceType"),
1081
+ title: optionalTrimmedString(value.title, "title")
1082
+ };
1083
+ }
1084
+ function progressReporter(context, operation, strategy, totalBytes, options) {
1085
+ let lastUploadedBytes = 0;
1086
+ const notify = (phase, uploadedBytes) => {
1087
+ if (!Number.isSafeInteger(uploadedBytes) || uploadedBytes < lastUploadedBytes) throw new QMindError("HOST_POLICY_ERROR", "upload progress must be a monotonic integer", {
1088
+ access: context.profile.access,
1089
+ details: {
1090
+ lastUploadedBytes,
1091
+ totalBytes,
1092
+ uploadedBytes
1093
+ },
1094
+ operation
1095
+ });
1096
+ if (uploadedBytes > totalBytes) throw new QMindError("HOST_POLICY_ERROR", "upload progress exceeds source size", {
1097
+ access: context.profile.access,
1098
+ details: {
1099
+ totalBytes,
1100
+ uploadedBytes
1101
+ },
1102
+ operation
1103
+ });
1104
+ lastUploadedBytes = uploadedBytes;
1105
+ const progress = {
1106
+ phase,
1107
+ strategy,
1108
+ totalBytes,
1109
+ uploadedBytes
1110
+ };
1111
+ options.onProgress?.(progress);
1112
+ };
1113
+ return {
1114
+ complete: () => notify("uploading", totalBytes),
1115
+ registering: () => notify("registering", totalBytes),
1116
+ start: () => notify("uploading", 0),
1117
+ update: (uploadedBytes) => notify("uploading", uploadedBytes)
1118
+ };
1119
+ }
1120
+ function requestOptions(options) {
1121
+ return {
1122
+ ...options.signal === void 0 ? {} : { signal: options.signal },
1123
+ ...options.timeoutMs === void 0 ? {} : { timeoutMs: options.timeoutMs }
1124
+ };
1125
+ }
1126
+ function transferCall(context, operation, request, schema) {
1127
+ return executeUnary({
1128
+ capability: operation.startsWith("images.") ? "images.presignUpload" : "transfers",
1129
+ operation,
1130
+ profile: context.profile,
1131
+ request,
1132
+ schema,
1133
+ transport: context.transport
1134
+ });
1135
+ }
1136
+ function hostRequest(path, method, idempotent, options, body) {
1137
+ const authorization = options.authorization?.trim();
1138
+ return {
1139
+ body,
1140
+ destination: {
1141
+ kind: "host",
1142
+ path
1143
+ },
1144
+ ...authorization === void 0 || authorization.length === 0 ? {} : { headers: { authorization: `Bearer ${authorization}` } },
1145
+ idempotent,
1146
+ method,
1147
+ ...requestOptions(options)
1148
+ };
1149
+ }
1150
+ function signedUploadRequest(ticket, source, progress, options) {
1151
+ return {
1152
+ body: {
1153
+ kind: "binary",
1154
+ onProgress: progress.update,
1155
+ source
1156
+ },
1157
+ destination: {
1158
+ kind: "signed",
1159
+ url: ticket.uploadUrl
1160
+ },
1161
+ headers: ticket.headers,
1162
+ idempotent: true,
1163
+ method: "PUT",
1164
+ ...requestOptions(options)
1165
+ };
1166
+ }
1167
+ function createTransferOperations(context) {
1168
+ return {
1169
+ async uploadImage(notebookId, sourceId, rawSource, rawOptions) {
1170
+ const notebook = requiredString$2(notebookId, "notebookId");
1171
+ const sourceIdValue = requiredString$2(sourceId, "sourceId");
1172
+ const source = binarySource(rawSource);
1173
+ const options = transferOptions(rawOptions);
1174
+ if (source.size > 52428800) throw new QMindError("FILE_TOO_LARGE", "image exceeds the 50 MiB upload limit", {
1175
+ access: context.profile.access,
1176
+ details: {
1177
+ limitBytes: QMIND_MULTIPART_UPLOAD_MAX_BYTES,
1178
+ size: source.size
1179
+ },
1180
+ operation: "images.upload"
1181
+ });
1182
+ const ticket = await transferCall(context, "images.presignUpload", hostRequest(context.profile.apiPath("notebooks", notebook, "sources", sourceIdValue, "images", "presign"), "POST", false, options, {
1183
+ kind: "json",
1184
+ value: {
1185
+ filename: source.filename,
1186
+ mimeType: source.mimeType,
1187
+ size: source.size
1188
+ }
1189
+ }), imageUploadTicketSchema);
1190
+ const progress = progressReporter(context, "images.upload", "presigned", source.size, options);
1191
+ progress.start();
1192
+ await transferCall(context, "images.upload", signedUploadRequest(ticket, source, progress, options), voidResponseSchema);
1193
+ progress.complete();
1194
+ return ticket;
1195
+ },
1196
+ async uploadSource(notebookId, rawSource, rawMetadata, rawOptions) {
1197
+ const notebook = requiredString$2(notebookId, "notebookId");
1198
+ const source = binarySource(rawSource);
1199
+ const metadata = uploadMetadata(rawMetadata);
1200
+ const options = transferOptions(rawOptions);
1201
+ if (source.size > 524288e3) throw new QMindError("FILE_TOO_LARGE", "file exceeds the 500 MiB upload limit", {
1202
+ access: context.profile.access,
1203
+ details: {
1204
+ limitBytes: QMIND_SOURCE_UPLOAD_MAX_BYTES,
1205
+ size: source.size
1206
+ },
1207
+ operation: "transfers.source.multipart"
1208
+ });
1209
+ if (source.size <= 52428800) {
1210
+ const progress = progressReporter(context, "transfers.source.multipart", "multipart", source.size, options);
1211
+ progress.start();
1212
+ const fields = compactUndefined({
1213
+ parent_source_id: metadata.parentSourceId,
1214
+ path: metadata.path,
1215
+ retain_original_file: "true",
1216
+ source_type: metadata.sourceType,
1217
+ title: metadata.title
1218
+ });
1219
+ const result = await transferCall(context, "transfers.source.multipart", hostRequest(queryPath(context.profile.apiPath("notebooks", notebook, "sources", "upload"), [["client", context.client]]), "POST", false, options, {
1220
+ file: source,
1221
+ fields,
1222
+ kind: "multipart",
1223
+ onProgress: progress.update
1224
+ }), sourceSchema);
1225
+ progress.complete();
1226
+ return result;
1227
+ }
1228
+ const ticket = await transferCall(context, "transfers.source.presign", hostRequest(context.profile.apiPath("notebooks", notebook, "sources", "presign"), "POST", false, options, {
1229
+ kind: "json",
1230
+ value: {
1231
+ filename: source.filename,
1232
+ mimeType: source.mimeType,
1233
+ size: source.size
1234
+ }
1235
+ }), sourceUploadTicketSchema);
1236
+ const progress = progressReporter(context, "transfers.source.upload", "presigned", source.size, options);
1237
+ progress.start();
1238
+ await transferCall(context, "transfers.source.upload", signedUploadRequest(ticket, source, progress, options), voidResponseSchema);
1239
+ progress.complete();
1240
+ progress.registering();
1241
+ return transferCall(context, "transfers.source.register", hostRequest(context.profile.apiPath("notebooks", notebook, "sources", "register"), "POST", false, options, {
1242
+ kind: "json",
1243
+ value: compactUndefined({
1244
+ filename: source.filename,
1245
+ mimeType: source.mimeType,
1246
+ parentSourceId: metadata.parentSourceId,
1247
+ path: metadata.path,
1248
+ sha256: metadata.sha256,
1249
+ size: source.size,
1250
+ sourceType: metadata.sourceType,
1251
+ title: metadata.title,
1252
+ uri: ticket.uri
1253
+ })
1254
+ }), sourceSchema);
1255
+ }
1256
+ };
1257
+ }
1258
+ //#endregion
1259
+ //#region ../sdk/src/capabilities.ts
1260
+ const dashboardCapabilities = Object.freeze({
1261
+ botGateway: false,
1262
+ "cards.read": true,
1263
+ "cards.write": true,
1264
+ chat: true,
1265
+ compilation: true,
1266
+ "featuredNotebooks.read": true,
1267
+ "images.presignUpload": false,
1268
+ "images.presignUrls": false,
1269
+ "imports.aliDing": true,
1270
+ "imports.discover": true,
1271
+ members: true,
1272
+ "notebooks.filterByClient": false,
1273
+ "notebooks.read": true,
1274
+ "notebooks.write": true,
1275
+ "notes.read": false,
1276
+ "notes.write": false,
1277
+ retrieval: true,
1278
+ scheduledTasks: true,
1279
+ "sharedNotebooks.read": true,
1280
+ "sources.children": true,
1281
+ "sources.childrenCount": true,
1282
+ "sources.actionAccess": true,
1283
+ "sources.importWeb": true,
1284
+ "sources.preview": true,
1285
+ "sources.rawContent": true,
1286
+ "sources.read": true,
1287
+ "sources.search": true,
1288
+ "sources.tree": true,
1289
+ "sources.webOffice": true,
1290
+ "sources.write": true,
1291
+ "taskRuns.read": true,
1292
+ "taskRuns.write": true,
1293
+ transfers: true,
1294
+ "wikiBranches.read": true
1295
+ });
1296
+ const sashCapabilities = Object.freeze({
1297
+ botGateway: false,
1298
+ "cards.read": true,
1299
+ "cards.write": true,
1300
+ chat: true,
1301
+ compilation: false,
1302
+ "featuredNotebooks.read": false,
1303
+ "images.presignUpload": true,
1304
+ "images.presignUrls": true,
1305
+ "imports.aliDing": false,
1306
+ "imports.discover": false,
1307
+ members: false,
1308
+ "notebooks.filterByClient": true,
1309
+ "notebooks.read": true,
1310
+ "notebooks.write": true,
1311
+ "notes.read": false,
1312
+ "notes.write": false,
1313
+ retrieval: true,
1314
+ scheduledTasks: false,
1315
+ "sharedNotebooks.read": false,
1316
+ "sources.children": true,
1317
+ "sources.childrenCount": false,
1318
+ "sources.actionAccess": true,
1319
+ "sources.importWeb": true,
1320
+ "sources.preview": true,
1321
+ "sources.rawContent": true,
1322
+ "sources.read": true,
1323
+ "sources.search": false,
1324
+ "sources.tree": true,
1325
+ "sources.webOffice": true,
1326
+ "sources.write": true,
1327
+ "taskRuns.read": true,
1328
+ "taskRuns.write": true,
1329
+ transfers: true,
1330
+ "wikiBranches.read": true
1331
+ });
1332
+ function capabilitiesFor(access) {
1333
+ return access === "sash" ? sashCapabilities : dashboardCapabilities;
1334
+ }
1335
+ //#endregion
1336
+ //#region ../sdk/src/profiles.ts
1337
+ function isRecord$1(value) {
1338
+ return typeof value === "object" && value !== null && !Array.isArray(value);
1339
+ }
1340
+ function encodeId(value) {
1341
+ return encodeURIComponent(value);
1342
+ }
1343
+ function appendQuery(path, entries) {
1344
+ const query = new URLSearchParams();
1345
+ for (const [key, value] of entries) if (value !== void 0 && value !== "") query.set(key, String(value));
1346
+ const suffix = query.toString();
1347
+ return suffix ? `${path}?${suffix}` : path;
1348
+ }
1349
+ function dashboardEnvelope(body) {
1350
+ if (!isRecord$1(body) || typeof body.status !== "number" || !("data" in body)) return body;
1351
+ return body.data;
1352
+ }
1353
+ function firstResultSource(access, operation, body) {
1354
+ if (!isRecord$1(body) || !Array.isArray(body.results)) return body;
1355
+ const first = body.results[0];
1356
+ if (!isRecord$1(first)) return void 0;
1357
+ if ("source" in first) return first.source;
1358
+ if (typeof first.error === "string" && first.error.length > 0) throw new QMindError("REMOTE_ERROR", first.error, {
1359
+ access,
1360
+ details: first,
1361
+ operation,
1362
+ status: 200
1363
+ });
1364
+ }
1365
+ function dashboardBatchMutation(body) {
1366
+ if (!isRecord$1(body)) return body;
1367
+ const successCount = typeof body.successCount === "number" ? body.successCount : body.success_count;
1368
+ const failureCount = typeof body.failureCount === "number" ? body.failureCount : body.failure_count;
1369
+ if (typeof successCount !== "number" && typeof failureCount !== "number") return body;
1370
+ return {
1371
+ ...body,
1372
+ failureCount: typeof failureCount === "number" ? failureCount : 0,
1373
+ successCount: typeof successCount === "number" ? successCount : 0
1374
+ };
1375
+ }
1376
+ function normalizeSuccess(access, operation, body) {
1377
+ const unwrapped = access === "dashboard" ? dashboardEnvelope(body) : body;
1378
+ if (access !== "dashboard") return unwrapped;
1379
+ if (operation === "cards.get" && isRecord$1(unwrapped) && "card" in unwrapped) return unwrapped.card;
1380
+ if (operation === "cards.delete" || operation === "cards.batchDelete") return dashboardBatchMutation(unwrapped);
1381
+ if (operation === "sources.create" || operation === "sources.importSite") return firstResultSource(access, operation, unwrapped);
1382
+ if (operation === "transfers.source.multipart" || operation === "transfers.source.register") return firstResultSource(access, operation, unwrapped);
1383
+ return unwrapped;
1384
+ }
1385
+ function streamError(access, message, details) {
1386
+ return new QMindError("STREAM_ERROR", message, {
1387
+ access,
1388
+ ...details === void 0 ? {} : { details },
1389
+ operation: "chat.stream"
1390
+ });
1391
+ }
1392
+ function requiredString$1(access, value, field) {
1393
+ if (value !== void 0 && value.length > 0) return value;
1394
+ throw streamError(access, `stream event is missing ${field}`, { field });
1395
+ }
1396
+ function stringValue$1(value) {
1397
+ return value ?? "";
1398
+ }
1399
+ function presentString(access, value, field) {
1400
+ if (value !== void 0) return value;
1401
+ throw streamError(access, `stream event is missing ${field}`, { field });
1402
+ }
1403
+ function decodeStreamEvent(access, rawBody) {
1404
+ const candidate = access === "dashboard" ? dashboardEnvelope(rawBody) : rawBody;
1405
+ const parsed = streamWireEventSchema.safeParse(candidate);
1406
+ if (!parsed.success) throw streamError(access, "stream event does not match the QMind contract", { issues: parsed.error.issues });
1407
+ const event = parsed.data;
1408
+ const eventType = event.eventType ?? event.event_type ?? event.type;
1409
+ switch (eventType) {
1410
+ case "message_start": return {
1411
+ citations: event.citations ?? [],
1412
+ reranked: event.reranked ?? false,
1413
+ totalContextChunks: event.totalContextChunks ?? 0,
1414
+ type: "messageStart",
1415
+ ...event.assistantMessageId === void 0 ? {} : { assistantMessageId: event.assistantMessageId },
1416
+ ...event.userMessage === void 0 ? {} : { userMessage: event.userMessage }
1417
+ };
1418
+ case "tool_call_start": return {
1419
+ step: event.step ?? 0,
1420
+ toolArguments: stringValue$1(event.toolArguments),
1421
+ toolName: requiredString$1(access, event.toolName, "toolName"),
1422
+ type: "toolCallStart",
1423
+ ...event.toolDisplayName === void 0 ? {} : { toolDisplayName: event.toolDisplayName }
1424
+ };
1425
+ case "tool_call_result": return {
1426
+ step: event.step ?? 0,
1427
+ toolName: requiredString$1(access, event.toolName, "toolName"),
1428
+ toolResult: stringValue$1(event.toolResult),
1429
+ type: "toolCallResult",
1430
+ ...event.toolDisplayName === void 0 ? {} : { toolDisplayName: event.toolDisplayName }
1431
+ };
1432
+ case "delta": return {
1433
+ delta: presentString(access, event.delta, "delta"),
1434
+ type: "delta"
1435
+ };
1436
+ case "batch_progress": return {
1437
+ metadata: event.metadata ?? {},
1438
+ type: "batchProgress"
1439
+ };
1440
+ case "heartbeat": return {
1441
+ metadata: event.metadata ?? {},
1442
+ type: "heartbeat"
1443
+ };
1444
+ case "cancel": return {
1445
+ canceled: true,
1446
+ type: "cancel",
1447
+ ...event.cancelReason === void 0 ? {} : { cancelReason: event.cancelReason }
1448
+ };
1449
+ case "message_complete": return {
1450
+ canceled: event.canceled ?? false,
1451
+ citations: event.citations ?? [],
1452
+ metadata: event.metadata ?? {},
1453
+ promptContext: event.promptContext ?? "",
1454
+ reranked: event.reranked ?? false,
1455
+ totalContextChunks: event.totalContextChunks ?? 0,
1456
+ type: "messageComplete",
1457
+ ...event.assistantMessage === void 0 ? {} : { assistantMessage: event.assistantMessage },
1458
+ ...event.cancelReason === void 0 ? {} : { cancelReason: event.cancelReason }
1459
+ };
1460
+ case "error": return {
1461
+ message: requiredString$1(access, event.errorMessage ?? event.error, "errorMessage"),
1462
+ type: "error"
1463
+ };
1464
+ default: throw streamError(access, "stream event has an unsupported event type", { eventType });
1465
+ }
1466
+ }
1467
+ function decodeStreamPayload(access, payload) {
1468
+ let value;
1469
+ try {
1470
+ value = JSON.parse(payload);
1471
+ } catch (cause) {
1472
+ throw streamError(access, "stream event is not valid JSON", { cause });
1473
+ }
1474
+ return decodeStreamEvent(access, value);
1475
+ }
1476
+ function createProfile(access, apiPrefix) {
1477
+ const capabilities = capabilitiesFor(access);
1478
+ const apiPath = (...segments) => {
1479
+ return `${apiPrefix}/${segments.map((segment) => encodeId(segment)).join("/")}`;
1480
+ };
1481
+ return Object.freeze({
1482
+ access,
1483
+ capabilities,
1484
+ apiPath,
1485
+ decodeStreamPayload(payload) {
1486
+ return decodeStreamPayload(access, payload);
1487
+ },
1488
+ featuredNotebookPath(notebookId) {
1489
+ return notebookId === void 0 ? apiPath("featured-notebooks") : apiPath("featured-notebooks", notebookId);
1490
+ },
1491
+ listFeaturedNotebooksPath(input) {
1492
+ return appendQuery(`${apiPrefix}/featured-notebooks`, [["page", input.page], ["page_size", input.pageSize]]);
1493
+ },
1494
+ listNotebooksPath(input) {
1495
+ return appendQuery(`${apiPrefix}/notebooks`, [
1496
+ ["page", input.page],
1497
+ ["page_size", input.pageSize],
1498
+ ["client", input.client]
1499
+ ]);
1500
+ },
1501
+ listSharedNotebooksPath(input) {
1502
+ return appendQuery(`${apiPrefix}/notebooks/shared`, [["page", input.page], ["page_size", input.pageSize]]);
1503
+ },
1504
+ mindPath(...segments) {
1505
+ return `/api/v1/mind/${segments.map((segment) => encodeId(segment)).join("/")}`;
1506
+ },
1507
+ notebookPath(notebookId) {
1508
+ return notebookId === void 0 ? apiPath("notebooks") : apiPath("notebooks", notebookId);
1509
+ },
1510
+ unwrapError(body) {
1511
+ return access === "dashboard" ? dashboardEnvelope(body) : body;
1512
+ },
1513
+ unwrapSuccess(operation, body) {
1514
+ return normalizeSuccess(access, operation, body);
1515
+ },
1516
+ wikiBranchesPath(notebookId) {
1517
+ return apiPath("notebooks", notebookId, "wiki-branches");
1518
+ }
1519
+ });
1520
+ }
1521
+ const dashboardProfile = createProfile("dashboard", "/api/v1");
1522
+ const sashProfile = createProfile("sash", "/sash/api/v1");
1523
+ function profileFor(access) {
1524
+ return access === "sash" ? sashProfile : dashboardProfile;
1525
+ }
1526
+ //#endregion
1527
+ //#region ../sdk/src/client.ts
1528
+ function isRecord(value) {
1529
+ return typeof value === "object" && value !== null && !Array.isArray(value);
1530
+ }
1531
+ function isQMindAccess(value) {
1532
+ return value === "sash" || value === "dashboard";
1533
+ }
1534
+ function isQMindTransport(value) {
1535
+ if (!isRecord(value)) return false;
1536
+ return typeof value.request === "function" && typeof value.stream === "function";
1537
+ }
1538
+ const managementOperationNames = [
1539
+ "addMember",
1540
+ "createScheduledTask",
1541
+ "deleteScheduledTask",
1542
+ "getCompilationSettings",
1543
+ "getCompilationStatus",
1544
+ "listMembers",
1545
+ "listScheduledTasks",
1546
+ "removeMember",
1547
+ "saveCompilationSettings",
1548
+ "startCompilation",
1549
+ "updateMemberPermission",
1550
+ "updateScheduledTask"
1551
+ ];
1552
+ const cardOperationNames = [
1553
+ "batchDeleteCards",
1554
+ "deleteCard",
1555
+ "getCard",
1556
+ "listCards",
1557
+ "listFeaturedCards"
1558
+ ];
1559
+ const importOperationNames = [
1560
+ "getAliDingImportRun",
1561
+ "listAliDingImportRunItems",
1562
+ "listImportProviders",
1563
+ "startAliDingKnowledgeBaseImport",
1564
+ "startAliDingNodeSubtreeImport"
1565
+ ];
1566
+ function createLazyCardOperations(context) {
1567
+ let operations;
1568
+ const load = () => {
1569
+ operations ??= import("./cards-2J6ebaI0.js").then(({ createCardOperations }) => createCardOperations(context));
1570
+ return operations;
1571
+ };
1572
+ return Object.fromEntries(cardOperationNames.map((name) => [name, (...args) => load().then((loaded) => Reflect.apply(loaded[name], loaded, args))]));
1573
+ }
1574
+ function createLazyImportOperations(context) {
1575
+ let operations;
1576
+ const load = () => {
1577
+ operations ??= import("./imports-Da88W09Q.js").then(({ createImportOperations }) => createImportOperations(context));
1578
+ return operations;
1579
+ };
1580
+ return Object.fromEntries(importOperationNames.map((name) => [name, (...args) => load().then((loaded) => Reflect.apply(loaded[name], loaded, args))]));
1581
+ }
1582
+ function createLazyManagementOperations(context) {
1583
+ let operations;
1584
+ const load = () => {
1585
+ operations ??= import("./management-xYuUDFtg.js").then(({ createManagementOperations }) => createManagementOperations(context));
1586
+ return operations;
1587
+ };
1588
+ return Object.fromEntries(managementOperationNames.map((name) => [name, (...args) => load().then((loaded) => Reflect.apply(loaded[name], loaded, args))]));
1589
+ }
1590
+ function createQMindClient(options) {
1591
+ if (!isRecord(options)) throw invalidArgument$1("client options must be an object", "options");
1592
+ if (!isQMindAccess(options.access)) throw invalidArgument$1("access must be either \"sash\" or \"dashboard\"", "access");
1593
+ if (!isQMindTransport(options.transport)) throw invalidArgument$1("transport must implement request() and stream()", "transport");
1594
+ const profile = profileFor(options.access);
1595
+ const context = {
1596
+ client: optionalTrimmedString(options.client, "client") ?? "qmind",
1597
+ profile,
1598
+ transport: options.transport
1599
+ };
1600
+ return Object.freeze({
1601
+ access: profile.access,
1602
+ capabilities: profile.capabilities,
1603
+ ...createNotebookOperations(context),
1604
+ ...createLazyCardOperations(context),
1605
+ ...createSourceOperations(context),
1606
+ ...createLazyImportOperations(context),
1607
+ ...createLazyManagementOperations(context),
1608
+ ...createTransferOperations(context),
1609
+ ...createAgentOperations(context),
1610
+ supports(capability) {
1611
+ return profile.capabilities[capability] === true;
1612
+ }
1613
+ });
1614
+ }
1615
+ //#endregion
1616
+ //#region ../node-host/src/auth.ts
1617
+ const responseRecordSchema = z.record(z.string(), z.unknown());
1618
+ const personalTokenExchangeSchema = z.object({ token: z.string().min(1) });
1619
+ const devicePollSchema = z.object({
1620
+ expires_at: z.string().default(""),
1621
+ refresh_token: z.string().default(""),
1622
+ refresh_token_expires_at: z.string().default(""),
1623
+ token: z.string().min(1)
1624
+ });
1625
+ const refreshSchema = z.object({
1626
+ device_token: z.string().min(1),
1627
+ expires_at: z.string().default(""),
1628
+ refresh_token: z.string().optional(),
1629
+ refresh_token_expires_at: z.string().optional()
1630
+ });
1631
+ const POLL_INTERVAL_MS = 2e3;
1632
+ const LOGIN_TIMEOUT_MS = 3e5;
1633
+ const EXPIRY_SKEW_MS = 3e4;
1634
+ function base64Url(bytes) {
1635
+ return Buffer.from(bytes).toString("base64url");
1636
+ }
1637
+ function createPkce() {
1638
+ const verifier = base64Url(randomBytes(32));
1639
+ return {
1640
+ challenge: createHash("sha256").update(verifier).digest("base64url"),
1641
+ nonce: base64Url(randomBytes(16)),
1642
+ verifier
1643
+ };
1644
+ }
1645
+ function errorCodeForStatus(status) {
1646
+ if (status === 401 || status === 403) return "AUTH_REQUIRED";
1647
+ if (status === 404) return "NOT_FOUND";
1648
+ if (status === 409) return "CONFLICT";
1649
+ if (status === 429) return "RATE_LIMITED";
1650
+ if (status >= 400 && status < 500) return "INVALID_ARGUMENT";
1651
+ return "REMOTE_ERROR";
1652
+ }
1653
+ function record$1(value) {
1654
+ return typeof value === "object" && value !== null && !Array.isArray(value) ? value : void 0;
1655
+ }
1656
+ function responseString(response, ...keys) {
1657
+ const body = record$1(response.body);
1658
+ const nestedError = record$1(body?.error);
1659
+ for (const source of [body, nestedError]) for (const key of keys) {
1660
+ const value = source?.[key];
1661
+ if (typeof value === "string" && value.length > 0) return value;
1662
+ }
1663
+ }
1664
+ function header(response, name) {
1665
+ const normalized = name.toLowerCase();
1666
+ for (const [key, value] of Object.entries(response.headers)) if (key.toLowerCase() === normalized && value.length > 0) return value;
1667
+ }
1668
+ function authResponseError(response, operation) {
1669
+ const requestId = responseString(response, "requestId", "request_id") ?? header(response, "x-request-id");
1670
+ const upstreamCode = responseString(response, "errorCode", "error_code", "code");
1671
+ return new QMindError(errorCodeForStatus(response.status), `${operation} failed`, {
1672
+ details: { operation },
1673
+ ...requestId === void 0 ? {} : { requestId },
1674
+ status: response.status,
1675
+ ...upstreamCode === void 0 ? {} : { upstreamCode }
1676
+ });
1677
+ }
1678
+ async function authRequest(transport, method, path, operation, body) {
1679
+ const request = {
1680
+ ...body === void 0 ? {} : { body: {
1681
+ kind: "json",
1682
+ value: body
1683
+ } },
1684
+ destination: {
1685
+ kind: "host",
1686
+ path
1687
+ },
1688
+ idempotent: method === "GET",
1689
+ method
1690
+ };
1691
+ let response;
1692
+ try {
1693
+ response = await transport.request(request);
1694
+ } catch (cause) {
1695
+ if (cause instanceof QMindError) throw cause;
1696
+ throw new QMindError("NETWORK_ERROR", `${operation} failed`);
1697
+ }
1698
+ if (response.status < 200 || response.status >= 300) throw authResponseError(response, operation);
1699
+ const parsed = responseRecordSchema.safeParse(response.body);
1700
+ if (!parsed.success) throw new QMindError("PROTOCOL_ERROR", `${operation} returned an invalid response`, {
1701
+ details: { operation },
1702
+ status: response.status
1703
+ });
1704
+ return parsed.data;
1705
+ }
1706
+ function credentialsFromLogin(value, sashOrigin) {
1707
+ return {
1708
+ deviceToken: value.token,
1709
+ expiresAt: value.expires_at,
1710
+ refreshToken: value.refresh_token,
1711
+ refreshTokenExpiresAt: value.refresh_token_expires_at,
1712
+ sashOrigin,
1713
+ savedAt: "",
1714
+ schemaVersion: 2
1715
+ };
1716
+ }
1717
+ function isExpired(timestamp, now, skew = 0) {
1718
+ if (timestamp.length === 0) return false;
1719
+ const value = Date.parse(timestamp);
1720
+ return Number.isFinite(value) && value <= now + skew;
1721
+ }
1722
+ function aborted(signal) {
1723
+ return signal?.aborted === true ? new QMindError("ABORTED", "QMind authorization was aborted") : void 0;
1724
+ }
1725
+ function createSystemClock() {
1726
+ return Object.freeze({
1727
+ now: Date.now,
1728
+ sleep(milliseconds, signal) {
1729
+ return new Promise((resolve, reject) => {
1730
+ const alreadyAborted = aborted(signal);
1731
+ if (alreadyAborted !== void 0) {
1732
+ reject(alreadyAborted);
1733
+ return;
1734
+ }
1735
+ const onAbort = () => {
1736
+ clearTimeout(timer);
1737
+ reject(new QMindError("ABORTED", "QMind authorization was aborted"));
1738
+ };
1739
+ const timer = setTimeout(() => {
1740
+ signal?.removeEventListener("abort", onAbort);
1741
+ resolve();
1742
+ }, milliseconds);
1743
+ signal?.addEventListener("abort", onAbort, { once: true });
1744
+ });
1745
+ }
1746
+ });
1747
+ }
1748
+ function createQMindCliAuthManager(options) {
1749
+ let credentials = options.credentials;
1750
+ let explicitToken = options.explicitToken;
1751
+ let token = "";
1752
+ let refreshPromise;
1753
+ const login = async (loginOptions = {}) => {
1754
+ if (options.config.nonInteractive) throw new QMindError("AUTH_REQUIRED", "login requires an interactive terminal");
1755
+ const pkce = createPkce();
1756
+ const browserUrl = new URL("/device/selectAccounts", options.config.dashboardOrigin);
1757
+ browserUrl.searchParams.set("nonce", pkce.nonce);
1758
+ browserUrl.searchParams.set("challenge", pkce.challenge);
1759
+ browserUrl.searchParams.set("challenge_method", "S256");
1760
+ browserUrl.searchParams.set("machine_id", pkce.nonce);
1761
+ if (loginOptions.clientId !== void 0 && loginOptions.clientId.trim().length > 0) browserUrl.searchParams.set("client_id", loginOptions.clientId.trim());
1762
+ options.reporter.info(`Opening browser for authorization. If it does not open, visit:\n${browserUrl.toString()}`);
1763
+ if (loginOptions.openBrowser !== false) try {
1764
+ await options.browser.open(browserUrl.toString());
1765
+ } catch {
1766
+ options.reporter.warn("warning: unable to open a browser; use the authorization URL above");
1767
+ }
1768
+ options.reporter.info("Waiting for authorization (timeout: 5m)...");
1769
+ const deadline = options.clock.now() + LOGIN_TIMEOUT_MS;
1770
+ while (options.clock.now() < deadline) {
1771
+ const abortError = aborted(loginOptions.signal);
1772
+ if (abortError !== void 0) throw abortError;
1773
+ await options.clock.sleep(POLL_INTERVAL_MS, loginOptions.signal);
1774
+ const pollPath = `/api/v1/deviceToken/poll?${new URLSearchParams({
1775
+ challenge_method: "S256",
1776
+ nonce: pkce.nonce,
1777
+ verifier: pkce.verifier
1778
+ }).toString()}`;
1779
+ try {
1780
+ const raw = await authRequest(options.transport, "GET", pollPath, "device authorization polling");
1781
+ const parsed = devicePollSchema.safeParse(raw);
1782
+ if (!parsed.success) throw new QMindError("PROTOCOL_ERROR", "device authorization returned an invalid response");
1783
+ credentials = credentialsFromLogin(parsed.data, options.config.sashOrigin);
1784
+ await options.vault.save(credentials);
1785
+ explicitToken = "";
1786
+ token = credentials.deviceToken;
1787
+ return {
1788
+ expiresAt: credentials.expiresAt,
1789
+ sashOrigin: credentials.sashOrigin
1790
+ };
1791
+ } catch (error) {
1792
+ if (error instanceof QMindError && error.status === 404) continue;
1793
+ throw error;
1794
+ }
1795
+ }
1796
+ throw new QMindError("AUTH_REQUIRED", "authorization timed out after 5m");
1797
+ };
1798
+ const refreshInternal = async (reason) => {
1799
+ if (credentials === void 0 || credentials.refreshToken.length === 0) {
1800
+ token = "";
1801
+ throw new QMindError("AUTH_REQUIRED", `credentials cannot be refreshed after ${reason}`);
1802
+ }
1803
+ if (isExpired(credentials.refreshTokenExpiresAt, options.clock.now())) {
1804
+ await options.vault.deleteIf(credentials);
1805
+ credentials = void 0;
1806
+ token = "";
1807
+ throw new QMindError("AUTH_REQUIRED", "stored refresh credentials have expired");
1808
+ }
1809
+ const raw = await authRequest(options.transport, "POST", "/api/v1/deviceToken/refresh", "credential refresh", { refresh_token: credentials.refreshToken });
1810
+ const parsed = refreshSchema.safeParse(raw);
1811
+ if (!parsed.success) throw new QMindError("PROTOCOL_ERROR", "credential refresh returned an invalid response");
1812
+ const basis = credentials;
1813
+ const legacy = basis.schemaVersion !== 2;
1814
+ const refreshed = {
1815
+ ...basis,
1816
+ deviceToken: parsed.data.device_token,
1817
+ expiresAt: parsed.data.expires_at,
1818
+ refreshToken: parsed.data.refresh_token ?? basis.refreshToken,
1819
+ refreshTokenExpiresAt: parsed.data.refresh_token_expires_at ?? basis.refreshTokenExpiresAt,
1820
+ sashOrigin: options.config.sashOrigin,
1821
+ schemaVersion: 2
1822
+ };
1823
+ const current = await options.vault.compareAndSwap(basis, refreshed, { migrateLegacy: legacy }) ? refreshed : await options.vault.load();
1824
+ if (current === void 0) {
1825
+ credentials = void 0;
1826
+ token = "";
1827
+ throw new QMindError("AUTH_REQUIRED", "credentials changed during refresh");
1828
+ }
1829
+ credentials = current;
1830
+ token = current.deviceToken;
1831
+ return token;
1832
+ };
1833
+ const manager = {
1834
+ async getToken() {
1835
+ if (token.length > 0) return token;
1836
+ const candidate = explicitToken || credentials?.deviceToken || "";
1837
+ if (candidate.length === 0) {
1838
+ if (options.config.nonInteractive) throw new QMindError("AUTH_REQUIRED", `no credentials found; run '${options.loginCommand ?? "qmind login"}' in an interactive terminal`);
1839
+ await login();
1840
+ return token;
1841
+ }
1842
+ if (candidate.startsWith("pt-")) {
1843
+ const raw = await authRequest(options.transport, "POST", "/api/v1/jobToken/exchange", "personal token exchange", { personal_token: candidate });
1844
+ const parsed = personalTokenExchangeSchema.safeParse(raw);
1845
+ if (!parsed.success) throw new QMindError("PROTOCOL_ERROR", "personal token exchange returned an invalid response");
1846
+ token = parsed.data.token;
1847
+ return token;
1848
+ }
1849
+ if (credentials !== void 0 && candidate === credentials.deviceToken && isExpired(credentials.expiresAt, options.clock.now(), EXPIRY_SKEW_MS)) return manager.refreshToken("expired", candidate);
1850
+ token = candidate;
1851
+ return token;
1852
+ },
1853
+ login,
1854
+ async logout() {
1855
+ await options.vault.delete();
1856
+ credentials = void 0;
1857
+ explicitToken = "";
1858
+ token = "";
1859
+ refreshPromise = void 0;
1860
+ },
1861
+ async refreshToken(reason, rejectedToken) {
1862
+ if (rejectedToken !== void 0 && token.length > 0 && token !== rejectedToken) return token;
1863
+ refreshPromise ??= refreshInternal(reason).finally(() => {
1864
+ refreshPromise = void 0;
1865
+ });
1866
+ return await refreshPromise;
1867
+ }
1868
+ };
1869
+ return Object.freeze(manager);
1870
+ }
1871
+ //#endregion
1872
+ //#region ../node-host/src/authenticated-transport.ts
1873
+ function withBearer(request, token) {
1874
+ const headers = Object.fromEntries(Object.entries(request.headers ?? {}).filter(([name]) => name.toLowerCase() !== "authorization"));
1875
+ return {
1876
+ ...request,
1877
+ headers: {
1878
+ ...headers,
1879
+ authorization: `Bearer ${token}`
1880
+ }
1881
+ };
1882
+ }
1883
+ /** A caller-supplied per-request bearer wins over process-level auth and skips refresh. */
1884
+ function hasRequestAuthorization(request) {
1885
+ const headers = request.headers;
1886
+ if (headers === void 0) return false;
1887
+ return Object.keys(headers).some((name) => name.toLowerCase() === "authorization");
1888
+ }
1889
+ function hostCredentialRequired() {
1890
+ return new QMindError("AUTH_REQUIRED", "The embedding host did not provide a credential for this request.");
1891
+ }
1892
+ function createAuthenticatedQMindTransport(transport, auth, requestCredential) {
1893
+ return Object.freeze({
1894
+ async request(request) {
1895
+ if (request.destination.kind === "signed") return await transport.request(request);
1896
+ const providedToken = (await requestCredential?.())?.trim();
1897
+ if (providedToken) return await transport.request(withBearer(request, providedToken));
1898
+ if (hasRequestAuthorization(request)) return await transport.request(request);
1899
+ if (requestCredential !== void 0) throw hostCredentialRequired();
1900
+ const token = await auth.getToken();
1901
+ const response = await transport.request(withBearer(request, token));
1902
+ if (response.status !== 401) return response;
1903
+ const refreshed = await auth.refreshToken("unauthorized", token);
1904
+ return await transport.request(withBearer(request, refreshed));
1905
+ },
1906
+ async stream(request) {
1907
+ const providedToken = (await requestCredential?.())?.trim();
1908
+ if (providedToken) return await transport.stream(withBearer(request, providedToken));
1909
+ if (hasRequestAuthorization(request)) return await transport.stream(request);
1910
+ if (requestCredential !== void 0) throw hostCredentialRequired();
1911
+ const token = await auth.getToken();
1912
+ const response = await transport.stream(withBearer(request, token));
1913
+ if (response.kind !== "response" || response.status !== 401) return response;
1914
+ const refreshed = await auth.refreshToken("unauthorized", token);
1915
+ return await transport.stream(withBearer(request, refreshed));
1916
+ }
1917
+ });
1918
+ }
1919
+ //#endregion
1920
+ //#region ../node-host/src/config.ts
1921
+ const fileConfigSchema = z.object({
1922
+ client: z.string().optional(),
1923
+ dashboard_url: z.string().optional(),
1924
+ proxy_from_env: z.boolean().optional(),
1925
+ request_timeout_ms: z.number().int().positive().optional(),
1926
+ sash_url: z.string().optional(),
1927
+ stream_idle_timeout_ms: z.number().int().positive().optional(),
1928
+ tls_ca_file: z.string().optional(),
1929
+ tls_reject_unauthorized: z.boolean().optional(),
1930
+ update_public_key: z.string().optional()
1931
+ }).passthrough();
1932
+ const PRESETS = Object.freeze({
1933
+ daily: {
1934
+ dashboardOrigin: "https://daily.qoder.ai",
1935
+ sashOrigin: "https://daily-openapi.qoder.sh"
1936
+ },
1937
+ prod: {
1938
+ dashboardOrigin: "https://qoder.com",
1939
+ sashOrigin: "https://openapi.qoder.sh"
1940
+ },
1941
+ test: {
1942
+ dashboardOrigin: "https://test.qoder.ai",
1943
+ sashOrigin: "https://test-openapi.qoder.sh"
1944
+ }
1945
+ });
1946
+ /**
1947
+ * Embedding product identity → that product's backends per environment. Two products share neither
1948
+ * dashboard nor Sash, so a host that declares its identity must be routed by it; falling back to the
1949
+ * environment table alone would send the second product's requests to the first product's backend,
1950
+ * which is indistinguishable from a correct call on the wire.
1951
+ *
1952
+ * Cells that no backend exists for yet are absent rather than borrowed: resolving one throws.
1953
+ */
1954
+ const PRODUCT_PRESETS = Object.freeze({
1955
+ qoder: PRESETS,
1956
+ "qoder-cn": Object.freeze({
1957
+ prod: {
1958
+ dashboardOrigin: "https://qoder.cn",
1959
+ sashOrigin: "https://openapi.qoder.com.cn"
1960
+ },
1961
+ test: {
1962
+ dashboardOrigin: "https://test.qoder.com.cn",
1963
+ sashOrigin: "https://test-openapi.qoder.com.cn"
1964
+ }
1965
+ })
1966
+ });
1967
+ function invalidConfig(message, field) {
1968
+ return new QMindError("INVALID_ARGUMENT", message, { ...field === void 0 ? {} : { details: { field } } });
1969
+ }
1970
+ function normalizeQMindOrigin(rawValue, field) {
1971
+ let url;
1972
+ try {
1973
+ url = new URL(rawValue);
1974
+ } catch {
1975
+ throw invalidConfig(`${field} must be a valid HTTP(S) origin`, field);
1976
+ }
1977
+ if (url.protocol !== "http:" && url.protocol !== "https:" || url.username.length > 0 || url.password.length > 0 || url.pathname !== "/" || url.search.length > 0 || url.hash.length > 0) throw invalidConfig(`${field} must contain only an HTTP(S) scheme and authority`, field);
1978
+ return url.origin;
1979
+ }
1980
+ /**
1981
+ * Resolves one registered product/environment without fallback. The default Host config loader
1982
+ * invokes this lazily after explicit overrides; fixed-product callers select the target up front.
1983
+ */
1984
+ function resolveQMindProductEndpoints(productId, presetName, field = "env") {
1985
+ const presets = Object.hasOwn(PRODUCT_PRESETS, productId) ? PRODUCT_PRESETS[productId] : void 0;
1986
+ if (presets === void 0) throw invalidConfig(`no QMind endpoints are registered for product ${JSON.stringify(productId)}`, field);
1987
+ const preset = Object.hasOwn(presets, presetName) ? presets[presetName] : void 0;
1988
+ if (preset === void 0) throw invalidConfig(`product ${JSON.stringify(productId)} has no ${JSON.stringify(presetName)} endpoints; set ${field} explicitly`, field);
1989
+ return { ...preset };
1990
+ }
1991
+ function optionalTrimmed(value) {
1992
+ const trimmed = value?.trim();
1993
+ return trimmed === void 0 || trimmed.length === 0 ? void 0 : trimmed;
1994
+ }
1995
+ function parseBoolean(value, field) {
1996
+ const normalized = optionalTrimmed(value)?.toLowerCase();
1997
+ if (normalized === void 0) return void 0;
1998
+ if (normalized === "1" || normalized === "true") return true;
1999
+ if (normalized === "0" || normalized === "false") return false;
2000
+ throw invalidConfig(`${field} must be 1, 0, true, or false`, field);
2001
+ }
2002
+ function parsePositiveInteger(value, field) {
2003
+ const normalized = optionalTrimmed(value);
2004
+ if (normalized === void 0) return void 0;
2005
+ const parsed = Number(normalized);
2006
+ if (!Number.isSafeInteger(parsed) || parsed <= 0) throw invalidConfig(`${field} must be a positive safe integer`, field);
2007
+ return parsed;
2008
+ }
2009
+ function qmindEnvironmentAssignment(line) {
2010
+ const trimmed = line.trim();
2011
+ if (trimmed.length === 0 || trimmed.startsWith("#")) return void 0;
2012
+ const assignment = trimmed.startsWith("export ") ? trimmed.slice(7) : trimmed;
2013
+ const separator = assignment.indexOf("=");
2014
+ if (separator < 1) return void 0;
2015
+ const key = assignment.slice(0, separator).trim();
2016
+ if (!/^QMIND_[A-Z0-9_]+$/.test(key)) return void 0;
2017
+ let value = assignment.slice(separator + 1).trim();
2018
+ if (value.length >= 2 && (value.startsWith("\"") && value.endsWith("\"") || value.startsWith("'") && value.endsWith("'"))) value = value.slice(1, -1);
2019
+ return [key, value];
2020
+ }
2021
+ async function mergedEnvironment(environment, legacyEnvironmentPath) {
2022
+ const merged = { ...environment };
2023
+ if (legacyEnvironmentPath === false) return merged;
2024
+ let content;
2025
+ try {
2026
+ content = await readFile(legacyEnvironmentPath, "utf8");
2027
+ } catch (error) {
2028
+ if (error.code === "ENOENT") return merged;
2029
+ throw invalidConfig("unable to read the legacy QMind environment file");
2030
+ }
2031
+ for (const line of content.split(/\r?\n/)) {
2032
+ const parsed = qmindEnvironmentAssignment(line);
2033
+ if (parsed === void 0) continue;
2034
+ const [key, value] = parsed;
2035
+ if (merged[key] === void 0) merged[key] = value;
2036
+ }
2037
+ return merged;
2038
+ }
2039
+ async function fileConfiguration(homeDirectory, fixedEndpoints = false) {
2040
+ try {
2041
+ return fileConfigSchema.parse(JSON.parse(await readFile(join(homeDirectory, "config.json"), "utf8")));
2042
+ } catch (error) {
2043
+ if (error.code === "ENOENT") return {};
2044
+ if (fixedEndpoints && error instanceof z.ZodError) {
2045
+ const field = error.issues.find((issue) => issue.path[0] === "sash_url" || issue.path[0] === "dashboard_url")?.path[0];
2046
+ if (field !== void 0) throw invalidEndpointHint(`config.json ${String(field)}`);
2047
+ }
2048
+ throw invalidConfig("stored QMind configuration is invalid");
2049
+ }
2050
+ }
2051
+ function defaultProcessEnvironment() {
2052
+ return process.env;
2053
+ }
2054
+ function invalidEndpointHint(field) {
2055
+ return invalidConfig(`${field} is invalid or conflicts with the selected target; remove ${field} and select the target with --env or paired --sash/--dashboard`, field);
2056
+ }
2057
+ function validateFixedEndpoints(options, environment, file) {
2058
+ const target = options.fixedEndpoints;
2059
+ if (target === void 0) return;
2060
+ const checkOrigin = (raw, expected, field, deprecated = true) => {
2061
+ if (raw === void 0) return;
2062
+ let normalized;
2063
+ try {
2064
+ normalized = normalizeQMindOrigin(raw.trim(), field);
2065
+ } catch {
2066
+ throw invalidEndpointHint(field);
2067
+ }
2068
+ if (normalized !== expected) throw invalidEndpointHint(field);
2069
+ if (deprecated) options.reporter?.warn(`warning: ${field} is deprecated and does not select a target; remove ${field}`);
2070
+ };
2071
+ checkOrigin(environment.QMIND_SASH_URL, target.sashOrigin, "QMIND_SASH_URL");
2072
+ checkOrigin(environment.QMIND_DASHBOARD_URL, target.dashboardOrigin, "QMIND_DASHBOARD_URL");
2073
+ checkOrigin(file.sash_url, target.sashOrigin, "config.json sash_url");
2074
+ checkOrigin(file.dashboard_url, target.dashboardOrigin, "config.json dashboard_url");
2075
+ checkOrigin(options.credentialSashOrigin, target.sashOrigin, "credentials.json sash_url", false);
2076
+ if (environment.QMIND_ENV !== void 0) {
2077
+ let legacy;
2078
+ try {
2079
+ legacy = resolveQMindProductEndpoints(options.productId ?? "", environment.QMIND_ENV.trim());
2080
+ } catch {
2081
+ throw invalidEndpointHint("QMIND_ENV");
2082
+ }
2083
+ if (legacy.sashOrigin !== target.sashOrigin || legacy.dashboardOrigin !== target.dashboardOrigin) throw invalidEndpointHint("QMIND_ENV");
2084
+ options.reporter?.warn("warning: QMIND_ENV is deprecated and does not select a target; remove QMIND_ENV");
2085
+ }
2086
+ }
2087
+ async function loadQMindCliConfig(options = {}) {
2088
+ const initialEnvironment = options.environment ?? defaultProcessEnvironment();
2089
+ const baseHome = options.homeDirectory ?? homedir();
2090
+ const environment = await mergedEnvironment(initialEnvironment, options.legacyEnvironmentPath === void 0 ? join(baseHome, ".qmind-env") : options.legacyEnvironmentPath);
2091
+ const configuredHome = optionalTrimmed(environment.QMIND_HOME);
2092
+ const homeDirectory = resolve(options.homeDirectory ?? configuredHome ?? join(baseHome, ".qmind"));
2093
+ const file = await fileConfiguration(homeDirectory, options.fixedEndpoints !== void 0);
2094
+ validateFixedEndpoints(options, environment, file);
2095
+ const flags = options.flags ?? {};
2096
+ const stdinIsTTY = options.stdinIsTTY ?? process.stdin.isTTY === true;
2097
+ const presetName = optionalTrimmed(environment.QMIND_ENV) ?? "prod";
2098
+ const productId = optionalTrimmed(options.productId);
2099
+ const defaultOrigin = (field) => {
2100
+ const preset = productId === void 0 ? PRESETS[presetName] ?? PRESETS.prod : resolveQMindProductEndpoints(productId, presetName, field);
2101
+ return field === "sash" ? preset.sashOrigin : preset.dashboardOrigin;
2102
+ };
2103
+ if (options.fixedEndpoints === void 0 && productId === void 0 && !(presetName in PRESETS)) options.reporter?.warn(`warning: unknown QMIND_ENV ${JSON.stringify(presetName)}, falling back to prod`);
2104
+ const sashOrigin = normalizeQMindOrigin(options.fixedEndpoints?.sashOrigin ?? optionalTrimmed(flags.sash) ?? optionalTrimmed(environment.QMIND_SASH_URL) ?? optionalTrimmed(file.sash_url) ?? optionalTrimmed(options.credentialSashOrigin) ?? defaultOrigin("sash"), "sash");
2105
+ const dashboardOrigin = normalizeQMindOrigin(options.fixedEndpoints?.dashboardOrigin ?? optionalTrimmed(flags.dashboard) ?? optionalTrimmed(environment.QMIND_DASHBOARD_URL) ?? optionalTrimmed(file.dashboard_url) ?? defaultOrigin("dashboard"), "dashboard");
2106
+ const runtime = Object.freeze({
2107
+ client: optionalTrimmed(flags.client) ?? optionalTrimmed(environment.QMIND_CLIENT) ?? optionalTrimmed(file.client) ?? "qmind",
2108
+ dashboardOrigin,
2109
+ homeDirectory,
2110
+ nonInteractive: flags.nonInteractive === true || parseBoolean(environment.QMIND_NON_INTERACTIVE, "QMIND_NON_INTERACTIVE") === true || !stdinIsTTY,
2111
+ sashOrigin,
2112
+ updatePublicKey: optionalTrimmed(environment.QMIND_UPDATE_PUBLIC_KEY) ?? optionalTrimmed(file.update_public_key) ?? ""
2113
+ });
2114
+ return {
2115
+ debug: parseBoolean(environment.QMIND_DEBUG, "QMIND_DEBUG") ?? false,
2116
+ environment,
2117
+ proxyFromEnvironment: parseBoolean(environment.QMIND_PROXY_FROM_ENV, "QMIND_PROXY_FROM_ENV") ?? file.proxy_from_env ?? true,
2118
+ requestTimeoutMs: parsePositiveInteger(environment.QMIND_REQUEST_TIMEOUT_MS, "QMIND_REQUEST_TIMEOUT_MS") ?? file.request_timeout_ms ?? 3e4,
2119
+ runtime,
2120
+ streamIdleTimeoutMs: parsePositiveInteger(environment.QMIND_STREAM_IDLE_TIMEOUT_MS, "QMIND_STREAM_IDLE_TIMEOUT_MS") ?? file.stream_idle_timeout_ms ?? 12e4,
2121
+ ...(() => {
2122
+ const tlsCaFile = optionalTrimmed(environment.QMIND_TLS_CA_FILE) ?? optionalTrimmed(file.tls_ca_file);
2123
+ return tlsCaFile === void 0 ? {} : { tlsCaFile };
2124
+ })(),
2125
+ tlsRejectUnauthorized: parseBoolean(environment.QMIND_TLS_REJECT_UNAUTHORIZED, "QMIND_TLS_REJECT_UNAUTHORIZED") ?? file.tls_reject_unauthorized ?? true,
2126
+ token: optionalTrimmed(flags.token) ?? optionalTrimmed(environment.QMIND_TOKEN) ?? ""
2127
+ };
2128
+ }
2129
+ //#endregion
2130
+ //#region ../node-host/src/credentials.ts
2131
+ const serializedCredentialsSchema = z.object({
2132
+ device_token: z.string().default(""),
2133
+ expires_at: z.string().default(""),
2134
+ refresh_token: z.string().default(""),
2135
+ refresh_token_expires_at: z.string().default(""),
2136
+ sash_url: z.string().default(""),
2137
+ saved_at: z.string().default(""),
2138
+ schema_version: z.number().int().positive().optional()
2139
+ }).passthrough();
2140
+ function storedCredentialsInvalid() {
2141
+ return new QMindError("AUTH_REQUIRED", "stored QMind credentials are invalid");
2142
+ }
2143
+ function isMissing(error) {
2144
+ return error.code === "ENOENT";
2145
+ }
2146
+ function isValidOptionalTimestamp(value) {
2147
+ return value.length === 0 || Number.isFinite(Date.parse(value));
2148
+ }
2149
+ function normalizeStoredCredentials(value) {
2150
+ let parsed;
2151
+ try {
2152
+ parsed = serializedCredentialsSchema.parse(value);
2153
+ } catch {
2154
+ throw storedCredentialsInvalid();
2155
+ }
2156
+ if (parsed.schema_version !== void 0 && parsed.schema_version > 2 || parsed.device_token.length === 0 && parsed.refresh_token.length === 0 || !isValidOptionalTimestamp(parsed.expires_at) || !isValidOptionalTimestamp(parsed.refresh_token_expires_at)) throw storedCredentialsInvalid();
2157
+ return {
2158
+ deviceToken: parsed.device_token,
2159
+ expiresAt: parsed.expires_at,
2160
+ refreshToken: parsed.refresh_token,
2161
+ refreshTokenExpiresAt: parsed.refresh_token_expires_at,
2162
+ sashOrigin: parsed.sash_url,
2163
+ savedAt: parsed.saved_at,
2164
+ schemaVersion: parsed.schema_version ?? 1
2165
+ };
2166
+ }
2167
+ function serializedCredentials(credentials, now) {
2168
+ return `${JSON.stringify({
2169
+ device_token: credentials.deviceToken,
2170
+ expires_at: credentials.expiresAt,
2171
+ refresh_token: credentials.refreshToken,
2172
+ refresh_token_expires_at: credentials.refreshTokenExpiresAt,
2173
+ sash_url: credentials.sashOrigin,
2174
+ saved_at: new Date(now).toISOString().replace(/\.\d{3}Z$/, "Z"),
2175
+ schema_version: 2
2176
+ }, null, 2)}\n`;
2177
+ }
2178
+ function sameCredentials(left, right) {
2179
+ return left !== void 0 && right !== void 0 && left.deviceToken === right.deviceToken && left.expiresAt === right.expiresAt && left.refreshToken === right.refreshToken && left.refreshTokenExpiresAt === right.refreshTokenExpiresAt && left.sashOrigin === right.sashOrigin && left.schemaVersion === right.schemaVersion;
2180
+ }
2181
+ async function secureDirectory(path) {
2182
+ await mkdir(path, {
2183
+ mode: 448,
2184
+ recursive: true
2185
+ });
2186
+ const info = await lstat(path);
2187
+ if (!info.isDirectory() || info.isSymbolicLink()) throw storedCredentialsInvalid();
2188
+ if (process.platform !== "win32") await chmod(path, 448);
2189
+ }
2190
+ async function secureRegularFile(path) {
2191
+ const info = await lstat(path);
2192
+ if (!info.isFile() || info.isSymbolicLink()) throw storedCredentialsInvalid();
2193
+ if (process.platform !== "win32" && (info.mode & 63) !== 0) await chmod(path, 384);
2194
+ }
2195
+ async function replaceFileAtomically(tempPath, targetPath) {
2196
+ try {
2197
+ await rename(tempPath, targetPath);
2198
+ return;
2199
+ } catch (error) {
2200
+ if (process.platform !== "win32") throw error;
2201
+ }
2202
+ const backupPath = `${targetPath}.${randomBytes(6).toString("hex")}.old`;
2203
+ let backedUp = false;
2204
+ try {
2205
+ try {
2206
+ await rename(targetPath, backupPath);
2207
+ backedUp = true;
2208
+ } catch (error) {
2209
+ if (!isMissing(error)) throw error;
2210
+ }
2211
+ await rename(tempPath, targetPath);
2212
+ if (backedUp) await rm(backupPath, { force: true });
2213
+ } catch (error) {
2214
+ if (backedUp) await rename(backupPath, targetPath).catch(() => void 0);
2215
+ throw error;
2216
+ }
2217
+ }
2218
+ async function optionalSecret(operation, fallback) {
2219
+ try {
2220
+ return await operation();
2221
+ } catch {
2222
+ return fallback;
2223
+ }
2224
+ }
2225
+ function createNoopQMindSecretStore() {
2226
+ return Object.freeze({
2227
+ async delete() {},
2228
+ async get() {},
2229
+ async set(_token) {}
2230
+ });
2231
+ }
2232
+ function createFileCredentialVault(options) {
2233
+ const credentialsPath = join(options.homeDirectory, "credentials.json");
2234
+ const legacyBackupPath = `${credentialsPath}.v1.bak`;
2235
+ const secretStore = options.secretStore ?? createNoopQMindSecretStore();
2236
+ const now = options.now ?? Date.now;
2237
+ const loadUnlocked = async () => {
2238
+ let serialized;
2239
+ try {
2240
+ await secureRegularFile(credentialsPath);
2241
+ serialized = JSON.parse(await readFile(credentialsPath, "utf8"));
2242
+ } catch (error) {
2243
+ if (isMissing(error)) return void 0;
2244
+ if (error instanceof QMindError) throw error;
2245
+ throw storedCredentialsInvalid();
2246
+ }
2247
+ const credentials = normalizeStoredCredentials(serialized);
2248
+ const keychainToken = await optionalSecret(() => secretStore.get(), void 0);
2249
+ return keychainToken === void 0 || keychainToken.length === 0 ? credentials : {
2250
+ ...credentials,
2251
+ deviceToken: keychainToken
2252
+ };
2253
+ };
2254
+ const withLock = async (operation) => {
2255
+ await secureDirectory(options.homeDirectory);
2256
+ const release = await lockfile.lock(options.homeDirectory, {
2257
+ lockfilePath: join(options.homeDirectory, ".credentials.lock"),
2258
+ realpath: false,
2259
+ retries: {
2260
+ factor: 1.5,
2261
+ maxTimeout: 100,
2262
+ minTimeout: 20,
2263
+ retries: 8
2264
+ },
2265
+ stale: 1e4
2266
+ });
2267
+ try {
2268
+ return await operation();
2269
+ } finally {
2270
+ await release();
2271
+ }
2272
+ };
2273
+ const persist = async (credentials, saveOptions = {}) => {
2274
+ const tempPath = `${credentialsPath}.${process.pid}.${randomBytes(6).toString("hex")}.tmp`;
2275
+ try {
2276
+ if (saveOptions.migrateLegacy === true) try {
2277
+ await secureRegularFile(credentialsPath);
2278
+ await copyFile(credentialsPath, legacyBackupPath);
2279
+ if (process.platform !== "win32") await chmod(legacyBackupPath, 384);
2280
+ } catch (error) {
2281
+ if (!isMissing(error)) throw error;
2282
+ }
2283
+ await writeFile(tempPath, serializedCredentials(credentials, now()), {
2284
+ flag: "wx",
2285
+ mode: 384
2286
+ });
2287
+ if (process.platform !== "win32") await chmod(tempPath, 384);
2288
+ await replaceFileAtomically(tempPath, credentialsPath);
2289
+ if (process.platform !== "win32") await chmod(credentialsPath, 384);
2290
+ await optionalSecret(() => secretStore.set(credentials.deviceToken), void 0);
2291
+ } finally {
2292
+ await rm(tempPath, { force: true });
2293
+ }
2294
+ };
2295
+ const deleteUnlocked = async () => {
2296
+ await Promise.all([rm(credentialsPath, { force: true }), rm(legacyBackupPath, { force: true })]);
2297
+ await optionalSecret(() => secretStore.delete(), void 0);
2298
+ };
2299
+ return Object.freeze({
2300
+ async compareAndSwap(expected, credentials, saveOptions = {}) {
2301
+ return await withLock(async () => {
2302
+ if (!sameCredentials(await loadUnlocked(), expected)) return false;
2303
+ await persist(credentials, saveOptions);
2304
+ return true;
2305
+ });
2306
+ },
2307
+ async delete() {
2308
+ await withLock(deleteUnlocked);
2309
+ },
2310
+ async deleteIf(expected) {
2311
+ return await withLock(async () => {
2312
+ if (!sameCredentials(await loadUnlocked(), expected)) return false;
2313
+ await deleteUnlocked();
2314
+ return true;
2315
+ });
2316
+ },
2317
+ async load() {
2318
+ return await loadUnlocked();
2319
+ },
2320
+ async save(credentials, saveOptions = {}) {
2321
+ await withLock(async () => persist(credentials, saveOptions));
2322
+ }
2323
+ });
2324
+ }
2325
+ //#endregion
2326
+ //#region ../node-host/src/files.ts
2327
+ function safeQMindDownloadName(title, fallback) {
2328
+ const normalized = [...title.normalize("NFKC")].map((character) => {
2329
+ const codePoint = character.codePointAt(0) ?? 0;
2330
+ return codePoint <= 31 || codePoint === 127 ? "_" : character;
2331
+ }).join("").replace(/[\\/]/g, "_").replace(/^\.+$/, "").trim().slice(0, 240);
2332
+ const safe = basename(normalized);
2333
+ if (safe.length > 0 && safe !== "." && safe !== "..") return safe;
2334
+ return basename(fallback.replace(/[\\/]/g, "_")) || "download";
2335
+ }
2336
+ async function commitQMindTemporaryFile(temporary, path, overwrite) {
2337
+ if (!overwrite) {
2338
+ await link(temporary, path).catch((error) => {
2339
+ if (error.code === "EEXIST") throw new QMindError("CONFLICT", `target already exists: ${path}`);
2340
+ throw error;
2341
+ });
2342
+ await rm(temporary, { force: true });
2343
+ return;
2344
+ }
2345
+ if (process.platform !== "win32") {
2346
+ await rename(temporary, path);
2347
+ return;
2348
+ }
2349
+ const backup = `${path}.${randomBytes(8).toString("hex")}.old`;
2350
+ let backedUp = false;
2351
+ try {
2352
+ try {
2353
+ await rename(path, backup);
2354
+ backedUp = true;
2355
+ } catch (error) {
2356
+ if (error.code !== "ENOENT") throw error;
2357
+ }
2358
+ await rename(temporary, path);
2359
+ if (backedUp) await rm(backup, { force: true });
2360
+ } catch (error) {
2361
+ if (backedUp) await rename(backup, path).catch(() => void 0);
2362
+ throw error;
2363
+ }
2364
+ }
2365
+ /** Atomic write with a no-clobber default. Temporary files are always cleaned. */
2366
+ async function atomicQMindWrite(path, content, overwrite = false) {
2367
+ await mkdir(dirname(path), { recursive: true });
2368
+ const temporary = join(dirname(path), `.${basename(path)}.${randomBytes(8).toString("hex")}.tmp`);
2369
+ try {
2370
+ await writeFile(temporary, content, {
2371
+ flag: "wx",
2372
+ mode: 420
2373
+ });
2374
+ await commitQMindTemporaryFile(temporary, path, overwrite);
2375
+ } catch (error) {
2376
+ await rm(temporary, { force: true });
2377
+ throw error;
2378
+ }
2379
+ }
2380
+ //#endregion
2381
+ //#region ../node-host/src/node-transport.ts
2382
+ const RETRYABLE_STATUS = /* @__PURE__ */ new Set([
2383
+ 408,
2384
+ 429,
2385
+ 500,
2386
+ 502,
2387
+ 503,
2388
+ 504
2389
+ ]);
2390
+ const CREDENTIAL_HEADERS = /* @__PURE__ */ new Set([
2391
+ "authorization",
2392
+ "cookie",
2393
+ "proxy-authorization",
2394
+ "x-csrf-token",
2395
+ "x-xsrf-token"
2396
+ ]);
2397
+ const SSE_MAX_BUFFER_SIZE = 8388608;
2398
+ function invalidArgument(message, field) {
2399
+ return new QMindError("INVALID_ARGUMENT", message, { details: { field } });
2400
+ }
2401
+ function positiveInteger(value, fallback, field) {
2402
+ if (value === void 0) return fallback;
2403
+ if (!Number.isSafeInteger(value) || value <= 0) throw invalidArgument(`${field} must be a positive safe integer`, field);
2404
+ return value;
2405
+ }
2406
+ function nonNegativeInteger(value, fallback, field) {
2407
+ if (value === void 0) return fallback;
2408
+ if (!Number.isSafeInteger(value) || value < 0) throw invalidArgument(`${field} must be a non-negative safe integer`, field);
2409
+ return value;
2410
+ }
2411
+ function normalizeOrigin(value) {
2412
+ let url;
2413
+ try {
2414
+ url = new URL(value);
2415
+ } catch {
2416
+ throw invalidArgument("origin must be a valid HTTP(S) origin", "origin");
2417
+ }
2418
+ if (url.protocol !== "http:" && url.protocol !== "https:" || url.username.length > 0 || url.password.length > 0 || url.pathname !== "/" || url.search.length > 0 || url.hash.length > 0) throw invalidArgument("origin must contain only an HTTP(S) scheme and authority", "origin");
2419
+ return url;
2420
+ }
2421
+ function environmentValue(environment, ...keys) {
2422
+ for (const key of keys) {
2423
+ const value = environment[key]?.trim();
2424
+ if (value !== void 0 && value.length > 0) return value;
2425
+ }
2426
+ }
2427
+ function tlsConnectOptions(options, withTls) {
2428
+ const timeout = positiveInteger(options.connectTimeoutMs, 1e4, "connectTimeoutMs");
2429
+ if (!withTls) return { timeout };
2430
+ const ca = options.tls?.ca;
2431
+ return {
2432
+ ...ca === void 0 ? {} : { ca: typeof ca === "string" ? ca : [...ca] },
2433
+ rejectUnauthorized: options.tls?.rejectUnauthorized ?? true,
2434
+ timeout
2435
+ };
2436
+ }
2437
+ function createDispatchers(options) {
2438
+ const environment = options.environment ?? process.env;
2439
+ if (options.proxyFromEnvironment === false) return {
2440
+ host: new Agent({ connect: tlsConnectOptions(options, true) }),
2441
+ signed: new Agent({ connect: tlsConnectOptions(options, true) })
2442
+ };
2443
+ const httpProxy = environmentValue(environment, "HTTP_PROXY", "http_proxy");
2444
+ const httpsProxy = environmentValue(environment, "HTTPS_PROXY", "https_proxy");
2445
+ const noProxy = environmentValue(environment, "NO_PROXY", "no_proxy");
2446
+ if (httpProxy === void 0 && httpsProxy === void 0) return {
2447
+ host: new Agent({ connect: tlsConnectOptions(options, true) }),
2448
+ signed: new Agent({ connect: tlsConnectOptions(options, true) })
2449
+ };
2450
+ const proxy = {
2451
+ httpProxy: httpProxy ?? "",
2452
+ httpsProxy: httpsProxy ?? "",
2453
+ noProxy: noProxy ?? ""
2454
+ };
2455
+ return {
2456
+ host: new EnvHttpProxyAgent({
2457
+ ...proxy,
2458
+ requestTls: tlsConnectOptions(options, true)
2459
+ }),
2460
+ signed: new EnvHttpProxyAgent({
2461
+ ...proxy,
2462
+ requestTls: tlsConnectOptions(options, true)
2463
+ })
2464
+ };
2465
+ }
2466
+ function headersToRecord(headers) {
2467
+ const result = {};
2468
+ for (const [name, value] of Object.entries(headers)) {
2469
+ if (value === void 0) continue;
2470
+ result[name.toLowerCase()] = Array.isArray(value) ? value.join(", ") : value;
2471
+ }
2472
+ return result;
2473
+ }
2474
+ function normalizedRequestHeaders(headers) {
2475
+ const result = {};
2476
+ for (const [name, value] of Object.entries(headers ?? {})) result[name.toLowerCase()] = value;
2477
+ return result;
2478
+ }
2479
+ function parseRawBody(rawBody) {
2480
+ if (rawBody.trim().length === 0) return void 0;
2481
+ try {
2482
+ return JSON.parse(rawBody);
2483
+ } catch {
2484
+ return rawBody;
2485
+ }
2486
+ }
2487
+ async function wireResponse(response) {
2488
+ const rawBody = await response.body.text();
2489
+ return {
2490
+ body: parseRawBody(rawBody),
2491
+ headers: headersToRecord(response.headers),
2492
+ rawBody,
2493
+ status: response.statusCode
2494
+ };
2495
+ }
2496
+ function assertWireRequest(request) {
2497
+ if (request.timeoutMs !== void 0) positiveInteger(request.timeoutMs, request.timeoutMs, "timeoutMs");
2498
+ if (request.destination.kind === "host") {
2499
+ if (!request.destination.path.startsWith("/") || request.destination.path.startsWith("//")) throw new QMindError("HOST_POLICY_ERROR", "host destination must be root-relative");
2500
+ return;
2501
+ }
2502
+ let url;
2503
+ try {
2504
+ url = new URL(request.destination.url);
2505
+ } catch {
2506
+ throw new QMindError("HOST_POLICY_ERROR", "signed destination must be valid HTTPS");
2507
+ }
2508
+ if (url.protocol !== "https:" || url.username.length > 0 || url.password.length > 0) throw new QMindError("HOST_POLICY_ERROR", "signed destination must be credential-free HTTPS");
2509
+ for (const name of Object.keys(request.headers ?? {})) if (CREDENTIAL_HEADERS.has(name.toLowerCase())) throw new QMindError("HOST_POLICY_ERROR", "host credentials cannot reach a signed URL", { details: { header: name.toLowerCase() } });
2510
+ }
2511
+ function sourceFailure(message, source, yieldedBytes) {
2512
+ return new QMindError("HOST_POLICY_ERROR", message, { details: {
2513
+ filename: source.filename,
2514
+ size: source.size,
2515
+ ...yieldedBytes === void 0 ? {} : { yieldedBytes }
2516
+ } });
2517
+ }
2518
+ function assertSource(source) {
2519
+ if (!Number.isSafeInteger(source.size) || source.size <= 0 || typeof source.open !== "function") throw sourceFailure("binary source metadata is invalid", source);
2520
+ }
2521
+ async function* sourceBytes(source, signal, onProgress) {
2522
+ assertSource(source);
2523
+ let bytes = 0;
2524
+ for await (const chunk of source.open()) {
2525
+ if (signal?.aborted === true) throw signal.reason;
2526
+ if (!(chunk instanceof Uint8Array)) throw sourceFailure("binary source yielded a non-Uint8Array chunk", source, bytes);
2527
+ bytes += chunk.byteLength;
2528
+ if (bytes > source.size) throw sourceFailure("binary source yielded more bytes than declared", source, bytes);
2529
+ yield chunk;
2530
+ onProgress?.(bytes);
2531
+ }
2532
+ if (bytes !== source.size) throw sourceFailure("binary source yielded fewer bytes than declared", source, bytes);
2533
+ }
2534
+ function quoteDisposition(value) {
2535
+ return value.replace(/["\r\n]/g, "_");
2536
+ }
2537
+ function multipartPart(name, value, boundary) {
2538
+ return `--${boundary}\r\nContent-Disposition: form-data; name="${quoteDisposition(name)}"\r\n\r\n${value}\r\n`;
2539
+ }
2540
+ function multipartPreamble(source, boundary) {
2541
+ return `--${boundary}\r\nContent-Disposition: form-data; name="file"; filename="${quoteDisposition(source.filename)}"\r\nContent-Type: ${source.mimeType}\r\n\r\n`;
2542
+ }
2543
+ function byteLength(value) {
2544
+ return Buffer.byteLength(value, "utf8");
2545
+ }
2546
+ function encodeBody(request, boundary) {
2547
+ const initialHeaders = normalizedRequestHeaders(request.headers);
2548
+ const body = request.body;
2549
+ if (body === void 0) return { headers: initialHeaders };
2550
+ if (body.kind === "json") {
2551
+ initialHeaders["content-type"] ??= "application/json";
2552
+ return {
2553
+ body: JSON.stringify(body.value),
2554
+ headers: initialHeaders
2555
+ };
2556
+ }
2557
+ if (body.kind === "binary") {
2558
+ assertSource(body.source);
2559
+ initialHeaders["content-type"] ??= body.source.mimeType;
2560
+ initialHeaders["content-length"] = String(body.source.size);
2561
+ return {
2562
+ body: Readable.from(sourceBytes(body.source, request.signal, body.onProgress)),
2563
+ headers: initialHeaders
2564
+ };
2565
+ }
2566
+ const multipartBody = body;
2567
+ assertSource(multipartBody.file);
2568
+ const fieldParts = Object.entries(multipartBody.fields).map(([name, value]) => multipartPart(name, value, boundary));
2569
+ const preamble = multipartPreamble(multipartBody.file, boundary);
2570
+ const closing = `\r\n--${boundary}--\r\n`;
2571
+ const contentLength = fieldParts.reduce((total, part) => total + byteLength(part), 0) + byteLength(preamble) + multipartBody.file.size + byteLength(closing);
2572
+ async function* multipart() {
2573
+ for (const part of fieldParts) yield Buffer.from(part);
2574
+ yield Buffer.from(preamble);
2575
+ yield* sourceBytes(multipartBody.file, request.signal, multipartBody.onProgress);
2576
+ yield Buffer.from(closing);
2577
+ }
2578
+ initialHeaders["content-type"] = `multipart/form-data; boundary=${boundary}`;
2579
+ initialHeaders["content-length"] = String(contentLength);
2580
+ return {
2581
+ body: Readable.from(multipart()),
2582
+ headers: initialHeaders
2583
+ };
2584
+ }
2585
+ function isTimeoutCause(cause) {
2586
+ const code = cause.code;
2587
+ const name = cause.name;
2588
+ return code === "QMIND_REQUEST_TIMEOUT" || code === "UND_ERR_CONNECT_TIMEOUT" || code === "UND_ERR_HEADERS_TIMEOUT" || code === "UND_ERR_BODY_TIMEOUT" || name === "TimeoutError";
2589
+ }
2590
+ function safeCause(cause) {
2591
+ const record = cause;
2592
+ return {
2593
+ ...typeof record.name === "string" ? { name: record.name } : {},
2594
+ ...typeof record.code === "string" ? { code: record.code } : {}
2595
+ };
2596
+ }
2597
+ function transportFailure(request, cause, deadlineExpired = false) {
2598
+ if (cause instanceof QMindError) return cause;
2599
+ if (request.signal?.aborted === true) return new QMindError("ABORTED", "QMind request was aborted");
2600
+ if (deadlineExpired || isTimeoutCause(cause)) return new QMindError("TIMEOUT", "QMind request timed out", { cause: safeCause(cause) });
2601
+ return new QMindError("NETWORK_ERROR", "QMind network request failed", { cause: safeCause(cause) });
2602
+ }
2603
+ function requestDeadline(signal, milliseconds) {
2604
+ const controller = new AbortController();
2605
+ let timedOut = false;
2606
+ const timeout = setTimeout(() => {
2607
+ timedOut = true;
2608
+ const error = Object.assign(/* @__PURE__ */ new Error("QMind request deadline expired"), {
2609
+ code: "QMIND_REQUEST_TIMEOUT",
2610
+ name: "TimeoutError"
2611
+ });
2612
+ controller.abort(error);
2613
+ }, milliseconds);
2614
+ timeout.unref();
2615
+ const onAbort = () => controller.abort(signal?.reason);
2616
+ if (signal?.aborted === true) onAbort();
2617
+ else signal?.addEventListener("abort", onAbort, { once: true });
2618
+ const clearDeadline = () => clearTimeout(timeout);
2619
+ return {
2620
+ clearTimeout: clearDeadline,
2621
+ dispose() {
2622
+ clearDeadline();
2623
+ signal?.removeEventListener("abort", onAbort);
2624
+ },
2625
+ signal: controller.signal,
2626
+ get timedOut() {
2627
+ return timedOut;
2628
+ }
2629
+ };
2630
+ }
2631
+ function abortableSleep(milliseconds, signal) {
2632
+ return new Promise((resolve, reject) => {
2633
+ if (signal?.aborted === true) {
2634
+ reject(signal.reason);
2635
+ return;
2636
+ }
2637
+ const onAbort = () => {
2638
+ clearTimeout(timer);
2639
+ reject(signal?.reason);
2640
+ };
2641
+ const timer = setTimeout(() => {
2642
+ signal?.removeEventListener("abort", onAbort);
2643
+ resolve();
2644
+ }, milliseconds);
2645
+ signal?.addEventListener("abort", onAbort, { once: true });
2646
+ });
2647
+ }
2648
+ function requestUrl(origin, request) {
2649
+ return request.destination.kind === "host" ? new URL(request.destination.path, origin) : new URL(request.destination.url);
2650
+ }
2651
+ function debugRequest(options, request, url) {
2652
+ options.debugLog?.(`[qmind-debug] ${request.method} ${url.origin}${url.pathname}`);
2653
+ }
2654
+ async function* decodedEvents(body, request, disposeSignal) {
2655
+ const decoder = new TextDecoder("utf-8", { fatal: true });
2656
+ const pending = [];
2657
+ let done = false;
2658
+ let parserFailure;
2659
+ const parser = createParser({
2660
+ maxBufferSize: SSE_MAX_BUFFER_SIZE,
2661
+ onError(error) {
2662
+ if (error.type === "max-buffer-size-exceeded") parserFailure = error;
2663
+ },
2664
+ onEvent(event) {
2665
+ if (event.data.trim() === "[DONE]") done = true;
2666
+ else pending.push(event.data);
2667
+ }
2668
+ });
2669
+ const drain = function* () {
2670
+ while (pending.length > 0) {
2671
+ const value = pending.shift();
2672
+ if (value !== void 0) yield value;
2673
+ }
2674
+ };
2675
+ try {
2676
+ for await (const chunk of body) {
2677
+ parser.feed(decoder.decode(chunk, { stream: true }));
2678
+ if (parserFailure !== void 0) throw new QMindError("STREAM_ERROR", "SSE parser buffer limit exceeded", { details: { limitBytes: SSE_MAX_BUFFER_SIZE } });
2679
+ yield* drain();
2680
+ if (done) return;
2681
+ }
2682
+ parser.feed(decoder.decode());
2683
+ parser.reset({ consume: true });
2684
+ if (parserFailure !== void 0) throw new QMindError("STREAM_ERROR", "SSE parser buffer limit exceeded", { details: { limitBytes: SSE_MAX_BUFFER_SIZE } });
2685
+ yield* drain();
2686
+ } catch (cause) {
2687
+ if (cause instanceof QMindError) throw cause;
2688
+ if (request.signal?.aborted === true) throw new QMindError("ABORTED", "QMind stream was aborted");
2689
+ if (isTimeoutCause(cause)) throw new QMindError("TIMEOUT", "QMind stream timed out", { cause: safeCause(cause) });
2690
+ throw new QMindError("STREAM_ERROR", "QMind SSE stream failed", { cause: safeCause(cause) });
2691
+ } finally {
2692
+ disposeSignal();
2693
+ body.destroy();
2694
+ }
2695
+ }
2696
+ function createQMindNodeTransport(options) {
2697
+ const origin = normalizeOrigin(options.origin);
2698
+ const requestTimeoutMs = positiveInteger(options.requestTimeoutMs, 3e4, "requestTimeoutMs");
2699
+ const streamIdleTimeoutMs = positiveInteger(options.streamIdleTimeoutMs, 12e4, "streamIdleTimeoutMs");
2700
+ const maxRetries = nonNegativeInteger(options.maxRetries, 2, "maxRetries");
2701
+ const dispatchers = createDispatchers(options);
2702
+ const dispatcherFor = (request) => request.destination.kind === "host" ? dispatchers.host : dispatchers.signed;
2703
+ return Object.freeze({
2704
+ async close() {
2705
+ const unique = /* @__PURE__ */ new Set([dispatchers.host, dispatchers.signed]);
2706
+ await Promise.all([...unique].map(async (dispatcher) => dispatcher.close()));
2707
+ },
2708
+ async downloadToFile(download) {
2709
+ const request$1 = {
2710
+ destination: {
2711
+ kind: "signed",
2712
+ url: download.url
2713
+ },
2714
+ idempotent: true,
2715
+ method: "GET",
2716
+ ...download.signal === void 0 ? {} : { signal: download.signal }
2717
+ };
2718
+ assertWireRequest(request$1);
2719
+ const url = requestUrl(origin, request$1);
2720
+ await mkdir(dirname(download.targetPath), { recursive: true });
2721
+ if (download.overwrite !== true) try {
2722
+ await stat(download.targetPath);
2723
+ throw new QMindError("CONFLICT", `target already exists: ${download.targetPath}`);
2724
+ } catch (error) {
2725
+ if (error.code !== "ENOENT") throw error;
2726
+ }
2727
+ const temporary = join(dirname(download.targetPath), `.${basename(download.targetPath)}.${randomBytes(8).toString("hex")}.tmp`);
2728
+ let response;
2729
+ try {
2730
+ response = await request(url, {
2731
+ bodyTimeout: requestTimeoutMs,
2732
+ dispatcher: dispatchers.signed,
2733
+ headersTimeout: requestTimeoutMs,
2734
+ idempotent: true,
2735
+ method: "GET",
2736
+ ...download.signal === void 0 ? {} : { signal: download.signal }
2737
+ });
2738
+ if (response.statusCode < 200 || response.statusCode >= 300) {
2739
+ const decoded = await wireResponse(response);
2740
+ throw new QMindError("REMOTE_ERROR", "download request failed", {
2741
+ ...decoded.rawBody === void 0 ? {} : { rawBody: decoded.rawBody },
2742
+ status: decoded.status
2743
+ });
2744
+ }
2745
+ await pipeline(response.body, createWriteStream(temporary, {
2746
+ flags: "wx",
2747
+ mode: 384
2748
+ }));
2749
+ const size = (await stat(temporary)).size;
2750
+ await chmod(temporary, 420);
2751
+ await commitQMindTemporaryFile(temporary, download.targetPath, download.overwrite === true);
2752
+ return size;
2753
+ } catch (cause) {
2754
+ response?.body.destroy();
2755
+ await rm(temporary, { force: true });
2756
+ if (cause instanceof QMindError) throw cause;
2757
+ throw transportFailure(request$1, cause);
2758
+ }
2759
+ },
2760
+ async request(request$2) {
2761
+ assertWireRequest(request$2);
2762
+ const url = requestUrl(origin, request$2);
2763
+ const attempts = request$2.idempotent ? maxRetries + 1 : 1;
2764
+ let lastFailure;
2765
+ let lastDeadlineExpired = false;
2766
+ for (let attempt = 0; attempt < attempts; attempt += 1) {
2767
+ const deadline = requestDeadline(request$2.signal, request$2.timeoutMs ?? requestTimeoutMs);
2768
+ try {
2769
+ debugRequest(options, request$2, url);
2770
+ const encoded = encodeBody(request$2, `----qmind-${randomBytes(18).toString("hex")}`);
2771
+ const response = await request(url, {
2772
+ ...encoded.body === void 0 ? {} : { body: encoded.body },
2773
+ bodyTimeout: request$2.timeoutMs ?? requestTimeoutMs,
2774
+ dispatcher: dispatcherFor(request$2),
2775
+ headers: encoded.headers,
2776
+ headersTimeout: request$2.timeoutMs ?? requestTimeoutMs,
2777
+ idempotent: request$2.idempotent,
2778
+ method: request$2.method,
2779
+ signal: deadline.signal
2780
+ });
2781
+ if (attempt + 1 < attempts && RETRYABLE_STATUS.has(response.statusCode)) {
2782
+ await response.body.dump();
2783
+ await abortableSleep(250 * 2 ** attempt, request$2.signal);
2784
+ continue;
2785
+ }
2786
+ return await wireResponse(response);
2787
+ } catch (cause) {
2788
+ lastFailure = cause;
2789
+ lastDeadlineExpired = deadline.timedOut;
2790
+ if (attempt + 1 < attempts && request$2.signal?.aborted !== true) {
2791
+ await abortableSleep(250 * 2 ** attempt, request$2.signal);
2792
+ continue;
2793
+ }
2794
+ } finally {
2795
+ deadline.dispose();
2796
+ }
2797
+ }
2798
+ throw transportFailure(request$2, lastFailure, lastDeadlineExpired);
2799
+ },
2800
+ async stream(request$3) {
2801
+ assertWireRequest(request$3);
2802
+ const url = requestUrl(origin, request$3);
2803
+ debugRequest(options, request$3, url);
2804
+ const encoded = encodeBody(request$3, `----qmind-${randomBytes(18).toString("hex")}`);
2805
+ let response;
2806
+ const deadline = requestDeadline(request$3.signal, request$3.timeoutMs ?? requestTimeoutMs);
2807
+ try {
2808
+ response = await request(url, {
2809
+ ...encoded.body === void 0 ? {} : { body: encoded.body },
2810
+ bodyTimeout: streamIdleTimeoutMs,
2811
+ dispatcher: dispatchers.host,
2812
+ headers: {
2813
+ accept: "text/event-stream",
2814
+ ...encoded.headers
2815
+ },
2816
+ headersTimeout: request$3.timeoutMs ?? requestTimeoutMs,
2817
+ idempotent: request$3.idempotent,
2818
+ method: request$3.method,
2819
+ signal: deadline.signal
2820
+ });
2821
+ } catch (cause) {
2822
+ deadline.dispose();
2823
+ throw transportFailure(request$3, cause, deadline.timedOut);
2824
+ }
2825
+ deadline.clearTimeout();
2826
+ const headers = headersToRecord(response.headers);
2827
+ if (response.statusCode < 200 || response.statusCode >= 300 || !headers["content-type"]?.toLowerCase().includes("text/event-stream")) try {
2828
+ return {
2829
+ ...await wireResponse(response),
2830
+ kind: "response"
2831
+ };
2832
+ } finally {
2833
+ deadline.dispose();
2834
+ }
2835
+ return {
2836
+ events: decodedEvents(response.body, request$3, deadline.dispose),
2837
+ headers,
2838
+ kind: "events",
2839
+ status: response.statusCode
2840
+ };
2841
+ }
2842
+ });
2843
+ }
2844
+ const MIME_TYPES = Object.freeze({
2845
+ ".csv": "text/csv",
2846
+ ".docx": "application/vnd.openxmlformats-officedocument.wordprocessingml.document",
2847
+ ".gif": "image/gif",
2848
+ ".jpeg": "image/jpeg",
2849
+ ".jpg": "image/jpeg",
2850
+ ".md": "text/markdown",
2851
+ ".mdx": "text/markdown",
2852
+ ".pdf": "application/pdf",
2853
+ ".png": "image/png",
2854
+ ".pptx": "application/vnd.openxmlformats-officedocument.presentationml.presentation",
2855
+ ".svg": "image/svg+xml",
2856
+ ".txt": "text/plain",
2857
+ ".webp": "image/webp",
2858
+ ".xlsx": "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"
2859
+ });
2860
+ function detectQMindMimeType(filename) {
2861
+ return MIME_TYPES[extname(filename).toLowerCase()] ?? "application/octet-stream";
2862
+ }
2863
+ async function createQMindFileSource(path, options = {}) {
2864
+ let info;
2865
+ try {
2866
+ info = await stat(path);
2867
+ } catch {
2868
+ throw invalidArgument("source path must reference a readable file", "path");
2869
+ }
2870
+ if (!info.isFile() || info.size <= 0) throw invalidArgument("source path must reference a non-empty regular file", "path");
2871
+ const filename = options.filename?.trim() || basename(path);
2872
+ const mimeType = options.mimeType?.trim() || detectQMindMimeType(filename);
2873
+ return Object.freeze({
2874
+ filename,
2875
+ mimeType,
2876
+ async *open() {
2877
+ for await (const chunk of createReadStream(path)) yield chunk;
2878
+ },
2879
+ size: info.size
2880
+ });
2881
+ }
2882
+ //#endregion
2883
+ //#region ../node-host/src/host.ts
2884
+ function defaultReporter() {
2885
+ return Object.freeze({
2886
+ info(message) {
2887
+ console.error(message);
2888
+ },
2889
+ warn(message) {
2890
+ console.error(message);
2891
+ }
2892
+ });
2893
+ }
2894
+ function defaultBrowser() {
2895
+ return Object.freeze({ async open(url) {
2896
+ (await open(url, { wait: false })).unref();
2897
+ } });
2898
+ }
2899
+ async function tlsAuthority(path) {
2900
+ if (path === void 0) return void 0;
2901
+ try {
2902
+ return await readFile(path, "utf8");
2903
+ } catch {
2904
+ throw new QMindError("HOST_POLICY_ERROR", "unable to read the configured TLS CA file", { details: { field: "tlsCaFile" } });
2905
+ }
2906
+ }
2907
+ function configurationOptions(options, reporter) {
2908
+ return {
2909
+ ...options.credentialSashOrigin === void 0 ? {} : { credentialSashOrigin: options.credentialSashOrigin },
2910
+ ...options.environment === void 0 ? {} : { environment: options.environment },
2911
+ ...options.flags === void 0 ? {} : { flags: options.flags },
2912
+ ...options.fixedEndpoints === void 0 ? {} : { fixedEndpoints: options.fixedEndpoints },
2913
+ ...options.homeDirectory === void 0 ? {} : { homeDirectory: options.homeDirectory },
2914
+ ...options.legacyEnvironmentPath === void 0 ? {} : { legacyEnvironmentPath: options.legacyEnvironmentPath },
2915
+ ...options.productId === void 0 ? {} : { productId: options.productId },
2916
+ ...reporter === void 0 ? {} : { reporter },
2917
+ ...options.stdinIsTTY === void 0 ? {} : { stdinIsTTY: options.stdinIsTTY }
2918
+ };
2919
+ }
2920
+ async function createQMindCliHost(options = {}) {
2921
+ const reporter = options.adapters?.reporter ?? defaultReporter();
2922
+ const clock = options.adapters?.clock ?? createSystemClock();
2923
+ const vault = createFileCredentialVault({
2924
+ homeDirectory: (await loadQMindCliConfig(configurationOptions(options))).runtime.homeDirectory,
2925
+ now: () => clock.now(),
2926
+ secretStore: options.adapters?.secretStore ?? createNoopQMindSecretStore()
2927
+ });
2928
+ let credentials;
2929
+ try {
2930
+ credentials = await vault.load();
2931
+ } catch (error) {
2932
+ if (options.credentialReadFailure !== "unauthenticated") throw error;
2933
+ reporter.warn("QMind credential vault is unavailable; authentication is required.");
2934
+ credentials = void 0;
2935
+ }
2936
+ const resolved = await loadQMindCliConfig({
2937
+ ...configurationOptions(options, reporter),
2938
+ ...options.adapters?.requestCredential === void 0 && credentials?.sashOrigin ? { credentialSashOrigin: credentials.sashOrigin } : {},
2939
+ reporter
2940
+ });
2941
+ const ca = await tlsAuthority(resolved.tlsCaFile);
2942
+ const nodeOptions = {
2943
+ ...options.connectTimeoutMs === void 0 ? {} : { connectTimeoutMs: options.connectTimeoutMs },
2944
+ ...resolved.debug ? { debugLog: (message) => reporter.info(message) } : {},
2945
+ environment: resolved.environment,
2946
+ ...options.maxRetries === void 0 ? {} : { maxRetries: options.maxRetries },
2947
+ origin: resolved.runtime.sashOrigin,
2948
+ proxyFromEnvironment: resolved.proxyFromEnvironment,
2949
+ requestTimeoutMs: resolved.requestTimeoutMs,
2950
+ streamIdleTimeoutMs: resolved.streamIdleTimeoutMs,
2951
+ tls: {
2952
+ ...ca === void 0 ? {} : { ca },
2953
+ rejectUnauthorized: resolved.tlsRejectUnauthorized
2954
+ }
2955
+ };
2956
+ const ownedTransport = options.adapters?.transport === void 0 ? createQMindNodeTransport(nodeOptions) : void 0;
2957
+ const rawTransport = options.adapters?.transport ?? ownedTransport;
2958
+ if (rawTransport === void 0) throw new QMindError("HOST_POLICY_ERROR", "unable to initialize the QMind transport");
2959
+ const auth = createQMindCliAuthManager({
2960
+ browser: options.adapters?.browser ?? defaultBrowser(),
2961
+ clock,
2962
+ config: resolved.runtime,
2963
+ credentials,
2964
+ explicitToken: resolved.token,
2965
+ ...options.loginCommand === void 0 ? {} : { loginCommand: options.loginCommand },
2966
+ reporter,
2967
+ transport: rawTransport,
2968
+ vault
2969
+ });
2970
+ const client = createQMindClient({
2971
+ access: "sash",
2972
+ client: resolved.runtime.client,
2973
+ transport: createAuthenticatedQMindTransport(rawTransport, auth, options.adapters?.requestCredential)
2974
+ });
2975
+ return Object.freeze({
2976
+ client,
2977
+ async close() {
2978
+ await ownedTransport?.close();
2979
+ },
2980
+ config: resolved.runtime,
2981
+ createFileSource: createQMindFileSource,
2982
+ async downloadToFile(downloadOptions) {
2983
+ const downloader = options.adapters?.downloader ?? ownedTransport;
2984
+ if (downloader === void 0) throw new QMindError("UNSUPPORTED_OPERATION", "the injected CLI transport does not provide file downloads");
2985
+ return await downloader.downloadToFile(downloadOptions);
2986
+ },
2987
+ login: auth.login,
2988
+ logout: auth.logout
2989
+ });
2990
+ }
2991
+ //#endregion
2992
+ //#region src/argv.ts
2993
+ const SHORT_OPTIONS = /* @__PURE__ */ new Set([
2994
+ "-h",
2995
+ "-V",
2996
+ "-o",
2997
+ "-q"
2998
+ ]);
2999
+ /**
3000
+ * Preserve the historical Go CLI spelling where long options used one dash.
3001
+ * The delimiter is an intentional hard boundary: everything after `--` is a
3002
+ * positional value and must remain byte-for-byte unchanged.
3003
+ */
3004
+ function normalizeLegacyArgv(argv) {
3005
+ let positionalOnly = false;
3006
+ return argv.map((argument) => {
3007
+ if (argument === "--") {
3008
+ positionalOnly = true;
3009
+ return argument;
3010
+ }
3011
+ if (positionalOnly || SHORT_OPTIONS.has(argument) || argument.startsWith("--")) return argument;
3012
+ if (/^-[A-Za-z][A-Za-z0-9-]+(?:=.*)?$/.test(argument)) return `-${argument}`;
3013
+ return argument;
3014
+ });
3015
+ }
3016
+ /** Runtime errors use the JSON error envelope only when the caller explicitly
3017
+ * selected JSON, except folder sync whose compatibility default is JSON. */
3018
+ function requestsJsonOutput(argv) {
3019
+ let explicitFormat;
3020
+ let positionalOnly = false;
3021
+ for (let index = 0; index < argv.length; index += 1) {
3022
+ const argument = argv[index];
3023
+ if (argument === "--") {
3024
+ positionalOnly = true;
3025
+ continue;
3026
+ }
3027
+ if (positionalOnly) continue;
3028
+ if (argument === "--format" || argument === "-format") {
3029
+ explicitFormat = argv[index + 1];
3030
+ index += 1;
3031
+ continue;
3032
+ }
3033
+ const match = argument?.match(/^--?format=(.*)$/);
3034
+ if (match !== null && match !== void 0) explicitFormat = match[1];
3035
+ }
3036
+ if (explicitFormat !== void 0) return explicitFormat === "json";
3037
+ return argv[0] === "upload-folder" || argv[0] === "sync";
3038
+ }
3039
+ //#endregion
3040
+ //#region src/build-info.ts
3041
+ const QMIND_CLI_VERSION = "3.0.0";
3042
+ const QMIND_CLI_BUILD_TIME = "2026-09-01T07:33:25.000Z";
3043
+ function qmindCliPlatform() {
3044
+ return process.platform === "win32" ? "windows" : process.platform;
3045
+ }
3046
+ function qmindCliArchitecture() {
3047
+ return process.arch === "x64" ? "amd64" : process.arch;
3048
+ }
3049
+ //#endregion
3050
+ //#region src/distributions.ts
3051
+ /** Canonical runtime and release identity for every public CLI distribution. */
3052
+ const CLI_DISTRIBUTIONS = Object.freeze({
3053
+ global: {
3054
+ id: "global",
3055
+ command: "qmind",
3056
+ package: "@qoder-ai/qmind-cli",
3057
+ productId: "qoder",
3058
+ gitTagSlug: "qmind-cli",
3059
+ readmeDirectory: ".",
3060
+ productNames: {
3061
+ en: "Qoder Global",
3062
+ zhCN: "Qoder Global"
3063
+ }
3064
+ },
3065
+ cn: {
3066
+ id: "cn",
3067
+ command: "qmind-cn",
3068
+ package: "@qodercn-ai/qmind-cli",
3069
+ productId: "qoder-cn",
3070
+ gitTagSlug: "qmind-cli-cn",
3071
+ readmeDirectory: "readmes/cn",
3072
+ productNames: {
3073
+ en: "Qoder China",
3074
+ zhCN: "Qoder 中国版"
3075
+ }
3076
+ }
3077
+ });
3078
+ //#endregion
3079
+ //#region src/config.ts
3080
+ function cliHostConfiguration(distribution, flags, environment, stateRoot) {
3081
+ const { productId, command } = CLI_DISTRIBUTIONS[distribution];
3082
+ const custom = flags.sash !== void 0 || flags.dashboard !== void 0;
3083
+ if (custom && (typeof flags.sash !== "string" || typeof flags.dashboard !== "string" || flags.env !== void 0)) throw new QMindError("INVALID_ARGUMENT", "--sash and --dashboard must be supplied together and cannot be combined with --env");
3084
+ const preset = typeof flags.env === "string" ? flags.env.trim() : "prod";
3085
+ const endpoints = custom ? {
3086
+ dashboardOrigin: normalizeQMindOrigin(String(flags.dashboard).trim(), "--dashboard"),
3087
+ sashOrigin: normalizeQMindOrigin(String(flags.sash).trim(), "--sash")
3088
+ } : resolveQMindProductEndpoints(productId, preset);
3089
+ const target = custom ? `custom-${createHash("sha256").update(JSON.stringify([endpoints.dashboardOrigin, endpoints.sashOrigin])).digest("hex")}` : preset;
3090
+ return {
3091
+ environment: {
3092
+ ...environment,
3093
+ QMIND_TOKEN: target === "prod" ? environment.QMIND_TOKEN : environment.QMIND_DEBUG_TOKEN
3094
+ },
3095
+ flags,
3096
+ fixedEndpoints: endpoints,
3097
+ homeDirectory: join(resolve(stateRoot ?? (environment.QMIND_HOME?.trim() || join(homedir(), ".qmind"))), "cli", distribution, target),
3098
+ legacyEnvironmentPath: false,
3099
+ loginCommand: `${command} login${custom ? ` --sash ${endpoints.sashOrigin} --dashboard ${endpoints.dashboardOrigin}` : target === "prod" ? "" : ` --env ${target}`}`,
3100
+ productId
3101
+ };
3102
+ }
3103
+ //#endregion
3104
+ //#region src/folder-sync.ts
3105
+ const QMIND_FOLDER_SYNC_DEFAULT_EXTENSIONS = Object.freeze([
3106
+ ".mdx",
3107
+ ".md",
3108
+ ".pdf",
3109
+ ".txt",
3110
+ ".docx",
3111
+ ".pptx",
3112
+ ".png",
3113
+ ".jpg",
3114
+ ".jpeg",
3115
+ ".gif",
3116
+ ".svg",
3117
+ ".webp"
3118
+ ]);
3119
+ function posix(path) {
3120
+ return path.split(sep).join("/");
3121
+ }
3122
+ function qmindFolderSyncIndexKey(parentId, title) {
3123
+ return `${parentId}\u0000${title}`;
3124
+ }
3125
+ function normalizeExtensions(values) {
3126
+ return new Set(values.map((value) => value.trim().toLowerCase()).filter((value) => value.length > 0).map((value) => value.startsWith(".") ? value : `.${value}`));
3127
+ }
3128
+ function validateMapping(value) {
3129
+ if (value === null || typeof value !== "object" || Array.isArray(value)) throw new QMindError("INVALID_ARGUMENT", "directory ID mapping must be a JSON object");
3130
+ const result = {};
3131
+ for (const [path, id] of Object.entries(value)) {
3132
+ if (typeof id !== "string" || id.trim().length === 0) throw new QMindError("INVALID_ARGUMENT", "directory ID mapping values must be non-empty strings");
3133
+ result[posix(path)] = id.trim();
3134
+ }
3135
+ return result;
3136
+ }
3137
+ async function sha256(path) {
3138
+ const hash = createHash("sha256");
3139
+ for await (const chunk of createReadStream(path)) hash.update(chunk);
3140
+ return hash.digest("hex");
3141
+ }
3142
+ function createNodeQMindFolderSyncFileSystem() {
3143
+ return Object.freeze({
3144
+ async isDirectory(path) {
3145
+ return (await stat(path).catch(() => void 0))?.isDirectory() === true;
3146
+ },
3147
+ async readMapping(path) {
3148
+ try {
3149
+ return validateMapping(JSON.parse(await readFile(path, "utf8")));
3150
+ } catch (error) {
3151
+ if (error instanceof QMindError) throw error;
3152
+ throw new QMindError("INVALID_ARGUMENT", `unable to read directory ID mapping: ${path}`);
3153
+ }
3154
+ },
3155
+ async scan(baseDirectory, options) {
3156
+ const directories = [];
3157
+ const files = [];
3158
+ let skipped = 0;
3159
+ const isIgnored = options.ignorePatterns.length === 0 ? () => false : picomatch([...options.ignorePatterns], { dot: true });
3160
+ const visit = async (directory) => {
3161
+ const entries = await readdir(directory, { withFileTypes: true });
3162
+ entries.sort((left, right) => left.name.localeCompare(right.name));
3163
+ for (const entry of entries) {
3164
+ const absolute = join(directory, entry.name);
3165
+ const path = posix(relative(baseDirectory, absolute));
3166
+ if (entry.name.startsWith(".") || isIgnored(path)) {
3167
+ skipped += entry.isFile() ? 1 : 0;
3168
+ continue;
3169
+ }
3170
+ if (entry.isDirectory()) {
3171
+ directories.push(path);
3172
+ await visit(absolute);
3173
+ } else if (entry.isFile()) {
3174
+ if (options.extensions.has(extname(entry.name).toLowerCase())) files.push(path);
3175
+ else skipped += 1;
3176
+ }
3177
+ }
3178
+ };
3179
+ await visit(baseDirectory);
3180
+ directories.sort((left, right) => left.split("/").length - right.split("/").length || left.localeCompare(right));
3181
+ files.sort((left, right) => left.localeCompare(right));
3182
+ return {
3183
+ directories,
3184
+ files,
3185
+ skipped
3186
+ };
3187
+ },
3188
+ sha256,
3189
+ async writeMapping(path, mapping) {
3190
+ await mkdir(dirname(path), { recursive: true });
3191
+ await writeFile(path, `${JSON.stringify(mapping, null, 2)}\n`, { mode: 384 });
3192
+ }
3193
+ });
3194
+ }
3195
+ async function shouldReplaceQMindFolderFile(mode, existing, localPath, fileSystem = createNodeQMindFolderSyncFileSystem()) {
3196
+ if (mode === "skip") return false;
3197
+ if (mode === "overwrite") return true;
3198
+ const metadata = existing.metadata ?? {};
3199
+ const remoteHash = metadata.fileSha256 ?? metadata.rawFileSha256 ?? metadata.sha256;
3200
+ if (typeof remoteHash !== "string" || !/^[a-f0-9]{64}$/i.test(remoteHash)) return true;
3201
+ return (await fileSystem.sha256(localPath)).toLowerCase() !== remoteHash.toLowerCase();
3202
+ }
3203
+ function retryable(error) {
3204
+ if (!(error instanceof QMindError)) return true;
3205
+ if (error.code === "FILE_TOO_LARGE" || error.code === "INVALID_ARGUMENT" || error.code === "AUTH_REQUIRED" || error.code === "FORBIDDEN") return false;
3206
+ return error.status === void 0 || error.status === 408 || error.status === 429 || error.status >= 500;
3207
+ }
3208
+ async function withUploadRetry(task, sleep) {
3209
+ let lastFailure;
3210
+ for (let attempt = 0; attempt < 3; attempt += 1) try {
3211
+ return await task();
3212
+ } catch (error) {
3213
+ lastFailure = error;
3214
+ if (!retryable(error) || attempt === 2) break;
3215
+ await sleep(250 * 2 ** attempt);
3216
+ }
3217
+ throw lastFailure;
3218
+ }
3219
+ function defaultSleep(milliseconds) {
3220
+ return new Promise((resolvePromise) => setTimeout(resolvePromise, milliseconds));
3221
+ }
3222
+ function mappingPath(baseDirectory, cacheDirectory) {
3223
+ const cache = cacheDirectory ?? process.env.XDG_CACHE_HOME ?? join(homedir(), ".cache");
3224
+ const name = createHash("sha1").update(baseDirectory).digest("hex").slice(0, 16);
3225
+ return join(cache, "qmind", "dir-ids", `${name}.json`);
3226
+ }
3227
+ function addFailure(stats, field, path) {
3228
+ (stats[field] ??= []).push(path);
3229
+ }
3230
+ function publicStats(stats) {
3231
+ return Object.freeze({
3232
+ ...stats,
3233
+ ...stats.failed_dirs === void 0 ? {} : { failed_dirs: Object.freeze(stats.failed_dirs) },
3234
+ ...stats.failed_files === void 0 ? {} : { failed_files: Object.freeze(stats.failed_files) },
3235
+ ...stats.failed_deletes === void 0 ? {} : { failed_deletes: Object.freeze(stats.failed_deletes) }
3236
+ });
3237
+ }
3238
+ async function synchronizeQMindFolder(host, options) {
3239
+ const notebookId = options.notebookId.trim();
3240
+ if (notebookId.length === 0) throw new QMindError("INVALID_ARGUMENT", "notebookId is required");
3241
+ const fileSystem = options.fileSystem ?? createNodeQMindFolderSyncFileSystem();
3242
+ const baseDirectory = resolve(options.baseDirectory);
3243
+ if (!await fileSystem.isDirectory(baseDirectory)) throw new QMindError("INVALID_ARGUMENT", `${baseDirectory} is not a directory`);
3244
+ const mode = options.mode ?? "skip";
3245
+ if (options.skipUpload === true && options.delete === true) throw new QMindError("INVALID_ARGUMENT", "delete cannot be combined with skipUpload because local files would not enter the mirror plan");
3246
+ const requestedConcurrency = options.concurrency ?? 3;
3247
+ if (!Number.isSafeInteger(requestedConcurrency) || requestedConcurrency < 1) throw new QMindError("INVALID_ARGUMENT", "concurrency must be a positive integer");
3248
+ const concurrency = Math.min(10, requestedConcurrency);
3249
+ const diagnostics = [];
3250
+ if (requestedConcurrency > 10) diagnostics.push(`warning: concurrency ${requestedConcurrency} exceeds maximum (10); clamped to 10`);
3251
+ const extensions = normalizeExtensions(options.extensions ?? QMIND_FOLDER_SYNC_DEFAULT_EXTENSIONS);
3252
+ const tree = await fileSystem.scan(baseDirectory, {
3253
+ extensions,
3254
+ ignorePatterns: options.ignorePatterns ?? []
3255
+ });
3256
+ const remote = await host.client.listAllSources(notebookId);
3257
+ const remoteIndex = new Map(remote.map((source) => [qmindFolderSyncIndexKey(source.parentSourceId, source.title), source]));
3258
+ const remoteById = new Map(remote.map((source) => [source.id, source]));
3259
+ const stats = {
3260
+ dirs_created: 0,
3261
+ dirs_reused: 0,
3262
+ dirs_failed: 0,
3263
+ dirs_refresh_failed: 0,
3264
+ files_uploaded: 0,
3265
+ files_skipped: options.skipUpload === true ? 0 : tree.skipped,
3266
+ files_already_exist: 0,
3267
+ files_failed: 0,
3268
+ ...options.delete === true ? {
3269
+ delete_failed: 0,
3270
+ dirs_deleted: 0,
3271
+ files_deleted: 0
3272
+ } : {},
3273
+ ...options.dryRun === true ? { dry_run: true } : {}
3274
+ };
3275
+ const configuredMapping = options.directoryIdsPath === void 0 ? {} : await fileSystem.readMapping(options.directoryIdsPath);
3276
+ const directoryIds = {};
3277
+ const desiredKeys = /* @__PURE__ */ new Set();
3278
+ for (const directory of tree.directories) {
3279
+ const parentPath = posix(dirname(directory)) === "." ? "" : posix(dirname(directory));
3280
+ const parentId = directoryIds[parentPath] ?? "";
3281
+ if (parentPath.length > 0 && parentId.length === 0) {
3282
+ stats.dirs_failed += 1;
3283
+ addFailure(stats, "failed_dirs", directory);
3284
+ continue;
3285
+ }
3286
+ const title = basename(directory);
3287
+ const key = qmindFolderSyncIndexKey(parentId, title);
3288
+ desiredKeys.add(key);
3289
+ const mappedId = configuredMapping[directory];
3290
+ const mapped = mappedId === void 0 ? void 0 : remoteById.get(mappedId);
3291
+ const existing = mappedId === void 0 || mapped === void 0 ? remoteIndex.get(key) : mapped;
3292
+ if (existing?.isDir === true && existing.parentSourceId === parentId && existing.title === title) {
3293
+ directoryIds[directory] = existing.id;
3294
+ stats.dirs_reused += 1;
3295
+ continue;
3296
+ }
3297
+ if (existing !== void 0) {
3298
+ stats.dirs_failed += 1;
3299
+ addFailure(stats, "failed_dirs", directory);
3300
+ continue;
3301
+ }
3302
+ if (options.dryRun === true) {
3303
+ directoryIds[directory] = `dry-run:${directory}`;
3304
+ stats.dirs_created += 1;
3305
+ continue;
3306
+ }
3307
+ try {
3308
+ const created = await host.client.createDirectory(notebookId, {
3309
+ parentSourceId: parentId,
3310
+ path: title,
3311
+ title
3312
+ });
3313
+ directoryIds[directory] = created.id;
3314
+ remoteIndex.set(key, created);
3315
+ remoteById.set(created.id, created);
3316
+ stats.dirs_created += 1;
3317
+ } catch {
3318
+ stats.dirs_failed += 1;
3319
+ addFailure(stats, "failed_dirs", directory);
3320
+ }
3321
+ }
3322
+ let savedMappingPath;
3323
+ if (options.directoryIdsPath === void 0 && options.dryRun !== true) {
3324
+ savedMappingPath = options.saveDirectoryIdsPath ?? mappingPath(baseDirectory, options.cacheDirectory);
3325
+ await fileSystem.writeMapping(savedMappingPath, directoryIds);
3326
+ diagnostics.push(`directory mapping saved: ${savedMappingPath}`);
3327
+ }
3328
+ if (options.skipUpload !== true) {
3329
+ const limit = pLimit(concurrency);
3330
+ await Promise.all(tree.files.map((file) => limit(async () => {
3331
+ const absolute = join(baseDirectory, file);
3332
+ const parentPath = posix(dirname(file)) === "." ? "" : posix(dirname(file));
3333
+ const parentId = directoryIds[parentPath] ?? "";
3334
+ if (parentPath.length > 0 && parentId.length === 0) {
3335
+ stats.files_failed += 1;
3336
+ addFailure(stats, "failed_files", file);
3337
+ return;
3338
+ }
3339
+ const title = basename(file);
3340
+ const key = qmindFolderSyncIndexKey(parentId, title);
3341
+ desiredKeys.add(key);
3342
+ const existing = remoteIndex.get(key);
3343
+ if (existing?.isDir === true) {
3344
+ stats.files_failed += 1;
3345
+ addFailure(stats, "failed_files", file);
3346
+ return;
3347
+ }
3348
+ if (existing !== void 0 && !await shouldReplaceQMindFolderFile(mode, existing, absolute, fileSystem)) {
3349
+ stats.files_already_exist += 1;
3350
+ return;
3351
+ }
3352
+ if (options.dryRun === true) {
3353
+ stats.files_uploaded += 1;
3354
+ return;
3355
+ }
3356
+ try {
3357
+ const hash = await fileSystem.sha256(absolute);
3358
+ const uploaded = await withUploadRetry(async () => {
3359
+ const binary = await host.createFileSource(absolute);
3360
+ return await host.client.uploadSource(notebookId, binary, {
3361
+ parentSourceId: parentId,
3362
+ path: file,
3363
+ sha256: hash,
3364
+ title
3365
+ });
3366
+ }, options.sleep ?? defaultSleep);
3367
+ if (existing !== void 0) await host.client.deleteSource(notebookId, existing.id);
3368
+ remoteIndex.set(key, uploaded);
3369
+ remoteById.set(uploaded.id, uploaded);
3370
+ stats.files_uploaded += 1;
3371
+ } catch {
3372
+ stats.files_failed += 1;
3373
+ addFailure(stats, "failed_files", file);
3374
+ }
3375
+ })));
3376
+ }
3377
+ if (options.dryRun !== true) for (const [directory, id] of Object.entries(directoryIds)) {
3378
+ if (id.startsWith("dry-run:")) continue;
3379
+ try {
3380
+ await host.client.updateSource(notebookId, id, {
3381
+ isDir: true,
3382
+ title: basename(directory)
3383
+ });
3384
+ } catch {
3385
+ stats.dirs_refresh_failed += 1;
3386
+ }
3387
+ }
3388
+ const preDeleteFailures = stats.files_failed + stats.dirs_failed;
3389
+ if (options.delete === true && preDeleteFailures === 0) {
3390
+ const deletions = remote.filter((source) => !desiredKeys.has(qmindFolderSyncIndexKey(source.parentSourceId, source.title))).sort((left, right) => {
3391
+ if (left.isDir !== right.isDir) return left.isDir ? 1 : -1;
3392
+ return right.path.split("/").length - left.path.split("/").length;
3393
+ });
3394
+ for (const source of deletions) try {
3395
+ if (options.dryRun !== true) await host.client.deleteSource(notebookId, source.id);
3396
+ if (source.isDir) stats.dirs_deleted = (stats.dirs_deleted ?? 0) + 1;
3397
+ else stats.files_deleted = (stats.files_deleted ?? 0) + 1;
3398
+ } catch {
3399
+ stats.delete_failed = (stats.delete_failed ?? 0) + 1;
3400
+ (stats.failed_deletes ??= []).push(source.path || source.title);
3401
+ }
3402
+ }
3403
+ const exitCode = stats.files_failed > 0 || stats.dirs_failed > 0 || stats.dirs_refresh_failed > 0 || (stats.delete_failed ?? 0) > 0 ? 1 : 0;
3404
+ return {
3405
+ diagnostics: Object.freeze(diagnostics),
3406
+ exitCode,
3407
+ ...savedMappingPath === void 0 ? {} : { mappingPath: savedMappingPath },
3408
+ stats: publicStats(stats)
3409
+ };
3410
+ }
3411
+ //#endregion
3412
+ //#region src/output.ts
3413
+ var TextWriter = class {
3414
+ #chunks = [];
3415
+ line(value = "") {
3416
+ this.#chunks.push(`${value}\n`);
3417
+ }
3418
+ write(value) {
3419
+ this.#chunks.push(value);
3420
+ }
3421
+ toString() {
3422
+ return this.#chunks.join("");
3423
+ }
3424
+ };
3425
+ function parseQMindCliOutputFormat(value) {
3426
+ if (value === "json" || value === "agent" || value === "ndjson") return value;
3427
+ return "table";
3428
+ }
3429
+ function record(value) {
3430
+ return value !== null && typeof value === "object" && !Array.isArray(value) ? value : {};
3431
+ }
3432
+ function stringValue(value) {
3433
+ return value === void 0 || value === null ? "" : String(value);
3434
+ }
3435
+ function numberValue(value) {
3436
+ const parsed = Number(value ?? 0);
3437
+ return Number.isFinite(parsed) ? parsed : 0;
3438
+ }
3439
+ function boolValue(value) {
3440
+ return value === true;
3441
+ }
3442
+ function oneLine(value) {
3443
+ return stringValue(value).replace(/[\n\t]/g, " ").trim().split(/\s+/).filter(Boolean).join(" ");
3444
+ }
3445
+ function unescapeText(value) {
3446
+ return decodeHTML(stringValue(value));
3447
+ }
3448
+ function truncate(value, width) {
3449
+ const characters = [...stringValue(value)];
3450
+ if (characters.length <= width) return characters.join("");
3451
+ if (width <= 3) return characters.slice(0, width).join("");
3452
+ return `${characters.slice(0, width - 3).join("")}...`;
3453
+ }
3454
+ function pad(value, width) {
3455
+ const text = stringValue(value);
3456
+ return `${text}${" ".repeat(Math.max(0, width - [...text].length))}`;
3457
+ }
3458
+ function table(writer, headers, rows, widths, separatorWidth) {
3459
+ writer.line(headers.map((value, index) => pad(value, widths[index] ?? 0)).join(" "));
3460
+ writer.line("-".repeat(separatorWidth));
3461
+ for (const values of rows) writer.line(values.map((value, index) => pad(value, widths[index] ?? 0)).join(" "));
3462
+ }
3463
+ function goTimestamp(value) {
3464
+ if (value === void 0 || value === null || value === "") return null;
3465
+ const text = stringValue(value);
3466
+ const milliseconds = Date.parse(text);
3467
+ if (!Number.isFinite(milliseconds)) return null;
3468
+ const fraction = text.match(/\.(\d{1,9})/)?.[1] ?? "";
3469
+ return {
3470
+ nanos: fraction.length > 0 ? Number(fraction.padEnd(9, "0")) : milliseconds % 1e3 * 1e6,
3471
+ seconds: Math.floor(milliseconds / 1e3)
3472
+ };
3473
+ }
3474
+ function timestamp(value) {
3475
+ const parsed = goTimestamp(value);
3476
+ if (parsed === null || parsed.seconds === 0) return "-";
3477
+ return (/* @__PURE__ */ new Date(parsed.seconds * 1e3)).toISOString().replace(".000Z", "Z");
3478
+ }
3479
+ function optionalMap(value) {
3480
+ const result = record(value);
3481
+ return Object.keys(result).length === 0 ? void 0 : result;
3482
+ }
3483
+ function optionalStrings(value) {
3484
+ return Array.isArray(value) && value.length > 0 ? value.map(stringValue) : void 0;
3485
+ }
3486
+ function pagination(value) {
3487
+ if (value === void 0 || value === null) return "";
3488
+ const page = record(value);
3489
+ if ([
3490
+ page.totalSize,
3491
+ page.currentPage,
3492
+ page.lastPage,
3493
+ page.pageSize
3494
+ ].every((item) => numberValue(item) === 0)) return "";
3495
+ return ` (server totalSize=${numberValue(page.totalSize)}, page=${numberValue(page.currentPage)}/${numberValue(page.lastPage)}, pageSize=${numberValue(page.pageSize)})`;
3496
+ }
3497
+ function goNotebook(value) {
3498
+ const source = record(value);
3499
+ const settings = optionalMap(source.settings);
3500
+ return {
3501
+ id: stringValue(source.id),
3502
+ orgId: stringValue(source.orgId),
3503
+ userId: stringValue(source.userId),
3504
+ title: stringValue(source.title),
3505
+ description: stringValue(source.description),
3506
+ client: stringValue(source.client),
3507
+ status: stringValue(source.status),
3508
+ ...settings === void 0 ? {} : { settings },
3509
+ createdAt: goTimestamp(source.createdAt),
3510
+ updatedAt: goTimestamp(source.updatedAt)
3511
+ };
3512
+ }
3513
+ function goNotebookList(value) {
3514
+ return {
3515
+ notebooks: value.notebooks.map(goNotebook),
3516
+ totalSize: numberValue(value.totalSize),
3517
+ currentPage: numberValue(value.currentPage),
3518
+ pageSize: numberValue(value.pageSize)
3519
+ };
3520
+ }
3521
+ function goPage(value) {
3522
+ if (value === void 0 || value === null) return void 0;
3523
+ const source = record(value);
3524
+ return {
3525
+ prevPage: numberValue(source.prevPage),
3526
+ currentPage: numberValue(source.currentPage),
3527
+ nextPage: numberValue(source.nextPage),
3528
+ lastPage: numberValue(source.lastPage),
3529
+ pageSize: numberValue(source.pageSize),
3530
+ totalSize: numberValue(source.totalSize),
3531
+ ...source.nextToken ? { nextToken: stringValue(source.nextToken) } : {}
3532
+ };
3533
+ }
3534
+ function goSource(value) {
3535
+ const source = record(value);
3536
+ const metadata = optionalMap(source.metadata);
3537
+ return {
3538
+ id: stringValue(source.id),
3539
+ orgId: stringValue(source.orgId),
3540
+ userId: stringValue(source.userId),
3541
+ notebookId: stringValue(source.notebookId),
3542
+ title: stringValue(source.title),
3543
+ sourceType: stringValue(source.sourceType),
3544
+ uri: stringValue(source.uri),
3545
+ status: stringValue(source.status),
3546
+ errorMessage: stringValue(source.errorMessage),
3547
+ parentSourceId: stringValue(source.parentSourceId),
3548
+ path: stringValue(source.path),
3549
+ isDir: boolValue(source.isDir),
3550
+ ...metadata === void 0 ? {} : { metadata },
3551
+ originUrl: stringValue(source.originUrl),
3552
+ createdAt: goTimestamp(source.createdAt),
3553
+ updatedAt: goTimestamp(source.updatedAt)
3554
+ };
3555
+ }
3556
+ function goSourceList(value) {
3557
+ const pages = goPage(value.pages);
3558
+ return {
3559
+ sources: value.sources.map(goSource),
3560
+ ...pages === void 0 ? {} : { pages },
3561
+ totalSize: numberValue(value.totalSize),
3562
+ currentPage: numberValue(value.currentPage),
3563
+ pageSize: numberValue(value.pageSize)
3564
+ };
3565
+ }
3566
+ function goCardLink(value) {
3567
+ const source = record(value);
3568
+ return {
3569
+ linkType: stringValue(source.linkType),
3570
+ targetId: stringValue(source.targetId),
3571
+ ...source.targetTitle ? { targetTitle: stringValue(source.targetTitle) } : {},
3572
+ ...source.description ? { description: stringValue(source.description) } : {}
3573
+ };
3574
+ }
3575
+ function goCard(value) {
3576
+ const source = record(value);
3577
+ const keywords = optionalStrings(source.keywords);
3578
+ const links = Array.isArray(source.links) && source.links.length > 0 ? source.links.map(goCardLink) : void 0;
3579
+ return {
3580
+ id: stringValue(source.id),
3581
+ ...source.knowledgeId ? { knowledgeId: stringValue(source.knowledgeId) } : {},
3582
+ orgId: stringValue(source.orgId),
3583
+ ...source.notebookId ? { notebookId: stringValue(source.notebookId) } : {},
3584
+ source: stringValue(source.source),
3585
+ ...source.cardType ? { cardType: stringValue(source.cardType) } : {},
3586
+ ...source.category ? { category: stringValue(source.category) } : {},
3587
+ ...source.repo ? { repo: stringValue(source.repo) } : {},
3588
+ title: stringValue(source.title),
3589
+ content: stringValue(source.content),
3590
+ ...keywords === void 0 ? {} : { keywords },
3591
+ ...links === void 0 ? {} : { links },
3592
+ ...source.extra ? { extra: stringValue(source.extra) } : {},
3593
+ createdAt: goTimestamp(source.createdAt),
3594
+ updatedAt: goTimestamp(source.updatedAt)
3595
+ };
3596
+ }
3597
+ function goCardList(value) {
3598
+ return {
3599
+ cards: value.cards.map(goCard),
3600
+ totalSize: numberValue(value.totalSize),
3601
+ currentPage: numberValue(value.currentPage),
3602
+ pageSize: numberValue(value.pageSize)
3603
+ };
3604
+ }
3605
+ function goRetrieve(value) {
3606
+ return {
3607
+ chunks: value.chunks.map((chunk) => ({
3608
+ sourceId: chunk.sourceId,
3609
+ sourceTitle: chunk.sourceTitle,
3610
+ sourceUri: chunk.sourceUri,
3611
+ chunkId: chunk.chunkId,
3612
+ chunkIndex: chunk.chunkIndex,
3613
+ content: chunk.content,
3614
+ tokenCount: chunk.tokenCount,
3615
+ ...chunk.metadata === void 0 ? {} : { metadata: chunk.metadata },
3616
+ score: chunk.score,
3617
+ ...chunk.originUrl === void 0 ? {} : { originUrl: chunk.originUrl }
3618
+ })),
3619
+ citations: value.citations.map((citation) => ({
3620
+ sourceId: citation.sourceId,
3621
+ sourceTitle: citation.sourceTitle,
3622
+ sourceUri: citation.sourceUri,
3623
+ chunkId: citation.chunkId,
3624
+ chunkIndex: citation.chunkIndex,
3625
+ snippet: citation.snippet,
3626
+ ...citation.metadata === void 0 ? {} : { metadata: citation.metadata },
3627
+ ...citation.originUrl === void 0 ? {} : { originUrl: citation.originUrl }
3628
+ })),
3629
+ promptContext: value.promptContext,
3630
+ total: value.total,
3631
+ reranked: value.reranked,
3632
+ cards: value.cards.map((card) => ({
3633
+ id: card.id,
3634
+ title: card.title,
3635
+ content: card.content,
3636
+ ...card.category === void 0 ? {} : { category: card.category },
3637
+ ...card.cardType === void 0 ? {} : { cardType: card.cardType },
3638
+ keywords: card.keywords,
3639
+ ...card.links.length === 0 ? {} : { links: card.links },
3640
+ score: card.score,
3641
+ depth: card.depth
3642
+ }))
3643
+ };
3644
+ }
3645
+ function agentField(writer, name, value) {
3646
+ if (value === void 0 || value === null) return;
3647
+ const text = stringValue(value);
3648
+ if (text === "" || text === "-") return;
3649
+ writer.line(`${name}: ${oneLine(text)}`);
3650
+ }
3651
+ function agentText(writer, name, value) {
3652
+ const text = stringValue(value);
3653
+ if (text.length === 0) return;
3654
+ writer.line(`${name}: |-`);
3655
+ for (const line of text.split("\n")) writer.line(` ${line}`);
3656
+ }
3657
+ function agentHeader(writer, kind, total) {
3658
+ writer.line(`kind: ${kind}`);
3659
+ writer.line(`total: ${total}`);
3660
+ }
3661
+ function renderAgentNotebook(writer, value, index = 0) {
3662
+ writer.line("---");
3663
+ agentField(writer, "type", "notebook");
3664
+ if (index > 0) agentField(writer, "index", index);
3665
+ agentField(writer, "id", value.id);
3666
+ agentField(writer, "orgId", value.orgId);
3667
+ agentField(writer, "userId", value.userId);
3668
+ agentField(writer, "status", value.status);
3669
+ agentField(writer, "title", value.title);
3670
+ agentField(writer, "createdAt", timestamp(value.createdAt));
3671
+ agentField(writer, "updatedAt", timestamp(value.updatedAt));
3672
+ agentText(writer, "description", value.description);
3673
+ }
3674
+ function renderAgentSource(writer, value, index = 0) {
3675
+ writer.line("---");
3676
+ agentField(writer, "type", "source");
3677
+ if (index > 0) agentField(writer, "index", index);
3678
+ agentField(writer, "id", value.id);
3679
+ agentField(writer, "notebookId", value.notebookId);
3680
+ agentField(writer, "sourceType", value.sourceType);
3681
+ agentField(writer, "status", value.status);
3682
+ agentField(writer, "title", value.title);
3683
+ agentField(writer, "uri", value.uri);
3684
+ agentField(writer, "path", value.path);
3685
+ agentField(writer, "isDir", value.isDir);
3686
+ agentField(writer, "parentSourceId", value.parentSourceId);
3687
+ agentField(writer, "createdAt", timestamp(value.createdAt));
3688
+ agentField(writer, "updatedAt", timestamp(value.updatedAt));
3689
+ agentText(writer, "error", value.errorMessage);
3690
+ }
3691
+ function renderAgentCard(writer, value, index, score) {
3692
+ writer.line("---");
3693
+ agentField(writer, "type", "card");
3694
+ if (index > 0) agentField(writer, "index", index);
3695
+ if (score !== void 0) agentField(writer, "score", score.toFixed(4));
3696
+ agentField(writer, "id", value.id);
3697
+ agentField(writer, "knowledgeId", value.knowledgeId);
3698
+ agentField(writer, "notebookId", value.notebookId);
3699
+ agentField(writer, "source", value.source);
3700
+ agentField(writer, "cardType", value.cardType);
3701
+ agentField(writer, "category", value.category);
3702
+ agentField(writer, "repo", value.repo);
3703
+ agentField(writer, "title", value.title);
3704
+ if (value.keywords.length > 0) agentField(writer, "keywords", value.keywords.join(", "));
3705
+ agentField(writer, "createdAt", timestamp(value.createdAt));
3706
+ agentField(writer, "updatedAt", timestamp(value.updatedAt));
3707
+ agentText(writer, "content", value.content);
3708
+ if (value.links.length > 0) {
3709
+ writer.line("links:");
3710
+ for (const link of value.links) {
3711
+ writer.line(` - targetId: ${link.targetId}`);
3712
+ agentField(writer, " linkType", link.linkType);
3713
+ agentField(writer, " description", link.description);
3714
+ }
3715
+ }
3716
+ }
3717
+ function json(value, pretty) {
3718
+ return `${JSON.stringify(value, null, pretty ? 2 : 0)}\n`;
3719
+ }
3720
+ function renderNotebook(value, format) {
3721
+ if (format === "json") return json(goNotebook(value), true);
3722
+ if (format === "ndjson") return json(goNotebook(value), false);
3723
+ const writer = new TextWriter();
3724
+ if (format === "agent") renderAgentNotebook(writer, value);
3725
+ else {
3726
+ writer.line(`ID: ${value.id}`);
3727
+ writer.line(`Title: ${value.title}`);
3728
+ writer.line(`Status: ${value.status}`);
3729
+ writer.line(`OrgID: ${value.orgId}`);
3730
+ writer.line(`UserID: ${value.userId}`);
3731
+ writer.line(`CreatedAt: ${timestamp(value.createdAt)}`);
3732
+ writer.line(`UpdatedAt: ${timestamp(value.updatedAt)}`);
3733
+ if (value.description.length > 0) writer.line(`\n${value.description}`);
3734
+ }
3735
+ return writer.toString();
3736
+ }
3737
+ function renderNotebookList(value, format) {
3738
+ if (format === "json") return json(goNotebookList(value), true);
3739
+ if (format === "ndjson") return value.notebooks.map((item) => json(goNotebook(item), false)).join("");
3740
+ const writer = new TextWriter();
3741
+ if (format === "agent") {
3742
+ agentHeader(writer, "notebooks", value.notebooks.length);
3743
+ value.notebooks.forEach((item, index) => renderAgentNotebook(writer, item, index + 1));
3744
+ } else if (value.notebooks.length === 0) writer.line("No notebooks found.");
3745
+ else {
3746
+ writer.line(`Total: ${value.notebooks.length}`);
3747
+ writer.line();
3748
+ table(writer, [
3749
+ "ID",
3750
+ "STATUS",
3751
+ "TITLE",
3752
+ "USER",
3753
+ "UPDATED"
3754
+ ], value.notebooks.map((item) => [
3755
+ truncate(item.id, 36),
3756
+ truncate(item.status, 10),
3757
+ truncate(item.title, 28),
3758
+ truncate(item.userId, 18),
3759
+ timestamp(item.updatedAt)
3760
+ ]), [
3761
+ 36,
3762
+ 10,
3763
+ 28,
3764
+ 18,
3765
+ 20
3766
+ ], 120);
3767
+ }
3768
+ return writer.toString();
3769
+ }
3770
+ function renderSource(value, format) {
3771
+ if (format === "json") return json(goSource(value), true);
3772
+ if (format === "ndjson") return json(goSource(value), false);
3773
+ const writer = new TextWriter();
3774
+ if (format === "agent") renderAgentSource(writer, value);
3775
+ else {
3776
+ writer.line(`ID: ${value.id}`);
3777
+ writer.line(`NotebookID: ${value.notebookId}`);
3778
+ writer.line(`Title: ${value.title}`);
3779
+ writer.line(`Type: ${value.sourceType}`);
3780
+ writer.line(`Status: ${value.status}`);
3781
+ writer.line(`URI: ${value.uri}`);
3782
+ if (value.path.length > 0) writer.line(`Path: ${value.path}`);
3783
+ if (value.errorMessage.length > 0) writer.line(`Error: ${value.errorMessage}`);
3784
+ writer.line(`UpdatedAt: ${timestamp(value.updatedAt)}`);
3785
+ }
3786
+ return writer.toString();
3787
+ }
3788
+ function renderSourceList(value, format) {
3789
+ if (format === "json") return json(goSourceList(value), true);
3790
+ if (format === "ndjson") return value.sources.map((item) => json(goSource(item), false)).join("");
3791
+ const writer = new TextWriter();
3792
+ if (format === "agent") {
3793
+ agentHeader(writer, "sources", value.sources.length);
3794
+ const summary = pagination(value.pages);
3795
+ if (summary.length > 0) writer.line(`pagination:${summary}`);
3796
+ value.sources.forEach((item, index) => renderAgentSource(writer, item, index + 1));
3797
+ } else if (value.sources.length === 0) writer.line("No sources found.");
3798
+ else {
3799
+ writer.line(`Total: ${value.sources.length}${pagination(value.pages)}`);
3800
+ writer.line();
3801
+ table(writer, [
3802
+ "ID",
3803
+ "TYPE",
3804
+ "STATUS",
3805
+ "TITLE",
3806
+ "URI"
3807
+ ], value.sources.map((item) => [
3808
+ truncate(item.id, 36),
3809
+ truncate(item.sourceType, 10),
3810
+ truncate(item.status, 10),
3811
+ truncate(item.title, 30),
3812
+ truncate(item.uri || item.path, 28)
3813
+ ]), [
3814
+ 36,
3815
+ 10,
3816
+ 10,
3817
+ 30,
3818
+ 28
3819
+ ], 120);
3820
+ }
3821
+ return writer.toString();
3822
+ }
3823
+ function renderCardList(value, format) {
3824
+ if (format === "json") return json(goCardList(value), true);
3825
+ if (format === "ndjson") return value.cards.map((item) => json(goCard(item), false)).join("");
3826
+ const writer = new TextWriter();
3827
+ if (format === "agent") {
3828
+ agentHeader(writer, "cards", value.totalSize || value.cards.length);
3829
+ value.cards.forEach((item, index) => renderAgentCard(writer, item, index + 1));
3830
+ } else if (value.cards.length === 0) writer.line("No cards found.");
3831
+ else {
3832
+ writer.line(`Total: ${value.totalSize || value.cards.length}`);
3833
+ writer.line();
3834
+ table(writer, [
3835
+ "ID",
3836
+ "TYPE",
3837
+ "CATEGORY",
3838
+ "TITLE",
3839
+ "LINKS",
3840
+ "UPDATED"
3841
+ ], value.cards.map((item) => [
3842
+ truncate(item.id, 36),
3843
+ truncate(item.cardType, 8),
3844
+ truncate(item.category, 16),
3845
+ truncate(item.title, 30),
3846
+ item.links.length,
3847
+ timestamp(item.updatedAt)
3848
+ ]), [
3849
+ 36,
3850
+ 8,
3851
+ 16,
3852
+ 30,
3853
+ 5,
3854
+ 20
3855
+ ], 125);
3856
+ }
3857
+ return writer.toString();
3858
+ }
3859
+ function renderRetrieve(value, format) {
3860
+ if (format === "json") return json(goRetrieve(value), true);
3861
+ if (format === "ndjson") return [...value.chunks.map((item) => ({
3862
+ kind: "chunk",
3863
+ ...item
3864
+ })), ...value.cards.map((item) => ({
3865
+ kind: "card",
3866
+ ...item
3867
+ }))].map((item) => json(item, false)).join("");
3868
+ const writer = new TextWriter();
3869
+ if (format === "agent") {
3870
+ agentHeader(writer, "notebookContext", value.total);
3871
+ agentField(writer, "reranked", value.reranked);
3872
+ value.chunks.forEach((chunk, index) => {
3873
+ writer.line("---");
3874
+ agentField(writer, "type", "chunk");
3875
+ agentField(writer, "index", index + 1);
3876
+ agentField(writer, "chunkId", chunk.chunkId);
3877
+ agentField(writer, "chunkIndex", chunk.chunkIndex);
3878
+ agentField(writer, "sourceId", chunk.sourceId);
3879
+ agentField(writer, "sourceTitle", chunk.sourceTitle);
3880
+ agentField(writer, "sourceUri", chunk.sourceUri);
3881
+ agentField(writer, "score", chunk.score.toFixed(4));
3882
+ agentField(writer, "tokenCount", chunk.tokenCount);
3883
+ agentText(writer, "content", unescapeText(chunk.content));
3884
+ });
3885
+ value.cards.forEach((card, index) => {
3886
+ writer.line("---");
3887
+ agentField(writer, "type", "retrievedCard");
3888
+ agentField(writer, "index", index + 1);
3889
+ agentField(writer, "id", card.id);
3890
+ agentField(writer, "title", card.title);
3891
+ agentField(writer, "category", card.category);
3892
+ agentField(writer, "cardType", card.cardType);
3893
+ agentField(writer, "score", card.score.toFixed(4));
3894
+ agentField(writer, "depth", card.depth);
3895
+ if (card.keywords.length > 0) agentField(writer, "keywords", card.keywords.join(", "));
3896
+ agentText(writer, "content", unescapeText(card.content));
3897
+ });
3898
+ value.citations.forEach((citation, index) => {
3899
+ writer.line("---");
3900
+ agentField(writer, "type", "citation");
3901
+ agentField(writer, "index", index + 1);
3902
+ agentField(writer, "sourceId", citation.sourceId);
3903
+ agentField(writer, "sourceTitle", citation.sourceTitle);
3904
+ agentField(writer, "chunkId", citation.chunkId);
3905
+ agentText(writer, "snippet", unescapeText(citation.snippet));
3906
+ });
3907
+ agentText(writer, "promptContext", unescapeText(value.promptContext));
3908
+ return writer.toString();
3909
+ }
3910
+ writer.line(`Total chunks: ${value.total} Reranked: ${value.reranked}`);
3911
+ writer.line();
3912
+ if (value.chunks.length > 0) {
3913
+ writer.line("== Chunks ==");
3914
+ table(writer, [
3915
+ "SCORE",
3916
+ "CHUNK_ID",
3917
+ "IDX",
3918
+ "SOURCE",
3919
+ "CONTENT"
3920
+ ], value.chunks.map((chunk) => [
3921
+ chunk.score.toFixed(2),
3922
+ truncate(chunk.chunkId, 36),
3923
+ chunk.chunkIndex,
3924
+ truncate(chunk.sourceTitle, 30),
3925
+ truncate(unescapeText(chunk.content).replace(/\n/g, " "), 50)
3926
+ ]), [
3927
+ 6,
3928
+ 36,
3929
+ 6,
3930
+ 30,
3931
+ 50
3932
+ ], 130);
3933
+ writer.line();
3934
+ value.chunks.forEach((chunk, index) => {
3935
+ writer.line(`[${index + 1}] chunkId=${chunk.chunkId} sourceId=${chunk.sourceId} sourceTitle=${chunk.sourceTitle} score=${chunk.score.toFixed(4)} tokens=${chunk.tokenCount}`);
3936
+ if (chunk.sourceUri.length > 0) writer.line(` uri: ${chunk.sourceUri}`);
3937
+ if (chunk.content.length > 0) writer.line(` content: ${unescapeText(chunk.content)}`);
3938
+ writer.line();
3939
+ });
3940
+ }
3941
+ if (value.cards.length > 0) {
3942
+ writer.line("== Cards ==");
3943
+ table(writer, [
3944
+ "SCORE",
3945
+ "DEPTH",
3946
+ "ID",
3947
+ "CATEGORY",
3948
+ "TITLE",
3949
+ "CONTENT"
3950
+ ], value.cards.map((card) => [
3951
+ card.score.toFixed(2),
3952
+ card.depth,
3953
+ truncate(card.id, 36),
3954
+ truncate(card.category, 20),
3955
+ truncate(card.title, 30),
3956
+ truncate(unescapeText(card.content).replace(/\n/g, " "), 30)
3957
+ ]), [
3958
+ 6,
3959
+ 5,
3960
+ 36,
3961
+ 20,
3962
+ 30,
3963
+ 30
3964
+ ], 130);
3965
+ writer.line();
3966
+ }
3967
+ if (value.citations.length > 0) {
3968
+ writer.line("== Citations ==");
3969
+ value.citations.forEach((citation, index) => {
3970
+ writer.line(`[${index + 1}] sourceId=${citation.sourceId} sourceTitle=${citation.sourceTitle} chunkId=${citation.chunkId}`);
3971
+ if (citation.snippet.length > 0) writer.line(` snippet: ${oneLine(unescapeText(citation.snippet))}`);
3972
+ });
3973
+ writer.line();
3974
+ }
3975
+ if (value.promptContext.length > 0) writer.line(`== Prompt Context ==\n${unescapeText(value.promptContext)}`);
3976
+ return writer.toString();
3977
+ }
3978
+ function renderQMindCliPresentation(presentation, format) {
3979
+ switch (presentation.kind) {
3980
+ case "notebook": return renderNotebook(presentation.value, format);
3981
+ case "notebookList": return renderNotebookList(presentation.value, format);
3982
+ case "source": return renderSource(presentation.value, format);
3983
+ case "sourceList": return renderSourceList(presentation.value, format);
3984
+ case "cardList": return renderCardList(presentation.value, format);
3985
+ case "retrieve": return renderRetrieve(presentation.value, format);
3986
+ case "imageUpload": return json({
3987
+ uploadUrl: presentation.value.uploadUrl,
3988
+ method: presentation.value.method,
3989
+ ...Object.keys(presentation.value.headers).length === 0 ? {} : { headers: presentation.value.headers },
3990
+ ossKey: presentation.value.ossKey,
3991
+ ...presentation.value.expiresAt === void 0 ? {} : { expiresAt: presentation.value.expiresAt }
3992
+ }, format !== "ndjson");
3993
+ case "imageUrls": return json(presentation.value, format !== "ndjson");
3994
+ case "json": return json(presentation.value, format !== "ndjson");
3995
+ case "text": return presentation.value;
3996
+ }
3997
+ }
3998
+ function renderQMindCliError(error, jsonOutput) {
3999
+ const source = error instanceof QMindError ? {
4000
+ code: error.code,
4001
+ details: error.details,
4002
+ message: error.message,
4003
+ rawBody: error.rawBody,
4004
+ requestId: error.requestId,
4005
+ status: error.status,
4006
+ upstreamCode: error.upstreamCode
4007
+ } : {
4008
+ code: "INTERNAL_ERROR",
4009
+ details: void 0,
4010
+ message: error instanceof Error ? error.message : String(error),
4011
+ rawBody: void 0,
4012
+ requestId: void 0,
4013
+ status: void 0,
4014
+ upstreamCode: void 0
4015
+ };
4016
+ const exitCode = source.code === "INVALID_ARGUMENT" ? 2 : 1;
4017
+ if (!jsonOutput) return {
4018
+ exitCode,
4019
+ stderr: `error: ${source.message}\n`,
4020
+ stdout: ""
4021
+ };
4022
+ return {
4023
+ exitCode,
4024
+ stderr: "",
4025
+ stdout: json({ error: {
4026
+ code: source.code,
4027
+ message: source.message,
4028
+ status: source.status,
4029
+ errorCode: source.upstreamCode,
4030
+ requestId: source.requestId,
4031
+ rawBody: source.rawBody,
4032
+ details: source.details
4033
+ } }, true)
4034
+ };
4035
+ }
4036
+ function compatibilityQuestionEvent(event) {
4037
+ const goMessage = (message) => ({
4038
+ id: message.id,
4039
+ orgId: "",
4040
+ userId: "",
4041
+ notebookId: "",
4042
+ chatSessionId: "",
4043
+ role: message.role,
4044
+ content: message.content,
4045
+ sequence: 0,
4046
+ model: "",
4047
+ createdAt: null,
4048
+ updatedAt: null,
4049
+ sceneType: ""
4050
+ });
4051
+ switch (event.type) {
4052
+ case "messageStart": return {
4053
+ eventType: "message_start",
4054
+ ...event.userMessage === void 0 ? {} : { userMessage: goMessage(event.userMessage) },
4055
+ ...event.assistantMessageId === void 0 ? {} : { assistantMessageId: event.assistantMessageId },
4056
+ ...event.citations.length === 0 ? {} : { citations: event.citations },
4057
+ ...event.totalContextChunks === 0 ? {} : { totalContextChunks: event.totalContextChunks },
4058
+ ...event.reranked ? { reranked: true } : {}
4059
+ };
4060
+ case "toolCallStart": return {
4061
+ eventType: "tool_call_start",
4062
+ toolName: event.toolName,
4063
+ toolArguments: event.toolArguments,
4064
+ step: event.step
4065
+ };
4066
+ case "toolCallResult": return {
4067
+ eventType: "tool_call_result",
4068
+ toolName: event.toolName,
4069
+ toolResult: event.toolResult,
4070
+ step: event.step
4071
+ };
4072
+ case "delta": return {
4073
+ eventType: "delta",
4074
+ delta: event.delta
4075
+ };
4076
+ case "batchProgress": return {
4077
+ eventType: "batch_progress",
4078
+ metadata: event.metadata
4079
+ };
4080
+ case "heartbeat": return {
4081
+ eventType: "heartbeat",
4082
+ metadata: event.metadata
4083
+ };
4084
+ case "cancel": return {
4085
+ eventType: "cancel",
4086
+ canceled: true,
4087
+ ...event.cancelReason === void 0 ? {} : { cancelReason: event.cancelReason }
4088
+ };
4089
+ case "messageComplete": return {
4090
+ eventType: "message_complete",
4091
+ ...event.assistantMessage === void 0 ? {} : { assistantMessage: goMessage(event.assistantMessage) },
4092
+ ...event.citations.length === 0 ? {} : { citations: event.citations },
4093
+ ...event.promptContext.length === 0 ? {} : { promptContext: event.promptContext },
4094
+ ...event.totalContextChunks === 0 ? {} : { totalContextChunks: event.totalContextChunks },
4095
+ ...event.reranked ? { reranked: true } : {},
4096
+ ...event.canceled ? { canceled: true } : {},
4097
+ ...event.cancelReason === void 0 ? {} : { cancelReason: event.cancelReason },
4098
+ ...Object.keys(event.metadata).length === 0 ? {} : { metadata: event.metadata }
4099
+ };
4100
+ case "error": return {
4101
+ eventType: "error",
4102
+ errorMessage: event.message
4103
+ };
4104
+ }
4105
+ }
4106
+ function batchProgress(metadata) {
4107
+ const value = (key, fallback = "?") => metadata[key] || fallback;
4108
+ const index = () => {
4109
+ const raw = metadata.batchIndex;
4110
+ const parsed = Number(raw);
4111
+ return Number.isInteger(parsed) ? String(parsed + 1) : raw || "?";
4112
+ };
4113
+ switch (metadata.phase) {
4114
+ case "plan": return `[compile] 📋 plan: ${value("totalBatches")} batches, ${value("totalSources")} sources, ${value("totalBytes")} bytes`;
4115
+ case "batch_start": return `[compile] ▶ batch ${index()}/${value("totalBatches")} start (sources=${value("batchSources")}, bytes=${value("batchBytes")}, prepared=${value("preparedCardsTotal", "0")})`;
4116
+ case "batch_done": return `[compile] ✔ batch ${index()}/${value("totalBatches")} done (+${value("preparedCardsDelta", "0")} cards, total=${value("preparedCardsTotal", "0")}, steps=${value("batchStepsUsed")}, ${value("elapsedMs")}ms)`;
4117
+ case "persist_start": return `[compile] 💾 persist start (drafts=${value("draftsTotal")})`;
4118
+ case "persist_done": return metadata.saved === "true" ? `[compile] ✅ persisted ${value("upsertedCards", "0")} cards (failed=${value("failedCards", "0")})` : `[compile] ⚠️ persist failed: ${metadata.persistError || "not persisted"} (drafts retained for fallback save)`;
4119
+ default: return;
4120
+ }
4121
+ }
4122
+ /** Stateful only for terminal layout; it never owns the SDK stream or writes I/O. */
4123
+ function createQMindCliStreamRenderer(format) {
4124
+ let hasDelta = false;
4125
+ return {
4126
+ finish() {
4127
+ if (!hasDelta) return "";
4128
+ hasDelta = false;
4129
+ return "\n";
4130
+ },
4131
+ push(event) {
4132
+ const compatible = compatibilityQuestionEvent(event);
4133
+ if (format === "json") return json(compatible, true);
4134
+ if (format === "ndjson") return json(compatible, false);
4135
+ const writer = new TextWriter();
4136
+ const breakDelta = () => {
4137
+ if (hasDelta) writer.line();
4138
+ hasDelta = false;
4139
+ };
4140
+ switch (event.type) {
4141
+ case "toolCallStart":
4142
+ breakDelta();
4143
+ if (format === "agent") writer.line(`[tool] ${event.toolName}`);
4144
+ else {
4145
+ writer.line(`[step ${event.step}] tool: ${event.toolName}`);
4146
+ if (event.toolArguments.length > 0) writer.line(` args: ${event.toolArguments}`);
4147
+ }
4148
+ break;
4149
+ case "toolCallResult":
4150
+ if (format !== "agent") writer.line(` result: ${truncate(event.toolResult, 120)}`);
4151
+ break;
4152
+ case "batchProgress": {
4153
+ breakDelta();
4154
+ const line = batchProgress(event.metadata);
4155
+ if (line !== void 0) writer.line(line);
4156
+ break;
4157
+ }
4158
+ case "delta":
4159
+ writer.write(event.delta);
4160
+ hasDelta = true;
4161
+ break;
4162
+ case "messageComplete":
4163
+ breakDelta();
4164
+ if (format === "agent" && event.assistantMessage?.content !== void 0) writer.line(event.assistantMessage.content);
4165
+ if (format !== "agent" && event.citations.length > 0) {
4166
+ writer.line();
4167
+ writer.line("Citations:");
4168
+ event.citations.forEach((citation, index) => writer.line(` [${index + 1}] ${citation}`));
4169
+ }
4170
+ break;
4171
+ case "cancel":
4172
+ breakDelta();
4173
+ writer.line(`[canceled] ${event.cancelReason ?? ""}`.trimEnd());
4174
+ break;
4175
+ case "error":
4176
+ breakDelta();
4177
+ writer.line(`error: ${event.message}`);
4178
+ }
4179
+ return writer.toString();
4180
+ }
4181
+ };
4182
+ }
4183
+ //#endregion
4184
+ //#region src/application.ts
4185
+ const distribution = CLI_DISTRIBUTIONS["cn"];
4186
+ function processIO() {
4187
+ return Object.freeze({
4188
+ stdinIsTTY: process.stdin.isTTY === true,
4189
+ stderrIsTTY: process.stderr.isTTY === true,
4190
+ stderr(value) {
4191
+ process.stderr.write(value);
4192
+ },
4193
+ stdout(value) {
4194
+ process.stdout.write(value);
4195
+ }
4196
+ });
4197
+ }
4198
+ function interactiveConfirm(io) {
4199
+ return Object.freeze({ async confirm(message) {
4200
+ if (!io.stdinIsTTY || !io.stderrIsTTY) return false;
4201
+ const prompt = createInterface({
4202
+ input: process.stdin,
4203
+ output: process.stderr
4204
+ });
4205
+ try {
4206
+ return (await prompt.question(message)).trim().toLowerCase() === "y";
4207
+ } finally {
4208
+ prompt.close();
4209
+ }
4210
+ } });
4211
+ }
4212
+ function stringOption(options, key) {
4213
+ const value = options[key];
4214
+ return typeof value === "string" ? value : void 0;
4215
+ }
4216
+ function optionalString(options, key) {
4217
+ const value = stringOption(options, key)?.trim();
4218
+ return value === void 0 || value.length === 0 ? void 0 : value;
4219
+ }
4220
+ function requiredString(value, label) {
4221
+ if (typeof value !== "string" || value.trim().length === 0) throw new QMindError("INVALID_ARGUMENT", `${label} is required`);
4222
+ return value;
4223
+ }
4224
+ function integer(value) {
4225
+ if (!/^[+-]?\d+$/.test(value)) throw new QMindError("INVALID_ARGUMENT", `invalid integer value: ${JSON.stringify(value)}`);
4226
+ const parsed = Number(value);
4227
+ if (!Number.isSafeInteger(parsed)) throw new QMindError("INVALID_ARGUMENT", `integer value is out of range: ${value}`);
4228
+ return parsed;
4229
+ }
4230
+ function numberOption(options, key, fallback) {
4231
+ const value = options[key];
4232
+ return typeof value === "number" ? value : fallback;
4233
+ }
4234
+ function positiveNumberOption(options, key, fallback) {
4235
+ const value = numberOption(options, key, fallback);
4236
+ return value > 0 ? value : void 0;
4237
+ }
4238
+ function splitCsv(value) {
4239
+ return value?.split(",").map((item) => item.trim()).filter(Boolean) ?? [];
4240
+ }
4241
+ function formatOf(options) {
4242
+ return parseQMindCliOutputFormat(String(options.format ?? "table"));
4243
+ }
4244
+ function commonFlags(options) {
4245
+ const client = optionalString(options, "client");
4246
+ const dashboard = stringOption(options, "dashboard");
4247
+ const sash = stringOption(options, "sash");
4248
+ const env = stringOption(options, "env");
4249
+ const token = optionalString(options, "token");
4250
+ return {
4251
+ ...client === void 0 ? {} : { client },
4252
+ ...env === void 0 ? {} : { env },
4253
+ ...dashboard === void 0 ? {} : { dashboard },
4254
+ ...options.nonInteractive === true ? { nonInteractive: true } : {},
4255
+ ...sash === void 0 ? {} : { sash },
4256
+ ...token === void 0 ? {} : { token }
4257
+ };
4258
+ }
4259
+ async function hostFor(context, options) {
4260
+ if (context.host !== void 0) return context.host;
4261
+ const configuration = cliHostConfiguration("cn", commonFlags(options), context.hostOptions.environment ?? process.env, context.hostOptions.homeDirectory);
4262
+ const adapters = {
4263
+ ...context.hostOptions.adapters,
4264
+ secretStore: createNoopQMindSecretStore(),
4265
+ reporter: context.hostOptions.adapters?.reporter ?? {
4266
+ info(message) {
4267
+ context.io.stderr(`${message}\n`);
4268
+ },
4269
+ warn(message) {
4270
+ context.io.stderr(`${message}\n`);
4271
+ }
4272
+ }
4273
+ };
4274
+ context.host = await context.hostFactory({
4275
+ ...context.hostOptions,
4276
+ ...configuration,
4277
+ adapters,
4278
+ stdinIsTTY: context.io.stdinIsTTY
4279
+ });
4280
+ return context.host;
4281
+ }
4282
+ function render(context, options, presentation) {
4283
+ context.io.stdout(renderQMindCliPresentation(presentation, formatOf(options)));
4284
+ }
4285
+ function withCommon(command, output = true) {
4286
+ command.option("--token <token>", "Bearer access token").option("--env <name>", "product environment: prod (default), test, or daily where supported").option("--sash <url>", "custom debug Sash origin (requires --dashboard; excludes --env)").option("--dashboard <url>", "custom debug Dashboard origin (requires --sash; excludes --env)").option("--org <id>", "organization ID (deprecated and ignored)").option("--client <name>", "originating client identifier").option("--non-interactive", "never open a browser when credentials are missing");
4287
+ if (output) command.option("--format <format>", "output format: table | json | agent | ndjson", "table");
4288
+ return command;
4289
+ }
4290
+ function registerCardQueryCommand(context, command) {
4291
+ withCommon(command).option("--nb <id>", "notebook ID (required)").option("-q, --query <query>", "search query", "").option("--cat <category>", "category filter", "").option("--page <number>", "page number", integer, 0).option("--page-size <number>", "page size", integer, 20).action(async (raw) => {
4292
+ const host = await hostFor(context, raw);
4293
+ const page = positiveNumberOption(raw, "page", 0);
4294
+ const pageSize = positiveNumberOption(raw, "pageSize", 20);
4295
+ render(context, raw, {
4296
+ kind: "cardList",
4297
+ value: await host.client.listCards(requiredString(raw.nb, "--nb"), {
4298
+ category: String(raw.cat ?? ""),
4299
+ ...page === void 0 ? {} : { page },
4300
+ ...pageSize === void 0 ? {} : { pageSize },
4301
+ query: String(raw.query ?? "")
4302
+ })
4303
+ });
4304
+ });
4305
+ }
4306
+ function registerCoreCommands(context, program) {
4307
+ withCommon(program.command("login").description("Authorize via browser and save credentials"), false).option("--client-id <id>", "OAuth App client_id").action(async (raw) => {
4308
+ const host = await hostFor(context, raw);
4309
+ const clientId = optionalString(raw, "clientId");
4310
+ const result = await host.login({ ...clientId === void 0 ? {} : { clientId } });
4311
+ context.io.stdout(`Logged in successfully [${result.sashOrigin} -> ${host.config.dashboardOrigin}]\nToken saved to ${host.config.homeDirectory}/credentials.json\nDevice token expires at: ${result.expiresAt}\n`);
4312
+ });
4313
+ withCommon(program.command("logout").description("Remove saved credentials"), false).action(async (raw) => {
4314
+ await (await hostFor(context, raw)).logout();
4315
+ context.io.stdout("Logged out. Credentials removed.\n");
4316
+ });
4317
+ const notebook = program.command("notebook").description("Manage notebooks");
4318
+ withCommon(notebook.command("create").description("Create a notebook")).option("--title <title>", "notebook title (required)").option("--desc <description>", "notebook description", "").action(async (raw) => {
4319
+ render(context, raw, {
4320
+ kind: "notebook",
4321
+ value: await (await hostFor(context, raw)).client.createNotebook({
4322
+ description: String(raw.desc ?? ""),
4323
+ title: requiredString(raw.title, "--title")
4324
+ })
4325
+ });
4326
+ });
4327
+ withCommon(notebook.command("list").description("List notebooks")).option("--filter-client <name>", "filter by originating client", "").action(async (raw) => {
4328
+ const host = await hostFor(context, raw);
4329
+ const client = optionalString(raw, "filterClient");
4330
+ render(context, raw, {
4331
+ kind: "notebookList",
4332
+ value: await host.client.listNotebooks(client === void 0 ? {} : { client })
4333
+ });
4334
+ });
4335
+ withCommon(notebook.command("get <notebook_id>").description("Get a notebook")).action(async (notebookId, raw) => {
4336
+ render(context, raw, {
4337
+ kind: "notebook",
4338
+ value: await (await hostFor(context, raw)).client.getNotebook(notebookId)
4339
+ });
4340
+ });
4341
+ withCommon(notebook.command("delete <notebook_id>").description("Delete a notebook"), false).action(async (notebookId, raw) => {
4342
+ await (await hostFor(context, raw)).client.deleteNotebook(notebookId);
4343
+ context.io.stdout("deleted\n");
4344
+ });
4345
+ const cards = program.command("cards").description("Manage notebook cards");
4346
+ for (const action of ["list", "search"]) {
4347
+ const description = action === "search" ? "Search notebook cards" : "List notebook cards";
4348
+ registerCardQueryCommand(context, cards.command(action).description(description));
4349
+ registerCardQueryCommand(context, program.command(action).description(`${description} (alias for cards ${action})`));
4350
+ }
4351
+ withCommon(program.command("retrieve [query]").description("Retrieve notebook context without an LLM call")).option("--nb <id>", "notebook ID (required)").option("-q, --query <query>", "retrieval query").option("--sources <ids>", "comma-separated source IDs", "").option("--top-k <number>", "top-k for retrieval", integer, 0).option("--max-results <number>", "maximum context results", integer, 0).action(async (query, raw) => {
4352
+ render(context, raw, {
4353
+ kind: "retrieve",
4354
+ value: await (await hostFor(context, raw)).client.retrieve(requiredString(raw.nb, "--nb"), {
4355
+ maxResults: numberOption(raw, "maxResults", 0),
4356
+ query: requiredString(raw.query || query, "--query or positional query"),
4357
+ sourceIds: splitCsv(stringOption(raw, "sources")),
4358
+ topK: numberOption(raw, "topK", 0)
4359
+ })
4360
+ });
4361
+ });
4362
+ }
4363
+ function registerSourceCommands(context, program) {
4364
+ const source = program.command("source").description("Manage notebook sources");
4365
+ withCommon(source.command("create").description("Create a source record")).option("--nb <id>", "notebook ID (required)").option("--title <title>", "source title (required)").option("--type <type>", "source type", "").option("--parent <id>", "parent source ID", "").option("--path <path>", "source path", "").option("--is-dir", "create a directory source").option("--status <status>", "source status", "").action(async (raw) => {
4366
+ render(context, raw, {
4367
+ kind: "source",
4368
+ value: await (await hostFor(context, raw)).client.createSource(requiredString(raw.nb, "--nb"), {
4369
+ isDir: raw.isDir === true ? true : void 0,
4370
+ parentSourceId: String(raw.parent ?? ""),
4371
+ path: String(raw.path ?? ""),
4372
+ sourceType: String(raw.type ?? ""),
4373
+ status: String(raw.status ?? ""),
4374
+ title: requiredString(raw.title, "--title")
4375
+ })
4376
+ });
4377
+ });
4378
+ withCommon(source.command("mkdir").description("Create a directory source")).option("--nb <id>", "notebook ID (required)").option("--title <title>", "folder name (required)").option("--parent <id>", "parent source ID", "").option("--path <path>", "source path", "").action(async (raw) => {
4379
+ render(context, raw, {
4380
+ kind: "source",
4381
+ value: await (await hostFor(context, raw)).client.createDirectory(requiredString(raw.nb, "--nb"), {
4382
+ parentSourceId: String(raw.parent ?? ""),
4383
+ path: String(raw.path ?? ""),
4384
+ title: requiredString(raw.title, "--title")
4385
+ })
4386
+ });
4387
+ });
4388
+ withCommon(source.command("list").description("List sources")).option("--nb <id>", "notebook ID (required)").option("--page <number>", "page number", integer, 0).option("--page-size <number>", "page size", integer, 0).option("--all", "fetch and merge every page").option("--max-pages <number>", "all-pages safety limit", integer, 1e4).action(async (raw) => {
4389
+ const host = await hostFor(context, raw);
4390
+ const notebookId = requiredString(raw.nb, "--nb");
4391
+ let value;
4392
+ if (raw.all === true) {
4393
+ const pageSize = positiveNumberOption(raw, "pageSize", 0) ?? 100;
4394
+ value = { sources: await host.client.listAllSources(notebookId, {
4395
+ maxPages: numberOption(raw, "maxPages", 1e4),
4396
+ pageSize
4397
+ }) };
4398
+ } else {
4399
+ const page = positiveNumberOption(raw, "page", 0);
4400
+ const pageSize = positiveNumberOption(raw, "pageSize", 0);
4401
+ value = await host.client.listSources(notebookId, {
4402
+ ...page === void 0 ? {} : { page },
4403
+ ...pageSize === void 0 ? {} : { pageSize }
4404
+ });
4405
+ }
4406
+ render(context, raw, {
4407
+ kind: "sourceList",
4408
+ value
4409
+ });
4410
+ });
4411
+ withCommon(source.command("get <source_id>").description("Get a source")).option("--nb <id>", "notebook ID (required)").action(async (sourceId, raw) => {
4412
+ render(context, raw, {
4413
+ kind: "source",
4414
+ value: await (await hostFor(context, raw)).client.getSource(requiredString(raw.nb, "--nb"), sourceId)
4415
+ });
4416
+ });
4417
+ withCommon(source.command("upload").description("Upload a local file")).option("--nb <id>", "notebook ID (required)").option("--file <path>", "local file path (required)").option("--title <title>", "source title", "").option("--type <type>", "source type", "").option("--parent <id>", "parent source ID", "").option("--path <path>", "source path", "").action(async (raw) => {
4418
+ const host = await hostFor(context, raw);
4419
+ const file = requiredString(raw.file, "--file");
4420
+ render(context, raw, {
4421
+ kind: "source",
4422
+ value: await host.client.uploadSource(requiredString(raw.nb, "--nb"), await host.createFileSource(file), {
4423
+ ...optionalString(raw, "title") === void 0 ? {} : { title: optionalString(raw, "title") },
4424
+ ...optionalString(raw, "type") === void 0 ? {} : { sourceType: optionalString(raw, "type") },
4425
+ ...optionalString(raw, "parent") === void 0 ? {} : { parentSourceId: optionalString(raw, "parent") },
4426
+ ...optionalString(raw, "path") === void 0 ? {} : { path: optionalString(raw, "path") }
4427
+ })
4428
+ });
4429
+ });
4430
+ withCommon(source.command("download <source_id>").description("Download the original source file"), false).option("--nb <id>", "notebook ID (required)").option("-o, --output <path>", "output file path").option("--overwrite", "replace an existing target").action(async (sourceId, raw) => {
4431
+ const host = await hostFor(context, raw);
4432
+ const record = await host.client.getSource(requiredString(raw.nb, "--nb"), sourceId);
4433
+ if (record.originUrl === void 0 || record.originUrl.length === 0) throw new QMindError("NOT_FOUND", "source has no downloadable original file");
4434
+ const output = optionalString(raw, "output") ?? safeQMindDownloadName(record.title, sourceId);
4435
+ const bytes = await host.downloadToFile({
4436
+ overwrite: raw.overwrite === true,
4437
+ targetPath: output,
4438
+ url: record.originUrl
4439
+ });
4440
+ context.io.stderr(`Downloaded ${output} (${bytes} bytes)\n`);
4441
+ });
4442
+ withCommon(source.command("mv <source_id>").alias("move").description("Move or rename a source")).option("--nb <id>", "notebook ID (required)").option("--parent <id>", "new parent; pass an empty string for root").option("--path <path>", "new source path").option("--rename <title>", "new source title").option("--status <status>", "new source status").action(async (sourceId, raw) => {
4443
+ const patch = {
4444
+ ...raw.parent === void 0 ? {} : { parentSourceId: String(raw.parent) },
4445
+ ...raw.path === void 0 ? {} : { path: String(raw.path) },
4446
+ ...raw.rename === void 0 ? {} : { title: String(raw.rename) },
4447
+ ...raw.status === void 0 ? {} : { status: String(raw.status) }
4448
+ };
4449
+ if (Object.keys(patch).length === 0) throw new QMindError("INVALID_ARGUMENT", "at least one of --parent, --path, --rename or --status is required");
4450
+ render(context, raw, {
4451
+ kind: "source",
4452
+ value: await (await hostFor(context, raw)).client.moveSource(requiredString(raw.nb, "--nb"), sourceId, patch)
4453
+ });
4454
+ });
4455
+ withCommon(source.command("content <source_id>").description("Read parsed source content"), false).option("--nb <id>", "notebook ID (required)").option("--download <path>", "save content to a file").option("--no-overwrite", "refuse to replace an existing file").action(async (sourceId, raw) => {
4456
+ const content = await (await hostFor(context, raw)).client.getSourceContent(requiredString(raw.nb, "--nb"), sourceId);
4457
+ const path = optionalString(raw, "download");
4458
+ if (path === void 0) context.io.stdout(content.content);
4459
+ else {
4460
+ await atomicQMindWrite(path, content.content, raw.overwrite !== false);
4461
+ context.io.stdout(`content saved to ${path}\n`);
4462
+ }
4463
+ });
4464
+ withCommon(source.command("delete <source_id>").description("Delete a source"), false).option("--nb <id>", "notebook ID (required)").option("--force", "skip directory cascade confirmation").action(async (sourceId, raw) => {
4465
+ const host = await hostFor(context, raw);
4466
+ const notebookId = requiredString(raw.nb, "--nb");
4467
+ if (raw.force !== true) {
4468
+ const record = await host.client.getSource(notebookId, sourceId).catch(() => void 0);
4469
+ if (record?.isDir === true && !await context.confirm.confirm(`warning: source ${JSON.stringify(record.title)} is a directory. Deleting it will cascade-delete ALL children.\nType 'y' to proceed: `)) {
4470
+ context.io.stderr("aborted\n");
4471
+ context.exitCode = 1;
4472
+ return;
4473
+ }
4474
+ }
4475
+ await host.client.deleteSource(notebookId, sourceId);
4476
+ context.io.stdout("deleted\n");
4477
+ });
4478
+ withCommon(source.command("import-site").description("Import a complete web site")).option("--nb <id>", "notebook ID (required)").option("--url <url>", "site root URL (required)").option("--title <title>", "source title").option("--parent <id>", "parent source ID", "").option("--path-prefix <path>", "path prefix", "").option("--max-pages <number>", "maximum page count", integer, 50).option("--sitemap <url>", "sitemap URL", "").option("--include <patterns>", "comma-separated include patterns", "").option("--exclude <patterns>", "comma-separated exclude patterns", "").option("--compile-after-refresh", "enqueue compilation after import").action(async (raw) => {
4479
+ const host = await hostFor(context, raw);
4480
+ const rootUrl = requiredString(raw.url, "--url");
4481
+ const value = await host.client.importSite(requiredString(raw.nb, "--nb"), {
4482
+ compileAfterRefresh: raw.compileAfterRefresh === true,
4483
+ excludePaths: splitCsv(stringOption(raw, "exclude")),
4484
+ includePaths: splitCsv(stringOption(raw, "include")),
4485
+ maxPages: numberOption(raw, "maxPages", 50),
4486
+ parentSourceId: String(raw.parent ?? ""),
4487
+ pathPrefix: String(raw.pathPrefix ?? ""),
4488
+ rootUrl,
4489
+ sitemapUrl: optionalString(raw, "sitemap"),
4490
+ title: optionalString(raw, "title")
4491
+ });
4492
+ const format = formatOf(raw);
4493
+ if (format === "json" || format === "ndjson") render(context, raw, {
4494
+ kind: "source",
4495
+ value
4496
+ });
4497
+ else context.io.stdout(`Site source created: ${value.id}\n title : ${value.title}\n status: ${value.status}\n site : ${rootUrl}\n\nSite import is running asynchronously. Use '${distribution.command} source list' to check imported pages.\n`);
4498
+ });
4499
+ const image = program.command("image").description("Notebook image presign operations");
4500
+ withCommon(image.command("presign-upload").description("Presign or upload a notebook image"), false).option("--nb <id>", "notebook ID (required)").option("--source <id>", "source ID (required)").option("--filename <name>", "image filename (required unless --file is set)").option("--mime <type>", "MIME type (required unless --file is set)").option("--size <bytes>", "file size", integer, 0).option("--file <path>", "local image to upload").action(async (raw) => {
4501
+ const host = await hostFor(context, raw);
4502
+ const notebookId = requiredString(raw.nb, "--nb");
4503
+ const sourceId = requiredString(raw.source, "--source");
4504
+ const file = optionalString(raw, "file");
4505
+ const filename = optionalString(raw, "filename");
4506
+ const mimeType = optionalString(raw, "mime");
4507
+ const value = file === void 0 ? await host.client.presignImageUpload(notebookId, sourceId, {
4508
+ filename: requiredString(filename, "--filename"),
4509
+ mimeType: requiredString(mimeType, "--mime"),
4510
+ size: numberOption(raw, "size", 0)
4511
+ }) : await host.client.uploadImage(notebookId, sourceId, await host.createFileSource(file, {
4512
+ ...filename === void 0 ? {} : { filename },
4513
+ ...mimeType === void 0 ? {} : { mimeType }
4514
+ }));
4515
+ if (file !== void 0) context.io.stderr(`uploaded ${file} -> ossKey=${value.ossKey}\n`);
4516
+ context.io.stdout(renderQMindCliPresentation({
4517
+ kind: "imageUpload",
4518
+ value
4519
+ }, "json"));
4520
+ });
4521
+ withCommon(image.command("presign-urls").description("Create inline preview URLs"), false).option("--nb <id>", "notebook ID (required)").option("--keys <keys>", "comma-separated OSS keys (required)").action(async (raw) => {
4522
+ const host = await hostFor(context, raw);
4523
+ const keys = splitCsv(requiredString(raw.keys, "--keys"));
4524
+ const value = await host.client.presignImageUrls(requiredString(raw.nb, "--nb"), keys);
4525
+ context.io.stdout(renderQMindCliPresentation({
4526
+ kind: "imageUrls",
4527
+ value
4528
+ }, "json"));
4529
+ });
4530
+ }
4531
+ async function renderAgentRun(context, host, notebookId, raw, input) {
4532
+ const run = await host.client.startAgentRun(notebookId, input);
4533
+ const renderer = createQMindCliStreamRenderer(formatOf(raw));
4534
+ for await (const event of run.events) {
4535
+ if (event.type === "error") throw new QMindError("STREAM_ERROR", event.message);
4536
+ context.io.stdout(renderer.push(event));
4537
+ }
4538
+ context.io.stdout(renderer.finish());
4539
+ }
4540
+ function registerSceneCommands(context, program) {
4541
+ withCommon(program.command("rag [question]").description("Ask a question using notebook RAG")).option("--nb <id>", "notebook ID (required)").option("-q, --query <query>", "question").option("--top-k <number>", "top-k for retrieval", integer, 0).option("--max-results <number>", "maximum context results", integer, 0).action(async (question, raw) => {
4542
+ await renderAgentRun(context, await hostFor(context, raw), requiredString(raw.nb, "--nb"), raw, {
4543
+ maxResults: numberOption(raw, "maxResults", 0),
4544
+ question: requiredString(raw.query || question, "--query or positional question"),
4545
+ scene: "rag",
4546
+ topK: numberOption(raw, "topK", 0)
4547
+ });
4548
+ });
4549
+ for (const [name, scene] of [["compile", "compilation"], ["lint", "lint"]]) withCommon(program.command(name).description(`Run notebook ${scene}`)).option("--nb <id>", "notebook ID (required)").action(async (raw) => {
4550
+ await renderAgentRun(context, await hostFor(context, raw), requiredString(raw.nb, "--nb"), raw, { scene });
4551
+ });
4552
+ }
4553
+ function registerTaskCommands(context, program) {
4554
+ const task = program.command("task").description("Manage notebook task runs");
4555
+ withCommon(task.command("create").description("Create a notebook task run")).option("--nb <id>", "notebook ID (required)").option("--scene <scene>", "task scene: compilation | source_refresh | condensation | lint").option("--trigger <trigger>", "task trigger", "manual").option("--sources <ids>", "comma-separated source IDs", "").action(async (raw) => {
4556
+ const host = await hostFor(context, raw);
4557
+ const sourceIds = splitCsv(stringOption(raw, "sources"));
4558
+ render(context, raw, {
4559
+ kind: "json",
4560
+ value: await host.client.createTaskRun(requiredString(raw.nb, "--nb"), {
4561
+ ...sourceIds.length === 0 ? {} : { config: { sourceIds } },
4562
+ sceneType: requiredString(raw.scene, "--scene"),
4563
+ trigger: String(raw.trigger ?? "manual")
4564
+ })
4565
+ });
4566
+ });
4567
+ withCommon(task.command("list").description("List notebook task runs")).option("--nb <id>", "notebook ID (required)").option("--scene <scene>", "filter by scene", "").option("--trigger <trigger>", "filter by trigger", "").option("--status <status>", "filter by status", "").option("--parent-run <id>", "filter by parent run ID", "").option("--scheduled-task <id>", "filter by scheduled task ID", "").option("--page <number>", "page number", integer, 0).option("--page-size <number>", "page size", integer, 20).action(async (raw) => {
4568
+ const host = await hostFor(context, raw);
4569
+ const status = optionalString(raw, "status");
4570
+ render(context, raw, {
4571
+ kind: "json",
4572
+ value: await host.client.listTaskRuns(requiredString(raw.nb, "--nb"), {
4573
+ page: numberOption(raw, "page", 0),
4574
+ pageSize: numberOption(raw, "pageSize", 20),
4575
+ parentRunId: String(raw.parentRun ?? ""),
4576
+ sceneType: String(raw.scene ?? ""),
4577
+ scheduledTaskId: String(raw.scheduledTask ?? ""),
4578
+ ...status === void 0 ? {} : { status },
4579
+ trigger: String(raw.trigger ?? "")
4580
+ })
4581
+ });
4582
+ });
4583
+ withCommon(task.command("get <run_id>").description("Get a notebook task run")).option("--nb <id>", "notebook ID (required)").action(async (runId, raw) => {
4584
+ render(context, raw, {
4585
+ kind: "json",
4586
+ value: await (await hostFor(context, raw)).client.getTaskRun(requiredString(raw.nb, "--nb"), runId)
4587
+ });
4588
+ });
4589
+ }
4590
+ function syncText(stats) {
4591
+ return `[upload-folder] 完成: 创建目录 ${stats.dirs_created} (复用 ${stats.dirs_reused}, 失败 ${stats.dirs_failed}), 上传文件 ${stats.files_uploaded}, 已存在跳过 ${stats.files_already_exist}, 不支持或忽略 ${stats.files_skipped}, 失败 ${stats.files_failed}, 时间戳刷新失败 ${stats.dirs_refresh_failed}` + (stats.files_deleted === void 0 ? "" : `, 删除文件 ${stats.files_deleted}, 删除目录 ${stats.dirs_deleted ?? 0}, 删除失败 ${stats.delete_failed ?? 0}`) + "\n";
4592
+ }
4593
+ function registerSyncCommand(context, program) {
4594
+ withCommon(program.command("upload-folder").alias("sync").description("Synchronize a local folder"), false).option("--nb <id>", "notebook ID (required)").option("--dir <path>", "local directory (required)").option("--extensions <list>", "comma-separated extensions").option("--dir-ids <path>", "existing directory ID mapping").option("--skip-upload", "only refresh directory timestamps").option("--concurrency <number>", "parallel uploads", integer, 3).option("--save-dir-ids <path>", "directory ID mapping output").option("--ignore <patterns...>", "glob patterns to exclude").option("--delete", "delete remote entries absent from the local mirror").option("--dry-run", "calculate changes without mutating remote or local state").option("--format <format>", "summary output format: json | text", "json").addOption(new Option("--mode <mode>", "existing-file behavior").choices([
4595
+ "skip",
4596
+ "overwrite",
4597
+ "if-changed"
4598
+ ]).default("skip")).action(async (raw) => {
4599
+ const host = await hostFor(context, raw);
4600
+ const ignoreValues = Array.isArray(raw.ignore) ? raw.ignore.flatMap((value) => splitCsv(String(value))) : [];
4601
+ const directoryIdsPath = optionalString(raw, "dirIds");
4602
+ const saveDirectoryIdsPath = optionalString(raw, "saveDirIds");
4603
+ const extensions = splitCsv(optionalString(raw, "extensions"));
4604
+ const result = await synchronizeQMindFolder(host, {
4605
+ baseDirectory: requiredString(raw.dir, "--dir"),
4606
+ cacheDirectory: join(host.config.homeDirectory, "cache"),
4607
+ concurrency: Math.max(1, numberOption(raw, "concurrency", 3)),
4608
+ delete: raw.delete === true,
4609
+ dryRun: raw.dryRun === true,
4610
+ ...extensions.length === 0 ? {} : { extensions },
4611
+ ignorePatterns: ignoreValues,
4612
+ mode: String(raw.mode ?? "skip"),
4613
+ notebookId: requiredString(raw.nb, "--nb"),
4614
+ skipUpload: raw.skipUpload === true,
4615
+ ...directoryIdsPath === void 0 ? {} : { directoryIdsPath },
4616
+ ...saveDirectoryIdsPath === void 0 ? {} : { saveDirectoryIdsPath }
4617
+ });
4618
+ result.diagnostics.forEach((message) => context.io.stderr(`[upload-folder] ${message}\n`));
4619
+ if (String(raw.format ?? "json") === "text") context.io.stderr(syncText(result.stats));
4620
+ else context.io.stdout(renderQMindCliPresentation({
4621
+ kind: "json",
4622
+ value: result.stats
4623
+ }, "json"));
4624
+ if (result.exitCode === 1) context.exitCode = 1;
4625
+ });
4626
+ }
4627
+ function registerUpdateCommand(program) {
4628
+ program.command("self-update").description("Show the supported qmind CLI upgrade path").option("--url <url>", "legacy update URL (ignored)").addOption(new Option("--channel <channel>", "legacy release channel (ignored)").choices([
4629
+ "stable",
4630
+ "beta",
4631
+ "daily"
4632
+ ]).default("stable")).option("--public-key <key>", "legacy public key (ignored)").option("--check", "legacy check mode (ignored)").option("--force", "legacy reinstall mode (ignored)").option("--allow-downgrade", "legacy downgrade mode (ignored)").action(() => {
4633
+ throw new QMindError("UNSUPPORTED_OPERATION", `self-update is unavailable for npm installs; run npm update --global ${distribution.package}`);
4634
+ });
4635
+ }
4636
+ /** Internal parser adapter. Product consumers should call runQMindCli(). */
4637
+ function createQMindCliProgram(context) {
4638
+ const program = new Command().name(distribution.command).description("QMind knowledge CLI").version(QMIND_CLI_VERSION).showHelpAfterError().exitOverride().configureOutput({
4639
+ writeErr: (value) => context.io.stderr(value),
4640
+ writeOut: (value) => context.io.stdout(value)
4641
+ });
4642
+ program.command("version").description("Print version and build information").action(() => {
4643
+ context.io.stdout(`${distribution.command} ${QMIND_CLI_VERSION} (built ${QMIND_CLI_BUILD_TIME}, ${qmindCliPlatform()}/${qmindCliArchitecture()})\n`);
4644
+ });
4645
+ registerCoreCommands(context, program);
4646
+ registerSourceCommands(context, program);
4647
+ registerSceneCommands(context, program);
4648
+ registerTaskCommands(context, program);
4649
+ registerSyncCommand(context, program);
4650
+ registerUpdateCommand(program);
4651
+ return program;
4652
+ }
4653
+ async function runQMindCli(argv, options = {}) {
4654
+ const io = options.io ?? processIO();
4655
+ const context = {
4656
+ confirm: options.confirm ?? interactiveConfirm(io),
4657
+ exitCode: 0,
4658
+ host: void 0,
4659
+ hostFactory: options.hostFactory ?? createQMindCliHost,
4660
+ hostOptions: options.hostOptions ?? {},
4661
+ io
4662
+ };
4663
+ const normalized = normalizeLegacyArgv(argv);
4664
+ const requiresLegacyAction = normalized.length === 0 || normalized.length === 1 && (/* @__PURE__ */ new Set([
4665
+ "notebook",
4666
+ "cards",
4667
+ "source",
4668
+ "image"
4669
+ ])).has(normalized[0] ?? "");
4670
+ let exitCode;
4671
+ try {
4672
+ await createQMindCliProgram(context).parseAsync(normalized, { from: "user" });
4673
+ exitCode = context.exitCode;
4674
+ } catch (error) {
4675
+ if (error instanceof CommanderError) exitCode = error.code === "commander.help" || error.code === "commander.helpDisplayed" || error.code === "commander.version" ? 0 : 2;
4676
+ else {
4677
+ const rendered = renderQMindCliError(error, requestsJsonOutput(normalized));
4678
+ if (rendered.stdout.length > 0) io.stdout(rendered.stdout);
4679
+ if (rendered.stderr.length > 0) io.stderr(rendered.stderr);
4680
+ exitCode = rendered.exitCode;
4681
+ }
4682
+ }
4683
+ if (exitCode === 0 && requiresLegacyAction) exitCode = 2;
4684
+ try {
4685
+ await context.host?.close();
4686
+ } catch {
4687
+ io.stderr("error: failed to close the QMind CLI host\n");
4688
+ if (exitCode === 0) exitCode = 1;
4689
+ }
4690
+ return exitCode;
4691
+ }
4692
+ //#endregion
4693
+ //#region src/qmind.ts
4694
+ process.exitCode = await runQMindCli(process.argv.slice(2));
4695
+ //#endregion
4696
+ export {};