@qoder-ai/qmind-cli 1.1.0

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