@dinoxx/dinox-cli 2.2.0 → 2.4.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.
Files changed (102) hide show
  1. package/README.md +17 -3
  2. package/dist/cli.js +6 -0
  3. package/dist/commands/domain/options.d.ts +10 -0
  4. package/dist/commands/domain/options.js +75 -0
  5. package/dist/commands/domain/run.d.ts +24 -0
  6. package/dist/commands/domain/run.js +137 -0
  7. package/dist/commands/gateway/index.d.ts +4 -0
  8. package/dist/commands/gateway/index.js +300 -0
  9. package/dist/commands/notes/actions.js +47 -18
  10. package/dist/commands/notes/createFromTemplate.d.ts +35 -0
  11. package/dist/commands/notes/createFromTemplate.js +58 -0
  12. package/dist/commands/notes/descriptors.js +9 -4
  13. package/dist/commands/notes/repo/read.js +4 -23
  14. package/dist/commands/notes/repo/write.d.ts +2 -0
  15. package/dist/commands/notes/repo/write.js +1 -1
  16. package/dist/commands/properties/index.d.ts +16 -0
  17. package/dist/commands/properties/index.js +640 -0
  18. package/dist/commands/properties/repo.d.ts +150 -0
  19. package/dist/commands/properties/repo.js +272 -0
  20. package/dist/commands/savedViews/fields.d.ts +28 -0
  21. package/dist/commands/savedViews/fields.js +87 -0
  22. package/dist/commands/savedViews/index.js +203 -164
  23. package/dist/commands/savedViews/model.d.ts +27 -69
  24. package/dist/commands/savedViews/model.js +63 -279
  25. package/dist/commands/savedViews/repo.d.ts +64 -62
  26. package/dist/commands/savedViews/repo.js +309 -428
  27. package/dist/commands/schema/renderSkills.js +33 -5
  28. package/dist/commands/schema/spec.js +6 -0
  29. package/dist/commands/templates/index.d.ts +4 -0
  30. package/dist/commands/templates/index.js +290 -0
  31. package/dist/commands/templates/model.d.ts +12 -34
  32. package/dist/commands/templates/model.js +39 -83
  33. package/dist/commands/templates/repo.d.ts +53 -0
  34. package/dist/commands/templates/repo.js +78 -0
  35. package/dist/config/paths.d.ts +1 -0
  36. package/dist/config/paths.js +1 -1
  37. package/dist/daemon/server.d.ts +1 -0
  38. package/dist/daemon/server.js +31 -0
  39. package/dist/gateway/adapters/acp.d.ts +42 -0
  40. package/dist/gateway/adapters/acp.js +285 -0
  41. package/dist/gateway/adapters/echo.d.ts +12 -0
  42. package/dist/gateway/adapters/echo.js +51 -0
  43. package/dist/gateway/adapters/index.d.ts +9 -0
  44. package/dist/gateway/adapters/index.js +30 -0
  45. package/dist/gateway/assistantProjection.d.ts +32 -0
  46. package/dist/gateway/assistantProjection.js +193 -0
  47. package/dist/gateway/config.d.ts +50 -0
  48. package/dist/gateway/config.js +253 -0
  49. package/dist/gateway/detect.d.ts +40 -0
  50. package/dist/gateway/detect.js +145 -0
  51. package/dist/gateway/deviceHost.d.ts +82 -0
  52. package/dist/gateway/deviceHost.js +530 -0
  53. package/dist/gateway/mcpServer.d.ts +4 -0
  54. package/dist/gateway/mcpServer.js +118 -0
  55. package/dist/gateway/protocol.d.ts +29 -0
  56. package/dist/gateway/protocol.js +127 -0
  57. package/dist/gateway/runExecutor.d.ts +67 -0
  58. package/dist/gateway/runExecutor.js +316 -0
  59. package/dist/gateway/service.d.ts +26 -0
  60. package/dist/gateway/service.js +82 -0
  61. package/dist/gateway/streamPublisher.d.ts +33 -0
  62. package/dist/gateway/streamPublisher.js +93 -0
  63. package/dist/gateway/threadStore.d.ts +157 -0
  64. package/dist/gateway/threadStore.js +430 -0
  65. package/dist/gateway/types.d.ts +117 -0
  66. package/dist/gateway/types.js +9 -0
  67. package/dist/skills/registry.js +7 -0
  68. package/dist/utils/errorCodes.d.ts +212 -0
  69. package/dist/utils/errorCodes.js +53 -0
  70. package/dist/vendor/electron/domain/note-templates.d.ts +3 -0
  71. package/dist/vendor/electron/domain/note-templates.js +232 -0
  72. package/dist/vendor/electron/domain/properties.d.ts +10 -0
  73. package/dist/vendor/electron/domain/properties.js +1549 -0
  74. package/dist/vendor/electron/domain/saved-view-analytics.d.ts +45 -0
  75. package/dist/vendor/electron/domain/saved-view-analytics.js +365 -0
  76. package/dist/vendor/electron/domain/saved-view-relative-date.d.ts +21 -0
  77. package/dist/vendor/electron/domain/saved-view-relative-date.js +153 -0
  78. package/dist/vendor/electron/domain/saved-views.d.ts +61 -0
  79. package/dist/vendor/electron/domain/saved-views.js +1120 -0
  80. package/dist/vendor/electron/rpc.d.ts +22 -0
  81. package/dist/vendor/electron/rpc.js +27 -0
  82. package/dist/vendor/electron/shared/ai-errors.d.ts +11 -0
  83. package/dist/vendor/electron/shared/ai-errors.js +107 -0
  84. package/dist/vendor/electron/shared/external-types.d.ts +20 -0
  85. package/dist/vendor/electron/shared/external-types.js +1 -0
  86. package/dist/vendor/electron/shared/index.d.ts +9 -0
  87. package/dist/vendor/electron/shared/index.js +7 -0
  88. package/dist/vendor/electron/shared/note-properties.d.ts +328 -0
  89. package/dist/vendor/electron/shared/note-properties.js +1027 -0
  90. package/dist/vendor/electron/shared/note-templates.d.ts +82 -0
  91. package/dist/vendor/electron/shared/note-templates.js +84 -0
  92. package/dist/vendor/electron/shared/powersync-read-models.d.ts +1716 -0
  93. package/dist/vendor/electron/shared/powersync-read-models.js +1 -0
  94. package/dist/vendor/electron/shared/remote-device-agent.d.ts +373 -0
  95. package/dist/vendor/electron/shared/remote-device-agent.js +191 -0
  96. package/dist/vendor/electron/shared/saved-views.d.ts +394 -0
  97. package/dist/vendor/electron/shared/saved-views.js +981 -0
  98. package/dist/vendor/electron/sql-utils.d.ts +5 -0
  99. package/dist/vendor/electron/sql-utils.js +32 -0
  100. package/package.json +10 -4
  101. package/dist/commands/savedViews/semantics.d.ts +0 -46
  102. package/dist/commands/savedViews/semantics.js +0 -738
@@ -15,6 +15,8 @@ import { parseNoteSearchFields, projectNoteSearchFields } from './searchOutput.j
15
15
  import { parseCreatedAtRange } from './searchTime.js';
16
16
  import { buildNoteSearchResponse, normalizeDaysForMeta } from './searchResponse.js';
17
17
  import { parseMutationListInput, parseNoteType, parsePreviewLineCount, parseSearchLimit, parseSearchListInput, parseSearchOffset, parseStarredOption, parseStringInput, parseStringListInput, parseTargetNoteIds, syncBeforeNoteCommand, withNoteDatabase, } from './runtime.js';
18
+ import { readNoteProperties } from '../properties/repo.js';
19
+ import { createNoteWithSeed, previewNoteCreateProperties, resolveNoteCreateSeed } from './createFromTemplate.js';
18
20
  function toPublicNoteContext(note) {
19
21
  if (!note) {
20
22
  return null;
@@ -368,14 +370,31 @@ export async function runNoteDetail({ command, options, args }) {
368
370
  export async function runNoteCreate({ command, options }) {
369
371
  const createOptions = options;
370
372
  const structuredOutput = getRequestedStructuredOutputFormat() !== null;
373
+ if (createOptions.content === undefined && !createOptions.template) {
374
+ throw new DinoxError('--content is required unless --template is given', { code: 'USAGE_ERROR' });
375
+ }
371
376
  await withNoteDatabase(command, async (runtime) => {
372
377
  const { db, config, offline } = runtime;
373
378
  await syncBeforeNoteCommand(runtime);
374
379
  const durability = resolveWriteDurabilityForRuntime(runtime, createOptions.durability);
375
380
  const identity = await resolveAuthIdentity(config);
376
- const contentMd = await parseStringInput(String(createOptions.content));
377
- const canonicalContentMd = await canonicalizeBidlinksInMarkdown(db, contentMd, identity.userId);
378
- const { contentJson, contentText } = convertMarkdownToNoteContent(canonicalContentMd);
381
+ const seed = await resolveNoteCreateSeed(db, {
382
+ userId: identity.userId,
383
+ templateId: createOptions.template,
384
+ properties: createOptions.properties,
385
+ });
386
+ // Template content is used as the hydrated Tiptap document itself (as desktop does), so
387
+ // only explicit --content goes through Markdown conversion.
388
+ const useTemplateContent = createOptions.content === undefined && seed.templateContent !== null;
389
+ const contentMd = useTemplateContent
390
+ ? seed.templateContent.contentMd
391
+ : createOptions.content !== undefined ? await parseStringInput(String(createOptions.content)) : '';
392
+ const canonicalContentMd = useTemplateContent
393
+ ? contentMd
394
+ : await canonicalizeBidlinksInMarkdown(db, contentMd, identity.userId);
395
+ const { contentJson, contentText } = useTemplateContent
396
+ ? { contentJson: seed.templateContent.contentJson, contentText: seed.templateContent.contentText }
397
+ : convertMarkdownToNoteContent(canonicalContentMd);
379
398
  const tagsInput = typeof createOptions.tags === 'string'
380
399
  ? await parseStringListInput(createOptions.tags, '--tags')
381
400
  : [];
@@ -383,8 +402,21 @@ export async function runNoteCreate({ command, options }) {
383
402
  ? await parseStringListInput(createOptions.boxes, '--boxes')
384
403
  : [];
385
404
  const noteType = parseNoteType(createOptions.type);
405
+ const id = generateId();
406
+ const insert = async (tx) => createNote(tx, {
407
+ id,
408
+ title: String(createOptions.title),
409
+ noteType,
410
+ contentMd: canonicalContentMd,
411
+ contentJson,
412
+ contentText,
413
+ tags: await validateTagsForCreate(tx, tagsInput, identity.userId),
414
+ zettelBoxIds: await resolveZettelBoxIdsForCreate(tx, boxNames),
415
+ userId: identity.userId,
416
+ properties: seed.propertiesJson,
417
+ });
386
418
  if (Boolean(createOptions.dryRun)) {
387
- printDryRunPreview(await buildNoteCreateDryRunPreview({
419
+ const preview = await buildNoteCreateDryRunPreview({
388
420
  db,
389
421
  config,
390
422
  offline,
@@ -394,27 +426,24 @@ export async function runNoteCreate({ command, options }) {
394
426
  contentMd,
395
427
  tagsInput,
396
428
  zettelBoxNames: boxNames,
397
- }), runtime);
429
+ });
430
+ if (seed.template || seed.values) {
431
+ const properties = await previewNoteCreateProperties(db, { noteId: id, userId: identity.userId, seed, insert });
432
+ preview.inputs = { ...preview.inputs, ...(seed.template ? { template: { id: seed.template.id, name: seed.template.name } } : {}) };
433
+ preview.changes = { ...preview.changes, properties: properties.properties };
434
+ }
435
+ printDryRunPreview(preview, runtime);
398
436
  return;
399
437
  }
400
- const id = generateId();
401
- await createNote(db, {
402
- id,
403
- title: String(createOptions.title),
404
- noteType,
405
- contentMd: canonicalContentMd,
406
- contentJson,
407
- contentText,
408
- tags: await validateTagsForCreate(db, tagsInput, identity.userId),
409
- zettelBoxIds: await resolveZettelBoxIdsForCreate(db, boxNames),
410
- userId: identity.userId,
411
- });
438
+ await createNoteWithSeed(db, { noteId: id, userId: identity.userId, seed, insert });
412
439
  const upload = await waitForMutationUpload(runtime, { durability });
413
440
  const payload = {
414
441
  ok: true,
415
442
  id,
443
+ ...(seed.template ? { template: { id: seed.template.id, name: seed.template.name } } : {}),
444
+ ...(seed.template || seed.values ? { properties: (await readNoteProperties(db, { userId: identity.userId, noteId: id })).properties } : {}),
416
445
  ...(await readNoteWriteReceipt(db, id, {
417
- changed: ['title', 'type', 'content_md', 'content_json', 'content_text', 'tags', 'boxes'],
446
+ changed: ['title', 'type', 'content_md', 'content_json', 'content_text', 'tags', 'boxes', ...(seed.template || seed.values ? ['properties'] : [])],
418
447
  upload,
419
448
  stale: commandStale(runtime),
420
449
  })),
@@ -0,0 +1,35 @@
1
+ import type { AbstractPowerSyncDatabase } from '@powersync/common';
2
+ import { type NotePropertiesPatch } from '../../vendor/electron/shared/index.js';
3
+ import { type NotePropertiesView } from '../properties/repo.js';
4
+ import { type NoteTemplateDetail } from '../templates/model.js';
5
+ export type NoteCreateSeed = {
6
+ template: NoteTemplateDetail | null;
7
+ /** Hydrated template content projected like stored note content; null without content. */
8
+ templateContent: {
9
+ contentJson: unknown;
10
+ contentMd: string;
11
+ contentText: string;
12
+ } | null;
13
+ /** Serialized `c_note.properties` holding the template's declared empty slots. */
14
+ propertiesJson: string | null;
15
+ values: NotePropertiesPatch | null;
16
+ };
17
+ export declare function resolveNoteCreateSeed(db: AbstractPowerSyncDatabase, input: {
18
+ userId: string;
19
+ templateId?: string;
20
+ properties?: unknown;
21
+ }): Promise<NoteCreateSeed>;
22
+ /** Inserts the note and its initial property values atomically. */
23
+ export declare function createNoteWithSeed(db: AbstractPowerSyncDatabase, input: {
24
+ noteId: string;
25
+ userId: string;
26
+ seed: NoteCreateSeed;
27
+ insert: (tx: AbstractPowerSyncDatabase) => Promise<unknown>;
28
+ }): Promise<void>;
29
+ /** Dry-run: performs the insert and property writes, reads the resulting properties, rolls back. */
30
+ export declare function previewNoteCreateProperties(db: AbstractPowerSyncDatabase, input: {
31
+ noteId: string;
32
+ userId: string;
33
+ seed: NoteCreateSeed;
34
+ insert: (tx: AbstractPowerSyncDatabase) => Promise<unknown>;
35
+ }): Promise<NotePropertiesView>;
@@ -0,0 +1,58 @@
1
+ // `note create --template/--properties`: seeds a new note the way desktop's "create from
2
+ // template" does — hydrated template content plus the template's property keys declared as
3
+ // empty slots — and optionally writes initial property values in the same transaction.
4
+ import { DinoxError } from '../../utils/errors.js';
5
+ import { applyNotePropertySlotOperation, serializeRawNoteProperties, } from '../../vendor/electron/shared/index.js';
6
+ import { previewInRolledBackTransaction, runDomainCommand, withNestedWriteTransaction } from '../domain/run.js';
7
+ import { readJsonOption } from '../domain/options.js';
8
+ import { readNoteProperties } from '../properties/repo.js';
9
+ import { hydrateNoteTemplateDocument } from '../templates/model.js';
10
+ import { getNoteTemplate } from '../templates/repo.js';
11
+ import { projectNoteContent } from './contentEngine.js';
12
+ export async function resolveNoteCreateSeed(db, input) {
13
+ const template = input.templateId ? await getNoteTemplate(db, { userId: input.userId, id: input.templateId }) : null;
14
+ const projection = template?.document ? projectNoteContent(hydrateNoteTemplateDocument(template.document)) : null;
15
+ const templateContent = projection
16
+ ? { contentJson: projection.document, contentMd: projection.contentMd, contentText: projection.contentText }
17
+ : null;
18
+ const declared = applyNotePropertySlotOperation({}, 'declare', template?.propertyKeys ?? []).next;
19
+ let values = null;
20
+ if (input.properties !== undefined) {
21
+ const raw = await readJsonOption(input.properties, '--properties', 'note.create');
22
+ if (!raw || typeof raw !== 'object' || Array.isArray(raw)) {
23
+ throw new DinoxError('--properties must be a JSON object of property key to value', { code: 'USAGE_ERROR' });
24
+ }
25
+ values = raw;
26
+ if (Object.keys(values).length === 0)
27
+ values = null;
28
+ }
29
+ return { template, templateContent, propertiesJson: serializeRawNoteProperties(declared), values };
30
+ }
31
+ async function applyValues(tx, noteId, userId, seed) {
32
+ if (!seed.values)
33
+ return;
34
+ await runDomainCommand(tx, 'notes.updateProperties', {
35
+ noteId,
36
+ userId,
37
+ patch: seed.values,
38
+ updatedAt: new Date().toISOString(),
39
+ });
40
+ }
41
+ /** Inserts the note and its initial property values atomically. */
42
+ export async function createNoteWithSeed(db, input) {
43
+ if (!input.seed.values) {
44
+ await input.insert(db);
45
+ return;
46
+ }
47
+ await withNestedWriteTransaction(db, async (tx) => {
48
+ await input.insert(tx);
49
+ await applyValues(tx, input.noteId, input.userId, input.seed);
50
+ });
51
+ }
52
+ /** Dry-run: performs the insert and property writes, reads the resulting properties, rolls back. */
53
+ export async function previewNoteCreateProperties(db, input) {
54
+ return previewInRolledBackTransaction(db, async (tx) => {
55
+ await input.insert(tx);
56
+ await applyValues(tx, input.noteId, input.userId, input.seed);
57
+ }, (tx) => readNoteProperties(tx, { userId: input.userId, noteId: input.noteId }));
58
+ }
@@ -176,10 +176,12 @@ export const noteCommandGroup = {
176
176
  },
177
177
  {
178
178
  name: 'create',
179
- description: 'Create a new note from markdown content',
179
+ description: 'Create a new note from markdown content or a note template',
180
180
  options: [
181
181
  { flags: '--title <string>', description: 'Note title', required: true, type: 'string' },
182
- { flags: '--content <string|@file>', description: 'Markdown content', required: true, type: 'string' },
182
+ { flags: '--content <string|@file>', description: 'Markdown content (required unless --template is given; overrides template content)', type: 'string' },
183
+ { flags: '--template <id>', description: 'Note template id: hydrates its content ({{date}}, {{time}}, ...) and declares its property keys as empty slots', type: 'string' },
184
+ { flags: '--properties <json|@file>', description: 'Initial property values as a JSON object; validated against property definitions', type: 'string' },
183
185
  { flags: '--type <note|crawl>', description: 'Note type: note or crawl', type: 'string', defaultValue: 'crawl', enum: ['note', 'crawl'] },
184
186
  { flags: '--tags <string|@file>', description: 'Tag list (JSON array or comma/newline-separated)', type: 'string' },
185
187
  { flags: '--boxes <string|@file>', description: 'Box paths or unique names (JSON array or comma/newline-separated)', type: 'string' },
@@ -194,9 +196,12 @@ export const noteCommandGroup = {
194
196
  output: {
195
197
  type: 'structured',
196
198
  summary: 'Creation acknowledgement with write durability, queue state, and note revision receipt',
197
- fields: ['id', ...NOTE_WRITE_RECEIPT_FIELDS],
199
+ fields: ['id', 'template', 'properties', ...NOTE_WRITE_RECEIPT_FIELDS],
198
200
  },
199
- examples: ['dino note create --title "Weekly note" --content @/tmp/note.md --type note --durability uploaded --format json'],
201
+ examples: [
202
+ 'dino note create --title "Weekly note" --content @/tmp/note.md --type note --durability uploaded --format json',
203
+ 'dino note create --title "Dune" --template <templateId> --properties \'{"rating":5}\' --type note --format json --dry-run',
204
+ ],
200
205
  },
201
206
  action: runNoteCreate,
202
207
  },
@@ -1,6 +1,6 @@
1
1
  // Note reads: single-note lookup, context, preview, detail, and export rows.
2
- import { DinoxError } from '../../../utils/errors.js';
3
2
  import { loadZettelBoxNameMap, normalizeStoredFlag, normalizeStoredStarred, parseStoredTags, parseZettelBoxIds, resolveSummary, } from './shared.js';
3
+ import { parseRawNoteProperties } from '../../../vendor/electron/shared/index.js';
4
4
  export async function getNote(db, id) {
5
5
  return db.getOptional(`
6
6
  SELECT
@@ -158,29 +158,10 @@ function parseStoredJsonValue(raw) {
158
158
  return raw;
159
159
  }
160
160
  }
161
+ // Same forgiving read as desktop: malformed storage reads as {} instead of failing the
162
+ // whole note read; `dino prop show` reports it via `valid: false`.
161
163
  function parseStoredNoteProperties(raw) {
162
- const value = raw?.trim() ?? '';
163
- if (!value) {
164
- return {};
165
- }
166
- let parsed;
167
- try {
168
- parsed = JSON.parse(value);
169
- }
170
- catch (cause) {
171
- throw new DinoxError('c_note.properties must be valid JSON', {
172
- code: 'NOTE_INVALID_PROPERTIES',
173
- details: { cause: cause instanceof Error ? cause.message : String(cause) },
174
- recoverable: false,
175
- });
176
- }
177
- if (!parsed || typeof parsed !== 'object' || Array.isArray(parsed)) {
178
- throw new DinoxError('c_note.properties must be a JSON object', {
179
- code: 'NOTE_INVALID_PROPERTIES',
180
- recoverable: false,
181
- });
182
- }
183
- return Object.fromEntries(Object.entries(parsed));
164
+ return parseRawNoteProperties(raw);
184
165
  }
185
166
  function mapNoteExportRow(note, boxNameMap) {
186
167
  const boxIds = parseZettelBoxIds(note.zettel_boxes_raw);
@@ -11,6 +11,8 @@ export declare function createNote(db: AbstractPowerSyncDatabase, input: {
11
11
  zettelBoxIds: string[];
12
12
  userId: string;
13
13
  imageDetail?: unknown[];
14
+ /** Serialized `c_note.properties` (e.g. template-declared empty slots); defaults to NULL. */
15
+ properties?: string | null;
14
16
  }): Promise<{
15
17
  id: string;
16
18
  }>;
@@ -77,7 +77,7 @@ export async function createNote(db, input) {
77
77
  ['resource_id', null],
78
78
  ['note_ids', '[]'],
79
79
  ['extra_data', '{}'],
80
- ['properties', null],
80
+ ['properties', input.properties ?? null],
81
81
  ['is_pub', 0],
82
82
  ['format_type', ''],
83
83
  ['comments', '[]'],
@@ -0,0 +1,16 @@
1
+ import { Command } from 'commander';
2
+ import { PROPERTY_OPTION_COLORS, STATUS_PROPERTY_GROUPS } from '../../vendor/electron/shared/index.js';
3
+ import type { CommandGroupDescriptor } from '../descriptor/types.js';
4
+ type Color = (typeof PROPERTY_OPTION_COLORS)[number];
5
+ type Group = (typeof STATUS_PROPERTY_GROUPS)[number];
6
+ /** Parses `label[:color[:group]]`, peeling recognised color/group suffixes off the right. */
7
+ export declare function parseOptionSpec(spec: string): {
8
+ label: string;
9
+ color?: Color;
10
+ group?: Group;
11
+ };
12
+ /** Parses `key=value`; the value is JSON when it parses as JSON, otherwise a plain string. */
13
+ export declare function parseAssignment(spec: string): [string, unknown];
14
+ export declare const propertyCommandGroup: CommandGroupDescriptor;
15
+ export declare function registerPropertyCommands(program: Command): void;
16
+ export {};