@insforge/cli 0.1.20 → 0.1.23

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 CHANGED
@@ -137,6 +137,49 @@ insforge metadata
137
137
  insforge metadata --json
138
138
  ```
139
139
 
140
+ #### `insforge logs`
141
+
142
+ Fetch backend container logs.
143
+
144
+ ```bash
145
+ insforge logs <source> [options]
146
+ ```
147
+
148
+ **Sources:** `insforge.logs`, `postgREST.logs`, `postgres.logs`, `function.logs`
149
+
150
+ **Options:**
151
+ - `--limit <n>`: Number of log entries to return (default: 20)
152
+
153
+ **Examples:**
154
+ ```bash
155
+ insforge logs insforge.logs
156
+ insforge logs postgres.logs --limit 50
157
+ insforge logs function.logs --json
158
+ ```
159
+
160
+ #### `insforge docs`
161
+
162
+ Browse InsForge SDK documentation.
163
+
164
+ ```bash
165
+ insforge docs [feature] [language]
166
+ ```
167
+
168
+ **Features:** `db`, `storage`, `functions`, `auth`, `ai`, `realtime`, `instructions`
169
+ **Languages:** `typescript`, `swift`, `kotlin`, `rest-api`
170
+
171
+ **Examples:**
172
+ ```bash
173
+ # List all available docs
174
+ insforge docs
175
+
176
+ # Specific feature/language docs
177
+ insforge docs instructions # Show backend setup instructions
178
+ insforge docs db typescript # Show TypeScript database SDK docs
179
+ insforge docs auth swift # Show Swift auth SDK docs
180
+ insforge docs storage rest-api # Show REST API storage docs
181
+ ```
182
+
140
183
  ---
141
184
 
142
185
  ### Database — `insforge db`
@@ -258,6 +301,15 @@ insforge functions invoke my-function --method GET
258
301
  insforge functions invoke my-function --data '{"key": "value"}' --json
259
302
  ```
260
303
 
304
+ #### `insforge functions delete <slug>`
305
+
306
+ Delete an edge function.
307
+
308
+ ```bash
309
+ insforge functions delete my-function
310
+ insforge functions delete my-function -y # skip confirmation
311
+ ```
312
+
261
313
  ---
262
314
 
263
315
  ### Storage — `insforge storage`
@@ -409,6 +461,64 @@ insforge secrets delete STRIPE_API_KEY
409
461
  insforge secrets delete STRIPE_API_KEY -y # skip confirmation
410
462
  ```
411
463
 
464
+ ### Schedules — `insforge schedules`
465
+
466
+ Manage scheduled tasks (cron jobs).
467
+
468
+ #### `insforge schedules list`
469
+
470
+ List all schedules in the current project.
471
+
472
+ ```bash
473
+ insforge schedules list
474
+ insforge schedules list --json
475
+ ```
476
+
477
+ #### `insforge schedules create`
478
+
479
+ Create a new scheduled task.
480
+
481
+ ```bash
482
+ insforge schedules create --name "daily-cleanup" --cron "0 0 * * *" --url "https://api.example.com/cleanup" --method POST
483
+ insforge schedules create --name "hourly-sync" --cron "0 * * * *" --url "https://api.example.com/sync" --method GET --headers '{"Authorization": "Bearer xxx"}'
484
+ ```
485
+
486
+ #### `insforge schedules get <id>`
487
+
488
+ Get details of a specific schedule.
489
+
490
+ ```bash
491
+ insforge schedules get <id>
492
+ insforge schedules get 123 --json
493
+ ```
494
+
495
+ #### `insforge schedules update <id>`
496
+
497
+ Update an existing schedule.
498
+
499
+ ```bash
500
+ insforge schedules update <id> --name "weekly-cleanup" --cron "0 0 * * 0"
501
+ insforge schedules update 123 --active false
502
+ ```
503
+
504
+ #### `insforge schedules delete <id>`
505
+
506
+ Delete a schedule.
507
+
508
+ ```bash
509
+ insforge schedules delete <id>
510
+ insforge schedules delete 123 -y
511
+ ```
512
+
513
+ #### `insforge schedules logs <id>`
514
+
515
+ Fetch execution logs for a specific schedule.
516
+
517
+ ```bash
518
+ insforge schedules logs <id>
519
+ insforge schedules logs 123 --limit 100
520
+ ```
521
+
412
522
  ---
413
523
 
414
524
  ## Project Configuration
package/dist/index.js CHANGED
@@ -720,6 +720,16 @@ async function installSkills(json) {
720
720
  } catch {
721
721
  if (!json) clack5.log.warn("Failed to install agent skills. You can run manually: npx skills add insforge/agent-skills -s insforge -s insforge-cli");
722
722
  }
723
+ try {
724
+ if (!json) clack5.log.info("Installing find-skills...");
725
+ await execAsync("npx skills add https://github.com/vercel-labs/skills --skill find-skills -y", {
726
+ cwd: process.cwd(),
727
+ timeout: 6e4
728
+ });
729
+ if (!json) clack5.log.success("find-skills installed.");
730
+ } catch {
731
+ if (!json) clack5.log.warn("Failed to install find-skills. You can run manually: npx skills add https://github.com/vercel-labs/skills --skill find-skills");
732
+ }
723
733
  try {
724
734
  updateGitignore();
725
735
  } catch {
@@ -1433,8 +1443,8 @@ Specify --file <path> or create ${join3("insforge", "functions", slug, "index.ts
1433
1443
  }
1434
1444
  }
1435
1445
  }
1436
- await reportCliUsage("cli.functions.deploy", !deployFailed);
1437
- if (deployFailed) process.exit(1);
1446
+ if (deployFailed) throw new CLIError("Function deployment failed");
1447
+ await reportCliUsage("cli.functions.deploy", true);
1438
1448
  } catch (err) {
1439
1449
  await reportCliUsage("cli.functions.deploy", false);
1440
1450
  handleError(err, json);
@@ -1470,20 +1480,14 @@ function registerFunctionsInvokeCommand(functionsCmd2) {
1470
1480
  if (json) {
1471
1481
  outputJson({ status, body: data });
1472
1482
  } else {
1473
- if (status >= 400) {
1474
- console.error(`HTTP ${status}`);
1475
- }
1476
1483
  console.log(JSON.stringify(data, null, 2));
1477
1484
  }
1478
1485
  } else {
1479
1486
  const text3 = await res.text();
1480
- if (!json && status >= 400) {
1481
- console.error(`HTTP ${status}`);
1482
- }
1483
1487
  console.log(text3);
1484
1488
  }
1485
1489
  if (status >= 400) {
1486
- process.exit(1);
1490
+ throw new CLIError(`HTTP ${status}`, 1, "HTTP_ERROR");
1487
1491
  }
1488
1492
  } catch (err) {
1489
1493
  handleError(err, json);
@@ -1964,7 +1968,7 @@ async function copyDir(src, dest) {
1964
1968
  }
1965
1969
  }
1966
1970
  function registerCreateCommand(program2) {
1967
- program2.command("create").description("Create a new InsForge project").option("--name <name>", "Project name").option("--org-id <id>", "Organization ID").option("--region <region>", "Deployment region (us-east, us-west, eu-central, ap-southeast)").option("--template <template>", "Template to use: react, nextjs, or empty").action(async (opts, cmd) => {
1971
+ program2.command("create").description("Create a new InsForge project").option("--name <name>", "Project name").option("--org-id <id>", "Organization ID").option("--region <region>", "Deployment region (us-east, us-west, eu-central, ap-southeast)").option("--template <template>", "Template to use: react, nextjs, chatbot, or empty").action(async (opts, cmd) => {
1968
1972
  const { json, apiUrl } = getRootOpts(cmd);
1969
1973
  try {
1970
1974
  await requireAuth(apiUrl);
@@ -2013,6 +2017,7 @@ function registerCreateCommand(program2) {
2013
2017
  options: [
2014
2018
  { value: "react", label: "Web app template with React" },
2015
2019
  { value: "nextjs", label: "Web app template with Next.js" },
2020
+ { value: "chatbot", label: "AI Chatbot with Next.js" },
2016
2021
  { value: "empty", label: "Empty project" }
2017
2022
  ]
2018
2023
  });
@@ -2038,7 +2043,9 @@ function registerCreateCommand(program2) {
2038
2043
  saveProjectConfig(projectConfig);
2039
2044
  s?.stop(`Project "${project.name}" created and linked`);
2040
2045
  const hasTemplate = template !== "empty";
2041
- if (hasTemplate) {
2046
+ if (template === "chatbot") {
2047
+ await downloadGitHubTemplate("chatbot", projectConfig, json);
2048
+ } else if (hasTemplate) {
2042
2049
  await downloadTemplate(template, projectConfig, projectName, json, apiUrl);
2043
2050
  }
2044
2051
  await installCliGlobally(json);
@@ -2144,6 +2151,72 @@ async function downloadTemplate(framework, projectConfig, projectName, json, _ap
2144
2151
  }
2145
2152
  }
2146
2153
  }
2154
+ async function downloadGitHubTemplate(templateName, projectConfig, json) {
2155
+ const s = !json ? clack10.spinner() : null;
2156
+ s?.start(`Downloading ${templateName} template...`);
2157
+ const tempDir = path2.join(tmpdir(), `insforge-template-${Date.now()}`);
2158
+ try {
2159
+ await fs2.mkdir(tempDir, { recursive: true });
2160
+ await execAsync2(
2161
+ "git clone --depth 1 https://github.com/InsForge/insforge-templates.git .",
2162
+ { cwd: tempDir, maxBuffer: 10 * 1024 * 1024, timeout: 6e4 }
2163
+ );
2164
+ const templateDir = path2.join(tempDir, templateName);
2165
+ const stat3 = await fs2.stat(templateDir).catch(() => null);
2166
+ if (!stat3?.isDirectory()) {
2167
+ throw new Error(`Template "${templateName}" not found in repository`);
2168
+ }
2169
+ s?.message("Copying template files...");
2170
+ const cwd = process.cwd();
2171
+ await copyDir(templateDir, cwd);
2172
+ const envExamplePath = path2.join(cwd, ".env.example");
2173
+ const envExampleExists = await fs2.stat(envExamplePath).catch(() => null);
2174
+ if (envExampleExists) {
2175
+ const anonKey = await getAnonKey();
2176
+ const envExample = await fs2.readFile(envExamplePath, "utf-8");
2177
+ const envContent = envExample.replace(
2178
+ /^([A-Z_]+=)(.*)$/gm,
2179
+ (_, prefix, _value) => {
2180
+ const key = prefix.slice(0, -1);
2181
+ if (/INSFORGE.*(URL|BASE_URL)$/.test(key)) return `${prefix}${projectConfig.oss_host}`;
2182
+ if (/INSFORGE.*ANON_KEY$/.test(key)) return `${prefix}${anonKey}`;
2183
+ return `${prefix}${_value}`;
2184
+ }
2185
+ );
2186
+ await fs2.writeFile(path2.join(cwd, ".env.local"), envContent);
2187
+ }
2188
+ s?.stop(`${templateName} template downloaded`);
2189
+ const migrationPath = path2.join(cwd, "migrations", "db_int.sql");
2190
+ const migrationExists = await fs2.stat(migrationPath).catch(() => null);
2191
+ if (migrationExists) {
2192
+ const dbSpinner = !json ? clack10.spinner() : null;
2193
+ dbSpinner?.start("Running database migrations...");
2194
+ try {
2195
+ const sql = await fs2.readFile(migrationPath, "utf-8");
2196
+ await ossFetch("/api/database/advance/rawsql/unrestricted", {
2197
+ method: "POST",
2198
+ body: JSON.stringify({ query: sql })
2199
+ });
2200
+ dbSpinner?.stop("Database migrations applied");
2201
+ } catch (err) {
2202
+ dbSpinner?.stop("Database migration failed");
2203
+ if (!json) {
2204
+ clack10.log.warn(`Migration failed: ${err.message}`);
2205
+ clack10.log.info('You can run the migration manually: insforge db query --unrestricted "$(cat migrations/db_int.sql)"');
2206
+ }
2207
+ }
2208
+ }
2209
+ } catch (err) {
2210
+ s?.stop(`${templateName} template download failed`);
2211
+ if (!json) {
2212
+ clack10.log.warn(`Failed to download ${templateName} template: ${err.message}`);
2213
+ clack10.log.info("You can manually clone from: https://github.com/InsForge/insforge-templates");
2214
+ }
2215
+ } finally {
2216
+ await fs2.rm(tempDir, { recursive: true, force: true }).catch(() => {
2217
+ });
2218
+ }
2219
+ }
2147
2220
 
2148
2221
  // src/commands/info.ts
2149
2222
  function registerContextCommand(program2) {
@@ -2800,8 +2873,7 @@ function registerLogsCommand(program2) {
2800
2873
  await requireAuth();
2801
2874
  const resolved = SOURCE_LOOKUP.get(source.toLowerCase());
2802
2875
  if (!resolved) {
2803
- console.error(`Invalid log source "${source}". Valid sources: ${VALID_SOURCES.join(", ")}`);
2804
- process.exit(1);
2876
+ throw new CLIError(`Invalid log source "${source}". Valid sources: ${VALID_SOURCES.join(", ")}`);
2805
2877
  }
2806
2878
  const limit = parseInt(opts.limit, 10) || 20;
2807
2879
  const res = await ossFetch(`/api/logs/${encodeURIComponent(resolved)}?limit=${limit}`);