showpane 0.4.22 → 0.4.24

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.
@@ -10,6 +10,8 @@ type OrganizationNotReadyPayload = {
10
10
  orgSlug: string;
11
11
  reason: string;
12
12
  nextAction: string;
13
+ retryable: boolean;
14
+ retryAfterMs?: number;
13
15
  };
14
16
 
15
17
  type ShowpaneConfig = {
@@ -141,6 +141,8 @@ of guessing with ad-hoc SQL.
141
141
  Do not probe `showpane --help`, `package.json`, `scripts/`, `prisma/`, or template
142
142
  directories just to understand the project. Do not call `check-slug.ts` with
143
143
  anything except `--org-id`.
144
+ Do not re-read config, Prisma, or SQLite to rediscover org fields that were already
145
+ returned by `get-org.ts`.
144
146
 
145
147
  The canonical references for this skill are:
146
148
 
@@ -46,6 +46,8 @@ of guessing with ad-hoc SQL.
46
46
  Do not probe `showpane --help`, `package.json`, `scripts/`, `prisma/`, or template
47
47
  directories just to understand the project. Do not call `check-slug.ts` with
48
48
  anything except `--org-id`.
49
+ Do not re-read config, Prisma, or SQLite to rediscover org fields that were already
50
+ returned by `get-org.ts`.
49
51
 
50
52
  The canonical references for this skill are:
51
53
 
@@ -131,10 +131,9 @@ fi
131
131
  If the token is missing, stop and tell the user:
132
132
 
133
133
  - `showpane login` is the right next step
134
- - it opens browser auth for Showpane Cloud automatically
135
- - if they need an account first:
136
- - sign up: `https://app.showpane.com/sign-up`
137
- - sign in: `https://app.showpane.com/sign-in`
134
+ - it owns the whole browser flow now: sign up or sign in if needed, start checkout,
135
+ then return to CLI authorization automatically
136
+ - do not manually steer the user through separate sign-up, sign-in, or checkout steps
138
137
 
139
138
  If they are already inside Claude Code, explicitly suggest:
140
139
 
@@ -166,353 +165,65 @@ If type errors are found, display them and stop. Offer to fix simple issues (mis
166
165
 
167
166
  Run list-portals and warn about portals missing credentials. This is a warning, not a blocker.
168
167
 
169
- ### Cloud Step 2: Build the app
168
+ ### Cloud Step 2: Run the canonical deploy command
170
169
 
171
- Before building, ensure the hidden local project-link metadata exists:
170
+ Use the built-in deploy command instead of reimplementing the staged cloud protocol in shell.
172
171
 
173
172
  ```bash
174
- if [ ! -f "$APP_PATH/.vercel/project.json" ]; then
175
- cd "$APP_PATH" && NODE_PATH="$APP_PATH/node_modules" npx tsx --tsconfig "$APP_PATH/tsconfig.json" "$SKILL_DIR/bin/ensure-cloud-project-link.ts"
176
- fi
177
- ```
178
-
179
- Run the cloud build command that produces the prebuilt artifact:
180
-
181
- ```bash
182
- cd "$APP_PATH" && npm run cloud:build
183
- ```
184
-
185
- After the build completes, verify the output directory was created:
186
-
187
- ```bash
188
- ls -la "$APP_PATH/.vercel/output/"
189
- ```
190
-
191
- Expected: the `.vercel/output/` directory exists and contains `config.json`, `static/`, and optionally `functions/`. If the build fails or the output directory is missing, show the build errors and stop.
192
-
193
- The build typically takes 30-90 seconds depending on the number of portals.
194
-
195
- ### Cloud Step 3: Package the deploy artifact
196
-
197
- Package the prebuilt output plus the traced runtime files as a single zip artifact that can be handed to Showpane Cloud. This bundle must include:
198
- - `.vercel/output/**`
199
- - traced `.next/server/**` and `node_modules/**` files referenced by the build output
200
- - a sanitized `.env` stub so the traced runtime can resolve its expected path without leaking local secrets
201
-
202
- ```bash
203
- ARTIFACT_PATH="/tmp/showpane-deploy-${CLOUD_ORG_SLUG:-portal}.zip"
204
- rm -f "$ARTIFACT_PATH"
205
- cd "$APP_PATH" && NODE_PATH="$APP_PATH/node_modules" npx tsx --tsconfig "$APP_PATH/tsconfig.json" "$SKILL_DIR/bin/create-deploy-bundle.ts" --output "$ARTIFACT_PATH"
206
- test -f "$ARTIFACT_PATH" || { echo "ERROR: Artifact zip was not created"; exit 1; }
207
- echo "Artifact ready: $ARTIFACT_PATH"
208
- ```
209
-
210
- ### Cloud Step 4: Export runtime data
211
-
212
- Export the current local portal-runtime state so Showpane Cloud can sync credentials and portal metadata before publishing:
213
-
214
- ```bash
215
- RUNTIME_DATA_PATH="/tmp/showpane-runtime-${CLOUD_ORG_SLUG:-portal}.json"
216
- rm -f "$RUNTIME_DATA_PATH"
217
- cd "$APP_PATH" && NODE_PATH="$APP_PATH/node_modules" npx tsx --tsconfig "$APP_PATH/tsconfig.json" "$SKILL_DIR/bin/export-runtime-state.ts" > "$RUNTIME_DATA_PATH" \
218
- || { echo "ERROR: Runtime payload export failed"; exit 1; }
219
- test -s "$RUNTIME_DATA_PATH" || { echo "ERROR: Runtime payload was not created"; exit 1; }
220
- echo "Runtime payload ready: $RUNTIME_DATA_PATH"
221
- ```
222
-
223
- ### Cloud Step 5: Export file manifest
224
-
225
- Export uploaded document metadata and checksums so Showpane Cloud can determine which files need syncing:
226
-
227
- ```bash
228
- FILE_MANIFEST_PATH="/tmp/showpane-files-${CLOUD_ORG_SLUG:-portal}.json"
229
- rm -f "$FILE_MANIFEST_PATH"
230
- cd "$APP_PATH" && NODE_PATH="$APP_PATH/node_modules" npx tsx --tsconfig "$APP_PATH/tsconfig.json" "$SKILL_DIR/bin/export-file-manifest.ts" > "$FILE_MANIFEST_PATH" \
231
- || { echo "ERROR: File manifest export failed"; exit 1; }
232
- test -s "$FILE_MANIFEST_PATH" || { echo "ERROR: File manifest was not created"; exit 1; }
233
- echo "File manifest ready: $FILE_MANIFEST_PATH"
234
- ```
235
-
236
- ### Cloud Step 6: Sync uploaded files
237
-
238
- Ask Showpane Cloud which files are missing or stale, then upload only those file bytes:
239
-
240
- ```bash
241
- SYNC_PLAN_RESPONSE=$(curl -s -X POST "$CLOUD_API_BASE/api/files/plan" \
242
- -H "Authorization: Bearer $CLOUD_API_TOKEN" \
243
- -H "Content-Type: application/json" \
244
- --data-binary @"$FILE_MANIFEST_PATH")
245
-
246
- MISSING_COUNT=$(echo "$SYNC_PLAN_RESPONSE" | python3 -c "
247
- import sys, json
248
- d = json.load(sys.stdin)
249
- if 'error' in d:
250
- print('ERROR: ' + d['error'])
251
- sys.exit(1)
252
- print(len(d.get('missing', [])))
253
- ")
254
-
255
- echo \"Files to sync: $MISSING_COUNT\"
256
-
257
- if [ \"$MISSING_COUNT\" -gt 0 ]; then
258
- TMP_SYNC_DIR=$(mktemp -d /tmp/showpane-file-sync.XXXXXX)
259
- echo \"$SYNC_PLAN_RESPONSE\" | python3 -c '
260
- import json, sys
261
- for item in json.load(sys.stdin).get(\"missing\", []):
262
- print(\"\\t\".join([
263
- item[\"storagePath\"],
264
- item[\"portalSlug\"],
265
- item[\"filename\"],
266
- item[\"mimeType\"],
267
- str(item[\"size\"]),
268
- item[\"uploadedBy\"],
269
- item[\"uploadedAt\"],
270
- item[\"checksum\"],
271
- ]))
272
- ' | while IFS=$'\t' read -r STORAGE_PATH PORTAL_SLUG FILE_NAME MIME_TYPE FILE_SIZE UPLOADED_BY UPLOADED_AT CHECKSUM; do
273
- TMP_FILE="$TMP_SYNC_DIR/$CHECKSUM"
274
- cd "$APP_PATH" && NODE_PATH="$APP_PATH/node_modules" npx tsx --tsconfig "$APP_PATH/tsconfig.json" "$SKILL_DIR/bin/materialize-file.ts" --storage-path "$STORAGE_PATH" --output "$TMP_FILE"
275
- curl -s -X POST "$CLOUD_API_BASE/api/files/upload" \
276
- -H "Authorization: Bearer $CLOUD_API_TOKEN" \
277
- -F "file=@$TMP_FILE;type=$MIME_TYPE" \
278
- -F "storagePath=$STORAGE_PATH" \
279
- -F "portalSlug=$PORTAL_SLUG" \
280
- -F "filename=$FILE_NAME" \
281
- -F "mimeType=$MIME_TYPE" \
282
- -F "size=$FILE_SIZE" \
283
- -F "uploadedBy=$UPLOADED_BY" \
284
- -F "uploadedAt=$UPLOADED_AT" \
285
- -F "checksum=$CHECKSUM" \
286
- >/dev/null || exit 1
287
- done
288
- fi
289
- ```
290
-
291
- ### Cloud Step 7: Start the staged deployment
292
-
293
- ```bash
294
- PORTAL_COUNT=$(cd "$APP_PATH" && NODE_PATH="$APP_PATH/node_modules" npx tsx --tsconfig "$APP_PATH/tsconfig.json" "$SKILL_DIR/bin/list-portals.ts" \
295
- | python3 -c "import sys,json; print(len(json.load(sys.stdin).get('portals', [])))")
296
-
297
- INIT_RESPONSE=$(curl -s -X POST "$CLOUD_API_BASE/api/deployments" \
298
- -H "Authorization: Bearer $CLOUD_API_TOKEN" \
299
- -H "Content-Type: application/json" \
300
- --data-binary '{}')
301
-
302
- echo \"$INIT_RESPONSE\" | python3 -c "
303
- import sys, json
304
- d = json.load(sys.stdin)
305
- if 'error' in d:
306
- print('ERROR: ' + str(d['error']))
307
- sys.exit(1)
308
- print('Deployment initialized')
309
- print('ID: ' + d.get('deploymentId', 'unknown'))
310
- print('Status: ' + d.get('status', 'unknown'))
311
- "
173
+ DEPLOY_JSON=$(cd "$APP_PATH" && SHOWPANE_APP_PATH="$APP_PATH" NODE_PATH="$APP_PATH/node_modules" npx tsx --tsconfig "$SKILL_DIR/bin/tsconfig.json" "$SKILL_DIR/bin/deploy-to-cloud.ts" --app-path "$APP_PATH" --wait --json)
174
+ echo "$DEPLOY_JSON"
312
175
  ```
313
176
 
314
- Extract the deployment ID and the presigned artifact upload URL from the response. If the API returns an error, show it and stop.
177
+ That command already owns:
178
+ - type check
179
+ - project-link bootstrap
180
+ - `cloud:build`
181
+ - artifact packaging
182
+ - runtime export
183
+ - optional file sync
184
+ - deployment init/upload/finalize
185
+ - polling to terminal state
186
+ - hosted verification
315
187
 
316
- ### Cloud Step 8: Upload the artifact directly to storage
188
+ Parse the JSON and stop immediately if `ok` is false.
317
189
 
318
- ```bash
319
- DEPLOY_ID=$(echo "$INIT_RESPONSE" | python3 -c "import sys,json; d=json.load(sys.stdin); print(d.get('deploymentId',''))")
320
- ARTIFACT_UPLOAD_URL=$(echo "$INIT_RESPONSE" | python3 -c "import sys,json; d=json.load(sys.stdin); print(d.get('artifactUploadUrl',''))")
321
-
322
- if [ -z "$DEPLOY_ID" ] || [ -z "$ARTIFACT_UPLOAD_URL" ]; then
323
- echo "ERROR: Missing deploymentId or artifact upload URL"
324
- exit 1
325
- fi
326
-
327
- UPLOAD_STATUS=$(curl -s -o /dev/null -w "%{http_code}" -X PUT "$ARTIFACT_UPLOAD_URL" \
328
- -H "Content-Type: application/zip" \
329
- --data-binary @"$ARTIFACT_PATH" \
330
- )
331
-
332
- if [ "$UPLOAD_STATUS" != "200" ]; then
333
- echo "ERROR: Artifact upload failed with HTTP $UPLOAD_STATUS"
334
- exit 1
335
- fi
336
- ```
190
+ ### Cloud Step 3: Summarize the result
337
191
 
338
- ### Cloud Step 9: Finalize the deployment
339
-
340
- ```bash
341
- FINALIZE_PAYLOAD="/tmp/showpane-deploy-finalize-${DEPLOY_ID}.json"
342
- python3 - <<'PY' "$RUNTIME_DATA_PATH" "$PORTAL_COUNT" > "$FINALIZE_PAYLOAD"
343
- import json, sys
344
- runtime_path = sys.argv[1]
345
- portal_count = int(sys.argv[2])
346
- payload = {
347
- "portalCount": portal_count,
348
- "runtimeData": json.load(open(runtime_path)),
349
- }
350
- print(json.dumps(payload))
351
- PY
352
-
353
- FINALIZE_RESPONSE=$(curl -s -X POST "$CLOUD_API_BASE/api/deployments/$DEPLOY_ID/finalize" \
354
- -H "Authorization: Bearer $CLOUD_API_TOKEN" \
355
- -H "Content-Type: application/json" \
356
- --data-binary @"$FINALIZE_PAYLOAD")
357
-
358
- echo "$FINALIZE_RESPONSE" | python3 -c "
359
- import sys, json
360
- d = json.load(sys.stdin)
361
- if 'error' in d:
362
- print('ERROR: ' + str(d['error']))
363
- sys.exit(1)
364
- print('Deployment accepted')
365
- print('ID: ' + d.get('deploymentId', 'unknown'))
366
- print('Status: ' + d.get('status', 'unknown'))
367
- "
368
- ```
192
+ Read these fields from the returned JSON:
193
+ - `deploymentId`
194
+ - `status`
195
+ - `liveUrl`
196
+ - `portalCount`
197
+ - `firstPortalSlug`
198
+ - `fileSyncCount`
199
+ - `verification.portalStatus`
200
+ - `verification.healthStatus`
369
201
 
370
- ### Cloud Step 10: Wait for cloud publish to finish
202
+ If `status` is not `live`, treat the deploy as failed.
371
203
 
372
- Poll Showpane Cloud until the deployment reaches `live` or `failed`:
204
+ Print a concise summary:
373
205
 
374
- ```bash
375
- echo "Waiting for deployment to go live..."
376
- for i in $(seq 1 60); do
377
- sleep 5
378
- STATUS=$(curl -s \
379
- -H "Authorization: Bearer $CLOUD_API_TOKEN" \
380
- "$CLOUD_API_BASE/api/deployments/$DEPLOY_ID" \
381
- | python3 -c "import sys,json; d=json.load(sys.stdin); print(d.get('status','unknown'))")
382
-
383
- echo " Status: $STATUS"
384
-
385
- if [ "$STATUS" = "live" ]; then
386
- echo "Deployment is live!"
387
- break
388
- fi
389
-
390
- if [ "$STATUS" = "failed" ] || [ "$STATUS" = "unhealthy" ]; then
391
- echo "ERROR: Deployment failed with status: $STATUS"
392
- curl -s \
393
- -H "Authorization: Bearer $CLOUD_API_TOKEN" \
394
- "$CLOUD_API_BASE/api/deployments/$DEPLOY_ID"
395
- exit 1
396
- fi
397
- done
398
-
399
- if [ "$STATUS" != "live" ]; then
400
- echo "WARNING: Deployment did not become ready within 5 minutes."
401
- echo "Check Showpane Cloud for status."
402
- fi
403
- ```
404
-
405
- The publish typically takes 15-60 seconds. Poll every 5 seconds for up to 5 minutes.
406
-
407
- ### Cloud Step 11: Post-deploy verification
408
-
409
- Once the deployment is live, verify the portal is accessible:
410
-
411
- ```bash
412
- PORTAL_URL="${CLOUD_PORTAL_URL:-https://$CLOUD_ORG_SLUG.showpane.com}"
413
-
414
- HTTP_CODE=$(curl -s -o /dev/null -w "%{http_code}" "$PORTAL_URL")
415
- echo "Health check: $PORTAL_URL -> HTTP $HTTP_CODE"
416
- ```
417
-
418
- Expected: HTTP 200. If not 200, warn the user but do not treat it as a fatal error — DNS propagation or edge caching may cause a brief delay.
419
-
420
- Also check the API health endpoint:
421
-
422
- ```bash
423
- curl -s -o /dev/null -w "%{http_code}" "$PORTAL_URL/api/health"
424
- ```
425
-
426
- ### Cloud Step 12: Deployment summary
427
-
428
- Print a clear summary:
429
-
430
- ```
431
- Cloud deploy complete!
206
+ ```text
207
+ Cloud deploy complete
432
208
 
433
- Mode: cloud
434
- Type check: passed
435
- Build: .vercel/output/ (N files)
436
- Portals: N active
437
209
  Status: live
438
- Health: OK (200)
439
-
440
- Portal URL: https://orgslug.showpane.com
441
210
  Deploy ID: dep_xxxxxxxxxxxx
211
+ Live URL: https://orgslug.showpane.com
212
+ Portals: N
213
+ File sync: N
442
214
  ```
443
215
 
444
- ### Cloud Step 13: Record deployment
216
+ ### Cloud Step 4: Record deployment
445
217
 
446
218
  Log the cloud deployment for operational memory:
447
219
 
448
220
  ```bash
449
- echo '{"skill":"portal-deploy","key":"deploy","insight":"Cloud deploy to '$CLOUD_ORG_SLUG'.showpane.com. Migrations: <count>. Portals: <count> active. Deploy ID: '$DEPLOY_ID'.","confidence":10,"ts":"'$(date -u +%Y-%m-%dT%H:%M:%SZ)'"}' >> "$HOME/.showpane/learnings.jsonl"
450
- ```
451
-
452
- ### Cloud Step 14: Clean up
453
-
454
- Remove the temporary artifact:
455
-
456
- ```bash
457
- rm -f "$ARTIFACT_PATH"
458
- rm -f "$RUNTIME_DATA_PATH"
459
- rm -f "$FILE_MANIFEST_PATH"
460
- rm -f "$FINALIZE_PAYLOAD"
461
- [ -n "${TMP_SYNC_DIR:-}" ] && rm -rf "$TMP_SYNC_DIR"
221
+ DEPLOY_ID=$(echo "$DEPLOY_JSON" | python3 -c "import sys,json; print(json.load(sys.stdin).get('deploymentId',''))")
222
+ PORTAL_COUNT=$(echo "$DEPLOY_JSON" | python3 -c "import sys,json; print(json.load(sys.stdin).get('portalCount',0))")
223
+ echo '{"skill":"portal-deploy","key":"deploy","insight":"Cloud deploy to '$CLOUD_ORG_SLUG'.showpane.com. Portals: '$PORTAL_COUNT' active. Deploy ID: '$DEPLOY_ID'.","confidence":10,"ts":"'$(date -u +%Y-%m-%dT%H:%M:%SZ)'"}' >> "$HOME/.showpane/learnings.jsonl"
462
224
  ```
463
225
 
464
- ---
465
-
466
- ## Post-Deploy Verification
467
-
468
- After deployment succeeds, automatically run these verification steps. Do not ask the user — just do them.
469
-
470
- ### Step V1: DNS/URL Check
471
- Fetch the portal URL and verify it returns 200:
472
- ```bash
473
- PORTAL_URL="${CLOUD_PORTAL_URL:-https://$CLOUD_ORG_SLUG.showpane.com}"
474
- echo "Verifying $PORTAL_URL..."
475
- HTTP_CODE=$(curl -s -o /dev/null -w "%{http_code}" "$PORTAL_URL" 2>/dev/null)
476
- echo "HTTP status: $HTTP_CODE"
477
- ```
478
- If not 200, warn but don't fail — DNS propagation may take time for cloud deploys.
479
-
480
- ### Step V2: Portal Content Check
481
- If portals exist in the database, verify at least one portal login page loads:
482
- ```bash
483
- # Get the first active portal slug
484
- FIRST_SLUG=$(cd "$APP_PATH" && npx tsx -e "
485
- const { prisma } = require('./src/lib/db');
486
- prisma.clientPortal.findFirst({ where: { isActive: true }, select: { slug: true } })
487
- .then(p => console.log(p?.slug || ''))
488
- .finally(() => prisma.\$disconnect());
489
- " 2>/dev/null)
490
- if [ -n "$FIRST_SLUG" ]; then
491
- echo "Checking portal: $PORTAL_URL/client/$FIRST_SLUG"
492
- curl -s -o /dev/null -w "Portal login page: %{http_code}\n" "$PORTAL_URL/client/$FIRST_SLUG" 2>/dev/null
493
- fi
494
- ```
495
-
496
- ### Step V3: SSL Check (cloud only)
497
- For cloud deploys, verify SSL is working:
498
- ```bash
499
- if [ "$DEPLOY_MODE" = "cloud" ] && [ -n "$CLOUD_PORTAL_URL" ]; then
500
- echo "Checking SSL..."
501
- SSL_OK=$(curl -s -o /dev/null -w "%{http_code}" "https://${CLOUD_ORG_SLUG}.showpane.com" 2>/dev/null)
502
- echo "SSL status: $SSL_OK"
503
- fi
504
- ```
505
-
506
- ### Step V4: Summary
507
- Print a deployment summary:
508
- ```
509
- ✓ Deployed successfully
510
- URL: [portal URL]
511
- Portals: [count]
512
- SSL: [ok/pending]
513
-
514
- Next: /portal-status for ongoing monitoring
515
- ```
226
+ The deploy command already handles cleanup and hosted verification. Do not duplicate those steps here.
516
227
 
517
228
  ---
518
229
 
@@ -536,7 +247,7 @@ If the health endpoint returns non-200 after deploy:
536
247
  If the Showpane Cloud API returns 401/403 during pre-flight or publish:
537
248
  - **401 Unauthorized**: The access token is expired or revoked. Run `showpane login` to re-authenticate.
538
249
  - **403 Forbidden**: The token does not belong to an org that can publish. Re-authenticate or finish cloud onboarding.
539
- - **409 organization_required**: The user authenticated but has no org yet. Send them to Showpane Cloud checkout to start the trial, then re-run `showpane login`.
250
+ - **409 organization_required**: Tell the user to run `showpane login` again and let that browser flow continue. Do not manually redirect them into separate checkout steps from this skill.
540
251
  - **409 organization_not_ready**: The org exists, but billing or provisioning is not publish-ready. Surface the returned `reason`, `nextAction`, and settings/checkout URL directly instead of collapsing it into a generic deploy failure.
541
252
 
542
253
  ### Cloud: Artifact upload failure