pi-spotify-widget 0.1.15 → 0.1.19
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/CHANGELOG.md +29 -0
- package/README.md +2 -2
- package/docs/examples.md +4 -0
- package/docs/release.md +4 -2
- package/docs/verification/pi-sdk-smoke.json +284 -0
- package/lib/refresh-schedule.ts +6 -1
- package/package.json +66 -64
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,34 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## [0.1.19] - 2026-10-19
|
|
4
|
+
|
|
5
|
+
### Changed
|
|
6
|
+
|
|
7
|
+
- Add a copyable PowerShell Client ID setup example to the packaged examples guide.
|
|
8
|
+
|
|
9
|
+
## [0.1.18] - 2026-10-16
|
|
10
|
+
|
|
11
|
+
### Changed
|
|
12
|
+
|
|
13
|
+
- Verify compatibility with Pi SDK 1.2.0 and pin the SDK dev/test dependencies.
|
|
14
|
+
|
|
15
|
+
## [0.1.17] - 2026-10-12
|
|
16
|
+
|
|
17
|
+
### Changed
|
|
18
|
+
|
|
19
|
+
- Verify compatibility with Pi SDK 1.1.0 and pin the SDK dev/test dependencies.
|
|
20
|
+
|
|
21
|
+
### Fixed
|
|
22
|
+
|
|
23
|
+
- Keep a stable untagged version eligible for manual release after follow-up commits.
|
|
24
|
+
- Resume only missing release stages when an existing version tag is retried.
|
|
25
|
+
|
|
26
|
+
## [0.1.16] - 2026-09-30
|
|
27
|
+
|
|
28
|
+
### Changed
|
|
29
|
+
|
|
30
|
+
- Update `@earendil-works/pi-*` dependencies to `0.99.1`.
|
|
31
|
+
|
|
3
32
|
## [0.1.15] - 2026-09-27
|
|
4
33
|
|
|
5
34
|
- chore: periodic patch bump after 7+ days without npm publish
|
package/README.md
CHANGED
|
@@ -38,7 +38,7 @@ pi install npm:pi-spotify-widget
|
|
|
38
38
|
Pin a specific version when you want reproducible installs:
|
|
39
39
|
|
|
40
40
|
```bash
|
|
41
|
-
pi install npm:pi-spotify-widget@0.1.
|
|
41
|
+
pi install npm:pi-spotify-widget@0.1.19
|
|
42
42
|
```
|
|
43
43
|
|
|
44
44
|
Install into the current project instead of your user Pi settings:
|
|
@@ -155,7 +155,7 @@ npm version patch --no-git-tag-version
|
|
|
155
155
|
git push
|
|
156
156
|
```
|
|
157
157
|
|
|
158
|
-
|
|
158
|
+
Merging or pushing a version bump runs CI only; it does not release or publish. After separate release authorization, manually dispatch `.github/workflows/auto-release.yml` on `main` to create the tag and GitHub Release, then hand off to `.github/workflows/publish.yml`.
|
|
159
159
|
|
|
160
160
|
See [`docs/release.md`](docs/release.md) for setup details.
|
|
161
161
|
|
package/docs/examples.md
CHANGED
|
@@ -41,6 +41,10 @@ Redirect URI: `http://127.0.0.1:8888/callback`
|
|
|
41
41
|
|
|
42
42
|
Optional environment variable before starting Pi:
|
|
43
43
|
|
|
44
|
+
```powershell
|
|
45
|
+
$env:PI_SPOTIFY_CLIENT_ID = "your_client_id_here"
|
|
46
|
+
```
|
|
47
|
+
|
|
44
48
|
```bash
|
|
45
49
|
export PI_SPOTIFY_CLIENT_ID="your_client_id_here"
|
|
46
50
|
```
|
package/docs/release.md
CHANGED
|
@@ -19,13 +19,15 @@ npm version patch --no-git-tag-version
|
|
|
19
19
|
git push
|
|
20
20
|
```
|
|
21
21
|
|
|
22
|
-
|
|
22
|
+
Code pushes and merges, including version bumps on `main`, run CI only. They do not create tags/releases or publish npm packages.
|
|
23
|
+
|
|
24
|
+
After separate release authorization, manually dispatch `.github/workflows/auto-release.yml` on `main`. It validates the stable version and inspects the matching tag, GitHub Release, and publish-workflow runs. It creates only missing stages: an existing tag is retained, an existing release is not recreated, and publish is dispatched only when no successful or active run already exists for that tag. A failed publish run can be retried without recreating the tag or release. It does not require the version to differ from the previous commit, so an untagged version remains releasable after later documentation-only commits. Other branches are rejected by the workflow gate. Do not perform this protected release action as part of routine compatibility verification.
|
|
23
25
|
|
|
24
26
|
`publish.yml` also listens for `release.published` and manual `v*.*.*` tag pushes, but the explicit dispatch from `auto-release.yml` remains the primary path because `GITHUB_TOKEN` tag pushes do not reliably fan out into a second workflow on their own.
|
|
25
27
|
|
|
26
28
|
## Workflow guardrail
|
|
27
29
|
|
|
28
|
-
Tags or releases created by `GITHUB_TOKEN` do not reliably fan out into another workflow through normal `push.tags` or `release.published` triggers.
|
|
30
|
+
Tags or releases created by `GITHUB_TOKEN` do not reliably fan out into another workflow through normal `push.tags` or `release.published` triggers. The manually dispatched `auto-release.yml` explicitly dispatches `publish.yml` after creating the tag/release.
|
|
29
31
|
|
|
30
32
|
## GitHub Actions requirements
|
|
31
33
|
|
|
@@ -0,0 +1,284 @@
|
|
|
1
|
+
{
|
|
2
|
+
"schema_version": 1,
|
|
3
|
+
"verification_contract": "pi-sdk-behavior-v2",
|
|
4
|
+
"sdk_versions": {
|
|
5
|
+
"@earendil-works/pi-agent-core": "1.2.0",
|
|
6
|
+
"@earendil-works/pi-ai": "1.2.0",
|
|
7
|
+
"@earendil-works/pi-coding-agent": "1.2.0",
|
|
8
|
+
"@earendil-works/pi-tui": "1.2.0"
|
|
9
|
+
},
|
|
10
|
+
"platform": "win32",
|
|
11
|
+
"model_inference": "none; actual Pi host handles explicit extension commands offline",
|
|
12
|
+
"unhandled_errors": 0,
|
|
13
|
+
"extension_load_errors": 0,
|
|
14
|
+
"cases": [
|
|
15
|
+
{
|
|
16
|
+
"name": "extension_load",
|
|
17
|
+
"command": "npm run smoke:pi -- --case extension_load",
|
|
18
|
+
"exit_code": 0,
|
|
19
|
+
"passed": true,
|
|
20
|
+
"evidence": {
|
|
21
|
+
"note": "Captured actual Pi host/native-shell stdout and stderr. The Pi SDK, loader, extension lifecycle, command handler, RPC protocol, and widget implementation are not mocked; no model inference or Spotify OAuth/API request is used.",
|
|
22
|
+
"runs": [
|
|
23
|
+
{
|
|
24
|
+
"exit_code": 0,
|
|
25
|
+
"signal": null,
|
|
26
|
+
"error": null,
|
|
27
|
+
"stdout": "{\"type\":\"extension_ui_request\",\"id\":\"eef3544a-4f4a-4e2d-8f1c-e1fc18b9b178\",\"method\":\"setWidget\",\"widgetKey\":\"pi-spotify-widget\",\"widgetLines\":[\"\\u001b[39m\\u001b[2m♫ Run /spotify:login\\u001b[22;39m\"]}\n{\"type\":\"extension_ui_request\",\"id\":\"0fc4c63f-8f8f-47f5-92de-bea6ef3eea80\",\"method\":\"notify\",\"message\":\"### Spotify playback controls\",\"notifyType\":\"info\"}\n{\"type\":\"message_start\",\"message\":{\"role\":\"custom\",\"customType\":\"spotify-widget\",\"content\":\"### Spotify playback controls\\n\\nユーザーが曲操作や再生/停止を依頼したら、下表から意図に最も近い **Pi コマンド** を1つ選んで実行してください。\\n推測が曖昧なときは `/spotify:help` の一覧を参照するか、ユーザーに確認してください。\\n\\n| Command | Action | Example user phrases |\\n|---|---|---|\\n| /spotify:prev | 前の曲に戻る | 前の曲 · 前へ · 戻して · previous track |\\n| /spotify:next | 次の曲に進む | 次の曲 · 次へ · スキップ · skip |\\n| /spotify:pause | 再生を一時停止する(停止) | 止めて · 停止 · ストップ · 一時停止 |\\n| /spotify:play | 再生を再開する | 再生 · 再開 · play · resume |\\n\\nDiscovery / share:\\n| /spotify:share | Now Playing を X に投稿(API 設定時)または共有画面を開く | シェア · Xに投稿 · post to X · tweet |\\n| /spotify:play-uri | Spotify 曲 URL / URI を指定して再生 | この曲再生 · play this link · play uri |\\n\\nOther useful commands:\\n- `/spotify:refresh` — Now Playing を即時更新\\n- `/spotify:status` — 認証状態(token 値は出さない)\\n\\nNote: playback control requires `user-modify-playback-state` scope. If controls fail after an upgrade, run `/spotify:login` again.\",\"display\":true,\"details\":{\"kind\":\"help\"},\"timestamp\":1792110351657}}\n{\"type\":\"message_end\",\"message\":{\"role\":\"custom\",\"customType\":\"spotify-widget\",\"content\":\"### Spotify playback controls\\n\\nユーザーが曲操作や再生/停止を依頼したら、下表から意図に最も近い **Pi コマンド** を1つ選んで実行してください。\\n推測が曖昧なときは `/spotify:help` の一覧を参照するか、ユーザーに確認してください。\\n\\n| Command | Action | Example user phrases |\\n|---|---|---|\\n| /spotify:prev | 前の曲に戻る | 前の曲 · 前へ · 戻して · previous track |\\n| /spotify:next | 次の曲に進む | 次の曲 · 次へ · スキップ · skip |\\n| /spotify:pause | 再生を一時停止する(停止) | 止めて · 停止 · ストップ · 一時停止 |\\n| /spotify:play | 再生を再開する | 再生 · 再開 · play · resume |\\n\\nDiscovery / share:\\n| /spotify:share | Now Playing を X に投稿(API 設定時)または共有画面を開く | シェア · Xに投稿 · post to X · tweet |\\n| /spotify:play-uri | Spotify 曲 URL / URI を指定して再生 | この曲再生 · play this link · play uri |\\n\\nOther useful commands:\\n- `/spotify:refresh` — Now Playing を即時更新\\n- `/spotify:status` — 認証状態(token 値は出さない)\\n\\nNote: playback control requires `user-modify-playback-state` scope. If controls fail after an upgrade, run `/spotify:login` again.\",\"display\":true,\"details\":{\"kind\":\"help\"},\"timestamp\":1792110351657}}\n{\"type\":\"response\",\"command\":\"prompt\",\"success\":true,\"data\":{\"disposition\":\"handled\"}}\n",
|
|
28
|
+
"stderr": ""
|
|
29
|
+
}
|
|
30
|
+
]
|
|
31
|
+
},
|
|
32
|
+
"assertions": [
|
|
33
|
+
{
|
|
34
|
+
"id": "extension_registered",
|
|
35
|
+
"expected": "The real Pi host runs session_start for pi-spotify-widget, registers /spotify:help, and returns its handled feature response.",
|
|
36
|
+
"observed": {
|
|
37
|
+
"widget": {
|
|
38
|
+
"type": "extension_ui_request",
|
|
39
|
+
"id": "eef3544a-4f4a-4e2d-8f1c-e1fc18b9b178",
|
|
40
|
+
"method": "setWidget",
|
|
41
|
+
"widgetKey": "pi-spotify-widget",
|
|
42
|
+
"widgetLines": [
|
|
43
|
+
"\u001b[39m\u001b[2m♫ Run /spotify:login\u001b[22;39m"
|
|
44
|
+
]
|
|
45
|
+
},
|
|
46
|
+
"response": {
|
|
47
|
+
"type": "response",
|
|
48
|
+
"command": "prompt",
|
|
49
|
+
"success": true,
|
|
50
|
+
"data": {
|
|
51
|
+
"disposition": "handled"
|
|
52
|
+
}
|
|
53
|
+
},
|
|
54
|
+
"featureMessage": {
|
|
55
|
+
"type": "message_end",
|
|
56
|
+
"message": {
|
|
57
|
+
"role": "custom",
|
|
58
|
+
"customType": "spotify-widget",
|
|
59
|
+
"content": "### Spotify playback controls\n\nユーザーが曲操作や再生/停止を依頼したら、下表から意図に最も近い **Pi コマンド** を1つ選んで実行してください。\n推測が曖昧なときは `/spotify:help` の一覧を参照するか、ユーザーに確認してください。\n\n| Command | Action | Example user phrases |\n|---|---|---|\n| /spotify:prev | 前の曲に戻る | 前の曲 · 前へ · 戻して · previous track |\n| /spotify:next | 次の曲に進む | 次の曲 · 次へ · スキップ · skip |\n| /spotify:pause | 再生を一時停止する(停止) | 止めて · 停止 · ストップ · 一時停止 |\n| /spotify:play | 再生を再開する | 再生 · 再開 · play · resume |\n\nDiscovery / share:\n| /spotify:share | Now Playing を X に投稿(API 設定時)または共有画面を開く | シェア · Xに投稿 · post to X · tweet |\n| /spotify:play-uri | Spotify 曲 URL / URI を指定して再生 | この曲再生 · play this link · play uri |\n\nOther useful commands:\n- `/spotify:refresh` — Now Playing を即時更新\n- `/spotify:status` — 認証状態(token 値は出さない)\n\nNote: playback control requires `user-modify-playback-state` scope. If controls fail after an upgrade, run `/spotify:login` again.",
|
|
60
|
+
"display": true,
|
|
61
|
+
"details": {
|
|
62
|
+
"kind": "help"
|
|
63
|
+
},
|
|
64
|
+
"timestamp": 1792110351657
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
},
|
|
68
|
+
"passed": true
|
|
69
|
+
}
|
|
70
|
+
]
|
|
71
|
+
},
|
|
72
|
+
{
|
|
73
|
+
"name": "happy_path",
|
|
74
|
+
"command": "npm run smoke:pi -- --case happy_path",
|
|
75
|
+
"exit_code": 0,
|
|
76
|
+
"passed": true,
|
|
77
|
+
"evidence": {
|
|
78
|
+
"note": "Captured actual Pi host/native-shell stdout and stderr. The Pi SDK, loader, extension lifecycle, command handler, RPC protocol, and widget implementation are not mocked; no model inference or Spotify OAuth/API request is used.",
|
|
79
|
+
"runs": [
|
|
80
|
+
{
|
|
81
|
+
"exit_code": 0,
|
|
82
|
+
"signal": null,
|
|
83
|
+
"error": null,
|
|
84
|
+
"stdout": "{\"type\":\"extension_ui_request\",\"id\":\"66c4d889-6384-4544-8c2c-4834eee7a3cb\",\"method\":\"setWidget\",\"widgetKey\":\"pi-spotify-widget\",\"widgetLines\":[\"\\u001b[39m\\u001b[2m♫ Run /spotify:login\\u001b[22;39m\"]}\n{\"type\":\"extension_ui_request\",\"id\":\"d2465817-cb66-4573-acbf-b95c848be979\",\"method\":\"notify\",\"message\":\"### Spotify playback controls\",\"notifyType\":\"info\"}\n{\"type\":\"message_start\",\"message\":{\"role\":\"custom\",\"customType\":\"spotify-widget\",\"content\":\"### Spotify playback controls\\n\\nユーザーが曲操作や再生/停止を依頼したら、下表から意図に最も近い **Pi コマンド** を1つ選んで実行してください。\\n推測が曖昧なときは `/spotify:help` の一覧を参照するか、ユーザーに確認してください。\\n\\n| Command | Action | Example user phrases |\\n|---|---|---|\\n| /spotify:prev | 前の曲に戻る | 前の曲 · 前へ · 戻して · previous track |\\n| /spotify:next | 次の曲に進む | 次の曲 · 次へ · スキップ · skip |\\n| /spotify:pause | 再生を一時停止する(停止) | 止めて · 停止 · ストップ · 一時停止 |\\n| /spotify:play | 再生を再開する | 再生 · 再開 · play · resume |\\n\\nDiscovery / share:\\n| /spotify:share | Now Playing を X に投稿(API 設定時)または共有画面を開く | シェア · Xに投稿 · post to X · tweet |\\n| /spotify:play-uri | Spotify 曲 URL / URI を指定して再生 | この曲再生 · play this link · play uri |\\n\\nOther useful commands:\\n- `/spotify:refresh` — Now Playing を即時更新\\n- `/spotify:status` — 認証状態(token 値は出さない)\\n\\nNote: playback control requires `user-modify-playback-state` scope. If controls fail after an upgrade, run `/spotify:login` again.\",\"display\":true,\"details\":{\"kind\":\"help\"},\"timestamp\":1792110351956}}\n{\"type\":\"message_end\",\"message\":{\"role\":\"custom\",\"customType\":\"spotify-widget\",\"content\":\"### Spotify playback controls\\n\\nユーザーが曲操作や再生/停止を依頼したら、下表から意図に最も近い **Pi コマンド** を1つ選んで実行してください。\\n推測が曖昧なときは `/spotify:help` の一覧を参照するか、ユーザーに確認してください。\\n\\n| Command | Action | Example user phrases |\\n|---|---|---|\\n| /spotify:prev | 前の曲に戻る | 前の曲 · 前へ · 戻して · previous track |\\n| /spotify:next | 次の曲に進む | 次の曲 · 次へ · スキップ · skip |\\n| /spotify:pause | 再生を一時停止する(停止) | 止めて · 停止 · ストップ · 一時停止 |\\n| /spotify:play | 再生を再開する | 再生 · 再開 · play · resume |\\n\\nDiscovery / share:\\n| /spotify:share | Now Playing を X に投稿(API 設定時)または共有画面を開く | シェア · Xに投稿 · post to X · tweet |\\n| /spotify:play-uri | Spotify 曲 URL / URI を指定して再生 | この曲再生 · play this link · play uri |\\n\\nOther useful commands:\\n- `/spotify:refresh` — Now Playing を即時更新\\n- `/spotify:status` — 認証状態(token 値は出さない)\\n\\nNote: playback control requires `user-modify-playback-state` scope. If controls fail after an upgrade, run `/spotify:login` again.\",\"display\":true,\"details\":{\"kind\":\"help\"},\"timestamp\":1792110351956}}\n{\"type\":\"response\",\"command\":\"prompt\",\"success\":true,\"data\":{\"disposition\":\"handled\"}}\n",
|
|
85
|
+
"stderr": ""
|
|
86
|
+
}
|
|
87
|
+
]
|
|
88
|
+
},
|
|
89
|
+
"assertions": [
|
|
90
|
+
{
|
|
91
|
+
"id": "feature_response",
|
|
92
|
+
"expected": "The real /spotify:help command is handled and emits the visible widget help with playback commands.",
|
|
93
|
+
"observed": {
|
|
94
|
+
"response": {
|
|
95
|
+
"type": "response",
|
|
96
|
+
"command": "prompt",
|
|
97
|
+
"success": true,
|
|
98
|
+
"data": {
|
|
99
|
+
"disposition": "handled"
|
|
100
|
+
}
|
|
101
|
+
},
|
|
102
|
+
"featureMessage": {
|
|
103
|
+
"type": "message_end",
|
|
104
|
+
"message": {
|
|
105
|
+
"role": "custom",
|
|
106
|
+
"customType": "spotify-widget",
|
|
107
|
+
"content": "### Spotify playback controls\n\nユーザーが曲操作や再生/停止を依頼したら、下表から意図に最も近い **Pi コマンド** を1つ選んで実行してください。\n推測が曖昧なときは `/spotify:help` の一覧を参照するか、ユーザーに確認してください。\n\n| Command | Action | Example user phrases |\n|---|---|---|\n| /spotify:prev | 前の曲に戻る | 前の曲 · 前へ · 戻して · previous track |\n| /spotify:next | 次の曲に進む | 次の曲 · 次へ · スキップ · skip |\n| /spotify:pause | 再生を一時停止する(停止) | 止めて · 停止 · ストップ · 一時停止 |\n| /spotify:play | 再生を再開する | 再生 · 再開 · play · resume |\n\nDiscovery / share:\n| /spotify:share | Now Playing を X に投稿(API 設定時)または共有画面を開く | シェア · Xに投稿 · post to X · tweet |\n| /spotify:play-uri | Spotify 曲 URL / URI を指定して再生 | この曲再生 · play this link · play uri |\n\nOther useful commands:\n- `/spotify:refresh` — Now Playing を即時更新\n- `/spotify:status` — 認証状態(token 値は出さない)\n\nNote: playback control requires `user-modify-playback-state` scope. If controls fail after an upgrade, run `/spotify:login` again.",
|
|
108
|
+
"display": true,
|
|
109
|
+
"details": {
|
|
110
|
+
"kind": "help"
|
|
111
|
+
},
|
|
112
|
+
"timestamp": 1792110351956
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
},
|
|
116
|
+
"passed": true
|
|
117
|
+
}
|
|
118
|
+
]
|
|
119
|
+
},
|
|
120
|
+
{
|
|
121
|
+
"name": "error_path",
|
|
122
|
+
"command": "npm run smoke:pi -- --case error_path",
|
|
123
|
+
"exit_code": 0,
|
|
124
|
+
"passed": true,
|
|
125
|
+
"evidence": {
|
|
126
|
+
"note": "Captured actual Pi host/native-shell stdout and stderr. The Pi SDK, loader, extension lifecycle, command handler, RPC protocol, and widget implementation are not mocked; no model inference or Spotify OAuth/API request is used.",
|
|
127
|
+
"runs": [
|
|
128
|
+
{
|
|
129
|
+
"exit_code": 0,
|
|
130
|
+
"signal": null,
|
|
131
|
+
"error": null,
|
|
132
|
+
"stdout": "{\"type\":\"extension_ui_request\",\"id\":\"4ce58e34-3f32-4e38-bbec-4e1397964d74\",\"method\":\"setWidget\",\"widgetKey\":\"pi-spotify-widget\",\"widgetLines\":[\"\\u001b[39m\\u001b[2m♫ Run /spotify:login\\u001b[22;39m\"]}\n{\"type\":\"extension_ui_request\",\"id\":\"a4112fda-3c96-427d-9ab4-35a0fd026855\",\"method\":\"notify\",\"message\":\"Could not parse Spotify track URL or URI.\",\"notifyType\":\"warning\"}\n{\"type\":\"response\",\"command\":\"prompt\",\"success\":true,\"data\":{\"disposition\":\"handled\"}}\n",
|
|
133
|
+
"stderr": ""
|
|
134
|
+
}
|
|
135
|
+
]
|
|
136
|
+
},
|
|
137
|
+
"assertions": [
|
|
138
|
+
{
|
|
139
|
+
"id": "handled_error",
|
|
140
|
+
"expected": "A malformed Spotify track link is caught by the extension and reported as a warning without a Spotify network request.",
|
|
141
|
+
"observed": {
|
|
142
|
+
"response": {
|
|
143
|
+
"type": "response",
|
|
144
|
+
"command": "prompt",
|
|
145
|
+
"success": true,
|
|
146
|
+
"data": {
|
|
147
|
+
"disposition": "handled"
|
|
148
|
+
}
|
|
149
|
+
},
|
|
150
|
+
"handledError": {
|
|
151
|
+
"type": "extension_ui_request",
|
|
152
|
+
"id": "a4112fda-3c96-427d-9ab4-35a0fd026855",
|
|
153
|
+
"method": "notify",
|
|
154
|
+
"message": "Could not parse Spotify track URL or URI.",
|
|
155
|
+
"notifyType": "warning"
|
|
156
|
+
}
|
|
157
|
+
},
|
|
158
|
+
"passed": true
|
|
159
|
+
}
|
|
160
|
+
]
|
|
161
|
+
},
|
|
162
|
+
{
|
|
163
|
+
"name": "reload_cleanup",
|
|
164
|
+
"command": "npm run smoke:pi -- --case reload_cleanup",
|
|
165
|
+
"exit_code": 0,
|
|
166
|
+
"passed": true,
|
|
167
|
+
"evidence": {
|
|
168
|
+
"note": "Captured actual Pi host/native-shell stdout and stderr. The Pi SDK, loader, extension lifecycle, command handler, RPC protocol, and widget implementation are not mocked; no model inference or Spotify OAuth/API request is used.",
|
|
169
|
+
"runs": [
|
|
170
|
+
{
|
|
171
|
+
"exit_code": 0,
|
|
172
|
+
"signal": null,
|
|
173
|
+
"error": null,
|
|
174
|
+
"stdout": "{\"type\":\"extension_ui_request\",\"id\":\"ef22d22f-8c68-489f-8af9-0f8a4390a369\",\"method\":\"setWidget\",\"widgetKey\":\"pi-spotify-widget\",\"widgetLines\":[\"\\u001b[39m\\u001b[2m♫ Run /spotify:login\\u001b[22;39m\"]}\n{\"type\":\"extension_ui_request\",\"id\":\"392c48c4-caa4-426e-bc97-0380ea967f04\",\"method\":\"notify\",\"message\":\"### Spotify playback controls\",\"notifyType\":\"info\"}\n{\"type\":\"message_start\",\"message\":{\"role\":\"custom\",\"customType\":\"spotify-widget\",\"content\":\"### Spotify playback controls\\n\\nユーザーが曲操作や再生/停止を依頼したら、下表から意図に最も近い **Pi コマンド** を1つ選んで実行してください。\\n推測が曖昧なときは `/spotify:help` の一覧を参照するか、ユーザーに確認してください。\\n\\n| Command | Action | Example user phrases |\\n|---|---|---|\\n| /spotify:prev | 前の曲に戻る | 前の曲 · 前へ · 戻して · previous track |\\n| /spotify:next | 次の曲に進む | 次の曲 · 次へ · スキップ · skip |\\n| /spotify:pause | 再生を一時停止する(停止) | 止めて · 停止 · ストップ · 一時停止 |\\n| /spotify:play | 再生を再開する | 再生 · 再開 · play · resume |\\n\\nDiscovery / share:\\n| /spotify:share | Now Playing を X に投稿(API 設定時)または共有画面を開く | シェア · Xに投稿 · post to X · tweet |\\n| /spotify:play-uri | Spotify 曲 URL / URI を指定して再生 | この曲再生 · play this link · play uri |\\n\\nOther useful commands:\\n- `/spotify:refresh` — Now Playing を即時更新\\n- `/spotify:status` — 認証状態(token 値は出さない)\\n\\nNote: playback control requires `user-modify-playback-state` scope. If controls fail after an upgrade, run `/spotify:login` again.\",\"display\":true,\"details\":{\"kind\":\"help\"},\"timestamp\":1792110352516}}\n{\"type\":\"message_end\",\"message\":{\"role\":\"custom\",\"customType\":\"spotify-widget\",\"content\":\"### Spotify playback controls\\n\\nユーザーが曲操作や再生/停止を依頼したら、下表から意図に最も近い **Pi コマンド** を1つ選んで実行してください。\\n推測が曖昧なときは `/spotify:help` の一覧を参照するか、ユーザーに確認してください。\\n\\n| Command | Action | Example user phrases |\\n|---|---|---|\\n| /spotify:prev | 前の曲に戻る | 前の曲 · 前へ · 戻して · previous track |\\n| /spotify:next | 次の曲に進む | 次の曲 · 次へ · スキップ · skip |\\n| /spotify:pause | 再生を一時停止する(停止) | 止めて · 停止 · ストップ · 一時停止 |\\n| /spotify:play | 再生を再開する | 再生 · 再開 · play · resume |\\n\\nDiscovery / share:\\n| /spotify:share | Now Playing を X に投稿(API 設定時)または共有画面を開く | シェア · Xに投稿 · post to X · tweet |\\n| /spotify:play-uri | Spotify 曲 URL / URI を指定して再生 | この曲再生 · play this link · play uri |\\n\\nOther useful commands:\\n- `/spotify:refresh` — Now Playing を即時更新\\n- `/spotify:status` — 認証状態(token 値は出さない)\\n\\nNote: playback control requires `user-modify-playback-state` scope. If controls fail after an upgrade, run `/spotify:login` again.\",\"display\":true,\"details\":{\"kind\":\"help\"},\"timestamp\":1792110352516}}\n{\"type\":\"response\",\"command\":\"prompt\",\"success\":true,\"data\":{\"disposition\":\"handled\"}}\n",
|
|
175
|
+
"stderr": ""
|
|
176
|
+
},
|
|
177
|
+
{
|
|
178
|
+
"exit_code": 0,
|
|
179
|
+
"signal": null,
|
|
180
|
+
"error": null,
|
|
181
|
+
"stdout": "{\"type\":\"extension_ui_request\",\"id\":\"e28dc25b-3354-490b-8e50-6a69d1d6d80e\",\"method\":\"setWidget\",\"widgetKey\":\"pi-spotify-widget\",\"widgetLines\":[\"\\u001b[39m\\u001b[2m♫ Run /spotify:login\\u001b[22;39m\"]}\n{\"type\":\"extension_ui_request\",\"id\":\"6b9ea355-c58b-469f-a7e5-aded479029fe\",\"method\":\"notify\",\"message\":\"### Spotify playback controls\",\"notifyType\":\"info\"}\n{\"type\":\"message_start\",\"message\":{\"role\":\"custom\",\"customType\":\"spotify-widget\",\"content\":\"### Spotify playback controls\\n\\nユーザーが曲操作や再生/停止を依頼したら、下表から意図に最も近い **Pi コマンド** を1つ選んで実行してください。\\n推測が曖昧なときは `/spotify:help` の一覧を参照するか、ユーザーに確認してください。\\n\\n| Command | Action | Example user phrases |\\n|---|---|---|\\n| /spotify:prev | 前の曲に戻る | 前の曲 · 前へ · 戻して · previous track |\\n| /spotify:next | 次の曲に進む | 次の曲 · 次へ · スキップ · skip |\\n| /spotify:pause | 再生を一時停止する(停止) | 止めて · 停止 · ストップ · 一時停止 |\\n| /spotify:play | 再生を再開する | 再生 · 再開 · play · resume |\\n\\nDiscovery / share:\\n| /spotify:share | Now Playing を X に投稿(API 設定時)または共有画面を開く | シェア · Xに投稿 · post to X · tweet |\\n| /spotify:play-uri | Spotify 曲 URL / URI を指定して再生 | この曲再生 · play this link · play uri |\\n\\nOther useful commands:\\n- `/spotify:refresh` — Now Playing を即時更新\\n- `/spotify:status` — 認証状態(token 値は出さない)\\n\\nNote: playback control requires `user-modify-playback-state` scope. If controls fail after an upgrade, run `/spotify:login` again.\",\"display\":true,\"details\":{\"kind\":\"help\"},\"timestamp\":1792110352789}}\n{\"type\":\"message_end\",\"message\":{\"role\":\"custom\",\"customType\":\"spotify-widget\",\"content\":\"### Spotify playback controls\\n\\nユーザーが曲操作や再生/停止を依頼したら、下表から意図に最も近い **Pi コマンド** を1つ選んで実行してください。\\n推測が曖昧なときは `/spotify:help` の一覧を参照するか、ユーザーに確認してください。\\n\\n| Command | Action | Example user phrases |\\n|---|---|---|\\n| /spotify:prev | 前の曲に戻る | 前の曲 · 前へ · 戻して · previous track |\\n| /spotify:next | 次の曲に進む | 次の曲 · 次へ · スキップ · skip |\\n| /spotify:pause | 再生を一時停止する(停止) | 止めて · 停止 · ストップ · 一時停止 |\\n| /spotify:play | 再生を再開する | 再生 · 再開 · play · resume |\\n\\nDiscovery / share:\\n| /spotify:share | Now Playing を X に投稿(API 設定時)または共有画面を開く | シェア · Xに投稿 · post to X · tweet |\\n| /spotify:play-uri | Spotify 曲 URL / URI を指定して再生 | この曲再生 · play this link · play uri |\\n\\nOther useful commands:\\n- `/spotify:refresh` — Now Playing を即時更新\\n- `/spotify:status` — 認証状態(token 値は出さない)\\n\\nNote: playback control requires `user-modify-playback-state` scope. If controls fail after an upgrade, run `/spotify:login` again.\",\"display\":true,\"details\":{\"kind\":\"help\"},\"timestamp\":1792110352789}}\n{\"type\":\"response\",\"command\":\"prompt\",\"success\":true,\"data\":{\"disposition\":\"handled\"}}\n",
|
|
182
|
+
"stderr": ""
|
|
183
|
+
}
|
|
184
|
+
]
|
|
185
|
+
},
|
|
186
|
+
"assertions": [
|
|
187
|
+
{
|
|
188
|
+
"id": "reloaded_extension_registered",
|
|
189
|
+
"expected": "A second isolated Pi process reloads the extension, runs session_start, and handles /spotify:help.",
|
|
190
|
+
"observed": {
|
|
191
|
+
"widget": {
|
|
192
|
+
"type": "extension_ui_request",
|
|
193
|
+
"id": "e28dc25b-3354-490b-8e50-6a69d1d6d80e",
|
|
194
|
+
"method": "setWidget",
|
|
195
|
+
"widgetKey": "pi-spotify-widget",
|
|
196
|
+
"widgetLines": [
|
|
197
|
+
"\u001b[39m\u001b[2m♫ Run /spotify:login\u001b[22;39m"
|
|
198
|
+
]
|
|
199
|
+
},
|
|
200
|
+
"response": {
|
|
201
|
+
"type": "response",
|
|
202
|
+
"command": "prompt",
|
|
203
|
+
"success": true,
|
|
204
|
+
"data": {
|
|
205
|
+
"disposition": "handled"
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
},
|
|
209
|
+
"passed": true
|
|
210
|
+
},
|
|
211
|
+
{
|
|
212
|
+
"id": "reloaded_feature_response",
|
|
213
|
+
"expected": "The second process returns the actual widget help message containing playback commands.",
|
|
214
|
+
"observed": {
|
|
215
|
+
"response": {
|
|
216
|
+
"type": "response",
|
|
217
|
+
"command": "prompt",
|
|
218
|
+
"success": true,
|
|
219
|
+
"data": {
|
|
220
|
+
"disposition": "handled"
|
|
221
|
+
}
|
|
222
|
+
},
|
|
223
|
+
"featureMessage": {
|
|
224
|
+
"type": "message_end",
|
|
225
|
+
"message": {
|
|
226
|
+
"role": "custom",
|
|
227
|
+
"customType": "spotify-widget",
|
|
228
|
+
"content": "### Spotify playback controls\n\nユーザーが曲操作や再生/停止を依頼したら、下表から意図に最も近い **Pi コマンド** を1つ選んで実行してください。\n推測が曖昧なときは `/spotify:help` の一覧を参照するか、ユーザーに確認してください。\n\n| Command | Action | Example user phrases |\n|---|---|---|\n| /spotify:prev | 前の曲に戻る | 前の曲 · 前へ · 戻して · previous track |\n| /spotify:next | 次の曲に進む | 次の曲 · 次へ · スキップ · skip |\n| /spotify:pause | 再生を一時停止する(停止) | 止めて · 停止 · ストップ · 一時停止 |\n| /spotify:play | 再生を再開する | 再生 · 再開 · play · resume |\n\nDiscovery / share:\n| /spotify:share | Now Playing を X に投稿(API 設定時)または共有画面を開く | シェア · Xに投稿 · post to X · tweet |\n| /spotify:play-uri | Spotify 曲 URL / URI を指定して再生 | この曲再生 · play this link · play uri |\n\nOther useful commands:\n- `/spotify:refresh` — Now Playing を即時更新\n- `/spotify:status` — 認証状態(token 値は出さない)\n\nNote: playback control requires `user-modify-playback-state` scope. If controls fail after an upgrade, run `/spotify:login` again.",
|
|
229
|
+
"display": true,
|
|
230
|
+
"details": {
|
|
231
|
+
"kind": "help"
|
|
232
|
+
},
|
|
233
|
+
"timestamp": 1792110352789
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
},
|
|
237
|
+
"passed": true
|
|
238
|
+
},
|
|
239
|
+
{
|
|
240
|
+
"id": "clean_shutdown",
|
|
241
|
+
"expected": "Both real Pi processes exit zero and produce no unhandled, extension-load, or shutdown errors.",
|
|
242
|
+
"observed": {
|
|
243
|
+
"exits": [
|
|
244
|
+
0,
|
|
245
|
+
0
|
|
246
|
+
],
|
|
247
|
+
"firstFeatureResponse": true
|
|
248
|
+
},
|
|
249
|
+
"passed": true
|
|
250
|
+
}
|
|
251
|
+
]
|
|
252
|
+
},
|
|
253
|
+
{
|
|
254
|
+
"name": "host_shell",
|
|
255
|
+
"command": "npm run smoke:pi -- --case host_shell",
|
|
256
|
+
"exit_code": 0,
|
|
257
|
+
"passed": true,
|
|
258
|
+
"evidence": {
|
|
259
|
+
"note": "Captured actual Pi host/native-shell stdout and stderr. The Pi SDK, loader, extension lifecycle, command handler, RPC protocol, and widget implementation are not mocked; no model inference or Spotify OAuth/API request is used.",
|
|
260
|
+
"runs": [
|
|
261
|
+
{
|
|
262
|
+
"exit_code": 0,
|
|
263
|
+
"signal": null,
|
|
264
|
+
"error": null,
|
|
265
|
+
"stdout": "1.2.0\n",
|
|
266
|
+
"stderr": ""
|
|
267
|
+
}
|
|
268
|
+
]
|
|
269
|
+
},
|
|
270
|
+
"assertions": [
|
|
271
|
+
{
|
|
272
|
+
"id": "native_shell_boundary",
|
|
273
|
+
"expected": "The native host shell starts the local Pi CLI and prints exactly the frozen SDK version.",
|
|
274
|
+
"observed": {
|
|
275
|
+
"exit_code": 0,
|
|
276
|
+
"output": "1.2.0",
|
|
277
|
+
"expectedVersion": "1.2.0"
|
|
278
|
+
},
|
|
279
|
+
"passed": true
|
|
280
|
+
}
|
|
281
|
+
]
|
|
282
|
+
}
|
|
283
|
+
]
|
|
284
|
+
}
|
package/lib/refresh-schedule.ts
CHANGED
|
@@ -40,7 +40,12 @@ export function trackIdentity(snapshot: PlaybackSnapshot | null): string {
|
|
|
40
40
|
|
|
41
41
|
/** True when the 1s display tick should re-render (extrapolated progress changes). */
|
|
42
42
|
export function needsDisplayProgressUpdate(snapshot: PlaybackSnapshot | null): boolean {
|
|
43
|
-
return Boolean(
|
|
43
|
+
return Boolean(
|
|
44
|
+
snapshot?.track
|
|
45
|
+
&& snapshot.isPlaying
|
|
46
|
+
&& typeof snapshot.progressMs === "number"
|
|
47
|
+
&& Number.isFinite(snapshot.progressMs),
|
|
48
|
+
);
|
|
44
49
|
}
|
|
45
50
|
|
|
46
51
|
export const __testing = {
|
package/package.json
CHANGED
|
@@ -1,64 +1,66 @@
|
|
|
1
|
-
{
|
|
2
|
-
"name": "pi-spotify-widget",
|
|
3
|
-
"version": "0.1.
|
|
4
|
-
"description": "Lightweight Spotify Now Playing widget for Pi Coding Agent.",
|
|
5
|
-
"type": "module",
|
|
6
|
-
"license": "MIT",
|
|
7
|
-
"author": "eiei114",
|
|
8
|
-
"keywords": [
|
|
9
|
-
"pi-package",
|
|
10
|
-
"pi",
|
|
11
|
-
"agent-skill",
|
|
12
|
-
"typescript"
|
|
13
|
-
],
|
|
14
|
-
"repository": {
|
|
15
|
-
"type": "git",
|
|
16
|
-
"url": "git+https://github.com/eiei114/pi-spotify-widget.git"
|
|
17
|
-
},
|
|
18
|
-
"bugs": {
|
|
19
|
-
"url": "https://github.com/eiei114/pi-spotify-widget/issues"
|
|
20
|
-
},
|
|
21
|
-
"homepage": "https://github.com/eiei114/pi-spotify-widget#readme",
|
|
22
|
-
"files": [
|
|
23
|
-
"extensions/",
|
|
24
|
-
"lib/",
|
|
25
|
-
"docs/",
|
|
26
|
-
"README.md",
|
|
27
|
-
"SECURITY.md",
|
|
28
|
-
"LICENSE",
|
|
29
|
-
"CHANGELOG.md",
|
|
30
|
-
"skills/"
|
|
31
|
-
],
|
|
32
|
-
"scripts": {
|
|
33
|
-
"typecheck": "tsc --noEmit",
|
|
34
|
-
"test": "node --test tests/*.test.mjs",
|
|
35
|
-
"ci": "npm run typecheck && npm test && npm run pack:check",
|
|
36
|
-
"pack:check": "npm pack --dry-run",
|
|
37
|
-
"
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
"@earendil-works/pi-
|
|
53
|
-
"@earendil-works/pi-
|
|
54
|
-
"
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
"@earendil-works/pi-
|
|
59
|
-
"@earendil-works/pi-
|
|
60
|
-
"
|
|
61
|
-
"@
|
|
62
|
-
"
|
|
63
|
-
|
|
64
|
-
|
|
1
|
+
{
|
|
2
|
+
"name": "pi-spotify-widget",
|
|
3
|
+
"version": "0.1.19",
|
|
4
|
+
"description": "Lightweight Spotify Now Playing widget for Pi Coding Agent.",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"license": "MIT",
|
|
7
|
+
"author": "eiei114",
|
|
8
|
+
"keywords": [
|
|
9
|
+
"pi-package",
|
|
10
|
+
"pi",
|
|
11
|
+
"agent-skill",
|
|
12
|
+
"typescript"
|
|
13
|
+
],
|
|
14
|
+
"repository": {
|
|
15
|
+
"type": "git",
|
|
16
|
+
"url": "git+https://github.com/eiei114/pi-spotify-widget.git"
|
|
17
|
+
},
|
|
18
|
+
"bugs": {
|
|
19
|
+
"url": "https://github.com/eiei114/pi-spotify-widget/issues"
|
|
20
|
+
},
|
|
21
|
+
"homepage": "https://github.com/eiei114/pi-spotify-widget#readme",
|
|
22
|
+
"files": [
|
|
23
|
+
"extensions/",
|
|
24
|
+
"lib/",
|
|
25
|
+
"docs/",
|
|
26
|
+
"README.md",
|
|
27
|
+
"SECURITY.md",
|
|
28
|
+
"LICENSE",
|
|
29
|
+
"CHANGELOG.md",
|
|
30
|
+
"skills/"
|
|
31
|
+
],
|
|
32
|
+
"scripts": {
|
|
33
|
+
"typecheck": "tsc --noEmit",
|
|
34
|
+
"test": "node --test tests/*.test.mjs",
|
|
35
|
+
"ci": "npm run typecheck && npm test && npm run smoke:pi && npm run pack:check",
|
|
36
|
+
"pack:check": "npm pack --dry-run",
|
|
37
|
+
"smoke:pi": "node scripts/pi-sdk-smoke.mjs",
|
|
38
|
+
"version:check": "node scripts/check-version-bump.mjs"
|
|
39
|
+
},
|
|
40
|
+
"pi": {
|
|
41
|
+
"extensions": [
|
|
42
|
+
"./extensions"
|
|
43
|
+
],
|
|
44
|
+
"skills": [
|
|
45
|
+
"./skills/spotify-playback"
|
|
46
|
+
]
|
|
47
|
+
},
|
|
48
|
+
"publishConfig": {
|
|
49
|
+
"access": "public"
|
|
50
|
+
},
|
|
51
|
+
"peerDependencies": {
|
|
52
|
+
"@earendil-works/pi-ai": "*",
|
|
53
|
+
"@earendil-works/pi-coding-agent": "*",
|
|
54
|
+
"@earendil-works/pi-tui": "*",
|
|
55
|
+
"typebox": "*"
|
|
56
|
+
},
|
|
57
|
+
"devDependencies": {
|
|
58
|
+
"@earendil-works/pi-agent-core": "1.2.0",
|
|
59
|
+
"@earendil-works/pi-ai": "1.2.0",
|
|
60
|
+
"@earendil-works/pi-coding-agent": "1.2.0",
|
|
61
|
+
"@earendil-works/pi-tui": "1.2.0",
|
|
62
|
+
"typebox": "latest",
|
|
63
|
+
"@types/node": "^26.0.0",
|
|
64
|
+
"typescript": "^7.0.2"
|
|
65
|
+
}
|
|
66
|
+
}
|