@somewhere-tech/cli 0.1.0
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 +21 -0
- package/README.md +91 -0
- package/bin/somewhere.js +2 -0
- package/dist/commands/api.js +38 -0
- package/dist/commands/api.js.map +1 -0
- package/dist/commands/auth.js +58 -0
- package/dist/commands/auth.js.map +1 -0
- package/dist/commands/deploy.js +104 -0
- package/dist/commands/deploy.js.map +1 -0
- package/dist/commands/dev.js +73 -0
- package/dist/commands/dev.js.map +1 -0
- package/dist/commands/env.js +80 -0
- package/dist/commands/env.js.map +1 -0
- package/dist/commands/init.js +110 -0
- package/dist/commands/init.js.map +1 -0
- package/dist/commands/logs.js +81 -0
- package/dist/commands/logs.js.map +1 -0
- package/dist/commands/open.js +42 -0
- package/dist/commands/open.js.map +1 -0
- package/dist/commands/project.js +135 -0
- package/dist/commands/project.js.map +1 -0
- package/dist/commands/status.js +44 -0
- package/dist/commands/status.js.map +1 -0
- package/dist/index.js +27 -0
- package/dist/index.js.map +1 -0
- package/dist/lib/auth.js +68 -0
- package/dist/lib/auth.js.map +1 -0
- package/dist/lib/client.js +52 -0
- package/dist/lib/client.js.map +1 -0
- package/dist/lib/config.js +64 -0
- package/dist/lib/config.js.map +1 -0
- package/dist/lib/output.js +68 -0
- package/dist/lib/output.js.map +1 -0
- package/dist/types.js +2 -0
- package/dist/types.js.map +1 -0
- package/package.json +34 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 somewhere.tech
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
# @somewhere-tech/cli
|
|
2
|
+
|
|
3
|
+
CLI for [somewhere.tech](https://somewhere.tech). Like `gh` for GitHub, `vercel` for Vercel.
|
|
4
|
+
|
|
5
|
+
## Install
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
npm install -g @somewhere-tech/cli
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
Or run directly:
|
|
12
|
+
|
|
13
|
+
```bash
|
|
14
|
+
npx @somewhere-tech/cli init
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
## Quick start
|
|
18
|
+
|
|
19
|
+
```bash
|
|
20
|
+
somewhere login # OAuth in browser, stores API key
|
|
21
|
+
somewhere init # Create project, write .somewhere.json + .mcp.json
|
|
22
|
+
somewhere deploy # Deploy current directory
|
|
23
|
+
somewhere logs # Stream logs
|
|
24
|
+
somewhere open # Open in browser
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
After `somewhere init`, Claude Code and Codex auto-connect via the `.mcp.json` it writes. Just start coding.
|
|
28
|
+
|
|
29
|
+
## Commands
|
|
30
|
+
|
|
31
|
+
| Command | What it does |
|
|
32
|
+
|---|---|
|
|
33
|
+
| `somewhere login` | Authenticate via browser (Google OAuth) |
|
|
34
|
+
| `somewhere logout` | Clear stored credentials |
|
|
35
|
+
| `somewhere whoami` | Show current user, plan, project count |
|
|
36
|
+
| `somewhere init` | Create project + write `.somewhere.json` + `.mcp.json` |
|
|
37
|
+
| `somewhere init --link` | Link to an existing project |
|
|
38
|
+
| `somewhere projects` | List all projects with status |
|
|
39
|
+
| `somewhere project create <name>` | Create a new project |
|
|
40
|
+
| `somewhere project view [name]` | Show project details |
|
|
41
|
+
| `somewhere project delete <name>` | Delete with email confirmation |
|
|
42
|
+
| `somewhere deploy` | Deploy current directory to linked project |
|
|
43
|
+
| `somewhere deploy ./dist` | Deploy a specific directory |
|
|
44
|
+
| `somewhere logs` | Show recent logs |
|
|
45
|
+
| `somewhere logs --follow` | Stream logs in real-time |
|
|
46
|
+
| `somewhere logs --level error` | Filter by level |
|
|
47
|
+
| `somewhere env list` | Show environment variables |
|
|
48
|
+
| `somewhere env set KEY value` | Set an env var |
|
|
49
|
+
| `somewhere env delete KEY` | Delete an env var |
|
|
50
|
+
| `somewhere status` | Show project + workspace status |
|
|
51
|
+
| `somewhere open` | Open project URL in browser |
|
|
52
|
+
| `somewhere open --dashboard` | Open the dashboard |
|
|
53
|
+
| `somewhere dev` | Run local dev with platform env vars |
|
|
54
|
+
| `somewhere api GET /v1/projects` | Raw API call with auto-auth |
|
|
55
|
+
|
|
56
|
+
Short alias: `sw` works everywhere `somewhere` does.
|
|
57
|
+
|
|
58
|
+
## Auth flow
|
|
59
|
+
|
|
60
|
+
`somewhere login` opens your browser, you sign in with Google, the platform redirects back to a local server with your API key. The key is stored in `~/.somewhere/config.json` (mode 600).
|
|
61
|
+
|
|
62
|
+
## Init flow
|
|
63
|
+
|
|
64
|
+
`somewhere init` creates a project on the platform and writes two files:
|
|
65
|
+
|
|
66
|
+
- **`.somewhere.json`** — project ID, name, subdomain. The CLI reads this to know which project you're working on.
|
|
67
|
+
- **`.mcp.json`** — MCP server config with your API key. Claude Code and Codex auto-discover this and connect to the platform. No manual config.
|
|
68
|
+
|
|
69
|
+
After init, `claude "build me a booking app"` works immediately.
|
|
70
|
+
|
|
71
|
+
## Deploy
|
|
72
|
+
|
|
73
|
+
`somewhere deploy` reads all files in the current directory (skipping `node_modules`, `.git`, `.env`, etc.), sends them to the platform, and they're live at `{subdomain}.somewhere.tech`.
|
|
74
|
+
|
|
75
|
+
Files under `functions/` are deployed as server-side functions (Workers for Platforms).
|
|
76
|
+
|
|
77
|
+
## What the CLI does NOT do
|
|
78
|
+
|
|
79
|
+
- No hosting or running code (the platform does that)
|
|
80
|
+
- No building or compiling (your tools do that)
|
|
81
|
+
- No AI calls (Claude Code / Codex do that via MCP)
|
|
82
|
+
- No workspace management (dashboard)
|
|
83
|
+
- No billing (dashboard)
|
|
84
|
+
|
|
85
|
+
## Requirements
|
|
86
|
+
|
|
87
|
+
Node.js >= 18
|
|
88
|
+
|
|
89
|
+
## License
|
|
90
|
+
|
|
91
|
+
MIT © somewhere.tech
|
package/bin/somewhere.js
ADDED
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { ApiClient } from '../lib/client.js';
|
|
2
|
+
import { getToken } from '../lib/config.js';
|
|
3
|
+
import { error } from '../lib/output.js';
|
|
4
|
+
export function registerApi(program) {
|
|
5
|
+
program
|
|
6
|
+
.command('api <method> <path>')
|
|
7
|
+
.description('Make a raw API call (adds auth automatically)')
|
|
8
|
+
.option('-d, --data <json>', 'JSON body')
|
|
9
|
+
.action(async (method, path, opts) => {
|
|
10
|
+
const client = new ApiClient(getToken());
|
|
11
|
+
let body;
|
|
12
|
+
if (opts.data) {
|
|
13
|
+
try {
|
|
14
|
+
body = JSON.parse(opts.data);
|
|
15
|
+
}
|
|
16
|
+
catch {
|
|
17
|
+
error('--data must be valid JSON');
|
|
18
|
+
process.exit(1);
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
try {
|
|
22
|
+
const apiPath = path.startsWith('/v1/') ? path.slice(3) : path;
|
|
23
|
+
const result = await client.call(method.toUpperCase(), apiPath, body);
|
|
24
|
+
console.log(JSON.stringify(result, null, 2));
|
|
25
|
+
}
|
|
26
|
+
catch (err) {
|
|
27
|
+
if (err instanceof Error && 'code' in err) {
|
|
28
|
+
const apiErr = err;
|
|
29
|
+
console.error(JSON.stringify({ error: apiErr.code, message: apiErr.message, status: apiErr.statusCode }, null, 2));
|
|
30
|
+
}
|
|
31
|
+
else {
|
|
32
|
+
error(String(err));
|
|
33
|
+
}
|
|
34
|
+
process.exit(1);
|
|
35
|
+
}
|
|
36
|
+
});
|
|
37
|
+
}
|
|
38
|
+
//# sourceMappingURL=api.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"api.js","sourceRoot":"","sources":["../../src/commands/api.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;AAC7C,OAAO,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AAC5C,OAAO,EAAE,KAAK,EAAE,MAAM,kBAAkB,CAAC;AAEzC,MAAM,UAAU,WAAW,CAAC,OAAgB;IAC1C,OAAO;SACJ,OAAO,CAAC,qBAAqB,CAAC;SAC9B,WAAW,CAAC,+CAA+C,CAAC;SAC5D,MAAM,CAAC,mBAAmB,EAAE,WAAW,CAAC;SACxC,MAAM,CAAC,KAAK,EAAE,MAAc,EAAE,IAAY,EAAE,IAAI,EAAE,EAAE;QACnD,MAAM,MAAM,GAAG,IAAI,SAAS,CAAC,QAAQ,EAAE,CAAC,CAAC;QACzC,IAAI,IAAa,CAAC;QAClB,IAAI,IAAI,CAAC,IAAI,EAAE,CAAC;YACd,IAAI,CAAC;gBACH,IAAI,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;YAC/B,CAAC;YAAC,MAAM,CAAC;gBACP,KAAK,CAAC,2BAA2B,CAAC,CAAC;gBACnC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;YAClB,CAAC;QACH,CAAC;QAED,IAAI,CAAC;YACH,MAAM,OAAO,GAAG,IAAI,CAAC,UAAU,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC;YAC/D,MAAM,MAAM,GAAG,MAAM,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,WAAW,EAAE,EAAE,OAAO,EAAE,IAAI,CAAC,CAAC;YACtE,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,SAAS,CAAC,MAAM,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC;QAC/C,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,IAAI,GAAG,YAAY,KAAK,IAAI,MAAM,IAAI,GAAG,EAAE,CAAC;gBAC1C,MAAM,MAAM,GAAG,GAAmD,CAAC;gBACnE,OAAO,CAAC,KAAK,CACX,IAAI,CAAC,SAAS,CACZ,EAAE,KAAK,EAAE,MAAM,CAAC,IAAI,EAAE,OAAO,EAAE,MAAM,CAAC,OAAO,EAAE,MAAM,EAAE,MAAM,CAAC,UAAU,EAAE,EAC1E,IAAI,EACJ,CAAC,CACF,CACF,CAAC;YACJ,CAAC;iBAAM,CAAC;gBACN,KAAK,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC;YACrB,CAAC;YACD,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;QAClB,CAAC;IACH,CAAC,CAAC,CAAC;AACP,CAAC"}
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import ora from 'ora';
|
|
2
|
+
import { browserLogin } from '../lib/auth.js';
|
|
3
|
+
import { ApiClient } from '../lib/client.js';
|
|
4
|
+
import { clearConfig, loadConfig, saveConfig } from '../lib/config.js';
|
|
5
|
+
import { dim, error, info, success, teal } from '../lib/output.js';
|
|
6
|
+
export function registerAuth(program) {
|
|
7
|
+
program
|
|
8
|
+
.command('login')
|
|
9
|
+
.description('Authenticate with somewhere.tech via browser')
|
|
10
|
+
.action(async () => {
|
|
11
|
+
const spinner = ora('Opening browser for authentication...').start();
|
|
12
|
+
try {
|
|
13
|
+
const config = await browserLogin();
|
|
14
|
+
saveConfig(config);
|
|
15
|
+
spinner.stop();
|
|
16
|
+
success(`Logged in as ${teal(config.user.email)}`);
|
|
17
|
+
info(`API key stored in ${dim('~/.somewhere/config.json')}`);
|
|
18
|
+
}
|
|
19
|
+
catch (err) {
|
|
20
|
+
spinner.fail('Login failed');
|
|
21
|
+
error(err instanceof Error ? err.message : String(err));
|
|
22
|
+
process.exit(1);
|
|
23
|
+
}
|
|
24
|
+
});
|
|
25
|
+
program
|
|
26
|
+
.command('logout')
|
|
27
|
+
.description('Remove stored credentials')
|
|
28
|
+
.action(() => {
|
|
29
|
+
clearConfig();
|
|
30
|
+
success('Logged out. Token removed from ~/.somewhere/config.json');
|
|
31
|
+
});
|
|
32
|
+
program
|
|
33
|
+
.command('whoami')
|
|
34
|
+
.description('Show current user info')
|
|
35
|
+
.action(async () => {
|
|
36
|
+
const config = loadConfig();
|
|
37
|
+
if (!config) {
|
|
38
|
+
error('Not logged in. Run: somewhere login');
|
|
39
|
+
process.exit(1);
|
|
40
|
+
}
|
|
41
|
+
const client = new ApiClient(config.token);
|
|
42
|
+
try {
|
|
43
|
+
const billing = await client.call('GET', '/billing/status');
|
|
44
|
+
const projects = await client.call('GET', '/projects');
|
|
45
|
+
const deployed = projects.deployed_count ?? 0;
|
|
46
|
+
const draft = (projects.projects ?? []).filter((p) => p.status === 'draft').length;
|
|
47
|
+
console.log(`${teal(config.user.email)} (${config.user.username})`);
|
|
48
|
+
info(`Plan: ${billing.tier === 'builder' ? 'Builder' : 'Free'}`);
|
|
49
|
+
info(`Projects: ${projects.projects.length} (${deployed} deployed, ${draft} draft)`);
|
|
50
|
+
info(`Profile: ${config.user.username}.somewhere.tech`);
|
|
51
|
+
}
|
|
52
|
+
catch (err) {
|
|
53
|
+
console.log(teal(config.user.email));
|
|
54
|
+
info(dim('Could not fetch account details'));
|
|
55
|
+
}
|
|
56
|
+
});
|
|
57
|
+
}
|
|
58
|
+
//# sourceMappingURL=auth.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"auth.js","sourceRoot":"","sources":["../../src/commands/auth.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
import { readdirSync, readFileSync, statSync } from 'node:fs';
|
|
2
|
+
import { join, relative } from 'node:path';
|
|
3
|
+
import ora from 'ora';
|
|
4
|
+
import { ApiClient } from '../lib/client.js';
|
|
5
|
+
import { getToken, loadProjectConfig } from '../lib/config.js';
|
|
6
|
+
import { error, success, teal } from '../lib/output.js';
|
|
7
|
+
const IGNORE = new Set([
|
|
8
|
+
'node_modules',
|
|
9
|
+
'.git',
|
|
10
|
+
'.somewhere.json',
|
|
11
|
+
'.mcp.json',
|
|
12
|
+
'.env',
|
|
13
|
+
'.DS_Store',
|
|
14
|
+
'dist',
|
|
15
|
+
'.next',
|
|
16
|
+
'.vercel',
|
|
17
|
+
]);
|
|
18
|
+
const MAX_FILE_SIZE = 5 * 1024 * 1024; // 5MB per file
|
|
19
|
+
export function registerDeploy(program) {
|
|
20
|
+
program
|
|
21
|
+
.command('deploy [dir]')
|
|
22
|
+
.description('Deploy the current directory to the linked project')
|
|
23
|
+
.option('--project <id>', 'Override project ID')
|
|
24
|
+
.action(async (dir, opts) => {
|
|
25
|
+
const token = getToken();
|
|
26
|
+
const client = new ApiClient(token);
|
|
27
|
+
const targetDir = dir ? join(process.cwd(), dir) : process.cwd();
|
|
28
|
+
let projectId = opts.project;
|
|
29
|
+
if (!projectId) {
|
|
30
|
+
const config = loadProjectConfig(targetDir) ?? loadProjectConfig();
|
|
31
|
+
if (!config) {
|
|
32
|
+
error('No project linked. Run `somewhere init` or pass --project <id>');
|
|
33
|
+
process.exit(1);
|
|
34
|
+
}
|
|
35
|
+
projectId = config.project_id;
|
|
36
|
+
}
|
|
37
|
+
const spinner = ora('Collecting files...').start();
|
|
38
|
+
const files = {};
|
|
39
|
+
const functions = {};
|
|
40
|
+
collectFiles(targetDir, targetDir, files, functions);
|
|
41
|
+
const totalFiles = Object.keys(files).length + Object.keys(functions).length;
|
|
42
|
+
const totalBytes = Object.values(files)
|
|
43
|
+
.concat(Object.values(functions))
|
|
44
|
+
.reduce((sum, c) => sum + c.length, 0);
|
|
45
|
+
spinner.text = `Deploying ${totalFiles} files (${formatBytes(totalBytes)})...`;
|
|
46
|
+
try {
|
|
47
|
+
const body = {
|
|
48
|
+
project_id: projectId,
|
|
49
|
+
files,
|
|
50
|
+
};
|
|
51
|
+
if (Object.keys(functions).length > 0) {
|
|
52
|
+
body.functions = functions;
|
|
53
|
+
}
|
|
54
|
+
const result = await client.call('POST', '/deploy', body);
|
|
55
|
+
spinner.stop();
|
|
56
|
+
const fileCount = typeof result.files === 'number'
|
|
57
|
+
? result.files
|
|
58
|
+
: (result.files ?? []).length;
|
|
59
|
+
success(`${fileCount} files uploaded (${formatBytes(totalBytes)})`);
|
|
60
|
+
if (result.has_functions) {
|
|
61
|
+
success('Functions deployed');
|
|
62
|
+
}
|
|
63
|
+
success(`Live at ${teal(result.url)}`);
|
|
64
|
+
}
|
|
65
|
+
catch (err) {
|
|
66
|
+
spinner.fail('Deploy failed');
|
|
67
|
+
error(err instanceof Error ? err.message : String(err));
|
|
68
|
+
process.exit(1);
|
|
69
|
+
}
|
|
70
|
+
});
|
|
71
|
+
}
|
|
72
|
+
function collectFiles(baseDir, currentDir, files, functions) {
|
|
73
|
+
for (const entry of readdirSync(currentDir, { withFileTypes: true })) {
|
|
74
|
+
if (IGNORE.has(entry.name) || entry.name.startsWith('.'))
|
|
75
|
+
continue;
|
|
76
|
+
const fullPath = join(currentDir, entry.name);
|
|
77
|
+
const relPath = relative(baseDir, fullPath);
|
|
78
|
+
if (entry.isDirectory()) {
|
|
79
|
+
collectFiles(baseDir, fullPath, files, functions);
|
|
80
|
+
continue;
|
|
81
|
+
}
|
|
82
|
+
if (!entry.isFile())
|
|
83
|
+
continue;
|
|
84
|
+
const stat = statSync(fullPath);
|
|
85
|
+
if (stat.size > MAX_FILE_SIZE)
|
|
86
|
+
continue;
|
|
87
|
+
const content = readFileSync(fullPath, 'utf-8');
|
|
88
|
+
if (relPath.startsWith('functions/')) {
|
|
89
|
+
// Platform expects keys relative to functions/ (e.g. "api/health.js")
|
|
90
|
+
functions[relPath.slice('functions/'.length)] = content;
|
|
91
|
+
}
|
|
92
|
+
else {
|
|
93
|
+
files[relPath] = content;
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
function formatBytes(bytes) {
|
|
98
|
+
if (bytes < 1024)
|
|
99
|
+
return `${bytes} B`;
|
|
100
|
+
if (bytes < 1024 * 1024)
|
|
101
|
+
return `${Math.round(bytes / 1024)} KB`;
|
|
102
|
+
return `${(bytes / (1024 * 1024)).toFixed(1)} MB`;
|
|
103
|
+
}
|
|
104
|
+
//# 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,73 @@
|
|
|
1
|
+
import { spawn } from 'node:child_process';
|
|
2
|
+
import { existsSync, readFileSync } from 'node:fs';
|
|
3
|
+
import { join } from 'node:path';
|
|
4
|
+
import ora from 'ora';
|
|
5
|
+
import { ApiClient } from '../lib/client.js';
|
|
6
|
+
import { getToken, loadProjectConfig } from '../lib/config.js';
|
|
7
|
+
import { dim, error, info, success } from '../lib/output.js';
|
|
8
|
+
export function registerDev(program) {
|
|
9
|
+
program
|
|
10
|
+
.command('dev [cmd...]')
|
|
11
|
+
.description('Start local dev with platform env vars injected')
|
|
12
|
+
.action(async (cmdParts) => {
|
|
13
|
+
const token = getToken();
|
|
14
|
+
const client = new ApiClient(token);
|
|
15
|
+
const config = loadProjectConfig();
|
|
16
|
+
if (!config) {
|
|
17
|
+
error('No project linked. Run `somewhere init` first.');
|
|
18
|
+
process.exit(1);
|
|
19
|
+
}
|
|
20
|
+
const spinner = ora('Loading env vars from somewhere.tech...').start();
|
|
21
|
+
try {
|
|
22
|
+
const result = await client.call('GET', '/env', undefined, { project_id: config.project_id });
|
|
23
|
+
const vars = result.keys ?? result.vars ?? [];
|
|
24
|
+
spinner.stop();
|
|
25
|
+
success(`${vars.length} env vars loaded`);
|
|
26
|
+
// Detect dev command
|
|
27
|
+
let command = cmdParts.join(' ');
|
|
28
|
+
if (!command) {
|
|
29
|
+
command = detectDevCommand();
|
|
30
|
+
}
|
|
31
|
+
if (!command) {
|
|
32
|
+
error('Could not detect a dev command. Pass one: somewhere dev npm run dev');
|
|
33
|
+
process.exit(1);
|
|
34
|
+
}
|
|
35
|
+
info(`Starting: ${dim(command)}`);
|
|
36
|
+
console.log('');
|
|
37
|
+
const envOverrides = {
|
|
38
|
+
SOMEWHERE_PROJECT_ID: config.project_id,
|
|
39
|
+
SOMEWHERE_SUBDOMAIN: config.subdomain,
|
|
40
|
+
SOMEWHERE_URL: `https://${config.subdomain}.somewhere.tech`,
|
|
41
|
+
};
|
|
42
|
+
// Env var VALUES aren't returned by the API (encrypted). The user's
|
|
43
|
+
// functions/server will read them from the platform at runtime.
|
|
44
|
+
// Here we just inject the project context vars.
|
|
45
|
+
const child = spawn(command, {
|
|
46
|
+
shell: true,
|
|
47
|
+
stdio: 'inherit',
|
|
48
|
+
env: { ...process.env, ...envOverrides },
|
|
49
|
+
});
|
|
50
|
+
child.on('exit', (code) => process.exit(code ?? 0));
|
|
51
|
+
}
|
|
52
|
+
catch (err) {
|
|
53
|
+
spinner.fail('Failed to load env vars');
|
|
54
|
+
error(err instanceof Error ? err.message : String(err));
|
|
55
|
+
process.exit(1);
|
|
56
|
+
}
|
|
57
|
+
});
|
|
58
|
+
}
|
|
59
|
+
function detectDevCommand() {
|
|
60
|
+
const pkgPath = join(process.cwd(), 'package.json');
|
|
61
|
+
if (existsSync(pkgPath)) {
|
|
62
|
+
try {
|
|
63
|
+
const pkg = JSON.parse(readFileSync(pkgPath, 'utf-8'));
|
|
64
|
+
if (pkg.scripts?.dev)
|
|
65
|
+
return 'npm run dev';
|
|
66
|
+
if (pkg.scripts?.start)
|
|
67
|
+
return 'npm start';
|
|
68
|
+
}
|
|
69
|
+
catch { }
|
|
70
|
+
}
|
|
71
|
+
return '';
|
|
72
|
+
}
|
|
73
|
+
//# 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,80 @@
|
|
|
1
|
+
import { ApiClient } from '../lib/client.js';
|
|
2
|
+
import { getToken, loadProjectConfig } from '../lib/config.js';
|
|
3
|
+
import { dim, error, info, success, teal } from '../lib/output.js';
|
|
4
|
+
function resolveProjectId(explicit) {
|
|
5
|
+
if (explicit)
|
|
6
|
+
return explicit;
|
|
7
|
+
const config = loadProjectConfig();
|
|
8
|
+
if (!config) {
|
|
9
|
+
error('No project. Pass --project or run from a linked directory.');
|
|
10
|
+
process.exit(1);
|
|
11
|
+
}
|
|
12
|
+
return config.project_id;
|
|
13
|
+
}
|
|
14
|
+
export function registerEnv(program) {
|
|
15
|
+
const env = program
|
|
16
|
+
.command('env')
|
|
17
|
+
.description('Manage environment variables');
|
|
18
|
+
env
|
|
19
|
+
.command('list')
|
|
20
|
+
.alias('ls')
|
|
21
|
+
.description('List environment variables')
|
|
22
|
+
.option('--project <id>', 'Project ID')
|
|
23
|
+
.action(async (opts) => {
|
|
24
|
+
const client = new ApiClient(getToken());
|
|
25
|
+
const pid = resolveProjectId(opts.project);
|
|
26
|
+
try {
|
|
27
|
+
const result = await client.call('GET', '/env', undefined, { project_id: pid });
|
|
28
|
+
const vars = result.keys ?? result.vars ?? [];
|
|
29
|
+
if (!vars.length) {
|
|
30
|
+
info(dim('No environment variables set.'));
|
|
31
|
+
return;
|
|
32
|
+
}
|
|
33
|
+
for (const v of vars) {
|
|
34
|
+
console.log(` ${teal(v.key)}`);
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
catch (err) {
|
|
38
|
+
error(err instanceof Error ? err.message : String(err));
|
|
39
|
+
process.exit(1);
|
|
40
|
+
}
|
|
41
|
+
});
|
|
42
|
+
env
|
|
43
|
+
.command('set <key> <value>')
|
|
44
|
+
.description('Set an environment variable')
|
|
45
|
+
.option('--project <id>', 'Project ID')
|
|
46
|
+
.action(async (key, value, opts) => {
|
|
47
|
+
const client = new ApiClient(getToken());
|
|
48
|
+
const pid = resolveProjectId(opts.project);
|
|
49
|
+
try {
|
|
50
|
+
await client.call('POST', '/env', {
|
|
51
|
+
project_id: pid,
|
|
52
|
+
key,
|
|
53
|
+
value,
|
|
54
|
+
});
|
|
55
|
+
success(`${key} updated`);
|
|
56
|
+
}
|
|
57
|
+
catch (err) {
|
|
58
|
+
error(err instanceof Error ? err.message : String(err));
|
|
59
|
+
process.exit(1);
|
|
60
|
+
}
|
|
61
|
+
});
|
|
62
|
+
env
|
|
63
|
+
.command('delete <key>')
|
|
64
|
+
.alias('rm')
|
|
65
|
+
.description('Delete an environment variable')
|
|
66
|
+
.option('--project <id>', 'Project ID')
|
|
67
|
+
.action(async (key, opts) => {
|
|
68
|
+
const client = new ApiClient(getToken());
|
|
69
|
+
const pid = resolveProjectId(opts.project);
|
|
70
|
+
try {
|
|
71
|
+
await client.call('DELETE', `/env/${encodeURIComponent(pid)}/${encodeURIComponent(key)}`);
|
|
72
|
+
success(`${key} deleted`);
|
|
73
|
+
}
|
|
74
|
+
catch (err) {
|
|
75
|
+
error(err instanceof Error ? err.message : String(err));
|
|
76
|
+
process.exit(1);
|
|
77
|
+
}
|
|
78
|
+
});
|
|
79
|
+
}
|
|
80
|
+
//# sourceMappingURL=env.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"env.js","sourceRoot":"","sources":["../../src/commands/env.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
import prompts from 'prompts';
|
|
2
|
+
import ora from 'ora';
|
|
3
|
+
import { ApiClient } from '../lib/client.js';
|
|
4
|
+
import { getToken, loadProjectConfig, saveMcpConfig, saveProjectConfig, } from '../lib/config.js';
|
|
5
|
+
import { dim, error, info, success, teal, warn } from '../lib/output.js';
|
|
6
|
+
export function registerInit(program) {
|
|
7
|
+
program
|
|
8
|
+
.command('init')
|
|
9
|
+
.description('Initialize a somewhere.tech project in the current directory')
|
|
10
|
+
.option('--name <name>', 'Project name (skip prompt)')
|
|
11
|
+
.option('--link', 'Link to an existing project instead of creating one')
|
|
12
|
+
.action(async (opts) => {
|
|
13
|
+
const token = getToken();
|
|
14
|
+
const client = new ApiClient(token);
|
|
15
|
+
const dir = process.cwd();
|
|
16
|
+
const existing = loadProjectConfig(dir);
|
|
17
|
+
if (existing) {
|
|
18
|
+
warn(`This directory is already linked to ${teal(existing.name)}`);
|
|
19
|
+
const { overwrite } = await prompts({
|
|
20
|
+
type: 'confirm',
|
|
21
|
+
name: 'overwrite',
|
|
22
|
+
message: 'Overwrite?',
|
|
23
|
+
initial: false,
|
|
24
|
+
});
|
|
25
|
+
if (!overwrite)
|
|
26
|
+
return;
|
|
27
|
+
}
|
|
28
|
+
if (opts.link) {
|
|
29
|
+
await linkExisting(client, token, dir);
|
|
30
|
+
return;
|
|
31
|
+
}
|
|
32
|
+
let name = opts.name;
|
|
33
|
+
if (!name) {
|
|
34
|
+
const res = await prompts({
|
|
35
|
+
type: 'text',
|
|
36
|
+
name: 'name',
|
|
37
|
+
message: 'Project name',
|
|
38
|
+
initial: dir.split('/').pop() ?? 'my-app',
|
|
39
|
+
});
|
|
40
|
+
name = res.name;
|
|
41
|
+
if (!name)
|
|
42
|
+
return;
|
|
43
|
+
}
|
|
44
|
+
const subRes = await prompts({
|
|
45
|
+
type: 'text',
|
|
46
|
+
name: 'subdomain',
|
|
47
|
+
message: 'Deploy to',
|
|
48
|
+
initial: name.toLowerCase().replace(/[^a-z0-9-]/g, '-'),
|
|
49
|
+
format: (v) => `${v}.somewhere.tech`,
|
|
50
|
+
});
|
|
51
|
+
const subdomain = subRes.subdomain
|
|
52
|
+
?.replace('.somewhere.tech', '')
|
|
53
|
+
.trim();
|
|
54
|
+
if (!subdomain)
|
|
55
|
+
return;
|
|
56
|
+
const spinner = ora('Creating project...').start();
|
|
57
|
+
try {
|
|
58
|
+
const project = await client.call('POST', '/projects', { name, subdomain });
|
|
59
|
+
spinner.stop();
|
|
60
|
+
success(`Project created: ${teal(project.name)} (draft)`);
|
|
61
|
+
saveProjectConfig(dir, {
|
|
62
|
+
project_id: project.id,
|
|
63
|
+
name: project.name,
|
|
64
|
+
subdomain: project.subdomain ?? subdomain,
|
|
65
|
+
});
|
|
66
|
+
success('.somewhere.json written');
|
|
67
|
+
saveMcpConfig(dir, token);
|
|
68
|
+
success('.mcp.json written — Claude Code and Codex are now connected');
|
|
69
|
+
console.log('');
|
|
70
|
+
info('Your project is ready. Start building:');
|
|
71
|
+
info(` ${dim('claude "build me a landing page"')}`);
|
|
72
|
+
info(` ${dim('somewhere deploy')}`);
|
|
73
|
+
}
|
|
74
|
+
catch (err) {
|
|
75
|
+
spinner.fail('Failed to create project');
|
|
76
|
+
error(err instanceof Error ? err.message : String(err));
|
|
77
|
+
process.exit(1);
|
|
78
|
+
}
|
|
79
|
+
});
|
|
80
|
+
}
|
|
81
|
+
async function linkExisting(client, token, dir) {
|
|
82
|
+
const spinner = ora('Fetching projects...').start();
|
|
83
|
+
const result = await client.call('GET', '/projects');
|
|
84
|
+
spinner.stop();
|
|
85
|
+
if (!result.projects.length) {
|
|
86
|
+
error('No projects found. Create one first: somewhere project create <name>');
|
|
87
|
+
return;
|
|
88
|
+
}
|
|
89
|
+
const { projectId } = await prompts({
|
|
90
|
+
type: 'select',
|
|
91
|
+
name: 'projectId',
|
|
92
|
+
message: 'Select a project to link',
|
|
93
|
+
choices: result.projects.map((p) => ({
|
|
94
|
+
title: `${p.name} (${p.subdomain ?? 'no subdomain'})`,
|
|
95
|
+
value: p.id,
|
|
96
|
+
})),
|
|
97
|
+
});
|
|
98
|
+
if (!projectId)
|
|
99
|
+
return;
|
|
100
|
+
const project = result.projects.find((p) => p.id === projectId);
|
|
101
|
+
saveProjectConfig(dir, {
|
|
102
|
+
project_id: project.id,
|
|
103
|
+
name: project.name,
|
|
104
|
+
subdomain: project.subdomain,
|
|
105
|
+
});
|
|
106
|
+
success(`.somewhere.json linked to ${teal(project.name)}`);
|
|
107
|
+
saveMcpConfig(dir, token);
|
|
108
|
+
success('.mcp.json written — Claude Code and Codex are now connected');
|
|
109
|
+
}
|
|
110
|
+
//# sourceMappingURL=init.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"init.js","sourceRoot":"","sources":["../../src/commands/init.ts"],"names":[],"mappings":"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"}
|