@dinoxx/dinox-cli 1.0.18 → 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
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { authCommandGroup } from '../auth/index.js';
|
|
2
|
+
import { boxCommandGroup } from '../boxes/index.js';
|
|
3
|
+
import { commandSchemasFromGroupDescriptor } from '../descriptor/register.js';
|
|
4
|
+
import { standaloneCommandSchemaFromDescriptor } from '../descriptor/register.js';
|
|
5
|
+
import { configCommandGroup } from '../config/index.js';
|
|
6
|
+
import { graphCommandGroup } from '../graph/index.js';
|
|
7
|
+
import { infoCommandDescriptor } from '../info/index.js';
|
|
8
|
+
import { noteCommandGroup } from '../notes/index.js';
|
|
9
|
+
import { promptCommandGroup } from '../prompt/index.js';
|
|
10
|
+
import { schemaCommandMetadata } from './commandMetadata.js';
|
|
11
|
+
import { syncCommandDescriptor } from '../sync.js';
|
|
12
|
+
import { storageCommandGroup } from '../storage/index.js';
|
|
13
|
+
import { tagCommandGroup } from '../tags/index.js';
|
|
14
|
+
import { todoCommandGroup } from '../todo/index.js';
|
|
15
|
+
import { updateCommandDescriptor } from '../update.js';
|
|
16
|
+
export const COMMAND_SCHEMAS = [
|
|
17
|
+
...commandSchemasFromGroupDescriptor(authCommandGroup),
|
|
18
|
+
...commandSchemasFromGroupDescriptor(configCommandGroup),
|
|
19
|
+
standaloneCommandSchemaFromDescriptor(syncCommandDescriptor),
|
|
20
|
+
standaloneCommandSchemaFromDescriptor({ ...schemaCommandMetadata, action: async () => undefined }),
|
|
21
|
+
standaloneCommandSchemaFromDescriptor(infoCommandDescriptor),
|
|
22
|
+
standaloneCommandSchemaFromDescriptor(updateCommandDescriptor),
|
|
23
|
+
...commandSchemasFromGroupDescriptor(tagCommandGroup),
|
|
24
|
+
...commandSchemasFromGroupDescriptor(boxCommandGroup),
|
|
25
|
+
...commandSchemasFromGroupDescriptor(promptCommandGroup),
|
|
26
|
+
...commandSchemasFromGroupDescriptor(storageCommandGroup),
|
|
27
|
+
...commandSchemasFromGroupDescriptor(noteCommandGroup),
|
|
28
|
+
...commandSchemasFromGroupDescriptor(graphCommandGroup),
|
|
29
|
+
...commandSchemasFromGroupDescriptor(todoCommandGroup),
|
|
30
|
+
];
|
|
31
|
+
const COMMAND_SCHEMA_MAP = new Map(COMMAND_SCHEMAS.map((schema) => [schema.path, schema]));
|
|
32
|
+
export function listCommandSchemas() {
|
|
33
|
+
return [...COMMAND_SCHEMAS];
|
|
34
|
+
}
|
|
35
|
+
export function findCommandSchema(path) {
|
|
36
|
+
return COMMAND_SCHEMA_MAP.get(path) ?? null;
|
|
37
|
+
}
|
|
38
|
+
export function listMcpCommandSchemas() {
|
|
39
|
+
return COMMAND_SCHEMAS.filter((schema) => schema.mcp);
|
|
40
|
+
}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
export type CommandSchemaRisk = 'read' | 'write' | 'high-risk-write';
|
|
2
|
+
export type CommandSchemaAuth = 'none' | 'optional' | 'required';
|
|
3
|
+
export type CommandSchemaArgument = {
|
|
4
|
+
name: string;
|
|
5
|
+
required: boolean;
|
|
6
|
+
description: string;
|
|
7
|
+
};
|
|
8
|
+
export type CommandSchemaOption = {
|
|
9
|
+
name: string;
|
|
10
|
+
description: string;
|
|
11
|
+
required?: boolean;
|
|
12
|
+
type?: 'string' | 'number' | 'boolean' | 'string[]';
|
|
13
|
+
defaultValue?: string | number | boolean | string[];
|
|
14
|
+
enum?: string[];
|
|
15
|
+
};
|
|
16
|
+
export type CommandSchemaOutput = {
|
|
17
|
+
type: 'structured';
|
|
18
|
+
summary: string;
|
|
19
|
+
entity?: string;
|
|
20
|
+
fields?: string[];
|
|
21
|
+
metaFields?: string[];
|
|
22
|
+
};
|
|
23
|
+
export type CommandSchemaMcpProperty = {
|
|
24
|
+
name: string;
|
|
25
|
+
type: 'string' | 'number' | 'boolean' | 'string[]';
|
|
26
|
+
description: string;
|
|
27
|
+
required?: boolean;
|
|
28
|
+
enum?: string[];
|
|
29
|
+
};
|
|
30
|
+
export type CommandSchemaMcp = {
|
|
31
|
+
toolName: string;
|
|
32
|
+
description: string;
|
|
33
|
+
properties: CommandSchemaMcpProperty[];
|
|
34
|
+
};
|
|
35
|
+
export type CommandSchema = {
|
|
36
|
+
path: string;
|
|
37
|
+
command: string;
|
|
38
|
+
summary: string;
|
|
39
|
+
risk: CommandSchemaRisk;
|
|
40
|
+
auth: CommandSchemaAuth;
|
|
41
|
+
globalOptions: string[];
|
|
42
|
+
arguments?: CommandSchemaArgument[];
|
|
43
|
+
options?: CommandSchemaOption[];
|
|
44
|
+
output: CommandSchemaOutput;
|
|
45
|
+
mcp?: CommandSchemaMcp;
|
|
46
|
+
examples: string[];
|
|
47
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,292 @@
|
|
|
1
|
+
import { resolveAuthIdentity } from '../../auth/userInfo.js';
|
|
2
|
+
import { loadConfig } from '../../config/store.js';
|
|
3
|
+
import { resolveConfig } from '../../config/resolve.js';
|
|
4
|
+
import { connectPowerSync, waitForIdleDataFlow } from '../../powersync/runtime.js';
|
|
5
|
+
import { registerCommandGroup } from '../descriptor/register.js';
|
|
6
|
+
import { FORMAT_OPTIONS, OFFLINE_OPTIONS, SYNC_TIMEOUT_OPTIONS } from '../schema/globalOptions.js';
|
|
7
|
+
import { buildStorageTestDryRunPreview, buildStorageUploadDryRunPreview } from '../writeDryRun.js';
|
|
8
|
+
import { printDryRunPreview } from '../../utils/dryRun.js';
|
|
9
|
+
import { DinoxError } from '../../utils/errors.js';
|
|
10
|
+
import { getRequestedStructuredOutputFormat, printHumanReadableLine, printSuccess, } from '../../utils/output.js';
|
|
11
|
+
import { parseSyncTimeoutMs } from '../../utils/syncTimeout.js';
|
|
12
|
+
import { getStorageStats, inspectStorageTest, inspectStorageUpload, listStorageConfigs, parseStorageUploadCategory, testStorageUpload, uploadFileToStorage, } from './repo.js';
|
|
13
|
+
const STORAGE_GLOBAL_OPTIONS = [...FORMAT_OPTIONS, ...OFFLINE_OPTIONS, ...SYNC_TIMEOUT_OPTIONS];
|
|
14
|
+
async function syncBeforeStorageCommand(db, offline, timeoutMs) {
|
|
15
|
+
if (offline) {
|
|
16
|
+
return;
|
|
17
|
+
}
|
|
18
|
+
await waitForIdleDataFlow(db, Math.min(timeoutMs, 10_000));
|
|
19
|
+
}
|
|
20
|
+
async function withStorageDb(context, run) {
|
|
21
|
+
const rawConfig = await loadConfig();
|
|
22
|
+
const config = resolveConfig(rawConfig);
|
|
23
|
+
const timeoutMs = parseSyncTimeoutMs(context.globals.syncTimeout) ?? config.sync.timeoutMs;
|
|
24
|
+
const offline = Boolean(context.globals.offline);
|
|
25
|
+
const { db, stale } = await connectPowerSync({ config, offline, timeoutMs });
|
|
26
|
+
try {
|
|
27
|
+
await run({ db, config, rawConfig, offline, timeoutMs, stale });
|
|
28
|
+
}
|
|
29
|
+
finally {
|
|
30
|
+
await db.close().catch(() => undefined);
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
async function runStorageList(context) {
|
|
34
|
+
await withStorageDb(context, async ({ db, config, rawConfig, offline, timeoutMs, stale }) => {
|
|
35
|
+
const identity = await resolveAuthIdentity(rawConfig, config);
|
|
36
|
+
await syncBeforeStorageCommand(db, offline, timeoutMs);
|
|
37
|
+
printSuccess({
|
|
38
|
+
stale: offline ? true : stale,
|
|
39
|
+
...(await listStorageConfigs(db, identity.userId)),
|
|
40
|
+
});
|
|
41
|
+
});
|
|
42
|
+
}
|
|
43
|
+
async function runStorageUpload(context) {
|
|
44
|
+
const filePath = context.args.file;
|
|
45
|
+
if (!filePath) {
|
|
46
|
+
throw new DinoxError('File path is required. Use `dino storage upload <file>`.');
|
|
47
|
+
}
|
|
48
|
+
const options = context.options;
|
|
49
|
+
const category = parseStorageUploadCategory(options.category ?? 'files');
|
|
50
|
+
const structuredOutput = getRequestedStructuredOutputFormat() !== null;
|
|
51
|
+
await withStorageDb(context, async ({ db, config, rawConfig, offline, timeoutMs, stale }) => {
|
|
52
|
+
const identity = await resolveAuthIdentity(rawConfig, config);
|
|
53
|
+
await syncBeforeStorageCommand(db, offline, timeoutMs);
|
|
54
|
+
const plan = await inspectStorageUpload(db, {
|
|
55
|
+
userId: identity.userId,
|
|
56
|
+
filePath,
|
|
57
|
+
category,
|
|
58
|
+
storageId: options.storageId,
|
|
59
|
+
key: options.key,
|
|
60
|
+
});
|
|
61
|
+
if (options.dryRun) {
|
|
62
|
+
printDryRunPreview(buildStorageUploadDryRunPreview({
|
|
63
|
+
config,
|
|
64
|
+
offline,
|
|
65
|
+
filePath: plan.filePath,
|
|
66
|
+
fileName: plan.fileName,
|
|
67
|
+
sizeBytes: plan.sizeBytes,
|
|
68
|
+
category: plan.category,
|
|
69
|
+
storage: plan.storage,
|
|
70
|
+
storageKey: plan.storageKey,
|
|
71
|
+
storageUrl: plan.storageUrl,
|
|
72
|
+
selectionSource: plan.selectionSource,
|
|
73
|
+
}));
|
|
74
|
+
return;
|
|
75
|
+
}
|
|
76
|
+
const result = await uploadFileToStorage(db, {
|
|
77
|
+
userId: identity.userId,
|
|
78
|
+
filePath,
|
|
79
|
+
category,
|
|
80
|
+
storageId: options.storageId,
|
|
81
|
+
key: options.key,
|
|
82
|
+
});
|
|
83
|
+
if (!offline && !config.powersync.uploadBaseUrl) {
|
|
84
|
+
printHumanReadableLine('warning: upload disabled (powersync.uploadBaseUrl is unset); resource metadata is local-only for now');
|
|
85
|
+
}
|
|
86
|
+
if (!offline) {
|
|
87
|
+
await waitForIdleDataFlow(db, Math.min(timeoutMs, 5_000));
|
|
88
|
+
}
|
|
89
|
+
const payload = {
|
|
90
|
+
ok: true,
|
|
91
|
+
stale: offline ? true : stale,
|
|
92
|
+
...result,
|
|
93
|
+
};
|
|
94
|
+
if (structuredOutput) {
|
|
95
|
+
printSuccess(payload);
|
|
96
|
+
return;
|
|
97
|
+
}
|
|
98
|
+
printHumanReadableLine(`Uploaded ${result.fileName}`);
|
|
99
|
+
printHumanReadableLine(`- resourceId: ${result.resourceId}`);
|
|
100
|
+
printHumanReadableLine(`- storage: ${result.aliasName || result.storageConfigId}`);
|
|
101
|
+
printHumanReadableLine(`- bucket: ${result.bucket}`);
|
|
102
|
+
printHumanReadableLine(`- key: ${result.storageKey}`);
|
|
103
|
+
printHumanReadableLine(`- url: ${result.storageUrl}`);
|
|
104
|
+
if (result.thumbnailUrl) {
|
|
105
|
+
printHumanReadableLine(`- thumbnail: ${result.thumbnailUrl}`);
|
|
106
|
+
}
|
|
107
|
+
if (stale && !offline) {
|
|
108
|
+
printHumanReadableLine('warning: sync timed out; local cache may be stale');
|
|
109
|
+
}
|
|
110
|
+
});
|
|
111
|
+
}
|
|
112
|
+
function formatBytes(bytes) {
|
|
113
|
+
if (!Number.isFinite(bytes) || bytes <= 0)
|
|
114
|
+
return '0 B';
|
|
115
|
+
if (bytes < 1024)
|
|
116
|
+
return `${bytes} B`;
|
|
117
|
+
if (bytes < 1024 * 1024)
|
|
118
|
+
return `${(bytes / 1024).toFixed(1)} KB`;
|
|
119
|
+
if (bytes < 1024 * 1024 * 1024)
|
|
120
|
+
return `${(bytes / (1024 * 1024)).toFixed(1)} MB`;
|
|
121
|
+
return `${(bytes / (1024 * 1024 * 1024)).toFixed(2)} GB`;
|
|
122
|
+
}
|
|
123
|
+
async function runStorageTest(context) {
|
|
124
|
+
const options = context.options;
|
|
125
|
+
const structuredOutput = getRequestedStructuredOutputFormat() !== null;
|
|
126
|
+
await withStorageDb(context, async ({ db, config, rawConfig, offline, timeoutMs, stale }) => {
|
|
127
|
+
const identity = await resolveAuthIdentity(rawConfig, config);
|
|
128
|
+
await syncBeforeStorageCommand(db, offline, timeoutMs);
|
|
129
|
+
const plan = await inspectStorageTest(db, {
|
|
130
|
+
userId: identity.userId,
|
|
131
|
+
storageId: options.storageId,
|
|
132
|
+
});
|
|
133
|
+
if (options.dryRun) {
|
|
134
|
+
printDryRunPreview(buildStorageTestDryRunPreview({
|
|
135
|
+
config,
|
|
136
|
+
offline,
|
|
137
|
+
storage: plan.storage,
|
|
138
|
+
storageKey: plan.storageKey,
|
|
139
|
+
storageUrl: plan.storageUrl,
|
|
140
|
+
sizeBytes: plan.sizeBytes,
|
|
141
|
+
selectionSource: plan.selectionSource,
|
|
142
|
+
}));
|
|
143
|
+
return;
|
|
144
|
+
}
|
|
145
|
+
const result = await testStorageUpload(db, {
|
|
146
|
+
userId: identity.userId,
|
|
147
|
+
storageId: options.storageId,
|
|
148
|
+
});
|
|
149
|
+
const payload = {
|
|
150
|
+
ok: true,
|
|
151
|
+
stale: offline ? true : stale,
|
|
152
|
+
...result,
|
|
153
|
+
};
|
|
154
|
+
if (structuredOutput) {
|
|
155
|
+
printSuccess(payload);
|
|
156
|
+
return;
|
|
157
|
+
}
|
|
158
|
+
printHumanReadableLine(`Storage test succeeded for ${result.aliasName || result.storageConfigId}`);
|
|
159
|
+
printHumanReadableLine(`- bucket: ${result.bucket}`);
|
|
160
|
+
printHumanReadableLine(`- key: ${result.storageKey}`);
|
|
161
|
+
printHumanReadableLine(`- url: ${result.storageUrl}`);
|
|
162
|
+
if (result.isPrivate) {
|
|
163
|
+
printHumanReadableLine('- note: the target bucket is private; direct reads may still require signed access.');
|
|
164
|
+
}
|
|
165
|
+
if (stale && !offline) {
|
|
166
|
+
printHumanReadableLine('warning: sync timed out; local cache may be stale');
|
|
167
|
+
}
|
|
168
|
+
});
|
|
169
|
+
}
|
|
170
|
+
async function runStorageStats(context) {
|
|
171
|
+
const structuredOutput = getRequestedStructuredOutputFormat() !== null;
|
|
172
|
+
await withStorageDb(context, async ({ db, config, rawConfig, offline, timeoutMs, stale }) => {
|
|
173
|
+
const identity = await resolveAuthIdentity(rawConfig, config);
|
|
174
|
+
await syncBeforeStorageCommand(db, offline, timeoutMs);
|
|
175
|
+
const stats = await getStorageStats(db, identity.userId);
|
|
176
|
+
const payload = {
|
|
177
|
+
ok: true,
|
|
178
|
+
stale: offline ? true : stale,
|
|
179
|
+
...stats,
|
|
180
|
+
};
|
|
181
|
+
if (structuredOutput) {
|
|
182
|
+
printSuccess(payload);
|
|
183
|
+
return;
|
|
184
|
+
}
|
|
185
|
+
printHumanReadableLine(`Storage usage: ${formatBytes(stats.totalBytes)} across ${stats.totalFiles} files`);
|
|
186
|
+
if (stats.entries.length === 0) {
|
|
187
|
+
printHumanReadableLine('No uploaded storage resources found.');
|
|
188
|
+
}
|
|
189
|
+
else {
|
|
190
|
+
for (const entry of stats.entries) {
|
|
191
|
+
printHumanReadableLine(`- ${entry.label}: ${entry.fileCount} files, ${formatBytes(entry.totalBytes)}`);
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
if (stale && !offline) {
|
|
195
|
+
printHumanReadableLine('warning: sync timed out; local cache may be stale');
|
|
196
|
+
}
|
|
197
|
+
});
|
|
198
|
+
}
|
|
199
|
+
export const storageCommandGroup = {
|
|
200
|
+
name: 'storage',
|
|
201
|
+
description: 'Inspect custom storage configs, test connectivity, upload files, and inspect usage',
|
|
202
|
+
commands: [
|
|
203
|
+
{
|
|
204
|
+
name: 'list',
|
|
205
|
+
description: 'List custom storage configs from c_storage and mark the active one',
|
|
206
|
+
schema: {
|
|
207
|
+
path: 'storage.list',
|
|
208
|
+
risk: 'read',
|
|
209
|
+
auth: 'required',
|
|
210
|
+
globalOptions: STORAGE_GLOBAL_OPTIONS,
|
|
211
|
+
output: {
|
|
212
|
+
type: 'structured',
|
|
213
|
+
summary: 'Custom storage configs and active selection payload',
|
|
214
|
+
fields: ['activeCustomStorageConfigId', 'usesOfficialStorage', 'items'],
|
|
215
|
+
},
|
|
216
|
+
examples: ['dino storage list --format json'],
|
|
217
|
+
},
|
|
218
|
+
action: runStorageList,
|
|
219
|
+
},
|
|
220
|
+
{
|
|
221
|
+
name: 'test',
|
|
222
|
+
description: 'Upload a tiny temporary object to a custom S3 storage target without persisting a c_resource row',
|
|
223
|
+
options: [
|
|
224
|
+
{ flags: '--storage-id <id>', description: 'Explicit storage config id (otherwise use active custom config)', type: 'string' },
|
|
225
|
+
{ flags: '--dry-run', description: 'Preview the test object target without uploading', type: 'boolean' },
|
|
226
|
+
],
|
|
227
|
+
schema: {
|
|
228
|
+
path: 'storage.test',
|
|
229
|
+
risk: 'write',
|
|
230
|
+
auth: 'required',
|
|
231
|
+
globalOptions: STORAGE_GLOBAL_OPTIONS,
|
|
232
|
+
output: {
|
|
233
|
+
type: 'structured',
|
|
234
|
+
summary: 'Storage connectivity test result',
|
|
235
|
+
fields: ['storageConfigId', 'storageKey', 'storageUrl', 'provider', 'bucket', 'contentType', 'sizeBytes', 'stale'],
|
|
236
|
+
},
|
|
237
|
+
examples: [
|
|
238
|
+
'dino storage test --format json --dry-run',
|
|
239
|
+
'dino storage test --storage-id <id> --format json',
|
|
240
|
+
],
|
|
241
|
+
},
|
|
242
|
+
action: runStorageTest,
|
|
243
|
+
},
|
|
244
|
+
{
|
|
245
|
+
name: 'upload',
|
|
246
|
+
description: 'Upload one local file to a custom S3 storage target and persist a c_resource row',
|
|
247
|
+
arguments: [{ syntax: '<file>', description: 'Local file path' }],
|
|
248
|
+
options: [
|
|
249
|
+
{ flags: '--storage-id <id>', description: 'Explicit storage config id (otherwise use active custom config)', type: 'string' },
|
|
250
|
+
{ flags: '--category <kind>', description: 'Upload category: images|audios|files|videos', type: 'string', defaultValue: 'files', enum: ['images', 'audios', 'files', 'videos'] },
|
|
251
|
+
{ flags: '--key <string>', description: 'Explicit object key override', type: 'string' },
|
|
252
|
+
{ flags: '--dry-run', description: 'Preview the upload target and resource record without uploading', type: 'boolean' },
|
|
253
|
+
],
|
|
254
|
+
schema: {
|
|
255
|
+
path: 'storage.upload',
|
|
256
|
+
risk: 'write',
|
|
257
|
+
auth: 'required',
|
|
258
|
+
globalOptions: STORAGE_GLOBAL_OPTIONS,
|
|
259
|
+
output: {
|
|
260
|
+
type: 'structured',
|
|
261
|
+
summary: 'Storage upload acknowledgement',
|
|
262
|
+
fields: ['resourceId', 'storageConfigId', 'storageKey', 'storageUrl', 'thumbnailUrl', 'provider', 'bucket', 'contentType', 'etag', 'stale'],
|
|
263
|
+
},
|
|
264
|
+
examples: [
|
|
265
|
+
'dino storage upload ./report.pdf --format json --dry-run',
|
|
266
|
+
'dino storage upload ./photo.jpg --category images --storage-id <id> --format json',
|
|
267
|
+
],
|
|
268
|
+
},
|
|
269
|
+
action: runStorageUpload,
|
|
270
|
+
},
|
|
271
|
+
{
|
|
272
|
+
name: 'stats',
|
|
273
|
+
description: 'Summarize uploaded storage usage grouped by provider and bucket',
|
|
274
|
+
schema: {
|
|
275
|
+
path: 'storage.stats',
|
|
276
|
+
risk: 'read',
|
|
277
|
+
auth: 'required',
|
|
278
|
+
globalOptions: STORAGE_GLOBAL_OPTIONS,
|
|
279
|
+
output: {
|
|
280
|
+
type: 'structured',
|
|
281
|
+
summary: 'Aggregated storage usage payload',
|
|
282
|
+
fields: ['totalBytes', 'totalFiles', 'entries', 'stale'],
|
|
283
|
+
},
|
|
284
|
+
examples: ['dino storage stats --format json'],
|
|
285
|
+
},
|
|
286
|
+
action: runStorageStats,
|
|
287
|
+
},
|
|
288
|
+
],
|
|
289
|
+
};
|
|
290
|
+
export function registerStorageCommands(program) {
|
|
291
|
+
registerCommandGroup(program, storageCommandGroup);
|
|
292
|
+
}
|
|
@@ -0,0 +1,162 @@
|
|
|
1
|
+
import fsp from 'node:fs/promises';
|
|
2
|
+
import type { AbstractPowerSyncDatabase } from '@powersync/common';
|
|
3
|
+
declare const UPLOAD_CATEGORY_VALUES: readonly ["images", "audios", "files", "videos"];
|
|
4
|
+
export type StorageUploadCategory = (typeof UPLOAD_CATEGORY_VALUES)[number];
|
|
5
|
+
export type StorageListItem = {
|
|
6
|
+
id: string;
|
|
7
|
+
aliasName: string;
|
|
8
|
+
provider: string;
|
|
9
|
+
endpoint: string;
|
|
10
|
+
bucket: string;
|
|
11
|
+
region: string;
|
|
12
|
+
forcePathStyle: boolean;
|
|
13
|
+
pathPrefix: string;
|
|
14
|
+
customDomain: string;
|
|
15
|
+
legacyDomains: string[];
|
|
16
|
+
isPrivate: boolean;
|
|
17
|
+
hasAccessKeyId: boolean;
|
|
18
|
+
hasSecretAccessKey: boolean;
|
|
19
|
+
isActive: boolean;
|
|
20
|
+
};
|
|
21
|
+
export type StorageListResult = {
|
|
22
|
+
activeCustomStorageConfigId: string | null;
|
|
23
|
+
usesOfficialStorage: boolean;
|
|
24
|
+
items: StorageListItem[];
|
|
25
|
+
};
|
|
26
|
+
type StoredCustomStorageConfig = {
|
|
27
|
+
id: string;
|
|
28
|
+
aliasName: string;
|
|
29
|
+
provider: string;
|
|
30
|
+
endpoint: string;
|
|
31
|
+
bucket: string;
|
|
32
|
+
region: string;
|
|
33
|
+
accessKeyId: string;
|
|
34
|
+
secretAccessKey: string;
|
|
35
|
+
forcePathStyle: boolean;
|
|
36
|
+
pathPrefix: string;
|
|
37
|
+
customDomain: string;
|
|
38
|
+
legacyDomains: string[];
|
|
39
|
+
isPrivate: boolean;
|
|
40
|
+
};
|
|
41
|
+
export type StorageUploadPlan = {
|
|
42
|
+
storage: StorageListItem;
|
|
43
|
+
selectionSource: 'storage_id' | 'active' | 'single_available_config';
|
|
44
|
+
filePath: string;
|
|
45
|
+
fileName: string;
|
|
46
|
+
sizeBytes: number;
|
|
47
|
+
category: StorageUploadCategory;
|
|
48
|
+
contentType: string;
|
|
49
|
+
storageKey: string;
|
|
50
|
+
storageUrl: string;
|
|
51
|
+
domain: string;
|
|
52
|
+
};
|
|
53
|
+
export type StorageUploadResult = {
|
|
54
|
+
resourceId: string;
|
|
55
|
+
storageConfigId: string;
|
|
56
|
+
aliasName: string;
|
|
57
|
+
provider: string;
|
|
58
|
+
bucket: string;
|
|
59
|
+
endpoint: string;
|
|
60
|
+
domain: string;
|
|
61
|
+
storageKey: string;
|
|
62
|
+
storageUrl: string;
|
|
63
|
+
fileName: string;
|
|
64
|
+
sizeBytes: number;
|
|
65
|
+
contentType: string;
|
|
66
|
+
category: StorageUploadCategory;
|
|
67
|
+
etag: string | null;
|
|
68
|
+
thumbnailUrl: string | null;
|
|
69
|
+
isPrivate: boolean;
|
|
70
|
+
selectionSource: StorageUploadPlan['selectionSource'];
|
|
71
|
+
};
|
|
72
|
+
export type StorageTestPlan = {
|
|
73
|
+
storage: StorageListItem;
|
|
74
|
+
selectionSource: StorageUploadPlan['selectionSource'];
|
|
75
|
+
contentType: string;
|
|
76
|
+
sizeBytes: number;
|
|
77
|
+
storageKey: string;
|
|
78
|
+
storageUrl: string;
|
|
79
|
+
domain: string;
|
|
80
|
+
};
|
|
81
|
+
export type StorageTestResult = {
|
|
82
|
+
storageConfigId: string;
|
|
83
|
+
aliasName: string;
|
|
84
|
+
provider: string;
|
|
85
|
+
bucket: string;
|
|
86
|
+
endpoint: string;
|
|
87
|
+
domain: string;
|
|
88
|
+
storageKey: string;
|
|
89
|
+
storageUrl: string;
|
|
90
|
+
contentType: string;
|
|
91
|
+
sizeBytes: number;
|
|
92
|
+
selectionSource: StorageUploadPlan['selectionSource'];
|
|
93
|
+
isPrivate: boolean;
|
|
94
|
+
};
|
|
95
|
+
export type StorageStatsEntry = {
|
|
96
|
+
provider: string;
|
|
97
|
+
bucket: string;
|
|
98
|
+
label: string;
|
|
99
|
+
totalBytes: number;
|
|
100
|
+
fileCount: number;
|
|
101
|
+
storageConfigId: string | null;
|
|
102
|
+
aliasName: string | null;
|
|
103
|
+
isOfficial: boolean;
|
|
104
|
+
};
|
|
105
|
+
export type StorageStatsResult = {
|
|
106
|
+
totalBytes: number;
|
|
107
|
+
totalFiles: number;
|
|
108
|
+
entries: StorageStatsEntry[];
|
|
109
|
+
};
|
|
110
|
+
type UploadObjectInput = {
|
|
111
|
+
localPath: string;
|
|
112
|
+
bucket: string;
|
|
113
|
+
key: string;
|
|
114
|
+
contentType: string;
|
|
115
|
+
contentLength: number;
|
|
116
|
+
storage: StoredCustomStorageConfig;
|
|
117
|
+
};
|
|
118
|
+
type UploadObjectResult = {
|
|
119
|
+
etag: string | null;
|
|
120
|
+
};
|
|
121
|
+
type UploadDeps = {
|
|
122
|
+
statFile?: typeof fsp.stat;
|
|
123
|
+
uploadObject?: (input: UploadObjectInput) => Promise<UploadObjectResult>;
|
|
124
|
+
deleteObject?: (input: {
|
|
125
|
+
bucket: string;
|
|
126
|
+
key: string;
|
|
127
|
+
storage: StoredCustomStorageConfig;
|
|
128
|
+
}) => Promise<void>;
|
|
129
|
+
uploadBytes?: (input: {
|
|
130
|
+
body: Buffer;
|
|
131
|
+
bucket: string;
|
|
132
|
+
key: string;
|
|
133
|
+
contentType: string;
|
|
134
|
+
storage: StoredCustomStorageConfig;
|
|
135
|
+
}) => Promise<UploadObjectResult>;
|
|
136
|
+
};
|
|
137
|
+
export declare function parseStorageUploadCategory(value: unknown): StorageUploadCategory;
|
|
138
|
+
export declare function listStorageConfigs(db: AbstractPowerSyncDatabase, userId: string): Promise<StorageListResult>;
|
|
139
|
+
export declare function getStorageStats(db: AbstractPowerSyncDatabase, userId: string): Promise<StorageStatsResult>;
|
|
140
|
+
export declare function inspectStorageUpload(db: AbstractPowerSyncDatabase, input: {
|
|
141
|
+
userId: string;
|
|
142
|
+
filePath: string;
|
|
143
|
+
category: StorageUploadCategory;
|
|
144
|
+
storageId?: string;
|
|
145
|
+
key?: string;
|
|
146
|
+
}, deps?: Pick<UploadDeps, 'statFile'>): Promise<StorageUploadPlan>;
|
|
147
|
+
export declare function inspectStorageTest(db: AbstractPowerSyncDatabase, input: {
|
|
148
|
+
userId: string;
|
|
149
|
+
storageId?: string;
|
|
150
|
+
}): Promise<StorageTestPlan>;
|
|
151
|
+
export declare function testStorageUpload(db: AbstractPowerSyncDatabase, input: {
|
|
152
|
+
userId: string;
|
|
153
|
+
storageId?: string;
|
|
154
|
+
}, deps?: Pick<UploadDeps, 'uploadBytes'>): Promise<StorageTestResult>;
|
|
155
|
+
export declare function uploadFileToStorage(db: AbstractPowerSyncDatabase, input: {
|
|
156
|
+
userId: string;
|
|
157
|
+
filePath: string;
|
|
158
|
+
category: StorageUploadCategory;
|
|
159
|
+
storageId?: string;
|
|
160
|
+
key?: string;
|
|
161
|
+
}, deps?: UploadDeps): Promise<StorageUploadResult>;
|
|
162
|
+
export {};
|