@fold-run/cli 0.1.1
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 +157 -0
- package/dist/commands/create-tool.d.ts +8 -0
- package/dist/commands/create-tool.js +76 -0
- package/dist/commands/create-tool.js.map +1 -0
- package/dist/commands/delete.d.ts +3 -0
- package/dist/commands/delete.js +17 -0
- package/dist/commands/delete.js.map +1 -0
- package/dist/commands/deploy.d.ts +5 -0
- package/dist/commands/deploy.js +77 -0
- package/dist/commands/deploy.js.map +1 -0
- package/dist/commands/dev.d.ts +6 -0
- package/dist/commands/dev.js +178 -0
- package/dist/commands/dev.js.map +1 -0
- package/dist/commands/env-vars.d.ts +12 -0
- package/dist/commands/env-vars.js +40 -0
- package/dist/commands/env-vars.js.map +1 -0
- package/dist/commands/functions.d.ts +3 -0
- package/dist/commands/functions.js +26 -0
- package/dist/commands/functions.js.map +1 -0
- package/dist/commands/init.d.ts +3 -0
- package/dist/commands/init.js +94 -0
- package/dist/commands/init.js.map +1 -0
- package/dist/commands/login.d.ts +3 -0
- package/dist/commands/login.js +125 -0
- package/dist/commands/login.js.map +1 -0
- package/dist/commands/logs.d.ts +7 -0
- package/dist/commands/logs.js +81 -0
- package/dist/commands/logs.js.map +1 -0
- package/dist/commands/open.d.ts +3 -0
- package/dist/commands/open.js +24 -0
- package/dist/commands/open.js.map +1 -0
- package/dist/commands/rollback.d.ts +1 -0
- package/dist/commands/rollback.js +13 -0
- package/dist/commands/rollback.js.map +1 -0
- package/dist/commands/schedules.d.ts +11 -0
- package/dist/commands/schedules.js +49 -0
- package/dist/commands/schedules.js.map +1 -0
- package/dist/commands/secrets.d.ts +7 -0
- package/dist/commands/secrets.js +47 -0
- package/dist/commands/secrets.js.map +1 -0
- package/dist/commands/status.d.ts +3 -0
- package/dist/commands/status.js +62 -0
- package/dist/commands/status.js.map +1 -0
- package/dist/commands/tail.d.ts +4 -0
- package/dist/commands/tail.js +108 -0
- package/dist/commands/tail.js.map +1 -0
- package/dist/commands/templates.d.ts +8 -0
- package/dist/commands/templates.js +69 -0
- package/dist/commands/templates.js.map +1 -0
- package/dist/commands/webhooks.d.ts +8 -0
- package/dist/commands/webhooks.js +38 -0
- package/dist/commands/webhooks.js.map +1 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +259 -0
- package/dist/index.js.map +1 -0
- package/dist/lib/api.d.ts +12 -0
- package/dist/lib/api.js +31 -0
- package/dist/lib/api.js.map +1 -0
- package/dist/lib/bundler.d.ts +10 -0
- package/dist/lib/bundler.js +124 -0
- package/dist/lib/bundler.js.map +1 -0
- package/dist/lib/config.d.ts +9 -0
- package/dist/lib/config.js +56 -0
- package/dist/lib/config.js.map +1 -0
- package/dist/lib/output.d.ts +6 -0
- package/dist/lib/output.js +19 -0
- package/dist/lib/output.js.map +1 -0
- package/dist/lib/prompt.d.ts +5 -0
- package/dist/lib/prompt.js +40 -0
- package/dist/lib/prompt.js.map +1 -0
- package/package.json +50 -0
package/README.md
ADDED
|
@@ -0,0 +1,157 @@
|
|
|
1
|
+
# @fold-run/cli
|
|
2
|
+
|
|
3
|
+
Command-line interface for [fold.run](https://fold.run) — deploy serverless functions with automatic TypeScript bundling, secrets management, and AI-powered code generation.
|
|
4
|
+
|
|
5
|
+
## Install
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
npm install -g @fold-run/cli
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
Requires Node.js 18+.
|
|
12
|
+
|
|
13
|
+
## Quick start
|
|
14
|
+
|
|
15
|
+
```bash
|
|
16
|
+
# Authenticate
|
|
17
|
+
fold login
|
|
18
|
+
|
|
19
|
+
# Scaffold a new project
|
|
20
|
+
fold init my-worker
|
|
21
|
+
|
|
22
|
+
# Start local dev server
|
|
23
|
+
cd my-worker
|
|
24
|
+
fold dev
|
|
25
|
+
|
|
26
|
+
# Deploy
|
|
27
|
+
fold deploy
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
## Commands
|
|
31
|
+
|
|
32
|
+
### Getting started
|
|
33
|
+
|
|
34
|
+
| Command | Description |
|
|
35
|
+
|---------|-------------|
|
|
36
|
+
| `fold init [name]` | Scaffold a new project with `worker.ts` and `fold.json` |
|
|
37
|
+
| `fold login` | Authenticate via email/password or API token |
|
|
38
|
+
| `fold logout` | Remove saved credentials |
|
|
39
|
+
| `fold whoami` | Show current login info (API, tenant, role) |
|
|
40
|
+
| `fold status` | Show config and deployed functions |
|
|
41
|
+
|
|
42
|
+
### Deploy & manage
|
|
43
|
+
|
|
44
|
+
| Command | Description |
|
|
45
|
+
|---------|-------------|
|
|
46
|
+
| `fold deploy [file]` | Deploy a function (reads `fold.json` if no file given) |
|
|
47
|
+
| `fold dev [file]` | Start a local dev server on port 8787 |
|
|
48
|
+
| `fold functions` | List deployed functions |
|
|
49
|
+
| `fold delete <function-id>` | Delete a deployed function |
|
|
50
|
+
| `fold rollback <function-id> <version>` | Rollback to a previous version |
|
|
51
|
+
| `fold logs` | View activation logs (`-F` to follow, `-v` for details) |
|
|
52
|
+
|
|
53
|
+
### Configuration
|
|
54
|
+
|
|
55
|
+
| Command | Description |
|
|
56
|
+
|---------|-------------|
|
|
57
|
+
| `fold secrets set <name>` | Create or update a tenant secret (masked input) |
|
|
58
|
+
| `fold secrets list` | List secrets (names only) |
|
|
59
|
+
| `fold secrets delete <id>` | Delete a secret |
|
|
60
|
+
| `fold env set <name> -f <fn-id>` | Set a function environment variable (masked input) |
|
|
61
|
+
| `fold env list -f <fn-id>` | List function env vars |
|
|
62
|
+
| `fold env delete <name> -f <fn-id>` | Delete a function env var |
|
|
63
|
+
|
|
64
|
+
### Integrations
|
|
65
|
+
|
|
66
|
+
| Command | Description |
|
|
67
|
+
|---------|-------------|
|
|
68
|
+
| `fold webhooks list` | List webhook endpoints |
|
|
69
|
+
| `fold webhooks create <fn-id> <path>` | Create a webhook endpoint |
|
|
70
|
+
| `fold webhooks delete <id>` | Delete a webhook endpoint |
|
|
71
|
+
| `fold schedules list` | List scheduled triggers |
|
|
72
|
+
| `fold schedules create <fn-id> <cron>` | Create a cron trigger |
|
|
73
|
+
| `fold schedules toggle <fn-id>` | Enable or disable a schedule |
|
|
74
|
+
| `fold schedules delete <fn-id>` | Delete a schedule |
|
|
75
|
+
|
|
76
|
+
### AI & templates
|
|
77
|
+
|
|
78
|
+
| Command | Description |
|
|
79
|
+
|---------|-------------|
|
|
80
|
+
| `fold create-tool <name> <description>` | Generate and deploy an MCP tool from natural language |
|
|
81
|
+
| `fold tools list` | List deployed MCP tools |
|
|
82
|
+
| `fold templates list` | Browse the template marketplace |
|
|
83
|
+
| `fold templates get <name>` | View a template's details and code |
|
|
84
|
+
| `fold templates deploy <name>` | Deploy a function from a template |
|
|
85
|
+
|
|
86
|
+
## fold.json
|
|
87
|
+
|
|
88
|
+
The project manifest configures defaults for `fold deploy`:
|
|
89
|
+
|
|
90
|
+
```json
|
|
91
|
+
{
|
|
92
|
+
"name": "my-worker",
|
|
93
|
+
"entrypoint": "worker.ts",
|
|
94
|
+
"tenant_id": "tenant-abc",
|
|
95
|
+
"intent": {
|
|
96
|
+
"trigger": "http",
|
|
97
|
+
"bindings": ["KV:cache", "D1:db"]
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
| Field | Description |
|
|
103
|
+
|-------|-------------|
|
|
104
|
+
| `name` | Function name (used if `--name` not passed) |
|
|
105
|
+
| `entrypoint` | File to deploy (used if no file argument given) |
|
|
106
|
+
| `tenant_id` | Default tenant (overridden by `--tenant` flag or config) |
|
|
107
|
+
| `intent.trigger` | Trigger type hint |
|
|
108
|
+
| `intent.bindings` | Resource bindings in `TYPE:name` format |
|
|
109
|
+
|
|
110
|
+
## Global options
|
|
111
|
+
|
|
112
|
+
| Flag | Description |
|
|
113
|
+
|------|-------------|
|
|
114
|
+
| `--json` | Output results as JSON (works with `functions`, `logs`, `deploy`, `secrets list`, `env list`) |
|
|
115
|
+
| `-V, --version` | Print version |
|
|
116
|
+
| `-h, --help` | Show help |
|
|
117
|
+
|
|
118
|
+
## Authentication
|
|
119
|
+
|
|
120
|
+
Credentials are stored in `~/.config/fold/config.json` (mode `0600`). The CLI supports two auth methods:
|
|
121
|
+
|
|
122
|
+
- **Email/password** — authenticates via the API and stores a JWT
|
|
123
|
+
- **API token** — direct token input for CI/automation
|
|
124
|
+
|
|
125
|
+
The CLI checks JWT expiry before each command and exits early if the token has expired.
|
|
126
|
+
|
|
127
|
+
## TypeScript bundling
|
|
128
|
+
|
|
129
|
+
`.ts` and `.tsx` files are automatically bundled with esbuild before deploy:
|
|
130
|
+
|
|
131
|
+
- Target: `esnext`, format: ESM
|
|
132
|
+
- Tree-shaking and minification enabled
|
|
133
|
+
- `cloudflare:*` imports kept external
|
|
134
|
+
- Skip with `--no-bundle` to deploy raw JS
|
|
135
|
+
|
|
136
|
+
## Development
|
|
137
|
+
|
|
138
|
+
```bash
|
|
139
|
+
# Install dependencies
|
|
140
|
+
pnpm install
|
|
141
|
+
|
|
142
|
+
# Build
|
|
143
|
+
pnpm build
|
|
144
|
+
|
|
145
|
+
# Watch mode
|
|
146
|
+
pnpm dev
|
|
147
|
+
|
|
148
|
+
# Run tests
|
|
149
|
+
pnpm test
|
|
150
|
+
|
|
151
|
+
# Type check
|
|
152
|
+
pnpm typecheck
|
|
153
|
+
```
|
|
154
|
+
|
|
155
|
+
## License
|
|
156
|
+
|
|
157
|
+
MIT
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
import { readFileSync } from 'node:fs';
|
|
2
|
+
import { resolve } from 'node:path';
|
|
3
|
+
import { apiFetch } from '../lib/api.js';
|
|
4
|
+
import { requireConfig } from '../lib/config.js';
|
|
5
|
+
export async function createToolCommand(name, description, opts) {
|
|
6
|
+
const config = requireConfig();
|
|
7
|
+
const tenantId = opts.tenant ?? config.tenantId;
|
|
8
|
+
if (!tenantId) {
|
|
9
|
+
console.error('No tenant ID. Use --tenant or set one during `fold login`.');
|
|
10
|
+
process.exit(1);
|
|
11
|
+
}
|
|
12
|
+
// Load optional input schema from file
|
|
13
|
+
let inputSchema;
|
|
14
|
+
if (opts.schema) {
|
|
15
|
+
try {
|
|
16
|
+
const schemaText = readFileSync(resolve(opts.schema), 'utf-8');
|
|
17
|
+
inputSchema = JSON.parse(schemaText);
|
|
18
|
+
}
|
|
19
|
+
catch (err) {
|
|
20
|
+
console.error(`Could not read schema file: ${err}`);
|
|
21
|
+
process.exit(1);
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
console.log(`Generating tool "${name}"...`);
|
|
25
|
+
// Step 1: Generate tool code + schema
|
|
26
|
+
const genResult = (await apiFetch(config, '/ai/generate-tool', {
|
|
27
|
+
body: { description, input_schema: inputSchema },
|
|
28
|
+
}));
|
|
29
|
+
if (!opts.yes) {
|
|
30
|
+
console.log('\nGenerated code:\n');
|
|
31
|
+
console.log(genResult.code);
|
|
32
|
+
console.log('\nInput schema:');
|
|
33
|
+
console.log(JSON.stringify(genResult.input_schema, null, 2));
|
|
34
|
+
console.log('');
|
|
35
|
+
}
|
|
36
|
+
// Step 2: Deploy with tool metadata
|
|
37
|
+
console.log(`Deploying to tenant ${tenantId}...`);
|
|
38
|
+
const result = (await apiFetch(config, '/deploy', {
|
|
39
|
+
body: {
|
|
40
|
+
tenant_id: tenantId,
|
|
41
|
+
name,
|
|
42
|
+
code: genResult.code,
|
|
43
|
+
tool_metadata: {
|
|
44
|
+
description,
|
|
45
|
+
input_schema: genResult.input_schema,
|
|
46
|
+
},
|
|
47
|
+
},
|
|
48
|
+
}));
|
|
49
|
+
console.log('\nDeployed as MCP tool!');
|
|
50
|
+
console.log(` Function: ${result.function_id}`);
|
|
51
|
+
console.log(` URL: ${result.url}`);
|
|
52
|
+
console.log(` MCP endpoint: ${result.mcp_endpoint}`);
|
|
53
|
+
console.log(` Tool name: ${result.tool_name}`);
|
|
54
|
+
console.log(` Version: ${result.version}`);
|
|
55
|
+
}
|
|
56
|
+
export async function toolsListCommand(opts) {
|
|
57
|
+
const config = requireConfig();
|
|
58
|
+
const tenantId = opts.tenant ?? config.tenantId;
|
|
59
|
+
if (!tenantId) {
|
|
60
|
+
console.error('No tenant ID. Use --tenant or set one during `fold login`.');
|
|
61
|
+
process.exit(1);
|
|
62
|
+
}
|
|
63
|
+
const result = (await apiFetch(config, `/tools?tenant_id=${tenantId}`));
|
|
64
|
+
if (result.tools.length === 0) {
|
|
65
|
+
console.log('No MCP tools deployed. Use `fold create-tool` to create one.');
|
|
66
|
+
return;
|
|
67
|
+
}
|
|
68
|
+
console.log(`MCP endpoint: ${result.mcp_endpoint}\n`);
|
|
69
|
+
console.log('Tools:');
|
|
70
|
+
for (const tool of result.tools) {
|
|
71
|
+
console.log(` ${tool.tool_name} — ${tool.description}`);
|
|
72
|
+
console.log(` URL: ${tool.url}`);
|
|
73
|
+
console.log('');
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
//# sourceMappingURL=create-tool.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"create-tool.js","sourceRoot":"","sources":["../../src/commands/create-tool.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { apiFetch } from '../lib/api.js';
|
|
2
|
+
import { requireConfig } from '../lib/config.js';
|
|
3
|
+
export async function deleteCommand(functionId, opts) {
|
|
4
|
+
const config = requireConfig();
|
|
5
|
+
const tenantId = opts.tenant ?? config.tenantId;
|
|
6
|
+
if (!tenantId) {
|
|
7
|
+
console.error('No tenant ID. Use --tenant or set one during `fold login`.');
|
|
8
|
+
process.exit(1);
|
|
9
|
+
}
|
|
10
|
+
console.log(`Deleting function ${functionId}...`);
|
|
11
|
+
await apiFetch(config, `/functions/${functionId}`, {
|
|
12
|
+
method: 'DELETE',
|
|
13
|
+
body: { tenant_id: tenantId },
|
|
14
|
+
});
|
|
15
|
+
console.log('Function deleted.');
|
|
16
|
+
}
|
|
17
|
+
//# sourceMappingURL=delete.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"delete.js","sourceRoot":"","sources":["../../src/commands/delete.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAC;AACzC,OAAO,EAAE,aAAa,EAAE,MAAM,kBAAkB,CAAC;AAEjD,MAAM,CAAC,KAAK,UAAU,aAAa,CAAC,UAAkB,EAAE,IAAyB;IAC/E,MAAM,MAAM,GAAG,aAAa,EAAE,CAAC;IAC/B,MAAM,QAAQ,GAAG,IAAI,CAAC,MAAM,IAAI,MAAM,CAAC,QAAQ,CAAC;IAEhD,IAAI,CAAC,QAAQ,EAAE,CAAC;QACd,OAAO,CAAC,KAAK,CAAC,4DAA4D,CAAC,CAAC;QAC5E,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAClB,CAAC;IAED,OAAO,CAAC,GAAG,CAAC,qBAAqB,UAAU,KAAK,CAAC,CAAC;IAElD,MAAM,QAAQ,CAAC,MAAM,EAAE,cAAc,UAAU,EAAE,EAAE;QACjD,MAAM,EAAE,QAAQ;QAChB,IAAI,EAAE,EAAE,SAAS,EAAE,QAAQ,EAAE;KAC9B,CAAC,CAAC;IAEH,OAAO,CAAC,GAAG,CAAC,mBAAmB,CAAC,CAAC;AACnC,CAAC"}
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
import { existsSync, readFileSync } from 'node:fs';
|
|
2
|
+
import { basename, resolve } from 'node:path';
|
|
3
|
+
import { apiFetch } from '../lib/api.js';
|
|
4
|
+
import { bundleFile } from '../lib/bundler.js';
|
|
5
|
+
import { requireConfig } from '../lib/config.js';
|
|
6
|
+
import { output } from '../lib/output.js';
|
|
7
|
+
function loadManifest() {
|
|
8
|
+
const manifestPath = resolve('fold.json');
|
|
9
|
+
if (!existsSync(manifestPath))
|
|
10
|
+
return null;
|
|
11
|
+
try {
|
|
12
|
+
return JSON.parse(readFileSync(manifestPath, 'utf-8'));
|
|
13
|
+
}
|
|
14
|
+
catch {
|
|
15
|
+
return null;
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
export async function deployCommand(file, opts) {
|
|
19
|
+
const config = requireConfig();
|
|
20
|
+
const manifest = loadManifest();
|
|
21
|
+
const tenantId = opts.tenant ?? manifest?.tenant_id ?? config.tenantId;
|
|
22
|
+
if (!tenantId) {
|
|
23
|
+
console.error('No tenant ID. Use --tenant or set one during `fold login`.');
|
|
24
|
+
process.exit(1);
|
|
25
|
+
}
|
|
26
|
+
const resolvedFile = file ?? manifest?.entrypoint;
|
|
27
|
+
if (!resolvedFile) {
|
|
28
|
+
console.error('No file specified and no fold.json with entrypoint found.\nUsage: fold deploy <file>');
|
|
29
|
+
process.exit(1);
|
|
30
|
+
}
|
|
31
|
+
const filePath = resolve(resolvedFile);
|
|
32
|
+
let code;
|
|
33
|
+
if (!opts.noBundle && /\.tsx?$/.test(resolvedFile)) {
|
|
34
|
+
// Bundle TypeScript files with esbuild
|
|
35
|
+
console.log('Bundling TypeScript...');
|
|
36
|
+
try {
|
|
37
|
+
code = await bundleFile(filePath);
|
|
38
|
+
}
|
|
39
|
+
catch (err) {
|
|
40
|
+
console.error(`Bundle failed: ${err}`);
|
|
41
|
+
process.exit(1);
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
else {
|
|
45
|
+
try {
|
|
46
|
+
code = readFileSync(filePath, 'utf-8');
|
|
47
|
+
}
|
|
48
|
+
catch {
|
|
49
|
+
console.error(`Could not read file: ${resolvedFile}`);
|
|
50
|
+
process.exit(1);
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
const name = opts.name ?? manifest?.name ?? basename(resolvedFile).replace(/\.(tsx?|js|mjs)$/, '');
|
|
54
|
+
console.log(`Deploying ${name} to tenant ${tenantId}...`);
|
|
55
|
+
const body = { tenant_id: tenantId, name, code };
|
|
56
|
+
if (manifest?.intent)
|
|
57
|
+
body.intent = manifest.intent;
|
|
58
|
+
const result = (await apiFetch(config, '/deploy', { body }));
|
|
59
|
+
output(result, () => {
|
|
60
|
+
console.log(`\nDeployed successfully!`);
|
|
61
|
+
console.log(` Function: ${result.function_id}`);
|
|
62
|
+
console.log(` URL: ${result.url}`);
|
|
63
|
+
console.log(` Version: ${result.version}`);
|
|
64
|
+
console.log(` Status: ${result.status}`);
|
|
65
|
+
if (result.leak_warnings?.length) {
|
|
66
|
+
console.log(`\n\x1b[33mWarning: ${result.leak_warnings.length} potential secret leak${result.leak_warnings.length > 1 ? 's' : ''} detected in deployed code:\x1b[0m`);
|
|
67
|
+
for (const w of result.leak_warnings.slice(0, 10)) {
|
|
68
|
+
console.log(` \x1b[33mLine ${w.line}:\x1b[0m ${w.description} (${w.match})`);
|
|
69
|
+
}
|
|
70
|
+
if (result.leak_warnings.length > 10) {
|
|
71
|
+
console.log(` ...and ${result.leak_warnings.length - 10} more`);
|
|
72
|
+
}
|
|
73
|
+
console.log(`\n Consider using secrets or env vars instead of hardcoding sensitive values.`);
|
|
74
|
+
}
|
|
75
|
+
});
|
|
76
|
+
}
|
|
77
|
+
//# sourceMappingURL=deploy.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"deploy.js","sourceRoot":"","sources":["../../src/commands/deploy.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,178 @@
|
|
|
1
|
+
import { spawn } from 'node:child_process';
|
|
2
|
+
import { existsSync, readFileSync, watch } from 'node:fs';
|
|
3
|
+
import { dirname, resolve } from 'node:path';
|
|
4
|
+
import { bundleFile } from '../lib/bundler.js';
|
|
5
|
+
/** Parse KEY=VALUE pairs from --env flags and a .env file in cwd */
|
|
6
|
+
function loadEnvVars(envFlags) {
|
|
7
|
+
const vars = {};
|
|
8
|
+
// Load .env file if present
|
|
9
|
+
const dotenvPath = resolve('.env');
|
|
10
|
+
if (existsSync(dotenvPath)) {
|
|
11
|
+
const lines = readFileSync(dotenvPath, 'utf-8').split('\n');
|
|
12
|
+
for (const line of lines) {
|
|
13
|
+
const trimmed = line.trim();
|
|
14
|
+
if (!trimmed || trimmed.startsWith('#'))
|
|
15
|
+
continue;
|
|
16
|
+
const eq = trimmed.indexOf('=');
|
|
17
|
+
if (eq === -1)
|
|
18
|
+
continue;
|
|
19
|
+
const key = trimmed.slice(0, eq).trim();
|
|
20
|
+
const raw = trimmed.slice(eq + 1).trim();
|
|
21
|
+
// Strip surrounding quotes
|
|
22
|
+
vars[key] = raw.replace(/^(['"])(.*)\1$/, '$2');
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
// --env flags override .env file
|
|
26
|
+
for (const flag of envFlags) {
|
|
27
|
+
const eq = flag.indexOf('=');
|
|
28
|
+
if (eq === -1) {
|
|
29
|
+
vars[flag.trim()] = '';
|
|
30
|
+
}
|
|
31
|
+
else {
|
|
32
|
+
vars[flag.slice(0, eq).trim()] = flag.slice(eq + 1);
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
return vars;
|
|
36
|
+
}
|
|
37
|
+
function loadManifest() {
|
|
38
|
+
const manifestPath = resolve('fold.json');
|
|
39
|
+
if (!existsSync(manifestPath))
|
|
40
|
+
return null;
|
|
41
|
+
try {
|
|
42
|
+
return JSON.parse(readFileSync(manifestPath, 'utf-8'));
|
|
43
|
+
}
|
|
44
|
+
catch {
|
|
45
|
+
return null;
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
async function buildCode(filePath, resolvedFile, noBundle) {
|
|
49
|
+
if (!noBundle && /\.tsx?$/.test(resolvedFile)) {
|
|
50
|
+
return await bundleFile(filePath, { dev: true });
|
|
51
|
+
}
|
|
52
|
+
return readFileSync(filePath, 'utf-8');
|
|
53
|
+
}
|
|
54
|
+
function makeLoaderCode(code, port, env) {
|
|
55
|
+
const encodedCode = Buffer.from(code).toString('base64');
|
|
56
|
+
const encodedEnv = JSON.stringify(env);
|
|
57
|
+
return `
|
|
58
|
+
import { createServer } from 'node:http';
|
|
59
|
+
|
|
60
|
+
const code = Buffer.from(${JSON.stringify(encodedCode)}, 'base64').toString();
|
|
61
|
+
const blob = new Blob([code], { type: 'application/javascript' });
|
|
62
|
+
const url = URL.createObjectURL(blob);
|
|
63
|
+
const mod = await import(url);
|
|
64
|
+
const handler = mod.default;
|
|
65
|
+
const env = ${encodedEnv};
|
|
66
|
+
|
|
67
|
+
if (!handler?.fetch) {
|
|
68
|
+
console.error('Function must export default { fetch(request, env) { ... } }');
|
|
69
|
+
process.exit(1);
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
const server = createServer(async (req, res) => {
|
|
73
|
+
const protocol = 'http';
|
|
74
|
+
const host = req.headers.host || 'localhost:${port}';
|
|
75
|
+
const requestUrl = new URL(req.url || '/', protocol + '://' + host);
|
|
76
|
+
|
|
77
|
+
const chunks = [];
|
|
78
|
+
for await (const chunk of req) chunks.push(chunk);
|
|
79
|
+
const body = chunks.length > 0 ? Buffer.concat(chunks) : undefined;
|
|
80
|
+
|
|
81
|
+
const request = new Request(requestUrl.toString(), {
|
|
82
|
+
method: req.method,
|
|
83
|
+
headers: Object.entries(req.headers).reduce((h, [k, v]) => {
|
|
84
|
+
if (v) h[k] = Array.isArray(v) ? v.join(', ') : v;
|
|
85
|
+
return h;
|
|
86
|
+
}, {}),
|
|
87
|
+
body: req.method !== 'GET' && req.method !== 'HEAD' ? body : undefined,
|
|
88
|
+
});
|
|
89
|
+
|
|
90
|
+
try {
|
|
91
|
+
const response = await handler.fetch(request, env);
|
|
92
|
+
res.writeHead(response.status, Object.fromEntries(response.headers.entries()));
|
|
93
|
+
const respBody = await response.arrayBuffer();
|
|
94
|
+
res.end(Buffer.from(respBody));
|
|
95
|
+
} catch (err) {
|
|
96
|
+
console.error('Function error:', err);
|
|
97
|
+
res.writeHead(500);
|
|
98
|
+
res.end('Internal Server Error');
|
|
99
|
+
}
|
|
100
|
+
});
|
|
101
|
+
|
|
102
|
+
server.listen(${port}, () => {
|
|
103
|
+
console.log('Local dev server running at http://localhost:${port}');
|
|
104
|
+
});
|
|
105
|
+
`;
|
|
106
|
+
}
|
|
107
|
+
function spawnServer(loaderCode) {
|
|
108
|
+
return spawn('node', ['--input-type=module', '-e', loaderCode], {
|
|
109
|
+
stdio: 'inherit',
|
|
110
|
+
});
|
|
111
|
+
}
|
|
112
|
+
export async function devCommand(file, opts) {
|
|
113
|
+
const manifest = loadManifest();
|
|
114
|
+
const resolvedFile = file ?? manifest?.entrypoint;
|
|
115
|
+
if (!resolvedFile) {
|
|
116
|
+
console.error('No file specified and no fold.json with entrypoint found.\nUsage: fold dev <file>');
|
|
117
|
+
process.exit(1);
|
|
118
|
+
}
|
|
119
|
+
const filePath = resolve(resolvedFile);
|
|
120
|
+
const port = parseInt(opts.port ?? '8787', 10);
|
|
121
|
+
const noBundle = opts.noBundle ?? false;
|
|
122
|
+
const watchMode = opts.watch !== false; // default true
|
|
123
|
+
const env = loadEnvVars(opts.env ?? []);
|
|
124
|
+
if (Object.keys(env).length > 0) {
|
|
125
|
+
console.log(`Loaded env: ${Object.keys(env).join(', ')}`);
|
|
126
|
+
}
|
|
127
|
+
let code;
|
|
128
|
+
try {
|
|
129
|
+
code = await buildCode(filePath, resolvedFile, noBundle);
|
|
130
|
+
}
|
|
131
|
+
catch (err) {
|
|
132
|
+
console.error(`Build failed: ${err}`);
|
|
133
|
+
process.exit(1);
|
|
134
|
+
}
|
|
135
|
+
let child = spawnServer(makeLoaderCode(code, port, env));
|
|
136
|
+
if (watchMode) {
|
|
137
|
+
let debounce = null;
|
|
138
|
+
const watchDir = dirname(filePath);
|
|
139
|
+
console.log(`Watching ${watchDir} for changes...\n`);
|
|
140
|
+
watch(watchDir, { recursive: true }, (_event, filename) => {
|
|
141
|
+
if (!filename || /node_modules|\.git|dist/.test(filename))
|
|
142
|
+
return;
|
|
143
|
+
if (!/\.(ts|tsx|js|mjs|json)$/.test(filename))
|
|
144
|
+
return;
|
|
145
|
+
if (debounce)
|
|
146
|
+
clearTimeout(debounce);
|
|
147
|
+
debounce = setTimeout(async () => {
|
|
148
|
+
console.log(`\n\x1b[33mChange detected:\x1b[0m ${filename}`);
|
|
149
|
+
try {
|
|
150
|
+
const newCode = await buildCode(filePath, resolvedFile, noBundle);
|
|
151
|
+
child.kill('SIGTERM');
|
|
152
|
+
await new Promise((r) => child.on('exit', () => r()));
|
|
153
|
+
child = spawnServer(makeLoaderCode(newCode, port, env));
|
|
154
|
+
console.log('\x1b[32mReloaded.\x1b[0m\n');
|
|
155
|
+
}
|
|
156
|
+
catch (err) {
|
|
157
|
+
console.error(`\x1b[31mRebuild failed:\x1b[0m ${err}`);
|
|
158
|
+
}
|
|
159
|
+
}, 200);
|
|
160
|
+
});
|
|
161
|
+
}
|
|
162
|
+
else {
|
|
163
|
+
console.log('Press Ctrl+C to stop\n');
|
|
164
|
+
}
|
|
165
|
+
child.on('exit', (exitCode) => {
|
|
166
|
+
if (!watchMode)
|
|
167
|
+
process.exit(exitCode ?? 0);
|
|
168
|
+
});
|
|
169
|
+
process.on('SIGINT', () => {
|
|
170
|
+
child.kill('SIGINT');
|
|
171
|
+
process.exit(0);
|
|
172
|
+
});
|
|
173
|
+
process.on('SIGTERM', () => {
|
|
174
|
+
child.kill('SIGTERM');
|
|
175
|
+
process.exit(0);
|
|
176
|
+
});
|
|
177
|
+
}
|
|
178
|
+
//# sourceMappingURL=dev.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"dev.js","sourceRoot":"","sources":["../../src/commands/dev.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
export declare function envSetCommand(name: string, opts: {
|
|
2
|
+
functionId: string;
|
|
3
|
+
tenant?: string;
|
|
4
|
+
value?: string;
|
|
5
|
+
}): Promise<void>;
|
|
6
|
+
export declare function envListCommand(opts: {
|
|
7
|
+
functionId: string;
|
|
8
|
+
tenant?: string;
|
|
9
|
+
}): Promise<void>;
|
|
10
|
+
export declare function envDeleteCommand(name: string, opts: {
|
|
11
|
+
functionId: string;
|
|
12
|
+
}): Promise<void>;
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { apiFetch } from '../lib/api.js';
|
|
2
|
+
import { requireConfig } from '../lib/config.js';
|
|
3
|
+
import { output } from '../lib/output.js';
|
|
4
|
+
import { readSecret } from '../lib/prompt.js';
|
|
5
|
+
export async function envSetCommand(name, opts) {
|
|
6
|
+
const config = requireConfig();
|
|
7
|
+
let value = opts.value;
|
|
8
|
+
if (!value) {
|
|
9
|
+
value = (await readSecret('Value: ')).trim();
|
|
10
|
+
}
|
|
11
|
+
if (!value) {
|
|
12
|
+
console.error('Value is required.');
|
|
13
|
+
process.exit(1);
|
|
14
|
+
}
|
|
15
|
+
await apiFetch(config, '/env-vars', {
|
|
16
|
+
body: { function_id: opts.functionId, name, value },
|
|
17
|
+
});
|
|
18
|
+
console.log(`Env var "${name}" set for function ${opts.functionId}.`);
|
|
19
|
+
}
|
|
20
|
+
export async function envListCommand(opts) {
|
|
21
|
+
const config = requireConfig();
|
|
22
|
+
const result = (await apiFetch(config, `/env-vars/${opts.functionId}`));
|
|
23
|
+
output(result.env_vars, () => {
|
|
24
|
+
if (!result.env_vars.length) {
|
|
25
|
+
console.log('No env vars configured.');
|
|
26
|
+
return;
|
|
27
|
+
}
|
|
28
|
+
console.log(`Env vars for ${opts.functionId}:\n`);
|
|
29
|
+
for (const e of result.env_vars) {
|
|
30
|
+
const created = new Date(e.created_at).toISOString().slice(0, 10);
|
|
31
|
+
console.log(` ${e.name} = ${e.value_preview} (${created})`);
|
|
32
|
+
}
|
|
33
|
+
});
|
|
34
|
+
}
|
|
35
|
+
export async function envDeleteCommand(name, opts) {
|
|
36
|
+
const config = requireConfig();
|
|
37
|
+
await apiFetch(config, `/env-vars/${opts.functionId}/${name}`, { method: 'DELETE' });
|
|
38
|
+
console.log(`Env var "${name}" deleted.`);
|
|
39
|
+
}
|
|
40
|
+
//# sourceMappingURL=env-vars.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"env-vars.js","sourceRoot":"","sources":["../../src/commands/env-vars.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAC;AACzC,OAAO,EAAE,aAAa,EAAE,MAAM,kBAAkB,CAAC;AACjD,OAAO,EAAE,MAAM,EAAE,MAAM,kBAAkB,CAAC;AAC1C,OAAO,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAU9C,MAAM,CAAC,KAAK,UAAU,aAAa,CACjC,IAAY,EACZ,IAA6D;IAE7D,MAAM,MAAM,GAAG,aAAa,EAAE,CAAC;IAE/B,IAAI,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC;IACvB,IAAI,CAAC,KAAK,EAAE,CAAC;QACX,KAAK,GAAG,CAAC,MAAM,UAAU,CAAC,SAAS,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC;IAC/C,CAAC;IAED,IAAI,CAAC,KAAK,EAAE,CAAC;QACX,OAAO,CAAC,KAAK,CAAC,oBAAoB,CAAC,CAAC;QACpC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAClB,CAAC;IAED,MAAM,QAAQ,CAAC,MAAM,EAAE,WAAW,EAAE;QAClC,IAAI,EAAE,EAAE,WAAW,EAAE,IAAI,CAAC,UAAU,EAAE,IAAI,EAAE,KAAK,EAAE;KACpD,CAAC,CAAC;IAEH,OAAO,CAAC,GAAG,CAAC,YAAY,IAAI,sBAAsB,IAAI,CAAC,UAAU,GAAG,CAAC,CAAC;AACxE,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,cAAc,CAAC,IAA6C;IAChF,MAAM,MAAM,GAAG,aAAa,EAAE,CAAC;IAE/B,MAAM,MAAM,GAAG,CAAC,MAAM,QAAQ,CAAC,MAAM,EAAE,aAAa,IAAI,CAAC,UAAU,EAAE,CAAC,CAA2B,CAAC;IAElG,MAAM,CAAC,MAAM,CAAC,QAAQ,EAAE,GAAG,EAAE;QAC3B,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,MAAM,EAAE,CAAC;YAC5B,OAAO,CAAC,GAAG,CAAC,yBAAyB,CAAC,CAAC;YACvC,OAAO;QACT,CAAC;QAED,OAAO,CAAC,GAAG,CAAC,gBAAgB,IAAI,CAAC,UAAU,KAAK,CAAC,CAAC;QAClD,KAAK,MAAM,CAAC,IAAI,MAAM,CAAC,QAAQ,EAAE,CAAC;YAChC,MAAM,OAAO,GAAG,IAAI,IAAI,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC,WAAW,EAAE,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;YAClE,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC,IAAI,MAAM,CAAC,CAAC,aAAa,MAAM,OAAO,GAAG,CAAC,CAAC;QAChE,CAAC;IACH,CAAC,CAAC,CAAC;AACL,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,gBAAgB,CAAC,IAAY,EAAE,IAA4B;IAC/E,MAAM,MAAM,GAAG,aAAa,EAAE,CAAC;IAE/B,MAAM,QAAQ,CAAC,MAAM,EAAE,aAAa,IAAI,CAAC,UAAU,IAAI,IAAI,EAAE,EAAE,EAAE,MAAM,EAAE,QAAQ,EAAE,CAAC,CAAC;IACrF,OAAO,CAAC,GAAG,CAAC,YAAY,IAAI,YAAY,CAAC,CAAC;AAC5C,CAAC"}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { apiFetch } from '../lib/api.js';
|
|
2
|
+
import { requireConfig } from '../lib/config.js';
|
|
3
|
+
import { output } from '../lib/output.js';
|
|
4
|
+
export async function functionsCommand(opts) {
|
|
5
|
+
const config = requireConfig();
|
|
6
|
+
const tenantId = opts.tenant ?? config.tenantId;
|
|
7
|
+
if (!tenantId) {
|
|
8
|
+
console.error('No tenant ID. Use --tenant or set one during `fold login`.');
|
|
9
|
+
process.exit(1);
|
|
10
|
+
}
|
|
11
|
+
const result = (await apiFetch(config, `/functions?tenant_id=${tenantId}`));
|
|
12
|
+
output(result.functions, () => {
|
|
13
|
+
if (!result.functions.length) {
|
|
14
|
+
console.log('No functions deployed.');
|
|
15
|
+
return;
|
|
16
|
+
}
|
|
17
|
+
console.log(`Functions for ${tenantId}:\n`);
|
|
18
|
+
for (const fn of result.functions) {
|
|
19
|
+
console.log(` ${fn.name} (v${fn.version}) [${fn.status}]`);
|
|
20
|
+
console.log(` ID: ${fn.id}`);
|
|
21
|
+
console.log(` URL: ${fn.url}`);
|
|
22
|
+
console.log('');
|
|
23
|
+
}
|
|
24
|
+
});
|
|
25
|
+
}
|
|
26
|
+
//# sourceMappingURL=functions.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"functions.js","sourceRoot":"","sources":["../../src/commands/functions.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAC;AACzC,OAAO,EAAE,aAAa,EAAE,MAAM,kBAAkB,CAAC;AACjD,OAAO,EAAE,MAAM,EAAE,MAAM,kBAAkB,CAAC;AAW1C,MAAM,CAAC,KAAK,UAAU,gBAAgB,CAAC,IAAyB;IAC9D,MAAM,MAAM,GAAG,aAAa,EAAE,CAAC;IAC/B,MAAM,QAAQ,GAAG,IAAI,CAAC,MAAM,IAAI,MAAM,CAAC,QAAQ,CAAC;IAEhD,IAAI,CAAC,QAAQ,EAAE,CAAC;QACd,OAAO,CAAC,KAAK,CAAC,4DAA4D,CAAC,CAAC;QAC5E,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAClB,CAAC;IAED,MAAM,MAAM,GAAG,CAAC,MAAM,QAAQ,CAAC,MAAM,EAAE,wBAAwB,QAAQ,EAAE,CAAC,CAAkC,CAAC;IAE7G,MAAM,CAAC,MAAM,CAAC,SAAS,EAAE,GAAG,EAAE;QAC5B,IAAI,CAAC,MAAM,CAAC,SAAS,CAAC,MAAM,EAAE,CAAC;YAC7B,OAAO,CAAC,GAAG,CAAC,wBAAwB,CAAC,CAAC;YACtC,OAAO;QACT,CAAC;QAED,OAAO,CAAC,GAAG,CAAC,iBAAiB,QAAQ,KAAK,CAAC,CAAC;QAC5C,KAAK,MAAM,EAAE,IAAI,MAAM,CAAC,SAAS,EAAE,CAAC;YAClC,OAAO,CAAC,GAAG,CAAC,KAAK,EAAE,CAAC,IAAI,MAAM,EAAE,CAAC,OAAO,MAAM,EAAE,CAAC,MAAM,GAAG,CAAC,CAAC;YAC5D,OAAO,CAAC,GAAG,CAAC,YAAY,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC;YACjC,OAAO,CAAC,GAAG,CAAC,YAAY,EAAE,CAAC,GAAG,EAAE,CAAC,CAAC;YAClC,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;QAClB,CAAC;IACH,CAAC,CAAC,CAAC;AACL,CAAC"}
|