shsu 0.0.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/.claude/settings.local.json +7 -0
- package/.github/workflows/publish.yml +19 -0
- package/README.md +91 -0
- package/bin/shsu.mjs +406 -0
- package/package.json +34 -0
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
name: Publish to npm
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
tags:
|
|
6
|
+
- 'v*'
|
|
7
|
+
|
|
8
|
+
jobs:
|
|
9
|
+
publish:
|
|
10
|
+
runs-on: ubuntu-latest
|
|
11
|
+
steps:
|
|
12
|
+
- uses: actions/checkout@v4
|
|
13
|
+
- uses: actions/setup-node@v4
|
|
14
|
+
with:
|
|
15
|
+
node-version: '20'
|
|
16
|
+
registry-url: 'https://registry.npmjs.org'
|
|
17
|
+
- run: npm publish
|
|
18
|
+
env:
|
|
19
|
+
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
|
package/README.md
ADDED
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
# shsu
|
|
2
|
+
|
|
3
|
+
**S**elf-**H**osted **S**upabase **U**tilities
|
|
4
|
+
|
|
5
|
+
Deploy and manage Supabase Edge Functions on Coolify-hosted Supabase.
|
|
6
|
+
|
|
7
|
+
## Install
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
npm install -g shsu
|
|
11
|
+
# or use directly
|
|
12
|
+
npx shsu
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
## Setup
|
|
16
|
+
|
|
17
|
+
Run the init command to configure your project:
|
|
18
|
+
|
|
19
|
+
```bash
|
|
20
|
+
npx shsu init
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
This adds config to your `package.json`:
|
|
24
|
+
|
|
25
|
+
```json
|
|
26
|
+
{
|
|
27
|
+
"shsu": {
|
|
28
|
+
"server": "root@your-coolify-server",
|
|
29
|
+
"remotePath": "/data/coolify/services/YOUR_SERVICE_ID/volumes/functions",
|
|
30
|
+
"url": "https://your-supabase.example.com"
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
Find your `remotePath` by running on your server:
|
|
36
|
+
|
|
37
|
+
```bash
|
|
38
|
+
docker inspect $(docker ps -q --filter 'name=edge') | grep -A 5 "Mounts"
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
## Usage
|
|
42
|
+
|
|
43
|
+
```bash
|
|
44
|
+
# Configure project
|
|
45
|
+
shsu init
|
|
46
|
+
|
|
47
|
+
# Show current configuration
|
|
48
|
+
shsu env
|
|
49
|
+
|
|
50
|
+
# Deploy all functions
|
|
51
|
+
shsu deploy
|
|
52
|
+
|
|
53
|
+
# Deploy single function
|
|
54
|
+
shsu deploy hello-world
|
|
55
|
+
|
|
56
|
+
# Deploy without restarting edge-runtime
|
|
57
|
+
shsu deploy hello-world --no-restart
|
|
58
|
+
|
|
59
|
+
# Stream logs
|
|
60
|
+
shsu logs
|
|
61
|
+
|
|
62
|
+
# Stream logs filtered by function name
|
|
63
|
+
shsu logs hello-world
|
|
64
|
+
|
|
65
|
+
# List local and remote functions
|
|
66
|
+
shsu list
|
|
67
|
+
|
|
68
|
+
# Invoke a function
|
|
69
|
+
shsu invoke hello-world '{"name":"Stefan"}'
|
|
70
|
+
|
|
71
|
+
# Create new function from template
|
|
72
|
+
shsu new my-function
|
|
73
|
+
|
|
74
|
+
# Restart edge-runtime
|
|
75
|
+
shsu restart
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
## Configuration
|
|
79
|
+
|
|
80
|
+
Config is read from `package.json` "shsu" key. Environment variables override package.json values.
|
|
81
|
+
|
|
82
|
+
| Key / Env Var | Required | Description |
|
|
83
|
+
|---------------|----------|-------------|
|
|
84
|
+
| `server` / `SHSU_SERVER` | Yes | SSH host (e.g., `root@your-server.com`) |
|
|
85
|
+
| `remotePath` / `SHSU_REMOTE_PATH` | Yes | Remote path to functions directory |
|
|
86
|
+
| `url` / `SHSU_URL` | For `invoke` | Supabase URL |
|
|
87
|
+
| `localPath` / `SHSU_LOCAL_PATH` | No | Local functions path (default: `./supabase/functions`) |
|
|
88
|
+
|
|
89
|
+
## License
|
|
90
|
+
|
|
91
|
+
MIT
|
package/bin/shsu.mjs
ADDED
|
@@ -0,0 +1,406 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
import { spawn, execSync } from 'node:child_process';
|
|
4
|
+
import { existsSync, mkdirSync, writeFileSync, readFileSync, readdirSync } from 'node:fs';
|
|
5
|
+
import { join, resolve } from 'node:path';
|
|
6
|
+
import { createInterface } from 'node:readline';
|
|
7
|
+
|
|
8
|
+
// ─────────────────────────────────────────────────────────────
|
|
9
|
+
// Configuration (package.json + environment variables)
|
|
10
|
+
// ─────────────────────────────────────────────────────────────
|
|
11
|
+
function loadConfig() {
|
|
12
|
+
let pkgConfig = {};
|
|
13
|
+
|
|
14
|
+
// Try to load from package.json
|
|
15
|
+
const pkgPath = join(process.cwd(), 'package.json');
|
|
16
|
+
if (existsSync(pkgPath)) {
|
|
17
|
+
try {
|
|
18
|
+
const pkg = JSON.parse(readFileSync(pkgPath, 'utf-8'));
|
|
19
|
+
pkgConfig = pkg.shsu || {};
|
|
20
|
+
} catch (e) {
|
|
21
|
+
// Ignore parse errors
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
// Env vars override package.json
|
|
26
|
+
return {
|
|
27
|
+
server: process.env.SHSU_SERVER || pkgConfig.server,
|
|
28
|
+
remotePath: process.env.SHSU_REMOTE_PATH || pkgConfig.remotePath,
|
|
29
|
+
localPath: process.env.SHSU_LOCAL_PATH || pkgConfig.localPath || './supabase/functions',
|
|
30
|
+
url: process.env.SHSU_URL || pkgConfig.url,
|
|
31
|
+
};
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
const config = loadConfig();
|
|
35
|
+
|
|
36
|
+
// ─────────────────────────────────────────────────────────────
|
|
37
|
+
// Colors
|
|
38
|
+
// ─────────────────────────────────────────────────────────────
|
|
39
|
+
const c = {
|
|
40
|
+
red: (s) => `\x1b[31m${s}\x1b[0m`,
|
|
41
|
+
green: (s) => `\x1b[32m${s}\x1b[0m`,
|
|
42
|
+
yellow: (s) => `\x1b[33m${s}\x1b[0m`,
|
|
43
|
+
blue: (s) => `\x1b[34m${s}\x1b[0m`,
|
|
44
|
+
dim: (s) => `\x1b[2m${s}\x1b[0m`,
|
|
45
|
+
};
|
|
46
|
+
|
|
47
|
+
const info = (msg) => console.log(`${c.blue('▸')} ${msg}`);
|
|
48
|
+
const success = (msg) => console.log(`${c.green('✓')} ${msg}`);
|
|
49
|
+
const error = (msg) => {
|
|
50
|
+
console.error(`${c.red('✗')} ${msg}`);
|
|
51
|
+
process.exit(1);
|
|
52
|
+
};
|
|
53
|
+
|
|
54
|
+
// ─────────────────────────────────────────────────────────────
|
|
55
|
+
// Helpers
|
|
56
|
+
// ─────────────────────────────────────────────────────────────
|
|
57
|
+
function requireVar(name) {
|
|
58
|
+
if (!config[name]) {
|
|
59
|
+
error(`Missing required env var: SHSU_${name.toUpperCase()} (see 'shsu env')`);
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
function requireServer() {
|
|
64
|
+
requireVar('server');
|
|
65
|
+
requireVar('remotePath');
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
function run(cmd, args, options = {}) {
|
|
69
|
+
return new Promise((resolve, reject) => {
|
|
70
|
+
const proc = spawn(cmd, args, { stdio: 'inherit', ...options });
|
|
71
|
+
proc.on('close', (code) => {
|
|
72
|
+
if (code === 0) resolve();
|
|
73
|
+
else reject(new Error(`Command failed with code ${code}`));
|
|
74
|
+
});
|
|
75
|
+
proc.on('error', reject);
|
|
76
|
+
});
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
function runSync(cmd) {
|
|
80
|
+
try {
|
|
81
|
+
return execSync(cmd, { encoding: 'utf-8', stdio: ['pipe', 'pipe', 'pipe'] }).trim();
|
|
82
|
+
} catch (e) {
|
|
83
|
+
return null;
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
function getEdgeContainer() {
|
|
88
|
+
return runSync(`ssh ${config.server} "docker ps -q --filter 'name=edge'"`);
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
// ─────────────────────────────────────────────────────────────
|
|
92
|
+
// Commands
|
|
93
|
+
// ─────────────────────────────────────────────────────────────
|
|
94
|
+
|
|
95
|
+
async function cmdDeploy(funcName, noRestart = false) {
|
|
96
|
+
requireServer();
|
|
97
|
+
|
|
98
|
+
if (!funcName) {
|
|
99
|
+
info('Deploying all functions...');
|
|
100
|
+
await run('rsync', [
|
|
101
|
+
'-avz', '--delete',
|
|
102
|
+
'--exclude=*.test.ts',
|
|
103
|
+
'--exclude=*.spec.ts',
|
|
104
|
+
`${config.localPath}/`,
|
|
105
|
+
`${config.server}:${config.remotePath}/`,
|
|
106
|
+
]);
|
|
107
|
+
} else {
|
|
108
|
+
const funcPath = join(config.localPath, funcName);
|
|
109
|
+
if (!existsSync(funcPath)) {
|
|
110
|
+
error(`Function not found: ${funcPath}`);
|
|
111
|
+
}
|
|
112
|
+
info(`Deploying ${funcName}...`);
|
|
113
|
+
await run('rsync', [
|
|
114
|
+
'-avz',
|
|
115
|
+
`${funcPath}/`,
|
|
116
|
+
`${config.server}:${config.remotePath}/${funcName}/`,
|
|
117
|
+
]);
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
if (!noRestart) {
|
|
121
|
+
info('Restarting edge-runtime...');
|
|
122
|
+
await run('ssh', [
|
|
123
|
+
config.server,
|
|
124
|
+
`docker restart $(docker ps -q --filter 'name=edge')`,
|
|
125
|
+
], { stdio: ['inherit', 'pipe', 'inherit'] });
|
|
126
|
+
success(`Deployed${funcName ? ` ${funcName}` : ''}`);
|
|
127
|
+
} else {
|
|
128
|
+
success(`Synced${funcName ? ` ${funcName}` : ''} (no restart)`);
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
async function cmdLogs(filter, lines = 100) {
|
|
133
|
+
requireServer();
|
|
134
|
+
|
|
135
|
+
info(`Streaming logs${filter ? ` (filter: ${filter})` : ''}... (Ctrl+C to exit)`);
|
|
136
|
+
|
|
137
|
+
const sshArgs = [
|
|
138
|
+
config.server,
|
|
139
|
+
`docker logs -f $(docker ps -q --filter 'name=edge') --tail ${lines} 2>&1`,
|
|
140
|
+
];
|
|
141
|
+
|
|
142
|
+
if (filter) {
|
|
143
|
+
const ssh = spawn('ssh', sshArgs, { stdio: ['inherit', 'pipe', 'inherit'] });
|
|
144
|
+
const grep = spawn('grep', ['--line-buffered', '-i', filter], {
|
|
145
|
+
stdio: ['pipe', 'inherit', 'inherit'],
|
|
146
|
+
});
|
|
147
|
+
ssh.stdout.pipe(grep.stdin);
|
|
148
|
+
|
|
149
|
+
await new Promise((resolve) => {
|
|
150
|
+
ssh.on('close', resolve);
|
|
151
|
+
grep.on('close', resolve);
|
|
152
|
+
});
|
|
153
|
+
} else {
|
|
154
|
+
await run('ssh', sshArgs);
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
async function cmdList() {
|
|
159
|
+
requireServer();
|
|
160
|
+
|
|
161
|
+
info('Remote functions:');
|
|
162
|
+
const remote = runSync(`ssh ${config.server} "ls -1 ${config.remotePath} 2>/dev/null"`);
|
|
163
|
+
if (remote) {
|
|
164
|
+
remote.split('\n').filter(Boolean).forEach((f) => console.log(` • ${f}`));
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
console.log('');
|
|
168
|
+
info('Local functions:');
|
|
169
|
+
if (existsSync(config.localPath)) {
|
|
170
|
+
readdirSync(config.localPath, { withFileTypes: true })
|
|
171
|
+
.filter((d) => d.isDirectory())
|
|
172
|
+
.forEach((d) => console.log(` • ${d.name}`));
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
async function cmdInvoke(funcName, data = '{}') {
|
|
177
|
+
requireVar('url');
|
|
178
|
+
|
|
179
|
+
if (!funcName) {
|
|
180
|
+
error('Usage: shsu invoke <function-name> [json-data]');
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
info(`Invoking ${funcName}...`);
|
|
184
|
+
await run('curl', [
|
|
185
|
+
'-s', '-X', 'POST',
|
|
186
|
+
`${config.url}/functions/v1/${funcName}`,
|
|
187
|
+
'-H', 'Content-Type: application/json',
|
|
188
|
+
'-d', data,
|
|
189
|
+
]);
|
|
190
|
+
console.log('');
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
async function cmdRestart() {
|
|
194
|
+
requireServer();
|
|
195
|
+
|
|
196
|
+
info('Restarting edge-runtime...');
|
|
197
|
+
await run('ssh', [
|
|
198
|
+
config.server,
|
|
199
|
+
`docker restart $(docker ps -q --filter 'name=edge')`,
|
|
200
|
+
], { stdio: ['inherit', 'pipe', 'inherit'] });
|
|
201
|
+
success('Restarted');
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
async function cmdNew(funcName) {
|
|
205
|
+
if (!funcName) {
|
|
206
|
+
error('Usage: shsu new <function-name>');
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
const funcPath = join(config.localPath, funcName);
|
|
210
|
+
if (existsSync(funcPath)) {
|
|
211
|
+
error(`Function already exists: ${funcName}`);
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
mkdirSync(funcPath, { recursive: true });
|
|
215
|
+
writeFileSync(
|
|
216
|
+
join(funcPath, 'index.ts'),
|
|
217
|
+
`Deno.serve(async (req) => {
|
|
218
|
+
try {
|
|
219
|
+
const { name } = await req.json()
|
|
220
|
+
|
|
221
|
+
return new Response(
|
|
222
|
+
JSON.stringify({ message: \`Hello \${name}!\` }),
|
|
223
|
+
{ headers: { "Content-Type": "application/json" } }
|
|
224
|
+
)
|
|
225
|
+
} catch (error) {
|
|
226
|
+
return new Response(
|
|
227
|
+
JSON.stringify({ error: error.message }),
|
|
228
|
+
{ status: 400, headers: { "Content-Type": "application/json" } }
|
|
229
|
+
)
|
|
230
|
+
}
|
|
231
|
+
})
|
|
232
|
+
`
|
|
233
|
+
);
|
|
234
|
+
|
|
235
|
+
success(`Created ${funcPath}/index.ts`);
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
function cmdEnv() {
|
|
239
|
+
console.log(`
|
|
240
|
+
${c.yellow('Configuration (package.json "shsu" key or environment variables):')}
|
|
241
|
+
|
|
242
|
+
server SSH host for your Coolify server
|
|
243
|
+
remotePath Remote path to functions directory
|
|
244
|
+
url Supabase URL (for invoke command)
|
|
245
|
+
localPath Local functions path (default: ./supabase/functions)
|
|
246
|
+
|
|
247
|
+
${c.yellow('Environment variables override package.json:')}
|
|
248
|
+
|
|
249
|
+
SHSU_SERVER, SHSU_REMOTE_PATH, SHSU_URL, SHSU_LOCAL_PATH
|
|
250
|
+
|
|
251
|
+
${c.yellow('Current values:')}
|
|
252
|
+
|
|
253
|
+
server = ${config.server || c.dim('(not set)')}
|
|
254
|
+
remotePath = ${config.remotePath || c.dim('(not set)')}
|
|
255
|
+
url = ${config.url || c.dim('(not set)')}
|
|
256
|
+
localPath = ${config.localPath}
|
|
257
|
+
|
|
258
|
+
${c.dim('Run "shsu init" to configure via prompts.')}
|
|
259
|
+
`);
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
async function cmdInit() {
|
|
263
|
+
const pkgPath = join(process.cwd(), 'package.json');
|
|
264
|
+
|
|
265
|
+
if (!existsSync(pkgPath)) {
|
|
266
|
+
error('No package.json found. Run "npm init" first.');
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
const rl = createInterface({
|
|
270
|
+
input: process.stdin,
|
|
271
|
+
output: process.stdout,
|
|
272
|
+
});
|
|
273
|
+
|
|
274
|
+
const ask = (question, defaultVal) =>
|
|
275
|
+
new Promise((resolve) => {
|
|
276
|
+
const prompt = defaultVal ? `${question} [${defaultVal}]: ` : `${question}: `;
|
|
277
|
+
rl.question(prompt, (answer) => {
|
|
278
|
+
resolve(answer.trim() || defaultVal || '');
|
|
279
|
+
});
|
|
280
|
+
});
|
|
281
|
+
|
|
282
|
+
console.log(`\n${c.blue('shsu init')} - Configure project\n`);
|
|
283
|
+
|
|
284
|
+
const server = await ask('Server (e.g. root@server.com)', config.server);
|
|
285
|
+
const remotePath = await ask('Remote path to functions', config.remotePath);
|
|
286
|
+
const url = await ask('Supabase URL', config.url);
|
|
287
|
+
const localPath = await ask('Local functions path', config.localPath || './supabase/functions');
|
|
288
|
+
|
|
289
|
+
rl.close();
|
|
290
|
+
|
|
291
|
+
// Read and update package.json
|
|
292
|
+
const pkg = JSON.parse(readFileSync(pkgPath, 'utf-8'));
|
|
293
|
+
pkg.shsu = {
|
|
294
|
+
server: server || undefined,
|
|
295
|
+
remotePath: remotePath || undefined,
|
|
296
|
+
url: url || undefined,
|
|
297
|
+
localPath: localPath !== './supabase/functions' ? localPath : undefined,
|
|
298
|
+
};
|
|
299
|
+
|
|
300
|
+
// Remove undefined values
|
|
301
|
+
Object.keys(pkg.shsu).forEach((key) => {
|
|
302
|
+
if (pkg.shsu[key] === undefined) delete pkg.shsu[key];
|
|
303
|
+
});
|
|
304
|
+
|
|
305
|
+
writeFileSync(pkgPath, JSON.stringify(pkg, null, 2) + '\n');
|
|
306
|
+
|
|
307
|
+
console.log('');
|
|
308
|
+
success('Added shsu config to package.json');
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
function cmdHelp() {
|
|
312
|
+
console.log(`
|
|
313
|
+
${c.blue('shsu')} - Self-Hosted Supabase Utilities
|
|
314
|
+
|
|
315
|
+
${c.yellow('Usage:')}
|
|
316
|
+
shsu <command> [options]
|
|
317
|
+
|
|
318
|
+
${c.yellow('Commands:')}
|
|
319
|
+
init Configure shsu for this project
|
|
320
|
+
|
|
321
|
+
deploy [name] Deploy function(s) to server
|
|
322
|
+
- No args: deploy all functions
|
|
323
|
+
- With name: deploy single function
|
|
324
|
+
Options: --no-restart
|
|
325
|
+
|
|
326
|
+
logs [filter] Stream edge-runtime logs
|
|
327
|
+
- Optional filter string
|
|
328
|
+
|
|
329
|
+
list List functions (local and remote)
|
|
330
|
+
|
|
331
|
+
invoke <n> [json] Invoke a function
|
|
332
|
+
|
|
333
|
+
restart Restart edge-runtime container
|
|
334
|
+
|
|
335
|
+
new <name> Create new function from template
|
|
336
|
+
|
|
337
|
+
env Show current configuration
|
|
338
|
+
|
|
339
|
+
${c.yellow('Examples:')}
|
|
340
|
+
shsu init
|
|
341
|
+
shsu deploy
|
|
342
|
+
shsu deploy hello-world --no-restart
|
|
343
|
+
shsu logs hello-world
|
|
344
|
+
shsu invoke hello-world '{"name":"Stefan"}'
|
|
345
|
+
shsu new my-function
|
|
346
|
+
|
|
347
|
+
${c.yellow('Setup:')}
|
|
348
|
+
Run 'shsu init' to configure, or set values in package.json "shsu" key.
|
|
349
|
+
`);
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
// ─────────────────────────────────────────────────────────────
|
|
353
|
+
// Main
|
|
354
|
+
// ─────────────────────────────────────────────────────────────
|
|
355
|
+
async function main() {
|
|
356
|
+
const args = process.argv.slice(2);
|
|
357
|
+
const cmd = args[0] || 'help';
|
|
358
|
+
|
|
359
|
+
try {
|
|
360
|
+
switch (cmd) {
|
|
361
|
+
case 'deploy': {
|
|
362
|
+
const noRestart = args.includes('--no-restart');
|
|
363
|
+
const funcName = args[1] === '--no-restart' ? args[2] : args[1];
|
|
364
|
+
await cmdDeploy(funcName, noRestart);
|
|
365
|
+
break;
|
|
366
|
+
}
|
|
367
|
+
case 'logs':
|
|
368
|
+
case 'log':
|
|
369
|
+
await cmdLogs(args[1], args[2] || 100);
|
|
370
|
+
break;
|
|
371
|
+
case 'list':
|
|
372
|
+
case 'ls':
|
|
373
|
+
await cmdList();
|
|
374
|
+
break;
|
|
375
|
+
case 'invoke':
|
|
376
|
+
case 'call':
|
|
377
|
+
await cmdInvoke(args[1], args[2]);
|
|
378
|
+
break;
|
|
379
|
+
case 'restart':
|
|
380
|
+
await cmdRestart();
|
|
381
|
+
break;
|
|
382
|
+
case 'new':
|
|
383
|
+
case 'create':
|
|
384
|
+
await cmdNew(args[1]);
|
|
385
|
+
break;
|
|
386
|
+
case 'init':
|
|
387
|
+
await cmdInit();
|
|
388
|
+
break;
|
|
389
|
+
case 'env':
|
|
390
|
+
case 'config':
|
|
391
|
+
cmdEnv();
|
|
392
|
+
break;
|
|
393
|
+
case 'help':
|
|
394
|
+
case '-h':
|
|
395
|
+
case '--help':
|
|
396
|
+
cmdHelp();
|
|
397
|
+
break;
|
|
398
|
+
default:
|
|
399
|
+
error(`Unknown command: ${cmd} (try 'shsu help')`);
|
|
400
|
+
}
|
|
401
|
+
} catch (e) {
|
|
402
|
+
error(e.message);
|
|
403
|
+
}
|
|
404
|
+
}
|
|
405
|
+
|
|
406
|
+
main();
|
package/package.json
ADDED
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "shsu",
|
|
3
|
+
"version": "0.0.1",
|
|
4
|
+
"description": "CLI for deploying and managing Supabase Edge Functions on self-hosted Supabase (Coolify, Docker Compose). Sync functions via rsync, stream logs, and invoke endpoints.",
|
|
5
|
+
"scripts": {
|
|
6
|
+
"test": "echo \"Error: no test specified\" && exit 1"
|
|
7
|
+
},
|
|
8
|
+
"bin": {
|
|
9
|
+
"shsu": "./bin/shsu.mjs"
|
|
10
|
+
},
|
|
11
|
+
"repository": {
|
|
12
|
+
"type": "git",
|
|
13
|
+
"url": "git+https://github.com/YUZU-Hub/shsu.git"
|
|
14
|
+
},
|
|
15
|
+
"keywords": [
|
|
16
|
+
"supabase",
|
|
17
|
+
"edge-functions",
|
|
18
|
+
"deno",
|
|
19
|
+
"coolify",
|
|
20
|
+
"self-hosted",
|
|
21
|
+
"deploy",
|
|
22
|
+
"cli",
|
|
23
|
+
"serverless",
|
|
24
|
+
"docker",
|
|
25
|
+
"rsync"
|
|
26
|
+
],
|
|
27
|
+
"author": "Stefan Lange-Hegermann <stefan@yuzuhub.com>",
|
|
28
|
+
"license": "MIT",
|
|
29
|
+
"type": "module",
|
|
30
|
+
"bugs": {
|
|
31
|
+
"url": "https://github.com/YUZU-Hub/shsu/issues"
|
|
32
|
+
},
|
|
33
|
+
"homepage": "https://github.com/YUZU-Hub/shsu#readme"
|
|
34
|
+
}
|