psn-mcp 0.1.0 → 0.2.0
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/LICENSE +1 -1
- package/README.md +76 -28
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +11 -6
- package/dist/index.js.map +1 -1
- package/dist/psn/api.d.ts +1 -0
- package/dist/psn/api.d.ts.map +1 -0
- package/dist/psn/api.js.map +1 -1
- package/dist/psn/auth.d.ts +5 -2
- package/dist/psn/auth.d.ts.map +1 -0
- package/dist/psn/auth.js +15 -5
- package/dist/psn/auth.js.map +1 -1
- package/dist/psn/browser-login.d.ts +12 -0
- package/dist/psn/browser-login.d.ts.map +1 -0
- package/dist/psn/browser-login.js +201 -0
- package/dist/psn/browser-login.js.map +1 -0
- package/dist/psn/credentials.d.ts +4 -0
- package/dist/psn/credentials.d.ts.map +1 -0
- package/dist/psn/credentials.js +29 -0
- package/dist/psn/credentials.js.map +1 -0
- package/dist/psn/http.d.ts +1 -0
- package/dist/psn/http.d.ts.map +1 -0
- package/dist/psn/http.js +3 -1
- package/dist/psn/http.js.map +1 -1
- package/dist/psn/store.d.ts +79 -0
- package/dist/psn/store.d.ts.map +1 -0
- package/dist/psn/store.js +188 -0
- package/dist/psn/store.js.map +1 -0
- package/dist/psn/types.d.ts +1 -0
- package/dist/psn/types.d.ts.map +1 -0
- package/dist/tools.d.ts +4 -1
- package/dist/tools.d.ts.map +1 -0
- package/dist/tools.js +142 -11
- package/dist/tools.js.map +1 -1
- package/package.json +33 -13
package/LICENSE
CHANGED
package/README.md
CHANGED
|
@@ -1,37 +1,58 @@
|
|
|
1
1
|
# psn-mcp
|
|
2
2
|
|
|
3
|
-
An [MCP](https://modelcontextprotocol.io) (Model Context Protocol) server for
|
|
3
|
+
An [MCP](https://modelcontextprotocol.io) (Model Context Protocol) server for
|
|
4
|
+
interfacing with the PlayStation Network. Lets MCP clients like Claude look up
|
|
5
|
+
PSN profiles, friends, online presence, trophies, and play history.
|
|
4
6
|
|
|
5
|
-
The PSN API layer is written from scratch in TypeScript on Node's built-in
|
|
7
|
+
The PSN API layer is written from scratch in TypeScript on Node's built-in
|
|
8
|
+
`fetch` — no PSN client dependencies. It authenticates the same way the official
|
|
9
|
+
PlayStation mobile app does and talks directly to Sony's `m.np.playstation.com`
|
|
10
|
+
API.
|
|
6
11
|
|
|
7
12
|
## Requirements
|
|
8
13
|
|
|
9
|
-
- Node.js 18+
|
|
10
|
-
- A PlayStation Network account
|
|
14
|
+
- Node.js 24.18+
|
|
15
|
+
- A PlayStation Network account
|
|
11
16
|
|
|
12
|
-
##
|
|
17
|
+
## Signing in
|
|
18
|
+
|
|
19
|
+
The MCP server can automate NPSSO capture through an isolated browser profile:
|
|
20
|
+
|
|
21
|
+
1. Start the MCP server without `PSN_NPSSO`.
|
|
22
|
+
2. Call the `psn_begin_login` tool.
|
|
23
|
+
3. Sign in to PlayStation in the browser window it opens.
|
|
24
|
+
4. Call the `psn_complete_login` tool.
|
|
25
|
+
|
|
26
|
+
`psn_complete_login` reads the PlayStation `npsso` browser cookie, verifies it,
|
|
27
|
+
and stores it in `~/.config/psn-mcp/credentials.json` with owner-only file
|
|
28
|
+
permissions. Future server starts use the stored token automatically. Set
|
|
29
|
+
`PSN_NPSSO_FILE` to choose a different credential file.
|
|
30
|
+
|
|
31
|
+
If browser automation is unavailable, you can still configure an NPSSO manually:
|
|
13
32
|
|
|
14
33
|
1. Sign in at [playstation.com](https://www.playstation.com).
|
|
15
34
|
2. In the same browser, open <https://ca.account.sony.com/api/v1/ssocookie>.
|
|
16
|
-
3. Copy the 64-character `npsso` value from the JSON response
|
|
35
|
+
3. Copy the 64-character `npsso` value from the JSON response into `PSN_NPSSO`.
|
|
17
36
|
|
|
18
|
-
The server exchanges the NPSSO for an OAuth access token on first use and
|
|
37
|
+
The server exchanges the NPSSO for an OAuth access token on first use and
|
|
38
|
+
refreshes it automatically. NPSSO tokens expire after about two months; when
|
|
39
|
+
tools start failing with an auth error, run the login flow again.
|
|
19
40
|
|
|
20
|
-
> **Note:** this uses your personal account session. What you can see (other
|
|
41
|
+
> **Note:** this uses your personal account session. What you can see (other
|
|
42
|
+
> users' friends, presence, play history) is governed by normal PSN privacy
|
|
43
|
+
> settings.
|
|
21
44
|
|
|
22
45
|
## Usage with an MCP client
|
|
23
46
|
|
|
24
|
-
No installation needed — run it with `npx`. Add to your client's MCP
|
|
47
|
+
No installation needed — run it with `npx`. Add to your client's MCP
|
|
48
|
+
configuration (e.g. `claude_desktop_config.json`):
|
|
25
49
|
|
|
26
50
|
```json
|
|
27
51
|
{
|
|
28
52
|
"mcpServers": {
|
|
29
53
|
"psn": {
|
|
30
54
|
"command": "npx",
|
|
31
|
-
"args": ["-y", "psn-mcp"]
|
|
32
|
-
"env": {
|
|
33
|
-
"PSN_NPSSO": "<your npsso token>"
|
|
34
|
-
}
|
|
55
|
+
"args": ["-y", "psn-mcp"]
|
|
35
56
|
}
|
|
36
57
|
}
|
|
37
58
|
}
|
|
@@ -40,26 +61,42 @@ No installation needed — run it with `npx`. Add to your client's MCP configura
|
|
|
40
61
|
Or for Claude Code:
|
|
41
62
|
|
|
42
63
|
```sh
|
|
43
|
-
claude mcp add psn --
|
|
64
|
+
claude mcp add psn -- npx -y psn-mcp
|
|
44
65
|
```
|
|
45
66
|
|
|
46
67
|
## Tools
|
|
47
68
|
|
|
48
|
-
| Tool
|
|
49
|
-
|
|
|
50
|
-
| `
|
|
51
|
-
| `
|
|
52
|
-
| `
|
|
53
|
-
| `
|
|
54
|
-
| `
|
|
55
|
-
| `
|
|
56
|
-
| `
|
|
69
|
+
| Tool | Description |
|
|
70
|
+
| ------------------------- | ------------------------------------------------------------------ |
|
|
71
|
+
| `psn_auth_status` | Whether PSN credentials are configured or login is in progress |
|
|
72
|
+
| `psn_begin_login` | Open the browser-based PSN login helper |
|
|
73
|
+
| `psn_complete_login` | Capture and save the NPSSO token from the login helper |
|
|
74
|
+
| `psn_cancel_login` | Close the login helper without saving credentials |
|
|
75
|
+
| `psn_get_profile` | Profile for a user: online id, about-me, avatars, PS Plus status |
|
|
76
|
+
| `psn_search_players` | Search PSN players by name; returns online ids and account ids |
|
|
77
|
+
| `psn_get_friends` | A user's friends list, resolved to profiles |
|
|
78
|
+
| `psn_get_presence` | Online status, current platform, and the game being played |
|
|
79
|
+
| `psn_get_trophy_summary` | Trophy level, tier, and total trophy counts |
|
|
80
|
+
| `psn_get_trophy_titles` | Games with trophy progress, most recently played first |
|
|
81
|
+
| `psn_get_title_trophies` | Full trophy list defined for a game (names, types, groups) |
|
|
57
82
|
| `psn_get_earned_trophies` | Which trophies a user earned in a game, with timestamps and rarity |
|
|
58
|
-
| `psn_get_played_games`
|
|
83
|
+
| `psn_get_played_games` | Played PS4/PS5 games with play counts and durations |
|
|
84
|
+
| `psn_get_store_deals` | Games on sale, with prices, discounts, and (optional) star ratings |
|
|
85
|
+
| `psn_get_store_product` | Store product details: price, discount, and community star rating |
|
|
86
|
+
| `psn_search_store` | Search the store catalog by name, with current prices |
|
|
87
|
+
|
|
88
|
+
Every user-scoped tool accepts `"me"` (the authenticated account), a PSN online
|
|
89
|
+
id (username), or a numeric account id — online ids are resolved automatically.
|
|
59
90
|
|
|
60
|
-
|
|
91
|
+
For PS5 titles pass `npServiceName: "trophy2"`; for PS4 and earlier use
|
|
92
|
+
`"trophy"`. `psn_get_trophy_titles` reports the right value per game.
|
|
61
93
|
|
|
62
|
-
|
|
94
|
+
The three `store` tools browse the public PlayStation Store and need **no PSN
|
|
95
|
+
account**. They power prompts like _"give me the top 10 games currently on sale
|
|
96
|
+
based on review score"_ — call `psn_get_store_deals` with `includeRatings: true`
|
|
97
|
+
and rank by `starRating.averageRating`. Set `PSN_STORE_LOCALE` (e.g. `en-gb`,
|
|
98
|
+
`de-de`, `ja-jp`) to change the store region and currency; the default is
|
|
99
|
+
`en-us`.
|
|
63
100
|
|
|
64
101
|
## Architecture
|
|
65
102
|
|
|
@@ -71,11 +108,22 @@ src/
|
|
|
71
108
|
auth.ts NPSSO -> OAuth code -> access token exchange, auto-refresh
|
|
72
109
|
http.ts Authenticated JSON client for m.np.playstation.com
|
|
73
110
|
api.ts Typed endpoint wrappers (profiles, trophies, games, search)
|
|
111
|
+
store.ts Public store catalog: deals, search, prices, star ratings
|
|
74
112
|
types.ts PSN API response types
|
|
75
113
|
```
|
|
76
114
|
|
|
77
|
-
|
|
115
|
+
The store module works differently from the account API: the store's GraphQL
|
|
116
|
+
endpoint only accepts Sony's whitelisted persisted queries, so it instead reads
|
|
117
|
+
the Apollo state and star-rating payloads that the store server-renders into
|
|
118
|
+
every page's `__NEXT_DATA__` blob. This needs no authentication but is
|
|
119
|
+
inherently coupled to the store's page structure.
|
|
120
|
+
|
|
121
|
+
Authentication is lazy: the server starts and lists tools without credentials;
|
|
122
|
+
the token exchange happens on the first tool call. Access tokens are refreshed
|
|
123
|
+
ahead of expiry, falling back to a full NPSSO re-exchange if the refresh token
|
|
124
|
+
has expired.
|
|
78
125
|
|
|
79
126
|
## Disclaimer
|
|
80
127
|
|
|
81
|
-
This project uses undocumented PSN endpoints and is not affiliated with or
|
|
128
|
+
This project uses undocumented PSN endpoints and is not affiliated with or
|
|
129
|
+
endorsed by Sony Interactive Entertainment. Use at your own risk.
|
package/dist/index.d.ts
CHANGED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":""}
|
package/dist/index.js
CHANGED
|
@@ -2,22 +2,27 @@
|
|
|
2
2
|
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
3
3
|
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
4
4
|
import { TokenManager } from "./psn/auth.js";
|
|
5
|
+
import { loadStoredNpsso } from "./psn/credentials.js";
|
|
5
6
|
import { PsnHttpClient } from "./psn/http.js";
|
|
6
7
|
import { PsnApi } from "./psn/api.js";
|
|
8
|
+
import { PsnStore } from "./psn/store.js";
|
|
7
9
|
import { registerTools } from "./tools.js";
|
|
8
10
|
async function main() {
|
|
9
|
-
const npsso = process.env.PSN_NPSSO
|
|
11
|
+
const npsso = process.env.PSN_NPSSO || (await loadStoredNpsso()) || "";
|
|
10
12
|
if (!npsso) {
|
|
11
13
|
// stdout is reserved for the MCP protocol; diagnostics go to stderr.
|
|
12
|
-
console.error("warning:
|
|
13
|
-
"
|
|
14
|
-
"
|
|
14
|
+
console.error("warning: no PSN credentials are configured - account tools will fail until login completes.\n" +
|
|
15
|
+
"Call the psn_begin_login MCP tool, sign in to PlayStation in the opened browser, " +
|
|
16
|
+
"then call psn_complete_login.");
|
|
15
17
|
}
|
|
16
18
|
// Auth is lazy: the token exchange happens on the first tool call, so the
|
|
17
19
|
// server starts (and lists tools) even before credentials are configured.
|
|
18
|
-
const
|
|
20
|
+
const tokens = new TokenManager(npsso);
|
|
21
|
+
const psn = new PsnApi(new PsnHttpClient(tokens));
|
|
22
|
+
// Store browsing is public web data; no PSN account needed.
|
|
23
|
+
const store = new PsnStore(process.env.PSN_STORE_LOCALE ?? "en-us");
|
|
19
24
|
const server = new McpServer({ name: "psn-mcp", version: "0.1.0" });
|
|
20
|
-
registerTools(server, psn);
|
|
25
|
+
registerTools(server, psn, store, tokens);
|
|
21
26
|
await server.connect(new StdioServerTransport());
|
|
22
27
|
console.error("psn-mcp server running on stdio");
|
|
23
28
|
}
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AACA,OAAO,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AACpE,OAAO,EAAE,oBAAoB,EAAE,MAAM,2CAA2C,CAAC;AACjF,OAAO,EAAE,YAAY,EAAE,MAAM,eAAe,CAAC;AAC7C,OAAO,EAAE,aAAa,EAAE,MAAM,eAAe,CAAC;AAC9C,OAAO,EAAE,MAAM,EAAE,MAAM,cAAc,CAAC;AACtC,OAAO,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAE3C,KAAK,UAAU,IAAI;IACjB,MAAM,KAAK,GAAG,OAAO,CAAC,GAAG,CAAC,SAAS,IAAI,EAAE,CAAC;
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AACA,OAAO,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AACpE,OAAO,EAAE,oBAAoB,EAAE,MAAM,2CAA2C,CAAC;AACjF,OAAO,EAAE,YAAY,EAAE,MAAM,eAAe,CAAC;AAC7C,OAAO,EAAE,eAAe,EAAE,MAAM,sBAAsB,CAAC;AACvD,OAAO,EAAE,aAAa,EAAE,MAAM,eAAe,CAAC;AAC9C,OAAO,EAAE,MAAM,EAAE,MAAM,cAAc,CAAC;AACtC,OAAO,EAAE,QAAQ,EAAE,MAAM,gBAAgB,CAAC;AAC1C,OAAO,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAE3C,KAAK,UAAU,IAAI;IACjB,MAAM,KAAK,GAAG,OAAO,CAAC,GAAG,CAAC,SAAS,IAAI,CAAC,MAAM,eAAe,EAAE,CAAC,IAAI,EAAE,CAAC;IACvE,IAAI,CAAC,KAAK,EAAE,CAAC;QACX,qEAAqE;QACrE,OAAO,CAAC,KAAK,CACX,+FAA+F;YAC7F,mFAAmF;YACnF,+BAA+B,CAClC,CAAC;IACJ,CAAC;IAED,0EAA0E;IAC1E,0EAA0E;IAC1E,MAAM,MAAM,GAAG,IAAI,YAAY,CAAC,KAAK,CAAC,CAAC;IACvC,MAAM,GAAG,GAAG,IAAI,MAAM,CAAC,IAAI,aAAa,CAAC,MAAM,CAAC,CAAC,CAAC;IAClD,4DAA4D;IAC5D,MAAM,KAAK,GAAG,IAAI,QAAQ,CAAC,OAAO,CAAC,GAAG,CAAC,gBAAgB,IAAI,OAAO,CAAC,CAAC;IAEpE,MAAM,MAAM,GAAG,IAAI,SAAS,CAAC,EAAE,IAAI,EAAE,SAAS,EAAE,OAAO,EAAE,OAAO,EAAE,CAAC,CAAC;IACpE,aAAa,CAAC,MAAM,EAAE,GAAG,EAAE,KAAK,EAAE,MAAM,CAAC,CAAC;IAE1C,MAAM,MAAM,CAAC,OAAO,CAAC,IAAI,oBAAoB,EAAE,CAAC,CAAC;IACjD,OAAO,CAAC,KAAK,CAAC,iCAAiC,CAAC,CAAC;AACnD,CAAC;AAED,IAAI,EAAE,CAAC,KAAK,CAAC,CAAC,KAAK,EAAE,EAAE;IACrB,OAAO,CAAC,KAAK,CAAC,QAAQ,EAAE,KAAK,CAAC,CAAC;IAC/B,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;AAClB,CAAC,CAAC,CAAC"}
|
package/dist/psn/api.d.ts
CHANGED
|
@@ -34,3 +34,4 @@ export declare class PsnApi {
|
|
|
34
34
|
getEarnedTrophies(accountId: string, npCommunicationId: string, npServiceName: NpServiceName, trophyGroupId?: string, limit?: number, offset?: number): Promise<TrophiesResponse>;
|
|
35
35
|
getPlayedGames(accountId: string, limit?: number, offset?: number): Promise<PlayedGamesResponse>;
|
|
36
36
|
}
|
|
37
|
+
//# sourceMappingURL=api.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"api.d.ts","sourceRoot":"","sources":["../../src/psn/api.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,WAAW,CAAC;AAC1C,OAAO,KAAK,EACV,aAAa,EACb,eAAe,EACf,mBAAmB,EACnB,gBAAgB,EAChB,aAAa,EACb,oBAAoB,EACpB,uBAAuB,EACvB,WAAW,EACZ,MAAM,YAAY,CAAC;AAEpB,MAAM,MAAM,aAAa,GAAG,QAAQ,GAAG,SAAS,CAAC;AAEjD;;;;;;GAMG;AACH,qBAAa,MAAM;IACL,OAAO,CAAC,QAAQ,CAAC,IAAI;gBAAJ,IAAI,EAAE,aAAa;IAIhD,UAAU,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,WAAW,CAAC;IAMnD,aAAa,CACX,UAAU,EAAE,MAAM,EAClB,KAAK,SAAK,GACT,OAAO,CAAC,uBAAuB,CAAC;IAkBnC;;;;OAIG;IACG,gBAAgB,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;IAmBrD,UAAU,CACR,SAAS,EAAE,MAAM,EACjB,KAAK,SAAM,EACX,MAAM,SAAI,GACT,OAAO,CAAC,eAAe,CAAC;IAO3B,gBAAgB,CACd,SAAS,EAAE,MAAM,GAChB,OAAO,CAAC;QAAE,aAAa,EAAE,aAAa,CAAA;KAAE,CAAC;IAO5C,iBAAiB,CACf,UAAU,EAAE,MAAM,EAAE,GACnB,OAAO,CAAC;QAAE,cAAc,EAAE,aAAa,EAAE,CAAA;KAAE,CAAC;IAS/C,gBAAgB,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,aAAa,CAAC;IAM3D,eAAe,CACb,SAAS,EAAE,MAAM,EACjB,KAAK,SAAM,EACX,MAAM,SAAI,GACT,OAAO,CAAC,oBAAoB,CAAC;IAOhC,mEAAmE;IACnE,gBAAgB,CACd,iBAAiB,EAAE,MAAM,EACzB,aAAa,EAAE,aAAa,EAC5B,aAAa,SAAQ,EACrB,KAAK,SAAM,EACX,MAAM,SAAI,GACT,OAAO,CAAC,gBAAgB,CAAC;IAQ5B,kEAAkE;IAClE,iBAAiB,CACf,SAAS,EAAE,MAAM,EACjB,iBAAiB,EAAE,MAAM,EACzB,aAAa,EAAE,aAAa,EAC5B,aAAa,SAAQ,EACrB,KAAK,SAAM,EACX,MAAM,SAAI,GACT,OAAO,CAAC,gBAAgB,CAAC;IAW5B,cAAc,CACZ,SAAS,EAAE,MAAM,EACjB,KAAK,SAAK,EACV,MAAM,SAAI,GACT,OAAO,CAAC,mBAAmB,CAAC;CAYhC"}
|
package/dist/psn/api.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"api.js","sourceRoot":"","sources":["../../src/psn/api.ts"],"names":[],"mappings":"AAcA;;;;;;GAMG;AACH,MAAM,OAAO,MAAM;IACY;IAA7B,YAA6B,IAAmB;QAAnB,SAAI,GAAJ,IAAI,CAAe;IAAG,CAAC;IAEpD,2EAA2E;IAE3E,UAAU,CAAC,SAAiB;QAC1B,OAAO,IAAI,CAAC,IAAI,CAAC,OAAO,CACtB,kCAAkC,kBAAkB,CAAC,SAAS,CAAC,WAAW,CAC3E,CAAC;IACJ,CAAC;IAED,aAAa,
|
|
1
|
+
{"version":3,"file":"api.js","sourceRoot":"","sources":["../../src/psn/api.ts"],"names":[],"mappings":"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"}
|
package/dist/psn/auth.d.ts
CHANGED
|
@@ -29,11 +29,14 @@ export declare function refreshTokens(refreshToken: string): Promise<TokenSet>;
|
|
|
29
29
|
* token itself has expired.
|
|
30
30
|
*/
|
|
31
31
|
export declare class TokenManager {
|
|
32
|
-
private readonly npsso;
|
|
33
32
|
private tokens;
|
|
34
33
|
private pending;
|
|
35
|
-
|
|
34
|
+
private npsso;
|
|
35
|
+
constructor(npsso?: string | null);
|
|
36
|
+
hasNpsso(): boolean;
|
|
37
|
+
setNpsso(npsso: string): void;
|
|
36
38
|
/** Returns a valid access token, authenticating or refreshing as needed. */
|
|
37
39
|
getAccessToken(): Promise<string>;
|
|
38
40
|
private renew;
|
|
39
41
|
}
|
|
42
|
+
//# sourceMappingURL=auth.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"auth.d.ts","sourceRoot":"","sources":["../../src/psn/auth.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAQH,MAAM,WAAW,QAAQ;IACvB,WAAW,EAAE,MAAM,CAAC;IACpB,gEAAgE;IAChE,SAAS,EAAE,MAAM,CAAC;IAClB,YAAY,EAAE,MAAM,CAAC;IACrB,iEAAiE;IACjE,gBAAgB,EAAE,MAAM,CAAC;CAC1B;AAED,qBAAa,YAAa,SAAQ,KAAK;gBACzB,OAAO,EAAE,MAAM;CAI5B;AAwED,iEAAiE;AACjE,wBAAsB,qBAAqB,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,QAAQ,CAAC,CAU5E;AAED,wBAAsB,aAAa,CAAC,YAAY,EAAE,MAAM,GAAG,OAAO,CAAC,QAAQ,CAAC,CAS3E;AAED;;;;GAIG;AACH,qBAAa,YAAY;IACvB,OAAO,CAAC,MAAM,CAAyB;IACvC,OAAO,CAAC,OAAO,CAAkC;IACjD,OAAO,CAAC,KAAK,CAAgB;gBAEjB,KAAK,CAAC,EAAE,MAAM,GAAG,IAAI;IAIjC,QAAQ,IAAI,OAAO;IAInB,QAAQ,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI;IAS7B,4EAA4E;IACtE,cAAc,IAAI,OAAO,CAAC,MAAM,CAAC;YAezB,KAAK;CAkBpB"}
|
package/dist/psn/auth.js
CHANGED
|
@@ -92,16 +92,22 @@ export async function refreshTokens(refreshToken) {
|
|
|
92
92
|
* token itself has expired.
|
|
93
93
|
*/
|
|
94
94
|
export class TokenManager {
|
|
95
|
-
npsso;
|
|
96
95
|
tokens = null;
|
|
97
96
|
pending = null;
|
|
97
|
+
npsso;
|
|
98
98
|
constructor(npsso) {
|
|
99
|
-
this.npsso = npsso;
|
|
99
|
+
this.npsso = npsso || null;
|
|
100
|
+
}
|
|
101
|
+
hasNpsso() {
|
|
102
|
+
return Boolean(this.npsso);
|
|
103
|
+
}
|
|
104
|
+
setNpsso(npsso) {
|
|
100
105
|
if (!npsso) {
|
|
101
|
-
throw new PsnAuthError("
|
|
102
|
-
"Sign in at https://www.playstation.com, then visit " +
|
|
103
|
-
"https://ca.account.sony.com/api/v1/ssocookie to copy your token.");
|
|
106
|
+
throw new PsnAuthError("Cannot set an empty NPSSO token.");
|
|
104
107
|
}
|
|
108
|
+
this.npsso = npsso;
|
|
109
|
+
this.tokens = null;
|
|
110
|
+
this.pending = null;
|
|
105
111
|
}
|
|
106
112
|
/** Returns a valid access token, authenticating or refreshing as needed. */
|
|
107
113
|
async getAccessToken() {
|
|
@@ -119,6 +125,10 @@ export class TokenManager {
|
|
|
119
125
|
return this.tokens.accessToken;
|
|
120
126
|
}
|
|
121
127
|
async renew() {
|
|
128
|
+
if (!this.npsso) {
|
|
129
|
+
throw new PsnAuthError("No NPSSO token configured. Run psn_begin_login and psn_complete_login, " +
|
|
130
|
+
"or set the PSN_NPSSO environment variable.");
|
|
131
|
+
}
|
|
122
132
|
const margin = 60_000;
|
|
123
133
|
if (this.tokens && this.tokens.refreshExpiresAt - margin > Date.now()) {
|
|
124
134
|
try {
|
package/dist/psn/auth.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"auth.js","sourceRoot":"","sources":["../../src/psn/auth.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,MAAM,SAAS,GAAG,gDAAgD,CAAC;AACnE,MAAM,SAAS,GAAG,sCAAsC,CAAC;AACzD,MAAM,aAAa,GAAG,kBAAkB,CAAC;AACzC,MAAM,YAAY,GAAG,4CAA4C,CAAC;AAClE,MAAM,KAAK,GAAG,kCAAkC,CAAC;AAWjD,MAAM,OAAO,YAAa,SAAQ,KAAK;IACrC,YAAY,OAAe;QACzB,KAAK,CAAC,OAAO,CAAC,CAAC;QACf,IAAI,CAAC,IAAI,GAAG,cAAc,CAAC;IAC7B,CAAC;CACF;AAWD,+DAA+D;AAC/D,KAAK,UAAU,oBAAoB,CAAC,KAAa;IAC/C,MAAM,MAAM,GAAG,IAAI,eAAe,CAAC;QACjC,WAAW,EAAE,SAAS;QACtB,SAAS,EAAE,SAAS;QACpB,YAAY,EAAE,YAAY;QAC1B,aAAa,EAAE,MAAM;QACrB,KAAK,EAAE,KAAK;KACb,CAAC,CAAC;IAEH,MAAM,GAAG,GAAG,MAAM,KAAK,CAAC,GAAG,SAAS,cAAc,MAAM,EAAE,EAAE;QAC1D,MAAM,EAAE,KAAK;QACb,OAAO,EAAE,EAAE,MAAM,EAAE,SAAS,KAAK,EAAE,EAAE;QACrC,QAAQ,EAAE,QAAQ;KACnB,CAAC,CAAC;IAEH,MAAM,QAAQ,GAAG,GAAG,CAAC,OAAO,CAAC,GAAG,CAAC,UAAU,CAAC,CAAC;IAC7C,IAAI,CAAC,QAAQ,IAAI,CAAC,QAAQ,CAAC,QAAQ,CAAC,OAAO,CAAC,EAAE,CAAC;QAC7C,MAAM,IAAI,YAAY,CACpB,+DAA+D;YAC7D,uEAAuE;YACvE,8EAA8E,CACjF,CAAC;IACJ,CAAC;IAED,MAAM,IAAI,GAAG,IAAI,GAAG,CAAC,QAAQ,CAAC,CAAC,YAAY,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC;IACxD,IAAI,CAAC,IAAI,EAAE,CAAC;QACV,MAAM,IAAI,YAAY,
|
|
1
|
+
{"version":3,"file":"auth.js","sourceRoot":"","sources":["../../src/psn/auth.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
export interface BeginBrowserLoginResult {
|
|
2
|
+
loginUrl: string;
|
|
3
|
+
message: string;
|
|
4
|
+
}
|
|
5
|
+
export interface CompleteBrowserLoginResult {
|
|
6
|
+
npsso: string;
|
|
7
|
+
}
|
|
8
|
+
export declare function hasActiveBrowserLogin(): boolean;
|
|
9
|
+
export declare function beginBrowserLogin(): Promise<BeginBrowserLoginResult>;
|
|
10
|
+
export declare function completeBrowserLogin(): Promise<CompleteBrowserLoginResult>;
|
|
11
|
+
export declare function cancelBrowserLogin(): Promise<void>;
|
|
12
|
+
//# sourceMappingURL=browser-login.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"browser-login.d.ts","sourceRoot":"","sources":["../../src/psn/browser-login.ts"],"names":[],"mappings":"AAqCA,MAAM,WAAW,uBAAuB;IACtC,QAAQ,EAAE,MAAM,CAAC;IACjB,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,0BAA0B;IACzC,KAAK,EAAE,MAAM,CAAC;CACf;AAED,wBAAgB,qBAAqB,IAAI,OAAO,CAE/C;AAED,wBAAsB,iBAAiB,IAAI,OAAO,CAAC,uBAAuB,CAAC,CAkD1E;AAED,wBAAsB,oBAAoB,IAAI,OAAO,CAAC,0BAA0B,CAAC,CAiBhF;AAED,wBAAsB,kBAAkB,IAAI,OAAO,CAAC,IAAI,CAAC,CAExD"}
|
|
@@ -0,0 +1,201 @@
|
|
|
1
|
+
import { spawn } from "node:child_process";
|
|
2
|
+
import { existsSync } from "node:fs";
|
|
3
|
+
import { mkdtemp, rm } from "node:fs/promises";
|
|
4
|
+
import { tmpdir } from "node:os";
|
|
5
|
+
import { join } from "node:path";
|
|
6
|
+
import { createServer } from "node:net";
|
|
7
|
+
const LOGIN_URL = "https://www.playstation.com/";
|
|
8
|
+
let activeSession = null;
|
|
9
|
+
export function hasActiveBrowserLogin() {
|
|
10
|
+
return activeSession !== null;
|
|
11
|
+
}
|
|
12
|
+
export async function beginBrowserLogin() {
|
|
13
|
+
if (activeSession) {
|
|
14
|
+
return {
|
|
15
|
+
loginUrl: LOGIN_URL,
|
|
16
|
+
message: "A PSN login browser is already open. Sign in there, then call psn_complete_login.",
|
|
17
|
+
};
|
|
18
|
+
}
|
|
19
|
+
const browserPath = findBrowserPath();
|
|
20
|
+
if (!browserPath) {
|
|
21
|
+
throw new Error("Could not find a supported browser. Install Chrome, Edge, Brave, or Chromium, " +
|
|
22
|
+
"or set PSN_BROWSER_PATH to the browser executable.");
|
|
23
|
+
}
|
|
24
|
+
const port = await getAvailablePort();
|
|
25
|
+
const userDataDir = await mkdtemp(join(tmpdir(), "psn-mcp-login-"));
|
|
26
|
+
const browser = spawn(browserPath, [
|
|
27
|
+
`--remote-debugging-port=${port}`,
|
|
28
|
+
"--remote-debugging-address=127.0.0.1",
|
|
29
|
+
`--user-data-dir=${userDataDir}`,
|
|
30
|
+
"--no-first-run",
|
|
31
|
+
"--no-default-browser-check",
|
|
32
|
+
LOGIN_URL,
|
|
33
|
+
], {
|
|
34
|
+
detached: true,
|
|
35
|
+
stdio: "ignore",
|
|
36
|
+
});
|
|
37
|
+
browser.unref();
|
|
38
|
+
activeSession = { browser, port, userDataDir };
|
|
39
|
+
try {
|
|
40
|
+
await waitForDebugger(port);
|
|
41
|
+
}
|
|
42
|
+
catch (error) {
|
|
43
|
+
await cleanupActiveSession();
|
|
44
|
+
throw error;
|
|
45
|
+
}
|
|
46
|
+
return {
|
|
47
|
+
loginUrl: LOGIN_URL,
|
|
48
|
+
message: "A browser window was opened with an isolated profile. Sign in to PlayStation, " +
|
|
49
|
+
"then call psn_complete_login to capture the session automatically.",
|
|
50
|
+
};
|
|
51
|
+
}
|
|
52
|
+
export async function completeBrowserLogin() {
|
|
53
|
+
if (!activeSession) {
|
|
54
|
+
throw new Error("No PSN browser login is active. Call psn_begin_login first.");
|
|
55
|
+
}
|
|
56
|
+
const npsso = await readNpssoCookie(activeSession.port);
|
|
57
|
+
if (!npsso) {
|
|
58
|
+
throw new Error("Could not find an NPSSO cookie yet. Finish signing in to PlayStation in " +
|
|
59
|
+
"the opened browser window, then call psn_complete_login again.");
|
|
60
|
+
}
|
|
61
|
+
await cleanupActiveSession();
|
|
62
|
+
return { npsso };
|
|
63
|
+
}
|
|
64
|
+
export async function cancelBrowserLogin() {
|
|
65
|
+
await cleanupActiveSession();
|
|
66
|
+
}
|
|
67
|
+
function findBrowserPath() {
|
|
68
|
+
if (process.env.PSN_BROWSER_PATH)
|
|
69
|
+
return process.env.PSN_BROWSER_PATH;
|
|
70
|
+
const candidates = browserCandidates();
|
|
71
|
+
return candidates.find((candidate) => existsSync(candidate)) ?? null;
|
|
72
|
+
}
|
|
73
|
+
function browserCandidates() {
|
|
74
|
+
if (process.platform === "darwin") {
|
|
75
|
+
return [
|
|
76
|
+
"/Applications/Google Chrome.app/Contents/MacOS/Google Chrome",
|
|
77
|
+
"/Applications/Microsoft Edge.app/Contents/MacOS/Microsoft Edge",
|
|
78
|
+
"/Applications/Brave Browser.app/Contents/MacOS/Brave Browser",
|
|
79
|
+
"/Applications/Chromium.app/Contents/MacOS/Chromium",
|
|
80
|
+
];
|
|
81
|
+
}
|
|
82
|
+
if (process.platform === "win32") {
|
|
83
|
+
const roots = [
|
|
84
|
+
process.env.PROGRAMFILES,
|
|
85
|
+
process.env["PROGRAMFILES(X86)"],
|
|
86
|
+
process.env.LOCALAPPDATA,
|
|
87
|
+
].filter(Boolean);
|
|
88
|
+
return roots.flatMap((root) => [
|
|
89
|
+
join(root, "Google", "Chrome", "Application", "chrome.exe"),
|
|
90
|
+
join(root, "Microsoft", "Edge", "Application", "msedge.exe"),
|
|
91
|
+
join(root, "BraveSoftware", "Brave-Browser", "Application", "brave.exe"),
|
|
92
|
+
]);
|
|
93
|
+
}
|
|
94
|
+
return [
|
|
95
|
+
"/usr/bin/google-chrome",
|
|
96
|
+
"/usr/bin/google-chrome-stable",
|
|
97
|
+
"/usr/bin/chromium",
|
|
98
|
+
"/usr/bin/chromium-browser",
|
|
99
|
+
"/usr/bin/microsoft-edge",
|
|
100
|
+
"/usr/bin/brave-browser",
|
|
101
|
+
];
|
|
102
|
+
}
|
|
103
|
+
async function getAvailablePort() {
|
|
104
|
+
return new Promise((resolve, reject) => {
|
|
105
|
+
const server = createServer();
|
|
106
|
+
server.once("error", reject);
|
|
107
|
+
server.listen(0, "127.0.0.1", () => {
|
|
108
|
+
const address = server.address();
|
|
109
|
+
server.close(() => {
|
|
110
|
+
if (typeof address === "object" && address) {
|
|
111
|
+
resolve(address.port);
|
|
112
|
+
}
|
|
113
|
+
else {
|
|
114
|
+
reject(new Error("Could not allocate a local debugger port."));
|
|
115
|
+
}
|
|
116
|
+
});
|
|
117
|
+
});
|
|
118
|
+
});
|
|
119
|
+
}
|
|
120
|
+
async function waitForDebugger(port) {
|
|
121
|
+
const deadline = Date.now() + 10_000;
|
|
122
|
+
while (Date.now() < deadline) {
|
|
123
|
+
try {
|
|
124
|
+
const res = await fetch(`http://127.0.0.1:${port}/json/version`);
|
|
125
|
+
if (res.ok)
|
|
126
|
+
return;
|
|
127
|
+
}
|
|
128
|
+
catch {
|
|
129
|
+
// Browser is still starting.
|
|
130
|
+
}
|
|
131
|
+
await delay(250);
|
|
132
|
+
}
|
|
133
|
+
throw new Error("Timed out waiting for the browser debugging endpoint.");
|
|
134
|
+
}
|
|
135
|
+
async function readNpssoCookie(port) {
|
|
136
|
+
const targets = (await fetchJson(`http://127.0.0.1:${port}/json/list`));
|
|
137
|
+
const page = targets.find((target) => target.type === "page" && target.webSocketDebuggerUrl);
|
|
138
|
+
if (!page?.webSocketDebuggerUrl) {
|
|
139
|
+
throw new Error("Could not find the browser page debugging target.");
|
|
140
|
+
}
|
|
141
|
+
const socket = await openCdpSocket(page.webSocketDebuggerUrl);
|
|
142
|
+
try {
|
|
143
|
+
const response = await sendCdp(socket, "Network.getAllCookies");
|
|
144
|
+
const cookie = response.cookies.find((candidate) => candidate.name.toLowerCase() === "npsso" && candidate.value);
|
|
145
|
+
return cookie?.value ?? null;
|
|
146
|
+
}
|
|
147
|
+
finally {
|
|
148
|
+
socket.close();
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
async function openCdpSocket(url) {
|
|
152
|
+
const WebSocketCtor = globalThis.WebSocket;
|
|
153
|
+
if (!WebSocketCtor) {
|
|
154
|
+
throw new Error("This Node.js runtime does not expose WebSocket. Use Node.js 22+ for browser login.");
|
|
155
|
+
}
|
|
156
|
+
const socket = new WebSocketCtor(url);
|
|
157
|
+
await new Promise((resolve, reject) => {
|
|
158
|
+
socket.addEventListener("open", () => resolve(), { once: true });
|
|
159
|
+
socket.addEventListener("error", (event) => reject(event.error ?? new Error("WebSocket connection failed.")), { once: true });
|
|
160
|
+
});
|
|
161
|
+
return socket;
|
|
162
|
+
}
|
|
163
|
+
async function sendCdp(socket, method) {
|
|
164
|
+
const id = Math.floor(Math.random() * Number.MAX_SAFE_INTEGER);
|
|
165
|
+
const result = await new Promise((resolve, reject) => {
|
|
166
|
+
const timeout = setTimeout(() => reject(new Error(`Timed out calling Chrome DevTools ${method}.`)), 5_000);
|
|
167
|
+
socket.addEventListener("message", (event) => {
|
|
168
|
+
const data = JSON.parse(String(event.data));
|
|
169
|
+
if (data.id !== id)
|
|
170
|
+
return;
|
|
171
|
+
clearTimeout(timeout);
|
|
172
|
+
if (data.error) {
|
|
173
|
+
reject(new Error(data.error.message ?? `${method} failed.`));
|
|
174
|
+
}
|
|
175
|
+
else {
|
|
176
|
+
resolve(data.result);
|
|
177
|
+
}
|
|
178
|
+
});
|
|
179
|
+
socket.send(JSON.stringify({ id, method }));
|
|
180
|
+
});
|
|
181
|
+
return result;
|
|
182
|
+
}
|
|
183
|
+
async function fetchJson(url) {
|
|
184
|
+
const res = await fetch(url);
|
|
185
|
+
if (!res.ok) {
|
|
186
|
+
throw new Error(`Browser debugging endpoint failed with HTTP ${res.status}.`);
|
|
187
|
+
}
|
|
188
|
+
return res.json();
|
|
189
|
+
}
|
|
190
|
+
async function cleanupActiveSession() {
|
|
191
|
+
const session = activeSession;
|
|
192
|
+
activeSession = null;
|
|
193
|
+
if (!session)
|
|
194
|
+
return;
|
|
195
|
+
session.browser.kill();
|
|
196
|
+
await rm(session.userDataDir, { recursive: true, force: true });
|
|
197
|
+
}
|
|
198
|
+
function delay(ms) {
|
|
199
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
200
|
+
}
|
|
201
|
+
//# sourceMappingURL=browser-login.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"browser-login.js","sourceRoot":"","sources":["../../src/psn/browser-login.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"credentials.d.ts","sourceRoot":"","sources":["../../src/psn/credentials.ts"],"names":[],"mappings":"AAQA,wBAAgB,eAAe,IAAI,MAAM,CAKxC;AAED,wBAAsB,eAAe,IAAI,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAU9D;AAED,wBAAsB,eAAe,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CASpE"}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { chmod, mkdir, readFile, writeFile } from "node:fs/promises";
|
|
2
|
+
import { homedir } from "node:os";
|
|
3
|
+
import { dirname, join } from "node:path";
|
|
4
|
+
export function credentialsPath() {
|
|
5
|
+
return (process.env.PSN_NPSSO_FILE ??
|
|
6
|
+
join(homedir(), ".config", "psn-mcp", "credentials.json"));
|
|
7
|
+
}
|
|
8
|
+
export async function loadStoredNpsso() {
|
|
9
|
+
try {
|
|
10
|
+
const data = JSON.parse(await readFile(credentialsPath(), "utf8"));
|
|
11
|
+
return data.npsso || null;
|
|
12
|
+
}
|
|
13
|
+
catch (error) {
|
|
14
|
+
if (error.code === "ENOENT")
|
|
15
|
+
return null;
|
|
16
|
+
throw error;
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
export async function saveStoredNpsso(npsso) {
|
|
20
|
+
const path = credentialsPath();
|
|
21
|
+
await mkdir(dirname(path), { recursive: true, mode: 0o700 });
|
|
22
|
+
await writeFile(path, `${JSON.stringify({ npsso }, null, 2)}\n`, {
|
|
23
|
+
encoding: "utf8",
|
|
24
|
+
mode: 0o600,
|
|
25
|
+
});
|
|
26
|
+
await chmod(path, 0o600);
|
|
27
|
+
return path;
|
|
28
|
+
}
|
|
29
|
+
//# sourceMappingURL=credentials.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"credentials.js","sourceRoot":"","sources":["../../src/psn/credentials.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,KAAK,EAAE,QAAQ,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;AACrE,OAAO,EAAE,OAAO,EAAE,MAAM,SAAS,CAAC;AAClC,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAM1C,MAAM,UAAU,eAAe;IAC7B,OAAO,CACL,OAAO,CAAC,GAAG,CAAC,cAAc;QAC1B,IAAI,CAAC,OAAO,EAAE,EAAE,SAAS,EAAE,SAAS,EAAE,kBAAkB,CAAC,CAC1D,CAAC;AACJ,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,eAAe;IACnC,IAAI,CAAC;QACH,MAAM,IAAI,GAAG,IAAI,CAAC,KAAK,CACrB,MAAM,QAAQ,CAAC,eAAe,EAAE,EAAE,MAAM,CAAC,CACvB,CAAC;QACrB,OAAO,IAAI,CAAC,KAAK,IAAI,IAAI,CAAC;IAC5B,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,IAAK,KAA+B,CAAC,IAAI,KAAK,QAAQ;YAAE,OAAO,IAAI,CAAC;QACpE,MAAM,KAAK,CAAC;IACd,CAAC;AACH,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,eAAe,CAAC,KAAa;IACjD,MAAM,IAAI,GAAG,eAAe,EAAE,CAAC;IAC/B,MAAM,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC,CAAC;IAC7D,MAAM,SAAS,CAAC,IAAI,EAAE,GAAG,IAAI,CAAC,SAAS,CAAC,EAAE,KAAK,EAAE,EAAE,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE;QAC/D,QAAQ,EAAE,MAAM;QAChB,IAAI,EAAE,KAAK;KACZ,CAAC,CAAC;IACH,MAAM,KAAK,CAAC,IAAI,EAAE,KAAK,CAAC,CAAC;IACzB,OAAO,IAAI,CAAC;AACd,CAAC"}
|
package/dist/psn/http.d.ts
CHANGED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"http.d.ts","sourceRoot":"","sources":["../../src/psn/http.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,WAAW,CAAC;AAI9C,qBAAa,WAAY,SAAQ,KAAK;aAGlB,MAAM,EAAE,MAAM;aACd,IAAI,CAAC,EAAE,OAAO;gBAF9B,OAAO,EAAE,MAAM,EACC,MAAM,EAAE,MAAM,EACd,IAAI,CAAC,EAAE,OAAO,YAAA;CAKjC;AAED,MAAM,WAAW,cAAc;IAC7B,MAAM,CAAC,EAAE,KAAK,GAAG,MAAM,CAAC;IACxB,6DAA6D;IAC7D,KAAK,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,GAAG,OAAO,GAAG,SAAS,CAAC,CAAC;IAC9D,mCAAmC;IACnC,IAAI,CAAC,EAAE,OAAO,CAAC;IACf,8CAA8C;IAC9C,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CAClC;AAED,kEAAkE;AAClE,qBAAa,aAAa;IACZ,OAAO,CAAC,QAAQ,CAAC,MAAM;gBAAN,MAAM,EAAE,YAAY;IAE3C,OAAO,CAAC,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,GAAE,cAAmB,GAAG,OAAO,CAAC,CAAC,CAAC;CA2CzE"}
|
package/dist/psn/http.js
CHANGED
|
@@ -27,7 +27,9 @@ export class PsnHttpClient {
|
|
|
27
27
|
headers: {
|
|
28
28
|
Authorization: `Bearer ${accessToken}`,
|
|
29
29
|
"Accept-Language": "en-US",
|
|
30
|
-
...(options.body !== undefined
|
|
30
|
+
...(options.body !== undefined
|
|
31
|
+
? { "Content-Type": "application/json" }
|
|
32
|
+
: {}),
|
|
31
33
|
...options.headers,
|
|
32
34
|
},
|
|
33
35
|
body: options.body !== undefined ? JSON.stringify(options.body) : undefined,
|