@dinoxx/dinox-cli 1.1.0 → 2.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +48 -4
- package/dist/cli.js +4 -0
- package/dist/commands/auth/index.js +1 -2
- package/dist/commands/boxes/index.js +37 -13
- package/dist/commands/boxes/operations.d.ts +3 -3
- package/dist/commands/boxes/operations.js +51 -28
- package/dist/commands/info/index.js +30 -5
- package/dist/commands/meta/repo.js +2 -1
- package/dist/commands/notes/actions.js +20 -37
- package/dist/commands/notes/bulk.d.ts +2 -2
- package/dist/commands/notes/bulk.js +3 -18
- package/dist/commands/notes/contentPatch.d.ts +5 -1
- package/dist/commands/notes/contentPatch.js +12 -1
- package/dist/commands/notes/contentPatchCommand.d.ts +3 -0
- package/dist/commands/notes/contentPatchCommand.js +29 -8
- package/dist/commands/notes/descriptors.js +19 -16
- package/dist/commands/notes/export.js +3 -0
- package/dist/commands/notes/repo.d.ts +9 -0
- package/dist/commands/notes/repo.js +65 -15
- package/dist/commands/notes/searchOutput.d.ts +2 -1
- package/dist/commands/notes/searchOutput.js +2 -1
- package/dist/commands/notes/searchSql.js +9 -3
- package/dist/commands/prompt/index.d.ts +1 -0
- package/dist/commands/prompt/index.js +26 -11
- package/dist/commands/runtime.js +17 -6
- package/dist/commands/savedViews/index.d.ts +7 -0
- package/dist/commands/savedViews/index.js +458 -0
- package/dist/commands/savedViews/model.d.ts +87 -0
- package/dist/commands/savedViews/model.js +306 -0
- package/dist/commands/savedViews/repo.d.ts +123 -0
- package/dist/commands/savedViews/repo.js +512 -0
- package/dist/commands/savedViews/semantics.d.ts +46 -0
- package/dist/commands/savedViews/semantics.js +738 -0
- package/dist/commands/schema/renderSkills.d.ts +2 -0
- package/dist/commands/schema/renderSkills.js +69 -10
- package/dist/commands/schema/spec.js +4 -0
- package/dist/commands/skills/index.d.ts +54 -0
- package/dist/commands/skills/index.js +468 -0
- package/dist/commands/storage/index.js +2 -2
- package/dist/commands/sync.d.ts +23 -1
- package/dist/commands/sync.js +100 -58
- package/dist/commands/tags/index.js +30 -11
- package/dist/commands/tags/operations.d.ts +3 -3
- package/dist/commands/tags/operations.js +45 -20
- package/dist/commands/templates/model.d.ts +50 -0
- package/dist/commands/templates/model.js +239 -0
- package/dist/commands/todo/index.js +3 -3
- package/dist/commands/update.d.ts +1 -0
- package/dist/commands/update.js +46 -9
- package/dist/commands/writeConfirmation.d.ts +13 -0
- package/dist/commands/writeConfirmation.js +54 -0
- package/dist/config/resolve.js +5 -10
- package/dist/config/types.d.ts +3 -6
- package/dist/constants/links.d.ts +18 -1
- package/dist/constants/links.js +26 -1
- package/dist/daemon/client.d.ts +2 -2
- package/dist/daemon/client.js +106 -52
- package/dist/daemon/index.d.ts +5 -3
- package/dist/daemon/index.js +28 -5
- package/dist/daemon/lifecycle.d.ts +10 -3
- package/dist/daemon/lifecycle.js +54 -11
- package/dist/daemon/pid.d.ts +1 -1
- package/dist/daemon/pid.js +6 -3
- package/dist/dinox.js +9 -1
- package/dist/powersync/exclusiveRuntime.d.ts +2 -0
- package/dist/powersync/exclusiveRuntime.js +28 -4
- package/dist/powersync/runtime.d.ts +4 -2
- package/dist/powersync/runtime.js +43 -5
- package/dist/powersync/schema/content.d.ts +1 -78
- package/dist/powersync/schema/content.js +1 -78
- package/dist/powersync/schema/electronCanonical.d.ts +1561 -0
- package/dist/powersync/schema/electronCanonical.js +1154 -0
- package/dist/powersync/schema/index.d.ts +573 -129
- package/dist/powersync/schema/index.js +3 -32
- package/dist/powersync/schema/local.d.ts +1 -56
- package/dist/powersync/schema/local.js +1 -60
- package/dist/powersync/schema/note.d.ts +1 -35
- package/dist/powersync/schema/note.js +1 -35
- package/dist/powersync/schema/notesExtras.d.ts +1 -62
- package/dist/powersync/schema/notesExtras.js +1 -71
- package/dist/powersync/schema/projects.d.ts +1 -104
- package/dist/powersync/schema/projects.js +1 -104
- package/dist/powersync/schema/savedViews.d.ts +1 -0
- package/dist/powersync/schema/savedViews.js +1 -0
- package/dist/powersync/schema/storage.d.ts +1 -24
- package/dist/powersync/schema/storage.js +1 -24
- package/dist/powersync/schema/tags.d.ts +1 -37
- package/dist/powersync/schema/tags.js +1 -37
- package/dist/powersync/syncGate.d.ts +22 -3
- package/dist/powersync/syncGate.js +4 -1
- package/dist/powersync/syncPass.d.ts +2 -0
- package/dist/powersync/syncPass.js +15 -4
- package/dist/powersync/tokenIndex.d.ts +2 -0
- package/dist/powersync/tokenIndex.js +60 -18
- package/dist/powersync/uploader.d.ts +16 -2
- package/dist/powersync/uploader.js +152 -158
- package/dist/skills/check.d.ts +1 -0
- package/dist/skills/check.js +538 -70
- package/dist/skills/retired.d.ts +60 -0
- package/dist/skills/retired.js +58 -0
- package/dist/utils/deadline.d.ts +8 -0
- package/dist/utils/deadline.js +31 -0
- package/dist/utils/dryRun.d.ts +20 -1
- package/dist/utils/dryRun.js +22 -3
- package/dist/utils/errors.d.ts +2 -0
- package/dist/utils/errors.js +5 -3
- package/dist/utils/processExit.d.ts +1 -0
- package/dist/utils/processExit.js +33 -0
- package/dist/utils/updateNotice.d.ts +1 -0
- package/dist/utils/updateNotice.js +1 -1
- package/package.json +10 -5
|
@@ -3,12 +3,14 @@ import { printDryRunPreview } from '../../utils/dryRun.js';
|
|
|
3
3
|
import { getRequestedStructuredOutputFormat, printHumanReadableLine, printSuccess, } from '../../utils/output.js';
|
|
4
4
|
import { DinoxError } from '../../utils/errors.js';
|
|
5
5
|
import { commandStale, printStaleCacheWarning, resolveWriteDurabilityForRuntime, waitForMutationUpload, } from '../runtime.js';
|
|
6
|
+
import { parseExpectedCount } from '../writeConfirmation.js';
|
|
6
7
|
import { resolveZettelBoxIdsForBulk, resolveZettelBoxIdsForSearch, searchNotes, validateTagsForBulk, } from './repo.js';
|
|
7
8
|
import { applyNoteMetadataMutation, planNoteMetadataMutation, } from './metadataMutation.js';
|
|
8
9
|
import { parseCreatedAtRange } from './searchTime.js';
|
|
9
10
|
import { normalizeDaysForMeta, } from './searchResponse.js';
|
|
10
11
|
import { parseMutationListInput, parseSearchListInput, parseStarredOption, syncBeforeNoteCommand, withNoteDatabase, } from './runtime.js';
|
|
11
12
|
import { buildBatchWriteReceiptFields, readNoteRevisionReceipts, } from './writeReceipt.js';
|
|
13
|
+
export { parseExpectedCount };
|
|
12
14
|
const BULK_PREVIEW_LIMIT = 100;
|
|
13
15
|
export function selectBulkMutation(options) {
|
|
14
16
|
const candidates = [
|
|
@@ -25,23 +27,6 @@ export function selectBulkMutation(options) {
|
|
|
25
27
|
}
|
|
26
28
|
return selected[0];
|
|
27
29
|
}
|
|
28
|
-
export function parseExpectedCount(value) {
|
|
29
|
-
if (value === undefined || value === null || value === '') {
|
|
30
|
-
return null;
|
|
31
|
-
}
|
|
32
|
-
if (typeof value !== 'string') {
|
|
33
|
-
throw new DinoxError('--expected-count must be a non-negative integer');
|
|
34
|
-
}
|
|
35
|
-
const trimmed = value.trim();
|
|
36
|
-
if (!/^\d+$/.test(trimmed)) {
|
|
37
|
-
throw new DinoxError('--expected-count must be a non-negative integer');
|
|
38
|
-
}
|
|
39
|
-
const parsed = Number(trimmed);
|
|
40
|
-
if (!Number.isSafeInteger(parsed) || parsed < 0) {
|
|
41
|
-
throw new DinoxError('--expected-count must be a non-negative integer');
|
|
42
|
-
}
|
|
43
|
-
return parsed;
|
|
44
|
-
}
|
|
45
30
|
function hasConcreteTargetFilter(input) {
|
|
46
31
|
const { query, options } = input;
|
|
47
32
|
const hasValue = (value) => typeof value === 'string' && value.trim().length > 0;
|
|
@@ -169,7 +154,7 @@ export async function runNoteBulk({ command, options, args }) {
|
|
|
169
154
|
[`${selected.field}_to`]: item.after,
|
|
170
155
|
changed: item.changed,
|
|
171
156
|
})),
|
|
172
|
-
});
|
|
157
|
+
}, runtime);
|
|
173
158
|
return;
|
|
174
159
|
}
|
|
175
160
|
if (!bulkOptions.confirm) {
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import type { AbstractPowerSyncDatabase } from '@powersync/common';
|
|
2
2
|
import { type NoteContentBlockDescriptor, type NoteContentOutlineItem } from './contentEngine.js';
|
|
3
|
+
export type NoteContentReadScope = 'structural-metadata' | 'full-content';
|
|
3
4
|
export type NoteContentPatchOperation = {
|
|
4
5
|
kind: 'appendSection';
|
|
5
6
|
heading: string;
|
|
@@ -24,6 +25,7 @@ export type NoteContentReadResult = {
|
|
|
24
25
|
title: string;
|
|
25
26
|
readToken: string;
|
|
26
27
|
readTokenExpiresAt: string;
|
|
28
|
+
readTokenScope: NoteContentReadScope;
|
|
27
29
|
base: {
|
|
28
30
|
contentHash: string;
|
|
29
31
|
updatedAt: string | null;
|
|
@@ -72,7 +74,9 @@ export declare function planMarkdownPatch(input: {
|
|
|
72
74
|
contentMd: string;
|
|
73
75
|
operation: NoteContentPatchOperation;
|
|
74
76
|
}): NoteContentPatchPlan;
|
|
75
|
-
export declare function readNoteContentForPatch(db: AbstractPowerSyncDatabase, id: string, databaseScope: string, userId: string
|
|
77
|
+
export declare function readNoteContentForPatch(db: AbstractPowerSyncDatabase, id: string, databaseScope: string, userId: string, options?: {
|
|
78
|
+
includeContent?: boolean;
|
|
79
|
+
}): Promise<NoteContentReadResult>;
|
|
76
80
|
export declare function planNoteContentPatch(db: AbstractPowerSyncDatabase, input: {
|
|
77
81
|
id: string;
|
|
78
82
|
userId: string;
|
|
@@ -154,6 +154,7 @@ async function rememberReadToken(input) {
|
|
|
154
154
|
contentHash: input.contentHash,
|
|
155
155
|
updatedAt: input.updatedAt,
|
|
156
156
|
version: input.version,
|
|
157
|
+
reviewScope: input.reviewScope,
|
|
157
158
|
createdAt: new Date(nowMs).toISOString(),
|
|
158
159
|
expiresAt,
|
|
159
160
|
};
|
|
@@ -296,7 +297,7 @@ export function planMarkdownPatch(input) {
|
|
|
296
297
|
});
|
|
297
298
|
return plan;
|
|
298
299
|
}
|
|
299
|
-
export async function readNoteContentForPatch(db, id, databaseScope, userId) {
|
|
300
|
+
export async function readNoteContentForPatch(db, id, databaseScope, userId, options = {}) {
|
|
300
301
|
const row = await readNoteContentRow(db, id);
|
|
301
302
|
const tagLookup = await loadTagReferenceLookup(db, userId);
|
|
302
303
|
const document = parseStoredNoteDocument({
|
|
@@ -308,18 +309,23 @@ export async function readNoteContentForPatch(db, id, databaseScope, userId) {
|
|
|
308
309
|
});
|
|
309
310
|
const contentHash = getNoteContentHash(document);
|
|
310
311
|
const version = normalizeVersion(row.version);
|
|
312
|
+
const readTokenScope = options.includeContent === true
|
|
313
|
+
? 'full-content'
|
|
314
|
+
: 'structural-metadata';
|
|
311
315
|
const token = await rememberReadToken({
|
|
312
316
|
databaseScope,
|
|
313
317
|
noteId: row.id,
|
|
314
318
|
contentHash,
|
|
315
319
|
updatedAt: row.updated_at ?? null,
|
|
316
320
|
version,
|
|
321
|
+
reviewScope: readTokenScope,
|
|
317
322
|
});
|
|
318
323
|
return {
|
|
319
324
|
noteId: row.id,
|
|
320
325
|
title: row.title?.trim() ?? '',
|
|
321
326
|
readToken: token.token,
|
|
322
327
|
readTokenExpiresAt: token.expiresAt,
|
|
328
|
+
readTokenScope,
|
|
323
329
|
base: {
|
|
324
330
|
contentHash,
|
|
325
331
|
updatedAt: row.updated_at ?? null,
|
|
@@ -353,6 +359,11 @@ export async function applyNoteContentPatch(db, input) {
|
|
|
353
359
|
if (token.databaseScopeFingerprint !== fingerprintDatabaseScope(input.databaseScope)) {
|
|
354
360
|
throw new DinoxError('read token was issued for a different local account database. Run `dino note content-read <id>` again.');
|
|
355
361
|
}
|
|
362
|
+
if ('allowProtectedReplace' in input.operation
|
|
363
|
+
&& input.operation.allowProtectedReplace === true
|
|
364
|
+
&& token.reviewScope !== 'full-content') {
|
|
365
|
+
throw new DinoxError('Protected content replacement requires a full-content read token. Run `dino note content-read <id> --include-content --format json`, review the affected protected blocks, and retry with the new token.');
|
|
366
|
+
}
|
|
356
367
|
const result = await withPowerSyncWriteTransaction(db, async (tx) => {
|
|
357
368
|
const tagLookup = await loadTagReferenceLookup(tx, input.userId);
|
|
358
369
|
const row = await readNoteContentRow(tx, input.id);
|
|
@@ -1,3 +1,6 @@
|
|
|
1
1
|
import type { CommandExecutionContext } from '../descriptor/types.js';
|
|
2
|
+
import type { NoteContentBlockDescriptor } from './contentEngine.js';
|
|
3
|
+
export type PublicNoteContentBlockDescriptor = Pick<NoteContentBlockDescriptor, 'anchorId' | 'hasStableAnchor' | 'nodeIndex' | 'nodeRange' | 'type' | 'editMode' | 'level' | 'textHash' | 'nodeHash' | 'charCount'>;
|
|
4
|
+
export declare function toPublicNoteContentBlocks(blocks: NoteContentBlockDescriptor[]): PublicNoteContentBlockDescriptor[];
|
|
2
5
|
export declare function runNoteContentRead({ command, options, args }: CommandExecutionContext): Promise<void>;
|
|
3
6
|
export declare function runNotePatch({ command, options, args }: CommandExecutionContext): Promise<void>;
|
|
@@ -7,6 +7,20 @@ import { commandStale, printStaleCacheWarning, resolveWriteDurabilityForRuntime,
|
|
|
7
7
|
import { readNoteWriteReceipt } from './writeReceipt.js';
|
|
8
8
|
import { applyNoteContentPatch, planNoteContentPatch, readNoteContentForPatch, } from './contentPatch.js';
|
|
9
9
|
import { parseStringInput, syncBeforeNoteCommand, withNoteDatabase, } from './runtime.js';
|
|
10
|
+
export function toPublicNoteContentBlocks(blocks) {
|
|
11
|
+
return blocks.map((block) => ({
|
|
12
|
+
anchorId: block.anchorId,
|
|
13
|
+
hasStableAnchor: block.hasStableAnchor,
|
|
14
|
+
nodeIndex: block.nodeIndex,
|
|
15
|
+
nodeRange: block.nodeRange,
|
|
16
|
+
type: block.type,
|
|
17
|
+
editMode: block.editMode,
|
|
18
|
+
level: block.level,
|
|
19
|
+
textHash: block.textHash,
|
|
20
|
+
nodeHash: block.nodeHash,
|
|
21
|
+
charCount: block.charCount,
|
|
22
|
+
}));
|
|
23
|
+
}
|
|
10
24
|
function requireNoteId(id) {
|
|
11
25
|
const normalized = id?.trim() ?? '';
|
|
12
26
|
if (!normalized) {
|
|
@@ -60,11 +74,11 @@ export async function runNoteContentRead({ command, options, args }) {
|
|
|
60
74
|
await withNoteDatabase(command, async (runtime) => {
|
|
61
75
|
await syncBeforeNoteCommand(runtime);
|
|
62
76
|
const identity = await resolveAuthIdentity(runtime.config);
|
|
63
|
-
const result = await readNoteContentForPatch(runtime.db, id, getPowerSyncDbPath(identity.userId), identity.userId);
|
|
64
|
-
const { content, ...rest } = result;
|
|
77
|
+
const result = await readNoteContentForPatch(runtime.db, id, getPowerSyncDbPath(identity.userId), identity.userId, { includeContent: readOptions.includeContent === true });
|
|
78
|
+
const { content, blocks, ...rest } = result;
|
|
65
79
|
const payload = readOptions.includeContent
|
|
66
|
-
? { ...rest, content, stale: commandStale(runtime) }
|
|
67
|
-
: { ...rest, stale: commandStale(runtime) };
|
|
80
|
+
? { ...rest, blocks, content, stale: commandStale(runtime) }
|
|
81
|
+
: { ...rest, blocks: toPublicNoteContentBlocks(blocks), stale: commandStale(runtime) };
|
|
68
82
|
printSuccess(payload);
|
|
69
83
|
});
|
|
70
84
|
}
|
|
@@ -88,6 +102,9 @@ export async function runNotePatch({ command, options, args }) {
|
|
|
88
102
|
summary: `Preview ${plan.operation} for note "${plan.title}" (${plan.noteId}); content ${plan.changed ? 'would change' : 'is unchanged'}.`,
|
|
89
103
|
warnings: [
|
|
90
104
|
'Run `dino note content-read <id> --format json` before the real edit and pass the returned readToken as --read-token.',
|
|
105
|
+
...('allowProtectedReplace' in operation && operation.allowProtectedReplace === true
|
|
106
|
+
? ['Protected replacement requires `content-read --include-content`; review the affected protected blocks before using its full-content token.']
|
|
107
|
+
: []),
|
|
91
108
|
...(offline ? ['Execution would run in offline mode and only use the local cache.'] : []),
|
|
92
109
|
],
|
|
93
110
|
inputs: {
|
|
@@ -102,12 +119,14 @@ export async function runNotePatch({ command, options, args }) {
|
|
|
102
119
|
after_chars: plan.afterContent.length,
|
|
103
120
|
changed: plan.changed,
|
|
104
121
|
},
|
|
105
|
-
});
|
|
122
|
+
}, runtime);
|
|
106
123
|
return;
|
|
107
124
|
}
|
|
108
|
-
const readToken = patchOptions.readToken
|
|
125
|
+
const readToken = typeof patchOptions.readToken === 'string'
|
|
126
|
+
? (await parseStringInput(patchOptions.readToken)).trim()
|
|
127
|
+
: '';
|
|
109
128
|
if (!readToken) {
|
|
110
|
-
throw new DinoxError('Real note content edits require --read-token from `dino note content-read <id>`.');
|
|
129
|
+
throw new DinoxError('Real note content edits require --read-token <token|@file> from `dino note content-read <id>`.');
|
|
111
130
|
}
|
|
112
131
|
const result = await applyNoteContentPatch(db, {
|
|
113
132
|
id,
|
|
@@ -124,10 +143,12 @@ export async function runNotePatch({ command, options, args }) {
|
|
|
124
143
|
upload,
|
|
125
144
|
stale: commandStale(runtime),
|
|
126
145
|
});
|
|
127
|
-
const { changed: contentChanged, ...publicResult } = result;
|
|
146
|
+
const { changed: contentChanged, beforeBlocks, afterBlocks, ...publicResult } = result;
|
|
128
147
|
const payload = {
|
|
129
148
|
ok: true,
|
|
130
149
|
...publicResult,
|
|
150
|
+
beforeBlocks: toPublicNoteContentBlocks(beforeBlocks),
|
|
151
|
+
afterBlocks: toPublicNoteContentBlocks(afterBlocks),
|
|
131
152
|
content_changed: contentChanged,
|
|
132
153
|
...receipt,
|
|
133
154
|
};
|
|
@@ -42,7 +42,7 @@ export const noteCommandGroup = {
|
|
|
42
42
|
{ flags: '--sql <expr>', description: 'SQL-like expression over id/content_md/summary/tags/zettel_boxes/created_at/type/is_starred', type: 'string' },
|
|
43
43
|
{ flags: '--limit <n>', description: 'Maximum returned notes', type: 'number' },
|
|
44
44
|
{ flags: '--offset <n>', description: 'Result offset for pagination', type: 'number', defaultValue: 0 },
|
|
45
|
-
{ flags: '--fields <list>', description: 'Comma-separated fields: id,title,summary,tags,created_at,boxes,is_starred', type: 'string[]' },
|
|
45
|
+
{ flags: '--fields <list>', description: 'Comma-separated fields: id,title,summary,tags,created_at,starred_at,boxes,is_starred', type: 'string[]' },
|
|
46
46
|
{ flags: '--include-deleted', description: 'Include soft-deleted notes', type: 'boolean' },
|
|
47
47
|
],
|
|
48
48
|
schema: {
|
|
@@ -54,7 +54,7 @@ export const noteCommandGroup = {
|
|
|
54
54
|
type: 'structured',
|
|
55
55
|
summary: 'AI-friendly note search payload',
|
|
56
56
|
entity: 'note_search_result',
|
|
57
|
-
fields: ['id', 'title', 'summary', 'tags', 'created_at', 'boxes', 'is_starred'],
|
|
57
|
+
fields: ['id', 'title', 'summary', 'tags', 'created_at', 'starred_at', 'boxes', 'is_starred'],
|
|
58
58
|
metaFields: ['query', 'returned_count', 'total_count', 'stale', 'pagination', 'fields', 'applied_filters'],
|
|
59
59
|
},
|
|
60
60
|
examples: [
|
|
@@ -66,9 +66,9 @@ export const noteCommandGroup = {
|
|
|
66
66
|
},
|
|
67
67
|
{
|
|
68
68
|
name: 'get',
|
|
69
|
-
description: 'Get
|
|
69
|
+
description: 'Get lightweight note context by id without exposing full note content',
|
|
70
70
|
arguments: [{ syntax: '<id>', description: 'Note id' }],
|
|
71
|
-
options: [{ flags: '--context-only', description: '
|
|
71
|
+
options: [{ flags: '--context-only', description: 'Compatibility flag; note get is context-only by default', type: 'boolean' }],
|
|
72
72
|
schema: {
|
|
73
73
|
path: 'note.get',
|
|
74
74
|
risk: 'read',
|
|
@@ -76,11 +76,11 @@ export const noteCommandGroup = {
|
|
|
76
76
|
globalOptions: NOTE_GLOBAL_OPTIONS,
|
|
77
77
|
output: {
|
|
78
78
|
type: 'structured',
|
|
79
|
-
summary: '
|
|
79
|
+
summary: 'Lightweight note context payload; use note detail or content-read --include-content for full content',
|
|
80
80
|
entity: 'note_context',
|
|
81
|
-
fields: ['id', 'title', 'summary', 'tags', 'created_at', 'updated_at', 'is_del', 'boxes', 'links'],
|
|
81
|
+
fields: ['stale', 'note.id', 'note.title', 'note.summary', 'note.tags', 'note.created_at', 'note.updated_at', 'note.is_del', 'note.starred_at', 'note.is_starred', 'note.boxes', 'note.links'],
|
|
82
82
|
},
|
|
83
|
-
examples: ['dino note get <id> --
|
|
83
|
+
examples: ['dino note get <id> --format json'],
|
|
84
84
|
},
|
|
85
85
|
action: runNoteGet,
|
|
86
86
|
},
|
|
@@ -115,8 +115,8 @@ export const noteCommandGroup = {
|
|
|
115
115
|
globalOptions: NOTE_GLOBAL_OPTIONS,
|
|
116
116
|
output: {
|
|
117
117
|
type: 'structured',
|
|
118
|
-
summary: '
|
|
119
|
-
fields: ['id', 'title', 'content_md', 'content_text', 'content_json', 'tags', 'boxes', 'created_at', 'updated_at', 'is_del'],
|
|
118
|
+
summary: 'Ordered full-note collection with freshness metadata',
|
|
119
|
+
fields: ['stale', 'count', 'notes.id', 'notes.title', 'notes.content_md', 'notes.content_text', 'notes.content_json', 'notes.properties', 'notes.is_pub', 'notes.starred_at', 'notes.is_starred', 'notes.tags', 'notes.boxes', 'notes.created_at', 'notes.updated_at', 'notes.is_del'],
|
|
120
120
|
},
|
|
121
121
|
examples: ['dino note detail <id> --format json', 'dino note detail --ids @/tmp/note-ids.txt --format json'],
|
|
122
122
|
},
|
|
@@ -155,7 +155,7 @@ export const noteCommandGroup = {
|
|
|
155
155
|
description: 'Read note content context and issue a short-lived token required before content patching',
|
|
156
156
|
arguments: [{ syntax: '<id>', description: 'Note id' }],
|
|
157
157
|
options: [
|
|
158
|
-
{ flags: '--include-content', description: 'Include full markdown
|
|
158
|
+
{ flags: '--include-content', description: 'Include full markdown plus detailed block text and attributes', type: 'boolean' },
|
|
159
159
|
],
|
|
160
160
|
schema: {
|
|
161
161
|
path: 'note.content-read',
|
|
@@ -164,10 +164,13 @@ export const noteCommandGroup = {
|
|
|
164
164
|
globalOptions: NOTE_GLOBAL_OPTIONS,
|
|
165
165
|
output: {
|
|
166
166
|
type: 'structured',
|
|
167
|
-
summary: '
|
|
168
|
-
fields: ['noteId', 'title', 'readToken', 'readTokenExpiresAt', 'base', 'outline', 'blocks', 'derived', 'content', 'stale'],
|
|
167
|
+
summary: 'Privacy-preserving note edit context; full content and detailed blocks require --include-content',
|
|
168
|
+
fields: ['noteId', 'title', 'readToken', 'readTokenExpiresAt', 'readTokenScope', 'base', 'outline', 'blocks.type', 'blocks.editMode', 'blocks.nodeRange', 'blocks.textHash', 'blocks.nodeHash', 'blocks.charCount', 'derived', 'content', 'stale'],
|
|
169
169
|
},
|
|
170
|
-
examples: [
|
|
170
|
+
examples: [
|
|
171
|
+
'dino note content-read <id> --format json',
|
|
172
|
+
'dino note content-read <id> --include-content --format json',
|
|
173
|
+
],
|
|
171
174
|
},
|
|
172
175
|
action: runNoteContentRead,
|
|
173
176
|
},
|
|
@@ -295,8 +298,8 @@ export const noteCommandGroup = {
|
|
|
295
298
|
{ flags: '--replace-block', description: 'Replace one exact markdown block matched by --match', type: 'boolean' },
|
|
296
299
|
{ flags: '--match <string|@file>', description: 'Exact markdown to replace when using --replace-block', type: 'string' },
|
|
297
300
|
{ flags: '--content <string|@file>', description: 'Markdown content to insert', type: 'string' },
|
|
298
|
-
{ flags: '--read-token <token>', description: '
|
|
299
|
-
{ flags: '--allow-protected-replace', description: 'Allow
|
|
301
|
+
{ flags: '--read-token <token|@file>', description: 'Single-use patch capability returned by note content-read; prefer @file to avoid exposing it in argv', type: 'string' },
|
|
302
|
+
{ flags: '--allow-protected-replace', description: 'Allow replacement of media, table, container, or unknown blocks only with a reviewed full-content read token', type: 'boolean' },
|
|
300
303
|
WRITE_DURABILITY_OPTION,
|
|
301
304
|
{ flags: '--dry-run', description: 'Preview the patch without writing', type: 'boolean' },
|
|
302
305
|
],
|
|
@@ -312,7 +315,7 @@ export const noteCommandGroup = {
|
|
|
312
315
|
},
|
|
313
316
|
examples: [
|
|
314
317
|
'dino note content-read <id> --format json',
|
|
315
|
-
'dino note patch <id> --append-section "复盘" --content @/tmp/section.md --read-token
|
|
318
|
+
'dino note patch <id> --append-section "复盘" --content @/tmp/section.md --read-token @/tmp/dinox-read-token --format json',
|
|
316
319
|
],
|
|
317
320
|
},
|
|
318
321
|
action: runNotePatch,
|
|
@@ -59,6 +59,9 @@ function serializeMarkdownExport(note) {
|
|
|
59
59
|
tags: note.tags,
|
|
60
60
|
boxes: note.boxes,
|
|
61
61
|
box_ids: note.box_ids,
|
|
62
|
+
properties: note.properties,
|
|
63
|
+
is_pub: note.is_pub,
|
|
64
|
+
starred_at: note.starred_at,
|
|
62
65
|
is_starred: note.is_starred,
|
|
63
66
|
is_del: note.is_del ?? 0,
|
|
64
67
|
}, {
|
|
@@ -11,6 +11,7 @@ export type NoteRow = {
|
|
|
11
11
|
created_at: string | null;
|
|
12
12
|
updated_at: string | null;
|
|
13
13
|
is_del: number | null;
|
|
14
|
+
starred_at: string | null;
|
|
14
15
|
is_starred: number | null;
|
|
15
16
|
version: number | null;
|
|
16
17
|
};
|
|
@@ -30,6 +31,7 @@ export type SearchedNote = {
|
|
|
30
31
|
summary: string;
|
|
31
32
|
tags: string[];
|
|
32
33
|
created_at: string | null;
|
|
34
|
+
starred_at: string | null;
|
|
33
35
|
is_starred: boolean;
|
|
34
36
|
zettel_boxes: string[];
|
|
35
37
|
};
|
|
@@ -42,6 +44,9 @@ export type NoteDetail = {
|
|
|
42
44
|
source: string | null;
|
|
43
45
|
tags: string[];
|
|
44
46
|
is_del: number | null;
|
|
47
|
+
is_pub: boolean;
|
|
48
|
+
properties: Record<string, unknown>;
|
|
49
|
+
starred_at: string | null;
|
|
45
50
|
is_starred: boolean;
|
|
46
51
|
content_md: string | null;
|
|
47
52
|
content_text: string | null;
|
|
@@ -58,6 +63,9 @@ export type NoteExportItem = {
|
|
|
58
63
|
summary: string | null;
|
|
59
64
|
tags: string[];
|
|
60
65
|
is_del: number | null;
|
|
66
|
+
is_pub: boolean;
|
|
67
|
+
properties: Record<string, unknown>;
|
|
68
|
+
starred_at: string | null;
|
|
61
69
|
is_starred: boolean;
|
|
62
70
|
content_md: string;
|
|
63
71
|
content_text: string | null;
|
|
@@ -73,6 +81,7 @@ export type NoteContext = {
|
|
|
73
81
|
created_at: string | null;
|
|
74
82
|
updated_at: string | null;
|
|
75
83
|
is_del: number | null;
|
|
84
|
+
starred_at: string | null;
|
|
76
85
|
is_starred: boolean;
|
|
77
86
|
zettel_boxes: string[];
|
|
78
87
|
links: {
|
|
@@ -6,7 +6,7 @@ import { listTags } from '../tags/repo.js';
|
|
|
6
6
|
import { deriveImageDetailFromContentJson } from '../todo/contentModel.js';
|
|
7
7
|
import { buildNoteSearchSqlFilter } from './searchSql.js';
|
|
8
8
|
const MAX_SEARCH_SUMMARY_CHARS = 200;
|
|
9
|
-
function
|
|
9
|
+
function normalizeStoredFlag(value) {
|
|
10
10
|
if (typeof value === 'number') {
|
|
11
11
|
return value === 1;
|
|
12
12
|
}
|
|
@@ -15,6 +15,9 @@ function normalizeStoredStarred(value) {
|
|
|
15
15
|
}
|
|
16
16
|
return false;
|
|
17
17
|
}
|
|
18
|
+
function normalizeStoredStarred(value) {
|
|
19
|
+
return normalizeStoredFlag(value);
|
|
20
|
+
}
|
|
18
21
|
/**
|
|
19
22
|
* Resolve box names to ids and run the note search. Shared by the local command path and
|
|
20
23
|
* the daemon RPC handler so both produce identical results from one implementation.
|
|
@@ -129,7 +132,8 @@ export async function searchNotesPage(db, query, options) {
|
|
|
129
132
|
n.tags,
|
|
130
133
|
n.zettel_boxes,
|
|
131
134
|
n.created_at,
|
|
132
|
-
n.
|
|
135
|
+
n.starred_at,
|
|
136
|
+
CASE WHEN n.starred_at IS NOT NULL THEN 1 ELSE 0 END AS is_starred
|
|
133
137
|
${baseFromWhere}
|
|
134
138
|
ORDER BY COALESCE(n.updated_at, n.created_at) DESC, n.id DESC
|
|
135
139
|
${buildPaginationSql(options.limit, options.offset)}
|
|
@@ -178,7 +182,8 @@ async function searchNotesWithFts(db, matchQuery, tagFilter, createdAtFilter, st
|
|
|
178
182
|
n.tags,
|
|
179
183
|
n.zettel_boxes,
|
|
180
184
|
n.created_at,
|
|
181
|
-
n.
|
|
185
|
+
n.starred_at,
|
|
186
|
+
CASE WHEN n.starred_at IS NOT NULL THEN 1 ELSE 0 END AS is_starred
|
|
182
187
|
${baseFromWhere}
|
|
183
188
|
ORDER BY bm25(note_local_fts, 0.0, 8.0, 4.0, 1.0) ASC, COALESCE(n.updated_at, n.created_at) DESC, n.id DESC
|
|
184
189
|
${buildPaginationSql(limit, offset)}
|
|
@@ -219,6 +224,7 @@ async function enrichSearchRows(db, rows) {
|
|
|
219
224
|
summary,
|
|
220
225
|
tags: parseStoredTags(row.tags),
|
|
221
226
|
created_at: row.created_at,
|
|
227
|
+
starred_at: row.starred_at ?? null,
|
|
222
228
|
is_starred: normalizeStoredStarred(row.is_starred),
|
|
223
229
|
zettel_boxes: zettelBoxes,
|
|
224
230
|
};
|
|
@@ -342,8 +348,8 @@ function buildStarredFilter(starred, noteAlias) {
|
|
|
342
348
|
return null;
|
|
343
349
|
}
|
|
344
350
|
return {
|
|
345
|
-
sql: `(${noteAlias}.
|
|
346
|
-
params: [
|
|
351
|
+
sql: starred ? `(${noteAlias}.starred_at IS NOT NULL)` : `(${noteAlias}.starred_at IS NULL)`,
|
|
352
|
+
params: [],
|
|
347
353
|
};
|
|
348
354
|
}
|
|
349
355
|
function buildZettelBoxFilter(zettelBoxIds, matchEmptyZettelBoxes, noteAlias) {
|
|
@@ -588,7 +594,8 @@ export async function getNote(db, id) {
|
|
|
588
594
|
created_at,
|
|
589
595
|
updated_at,
|
|
590
596
|
is_del,
|
|
591
|
-
|
|
597
|
+
starred_at,
|
|
598
|
+
CASE WHEN starred_at IS NOT NULL THEN 1 ELSE 0 END AS is_starred,
|
|
592
599
|
version
|
|
593
600
|
FROM c_note
|
|
594
601
|
WHERE id = ?
|
|
@@ -638,7 +645,8 @@ export async function getNoteContext(db, id) {
|
|
|
638
645
|
n.created_at,
|
|
639
646
|
n.updated_at,
|
|
640
647
|
n.is_del,
|
|
641
|
-
n.
|
|
648
|
+
n.starred_at,
|
|
649
|
+
CASE WHEN n.starred_at IS NOT NULL THEN 1 ELSE 0 END AS is_starred,
|
|
642
650
|
n.zettel_boxes AS zettel_boxes_raw
|
|
643
651
|
FROM c_note n
|
|
644
652
|
WHERE n.id = ?
|
|
@@ -660,6 +668,7 @@ export async function getNoteContext(db, id) {
|
|
|
660
668
|
created_at: row.created_at,
|
|
661
669
|
updated_at: row.updated_at,
|
|
662
670
|
is_del: row.is_del,
|
|
671
|
+
starred_at: row.starred_at ?? null,
|
|
663
672
|
is_starred: normalizeStoredStarred(row.is_starred),
|
|
664
673
|
zettel_boxes: boxIds
|
|
665
674
|
.map((boxId) => boxNameMap.get(boxId))
|
|
@@ -709,6 +718,9 @@ function mapNoteDetailRow(note, boxNameMap) {
|
|
|
709
718
|
source: note.source,
|
|
710
719
|
tags: parseStoredTags(note.tags),
|
|
711
720
|
is_del: note.is_del,
|
|
721
|
+
is_pub: normalizeStoredFlag(note.is_pub),
|
|
722
|
+
properties: parseStoredNoteProperties(note.properties),
|
|
723
|
+
starred_at: note.starred_at ?? null,
|
|
712
724
|
is_starred: normalizeStoredStarred(note.is_starred),
|
|
713
725
|
content_md: note.content_md,
|
|
714
726
|
content_text: note.content_text,
|
|
@@ -728,6 +740,30 @@ function parseStoredJsonValue(raw) {
|
|
|
728
740
|
return raw;
|
|
729
741
|
}
|
|
730
742
|
}
|
|
743
|
+
function parseStoredNoteProperties(raw) {
|
|
744
|
+
const value = raw?.trim() ?? '';
|
|
745
|
+
if (!value) {
|
|
746
|
+
return {};
|
|
747
|
+
}
|
|
748
|
+
let parsed;
|
|
749
|
+
try {
|
|
750
|
+
parsed = JSON.parse(value);
|
|
751
|
+
}
|
|
752
|
+
catch (cause) {
|
|
753
|
+
throw new DinoxError('c_note.properties must be valid JSON', {
|
|
754
|
+
code: 'NOTE_INVALID_PROPERTIES',
|
|
755
|
+
details: { cause: cause instanceof Error ? cause.message : String(cause) },
|
|
756
|
+
recoverable: false,
|
|
757
|
+
});
|
|
758
|
+
}
|
|
759
|
+
if (!parsed || typeof parsed !== 'object' || Array.isArray(parsed)) {
|
|
760
|
+
throw new DinoxError('c_note.properties must be a JSON object', {
|
|
761
|
+
code: 'NOTE_INVALID_PROPERTIES',
|
|
762
|
+
recoverable: false,
|
|
763
|
+
});
|
|
764
|
+
}
|
|
765
|
+
return Object.fromEntries(Object.entries(parsed));
|
|
766
|
+
}
|
|
731
767
|
function mapNoteExportRow(note, boxNameMap) {
|
|
732
768
|
const boxIds = parseZettelBoxIds(note.zettel_boxes_raw);
|
|
733
769
|
const boxes = boxIds
|
|
@@ -743,6 +779,9 @@ function mapNoteExportRow(note, boxNameMap) {
|
|
|
743
779
|
summary: note.summary,
|
|
744
780
|
tags: parseStoredTags(note.tags),
|
|
745
781
|
is_del: note.is_del,
|
|
782
|
+
is_pub: normalizeStoredFlag(note.is_pub),
|
|
783
|
+
properties: parseStoredNoteProperties(note.properties),
|
|
784
|
+
starred_at: note.starred_at ?? null,
|
|
746
785
|
is_starred: normalizeStoredStarred(note.is_starred),
|
|
747
786
|
content_md: note.content_md ?? '',
|
|
748
787
|
content_text: note.content_text,
|
|
@@ -773,7 +812,10 @@ export async function getNoteDetails(db, ids) {
|
|
|
773
812
|
source,
|
|
774
813
|
tags,
|
|
775
814
|
is_del,
|
|
776
|
-
|
|
815
|
+
is_pub,
|
|
816
|
+
properties,
|
|
817
|
+
starred_at,
|
|
818
|
+
CASE WHEN starred_at IS NOT NULL THEN 1 ELSE 0 END AS is_starred,
|
|
777
819
|
content_md,
|
|
778
820
|
content_text,
|
|
779
821
|
content_json,
|
|
@@ -828,7 +870,10 @@ export async function getNoteExportItems(db, ids) {
|
|
|
828
870
|
tags,
|
|
829
871
|
summary,
|
|
830
872
|
is_del,
|
|
831
|
-
|
|
873
|
+
is_pub,
|
|
874
|
+
properties,
|
|
875
|
+
starred_at,
|
|
876
|
+
CASE WHEN starred_at IS NOT NULL THEN 1 ELSE 0 END AS is_starred,
|
|
832
877
|
content_md,
|
|
833
878
|
content_text,
|
|
834
879
|
content_json,
|
|
@@ -930,11 +975,13 @@ export async function createNote(db, input) {
|
|
|
930
975
|
['resource_id', null],
|
|
931
976
|
['note_ids', '[]'],
|
|
932
977
|
['extra_data', '{}'],
|
|
978
|
+
['properties', null],
|
|
979
|
+
['is_pub', 0],
|
|
933
980
|
['format_type', ''],
|
|
934
981
|
['comments', '[]'],
|
|
935
|
-
['is_starred', 0],
|
|
936
982
|
['is_top', 0],
|
|
937
983
|
['pinned_at', '1997-01-01T00:00:00.000Z'],
|
|
984
|
+
['starred_at', null],
|
|
938
985
|
['chat_context', '{}'],
|
|
939
986
|
['bid_links', '[]'],
|
|
940
987
|
];
|
|
@@ -1082,7 +1129,7 @@ export async function updateNote(db, input) {
|
|
|
1082
1129
|
throw new DinoxError('At least one field is required: tags, boxes, or starred');
|
|
1083
1130
|
}
|
|
1084
1131
|
const existing = await db.getOptional(`
|
|
1085
|
-
SELECT id, tags, zettel_boxes, is_del,
|
|
1132
|
+
SELECT id, tags, zettel_boxes, is_del, starred_at
|
|
1086
1133
|
FROM c_note
|
|
1087
1134
|
WHERE id = ?
|
|
1088
1135
|
`, [id]);
|
|
@@ -1094,7 +1141,7 @@ export async function updateNote(db, input) {
|
|
|
1094
1141
|
}
|
|
1095
1142
|
const currentTags = parseStoredTags(existing.tags);
|
|
1096
1143
|
const currentZettelBoxIds = parseZettelBoxIds(existing.zettel_boxes);
|
|
1097
|
-
const currentStarred =
|
|
1144
|
+
const currentStarred = existing.starred_at !== null;
|
|
1098
1145
|
const nextTags = shouldUpdateTags ? normalizeDistinctStrings(input.tags) : currentTags;
|
|
1099
1146
|
const nextZettelBoxIds = shouldUpdateBoxes ? normalizeDistinctStrings(input.zettelBoxIds) : currentZettelBoxIds;
|
|
1100
1147
|
const nextStarred = shouldUpdateStarred ? input.starred : currentStarred;
|
|
@@ -1122,11 +1169,14 @@ export async function updateNote(db, input) {
|
|
|
1122
1169
|
params.push(JSON.stringify(nextZettelBoxIds));
|
|
1123
1170
|
}
|
|
1124
1171
|
if (starredChanged) {
|
|
1125
|
-
setParts.push('
|
|
1126
|
-
|
|
1172
|
+
setParts.push('starred_at = ?');
|
|
1173
|
+
}
|
|
1174
|
+
const updatedAt = nowIsoUtc();
|
|
1175
|
+
if (starredChanged) {
|
|
1176
|
+
params.push(nextStarred ? updatedAt : null);
|
|
1127
1177
|
}
|
|
1128
1178
|
setParts.push('updated_at = ?');
|
|
1129
|
-
params.push(
|
|
1179
|
+
params.push(updatedAt);
|
|
1130
1180
|
setParts.push('version = COALESCE(version, 0) + 1');
|
|
1131
1181
|
params.push(id);
|
|
1132
1182
|
await db.execute(`
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { SearchedNote } from './repo.js';
|
|
2
|
-
export declare const NOTE_SEARCH_FIELDS: readonly ["id", "title", "summary", "tags", "created_at", "boxes", "is_starred"];
|
|
2
|
+
export declare const NOTE_SEARCH_FIELDS: readonly ["id", "title", "summary", "tags", "created_at", "starred_at", "boxes", "is_starred"];
|
|
3
3
|
export type NoteSearchField = (typeof NOTE_SEARCH_FIELDS)[number];
|
|
4
4
|
export type NoteSearchResultItem = {
|
|
5
5
|
id?: string;
|
|
@@ -7,6 +7,7 @@ export type NoteSearchResultItem = {
|
|
|
7
7
|
summary?: string;
|
|
8
8
|
tags?: string[];
|
|
9
9
|
created_at?: string | null;
|
|
10
|
+
starred_at?: string | null;
|
|
10
11
|
is_starred?: boolean;
|
|
11
12
|
boxes?: string[];
|
|
12
13
|
};
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { DinoxError } from '../../utils/errors.js';
|
|
2
|
-
export const NOTE_SEARCH_FIELDS = ['id', 'title', 'summary', 'tags', 'created_at', 'boxes', 'is_starred'];
|
|
2
|
+
export const NOTE_SEARCH_FIELDS = ['id', 'title', 'summary', 'tags', 'created_at', 'starred_at', 'boxes', 'is_starred'];
|
|
3
3
|
const NOTE_SEARCH_FIELD_SET = new Set(NOTE_SEARCH_FIELDS);
|
|
4
4
|
export function parseNoteSearchFields(value, optionName) {
|
|
5
5
|
if (value === undefined || value === null || value === '') {
|
|
@@ -35,6 +35,7 @@ export function projectNoteSearchFields(notes, fields) {
|
|
|
35
35
|
summary: note.summary,
|
|
36
36
|
tags: note.tags,
|
|
37
37
|
created_at: note.created_at,
|
|
38
|
+
starred_at: note.starred_at,
|
|
38
39
|
is_starred: note.is_starred,
|
|
39
40
|
boxes: note.zettel_boxes,
|
|
40
41
|
}));
|
|
@@ -32,8 +32,14 @@ const SCALAR_FIELD_COLUMNS = {
|
|
|
32
32
|
summary: 'summary',
|
|
33
33
|
created_at: 'created_at',
|
|
34
34
|
type: 'type',
|
|
35
|
-
is_starred: '
|
|
35
|
+
is_starred: 'starred_at',
|
|
36
36
|
};
|
|
37
|
+
function scalarFieldSql(field, noteAlias) {
|
|
38
|
+
if (field === 'is_starred') {
|
|
39
|
+
return `(CASE WHEN ${noteAlias}.starred_at IS NOT NULL THEN 1 ELSE 0 END)`;
|
|
40
|
+
}
|
|
41
|
+
return `${noteAlias}.${SCALAR_FIELD_COLUMNS[field]}`;
|
|
42
|
+
}
|
|
37
43
|
export async function buildNoteSearchSqlFilter(options) {
|
|
38
44
|
const expression = options.expression?.trim() ?? '';
|
|
39
45
|
if (!expression) {
|
|
@@ -527,7 +533,7 @@ function compileComparison(node, noteAlias, params, zettelBoxIdByName) {
|
|
|
527
533
|
`;
|
|
528
534
|
return node.operator === '!=' ? `NOT (${existsSql})` : existsSql;
|
|
529
535
|
}
|
|
530
|
-
const column =
|
|
536
|
+
const column = scalarFieldSql(node.field, noteAlias);
|
|
531
537
|
params.push(node.value);
|
|
532
538
|
if (node.operator === 'LIKE' || node.operator === 'NOT LIKE') {
|
|
533
539
|
return `(${column} IS NOT NULL AND ${column} ${node.operator} ?)`;
|
|
@@ -586,7 +592,7 @@ function compileInCondition(node, noteAlias, params, zettelBoxIdByName) {
|
|
|
586
592
|
if (values.length === 0) {
|
|
587
593
|
throw new Error('IN list cannot be empty');
|
|
588
594
|
}
|
|
589
|
-
const column =
|
|
595
|
+
const column = scalarFieldSql(node.field, noteAlias);
|
|
590
596
|
const placeholders = values.map(() => '?').join(', ');
|
|
591
597
|
params.push(...values);
|
|
592
598
|
if (node.not) {
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { Command } from 'commander';
|
|
2
2
|
import type { CommandGroupDescriptor } from '../descriptor/types.js';
|
|
3
|
+
export declare function parsePromptTextInput(value: unknown): Promise<string>;
|
|
3
4
|
export declare const promptCommandGroup: CommandGroupDescriptor;
|
|
4
5
|
export declare function registerPromptCommands(program: Command): void;
|