flingit 0.0.30 → 0.0.32

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.
@@ -0,0 +1,121 @@
1
+ /**
2
+ * Worker entry point factory for Cloudflare Workers.
3
+ *
4
+ * This module contains the actual entry logic that was previously
5
+ * generated as a string template in the bundler. It exports a factory
6
+ * function that creates the Worker's fetch handler.
7
+ *
8
+ * The bundler generates a minimal shim that wires the user app path:
9
+ * import { createWorkerEntry } from "./entry.js";
10
+ * export default createWorkerEntry(() => import("./user-app.js"));
11
+ */
12
+ import { __initEnv, __getApp, __runMigrations, __getCronHandlers } from "./index.js";
13
+ import { __handlePluginRequest as __handle_discord } from "./discord.js";
14
+ import { __handlePluginRequest as __handle_slack } from "./slack.js";
15
+ const pluginHandlers = {
16
+ discord: __handle_discord,
17
+ slack: __handle_slack,
18
+ };
19
+ const pluginPathPattern = /^\/__plugin\/(\w+)$/;
20
+ /**
21
+ * Create a Cloudflare Worker entry point.
22
+ *
23
+ * @param loadUserApp - Function that imports the user's app module (triggers route registration)
24
+ * @returns Worker module with fetch handler
25
+ */
26
+ export function createWorkerEntry(loadUserApp) {
27
+ let initialized = false;
28
+ let initError = null;
29
+ async function ensureInit(env) {
30
+ // If initialization previously failed, throw the same error
31
+ if (initError) {
32
+ throw initError instanceof Error
33
+ ? initError
34
+ : new Error(typeof initError === "string" ? initError : "Initialization failed");
35
+ }
36
+ if (!initialized) {
37
+ __initEnv(env);
38
+ try {
39
+ await loadUserApp();
40
+ await __runMigrations();
41
+ initialized = true;
42
+ }
43
+ catch (error) {
44
+ // Log the error clearly before re-throwing
45
+ console.error("=".repeat(60));
46
+ console.error("WORKER INITIALIZATION FAILED");
47
+ console.error("=".repeat(60));
48
+ console.error("Error:", error instanceof Error ? error.message : String(error));
49
+ if (error instanceof Error && error.stack) {
50
+ console.error("Stack:", error.stack);
51
+ }
52
+ console.error("=".repeat(60));
53
+ // Store the error so subsequent requests also fail clearly
54
+ initError = error;
55
+ throw error;
56
+ }
57
+ }
58
+ }
59
+ async function handleCronRequest(request, env) {
60
+ await ensureInit(env);
61
+ // Parse cron job name and scheduled time from request body
62
+ const body = (await request.json());
63
+ const { name } = body;
64
+ if (!name) {
65
+ return new Response(JSON.stringify({ success: false, error: "Missing cron job name" }), {
66
+ status: 400,
67
+ headers: { "Content-Type": "application/json" },
68
+ });
69
+ }
70
+ const handlers = __getCronHandlers();
71
+ const job = handlers.get(name);
72
+ if (!job) {
73
+ return new Response(JSON.stringify({ success: false, error: "Cron job not found: " + name }), {
74
+ status: 404,
75
+ headers: { "Content-Type": "application/json" },
76
+ });
77
+ }
78
+ try {
79
+ console.log("Running cron job " + name);
80
+ const result = await job.handler();
81
+ return new Response(JSON.stringify({
82
+ success: true,
83
+ result: result !== undefined ? result : null,
84
+ }), {
85
+ headers: { "Content-Type": "application/json" },
86
+ });
87
+ }
88
+ catch (error) {
89
+ const errorMessage = error instanceof Error ? error.message : String(error);
90
+ console.error("[cron:" + name + "] Error:", error);
91
+ return new Response(JSON.stringify({
92
+ success: false,
93
+ error: errorMessage,
94
+ }), {
95
+ status: 500,
96
+ headers: { "Content-Type": "application/json" },
97
+ });
98
+ }
99
+ }
100
+ return {
101
+ async fetch(request, env, ctx) {
102
+ const url = new URL(request.url);
103
+ // Handle internal cron requests
104
+ if (url.pathname === "/__cron" && request.method === "POST") {
105
+ return handleCronRequest(request, env);
106
+ }
107
+ // Handle plugin events (forwarded from plugin workers via internal paths)
108
+ const match = url.pathname.match(pluginPathPattern);
109
+ if (match?.[1] && request.method === "POST") {
110
+ const handler = pluginHandlers[match[1]];
111
+ if (handler) {
112
+ await ensureInit(env);
113
+ return handler(request);
114
+ }
115
+ }
116
+ await ensureInit(env);
117
+ return __getApp().fetch(request, env, ctx);
118
+ },
119
+ };
120
+ }
121
+ //# sourceMappingURL=entry.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"entry.js","sourceRoot":"","sources":["../../src/worker-runtime/entry.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "flingit",
3
- "version": "0.0.30",
3
+ "version": "0.0.32",
4
4
  "description": "Personal Software Platform - Build and deploy personal tools through conversation",
5
5
  "type": "module",
6
6
  "engines": {
@@ -38,6 +38,7 @@
38
38
  "files": [
39
39
  "dist",
40
40
  "!dist/cli-admin",
41
+ "!dist/cli-infra",
41
42
  "templates"
42
43
  ],
43
44
  "repository": {
@@ -51,6 +52,7 @@
51
52
  "start": "npm run cli --",
52
53
  "cli": "npm run build && node dist/cli/index.js",
53
54
  "cli-admin": "npm run build && node dist/cli-admin/index.js",
55
+ "cli-infra": "npm run build && node dist/cli-infra/index.js",
54
56
  "typecheck": "tsc --noEmit",
55
57
  "lint": "eslint src/ platform/",
56
58
  "lint:fix": "eslint src/ platform/ --fix",
@@ -1 +1,6 @@
1
- { "permissions": { "allow": ["Skill(fling)"] } }
1
+ {
2
+ "permissions": {
3
+ "defaultMode": "acceptEdits",
4
+ "allow": ["Skill(fling)", "Bash(npm start)", "Bash(npm exec fling:*)"]
5
+ }
6
+ }
@@ -1 +1 @@
1
- 22899e72887b1287e26b8e8d78f0a6e2
1
+ ad3649ed69feee6723e0a45eae1c3819
@@ -314,3 +314,18 @@ npm exec fling plugin remove discord # Disconnect, release all servers
314
314
  3. **One project per server** — A Discord server can only be claimed by one Fling project at a time.
315
315
 
316
316
  4. **Plugin must be installed first** — Run `fling plugin install discord` before using any Discord features. Check with `fling plugin permissions discord`.
317
+
318
+ 5. **Rate limit: 60 things/hour per project** — `reply`, `followup`, `sendMessage`, `editMessage`, and `addReaction` all count as "things". When exceeded, methods throw an error containing `PLUGIN_RATE_LIMIT_EXCEEDED`.
319
+
320
+ ```typescript
321
+ try {
322
+ await discord.sendMessage({ channelId, content: "Update" });
323
+ } catch (error) {
324
+ const message = error instanceof Error ? error.message : String(error);
325
+ if (message.includes("PLUGIN_RATE_LIMIT_EXCEEDED")) {
326
+ // Back off and retry in the next window.
327
+ return;
328
+ }
329
+ throw error;
330
+ }
331
+ ```
@@ -150,6 +150,7 @@ npm exec fling project slug:set <new-slug> # Change project slug (affects URL)
150
150
  npm exec fling cron list # List registered cron jobs
151
151
  npm exec fling cron history <name> # View invocation history
152
152
  npm exec fling cron trigger <name> # Manually trigger a cron job
153
+ npm exec fling cron trigger <name> --port 4000 # If dev API uses custom port
153
154
  npm exec fling storage list # List storage objects
154
155
  npm exec fling storage put <key> <file> # Upload file to storage
155
156
  npm exec fling storage get <key> [output] # Download object (stdout if no output)
@@ -189,6 +190,7 @@ npm exec fling -- --prod secret list # Deployed secrets
189
190
  npm exec fling -- --prod logs # Deployed logs
190
191
  npm exec fling -- --prod db sql "SELECT 1" # Deployed D1
191
192
  npm exec fling -- --prod storage list # R2 storage
193
+ npm exec fling -- --prod cron list # Deployed cron jobs
192
194
  ```
193
195
 
194
196
  **Note:** Production logs have a delay of ~10 seconds or more before they appear.
@@ -210,7 +212,7 @@ Just edit and save - changes appear immediately.
210
212
 
211
213
  ## Deployment
212
214
 
213
- When the user's request is complete and working locally, offer to deploy it. But if their app has a backend and no secure authorization method, you must make it clear to them that the deployed app will be visible on the internet, and that they should not expose anything that should not be publicly visible!
215
+ When you've completed the user's request and verified that the app works locally, EXPLICITLY OFFER to deploy it, but also tell them that they can try it locally first. If their app has a backend and no secure authorization method, you MUST make it VERY CLEAR to them that the deployed app will be visible on the internet, and explicitly ask them if they want to proceed!
214
216
 
215
217
  To deploy:
216
218
  **Run `npm exec fling push` directly** - you have bash access, don't ask the user to run commands.
@@ -253,7 +255,7 @@ Slugs must be:
253
255
 
254
256
  ## Security
255
257
 
256
- If the user does not want their app to be deployed because of security issues, or asks about that, offer implementing proper backend-supported security. In particular, suggest these two auth methods first:
258
+ If the user does not want their app to be deployed because of security issues, or asks about that, offer implementing proper backend-enforced security. In particular, suggest these two auth methods first:
257
259
 
258
260
  - Login with Google, with a filter on which emails/domains are allowed
259
261
  - Simple password-based auth, where the backend has a list of allowed passwords (hashed)
@@ -349,4 +351,8 @@ See API.md for detailed API reference, EXAMPLES.md for common patterns, and FEED
349
351
 
350
352
  ## Updates
351
353
 
352
- When the fling CLI mentions that there is a new version available, strongly suggest to the user to update, becasue it might contain bug fixes or new features.
354
+ When the fling CLI mentions that there is a new version available, strongly suggest to the user to update, becasue it might contain bug fixes or new features. To update, run
355
+
356
+ ```bash
357
+ npm install flingit@latest
358
+ ```
@@ -212,3 +212,18 @@ npm exec fling plugin remove slack # Disconnect, release all workspaces
212
212
  3. **One project per workspace** — A Slack workspace can only be claimed by one Fling project at a time.
213
213
 
214
214
  4. **Plugin must be installed first** — Run `fling plugin install slack` before using any Slack features. Check with `fling plugin permissions slack`.
215
+
216
+ 5. **Rate limit: 60 things/hour per project** — `sendMessage`, `editMessage`, `addReaction`, and thread replies all count as "things". When exceeded, methods throw an error containing `PLUGIN_RATE_LIMIT_EXCEEDED`.
217
+
218
+ ```typescript
219
+ try {
220
+ await slack.sendMessage({ channelId, text: "Update" });
221
+ } catch (error) {
222
+ const message = error instanceof Error ? error.message : String(error);
223
+ if (message.includes("PLUGIN_RATE_LIMIT_EXCEEDED")) {
224
+ // Back off and retry in the next window.
225
+ return;
226
+ }
227
+ throw error;
228
+ }
229
+ ```
@@ -3,19 +3,22 @@ import tailwindcss from "@tailwindcss/vite";
3
3
  import react from "@vitejs/plugin-react";
4
4
 
5
5
  export default defineConfig({
6
- base: process.env["VITE_BASE"] || "/",
7
- plugins: [tailwindcss(), react()],
8
- build: {
9
- outDir: "dist/client",
10
- emptyOutDir: true,
11
- },
12
- server: {
13
- port: 5173,
14
- proxy: {
15
- "/api": {
16
- target: "http://localhost:3210",
17
- changeOrigin: true,
18
- },
6
+ base: process.env["VITE_BASE"] || "/",
7
+ plugins: [tailwindcss(), react()],
8
+ build: {
9
+ outDir: "dist/client",
10
+ emptyOutDir: true,
11
+ },
12
+ server: {
13
+ port: 5173,
14
+ watch: {
15
+ ignored: ["**/.fling/**"],
16
+ },
17
+ proxy: {
18
+ "/api": {
19
+ target: "http://localhost:3210",
20
+ changeOrigin: true,
21
+ },
22
+ },
19
23
  },
20
- },
21
24
  });