showpane 0.4.9 → 0.4.11
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/bundle/meta/scaffold-manifest.json +17 -24
- package/bundle/scaffold/VERSION +1 -1
- package/bundle/scaffold/__dot__env.example +3 -5
- package/bundle/scaffold/package.json +2 -1
- package/bundle/scaffold/prisma/schema.local.prisma +1 -1
- package/bundle/scaffold/prisma/seed.ts +6 -1
- package/bundle/scaffold/prisma.config.ts +5 -0
- package/bundle/scaffold/scripts/prisma-schema.mjs +1 -53
- package/bundle/scaffold/src/__tests__/client-portals.test.ts +4 -37
- package/bundle/scaffold/src/__tests__/deploy-bundle.test.ts +48 -0
- package/bundle/scaffold/src/app/api/client-auth/route.ts +1 -1
- package/bundle/scaffold/src/lib/client-portals.ts +8 -13
- package/bundle/scaffold/src/lib/deploy-bundle.ts +106 -0
- package/bundle/scaffold/src/lib/portal-contracts.ts +33 -0
- package/bundle/scaffold/src/lib/runtime-state.ts +2 -32
- package/bundle/scaffold/src/types/adm-zip.d.ts +15 -0
- package/bundle/toolchain/VERSION +1 -1
- package/bundle/toolchain/bin/create-deploy-bundle.ts +3 -72
- package/bundle/toolchain/bin/ensure-cloud-project-link.ts +73 -0
- package/bundle/toolchain/skills/portal-analytics/SKILL.md +2 -2
- package/bundle/toolchain/skills/portal-create/SKILL.md +2 -2
- package/bundle/toolchain/skills/portal-credentials/SKILL.md +3 -3
- package/bundle/toolchain/skills/portal-delete/SKILL.md +4 -4
- package/bundle/toolchain/skills/portal-deploy/SKILL.md +32 -264
- package/bundle/toolchain/skills/portal-dev/SKILL.md +15 -13
- package/bundle/toolchain/skills/portal-list/SKILL.md +2 -2
- package/bundle/toolchain/skills/portal-onboard/SKILL.md +2 -2
- package/bundle/toolchain/skills/portal-preview/SKILL.md +9 -23
- package/bundle/toolchain/skills/portal-setup/SKILL.md +19 -28
- package/bundle/toolchain/skills/portal-share/SKILL.md +3 -4
- package/bundle/toolchain/skills/portal-status/SKILL.md +2 -2
- package/bundle/toolchain/skills/portal-update/SKILL.md +2 -2
- package/bundle/toolchain/skills/portal-upgrade/SKILL.md +2 -2
- package/bundle/toolchain/skills/portal-verify/SKILL.md +21 -33
- package/bundle/toolchain/skills/shared/bin/check-portal-guard.sh +1 -5
- package/bundle/toolchain/skills/shared/preamble.md +2 -2
- package/dist/index.js +56 -10
- package/package.json +1 -1
- package/bundle/scaffold/docker/Caddyfile +0 -3
- package/bundle/scaffold/docker/Dockerfile +0 -30
- package/bundle/scaffold/docker-compose.yml +0 -53
- package/bundle/scaffold/prisma/migrations/20260408000000_init/migration.sql +0 -143
- package/bundle/scaffold/prisma/migrations/20260408010000_add_visitor_tracking/migration.sql +0 -6
- package/bundle/scaffold/prisma/migrations/20260409040000_add_portal_file_checksum/migration.sql +0 -2
- package/bundle/scaffold/prisma/migrations/migration_lock.toml +0 -3
- package/bundle/scaffold/prisma/schema.prisma +0 -128
- package/bundle/scaffold/scripts/backup.sh +0 -19
- package/bundle/scaffold/scripts/e2e-verify.sh +0 -487
- package/bundle/scaffold/scripts/restore.sh +0 -31
|
@@ -28,7 +28,7 @@ echo "APP_PATH: $APP_PATH"
|
|
|
28
28
|
|
|
29
29
|
# Predictive next-skill suggestion
|
|
30
30
|
if [ -f "$HOME/.showpane/timeline.jsonl" ]; then
|
|
31
|
-
_RECENT=$(grep '"event":"completed"' "$HOME/.showpane/timeline.jsonl" 2>/dev/null | tail -3 | grep -o '"skill":"[^"]*"' | sed 's/"skill":"//;s/"//' | tr '\n' ',' | sed 's/,$//')
|
|
31
|
+
_RECENT=$(grep '"event":"completed"' "$HOME/.showpane/timeline.jsonl" 2>/dev/null | tail -3 | grep -o '"skill":"[^"]*"' | sed 's/"skill":"//;s/"//' | tr '\n' ',' | sed 's/,$//' || true)
|
|
32
32
|
[ -n "$_RECENT" ] && echo "RECENT_SKILLS: $_RECENT"
|
|
33
33
|
fi
|
|
34
34
|
|
|
@@ -100,7 +100,7 @@ if [ ! -d "$APP_PATH/node_modules" ]; then
|
|
|
100
100
|
cd "$APP_PATH" && npm install
|
|
101
101
|
elif [ ! -d "$APP_PATH/node_modules/.prisma" ]; then
|
|
102
102
|
echo "Generating Prisma client..."
|
|
103
|
-
cd "$APP_PATH" &&
|
|
103
|
+
cd "$APP_PATH" && npm run prisma:generate
|
|
104
104
|
fi
|
|
105
105
|
```
|
|
106
106
|
|
|
@@ -117,7 +117,7 @@ else
|
|
|
117
117
|
echo "WARNING: No .env file found at $APP_PATH/.env"
|
|
118
118
|
echo "The dev server may fail without DATABASE_URL and AUTH_SECRET."
|
|
119
119
|
echo "Create $APP_PATH/.env with at minimum:"
|
|
120
|
-
echo " DATABASE_URL=
|
|
120
|
+
echo " DATABASE_URL=file:./dev.db"
|
|
121
121
|
echo " AUTH_SECRET=<any-random-string>"
|
|
122
122
|
fi
|
|
123
123
|
```
|
|
@@ -143,15 +143,19 @@ fi
|
|
|
143
143
|
|
|
144
144
|
This is a non-blocking check. If the database is down, the dev server still starts — it just won't be able to serve authenticated portal pages. Static pages and the example portal (which uses hardcoded data) will still work.
|
|
145
145
|
|
|
146
|
-
### Step 5: Check
|
|
146
|
+
### Step 5: Check whether the local schema is ready
|
|
147
147
|
|
|
148
148
|
```bash
|
|
149
|
-
|
|
149
|
+
if [[ "$DATABASE_URL" == file:* ]]; then
|
|
150
|
+
echo "SQLite detected; local schema uses db push."
|
|
151
|
+
else
|
|
152
|
+
echo "Unexpected non-SQLite DATABASE_URL detected. Reset .env to file:./dev.db for the supported local workflow."
|
|
153
|
+
fi
|
|
150
154
|
```
|
|
151
155
|
|
|
152
|
-
If
|
|
156
|
+
If the SQLite schema has not been applied yet, inform the user:
|
|
153
157
|
|
|
154
|
-
> "
|
|
158
|
+
> "The local database schema has not been applied yet. Run `cd $APP_PATH && npm run prisma:db-push` before continuing."
|
|
155
159
|
|
|
156
160
|
This is informational only — do not block the dev server start.
|
|
157
161
|
|
|
@@ -232,11 +236,9 @@ Common causes:
|
|
|
232
236
|
### Database-related page errors
|
|
233
237
|
If the dev server starts but portal pages show errors:
|
|
234
238
|
- Check the terminal output for Prisma errors
|
|
235
|
-
- Verify DATABASE_URL is correct
|
|
236
|
-
-
|
|
237
|
-
- Run `cd $APP_PATH &&
|
|
238
|
-
- If using Docker for the database, ensure the database container is running: `docker compose ps`
|
|
239
|
-
- Check that the database user has the correct permissions for the showpane database
|
|
239
|
+
- Verify DATABASE_URL is correct
|
|
240
|
+
- If `DATABASE_URL` starts with `file:`, run `cd $APP_PATH && npm run prisma:db-push`
|
|
241
|
+
- Run `cd $APP_PATH && npm run prisma:generate` to regenerate the Prisma client if the schema changed
|
|
240
242
|
|
|
241
243
|
## Working with the Dev Server
|
|
242
244
|
|
|
@@ -248,7 +250,7 @@ The Next.js dev server watches all files under `src/` for changes. When you edit
|
|
|
248
250
|
- Adding a new tab: reflected instantly
|
|
249
251
|
- Changing PortalShell props: reflected instantly
|
|
250
252
|
- Adding a new portal directory: requires navigating to the new URL manually (hot reload only works on already-loaded pages)
|
|
251
|
-
- Changing `prisma
|
|
253
|
+
- Changing files under `prisma/`: requires restarting the dev server after running `npm run prisma:generate`
|
|
252
254
|
|
|
253
255
|
### Accessing portals during development
|
|
254
256
|
|
|
@@ -17,7 +17,7 @@ if [ ! -f "$CONFIG" ]; then
|
|
|
17
17
|
exit 1
|
|
18
18
|
fi
|
|
19
19
|
APP_PATH=$(cat "$CONFIG" | python3 -c "import sys,json; print(json.loads(sys.stdin.read()).get('app_path',''))" 2>/dev/null)
|
|
20
|
-
DEPLOY_MODE=$(cat "$CONFIG" | python3 -c "import sys,json; print(json.loads(sys.stdin.read()).get('deploy_mode','
|
|
20
|
+
DEPLOY_MODE=$(cat "$CONFIG" | python3 -c "import sys,json; print(json.loads(sys.stdin.read()).get('deploy_mode','local'))" 2>/dev/null)
|
|
21
21
|
ORG_SLUG=$(cat "$CONFIG" | python3 -c "import sys,json; d=json.loads(sys.stdin.read()); print(d.get('orgSlug','') or d.get('org_slug',''))" 2>/dev/null)
|
|
22
22
|
APP_PATH="${SHOWPANE_APP_PATH:-$APP_PATH}"
|
|
23
23
|
if [ -f "$APP_PATH/.env" ]; then set -a && source "$APP_PATH/.env" && set +a; fi
|
|
@@ -34,7 +34,7 @@ LEARN_FILE="$HOME/.showpane/learnings.jsonl"
|
|
|
34
34
|
|
|
35
35
|
# Predictive next-skill suggestion
|
|
36
36
|
if [ -f "$HOME/.showpane/timeline.jsonl" ]; then
|
|
37
|
-
_RECENT=$(grep '"event":"completed"' "$HOME/.showpane/timeline.jsonl" 2>/dev/null | tail -3 | grep -o '"skill":"[^"]*"' | sed 's/"skill":"//;s/"//' | tr '\n' ',' | sed 's/,$//')
|
|
37
|
+
_RECENT=$(grep '"event":"completed"' "$HOME/.showpane/timeline.jsonl" 2>/dev/null | tail -3 | grep -o '"skill":"[^"]*"' | sed 's/"skill":"//;s/"//' | tr '\n' ',' | sed 's/,$//' || true)
|
|
38
38
|
[ -n "$_RECENT" ] && echo "RECENT_SKILLS: $_RECENT"
|
|
39
39
|
fi
|
|
40
40
|
|
|
@@ -18,7 +18,7 @@ if [ ! -f "$CONFIG" ]; then
|
|
|
18
18
|
exit 1
|
|
19
19
|
fi
|
|
20
20
|
APP_PATH=$(cat "$CONFIG" | python3 -c "import sys,json; print(json.loads(sys.stdin.read()).get('app_path',''))" 2>/dev/null)
|
|
21
|
-
DEPLOY_MODE=$(cat "$CONFIG" | python3 -c "import sys,json; print(json.loads(sys.stdin.read()).get('deploy_mode','
|
|
21
|
+
DEPLOY_MODE=$(cat "$CONFIG" | python3 -c "import sys,json; print(json.loads(sys.stdin.read()).get('deploy_mode','local'))" 2>/dev/null)
|
|
22
22
|
ORG_SLUG=$(cat "$CONFIG" | python3 -c "import sys,json; d=json.loads(sys.stdin.read()); print(d.get('orgSlug','') or d.get('org_slug',''))" 2>/dev/null)
|
|
23
23
|
APP_PATH="${SHOWPANE_APP_PATH:-$APP_PATH}"
|
|
24
24
|
if [ -f "$APP_PATH/.env" ]; then set -a && source "$APP_PATH/.env" && set +a; fi
|
|
@@ -35,7 +35,7 @@ LEARN_FILE="$HOME/.showpane/learnings.jsonl"
|
|
|
35
35
|
|
|
36
36
|
# Predictive next-skill suggestion
|
|
37
37
|
if [ -f "$HOME/.showpane/timeline.jsonl" ]; then
|
|
38
|
-
_RECENT=$(grep '"event":"completed"' "$HOME/.showpane/timeline.jsonl" 2>/dev/null | tail -3 | grep -o '"skill":"[^"]*"' | sed 's/"skill":"//;s/"//' | tr '\n' ',' | sed 's/,$//')
|
|
38
|
+
_RECENT=$(grep '"event":"completed"' "$HOME/.showpane/timeline.jsonl" 2>/dev/null | tail -3 | grep -o '"skill":"[^"]*"' | sed 's/"skill":"//;s/"//' | tr '\n' ',' | sed 's/,$//' || true)
|
|
39
39
|
[ -n "$_RECENT" ] && echo "RECENT_SKILLS: $_RECENT"
|
|
40
40
|
fi
|
|
41
41
|
|
|
@@ -17,7 +17,7 @@ if [ ! -f "$CONFIG" ]; then
|
|
|
17
17
|
exit 1
|
|
18
18
|
fi
|
|
19
19
|
APP_PATH=$(cat "$CONFIG" | python3 -c "import sys,json; print(json.loads(sys.stdin.read()).get('app_path',''))" 2>/dev/null)
|
|
20
|
-
DEPLOY_MODE=$(cat "$CONFIG" | python3 -c "import sys,json; print(json.loads(sys.stdin.read()).get('deploy_mode','
|
|
20
|
+
DEPLOY_MODE=$(cat "$CONFIG" | python3 -c "import sys,json; print(json.loads(sys.stdin.read()).get('deploy_mode','local'))" 2>/dev/null)
|
|
21
21
|
ORG_SLUG=$(cat "$CONFIG" | python3 -c "import sys,json; d=json.loads(sys.stdin.read()); print(d.get('orgSlug','') or d.get('org_slug',''))" 2>/dev/null)
|
|
22
22
|
APP_PATH="${SHOWPANE_APP_PATH:-$APP_PATH}"
|
|
23
23
|
if [ -f "$APP_PATH/.env" ]; then set -a && source "$APP_PATH/.env" && set +a; fi
|
|
@@ -34,7 +34,7 @@ LEARN_FILE="$HOME/.showpane/learnings.jsonl"
|
|
|
34
34
|
|
|
35
35
|
# Predictive next-skill suggestion
|
|
36
36
|
if [ -f "$HOME/.showpane/timeline.jsonl" ]; then
|
|
37
|
-
_RECENT=$(grep '"event":"completed"' "$HOME/.showpane/timeline.jsonl" 2>/dev/null | tail -3 | grep -o '"skill":"[^"]*"' | sed 's/"skill":"//;s/"//' | tr '\n' ',' | sed 's/,$//')
|
|
37
|
+
_RECENT=$(grep '"event":"completed"' "$HOME/.showpane/timeline.jsonl" 2>/dev/null | tail -3 | grep -o '"skill":"[^"]*"' | sed 's/"skill":"//;s/"//' | tr '\n' ',' | sed 's/,$//' || true)
|
|
38
38
|
[ -n "$_RECENT" ] && echo "RECENT_SKILLS: $_RECENT"
|
|
39
39
|
fi
|
|
40
40
|
|
|
@@ -69,7 +69,7 @@ mention them unless they directly affect the current task.
|
|
|
69
69
|
|
|
70
70
|
## Overview
|
|
71
71
|
|
|
72
|
-
This skill opens a portal in the user's default web browser. It is the fastest way to see what a portal looks like after creating or updating it. The skill determines the correct URL based on
|
|
72
|
+
This skill opens a portal in the user's default web browser. It is the fastest way to see what a portal looks like after creating or updating it. The skill determines the correct URL based on whether a local dev server is running and whether a public app URL is configured, then opens it using the platform's native command.
|
|
73
73
|
|
|
74
74
|
This is a lightweight skill -- it does not start a dev server, build the app, or modify anything. It just opens a URL. If the dev server is not running, it tells the user how to start it.
|
|
75
75
|
|
|
@@ -101,27 +101,14 @@ If a process is listening on port 3000, the URL is:
|
|
|
101
101
|
http://localhost:3000/client/<slug>
|
|
102
102
|
```
|
|
103
103
|
|
|
104
|
-
**Option B:
|
|
105
|
-
|
|
106
|
-
If `DEPLOY_MODE` is `docker`, check if the container is running:
|
|
107
|
-
|
|
108
|
-
```bash
|
|
109
|
-
lsof -i :8080 -sTCP:LISTEN -t >/dev/null 2>&1 && echo "DOCKER_RUNNING=true" || echo "DOCKER_RUNNING=false"
|
|
110
|
-
```
|
|
111
|
-
|
|
112
|
-
If running, the URL is:
|
|
113
|
-
```
|
|
114
|
-
http://localhost:8080/client/<slug>
|
|
115
|
-
```
|
|
116
|
-
|
|
117
|
-
**Option C: NEXT_PUBLIC_APP_URL is set**
|
|
104
|
+
**Option B: NEXT_PUBLIC_APP_URL is set**
|
|
118
105
|
|
|
119
106
|
If the environment variable `NEXT_PUBLIC_APP_URL` is set (sourced from `.env` by the preamble), use it:
|
|
120
107
|
```
|
|
121
108
|
${NEXT_PUBLIC_APP_URL}/client/<slug>
|
|
122
109
|
```
|
|
123
110
|
|
|
124
|
-
This handles
|
|
111
|
+
This handles deployed URLs, custom domains, and any other public preview target.
|
|
125
112
|
|
|
126
113
|
**Fallback: Nothing running**
|
|
127
114
|
|
|
@@ -174,9 +161,8 @@ If the portal is the example portal (slug is "example"), no credentials are need
|
|
|
174
161
|
To be explicit about resolution order:
|
|
175
162
|
|
|
176
163
|
1. **Dev server on port 3000** -- always preferred for local development, regardless of other settings.
|
|
177
|
-
2. **
|
|
178
|
-
3. **
|
|
179
|
-
4. **Fallback** -- no URL available, prompt the user to start a server.
|
|
164
|
+
2. **NEXT_PUBLIC_APP_URL** -- used for deployed environments or when no local server is detected.
|
|
165
|
+
3. **Fallback** -- no URL available, prompt the user to start a server.
|
|
180
166
|
|
|
181
167
|
This order ensures that during development, the user always sees the latest local version, even if NEXT_PUBLIC_APP_URL points to production.
|
|
182
168
|
|
|
@@ -213,9 +199,9 @@ If the user is previewing to check content before sharing with a client, suggest
|
|
|
213
199
|
|
|
214
200
|
## Previewing After Changes
|
|
215
201
|
|
|
216
|
-
A common workflow is: edit portal content with `/portal update`, then preview to verify. If the dev server is running with Next.js hot reload, changes to the client component files will appear immediately without a page refresh.
|
|
202
|
+
A common workflow is: edit portal content with `/portal update`, then preview to verify. If the dev server is running with Next.js hot reload, changes to the client component files will appear immediately without a page refresh.
|
|
217
203
|
|
|
218
|
-
If the user just ran `/portal update` and then `/portal preview`, mention: "If you don't see your changes, make sure the dev server is running
|
|
204
|
+
If the user just ran `/portal update` and then `/portal preview`, mention: "If you don't see your changes, make sure the dev server is running so hot reload can pick them up."
|
|
219
205
|
|
|
220
206
|
## Multiple Portals Preview
|
|
221
207
|
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: portal-setup
|
|
3
3
|
description: |
|
|
4
|
-
Interactive setup wizard for Showpane. Creates config, installs deps,
|
|
4
|
+
Interactive setup wizard for Showpane. Creates config, installs deps, applies the local SQLite schema, and creates the organization.
|
|
5
5
|
Trigger phrases: "portal setup", "configure showpane", "set up showpane", "initialize showpane". (showpane)
|
|
6
6
|
allowed-tools: [Bash, Read, Write, Edit, Glob, Grep]
|
|
7
7
|
hooks:
|
|
@@ -40,7 +40,7 @@ echo "SHOWPANE: v$SKILL_VERSION | SETUP MODE"
|
|
|
40
40
|
|
|
41
41
|
# Predictive next-skill suggestion
|
|
42
42
|
if [ -f "$HOME/.showpane/timeline.jsonl" ]; then
|
|
43
|
-
_RECENT=$(grep '"event":"completed"' "$HOME/.showpane/timeline.jsonl" 2>/dev/null | tail -3 | grep -o '"skill":"[^"]*"' | sed 's/"skill":"//;s/"//' | tr '\n' ',' | sed 's/,$//')
|
|
43
|
+
_RECENT=$(grep '"event":"completed"' "$HOME/.showpane/timeline.jsonl" 2>/dev/null | tail -3 | grep -o '"skill":"[^"]*"' | sed 's/"skill":"//;s/"//' | tr '\n' ',' | sed 's/,$//' || true)
|
|
44
44
|
[ -n "$_RECENT" ] && echo "RECENT_SKILLS: $_RECENT"
|
|
45
45
|
fi
|
|
46
46
|
|
|
@@ -93,7 +93,7 @@ Run the detection:
|
|
|
93
93
|
|
|
94
94
|
```bash
|
|
95
95
|
for candidate in "$(pwd)" "$(pwd)/../app" "$HOME/git/showpane/app" "$HOME/showpane/app"; do
|
|
96
|
-
if [ -f "$candidate/package.json" ] && [ -f "$candidate/prisma/schema.prisma" ]; then
|
|
96
|
+
if [ -f "$candidate/package.json" ] && { [ -f "$candidate/prisma/schema.local.prisma" ] || [ -f "$candidate/prisma.config.ts" ]; }; then
|
|
97
97
|
echo "FOUND: $(cd "$candidate" && pwd)"
|
|
98
98
|
break
|
|
99
99
|
fi
|
|
@@ -102,7 +102,7 @@ done
|
|
|
102
102
|
|
|
103
103
|
If found, confirm with the user: "Found Showpane app at /path/to/app. Use this? (Y/n)"
|
|
104
104
|
|
|
105
|
-
If not found, ask the user to provide the path. Validate that the path exists and contains
|
|
105
|
+
If not found, ask the user to provide the path. Validate that the path exists and contains `package.json` plus either `prisma/schema.local.prisma` or `prisma.config.ts`. If those markers are missing, the path is not a valid Showpane app directory — explain which file is missing and ask again.
|
|
106
106
|
|
|
107
107
|
Resolve the path to an absolute path (no `~` or relative components) before storing it. Use `cd "$path" && pwd` to resolve.
|
|
108
108
|
|
|
@@ -117,14 +117,11 @@ When the user is inside a freshly generated Showpane project, the setup should:
|
|
|
117
117
|
- Still ask for org name, contact details, and website URL
|
|
118
118
|
- Be concise — the user just ran `npx showpane` and wants to get going fast
|
|
119
119
|
|
|
120
|
-
### Step 3:
|
|
120
|
+
### Step 3: Set the workspace mode
|
|
121
121
|
|
|
122
|
-
|
|
122
|
+
Use `local` as the setup mode.
|
|
123
123
|
|
|
124
|
-
|
|
125
|
-
- **vercel** — Deployed via Vercel. Pushes to git trigger automatic deploys.
|
|
126
|
-
|
|
127
|
-
Default to `docker` if the user doesn't have a preference. Store as `DEPLOY_MODE`.
|
|
124
|
+
Store `DEPLOY_MODE=local` in the config written later in this flow.
|
|
128
125
|
|
|
129
126
|
### Step 4: Install dependencies
|
|
130
127
|
|
|
@@ -137,12 +134,12 @@ cd "$APP_PATH" && npm install
|
|
|
137
134
|
If node_modules exists but `.prisma` client is missing, run:
|
|
138
135
|
|
|
139
136
|
```bash
|
|
140
|
-
cd "$APP_PATH" &&
|
|
137
|
+
cd "$APP_PATH" && npm run prisma:generate
|
|
141
138
|
```
|
|
142
139
|
|
|
143
140
|
Wait for installation to complete before proceeding.
|
|
144
141
|
|
|
145
|
-
### Step 5:
|
|
142
|
+
### Step 5: Apply the local database schema
|
|
146
143
|
|
|
147
144
|
Source the app's `.env` file to get `DATABASE_URL`:
|
|
148
145
|
|
|
@@ -150,24 +147,18 @@ Source the app's `.env` file to get `DATABASE_URL`:
|
|
|
150
147
|
if [ -f "$APP_PATH/.env" ]; then set -a && source "$APP_PATH/.env" && set +a; fi
|
|
151
148
|
```
|
|
152
149
|
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
```bash
|
|
156
|
-
cd "$APP_PATH" && npx prisma migrate dev
|
|
157
|
-
```
|
|
158
|
-
|
|
159
|
-
For an existing database with pending migrations:
|
|
150
|
+
The supported setup path is SQLite. Apply the local schema:
|
|
160
151
|
|
|
161
152
|
```bash
|
|
162
|
-
cd "$APP_PATH" &&
|
|
153
|
+
cd "$APP_PATH" && npm run prisma:db-push
|
|
163
154
|
```
|
|
164
155
|
|
|
165
156
|
If `DATABASE_URL` is not set in `.env`, inform the user they need to configure it. Provide guidance:
|
|
166
|
-
- For local development: `
|
|
167
|
-
|
|
168
|
-
|
|
157
|
+
- For local development: `file:./dev.db`
|
|
158
|
+
|
|
159
|
+
If `DATABASE_URL` is set to anything other than a `file:` URL, explain that the supported setup flow is SQLite-first and suggest resetting `.env` back to `file:./dev.db`.
|
|
169
160
|
|
|
170
|
-
Do NOT proceed until
|
|
161
|
+
Do NOT proceed until the local schema is applied successfully.
|
|
171
162
|
|
|
172
163
|
### Step 6: Create or find Organization record
|
|
173
164
|
|
|
@@ -239,7 +230,7 @@ Write the config file:
|
|
|
239
230
|
cat > "$HOME/.showpane/config.json" << 'CONFIGEOF'
|
|
240
231
|
{
|
|
241
232
|
"app_path": "<resolved_absolute_path>",
|
|
242
|
-
"deploy_mode": "
|
|
233
|
+
"deploy_mode": "local",
|
|
243
234
|
"orgSlug": "<org_slug>",
|
|
244
235
|
"telemetry": "<community|anonymous|off>"
|
|
245
236
|
}
|
|
@@ -257,7 +248,7 @@ Display a clear summary:
|
|
|
257
248
|
Showpane setup complete!
|
|
258
249
|
|
|
259
250
|
App path: /path/to/showpane-project
|
|
260
|
-
Deploy mode:
|
|
251
|
+
Deploy mode: local
|
|
261
252
|
Organization: Acme Consulting (acme-consulting)
|
|
262
253
|
Brand color: #2563eb
|
|
263
254
|
Telemetry: off
|
|
@@ -283,7 +274,7 @@ Each step can fail independently. Handle failures gracefully:
|
|
|
283
274
|
|
|
284
275
|
- **App path not found**: Ask the user to run `npx showpane` first or point setup at an existing generated Showpane project
|
|
285
276
|
- **npm install fails**: Check Node.js version (requires 18+), check internet connectivity, suggest clearing `node_modules` and retrying
|
|
286
|
-
- **Prisma
|
|
277
|
+
- **Prisma db push fails**: Check that DATABASE_URL is set to `file:./dev.db` (or another valid SQLite `file:` URL) in `.env`
|
|
287
278
|
- **Organization creation fails**: Check database connectivity. If the Prisma client throws a connection error, verify DATABASE_URL in `.env`
|
|
288
279
|
- **Config write fails**: Check that `$HOME/.showpane/` is writable. On some systems, home directory permissions may block directory creation
|
|
289
280
|
|
|
@@ -306,4 +297,4 @@ echo '{"skill":"portal-setup","event":"completed","ts":"'$(date -u +%Y-%m-%dT%H:
|
|
|
306
297
|
- If any step fails, provide a clear error message and suggest how to fix it, but do not silently continue
|
|
307
298
|
- The setup wizard is interactive — ask one question at a time, don't dump all questions at once
|
|
308
299
|
- When reconfiguring, show current values as defaults so the user can press enter to keep them
|
|
309
|
-
- The setup wizard should complete in under 5 minutes for a user with
|
|
300
|
+
- The setup wizard should complete in under 5 minutes for a user with Node.js, internet access, and a generated Showpane project
|
|
@@ -20,7 +20,7 @@ if [ ! -f "$CONFIG" ]; then
|
|
|
20
20
|
exit 1
|
|
21
21
|
fi
|
|
22
22
|
APP_PATH=$(cat "$CONFIG" | python3 -c "import sys,json; print(json.loads(sys.stdin.read()).get('app_path',''))" 2>/dev/null)
|
|
23
|
-
DEPLOY_MODE=$(cat "$CONFIG" | python3 -c "import sys,json; print(json.loads(sys.stdin.read()).get('deploy_mode','
|
|
23
|
+
DEPLOY_MODE=$(cat "$CONFIG" | python3 -c "import sys,json; print(json.loads(sys.stdin.read()).get('deploy_mode','local'))" 2>/dev/null)
|
|
24
24
|
ORG_SLUG=$(cat "$CONFIG" | python3 -c "import sys,json; d=json.loads(sys.stdin.read()); print(d.get('orgSlug','') or d.get('org_slug',''))" 2>/dev/null)
|
|
25
25
|
APP_PATH="${SHOWPANE_APP_PATH:-$APP_PATH}"
|
|
26
26
|
if [ -f "$APP_PATH/.env" ]; then set -a && source "$APP_PATH/.env" && set +a; fi
|
|
@@ -37,7 +37,7 @@ LEARN_FILE="$HOME/.showpane/learnings.jsonl"
|
|
|
37
37
|
|
|
38
38
|
# Predictive next-skill suggestion
|
|
39
39
|
if [ -f "$HOME/.showpane/timeline.jsonl" ]; then
|
|
40
|
-
_RECENT=$(grep '"event":"completed"' "$HOME/.showpane/timeline.jsonl" 2>/dev/null | tail -3 | grep -o '"skill":"[^"]*"' | sed 's/"skill":"//;s/"//' | tr '\n' ',' | sed 's/,$//')
|
|
40
|
+
_RECENT=$(grep '"event":"completed"' "$HOME/.showpane/timeline.jsonl" 2>/dev/null | tail -3 | grep -o '"skill":"[^"]*"' | sed 's/"skill":"//;s/"//' | tr '\n' ',' | sed 's/,$//' || true)
|
|
41
41
|
[ -n "$_RECENT" ] && echo "RECENT_SKILLS: $_RECENT"
|
|
42
42
|
fi
|
|
43
43
|
|
|
@@ -147,8 +147,7 @@ Do NOT log the share URL to learnings or telemetry. The URL contains the signed
|
|
|
147
147
|
- Always display the full URL, never truncate or abbreviate it. The user needs to copy-paste it.
|
|
148
148
|
- Make it clear that the link does not expire automatically and is revoked by credential rotation or portal deactivation.
|
|
149
149
|
- Use double-line box drawing (`═`) for the border around the link.
|
|
150
|
-
- If NEXT_PUBLIC_APP_URL is `http://localhost:3000`, warn the user: "This is a local development URL. The client won't be able to access it remotely. Deploy the app
|
|
151
|
-
- If the deploy mode is `docker` and the URL looks like localhost, same warning applies.
|
|
150
|
+
- If NEXT_PUBLIC_APP_URL is `http://localhost:3000`, warn the user: "This is a local development URL. The client won't be able to access it remotely. Deploy the app with /portal deploy, then generate the share link again."
|
|
152
151
|
|
|
153
152
|
## Error Handling
|
|
154
153
|
|
|
@@ -17,7 +17,7 @@ if [ ! -f "$CONFIG" ]; then
|
|
|
17
17
|
exit 1
|
|
18
18
|
fi
|
|
19
19
|
APP_PATH=$(cat "$CONFIG" | python3 -c "import sys,json; print(json.loads(sys.stdin.read()).get('app_path',''))" 2>/dev/null)
|
|
20
|
-
DEPLOY_MODE=$(cat "$CONFIG" | python3 -c "import sys,json; print(json.loads(sys.stdin.read()).get('deploy_mode','
|
|
20
|
+
DEPLOY_MODE=$(cat "$CONFIG" | python3 -c "import sys,json; print(json.loads(sys.stdin.read()).get('deploy_mode','local'))" 2>/dev/null)
|
|
21
21
|
ORG_SLUG=$(cat "$CONFIG" | python3 -c "import sys,json; d=json.loads(sys.stdin.read()); print(d.get('orgSlug','') or d.get('org_slug',''))" 2>/dev/null)
|
|
22
22
|
APP_PATH="${SHOWPANE_APP_PATH:-$APP_PATH}"
|
|
23
23
|
if [ -f "$APP_PATH/.env" ]; then set -a && source "$APP_PATH/.env" && set +a; fi
|
|
@@ -34,7 +34,7 @@ LEARN_FILE="$HOME/.showpane/learnings.jsonl"
|
|
|
34
34
|
|
|
35
35
|
# Predictive next-skill suggestion
|
|
36
36
|
if [ -f "$HOME/.showpane/timeline.jsonl" ]; then
|
|
37
|
-
_RECENT=$(grep '"event":"completed"' "$HOME/.showpane/timeline.jsonl" 2>/dev/null | tail -3 | grep -o '"skill":"[^"]*"' | sed 's/"skill":"//;s/"//' | tr '\n' ',' | sed 's/,$//')
|
|
37
|
+
_RECENT=$(grep '"event":"completed"' "$HOME/.showpane/timeline.jsonl" 2>/dev/null | tail -3 | grep -o '"skill":"[^"]*"' | sed 's/"skill":"//;s/"//' | tr '\n' ',' | sed 's/,$//' || true)
|
|
38
38
|
[ -n "$_RECENT" ] && echo "RECENT_SKILLS: $_RECENT"
|
|
39
39
|
fi
|
|
40
40
|
|
|
@@ -16,7 +16,7 @@ if [ ! -f "$CONFIG" ]; then
|
|
|
16
16
|
exit 1
|
|
17
17
|
fi
|
|
18
18
|
APP_PATH=$(python3 -c "import json; d=json.load(open('$CONFIG')); print(d.get('app_path',''))" 2>/dev/null)
|
|
19
|
-
DEPLOY_MODE=$(python3 -c "import json; d=json.load(open('$CONFIG')); print(d.get('deploy_mode','
|
|
19
|
+
DEPLOY_MODE=$(python3 -c "import json; d=json.load(open('$CONFIG')); print(d.get('deploy_mode','local'))" 2>/dev/null)
|
|
20
20
|
ORG_SLUG=$(python3 -c "import json; d=json.load(open('$CONFIG')); print(d.get('orgSlug','') or d.get('org_slug',''))" 2>/dev/null)
|
|
21
21
|
APP_PATH="${SHOWPANE_APP_PATH:-$APP_PATH}"
|
|
22
22
|
if [ -f "$APP_PATH/.env" ]; then set -a && source "$APP_PATH/.env" && set +a; fi
|
|
@@ -33,7 +33,7 @@ LEARN_FILE="$HOME/.showpane/learnings.jsonl"
|
|
|
33
33
|
|
|
34
34
|
# Predictive next-skill suggestion
|
|
35
35
|
if [ -f "$HOME/.showpane/timeline.jsonl" ]; then
|
|
36
|
-
_RECENT=$(grep '"event":"completed"' "$HOME/.showpane/timeline.jsonl" 2>/dev/null | tail -3 | grep -o '"skill":"[^"]*"' | sed 's/"skill":"//;s/"//' | tr '\n' ',' | sed 's/,$//')
|
|
36
|
+
_RECENT=$(grep '"event":"completed"' "$HOME/.showpane/timeline.jsonl" 2>/dev/null | tail -3 | grep -o '"skill":"[^"]*"' | sed 's/"skill":"//;s/"//' | tr '\n' ',' | sed 's/,$//' || true)
|
|
37
37
|
[ -n "$_RECENT" ] && echo "RECENT_SKILLS: $_RECENT"
|
|
38
38
|
fi
|
|
39
39
|
|
|
@@ -17,7 +17,7 @@ if [ ! -f "$CONFIG" ]; then
|
|
|
17
17
|
exit 1
|
|
18
18
|
fi
|
|
19
19
|
APP_PATH=$(cat "$CONFIG" | python3 -c "import sys,json; print(json.loads(sys.stdin.read()).get('app_path',''))" 2>/dev/null)
|
|
20
|
-
DEPLOY_MODE=$(cat "$CONFIG" | python3 -c "import sys,json; print(json.loads(sys.stdin.read()).get('deploy_mode','
|
|
20
|
+
DEPLOY_MODE=$(cat "$CONFIG" | python3 -c "import sys,json; print(json.loads(sys.stdin.read()).get('deploy_mode','local'))" 2>/dev/null)
|
|
21
21
|
ORG_SLUG=$(cat "$CONFIG" | python3 -c "import sys,json; d=json.loads(sys.stdin.read()); print(d.get('orgSlug','') or d.get('org_slug',''))" 2>/dev/null)
|
|
22
22
|
APP_PATH="${SHOWPANE_APP_PATH:-$APP_PATH}"
|
|
23
23
|
if [ -f "$APP_PATH/.env" ]; then set -a && source "$APP_PATH/.env" && set +a; fi
|
|
@@ -34,7 +34,7 @@ LEARN_FILE="$HOME/.showpane/learnings.jsonl"
|
|
|
34
34
|
|
|
35
35
|
# Predictive next-skill suggestion
|
|
36
36
|
if [ -f "$HOME/.showpane/timeline.jsonl" ]; then
|
|
37
|
-
_RECENT=$(grep '"event":"completed"' "$HOME/.showpane/timeline.jsonl" 2>/dev/null | tail -3 | grep -o '"skill":"[^"]*"' | sed 's/"skill":"//;s/"//' | tr '\n' ',' | sed 's/,$//')
|
|
37
|
+
_RECENT=$(grep '"event":"completed"' "$HOME/.showpane/timeline.jsonl" 2>/dev/null | tail -3 | grep -o '"skill":"[^"]*"' | sed 's/"skill":"//;s/"//' | tr '\n' ',' | sed 's/,$//' || true)
|
|
38
38
|
[ -n "$_RECENT" ] && echo "RECENT_SKILLS: $_RECENT"
|
|
39
39
|
fi
|
|
40
40
|
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: portal-verify
|
|
3
3
|
description: |
|
|
4
|
-
Verify
|
|
4
|
+
Verify a Showpane Cloud portal after publish: DNS, SSL, login page, content rendering, engagement tracking.
|
|
5
5
|
Trigger phrases: "verify portal", "check deployment", "is my portal working", "portal health". (showpane)
|
|
6
6
|
allowed-tools: [Bash, Read, Glob, Grep]
|
|
7
7
|
---
|
|
@@ -16,7 +16,7 @@ if [ ! -f "$CONFIG" ]; then
|
|
|
16
16
|
exit 1
|
|
17
17
|
fi
|
|
18
18
|
APP_PATH=$(python3 -c "import json; d=json.load(open('$CONFIG')); print(d.get('app_path',''))" 2>/dev/null)
|
|
19
|
-
DEPLOY_MODE=$(python3 -c "import json; d=json.load(open('$CONFIG')); print(d.get('deploy_mode','
|
|
19
|
+
DEPLOY_MODE=$(python3 -c "import json; d=json.load(open('$CONFIG')); print(d.get('deploy_mode','local'))" 2>/dev/null)
|
|
20
20
|
ORG_SLUG=$(python3 -c "import json; d=json.load(open('$CONFIG')); print(d.get('orgSlug','') or d.get('org_slug',''))" 2>/dev/null)
|
|
21
21
|
CLOUD_API_TOKEN=$(python3 -c "import json; d=json.load(open('$CONFIG')); print(d.get('cloud',d).get('api_token', d.get('accessToken','')))" 2>/dev/null)
|
|
22
22
|
CLOUD_ORG_SLUG=$(python3 -c "import json; d=json.load(open('$CONFIG')); print(d.get('cloud',d).get('org_slug', d.get('orgSlug','')))" 2>/dev/null)
|
|
@@ -41,38 +41,26 @@ LEARN_FILE="$HOME/.showpane/learnings.jsonl"
|
|
|
41
41
|
|
|
42
42
|
### Step 1: URL Reachability
|
|
43
43
|
|
|
44
|
-
Determine the portal URL
|
|
44
|
+
Determine the portal URL and check that it responds.
|
|
45
45
|
|
|
46
|
-
**For self-hosted (docker/vercel):**
|
|
47
46
|
```bash
|
|
48
|
-
|
|
49
|
-
echo "
|
|
50
|
-
|
|
51
|
-
echo "HTTP status: $HTTP_CODE"
|
|
52
|
-
if [ "$HTTP_CODE" = "200" ]; then
|
|
53
|
-
URL_STATUS="ok"
|
|
54
|
-
else
|
|
55
|
-
URL_STATUS="unreachable ($HTTP_CODE)"
|
|
47
|
+
if [ -z "$CLOUD_PORTAL_URL" ] && [ -z "$CLOUD_ORG_SLUG" ]; then
|
|
48
|
+
echo "No hosted portal URL configured. Run /portal deploy first."
|
|
49
|
+
exit 1
|
|
56
50
|
fi
|
|
57
|
-
|
|
51
|
+
PORTAL_URL="${CLOUD_PORTAL_URL:-https://$CLOUD_ORG_SLUG.showpane.com}"
|
|
52
|
+
echo "Checking app URL: $CLOUD_API_BASE"
|
|
53
|
+
APP_CODE=$(curl -s -o /dev/null -w "%{http_code}" "$CLOUD_API_BASE/api/health" 2>/dev/null)
|
|
54
|
+
echo "App health: $APP_CODE"
|
|
58
55
|
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
echo "Checking org URL: $PORTAL_URL"
|
|
68
|
-
ORG_CODE=$(curl -s -o /dev/null -w "%{http_code}" "$PORTAL_URL" 2>/dev/null)
|
|
69
|
-
echo "Org portal: $ORG_CODE"
|
|
70
|
-
|
|
71
|
-
if [ "$ORG_CODE" = "200" ]; then
|
|
72
|
-
URL_STATUS="ok"
|
|
73
|
-
else
|
|
74
|
-
URL_STATUS="unreachable ($ORG_CODE)"
|
|
75
|
-
fi
|
|
56
|
+
echo "Checking org URL: $PORTAL_URL"
|
|
57
|
+
ORG_CODE=$(curl -s -o /dev/null -w "%{http_code}" "$PORTAL_URL" 2>/dev/null)
|
|
58
|
+
echo "Org portal: $ORG_CODE"
|
|
59
|
+
|
|
60
|
+
if [ "$ORG_CODE" = "200" ]; then
|
|
61
|
+
URL_STATUS="ok"
|
|
62
|
+
else
|
|
63
|
+
URL_STATUS="unreachable ($ORG_CODE)"
|
|
76
64
|
fi
|
|
77
65
|
```
|
|
78
66
|
|
|
@@ -110,7 +98,7 @@ if [ "$DEPLOY_MODE" = "cloud" ] && [ -n "$CLOUD_ORG_SLUG" ]; then
|
|
|
110
98
|
echo "HTTP->HTTPS redirect: $REDIRECT_CODE (expected 301 or 308)"
|
|
111
99
|
fi
|
|
112
100
|
else
|
|
113
|
-
SSL_STATUS="n/a (
|
|
101
|
+
SSL_STATUS="n/a (no cloud org configured)"
|
|
114
102
|
fi
|
|
115
103
|
```
|
|
116
104
|
|
|
@@ -132,7 +120,7 @@ echo "Active portals: $PORTAL_LIST"
|
|
|
132
120
|
For each portal, verify the login page returns a response and contains auth-related content:
|
|
133
121
|
|
|
134
122
|
```bash
|
|
135
|
-
PORTAL_URL="${CLOUD_PORTAL_URL:-
|
|
123
|
+
PORTAL_URL="${CLOUD_PORTAL_URL:-https://$CLOUD_ORG_SLUG.showpane.com}"
|
|
136
124
|
echo "$PORTAL_LIST" | python3 -c "
|
|
137
125
|
import sys, json
|
|
138
126
|
portals = json.load(sys.stdin)
|
|
@@ -261,5 +249,5 @@ After the report, suggest next steps:
|
|
|
261
249
|
- Cloud checks verify both the control plane (app.showpane.com) and the org portal (orgslug.showpane.com)
|
|
262
250
|
- The PortalFile model may not exist in all schema versions — catch errors gracefully
|
|
263
251
|
- If the database is unreachable, skip DB-dependent checks (Steps 3, 5) and note it in the report
|
|
264
|
-
- SSL checks only apply
|
|
252
|
+
- SSL checks only apply when a cloud portal URL is configured
|
|
265
253
|
- The events endpoint check uses OPTIONS to avoid creating spurious event records
|
|
@@ -24,10 +24,6 @@ if [ -n "$command" ]; then
|
|
|
24
24
|
echo '{"permissionDecision":"ask","message":"⚠️ This SQL command will destroy portal or organization data."}'
|
|
25
25
|
exit 0
|
|
26
26
|
;;
|
|
27
|
-
*"vercel"*"rm"*|*"vercel"*"remove"*|*"vercel"*"delete"*)
|
|
28
|
-
echo '{"permissionDecision":"ask","message":"⚠️ This will delete a Vercel project. Published portals will go offline."}'
|
|
29
|
-
exit 0
|
|
30
|
-
;;
|
|
31
27
|
esac
|
|
32
28
|
fi
|
|
33
29
|
|
|
@@ -38,7 +34,7 @@ if [ -n "$file_path" ]; then
|
|
|
38
34
|
echo '{"permissionDecision":"ask","message":"⚠️ Modifying credentials/secrets file. Verify this is intentional."}'
|
|
39
35
|
exit 0
|
|
40
36
|
;;
|
|
41
|
-
*"prisma/schema.prisma"*)
|
|
37
|
+
*"prisma/schema.local.prisma"*|*"prisma.config.ts"*)
|
|
42
38
|
echo '{"permissionDecision":"ask","message":"⚠️ Modifying the database schema. This may require a migration."}'
|
|
43
39
|
exit 0
|
|
44
40
|
;;
|
|
@@ -32,7 +32,7 @@ export DATABASE_URL="${DATABASE_URL:-}"
|
|
|
32
32
|
|
|
33
33
|
# 3. Verify app installed
|
|
34
34
|
if [ ! -d "$APP_PATH/node_modules/.prisma" ]; then
|
|
35
|
-
echo "ERROR: Prisma client not generated. Run: cd $APP_PATH && npm install &&
|
|
35
|
+
echo "ERROR: Prisma client not generated. Run: cd $APP_PATH && npm install && npm run prisma:generate" >&2
|
|
36
36
|
exit 1
|
|
37
37
|
fi
|
|
38
38
|
|
|
@@ -57,7 +57,7 @@ fi
|
|
|
57
57
|
|
|
58
58
|
# 5b. Predictive next-skill suggestion
|
|
59
59
|
if [ -f "$HOME/.showpane/timeline.jsonl" ]; then
|
|
60
|
-
_RECENT=$(grep '"event":"completed"' "$HOME/.showpane/timeline.jsonl" 2>/dev/null | tail -3 | grep -o '"skill":"[^"]*"' | sed 's/"skill":"//;s/"//' | tr '\n' ',' | sed 's/,$//')
|
|
60
|
+
_RECENT=$(grep '"event":"completed"' "$HOME/.showpane/timeline.jsonl" 2>/dev/null | tail -3 | grep -o '"skill":"[^"]*"' | sed 's/"skill":"//;s/"//' | tr '\n' ',' | sed 's/,$//' || true)
|
|
61
61
|
[ -n "$_RECENT" ] && echo "RECENT_SKILLS: $_RECENT"
|
|
62
62
|
fi
|
|
63
63
|
|