@dinoxx/dinox-cli 2.0.0 → 2.1.1

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.
Files changed (48) hide show
  1. package/dist/commands/notes/repo/read.d.ts +8 -0
  2. package/dist/commands/notes/repo/read.js +321 -0
  3. package/dist/commands/notes/repo/resolve.d.ts +8 -0
  4. package/dist/commands/notes/repo/resolve.js +187 -0
  5. package/dist/commands/notes/repo/search.d.ts +42 -0
  6. package/dist/commands/notes/repo/search.js +513 -0
  7. package/dist/commands/notes/repo/shared.d.ts +190 -0
  8. package/dist/commands/notes/repo/shared.js +102 -0
  9. package/dist/commands/notes/repo/write.d.ts +24 -0
  10. package/dist/commands/notes/repo/write.js +251 -0
  11. package/dist/commands/notes/repo.d.ts +7 -186
  12. package/dist/commands/notes/repo.js +7 -1360
  13. package/dist/commands/schema/commandMetadata.js +4 -4
  14. package/dist/commands/schema/index.d.ts +1 -0
  15. package/dist/commands/schema/index.js +23 -2
  16. package/dist/commands/schema/renderSkills.d.ts +4 -0
  17. package/dist/commands/schema/renderSkills.js +41 -2
  18. package/dist/commands/skills/index.d.ts +26 -0
  19. package/dist/commands/skills/index.js +115 -11
  20. package/dist/commands/storage/repo/configs.d.ts +30 -0
  21. package/dist/commands/storage/repo/configs.js +259 -0
  22. package/dist/commands/storage/repo/operations.d.ts +25 -0
  23. package/dist/commands/storage/repo/operations.js +337 -0
  24. package/dist/commands/storage/repo/remote.d.ts +32 -0
  25. package/dist/commands/storage/repo/remote.js +132 -0
  26. package/dist/commands/storage/repo/resources.d.ts +12 -0
  27. package/dist/commands/storage/repo/resources.js +134 -0
  28. package/dist/commands/storage/repo/shared.d.ts +181 -0
  29. package/dist/commands/storage/repo/shared.js +36 -0
  30. package/dist/commands/storage/repo/targets.d.ts +12 -0
  31. package/dist/commands/storage/repo/targets.js +178 -0
  32. package/dist/commands/storage/repo.d.ts +3 -166
  33. package/dist/commands/storage/repo.js +4 -1034
  34. package/dist/contract/snapshot.d.ts +30 -0
  35. package/dist/contract/snapshot.js +114 -0
  36. package/dist/daemon/client.js +2 -2
  37. package/dist/powersync/uploader.js +27 -2
  38. package/dist/skills/commandRefs.d.ts +21 -0
  39. package/dist/skills/commandRefs.js +132 -0
  40. package/dist/skills/generate.d.ts +13 -0
  41. package/dist/skills/generate.js +80 -0
  42. package/dist/skills/registry.d.ts +13 -0
  43. package/dist/skills/registry.js +58 -0
  44. package/dist/utils/errorCodes.d.ts +311 -0
  45. package/dist/utils/errorCodes.js +92 -0
  46. package/dist/utils/errors.d.ts +13 -2
  47. package/dist/utils/errors.js +11 -0
  48. package/package.json +11 -4
@@ -0,0 +1,8 @@
1
+ import type { AbstractPowerSyncDatabase } from '@powersync/common';
2
+ import { type NoteContext, type NoteDetail, type NoteExportItem, type NotePreview, type NoteRow } from './shared.js';
3
+ export declare function getNote(db: AbstractPowerSyncDatabase, id: string): Promise<NoteRow | null>;
4
+ export declare function getNoteContext(db: AbstractPowerSyncDatabase, id: string): Promise<NoteContext | null>;
5
+ export declare function getNotePreview(db: AbstractPowerSyncDatabase, id: string, requestedLines: number): Promise<NotePreview | null>;
6
+ export declare function getNoteDetails(db: AbstractPowerSyncDatabase, ids: string[]): Promise<NoteDetail[]>;
7
+ export declare function getNoteDetail(db: AbstractPowerSyncDatabase, id: string): Promise<NoteDetail | null>;
8
+ export declare function getNoteExportItems(db: AbstractPowerSyncDatabase, ids: string[]): Promise<NoteExportItem[]>;
@@ -0,0 +1,321 @@
1
+ // Note reads: single-note lookup, context, preview, detail, and export rows.
2
+ import { DinoxError } from '../../../utils/errors.js';
3
+ import { loadZettelBoxNameMap, normalizeStoredFlag, normalizeStoredStarred, parseStoredTags, parseZettelBoxIds, resolveSummary, } from './shared.js';
4
+ export async function getNote(db, id) {
5
+ return db.getOptional(`
6
+ SELECT
7
+ id,
8
+ title,
9
+ content_md,
10
+ content_json,
11
+ content_text,
12
+ created_at,
13
+ updated_at,
14
+ is_del,
15
+ starred_at,
16
+ CASE WHEN starred_at IS NOT NULL THEN 1 ELSE 0 END AS is_starred,
17
+ version
18
+ FROM c_note
19
+ WHERE id = ?
20
+ `, [id]);
21
+ }
22
+ async function getLinkCount(db, direction, noteId) {
23
+ const [column, boundColumn] = direction === 'in'
24
+ ? ['src_note_id', 'dst_note_id']
25
+ : ['dst_note_id', 'src_note_id'];
26
+ const row = await db.getOptional(`
27
+ SELECT COUNT(1) AS count
28
+ FROM c_note_link l
29
+ JOIN c_note n ON n.id = l.${column}
30
+ WHERE l.${boundColumn} = ?
31
+ AND (l.is_del IS NULL OR l.is_del = 0)
32
+ AND (n.is_del IS NULL OR n.is_del = 0)
33
+ `, [noteId]);
34
+ return Number(row?.count ?? 0);
35
+ }
36
+ function buildPreviewText(contentMd, requestedLines) {
37
+ const normalized = (contentMd ?? '').replace(/\r\n/g, '\n');
38
+ if (!normalized) {
39
+ return {
40
+ preview: '',
41
+ totalLines: 0,
42
+ returnedLines: 0,
43
+ truncated: false,
44
+ };
45
+ }
46
+ const lines = normalized.split('\n');
47
+ const selected = lines.slice(0, requestedLines);
48
+ return {
49
+ preview: selected.join('\n'),
50
+ totalLines: lines.length,
51
+ returnedLines: selected.length,
52
+ truncated: selected.length < lines.length,
53
+ };
54
+ }
55
+ export async function getNoteContext(db, id) {
56
+ const row = await db.getOptional(`
57
+ SELECT
58
+ n.id,
59
+ n.title,
60
+ n.summary,
61
+ n.content_text,
62
+ n.tags,
63
+ n.created_at,
64
+ n.updated_at,
65
+ n.is_del,
66
+ n.starred_at,
67
+ CASE WHEN n.starred_at IS NOT NULL THEN 1 ELSE 0 END AS is_starred,
68
+ n.zettel_boxes AS zettel_boxes_raw
69
+ FROM c_note n
70
+ WHERE n.id = ?
71
+ `, [id]);
72
+ if (!row) {
73
+ return null;
74
+ }
75
+ const boxIds = parseZettelBoxIds(row.zettel_boxes_raw);
76
+ const boxNameMap = await loadZettelBoxNameMap(db, boxIds);
77
+ const [backlinks, outlinks] = await Promise.all([
78
+ getLinkCount(db, 'in', id),
79
+ getLinkCount(db, 'out', id),
80
+ ]);
81
+ return {
82
+ id: row.id,
83
+ title: row.title?.trim() ?? '',
84
+ summary: resolveSummary(row.summary, row.content_text),
85
+ tags: parseStoredTags(row.tags),
86
+ created_at: row.created_at,
87
+ updated_at: row.updated_at,
88
+ is_del: row.is_del,
89
+ starred_at: row.starred_at ?? null,
90
+ is_starred: normalizeStoredStarred(row.is_starred),
91
+ zettel_boxes: boxIds
92
+ .map((boxId) => boxNameMap.get(boxId))
93
+ .filter((name) => typeof name === 'string' && name.length > 0),
94
+ links: {
95
+ backlinks,
96
+ outlinks,
97
+ },
98
+ };
99
+ }
100
+ export async function getNotePreview(db, id, requestedLines) {
101
+ const row = await db.getOptional(`
102
+ SELECT
103
+ n.id,
104
+ n.title,
105
+ n.content_md,
106
+ n.updated_at
107
+ FROM c_note n
108
+ WHERE n.id = ?
109
+ `, [id]);
110
+ if (!row) {
111
+ return null;
112
+ }
113
+ const preview = buildPreviewText(row.content_md, requestedLines);
114
+ return {
115
+ id: row.id,
116
+ title: row.title?.trim() ?? '',
117
+ requested_lines: requestedLines,
118
+ total_lines: preview.totalLines,
119
+ returned_lines: preview.returnedLines,
120
+ truncated: preview.truncated,
121
+ preview: preview.preview,
122
+ updated_at: row.updated_at ?? null,
123
+ };
124
+ }
125
+ function mapNoteDetailRow(note, boxNameMap) {
126
+ const boxIds = parseZettelBoxIds(note.zettel_boxes_raw);
127
+ const zettelBoxes = boxIds
128
+ .map((boxId) => boxNameMap.get(boxId))
129
+ .filter((name) => typeof name === 'string' && name.length > 0);
130
+ return {
131
+ id: note.id,
132
+ created_at: note.created_at,
133
+ updated_at: note.updated_at,
134
+ title: note.title,
135
+ type: note.type,
136
+ source: note.source,
137
+ tags: parseStoredTags(note.tags),
138
+ is_del: note.is_del,
139
+ is_pub: normalizeStoredFlag(note.is_pub),
140
+ properties: parseStoredNoteProperties(note.properties),
141
+ starred_at: note.starred_at ?? null,
142
+ is_starred: normalizeStoredStarred(note.is_starred),
143
+ content_md: note.content_md,
144
+ content_text: note.content_text,
145
+ content_json: parseStoredJsonValue(note.content_json),
146
+ zettel_boxes: zettelBoxes,
147
+ };
148
+ }
149
+ function parseStoredJsonValue(raw) {
150
+ const value = raw?.trim() ?? '';
151
+ if (!value) {
152
+ return null;
153
+ }
154
+ try {
155
+ return JSON.parse(value);
156
+ }
157
+ catch {
158
+ return raw;
159
+ }
160
+ }
161
+ function parseStoredNoteProperties(raw) {
162
+ const value = raw?.trim() ?? '';
163
+ if (!value) {
164
+ return {};
165
+ }
166
+ let parsed;
167
+ try {
168
+ parsed = JSON.parse(value);
169
+ }
170
+ catch (cause) {
171
+ throw new DinoxError('c_note.properties must be valid JSON', {
172
+ code: 'NOTE_INVALID_PROPERTIES',
173
+ details: { cause: cause instanceof Error ? cause.message : String(cause) },
174
+ recoverable: false,
175
+ });
176
+ }
177
+ if (!parsed || typeof parsed !== 'object' || Array.isArray(parsed)) {
178
+ throw new DinoxError('c_note.properties must be a JSON object', {
179
+ code: 'NOTE_INVALID_PROPERTIES',
180
+ recoverable: false,
181
+ });
182
+ }
183
+ return Object.fromEntries(Object.entries(parsed));
184
+ }
185
+ function mapNoteExportRow(note, boxNameMap) {
186
+ const boxIds = parseZettelBoxIds(note.zettel_boxes_raw);
187
+ const boxes = boxIds
188
+ .map((boxId) => boxNameMap.get(boxId))
189
+ .filter((name) => typeof name === 'string' && name.length > 0);
190
+ return {
191
+ id: note.id,
192
+ created_at: note.created_at,
193
+ updated_at: note.updated_at,
194
+ title: note.title,
195
+ type: note.type,
196
+ source: note.source,
197
+ summary: note.summary,
198
+ tags: parseStoredTags(note.tags),
199
+ is_del: note.is_del,
200
+ is_pub: normalizeStoredFlag(note.is_pub),
201
+ properties: parseStoredNoteProperties(note.properties),
202
+ starred_at: note.starred_at ?? null,
203
+ is_starred: normalizeStoredStarred(note.is_starred),
204
+ content_md: note.content_md ?? '',
205
+ content_text: note.content_text,
206
+ content_json: parseStoredJsonValue(note.content_json),
207
+ box_ids: boxIds,
208
+ boxes,
209
+ };
210
+ }
211
+ export async function getNoteDetails(db, ids) {
212
+ const targetIds = Array.from(new Set(ids
213
+ .map((id) => id.trim())
214
+ .filter((id) => id.length > 0)));
215
+ if (targetIds.length === 0) {
216
+ return [];
217
+ }
218
+ const noteRowsById = new Map();
219
+ const chunkSize = 500;
220
+ for (let index = 0; index < targetIds.length; index += chunkSize) {
221
+ const batch = targetIds.slice(index, index + chunkSize);
222
+ const placeholders = batch.map(() => '?').join(', ');
223
+ const rows = await db.getAll(`
224
+ SELECT
225
+ id,
226
+ created_at,
227
+ updated_at,
228
+ title,
229
+ type,
230
+ source,
231
+ tags,
232
+ is_del,
233
+ is_pub,
234
+ properties,
235
+ starred_at,
236
+ CASE WHEN starred_at IS NOT NULL THEN 1 ELSE 0 END AS is_starred,
237
+ content_md,
238
+ content_text,
239
+ content_json,
240
+ zettel_boxes AS zettel_boxes_raw
241
+ FROM c_note
242
+ WHERE id IN (${placeholders})
243
+ `, batch);
244
+ for (const row of rows) {
245
+ const id = row.id?.trim() ?? '';
246
+ if (!id || noteRowsById.has(id)) {
247
+ continue;
248
+ }
249
+ noteRowsById.set(id, row);
250
+ }
251
+ }
252
+ const allBoxIds = Array.from(new Set(Array.from(noteRowsById.values()).flatMap((row) => parseZettelBoxIds(row.zettel_boxes_raw))));
253
+ const boxNameMap = await loadZettelBoxNameMap(db, allBoxIds);
254
+ const details = [];
255
+ for (const id of targetIds) {
256
+ const row = noteRowsById.get(id);
257
+ if (!row) {
258
+ continue;
259
+ }
260
+ details.push(mapNoteDetailRow(row, boxNameMap));
261
+ }
262
+ return details;
263
+ }
264
+ export async function getNoteDetail(db, id) {
265
+ const details = await getNoteDetails(db, [id]);
266
+ return details[0] ?? null;
267
+ }
268
+ export async function getNoteExportItems(db, ids) {
269
+ const targetIds = Array.from(new Set(ids
270
+ .map((id) => id.trim())
271
+ .filter((id) => id.length > 0)));
272
+ if (targetIds.length === 0) {
273
+ return [];
274
+ }
275
+ const noteRowsById = new Map();
276
+ const chunkSize = 500;
277
+ for (let index = 0; index < targetIds.length; index += chunkSize) {
278
+ const batch = targetIds.slice(index, index + chunkSize);
279
+ const placeholders = batch.map(() => '?').join(', ');
280
+ const rows = await db.getAll(`
281
+ SELECT
282
+ id,
283
+ created_at,
284
+ updated_at,
285
+ title,
286
+ type,
287
+ source,
288
+ tags,
289
+ summary,
290
+ is_del,
291
+ is_pub,
292
+ properties,
293
+ starred_at,
294
+ CASE WHEN starred_at IS NOT NULL THEN 1 ELSE 0 END AS is_starred,
295
+ content_md,
296
+ content_text,
297
+ content_json,
298
+ zettel_boxes AS zettel_boxes_raw
299
+ FROM c_note
300
+ WHERE id IN (${placeholders})
301
+ `, batch);
302
+ for (const row of rows) {
303
+ const id = row.id?.trim() ?? '';
304
+ if (!id || noteRowsById.has(id)) {
305
+ continue;
306
+ }
307
+ noteRowsById.set(id, row);
308
+ }
309
+ }
310
+ const allBoxIds = Array.from(new Set(Array.from(noteRowsById.values()).flatMap((row) => parseZettelBoxIds(row.zettel_boxes_raw))));
311
+ const boxNameMap = await loadZettelBoxNameMap(db, allBoxIds);
312
+ const items = [];
313
+ for (const id of targetIds) {
314
+ const row = noteRowsById.get(id);
315
+ if (!row) {
316
+ continue;
317
+ }
318
+ items.push(mapNoteExportRow(row, boxNameMap));
319
+ }
320
+ return items;
321
+ }
@@ -0,0 +1,8 @@
1
+ import type { AbstractPowerSyncDatabase } from '@powersync/common';
2
+ export declare function validateTagsForCreate(db: AbstractPowerSyncDatabase, requestedTags: string[], userId: string): Promise<string[]>;
3
+ export declare function validateTagsForUpdate(db: AbstractPowerSyncDatabase, requestedTags: string[], userId: string): Promise<string[]>;
4
+ export declare function validateTagsForBulk(db: AbstractPowerSyncDatabase, requestedTags: string[], userId: string): Promise<string[]>;
5
+ export declare function resolveZettelBoxIdsForCreate(db: AbstractPowerSyncDatabase, requestedNames: string[]): Promise<string[]>;
6
+ export declare function resolveZettelBoxIdsForSearch(db: AbstractPowerSyncDatabase, requestedNames: string[]): Promise<string[]>;
7
+ export declare function resolveZettelBoxIdsForUpdate(db: AbstractPowerSyncDatabase, requestedNames: string[]): Promise<string[]>;
8
+ export declare function resolveZettelBoxIdsForBulk(db: AbstractPowerSyncDatabase, requestedNames: string[]): Promise<string[]>;
@@ -0,0 +1,187 @@
1
+ // Tag validation and zettel box name/path resolution for note writes and search.
2
+ import { DinoxError } from '../../../utils/errors.js';
3
+ import { canonicalizeBoxPath, listBoxes } from '../../boxes/repo.js';
4
+ import { listTags } from '../../tags/repo.js';
5
+ import { normalizeLookupKey } from './shared.js';
6
+ function normalizeZettelBoxLookupKey(value) {
7
+ return canonicalizeBoxPath(value).toLowerCase();
8
+ }
9
+ function buildZettelBoxRetryQuestion(intent, type) {
10
+ const command = intent === 'search'
11
+ ? 'dino note search'
12
+ : intent === 'update'
13
+ ? 'dino note update'
14
+ : intent === 'bulk'
15
+ ? 'dino note bulk'
16
+ : 'dino note create';
17
+ if (type === 'ambiguous') {
18
+ return `Do you want to specify box paths and retry \`${command}\`?`;
19
+ }
20
+ return `Do you want to add these missing boxes and retry \`${command}\`?`;
21
+ }
22
+ export async function validateTagsForCreate(db, requestedTags, userId) {
23
+ return validateTagsForWrite(db, requestedTags, userId, 'dino note create');
24
+ }
25
+ export async function validateTagsForUpdate(db, requestedTags, userId) {
26
+ return validateTagsForWrite(db, requestedTags, userId, 'dino note update');
27
+ }
28
+ export async function validateTagsForBulk(db, requestedTags, userId) {
29
+ return validateTagsForWrite(db, requestedTags, userId, 'dino note bulk');
30
+ }
31
+ async function validateTagsForWrite(db, requestedTags, userId, retryCommand) {
32
+ const normalizedUserId = userId.trim();
33
+ if (!normalizedUserId) {
34
+ throw new DinoxError('Tag validation requires the current userId. Run `dino auth login` first, or set DINOX_TOKEN and run an online command so the CLI can resolve the token identity.');
35
+ }
36
+ if (requestedTags.length === 0) {
37
+ return [];
38
+ }
39
+ const tags = await listTags(db, { userId: normalizedUserId });
40
+ const existingTagMap = new Map();
41
+ for (const tag of tags) {
42
+ const key = normalizeLookupKey(tag);
43
+ if (!key || existingTagMap.has(key)) {
44
+ continue;
45
+ }
46
+ existingTagMap.set(key, tag);
47
+ }
48
+ const resolved = [];
49
+ const seen = new Set();
50
+ const missing = [];
51
+ for (const requested of requestedTags) {
52
+ const key = normalizeLookupKey(requested);
53
+ if (!key || seen.has(key)) {
54
+ continue;
55
+ }
56
+ seen.add(key);
57
+ const matched = existingTagMap.get(key);
58
+ if (!matched) {
59
+ missing.push(requested);
60
+ continue;
61
+ }
62
+ resolved.push(matched);
63
+ }
64
+ if (missing.length > 0) {
65
+ throw new DinoxError(`Unknown tags: ${missing.join(', ')}`, {
66
+ details: {
67
+ type: 'missing_tags',
68
+ missing,
69
+ knownCount: tags.length,
70
+ knownSample: tags.slice(0, 20),
71
+ question: `Do you want to add these missing tags and retry \`${retryCommand}\`?`,
72
+ },
73
+ });
74
+ }
75
+ return resolved;
76
+ }
77
+ export async function resolveZettelBoxIdsForCreate(db, requestedNames) {
78
+ return resolveZettelBoxIdsByName(db, requestedNames, 'create');
79
+ }
80
+ export async function resolveZettelBoxIdsForSearch(db, requestedNames) {
81
+ return resolveZettelBoxIdsByName(db, requestedNames, 'search');
82
+ }
83
+ export async function resolveZettelBoxIdsForUpdate(db, requestedNames) {
84
+ return resolveZettelBoxIdsByName(db, requestedNames, 'update');
85
+ }
86
+ export async function resolveZettelBoxIdsForBulk(db, requestedNames) {
87
+ return resolveZettelBoxIdsByName(db, requestedNames, 'bulk');
88
+ }
89
+ async function resolveZettelBoxIdsByName(db, requestedNames, intent) {
90
+ if (requestedNames.length === 0) {
91
+ return [];
92
+ }
93
+ const boxes = await listBoxes(db);
94
+ const availablePaths = Array.from(new Set(boxes.map((box) => box.path.trim()).filter(Boolean)));
95
+ const boxByPath = new Map();
96
+ const boxByName = new Map();
97
+ for (const box of boxes) {
98
+ const name = box.name.trim();
99
+ const path = canonicalizeBoxPath(box.path || name);
100
+ const pathKey = normalizeZettelBoxLookupKey(path);
101
+ if (pathKey && !boxByPath.has(pathKey)) {
102
+ boxByPath.set(pathKey, box.id);
103
+ }
104
+ const nameKey = normalizeLookupKey(name);
105
+ if (!nameKey) {
106
+ continue;
107
+ }
108
+ const current = boxByName.get(nameKey);
109
+ if (!current) {
110
+ boxByName.set(nameKey, {
111
+ name,
112
+ ids: [box.id],
113
+ paths: path ? [path] : [],
114
+ });
115
+ continue;
116
+ }
117
+ if (!current.ids.includes(box.id)) {
118
+ current.ids.push(box.id);
119
+ }
120
+ if (path && !current.paths.includes(path)) {
121
+ current.paths.push(path);
122
+ }
123
+ }
124
+ const resolvedIds = [];
125
+ const seenIds = new Set();
126
+ const missing = [];
127
+ const ambiguous = [];
128
+ const seenAmbiguous = new Set();
129
+ for (const requested of requestedNames) {
130
+ const pathKey = normalizeZettelBoxLookupKey(requested);
131
+ if (!pathKey) {
132
+ continue;
133
+ }
134
+ const pathMatchId = boxByPath.get(pathKey);
135
+ if (pathMatchId) {
136
+ if (!seenIds.has(pathMatchId)) {
137
+ seenIds.add(pathMatchId);
138
+ resolvedIds.push(pathMatchId);
139
+ }
140
+ continue;
141
+ }
142
+ const lookup = boxByName.get(normalizeLookupKey(requested));
143
+ if (!lookup) {
144
+ missing.push(requested);
145
+ continue;
146
+ }
147
+ if (lookup.ids.length > 1) {
148
+ const marker = `${requested.toLowerCase()}::${lookup.ids.join(',')}`;
149
+ if (!seenAmbiguous.has(marker)) {
150
+ seenAmbiguous.add(marker);
151
+ ambiguous.push({
152
+ name: requested,
153
+ ids: lookup.ids,
154
+ paths: lookup.paths,
155
+ });
156
+ }
157
+ continue;
158
+ }
159
+ const id = lookup.ids[0];
160
+ if (!seenIds.has(id)) {
161
+ seenIds.add(id);
162
+ resolvedIds.push(id);
163
+ }
164
+ }
165
+ if (ambiguous.length > 0) {
166
+ throw new DinoxError(`Ambiguous zettel box names: ${ambiguous.map((item) => `${item.name} -> [${item.paths.join(', ')}]`).join('; ')}`, {
167
+ details: {
168
+ type: 'ambiguous_zettel_boxes',
169
+ ambiguous,
170
+ availablePathsSample: availablePaths.slice(0, 20),
171
+ question: buildZettelBoxRetryQuestion(intent, 'ambiguous'),
172
+ },
173
+ });
174
+ }
175
+ if (missing.length > 0) {
176
+ throw new DinoxError(`Unknown zettel box paths or names: ${missing.join(', ')}`, {
177
+ details: {
178
+ type: 'missing_zettel_boxes',
179
+ missing,
180
+ availableCount: availablePaths.length,
181
+ availablePathsSample: availablePaths.slice(0, 20),
182
+ question: buildZettelBoxRetryQuestion(intent, 'missing'),
183
+ },
184
+ });
185
+ }
186
+ return resolvedIds;
187
+ }
@@ -0,0 +1,42 @@
1
+ import type { AbstractPowerSyncDatabase } from '@powersync/common';
2
+ import { type SearchNotesOptions, type SearchedNote } from './shared.js';
3
+ /**
4
+ * JSON-safe note-search request. Box *names/paths* are carried (not resolved ids) so the
5
+ * same payload can be executed either in-process or sent to the daemon over the wire;
6
+ * id resolution happens inside {@link executeNoteSearch} against whichever db runs it.
7
+ */
8
+ export type NoteSearchExecuteParams = {
9
+ keyword: string;
10
+ includeDeleted: boolean;
11
+ tagsExpression?: string;
12
+ createdAtFrom?: string;
13
+ createdAtTo?: string;
14
+ starred?: boolean;
15
+ boxValues: string[];
16
+ matchEmptyZettelBoxes: boolean;
17
+ sqlExpression?: string;
18
+ limit?: number;
19
+ offset?: number;
20
+ };
21
+ export type NoteSearchExecuteResult = {
22
+ rows: SearchedNote[];
23
+ totalCount: number;
24
+ /** Resolved zettel-box ids, surfaced so callers can echo them in response metadata. */
25
+ zettelBoxIds: string[];
26
+ };
27
+ /**
28
+ * Resolve box names to ids and run the note search. Shared by the local command path and
29
+ * the daemon RPC handler so both produce identical results from one implementation.
30
+ */
31
+ export declare function executeNoteSearch(db: AbstractPowerSyncDatabase, params: NoteSearchExecuteParams): Promise<NoteSearchExecuteResult>;
32
+ export declare function searchNotes(db: AbstractPowerSyncDatabase, query: string, options: SearchNotesOptions): Promise<SearchedNote[]>;
33
+ type SearchNotesPageOptions = SearchNotesOptions & {
34
+ limit?: number;
35
+ offset?: number;
36
+ };
37
+ type SearchNotesPageResult = {
38
+ rows: SearchedNote[];
39
+ totalCount: number;
40
+ };
41
+ export declare function searchNotesPage(db: AbstractPowerSyncDatabase, query: string, options: SearchNotesPageOptions): Promise<SearchNotesPageResult>;
42
+ export {};