@dinoxx/dinox-cli 1.0.22 → 1.1.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 (144) hide show
  1. package/README.md +29 -9
  2. package/dist/auth/userInfo.d.ts +2 -0
  3. package/dist/auth/userInfo.js +55 -14
  4. package/dist/cli.js +36 -2
  5. package/dist/commands/auth/index.d.ts +35 -0
  6. package/dist/commands/auth/index.js +382 -83
  7. package/dist/commands/boxes/index.js +2 -6
  8. package/dist/commands/boxes/model.d.ts +3 -0
  9. package/dist/commands/boxes/model.js +16 -1
  10. package/dist/commands/boxes/operations.js +18 -8
  11. package/dist/commands/boxes/repo.js +13 -11
  12. package/dist/commands/boxes/store.js +1 -1
  13. package/dist/commands/config/index.js +7 -5
  14. package/dist/commands/descriptor/register.js +9 -0
  15. package/dist/commands/doctor.d.ts +123 -0
  16. package/dist/commands/doctor.js +539 -0
  17. package/dist/commands/graph/index.d.ts +1 -1
  18. package/dist/commands/graph/index.js +35 -21
  19. package/dist/commands/graph/repo.js +92 -17
  20. package/dist/commands/meta/index.js +4 -4
  21. package/dist/commands/meta/repo.js +4 -3
  22. package/dist/commands/notes/actions.d.ts +1 -1
  23. package/dist/commands/notes/actions.js +121 -78
  24. package/dist/commands/notes/bulk.d.ts +1 -0
  25. package/dist/commands/notes/bulk.js +23 -17
  26. package/dist/commands/notes/contentEngine.js +69 -11
  27. package/dist/commands/notes/contentPatch.d.ts +4 -1
  28. package/dist/commands/notes/contentPatch.js +105 -41
  29. package/dist/commands/notes/contentPatchCommand.js +28 -11
  30. package/dist/commands/notes/descriptors.js +50 -20
  31. package/dist/commands/notes/dryRun.d.ts +1 -0
  32. package/dist/commands/notes/dryRun.js +4 -7
  33. package/dist/commands/notes/export.d.ts +6 -0
  34. package/dist/commands/notes/export.js +26 -6
  35. package/dist/commands/notes/metadataMutation.d.ts +1 -0
  36. package/dist/commands/notes/metadataMutation.js +14 -3
  37. package/dist/commands/notes/repo.d.ts +46 -4
  38. package/dist/commands/notes/repo.js +113 -28
  39. package/dist/commands/notes/runtime.d.ts +2 -1
  40. package/dist/commands/notes/runtime.js +17 -1
  41. package/dist/commands/notes/searchSql.js +13 -6
  42. package/dist/commands/notes/writeReceipt.d.ts +20 -0
  43. package/dist/commands/notes/writeReceipt.js +82 -0
  44. package/dist/commands/prompt/index.js +3 -3
  45. package/dist/commands/prompt/repo.d.ts +4 -1
  46. package/dist/commands/prompt/repo.js +17 -7
  47. package/dist/commands/runtime.d.ts +14 -2
  48. package/dist/commands/runtime.js +74 -15
  49. package/dist/commands/schema/renderSkills.js +5 -2
  50. package/dist/commands/schema/spec.js +4 -0
  51. package/dist/commands/storage/index.js +8 -2
  52. package/dist/commands/storage/repo.d.ts +4 -0
  53. package/dist/commands/storage/repo.js +356 -92
  54. package/dist/commands/sync.js +16 -12
  55. package/dist/commands/tags/content.js +9 -0
  56. package/dist/commands/tags/index.js +2 -6
  57. package/dist/commands/tags/operations.js +15 -10
  58. package/dist/commands/tags/repo.d.ts +2 -0
  59. package/dist/commands/tags/repo.js +18 -5
  60. package/dist/commands/tags/store.js +1 -1
  61. package/dist/commands/todo/contentModel.d.ts +4 -3
  62. package/dist/commands/todo/contentModel.js +78 -28
  63. package/dist/commands/todo/dryRun.d.ts +1 -0
  64. package/dist/commands/todo/dryRun.js +17 -13
  65. package/dist/commands/todo/index.d.ts +6 -0
  66. package/dist/commands/todo/index.js +89 -123
  67. package/dist/commands/todo/repo.d.ts +36 -1
  68. package/dist/commands/todo/repo.js +146 -37
  69. package/dist/commands/todo/taskIdentity.d.ts +2 -0
  70. package/dist/commands/todo/taskIdentity.js +64 -0
  71. package/dist/commands/todo/taskModel.js +27 -17
  72. package/dist/commands/update.d.ts +13 -1
  73. package/dist/commands/update.js +135 -23
  74. package/dist/commands/writeDryRun.d.ts +3 -2
  75. package/dist/commands/writeDryRun.js +4 -3
  76. package/dist/commands/writeDurability.d.ts +5 -0
  77. package/dist/commands/writeDurability.js +22 -0
  78. package/dist/config/paths.js +6 -1
  79. package/dist/config/resolve.d.ts +2 -0
  80. package/dist/config/resolve.js +23 -9
  81. package/dist/config/store.d.ts +3 -0
  82. package/dist/config/store.js +61 -14
  83. package/dist/config/types.d.ts +4 -0
  84. package/dist/daemon/client.d.ts +26 -1
  85. package/dist/daemon/client.js +236 -12
  86. package/dist/daemon/ensure.js +74 -12
  87. package/dist/daemon/index.d.ts +42 -0
  88. package/dist/daemon/index.js +202 -52
  89. package/dist/daemon/lifecycle.d.ts +42 -0
  90. package/dist/daemon/lifecycle.js +115 -0
  91. package/dist/daemon/pid.d.ts +19 -6
  92. package/dist/daemon/pid.js +147 -13
  93. package/dist/daemon/route.d.ts +14 -0
  94. package/dist/daemon/route.js +40 -0
  95. package/dist/daemon/server.d.ts +11 -0
  96. package/dist/daemon/server.js +201 -50
  97. package/dist/daemon/types.d.ts +29 -1
  98. package/dist/daemon/types.js +1 -0
  99. package/dist/dinox.js +25 -1
  100. package/dist/powersync/connector.js +31 -16
  101. package/dist/powersync/exclusiveRuntime.d.ts +19 -0
  102. package/dist/powersync/exclusiveRuntime.js +42 -0
  103. package/dist/powersync/runtime.d.ts +24 -4
  104. package/dist/powersync/runtime.js +234 -36
  105. package/dist/powersync/syncGate.d.ts +3 -1
  106. package/dist/powersync/syncGate.js +7 -1
  107. package/dist/powersync/tokenIndex.d.ts +11 -0
  108. package/dist/powersync/tokenIndex.js +159 -22
  109. package/dist/powersync/uploader.js +107 -52
  110. package/dist/utils/abort.js +0 -4
  111. package/dist/utils/asyncRelease.d.ts +2 -0
  112. package/dist/utils/asyncRelease.js +25 -0
  113. package/dist/utils/bidlinkCanonicalization.js +3 -0
  114. package/dist/utils/dryRun.d.ts +1 -1
  115. package/dist/utils/errors.d.ts +15 -1
  116. package/dist/utils/errors.js +223 -5
  117. package/dist/utils/output.d.ts +1 -0
  118. package/dist/utils/output.js +51 -15
  119. package/dist/utils/redact.js +1 -1
  120. package/dist/utils/sqliteLease.d.ts +8 -0
  121. package/dist/utils/sqliteLease.js +59 -0
  122. package/dist/utils/syncTimeout.d.ts +7 -1
  123. package/dist/utils/syncTimeout.js +8 -7
  124. package/dist/utils/terminalSanitization.js +6 -2
  125. package/dist/utils/tiptapMarkdown.js +49 -3
  126. package/dist/utils/tiptapMarkdownSyntax.d.ts +9 -0
  127. package/dist/utils/tiptapMarkdownSyntax.js +249 -0
  128. package/dist/utils/tokenize.js +24 -2
  129. package/dist/utils/updateNotice.js +60 -12
  130. package/package.json +12 -10
  131. package/dist/commands/mcp/auth.d.ts +0 -4
  132. package/dist/commands/mcp/auth.js +0 -75
  133. package/dist/commands/mcp/index.d.ts +0 -2
  134. package/dist/commands/mcp/index.js +0 -86
  135. package/dist/commands/mcp/noteContentTools.d.ts +0 -222
  136. package/dist/commands/mcp/noteContentTools.js +0 -390
  137. package/dist/commands/mcp/server.d.ts +0 -34
  138. package/dist/commands/mcp/server.js +0 -424
  139. package/dist/commands/mcp/tools.d.ts +0 -2
  140. package/dist/commands/mcp/tools.js +0 -32
  141. package/dist/commands/mcp/types.d.ts +0 -39
  142. package/dist/commands/mcp/types.js +0 -1
  143. package/dist/utils/noteContentEngine.d.ts +0 -333
  144. package/dist/utils/noteContentEngine.js +0 -740
package/README.md CHANGED
@@ -52,7 +52,7 @@ Dinox CLI 适合这几类场景:
52
52
 
53
53
  安装前请先确认:
54
54
 
55
- - 已安装 Node.js 20 或更高版本
55
+ - 推荐使用 Node.js 24 LTS;支持 Node.js 20.18.1+、22、23、24、25(不支持 Node.js 21 或 26+)
56
56
  - 已安装 npm
57
57
  - 你的系统可以正常执行全局 npm 命令
58
58
 
@@ -114,13 +114,15 @@ dino update
114
114
  ### 第 1 步:登录
115
115
 
116
116
  ```bash
117
- dino auth login "<你的 token>"
117
+ read -rs DINOX_TOKEN
118
+ printf '%s' "$DINOX_TOKEN" | dino auth login --token-stdin
119
+ unset DINOX_TOKEN
118
120
  ```
119
121
 
120
122
  说明:
121
123
 
122
- - 这里填的是登录 token 本体
123
- - `dino auth login` 建议传 token 本体,不要加 `Bearer ` 前缀
124
+ - 第一行会在当前终端隐藏输入 token,避免 token 出现在命令参数和 shell 历史中
125
+ - `--token-stdin` 接受 token 本体或 `Bearer ...`
124
126
  - 登录时 CLI 会顺便验证连接是否可用
125
127
  - 自动化或 CI 可以改用 `DINOX_TOKEN` 环境变量注入 token;它优先于本地配置,并支持 token 本体或 `Bearer ...`
126
128
 
@@ -452,7 +454,7 @@ dino note content-read <note-id> --format json
452
454
  dino note patch <note-id> --append-section "复盘" --content @./section.md --read-token <token> --format json
453
455
  ```
454
456
 
455
- `note patch` 只用于正文结构化编辑。真实写入前必须先 `content-read`,并传回 `readToken`;替换媒体、表格、未知节点等受保护 block 时,还需要显式 `--allow-protected-replace`。
457
+ `note patch` 只用于正文结构化编辑。真实写入前必须先 `content-read`,并传回 `readToken`;token 短期有效(当前为 30 分钟,以 `readTokenExpiresAt` 为准),绑定生成它时当前账号对应的本地 PowerSync 数据库作用域,不能在切换账号或本地数据库后复用。token 也是单次使用的,任何真实 patch 尝试都会先消费它,失败后重试必须重新执行 `content-read`。替换媒体、表格、未知节点等受保护 block 时,还需要显式 `--allow-protected-replace`。
456
458
 
457
459
  #### 收藏状态
458
460
 
@@ -527,6 +529,7 @@ dino todo append --note-id <note-id> --tasks @./todo.txt
527
529
 
528
530
  ```bash
529
531
  dino todo update <task-id> --status completed
532
+ dino todo update <task-id> --note-id <note-id> --status completed
530
533
  dino todo update <task-id> --status uncompleted
531
534
  ```
532
535
 
@@ -542,6 +545,8 @@ dino todo update <task-id> --status false
542
545
  - `completed`
543
546
  - `uncompleted`
544
547
 
548
+ 对没有持久 task id 的旧笔记,`todo search` 会返回 `legacy-task-*` 合成 id。该 id 绑定搜索时的任务位置和内容;如果笔记结构随后变化,更新会安全失败,必须重新搜索,避免把状态写到另一条任务。建议始终把搜索结果中的 `note_id` 一并传给 `--note-id`。
549
+
545
550
  ### 5.7 管理标签
546
551
 
547
552
  #### 查看全部标签
@@ -655,7 +660,9 @@ dino note search "AI" --offline --format json
655
660
  ### 6.2 `auth`:登录与状态
656
661
 
657
662
  ```bash
658
- dino auth login "<token>"
663
+ read -rs DINOX_TOKEN
664
+ printf '%s' "$DINOX_TOKEN" | dino auth login --token-stdin
665
+ unset DINOX_TOKEN
659
666
  dino auth status
660
667
  dino auth logout
661
668
  dino auth logout --clear-local-db
@@ -667,6 +674,8 @@ dino auth logout --clear-local-db
667
674
  - `status`:查看登录状态、本地库位置、同步状态
668
675
  - `logout`:退出登录
669
676
  - `logout --clear-local-db`:退出并删除本地 SQLite 缓存
677
+ - logout 会优先清除配置中保存的凭据;若 daemon 停止、身份解析、数据库 owner lease 获取或释放、缓存删除失败,错误详情会返回 `persistedCredentialsCleared`、`cleanupPhase` 和缓存路径。CLI 会尝试释放全部已取得的 lease;即使某个 release 失败,也不会跳过后续 release 或覆盖更早的主错误。此时凭据已清,但不能认为本地清理已安全完成。
678
+ - 当前 shell 中的 `DINOX_TOKEN` 不受 logout 影响;结果中的 `environmentTokenActive: true` 表示该环境仍可认证。
670
679
 
671
680
  ### 6.3 `sync`:手动同步
672
681
 
@@ -755,6 +764,7 @@ dino todo update <task-id> --status completed
755
764
  - `--dry-run`
756
765
  - `todo update`
757
766
  - `--status <status>` 必填
767
+ - `--note-id <id>`:用搜索结果中的笔记 id 精确限定目标
758
768
  - `--dry-run`
759
769
 
760
770
  ### 6.6 `tag`:标签管理
@@ -806,8 +816,9 @@ dino graph stats
806
816
  说明:
807
817
 
808
818
  - 这些命令用于查看笔记之间的链接关系
809
- - 在线模式下,CLI 会自动确保 daemon 可用
819
+ - 在线模式下,CLI 会自动确保 daemon 可用,并通过 daemon-owned DB runtime 执行
810
820
  - 如果你只想用本地缓存而不启动 daemon,可加 `--offline`
821
+ - 如果 daemon 不可用,默认在线模式会返回带 `suggested_action` 的结构化错误;不会静默回退到另一个进程打开本地 DB
811
822
 
812
823
  例如:
813
824
 
@@ -835,6 +846,7 @@ dino storage test
835
846
  dino storage test --storage-id <id>
836
847
  dino storage upload ./report.pdf
837
848
  dino storage upload ./photo.jpg --category images
849
+ dino storage upload ./report.pdf --key imports/report.pdf --overwrite
838
850
  dino storage stats
839
851
  ```
840
852
 
@@ -846,7 +858,9 @@ dino storage stats
846
858
  - `storage upload` 会上传本地文件,并写入资源记录
847
859
  - 如果不是上传到当前活动存储,可用 `--storage-id <id>` 指定目标
848
860
  - `--category` 支持:`images`、`audios`、`files`、`videos`
849
- - 如果你想自己指定对象 key,可用 `--key <string>`
861
+ - 如果你想自己指定对象 key,可用 `--key <string>`;key 和存储路径前缀不能包含纯 `.` 或 `..` 路径段,否则标准 URL 客户端会把路径归一化到错误对象
862
+ - 显式 `--key` 默认拒绝覆盖已有主对象或图片缩略图;只有在确认替换这些精确 key 后才使用 `--overwrite`
863
+ - 使用 `--overwrite` 时,一旦远端对象写入成功,后续缩略图或资源 metadata 记录失败不会自动删除已经写入的远端对象。不要把命令失败理解为远端已回滚;请检查结构化错误详情中的 `remoteObjectChanged`、`affectedKeys` 和主对象 `storageKey`,再核查或重试相关 key
850
864
  - `--dry-run` 非常适合在正式上传前确认目标位置
851
865
 
852
866
  ### 6.13 `schema`:查看命令 schema
@@ -884,6 +898,10 @@ dino daemon start --no-detach
884
898
  dino schema daemon.start --format json
885
899
  ```
886
900
 
901
+ daemon 通过用户数据目录下的私有 Unix domain socket 通信,Windows 使用命名管道;`--port` 仅保留给旧脚本作为兼容字段,不再作为监听地址。
902
+
903
+ 需要在当前进程打开 PowerSync SQLite 的命令(例如写命令、`sync`、在线 `auth status`、`--offline` 或 `--require-sync` 读取)会先取得跨进程生命周期锁。如果同一用户数据库正由健康 daemon 持有,CLI 会暂时停止该实例;只有在命令结束、数据库关闭且 owner lease 成功释放后,才会按原端口恢复它。使用其他用户数据库的 daemon 不会被停止;如果关闭或释放失败,CLI 会保留 daemon 为停止状态并在 stderr 报告警告,避免两个运行时同时持有同一 SQLite 数据库。
904
+
887
905
  ### 6.15 `info`:查看版本与技能仓库地址
888
906
 
889
907
  ```bash
@@ -935,7 +953,9 @@ schema 包含命令路径、风险级别、参数、选项、输出字段和示
935
953
  解决:
936
954
 
937
955
  ```bash
938
- dino auth login "<token>"
956
+ read -rs DINOX_TOKEN
957
+ printf '%s' "$DINOX_TOKEN" | dino auth login --token-stdin
958
+ unset DINOX_TOKEN
939
959
  dino sync
940
960
  ```
941
961
 
@@ -5,6 +5,8 @@ export type AuthIdentity = {
5
5
  email: string | null;
6
6
  syncedAt: string;
7
7
  };
8
+ export declare function persistedAuthStateMatches(left: DinoxConfig, right: DinoxConfig): boolean;
9
+ export declare function rotatePersistedAuthStateRevision(config: DinoxConfig): DinoxConfig;
8
10
  export declare function buildPendingAuthConfig(rawConfig: DinoxConfig, authorization: string): DinoxConfig;
9
11
  export declare function mergeAuthIdentityIntoConfig(rawConfig: DinoxConfig, authorization: string, identity: AuthIdentity): DinoxConfig;
10
12
  export declare function fetchUserInfoWithAuthorization(input: {
@@ -1,8 +1,22 @@
1
- import { saveConfig } from '../config/store.js';
1
+ import { isDeepStrictEqual } from 'node:util';
2
+ import { updateConfig } from '../config/store.js';
2
3
  import { DinoxError } from '../utils/errors.js';
3
4
  import { createTimeoutSignal, mergeAbortSignals } from '../utils/abort.js';
4
- import { toAuthorizationHeaderValue } from '../utils/authToken.js';
5
+ import { normalizeAuthToken, toAuthorizationHeaderValue } from '../utils/authToken.js';
6
+ import { generateId } from '../utils/id.js';
5
7
  const AUTH_CODE_OK = '000000';
8
+ export function persistedAuthStateMatches(left, right) {
9
+ return isDeepStrictEqual(left.auth ?? {}, right.auth ?? {});
10
+ }
11
+ export function rotatePersistedAuthStateRevision(config) {
12
+ return {
13
+ ...config,
14
+ auth: {
15
+ ...(config.auth ?? {}),
16
+ stateRevision: generateId(),
17
+ },
18
+ };
19
+ }
6
20
  export function buildPendingAuthConfig(rawConfig, authorization) {
7
21
  return {
8
22
  ...rawConfig,
@@ -73,9 +87,9 @@ function parseIdentity(payload) {
73
87
  };
74
88
  }
75
89
  function resolveUserInfoEndpoint(config) {
76
- const base = normalizeOptionalString(config.powersync.uploadBaseUrl) ?? normalizeOptionalString(config.powersync.tokenEndpoint);
90
+ const base = normalizeOptionalString(config.powersync.tokenEndpoint);
77
91
  if (!base) {
78
- throw new DinoxError('Missing backend base URL for user info');
92
+ throw new DinoxError('Missing PowerSync token endpoint for user info');
79
93
  }
80
94
  try {
81
95
  const url = new URL('/auth/userInfo', base);
@@ -96,9 +110,8 @@ export async function fetchUserInfoWithAuthorization(input) {
96
110
  ? createTimeoutSignal(effectiveTimeoutMs)
97
111
  : null;
98
112
  const requestSignal = mergeAbortSignals([input.signal, timeout?.signal]);
99
- let response;
100
113
  try {
101
- response = await fetch(endpoint, {
114
+ const response = await fetch(endpoint, {
102
115
  method: 'GET',
103
116
  headers: {
104
117
  Authorization: toAuthorizationHeaderValue(authorization),
@@ -106,6 +119,28 @@ export async function fetchUserInfoWithAuthorization(input) {
106
119
  },
107
120
  signal: requestSignal,
108
121
  });
122
+ if (!response.ok) {
123
+ let body = '';
124
+ try {
125
+ body = await response.text();
126
+ }
127
+ catch (error) {
128
+ if (requestSignal?.aborted) {
129
+ throw error;
130
+ }
131
+ }
132
+ throw new DinoxError(`User info request failed (${response.status}): ${body || endpoint}`);
133
+ }
134
+ let payload = null;
135
+ try {
136
+ payload = await response.json();
137
+ }
138
+ catch (error) {
139
+ if (requestSignal?.aborted) {
140
+ throw error;
141
+ }
142
+ }
143
+ return parseIdentity(payload);
109
144
  }
110
145
  catch (error) {
111
146
  if (timeout?.signal.aborted && !input.signal?.aborted) {
@@ -116,12 +151,6 @@ export async function fetchUserInfoWithAuthorization(input) {
116
151
  finally {
117
152
  timeout?.cancel();
118
153
  }
119
- if (!response.ok) {
120
- const body = await response.text().catch(() => '');
121
- throw new DinoxError(`User info request failed (${response.status}): ${body || endpoint}`);
122
- }
123
- const payload = await response.json().catch(() => null);
124
- return parseIdentity(payload);
125
154
  }
126
155
  export async function refreshAndPersistAuthIdentity(rawConfig, resolvedConfig) {
127
156
  const authorization = normalizeOptionalString(resolvedConfig.auth.authorization);
@@ -132,8 +161,20 @@ export async function refreshAndPersistAuthIdentity(rawConfig, resolvedConfig) {
132
161
  authorization,
133
162
  config: resolvedConfig,
134
163
  });
135
- const nextConfig = mergeAuthIdentityIntoConfig(rawConfig, authorization, identity);
136
- await saveConfig(nextConfig);
164
+ // Environment-only credentials are process-scoped and must never be copied into config.json.
165
+ if (normalizeAuthToken(rawConfig.auth?.authorization) !== authorization) {
166
+ return identity;
167
+ }
168
+ await updateConfig((current) => {
169
+ if (!persistedAuthStateMatches(current, rawConfig)) {
170
+ throw new DinoxError('Authentication changed while user identity was being refreshed.', {
171
+ code: 'AUTH_STATE_CHANGED',
172
+ suggestion: 'Retry the command so it uses the current login.',
173
+ recoverable: true,
174
+ });
175
+ }
176
+ return mergeAuthIdentityIntoConfig(current, authorization, identity);
177
+ }, { timeoutMs: resolvedConfig.sync.timeoutMs });
137
178
  return identity;
138
179
  }
139
180
  function authIdentityFromResolvedConfig(resolvedConfig) {
package/dist/cli.js CHANGED
@@ -3,6 +3,7 @@ import { registerAuthCommands } from './commands/auth/index.js';
3
3
  import { registerBoxesCommand } from './commands/boxes/index.js';
4
4
  import { registerConfigCommands } from './commands/config/index.js';
5
5
  import { registerDaemonCommands } from './daemon/index.js';
6
+ import { registerDoctorCommand } from './commands/doctor.js';
6
7
  import { registerGraphCommands } from './commands/graph/index.js';
7
8
  import { registerInfoCommand } from './commands/info/index.js';
8
9
  import { registerMetaCommands } from './commands/meta/index.js';
@@ -15,15 +16,47 @@ import { registerUpdateCommand } from './commands/update.js';
15
16
  import { registerTagsCommand } from './commands/tags/index.js';
16
17
  import { registerTodoCommands } from './commands/todo/index.js';
17
18
  import { getPackageVersion } from './utils/version.js';
18
- import { clearRequestedStructuredOutputFormat, resolveStructuredOutputFormat, setRequestedStructuredOutputFormat, } from './utils/output.js';
19
+ import { buildStructuredErrorPayload, DinoxError } from './utils/errors.js';
20
+ import { clearRequestedStructuredOutputFormat, parseStructuredOutputFormatFromArgv, printError, resolveStructuredOutputFormat, setRequestedStructuredOutputFormat, } from './utils/output.js';
19
21
  export function createCli() {
20
22
  clearRequestedStructuredOutputFormat();
21
23
  const program = new Command();
24
+ let structuredCommanderErrorWritten = false;
22
25
  program
23
26
  .name('dino')
24
27
  .description('Dinox CLI')
25
28
  .version(getPackageVersion())
26
- .showHelpAfterError();
29
+ .showHelpAfterError()
30
+ .configureOutput({
31
+ writeErr: (text) => {
32
+ if (!structuredCommanderErrorWritten) {
33
+ process.stderr.write(text);
34
+ }
35
+ },
36
+ outputError: (text, write) => {
37
+ let format = null;
38
+ try {
39
+ const rawArgs = program.rawArgs ?? process.argv;
40
+ format = parseStructuredOutputFormatFromArgv(rawArgs.slice(2));
41
+ }
42
+ catch {
43
+ write(text);
44
+ return;
45
+ }
46
+ if (!format) {
47
+ write(text);
48
+ return;
49
+ }
50
+ structuredCommanderErrorWritten = true;
51
+ const message = text.trim().replace(/^error:\s*/i, '');
52
+ const error = new DinoxError(message, {
53
+ code: 'USAGE_ERROR',
54
+ recoverable: true,
55
+ });
56
+ printError(buildStructuredErrorPayload(error, false), format);
57
+ },
58
+ })
59
+ .exitOverride();
27
60
  program
28
61
  .option('--json', 'legacy machine-readable YAML output (equivalent to --format yaml)')
29
62
  .option('--format <format>', 'structured output format: yaml | json')
@@ -42,6 +75,7 @@ export function createCli() {
42
75
  registerBoxesCommand(program);
43
76
  registerConfigCommands(program);
44
77
  registerDaemonCommands(program);
78
+ registerDoctorCommand(program);
45
79
  registerGraphCommands(program);
46
80
  registerInfoCommand(program);
47
81
  registerMetaCommands(program);
@@ -1,4 +1,39 @@
1
1
  import { Command } from 'commander';
2
+ import { fetchUserInfoWithAuthorization } from '../../auth/userInfo.js';
3
+ import { loadConfig, updateConfig } from '../../config/store.js';
4
+ import { resolveConfig } from '../../config/resolve.js';
5
+ import { restoreSuspendedDaemonLocked, suspendDaemonForExclusiveAccessLocked } from '../../daemon/lifecycle.js';
6
+ import { acquireDaemonStartLock } from '../../daemon/pid.js';
7
+ import { fetchCredentialsWithAuthorization } from '../../powersync/connector.js';
8
+ import { closePowerSyncAndReleaseOwner, connectPowerSync, getStatusSnapshot } from '../../powersync/runtime.js';
9
+ import { runSingleSyncPass } from '../../powersync/syncPass.js';
2
10
  import type { CommandGroupDescriptor } from '../descriptor/types.js';
11
+ import { printHumanReadableWarning } from '../../utils/output.js';
12
+ export declare function releaseOwnerLeases(releases: ReadonlyArray<() => Promise<void>>): Promise<unknown[]>;
13
+ type AuthLoginDependencies = {
14
+ acquireDaemonStartLock: typeof acquireDaemonStartLock;
15
+ loadConfig: typeof loadConfig;
16
+ resolveConfig: typeof resolveConfig;
17
+ fetchCredentials: typeof fetchCredentialsWithAuthorization;
18
+ fetchUserInfo: typeof fetchUserInfoWithAuthorization;
19
+ suspendDaemon: typeof suspendDaemonForExclusiveAccessLocked;
20
+ connectPowerSync: typeof connectPowerSync;
21
+ runSingleSyncPass: typeof runSingleSyncPass;
22
+ getStatusSnapshot: typeof getStatusSnapshot;
23
+ closePowerSyncAndReleaseOwner: typeof closePowerSyncAndReleaseOwner;
24
+ updateConfig: typeof updateConfig;
25
+ restoreDaemon: typeof restoreSuspendedDaemonLocked;
26
+ printWarning: typeof printHumanReadableWarning;
27
+ };
28
+ export declare function performAuthLogin(rawToken: string, syncTimeout: unknown, overrides?: Partial<AuthLoginDependencies>): Promise<{
29
+ ok: true;
30
+ verified: true;
31
+ endpoint: string;
32
+ dbPath: string;
33
+ stale: boolean;
34
+ lastSyncedAt: string | null;
35
+ userId: string;
36
+ }>;
3
37
  export declare const authCommandGroup: CommandGroupDescriptor;
4
38
  export declare function registerAuthCommands(program: Command): void;
39
+ export {};