@dinoxx/dinox-cli 1.0.19 → 1.0.20
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 +799 -448
- package/dist/auth/userInfo.d.ts +2 -0
- package/dist/auth/userInfo.js +28 -11
- package/dist/commands/auth/index.js +19 -11
- package/dist/commands/mcp/server.js +2 -1
- package/dist/commands/storage/repo.d.ts +1 -1
- package/dist/commands/storage/repo.js +20 -1
- package/dist/config/paths.d.ts +2 -1
- package/dist/config/paths.js +11 -1
- package/dist/config/resolve.js +7 -5
- package/dist/powersync/runtime.d.ts +1 -1
- package/dist/powersync/runtime.js +5 -3
- package/package.json +1 -1
package/dist/auth/userInfo.d.ts
CHANGED
|
@@ -5,6 +5,8 @@ export type AuthIdentity = {
|
|
|
5
5
|
email: string | null;
|
|
6
6
|
syncedAt: string;
|
|
7
7
|
};
|
|
8
|
+
export declare function buildPendingAuthConfig(rawConfig: DinoxConfig, authorization: string): DinoxConfig;
|
|
9
|
+
export declare function mergeAuthIdentityIntoConfig(rawConfig: DinoxConfig, authorization: string, identity: AuthIdentity): DinoxConfig;
|
|
8
10
|
export declare function fetchUserInfoWithAuthorization(input: {
|
|
9
11
|
authorization: string;
|
|
10
12
|
config: ResolvedDinoxConfig;
|
package/dist/auth/userInfo.js
CHANGED
|
@@ -3,6 +3,33 @@ import { DinoxError } from '../utils/errors.js';
|
|
|
3
3
|
import { createTimeoutSignal, mergeAbortSignals } from '../utils/abort.js';
|
|
4
4
|
import { toAuthorizationHeaderValue } from '../utils/authToken.js';
|
|
5
5
|
const AUTH_CODE_OK = '000000';
|
|
6
|
+
export function buildPendingAuthConfig(rawConfig, authorization) {
|
|
7
|
+
return {
|
|
8
|
+
...rawConfig,
|
|
9
|
+
auth: {
|
|
10
|
+
...(rawConfig.auth ?? {}),
|
|
11
|
+
authorization,
|
|
12
|
+
userId: undefined,
|
|
13
|
+
username: undefined,
|
|
14
|
+
email: undefined,
|
|
15
|
+
userInfoSyncedAt: undefined,
|
|
16
|
+
},
|
|
17
|
+
};
|
|
18
|
+
}
|
|
19
|
+
export function mergeAuthIdentityIntoConfig(rawConfig, authorization, identity) {
|
|
20
|
+
const pendingConfig = buildPendingAuthConfig(rawConfig, authorization);
|
|
21
|
+
return {
|
|
22
|
+
...pendingConfig,
|
|
23
|
+
auth: {
|
|
24
|
+
...(pendingConfig.auth ?? {}),
|
|
25
|
+
authorization,
|
|
26
|
+
userId: identity.userId,
|
|
27
|
+
username: identity.username,
|
|
28
|
+
email: identity.email,
|
|
29
|
+
userInfoSyncedAt: identity.syncedAt,
|
|
30
|
+
},
|
|
31
|
+
};
|
|
32
|
+
}
|
|
6
33
|
function normalizeOptionalString(value) {
|
|
7
34
|
if (typeof value !== 'string') {
|
|
8
35
|
return undefined;
|
|
@@ -105,17 +132,7 @@ export async function refreshAndPersistAuthIdentity(rawConfig, resolvedConfig) {
|
|
|
105
132
|
authorization,
|
|
106
133
|
config: resolvedConfig,
|
|
107
134
|
});
|
|
108
|
-
const nextConfig =
|
|
109
|
-
...rawConfig,
|
|
110
|
-
auth: {
|
|
111
|
-
...(rawConfig.auth ?? {}),
|
|
112
|
-
authorization: rawConfig.auth?.authorization ?? authorization,
|
|
113
|
-
userId: identity.userId,
|
|
114
|
-
username: identity.username,
|
|
115
|
-
email: identity.email,
|
|
116
|
-
userInfoSyncedAt: identity.syncedAt,
|
|
117
|
-
},
|
|
118
|
-
};
|
|
135
|
+
const nextConfig = mergeAuthIdentityIntoConfig(rawConfig, authorization, identity);
|
|
119
136
|
await saveConfig(nextConfig);
|
|
120
137
|
return identity;
|
|
121
138
|
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import fs from 'node:fs/promises';
|
|
2
|
-
import {
|
|
3
|
-
import { getPowerSyncDbPath } from '../../config/paths.js';
|
|
2
|
+
import { buildPendingAuthConfig, fetchUserInfoWithAuthorization, mergeAuthIdentityIntoConfig, } from '../../auth/userInfo.js';
|
|
3
|
+
import { getLegacyPowerSyncDbPath, getPowerSyncDbPath } from '../../config/paths.js';
|
|
4
4
|
import { loadConfig, saveConfig } from '../../config/store.js';
|
|
5
5
|
import { resolveConfig } from '../../config/resolve.js';
|
|
6
6
|
import { fetchCredentialsWithAuthorization } from '../../powersync/connector.js';
|
|
@@ -26,10 +26,9 @@ async function runAuthLogin(context) {
|
|
|
26
26
|
if (!rawToken) {
|
|
27
27
|
throw new DinoxError('Token is required');
|
|
28
28
|
}
|
|
29
|
-
const
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
const resolved = resolveConfig(config);
|
|
29
|
+
const previousConfig = await loadConfig();
|
|
30
|
+
const pendingConfig = buildPendingAuthConfig(previousConfig, rawToken);
|
|
31
|
+
const resolved = resolveConfig(pendingConfig);
|
|
33
32
|
const timeoutMs = parseSyncTimeoutMs(context.globals.syncTimeout) ?? resolved.sync.timeoutMs;
|
|
34
33
|
const structuredOutput = getRequestedStructuredOutputFormat() !== null;
|
|
35
34
|
await fetchCredentialsWithAuthorization({
|
|
@@ -38,19 +37,26 @@ async function runAuthLogin(context) {
|
|
|
38
37
|
credentialsUrl: resolved.powersync.tokenEndpoint,
|
|
39
38
|
timeoutMs,
|
|
40
39
|
});
|
|
41
|
-
const identity = await
|
|
42
|
-
|
|
40
|
+
const identity = await fetchUserInfoWithAuthorization({
|
|
41
|
+
authorization: rawToken,
|
|
43
42
|
config: resolved,
|
|
43
|
+
timeoutMs,
|
|
44
|
+
});
|
|
45
|
+
const verifiedConfig = mergeAuthIdentityIntoConfig(previousConfig, rawToken, identity);
|
|
46
|
+
const verifiedResolved = resolveConfig(verifiedConfig);
|
|
47
|
+
const { db, dbPath, stale } = await connectPowerSync({
|
|
48
|
+
config: verifiedResolved,
|
|
44
49
|
offline: false,
|
|
45
50
|
timeoutMs,
|
|
46
51
|
});
|
|
47
52
|
try {
|
|
48
53
|
await runSingleSyncPass(db, timeoutMs);
|
|
54
|
+
await saveConfig(verifiedConfig);
|
|
49
55
|
const status = getStatusSnapshot(db);
|
|
50
56
|
const result = {
|
|
51
57
|
ok: true,
|
|
52
58
|
verified: true,
|
|
53
|
-
endpoint:
|
|
59
|
+
endpoint: verifiedResolved.powersync.endpoint,
|
|
54
60
|
dbPath,
|
|
55
61
|
stale,
|
|
56
62
|
lastSyncedAt: status.lastSyncedAt,
|
|
@@ -78,6 +84,7 @@ async function runAuthLogin(context) {
|
|
|
78
84
|
async function runAuthLogout(context) {
|
|
79
85
|
const options = context.options;
|
|
80
86
|
const config = await loadConfig();
|
|
87
|
+
const currentUserId = config.auth?.userId;
|
|
81
88
|
if (config.auth) {
|
|
82
89
|
config.auth.authorization = undefined;
|
|
83
90
|
config.auth.userId = undefined;
|
|
@@ -87,7 +94,8 @@ async function runAuthLogout(context) {
|
|
|
87
94
|
}
|
|
88
95
|
await saveConfig(config);
|
|
89
96
|
if (options.clearLocalDb) {
|
|
90
|
-
await removeSqliteArtifacts(getPowerSyncDbPath());
|
|
97
|
+
await removeSqliteArtifacts(getPowerSyncDbPath(currentUserId));
|
|
98
|
+
await removeSqliteArtifacts(getLegacyPowerSyncDbPath());
|
|
91
99
|
}
|
|
92
100
|
if (getRequestedStructuredOutputFormat()) {
|
|
93
101
|
printSuccess({
|
|
@@ -103,7 +111,7 @@ async function runAuthStatus(context) {
|
|
|
103
111
|
const offline = Boolean(context.globals.offline);
|
|
104
112
|
const structuredOutput = getRequestedStructuredOutputFormat() !== null;
|
|
105
113
|
const timeoutMs = parseSyncTimeoutMs(context.globals.syncTimeout) ?? resolved.sync.timeoutMs;
|
|
106
|
-
const dbPath = getPowerSyncDbPath();
|
|
114
|
+
const dbPath = getPowerSyncDbPath(resolved.auth.userId);
|
|
107
115
|
const base = {
|
|
108
116
|
loggedIn: Boolean(resolved.auth.authorization),
|
|
109
117
|
authorization: redactAuthorization(resolved.auth.authorization),
|
|
@@ -216,7 +216,8 @@ export class MCPServer {
|
|
|
216
216
|
case 'dinox_search_notes': {
|
|
217
217
|
const keyword = typeof args.query === 'string' ? args.query : '';
|
|
218
218
|
const tagsExpression = typeof args.tags === 'string' ? args.tags.trim() : '';
|
|
219
|
-
const
|
|
219
|
+
const rawBoxes = args.boxes ?? args.box;
|
|
220
|
+
const { names: boxNames, matchEmpty: matchEmptyBoxes } = parseBoxFilter(rawBoxes);
|
|
220
221
|
const zettelBoxIds = await resolveZettelBoxIdsForSearch(this.db, boxNames);
|
|
221
222
|
const daysInput = typeof args.days === 'number' ? String(args.days) : args.days;
|
|
222
223
|
const { createdAtFrom, createdAtTo } = parseCreatedAtRange({
|
|
@@ -151,7 +151,7 @@ export declare function inspectStorageTest(db: AbstractPowerSyncDatabase, input:
|
|
|
151
151
|
export declare function testStorageUpload(db: AbstractPowerSyncDatabase, input: {
|
|
152
152
|
userId: string;
|
|
153
153
|
storageId?: string;
|
|
154
|
-
}, deps?: Pick<UploadDeps, 'uploadBytes'>): Promise<StorageTestResult>;
|
|
154
|
+
}, deps?: Pick<UploadDeps, 'deleteObject' | 'uploadBytes'>): Promise<StorageTestResult>;
|
|
155
155
|
export declare function uploadFileToStorage(db: AbstractPowerSyncDatabase, input: {
|
|
156
156
|
userId: string;
|
|
157
157
|
filePath: string;
|
|
@@ -515,12 +515,13 @@ export async function getStorageStats(db, userId) {
|
|
|
515
515
|
COUNT(1) AS file_count
|
|
516
516
|
FROM c_resource
|
|
517
517
|
WHERE (is_del IS NULL OR is_del = 0)
|
|
518
|
+
AND TRIM(COALESCE(user_id, '')) = ?
|
|
518
519
|
AND TRIM(COALESCE(storage_provider, '')) <> ''
|
|
519
520
|
AND TRIM(COALESCE(storage_provider, '')) <> 'local'
|
|
520
521
|
AND TRIM(COALESCE(status, '')) = 'uploaded'
|
|
521
522
|
GROUP BY storage_provider, bucket
|
|
522
523
|
ORDER BY total_bytes DESC, provider ASC, bucket ASC
|
|
523
|
-
`, []);
|
|
524
|
+
`, [userId]);
|
|
524
525
|
const configs = await listStoredCustomStorageConfigs(db, userId);
|
|
525
526
|
const configsByKey = new Map();
|
|
526
527
|
const configsByProvider = new Map();
|
|
@@ -639,6 +640,7 @@ export async function testStorageUpload(db, input, deps) {
|
|
|
639
640
|
throw new DinoxError(`Storage config not found: ${plan.storage.id}`);
|
|
640
641
|
}
|
|
641
642
|
const uploadBytes = deps?.uploadBytes ?? defaultUploadBytes;
|
|
643
|
+
const deleteObject = deps?.deleteObject ?? defaultDeleteObject;
|
|
642
644
|
const payload = Buffer.from(`dinox-cli storage test\nprovider=${storage.provider}\nuser=${input.userId}\ncreated_at=${new Date().toISOString()}\n`, 'utf8');
|
|
643
645
|
await uploadBytes({
|
|
644
646
|
body: payload,
|
|
@@ -647,6 +649,23 @@ export async function testStorageUpload(db, input, deps) {
|
|
|
647
649
|
contentType: plan.contentType,
|
|
648
650
|
storage,
|
|
649
651
|
});
|
|
652
|
+
try {
|
|
653
|
+
await deleteObject({
|
|
654
|
+
bucket: storage.bucket,
|
|
655
|
+
key: plan.storageKey,
|
|
656
|
+
storage,
|
|
657
|
+
});
|
|
658
|
+
}
|
|
659
|
+
catch (error) {
|
|
660
|
+
throw new DinoxError('Storage test uploaded the temporary object but failed to clean it up.', {
|
|
661
|
+
cause: error,
|
|
662
|
+
code: 'STORAGE_TEST_CLEANUP_FAILED',
|
|
663
|
+
details: {
|
|
664
|
+
storageKey: plan.storageKey,
|
|
665
|
+
bucket: storage.bucket,
|
|
666
|
+
},
|
|
667
|
+
});
|
|
668
|
+
}
|
|
650
669
|
return {
|
|
651
670
|
storageConfigId: storage.id,
|
|
652
671
|
aliasName: storage.aliasName,
|
package/dist/config/paths.d.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
export declare function getConfigDir(): string;
|
|
2
2
|
export declare function getConfigFilePath(): string;
|
|
3
3
|
export declare function getDataDir(): string;
|
|
4
|
-
export declare function
|
|
4
|
+
export declare function getLegacyPowerSyncDbPath(): string;
|
|
5
|
+
export declare function getPowerSyncDbPath(userId?: string | null): string;
|
package/dist/config/paths.js
CHANGED
|
@@ -1,6 +1,9 @@
|
|
|
1
1
|
import os from 'node:os';
|
|
2
2
|
import path from 'node:path';
|
|
3
3
|
const POWERSYNC_DB_FILENAME = 'dinox-cli.sqlite';
|
|
4
|
+
function normalizeUserPathSegment(userId) {
|
|
5
|
+
return encodeURIComponent(userId.trim());
|
|
6
|
+
}
|
|
4
7
|
function resolveConfigHome() {
|
|
5
8
|
if (process.env.DINOX_CONFIG_DIR) {
|
|
6
9
|
return process.env.DINOX_CONFIG_DIR;
|
|
@@ -35,6 +38,13 @@ export function getConfigFilePath() {
|
|
|
35
38
|
export function getDataDir() {
|
|
36
39
|
return path.join(resolveDataHome(), 'dinox');
|
|
37
40
|
}
|
|
38
|
-
export function
|
|
41
|
+
export function getLegacyPowerSyncDbPath() {
|
|
39
42
|
return path.join(getDataDir(), POWERSYNC_DB_FILENAME);
|
|
40
43
|
}
|
|
44
|
+
export function getPowerSyncDbPath(userId) {
|
|
45
|
+
const normalizedUserId = typeof userId === 'string' ? userId.trim() : '';
|
|
46
|
+
if (!normalizedUserId) {
|
|
47
|
+
return getLegacyPowerSyncDbPath();
|
|
48
|
+
}
|
|
49
|
+
return path.join(getDataDir(), 'users', normalizeUserPathSegment(normalizedUserId), POWERSYNC_DB_FILENAME);
|
|
50
|
+
}
|
package/dist/config/resolve.js
CHANGED
|
@@ -35,13 +35,15 @@ export function resolveConfig(raw) {
|
|
|
35
35
|
const envUploadV4Path = nonEmptyString(process.env.DINOX_POWERSYNC_UPLOAD_V4_PATH);
|
|
36
36
|
const envUploadV2Path = nonEmptyString(process.env.DINOX_POWERSYNC_UPLOAD_V2_PATH);
|
|
37
37
|
const envTimeoutMs = parsePositiveInt(process.env.DINOX_SYNC_TIMEOUT_MS);
|
|
38
|
+
const effectiveAuth = envAuth ?? fileAuth;
|
|
39
|
+
const canReusePersistedIdentity = !envAuth || envAuth === fileAuth;
|
|
38
40
|
return {
|
|
39
41
|
auth: {
|
|
40
|
-
authorization:
|
|
41
|
-
userId: fileUserId,
|
|
42
|
-
username: fileUsername,
|
|
43
|
-
email: fileEmail,
|
|
44
|
-
userInfoSyncedAt: fileUserInfoSyncedAt,
|
|
42
|
+
authorization: effectiveAuth,
|
|
43
|
+
userId: canReusePersistedIdentity ? fileUserId : undefined,
|
|
44
|
+
username: canReusePersistedIdentity ? fileUsername : undefined,
|
|
45
|
+
email: canReusePersistedIdentity ? fileEmail : undefined,
|
|
46
|
+
userInfoSyncedAt: canReusePersistedIdentity ? fileUserInfoSyncedAt : undefined,
|
|
45
47
|
},
|
|
46
48
|
powersync: {
|
|
47
49
|
endpoint: POWERSYNC_DEFAULT_ENDPOINT,
|
|
@@ -25,7 +25,7 @@ export declare function getStatusSnapshot(db: AbstractPowerSyncDatabase): {
|
|
|
25
25
|
};
|
|
26
26
|
raw: SyncStatusOptions;
|
|
27
27
|
};
|
|
28
|
-
export declare function openPowerSyncDatabase(): Promise<{
|
|
28
|
+
export declare function openPowerSyncDatabase(userId?: string | null): Promise<{
|
|
29
29
|
db: PowerSyncDatabase;
|
|
30
30
|
dbPath: string;
|
|
31
31
|
}>;
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import fs from 'node:fs/promises';
|
|
2
|
+
import path from 'node:path';
|
|
2
3
|
import { PowerSyncDatabase } from '@powersync/node';
|
|
3
4
|
import { getDataDir, getPowerSyncDbPath } from '../config/paths.js';
|
|
4
5
|
import { DinoxError } from '../utils/errors.js';
|
|
@@ -33,9 +34,10 @@ export function getStatusSnapshot(db) {
|
|
|
33
34
|
raw,
|
|
34
35
|
};
|
|
35
36
|
}
|
|
36
|
-
export async function openPowerSyncDatabase() {
|
|
37
|
+
export async function openPowerSyncDatabase(userId) {
|
|
37
38
|
await ensureDataDir();
|
|
38
|
-
const dbPath = getPowerSyncDbPath();
|
|
39
|
+
const dbPath = getPowerSyncDbPath(userId);
|
|
40
|
+
await fs.mkdir(path.dirname(dbPath), { recursive: true, mode: 0o700 });
|
|
39
41
|
const db = new PowerSyncDatabase({
|
|
40
42
|
schema: AppSchema,
|
|
41
43
|
database: {
|
|
@@ -52,7 +54,7 @@ function assertConfigured(config) {
|
|
|
52
54
|
}
|
|
53
55
|
}
|
|
54
56
|
export async function connectPowerSync(args) {
|
|
55
|
-
const { db, dbPath } = await openPowerSyncDatabase();
|
|
57
|
+
const { db, dbPath } = await openPowerSyncDatabase(args.config.auth.userId);
|
|
56
58
|
if (args.offline) {
|
|
57
59
|
return { db, dbPath, stale: true };
|
|
58
60
|
}
|