@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.
- package/README.md +294 -0
- package/dist/auth/userInfo.d.ts +14 -0
- package/dist/auth/userInfo.js +115 -0
- package/dist/cli.d.ts +2 -0
- package/dist/cli.js +32 -0
- package/dist/cliTypes.d.ts +6 -0
- package/dist/cliTypes.js +1 -0
- package/dist/commands/auth/index.d.ts +2 -0
- package/dist/commands/auth/index.js +193 -0
- package/dist/commands/boxes/index.d.ts +2 -0
- package/dist/commands/boxes/index.js +107 -0
- package/dist/commands/boxes/repo.d.ts +21 -0
- package/dist/commands/boxes/repo.js +154 -0
- package/dist/commands/config/index.d.ts +2 -0
- package/dist/commands/config/index.js +67 -0
- package/dist/commands/info/index.d.ts +2 -0
- package/dist/commands/info/index.js +20 -0
- package/dist/commands/notes/index.d.ts +2 -0
- package/dist/commands/notes/index.js +271 -0
- package/dist/commands/notes/repo.d.ts +70 -0
- package/dist/commands/notes/repo.js +674 -0
- package/dist/commands/notes/searchTime.d.ts +9 -0
- package/dist/commands/notes/searchTime.js +85 -0
- package/dist/commands/prompt/index.d.ts +2 -0
- package/dist/commands/prompt/index.js +51 -0
- package/dist/commands/prompt/repo.d.ts +6 -0
- package/dist/commands/prompt/repo.js +18 -0
- package/dist/commands/sync.d.ts +2 -0
- package/dist/commands/sync.js +68 -0
- package/dist/commands/tags/index.d.ts +2 -0
- package/dist/commands/tags/index.js +120 -0
- package/dist/commands/tags/repo.d.ts +14 -0
- package/dist/commands/tags/repo.js +247 -0
- package/dist/config/keys.d.ts +9 -0
- package/dist/config/keys.js +17 -0
- package/dist/config/paths.d.ts +4 -0
- package/dist/config/paths.js +39 -0
- package/dist/config/resolve.d.ts +2 -0
- package/dist/config/resolve.js +56 -0
- package/dist/config/serviceEndpoints.d.ts +3 -0
- package/dist/config/serviceEndpoints.js +3 -0
- package/dist/config/store.d.ts +5 -0
- package/dist/config/store.js +87 -0
- package/dist/config/types.d.ts +51 -0
- package/dist/config/types.js +1 -0
- package/dist/dinox.d.ts +2 -0
- package/dist/dinox.js +50 -0
- package/dist/powersync/connector.d.ts +21 -0
- package/dist/powersync/connector.js +58 -0
- package/dist/powersync/runtime.d.ts +37 -0
- package/dist/powersync/runtime.js +107 -0
- package/dist/powersync/schema/content.d.ts +76 -0
- package/dist/powersync/schema/content.js +76 -0
- package/dist/powersync/schema/index.d.ts +371 -0
- package/dist/powersync/schema/index.js +35 -0
- package/dist/powersync/schema/local.d.ts +68 -0
- package/dist/powersync/schema/local.js +83 -0
- package/dist/powersync/schema/note.d.ts +34 -0
- package/dist/powersync/schema/note.js +34 -0
- package/dist/powersync/schema/notesExtras.d.ts +62 -0
- package/dist/powersync/schema/notesExtras.js +71 -0
- package/dist/powersync/schema/projects.d.ts +101 -0
- package/dist/powersync/schema/projects.js +101 -0
- package/dist/powersync/schema/tags.d.ts +37 -0
- package/dist/powersync/schema/tags.js +37 -0
- package/dist/powersync/tokenIndex.d.ts +17 -0
- package/dist/powersync/tokenIndex.js +202 -0
- package/dist/powersync/uploader.d.ts +7 -0
- package/dist/powersync/uploader.js +134 -0
- package/dist/utils/argValue.d.ts +1 -0
- package/dist/utils/argValue.js +17 -0
- package/dist/utils/errors.d.ts +10 -0
- package/dist/utils/errors.js +17 -0
- package/dist/utils/id.d.ts +1 -0
- package/dist/utils/id.js +4 -0
- package/dist/utils/output.d.ts +2 -0
- package/dist/utils/output.js +10 -0
- package/dist/utils/redact.d.ts +1 -0
- package/dist/utils/redact.js +10 -0
- package/dist/utils/text.d.ts +1 -0
- package/dist/utils/text.js +35 -0
- package/dist/utils/time.d.ts +1 -0
- package/dist/utils/time.js +3 -0
- package/dist/utils/tiptapMarkdown.d.ts +6 -0
- package/dist/utils/tiptapMarkdown.js +149 -0
- package/dist/utils/tokenize.d.ts +1 -0
- package/dist/utils/tokenize.js +56 -0
- package/dist/utils/version.d.ts +1 -0
- package/dist/utils/version.js +21 -0
- 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
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
|
+
}
|
package/dist/cliTypes.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -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
|
+
}
|