showpane 0.4.13 → 0.4.14
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 +2 -1
- package/bundle/meta/scaffold-manifest.json +7 -7
- package/bundle/scaffold/VERSION +1 -1
- package/bundle/scaffold/src/__tests__/portal-contracts.test.ts +7 -0
- package/bundle/scaffold/src/app/page.tsx +43 -6
- package/bundle/scaffold/src/components/portal-shell.tsx +23 -0
- package/bundle/scaffold/src/lib/portal-contracts.ts +33 -0
- package/bundle/toolchain/CLI_VERSION +1 -0
- package/bundle/toolchain/TELEMETRY_CONFIG.json +4 -0
- package/bundle/toolchain/VERSION +1 -1
- package/bundle/toolchain/bin/ensure-cloud-project-link.ts +34 -1
- package/bundle/toolchain/bin/showpane-config +108 -29
- package/bundle/toolchain/bin/showpane-telemetry-log +84 -0
- package/bundle/toolchain/bin/showpane-telemetry-sync +212 -0
- package/bundle/toolchain/bin/showpane-update-check +130 -0
- package/bundle/toolchain/skills/SKILL.md.tmpl +13 -0
- package/bundle/toolchain/skills/VERSION +1 -1
- package/bundle/toolchain/skills/portal-analytics/SKILL.md +59 -34
- package/bundle/toolchain/skills/portal-analytics/SKILL.md.tmpl +192 -0
- package/bundle/toolchain/skills/portal-create/SKILL.md +59 -58
- package/bundle/toolchain/skills/portal-create/SKILL.md.tmpl +264 -0
- package/bundle/toolchain/skills/portal-credentials/SKILL.md +62 -42
- package/bundle/toolchain/skills/portal-credentials/SKILL.md.tmpl +198 -0
- package/bundle/toolchain/skills/portal-delete/SKILL.md +56 -31
- package/bundle/toolchain/skills/portal-delete/SKILL.md.tmpl +194 -0
- package/bundle/toolchain/skills/portal-deploy/SKILL.md +59 -46
- package/bundle/toolchain/skills/portal-deploy/SKILL.md.tmpl +452 -0
- package/bundle/toolchain/skills/portal-dev/SKILL.md +62 -41
- package/bundle/toolchain/skills/portal-dev/SKILL.md.tmpl +228 -0
- package/bundle/toolchain/skills/portal-list/SKILL.md +56 -32
- package/bundle/toolchain/skills/portal-list/SKILL.md.tmpl +181 -0
- package/bundle/toolchain/skills/portal-onboard/SKILL.md +310 -161
- package/bundle/toolchain/skills/portal-onboard/SKILL.md.tmpl +317 -0
- package/bundle/toolchain/skills/portal-preview/SKILL.md +58 -34
- package/bundle/toolchain/skills/portal-preview/SKILL.md.tmpl +171 -0
- package/bundle/toolchain/skills/portal-setup/SKILL.md +73 -56
- package/bundle/toolchain/skills/portal-setup/SKILL.md.tmpl +222 -0
- package/bundle/toolchain/skills/portal-share/SKILL.md +62 -37
- package/bundle/toolchain/skills/portal-share/SKILL.md.tmpl +162 -0
- package/bundle/toolchain/skills/portal-status/SKILL.md +57 -33
- package/bundle/toolchain/skills/portal-status/SKILL.md.tmpl +196 -0
- package/bundle/toolchain/skills/portal-update/SKILL.md +58 -41
- package/bundle/toolchain/skills/portal-update/SKILL.md.tmpl +269 -0
- package/bundle/toolchain/skills/portal-upgrade/SKILL.md +56 -31
- package/bundle/toolchain/skills/portal-upgrade/SKILL.md.tmpl +164 -0
- package/bundle/toolchain/skills/portal-verify/SKILL.md +72 -14
- package/bundle/toolchain/skills/portal-verify/SKILL.md.tmpl +224 -0
- package/bundle/toolchain/skills/shared/preamble.md +30 -126
- package/bundle/toolchain/skills/shared/runtime-principles.md +25 -0
- package/dist/index.js +51 -9
- package/package.json +5 -2
|
@@ -18,16 +18,20 @@ hooks:
|
|
|
18
18
|
|
|
19
19
|
## Preamble (run first)
|
|
20
20
|
|
|
21
|
+
Before doing anything else, execute this block in a Bash tool call:
|
|
22
|
+
|
|
21
23
|
```bash
|
|
22
|
-
|
|
23
|
-
|
|
24
|
+
SHOWPANE_HOME="$HOME/.showpane"
|
|
25
|
+
SHOWPANE_BIN="$SHOWPANE_HOME/bin"
|
|
26
|
+
CONFIG="$SHOWPANE_HOME/config.json"
|
|
24
27
|
if [ ! -f "$CONFIG" ]; then
|
|
25
28
|
echo "Showpane not configured. Run /portal setup first."
|
|
26
29
|
exit 1
|
|
27
30
|
fi
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
+
|
|
32
|
+
APP_PATH=$("$SHOWPANE_BIN/showpane-config" get app_path 2>/dev/null || python3 -c "import json; d=json.load(open('$CONFIG')); print(d.get('app_path',''))" 2>/dev/null)
|
|
33
|
+
DEPLOY_MODE=$("$SHOWPANE_BIN/showpane-config" get deploy_mode 2>/dev/null || python3 -c "import json; d=json.load(open('$CONFIG')); print(d.get('deploy_mode','local'))" 2>/dev/null || echo "local")
|
|
34
|
+
ORG_SLUG=$("$SHOWPANE_BIN/showpane-config" get orgSlug 2>/dev/null || python3 -c "import json; d=json.load(open('$CONFIG')); print(d.get('orgSlug','') or d.get('org_slug',''))" 2>/dev/null || true)
|
|
31
35
|
APP_PATH="${SHOWPANE_APP_PATH:-$APP_PATH}"
|
|
32
36
|
if [ -f "$APP_PATH/.env" ]; then set -a && source "$APP_PATH/.env" && set +a; fi
|
|
33
37
|
DATABASE_URL="${DATABASE_URL:-}"
|
|
@@ -35,20 +39,26 @@ if [ ! -d "$APP_PATH/node_modules/.prisma" ]; then
|
|
|
35
39
|
echo "App dependencies not installed. Run: cd $APP_PATH && npm install"
|
|
36
40
|
exit 1
|
|
37
41
|
fi
|
|
38
|
-
|
|
42
|
+
|
|
43
|
+
SKILL_DIR="${SHOWPANE_TOOLCHAIN_DIR:-$SHOWPANE_HOME/current}"
|
|
39
44
|
SKILL_VERSION=$(cat "$SKILL_DIR/VERSION" 2>/dev/null || echo "unknown")
|
|
40
|
-
|
|
41
|
-
|
|
45
|
+
_UPD=$("$SHOWPANE_BIN/showpane-update-check" 2>/dev/null || true)
|
|
46
|
+
[ -n "$_UPD" ] && echo "$_UPD" || true
|
|
47
|
+
mkdir -p "$SHOWPANE_HOME/sessions" "$SHOWPANE_HOME/analytics" "$SHOWPANE_HOME/checkpoints"
|
|
48
|
+
touch "$SHOWPANE_HOME/sessions/$PPID"
|
|
49
|
+
find "$SHOWPANE_HOME/sessions" -mmin +120 -type f -delete 2>/dev/null || true
|
|
50
|
+
TEL=$("$SHOWPANE_BIN/showpane-config" get telemetry 2>/dev/null || echo "anonymous")
|
|
51
|
+
TEL_PROMPTED=$([ -f "$SHOWPANE_HOME/.telemetry-prompted" ] && echo "yes" || echo "no")
|
|
52
|
+
_TEL_START=$(date +%s)
|
|
53
|
+
_SESSION_ID="${PPID:-0}-$(date +%s)"
|
|
54
|
+
|
|
55
|
+
LEARN_FILE="$SHOWPANE_HOME/learnings.jsonl"
|
|
42
56
|
[ -f "$LEARN_FILE" ] && echo "LEARNINGS: $(wc -l < "$LEARN_FILE" | tr -d ' ') loaded" || echo "LEARNINGS: 0"
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
_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)
|
|
57
|
+
if [ -f "$SHOWPANE_HOME/timeline.jsonl" ]; then
|
|
58
|
+
_RECENT=$(grep '"event":"completed"' "$SHOWPANE_HOME/timeline.jsonl" 2>/dev/null | tail -3 | grep -o '"skill":"[^"]*"' | sed 's/"skill":"//;s/"//' | tr '
|
|
59
|
+
' ',' | sed 's/,$//' || true)
|
|
47
60
|
[ -n "$_RECENT" ] && echo "RECENT_SKILLS: $_RECENT"
|
|
48
61
|
fi
|
|
49
|
-
|
|
50
|
-
# Search relevant learnings
|
|
51
|
-
LEARN_FILE="$HOME/.showpane/learnings.jsonl"
|
|
52
62
|
if [ -f "$LEARN_FILE" ]; then
|
|
53
63
|
_LEARN_COUNT=$(wc -l < "$LEARN_FILE" 2>/dev/null | tr -d ' ')
|
|
54
64
|
echo "LEARNINGS: $_LEARN_COUNT entries"
|
|
@@ -58,23 +68,42 @@ if [ -f "$LEARN_FILE" ]; then
|
|
|
58
68
|
fi
|
|
59
69
|
fi
|
|
60
70
|
|
|
61
|
-
|
|
62
|
-
SHOWPANE_TIMELINE="$HOME/.showpane/timeline.jsonl"
|
|
71
|
+
SHOWPANE_TIMELINE="$SHOWPANE_HOME/timeline.jsonl"
|
|
63
72
|
mkdir -p "$(dirname "$SHOWPANE_TIMELINE")"
|
|
64
73
|
echo '{"skill":"portal-credentials","event":"started","ts":"'$(date -u +%Y-%m-%dT%H:%M:%SZ)'"}' >> "$SHOWPANE_TIMELINE" 2>/dev/null
|
|
74
|
+
echo "SHOWPANE: v$SKILL_VERSION | MODE: $DEPLOY_MODE | APP: $APP_PATH"
|
|
75
|
+
echo "TELEMETRY: $TEL"
|
|
76
|
+
echo "TEL_PROMPTED: $TEL_PROMPTED"
|
|
65
77
|
```
|
|
66
78
|
|
|
67
|
-
If
|
|
79
|
+
If output shows `JUST_UPGRADED <from> <to>`, tell the user Showpane was just upgraded and continue.
|
|
80
|
+
|
|
81
|
+
If output shows `UPGRADE_AVAILABLE <old> <new>`, tell the user a newer Showpane toolchain is available and recommend `/portal upgrade`.
|
|
82
|
+
|
|
83
|
+
If `TEL_PROMPTED` is `no`, ask the user once about telemetry and then record the decision:
|
|
84
|
+
|
|
85
|
+
- anonymous — local analytics plus anonymous remote sync, with no stable device id
|
|
86
|
+
- off — local analytics only, no remote sync
|
|
87
|
+
|
|
88
|
+
After the user chooses, run:
|
|
89
|
+
```bash
|
|
90
|
+
"$SHOWPANE_BIN/showpane-config" set telemetry <anonymous|off>
|
|
91
|
+
touch "$SHOWPANE_HOME/.telemetry-prompted"
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
If `RECENT_SKILLS` is shown, suggest the likely next skill:
|
|
68
95
|
- After portal-create → suggest /portal-preview
|
|
69
|
-
- After portal-preview → suggest /portal-deploy
|
|
96
|
+
- After portal-preview → suggest /portal-deploy
|
|
70
97
|
- After portal-deploy → suggest /portal-status or /portal-verify
|
|
71
|
-
- After portal-setup → suggest /portal-create
|
|
72
|
-
- After portal-credentials → suggest /portal-
|
|
73
|
-
- After portal-update → suggest /portal-deploy
|
|
98
|
+
- After portal-setup → suggest /portal-onboard for a first run, or /portal-create for the fast path
|
|
99
|
+
- After portal-credentials → suggest /portal-deploy before external sharing
|
|
100
|
+
- After portal-update → suggest /portal-preview or /portal-deploy
|
|
101
|
+
|
|
102
|
+
If `RECENT_LEARNINGS` is shown, review them before proceeding. Apply them where relevant but do not mention them unless they materially affect the current task.
|
|
74
103
|
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
104
|
+
Read `skills/shared/runtime-principles.md` once near the start of the skill and apply the relevant product defaults.
|
|
105
|
+
|
|
106
|
+
If `skills/shared/platform-constraints.md` exists, read it once near the start of the skill and apply only the relevant limits.
|
|
78
107
|
|
|
79
108
|
## Steps
|
|
80
109
|
|
|
@@ -167,7 +196,8 @@ Provide guidance on how to share the credentials with the client:
|
|
|
167
196
|
> **How to share with your client:**
|
|
168
197
|
> - Send the username and password via a secure channel (encrypted email, Signal, etc.)
|
|
169
198
|
> - The login page is at `/client` — the client enters the portal slug as the "company" field, then the username and password
|
|
170
|
-
> - For
|
|
199
|
+
> - For external access, publish the portal first with `/portal deploy`
|
|
200
|
+
> - After publish, you can use `/portal share <slug>` if you want a direct hosted link instead of asking the client to log in
|
|
171
201
|
|
|
172
202
|
Recommend against sharing credentials via:
|
|
173
203
|
- Unencrypted email (can be intercepted)
|
|
@@ -206,8 +236,8 @@ Credentials created for: acme-health
|
|
|
206
236
|
|
|
207
237
|
Next steps:
|
|
208
238
|
1. Send credentials to the client via a secure channel
|
|
209
|
-
2.
|
|
210
|
-
3.
|
|
239
|
+
2. Deploy if not already live: /portal deploy
|
|
240
|
+
3. Optional hosted direct link after publish: /portal share <slug>
|
|
211
241
|
```
|
|
212
242
|
|
|
213
243
|
### Step 9: Record credential event
|
|
@@ -254,21 +284,11 @@ Even in bulk mode, show the security warning once after all credentials are disp
|
|
|
254
284
|
|
|
255
285
|
## Completion
|
|
256
286
|
|
|
257
|
-
As a final step, log skill completion:
|
|
287
|
+
As a final step, log skill completion and telemetry:
|
|
258
288
|
|
|
259
289
|
```bash
|
|
260
290
|
echo '{"skill":"portal-credentials","event":"completed","ts":"'$(date -u +%Y-%m-%dT%H:%M:%SZ)'"}' >> "$HOME/.showpane/timeline.jsonl" 2>/dev/null
|
|
291
|
+
_TEL_END=$(date +%s)
|
|
292
|
+
_TEL_DUR=$(( _TEL_END - ${_TEL_START:-_TEL_END} ))
|
|
293
|
+
"$HOME/.showpane/bin/showpane-telemetry-log" --skill "portal-credentials" --duration "$_TEL_DUR" --outcome success --session-id "${_SESSION_ID:-}" 2>/dev/null || true
|
|
261
294
|
```
|
|
262
|
-
|
|
263
|
-
## Conventions
|
|
264
|
-
|
|
265
|
-
- Always run the bin script rather than manipulating the database directly — never generate passwords, hash them, or write to the database from the skill
|
|
266
|
-
- Show credentials in an ASCII box for clear visibility in the terminal
|
|
267
|
-
- Warn about credential impermanence immediately after displaying them
|
|
268
|
-
- If the portal has no DB record, direct the user to `/portal create` first
|
|
269
|
-
- Never store, log, or record the plaintext password anywhere — not in learnings, not in telemetry, not in config
|
|
270
|
-
- Confirm before rotating existing credentials (default to "no") since rotation invalidates sessions
|
|
271
|
-
- The username is always the same as the portal slug — this is by design for simplicity
|
|
272
|
-
- If the user asks "what is the password for X portal", explain that passwords are one-way hashed and cannot be retrieved — offer to rotate to a new one instead
|
|
273
|
-
- After creating credentials, always suggest the next step: either share with the client or deploy if not already live
|
|
274
|
-
- If the app is not deployed yet, credentials still work — they are stored in the database regardless of whether the app is running in production
|
|
@@ -0,0 +1,198 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: portal-credentials
|
|
3
|
+
description: |
|
|
4
|
+
Create or rotate login credentials for a client portal. Generates username and password, hashes and stores in DB.
|
|
5
|
+
Trigger phrases: "portal credentials", "create login", "rotate password", "reset credentials", "portal password". (showpane)
|
|
6
|
+
allowed-tools: [Bash, Read, Write, Edit]
|
|
7
|
+
hooks:
|
|
8
|
+
PreToolUse:
|
|
9
|
+
- matcher: "Bash"
|
|
10
|
+
hooks:
|
|
11
|
+
- type: command
|
|
12
|
+
command: "bash ${CLAUDE_SKILL_DIR}/../showpane-shared/bin/check-portal-guard.sh"
|
|
13
|
+
- matcher: "Edit"
|
|
14
|
+
hooks:
|
|
15
|
+
- type: command
|
|
16
|
+
command: "bash ${CLAUDE_SKILL_DIR}/../showpane-shared/bin/check-portal-guard.sh"
|
|
17
|
+
---
|
|
18
|
+
|
|
19
|
+
{{PREAMBLE}}
|
|
20
|
+
|
|
21
|
+
## Steps
|
|
22
|
+
|
|
23
|
+
### Step 1: Identify the portal
|
|
24
|
+
|
|
25
|
+
If the user provided a slug (e.g., `/portal credentials acme-health`), use it. Otherwise, list available portals to help the user choose:
|
|
26
|
+
|
|
27
|
+
```bash
|
|
28
|
+
cd "$APP_PATH" && NODE_PATH="$APP_PATH/node_modules" npx tsx --tsconfig "$APP_PATH/tsconfig.json" "$SKILL_DIR/bin/list-portals.ts" --org-id <org_id>
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
Present the list and ask which portal needs credentials. If there is only one portal, confirm it rather than asking.
|
|
32
|
+
|
|
33
|
+
Verify the portal exists by checking the database. The `create-portal.ts` script should have been run during `/portal create` to register the portal. If no DB record exists, inform the user:
|
|
34
|
+
|
|
35
|
+
> "No portal record found for '<slug>'. Run `/portal create <slug>` first to register it."
|
|
36
|
+
|
|
37
|
+
Also check the portal's current credential status from the list output. If credentials already exist, inform the user before proceeding:
|
|
38
|
+
|
|
39
|
+
> "Portal '<slug>' already has credentials (username: <username>). Running this will rotate to a new password and invalidate all existing sessions. Continue? (y/N)"
|
|
40
|
+
|
|
41
|
+
This confirmation is important because credential rotation has an immediate impact on any clients currently logged in. Default to "no" to prevent accidental rotation.
|
|
42
|
+
|
|
43
|
+
### Step 2: Run the rotate-credentials script
|
|
44
|
+
|
|
45
|
+
This script handles both initial credential creation and rotation:
|
|
46
|
+
|
|
47
|
+
```bash
|
|
48
|
+
cd "$APP_PATH" && NODE_PATH="$APP_PATH/node_modules" npx tsx --tsconfig "$APP_PATH/tsconfig.json" "$SKILL_DIR/bin/rotate-credentials.ts" --slug <slug> --org-id <org_id>
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
The script returns JSON on stdout:
|
|
52
|
+
|
|
53
|
+
**For initial creation:**
|
|
54
|
+
```json
|
|
55
|
+
{"ok": true, "username": "acme-health", "password": "xK9mP2vL8nQr", "rotated": false}
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
**For rotation (credentials already existed):**
|
|
59
|
+
```json
|
|
60
|
+
{"ok": true, "username": "acme-health", "password": "bT4wN7jF3hYs", "rotated": true}
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
**On error:**
|
|
64
|
+
```json
|
|
65
|
+
{"error": "portal_not_found", "message": "No portal with slug 'acme-health' in org 'demo'"}
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
### Step 3: Handle script errors
|
|
69
|
+
|
|
70
|
+
If the script returns an error, handle it based on the error type:
|
|
71
|
+
|
|
72
|
+
- `portal_not_found` — The portal slug doesn't exist in the database for this org. Suggest running `/portal create <slug>` first.
|
|
73
|
+
- `database_error` — Connection or query failure. Check DATABASE_URL and re-apply the local schema with `cd $APP_PATH && npm run prisma:db-push`.
|
|
74
|
+
- `auth_secret_missing` — The AUTH_SECRET environment variable is not set in `$APP_PATH/.env`. The rotate-credentials script needs this to function. Suggest adding `AUTH_SECRET=<random-string>` to the .env file.
|
|
75
|
+
|
|
76
|
+
If the script exits with a non-zero code but produces no JSON output, check stderr for Prisma or Node.js errors and relay them to the user.
|
|
77
|
+
|
|
78
|
+
### Step 4: Display credentials
|
|
79
|
+
|
|
80
|
+
Present the credentials clearly and prominently. Use an ASCII box for visibility:
|
|
81
|
+
|
|
82
|
+
```
|
|
83
|
+
┌─────────────────────────────────────────┐
|
|
84
|
+
│ Portal credentials for: acme-health │
|
|
85
|
+
│ │
|
|
86
|
+
│ Username: acme-health │
|
|
87
|
+
│ Password: xK9mP2vL8nQr │
|
|
88
|
+
│ │
|
|
89
|
+
│ Login URL: /client │
|
|
90
|
+
└─────────────────────────────────────────┘
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
The ASCII box ensures the credentials stand out clearly in terminal output. Do not use markdown formatting (bold, code blocks) as these may not render in all terminal environments.
|
|
94
|
+
|
|
95
|
+
### Step 5: Security warning
|
|
96
|
+
|
|
97
|
+
Immediately after displaying credentials, show this warning:
|
|
98
|
+
|
|
99
|
+
> **Save these credentials now.** They will not be shown again. The password is hashed in the database and cannot be retrieved. If lost, run `/portal credentials <slug>` again to rotate to a new password.
|
|
100
|
+
|
|
101
|
+
If this was a rotation (`"rotated": true`), add:
|
|
102
|
+
|
|
103
|
+
> **Credential rotation complete.** All existing sessions for this portal have been invalidated immediately. Anyone currently logged in will need to re-authenticate with the new password.
|
|
104
|
+
|
|
105
|
+
### Step 6: Suggest sharing method
|
|
106
|
+
|
|
107
|
+
Provide guidance on how to share the credentials with the client:
|
|
108
|
+
|
|
109
|
+
> **How to share with your client:**
|
|
110
|
+
> - Send the username and password via a secure channel (encrypted email, Signal, etc.)
|
|
111
|
+
> - The login page is at `/client` — the client enters the portal slug as the "company" field, then the username and password
|
|
112
|
+
> - For external access, publish the portal first with `/portal deploy`
|
|
113
|
+
> - After publish, you can use `/portal share <slug>` if you want a direct hosted link instead of asking the client to log in
|
|
114
|
+
|
|
115
|
+
Recommend against sharing credentials via:
|
|
116
|
+
- Unencrypted email (can be intercepted)
|
|
117
|
+
- Slack or Teams messages (persistent and searchable by admins)
|
|
118
|
+
- Shared documents or spreadsheets
|
|
119
|
+
|
|
120
|
+
Good sharing channels:
|
|
121
|
+
- Signal or WhatsApp (encrypted, ephemeral)
|
|
122
|
+
- A phone call (verbal)
|
|
123
|
+
- An encrypted email service
|
|
124
|
+
- A password manager's secure sharing feature
|
|
125
|
+
|
|
126
|
+
### Step 7: Verify login works (if dev server running)
|
|
127
|
+
|
|
128
|
+
If the dev server is running, suggest testing the credentials:
|
|
129
|
+
|
|
130
|
+
```bash
|
|
131
|
+
lsof -i :3000 -sTCP:LISTEN -t 2>/dev/null
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
If running, inform the user:
|
|
135
|
+
|
|
136
|
+
> "Test the login at http://localhost:3000/client — enter slug '<slug>', username '<username>', and the password shown above."
|
|
137
|
+
|
|
138
|
+
This step is optional but helps catch configuration issues (like AUTH_SECRET not being set) before sharing credentials with a client.
|
|
139
|
+
|
|
140
|
+
### Step 8: Summary
|
|
141
|
+
|
|
142
|
+
Print a brief summary:
|
|
143
|
+
|
|
144
|
+
```
|
|
145
|
+
Credentials created for: acme-health
|
|
146
|
+
Action: new credentials (or: rotated — old sessions invalidated)
|
|
147
|
+
Username: acme-health
|
|
148
|
+
Login: /client
|
|
149
|
+
|
|
150
|
+
Next steps:
|
|
151
|
+
1. Send credentials to the client via a secure channel
|
|
152
|
+
2. Deploy if not already live: /portal deploy
|
|
153
|
+
3. Optional hosted direct link after publish: /portal share <slug>
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
### Step 9: Record credential event
|
|
157
|
+
|
|
158
|
+
Record that credentials were created or rotated (but NEVER record the actual credentials):
|
|
159
|
+
|
|
160
|
+
```bash
|
|
161
|
+
echo '{"skill":"portal-credentials","key":"credentials-created","insight":"Created credentials for <slug>. Rotated: <true|false>.","confidence":10,"ts":"'$(date -u +%Y-%m-%dT%H:%M:%SZ)'"}' >> "$HOME/.showpane/learnings.jsonl"
|
|
162
|
+
```
|
|
163
|
+
|
|
164
|
+
## Security Conventions
|
|
165
|
+
|
|
166
|
+
- **Credentials are shown exactly once.** Never log them, never write them to a file, never include them in learnings or telemetry.
|
|
167
|
+
- **Rotation is immediate.** When credentials are rotated, the `credentialVersion` is bumped in the database. All existing session tokens for that portal are invalidated instantly because they reference the old version.
|
|
168
|
+
- **Passwords are generated server-side** by the `rotate-credentials.ts` script using cryptographically secure random bytes. Do not generate passwords in the skill — always use the script.
|
|
169
|
+
- **Usernames are derived from the slug.** The username is typically the same as the portal slug (e.g., slug `acme-health` gets username `acme-health`).
|
|
170
|
+
- **No password in config or learnings.** The `~/.showpane/config.json` and `~/.showpane/learnings.jsonl` files must never contain passwords or password hashes.
|
|
171
|
+
- **AUTH_SECRET stays in .env.** The secret used for token signing lives in `$APP_PATH/.env` and is read at runtime by the app. It is never copied to config.json.
|
|
172
|
+
|
|
173
|
+
## Error Handling
|
|
174
|
+
|
|
175
|
+
- **Script not found**: If `$SKILL_DIR/bin/rotate-credentials.ts` does not exist, the skill pack may not be fully installed. Suggest running `/portal upgrade` or checking the Showpane installation.
|
|
176
|
+
- **Prisma connection error**: DATABASE_URL may be wrong or the database may be down. Check the .env file and database status.
|
|
177
|
+
- **Permission denied on .env**: The preamble sources `$APP_PATH/.env`. If this file is not readable, the DATABASE_URL will not be set. Check file permissions.
|
|
178
|
+
- **Script timeout**: If the script takes more than 30 seconds, something is wrong. The most common cause is a database connection timeout — check network connectivity to the database host.
|
|
179
|
+
|
|
180
|
+
## Bulk credential creation
|
|
181
|
+
|
|
182
|
+
If the user asks to create credentials for multiple portals at once, run the rotate-credentials script for each portal sequentially. Display all credential sets together in a single output block:
|
|
183
|
+
|
|
184
|
+
```
|
|
185
|
+
┌─────────────────────────────────────────┐
|
|
186
|
+
│ 1. acme-health │
|
|
187
|
+
│ Username: acme-health │
|
|
188
|
+
│ Password: xK9mP2vL8nQr │
|
|
189
|
+
│ │
|
|
190
|
+
│ 2. beta-corp │
|
|
191
|
+
│ Username: beta-corp │
|
|
192
|
+
│ Password: mT7kR4wQ9nFs │
|
|
193
|
+
└─────────────────────────────────────────┘
|
|
194
|
+
```
|
|
195
|
+
|
|
196
|
+
Even in bulk mode, show the security warning once after all credentials are displayed.
|
|
197
|
+
|
|
198
|
+
{{COMPLETION}}
|
|
@@ -21,14 +21,17 @@ hooks:
|
|
|
21
21
|
Before doing anything else, execute this block in a Bash tool call:
|
|
22
22
|
|
|
23
23
|
```bash
|
|
24
|
-
|
|
24
|
+
SHOWPANE_HOME="$HOME/.showpane"
|
|
25
|
+
SHOWPANE_BIN="$SHOWPANE_HOME/bin"
|
|
26
|
+
CONFIG="$SHOWPANE_HOME/config.json"
|
|
25
27
|
if [ ! -f "$CONFIG" ]; then
|
|
26
28
|
echo "Showpane not configured. Run /portal setup first."
|
|
27
29
|
exit 1
|
|
28
30
|
fi
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
31
|
+
|
|
32
|
+
APP_PATH=$("$SHOWPANE_BIN/showpane-config" get app_path 2>/dev/null || python3 -c "import json; d=json.load(open('$CONFIG')); print(d.get('app_path',''))" 2>/dev/null)
|
|
33
|
+
DEPLOY_MODE=$("$SHOWPANE_BIN/showpane-config" get deploy_mode 2>/dev/null || python3 -c "import json; d=json.load(open('$CONFIG')); print(d.get('deploy_mode','local'))" 2>/dev/null || echo "local")
|
|
34
|
+
ORG_SLUG=$("$SHOWPANE_BIN/showpane-config" get orgSlug 2>/dev/null || python3 -c "import json; d=json.load(open('$CONFIG')); print(d.get('orgSlug','') or d.get('org_slug',''))" 2>/dev/null || true)
|
|
32
35
|
APP_PATH="${SHOWPANE_APP_PATH:-$APP_PATH}"
|
|
33
36
|
if [ -f "$APP_PATH/.env" ]; then set -a && source "$APP_PATH/.env" && set +a; fi
|
|
34
37
|
DATABASE_URL="${DATABASE_URL:-}"
|
|
@@ -36,20 +39,26 @@ if [ ! -d "$APP_PATH/node_modules/.prisma" ]; then
|
|
|
36
39
|
echo "App dependencies not installed. Run: cd $APP_PATH && npm install"
|
|
37
40
|
exit 1
|
|
38
41
|
fi
|
|
39
|
-
|
|
42
|
+
|
|
43
|
+
SKILL_DIR="${SHOWPANE_TOOLCHAIN_DIR:-$SHOWPANE_HOME/current}"
|
|
40
44
|
SKILL_VERSION=$(cat "$SKILL_DIR/VERSION" 2>/dev/null || echo "unknown")
|
|
41
|
-
|
|
42
|
-
|
|
45
|
+
_UPD=$("$SHOWPANE_BIN/showpane-update-check" 2>/dev/null || true)
|
|
46
|
+
[ -n "$_UPD" ] && echo "$_UPD" || true
|
|
47
|
+
mkdir -p "$SHOWPANE_HOME/sessions" "$SHOWPANE_HOME/analytics" "$SHOWPANE_HOME/checkpoints"
|
|
48
|
+
touch "$SHOWPANE_HOME/sessions/$PPID"
|
|
49
|
+
find "$SHOWPANE_HOME/sessions" -mmin +120 -type f -delete 2>/dev/null || true
|
|
50
|
+
TEL=$("$SHOWPANE_BIN/showpane-config" get telemetry 2>/dev/null || echo "anonymous")
|
|
51
|
+
TEL_PROMPTED=$([ -f "$SHOWPANE_HOME/.telemetry-prompted" ] && echo "yes" || echo "no")
|
|
52
|
+
_TEL_START=$(date +%s)
|
|
53
|
+
_SESSION_ID="${PPID:-0}-$(date +%s)"
|
|
54
|
+
|
|
55
|
+
LEARN_FILE="$SHOWPANE_HOME/learnings.jsonl"
|
|
43
56
|
[ -f "$LEARN_FILE" ] && echo "LEARNINGS: $(wc -l < "$LEARN_FILE" | tr -d ' ') loaded" || echo "LEARNINGS: 0"
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
_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)
|
|
57
|
+
if [ -f "$SHOWPANE_HOME/timeline.jsonl" ]; then
|
|
58
|
+
_RECENT=$(grep '"event":"completed"' "$SHOWPANE_HOME/timeline.jsonl" 2>/dev/null | tail -3 | grep -o '"skill":"[^"]*"' | sed 's/"skill":"//;s/"//' | tr '
|
|
59
|
+
' ',' | sed 's/,$//' || true)
|
|
48
60
|
[ -n "$_RECENT" ] && echo "RECENT_SKILLS: $_RECENT"
|
|
49
61
|
fi
|
|
50
|
-
|
|
51
|
-
# Search relevant learnings
|
|
52
|
-
LEARN_FILE="$HOME/.showpane/learnings.jsonl"
|
|
53
62
|
if [ -f "$LEARN_FILE" ]; then
|
|
54
63
|
_LEARN_COUNT=$(wc -l < "$LEARN_FILE" 2>/dev/null | tr -d ' ')
|
|
55
64
|
echo "LEARNINGS: $_LEARN_COUNT entries"
|
|
@@ -59,23 +68,42 @@ if [ -f "$LEARN_FILE" ]; then
|
|
|
59
68
|
fi
|
|
60
69
|
fi
|
|
61
70
|
|
|
62
|
-
|
|
63
|
-
SHOWPANE_TIMELINE="$HOME/.showpane/timeline.jsonl"
|
|
71
|
+
SHOWPANE_TIMELINE="$SHOWPANE_HOME/timeline.jsonl"
|
|
64
72
|
mkdir -p "$(dirname "$SHOWPANE_TIMELINE")"
|
|
65
73
|
echo '{"skill":"portal-delete","event":"started","ts":"'$(date -u +%Y-%m-%dT%H:%M:%SZ)'"}' >> "$SHOWPANE_TIMELINE" 2>/dev/null
|
|
74
|
+
echo "SHOWPANE: v$SKILL_VERSION | MODE: $DEPLOY_MODE | APP: $APP_PATH"
|
|
75
|
+
echo "TELEMETRY: $TEL"
|
|
76
|
+
echo "TEL_PROMPTED: $TEL_PROMPTED"
|
|
66
77
|
```
|
|
67
78
|
|
|
68
|
-
If
|
|
79
|
+
If output shows `JUST_UPGRADED <from> <to>`, tell the user Showpane was just upgraded and continue.
|
|
80
|
+
|
|
81
|
+
If output shows `UPGRADE_AVAILABLE <old> <new>`, tell the user a newer Showpane toolchain is available and recommend `/portal upgrade`.
|
|
82
|
+
|
|
83
|
+
If `TEL_PROMPTED` is `no`, ask the user once about telemetry and then record the decision:
|
|
84
|
+
|
|
85
|
+
- anonymous — local analytics plus anonymous remote sync, with no stable device id
|
|
86
|
+
- off — local analytics only, no remote sync
|
|
87
|
+
|
|
88
|
+
After the user chooses, run:
|
|
89
|
+
```bash
|
|
90
|
+
"$SHOWPANE_BIN/showpane-config" set telemetry <anonymous|off>
|
|
91
|
+
touch "$SHOWPANE_HOME/.telemetry-prompted"
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
If `RECENT_SKILLS` is shown, suggest the likely next skill:
|
|
69
95
|
- After portal-create → suggest /portal-preview
|
|
70
|
-
- After portal-preview → suggest /portal-deploy
|
|
96
|
+
- After portal-preview → suggest /portal-deploy
|
|
71
97
|
- After portal-deploy → suggest /portal-status or /portal-verify
|
|
72
|
-
- After portal-setup → suggest /portal-create
|
|
73
|
-
- After portal-credentials → suggest /portal-
|
|
74
|
-
- After portal-update → suggest /portal-deploy
|
|
98
|
+
- After portal-setup → suggest /portal-onboard for a first run, or /portal-create for the fast path
|
|
99
|
+
- After portal-credentials → suggest /portal-deploy before external sharing
|
|
100
|
+
- After portal-update → suggest /portal-preview or /portal-deploy
|
|
75
101
|
|
|
76
|
-
If RECENT_LEARNINGS is shown, review them before proceeding.
|
|
77
|
-
|
|
78
|
-
|
|
102
|
+
If `RECENT_LEARNINGS` is shown, review them before proceeding. Apply them where relevant but do not mention them unless they materially affect the current task.
|
|
103
|
+
|
|
104
|
+
Read `skills/shared/runtime-principles.md` once near the start of the skill and apply the relevant product defaults.
|
|
105
|
+
|
|
106
|
+
If `skills/shared/platform-constraints.md` exists, read it once near the start of the skill and apply only the relevant limits.
|
|
79
107
|
|
|
80
108
|
## Safety Guard
|
|
81
109
|
|
|
@@ -252,14 +280,11 @@ If the user wants to immediately revoke all access AND deactivate, the sequence
|
|
|
252
280
|
|
|
253
281
|
## Completion
|
|
254
282
|
|
|
255
|
-
As a final step, log skill completion:
|
|
283
|
+
As a final step, log skill completion and telemetry:
|
|
256
284
|
|
|
257
285
|
```bash
|
|
258
286
|
echo '{"skill":"portal-delete","event":"completed","ts":"'$(date -u +%Y-%m-%dT%H:%M:%SZ)'"}' >> "$HOME/.showpane/timeline.jsonl" 2>/dev/null
|
|
287
|
+
_TEL_END=$(date +%s)
|
|
288
|
+
_TEL_DUR=$(( _TEL_END - ${_TEL_START:-_TEL_END} ))
|
|
289
|
+
"$HOME/.showpane/bin/showpane-telemetry-log" --skill "portal-delete" --duration "$_TEL_DUR" --outcome success --session-id "${_SESSION_ID:-}" 2>/dev/null || true
|
|
259
290
|
```
|
|
260
|
-
|
|
261
|
-
## Related Skills
|
|
262
|
-
|
|
263
|
-
- `/portal list` -- see all portals before deciding which to deactivate
|
|
264
|
-
- `/portal credentials` -- rotate credentials to immediately revoke all access (stronger than deactivate)
|
|
265
|
-
- `/portal status` -- check portal health to identify candidates for deactivation
|