@dinoxx/dinox-cli 1.0.17 → 1.0.19
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +72 -34
- package/dist/cli.js +15 -1
- package/dist/commands/auth/index.d.ts +2 -0
- package/dist/commands/auth/index.js +209 -163
- package/dist/commands/boxes/index.d.ts +2 -0
- package/dist/commands/boxes/index.js +119 -75
- package/dist/commands/boxes/repo.d.ts +8 -0
- package/dist/commands/boxes/repo.js +41 -0
- package/dist/commands/config/index.d.ts +2 -0
- package/dist/commands/config/index.js +85 -36
- package/dist/commands/descriptor/register.d.ts +8 -0
- package/dist/commands/descriptor/register.js +113 -0
- package/dist/commands/descriptor/types.d.ts +37 -0
- package/dist/commands/descriptor/types.js +17 -0
- package/dist/commands/graph/index.d.ts +2 -0
- package/dist/commands/graph/index.js +143 -73
- package/dist/commands/info/index.d.ts +2 -0
- package/dist/commands/info/index.js +41 -14
- package/dist/commands/mcp/index.js +11 -10
- package/dist/commands/mcp/server.js +6 -3
- package/dist/commands/mcp/tools.js +30 -104
- package/dist/commands/mcp/types.d.ts +8 -1
- package/dist/commands/meta/index.d.ts +2 -0
- package/dist/commands/meta/index.js +64 -33
- package/dist/commands/meta/repo.d.ts +6 -0
- package/dist/commands/meta/repo.js +6 -0
- package/dist/commands/notes/actions.d.ts +10 -0
- package/dist/commands/notes/actions.js +403 -0
- package/dist/commands/notes/descriptors.d.ts +4 -0
- package/dist/commands/notes/descriptors.js +250 -0
- package/dist/commands/notes/dryRun.d.ts +31 -0
- package/dist/commands/notes/dryRun.js +130 -0
- package/dist/commands/notes/index.d.ts +2 -51
- package/dist/commands/notes/index.js +2 -581
- package/dist/commands/notes/repo.d.ts +9 -0
- package/dist/commands/notes/repo.js +56 -10
- package/dist/commands/notes/runtime.d.ts +28 -0
- package/dist/commands/notes/runtime.js +185 -0
- package/dist/commands/notes/searchOutput.d.ts +10 -2
- package/dist/commands/notes/searchOutput.js +14 -2
- package/dist/commands/notes/searchResponse.d.ts +51 -0
- package/dist/commands/notes/searchResponse.js +56 -0
- package/dist/commands/notes/searchSql.js +2 -0
- package/dist/commands/prompt/index.d.ts +2 -0
- package/dist/commands/prompt/index.js +122 -72
- package/dist/commands/prompt/repo.d.ts +7 -0
- package/dist/commands/prompt/repo.js +46 -0
- package/dist/commands/schema/commandMetadata.d.ts +2 -0
- package/dist/commands/schema/commandMetadata.js +18 -0
- package/dist/commands/schema/globalOptions.d.ts +3 -0
- package/dist/commands/schema/globalOptions.js +3 -0
- package/dist/commands/schema/index.d.ts +4 -0
- package/dist/commands/schema/index.js +109 -0
- package/dist/commands/schema/renderSkills.d.ts +6 -0
- package/dist/commands/schema/renderSkills.js +216 -0
- package/dist/commands/schema/spec.d.ts +6 -0
- package/dist/commands/schema/spec.js +40 -0
- package/dist/commands/schema/types.d.ts +47 -0
- package/dist/commands/schema/types.js +1 -0
- package/dist/commands/storage/index.d.ts +4 -0
- package/dist/commands/storage/index.js +292 -0
- package/dist/commands/storage/repo.d.ts +162 -0
- package/dist/commands/storage/repo.js +751 -0
- package/dist/commands/sync.d.ts +2 -0
- package/dist/commands/sync.js +41 -27
- package/dist/commands/tags/index.d.ts +2 -0
- package/dist/commands/tags/index.js +121 -79
- package/dist/commands/tags/repo.d.ts +7 -0
- package/dist/commands/tags/repo.js +52 -0
- package/dist/commands/todo/dryRun.d.ts +23 -0
- package/dist/commands/todo/dryRun.js +133 -0
- package/dist/commands/todo/index.d.ts +2 -0
- package/dist/commands/todo/index.js +285 -135
- package/dist/commands/todo/repo.d.ts +11 -0
- package/dist/commands/todo/repo.js +37 -9
- package/dist/commands/update.d.ts +3 -0
- package/dist/commands/update.js +35 -13
- package/dist/commands/writeDryRun.d.ts +62 -0
- package/dist/commands/writeDryRun.js +131 -0
- package/dist/constants/links.d.ts +1 -1
- package/dist/constants/links.js +1 -1
- package/dist/daemon/ensure.js +3 -2
- package/dist/daemon/index.js +17 -15
- package/dist/dinox.js +9 -13
- package/dist/powersync/schema/index.d.ts +27 -0
- package/dist/powersync/schema/index.js +2 -0
- package/dist/powersync/schema/note.d.ts +1 -0
- package/dist/powersync/schema/note.js +1 -0
- package/dist/powersync/schema/projects.d.ts +3 -0
- package/dist/powersync/schema/projects.js +3 -0
- package/dist/powersync/schema/storage.d.ts +24 -0
- package/dist/powersync/schema/storage.js +24 -0
- package/dist/skills/check.d.ts +6 -0
- package/dist/skills/check.js +254 -0
- package/dist/utils/bidlinkCanonicalization.d.ts +2 -0
- package/dist/utils/bidlinkCanonicalization.js +161 -0
- package/dist/utils/dryRun.d.ts +13 -0
- package/dist/utils/dryRun.js +69 -0
- package/dist/utils/output.d.ts +19 -0
- package/dist/utils/output.js +155 -1
- package/dist/utils/terminalSanitization.d.ts +2 -0
- package/dist/utils/terminalSanitization.js +33 -0
- package/dist/utils/tiptapMarkdown.d.ts +4 -1
- package/dist/utils/tiptapMarkdown.js +285 -113
- package/dist/utils/tiptapMarkdownSchema.d.ts +37 -0
- package/dist/utils/tiptapMarkdownSchema.js +577 -0
- package/dist/utils/tiptapMarkdownSchemaUtils.d.ts +74 -0
- package/dist/utils/tiptapMarkdownSchemaUtils.js +394 -0
- package/dist/utils/updateNotice.d.ts +7 -0
- package/dist/utils/updateNotice.js +64 -0
- package/package.json +25 -3
|
@@ -1,183 +1,229 @@
|
|
|
1
1
|
import fs from 'node:fs/promises';
|
|
2
|
+
import { refreshAndPersistAuthIdentity } from '../../auth/userInfo.js';
|
|
3
|
+
import { getPowerSyncDbPath } from '../../config/paths.js';
|
|
2
4
|
import { loadConfig, saveConfig } from '../../config/store.js';
|
|
3
5
|
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 { parseSyncTimeoutMs } from '../../utils/syncTimeout.js';
|
|
9
|
-
import { normalizeAuthToken } from '../../utils/authToken.js';
|
|
10
|
-
import { refreshAndPersistAuthIdentity } from '../../auth/userInfo.js';
|
|
11
|
-
import { connectPowerSync, getStatusSnapshot } from '../../powersync/runtime.js';
|
|
12
6
|
import { fetchCredentialsWithAuthorization } from '../../powersync/connector.js';
|
|
7
|
+
import { connectPowerSync, getStatusSnapshot } from '../../powersync/runtime.js';
|
|
13
8
|
import { runSingleSyncPass } from '../../powersync/syncPass.js';
|
|
9
|
+
import { registerCommandGroup } from '../descriptor/register.js';
|
|
10
|
+
import { FORMAT_OPTIONS, OFFLINE_OPTIONS, SYNC_TIMEOUT_OPTIONS } from '../schema/globalOptions.js';
|
|
11
|
+
import { normalizeAuthToken } from '../../utils/authToken.js';
|
|
12
|
+
import { DinoxError } from '../../utils/errors.js';
|
|
13
|
+
import { getRequestedStructuredOutputFormat, printHumanReadableLine, printSuccess, } from '../../utils/output.js';
|
|
14
|
+
import { redactAuthorization } from '../../utils/redact.js';
|
|
15
|
+
import { parseSyncTimeoutMs } from '../../utils/syncTimeout.js';
|
|
16
|
+
const AUTH_LOGIN_GLOBAL_OPTIONS = [...FORMAT_OPTIONS, ...SYNC_TIMEOUT_OPTIONS];
|
|
17
|
+
const AUTH_STATUS_GLOBAL_OPTIONS = [...FORMAT_OPTIONS, ...OFFLINE_OPTIONS, ...SYNC_TIMEOUT_OPTIONS];
|
|
14
18
|
async function removeSqliteArtifacts(dbPath) {
|
|
15
|
-
const
|
|
16
|
-
for (const suffix of artifacts) {
|
|
19
|
+
for (const suffix of ['', '-wal', '-shm']) {
|
|
17
20
|
await fs.rm(`${dbPath}${suffix}`, { force: true }).catch(() => undefined);
|
|
18
21
|
}
|
|
19
22
|
}
|
|
20
|
-
|
|
21
|
-
const
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
const globals = command.optsWithGlobals?.() ?? {};
|
|
38
|
-
const timeoutMs = parseSyncTimeoutMs(globals.syncTimeout) ?? resolved.sync.timeoutMs;
|
|
39
|
-
const jsonOutput = Boolean(globals.json);
|
|
40
|
-
// 1) Token exchange sanity check.
|
|
41
|
-
await fetchCredentialsWithAuthorization({
|
|
42
|
-
authorization: rawToken,
|
|
43
|
-
endpoint: resolved.powersync.endpoint,
|
|
44
|
-
credentialsUrl: resolved.powersync.tokenEndpoint,
|
|
45
|
-
timeoutMs,
|
|
46
|
-
});
|
|
47
|
-
// 1.5) Fetch and persist user identity.
|
|
48
|
-
const identity = await refreshAndPersistAuthIdentity(config, resolved);
|
|
49
|
-
// 2) Connect + initial sync.
|
|
50
|
-
const { db, dbPath, stale } = await connectPowerSync({
|
|
51
|
-
config: resolved,
|
|
52
|
-
offline: false,
|
|
53
|
-
timeoutMs,
|
|
54
|
-
});
|
|
55
|
-
try {
|
|
56
|
-
await runSingleSyncPass(db, timeoutMs);
|
|
57
|
-
const status = getStatusSnapshot(db);
|
|
58
|
-
const result = {
|
|
59
|
-
ok: true,
|
|
60
|
-
verified: true,
|
|
61
|
-
endpoint: resolved.powersync.endpoint,
|
|
62
|
-
dbPath,
|
|
63
|
-
stale,
|
|
64
|
-
lastSyncedAt: status.lastSyncedAt,
|
|
65
|
-
userId: identity.userId,
|
|
66
|
-
};
|
|
67
|
-
if (jsonOutput) {
|
|
68
|
-
printYaml(result);
|
|
69
|
-
}
|
|
70
|
-
else {
|
|
71
|
-
console.log('Login verified.');
|
|
72
|
-
console.log(`- endpoint: ${result.endpoint}`);
|
|
73
|
-
console.log(`- db: ${result.dbPath}`);
|
|
74
|
-
console.log(`- userId: ${result.userId}`);
|
|
75
|
-
if (result.lastSyncedAt) {
|
|
76
|
-
console.log(`- lastSyncedAt: ${result.lastSyncedAt}`);
|
|
77
|
-
}
|
|
78
|
-
if (stale) {
|
|
79
|
-
console.log('- warning: initial sync timed out; local cache may be stale');
|
|
80
|
-
}
|
|
81
|
-
}
|
|
82
|
-
}
|
|
83
|
-
finally {
|
|
84
|
-
await db.close().catch(() => undefined);
|
|
85
|
-
}
|
|
23
|
+
async function runAuthLogin(context) {
|
|
24
|
+
const token = context.args.token;
|
|
25
|
+
const rawToken = normalizeAuthToken(token);
|
|
26
|
+
if (!rawToken) {
|
|
27
|
+
throw new DinoxError('Token is required');
|
|
28
|
+
}
|
|
29
|
+
const config = await loadConfig();
|
|
30
|
+
config.auth = { ...(config.auth ?? {}), authorization: rawToken };
|
|
31
|
+
await saveConfig(config);
|
|
32
|
+
const resolved = resolveConfig(config);
|
|
33
|
+
const timeoutMs = parseSyncTimeoutMs(context.globals.syncTimeout) ?? resolved.sync.timeoutMs;
|
|
34
|
+
const structuredOutput = getRequestedStructuredOutputFormat() !== null;
|
|
35
|
+
await fetchCredentialsWithAuthorization({
|
|
36
|
+
authorization: rawToken,
|
|
37
|
+
endpoint: resolved.powersync.endpoint,
|
|
38
|
+
credentialsUrl: resolved.powersync.tokenEndpoint,
|
|
39
|
+
timeoutMs,
|
|
86
40
|
});
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
const config = await loadConfig();
|
|
93
|
-
if (config.auth) {
|
|
94
|
-
config.auth.authorization = undefined;
|
|
95
|
-
config.auth.userId = undefined;
|
|
96
|
-
config.auth.username = undefined;
|
|
97
|
-
config.auth.email = undefined;
|
|
98
|
-
config.auth.userInfoSyncedAt = undefined;
|
|
99
|
-
}
|
|
100
|
-
await saveConfig(config);
|
|
101
|
-
if (options.clearLocalDb) {
|
|
102
|
-
const dbPath = getPowerSyncDbPath();
|
|
103
|
-
await removeSqliteArtifacts(dbPath);
|
|
104
|
-
}
|
|
41
|
+
const identity = await refreshAndPersistAuthIdentity(config, resolved);
|
|
42
|
+
const { db, dbPath, stale } = await connectPowerSync({
|
|
43
|
+
config: resolved,
|
|
44
|
+
offline: false,
|
|
45
|
+
timeoutMs,
|
|
105
46
|
});
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
const offline = Boolean(globals.offline);
|
|
114
|
-
const jsonOutput = Boolean(globals.json);
|
|
115
|
-
const timeoutMs = parseSyncTimeoutMs(globals.syncTimeout) ?? resolved.sync.timeoutMs;
|
|
116
|
-
const dbPath = getPowerSyncDbPath();
|
|
117
|
-
const base = {
|
|
118
|
-
loggedIn: Boolean(resolved.auth.authorization),
|
|
119
|
-
authorization: redactAuthorization(resolved.auth.authorization),
|
|
120
|
-
userId: resolved.auth.userId ?? null,
|
|
121
|
-
username: resolved.auth.username ?? null,
|
|
122
|
-
email: resolved.auth.email ?? null,
|
|
123
|
-
powersync: {
|
|
124
|
-
endpoint: resolved.powersync.endpoint ?? null,
|
|
125
|
-
tokenEndpoint: resolved.powersync.tokenEndpoint ?? null,
|
|
126
|
-
uploadBaseUrl: resolved.powersync.uploadBaseUrl ?? null,
|
|
127
|
-
uploadV4Path: resolved.powersync.uploadV4Path,
|
|
128
|
-
uploadV2Path: resolved.powersync.uploadV2Path,
|
|
129
|
-
},
|
|
47
|
+
try {
|
|
48
|
+
await runSingleSyncPass(db, timeoutMs);
|
|
49
|
+
const status = getStatusSnapshot(db);
|
|
50
|
+
const result = {
|
|
51
|
+
ok: true,
|
|
52
|
+
verified: true,
|
|
53
|
+
endpoint: resolved.powersync.endpoint,
|
|
130
54
|
dbPath,
|
|
55
|
+
stale,
|
|
56
|
+
lastSyncedAt: status.lastSyncedAt,
|
|
57
|
+
userId: identity.userId,
|
|
131
58
|
};
|
|
132
|
-
if (
|
|
133
|
-
|
|
134
|
-
if (jsonOutput) {
|
|
135
|
-
printYaml(payload);
|
|
136
|
-
}
|
|
137
|
-
else {
|
|
138
|
-
console.log(`loggedIn: ${payload.loggedIn ? 'yes' : 'no'}`);
|
|
139
|
-
if (payload.userId) {
|
|
140
|
-
console.log(`userId: ${payload.userId}`);
|
|
141
|
-
}
|
|
142
|
-
console.log(`db: ${payload.dbPath}`);
|
|
143
|
-
console.log(`powersync.endpoint: ${payload.powersync.endpoint ?? '(unset)'}`);
|
|
144
|
-
console.log(`powersync.tokenEndpoint: ${payload.powersync.tokenEndpoint ?? '(unset)'}`);
|
|
145
|
-
console.log(`powersync.uploadBaseUrl: ${payload.powersync.uploadBaseUrl ?? '(unset)'}`);
|
|
146
|
-
}
|
|
59
|
+
if (structuredOutput) {
|
|
60
|
+
printSuccess(result);
|
|
147
61
|
return;
|
|
148
62
|
}
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
63
|
+
printHumanReadableLine('Login verified.');
|
|
64
|
+
printHumanReadableLine(`- endpoint: ${result.endpoint}`);
|
|
65
|
+
printHumanReadableLine(`- db: ${result.dbPath}`);
|
|
66
|
+
printHumanReadableLine(`- userId: ${result.userId}`);
|
|
67
|
+
if (result.lastSyncedAt) {
|
|
68
|
+
printHumanReadableLine(`- lastSyncedAt: ${result.lastSyncedAt}`);
|
|
69
|
+
}
|
|
70
|
+
if (stale) {
|
|
71
|
+
printHumanReadableLine('- warning: initial sync timed out; local cache may be stale');
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
finally {
|
|
75
|
+
await db.close().catch(() => undefined);
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
async function runAuthLogout(context) {
|
|
79
|
+
const options = context.options;
|
|
80
|
+
const config = await loadConfig();
|
|
81
|
+
if (config.auth) {
|
|
82
|
+
config.auth.authorization = undefined;
|
|
83
|
+
config.auth.userId = undefined;
|
|
84
|
+
config.auth.username = undefined;
|
|
85
|
+
config.auth.email = undefined;
|
|
86
|
+
config.auth.userInfoSyncedAt = undefined;
|
|
87
|
+
}
|
|
88
|
+
await saveConfig(config);
|
|
89
|
+
if (options.clearLocalDb) {
|
|
90
|
+
await removeSqliteArtifacts(getPowerSyncDbPath());
|
|
91
|
+
}
|
|
92
|
+
if (getRequestedStructuredOutputFormat()) {
|
|
93
|
+
printSuccess({
|
|
94
|
+
ok: true,
|
|
95
|
+
loggedOut: true,
|
|
96
|
+
clearedLocalDb: Boolean(options.clearLocalDb),
|
|
153
97
|
});
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
async function runAuthStatus(context) {
|
|
101
|
+
const config = await loadConfig();
|
|
102
|
+
const resolved = resolveConfig(config);
|
|
103
|
+
const offline = Boolean(context.globals.offline);
|
|
104
|
+
const structuredOutput = getRequestedStructuredOutputFormat() !== null;
|
|
105
|
+
const timeoutMs = parseSyncTimeoutMs(context.globals.syncTimeout) ?? resolved.sync.timeoutMs;
|
|
106
|
+
const dbPath = getPowerSyncDbPath();
|
|
107
|
+
const base = {
|
|
108
|
+
loggedIn: Boolean(resolved.auth.authorization),
|
|
109
|
+
authorization: redactAuthorization(resolved.auth.authorization),
|
|
110
|
+
userId: resolved.auth.userId ?? null,
|
|
111
|
+
username: resolved.auth.username ?? null,
|
|
112
|
+
email: resolved.auth.email ?? null,
|
|
113
|
+
powersync: {
|
|
114
|
+
endpoint: resolved.powersync.endpoint ?? null,
|
|
115
|
+
tokenEndpoint: resolved.powersync.tokenEndpoint ?? null,
|
|
116
|
+
uploadBaseUrl: resolved.powersync.uploadBaseUrl ?? null,
|
|
117
|
+
uploadV4Path: resolved.powersync.uploadV4Path,
|
|
118
|
+
uploadV2Path: resolved.powersync.uploadV2Path,
|
|
119
|
+
},
|
|
120
|
+
dbPath,
|
|
121
|
+
};
|
|
122
|
+
if (offline || !resolved.auth.authorization || !resolved.powersync.endpoint || !resolved.powersync.tokenEndpoint) {
|
|
123
|
+
const payload = { ...base, offline: true };
|
|
124
|
+
if (structuredOutput) {
|
|
125
|
+
printSuccess(payload);
|
|
126
|
+
return;
|
|
175
127
|
}
|
|
176
|
-
|
|
177
|
-
|
|
128
|
+
printHumanReadableLine(`loggedIn: ${payload.loggedIn ? 'yes' : 'no'}`);
|
|
129
|
+
if (payload.userId) {
|
|
130
|
+
printHumanReadableLine(`userId: ${payload.userId}`);
|
|
178
131
|
}
|
|
132
|
+
printHumanReadableLine(`db: ${payload.dbPath}`);
|
|
133
|
+
printHumanReadableLine(`powersync.endpoint: ${payload.powersync.endpoint ?? '(unset)'}`);
|
|
134
|
+
printHumanReadableLine(`powersync.tokenEndpoint: ${payload.powersync.tokenEndpoint ?? '(unset)'}`);
|
|
135
|
+
printHumanReadableLine(`powersync.uploadBaseUrl: ${payload.powersync.uploadBaseUrl ?? '(unset)'}`);
|
|
136
|
+
return;
|
|
137
|
+
}
|
|
138
|
+
const { db, stale } = await connectPowerSync({
|
|
139
|
+
config: resolved,
|
|
140
|
+
offline: false,
|
|
141
|
+
timeoutMs,
|
|
179
142
|
});
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
143
|
+
try {
|
|
144
|
+
const status = getStatusSnapshot(db);
|
|
145
|
+
const payload = { ...base, offline: false, stale, status };
|
|
146
|
+
if (structuredOutput) {
|
|
147
|
+
printSuccess(payload);
|
|
148
|
+
return;
|
|
149
|
+
}
|
|
150
|
+
printHumanReadableLine(`loggedIn: ${payload.loggedIn ? 'yes' : 'no'}`);
|
|
151
|
+
if (payload.userId) {
|
|
152
|
+
printHumanReadableLine(`userId: ${payload.userId}`);
|
|
153
|
+
}
|
|
154
|
+
printHumanReadableLine(`db: ${payload.dbPath}`);
|
|
155
|
+
printHumanReadableLine(`connected: ${payload.status.connected ? 'yes' : 'no'}`);
|
|
156
|
+
if (payload.status.lastSyncedAt) {
|
|
157
|
+
printHumanReadableLine(`lastSyncedAt: ${payload.status.lastSyncedAt}`);
|
|
158
|
+
}
|
|
159
|
+
printHumanReadableLine(`dataFlow: downloading=${payload.status.dataFlowStatus.downloading} uploading=${payload.status.dataFlowStatus.uploading}`);
|
|
160
|
+
if (payload.stale) {
|
|
161
|
+
printHumanReadableLine('warning: sync timed out; local cache may be stale');
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
finally {
|
|
165
|
+
await db.close().catch(() => undefined);
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
export const authCommandGroup = {
|
|
169
|
+
name: 'auth',
|
|
170
|
+
description: 'Authentication and PowerSync connectivity commands',
|
|
171
|
+
commands: [
|
|
172
|
+
{
|
|
173
|
+
name: 'login',
|
|
174
|
+
description: 'Save login token and verify PowerSync connectivity',
|
|
175
|
+
arguments: [{ syntax: '<token>', description: 'Login token (without Bearer prefix)' }],
|
|
176
|
+
schema: {
|
|
177
|
+
path: 'auth.login',
|
|
178
|
+
risk: 'write',
|
|
179
|
+
auth: 'optional',
|
|
180
|
+
globalOptions: AUTH_LOGIN_GLOBAL_OPTIONS,
|
|
181
|
+
output: {
|
|
182
|
+
type: 'structured',
|
|
183
|
+
summary: 'Login verification result',
|
|
184
|
+
fields: ['verified', 'endpoint', 'dbPath', 'stale', 'lastSyncedAt', 'userId'],
|
|
185
|
+
},
|
|
186
|
+
examples: ['dino auth login "<token>" --format json'],
|
|
187
|
+
},
|
|
188
|
+
action: runAuthLogin,
|
|
189
|
+
},
|
|
190
|
+
{
|
|
191
|
+
name: 'logout',
|
|
192
|
+
description: 'Clear saved login token and optionally remove the local cache',
|
|
193
|
+
options: [{ flags: '--clear-local-db', description: 'Delete the local PowerSync SQLite database' }],
|
|
194
|
+
schema: {
|
|
195
|
+
path: 'auth.logout',
|
|
196
|
+
risk: 'write',
|
|
197
|
+
auth: 'optional',
|
|
198
|
+
globalOptions: FORMAT_OPTIONS,
|
|
199
|
+
output: {
|
|
200
|
+
type: 'structured',
|
|
201
|
+
summary: 'Logout acknowledgement',
|
|
202
|
+
fields: ['loggedOut', 'clearedLocalDb'],
|
|
203
|
+
},
|
|
204
|
+
examples: ['dino auth logout --format json', 'dino auth logout --clear-local-db --format json'],
|
|
205
|
+
},
|
|
206
|
+
action: runAuthLogout,
|
|
207
|
+
},
|
|
208
|
+
{
|
|
209
|
+
name: 'status',
|
|
210
|
+
description: 'Show current login, local cache, and sync status',
|
|
211
|
+
schema: {
|
|
212
|
+
path: 'auth.status',
|
|
213
|
+
risk: 'read',
|
|
214
|
+
auth: 'optional',
|
|
215
|
+
globalOptions: AUTH_STATUS_GLOBAL_OPTIONS,
|
|
216
|
+
output: {
|
|
217
|
+
type: 'structured',
|
|
218
|
+
summary: 'Auth and connectivity status envelope',
|
|
219
|
+
fields: ['loggedIn', 'authorization', 'userId', 'username', 'email', 'powersync', 'dbPath', 'offline', 'stale', 'status'],
|
|
220
|
+
},
|
|
221
|
+
examples: ['dino auth status --format json', 'dino auth status --offline --format json'],
|
|
222
|
+
},
|
|
223
|
+
action: runAuthStatus,
|
|
224
|
+
},
|
|
225
|
+
],
|
|
226
|
+
};
|
|
227
|
+
export function registerAuthCommands(program) {
|
|
228
|
+
registerCommandGroup(program, authCommandGroup);
|
|
183
229
|
}
|
|
@@ -1,12 +1,17 @@
|
|
|
1
|
+
import { resolveAuthIdentity } from '../../auth/userInfo.js';
|
|
1
2
|
import { loadConfig } from '../../config/store.js';
|
|
2
3
|
import { resolveConfig } from '../../config/resolve.js';
|
|
3
|
-
import { DinoxError } from '../../utils/errors.js';
|
|
4
|
-
import { printYaml } from '../../utils/output.js';
|
|
5
|
-
import { parseSyncTimeoutMs } from '../../utils/syncTimeout.js';
|
|
6
4
|
import { connectPowerSync, waitForIdleDataFlow } from '../../powersync/runtime.js';
|
|
7
5
|
import { syncNoteTokenIndex } from '../../powersync/tokenIndex.js';
|
|
8
|
-
import {
|
|
9
|
-
import {
|
|
6
|
+
import { registerCommandGroup } from '../descriptor/register.js';
|
|
7
|
+
import { FORMAT_OPTIONS, OFFLINE_OPTIONS, SYNC_TIMEOUT_OPTIONS } from '../schema/globalOptions.js';
|
|
8
|
+
import { buildBoxAddDryRunPreview } from '../writeDryRun.js';
|
|
9
|
+
import { printDryRunPreview } from '../../utils/dryRun.js';
|
|
10
|
+
import { DinoxError } from '../../utils/errors.js';
|
|
11
|
+
import { getRequestedStructuredOutputFormat, printHumanReadableLine, printSuccess, } from '../../utils/output.js';
|
|
12
|
+
import { parseSyncTimeoutMs } from '../../utils/syncTimeout.js';
|
|
13
|
+
import { addBox, inspectAddBox, listBoxes } from './repo.js';
|
|
14
|
+
const BOX_GLOBAL_OPTIONS = [...FORMAT_OPTIONS, ...OFFLINE_OPTIONS, ...SYNC_TIMEOUT_OPTIONS];
|
|
10
15
|
async function syncBeforeBoxCommand(db, offline, timeoutMs) {
|
|
11
16
|
if (offline) {
|
|
12
17
|
return;
|
|
@@ -14,85 +19,124 @@ async function syncBeforeBoxCommand(db, offline, timeoutMs) {
|
|
|
14
19
|
await waitForIdleDataFlow(db, Math.min(timeoutMs, 10_000));
|
|
15
20
|
await syncNoteTokenIndex(db);
|
|
16
21
|
}
|
|
17
|
-
async function
|
|
18
|
-
const
|
|
19
|
-
const
|
|
20
|
-
const
|
|
21
|
-
const
|
|
22
|
-
const { db } = await connectPowerSync({ config, offline, timeoutMs });
|
|
22
|
+
async function withBoxDb(context, run) {
|
|
23
|
+
const rawConfig = await loadConfig();
|
|
24
|
+
const config = resolveConfig(rawConfig);
|
|
25
|
+
const timeoutMs = parseSyncTimeoutMs(context.globals.syncTimeout) ?? config.sync.timeoutMs;
|
|
26
|
+
const offline = Boolean(context.globals.offline);
|
|
27
|
+
const { db, stale } = await connectPowerSync({ config, offline, timeoutMs });
|
|
23
28
|
try {
|
|
24
|
-
await
|
|
25
|
-
const boxes = await listBoxes(db);
|
|
26
|
-
printYaml(boxes);
|
|
29
|
+
await run({ db, config, rawConfig, offline, timeoutMs, stale });
|
|
27
30
|
}
|
|
28
31
|
finally {
|
|
29
32
|
await db.close().catch(() => undefined);
|
|
30
33
|
}
|
|
31
34
|
}
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
const
|
|
53
|
-
const
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
35
|
+
async function runBoxList(context) {
|
|
36
|
+
await withBoxDb(context, async ({ db, offline, timeoutMs }) => {
|
|
37
|
+
await syncBeforeBoxCommand(db, offline, timeoutMs);
|
|
38
|
+
printSuccess(await listBoxes(db));
|
|
39
|
+
});
|
|
40
|
+
}
|
|
41
|
+
async function runBoxAdd(context) {
|
|
42
|
+
const nameArg = context.args.name;
|
|
43
|
+
const optionName = typeof context.options.name === 'string' ? context.options.name : undefined;
|
|
44
|
+
const name = (nameArg ?? optionName ?? '').trim();
|
|
45
|
+
if (!name) {
|
|
46
|
+
throw new DinoxError('Box name is required. Use `dino box add <name>` or `dino box add --name <name>`.');
|
|
47
|
+
}
|
|
48
|
+
if (nameArg && optionName && nameArg.trim() !== optionName.trim()) {
|
|
49
|
+
throw new DinoxError('Conflicting box name values from positional argument and --name option.');
|
|
50
|
+
}
|
|
51
|
+
const structuredOutput = getRequestedStructuredOutputFormat() !== null;
|
|
52
|
+
await withBoxDb(context, async ({ db, config, rawConfig, offline, timeoutMs, stale }) => {
|
|
53
|
+
const identity = await resolveAuthIdentity(rawConfig, config);
|
|
54
|
+
await syncBeforeBoxCommand(db, offline, timeoutMs);
|
|
55
|
+
const description = typeof context.options.description === 'string' ? context.options.description : undefined;
|
|
56
|
+
const color = typeof context.options.color === 'string' ? context.options.color : undefined;
|
|
57
|
+
if (Boolean(context.options.dryRun)) {
|
|
58
|
+
printDryRunPreview(buildBoxAddDryRunPreview({
|
|
59
|
+
config,
|
|
60
|
+
offline,
|
|
61
|
+
...(await inspectAddBox(db, {
|
|
62
|
+
name,
|
|
63
|
+
userId: identity.userId,
|
|
64
|
+
description,
|
|
65
|
+
color,
|
|
66
|
+
})),
|
|
67
|
+
}));
|
|
68
|
+
return;
|
|
58
69
|
}
|
|
59
|
-
|
|
60
|
-
|
|
70
|
+
const result = await addBox(db, {
|
|
71
|
+
name,
|
|
72
|
+
userId: identity.userId,
|
|
73
|
+
description,
|
|
74
|
+
color,
|
|
75
|
+
});
|
|
76
|
+
if (!offline && !config.powersync.uploadBaseUrl) {
|
|
77
|
+
printHumanReadableLine('warning: upload disabled (powersync.uploadBaseUrl is unset); changes are local-only for now');
|
|
61
78
|
}
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
const timeoutMs = parseSyncTimeoutMs(options.syncTimeout ?? globals.syncTimeout) ?? config.sync.timeoutMs;
|
|
65
|
-
const identity = await resolveAuthIdentity(rawConfig, config);
|
|
66
|
-
const { db, stale } = await connectPowerSync({ config, offline, timeoutMs });
|
|
67
|
-
try {
|
|
68
|
-
await syncBeforeBoxCommand(db, offline, timeoutMs);
|
|
69
|
-
const result = await addBox(db, {
|
|
70
|
-
name,
|
|
71
|
-
userId: identity.userId,
|
|
72
|
-
description: typeof options.description === 'string' ? options.description : undefined,
|
|
73
|
-
color: typeof options.color === 'string' ? options.color : undefined,
|
|
74
|
-
});
|
|
75
|
-
if (!offline && !config.powersync.uploadBaseUrl) {
|
|
76
|
-
console.log('warning: upload disabled (powersync.uploadBaseUrl is unset); changes are local-only for now');
|
|
77
|
-
}
|
|
78
|
-
if (!offline) {
|
|
79
|
-
await waitForIdleDataFlow(db, Math.min(timeoutMs, 5_000));
|
|
80
|
-
}
|
|
81
|
-
const payload = { ok: true, stale: offline ? true : stale, ...result };
|
|
82
|
-
if (jsonOutput) {
|
|
83
|
-
printYaml(payload);
|
|
84
|
-
return;
|
|
85
|
-
}
|
|
86
|
-
console.log(result.id);
|
|
87
|
-
if (stale && !offline) {
|
|
88
|
-
console.log('warning: sync timed out; results may be stale');
|
|
89
|
-
}
|
|
79
|
+
if (!offline) {
|
|
80
|
+
await waitForIdleDataFlow(db, Math.min(timeoutMs, 5_000));
|
|
90
81
|
}
|
|
91
|
-
|
|
92
|
-
|
|
82
|
+
const payload = { ok: true, stale: offline ? true : stale, ...result };
|
|
83
|
+
if (structuredOutput) {
|
|
84
|
+
printSuccess(payload);
|
|
85
|
+
return;
|
|
86
|
+
}
|
|
87
|
+
printHumanReadableLine(result.id);
|
|
88
|
+
if (stale && !offline) {
|
|
89
|
+
printHumanReadableLine('warning: sync timed out; results may be stale');
|
|
93
90
|
}
|
|
94
91
|
});
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
92
|
+
}
|
|
93
|
+
export const boxCommandGroup = {
|
|
94
|
+
name: 'box',
|
|
95
|
+
description: 'Manage zettel boxes',
|
|
96
|
+
commands: [
|
|
97
|
+
{
|
|
98
|
+
name: 'list',
|
|
99
|
+
description: 'List all zettel boxes from c_zettel_box',
|
|
100
|
+
schema: {
|
|
101
|
+
path: 'box.list',
|
|
102
|
+
risk: 'read',
|
|
103
|
+
auth: 'required',
|
|
104
|
+
globalOptions: BOX_GLOBAL_OPTIONS,
|
|
105
|
+
output: {
|
|
106
|
+
type: 'structured',
|
|
107
|
+
summary: 'Box list payload',
|
|
108
|
+
fields: ['id', 'name', 'description'],
|
|
109
|
+
},
|
|
110
|
+
examples: ['dino box list --format json'],
|
|
111
|
+
},
|
|
112
|
+
action: runBoxList,
|
|
113
|
+
},
|
|
114
|
+
{
|
|
115
|
+
name: 'add',
|
|
116
|
+
description: 'Create a zettel box, restoring a deleted row when possible',
|
|
117
|
+
arguments: [{ syntax: '[name]', description: 'Box name' }],
|
|
118
|
+
options: [
|
|
119
|
+
{ flags: '--name <string>', description: 'Box name (alternative to positional name)' },
|
|
120
|
+
{ flags: '--description <string>', description: 'Box purpose/usage description' },
|
|
121
|
+
{ flags: '--color <string>', description: 'Box color' },
|
|
122
|
+
{ flags: '--dry-run', description: 'Preview the write without executing it' },
|
|
123
|
+
],
|
|
124
|
+
schema: {
|
|
125
|
+
path: 'box.add',
|
|
126
|
+
risk: 'write',
|
|
127
|
+
auth: 'required',
|
|
128
|
+
globalOptions: BOX_GLOBAL_OPTIONS,
|
|
129
|
+
output: {
|
|
130
|
+
type: 'structured',
|
|
131
|
+
summary: 'Box creation acknowledgement',
|
|
132
|
+
fields: ['id', 'name', 'description', 'color', 'restored', 'stale'],
|
|
133
|
+
},
|
|
134
|
+
examples: ['dino box add "Inbox" --description "用于存放待整理的想法和资料" --format json --dry-run'],
|
|
135
|
+
},
|
|
136
|
+
action: runBoxAdd,
|
|
137
|
+
},
|
|
138
|
+
],
|
|
139
|
+
};
|
|
140
|
+
export function registerBoxesCommand(program) {
|
|
141
|
+
registerCommandGroup(program, boxCommandGroup);
|
|
98
142
|
}
|
|
@@ -17,5 +17,13 @@ export type AddBoxResult = {
|
|
|
17
17
|
color: string;
|
|
18
18
|
restored: boolean;
|
|
19
19
|
};
|
|
20
|
+
export type InspectAddBoxResult = {
|
|
21
|
+
name: string;
|
|
22
|
+
description: string;
|
|
23
|
+
color: string;
|
|
24
|
+
restored: boolean;
|
|
25
|
+
existingId?: string;
|
|
26
|
+
};
|
|
20
27
|
export declare function listBoxes(db: AbstractPowerSyncDatabase): Promise<BoxListItem[]>;
|
|
21
28
|
export declare function addBox(db: AbstractPowerSyncDatabase, input: AddBoxInput): Promise<AddBoxResult>;
|
|
29
|
+
export declare function inspectAddBox(db: AbstractPowerSyncDatabase, input: AddBoxInput): Promise<InspectAddBoxResult>;
|