@dinoxx/dinox-cli 1.0.18 → 1.0.20
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 +807 -418
- package/dist/auth/userInfo.d.ts +2 -0
- package/dist/auth/userInfo.js +28 -11
- package/dist/cli.js +15 -1
- package/dist/commands/auth/index.d.ts +2 -0
- package/dist/commands/auth/index.js +217 -163
- package/dist/commands/boxes/index.d.ts +2 -0
- package/dist/commands/boxes/index.js +119 -75
- package/dist/commands/boxes/repo.d.ts +8 -0
- package/dist/commands/boxes/repo.js +41 -0
- package/dist/commands/config/index.d.ts +2 -0
- package/dist/commands/config/index.js +85 -36
- package/dist/commands/descriptor/register.d.ts +8 -0
- package/dist/commands/descriptor/register.js +113 -0
- package/dist/commands/descriptor/types.d.ts +37 -0
- package/dist/commands/descriptor/types.js +17 -0
- package/dist/commands/graph/index.d.ts +2 -0
- package/dist/commands/graph/index.js +143 -73
- package/dist/commands/info/index.d.ts +2 -0
- package/dist/commands/info/index.js +41 -14
- package/dist/commands/mcp/index.js +11 -10
- package/dist/commands/mcp/server.js +8 -4
- package/dist/commands/mcp/tools.js +30 -104
- package/dist/commands/mcp/types.d.ts +8 -1
- package/dist/commands/meta/index.d.ts +2 -0
- package/dist/commands/meta/index.js +64 -33
- package/dist/commands/meta/repo.d.ts +6 -0
- package/dist/commands/meta/repo.js +6 -0
- package/dist/commands/notes/actions.d.ts +10 -0
- package/dist/commands/notes/actions.js +403 -0
- package/dist/commands/notes/descriptors.d.ts +4 -0
- package/dist/commands/notes/descriptors.js +250 -0
- package/dist/commands/notes/dryRun.d.ts +31 -0
- package/dist/commands/notes/dryRun.js +130 -0
- package/dist/commands/notes/index.d.ts +2 -51
- package/dist/commands/notes/index.js +2 -581
- package/dist/commands/notes/repo.d.ts +9 -0
- package/dist/commands/notes/repo.js +56 -10
- package/dist/commands/notes/runtime.d.ts +28 -0
- package/dist/commands/notes/runtime.js +185 -0
- package/dist/commands/notes/searchOutput.d.ts +10 -2
- package/dist/commands/notes/searchOutput.js +14 -2
- package/dist/commands/notes/searchResponse.d.ts +51 -0
- package/dist/commands/notes/searchResponse.js +56 -0
- package/dist/commands/notes/searchSql.js +2 -0
- package/dist/commands/prompt/index.d.ts +2 -0
- package/dist/commands/prompt/index.js +122 -72
- package/dist/commands/prompt/repo.d.ts +7 -0
- package/dist/commands/prompt/repo.js +46 -0
- package/dist/commands/schema/commandMetadata.d.ts +2 -0
- package/dist/commands/schema/commandMetadata.js +18 -0
- package/dist/commands/schema/globalOptions.d.ts +3 -0
- package/dist/commands/schema/globalOptions.js +3 -0
- package/dist/commands/schema/index.d.ts +4 -0
- package/dist/commands/schema/index.js +109 -0
- package/dist/commands/schema/renderSkills.d.ts +6 -0
- package/dist/commands/schema/renderSkills.js +216 -0
- package/dist/commands/schema/spec.d.ts +6 -0
- package/dist/commands/schema/spec.js +40 -0
- package/dist/commands/schema/types.d.ts +47 -0
- package/dist/commands/schema/types.js +1 -0
- package/dist/commands/storage/index.d.ts +4 -0
- package/dist/commands/storage/index.js +292 -0
- package/dist/commands/storage/repo.d.ts +162 -0
- package/dist/commands/storage/repo.js +770 -0
- package/dist/commands/sync.d.ts +2 -0
- package/dist/commands/sync.js +41 -27
- package/dist/commands/tags/index.d.ts +2 -0
- package/dist/commands/tags/index.js +121 -79
- package/dist/commands/tags/repo.d.ts +7 -0
- package/dist/commands/tags/repo.js +52 -0
- package/dist/commands/todo/dryRun.d.ts +23 -0
- package/dist/commands/todo/dryRun.js +133 -0
- package/dist/commands/todo/index.d.ts +2 -0
- package/dist/commands/todo/index.js +285 -135
- package/dist/commands/todo/repo.d.ts +11 -0
- package/dist/commands/todo/repo.js +37 -9
- package/dist/commands/update.d.ts +3 -0
- package/dist/commands/update.js +35 -13
- package/dist/commands/writeDryRun.d.ts +62 -0
- package/dist/commands/writeDryRun.js +131 -0
- package/dist/config/paths.d.ts +2 -1
- package/dist/config/paths.js +11 -1
- package/dist/config/resolve.js +7 -5
- package/dist/constants/links.d.ts +1 -1
- package/dist/constants/links.js +1 -1
- package/dist/daemon/ensure.js +3 -2
- package/dist/daemon/index.js +17 -15
- package/dist/dinox.js +9 -13
- package/dist/powersync/runtime.d.ts +1 -1
- package/dist/powersync/runtime.js +5 -3
- package/dist/powersync/schema/index.d.ts +27 -0
- package/dist/powersync/schema/index.js +2 -0
- package/dist/powersync/schema/note.d.ts +1 -0
- package/dist/powersync/schema/note.js +1 -0
- package/dist/powersync/schema/projects.d.ts +3 -0
- package/dist/powersync/schema/projects.js +3 -0
- package/dist/powersync/schema/storage.d.ts +24 -0
- package/dist/powersync/schema/storage.js +24 -0
- package/dist/skills/check.d.ts +6 -0
- package/dist/skills/check.js +254 -0
- package/dist/utils/bidlinkCanonicalization.d.ts +2 -0
- package/dist/utils/bidlinkCanonicalization.js +161 -0
- package/dist/utils/dryRun.d.ts +13 -0
- package/dist/utils/dryRun.js +69 -0
- package/dist/utils/output.d.ts +19 -0
- package/dist/utils/output.js +155 -1
- package/dist/utils/terminalSanitization.d.ts +2 -0
- package/dist/utils/terminalSanitization.js +33 -0
- package/dist/utils/tiptapMarkdown.d.ts +4 -1
- package/dist/utils/tiptapMarkdown.js +285 -113
- package/dist/utils/tiptapMarkdownSchema.d.ts +37 -0
- package/dist/utils/tiptapMarkdownSchema.js +577 -0
- package/dist/utils/tiptapMarkdownSchemaUtils.d.ts +74 -0
- package/dist/utils/tiptapMarkdownSchemaUtils.js +394 -0
- package/dist/utils/updateNotice.d.ts +7 -0
- package/dist/utils/updateNotice.js +64 -0
- package/package.json +25 -3
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
import { loadConfig } from '../../config/store.js';
|
|
2
2
|
import { resolveConfig } from '../../config/resolve.js';
|
|
3
|
-
import { printYaml } from '../../utils/output.js';
|
|
4
|
-
import { parseSyncTimeoutMs } from '../../utils/syncTimeout.js';
|
|
5
3
|
import { connectPowerSync, waitForIdleDataFlow } from '../../powersync/runtime.js';
|
|
4
|
+
import { registerCommandGroup } from '../descriptor/register.js';
|
|
5
|
+
import { OFFLINE_OPTIONS, SYNC_TIMEOUT_OPTIONS } from '../schema/globalOptions.js';
|
|
6
|
+
import { printSuccess } from '../../utils/output.js';
|
|
7
|
+
import { parseSyncTimeoutMs } from '../../utils/syncTimeout.js';
|
|
6
8
|
import { getMetaSchema, getMetaStats } from './repo.js';
|
|
7
9
|
async function syncBeforeMetaCommand(db, offline, timeoutMs) {
|
|
8
10
|
if (offline) {
|
|
@@ -10,36 +12,65 @@ async function syncBeforeMetaCommand(db, offline, timeoutMs) {
|
|
|
10
12
|
}
|
|
11
13
|
await waitForIdleDataFlow(db, Math.min(timeoutMs, 10_000));
|
|
12
14
|
}
|
|
15
|
+
async function runMetaStats(context) {
|
|
16
|
+
const config = resolveConfig(await loadConfig());
|
|
17
|
+
const offline = Boolean(context.globals.offline);
|
|
18
|
+
const timeoutMs = parseSyncTimeoutMs(context.globals.syncTimeout) ?? config.sync.timeoutMs;
|
|
19
|
+
const { db, stale } = await connectPowerSync({ config, offline, timeoutMs });
|
|
20
|
+
try {
|
|
21
|
+
await syncBeforeMetaCommand(db, offline, timeoutMs);
|
|
22
|
+
printSuccess({
|
|
23
|
+
stale: offline ? true : stale,
|
|
24
|
+
stats: await getMetaStats(db),
|
|
25
|
+
});
|
|
26
|
+
}
|
|
27
|
+
finally {
|
|
28
|
+
await db.close().catch(() => undefined);
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
async function runMetaSchema() {
|
|
32
|
+
printSuccess(getMetaSchema());
|
|
33
|
+
}
|
|
34
|
+
export const metaCommandGroup = {
|
|
35
|
+
name: 'meta',
|
|
36
|
+
description: 'Query Dinox metadata',
|
|
37
|
+
commands: [
|
|
38
|
+
{
|
|
39
|
+
name: 'stats',
|
|
40
|
+
description: 'Get metadata stats (notes/tags/boxes)',
|
|
41
|
+
schema: {
|
|
42
|
+
path: 'meta.stats',
|
|
43
|
+
risk: 'read',
|
|
44
|
+
auth: 'required',
|
|
45
|
+
globalOptions: [...OFFLINE_OPTIONS, ...SYNC_TIMEOUT_OPTIONS],
|
|
46
|
+
output: {
|
|
47
|
+
type: 'structured',
|
|
48
|
+
summary: 'Metadata stats payload',
|
|
49
|
+
fields: ['stale', 'stats'],
|
|
50
|
+
},
|
|
51
|
+
examples: ['dino meta stats --format json'],
|
|
52
|
+
},
|
|
53
|
+
action: runMetaStats,
|
|
54
|
+
},
|
|
55
|
+
{
|
|
56
|
+
name: 'schema',
|
|
57
|
+
description: 'Get structured Dinox data schema for AI integrations',
|
|
58
|
+
schema: {
|
|
59
|
+
path: 'meta.schema',
|
|
60
|
+
risk: 'read',
|
|
61
|
+
auth: 'none',
|
|
62
|
+
globalOptions: [],
|
|
63
|
+
output: {
|
|
64
|
+
type: 'structured',
|
|
65
|
+
summary: 'Structured Dinox data schema payload',
|
|
66
|
+
fields: ['tables', 'relations', 'command_schema'],
|
|
67
|
+
},
|
|
68
|
+
examples: ['dino meta schema --format json'],
|
|
69
|
+
},
|
|
70
|
+
action: runMetaSchema,
|
|
71
|
+
},
|
|
72
|
+
],
|
|
73
|
+
};
|
|
13
74
|
export function registerMetaCommands(program) {
|
|
14
|
-
|
|
15
|
-
meta
|
|
16
|
-
.command('stats')
|
|
17
|
-
.description('Get metadata stats (notes/tags/boxes)')
|
|
18
|
-
.action(async (_options, command) => {
|
|
19
|
-
const globals = command.optsWithGlobals?.() ?? {};
|
|
20
|
-
const offline = Boolean(globals.offline);
|
|
21
|
-
const config = resolveConfig(await loadConfig());
|
|
22
|
-
const timeoutMs = parseSyncTimeoutMs(globals.syncTimeout) ?? config.sync.timeoutMs;
|
|
23
|
-
const { db, stale } = await connectPowerSync({ config, offline, timeoutMs });
|
|
24
|
-
try {
|
|
25
|
-
await syncBeforeMetaCommand(db, offline, timeoutMs);
|
|
26
|
-
const stats = await getMetaStats(db);
|
|
27
|
-
printYaml({
|
|
28
|
-
stale: offline ? true : stale,
|
|
29
|
-
stats,
|
|
30
|
-
});
|
|
31
|
-
}
|
|
32
|
-
finally {
|
|
33
|
-
await db.close().catch(() => undefined);
|
|
34
|
-
}
|
|
35
|
-
});
|
|
36
|
-
meta
|
|
37
|
-
.command('schema')
|
|
38
|
-
.description('Get structured Dinox data schema for AI integrations')
|
|
39
|
-
.action(() => {
|
|
40
|
-
printYaml(getMetaSchema());
|
|
41
|
-
});
|
|
42
|
-
meta.action((_options, command) => {
|
|
43
|
-
command.outputHelp();
|
|
44
|
-
});
|
|
75
|
+
registerCommandGroup(program, metaCommandGroup);
|
|
45
76
|
}
|
|
@@ -21,6 +21,12 @@ export type MetaSchema = {
|
|
|
21
21
|
meta_stats: {
|
|
22
22
|
fields: string[];
|
|
23
23
|
};
|
|
24
|
+
command_schema: {
|
|
25
|
+
fields: string[];
|
|
26
|
+
};
|
|
27
|
+
mcp_tool: {
|
|
28
|
+
fields: string[];
|
|
29
|
+
};
|
|
24
30
|
};
|
|
25
31
|
};
|
|
26
32
|
export declare function getMetaStats(db: AbstractPowerSyncDatabase): Promise<MetaStats>;
|
|
@@ -66,6 +66,12 @@ export function getMetaSchema() {
|
|
|
66
66
|
meta_stats: {
|
|
67
67
|
fields: ['notes_count', 'tags_count', 'boxes_count', 'latest_note_updated_at'],
|
|
68
68
|
},
|
|
69
|
+
command_schema: {
|
|
70
|
+
fields: ['path', 'command', 'summary', 'risk', 'auth', 'globalOptions', 'arguments', 'options', 'output', 'examples', 'mcp'],
|
|
71
|
+
},
|
|
72
|
+
mcp_tool: {
|
|
73
|
+
fields: ['name', 'description', 'inputSchema'],
|
|
74
|
+
},
|
|
69
75
|
},
|
|
70
76
|
};
|
|
71
77
|
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { CommandExecutionContext } from '../descriptor/types.js';
|
|
2
|
+
export declare function runNoteSearch({ command, options, args }: CommandExecutionContext): Promise<void>;
|
|
3
|
+
export declare function runNoteGet({ command, options, args }: CommandExecutionContext): Promise<void>;
|
|
4
|
+
export declare function runNotePreview({ command, options, args }: CommandExecutionContext): Promise<void>;
|
|
5
|
+
export declare function runNoteDetail({ command, options, args }: CommandExecutionContext): Promise<void>;
|
|
6
|
+
export declare function runNoteCreate({ command, options }: CommandExecutionContext): Promise<void>;
|
|
7
|
+
export declare function runNoteDelete({ command, options, args }: CommandExecutionContext): Promise<void>;
|
|
8
|
+
export declare function runNoteUpdate({ command, options, args }: CommandExecutionContext): Promise<void>;
|
|
9
|
+
export declare function runNoteStar({ command, options, args }: CommandExecutionContext): Promise<void>;
|
|
10
|
+
export declare function runNoteUnstar({ command, options, args }: CommandExecutionContext): Promise<void>;
|
|
@@ -0,0 +1,403 @@
|
|
|
1
|
+
import { resolveAuthIdentity } from '../../auth/userInfo.js';
|
|
2
|
+
import { canonicalizeBidlinksInMarkdown } from '../../utils/bidlinkCanonicalization.js';
|
|
3
|
+
import { printDryRunPreview } from '../../utils/dryRun.js';
|
|
4
|
+
import { generateId } from '../../utils/id.js';
|
|
5
|
+
import { convertMarkdownToNoteContent } from '../../utils/tiptapMarkdown.js';
|
|
6
|
+
import { getRequestedStructuredOutputFormat, printHumanReadableLine, printSuccess, } from '../../utils/output.js';
|
|
7
|
+
import { DinoxError } from '../../utils/errors.js';
|
|
8
|
+
import { waitForIdleDataFlow } from '../../powersync/runtime.js';
|
|
9
|
+
import { createNote, getNote, getNoteContext, getNoteDetails, getNotePreview, assertNotesUpdatable, resolveZettelBoxIdsForCreate, resolveZettelBoxIdsForSearch, resolveZettelBoxIdsForUpdate, searchNotes, softDeleteNote, updateNote, validateTagsForCreate, validateTagsForUpdate, } from './repo.js';
|
|
10
|
+
import { buildNoteCreateDryRunPreview, buildNoteDeleteDryRunPreview, buildNoteUpdateDryRunPreview, } from './dryRun.js';
|
|
11
|
+
import { parseNoteSearchFields, projectNoteSearchFields } from './searchOutput.js';
|
|
12
|
+
import { parseCreatedAtRange } from './searchTime.js';
|
|
13
|
+
import { buildNoteSearchResponse, normalizeDaysForMeta } from './searchResponse.js';
|
|
14
|
+
import { parseNoteType, parsePreviewLineCount, parseSearchLimit, parseSearchListInput, parseSearchOffset, parseStarredOption, parseStringInput, parseStringListInput, parseTargetNoteIds, syncBeforeNoteCommand, withNoteDatabase, } from './runtime.js';
|
|
15
|
+
function toPublicNoteContext(note) {
|
|
16
|
+
if (!note) {
|
|
17
|
+
return null;
|
|
18
|
+
}
|
|
19
|
+
const { zettel_boxes, ...rest } = note;
|
|
20
|
+
return {
|
|
21
|
+
...rest,
|
|
22
|
+
boxes: zettel_boxes,
|
|
23
|
+
};
|
|
24
|
+
}
|
|
25
|
+
function toPublicNoteDetail(note) {
|
|
26
|
+
const { zettel_boxes, ...rest } = note;
|
|
27
|
+
return {
|
|
28
|
+
...rest,
|
|
29
|
+
boxes: zettel_boxes,
|
|
30
|
+
};
|
|
31
|
+
}
|
|
32
|
+
function toPublicNote(note) {
|
|
33
|
+
if (!note) {
|
|
34
|
+
return null;
|
|
35
|
+
}
|
|
36
|
+
const { zettel_boxes, ...rest } = note;
|
|
37
|
+
return {
|
|
38
|
+
...rest,
|
|
39
|
+
...(zettel_boxes !== undefined ? { boxes: zettel_boxes } : {}),
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
async function settleMutation(db, timeoutMs) {
|
|
43
|
+
await waitForIdleDataFlow(db, Math.min(timeoutMs, 5_000));
|
|
44
|
+
}
|
|
45
|
+
async function executeNoteUpdateOperation(args) {
|
|
46
|
+
const structuredOutput = getRequestedStructuredOutputFormat() !== null;
|
|
47
|
+
await withNoteDatabase(args.command, async ({ db, config, offline, timeoutMs, stale }) => {
|
|
48
|
+
await syncBeforeNoteCommand(db, offline, timeoutMs);
|
|
49
|
+
const ids = await parseTargetNoteIds(args.id, args.idsOption, 'update');
|
|
50
|
+
const hasTags = typeof args.tagsOption === 'string';
|
|
51
|
+
const hasBoxes = typeof args.boxOption === 'string';
|
|
52
|
+
const hasStarred = typeof args.starred === 'boolean';
|
|
53
|
+
if (!hasTags && !hasBoxes && !hasStarred) {
|
|
54
|
+
throw new DinoxError('At least one option is required: --tags, --boxes, or --starred');
|
|
55
|
+
}
|
|
56
|
+
const tags = hasTags
|
|
57
|
+
? await validateTagsForUpdate(db, await parseStringListInput(args.tagsOption, '--tags'))
|
|
58
|
+
: undefined;
|
|
59
|
+
const zettelBoxIds = hasBoxes
|
|
60
|
+
? await resolveZettelBoxIdsForUpdate(db, await parseStringListInput(args.boxOption, '--boxes'))
|
|
61
|
+
: undefined;
|
|
62
|
+
await assertNotesUpdatable(db, ids);
|
|
63
|
+
if (args.dryRun) {
|
|
64
|
+
const preview = await buildNoteUpdateDryRunPreview({
|
|
65
|
+
db,
|
|
66
|
+
config,
|
|
67
|
+
offline,
|
|
68
|
+
ids,
|
|
69
|
+
tagsInput: tags,
|
|
70
|
+
zettelBoxNames: hasBoxes ? await parseStringListInput(args.boxOption, '--boxes') : undefined,
|
|
71
|
+
starred: hasStarred ? args.starred : undefined,
|
|
72
|
+
commandName: args.commandName ?? 'dino note update',
|
|
73
|
+
action: args.actionName ?? 'update_note',
|
|
74
|
+
});
|
|
75
|
+
printDryRunPreview(preview);
|
|
76
|
+
return;
|
|
77
|
+
}
|
|
78
|
+
const results = [];
|
|
79
|
+
for (const targetId of ids) {
|
|
80
|
+
results.push(await updateNote(db, {
|
|
81
|
+
id: targetId,
|
|
82
|
+
tags,
|
|
83
|
+
zettelBoxIds,
|
|
84
|
+
starred: hasStarred ? args.starred : undefined,
|
|
85
|
+
}));
|
|
86
|
+
}
|
|
87
|
+
const updatedCount = results.filter((item) => item.updated).length;
|
|
88
|
+
if (!offline && !config.powersync.uploadBaseUrl) {
|
|
89
|
+
printHumanReadableLine('warning: upload disabled (powersync.uploadBaseUrl is unset); changes are local-only for now');
|
|
90
|
+
}
|
|
91
|
+
if (!offline) {
|
|
92
|
+
await settleMutation(db, timeoutMs);
|
|
93
|
+
}
|
|
94
|
+
const payload = {
|
|
95
|
+
ok: true,
|
|
96
|
+
total: results.length,
|
|
97
|
+
updatedCount,
|
|
98
|
+
results: results.map((item) => ({
|
|
99
|
+
id: item.id,
|
|
100
|
+
updated: item.updated,
|
|
101
|
+
tags: item.tags,
|
|
102
|
+
boxes: item.zettelBoxIds,
|
|
103
|
+
is_starred: item.is_starred,
|
|
104
|
+
})),
|
|
105
|
+
stale: offline ? true : stale,
|
|
106
|
+
};
|
|
107
|
+
if (structuredOutput) {
|
|
108
|
+
printSuccess(payload);
|
|
109
|
+
return;
|
|
110
|
+
}
|
|
111
|
+
if (results.length === 1) {
|
|
112
|
+
if (!args.actionLabel) {
|
|
113
|
+
printHumanReadableLine(results[0].updated ? 'OK' : 'No changes');
|
|
114
|
+
}
|
|
115
|
+
else if (results[0].updated) {
|
|
116
|
+
printHumanReadableLine(args.actionLabel);
|
|
117
|
+
}
|
|
118
|
+
else {
|
|
119
|
+
printHumanReadableLine(`Already ${args.staleVerb ?? args.actionLabel.toLowerCase()}`);
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
else if (args.actionLabel) {
|
|
123
|
+
printHumanReadableLine(`${args.actionLabel} ${updatedCount}/${results.length} notes`);
|
|
124
|
+
}
|
|
125
|
+
else {
|
|
126
|
+
printHumanReadableLine(`Updated ${updatedCount}/${results.length} notes`);
|
|
127
|
+
}
|
|
128
|
+
if (stale && !offline) {
|
|
129
|
+
printHumanReadableLine('warning: sync timed out; local cache may be stale');
|
|
130
|
+
}
|
|
131
|
+
});
|
|
132
|
+
}
|
|
133
|
+
export async function runNoteSearch({ command, options, args }) {
|
|
134
|
+
const searchOptions = options;
|
|
135
|
+
await withNoteDatabase(command, async ({ db, offline, timeoutMs, stale }) => {
|
|
136
|
+
await syncBeforeNoteCommand(db, offline, timeoutMs);
|
|
137
|
+
const keyword = args.query?.trim() ?? '';
|
|
138
|
+
const tagsExpression = typeof searchOptions.tags === 'string' ? searchOptions.tags.trim() : '';
|
|
139
|
+
const { createdAtFrom, createdAtTo } = parseCreatedAtRange({
|
|
140
|
+
from: searchOptions.from,
|
|
141
|
+
to: searchOptions.to,
|
|
142
|
+
days: searchOptions.days,
|
|
143
|
+
});
|
|
144
|
+
const starred = typeof searchOptions.starred === 'string'
|
|
145
|
+
? parseStarredOption(searchOptions.starred, '--starred')
|
|
146
|
+
: undefined;
|
|
147
|
+
const boxInput = typeof searchOptions.boxes === 'string' ? searchOptions.boxes : '';
|
|
148
|
+
const parsedBoxInput = boxInput
|
|
149
|
+
? await parseSearchListInput(boxInput, '--boxes')
|
|
150
|
+
: { values: [], isExplicitEmptyArray: false };
|
|
151
|
+
const zettelBoxIds = await resolveZettelBoxIdsForSearch(db, parsedBoxInput.values);
|
|
152
|
+
const sqlExpression = typeof searchOptions.sql === 'string' ? searchOptions.sql.trim() : '';
|
|
153
|
+
const limit = parseSearchLimit(searchOptions.limit);
|
|
154
|
+
const offset = parseSearchOffset(searchOptions.offset);
|
|
155
|
+
const fields = parseNoteSearchFields(searchOptions.fields, '--fields');
|
|
156
|
+
const rows = await searchNotes(db, keyword, {
|
|
157
|
+
includeDeleted: Boolean(searchOptions.includeDeleted),
|
|
158
|
+
tagsExpression: tagsExpression || undefined,
|
|
159
|
+
createdAtFrom,
|
|
160
|
+
createdAtTo,
|
|
161
|
+
starred,
|
|
162
|
+
zettelBoxIds,
|
|
163
|
+
matchEmptyZettelBoxes: parsedBoxInput.isExplicitEmptyArray,
|
|
164
|
+
sqlExpression: sqlExpression || undefined,
|
|
165
|
+
});
|
|
166
|
+
printSuccess(buildNoteSearchResponse({
|
|
167
|
+
query: keyword,
|
|
168
|
+
notes: projectNoteSearchFields(rows.slice(offset, offset + limit), fields),
|
|
169
|
+
totalCount: rows.length,
|
|
170
|
+
stale: offline ? true : stale,
|
|
171
|
+
includeDeleted: Boolean(searchOptions.includeDeleted),
|
|
172
|
+
limit,
|
|
173
|
+
offset,
|
|
174
|
+
fields,
|
|
175
|
+
tagsExpression: tagsExpression || undefined,
|
|
176
|
+
createdAtFrom,
|
|
177
|
+
createdAtTo,
|
|
178
|
+
days: normalizeDaysForMeta(searchOptions.days),
|
|
179
|
+
starred,
|
|
180
|
+
boxNames: parsedBoxInput.values,
|
|
181
|
+
boxIds: zettelBoxIds,
|
|
182
|
+
matchEmptyBoxes: parsedBoxInput.isExplicitEmptyArray,
|
|
183
|
+
sqlExpression: sqlExpression || undefined,
|
|
184
|
+
}));
|
|
185
|
+
});
|
|
186
|
+
}
|
|
187
|
+
export async function runNoteGet({ command, options, args }) {
|
|
188
|
+
const getOptions = options;
|
|
189
|
+
const id = args.id;
|
|
190
|
+
if (!id) {
|
|
191
|
+
throw new DinoxError('Note id is required');
|
|
192
|
+
}
|
|
193
|
+
const structuredOutput = getRequestedStructuredOutputFormat() !== null;
|
|
194
|
+
await withNoteDatabase(command, async ({ db, offline, timeoutMs, stale }) => {
|
|
195
|
+
await syncBeforeNoteCommand(db, offline, timeoutMs);
|
|
196
|
+
if (getOptions.contextOnly) {
|
|
197
|
+
const context = await getNoteContext(db, id);
|
|
198
|
+
if (!context) {
|
|
199
|
+
throw new DinoxError(`Note not found: ${id}`, {
|
|
200
|
+
code: 'NOTE_NOT_FOUND',
|
|
201
|
+
suggestion: "Run 'dino note search <keyword>' to locate the note id.",
|
|
202
|
+
recoverable: true,
|
|
203
|
+
});
|
|
204
|
+
}
|
|
205
|
+
printSuccess({ stale: offline ? true : stale, note: toPublicNoteContext(context) });
|
|
206
|
+
return;
|
|
207
|
+
}
|
|
208
|
+
const note = await getNote(db, id);
|
|
209
|
+
if (!note) {
|
|
210
|
+
throw new DinoxError(`Note not found: ${id}`, {
|
|
211
|
+
code: 'NOTE_NOT_FOUND',
|
|
212
|
+
suggestion: "Run 'dino note search <keyword>' to locate the note id.",
|
|
213
|
+
recoverable: true,
|
|
214
|
+
});
|
|
215
|
+
}
|
|
216
|
+
if (structuredOutput) {
|
|
217
|
+
printSuccess({ stale, note: toPublicNote(note) });
|
|
218
|
+
return;
|
|
219
|
+
}
|
|
220
|
+
if (stale && !offline) {
|
|
221
|
+
printHumanReadableLine('warning: sync timed out; result may be stale');
|
|
222
|
+
}
|
|
223
|
+
printHumanReadableLine(`${note.id}`);
|
|
224
|
+
printHumanReadableLine(`title: ${note.title ?? ''}`);
|
|
225
|
+
if (note.updated_at) {
|
|
226
|
+
printHumanReadableLine(`updated_at: ${note.updated_at}`);
|
|
227
|
+
}
|
|
228
|
+
if (note.is_starred) {
|
|
229
|
+
printHumanReadableLine('is_starred: 1');
|
|
230
|
+
}
|
|
231
|
+
if (note.is_del) {
|
|
232
|
+
printHumanReadableLine('is_del: 1');
|
|
233
|
+
}
|
|
234
|
+
});
|
|
235
|
+
}
|
|
236
|
+
export async function runNotePreview({ command, options, args }) {
|
|
237
|
+
const previewOptions = options;
|
|
238
|
+
const id = args.id;
|
|
239
|
+
if (!id) {
|
|
240
|
+
throw new DinoxError('Note id is required');
|
|
241
|
+
}
|
|
242
|
+
await withNoteDatabase(command, async ({ db, offline, timeoutMs, stale }) => {
|
|
243
|
+
await syncBeforeNoteCommand(db, offline, timeoutMs);
|
|
244
|
+
const preview = await getNotePreview(db, id, parsePreviewLineCount(previewOptions.lines));
|
|
245
|
+
if (!preview) {
|
|
246
|
+
throw new DinoxError(`Note not found: ${id}`, {
|
|
247
|
+
code: 'NOTE_NOT_FOUND',
|
|
248
|
+
suggestion: "Run 'dino note search <keyword>' to locate the note id.",
|
|
249
|
+
recoverable: true,
|
|
250
|
+
});
|
|
251
|
+
}
|
|
252
|
+
printSuccess({ stale: offline ? true : stale, note: preview });
|
|
253
|
+
});
|
|
254
|
+
}
|
|
255
|
+
export async function runNoteDetail({ command, options, args }) {
|
|
256
|
+
const detailOptions = options;
|
|
257
|
+
await withNoteDatabase(command, async ({ db, offline, timeoutMs }) => {
|
|
258
|
+
await syncBeforeNoteCommand(db, offline, timeoutMs);
|
|
259
|
+
const ids = await parseTargetNoteIds(args.id, detailOptions.ids, 'detail');
|
|
260
|
+
const notes = await getNoteDetails(db, ids);
|
|
261
|
+
const foundIds = new Set(notes.map((note) => note.id));
|
|
262
|
+
const missing = ids.filter((noteId) => !foundIds.has(noteId));
|
|
263
|
+
if (missing.length > 0) {
|
|
264
|
+
throw new DinoxError(ids.length === 1 ? `Note not found: ${missing[0]}` : `Notes not found: ${missing.join(', ')}`);
|
|
265
|
+
}
|
|
266
|
+
const publicNotes = notes.map((note) => toPublicNoteDetail(note));
|
|
267
|
+
printSuccess(publicNotes.length === 1 ? publicNotes[0] : publicNotes);
|
|
268
|
+
});
|
|
269
|
+
}
|
|
270
|
+
export async function runNoteCreate({ command, options }) {
|
|
271
|
+
const createOptions = options;
|
|
272
|
+
const structuredOutput = getRequestedStructuredOutputFormat() !== null;
|
|
273
|
+
await withNoteDatabase(command, async ({ db, rawConfig, config, offline, timeoutMs, stale }) => {
|
|
274
|
+
await syncBeforeNoteCommand(db, offline, timeoutMs);
|
|
275
|
+
const identity = await resolveAuthIdentity(rawConfig, config);
|
|
276
|
+
const contentMd = await parseStringInput(String(createOptions.content));
|
|
277
|
+
const canonicalContentMd = await canonicalizeBidlinksInMarkdown(db, contentMd, identity.userId);
|
|
278
|
+
const { contentJson, contentText } = convertMarkdownToNoteContent(canonicalContentMd);
|
|
279
|
+
const tagsInput = typeof createOptions.tags === 'string'
|
|
280
|
+
? await parseStringListInput(createOptions.tags, '--tags')
|
|
281
|
+
: [];
|
|
282
|
+
const boxNames = typeof createOptions.boxes === 'string'
|
|
283
|
+
? await parseStringListInput(createOptions.boxes, '--boxes')
|
|
284
|
+
: [];
|
|
285
|
+
const noteType = parseNoteType(createOptions.type);
|
|
286
|
+
if (Boolean(createOptions.dryRun)) {
|
|
287
|
+
printDryRunPreview(await buildNoteCreateDryRunPreview({
|
|
288
|
+
db,
|
|
289
|
+
config,
|
|
290
|
+
offline,
|
|
291
|
+
userId: identity.userId,
|
|
292
|
+
title: String(createOptions.title),
|
|
293
|
+
noteType,
|
|
294
|
+
contentMd,
|
|
295
|
+
tagsInput,
|
|
296
|
+
zettelBoxNames: boxNames,
|
|
297
|
+
}));
|
|
298
|
+
return;
|
|
299
|
+
}
|
|
300
|
+
const id = generateId();
|
|
301
|
+
await createNote(db, {
|
|
302
|
+
id,
|
|
303
|
+
title: String(createOptions.title),
|
|
304
|
+
noteType,
|
|
305
|
+
contentMd: canonicalContentMd,
|
|
306
|
+
contentJson,
|
|
307
|
+
contentText,
|
|
308
|
+
tags: await validateTagsForCreate(db, tagsInput),
|
|
309
|
+
zettelBoxIds: await resolveZettelBoxIdsForCreate(db, boxNames),
|
|
310
|
+
userId: identity.userId,
|
|
311
|
+
});
|
|
312
|
+
if (!offline && !config.powersync.uploadBaseUrl) {
|
|
313
|
+
printHumanReadableLine('warning: upload disabled (powersync.uploadBaseUrl is unset); changes are local-only for now');
|
|
314
|
+
}
|
|
315
|
+
if (!offline) {
|
|
316
|
+
await settleMutation(db, timeoutMs);
|
|
317
|
+
}
|
|
318
|
+
const payload = { ok: true, id, stale: offline ? true : stale };
|
|
319
|
+
if (structuredOutput) {
|
|
320
|
+
printSuccess(payload);
|
|
321
|
+
return;
|
|
322
|
+
}
|
|
323
|
+
printHumanReadableLine(id);
|
|
324
|
+
if (stale && !offline) {
|
|
325
|
+
printHumanReadableLine('warning: sync timed out; local cache may be stale');
|
|
326
|
+
}
|
|
327
|
+
});
|
|
328
|
+
}
|
|
329
|
+
export async function runNoteDelete({ command, options, args }) {
|
|
330
|
+
const deleteOptions = options;
|
|
331
|
+
const id = args.id;
|
|
332
|
+
if (!id) {
|
|
333
|
+
throw new DinoxError('Note id is required');
|
|
334
|
+
}
|
|
335
|
+
const structuredOutput = getRequestedStructuredOutputFormat() !== null;
|
|
336
|
+
await withNoteDatabase(command, async ({ db, config, offline, timeoutMs, stale }) => {
|
|
337
|
+
await syncBeforeNoteCommand(db, offline, timeoutMs);
|
|
338
|
+
if (Boolean(deleteOptions.dryRun)) {
|
|
339
|
+
printDryRunPreview(await buildNoteDeleteDryRunPreview({ db, config, offline, id }));
|
|
340
|
+
return;
|
|
341
|
+
}
|
|
342
|
+
await softDeleteNote(db, id);
|
|
343
|
+
if (!offline && !config.powersync.uploadBaseUrl) {
|
|
344
|
+
printHumanReadableLine('warning: upload disabled (powersync.uploadBaseUrl is unset); changes are local-only for now');
|
|
345
|
+
}
|
|
346
|
+
if (!offline) {
|
|
347
|
+
await settleMutation(db, timeoutMs);
|
|
348
|
+
}
|
|
349
|
+
const payload = { ok: true, id, stale: offline ? true : stale };
|
|
350
|
+
if (structuredOutput) {
|
|
351
|
+
printSuccess(payload);
|
|
352
|
+
return;
|
|
353
|
+
}
|
|
354
|
+
printHumanReadableLine('OK');
|
|
355
|
+
if (stale && !offline) {
|
|
356
|
+
printHumanReadableLine('warning: sync timed out; local cache may be stale');
|
|
357
|
+
}
|
|
358
|
+
});
|
|
359
|
+
}
|
|
360
|
+
export async function runNoteUpdate({ command, options, args }) {
|
|
361
|
+
const updateOptions = options;
|
|
362
|
+
await executeNoteUpdateOperation({
|
|
363
|
+
boxOption: updateOptions.boxes,
|
|
364
|
+
command,
|
|
365
|
+
dryRun: Boolean(updateOptions.dryRun),
|
|
366
|
+
id: args.id,
|
|
367
|
+
idsOption: updateOptions.ids,
|
|
368
|
+
starred: typeof updateOptions.starred === 'string'
|
|
369
|
+
? parseStarredOption(updateOptions.starred, '--starred')
|
|
370
|
+
: undefined,
|
|
371
|
+
tagsOption: updateOptions.tags,
|
|
372
|
+
commandName: 'dino note update',
|
|
373
|
+
actionName: 'update_note',
|
|
374
|
+
});
|
|
375
|
+
}
|
|
376
|
+
export async function runNoteStar({ command, options, args }) {
|
|
377
|
+
const starOptions = options;
|
|
378
|
+
await executeNoteUpdateOperation({
|
|
379
|
+
actionLabel: 'Starred',
|
|
380
|
+
command,
|
|
381
|
+
dryRun: Boolean(starOptions.dryRun),
|
|
382
|
+
id: args.id,
|
|
383
|
+
idsOption: starOptions.ids,
|
|
384
|
+
starred: true,
|
|
385
|
+
staleVerb: 'starred',
|
|
386
|
+
commandName: 'dino note star',
|
|
387
|
+
actionName: 'star_note',
|
|
388
|
+
});
|
|
389
|
+
}
|
|
390
|
+
export async function runNoteUnstar({ command, options, args }) {
|
|
391
|
+
const unstarOptions = options;
|
|
392
|
+
await executeNoteUpdateOperation({
|
|
393
|
+
actionLabel: 'Unstarred',
|
|
394
|
+
command,
|
|
395
|
+
dryRun: Boolean(unstarOptions.dryRun),
|
|
396
|
+
id: args.id,
|
|
397
|
+
idsOption: unstarOptions.ids,
|
|
398
|
+
starred: false,
|
|
399
|
+
staleVerb: 'unstarred',
|
|
400
|
+
commandName: 'dino note unstar',
|
|
401
|
+
actionName: 'unstar_note',
|
|
402
|
+
});
|
|
403
|
+
}
|