@daileyos/cli 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (70) hide show
  1. package/dist/api.d.ts +12 -0
  2. package/dist/api.js +98 -0
  3. package/dist/api.js.map +1 -0
  4. package/dist/auth.d.ts +10 -0
  5. package/dist/auth.js +40 -0
  6. package/dist/auth.js.map +1 -0
  7. package/dist/commands/auth.d.ts +2 -0
  8. package/dist/commands/auth.js +106 -0
  9. package/dist/commands/auth.js.map +1 -0
  10. package/dist/commands/billing.d.ts +2 -0
  11. package/dist/commands/billing.js +58 -0
  12. package/dist/commands/billing.js.map +1 -0
  13. package/dist/commands/db.d.ts +2 -0
  14. package/dist/commands/db.js +191 -0
  15. package/dist/commands/db.js.map +1 -0
  16. package/dist/commands/deploy.d.ts +2 -0
  17. package/dist/commands/deploy.js +72 -0
  18. package/dist/commands/deploy.js.map +1 -0
  19. package/dist/commands/domains.d.ts +2 -0
  20. package/dist/commands/domains.js +48 -0
  21. package/dist/commands/domains.js.map +1 -0
  22. package/dist/commands/env.d.ts +2 -0
  23. package/dist/commands/env.js +54 -0
  24. package/dist/commands/env.js.map +1 -0
  25. package/dist/commands/jobs.d.ts +2 -0
  26. package/dist/commands/jobs.js +160 -0
  27. package/dist/commands/jobs.js.map +1 -0
  28. package/dist/commands/open.d.ts +2 -0
  29. package/dist/commands/open.js +19 -0
  30. package/dist/commands/open.js.map +1 -0
  31. package/dist/commands/platform.d.ts +2 -0
  32. package/dist/commands/platform.js +57 -0
  33. package/dist/commands/platform.js.map +1 -0
  34. package/dist/commands/projects.d.ts +2 -0
  35. package/dist/commands/projects.js +91 -0
  36. package/dist/commands/projects.js.map +1 -0
  37. package/dist/commands/scale.d.ts +2 -0
  38. package/dist/commands/scale.js +39 -0
  39. package/dist/commands/scale.js.map +1 -0
  40. package/dist/commands/storage.d.ts +2 -0
  41. package/dist/commands/storage.js +128 -0
  42. package/dist/commands/storage.js.map +1 -0
  43. package/dist/commands/usage.d.ts +2 -0
  44. package/dist/commands/usage.js +25 -0
  45. package/dist/commands/usage.js.map +1 -0
  46. package/dist/index.d.ts +2 -0
  47. package/dist/index.js +35 -0
  48. package/dist/index.js.map +1 -0
  49. package/dist/util.d.ts +26 -0
  50. package/dist/util.js +72 -0
  51. package/dist/util.js.map +1 -0
  52. package/package.json +27 -0
  53. package/src/api.ts +124 -0
  54. package/src/auth.ts +53 -0
  55. package/src/commands/auth.ts +117 -0
  56. package/src/commands/billing.ts +75 -0
  57. package/src/commands/db.ts +237 -0
  58. package/src/commands/deploy.ts +91 -0
  59. package/src/commands/domains.ts +62 -0
  60. package/src/commands/env.ts +69 -0
  61. package/src/commands/jobs.ts +287 -0
  62. package/src/commands/open.ts +22 -0
  63. package/src/commands/platform.ts +88 -0
  64. package/src/commands/projects.ts +115 -0
  65. package/src/commands/scale.ts +48 -0
  66. package/src/commands/storage.ts +214 -0
  67. package/src/commands/usage.ts +30 -0
  68. package/src/index.ts +39 -0
  69. package/src/util.ts +92 -0
  70. package/tsconfig.json +18 -0
@@ -0,0 +1,214 @@
1
+ import { Command } from 'commander';
2
+ import chalk from 'chalk';
3
+ import ora from 'ora';
4
+ import { api } from '../api.js';
5
+ import { formatTable, handleJson, resolveProject, withErrorHandler } from '../util.js';
6
+
7
+ interface StorageInfoResponse {
8
+ project: {
9
+ id: string;
10
+ name: string;
11
+ slug: string;
12
+ };
13
+ binding: {
14
+ bucket: string;
15
+ provider: string;
16
+ endpoint: string;
17
+ region: string;
18
+ cdn_base_url: string;
19
+ binding_model: string;
20
+ upload_strategy: string;
21
+ download_strategy: string;
22
+ usage: {
23
+ total_objects: number;
24
+ total_size_mb: number;
25
+ storage_limit_mb: number;
26
+ };
27
+ limits: {
28
+ max_single_upload_mb: number;
29
+ signed_url_ttl_seconds: number;
30
+ };
31
+ };
32
+ next_step?: string;
33
+ }
34
+
35
+ interface StorageListResponse {
36
+ bucket: string;
37
+ prefix: string;
38
+ continuation_token: string | null;
39
+ objects: Array<{
40
+ key: string;
41
+ size_mb: number;
42
+ last_modified: string | null;
43
+ }>;
44
+ }
45
+
46
+ interface PresignedUploadResponse {
47
+ bucket: string;
48
+ key: string;
49
+ method: string;
50
+ upload_url: string;
51
+ expires_at: string;
52
+ headers: Record<string, string>;
53
+ }
54
+
55
+ interface PresignedDownloadResponse {
56
+ bucket: string;
57
+ key: string;
58
+ download_url: string;
59
+ expires_at: string;
60
+ content_type: string | null;
61
+ size_bytes: number | null;
62
+ }
63
+
64
+ export function registerStorageCommands(program: Command): void {
65
+ const storage = program.command('storage').description('Project-scoped object storage');
66
+
67
+ storage
68
+ .command('info <idOrName>')
69
+ .description('Show the storage binding for a project')
70
+ .option('--json', 'Output as JSON')
71
+ .action(
72
+ withErrorHandler(async (idOrName: unknown, opts: { json?: boolean }) => {
73
+ const project = await resolveProject(String(idOrName));
74
+ const spinner = opts.json ? null : ora('Loading storage binding...').start();
75
+ const data = await api<StorageInfoResponse>(`/projects/${project.id}/storage`);
76
+ spinner?.stop();
77
+
78
+ handleJson(opts.json, data);
79
+
80
+ console.log(chalk.bold(`Storage: ${data.project.name}`));
81
+ console.log(chalk.gray(`${data.project.slug} • ${data.binding.bucket}\n`));
82
+ console.log(` ${chalk.blue('Provider:')} ${data.binding.provider}`);
83
+ console.log(` ${chalk.blue('Endpoint:')} ${data.binding.endpoint}`);
84
+ console.log(` ${chalk.blue('Region:')} ${data.binding.region}`);
85
+ console.log(` ${chalk.blue('Mode:')} ${data.binding.binding_model}`);
86
+ console.log(` ${chalk.blue('Upload:')} ${data.binding.upload_strategy}`);
87
+ console.log(` ${chalk.blue('Download:')} ${data.binding.download_strategy}`);
88
+ console.log(` ${chalk.blue('Objects:')} ${data.binding.usage.total_objects}`);
89
+ console.log(` ${chalk.blue('Usage:')} ${data.binding.usage.total_size_mb} MB / ${data.binding.usage.storage_limit_mb} MB`);
90
+ console.log(` ${chalk.blue('Max File:')} ${data.binding.limits.max_single_upload_mb} MB`);
91
+ console.log(` ${chalk.blue('TTL:')} ${data.binding.limits.signed_url_ttl_seconds}s`);
92
+
93
+ if (data.next_step) {
94
+ console.log(`\n${chalk.gray(data.next_step)}`);
95
+ }
96
+ }),
97
+ );
98
+
99
+ storage
100
+ .command('ls <idOrName> [prefix]')
101
+ .description('List objects in a project bucket')
102
+ .option('--limit <n>', 'Maximum number of objects to return', '100')
103
+ .option('--json', 'Output as JSON')
104
+ .action(
105
+ withErrorHandler(async (idOrName: unknown, prefix: unknown, opts: { limit?: string; json?: boolean }) => {
106
+ const project = await resolveProject(String(idOrName));
107
+ const spinner = opts.json ? null : ora('Listing storage objects...').start();
108
+ const params: Record<string, string> = {
109
+ limit: String(opts.limit || '100'),
110
+ };
111
+ if (prefix) params.prefix = String(prefix);
112
+ const data = await api<StorageListResponse>(`/projects/${project.id}/storage/objects`, { params });
113
+ spinner?.stop();
114
+
115
+ handleJson(opts.json, data);
116
+
117
+ console.log(chalk.bold(`Objects: ${project.name}`));
118
+ console.log(chalk.gray(`${data.bucket}${data.prefix ? ` • prefix ${data.prefix}` : ''}\n`));
119
+
120
+ console.log(formatTable(
121
+ data.objects.map((object) => ({
122
+ key: object.key,
123
+ size: `${object.size_mb} MB`,
124
+ updated: object.last_modified ? new Date(object.last_modified).toLocaleString() : '-',
125
+ })),
126
+ [
127
+ { key: 'key', label: 'Key' },
128
+ { key: 'size', label: 'Size' },
129
+ { key: 'updated', label: 'Updated' },
130
+ ],
131
+ ));
132
+
133
+ if (data.continuation_token) {
134
+ console.log(chalk.gray(`\nContinuation token available. Re-run with --json to fetch the next page.`));
135
+ }
136
+ }),
137
+ );
138
+
139
+ storage
140
+ .command('presign-upload <idOrName> <key>')
141
+ .description('Create a presigned upload URL for a project object')
142
+ .option('--content-type <type>', 'Content-Type to bind into the signed request')
143
+ .option('--size <bytes>', 'Expected content length in bytes')
144
+ .option('--ttl <seconds>', 'Signed URL TTL in seconds')
145
+ .option('--json', 'Output as JSON')
146
+ .action(
147
+ withErrorHandler(async (
148
+ idOrName: unknown,
149
+ key: unknown,
150
+ opts: { contentType?: string; size?: string; ttl?: string; json?: boolean },
151
+ ) => {
152
+ const project = await resolveProject(String(idOrName));
153
+ const spinner = opts.json ? null : ora('Creating upload URL...').start();
154
+ const body: Record<string, unknown> = { key: String(key) };
155
+ if (opts.contentType) body.content_type = opts.contentType;
156
+ if (opts.size) body.content_length_bytes = Number(opts.size);
157
+ if (opts.ttl) body.expires_in_seconds = Number(opts.ttl);
158
+ const data = await api<PresignedUploadResponse>(`/projects/${project.id}/storage/presign-upload`, {
159
+ method: 'POST',
160
+ body,
161
+ });
162
+ spinner?.stop();
163
+
164
+ handleJson(opts.json, data);
165
+
166
+ console.log(chalk.bold('Presigned Upload URL'));
167
+ console.log(chalk.gray(`${data.bucket}/${data.key}\n`));
168
+ console.log(` ${chalk.blue('Method:')} ${data.method}`);
169
+ console.log(` ${chalk.blue('Expires:')} ${new Date(data.expires_at).toLocaleString()}`);
170
+ for (const [header, value] of Object.entries(data.headers || {})) {
171
+ console.log(` ${chalk.blue(header + ':')} ${value}`);
172
+ }
173
+ console.log(`\n${data.upload_url}`);
174
+ }),
175
+ );
176
+
177
+ storage
178
+ .command('presign-download <idOrName> <key>')
179
+ .description('Create a presigned download URL for a project object')
180
+ .option('--download-name <name>', 'Suggested filename for the download')
181
+ .option('--ttl <seconds>', 'Signed URL TTL in seconds')
182
+ .option('--json', 'Output as JSON')
183
+ .action(
184
+ withErrorHandler(async (
185
+ idOrName: unknown,
186
+ key: unknown,
187
+ opts: { downloadName?: string; ttl?: string; json?: boolean },
188
+ ) => {
189
+ const project = await resolveProject(String(idOrName));
190
+ const spinner = opts.json ? null : ora('Creating download URL...').start();
191
+ const body: Record<string, unknown> = { key: String(key) };
192
+ if (opts.downloadName) body.download_name = opts.downloadName;
193
+ if (opts.ttl) body.expires_in_seconds = Number(opts.ttl);
194
+ const data = await api<PresignedDownloadResponse>(`/projects/${project.id}/storage/presign-download`, {
195
+ method: 'POST',
196
+ body,
197
+ });
198
+ spinner?.stop();
199
+
200
+ handleJson(opts.json, data);
201
+
202
+ console.log(chalk.bold('Presigned Download URL'));
203
+ console.log(chalk.gray(`${data.bucket}/${data.key}\n`));
204
+ console.log(` ${chalk.blue('Expires:')} ${new Date(data.expires_at).toLocaleString()}`);
205
+ if (data.content_type) {
206
+ console.log(` ${chalk.blue('Type:')} ${data.content_type}`);
207
+ }
208
+ if (data.size_bytes !== null) {
209
+ console.log(` ${chalk.blue('Size:')} ${data.size_bytes} bytes`);
210
+ }
211
+ console.log(`\n${data.download_url}`);
212
+ }),
213
+ );
214
+ }
@@ -0,0 +1,30 @@
1
+ import { Command } from 'commander';
2
+ import chalk from 'chalk';
3
+ import ora from 'ora';
4
+ import { api } from '../api.js';
5
+ import { resolveProject, handleJson, withErrorHandler } from '../util.js';
6
+
7
+ export function registerUsageCommands(program: Command): void {
8
+ program
9
+ .command('usage <idOrName>')
10
+ .description('Show resource usage')
11
+ .option('--period <period>', 'Time period (e.g., 7d, 30d)', '7d')
12
+ .option('--json', 'Output as JSON')
13
+ .action(
14
+ withErrorHandler(async (idOrName: unknown, opts: { period: string; json?: boolean }) => {
15
+ const project = await resolveProject(String(idOrName));
16
+ const spinner = ora('Fetching usage...').start();
17
+ const data = await api<Record<string, unknown>>(`/projects/${project.id}/usage`, {
18
+ params: { period: opts.period },
19
+ });
20
+ spinner.stop();
21
+
22
+ handleJson(opts.json, data);
23
+
24
+ console.log(chalk.bold(`Usage for ${project.name} (${opts.period}):`));
25
+ for (const [key, value] of Object.entries(data)) {
26
+ console.log(` ${chalk.blue(key + ':')} ${value}`);
27
+ }
28
+ }),
29
+ );
30
+ }
package/src/index.ts ADDED
@@ -0,0 +1,39 @@
1
+ #!/usr/bin/env node
2
+
3
+ import { Command } from 'commander';
4
+ import { registerAuthCommands } from './commands/auth.js';
5
+ import { registerProjectCommands } from './commands/projects.js';
6
+ import { registerDeployCommands } from './commands/deploy.js';
7
+ import { registerScaleCommands } from './commands/scale.js';
8
+ import { registerEnvCommands } from './commands/env.js';
9
+ import { registerDomainCommands } from './commands/domains.js';
10
+ import { registerDbCommands } from './commands/db.js';
11
+ import { registerUsageCommands } from './commands/usage.js';
12
+ import { registerBillingCommands } from './commands/billing.js';
13
+ import { registerOpenCommand } from './commands/open.js';
14
+ import { registerPlatformCommands } from './commands/platform.js';
15
+ import { registerStorageCommands } from './commands/storage.js';
16
+ import { registerJobCommands } from './commands/jobs.js';
17
+
18
+ const program = new Command();
19
+
20
+ program
21
+ .name('dailey')
22
+ .description('Dailey OS CLI — manage your projects, deployments, and billing')
23
+ .version('0.1.0');
24
+
25
+ registerAuthCommands(program);
26
+ registerProjectCommands(program);
27
+ registerDeployCommands(program);
28
+ registerScaleCommands(program);
29
+ registerEnvCommands(program);
30
+ registerDomainCommands(program);
31
+ registerDbCommands(program);
32
+ registerUsageCommands(program);
33
+ registerBillingCommands(program);
34
+ registerOpenCommand(program);
35
+ registerPlatformCommands(program);
36
+ registerStorageCommands(program);
37
+ registerJobCommands(program);
38
+
39
+ program.parse();
package/src/util.ts ADDED
@@ -0,0 +1,92 @@
1
+ import chalk from 'chalk';
2
+ import { api } from './api.js';
3
+
4
+ const UUID_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
5
+
6
+ interface Project {
7
+ id: string;
8
+ name: string;
9
+ slug?: string;
10
+ [key: string]: unknown;
11
+ }
12
+
13
+ /**
14
+ * Resolve a project identifier (UUID or name/slug) to a project object.
15
+ */
16
+ export async function resolveProject(idOrName: string): Promise<Project> {
17
+ if (UUID_RE.test(idOrName)) {
18
+ return api<Project>(`/projects/${idOrName}`);
19
+ }
20
+
21
+ const { projects } = await api<{ projects: Project[] }>('/projects');
22
+ const lower = idOrName.toLowerCase();
23
+ const match = projects.find(
24
+ (p) =>
25
+ p.name.toLowerCase() === lower ||
26
+ (p.slug && p.slug.toLowerCase() === lower),
27
+ );
28
+
29
+ if (!match) {
30
+ console.error(chalk.red(`Project "${idOrName}" not found.`));
31
+ process.exit(1);
32
+ }
33
+
34
+ return match;
35
+ }
36
+
37
+ /**
38
+ * Format an array of objects as an aligned table.
39
+ */
40
+ export function formatTable(rows: Record<string, string>[], columns: { key: string; label: string }[]): string {
41
+ if (rows.length === 0) return chalk.gray(' No results.');
42
+
43
+ // Calculate column widths
44
+ const widths: Record<string, number> = {};
45
+ for (const col of columns) {
46
+ widths[col.key] = col.label.length;
47
+ }
48
+ for (const row of rows) {
49
+ for (const col of columns) {
50
+ const val = String(row[col.key] ?? '');
51
+ if (val.length > widths[col.key]) {
52
+ widths[col.key] = val.length;
53
+ }
54
+ }
55
+ }
56
+
57
+ const header = columns.map((c) => c.label.padEnd(widths[c.key])).join(' ');
58
+ const sep = columns.map((c) => '-'.repeat(widths[c.key])).join(' ');
59
+ const body = rows.map((row) =>
60
+ columns.map((c) => String(row[c.key] ?? '').padEnd(widths[c.key])).join(' '),
61
+ );
62
+
63
+ return [chalk.bold(header), chalk.gray(sep), ...body].join('\n');
64
+ }
65
+
66
+ /**
67
+ * Handle --json flag: if set, print raw JSON and exit.
68
+ */
69
+ export function handleJson(flag: boolean | undefined, data: unknown): void {
70
+ if (flag) {
71
+ console.log(JSON.stringify(data, null, 2));
72
+ process.exit(0);
73
+ }
74
+ }
75
+
76
+ /**
77
+ * Wrap an async command handler with standard error handling.
78
+ */
79
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
80
+ export function withErrorHandler<T extends (...args: any[]) => Promise<void>>(fn: T): T {
81
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
82
+ const wrapped = async (...args: any[]) => {
83
+ try {
84
+ await fn(...args);
85
+ } catch (err: unknown) {
86
+ const message = err instanceof Error ? err.message : String(err);
87
+ console.error(chalk.red(`Error: ${message}`));
88
+ process.exit(1);
89
+ }
90
+ };
91
+ return wrapped as unknown as T;
92
+ }
package/tsconfig.json ADDED
@@ -0,0 +1,18 @@
1
+ {
2
+ "compilerOptions": {
3
+ "target": "ES2022",
4
+ "module": "NodeNext",
5
+ "moduleResolution": "NodeNext",
6
+ "outDir": "dist",
7
+ "rootDir": "src",
8
+ "strict": true,
9
+ "esModuleInterop": true,
10
+ "skipLibCheck": true,
11
+ "forceConsistentCasingInFileNames": true,
12
+ "resolveJsonModule": true,
13
+ "declaration": true,
14
+ "sourceMap": true
15
+ },
16
+ "include": ["src/**/*"],
17
+ "exclude": ["node_modules", "dist"]
18
+ }