shsu 0.0.6 → 0.0.8
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 +1 -0
- package/bin/shsu.mjs +177 -12
- package/package.json +1 -1
package/README.md
CHANGED
package/bin/shsu.mjs
CHANGED
|
@@ -428,6 +428,11 @@ async function cmdMcp() {
|
|
|
428
428
|
description: 'Run SQL migrations on the database. Syncs migration files via rsync and executes them via psql.',
|
|
429
429
|
inputSchema: { type: 'object', properties: {} },
|
|
430
430
|
},
|
|
431
|
+
{
|
|
432
|
+
name: 'docs',
|
|
433
|
+
description: 'Get documentation on how to set up and use shsu for deploying Supabase Edge Functions.',
|
|
434
|
+
inputSchema: { type: 'object', properties: {} },
|
|
435
|
+
},
|
|
431
436
|
];
|
|
432
437
|
|
|
433
438
|
const serverInfo = {
|
|
@@ -460,7 +465,18 @@ async function cmdMcp() {
|
|
|
460
465
|
switch (name) {
|
|
461
466
|
case 'deploy': {
|
|
462
467
|
if (!config.server || !config.remotePath) {
|
|
463
|
-
return { content: [{ type: 'text', text:
|
|
468
|
+
return { content: [{ type: 'text', text: `Error: server and remotePath must be configured.
|
|
469
|
+
|
|
470
|
+
To fix, add to package.json:
|
|
471
|
+
{
|
|
472
|
+
"shsu": {
|
|
473
|
+
"server": "root@your-server.com",
|
|
474
|
+
"remotePath": "/data/coolify/services/xxx/volumes/functions"
|
|
475
|
+
}
|
|
476
|
+
}
|
|
477
|
+
|
|
478
|
+
Find remotePath by running on your server:
|
|
479
|
+
docker inspect $(docker ps -q --filter 'name=edge') | grep -A 5 "Mounts"` }] };
|
|
464
480
|
}
|
|
465
481
|
const funcName = args.name;
|
|
466
482
|
const noRestart = args.noRestart || false;
|
|
@@ -471,7 +487,9 @@ async function cmdMcp() {
|
|
|
471
487
|
} else {
|
|
472
488
|
const funcPath = join(config.localPath, funcName);
|
|
473
489
|
if (!existsSync(funcPath)) {
|
|
474
|
-
return { content: [{ type: 'text', text: `Error: Function not found: ${funcPath}
|
|
490
|
+
return { content: [{ type: 'text', text: `Error: Function not found: ${funcPath}
|
|
491
|
+
|
|
492
|
+
To fix, create the function first using the 'new' tool with name: "${funcName}"` }] };
|
|
475
493
|
}
|
|
476
494
|
output = captureExec(`rsync -avz "${funcPath}/" "${config.server}:${config.remotePath}/${funcName}/"`);
|
|
477
495
|
}
|
|
@@ -485,7 +503,18 @@ async function cmdMcp() {
|
|
|
485
503
|
|
|
486
504
|
case 'list': {
|
|
487
505
|
if (!config.server || !config.remotePath) {
|
|
488
|
-
return { content: [{ type: 'text', text:
|
|
506
|
+
return { content: [{ type: 'text', text: `Error: server and remotePath must be configured.
|
|
507
|
+
|
|
508
|
+
To fix, add to package.json:
|
|
509
|
+
{
|
|
510
|
+
"shsu": {
|
|
511
|
+
"server": "root@your-server.com",
|
|
512
|
+
"remotePath": "/data/coolify/services/xxx/volumes/functions"
|
|
513
|
+
}
|
|
514
|
+
}
|
|
515
|
+
|
|
516
|
+
Find remotePath by running on your server:
|
|
517
|
+
docker inspect $(docker ps -q --filter 'name=edge') | grep -A 5 "Mounts"` }] };
|
|
489
518
|
}
|
|
490
519
|
const remote = captureExec(`ssh ${config.server} "ls -1 ${config.remotePath} 2>/dev/null"`) || '(none)';
|
|
491
520
|
let local = '(none)';
|
|
@@ -500,10 +529,19 @@ async function cmdMcp() {
|
|
|
500
529
|
|
|
501
530
|
case 'invoke': {
|
|
502
531
|
if (!config.url) {
|
|
503
|
-
return { content: [{ type: 'text', text:
|
|
532
|
+
return { content: [{ type: 'text', text: `Error: url must be configured for invoke.
|
|
533
|
+
|
|
534
|
+
To fix, add to package.json:
|
|
535
|
+
{
|
|
536
|
+
"shsu": {
|
|
537
|
+
"url": "https://your-supabase.example.com"
|
|
538
|
+
}
|
|
539
|
+
}` }] };
|
|
504
540
|
}
|
|
505
541
|
if (!args.name) {
|
|
506
|
-
return { content: [{ type: 'text', text:
|
|
542
|
+
return { content: [{ type: 'text', text: `Error: function name is required.
|
|
543
|
+
|
|
544
|
+
Usage: invoke tool with { "name": "function-name", "data": "{\\"key\\": \\"value\\"}" }` }] };
|
|
507
545
|
}
|
|
508
546
|
const data = args.data || '{}';
|
|
509
547
|
const output = captureExec(`curl -s -X POST "${config.url}/functions/v1/${args.name}" -H "Content-Type: application/json" -d '${data}'`);
|
|
@@ -512,7 +550,14 @@ async function cmdMcp() {
|
|
|
512
550
|
|
|
513
551
|
case 'restart': {
|
|
514
552
|
if (!config.server) {
|
|
515
|
-
return { content: [{ type: 'text', text:
|
|
553
|
+
return { content: [{ type: 'text', text: `Error: server must be configured.
|
|
554
|
+
|
|
555
|
+
To fix, add to package.json:
|
|
556
|
+
{
|
|
557
|
+
"shsu": {
|
|
558
|
+
"server": "root@your-server.com"
|
|
559
|
+
}
|
|
560
|
+
}` }] };
|
|
516
561
|
}
|
|
517
562
|
const output = captureExec(`ssh ${config.server} "docker restart \\$(docker ps -q --filter 'name=${config.edgeContainer}')"`);
|
|
518
563
|
return { content: [{ type: 'text', text: `Restarted edge-runtime\n\n${output}` }] };
|
|
@@ -520,11 +565,15 @@ async function cmdMcp() {
|
|
|
520
565
|
|
|
521
566
|
case 'new': {
|
|
522
567
|
if (!args.name) {
|
|
523
|
-
return { content: [{ type: 'text', text:
|
|
568
|
+
return { content: [{ type: 'text', text: `Error: function name is required.
|
|
569
|
+
|
|
570
|
+
Usage: new tool with { "name": "my-function-name" }` }] };
|
|
524
571
|
}
|
|
525
572
|
const funcPath = join(config.localPath, args.name);
|
|
526
573
|
if (existsSync(funcPath)) {
|
|
527
|
-
return { content: [{ type: 'text', text: `Error: Function already exists: ${args.name}
|
|
574
|
+
return { content: [{ type: 'text', text: `Error: Function already exists: ${args.name}
|
|
575
|
+
|
|
576
|
+
The function already exists at ${funcPath}. To update it, edit the code and use the 'deploy' tool.` }] };
|
|
528
577
|
}
|
|
529
578
|
mkdirSync(funcPath, { recursive: true });
|
|
530
579
|
writeFileSync(
|
|
@@ -560,16 +609,35 @@ async function cmdMcp() {
|
|
|
560
609
|
|
|
561
610
|
case 'migrate': {
|
|
562
611
|
if (!config.server) {
|
|
563
|
-
return { content: [{ type: 'text', text:
|
|
612
|
+
return { content: [{ type: 'text', text: `Error: server must be configured.
|
|
613
|
+
|
|
614
|
+
To fix, add to package.json:
|
|
615
|
+
{
|
|
616
|
+
"shsu": {
|
|
617
|
+
"server": "root@your-server.com"
|
|
618
|
+
}
|
|
619
|
+
}` }] };
|
|
564
620
|
}
|
|
565
621
|
if (!existsSync(config.migrationsPath)) {
|
|
566
|
-
return { content: [{ type: 'text', text: `Error: Migrations folder not found: ${config.migrationsPath}
|
|
622
|
+
return { content: [{ type: 'text', text: `Error: Migrations folder not found: ${config.migrationsPath}
|
|
623
|
+
|
|
624
|
+
To fix, create the migrations directory and add .sql files:
|
|
625
|
+
mkdir -p ${config.migrationsPath}
|
|
626
|
+
|
|
627
|
+
Then add migration files like:
|
|
628
|
+
${config.migrationsPath}/001_create_tables.sql
|
|
629
|
+
${config.migrationsPath}/002_add_indexes.sql` }] };
|
|
567
630
|
}
|
|
568
631
|
const migrations = readdirSync(config.migrationsPath)
|
|
569
632
|
.filter((f) => f.endsWith('.sql'))
|
|
570
633
|
.sort();
|
|
571
634
|
if (migrations.length === 0) {
|
|
572
|
-
return { content: [{ type: 'text', text:
|
|
635
|
+
return { content: [{ type: 'text', text: `No migration files found in ${config.migrationsPath}
|
|
636
|
+
|
|
637
|
+
Add .sql files to the migrations folder, e.g.:
|
|
638
|
+
${config.migrationsPath}/001_create_tables.sql
|
|
639
|
+
|
|
640
|
+
Files are executed alphabetically, so use numeric prefixes for ordering.` }] };
|
|
573
641
|
}
|
|
574
642
|
let output = `Found ${migrations.length} migration(s): ${migrations.join(', ')}\n\n`;
|
|
575
643
|
// Sync migrations
|
|
@@ -577,7 +645,17 @@ async function cmdMcp() {
|
|
|
577
645
|
// Find db container
|
|
578
646
|
const dbContainer = captureExec(`ssh ${config.server} "docker ps -q --filter 'name=${config.dbContainer}'"`);
|
|
579
647
|
if (!dbContainer) {
|
|
580
|
-
return { content: [{ type: 'text', text: `Error: Database container not found (filter: ${config.dbContainer})
|
|
648
|
+
return { content: [{ type: 'text', text: `Error: Database container not found (filter: ${config.dbContainer})
|
|
649
|
+
|
|
650
|
+
To fix:
|
|
651
|
+
1. SSH to your server and run: docker ps
|
|
652
|
+
2. Find the postgres container name (e.g., abc123-supabase-db-1)
|
|
653
|
+
3. Update dbContainer in package.json to match a unique part of the name:
|
|
654
|
+
{
|
|
655
|
+
"shsu": {
|
|
656
|
+
"dbContainer": "supabase-db"
|
|
657
|
+
}
|
|
658
|
+
}` }] };
|
|
581
659
|
}
|
|
582
660
|
// Run migrations
|
|
583
661
|
for (const migration of migrations) {
|
|
@@ -588,6 +666,93 @@ async function cmdMcp() {
|
|
|
588
666
|
return { content: [{ type: 'text', text: output }] };
|
|
589
667
|
}
|
|
590
668
|
|
|
669
|
+
case 'docs': {
|
|
670
|
+
return {
|
|
671
|
+
content: [{
|
|
672
|
+
type: 'text',
|
|
673
|
+
text: `# shsu - Self-Hosted Supabase Utilities
|
|
674
|
+
|
|
675
|
+
Deploy and manage Supabase Edge Functions on Coolify-hosted Supabase.
|
|
676
|
+
|
|
677
|
+
## Project Setup
|
|
678
|
+
|
|
679
|
+
1. **Configure shsu** by adding to package.json:
|
|
680
|
+
\`\`\`json
|
|
681
|
+
{
|
|
682
|
+
"shsu": {
|
|
683
|
+
"server": "root@your-coolify-server",
|
|
684
|
+
"remotePath": "/data/coolify/services/YOUR_SERVICE_ID/volumes/functions",
|
|
685
|
+
"url": "https://your-supabase.example.com",
|
|
686
|
+
"edgeContainer": "edge",
|
|
687
|
+
"dbContainer": "postgres"
|
|
688
|
+
}
|
|
689
|
+
}
|
|
690
|
+
\`\`\`
|
|
691
|
+
|
|
692
|
+
Or run \`npx shsu init\` for interactive setup.
|
|
693
|
+
|
|
694
|
+
2. **Find configuration values** by SSH'ing to your server:
|
|
695
|
+
- Container names: \`docker ps\` (Coolify uses pattern \`<service>-<uuid>\`)
|
|
696
|
+
- Remote path: \`docker inspect $(docker ps -q --filter 'name=edge') | grep -A 5 "Mounts"\`
|
|
697
|
+
|
|
698
|
+
## Directory Structure
|
|
699
|
+
|
|
700
|
+
\`\`\`
|
|
701
|
+
your-project/
|
|
702
|
+
├── package.json # Contains shsu config
|
|
703
|
+
├── supabase/
|
|
704
|
+
│ ├── functions/ # Edge functions (default localPath)
|
|
705
|
+
│ │ ├── hello-world/
|
|
706
|
+
│ │ │ └── index.ts
|
|
707
|
+
│ │ └── another-func/
|
|
708
|
+
│ │ └── index.ts
|
|
709
|
+
│ └── migrations/ # SQL migrations (default migrationsPath)
|
|
710
|
+
│ ├── 001_create_users.sql
|
|
711
|
+
│ └── 002_add_indexes.sql
|
|
712
|
+
\`\`\`
|
|
713
|
+
|
|
714
|
+
## Configuration Options
|
|
715
|
+
|
|
716
|
+
| Key | Required | Default | Description |
|
|
717
|
+
|-----|----------|---------|-------------|
|
|
718
|
+
| server | Yes | - | SSH host (e.g., root@server.com) |
|
|
719
|
+
| remotePath | Yes | - | Remote path to functions directory |
|
|
720
|
+
| url | For invoke | - | Supabase URL |
|
|
721
|
+
| localPath | No | ./supabase/functions | Local functions path |
|
|
722
|
+
| migrationsPath | No | ./supabase/migrations | Local migrations path |
|
|
723
|
+
| edgeContainer | No | edge | Edge runtime container filter |
|
|
724
|
+
| dbContainer | No | postgres | Database container filter |
|
|
725
|
+
|
|
726
|
+
## Edge Function Template
|
|
727
|
+
|
|
728
|
+
Use \`new\` tool to create functions. Each function needs an index.ts:
|
|
729
|
+
|
|
730
|
+
\`\`\`typescript
|
|
731
|
+
Deno.serve(async (req) => {
|
|
732
|
+
const { name } = await req.json()
|
|
733
|
+
return new Response(
|
|
734
|
+
JSON.stringify({ message: \`Hello \${name}!\` }),
|
|
735
|
+
{ headers: { "Content-Type": "application/json" } }
|
|
736
|
+
)
|
|
737
|
+
})
|
|
738
|
+
\`\`\`
|
|
739
|
+
|
|
740
|
+
## Workflow
|
|
741
|
+
|
|
742
|
+
1. Create function: \`new\` tool with function name
|
|
743
|
+
2. Edit the function code in supabase/functions/<name>/index.ts
|
|
744
|
+
3. Deploy: \`deploy\` tool (syncs via rsync, restarts edge-runtime)
|
|
745
|
+
4. Test: \`invoke\` tool with JSON data
|
|
746
|
+
5. Debug: Check logs on the server
|
|
747
|
+
|
|
748
|
+
## Migrations
|
|
749
|
+
|
|
750
|
+
Place .sql files in supabase/migrations/. They execute alphabetically.
|
|
751
|
+
Use \`migrate\` tool to run all migrations via psql in the database container.`,
|
|
752
|
+
}],
|
|
753
|
+
};
|
|
754
|
+
}
|
|
755
|
+
|
|
591
756
|
default:
|
|
592
757
|
return { content: [{ type: 'text', text: `Unknown tool: ${name}` }], isError: true };
|
|
593
758
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "shsu",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.8",
|
|
4
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
5
|
"scripts": {
|
|
6
6
|
"test": "echo \"Error: no test specified\" && exit 1"
|