@dinoxx/dinox-cli 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (90) hide show
  1. package/README.md +294 -0
  2. package/dist/auth/userInfo.d.ts +14 -0
  3. package/dist/auth/userInfo.js +115 -0
  4. package/dist/cli.d.ts +2 -0
  5. package/dist/cli.js +32 -0
  6. package/dist/cliTypes.d.ts +6 -0
  7. package/dist/cliTypes.js +1 -0
  8. package/dist/commands/auth/index.d.ts +2 -0
  9. package/dist/commands/auth/index.js +193 -0
  10. package/dist/commands/boxes/index.d.ts +2 -0
  11. package/dist/commands/boxes/index.js +107 -0
  12. package/dist/commands/boxes/repo.d.ts +21 -0
  13. package/dist/commands/boxes/repo.js +154 -0
  14. package/dist/commands/config/index.d.ts +2 -0
  15. package/dist/commands/config/index.js +67 -0
  16. package/dist/commands/info/index.d.ts +2 -0
  17. package/dist/commands/info/index.js +20 -0
  18. package/dist/commands/notes/index.d.ts +2 -0
  19. package/dist/commands/notes/index.js +271 -0
  20. package/dist/commands/notes/repo.d.ts +70 -0
  21. package/dist/commands/notes/repo.js +674 -0
  22. package/dist/commands/notes/searchTime.d.ts +9 -0
  23. package/dist/commands/notes/searchTime.js +85 -0
  24. package/dist/commands/prompt/index.d.ts +2 -0
  25. package/dist/commands/prompt/index.js +51 -0
  26. package/dist/commands/prompt/repo.d.ts +6 -0
  27. package/dist/commands/prompt/repo.js +18 -0
  28. package/dist/commands/sync.d.ts +2 -0
  29. package/dist/commands/sync.js +68 -0
  30. package/dist/commands/tags/index.d.ts +2 -0
  31. package/dist/commands/tags/index.js +120 -0
  32. package/dist/commands/tags/repo.d.ts +14 -0
  33. package/dist/commands/tags/repo.js +247 -0
  34. package/dist/config/keys.d.ts +9 -0
  35. package/dist/config/keys.js +17 -0
  36. package/dist/config/paths.d.ts +4 -0
  37. package/dist/config/paths.js +39 -0
  38. package/dist/config/resolve.d.ts +2 -0
  39. package/dist/config/resolve.js +56 -0
  40. package/dist/config/serviceEndpoints.d.ts +3 -0
  41. package/dist/config/serviceEndpoints.js +3 -0
  42. package/dist/config/store.d.ts +5 -0
  43. package/dist/config/store.js +87 -0
  44. package/dist/config/types.d.ts +51 -0
  45. package/dist/config/types.js +1 -0
  46. package/dist/dinox.d.ts +2 -0
  47. package/dist/dinox.js +50 -0
  48. package/dist/powersync/connector.d.ts +21 -0
  49. package/dist/powersync/connector.js +58 -0
  50. package/dist/powersync/runtime.d.ts +37 -0
  51. package/dist/powersync/runtime.js +107 -0
  52. package/dist/powersync/schema/content.d.ts +76 -0
  53. package/dist/powersync/schema/content.js +76 -0
  54. package/dist/powersync/schema/index.d.ts +371 -0
  55. package/dist/powersync/schema/index.js +35 -0
  56. package/dist/powersync/schema/local.d.ts +68 -0
  57. package/dist/powersync/schema/local.js +83 -0
  58. package/dist/powersync/schema/note.d.ts +34 -0
  59. package/dist/powersync/schema/note.js +34 -0
  60. package/dist/powersync/schema/notesExtras.d.ts +62 -0
  61. package/dist/powersync/schema/notesExtras.js +71 -0
  62. package/dist/powersync/schema/projects.d.ts +101 -0
  63. package/dist/powersync/schema/projects.js +101 -0
  64. package/dist/powersync/schema/tags.d.ts +37 -0
  65. package/dist/powersync/schema/tags.js +37 -0
  66. package/dist/powersync/tokenIndex.d.ts +17 -0
  67. package/dist/powersync/tokenIndex.js +202 -0
  68. package/dist/powersync/uploader.d.ts +7 -0
  69. package/dist/powersync/uploader.js +134 -0
  70. package/dist/utils/argValue.d.ts +1 -0
  71. package/dist/utils/argValue.js +17 -0
  72. package/dist/utils/errors.d.ts +10 -0
  73. package/dist/utils/errors.js +17 -0
  74. package/dist/utils/id.d.ts +1 -0
  75. package/dist/utils/id.js +4 -0
  76. package/dist/utils/output.d.ts +2 -0
  77. package/dist/utils/output.js +10 -0
  78. package/dist/utils/redact.d.ts +1 -0
  79. package/dist/utils/redact.js +10 -0
  80. package/dist/utils/text.d.ts +1 -0
  81. package/dist/utils/text.js +35 -0
  82. package/dist/utils/time.d.ts +1 -0
  83. package/dist/utils/time.js +3 -0
  84. package/dist/utils/tiptapMarkdown.d.ts +6 -0
  85. package/dist/utils/tiptapMarkdown.js +149 -0
  86. package/dist/utils/tokenize.d.ts +1 -0
  87. package/dist/utils/tokenize.js +56 -0
  88. package/dist/utils/version.d.ts +1 -0
  89. package/dist/utils/version.js +21 -0
  90. package/package.json +63 -0
package/README.md ADDED
@@ -0,0 +1,294 @@
1
+ # Dinox CLI 使用说明(普通用户版)
2
+
3
+ `dino` 是一个命令行工具,用来管理你的 Dinox 数据(笔记、标签、卡片盒等)。
4
+
5
+ 如果你不熟悉技术细节也没关系,按这份文档一步一步操作就能用。
6
+
7
+ ---
8
+
9
+ ## 1. 先看你要做什么
10
+
11
+ 常见需求:
12
+
13
+ 1. 登录账号:`dino auth login ...`
14
+ 2. 同步数据:`dino sync`
15
+ 3. 搜索笔记:`dino note search "关键词"`
16
+ 4. 新建笔记:`dino note create ...`
17
+ 5. 新建标签:`dino tag add ...`
18
+ 6. 新建卡片盒:`dino box add ...`
19
+ 7. 看当前版本:`dino info`
20
+
21
+ ---
22
+
23
+ ## 2. 安装前准备
24
+
25
+ 你需要先安装 Node.js(建议 LTS,且版本 >= 20)。
26
+
27
+ 检查是否已安装:
28
+
29
+ ```bash
30
+ node -v
31
+ npm -v
32
+ ```
33
+
34
+ ---
35
+
36
+ ## 3. 安装方法(macOS / Windows)
37
+
38
+ ## 3.1 macOS
39
+
40
+ ### 方式 A:普通用户推荐(全局安装)
41
+
42
+ 1. 安装 Node.js(若未安装):
43
+
44
+ ```bash
45
+ brew install node
46
+ ```
47
+
48
+ 2. 安装 CLI:
49
+
50
+ ```bash
51
+ npm install -g @dinoxx/dinox-cli
52
+ ```
53
+
54
+ 3. 验证:
55
+
56
+ ```bash
57
+ dino info
58
+ ```
59
+
60
+ ---
61
+
62
+ ## 3.2 Windows
63
+
64
+ ### 方式 A:普通用户推荐(全局安装)
65
+
66
+ 1. 安装 Node.js(若未安装):
67
+
68
+ ```powershell
69
+ winget install OpenJS.NodeJS.LTS
70
+ ```
71
+
72
+ 2. 安装 CLI(PowerShell 或 CMD):
73
+
74
+ ```powershell
75
+ npm install -g @dinoxx/dinox-cli
76
+ ```
77
+
78
+ 3. 验证:
79
+
80
+ ```powershell
81
+ dino info
82
+ ```
83
+
84
+ ---
85
+
86
+ ## 4. 第一次使用(建议照抄)
87
+
88
+ ## 第 1 步:登录
89
+
90
+ ```bash
91
+ dino auth login "Bearer <你的token>"
92
+ ```
93
+
94
+ ## 第 2 步:同步
95
+
96
+ ```bash
97
+ dino sync
98
+ ```
99
+
100
+ ## 第 3 步:看状态(可选)
101
+
102
+ ```bash
103
+ dino auth status
104
+ ```
105
+
106
+ ---
107
+
108
+ ## 5. 最常用命令
109
+
110
+ ## 5.1 笔记
111
+
112
+ 搜索笔记:
113
+
114
+ ```bash
115
+ dino note search "AI"
116
+ dino note search "AI" --days 7
117
+ dino note search "AI" --from 2026-02-01 --to 2026-02-28
118
+ ```
119
+
120
+ 创建笔记:
121
+
122
+ ```bash
123
+ dino note create \
124
+ --title "今天的记录" \
125
+ --content "# 标题\n\n正文内容"
126
+ ```
127
+
128
+ 按 ID 查看:
129
+
130
+ ```bash
131
+ dino note get <note-id>
132
+ dino note detail <note-id>
133
+ ```
134
+
135
+ 删除笔记(软删除):
136
+
137
+ ```bash
138
+ dino note delete <note-id>
139
+ ```
140
+
141
+ ## 5.2 标签
142
+
143
+ 列出标签:
144
+
145
+ ```bash
146
+ dino tag list
147
+ ```
148
+
149
+ 新增标签(两种写法都可以):
150
+
151
+ ```bash
152
+ dino tag add "工作/项目A"
153
+ dino tag add --name "工作/项目A" --emoji "🧠"
154
+ ```
155
+
156
+ ## 5.3 卡片盒
157
+
158
+ 列出卡片盒:
159
+
160
+ ```bash
161
+ dino box list
162
+ ```
163
+
164
+ 新增卡片盒(两种写法都可以):
165
+
166
+ ```bash
167
+ dino box add "Inbox"
168
+ dino box add --name "Inbox" --description "用于存放待整理的想法和资料"
169
+ ```
170
+
171
+ `--description` 很有用:它能帮助 AI 更准确地把笔记分到正确卡片盒。
172
+
173
+ ## 5.4 Prompt
174
+
175
+ ```bash
176
+ dino prompt list
177
+ ```
178
+
179
+ ## 5.5 配置
180
+
181
+ 查看全部配置:
182
+
183
+ ```bash
184
+ dino config get
185
+ ```
186
+
187
+ 查看某一项:
188
+
189
+ ```bash
190
+ dino config get sync.timeoutMs
191
+ ```
192
+
193
+ 设置某一项:
194
+
195
+ ```bash
196
+ dino config set sync.timeoutMs 20000
197
+ ```
198
+
199
+ ---
200
+
201
+ ## 6. macOS 和 Windows 的使用差异
202
+
203
+ ## 6.1 路径位置不同
204
+
205
+ 配置文件:
206
+
207
+ 1. macOS: `~/Library/Application Support/dinox/config.json`
208
+ 2. Windows: `%APPDATA%\dinox\config.json`
209
+
210
+ 本地数据库:
211
+
212
+ 1. macOS: `~/Library/Application Support/dinox/powersync.sqlite`
213
+ 2. Windows: `%LOCALAPPDATA%\dinox\powersync.sqlite`
214
+
215
+ ## 6.2 多行命令写法不同
216
+
217
+ macOS / Linux(bash/zsh)用 `\` 续行:
218
+
219
+ ```bash
220
+ dino note create \
221
+ --title "标题" \
222
+ --content "正文"
223
+ ```
224
+
225
+ Windows PowerShell 用反引号 `` ` `` 续行:
226
+
227
+ ```powershell
228
+ dino note create `
229
+ --title "标题" `
230
+ --content "正文"
231
+ ```
232
+
233
+ ## 6.3 `@file` 参数在 Windows 建议加引号
234
+
235
+ 比如从文件读取正文:
236
+
237
+ ```bash
238
+ dino note create --title "测试" --content @./note.md
239
+ ```
240
+
241
+ 在 PowerShell 建议写成:
242
+
243
+ ```powershell
244
+ dino note create --title "测试" --content "@.\note.md"
245
+ ```
246
+
247
+ 这样更稳,不容易被 shell 误解析。
248
+
249
+ ---
250
+
251
+ ## 7. 常见问题
252
+
253
+ ## Q1:报错 `Missing persisted userId. Run dino auth login first.`
254
+
255
+ 你还没有完成登录。执行:
256
+
257
+ ```bash
258
+ dino auth login "Bearer <token>"
259
+ ```
260
+
261
+ ## Q2:创建笔记时报 `Unknown tags` 或 `Unknown zettel box names`
262
+
263
+ 说明你填的标签/卡片盒不存在,先创建再重试:
264
+
265
+ ```bash
266
+ dino tag add "你的标签"
267
+ dino box add "你的卡片盒"
268
+ ```
269
+
270
+ ## Q3:我设置了 `powersync.endpoint` 但没生效
271
+
272
+ 这两个配置是固定的,不允许修改:
273
+
274
+ 1. `powersync.endpoint = https://powersync.dinoai.fun`
275
+ 2. `powersync.uploadBaseUrl = https://dinoai.chatgo.pro`
276
+
277
+ ---
278
+
279
+ ## 8. 查看帮助
280
+
281
+ 随时可以看帮助:
282
+
283
+ ```bash
284
+ dino --help
285
+ dino <命令> --help
286
+ dino <命令> <子命令> --help
287
+ ```
288
+
289
+ 例如:
290
+
291
+ ```bash
292
+ dino note --help
293
+ dino note create --help
294
+ ```
@@ -0,0 +1,14 @@
1
+ import type { DinoxConfig, ResolvedDinoxConfig } from '../config/types.js';
2
+ export type AuthIdentity = {
3
+ userId: string;
4
+ username: string | null;
5
+ email: string | null;
6
+ syncedAt: string;
7
+ };
8
+ export declare function fetchUserInfoWithAuthorization(input: {
9
+ authorization: string;
10
+ config: ResolvedDinoxConfig;
11
+ signal?: AbortSignal;
12
+ }): Promise<AuthIdentity>;
13
+ export declare function refreshAndPersistAuthIdentity(rawConfig: DinoxConfig, resolvedConfig: ResolvedDinoxConfig): Promise<AuthIdentity>;
14
+ export declare function resolveAuthIdentity(rawConfig: DinoxConfig, resolvedConfig: ResolvedDinoxConfig): Promise<AuthIdentity>;
@@ -0,0 +1,115 @@
1
+ import { saveConfig } from '../config/store.js';
2
+ import { DinoxError } from '../utils/errors.js';
3
+ const AUTH_CODE_OK = '000000';
4
+ function normalizeOptionalString(value) {
5
+ if (typeof value !== 'string') {
6
+ return undefined;
7
+ }
8
+ const trimmed = value.trim();
9
+ return trimmed.length > 0 ? trimmed : undefined;
10
+ }
11
+ function normalizeOptionalNullableString(value) {
12
+ const text = normalizeOptionalString(value);
13
+ return text ?? null;
14
+ }
15
+ function parseUserInfoResult(payload) {
16
+ if (!payload || typeof payload !== 'object') {
17
+ throw new DinoxError('User info response is not a JSON object');
18
+ }
19
+ const body = payload;
20
+ if ('code' in body) {
21
+ const code = String(body.code ?? '');
22
+ if (code && code !== AUTH_CODE_OK) {
23
+ throw new DinoxError(`User info request failed: ${String(body.msg ?? 'unknown error')}`);
24
+ }
25
+ const wrapped = body.data;
26
+ if (!wrapped || typeof wrapped !== 'object') {
27
+ throw new DinoxError('User info response does not include data');
28
+ }
29
+ return wrapped;
30
+ }
31
+ return body;
32
+ }
33
+ function parseIdentity(payload) {
34
+ const parsed = parseUserInfoResult(payload);
35
+ const userId = normalizeOptionalString(parsed.userId ?? parsed.user_id ?? parsed.id);
36
+ if (!userId) {
37
+ throw new DinoxError('User info response is missing id');
38
+ }
39
+ return {
40
+ userId,
41
+ username: normalizeOptionalNullableString(parsed.username),
42
+ email: normalizeOptionalNullableString(parsed.email),
43
+ syncedAt: new Date().toISOString(),
44
+ };
45
+ }
46
+ function resolveUserInfoEndpoint(config) {
47
+ const base = normalizeOptionalString(config.powersync.uploadBaseUrl) ?? normalizeOptionalString(config.powersync.tokenEndpoint);
48
+ if (!base) {
49
+ throw new DinoxError('Missing backend base URL for user info');
50
+ }
51
+ try {
52
+ const url = new URL('/auth/userInfo', base);
53
+ return url.toString();
54
+ }
55
+ catch (error) {
56
+ throw new DinoxError(`Invalid backend base URL: ${base}`, { cause: error });
57
+ }
58
+ }
59
+ export async function fetchUserInfoWithAuthorization(input) {
60
+ const authorization = normalizeOptionalString(input.authorization);
61
+ if (!authorization) {
62
+ throw new DinoxError('Authorization is required for user info');
63
+ }
64
+ const endpoint = resolveUserInfoEndpoint(input.config);
65
+ const response = await fetch(endpoint, {
66
+ method: 'GET',
67
+ headers: {
68
+ Authorization: authorization,
69
+ 'Content-Type': 'application/json',
70
+ },
71
+ signal: input.signal,
72
+ });
73
+ if (!response.ok) {
74
+ const body = await response.text().catch(() => '');
75
+ throw new DinoxError(`User info request failed (${response.status}): ${body || endpoint}`);
76
+ }
77
+ const payload = await response.json().catch(() => null);
78
+ return parseIdentity(payload);
79
+ }
80
+ export async function refreshAndPersistAuthIdentity(rawConfig, resolvedConfig) {
81
+ const authorization = normalizeOptionalString(resolvedConfig.auth.authorization);
82
+ if (!authorization) {
83
+ throw new DinoxError('Authorization is required to fetch user info');
84
+ }
85
+ const identity = await fetchUserInfoWithAuthorization({
86
+ authorization,
87
+ config: resolvedConfig,
88
+ });
89
+ const nextConfig = {
90
+ ...rawConfig,
91
+ auth: {
92
+ ...(rawConfig.auth ?? {}),
93
+ authorization: rawConfig.auth?.authorization ?? authorization,
94
+ userId: identity.userId,
95
+ username: identity.username,
96
+ email: identity.email,
97
+ userInfoSyncedAt: identity.syncedAt,
98
+ },
99
+ };
100
+ await saveConfig(nextConfig);
101
+ return identity;
102
+ }
103
+ export async function resolveAuthIdentity(rawConfig, resolvedConfig) {
104
+ const savedUserId = normalizeOptionalString(rawConfig.auth?.userId ?? resolvedConfig.auth.userId);
105
+ const syncedAt = normalizeOptionalString(rawConfig.auth?.userInfoSyncedAt ?? resolvedConfig.auth.userInfoSyncedAt);
106
+ if (savedUserId) {
107
+ return {
108
+ userId: savedUserId,
109
+ username: normalizeOptionalNullableString(rawConfig.auth?.username ?? resolvedConfig.auth.username),
110
+ email: normalizeOptionalNullableString(rawConfig.auth?.email ?? resolvedConfig.auth.email),
111
+ syncedAt: syncedAt ?? new Date().toISOString(),
112
+ };
113
+ }
114
+ throw new DinoxError('Missing persisted userId. Run `dino auth login` first.');
115
+ }
package/dist/cli.d.ts ADDED
@@ -0,0 +1,2 @@
1
+ import { Command } from 'commander';
2
+ export declare function createCli(): Command;
package/dist/cli.js ADDED
@@ -0,0 +1,32 @@
1
+ import { Command } from 'commander';
2
+ import { registerAuthCommands } from './commands/auth/index.js';
3
+ import { registerBoxesCommand } from './commands/boxes/index.js';
4
+ import { registerConfigCommands } from './commands/config/index.js';
5
+ import { registerInfoCommand } from './commands/info/index.js';
6
+ import { registerNoteCommands } from './commands/notes/index.js';
7
+ import { registerPromptCommands } from './commands/prompt/index.js';
8
+ import { registerSyncCommand } from './commands/sync.js';
9
+ import { registerTagsCommand } from './commands/tags/index.js';
10
+ import { getPackageVersion } from './utils/version.js';
11
+ export function createCli() {
12
+ const program = new Command();
13
+ program
14
+ .name('dino')
15
+ .description('Dinox CLI')
16
+ .version(getPackageVersion())
17
+ .showHelpAfterError();
18
+ program
19
+ .option('--json', 'output machine-readable YAML')
20
+ .option('--offline', 'skip connect/sync and only use local cache')
21
+ .option('--sync-timeout <ms>', 'override sync/connect timeout (milliseconds)')
22
+ .option('--verbose', 'enable verbose logging');
23
+ registerAuthCommands(program);
24
+ registerBoxesCommand(program);
25
+ registerConfigCommands(program);
26
+ registerInfoCommand(program);
27
+ registerSyncCommand(program);
28
+ registerPromptCommands(program);
29
+ registerTagsCommand(program);
30
+ registerNoteCommands(program);
31
+ return program;
32
+ }
@@ -0,0 +1,6 @@
1
+ export type GlobalOptions = {
2
+ json?: boolean;
3
+ offline?: boolean;
4
+ syncTimeout?: string;
5
+ verbose?: boolean;
6
+ };
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,2 @@
1
+ import { Command } from 'commander';
2
+ export declare function registerAuthCommands(program: Command): void;
@@ -0,0 +1,193 @@
1
+ import fs from 'node:fs/promises';
2
+ import { loadConfig, saveConfig } from '../../config/store.js';
3
+ import { resolveConfig } from '../../config/resolve.js';
4
+ import { getPowerSyncDbPath } from '../../config/paths.js';
5
+ import { redactAuthorization } from '../../utils/redact.js';
6
+ import { DinoxError } from '../../utils/errors.js';
7
+ import { printYaml } from '../../utils/output.js';
8
+ import { refreshAndPersistAuthIdentity } from '../../auth/userInfo.js';
9
+ import { connectPowerSync, getStatusSnapshot } from '../../powersync/runtime.js';
10
+ import { fetchCredentialsWithAuthorization } from '../../powersync/connector.js';
11
+ function parseSyncTimeoutMs(value) {
12
+ if (typeof value !== 'string') {
13
+ return undefined;
14
+ }
15
+ const parsed = Number(value);
16
+ if (!Number.isFinite(parsed) || parsed <= 0) {
17
+ throw new DinoxError('--sync-timeout must be a positive integer');
18
+ }
19
+ return Math.trunc(parsed);
20
+ }
21
+ export function registerAuthCommands(program) {
22
+ const auth = program
23
+ .command('auth')
24
+ .description('Authentication and PowerSync connectivity commands');
25
+ auth
26
+ .command('login')
27
+ .description('Save authorization token and verify PowerSync connectivity')
28
+ .argument('<authorization>', 'full Authorization header value (e.g. "Bearer <token>")')
29
+ .option('--no-verify', 'skip credential exchange and initial sync')
30
+ .action(async (authorization, options, command) => {
31
+ const auth = authorization?.trim();
32
+ if (!auth) {
33
+ throw new DinoxError('Authorization token is required');
34
+ }
35
+ const config = await loadConfig();
36
+ config.auth = { ...(config.auth ?? {}), authorization: auth };
37
+ await saveConfig(config);
38
+ const resolved = resolveConfig(config);
39
+ const globals = command.optsWithGlobals?.() ?? {};
40
+ const timeoutMs = parseSyncTimeoutMs(globals.syncTimeout) ?? resolved.sync.timeoutMs;
41
+ const jsonOutput = Boolean(globals.json);
42
+ if (!options.verify) {
43
+ const result = {
44
+ ok: true,
45
+ verified: false,
46
+ authorization: redactAuthorization(auth),
47
+ };
48
+ if (jsonOutput) {
49
+ printYaml(result);
50
+ }
51
+ else {
52
+ console.log('Login saved (verification skipped).');
53
+ }
54
+ return;
55
+ }
56
+ // 1) Token exchange sanity check.
57
+ await fetchCredentialsWithAuthorization({
58
+ authorization: auth,
59
+ endpoint: resolved.powersync.endpoint,
60
+ credentialsUrl: resolved.powersync.tokenEndpoint,
61
+ });
62
+ // 1.5) Fetch and persist user identity.
63
+ const identity = await refreshAndPersistAuthIdentity(config, resolved);
64
+ // 2) Connect + initial sync.
65
+ const { db, dbPath, stale } = await connectPowerSync({
66
+ config: resolved,
67
+ offline: false,
68
+ timeoutMs,
69
+ });
70
+ try {
71
+ const status = getStatusSnapshot(db);
72
+ const result = {
73
+ ok: true,
74
+ verified: true,
75
+ endpoint: resolved.powersync.endpoint,
76
+ dbPath,
77
+ stale,
78
+ lastSyncedAt: status.lastSyncedAt,
79
+ userId: identity.userId,
80
+ };
81
+ if (jsonOutput) {
82
+ printYaml(result);
83
+ }
84
+ else {
85
+ console.log('Login verified.');
86
+ console.log(`- endpoint: ${result.endpoint}`);
87
+ console.log(`- db: ${result.dbPath}`);
88
+ console.log(`- userId: ${result.userId}`);
89
+ if (result.lastSyncedAt) {
90
+ console.log(`- lastSyncedAt: ${result.lastSyncedAt}`);
91
+ }
92
+ if (stale) {
93
+ console.log('- warning: initial sync timed out; local cache may be stale');
94
+ }
95
+ }
96
+ }
97
+ finally {
98
+ await db.close().catch(() => undefined);
99
+ }
100
+ });
101
+ auth
102
+ .command('logout')
103
+ .description('Clear saved authorization token')
104
+ .option('--clear-local-db', 'delete the local PowerSync SQLite database')
105
+ .action(async (options) => {
106
+ const config = await loadConfig();
107
+ if (config.auth) {
108
+ config.auth.authorization = undefined;
109
+ }
110
+ await saveConfig(config);
111
+ if (options.clearLocalDb) {
112
+ const dbPath = getPowerSyncDbPath();
113
+ await fs.rm(dbPath, { force: true }).catch(() => undefined);
114
+ }
115
+ });
116
+ auth
117
+ .command('status')
118
+ .description('Show current login and sync status')
119
+ .action(async (_options, command) => {
120
+ const config = await loadConfig();
121
+ const resolved = resolveConfig(config);
122
+ const globals = command.optsWithGlobals?.() ?? {};
123
+ const offline = Boolean(globals.offline);
124
+ const jsonOutput = Boolean(globals.json);
125
+ const timeoutMs = parseSyncTimeoutMs(globals.syncTimeout) ?? resolved.sync.timeoutMs;
126
+ const dbPath = getPowerSyncDbPath();
127
+ const base = {
128
+ loggedIn: Boolean(resolved.auth.authorization),
129
+ authorization: redactAuthorization(resolved.auth.authorization),
130
+ userId: resolved.auth.userId ?? null,
131
+ username: resolved.auth.username ?? null,
132
+ email: resolved.auth.email ?? null,
133
+ powersync: {
134
+ endpoint: resolved.powersync.endpoint ?? null,
135
+ tokenEndpoint: resolved.powersync.tokenEndpoint ?? null,
136
+ uploadBaseUrl: resolved.powersync.uploadBaseUrl ?? null,
137
+ uploadV4Path: resolved.powersync.uploadV4Path,
138
+ uploadV2Path: resolved.powersync.uploadV2Path,
139
+ },
140
+ dbPath,
141
+ };
142
+ if (offline || !resolved.auth.authorization || !resolved.powersync.endpoint || !resolved.powersync.tokenEndpoint) {
143
+ const payload = { ...base, offline: true };
144
+ if (jsonOutput) {
145
+ printYaml(payload);
146
+ }
147
+ else {
148
+ console.log(`loggedIn: ${payload.loggedIn ? 'yes' : 'no'}`);
149
+ if (payload.userId) {
150
+ console.log(`userId: ${payload.userId}`);
151
+ }
152
+ console.log(`db: ${payload.dbPath}`);
153
+ console.log(`powersync.endpoint: ${payload.powersync.endpoint ?? '(unset)'}`);
154
+ console.log(`powersync.tokenEndpoint: ${payload.powersync.tokenEndpoint ?? '(unset)'}`);
155
+ console.log(`powersync.uploadBaseUrl: ${payload.powersync.uploadBaseUrl ?? '(unset)'}`);
156
+ }
157
+ return;
158
+ }
159
+ const { db, stale } = await connectPowerSync({
160
+ config: resolved,
161
+ offline: false,
162
+ timeoutMs,
163
+ });
164
+ try {
165
+ const status = getStatusSnapshot(db);
166
+ const payload = { ...base, offline: false, stale, status };
167
+ if (jsonOutput) {
168
+ printYaml(payload);
169
+ }
170
+ else {
171
+ console.log(`loggedIn: ${payload.loggedIn ? 'yes' : 'no'}`);
172
+ if (payload.userId) {
173
+ console.log(`userId: ${payload.userId}`);
174
+ }
175
+ console.log(`db: ${payload.dbPath}`);
176
+ console.log(`connected: ${payload.status.connected ? 'yes' : 'no'}`);
177
+ if (payload.status.lastSyncedAt) {
178
+ console.log(`lastSyncedAt: ${payload.status.lastSyncedAt}`);
179
+ }
180
+ console.log(`dataFlow: downloading=${payload.status.dataFlowStatus.downloading} uploading=${payload.status.dataFlowStatus.uploading}`);
181
+ if (payload.stale) {
182
+ console.log('warning: sync timed out; local cache may be stale');
183
+ }
184
+ }
185
+ }
186
+ finally {
187
+ await db.close().catch(() => undefined);
188
+ }
189
+ });
190
+ auth.action((_options, command) => {
191
+ command.outputHelp();
192
+ });
193
+ }
@@ -0,0 +1,2 @@
1
+ import { Command } from 'commander';
2
+ export declare function registerBoxesCommand(program: Command): void;