showpane 0.4.23 → 0.4.25
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 +3 -3
- package/bundle/scaffold/src/__tests__/portal-contracts.test.ts +20 -0
- package/bundle/scaffold/src/lib/portal-contracts.ts +2 -0
- package/bundle/toolchain/CLI_VERSION +1 -1
- package/bundle/toolchain/bin/deploy-to-cloud.ts +761 -0
- package/bundle/toolchain/bin/ensure-cloud-project-link.ts +2 -0
- package/bundle/toolchain/skills/portal-create/SKILL.md +9 -11
- package/bundle/toolchain/skills/portal-create/SKILL.md.tmpl +9 -11
- package/bundle/toolchain/skills/portal-deploy/SKILL.md +37 -325
- package/bundle/toolchain/skills/portal-deploy/SKILL.md.tmpl +37 -325
- package/bundle/toolchain/skills/portal-onboard/SKILL.md +9 -5
- package/bundle/toolchain/skills/portal-onboard/SKILL.md.tmpl +9 -5
- package/dist/index.js +125 -1
- package/package.json +1 -1
|
@@ -138,13 +138,8 @@ Store:
|
|
|
138
138
|
If the helper fails, stop and tell the user to run `/portal-setup` again instead
|
|
139
139
|
of guessing with ad-hoc SQL.
|
|
140
140
|
|
|
141
|
-
|
|
142
|
-
|
|
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`.
|
|
146
|
-
|
|
147
|
-
The canonical references for this skill are:
|
|
141
|
+
Once `get-org.ts` succeeds, extra project probing rarely improves the draft.
|
|
142
|
+
Use that result plus the selected template/example as the canonical references:
|
|
148
143
|
|
|
149
144
|
- the configured `APP_PATH`
|
|
150
145
|
- the configured `ORG_SLUG`
|
|
@@ -153,6 +148,9 @@ The canonical references for this skill are:
|
|
|
153
148
|
- `$SKILL_DIR/templates/<chosen-template>/...`
|
|
154
149
|
- `$APP_PATH/src/app/(portal)/client/example/example-client.tsx`
|
|
155
150
|
|
|
151
|
+
For slug checks, use `check-slug.ts` with `--org-id`. Re-reading config, Prisma,
|
|
152
|
+
or SQLite usually just burns time without changing the correct org context.
|
|
153
|
+
|
|
156
154
|
### Step 2: Determine the portal slug
|
|
157
155
|
|
|
158
156
|
If the user provided a slug (e.g., `/portal-create acme-health`), use it. Otherwise, infer from context — the company name mentioned in conversation, a meeting transcript, or ask the user directly.
|
|
@@ -215,10 +213,10 @@ Always also read the example portal as your quality and style reference:
|
|
|
215
213
|
cat "$APP_PATH/src/app/(portal)/client/example/example-client.tsx"
|
|
216
214
|
```
|
|
217
215
|
|
|
218
|
-
The template provides
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
216
|
+
The template provides structure. The example provides quality and styling.
|
|
217
|
+
Read those directly from the known paths above, then match the example's card
|
|
218
|
+
styles, typography, spacing, and responsive breakpoints. Templates are
|
|
219
|
+
inspiration, not rigid scaffolds.
|
|
222
220
|
|
|
223
221
|
### Step 5: Analyze transcript (if available)
|
|
224
222
|
|
|
@@ -43,13 +43,8 @@ Store:
|
|
|
43
43
|
If the helper fails, stop and tell the user to run `/portal-setup` again instead
|
|
44
44
|
of guessing with ad-hoc SQL.
|
|
45
45
|
|
|
46
|
-
|
|
47
|
-
|
|
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`.
|
|
51
|
-
|
|
52
|
-
The canonical references for this skill are:
|
|
46
|
+
Once `get-org.ts` succeeds, extra project probing rarely improves the draft.
|
|
47
|
+
Use that result plus the selected template/example as the canonical references:
|
|
53
48
|
|
|
54
49
|
- the configured `APP_PATH`
|
|
55
50
|
- the configured `ORG_SLUG`
|
|
@@ -58,6 +53,9 @@ The canonical references for this skill are:
|
|
|
58
53
|
- `$SKILL_DIR/templates/<chosen-template>/...`
|
|
59
54
|
- `$APP_PATH/src/app/(portal)/client/example/example-client.tsx`
|
|
60
55
|
|
|
56
|
+
For slug checks, use `check-slug.ts` with `--org-id`. Re-reading config, Prisma,
|
|
57
|
+
or SQLite usually just burns time without changing the correct org context.
|
|
58
|
+
|
|
61
59
|
### Step 2: Determine the portal slug
|
|
62
60
|
|
|
63
61
|
If the user provided a slug (e.g., `/portal-create acme-health`), use it. Otherwise, infer from context — the company name mentioned in conversation, a meeting transcript, or ask the user directly.
|
|
@@ -120,10 +118,10 @@ Always also read the example portal as your quality and style reference:
|
|
|
120
118
|
cat "$APP_PATH/src/app/(portal)/client/example/example-client.tsx"
|
|
121
119
|
```
|
|
122
120
|
|
|
123
|
-
The template provides
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
121
|
+
The template provides structure. The example provides quality and styling.
|
|
122
|
+
Read those directly from the known paths above, then match the example's card
|
|
123
|
+
styles, typography, spacing, and responsive breakpoints. Templates are
|
|
124
|
+
inspiration, not rigid scaffolds.
|
|
127
125
|
|
|
128
126
|
### Step 5: Analyze transcript (if available)
|
|
129
127
|
|
|
@@ -165,353 +165,65 @@ If type errors are found, display them and stop. Offer to fix simple issues (mis
|
|
|
165
165
|
|
|
166
166
|
Run list-portals and warn about portals missing credentials. This is a warning, not a blocker.
|
|
167
167
|
|
|
168
|
-
### Cloud Step 2:
|
|
168
|
+
### Cloud Step 2: Run the canonical deploy command
|
|
169
169
|
|
|
170
|
-
|
|
170
|
+
Use the built-in deploy command instead of reimplementing the staged cloud protocol in shell.
|
|
171
171
|
|
|
172
172
|
```bash
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
fi
|
|
176
|
-
```
|
|
177
|
-
|
|
178
|
-
Run the cloud build command that produces the prebuilt artifact:
|
|
179
|
-
|
|
180
|
-
```bash
|
|
181
|
-
cd "$APP_PATH" && npm run cloud:build
|
|
182
|
-
```
|
|
183
|
-
|
|
184
|
-
After the build completes, verify the output directory was created:
|
|
185
|
-
|
|
186
|
-
```bash
|
|
187
|
-
ls -la "$APP_PATH/.vercel/output/"
|
|
188
|
-
```
|
|
189
|
-
|
|
190
|
-
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.
|
|
191
|
-
|
|
192
|
-
The build typically takes 30-90 seconds depending on the number of portals.
|
|
193
|
-
|
|
194
|
-
### Cloud Step 3: Package the deploy artifact
|
|
195
|
-
|
|
196
|
-
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:
|
|
197
|
-
- `.vercel/output/**`
|
|
198
|
-
- traced `.next/server/**` and `node_modules/**` files referenced by the build output
|
|
199
|
-
- a sanitized `.env` stub so the traced runtime can resolve its expected path without leaking local secrets
|
|
200
|
-
|
|
201
|
-
```bash
|
|
202
|
-
ARTIFACT_PATH="/tmp/showpane-deploy-${CLOUD_ORG_SLUG:-portal}.zip"
|
|
203
|
-
rm -f "$ARTIFACT_PATH"
|
|
204
|
-
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"
|
|
205
|
-
test -f "$ARTIFACT_PATH" || { echo "ERROR: Artifact zip was not created"; exit 1; }
|
|
206
|
-
echo "Artifact ready: $ARTIFACT_PATH"
|
|
207
|
-
```
|
|
208
|
-
|
|
209
|
-
### Cloud Step 4: Export runtime data
|
|
210
|
-
|
|
211
|
-
Export the current local portal-runtime state so Showpane Cloud can sync credentials and portal metadata before publishing:
|
|
212
|
-
|
|
213
|
-
```bash
|
|
214
|
-
RUNTIME_DATA_PATH="/tmp/showpane-runtime-${CLOUD_ORG_SLUG:-portal}.json"
|
|
215
|
-
rm -f "$RUNTIME_DATA_PATH"
|
|
216
|
-
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" \
|
|
217
|
-
|| { echo "ERROR: Runtime payload export failed"; exit 1; }
|
|
218
|
-
test -s "$RUNTIME_DATA_PATH" || { echo "ERROR: Runtime payload was not created"; exit 1; }
|
|
219
|
-
echo "Runtime payload ready: $RUNTIME_DATA_PATH"
|
|
220
|
-
```
|
|
221
|
-
|
|
222
|
-
### Cloud Step 5: Export file manifest
|
|
223
|
-
|
|
224
|
-
Export uploaded document metadata and checksums so Showpane Cloud can determine which files need syncing:
|
|
225
|
-
|
|
226
|
-
```bash
|
|
227
|
-
FILE_MANIFEST_PATH="/tmp/showpane-files-${CLOUD_ORG_SLUG:-portal}.json"
|
|
228
|
-
rm -f "$FILE_MANIFEST_PATH"
|
|
229
|
-
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" \
|
|
230
|
-
|| { echo "ERROR: File manifest export failed"; exit 1; }
|
|
231
|
-
test -s "$FILE_MANIFEST_PATH" || { echo "ERROR: File manifest was not created"; exit 1; }
|
|
232
|
-
echo "File manifest ready: $FILE_MANIFEST_PATH"
|
|
233
|
-
```
|
|
234
|
-
|
|
235
|
-
### Cloud Step 6: Sync uploaded files
|
|
236
|
-
|
|
237
|
-
Ask Showpane Cloud which files are missing or stale, then upload only those file bytes:
|
|
238
|
-
|
|
239
|
-
```bash
|
|
240
|
-
SYNC_PLAN_RESPONSE=$(curl -s -X POST "$CLOUD_API_BASE/api/files/plan" \
|
|
241
|
-
-H "Authorization: Bearer $CLOUD_API_TOKEN" \
|
|
242
|
-
-H "Content-Type: application/json" \
|
|
243
|
-
--data-binary @"$FILE_MANIFEST_PATH")
|
|
244
|
-
|
|
245
|
-
MISSING_COUNT=$(echo "$SYNC_PLAN_RESPONSE" | python3 -c "
|
|
246
|
-
import sys, json
|
|
247
|
-
d = json.load(sys.stdin)
|
|
248
|
-
if 'error' in d:
|
|
249
|
-
print('ERROR: ' + d['error'])
|
|
250
|
-
sys.exit(1)
|
|
251
|
-
print(len(d.get('missing', [])))
|
|
252
|
-
")
|
|
253
|
-
|
|
254
|
-
echo \"Files to sync: $MISSING_COUNT\"
|
|
255
|
-
|
|
256
|
-
if [ \"$MISSING_COUNT\" -gt 0 ]; then
|
|
257
|
-
TMP_SYNC_DIR=$(mktemp -d /tmp/showpane-file-sync.XXXXXX)
|
|
258
|
-
echo \"$SYNC_PLAN_RESPONSE\" | python3 -c '
|
|
259
|
-
import json, sys
|
|
260
|
-
for item in json.load(sys.stdin).get(\"missing\", []):
|
|
261
|
-
print(\"\\t\".join([
|
|
262
|
-
item[\"storagePath\"],
|
|
263
|
-
item[\"portalSlug\"],
|
|
264
|
-
item[\"filename\"],
|
|
265
|
-
item[\"mimeType\"],
|
|
266
|
-
str(item[\"size\"]),
|
|
267
|
-
item[\"uploadedBy\"],
|
|
268
|
-
item[\"uploadedAt\"],
|
|
269
|
-
item[\"checksum\"],
|
|
270
|
-
]))
|
|
271
|
-
' | while IFS=$'\t' read -r STORAGE_PATH PORTAL_SLUG FILE_NAME MIME_TYPE FILE_SIZE UPLOADED_BY UPLOADED_AT CHECKSUM; do
|
|
272
|
-
TMP_FILE="$TMP_SYNC_DIR/$CHECKSUM"
|
|
273
|
-
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"
|
|
274
|
-
curl -s -X POST "$CLOUD_API_BASE/api/files/upload" \
|
|
275
|
-
-H "Authorization: Bearer $CLOUD_API_TOKEN" \
|
|
276
|
-
-F "file=@$TMP_FILE;type=$MIME_TYPE" \
|
|
277
|
-
-F "storagePath=$STORAGE_PATH" \
|
|
278
|
-
-F "portalSlug=$PORTAL_SLUG" \
|
|
279
|
-
-F "filename=$FILE_NAME" \
|
|
280
|
-
-F "mimeType=$MIME_TYPE" \
|
|
281
|
-
-F "size=$FILE_SIZE" \
|
|
282
|
-
-F "uploadedBy=$UPLOADED_BY" \
|
|
283
|
-
-F "uploadedAt=$UPLOADED_AT" \
|
|
284
|
-
-F "checksum=$CHECKSUM" \
|
|
285
|
-
>/dev/null || exit 1
|
|
286
|
-
done
|
|
287
|
-
fi
|
|
288
|
-
```
|
|
289
|
-
|
|
290
|
-
### Cloud Step 7: Start the staged deployment
|
|
291
|
-
|
|
292
|
-
```bash
|
|
293
|
-
PORTAL_COUNT=$(cd "$APP_PATH" && NODE_PATH="$APP_PATH/node_modules" npx tsx --tsconfig "$APP_PATH/tsconfig.json" "$SKILL_DIR/bin/list-portals.ts" \
|
|
294
|
-
| python3 -c "import sys,json; print(len(json.load(sys.stdin).get('portals', [])))")
|
|
295
|
-
|
|
296
|
-
INIT_RESPONSE=$(curl -s -X POST "$CLOUD_API_BASE/api/deployments" \
|
|
297
|
-
-H "Authorization: Bearer $CLOUD_API_TOKEN" \
|
|
298
|
-
-H "Content-Type: application/json" \
|
|
299
|
-
--data-binary '{}')
|
|
300
|
-
|
|
301
|
-
echo \"$INIT_RESPONSE\" | python3 -c "
|
|
302
|
-
import sys, json
|
|
303
|
-
d = json.load(sys.stdin)
|
|
304
|
-
if 'error' in d:
|
|
305
|
-
print('ERROR: ' + str(d['error']))
|
|
306
|
-
sys.exit(1)
|
|
307
|
-
print('Deployment initialized')
|
|
308
|
-
print('ID: ' + d.get('deploymentId', 'unknown'))
|
|
309
|
-
print('Status: ' + d.get('status', 'unknown'))
|
|
310
|
-
"
|
|
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"
|
|
311
175
|
```
|
|
312
176
|
|
|
313
|
-
|
|
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
|
|
314
187
|
|
|
315
|
-
|
|
188
|
+
Parse the JSON and stop immediately if `ok` is false.
|
|
316
189
|
|
|
317
|
-
|
|
318
|
-
DEPLOY_ID=$(echo "$INIT_RESPONSE" | python3 -c "import sys,json; d=json.load(sys.stdin); print(d.get('deploymentId',''))")
|
|
319
|
-
ARTIFACT_UPLOAD_URL=$(echo "$INIT_RESPONSE" | python3 -c "import sys,json; d=json.load(sys.stdin); print(d.get('artifactUploadUrl',''))")
|
|
320
|
-
|
|
321
|
-
if [ -z "$DEPLOY_ID" ] || [ -z "$ARTIFACT_UPLOAD_URL" ]; then
|
|
322
|
-
echo "ERROR: Missing deploymentId or artifact upload URL"
|
|
323
|
-
exit 1
|
|
324
|
-
fi
|
|
325
|
-
|
|
326
|
-
UPLOAD_STATUS=$(curl -s -o /dev/null -w "%{http_code}" -X PUT "$ARTIFACT_UPLOAD_URL" \
|
|
327
|
-
-H "Content-Type: application/zip" \
|
|
328
|
-
--data-binary @"$ARTIFACT_PATH" \
|
|
329
|
-
)
|
|
330
|
-
|
|
331
|
-
if [ "$UPLOAD_STATUS" != "200" ]; then
|
|
332
|
-
echo "ERROR: Artifact upload failed with HTTP $UPLOAD_STATUS"
|
|
333
|
-
exit 1
|
|
334
|
-
fi
|
|
335
|
-
```
|
|
190
|
+
### Cloud Step 3: Summarize the result
|
|
336
191
|
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
"portalCount": portal_count,
|
|
347
|
-
"runtimeData": json.load(open(runtime_path)),
|
|
348
|
-
}
|
|
349
|
-
print(json.dumps(payload))
|
|
350
|
-
PY
|
|
351
|
-
|
|
352
|
-
FINALIZE_RESPONSE=$(curl -s -X POST "$CLOUD_API_BASE/api/deployments/$DEPLOY_ID/finalize" \
|
|
353
|
-
-H "Authorization: Bearer $CLOUD_API_TOKEN" \
|
|
354
|
-
-H "Content-Type: application/json" \
|
|
355
|
-
--data-binary @"$FINALIZE_PAYLOAD")
|
|
356
|
-
|
|
357
|
-
echo "$FINALIZE_RESPONSE" | python3 -c "
|
|
358
|
-
import sys, json
|
|
359
|
-
d = json.load(sys.stdin)
|
|
360
|
-
if 'error' in d:
|
|
361
|
-
print('ERROR: ' + str(d['error']))
|
|
362
|
-
sys.exit(1)
|
|
363
|
-
print('Deployment accepted')
|
|
364
|
-
print('ID: ' + d.get('deploymentId', 'unknown'))
|
|
365
|
-
print('Status: ' + d.get('status', 'unknown'))
|
|
366
|
-
"
|
|
367
|
-
```
|
|
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`
|
|
368
201
|
|
|
369
|
-
|
|
202
|
+
If `status` is not `live`, treat the deploy as failed.
|
|
370
203
|
|
|
371
|
-
|
|
204
|
+
Print a concise summary:
|
|
372
205
|
|
|
373
|
-
```
|
|
374
|
-
|
|
375
|
-
for i in $(seq 1 60); do
|
|
376
|
-
sleep 5
|
|
377
|
-
STATUS=$(curl -s \
|
|
378
|
-
-H "Authorization: Bearer $CLOUD_API_TOKEN" \
|
|
379
|
-
"$CLOUD_API_BASE/api/deployments/$DEPLOY_ID" \
|
|
380
|
-
| python3 -c "import sys,json; d=json.load(sys.stdin); print(d.get('status','unknown'))")
|
|
381
|
-
|
|
382
|
-
echo " Status: $STATUS"
|
|
383
|
-
|
|
384
|
-
if [ "$STATUS" = "live" ]; then
|
|
385
|
-
echo "Deployment is live!"
|
|
386
|
-
break
|
|
387
|
-
fi
|
|
388
|
-
|
|
389
|
-
if [ "$STATUS" = "failed" ] || [ "$STATUS" = "unhealthy" ]; then
|
|
390
|
-
echo "ERROR: Deployment failed with status: $STATUS"
|
|
391
|
-
curl -s \
|
|
392
|
-
-H "Authorization: Bearer $CLOUD_API_TOKEN" \
|
|
393
|
-
"$CLOUD_API_BASE/api/deployments/$DEPLOY_ID"
|
|
394
|
-
exit 1
|
|
395
|
-
fi
|
|
396
|
-
done
|
|
397
|
-
|
|
398
|
-
if [ "$STATUS" != "live" ]; then
|
|
399
|
-
echo "WARNING: Deployment did not become ready within 5 minutes."
|
|
400
|
-
echo "Check Showpane Cloud for status."
|
|
401
|
-
fi
|
|
402
|
-
```
|
|
403
|
-
|
|
404
|
-
The publish typically takes 15-60 seconds. Poll every 5 seconds for up to 5 minutes.
|
|
405
|
-
|
|
406
|
-
### Cloud Step 11: Post-deploy verification
|
|
407
|
-
|
|
408
|
-
Once the deployment is live, verify the portal is accessible:
|
|
409
|
-
|
|
410
|
-
```bash
|
|
411
|
-
PORTAL_URL="${CLOUD_PORTAL_URL:-https://$CLOUD_ORG_SLUG.showpane.com}"
|
|
412
|
-
|
|
413
|
-
HTTP_CODE=$(curl -s -o /dev/null -w "%{http_code}" "$PORTAL_URL")
|
|
414
|
-
echo "Health check: $PORTAL_URL -> HTTP $HTTP_CODE"
|
|
415
|
-
```
|
|
416
|
-
|
|
417
|
-
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.
|
|
418
|
-
|
|
419
|
-
Also check the API health endpoint:
|
|
420
|
-
|
|
421
|
-
```bash
|
|
422
|
-
curl -s -o /dev/null -w "%{http_code}" "$PORTAL_URL/api/health"
|
|
423
|
-
```
|
|
424
|
-
|
|
425
|
-
### Cloud Step 12: Deployment summary
|
|
426
|
-
|
|
427
|
-
Print a clear summary:
|
|
428
|
-
|
|
429
|
-
```
|
|
430
|
-
Cloud deploy complete!
|
|
206
|
+
```text
|
|
207
|
+
Cloud deploy complete
|
|
431
208
|
|
|
432
|
-
Mode: cloud
|
|
433
|
-
Type check: passed
|
|
434
|
-
Build: .vercel/output/ (N files)
|
|
435
|
-
Portals: N active
|
|
436
209
|
Status: live
|
|
437
|
-
Health: OK (200)
|
|
438
|
-
|
|
439
|
-
Portal URL: https://orgslug.showpane.com
|
|
440
210
|
Deploy ID: dep_xxxxxxxxxxxx
|
|
211
|
+
Live URL: https://orgslug.showpane.com
|
|
212
|
+
Portals: N
|
|
213
|
+
File sync: N
|
|
441
214
|
```
|
|
442
215
|
|
|
443
|
-
### Cloud Step
|
|
216
|
+
### Cloud Step 4: Record deployment
|
|
444
217
|
|
|
445
218
|
Log the cloud deployment for operational memory:
|
|
446
219
|
|
|
447
220
|
```bash
|
|
448
|
-
echo
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
### Cloud Step 14: Clean up
|
|
452
|
-
|
|
453
|
-
Remove the temporary artifact:
|
|
454
|
-
|
|
455
|
-
```bash
|
|
456
|
-
rm -f "$ARTIFACT_PATH"
|
|
457
|
-
rm -f "$RUNTIME_DATA_PATH"
|
|
458
|
-
rm -f "$FILE_MANIFEST_PATH"
|
|
459
|
-
rm -f "$FINALIZE_PAYLOAD"
|
|
460
|
-
[ -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"
|
|
461
224
|
```
|
|
462
225
|
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
## Post-Deploy Verification
|
|
466
|
-
|
|
467
|
-
After deployment succeeds, automatically run these verification steps. Do not ask the user — just do them.
|
|
468
|
-
|
|
469
|
-
### Step V1: DNS/URL Check
|
|
470
|
-
Fetch the portal URL and verify it returns 200:
|
|
471
|
-
```bash
|
|
472
|
-
PORTAL_URL="${CLOUD_PORTAL_URL:-https://$CLOUD_ORG_SLUG.showpane.com}"
|
|
473
|
-
echo "Verifying $PORTAL_URL..."
|
|
474
|
-
HTTP_CODE=$(curl -s -o /dev/null -w "%{http_code}" "$PORTAL_URL" 2>/dev/null)
|
|
475
|
-
echo "HTTP status: $HTTP_CODE"
|
|
476
|
-
```
|
|
477
|
-
If not 200, warn but don't fail — DNS propagation may take time for cloud deploys.
|
|
478
|
-
|
|
479
|
-
### Step V2: Portal Content Check
|
|
480
|
-
If portals exist in the database, verify at least one portal login page loads:
|
|
481
|
-
```bash
|
|
482
|
-
# Get the first active portal slug
|
|
483
|
-
FIRST_SLUG=$(cd "$APP_PATH" && npx tsx -e "
|
|
484
|
-
const { prisma } = require('./src/lib/db');
|
|
485
|
-
prisma.clientPortal.findFirst({ where: { isActive: true }, select: { slug: true } })
|
|
486
|
-
.then(p => console.log(p?.slug || ''))
|
|
487
|
-
.finally(() => prisma.\$disconnect());
|
|
488
|
-
" 2>/dev/null)
|
|
489
|
-
if [ -n "$FIRST_SLUG" ]; then
|
|
490
|
-
echo "Checking portal: $PORTAL_URL/client/$FIRST_SLUG"
|
|
491
|
-
curl -s -o /dev/null -w "Portal login page: %{http_code}\n" "$PORTAL_URL/client/$FIRST_SLUG" 2>/dev/null
|
|
492
|
-
fi
|
|
493
|
-
```
|
|
494
|
-
|
|
495
|
-
### Step V3: SSL Check (cloud only)
|
|
496
|
-
For cloud deploys, verify SSL is working:
|
|
497
|
-
```bash
|
|
498
|
-
if [ "$DEPLOY_MODE" = "cloud" ] && [ -n "$CLOUD_PORTAL_URL" ]; then
|
|
499
|
-
echo "Checking SSL..."
|
|
500
|
-
SSL_OK=$(curl -s -o /dev/null -w "%{http_code}" "https://${CLOUD_ORG_SLUG}.showpane.com" 2>/dev/null)
|
|
501
|
-
echo "SSL status: $SSL_OK"
|
|
502
|
-
fi
|
|
503
|
-
```
|
|
504
|
-
|
|
505
|
-
### Step V4: Summary
|
|
506
|
-
Print a deployment summary:
|
|
507
|
-
```
|
|
508
|
-
✓ Deployed successfully
|
|
509
|
-
URL: [portal URL]
|
|
510
|
-
Portals: [count]
|
|
511
|
-
SSL: [ok/pending]
|
|
512
|
-
|
|
513
|
-
Next: /portal-status for ongoing monitoring
|
|
514
|
-
```
|
|
226
|
+
The deploy command already handles cleanup and hosted verification. Do not duplicate those steps here.
|
|
515
227
|
|
|
516
228
|
---
|
|
517
229
|
|