@dinoxx/dinox-cli 1.0.17 → 1.0.19
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 +72 -34
- package/dist/cli.js +15 -1
- package/dist/commands/auth/index.d.ts +2 -0
- package/dist/commands/auth/index.js +209 -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 +6 -3
- 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 +751 -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/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/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
|
@@ -152,3 +152,44 @@ export async function addBox(db, input) {
|
|
|
152
152
|
restored: false,
|
|
153
153
|
};
|
|
154
154
|
}
|
|
155
|
+
export async function inspectAddBox(db, input) {
|
|
156
|
+
const userId = input.userId.trim();
|
|
157
|
+
const name = normalizeName(input.name);
|
|
158
|
+
if (!name) {
|
|
159
|
+
throw new DinoxError('Box name is required');
|
|
160
|
+
}
|
|
161
|
+
const description = normalizeName(input.description ?? '');
|
|
162
|
+
const color = normalizeName(input.color ?? '');
|
|
163
|
+
const lookup = normalizeLookup(name);
|
|
164
|
+
const columnNames = await getBoxColumns(db);
|
|
165
|
+
const requiredColumns = ['id', 'name', 'user_no'];
|
|
166
|
+
const missingRequired = requiredColumns.filter((column) => !columnNames.has(column));
|
|
167
|
+
if (missingRequired.length > 0) {
|
|
168
|
+
throw new DinoxError(`c_zettel_box table is missing required columns: ${missingRequired.join(', ')}`);
|
|
169
|
+
}
|
|
170
|
+
if (!userId) {
|
|
171
|
+
throw new DinoxError('c_zettel_box insert requires user_no. Missing persisted userId. Run `dino auth login` first.');
|
|
172
|
+
}
|
|
173
|
+
const rows = await listBoxRowsForWrite(db);
|
|
174
|
+
const sameNameRows = rows.filter((row) => normalizeLookup(row.name ?? '') === lookup);
|
|
175
|
+
const activeRows = sameNameRows.filter((row) => row.is_del !== 1);
|
|
176
|
+
if (activeRows.length > 0) {
|
|
177
|
+
const ids = activeRows.map((row) => row.id?.trim() ?? '').filter(Boolean);
|
|
178
|
+
throw new DinoxError(`Zettel box already exists: ${name}`, {
|
|
179
|
+
details: {
|
|
180
|
+
type: 'box_exists',
|
|
181
|
+
name,
|
|
182
|
+
ids,
|
|
183
|
+
question: 'This box already exists. Do you want to keep using the existing box?',
|
|
184
|
+
},
|
|
185
|
+
});
|
|
186
|
+
}
|
|
187
|
+
const deletedRow = sameNameRows.find((row) => (row.id?.trim() ?? '').length > 0);
|
|
188
|
+
return {
|
|
189
|
+
name,
|
|
190
|
+
description,
|
|
191
|
+
color,
|
|
192
|
+
restored: Boolean(deletedRow?.id),
|
|
193
|
+
...(deletedRow?.id ? { existingId: deletedRow.id } : {}),
|
|
194
|
+
};
|
|
195
|
+
}
|
|
@@ -1,3 +1,5 @@
|
|
|
1
1
|
import { Command } from 'commander';
|
|
2
|
+
import type { CommandGroupDescriptor } from '../descriptor/types.js';
|
|
2
3
|
export declare function sanitizeConfigForDisplay(config: unknown): unknown;
|
|
4
|
+
export declare const configCommandGroup: CommandGroupDescriptor;
|
|
3
5
|
export declare function registerConfigCommands(program: Command): void;
|
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
import { isConfigKey, isFixedConfigKey, isReadableConfigKey } from '../../config/keys.js';
|
|
2
2
|
import { getConfigValue, setConfigValue } from '../../config/store.js';
|
|
3
|
-
import {
|
|
3
|
+
import { registerCommandGroup } from '../descriptor/register.js';
|
|
4
|
+
import { FORMAT_OPTIONS } from '../schema/globalOptions.js';
|
|
4
5
|
import { DinoxError } from '../../utils/errors.js';
|
|
5
|
-
import {
|
|
6
|
+
import { getRequestedStructuredOutputFormat, printSuccess } from '../../utils/output.js';
|
|
7
|
+
import { redactAuthorization } from '../../utils/redact.js';
|
|
6
8
|
export function sanitizeConfigForDisplay(config) {
|
|
7
9
|
if (!config || typeof config !== 'object' || Array.isArray(config)) {
|
|
8
10
|
return config;
|
|
@@ -15,40 +17,87 @@ export function sanitizeConfigForDisplay(config) {
|
|
|
15
17
|
delete clone.powersync;
|
|
16
18
|
return clone;
|
|
17
19
|
}
|
|
18
|
-
|
|
19
|
-
const
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
throw new DinoxError(`Unknown config key: ${key}`);
|
|
20
|
+
async function runConfigGet(context) {
|
|
21
|
+
const key = context.args.key;
|
|
22
|
+
if (key && !isReadableConfigKey(key)) {
|
|
23
|
+
throw new DinoxError(`Unknown config key: ${key}`);
|
|
24
|
+
}
|
|
25
|
+
const value = await getConfigValue(key);
|
|
26
|
+
printSuccess(key ? value : sanitizeConfigForDisplay(value));
|
|
27
|
+
}
|
|
28
|
+
async function runConfigSet(context) {
|
|
29
|
+
const key = context.args.key;
|
|
30
|
+
const value = context.args.value;
|
|
31
|
+
if (!key || value === undefined) {
|
|
32
|
+
throw new DinoxError('Config key and value are required');
|
|
33
|
+
}
|
|
34
|
+
if (isFixedConfigKey(key)) {
|
|
35
|
+
throw new DinoxError(`${key} is fixed and cannot be changed`);
|
|
36
|
+
}
|
|
37
|
+
if (!isConfigKey(key)) {
|
|
38
|
+
throw new DinoxError(`Unknown config key: ${key}`);
|
|
39
|
+
}
|
|
40
|
+
if (key === 'sync.timeoutMs') {
|
|
41
|
+
const parsed = Number(value);
|
|
42
|
+
if (!Number.isFinite(parsed) || parsed <= 0) {
|
|
43
|
+
throw new DinoxError('sync.timeoutMs must be a positive integer');
|
|
43
44
|
}
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
throw new DinoxError('sync.timeoutMs must be a positive integer');
|
|
48
|
-
}
|
|
49
|
-
await setConfigValue(key, Math.trunc(parsed));
|
|
50
|
-
return;
|
|
45
|
+
await setConfigValue(key, Math.trunc(parsed));
|
|
46
|
+
if (getRequestedStructuredOutputFormat()) {
|
|
47
|
+
printSuccess({ ok: true, key, value: Math.trunc(parsed) });
|
|
51
48
|
}
|
|
52
|
-
|
|
53
|
-
}
|
|
49
|
+
return;
|
|
50
|
+
}
|
|
51
|
+
await setConfigValue(key, value);
|
|
52
|
+
if (getRequestedStructuredOutputFormat()) {
|
|
53
|
+
printSuccess({ ok: true, key, value });
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
export const configCommandGroup = {
|
|
57
|
+
name: 'config',
|
|
58
|
+
description: 'Read and write Dinox configuration',
|
|
59
|
+
commands: [
|
|
60
|
+
{
|
|
61
|
+
name: 'get',
|
|
62
|
+
description: 'Read sanitized Dinox CLI configuration values',
|
|
63
|
+
arguments: [{ syntax: '[key]', description: 'Optional config key (dot path)' }],
|
|
64
|
+
schema: {
|
|
65
|
+
path: 'config.get',
|
|
66
|
+
risk: 'read',
|
|
67
|
+
auth: 'none',
|
|
68
|
+
globalOptions: FORMAT_OPTIONS,
|
|
69
|
+
output: {
|
|
70
|
+
type: 'structured',
|
|
71
|
+
summary: 'Requested config value or sanitized config object',
|
|
72
|
+
fields: ['sync.timeoutMs', 'auth'],
|
|
73
|
+
},
|
|
74
|
+
examples: ['dino config get --format json', 'dino config get sync.timeoutMs --format json'],
|
|
75
|
+
},
|
|
76
|
+
action: runConfigGet,
|
|
77
|
+
},
|
|
78
|
+
{
|
|
79
|
+
name: 'set',
|
|
80
|
+
description: 'Write configurable Dinox CLI settings',
|
|
81
|
+
arguments: [
|
|
82
|
+
{ syntax: '<key>', description: 'Config key (dot path)' },
|
|
83
|
+
{ syntax: '<value>', description: 'Config value' },
|
|
84
|
+
],
|
|
85
|
+
schema: {
|
|
86
|
+
path: 'config.set',
|
|
87
|
+
risk: 'write',
|
|
88
|
+
auth: 'none',
|
|
89
|
+
globalOptions: FORMAT_OPTIONS,
|
|
90
|
+
output: {
|
|
91
|
+
type: 'structured',
|
|
92
|
+
summary: 'Config write acknowledgement',
|
|
93
|
+
fields: ['key', 'value'],
|
|
94
|
+
},
|
|
95
|
+
examples: ['dino config set sync.timeoutMs 20000 --format json'],
|
|
96
|
+
},
|
|
97
|
+
action: runConfigSet,
|
|
98
|
+
},
|
|
99
|
+
],
|
|
100
|
+
};
|
|
101
|
+
export function registerConfigCommands(program) {
|
|
102
|
+
registerCommandGroup(program, configCommandGroup);
|
|
54
103
|
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { Command } from 'commander';
|
|
2
|
+
import type { CommandSchema } from '../schema/types.js';
|
|
3
|
+
import type { CommandDescriptor, CommandGroupDescriptor } from './types.js';
|
|
4
|
+
export declare function registerCommandGroup(program: Command, group: CommandGroupDescriptor): Command;
|
|
5
|
+
export declare function registerStandaloneCommand(program: Command, descriptor: CommandDescriptor): Command;
|
|
6
|
+
export declare function commandSchemaFromDescriptor(groupName: string, descriptor: CommandDescriptor): CommandSchema;
|
|
7
|
+
export declare function commandSchemasFromGroupDescriptor(group: CommandGroupDescriptor): CommandSchema[];
|
|
8
|
+
export declare function standaloneCommandSchemaFromDescriptor(descriptor: CommandDescriptor): CommandSchema;
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
import { toSchemaArgument, toSchemaOption } from './types.js';
|
|
2
|
+
function deriveArgumentKey(syntax) {
|
|
3
|
+
const normalized = syntax.trim().replace(/^[<[.]+|[>\].]+$/g, '');
|
|
4
|
+
return normalized.replace(/[-_]+([a-zA-Z0-9])/g, (_match, segment) => segment.toUpperCase());
|
|
5
|
+
}
|
|
6
|
+
function addArgument(command, argument) {
|
|
7
|
+
command.argument(argument.syntax, argument.description);
|
|
8
|
+
}
|
|
9
|
+
function addOption(command, option) {
|
|
10
|
+
if (option.required) {
|
|
11
|
+
if (option.parser && option.defaultValue !== undefined) {
|
|
12
|
+
command.requiredOption(option.flags, option.description, option.parser, option.defaultValue);
|
|
13
|
+
return;
|
|
14
|
+
}
|
|
15
|
+
if (option.parser) {
|
|
16
|
+
command.requiredOption(option.flags, option.description, option.parser);
|
|
17
|
+
return;
|
|
18
|
+
}
|
|
19
|
+
if (option.defaultValue !== undefined) {
|
|
20
|
+
command.requiredOption(option.flags, option.description, option.defaultValue);
|
|
21
|
+
return;
|
|
22
|
+
}
|
|
23
|
+
command.requiredOption(option.flags, option.description);
|
|
24
|
+
return;
|
|
25
|
+
}
|
|
26
|
+
if (option.parser && option.defaultValue !== undefined) {
|
|
27
|
+
command.option(option.flags, option.description, option.parser, option.defaultValue);
|
|
28
|
+
return;
|
|
29
|
+
}
|
|
30
|
+
if (option.parser) {
|
|
31
|
+
command.option(option.flags, option.description, option.parser);
|
|
32
|
+
return;
|
|
33
|
+
}
|
|
34
|
+
if (option.defaultValue !== undefined) {
|
|
35
|
+
command.option(option.flags, option.description, option.defaultValue);
|
|
36
|
+
return;
|
|
37
|
+
}
|
|
38
|
+
command.option(option.flags, option.description);
|
|
39
|
+
}
|
|
40
|
+
function buildExecutionContext(command, descriptors, values) {
|
|
41
|
+
const namedArgs = Object.fromEntries(descriptors.map((descriptor, index) => [deriveArgumentKey(descriptor.syntax), values[index]]));
|
|
42
|
+
return {
|
|
43
|
+
command,
|
|
44
|
+
globals: (command.optsWithGlobals?.() ?? {}),
|
|
45
|
+
options: (command.opts() ?? {}),
|
|
46
|
+
args: namedArgs,
|
|
47
|
+
};
|
|
48
|
+
}
|
|
49
|
+
export function registerCommandGroup(program, group) {
|
|
50
|
+
const parent = program.command(group.name).description(group.description);
|
|
51
|
+
for (const descriptor of group.commands) {
|
|
52
|
+
const subcommand = parent.command(descriptor.name).description(descriptor.description);
|
|
53
|
+
const argumentsList = descriptor.arguments ?? [];
|
|
54
|
+
for (const argument of argumentsList) {
|
|
55
|
+
addArgument(subcommand, argument);
|
|
56
|
+
}
|
|
57
|
+
for (const option of descriptor.options ?? []) {
|
|
58
|
+
addOption(subcommand, option);
|
|
59
|
+
}
|
|
60
|
+
subcommand.action(async (...actionArgs) => {
|
|
61
|
+
const command = actionArgs[actionArgs.length - 1];
|
|
62
|
+
const positionalValues = actionArgs.slice(0, argumentsList.length);
|
|
63
|
+
await descriptor.action(buildExecutionContext(command, argumentsList, positionalValues));
|
|
64
|
+
});
|
|
65
|
+
}
|
|
66
|
+
parent.action((_options, command) => {
|
|
67
|
+
command.outputHelp();
|
|
68
|
+
});
|
|
69
|
+
return parent;
|
|
70
|
+
}
|
|
71
|
+
function buildCommandInvocation(groupName, descriptor) {
|
|
72
|
+
return `dino ${groupName} ${descriptor.name}`.trimEnd();
|
|
73
|
+
}
|
|
74
|
+
function registerCommandDescriptor(command, descriptor) {
|
|
75
|
+
const argumentsList = descriptor.arguments ?? [];
|
|
76
|
+
for (const argument of argumentsList) {
|
|
77
|
+
addArgument(command, argument);
|
|
78
|
+
}
|
|
79
|
+
for (const option of descriptor.options ?? []) {
|
|
80
|
+
addOption(command, option);
|
|
81
|
+
}
|
|
82
|
+
command.action(async (...actionArgs) => {
|
|
83
|
+
const currentCommand = actionArgs[actionArgs.length - 1];
|
|
84
|
+
const positionalValues = actionArgs.slice(0, argumentsList.length);
|
|
85
|
+
await descriptor.action(buildExecutionContext(currentCommand, argumentsList, positionalValues));
|
|
86
|
+
});
|
|
87
|
+
return command;
|
|
88
|
+
}
|
|
89
|
+
export function registerStandaloneCommand(program, descriptor) {
|
|
90
|
+
const command = program.command(descriptor.name).description(descriptor.description);
|
|
91
|
+
return registerCommandDescriptor(command, descriptor);
|
|
92
|
+
}
|
|
93
|
+
export function commandSchemaFromDescriptor(groupName, descriptor) {
|
|
94
|
+
return {
|
|
95
|
+
...descriptor.schema,
|
|
96
|
+
command: buildCommandInvocation(groupName, descriptor),
|
|
97
|
+
summary: descriptor.description,
|
|
98
|
+
...(descriptor.arguments?.length ? { arguments: descriptor.arguments.map(toSchemaArgument) } : {}),
|
|
99
|
+
...(descriptor.options?.length ? { options: descriptor.options.map(toSchemaOption) } : {}),
|
|
100
|
+
};
|
|
101
|
+
}
|
|
102
|
+
export function commandSchemasFromGroupDescriptor(group) {
|
|
103
|
+
return group.commands.map((descriptor) => commandSchemaFromDescriptor(group.name, descriptor));
|
|
104
|
+
}
|
|
105
|
+
export function standaloneCommandSchemaFromDescriptor(descriptor) {
|
|
106
|
+
return {
|
|
107
|
+
...descriptor.schema,
|
|
108
|
+
command: `dino ${descriptor.name}`,
|
|
109
|
+
summary: descriptor.description,
|
|
110
|
+
...(descriptor.arguments?.length ? { arguments: descriptor.arguments.map(toSchemaArgument) } : {}),
|
|
111
|
+
...(descriptor.options?.length ? { options: descriptor.options.map(toSchemaOption) } : {}),
|
|
112
|
+
};
|
|
113
|
+
}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import type { Command } from 'commander';
|
|
2
|
+
import type { CommandSchema, CommandSchemaArgument, CommandSchemaOption } from '../schema/types.js';
|
|
3
|
+
export type CommandOptionDescriptor = {
|
|
4
|
+
flags: string;
|
|
5
|
+
description: string;
|
|
6
|
+
type?: 'string' | 'number' | 'boolean' | 'string[]';
|
|
7
|
+
required?: boolean;
|
|
8
|
+
parser?: (value: string, previous: unknown) => unknown;
|
|
9
|
+
defaultValue?: unknown;
|
|
10
|
+
enum?: string[];
|
|
11
|
+
};
|
|
12
|
+
export type CommandArgumentDescriptor = {
|
|
13
|
+
syntax: string;
|
|
14
|
+
description: string;
|
|
15
|
+
};
|
|
16
|
+
export type CommandExecutionContext = {
|
|
17
|
+
command: Command;
|
|
18
|
+
globals: Record<string, unknown>;
|
|
19
|
+
options: Record<string, unknown>;
|
|
20
|
+
args: Record<string, string | undefined>;
|
|
21
|
+
};
|
|
22
|
+
export type CommandDescriptorSchema = Omit<CommandSchema, 'command' | 'summary' | 'arguments' | 'options'>;
|
|
23
|
+
export type CommandDescriptor = {
|
|
24
|
+
name: string;
|
|
25
|
+
description: string;
|
|
26
|
+
arguments?: CommandArgumentDescriptor[];
|
|
27
|
+
options?: CommandOptionDescriptor[];
|
|
28
|
+
schema: CommandDescriptorSchema;
|
|
29
|
+
action: (context: CommandExecutionContext) => Promise<void> | void;
|
|
30
|
+
};
|
|
31
|
+
export type CommandGroupDescriptor = {
|
|
32
|
+
name: string;
|
|
33
|
+
description: string;
|
|
34
|
+
commands: CommandDescriptor[];
|
|
35
|
+
};
|
|
36
|
+
export declare function toSchemaArgument(argument: CommandArgumentDescriptor): CommandSchemaArgument;
|
|
37
|
+
export declare function toSchemaOption(option: CommandOptionDescriptor): CommandSchemaOption;
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
export function toSchemaArgument(argument) {
|
|
2
|
+
return {
|
|
3
|
+
name: argument.syntax,
|
|
4
|
+
required: argument.syntax.trim().startsWith('<'),
|
|
5
|
+
description: argument.description,
|
|
6
|
+
};
|
|
7
|
+
}
|
|
8
|
+
export function toSchemaOption(option) {
|
|
9
|
+
return {
|
|
10
|
+
name: option.flags,
|
|
11
|
+
description: option.description,
|
|
12
|
+
...(option.type ? { type: option.type } : {}),
|
|
13
|
+
...(option.required ? { required: true } : {}),
|
|
14
|
+
...(option.defaultValue !== undefined ? { defaultValue: option.defaultValue } : {}),
|
|
15
|
+
...(option.enum ? { enum: option.enum } : {}),
|
|
16
|
+
};
|
|
17
|
+
}
|
|
@@ -1,12 +1,15 @@
|
|
|
1
|
-
import { printYaml } from '../../utils/output.js';
|
|
2
|
-
import { sendDaemonRequest } from '../../daemon/client.js';
|
|
3
|
-
import { ensureDaemonRunning } from '../../daemon/ensure.js';
|
|
4
|
-
import { DinoxError } from '../../utils/errors.js';
|
|
5
1
|
import { loadConfig } from '../../config/store.js';
|
|
6
2
|
import { resolveConfig } from '../../config/resolve.js';
|
|
7
3
|
import { connectPowerSync } from '../../powersync/runtime.js';
|
|
4
|
+
import { sendDaemonRequest } from '../../daemon/client.js';
|
|
5
|
+
import { ensureDaemonRunning } from '../../daemon/ensure.js';
|
|
6
|
+
import { registerCommandGroup } from '../descriptor/register.js';
|
|
7
|
+
import { FORMAT_OPTIONS, OFFLINE_OPTIONS, SYNC_TIMEOUT_OPTIONS } from '../schema/globalOptions.js';
|
|
8
|
+
import { DinoxError } from '../../utils/errors.js';
|
|
9
|
+
import { printSuccess } from '../../utils/output.js';
|
|
8
10
|
import { parseSyncTimeoutMs } from '../../utils/syncTimeout.js';
|
|
9
11
|
import { getBacklinks, getGraphStats, getOutlinks, getRelatedNotes } from './repo.js';
|
|
12
|
+
const GRAPH_GLOBAL_OPTIONS = [...FORMAT_OPTIONS, ...OFFLINE_OPTIONS, ...SYNC_TIMEOUT_OPTIONS];
|
|
10
13
|
function parseDepth(value) {
|
|
11
14
|
if (value === undefined || value === null || value === '') {
|
|
12
15
|
return 2;
|
|
@@ -26,8 +29,7 @@ function parseDepth(value) {
|
|
|
26
29
|
}
|
|
27
30
|
async function withOfflineDb(command, fn) {
|
|
28
31
|
const globals = command.optsWithGlobals?.() ?? {};
|
|
29
|
-
|
|
30
|
-
if (!offline) {
|
|
32
|
+
if (!Boolean(globals.offline)) {
|
|
31
33
|
throw new DinoxError('withOfflineDb called without --offline');
|
|
32
34
|
}
|
|
33
35
|
const config = resolveConfig(await loadConfig());
|
|
@@ -40,74 +42,142 @@ async function withOfflineDb(command, fn) {
|
|
|
40
42
|
await db.close().catch(() => undefined);
|
|
41
43
|
}
|
|
42
44
|
}
|
|
43
|
-
|
|
44
|
-
const
|
|
45
|
-
|
|
46
|
-
.command
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
45
|
+
async function runGraphCommand(context, onlineAction, offlineAction) {
|
|
46
|
+
const globals = context.globals;
|
|
47
|
+
if (Boolean(globals.offline)) {
|
|
48
|
+
printSuccess(await withOfflineDb(context.command, offlineAction));
|
|
49
|
+
return;
|
|
50
|
+
}
|
|
51
|
+
await ensureDaemonRunning();
|
|
52
|
+
printSuccess(await onlineAction());
|
|
53
|
+
}
|
|
54
|
+
const graphBacklinksCommand = {
|
|
55
|
+
name: 'backlinks',
|
|
56
|
+
description: 'Get notes that link to a target note',
|
|
57
|
+
arguments: [{ syntax: '<id>', description: 'Note id' }],
|
|
58
|
+
schema: {
|
|
59
|
+
path: 'graph.backlinks',
|
|
60
|
+
risk: 'read',
|
|
61
|
+
auth: 'required',
|
|
62
|
+
globalOptions: GRAPH_GLOBAL_OPTIONS,
|
|
63
|
+
output: {
|
|
64
|
+
type: 'structured',
|
|
65
|
+
summary: 'Backlink graph payload',
|
|
66
|
+
fields: ['note_id', 'backlinks'],
|
|
67
|
+
},
|
|
68
|
+
mcp: {
|
|
69
|
+
toolName: 'dinox_graph_backlinks',
|
|
70
|
+
description: 'Get notes that link to this note',
|
|
71
|
+
properties: [{ name: 'id', type: 'string', description: 'Note ID', required: true }],
|
|
72
|
+
},
|
|
73
|
+
examples: ['dino graph backlinks <id> --format json'],
|
|
74
|
+
},
|
|
75
|
+
action: async (context) => {
|
|
76
|
+
const id = context.args.id;
|
|
77
|
+
if (!id) {
|
|
78
|
+
throw new DinoxError('Note id is required');
|
|
72
79
|
}
|
|
73
|
-
await
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
80
|
+
await runGraphCommand(context, () => sendDaemonRequest('graph.backlinks', { id }), (db) => getBacklinks(db, id));
|
|
81
|
+
},
|
|
82
|
+
};
|
|
83
|
+
const graphOutlinksCommand = {
|
|
84
|
+
name: 'outlinks',
|
|
85
|
+
description: 'Get notes that this note links to',
|
|
86
|
+
arguments: [{ syntax: '<id>', description: 'Note id' }],
|
|
87
|
+
schema: {
|
|
88
|
+
path: 'graph.outlinks',
|
|
89
|
+
risk: 'read',
|
|
90
|
+
auth: 'required',
|
|
91
|
+
globalOptions: GRAPH_GLOBAL_OPTIONS,
|
|
92
|
+
output: {
|
|
93
|
+
type: 'structured',
|
|
94
|
+
summary: 'Outlink graph payload',
|
|
95
|
+
fields: ['note_id', 'outlinks'],
|
|
96
|
+
},
|
|
97
|
+
mcp: {
|
|
98
|
+
toolName: 'dinox_graph_outlinks',
|
|
99
|
+
description: 'Get notes that this note links to',
|
|
100
|
+
properties: [{ name: 'id', type: 'string', description: 'Note ID', required: true }],
|
|
101
|
+
},
|
|
102
|
+
examples: ['dino graph outlinks <id> --format json'],
|
|
103
|
+
},
|
|
104
|
+
action: async (context) => {
|
|
105
|
+
const id = context.args.id;
|
|
106
|
+
if (!id) {
|
|
107
|
+
throw new DinoxError('Note id is required');
|
|
87
108
|
}
|
|
88
|
-
await
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
.
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
109
|
+
await runGraphCommand(context, () => sendDaemonRequest('graph.outlinks', { id }), (db) => getOutlinks(db, id));
|
|
110
|
+
},
|
|
111
|
+
};
|
|
112
|
+
const graphRelatedCommand = {
|
|
113
|
+
name: 'related',
|
|
114
|
+
description: 'Get notes related within N degrees of separation',
|
|
115
|
+
arguments: [{ syntax: '<id>', description: 'Note id' }],
|
|
116
|
+
options: [{ flags: '--depth <n>', description: 'Degrees of separation (1-5)', defaultValue: '2' }],
|
|
117
|
+
schema: {
|
|
118
|
+
path: 'graph.related',
|
|
119
|
+
risk: 'read',
|
|
120
|
+
auth: 'required',
|
|
121
|
+
globalOptions: GRAPH_GLOBAL_OPTIONS,
|
|
122
|
+
output: {
|
|
123
|
+
type: 'structured',
|
|
124
|
+
summary: 'Related-notes graph payload',
|
|
125
|
+
fields: ['note_id', 'depth', 'related_notes'],
|
|
126
|
+
},
|
|
127
|
+
mcp: {
|
|
128
|
+
toolName: 'dinox_graph_related',
|
|
129
|
+
description: 'Get related notes within N degrees',
|
|
130
|
+
properties: [
|
|
131
|
+
{ name: 'id', type: 'string', description: 'Note ID', required: true },
|
|
132
|
+
{ name: 'depth', type: 'number', description: 'Degrees (1-5, default: 2)' },
|
|
133
|
+
],
|
|
134
|
+
},
|
|
135
|
+
examples: ['dino graph related <id> --depth 2 --format json'],
|
|
136
|
+
},
|
|
137
|
+
action: async (context) => {
|
|
138
|
+
const id = context.args.id;
|
|
139
|
+
if (!id) {
|
|
140
|
+
throw new DinoxError('Note id is required');
|
|
105
141
|
}
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
142
|
+
const depth = parseDepth(context.options.depth);
|
|
143
|
+
await runGraphCommand(context, () => sendDaemonRequest('graph.related', { id, depth }), (db) => getRelatedNotes(db, id, depth));
|
|
144
|
+
},
|
|
145
|
+
};
|
|
146
|
+
const graphStatsCommand = {
|
|
147
|
+
name: 'stats',
|
|
148
|
+
description: 'Get graph statistics for notes and links',
|
|
149
|
+
schema: {
|
|
150
|
+
path: 'graph.stats',
|
|
151
|
+
risk: 'read',
|
|
152
|
+
auth: 'required',
|
|
153
|
+
globalOptions: GRAPH_GLOBAL_OPTIONS,
|
|
154
|
+
output: {
|
|
155
|
+
type: 'structured',
|
|
156
|
+
summary: 'Graph statistics payload',
|
|
157
|
+
entity: 'graph_stats',
|
|
158
|
+
fields: ['notes_count', 'links_count', 'connected_notes_count', 'isolated_notes_count'],
|
|
159
|
+
},
|
|
160
|
+
mcp: {
|
|
161
|
+
toolName: 'dinox_graph_stats',
|
|
162
|
+
description: 'Get graph statistics for notes and links',
|
|
163
|
+
properties: [],
|
|
164
|
+
},
|
|
165
|
+
examples: ['dino graph stats --format json'],
|
|
166
|
+
},
|
|
167
|
+
action: async (context) => {
|
|
168
|
+
await runGraphCommand(context, () => sendDaemonRequest('graph.stats'), (db) => getGraphStats(db));
|
|
169
|
+
},
|
|
170
|
+
};
|
|
171
|
+
export const graphCommandGroup = {
|
|
172
|
+
name: 'graph',
|
|
173
|
+
description: 'Query knowledge graph',
|
|
174
|
+
commands: [
|
|
175
|
+
graphBacklinksCommand,
|
|
176
|
+
graphOutlinksCommand,
|
|
177
|
+
graphRelatedCommand,
|
|
178
|
+
graphStatsCommand,
|
|
179
|
+
],
|
|
180
|
+
};
|
|
181
|
+
export function registerGraphCommands(program) {
|
|
182
|
+
registerCommandGroup(program, graphCommandGroup);
|
|
113
183
|
}
|