@recursiv/cli 0.1.11 → 0.1.13
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/LICENSE +105 -0
- package/dist/bin/create-recursiv-app.js +61 -9
- package/dist/bin/create-recursiv-app.js.map +1 -1
- package/dist/bin/recursiv.js +93 -20
- package/dist/bin/recursiv.js.map +1 -1
- package/package.json +12 -12
package/LICENSE
ADDED
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
# Functional Source License, Version 1.1, ALv2 Future License
|
|
2
|
+
|
|
3
|
+
## Abbreviation
|
|
4
|
+
|
|
5
|
+
FSL-1.1-ALv2
|
|
6
|
+
|
|
7
|
+
## Notice
|
|
8
|
+
|
|
9
|
+
Copyright 2026 Recursiv (recursivlabs)
|
|
10
|
+
|
|
11
|
+
## Terms and Conditions
|
|
12
|
+
|
|
13
|
+
### Licensor ("We")
|
|
14
|
+
|
|
15
|
+
The party offering the Software under these Terms and Conditions.
|
|
16
|
+
|
|
17
|
+
### The Software
|
|
18
|
+
|
|
19
|
+
The "Software" is each version of the software that we make available under
|
|
20
|
+
these Terms and Conditions, as indicated by our inclusion of these Terms and
|
|
21
|
+
Conditions with the Software.
|
|
22
|
+
|
|
23
|
+
### License Grant
|
|
24
|
+
|
|
25
|
+
Subject to your compliance with this License Grant and the Patents,
|
|
26
|
+
Redistribution and Trademark clauses below, we hereby grant you the right to
|
|
27
|
+
use, copy, modify, create derivative works, publicly perform, publicly display
|
|
28
|
+
and redistribute the Software for any Permitted Purpose identified below.
|
|
29
|
+
|
|
30
|
+
### Permitted Purpose
|
|
31
|
+
|
|
32
|
+
A Permitted Purpose is any purpose other than a Competing Use. A Competing Use
|
|
33
|
+
means making the Software available to others in a commercial product or
|
|
34
|
+
service that:
|
|
35
|
+
|
|
36
|
+
1. substitutes for the Software;
|
|
37
|
+
|
|
38
|
+
2. substitutes for any other product or service we offer using the Software
|
|
39
|
+
that exists as of the date we make the Software available; or
|
|
40
|
+
|
|
41
|
+
3. offers the same or substantially similar functionality as the Software.
|
|
42
|
+
|
|
43
|
+
Permitted Purposes specifically include using the Software:
|
|
44
|
+
|
|
45
|
+
1. for your internal use and access;
|
|
46
|
+
|
|
47
|
+
2. for non-commercial education;
|
|
48
|
+
|
|
49
|
+
3. for non-commercial research; and
|
|
50
|
+
|
|
51
|
+
4. in connection with professional services that you provide to a licensee
|
|
52
|
+
using the Software in accordance with these Terms and Conditions.
|
|
53
|
+
|
|
54
|
+
### Patents
|
|
55
|
+
|
|
56
|
+
To the extent your use for a Permitted Purpose would necessarily infringe our
|
|
57
|
+
patents, the license grant above includes a license under our patents. If you
|
|
58
|
+
make a claim against any party that the Software infringes or contributes to
|
|
59
|
+
the infringement of any patent, then your patent license to the Software ends
|
|
60
|
+
immediately.
|
|
61
|
+
|
|
62
|
+
### Redistribution
|
|
63
|
+
|
|
64
|
+
The Terms and Conditions apply to all copies, modifications and derivatives of
|
|
65
|
+
the Software.
|
|
66
|
+
|
|
67
|
+
If you redistribute any copies, modifications or derivatives of the Software,
|
|
68
|
+
you must include a copy of or a link to these Terms and Conditions and not
|
|
69
|
+
remove any copyright notices provided in or with the Software.
|
|
70
|
+
|
|
71
|
+
### Disclaimer
|
|
72
|
+
|
|
73
|
+
THE SOFTWARE IS PROVIDED "AS IS" AND WITHOUT WARRANTIES OF ANY KIND, EXPRESS OR
|
|
74
|
+
IMPLIED, INCLUDING WITHOUT LIMITATION WARRANTIES OF FITNESS FOR A PARTICULAR
|
|
75
|
+
PURPOSE, MERCHANTABILITY, TITLE OR NON-INFRINGEMENT.
|
|
76
|
+
|
|
77
|
+
IN NO EVENT WILL WE HAVE ANY LIABILITY TO YOU ARISING OUT OF OR RELATED TO THE
|
|
78
|
+
SOFTWARE, INCLUDING INDIRECT, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES,
|
|
79
|
+
EVEN IF WE HAVE BEEN INFORMED OF THEIR POSSIBILITY IN ADVANCE.
|
|
80
|
+
|
|
81
|
+
### Trademarks
|
|
82
|
+
|
|
83
|
+
Except for displaying the License Details and identifying us as the origin of
|
|
84
|
+
the Software, you have no right under these Terms and Conditions to use our
|
|
85
|
+
trademarks, trade names, service marks or product names.
|
|
86
|
+
|
|
87
|
+
## Grant of Future License
|
|
88
|
+
|
|
89
|
+
We hereby irrevocably grant you an additional license to use the Software under
|
|
90
|
+
the Apache License, Version 2.0 that is effective on the second anniversary of
|
|
91
|
+
the date we make the Software available. On or after that date, you may use the
|
|
92
|
+
Software under the Apache License, Version 2.0, in which case the following
|
|
93
|
+
will apply:
|
|
94
|
+
|
|
95
|
+
Licensed under the Apache License, Version 2.0 (the "License"); you may not use
|
|
96
|
+
this file except in compliance with the License.
|
|
97
|
+
|
|
98
|
+
You may obtain a copy of the License at
|
|
99
|
+
|
|
100
|
+
http://www.apache.org/licenses/LICENSE-2.0
|
|
101
|
+
|
|
102
|
+
Unless required by applicable law or agreed to in writing, software distributed
|
|
103
|
+
under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
|
|
104
|
+
CONDITIONS OF ANY KIND, either express or implied. See the License for the
|
|
105
|
+
specific language governing permissions and limitations under the License.
|
|
@@ -11,6 +11,7 @@ import { execSync } from "child_process";
|
|
|
11
11
|
import prompts2 from "prompts";
|
|
12
12
|
import pc3 from "picocolors";
|
|
13
13
|
import ora2 from "ora";
|
|
14
|
+
import { Recursiv } from "@recursiv/sdk";
|
|
14
15
|
|
|
15
16
|
// src/lib/logger.ts
|
|
16
17
|
import pc from "picocolors";
|
|
@@ -71,14 +72,29 @@ function createConfig(opts) {
|
|
|
71
72
|
// src/lib/env.ts
|
|
72
73
|
import { readFile as readFile2, writeFile as writeFile2 } from "fs/promises";
|
|
73
74
|
import { join as join2 } from "path";
|
|
74
|
-
async function writeEnvFile(dir,
|
|
75
|
+
async function writeEnvFile(dir, values) {
|
|
76
|
+
const v = typeof values === "string" ? { apiKey: values } : values;
|
|
75
77
|
const envPath = join2(dir, ".env");
|
|
76
|
-
const
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
""
|
|
80
|
-
|
|
81
|
-
|
|
78
|
+
const lines = [];
|
|
79
|
+
if (v.apiKey) {
|
|
80
|
+
lines.push("# Recursiv API key (developer key \u2014 CLI tooling and server scripts).");
|
|
81
|
+
lines.push("# The nextjs template does NOT use this at runtime: each user gets their");
|
|
82
|
+
lines.push("# own project-scoped key at sign-up, stored in an httpOnly cookie.");
|
|
83
|
+
lines.push(`RECURSIV_API_KEY=${v.apiKey}`);
|
|
84
|
+
lines.push("");
|
|
85
|
+
}
|
|
86
|
+
if (v.projectId !== void 0) {
|
|
87
|
+
lines.push("# Project that owns your database/storage; per-user API keys are scoped");
|
|
88
|
+
lines.push("# to it and signed-up users become project_members.");
|
|
89
|
+
lines.push(`RECURSIV_PROJECT_ID=${v.projectId}`);
|
|
90
|
+
lines.push("");
|
|
91
|
+
}
|
|
92
|
+
if (v.baseUrl) {
|
|
93
|
+
lines.push("# Only for staging / self-host.");
|
|
94
|
+
lines.push(`RECURSIV_API_BASE_URL=${v.baseUrl}`);
|
|
95
|
+
lines.push("");
|
|
96
|
+
}
|
|
97
|
+
await writeFile2(envPath, lines.join("\n"), "utf-8");
|
|
82
98
|
}
|
|
83
99
|
|
|
84
100
|
// src/lib/templates.ts
|
|
@@ -475,8 +491,30 @@ async function initCommand(nameArg) {
|
|
|
475
491
|
devCommand: runCommand(pm, "dev")
|
|
476
492
|
});
|
|
477
493
|
await writeConfig(projectDir, config);
|
|
494
|
+
let projectId = null;
|
|
495
|
+
if (apiKey && template.framework === "nextjs") {
|
|
496
|
+
const provisionSpinner = ora2("Provisioning Recursiv project...").start();
|
|
497
|
+
try {
|
|
498
|
+
const sdk = new Recursiv({ apiKey });
|
|
499
|
+
const { data: project } = await sdk.projects.create({ name: projectName });
|
|
500
|
+
await sdk.projectSettings.setAllowPublicSignup(project.id, {
|
|
501
|
+
allow_public_signup: true
|
|
502
|
+
});
|
|
503
|
+
projectId = project.id;
|
|
504
|
+
provisionSpinner.succeed(`Recursiv project provisioned (${pc3.dim(project.id)})`);
|
|
505
|
+
} catch {
|
|
506
|
+
provisionSpinner.warn(
|
|
507
|
+
"Could not provision a project \u2014 set RECURSIV_PROJECT_ID in .env (dashboard or Recursiv MCP) and enable public signup."
|
|
508
|
+
);
|
|
509
|
+
}
|
|
510
|
+
}
|
|
478
511
|
if (apiKey) {
|
|
479
|
-
await writeEnvFile(projectDir,
|
|
512
|
+
await writeEnvFile(projectDir, {
|
|
513
|
+
apiKey,
|
|
514
|
+
// Placeholder (empty) when provisioning failed, so the required var is
|
|
515
|
+
// visible in .env rather than silently absent.
|
|
516
|
+
...template.framework === "nextjs" ? { projectId: projectId ?? "" } : {}
|
|
517
|
+
});
|
|
480
518
|
}
|
|
481
519
|
const gitignorePath = resolve(projectDir, ".gitignore");
|
|
482
520
|
if (!existsSync2(gitignorePath)) {
|
|
@@ -526,9 +564,23 @@ async function initCommand(nameArg) {
|
|
|
526
564
|
log.blank();
|
|
527
565
|
}
|
|
528
566
|
|
|
567
|
+
// src/lib/version.ts
|
|
568
|
+
import { createRequire } from "module";
|
|
569
|
+
function getCliVersion() {
|
|
570
|
+
const require2 = createRequire(import.meta.url);
|
|
571
|
+
for (const candidate of ["../../package.json", "../package.json"]) {
|
|
572
|
+
try {
|
|
573
|
+
const pkg = require2(candidate);
|
|
574
|
+
if (pkg.name === "@recursiv/cli" && pkg.version) return pkg.version;
|
|
575
|
+
} catch {
|
|
576
|
+
}
|
|
577
|
+
}
|
|
578
|
+
return "0.0.0";
|
|
579
|
+
}
|
|
580
|
+
|
|
529
581
|
// src/bin/create-recursiv-app.ts
|
|
530
582
|
var program = new Command();
|
|
531
|
-
program.name("create-recursiv-app").description("Create a new Recursiv application").version(
|
|
583
|
+
program.name("create-recursiv-app").description("Create a new Recursiv application").version(getCliVersion()).argument("[name]", "project name").action(async (name) => {
|
|
532
584
|
try {
|
|
533
585
|
await initCommand(name);
|
|
534
586
|
} catch (err) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/bin/create-recursiv-app.ts","../../src/commands/init.ts","../../src/lib/logger.ts","../../src/lib/config.ts","../../src/lib/env.ts","../../src/lib/templates.ts","../../src/lib/auth-flow.ts","../../src/lib/package-manager.ts","../../src/templates/registry.ts"],"sourcesContent":["import { Command } from 'commander';\nimport { initCommand } from '../commands/init.js';\n\nconst program = new Command();\n\nprogram\n .name('create-recursiv-app')\n .description('Create a new Recursiv application')\n .version('0.1.0')\n .argument('[name]', 'project name')\n .action(async (name?: string) => {\n try {\n await initCommand(name);\n } catch (err) {\n console.error(err instanceof Error ? err.message : err);\n process.exit(1);\n }\n });\n\nprogram.parse();\n","import { existsSync } from 'node:fs';\nimport { mkdir, readFile, unlink, writeFile } from 'node:fs/promises';\nimport { resolve, basename } from 'node:path';\nimport { execSync } from 'node:child_process';\nimport prompts from 'prompts';\nimport pc from 'picocolors';\nimport ora from 'ora';\nimport { log, banner } from '../lib/logger.js';\nimport { createConfig, writeConfig } from '../lib/config.js';\nimport { writeEnvFile } from '../lib/env.js';\nimport { cloneTemplate } from '../lib/templates.js';\nimport { getOrCreateApiKey } from '../lib/auth-flow.js';\nimport { detectFromUserAgent, installCommand, runCommand } from '../lib/package-manager.js';\nimport { templates, type Template } from '../templates/registry.js';\n\nexport async function initCommand(nameArg?: string): Promise<void> {\n banner();\n\n const totalSteps = 7;\n\n // 1. Project name\n log.step(1, totalSteps, 'Project setup');\n let projectName: string;\n if (nameArg) {\n projectName = nameArg;\n } else {\n const { name } = await prompts(\n {\n type: 'text',\n name: 'name',\n message: 'Project name',\n initial: 'my-recursiv-app',\n validate: (v: string) => (v.trim() ? true : 'Name is required'),\n },\n { onCancel: () => process.exit(1) }\n );\n projectName = name;\n }\n\n const projectDir = resolve(process.cwd(), projectName);\n\n if (existsSync(projectDir)) {\n log.error(`Directory ${pc.bold(projectName)} already exists`);\n process.exit(1);\n }\n\n // 2. Template selection\n log.step(2, totalSteps, 'Choose a template');\n const templateChoices = templates.map((t) => ({\n title: t.recommended ? `${t.name} ${pc.dim('(recommended)')}` : t.name,\n description: t.description,\n value: t.id,\n }));\n\n const { templateId } = await prompts(\n {\n type: 'select',\n name: 'templateId',\n message: 'Template',\n choices: templateChoices,\n initial: 0,\n },\n { onCancel: () => process.exit(1) }\n );\n\n const template = templates.find((t) => t.id === templateId) as Template;\n\n // 3. API key\n log.step(3, totalSteps, 'Authentication');\n const apiKey = await getOrCreateApiKey('https://api.recursiv.io/api/v1');\n\n // 4. Clone template\n log.step(4, totalSteps, 'Creating project');\n await mkdir(projectDir, { recursive: true });\n await cloneTemplate(template, projectDir);\n\n // Fix: ensure next.config is valid JS (templates may ship TypeScript syntax in .mjs/.ts)\n const nextConfigTs = resolve(projectDir, 'next.config.ts');\n const nextConfigMjs = resolve(projectDir, 'next.config.mjs');\n const cleanNextConfig = '/** @type {import(\"next\").NextConfig} */\\nconst nextConfig = {};\\nexport default nextConfig;\\n';\n\n if (existsSync(nextConfigTs)) {\n await unlink(nextConfigTs);\n // Remove stale .mjs if template shipped both\n if (existsSync(nextConfigMjs)) await unlink(nextConfigMjs);\n await writeFile(nextConfigMjs, cleanNextConfig, 'utf-8');\n } else if (existsSync(nextConfigMjs)) {\n // Template may have .mjs with TypeScript syntax (import type) — replace it\n const content = await readFile(nextConfigMjs, 'utf-8');\n if (content.includes('import type') || content.includes(': NextConfig')) {\n await writeFile(nextConfigMjs, cleanNextConfig, 'utf-8');\n }\n }\n\n // 5. Write config files\n log.step(5, totalSteps, 'Writing config');\n const pm = detectFromUserAgent();\n\n const config = createConfig({\n name: projectName,\n template: template.id,\n framework: template.framework,\n port: template.devPort,\n devCommand: runCommand(pm, 'dev'),\n });\n await writeConfig(projectDir, config);\n\n if (apiKey) {\n await writeEnvFile(projectDir, apiKey);\n }\n\n // Write .gitignore if it doesn't exist\n const gitignorePath = resolve(projectDir, '.gitignore');\n if (!existsSync(gitignorePath)) {\n await writeFile(\n gitignorePath,\n ['node_modules', 'dist', '.env', '.env.local', '.next', '.turbo', ''].join('\\n'),\n 'utf-8'\n );\n }\n\n // 6. Install dependencies\n log.step(6, totalSteps, 'Installing dependencies');\n const installSpinner = ora(`Running ${pc.bold(installCommand(pm))}...`).start();\n try {\n execSync(installCommand(pm), {\n cwd: projectDir,\n stdio: 'pipe',\n timeout: 120_000,\n });\n installSpinner.succeed('Dependencies installed');\n } catch {\n installSpinner.warn('Could not install dependencies — run install manually');\n }\n\n // 7. Init git\n log.step(7, totalSteps, 'Initializing git');\n try {\n execSync('git init', { cwd: projectDir, stdio: 'pipe' });\n execSync('git add -A', { cwd: projectDir, stdio: 'pipe' });\n execSync('git commit -m \"Initial commit from create-recursiv-app\"', {\n cwd: projectDir,\n stdio: 'pipe',\n });\n log.success('Git repository initialized');\n } catch {\n log.warn('Could not initialize git repository');\n }\n\n // Done!\n log.blank();\n console.log(pc.bold(pc.green('Your Recursiv app is ready!')));\n log.blank();\n console.log(' Next steps:');\n console.log();\n console.log(` ${pc.dim('$')} cd ${projectName}`);\n if (!apiKey) {\n console.log(` ${pc.dim('$')} ${pc.dim('# Add your API key to .env')}`);\n }\n console.log(` ${pc.dim('$')} ${runCommand(pm, 'dev')}`);\n log.blank();\n console.log(pc.dim('Docs: https://docs.recursiv.io'));\n console.log(pc.dim('Dashboard: https://recursiv.io/dashboard'));\n log.blank();\n}\n","import pc from 'picocolors';\n\nexport const log = {\n info(msg: string) {\n console.log(pc.cyan('info') + ' ' + msg);\n },\n success(msg: string) {\n console.log(pc.green('ok') + ' ' + msg);\n },\n warn(msg: string) {\n console.log(pc.yellow('warn') + ' ' + msg);\n },\n error(msg: string) {\n console.error(pc.red('error') + ' ' + msg);\n },\n step(n: number, total: number, msg: string) {\n console.log(pc.dim(`[${n}/${total}]`) + ' ' + msg);\n },\n blank() {\n console.log();\n },\n};\n\nexport function banner() {\n console.log();\n console.log(pc.bold('Recursiv') + pc.dim(' — build and ship apps with AI'));\n console.log();\n}\n","import { readFile, writeFile } from 'node:fs/promises';\nimport { join } from 'node:path';\n\nexport interface RecursivConfig {\n version: number;\n project: {\n name: string;\n template: string;\n framework: string;\n };\n api: {\n baseUrl: string;\n };\n dev: {\n port: number;\n command: string;\n };\n}\n\nconst CONFIG_FILE = '.recursiv.json';\n\nexport function configPath(dir: string): string {\n return join(dir, CONFIG_FILE);\n}\n\nexport async function readConfig(dir: string): Promise<RecursivConfig | null> {\n try {\n const raw = await readFile(configPath(dir), 'utf-8');\n return JSON.parse(raw) as RecursivConfig;\n } catch {\n return null;\n }\n}\n\nexport async function writeConfig(dir: string, config: RecursivConfig): Promise<void> {\n await writeFile(configPath(dir), JSON.stringify(config, null, 2) + '\\n', 'utf-8');\n}\n\nexport function createConfig(opts: {\n name: string;\n template: string;\n framework: string;\n port?: number;\n devCommand?: string;\n}): RecursivConfig {\n return {\n version: 1,\n project: {\n name: opts.name,\n template: opts.template,\n framework: opts.framework,\n },\n api: {\n baseUrl: 'https://api.recursiv.io/api/v1',\n },\n dev: {\n port: opts.port ?? 3000,\n command: opts.devCommand ?? 'npm run dev',\n },\n };\n}\n","import { readFile, writeFile } from 'node:fs/promises';\nimport { join } from 'node:path';\n\nexport async function writeEnvFile(dir: string, apiKey: string): Promise<void> {\n const envPath = join(dir, '.env');\n const content = [\n '# Recursiv API key',\n `RECURSIV_API_KEY=${apiKey}`,\n '',\n ].join('\\n');\n await writeFile(envPath, content, 'utf-8');\n}\n\nexport async function readApiKeyFromEnv(dir: string): Promise<string | null> {\n try {\n const raw = await readFile(join(dir, '.env'), 'utf-8');\n const match = raw.match(/^RECURSIV_API_KEY=(.+)$/m);\n return match?.[1]?.trim() ?? null;\n } catch {\n return null;\n }\n}\n\nexport async function updateApiKeyInEnv(dir: string, apiKey: string): Promise<void> {\n const envPath = join(dir, '.env');\n let content: string;\n try {\n content = await readFile(envPath, 'utf-8');\n if (content.includes('RECURSIV_API_KEY=')) {\n content = content.replace(/^RECURSIV_API_KEY=.+$/m, `RECURSIV_API_KEY=${apiKey}`);\n } else {\n content += `\\nRECURSIV_API_KEY=${apiKey}\\n`;\n }\n } catch {\n content = `RECURSIV_API_KEY=${apiKey}\\n`;\n }\n await writeFile(envPath, content, 'utf-8');\n}\n","import degit from 'degit';\nimport ora from 'ora';\nimport type { Template } from '../templates/registry.js';\n\nexport async function cloneTemplate(template: Template, dest: string): Promise<void> {\n const spinner = ora(`Cloning ${template.name} template...`).start();\n try {\n const emitter = degit(template.repo, {\n cache: false,\n force: true,\n verbose: false,\n });\n\n await emitter.clone(dest);\n spinner.succeed(`Template cloned successfully`);\n } catch (err) {\n spinner.fail('Failed to clone template');\n const message = err instanceof Error ? err.message : String(err);\n throw new Error(\n `Could not clone template from ${template.repo}.\\n` +\n `Make sure you have internet access and the repo exists.\\n` +\n `Details: ${message}`\n );\n }\n}\n","import prompts from 'prompts';\nimport pc from 'picocolors';\nimport { log } from './logger.js';\n\nconst API_KEY_PATTERN = /^sk_(live|test)_[a-zA-Z0-9]{20,}$/;\nconst DASHBOARD_URL = 'https://recursiv.io/dashboard/api-keys';\n\n// Mirror the server's AVAILABLE_SCOPES list (packages/server/src/features/\n// api-keys/apiKeys.router.ts). The dashboard's create-key form defaults to\n// all-selected; keep the CLI quickstart in lockstep so a freshly-minted\n// key works for the full build-your-first-app flow (which needs at minimum\n// organizations:write for provision_app + projects:write for deploy).\nconst DEFAULT_SCOPES = [\n 'posts:read',\n 'posts:write',\n 'users:read',\n 'users:write',\n 'communities:read',\n 'communities:write',\n 'chat:read',\n 'chat:write',\n 'projects:read',\n 'projects:write',\n 'agents:read',\n 'agents:write',\n 'organizations:read',\n 'organizations:write',\n 'memory:read',\n 'memory:write',\n 'notifications:read',\n 'notifications:write',\n 'settings:read',\n 'settings:write',\n 'tags:read',\n 'tags:write',\n 'uploads:write',\n 'commands:read',\n 'commands:write',\n 'billing:read',\n 'billing:write',\n];\n\nexport function isValidKeyFormat(key: string): boolean {\n return API_KEY_PATTERN.test(key);\n}\n\nexport async function validateApiKey(apiKey: string, baseUrl: string): Promise<boolean> {\n try {\n const res = await fetch(`${baseUrl}/users/me`, {\n headers: {\n Authorization: `Bearer ${apiKey}`,\n Accept: 'application/json',\n },\n });\n return res.ok;\n } catch {\n return false;\n }\n}\n\n/**\n * Terminal signup flow — create a Recursiv account and API key without a browser.\n *\n * Flow (passwordless OTP):\n * 1. Prompt for email\n * 2. Send 6-digit OTP code via server\n * 3. Prompt user to enter the code\n * 4. Verify OTP → get session cookie\n * 5. Use session cookie to create an API key via REST endpoint\n * 6. Return the API key string\n */\nexport async function terminalSignup(baseUrl: string): Promise<string | null> {\n // baseUrl is like https://recursiv.io/api/v1 — we need the root for auth endpoints\n const rootUrl = baseUrl.replace(/\\/api\\/v1\\/?$/, '');\n\n console.log();\n console.log(pc.bold('Sign in to Recursiv'));\n console.log(pc.dim('We\\'ll send a 6-digit code to your email — no password needed.'));\n console.log(pc.dim('If your email isn\\'t registered yet, we\\'ll create a free account.'));\n console.log(pc.dim('Free tier: 1,000 API calls/day, 1 agent, 3 projects'));\n console.log();\n\n const { email } = await prompts(\n {\n type: 'text',\n name: 'email',\n message: 'Email',\n validate: (v: string) => {\n if (!v || !v.includes('@')) return 'Valid email required';\n return true;\n },\n },\n { onCancel: () => process.exit(1) },\n );\n\n // Better Auth requires an Origin header for CSRF protection\n const origin = rootUrl || 'https://api.recursiv.io';\n const authHeaders = { 'Content-Type': 'application/json', Origin: origin };\n\n // Send OTP code\n try {\n const sendRes = await fetch(`${rootUrl}/api/auth/email-otp/send-verification-otp`, {\n method: 'POST',\n headers: authHeaders,\n body: JSON.stringify({ email, type: 'sign-in' }),\n });\n\n if (!sendRes.ok) {\n const errBody = await sendRes.text().catch(() => '');\n log.error(`Failed to send code (${sendRes.status}): ${errBody || 'Unknown error'}`);\n return null;\n }\n } catch (err) {\n log.error(\n `Could not reach ${rootUrl} — ${err instanceof Error ? err.message : 'network error'}`,\n );\n log.info(`You can create an API key manually at ${pc.underline(DASHBOARD_URL)}`);\n return null;\n }\n\n log.success(`Verification code sent to ${email}`);\n console.log();\n\n // Prompt for OTP code\n const { code } = await prompts(\n {\n type: 'text',\n name: 'code',\n message: 'Enter the 6-digit code',\n validate: (v: string) => {\n if (!v || !/^\\d{6}$/.test(v.trim())) return 'Enter the 6-digit code from your email';\n return true;\n },\n },\n { onCancel: () => process.exit(1) },\n );\n\n // Verify OTP and sign in. better-auth's email-OTP plugin exposes two\n // separate endpoints:\n // - /api/auth/sign-in/email-otp → signs an existing user in (returns\n // a session), or creates the account if disableSignUp is false (our\n // config). This is the right endpoint for both first-time signup and\n // returning sign-in.\n // - /api/auth/email-otp/verify-email → strictly for confirming an\n // already-signed-in user's email address. Existing users hit\n // \"Invalid OTP\" here because the OTP was minted with type='sign-in'.\n let sessionCookie: string | null = null;\n\n try {\n const verifyRes = await fetch(`${rootUrl}/api/auth/sign-in/email-otp`, {\n method: 'POST',\n headers: authHeaders,\n body: JSON.stringify({ email, otp: code.trim() }),\n redirect: 'manual',\n });\n\n if (verifyRes.ok || verifyRes.status === 302) {\n sessionCookie = extractSessionCookie(verifyRes);\n } else {\n const errBody = await verifyRes.text().catch(() => '');\n log.error(`Verification failed (${verifyRes.status}): ${errBody || 'Invalid or expired code'}`);\n return null;\n }\n } catch (err) {\n log.error(\n `Verification failed — ${err instanceof Error ? err.message : 'network error'}`,\n );\n return null;\n }\n\n if (!sessionCookie) {\n log.error('No session returned from server');\n log.info(`Create an API key manually at ${pc.underline(DASHBOARD_URL)}`);\n return null;\n }\n\n log.success('Authenticated');\n\n // Create an API key using the session\n try {\n const keyRes = await fetch(`${baseUrl}/api-keys`, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n Cookie: sessionCookie,\n },\n body: JSON.stringify({\n name: 'CLI (auto-created)',\n scopes: DEFAULT_SCOPES,\n }),\n });\n\n if (!keyRes.ok) {\n const errBody = await keyRes.text().catch(() => '');\n log.error(`Failed to create API key (${keyRes.status}): ${errBody}`);\n log.info(`Create one manually at ${pc.underline(DASHBOARD_URL)}`);\n return null;\n }\n\n const { data } = (await keyRes.json()) as { data: { key: string } };\n log.success(`API key created (free tier: 1,000 calls/day)`);\n return data.key;\n } catch (err) {\n log.error(\n `Failed to create API key: ${err instanceof Error ? err.message : 'unknown error'}`,\n );\n log.info(`Create one manually at ${pc.underline(DASHBOARD_URL)}`);\n return null;\n }\n}\n\n/**\n * Extract session cookie from a Better Auth response.\n * Better Auth sets cookies via set-cookie header.\n */\nfunction extractSessionCookie(res: Response): string | null {\n // In Node.js, getSetCookie() returns individual cookie strings\n const setCookieHeaders =\n 'getSetCookie' in res.headers\n ? (res.headers as { getSetCookie(): string[] }).getSetCookie()\n : [];\n\n // Fall back to raw header if getSetCookie not available\n if (setCookieHeaders.length === 0) {\n const raw = res.headers.get('set-cookie');\n if (raw) {\n // Multiple cookies may be comma-separated (though non-standard)\n return raw\n .split(',')\n .map((c) => c.split(';')[0].trim())\n .join('; ');\n }\n return null;\n }\n\n // Extract cookie name=value pairs (strip attributes)\n return setCookieHeaders.map((c) => c.split(';')[0].trim()).join('; ');\n}\n\nexport async function promptApiKey(): Promise<string | null> {\n console.log(\n pc.dim(\n `Get your API key from ${pc.underline(DASHBOARD_URL)}\\n` +\n `Keys start with sk_live_ or sk_test_`,\n ),\n );\n console.log();\n\n const { apiKey } = await prompts(\n {\n type: 'text',\n name: 'apiKey',\n message: 'API key (or press Enter to skip)',\n validate: (value: string) => {\n if (!value) return true; // allow skip\n if (!isValidKeyFormat(value)) {\n return 'Invalid key format. Keys start with sk_live_ or sk_test_';\n }\n return true;\n },\n },\n { onCancel: () => process.exit(1) },\n );\n\n if (!apiKey) {\n log.warn('No API key provided — you can add one later in .env');\n return null;\n }\n\n return apiKey;\n}\n\nexport async function promptAndValidateApiKey(baseUrl: string): Promise<string | null> {\n const apiKey = await promptApiKey();\n if (!apiKey) return null;\n\n const valid = await validateApiKey(apiKey, baseUrl);\n if (!valid) {\n log.warn('Could not validate API key (server unreachable or invalid key)');\n log.info('Key saved to .env — you can update it later');\n } else {\n log.success('API key validated');\n }\n\n return apiKey;\n}\n\n/**\n * Get an API key — either from env, by prompting for an existing key,\n * or by creating a new account via terminal signup.\n */\nexport async function getOrCreateApiKey(baseUrl: string): Promise<string | null> {\n console.log();\n\n const { method } = await prompts(\n {\n type: 'select',\n name: 'method',\n message: 'How would you like to authenticate?',\n choices: [\n {\n title: 'Sign in or create an account with my email',\n description: 'We\\'ll send a 6-digit code. Works whether you\\'re new or returning.',\n value: 'signup',\n },\n {\n title: 'Paste an existing API key',\n description: 'Use a key you\\'ve already saved from the dashboard',\n value: 'paste',\n },\n {\n title: 'Skip for now',\n description: 'Add an API key later in .env',\n value: 'skip',\n },\n ],\n },\n { onCancel: () => process.exit(1) },\n );\n\n if (method === 'signup') {\n return terminalSignup(baseUrl);\n }\n\n if (method === 'paste') {\n return promptAndValidateApiKey(baseUrl);\n }\n\n log.warn('No API key — you can add one later in .env');\n return null;\n}\n","import { existsSync } from 'node:fs';\nimport { join } from 'node:path';\n\nexport type PackageManager = 'npm' | 'pnpm' | 'yarn' | 'bun';\n\nexport function detectPackageManager(dir: string): PackageManager {\n if (existsSync(join(dir, 'bun.lockb')) || existsSync(join(dir, 'bun.lock'))) return 'bun';\n if (existsSync(join(dir, 'pnpm-lock.yaml'))) return 'pnpm';\n if (existsSync(join(dir, 'yarn.lock'))) return 'yarn';\n return 'npm';\n}\n\nexport function detectFromUserAgent(): PackageManager {\n const ua = process.env.npm_config_user_agent ?? '';\n if (ua.startsWith('bun')) return 'bun';\n if (ua.startsWith('pnpm')) return 'pnpm';\n if (ua.startsWith('yarn')) return 'yarn';\n return 'npm';\n}\n\nexport function installCommand(pm: PackageManager): string {\n return pm === 'yarn' ? 'yarn' : `${pm} install`;\n}\n\nexport function runCommand(pm: PackageManager, script: string): string {\n if (pm === 'npm') return `npm run ${script}`;\n return `${pm} ${script}`;\n}\n","export interface Template {\n id: string;\n name: string;\n description: string;\n repo: string;\n framework: string;\n devCommand: string;\n devPort: number;\n recommended?: boolean;\n}\n\nexport const templates: Template[] = [\n {\n id: 'nextjs',\n name: 'Next.js + Recursiv',\n description: 'Full-stack Next.js app with feeds, chat, and agents',\n repo: 'recursivlabs/template-nextjs',\n framework: 'nextjs',\n devCommand: 'next dev',\n devPort: 3000,\n recommended: true,\n },\n {\n id: 'node',\n name: 'Node.js + Express',\n description: 'Express API server with Recursiv SDK',\n repo: 'recursivlabs/template-node',\n framework: 'express',\n devCommand: 'node --watch src/index.js',\n devPort: 4000,\n },\n {\n id: 'vite',\n name: 'Vite + React',\n description: 'React SPA with social feed components',\n repo: 'recursivlabs/template-vite',\n framework: 'vite',\n devCommand: 'vite',\n devPort: 5173,\n },\n];\n\nexport function getTemplate(id: string): Template | undefined {\n return templates.find((t) => t.id === id);\n}\n\nexport function getDefaultTemplate(): Template {\n return templates.find((t) => t.recommended) ?? templates[0]!;\n}\n"],"mappings":"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","names":["existsSync","readFile","writeFile","prompts","pc","ora","readFile","writeFile","join","pc","pc","join","prompts","existsSync","pc","writeFile","readFile","ora"]}
|
|
1
|
+
{"version":3,"sources":["../../src/bin/create-recursiv-app.ts","../../src/commands/init.ts","../../src/lib/logger.ts","../../src/lib/config.ts","../../src/lib/env.ts","../../src/lib/templates.ts","../../src/lib/auth-flow.ts","../../src/lib/package-manager.ts","../../src/templates/registry.ts","../../src/lib/version.ts"],"sourcesContent":["import { Command } from 'commander';\nimport { initCommand } from '../commands/init.js';\nimport { getCliVersion } from '../lib/version.js';\n\nconst program = new Command();\n\nprogram\n .name('create-recursiv-app')\n .description('Create a new Recursiv application')\n .version(getCliVersion())\n .argument('[name]', 'project name')\n .action(async (name?: string) => {\n try {\n await initCommand(name);\n } catch (err) {\n console.error(err instanceof Error ? err.message : err);\n process.exit(1);\n }\n });\n\nprogram.parse();\n","import { existsSync } from 'node:fs';\nimport { mkdir, readFile, unlink, writeFile } from 'node:fs/promises';\nimport { resolve, basename } from 'node:path';\nimport { execSync } from 'node:child_process';\nimport prompts from 'prompts';\nimport pc from 'picocolors';\nimport ora from 'ora';\nimport { Recursiv } from '@recursiv/sdk';\nimport { log, banner } from '../lib/logger.js';\nimport { createConfig, writeConfig } from '../lib/config.js';\nimport { writeEnvFile } from '../lib/env.js';\nimport { cloneTemplate } from '../lib/templates.js';\nimport { getOrCreateApiKey } from '../lib/auth-flow.js';\nimport { detectFromUserAgent, installCommand, runCommand } from '../lib/package-manager.js';\nimport { templates, type Template } from '../templates/registry.js';\n\nexport async function initCommand(nameArg?: string): Promise<void> {\n banner();\n\n const totalSteps = 7;\n\n // 1. Project name\n log.step(1, totalSteps, 'Project setup');\n let projectName: string;\n if (nameArg) {\n projectName = nameArg;\n } else {\n const { name } = await prompts(\n {\n type: 'text',\n name: 'name',\n message: 'Project name',\n initial: 'my-recursiv-app',\n validate: (v: string) => (v.trim() ? true : 'Name is required'),\n },\n { onCancel: () => process.exit(1) }\n );\n projectName = name;\n }\n\n const projectDir = resolve(process.cwd(), projectName);\n\n if (existsSync(projectDir)) {\n log.error(`Directory ${pc.bold(projectName)} already exists`);\n process.exit(1);\n }\n\n // 2. Template selection\n log.step(2, totalSteps, 'Choose a template');\n const templateChoices = templates.map((t) => ({\n title: t.recommended ? `${t.name} ${pc.dim('(recommended)')}` : t.name,\n description: t.description,\n value: t.id,\n }));\n\n const { templateId } = await prompts(\n {\n type: 'select',\n name: 'templateId',\n message: 'Template',\n choices: templateChoices,\n initial: 0,\n },\n { onCancel: () => process.exit(1) }\n );\n\n const template = templates.find((t) => t.id === templateId) as Template;\n\n // 3. API key\n log.step(3, totalSteps, 'Authentication');\n const apiKey = await getOrCreateApiKey('https://api.recursiv.io/api/v1');\n\n // 4. Clone template\n log.step(4, totalSteps, 'Creating project');\n await mkdir(projectDir, { recursive: true });\n await cloneTemplate(template, projectDir);\n\n // Fix: ensure next.config is valid JS (templates may ship TypeScript syntax in .mjs/.ts)\n const nextConfigTs = resolve(projectDir, 'next.config.ts');\n const nextConfigMjs = resolve(projectDir, 'next.config.mjs');\n const cleanNextConfig = '/** @type {import(\"next\").NextConfig} */\\nconst nextConfig = {};\\nexport default nextConfig;\\n';\n\n if (existsSync(nextConfigTs)) {\n await unlink(nextConfigTs);\n // Remove stale .mjs if template shipped both\n if (existsSync(nextConfigMjs)) await unlink(nextConfigMjs);\n await writeFile(nextConfigMjs, cleanNextConfig, 'utf-8');\n } else if (existsSync(nextConfigMjs)) {\n // Template may have .mjs with TypeScript syntax (import type) — replace it\n const content = await readFile(nextConfigMjs, 'utf-8');\n if (content.includes('import type') || content.includes(': NextConfig')) {\n await writeFile(nextConfigMjs, cleanNextConfig, 'utf-8');\n }\n }\n\n // 5. Write config files\n log.step(5, totalSteps, 'Writing config');\n const pm = detectFromUserAgent();\n\n const config = createConfig({\n name: projectName,\n template: template.id,\n framework: template.framework,\n port: template.devPort,\n devCommand: runCommand(pm, 'dev'),\n });\n await writeConfig(projectDir, config);\n\n // The nextjs template runs on the per-user-key pattern: it needs a\n // RECURSIV_PROJECT_ID (with public signup enabled) at runtime, not the\n // developer API key. Provision one now so the scaffold works first-run.\n let projectId: string | null = null;\n if (apiKey && template.framework === 'nextjs') {\n const provisionSpinner = ora('Provisioning Recursiv project...').start();\n try {\n const sdk = new Recursiv({ apiKey });\n const { data: project } = await sdk.projects.create({ name: projectName });\n await sdk.projectSettings.setAllowPublicSignup(project.id, {\n allow_public_signup: true,\n });\n projectId = project.id;\n provisionSpinner.succeed(`Recursiv project provisioned (${pc.dim(project.id)})`);\n } catch {\n provisionSpinner.warn(\n 'Could not provision a project — set RECURSIV_PROJECT_ID in .env (dashboard or Recursiv MCP) and enable public signup.'\n );\n }\n }\n\n if (apiKey) {\n await writeEnvFile(projectDir, {\n apiKey,\n // Placeholder (empty) when provisioning failed, so the required var is\n // visible in .env rather than silently absent.\n ...(template.framework === 'nextjs' ? { projectId: projectId ?? '' } : {}),\n });\n }\n\n // Write .gitignore if it doesn't exist\n const gitignorePath = resolve(projectDir, '.gitignore');\n if (!existsSync(gitignorePath)) {\n await writeFile(\n gitignorePath,\n ['node_modules', 'dist', '.env', '.env.local', '.next', '.turbo', ''].join('\\n'),\n 'utf-8'\n );\n }\n\n // 6. Install dependencies\n log.step(6, totalSteps, 'Installing dependencies');\n const installSpinner = ora(`Running ${pc.bold(installCommand(pm))}...`).start();\n try {\n execSync(installCommand(pm), {\n cwd: projectDir,\n stdio: 'pipe',\n timeout: 120_000,\n });\n installSpinner.succeed('Dependencies installed');\n } catch {\n installSpinner.warn('Could not install dependencies — run install manually');\n }\n\n // 7. Init git\n log.step(7, totalSteps, 'Initializing git');\n try {\n execSync('git init', { cwd: projectDir, stdio: 'pipe' });\n execSync('git add -A', { cwd: projectDir, stdio: 'pipe' });\n execSync('git commit -m \"Initial commit from create-recursiv-app\"', {\n cwd: projectDir,\n stdio: 'pipe',\n });\n log.success('Git repository initialized');\n } catch {\n log.warn('Could not initialize git repository');\n }\n\n // Done!\n log.blank();\n console.log(pc.bold(pc.green('Your Recursiv app is ready!')));\n log.blank();\n console.log(' Next steps:');\n console.log();\n console.log(` ${pc.dim('$')} cd ${projectName}`);\n if (!apiKey) {\n console.log(` ${pc.dim('$')} ${pc.dim('# Add your API key to .env')}`);\n }\n console.log(` ${pc.dim('$')} ${runCommand(pm, 'dev')}`);\n log.blank();\n console.log(pc.dim('Docs: https://docs.recursiv.io'));\n console.log(pc.dim('Dashboard: https://recursiv.io/dashboard'));\n log.blank();\n}\n","import pc from 'picocolors';\n\nexport const log = {\n info(msg: string) {\n console.log(pc.cyan('info') + ' ' + msg);\n },\n success(msg: string) {\n console.log(pc.green('ok') + ' ' + msg);\n },\n warn(msg: string) {\n console.log(pc.yellow('warn') + ' ' + msg);\n },\n error(msg: string) {\n console.error(pc.red('error') + ' ' + msg);\n },\n step(n: number, total: number, msg: string) {\n console.log(pc.dim(`[${n}/${total}]`) + ' ' + msg);\n },\n blank() {\n console.log();\n },\n};\n\nexport function banner() {\n console.log();\n console.log(pc.bold('Recursiv') + pc.dim(' — build and ship apps with AI'));\n console.log();\n}\n","import { readFile, writeFile } from 'node:fs/promises';\nimport { join } from 'node:path';\n\nexport interface RecursivConfig {\n version: number;\n project: {\n name: string;\n template: string;\n framework: string;\n };\n api: {\n baseUrl: string;\n };\n dev: {\n port: number;\n command: string;\n };\n}\n\nconst CONFIG_FILE = '.recursiv.json';\n\nexport function configPath(dir: string): string {\n return join(dir, CONFIG_FILE);\n}\n\nexport async function readConfig(dir: string): Promise<RecursivConfig | null> {\n try {\n const raw = await readFile(configPath(dir), 'utf-8');\n return JSON.parse(raw) as RecursivConfig;\n } catch {\n return null;\n }\n}\n\nexport async function writeConfig(dir: string, config: RecursivConfig): Promise<void> {\n await writeFile(configPath(dir), JSON.stringify(config, null, 2) + '\\n', 'utf-8');\n}\n\nexport function createConfig(opts: {\n name: string;\n template: string;\n framework: string;\n port?: number;\n devCommand?: string;\n}): RecursivConfig {\n return {\n version: 1,\n project: {\n name: opts.name,\n template: opts.template,\n framework: opts.framework,\n },\n api: {\n baseUrl: 'https://api.recursiv.io/api/v1',\n },\n dev: {\n port: opts.port ?? 3000,\n command: opts.devCommand ?? 'npm run dev',\n },\n };\n}\n","import { readFile, writeFile } from 'node:fs/promises';\nimport { join } from 'node:path';\n\nexport interface EnvValues {\n /** Developer API key. Used by CLI tooling and the node/vite templates. */\n apiKey?: string;\n /**\n * Project the app's per-user keys are scoped to. Required at runtime by the\n * nextjs template (per-user key pattern — no platform key on the server).\n */\n projectId?: string;\n /** Only for staging / self-host. */\n baseUrl?: string;\n}\n\nexport async function writeEnvFile(dir: string, values: string | EnvValues): Promise<void> {\n // Back-compat: a bare string is the developer API key.\n const v: EnvValues = typeof values === 'string' ? { apiKey: values } : values;\n const envPath = join(dir, '.env');\n const lines: string[] = [];\n\n if (v.apiKey) {\n lines.push('# Recursiv API key (developer key — CLI tooling and server scripts).');\n lines.push('# The nextjs template does NOT use this at runtime: each user gets their');\n lines.push('# own project-scoped key at sign-up, stored in an httpOnly cookie.');\n lines.push(`RECURSIV_API_KEY=${v.apiKey}`);\n lines.push('');\n }\n if (v.projectId !== undefined) {\n lines.push('# Project that owns your database/storage; per-user API keys are scoped');\n lines.push('# to it and signed-up users become project_members.');\n lines.push(`RECURSIV_PROJECT_ID=${v.projectId}`);\n lines.push('');\n }\n if (v.baseUrl) {\n lines.push('# Only for staging / self-host.');\n lines.push(`RECURSIV_API_BASE_URL=${v.baseUrl}`);\n lines.push('');\n }\n\n await writeFile(envPath, lines.join('\\n'), 'utf-8');\n}\n\nexport async function readApiKeyFromEnv(dir: string): Promise<string | null> {\n try {\n const raw = await readFile(join(dir, '.env'), 'utf-8');\n const match = raw.match(/^RECURSIV_API_KEY=(.+)$/m);\n return match?.[1]?.trim() ?? null;\n } catch {\n return null;\n }\n}\n\nexport async function updateApiKeyInEnv(dir: string, apiKey: string): Promise<void> {\n const envPath = join(dir, '.env');\n let content: string;\n try {\n content = await readFile(envPath, 'utf-8');\n if (content.includes('RECURSIV_API_KEY=')) {\n content = content.replace(/^RECURSIV_API_KEY=.+$/m, `RECURSIV_API_KEY=${apiKey}`);\n } else {\n content += `\\nRECURSIV_API_KEY=${apiKey}\\n`;\n }\n } catch {\n content = `RECURSIV_API_KEY=${apiKey}\\n`;\n }\n await writeFile(envPath, content, 'utf-8');\n}\n","import degit from 'degit';\nimport ora from 'ora';\nimport type { Template } from '../templates/registry.js';\n\nexport async function cloneTemplate(template: Template, dest: string): Promise<void> {\n const spinner = ora(`Cloning ${template.name} template...`).start();\n try {\n const emitter = degit(template.repo, {\n cache: false,\n force: true,\n verbose: false,\n });\n\n await emitter.clone(dest);\n spinner.succeed(`Template cloned successfully`);\n } catch (err) {\n spinner.fail('Failed to clone template');\n const message = err instanceof Error ? err.message : String(err);\n throw new Error(\n `Could not clone template from ${template.repo}.\\n` +\n `Make sure you have internet access and the repo exists.\\n` +\n `Details: ${message}`\n );\n }\n}\n","import prompts from 'prompts';\nimport pc from 'picocolors';\nimport { log } from './logger.js';\n\nconst API_KEY_PATTERN = /^sk_(live|test)_[a-zA-Z0-9]{20,}$/;\nconst DASHBOARD_URL = 'https://recursiv.io/dashboard/api-keys';\n\n// Mirror the server's AVAILABLE_SCOPES list (packages/server/src/features/\n// api-keys/apiKeys.router.ts). The dashboard's create-key form defaults to\n// all-selected; keep the CLI quickstart in lockstep so a freshly-minted\n// key works for the full build-your-first-app flow (which needs at minimum\n// organizations:write for provision_app + projects:write for deploy).\nconst DEFAULT_SCOPES = [\n 'posts:read',\n 'posts:write',\n 'users:read',\n 'users:write',\n 'communities:read',\n 'communities:write',\n 'chat:read',\n 'chat:write',\n 'projects:read',\n 'projects:write',\n 'agents:read',\n 'agents:write',\n 'organizations:read',\n 'organizations:write',\n 'memory:read',\n 'memory:write',\n 'notifications:read',\n 'notifications:write',\n 'settings:read',\n 'settings:write',\n 'tags:read',\n 'tags:write',\n 'uploads:write',\n 'commands:read',\n 'commands:write',\n 'billing:read',\n 'billing:write',\n];\n\nexport function isValidKeyFormat(key: string): boolean {\n return API_KEY_PATTERN.test(key);\n}\n\nexport async function validateApiKey(apiKey: string, baseUrl: string): Promise<boolean> {\n try {\n const res = await fetch(`${baseUrl}/users/me`, {\n headers: {\n Authorization: `Bearer ${apiKey}`,\n Accept: 'application/json',\n },\n });\n return res.ok;\n } catch {\n return false;\n }\n}\n\n/**\n * Terminal signup flow — create a Recursiv account and API key without a browser.\n *\n * Flow (passwordless OTP):\n * 1. Prompt for email\n * 2. Send 6-digit OTP code via server\n * 3. Prompt user to enter the code\n * 4. Verify OTP → get session cookie\n * 5. Use session cookie to create an API key via REST endpoint\n * 6. Return the API key string\n */\nexport async function terminalSignup(baseUrl: string): Promise<string | null> {\n // baseUrl is like https://recursiv.io/api/v1 — we need the root for auth endpoints\n const rootUrl = baseUrl.replace(/\\/api\\/v1\\/?$/, '');\n\n console.log();\n console.log(pc.bold('Sign in to Recursiv'));\n console.log(pc.dim('We\\'ll send a 6-digit code to your email — no password needed.'));\n console.log(pc.dim('If your email isn\\'t registered yet, we\\'ll create a free account.'));\n console.log(pc.dim('Free tier: 1,000 API calls/day, 1 agent, 3 projects'));\n console.log();\n\n const { email } = await prompts(\n {\n type: 'text',\n name: 'email',\n message: 'Email',\n validate: (v: string) => {\n if (!v || !v.includes('@')) return 'Valid email required';\n return true;\n },\n },\n { onCancel: () => process.exit(1) },\n );\n\n // Better Auth requires an Origin header for CSRF protection\n const origin = rootUrl || 'https://api.recursiv.io';\n const authHeaders = { 'Content-Type': 'application/json', Origin: origin };\n\n // Send OTP code\n try {\n const sendRes = await fetch(`${rootUrl}/api/auth/email-otp/send-verification-otp`, {\n method: 'POST',\n headers: authHeaders,\n body: JSON.stringify({ email, type: 'sign-in' }),\n });\n\n if (!sendRes.ok) {\n const errBody = await sendRes.text().catch(() => '');\n log.error(`Failed to send code (${sendRes.status}): ${errBody || 'Unknown error'}`);\n return null;\n }\n } catch (err) {\n log.error(\n `Could not reach ${rootUrl} — ${err instanceof Error ? err.message : 'network error'}`,\n );\n log.info(`You can create an API key manually at ${pc.underline(DASHBOARD_URL)}`);\n return null;\n }\n\n log.success(`Verification code sent to ${email}`);\n console.log();\n\n // Prompt for OTP code\n const { code } = await prompts(\n {\n type: 'text',\n name: 'code',\n message: 'Enter the 6-digit code',\n validate: (v: string) => {\n if (!v || !/^\\d{6}$/.test(v.trim())) return 'Enter the 6-digit code from your email';\n return true;\n },\n },\n { onCancel: () => process.exit(1) },\n );\n\n // Verify OTP and sign in. better-auth's email-OTP plugin exposes two\n // separate endpoints:\n // - /api/auth/sign-in/email-otp → signs an existing user in (returns\n // a session), or creates the account if disableSignUp is false (our\n // config). This is the right endpoint for both first-time signup and\n // returning sign-in.\n // - /api/auth/email-otp/verify-email → strictly for confirming an\n // already-signed-in user's email address. Existing users hit\n // \"Invalid OTP\" here because the OTP was minted with type='sign-in'.\n let sessionCookie: string | null = null;\n\n try {\n const verifyRes = await fetch(`${rootUrl}/api/auth/sign-in/email-otp`, {\n method: 'POST',\n headers: authHeaders,\n body: JSON.stringify({ email, otp: code.trim() }),\n redirect: 'manual',\n });\n\n if (verifyRes.ok || verifyRes.status === 302) {\n sessionCookie = extractSessionCookie(verifyRes);\n } else {\n const errBody = await verifyRes.text().catch(() => '');\n log.error(`Verification failed (${verifyRes.status}): ${errBody || 'Invalid or expired code'}`);\n return null;\n }\n } catch (err) {\n log.error(\n `Verification failed — ${err instanceof Error ? err.message : 'network error'}`,\n );\n return null;\n }\n\n if (!sessionCookie) {\n log.error('No session returned from server');\n log.info(`Create an API key manually at ${pc.underline(DASHBOARD_URL)}`);\n return null;\n }\n\n log.success('Authenticated');\n\n // Create an API key using the session\n try {\n const keyRes = await fetch(`${baseUrl}/api-keys`, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n Cookie: sessionCookie,\n },\n body: JSON.stringify({\n name: 'CLI (auto-created)',\n scopes: DEFAULT_SCOPES,\n }),\n });\n\n if (!keyRes.ok) {\n const errBody = await keyRes.text().catch(() => '');\n log.error(`Failed to create API key (${keyRes.status}): ${errBody}`);\n log.info(`Create one manually at ${pc.underline(DASHBOARD_URL)}`);\n return null;\n }\n\n const { data } = (await keyRes.json()) as { data: { key: string } };\n log.success(`API key created (free tier: 1,000 calls/day)`);\n return data.key;\n } catch (err) {\n log.error(\n `Failed to create API key: ${err instanceof Error ? err.message : 'unknown error'}`,\n );\n log.info(`Create one manually at ${pc.underline(DASHBOARD_URL)}`);\n return null;\n }\n}\n\n/**\n * Extract session cookie from a Better Auth response.\n * Better Auth sets cookies via set-cookie header.\n */\nfunction extractSessionCookie(res: Response): string | null {\n // In Node.js, getSetCookie() returns individual cookie strings\n const setCookieHeaders =\n 'getSetCookie' in res.headers\n ? (res.headers as { getSetCookie(): string[] }).getSetCookie()\n : [];\n\n // Fall back to raw header if getSetCookie not available\n if (setCookieHeaders.length === 0) {\n const raw = res.headers.get('set-cookie');\n if (raw) {\n // Multiple cookies may be comma-separated (though non-standard)\n return raw\n .split(',')\n .map((c) => c.split(';')[0].trim())\n .join('; ');\n }\n return null;\n }\n\n // Extract cookie name=value pairs (strip attributes)\n return setCookieHeaders.map((c) => c.split(';')[0].trim()).join('; ');\n}\n\nexport async function promptApiKey(): Promise<string | null> {\n console.log(\n pc.dim(\n `Get your API key from ${pc.underline(DASHBOARD_URL)}\\n` +\n `Keys start with sk_live_ or sk_test_`,\n ),\n );\n console.log();\n\n const { apiKey } = await prompts(\n {\n type: 'text',\n name: 'apiKey',\n message: 'API key (or press Enter to skip)',\n validate: (value: string) => {\n if (!value) return true; // allow skip\n if (!isValidKeyFormat(value)) {\n return 'Invalid key format. Keys start with sk_live_ or sk_test_';\n }\n return true;\n },\n },\n { onCancel: () => process.exit(1) },\n );\n\n if (!apiKey) {\n log.warn('No API key provided — you can add one later in .env');\n return null;\n }\n\n return apiKey;\n}\n\nexport async function promptAndValidateApiKey(baseUrl: string): Promise<string | null> {\n const apiKey = await promptApiKey();\n if (!apiKey) return null;\n\n const valid = await validateApiKey(apiKey, baseUrl);\n if (!valid) {\n log.warn('Could not validate API key (server unreachable or invalid key)');\n log.info('Key saved to .env — you can update it later');\n } else {\n log.success('API key validated');\n }\n\n return apiKey;\n}\n\n/**\n * Get an API key — either from env, by prompting for an existing key,\n * or by creating a new account via terminal signup.\n */\nexport async function getOrCreateApiKey(baseUrl: string): Promise<string | null> {\n console.log();\n\n const { method } = await prompts(\n {\n type: 'select',\n name: 'method',\n message: 'How would you like to authenticate?',\n choices: [\n {\n title: 'Sign in or create an account with my email',\n description: 'We\\'ll send a 6-digit code. Works whether you\\'re new or returning.',\n value: 'signup',\n },\n {\n title: 'Paste an existing API key',\n description: 'Use a key you\\'ve already saved from the dashboard',\n value: 'paste',\n },\n {\n title: 'Skip for now',\n description: 'Add an API key later in .env',\n value: 'skip',\n },\n ],\n },\n { onCancel: () => process.exit(1) },\n );\n\n if (method === 'signup') {\n return terminalSignup(baseUrl);\n }\n\n if (method === 'paste') {\n return promptAndValidateApiKey(baseUrl);\n }\n\n log.warn('No API key — you can add one later in .env');\n return null;\n}\n","import { existsSync } from 'node:fs';\nimport { join } from 'node:path';\n\nexport type PackageManager = 'npm' | 'pnpm' | 'yarn' | 'bun';\n\nexport function detectPackageManager(dir: string): PackageManager {\n if (existsSync(join(dir, 'bun.lockb')) || existsSync(join(dir, 'bun.lock'))) return 'bun';\n if (existsSync(join(dir, 'pnpm-lock.yaml'))) return 'pnpm';\n if (existsSync(join(dir, 'yarn.lock'))) return 'yarn';\n return 'npm';\n}\n\nexport function detectFromUserAgent(): PackageManager {\n const ua = process.env.npm_config_user_agent ?? '';\n if (ua.startsWith('bun')) return 'bun';\n if (ua.startsWith('pnpm')) return 'pnpm';\n if (ua.startsWith('yarn')) return 'yarn';\n return 'npm';\n}\n\nexport function installCommand(pm: PackageManager): string {\n return pm === 'yarn' ? 'yarn' : `${pm} install`;\n}\n\nexport function runCommand(pm: PackageManager, script: string): string {\n if (pm === 'npm') return `npm run ${script}`;\n return `${pm} ${script}`;\n}\n","export interface Template {\n id: string;\n name: string;\n description: string;\n repo: string;\n framework: string;\n devCommand: string;\n devPort: number;\n recommended?: boolean;\n}\n\nexport const templates: Template[] = [\n {\n id: 'nextjs',\n name: 'Next.js + Recursiv',\n description: 'Full-stack Next.js app with feeds, chat, and agents',\n repo: 'recursivlabs/template-nextjs',\n framework: 'nextjs',\n devCommand: 'next dev',\n devPort: 3000,\n recommended: true,\n },\n {\n id: 'node',\n name: 'Node.js + Express',\n description: 'Express API server with Recursiv SDK',\n repo: 'recursivlabs/template-node',\n framework: 'express',\n devCommand: 'node --watch src/index.js',\n devPort: 4000,\n },\n {\n id: 'vite',\n name: 'Vite + React',\n description: 'React SPA with social feed components',\n repo: 'recursivlabs/template-vite',\n framework: 'vite',\n devCommand: 'vite',\n devPort: 5173,\n },\n];\n\nexport function getTemplate(id: string): Template | undefined {\n return templates.find((t) => t.id === id);\n}\n\nexport function getDefaultTemplate(): Template {\n return templates.find((t) => t.recommended) ?? templates[0]!;\n}\n","/**\n * Resolve the CLI version from package.json at runtime so `--version`\n * never drifts from the published version (it was hardcoded to 0.1.0).\n *\n * Works both bundled (dist/bin/*.js → ../../package.json) and from source\n * (src/lib/version.ts → ../../package.json), since both live two levels\n * below the package root.\n */\nimport { createRequire } from 'node:module';\n\nexport function getCliVersion(): string {\n const require = createRequire(import.meta.url);\n for (const candidate of ['../../package.json', '../package.json']) {\n try {\n const pkg = require(candidate) as { name?: string; version?: string };\n if (pkg.name === '@recursiv/cli' && pkg.version) return pkg.version;\n } catch {\n // try next candidate\n }\n }\n return '0.0.0';\n}\n"],"mappings":"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","names":["existsSync","readFile","writeFile","prompts","pc","ora","readFile","writeFile","join","pc","pc","join","prompts","existsSync","pc","writeFile","readFile","ora","require"]}
|
package/dist/bin/recursiv.js
CHANGED
|
@@ -1,8 +1,13 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
var __defProp = Object.defineProperty;
|
|
3
3
|
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
4
|
-
var __esm = (fn, res) => function __init() {
|
|
5
|
-
|
|
4
|
+
var __esm = (fn, res, err) => function __init() {
|
|
5
|
+
if (err) throw err[0];
|
|
6
|
+
try {
|
|
7
|
+
return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
|
|
8
|
+
} catch (e) {
|
|
9
|
+
throw err = [e], e;
|
|
10
|
+
}
|
|
6
11
|
};
|
|
7
12
|
var __export = (target, all) => {
|
|
8
13
|
for (var name in all)
|
|
@@ -101,14 +106,29 @@ __export(env_exports, {
|
|
|
101
106
|
});
|
|
102
107
|
import { readFile as readFile2, writeFile as writeFile2 } from "fs/promises";
|
|
103
108
|
import { join as join2 } from "path";
|
|
104
|
-
async function writeEnvFile(dir,
|
|
109
|
+
async function writeEnvFile(dir, values) {
|
|
110
|
+
const v = typeof values === "string" ? { apiKey: values } : values;
|
|
105
111
|
const envPath = join2(dir, ".env");
|
|
106
|
-
const
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
""
|
|
110
|
-
|
|
111
|
-
|
|
112
|
+
const lines = [];
|
|
113
|
+
if (v.apiKey) {
|
|
114
|
+
lines.push("# Recursiv API key (developer key \u2014 CLI tooling and server scripts).");
|
|
115
|
+
lines.push("# The nextjs template does NOT use this at runtime: each user gets their");
|
|
116
|
+
lines.push("# own project-scoped key at sign-up, stored in an httpOnly cookie.");
|
|
117
|
+
lines.push(`RECURSIV_API_KEY=${v.apiKey}`);
|
|
118
|
+
lines.push("");
|
|
119
|
+
}
|
|
120
|
+
if (v.projectId !== void 0) {
|
|
121
|
+
lines.push("# Project that owns your database/storage; per-user API keys are scoped");
|
|
122
|
+
lines.push("# to it and signed-up users become project_members.");
|
|
123
|
+
lines.push(`RECURSIV_PROJECT_ID=${v.projectId}`);
|
|
124
|
+
lines.push("");
|
|
125
|
+
}
|
|
126
|
+
if (v.baseUrl) {
|
|
127
|
+
lines.push("# Only for staging / self-host.");
|
|
128
|
+
lines.push(`RECURSIV_API_BASE_URL=${v.baseUrl}`);
|
|
129
|
+
lines.push("");
|
|
130
|
+
}
|
|
131
|
+
await writeFile2(envPath, lines.join("\n"), "utf-8");
|
|
112
132
|
}
|
|
113
133
|
async function readApiKeyFromEnv(dir) {
|
|
114
134
|
try {
|
|
@@ -1221,11 +1241,12 @@ function addToolResult(session, toolUseId, result, isError = false) {
|
|
|
1221
1241
|
]
|
|
1222
1242
|
});
|
|
1223
1243
|
}
|
|
1224
|
-
var SESSIONS_DIR, MAX_HISTORY_MESSAGES;
|
|
1244
|
+
var RECURSIV_HOME, SESSIONS_DIR, MAX_HISTORY_MESSAGES;
|
|
1225
1245
|
var init_session = __esm({
|
|
1226
1246
|
"src/chat/session.ts"() {
|
|
1227
1247
|
"use strict";
|
|
1228
|
-
|
|
1248
|
+
RECURSIV_HOME = process.env.RECURSIV_HOME || join6(homedir3(), ".recursiv");
|
|
1249
|
+
SESSIONS_DIR = join6(RECURSIV_HOME, "sessions");
|
|
1229
1250
|
MAX_HISTORY_MESSAGES = 100;
|
|
1230
1251
|
}
|
|
1231
1252
|
});
|
|
@@ -1924,10 +1945,10 @@ import { createInterface } from "readline";
|
|
|
1924
1945
|
import { existsSync as existsSync3, readFileSync } from "fs";
|
|
1925
1946
|
import { basename as basename2, join as join7 } from "path";
|
|
1926
1947
|
import { execSync as execSync2 } from "child_process";
|
|
1927
|
-
import { Recursiv, InsufficientCreditsError as InsufficientCreditsError2 } from "@recursiv/sdk";
|
|
1948
|
+
import { Recursiv as Recursiv2, InsufficientCreditsError as InsufficientCreditsError2 } from "@recursiv/sdk";
|
|
1928
1949
|
import pc15 from "picocolors";
|
|
1929
1950
|
async function startAgent(config) {
|
|
1930
|
-
const client = new
|
|
1951
|
+
const client = new Recursiv2({
|
|
1931
1952
|
apiKey: config.recursivApiKey,
|
|
1932
1953
|
baseUrl: config.recursivBaseUrl
|
|
1933
1954
|
});
|
|
@@ -2508,6 +2529,20 @@ function printCliError(err) {
|
|
|
2508
2529
|
console.error(err instanceof Error ? err.message : err);
|
|
2509
2530
|
}
|
|
2510
2531
|
|
|
2532
|
+
// src/lib/version.ts
|
|
2533
|
+
import { createRequire } from "module";
|
|
2534
|
+
function getCliVersion() {
|
|
2535
|
+
const require2 = createRequire(import.meta.url);
|
|
2536
|
+
for (const candidate of ["../../package.json", "../package.json"]) {
|
|
2537
|
+
try {
|
|
2538
|
+
const pkg = require2(candidate);
|
|
2539
|
+
if (pkg.name === "@recursiv/cli" && pkg.version) return pkg.version;
|
|
2540
|
+
} catch {
|
|
2541
|
+
}
|
|
2542
|
+
}
|
|
2543
|
+
return "0.0.0";
|
|
2544
|
+
}
|
|
2545
|
+
|
|
2511
2546
|
// src/commands/init.ts
|
|
2512
2547
|
init_logger();
|
|
2513
2548
|
init_config();
|
|
@@ -2519,6 +2554,7 @@ import { execSync } from "child_process";
|
|
|
2519
2554
|
import prompts2 from "prompts";
|
|
2520
2555
|
import pc4 from "picocolors";
|
|
2521
2556
|
import ora2 from "ora";
|
|
2557
|
+
import { Recursiv } from "@recursiv/sdk";
|
|
2522
2558
|
|
|
2523
2559
|
// src/lib/templates.ts
|
|
2524
2560
|
import degit from "degit";
|
|
@@ -2677,8 +2713,30 @@ async function initCommand(nameArg) {
|
|
|
2677
2713
|
devCommand: runCommand(pm, "dev")
|
|
2678
2714
|
});
|
|
2679
2715
|
await writeConfig(projectDir, config);
|
|
2716
|
+
let projectId = null;
|
|
2717
|
+
if (apiKey && template.framework === "nextjs") {
|
|
2718
|
+
const provisionSpinner = ora2("Provisioning Recursiv project...").start();
|
|
2719
|
+
try {
|
|
2720
|
+
const sdk = new Recursiv({ apiKey });
|
|
2721
|
+
const { data: project } = await sdk.projects.create({ name: projectName });
|
|
2722
|
+
await sdk.projectSettings.setAllowPublicSignup(project.id, {
|
|
2723
|
+
allow_public_signup: true
|
|
2724
|
+
});
|
|
2725
|
+
projectId = project.id;
|
|
2726
|
+
provisionSpinner.succeed(`Recursiv project provisioned (${pc4.dim(project.id)})`);
|
|
2727
|
+
} catch {
|
|
2728
|
+
provisionSpinner.warn(
|
|
2729
|
+
"Could not provision a project \u2014 set RECURSIV_PROJECT_ID in .env (dashboard or Recursiv MCP) and enable public signup."
|
|
2730
|
+
);
|
|
2731
|
+
}
|
|
2732
|
+
}
|
|
2680
2733
|
if (apiKey) {
|
|
2681
|
-
await writeEnvFile(projectDir,
|
|
2734
|
+
await writeEnvFile(projectDir, {
|
|
2735
|
+
apiKey,
|
|
2736
|
+
// Placeholder (empty) when provisioning failed, so the required var is
|
|
2737
|
+
// visible in .env rather than silently absent.
|
|
2738
|
+
...template.framework === "nextjs" ? { projectId: projectId ?? "" } : {}
|
|
2739
|
+
});
|
|
2682
2740
|
}
|
|
2683
2741
|
const gitignorePath = resolve(projectDir, ".gitignore");
|
|
2684
2742
|
if (!existsSync2(gitignorePath)) {
|
|
@@ -2894,10 +2952,25 @@ init_auth_flow();
|
|
|
2894
2952
|
init_config();
|
|
2895
2953
|
init_credentials();
|
|
2896
2954
|
import pc7 from "picocolors";
|
|
2897
|
-
async function loginCommand() {
|
|
2955
|
+
async function loginCommand(opts = {}) {
|
|
2898
2956
|
const cwd = process.cwd();
|
|
2899
2957
|
const config = await readConfig(cwd);
|
|
2900
2958
|
const baseUrl = config?.api.baseUrl ?? "https://api.recursiv.io/api/v1";
|
|
2959
|
+
if (opts.apiKey) {
|
|
2960
|
+
const provided = opts.apiKey.trim();
|
|
2961
|
+
if (!provided) {
|
|
2962
|
+
log.error("Provided --api-key is empty");
|
|
2963
|
+
process.exit(1);
|
|
2964
|
+
}
|
|
2965
|
+
await saveCredentials({ recursivApiKey: provided });
|
|
2966
|
+
log.success("API key saved to ~/.recursiv/credentials");
|
|
2967
|
+
try {
|
|
2968
|
+
await updateApiKeyInEnv(cwd, provided);
|
|
2969
|
+
log.success("Also saved to .env");
|
|
2970
|
+
} catch {
|
|
2971
|
+
}
|
|
2972
|
+
return;
|
|
2973
|
+
}
|
|
2901
2974
|
const existing = await resolveRecursivKey(cwd);
|
|
2902
2975
|
if (existing) {
|
|
2903
2976
|
log.info(`Existing key found: ${pc7.dim(maskKey(existing))}`);
|
|
@@ -3789,12 +3862,12 @@ import pc11 from "picocolors";
|
|
|
3789
3862
|
|
|
3790
3863
|
// src/lib/api.ts
|
|
3791
3864
|
init_config();
|
|
3792
|
-
|
|
3865
|
+
init_credentials();
|
|
3793
3866
|
init_logger();
|
|
3794
3867
|
import pc10 from "picocolors";
|
|
3795
3868
|
async function getApiContext(cwd) {
|
|
3796
3869
|
const config = await readConfig(cwd);
|
|
3797
|
-
const apiKey = await
|
|
3870
|
+
const apiKey = await resolveRecursivKey(cwd);
|
|
3798
3871
|
if (!apiKey) {
|
|
3799
3872
|
log.error("No API key configured.");
|
|
3800
3873
|
log.info(`Run ${pc10.bold("recursiv auth login")} to set your key.`);
|
|
@@ -4060,7 +4133,7 @@ function truncate(value, max) {
|
|
|
4060
4133
|
|
|
4061
4134
|
// src/bin/recursiv.ts
|
|
4062
4135
|
var program = new Command();
|
|
4063
|
-
program.name("recursiv").description("Recursiv CLI \u2014 build and ship apps with AI").version(
|
|
4136
|
+
program.name("recursiv").description("Recursiv CLI \u2014 build and ship apps with AI").version(getCliVersion());
|
|
4064
4137
|
program.command("chat").description("Start the chat-first AI agent (default when no command is given)").option("--model <model>", "Claude model to use (default: claude-sonnet-4-20250514)").option("--resume", "Resume the most recent session").action(async (opts) => {
|
|
4065
4138
|
try {
|
|
4066
4139
|
await launchChatAgent({ model: opts.model, resume: opts.resume });
|
|
@@ -4094,9 +4167,9 @@ program.command("deploy <target>").description("Generate deployment config (verc
|
|
|
4094
4167
|
}
|
|
4095
4168
|
});
|
|
4096
4169
|
var auth = program.command("auth").description("Manage API authentication");
|
|
4097
|
-
auth.command("login").description("Set or update your API key").action(async () => {
|
|
4170
|
+
auth.command("login").description("Set or update your API key").option("--api-key <key>", "API key to save non-interactively (for headless / CI use)").action(async (opts) => {
|
|
4098
4171
|
try {
|
|
4099
|
-
await loginCommand();
|
|
4172
|
+
await loginCommand({ apiKey: opts.apiKey });
|
|
4100
4173
|
} catch (err) {
|
|
4101
4174
|
printCliError(err);
|
|
4102
4175
|
process.exit(1);
|