flingit 0.0.12 → 0.0.14

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 (93) hide show
  1. package/README.md +8 -1
  2. package/dist/cli/commands/db.d.ts +11 -0
  3. package/dist/cli/commands/db.d.ts.map +1 -1
  4. package/dist/cli/commands/db.js +60 -48
  5. package/dist/cli/commands/db.js.map +1 -1
  6. package/dist/cli/commands/dev.d.ts +9 -0
  7. package/dist/cli/commands/dev.d.ts.map +1 -1
  8. package/dist/cli/commands/dev.js +96 -100
  9. package/dist/cli/commands/dev.js.map +1 -1
  10. package/dist/cli/commands/feedback.d.ts +23 -0
  11. package/dist/cli/commands/feedback.d.ts.map +1 -1
  12. package/dist/cli/commands/feedback.js +60 -73
  13. package/dist/cli/commands/feedback.js.map +1 -1
  14. package/dist/cli/commands/init.d.ts +9 -0
  15. package/dist/cli/commands/init.d.ts.map +1 -1
  16. package/dist/cli/commands/init.js +96 -107
  17. package/dist/cli/commands/init.js.map +1 -1
  18. package/dist/cli/commands/launch.d.ts +11 -1
  19. package/dist/cli/commands/launch.d.ts.map +1 -1
  20. package/dist/cli/commands/launch.js +32 -36
  21. package/dist/cli/commands/launch.js.map +1 -1
  22. package/dist/cli/commands/login.d.ts +30 -0
  23. package/dist/cli/commands/login.d.ts.map +1 -1
  24. package/dist/cli/commands/login.js +71 -58
  25. package/dist/cli/commands/login.js.map +1 -1
  26. package/dist/cli/commands/logout.js +1 -1
  27. package/dist/cli/commands/logs.d.ts +39 -0
  28. package/dist/cli/commands/logs.d.ts.map +1 -1
  29. package/dist/cli/commands/logs.js +121 -90
  30. package/dist/cli/commands/logs.js.map +1 -1
  31. package/dist/cli/commands/onboard.d.ts +1 -1
  32. package/dist/cli/commands/onboard.d.ts.map +1 -1
  33. package/dist/cli/commands/onboard.js +97 -42
  34. package/dist/cli/commands/onboard.js.map +1 -1
  35. package/dist/cli/commands/plugin.d.ts +43 -0
  36. package/dist/cli/commands/plugin.d.ts.map +1 -1
  37. package/dist/cli/commands/plugin.js +229 -211
  38. package/dist/cli/commands/plugin.js.map +1 -1
  39. package/dist/cli/commands/project.d.ts +27 -0
  40. package/dist/cli/commands/project.d.ts.map +1 -1
  41. package/dist/cli/commands/project.js +101 -85
  42. package/dist/cli/commands/project.js.map +1 -1
  43. package/dist/cli/commands/push.d.ts +39 -0
  44. package/dist/cli/commands/push.d.ts.map +1 -1
  45. package/dist/cli/commands/push.js +152 -118
  46. package/dist/cli/commands/push.js.map +1 -1
  47. package/dist/cli/commands/register.js +1 -1
  48. package/dist/cli/commands/storage.d.ts +32 -0
  49. package/dist/cli/commands/storage.d.ts.map +1 -1
  50. package/dist/cli/commands/storage.js +180 -140
  51. package/dist/cli/commands/storage.js.map +1 -1
  52. package/dist/cli/commands/tunnel.d.ts +41 -0
  53. package/dist/cli/commands/tunnel.d.ts.map +1 -0
  54. package/dist/cli/commands/tunnel.js +210 -0
  55. package/dist/cli/commands/tunnel.js.map +1 -0
  56. package/dist/cli/commands/upgrade.d.ts +15 -0
  57. package/dist/cli/commands/upgrade.d.ts.map +1 -0
  58. package/dist/cli/commands/upgrade.js +82 -0
  59. package/dist/cli/commands/upgrade.js.map +1 -0
  60. package/dist/cli/index.js +35 -4
  61. package/dist/cli/index.js.map +1 -1
  62. package/dist/cli/utils/cli-io-impl.d.ts +64 -0
  63. package/dist/cli/utils/cli-io-impl.d.ts.map +1 -0
  64. package/dist/cli/utils/cli-io-impl.js +521 -0
  65. package/dist/cli/utils/cli-io-impl.js.map +1 -0
  66. package/dist/cli/utils/cli-io.d.ts +350 -0
  67. package/dist/cli/utils/cli-io.d.ts.map +1 -0
  68. package/dist/cli/utils/cli-io.js +13 -0
  69. package/dist/cli/utils/cli-io.js.map +1 -0
  70. package/dist/cli/utils/config.d.ts +60 -2
  71. package/dist/cli/utils/config.d.ts.map +1 -1
  72. package/dist/cli/utils/config.js +158 -37
  73. package/dist/cli/utils/config.js.map +1 -1
  74. package/dist/cli/utils/project.d.ts +60 -0
  75. package/dist/cli/utils/project.d.ts.map +1 -1
  76. package/dist/cli/utils/project.js +112 -2
  77. package/dist/cli/utils/project.js.map +1 -1
  78. package/dist/cli/utils/prompt-new-project.d.ts +1 -1
  79. package/dist/cli/utils/prompt-new-project.d.ts.map +1 -1
  80. package/dist/cli/utils/registry.d.ts +2 -2
  81. package/dist/cli/utils/registry.d.ts.map +1 -1
  82. package/dist/cli/utils/registry.js +13 -3
  83. package/dist/cli/utils/registry.js.map +1 -1
  84. package/dist/runtime/log.d.ts.map +1 -1
  85. package/dist/runtime/log.js +8 -4
  86. package/dist/runtime/log.js.map +1 -1
  87. package/dist/runtime/storage.js +1 -1
  88. package/dist/runtime/storage.js.map +1 -1
  89. package/package.json +3 -2
  90. package/templates/default/CLAUDE.md +1 -0
  91. package/templates/default/skills/fling/.hash +1 -0
  92. package/templates/default/skills/fling/SKILL.md +12 -4
  93. /package/templates/default/skills/{discord/SKILL.md → fling/DISCORD.md} +0 -0
@@ -6,5 +6,37 @@
6
6
  * - Use --prod to operate on production R2 storage
7
7
  */
8
8
  import { Command } from "commander";
9
+ import type { StorageIO } from "../utils/cli-io.js";
10
+ /**
11
+ * Check if project is initialized.
12
+ */
13
+ export declare function checkProject(io: StorageIO): void;
9
14
  export declare const storageCommand: Command;
15
+ /**
16
+ * List objects in storage.
17
+ */
18
+ export declare function listStorage(prefix: string | undefined, options: {
19
+ json?: boolean;
20
+ limit: string;
21
+ }, env: "local" | "prod", io: StorageIO): Promise<void>;
22
+ /**
23
+ * Upload a file to storage.
24
+ */
25
+ export declare function putStorage(key: string, file: string, options: {
26
+ contentType?: string;
27
+ }, env: "local" | "prod", io: StorageIO): Promise<void>;
28
+ /**
29
+ * Download an object from storage.
30
+ */
31
+ export declare function getStorage(key: string, output: string | undefined, env: "local" | "prod", io: StorageIO): Promise<void>;
32
+ /**
33
+ * Delete an object from storage.
34
+ */
35
+ export declare function deleteStorage(key: string, options: {
36
+ yes?: boolean;
37
+ }, env: "local" | "prod", io: StorageIO): Promise<void>;
38
+ /**
39
+ * Show storage information and usage.
40
+ */
41
+ export declare function infoStorage(env: "local" | "prod", io: StorageIO): Promise<void>;
10
42
  //# sourceMappingURL=storage.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"storage.d.ts","sourceRoot":"","sources":["../../../src/cli/commands/storage.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAgEpC,eAAO,MAAM,cAAc,SAmBxB,CAAC"}
1
+ {"version":3,"file":"storage.d.ts","sourceRoot":"","sources":["../../../src/cli/commands/storage.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAMpC,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,oBAAoB,CAAC;AAuBpD;;GAEG;AACH,wBAAgB,YAAY,CAAC,EAAE,EAAE,SAAS,GAAG,IAAI,CAKhD;AAuBD,eAAO,MAAM,cAAc,SAmBxB,CAAC;AAMJ;;GAEG;AACH,wBAAsB,WAAW,CAC/B,MAAM,EAAE,MAAM,GAAG,SAAS,EAC1B,OAAO,EAAE;IAAE,IAAI,CAAC,EAAE,OAAO,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,EAC1C,GAAG,EAAE,OAAO,GAAG,MAAM,EACrB,EAAE,EAAE,SAAS,GACZ,OAAO,CAAC,IAAI,CAAC,CA+Gf;AAoBD;;GAEG;AACH,wBAAsB,UAAU,CAC9B,GAAG,EAAE,MAAM,EACX,IAAI,EAAE,MAAM,EACZ,OAAO,EAAE;IAAE,WAAW,CAAC,EAAE,MAAM,CAAA;CAAE,EACjC,GAAG,EAAE,OAAO,GAAG,MAAM,EACrB,EAAE,EAAE,SAAS,GACZ,OAAO,CAAC,IAAI,CAAC,CAwEf;AAmBD;;GAEG;AACH,wBAAsB,UAAU,CAC9B,GAAG,EAAE,MAAM,EACX,MAAM,EAAE,MAAM,GAAG,SAAS,EAC1B,GAAG,EAAE,OAAO,GAAG,MAAM,EACrB,EAAE,EAAE,SAAS,GACZ,OAAO,CAAC,IAAI,CAAC,CAgEf;AAkBD;;GAEG;AACH,wBAAsB,aAAa,CACjC,GAAG,EAAE,MAAM,EACX,OAAO,EAAE;IAAE,GAAG,CAAC,EAAE,OAAO,CAAA;CAAE,EAC1B,GAAG,EAAE,OAAO,GAAG,MAAM,EACrB,EAAE,EAAE,SAAS,GACZ,OAAO,CAAC,IAAI,CAAC,CAiDf;AAmBD;;GAEG;AACH,wBAAsB,WAAW,CAC/B,GAAG,EAAE,OAAO,GAAG,MAAM,EACrB,EAAE,EAAE,SAAS,GACZ,OAAO,CAAC,IAAI,CAAC,CAiEf"}
@@ -6,11 +6,11 @@
6
6
  * - Use --prod to operate on production R2 storage
7
7
  */
8
8
  import { Command } from "commander";
9
- import { existsSync, readFileSync, writeFileSync, statSync, } from "node:fs";
10
9
  import { join } from "node:path";
11
10
  import { platformFetch } from "../utils/config.js";
12
11
  import { getTargetEnv, requireProdAuth } from "../utils/environment.js";
13
12
  import { storage } from "../../runtime/storage.js";
13
+ import { defaultStorageIO } from "../utils/cli-io-impl.js";
14
14
  // ============================================================================
15
15
  // Helpers
16
16
  // ============================================================================
@@ -33,10 +33,10 @@ function formatDate(date) {
33
33
  /**
34
34
  * Check if project is initialized.
35
35
  */
36
- function checkProject() {
37
- if (!existsSync(join(process.cwd(), ".fling"))) {
38
- console.error("Error: Not a Fling project. Run 'fling init' first.");
39
- process.exit(1);
36
+ export function checkProject(io) {
37
+ if (!io.file.exists(join(io.process.cwd(), ".fling"))) {
38
+ io.console.error("Error: Not a Fling project. Run 'fling init' first.");
39
+ io.process.exit(1);
40
40
  }
41
41
  }
42
42
  /**
@@ -74,17 +74,12 @@ Examples:
74
74
  // ============================================================================
75
75
  // storage list
76
76
  // ============================================================================
77
- storageCommand
78
- .command("list [prefix]")
79
- .description("List objects in storage")
80
- .option("--json", "Output as JSON")
81
- .option("--limit <n>", "Maximum objects to return", "100")
82
- .action(async (prefix, options, command) => {
83
- checkProject();
84
- const env = getTargetEnv(command);
77
+ /**
78
+ * List objects in storage.
79
+ */
80
+ export async function listStorage(prefix, options, env, io) {
85
81
  const limit = parseInt(options.limit, 10);
86
82
  if (env === "prod") {
87
- requireProdAuth(env);
88
83
  try {
89
84
  const projectName = await getProjectName();
90
85
  const params = new URLSearchParams();
@@ -94,12 +89,12 @@ storageCommand
94
89
  const response = await platformFetch(`/project/${encodeURIComponent(projectName)}/storage?${params.toString()}`);
95
90
  if (!response.ok) {
96
91
  const data = (await response.json());
97
- console.error(`Error: ${data.error ?? `API error: ${response.status}`}`);
98
- process.exit(1);
92
+ io.console.error(`Error: ${data.error ?? `API error: ${response.status}`}`);
93
+ io.process.exit(1);
99
94
  }
100
95
  const data = (await response.json());
101
96
  if (options.json) {
102
- console.log(JSON.stringify({
97
+ io.console.log(JSON.stringify({
103
98
  objects: data.objects,
104
99
  count: data.objects.length,
105
100
  totalSize: data.objects.reduce((sum, o) => sum + o.size, 0),
@@ -108,27 +103,27 @@ storageCommand
108
103
  }
109
104
  else {
110
105
  if (data.objects.length === 0) {
111
- console.log("No objects in storage.");
106
+ io.console.log("No objects in storage.");
112
107
  return;
113
108
  }
114
- console.log("Objects in production storage:\n");
109
+ io.console.log("Objects in production storage:\n");
115
110
  let totalSize = 0;
116
111
  for (const obj of data.objects) {
117
112
  const size = formatBytes(obj.size).padStart(10);
118
113
  const date = formatDate(new Date(obj.uploaded));
119
- console.log(` ${obj.key.padEnd(40)} ${size} ${date}`);
114
+ io.console.log(` ${obj.key.padEnd(40)} ${size} ${date}`);
120
115
  totalSize += obj.size;
121
116
  }
122
- console.log("");
123
- console.log(`${data.objects.length} objects, ${formatBytes(totalSize)} total`);
117
+ io.console.log("");
118
+ io.console.log(`${data.objects.length} objects, ${formatBytes(totalSize)} total`);
124
119
  if (data.truncated) {
125
- console.log(`(more objects available, use --limit to increase)`);
120
+ io.console.log(`(more objects available, use --limit to increase)`);
126
121
  }
127
122
  }
128
123
  }
129
124
  catch (error) {
130
- console.error(`Error: ${error instanceof Error ? error.message : String(error)}`);
131
- process.exit(1);
125
+ io.console.error(`Error: ${error instanceof Error ? error.message : String(error)}`);
126
+ io.process.exit(1);
132
127
  }
133
128
  }
134
129
  else {
@@ -139,7 +134,7 @@ storageCommand
139
134
  listOptions.prefix = prefix;
140
135
  const result = await storage.list(listOptions);
141
136
  if (options.json) {
142
- console.log(JSON.stringify({
137
+ io.console.log(JSON.stringify({
143
138
  objects: result.objects.map((o) => ({
144
139
  key: o.key,
145
140
  size: o.size,
@@ -153,69 +148,78 @@ storageCommand
153
148
  }
154
149
  else {
155
150
  if (result.objects.length === 0) {
156
- console.log("No objects in storage.");
151
+ io.console.log("No objects in storage.");
157
152
  return;
158
153
  }
159
- console.log("Objects in local storage:\n");
154
+ io.console.log("Objects in local storage:\n");
160
155
  let totalSize = 0;
161
156
  for (const obj of result.objects) {
162
157
  const size = formatBytes(obj.size).padStart(10);
163
158
  const date = formatDate(obj.uploaded);
164
- console.log(` ${obj.key.padEnd(40)} ${size} ${date}`);
159
+ io.console.log(` ${obj.key.padEnd(40)} ${size} ${date}`);
165
160
  totalSize += obj.size;
166
161
  }
167
- console.log("");
168
- console.log(`${result.objects.length} objects, ${formatBytes(totalSize)} total`);
162
+ io.console.log("");
163
+ io.console.log(`${result.objects.length} objects, ${formatBytes(totalSize)} total`);
169
164
  if (result.truncated) {
170
- console.log(`(more objects available, use --limit to increase)`);
165
+ io.console.log(`(more objects available, use --limit to increase)`);
171
166
  }
172
167
  }
173
168
  }
174
169
  catch (error) {
175
- console.error(`Error: ${error instanceof Error ? error.message : String(error)}`);
176
- process.exit(1);
170
+ io.console.error(`Error: ${error instanceof Error ? error.message : String(error)}`);
171
+ io.process.exit(1);
177
172
  }
178
173
  }
174
+ }
175
+ storageCommand
176
+ .command("list [prefix]")
177
+ .description("List objects in storage")
178
+ .option("--json", "Output as JSON")
179
+ .option("--limit <n>", "Maximum objects to return", "100")
180
+ .action(async (prefix, options, command) => {
181
+ checkProject(defaultStorageIO);
182
+ const env = getTargetEnv(command);
183
+ if (env === "prod") {
184
+ requireProdAuth(env);
185
+ }
186
+ await listStorage(prefix, options, env, defaultStorageIO);
179
187
  });
180
188
  // ============================================================================
181
189
  // storage put
182
190
  // ============================================================================
183
- storageCommand
184
- .command("put <key> <file>")
185
- .description("Upload a file to storage")
186
- .option("--content-type <type>", "Override MIME type")
187
- .action(async (key, file, options, command) => {
188
- checkProject();
189
- const env = getTargetEnv(command);
191
+ /**
192
+ * Upload a file to storage.
193
+ */
194
+ export async function putStorage(key, file, options, env, io) {
190
195
  // Read from stdin if file is "-"
191
196
  let content;
192
197
  const contentType = options.contentType;
193
198
  if (file === "-") {
194
199
  // Read from stdin
195
200
  const chunks = [];
196
- for await (const chunk of process.stdin) {
201
+ for await (const chunk of io.process.stdin) {
197
202
  chunks.push(chunk);
198
203
  }
199
204
  content = Buffer.concat(chunks);
200
205
  }
201
206
  else {
202
207
  // Read from file
203
- if (!existsSync(file)) {
204
- console.error(`Error: File not found: ${file}`);
205
- process.exit(1);
208
+ const fileContent = io.file.readFileBuffer(file);
209
+ if (fileContent === null) {
210
+ io.console.error(`Error: File not found: ${file}`);
211
+ io.process.exit(1);
206
212
  }
207
- const stat = statSync(file);
208
- if (stat.size > 100 * 1024 * 1024) {
209
- console.error("Error: File too large (maximum 100 MB).");
210
- process.exit(1);
213
+ if (fileContent.length > 100 * 1024 * 1024) {
214
+ io.console.error("Error: File too large (maximum 100 MB).");
215
+ io.process.exit(1);
211
216
  }
212
- content = readFileSync(file);
217
+ content = fileContent;
213
218
  }
214
219
  if (env === "prod") {
215
- requireProdAuth(env);
216
220
  try {
217
221
  const projectName = await getProjectName();
218
- console.log(`Uploading ${key}...`);
222
+ io.console.log(`Uploading ${key}...`);
219
223
  const headers = {};
220
224
  if (contentType) {
221
225
  headers["Content-Type"] = contentType;
@@ -227,52 +231,61 @@ storageCommand
227
231
  });
228
232
  if (!response.ok) {
229
233
  const data = (await response.json());
230
- console.error(`Error: ${data.error ?? `API error: ${response.status}`}`);
231
- process.exit(1);
234
+ io.console.error(`Error: ${data.error ?? `API error: ${response.status}`}`);
235
+ io.process.exit(1);
232
236
  }
233
237
  const data = (await response.json());
234
- console.log(` ${formatBytes(data.size)} uploaded\n`);
235
- console.log(`Object stored: ${data.key} (${data.size} bytes)`);
238
+ io.console.log(` ${formatBytes(data.size)} uploaded\n`);
239
+ io.console.log(`Object stored: ${data.key} (${data.size} bytes)`);
236
240
  }
237
241
  catch (error) {
238
- console.error(`Error: ${error instanceof Error ? error.message : String(error)}`);
239
- process.exit(1);
242
+ io.console.error(`Error: ${error instanceof Error ? error.message : String(error)}`);
243
+ io.process.exit(1);
240
244
  }
241
245
  }
242
246
  else {
243
247
  // Local storage
244
248
  try {
245
- console.log(`Uploading ${key}...`);
249
+ io.console.log(`Uploading ${key}...`);
246
250
  const putOptions = {};
247
251
  if (contentType)
248
252
  putOptions.contentType = contentType;
249
253
  const result = await storage.put(key, content, putOptions);
250
- console.log(` ${formatBytes(result.size)} uploaded\n`);
251
- console.log(`Object stored: ${result.key} (${result.size} bytes)`);
254
+ io.console.log(` ${formatBytes(result.size)} uploaded\n`);
255
+ io.console.log(`Object stored: ${result.key} (${result.size} bytes)`);
252
256
  }
253
257
  catch (error) {
254
- console.error(`Error: ${error instanceof Error ? error.message : String(error)}`);
255
- process.exit(1);
258
+ io.console.error(`Error: ${error instanceof Error ? error.message : String(error)}`);
259
+ io.process.exit(1);
256
260
  }
257
261
  }
262
+ }
263
+ storageCommand
264
+ .command("put <key> <file>")
265
+ .description("Upload a file to storage")
266
+ .option("--content-type <type>", "Override MIME type")
267
+ .action(async (key, file, options, command) => {
268
+ checkProject(defaultStorageIO);
269
+ const env = getTargetEnv(command);
270
+ if (env === "prod") {
271
+ requireProdAuth(env);
272
+ }
273
+ await putStorage(key, file, options, env, defaultStorageIO);
258
274
  });
259
275
  // ============================================================================
260
276
  // storage get
261
277
  // ============================================================================
262
- storageCommand
263
- .command("get <key> [output]")
264
- .description("Download an object from storage")
265
- .action(async (key, output, _options, command) => {
266
- checkProject();
267
- const env = getTargetEnv(command);
278
+ /**
279
+ * Download an object from storage.
280
+ */
281
+ export async function getStorage(key, output, env, io) {
268
282
  if (env === "prod") {
269
- requireProdAuth(env);
270
283
  try {
271
284
  const projectName = await getProjectName();
272
285
  const response = await platformFetch(`/project/${encodeURIComponent(projectName)}/storage/${encodeURIComponent(key)}`);
273
286
  if (response.status === 404) {
274
- console.error(`Error: Object not found: ${key}`);
275
- process.exit(1);
287
+ io.console.error(`Error: Object not found: ${key}`);
288
+ io.process.exit(1);
276
289
  }
277
290
  if (!response.ok) {
278
291
  let errorMessage = `API error: ${response.status}`;
@@ -284,24 +297,24 @@ storageCommand
284
297
  catch {
285
298
  // Ignore JSON parse errors
286
299
  }
287
- console.error(`Error: ${errorMessage}`);
288
- process.exit(1);
300
+ io.console.error(`Error: ${errorMessage}`);
301
+ io.process.exit(1);
289
302
  }
290
303
  const buffer = Buffer.from(await response.arrayBuffer());
291
304
  if (output) {
292
- console.error(`Downloading ${key}...`);
293
- writeFileSync(output, buffer);
294
- console.error(` ${formatBytes(buffer.length)} downloaded\n`);
295
- console.error(`Saved to: ${output}`);
305
+ io.console.error(`Downloading ${key}...`);
306
+ io.file.writeFileBuffer(output, buffer);
307
+ io.console.error(` ${formatBytes(buffer.length)} downloaded\n`);
308
+ io.console.error(`Saved to: ${output}`);
296
309
  }
297
310
  else {
298
311
  // Write to stdout
299
- process.stdout.write(buffer);
312
+ io.process.stdout.write(buffer);
300
313
  }
301
314
  }
302
315
  catch (error) {
303
- console.error(`Error: ${error instanceof Error ? error.message : String(error)}`);
304
- process.exit(1);
316
+ io.console.error(`Error: ${error instanceof Error ? error.message : String(error)}`);
317
+ io.process.exit(1);
305
318
  }
306
319
  }
307
320
  else {
@@ -309,45 +322,52 @@ storageCommand
309
322
  try {
310
323
  const obj = await storage.get(key);
311
324
  if (!obj) {
312
- console.error(`Error: Object not found: ${key}`);
313
- process.exit(1);
325
+ io.console.error(`Error: Object not found: ${key}`);
326
+ io.process.exit(1);
314
327
  }
315
328
  const buffer = Buffer.from(await obj.arrayBuffer());
316
329
  if (output) {
317
- console.error(`Downloading ${key}...`);
318
- writeFileSync(output, buffer);
319
- console.error(` ${formatBytes(buffer.length)} downloaded\n`);
320
- console.error(`Saved to: ${output}`);
330
+ io.console.error(`Downloading ${key}...`);
331
+ io.file.writeFileBuffer(output, buffer);
332
+ io.console.error(` ${formatBytes(buffer.length)} downloaded\n`);
333
+ io.console.error(`Saved to: ${output}`);
321
334
  }
322
335
  else {
323
336
  // Write to stdout
324
- process.stdout.write(buffer);
337
+ io.process.stdout.write(buffer);
325
338
  }
326
339
  }
327
340
  catch (error) {
328
- console.error(`Error: ${error instanceof Error ? error.message : String(error)}`);
329
- process.exit(1);
341
+ io.console.error(`Error: ${error instanceof Error ? error.message : String(error)}`);
342
+ io.process.exit(1);
330
343
  }
331
344
  }
345
+ }
346
+ storageCommand
347
+ .command("get <key> [output]")
348
+ .description("Download an object from storage")
349
+ .action(async (key, output, _options, command) => {
350
+ checkProject(defaultStorageIO);
351
+ const env = getTargetEnv(command);
352
+ if (env === "prod") {
353
+ requireProdAuth(env);
354
+ }
355
+ await getStorage(key, output, env, defaultStorageIO);
332
356
  });
333
357
  // ============================================================================
334
358
  // storage delete
335
359
  // ============================================================================
336
- storageCommand
337
- .command("delete <key>")
338
- .description("Delete an object from storage")
339
- .option("--yes", "Skip confirmation prompt")
340
- .action(async (key, options, command) => {
341
- checkProject();
342
- const env = getTargetEnv(command);
360
+ /**
361
+ * Delete an object from storage.
362
+ */
363
+ export async function deleteStorage(key, options, env, io) {
343
364
  if (env === "prod") {
344
- requireProdAuth(env);
345
365
  // Show warning for production deletion
346
366
  if (!options.yes) {
347
- console.log(`\n⚠️ You are about to delete from PRODUCTION storage:`);
348
- console.log(` ${key}\n`);
349
- console.log(`This cannot be undone. Use --yes to confirm.`);
350
- process.exit(1);
367
+ io.console.log(`\n⚠️ You are about to delete from PRODUCTION storage:`);
368
+ io.console.log(` ${key}\n`);
369
+ io.console.log(`This cannot be undone. Use --yes to confirm.`);
370
+ io.process.exit(1);
351
371
  }
352
372
  try {
353
373
  const projectName = await getProjectName();
@@ -355,19 +375,19 @@ storageCommand
355
375
  method: "DELETE",
356
376
  });
357
377
  if (response.status === 404) {
358
- console.error(`Error: Object not found: ${key}`);
359
- process.exit(1);
378
+ io.console.error(`Error: Object not found: ${key}`);
379
+ io.process.exit(1);
360
380
  }
361
381
  if (!response.ok) {
362
382
  const data = (await response.json());
363
- console.error(`Error: ${data.error ?? `API error: ${response.status}`}`);
364
- process.exit(1);
383
+ io.console.error(`Error: ${data.error ?? `API error: ${response.status}`}`);
384
+ io.process.exit(1);
365
385
  }
366
- console.log(`Deleted: ${key}`);
386
+ io.console.log(`Deleted: ${key}`);
367
387
  }
368
388
  catch (error) {
369
- console.error(`Error: ${error instanceof Error ? error.message : String(error)}`);
370
- process.exit(1);
389
+ io.console.error(`Error: ${error instanceof Error ? error.message : String(error)}`);
390
+ io.process.exit(1);
371
391
  }
372
392
  }
373
393
  else {
@@ -376,50 +396,59 @@ storageCommand
376
396
  // Check if object exists
377
397
  const obj = await storage.head(key);
378
398
  if (!obj) {
379
- console.error(`Error: Object not found: ${key}`);
380
- process.exit(1);
399
+ io.console.error(`Error: Object not found: ${key}`);
400
+ io.process.exit(1);
381
401
  }
382
402
  await storage.delete(key);
383
- console.log(`Deleted: ${key}`);
403
+ io.console.log(`Deleted: ${key}`);
384
404
  }
385
405
  catch (error) {
386
- console.error(`Error: ${error instanceof Error ? error.message : String(error)}`);
387
- process.exit(1);
406
+ io.console.error(`Error: ${error instanceof Error ? error.message : String(error)}`);
407
+ io.process.exit(1);
388
408
  }
389
409
  }
410
+ }
411
+ storageCommand
412
+ .command("delete <key>")
413
+ .description("Delete an object from storage")
414
+ .option("--yes", "Skip confirmation prompt")
415
+ .action(async (key, options, command) => {
416
+ checkProject(defaultStorageIO);
417
+ const env = getTargetEnv(command);
418
+ if (env === "prod") {
419
+ requireProdAuth(env);
420
+ }
421
+ await deleteStorage(key, options, env, defaultStorageIO);
390
422
  });
391
423
  // ============================================================================
392
424
  // storage info
393
425
  // ============================================================================
394
- storageCommand
395
- .command("info")
396
- .description("Show storage information and usage")
397
- .action(async (_options, command) => {
398
- checkProject();
399
- const env = getTargetEnv(command);
426
+ /**
427
+ * Show storage information and usage.
428
+ */
429
+ export async function infoStorage(env, io) {
400
430
  if (env === "prod") {
401
- requireProdAuth(env);
402
431
  try {
403
432
  const projectName = await getProjectName();
404
433
  const response = await platformFetch(`/project/${encodeURIComponent(projectName)}/storage/info`);
405
434
  if (!response.ok) {
406
435
  const data = (await response.json());
407
- console.error(`Error: ${data.error ?? `API error: ${response.status}`}`);
408
- process.exit(1);
436
+ io.console.error(`Error: ${data.error ?? `API error: ${response.status}`}`);
437
+ io.process.exit(1);
409
438
  }
410
439
  const data = (await response.json());
411
- console.log("Production Storage (R2)\n");
412
- console.log(` Bucket: ${data.bucket}`);
413
- console.log(` Objects: ${data.objectCount.toLocaleString()}`);
414
- console.log(` Size: ${formatBytes(data.totalSizeBytes)}`);
415
- console.log("");
416
- console.log(" Pricing: $0.015/GB-month storage");
417
- console.log(" $0.36/million Class A ops (write)");
418
- console.log(" $0.036/million Class B ops (read)");
440
+ io.console.log("Production Storage (R2)\n");
441
+ io.console.log(` Bucket: ${data.bucket}`);
442
+ io.console.log(` Objects: ${data.objectCount.toLocaleString()}`);
443
+ io.console.log(` Size: ${formatBytes(data.totalSizeBytes)}`);
444
+ io.console.log("");
445
+ io.console.log(" Pricing: $0.015/GB-month storage");
446
+ io.console.log(" $0.36/million Class A ops (write)");
447
+ io.console.log(" $0.036/million Class B ops (read)");
419
448
  }
420
449
  catch (error) {
421
- console.error(`Error: ${error instanceof Error ? error.message : String(error)}`);
422
- process.exit(1);
450
+ io.console.error(`Error: ${error instanceof Error ? error.message : String(error)}`);
451
+ io.process.exit(1);
423
452
  }
424
453
  }
425
454
  else {
@@ -444,16 +473,27 @@ storageCommand
444
473
  if (!more.truncated)
445
474
  break;
446
475
  }
447
- const storagePath = join(process.cwd(), ".fling", "data", "storage");
448
- console.log("Local Storage\n");
449
- console.log(` Location: ${storagePath}`);
450
- console.log(` Objects: ${count.toLocaleString()}`);
451
- console.log(` Size: ${formatBytes(totalSize)}`);
476
+ const storagePath = join(io.process.cwd(), ".fling", "data", "storage");
477
+ io.console.log("Local Storage\n");
478
+ io.console.log(` Location: ${storagePath}`);
479
+ io.console.log(` Objects: ${count.toLocaleString()}`);
480
+ io.console.log(` Size: ${formatBytes(totalSize)}`);
452
481
  }
453
482
  catch (error) {
454
- console.error(`Error: ${error instanceof Error ? error.message : String(error)}`);
455
- process.exit(1);
483
+ io.console.error(`Error: ${error instanceof Error ? error.message : String(error)}`);
484
+ io.process.exit(1);
456
485
  }
457
486
  }
487
+ }
488
+ storageCommand
489
+ .command("info")
490
+ .description("Show storage information and usage")
491
+ .action(async (_options, command) => {
492
+ checkProject(defaultStorageIO);
493
+ const env = getTargetEnv(command);
494
+ if (env === "prod") {
495
+ requireProdAuth(env);
496
+ }
497
+ await infoStorage(env, defaultStorageIO);
458
498
  });
459
499
  //# sourceMappingURL=storage.js.map