godot-cli 0.17.3 → 0.19.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/README.md +34 -2
- package/dist/commands/setup-mcp.js +3 -0
- package/dist/commands/setup-mcp.js.map +1 -1
- package/dist/lib/enroll.js +8 -4
- package/dist/lib/enroll.js.map +1 -1
- package/dist/lib/setup-mcp.js +14 -1
- package/dist/lib/setup-mcp.js.map +1 -1
- package/dist/lib/types.d.ts +9 -0
- package/dist/utils/addon-deps.js +1 -1
- package/dist/utils/cloud-login.d.ts +23 -8
- package/dist/utils/cloud-login.js +42 -19
- package/dist/utils/cloud-login.js.map +1 -1
- package/dist/utils/project-identity.d.ts +18 -72
- package/dist/utils/project-identity.js +21 -106
- package/dist/utils/project-identity.js.map +1 -1
- package/dist/utils/server-source.d.ts +1 -1
- package/dist/utils/server-source.js +1 -1
- package/package.json +2 -1
- package/dist/utils/auth.d.ts +0 -42
- package/dist/utils/auth.js +0 -113
- package/dist/utils/auth.js.map +0 -1
package/README.md
CHANGED
|
@@ -59,6 +59,9 @@ npm install -g godot-cli
|
|
|
59
59
|
# Install the godot_mcp addon into a project
|
|
60
60
|
godot-cli install-plugin ./MyGodotProject
|
|
61
61
|
|
|
62
|
+
# Sign in to the ai-game.dev cloud (OAuth 2.1 device login — once per machine)
|
|
63
|
+
godot-cli login
|
|
64
|
+
|
|
62
65
|
# Open the project (builds C# first, then launches the editor with MCP connection)
|
|
63
66
|
godot-cli open ./MyGodotProject
|
|
64
67
|
|
|
@@ -86,7 +89,8 @@ npx godot-cli install-plugin /path/to/godot/project
|
|
|
86
89
|
| `run-system-tool <tool> [path]` | POST to `<url>/api/system-tools/<tool>` (tools not exposed to MCP clients). |
|
|
87
90
|
| `status [path]` | Detect a running Godot editor for the project and probe MCP-server health. |
|
|
88
91
|
| `wait-for-ready [path]` | Poll the MCP server until it answers `ping`. |
|
|
89
|
-
| `
|
|
92
|
+
| `login [path]` | Authenticate with the ai-game.dev cloud via the OAuth 2.1 device-authorization flow (RFC 8628) — opens a browser, then saves a cloud credential to the shared machine store (`~/.ai-game-dev/credentials.json`) the editor plugin auto-adopts. See the `login` section below for its `--project` / `--base-url` / `--force` flags. |
|
|
93
|
+
| `setup-mcp <agent> [path]` | Write the agent's MCP-client config pointing at the project-pinned `<host>/mcp/p/<pin>` URL (so the agent routes to *this* project's editor). Add `--no-pin` for the bare `<host>/mcp` URL. |
|
|
90
94
|
| `setup-skills <agent> [path]` | Generate Godot-MCP skill files (a `SKILL.md`-per-tool-family) under the agent's skills path. `--list` shows each agent's skills support. |
|
|
91
95
|
| `configure [path]` | List / enable / disable tools, prompts, and resources in the project-local `.godot-mcp/features.json`. |
|
|
92
96
|
| `close [path]` | Gracefully stop the Godot editor running for a project (`--force` to hard-kill). |
|
|
@@ -149,6 +153,28 @@ Pass `--url http://localhost:<port>` to target a local/self-hosted server.
|
|
|
149
153
|
|
|
150
154
|

|
|
151
155
|
|
|
156
|
+
## `login`
|
|
157
|
+
|
|
158
|
+
`godot-cli login` authenticates the editor plugin's **cloud** connection to
|
|
159
|
+
[ai-game.dev](https://ai-game.dev) using the **OAuth 2.1 device-authorization flow** (RFC 8628) — it prints
|
|
160
|
+
a short user code + verification URL, opens your browser, and polls until you approve:
|
|
161
|
+
|
|
162
|
+
```bash
|
|
163
|
+
godot-cli login # sign in once per machine (default)
|
|
164
|
+
godot-cli login --project ./MyGame # keep a per-project credential instead
|
|
165
|
+
godot-cli login --base-url <url> # authenticate against a non-default server
|
|
166
|
+
godot-cli login --force # re-authenticate over an existing credential
|
|
167
|
+
```
|
|
168
|
+
|
|
169
|
+
- By default the credential is saved to the shared **machine store** `~/.ai-game-dev/credentials.json`
|
|
170
|
+
(`0600` on POSIX / DPAPI on Windows) — sign in **once per machine** and the Godot editor plugin
|
|
171
|
+
auto-adopts it, so `godot-cli open --mode Cloud` connects with **no `--token`**.
|
|
172
|
+
- `--project <path>` (or the positional `[path]`) keeps a per-project credential
|
|
173
|
+
(`<path>/.godot-mcp/credentials.json`, gitignored) for per-project accounts.
|
|
174
|
+
- The flow persists the **full** credential set (access token + rotating refresh token + expiry) — **no
|
|
175
|
+
personal access token (PAT) is ever minted**. On any failure nothing is written, so an existing
|
|
176
|
+
credential survives a denied / expired / network error intact.
|
|
177
|
+
|
|
152
178
|
## `setup-skills`
|
|
153
179
|
|
|
154
180
|
`godot-cli setup-skills <agent> [path]` generates AI-agent **skill files** for a Godot project under the
|
|
@@ -278,9 +304,15 @@ generate its skill files). Use `--list` on either command to see every supported
|
|
|
278
304
|
|
|
279
305
|
```bash
|
|
280
306
|
godot-cli setup-mcp --list # list every supported agent id
|
|
281
|
-
godot-cli setup-mcp claude-code ./MyGame # write the agent's MCP client config
|
|
307
|
+
godot-cli setup-mcp claude-code ./MyGame # write the agent's MCP client config (pinned to this project)
|
|
308
|
+
godot-cli setup-mcp claude-code ./MyGame --no-pin # write the bare, unpinned <host>/mcp URL instead
|
|
282
309
|
```
|
|
283
310
|
|
|
311
|
+
OAuth-capable agents (Claude Code, Cursor, Codex, Copilot, …) authenticate to the cloud with their **own**
|
|
312
|
+
OAuth handshake, so `setup-mcp` writes them a credential-free, URL-only config — no bearer token is stored
|
|
313
|
+
in the agent file. A static `Authorization` header is written only for a client that cannot OAuth, or when
|
|
314
|
+
you pass an explicit `--token` (a deliberate personal-token opt-in for a self-hosted, required-auth server).
|
|
315
|
+
|
|
284
316
|
> For the full Godot-MCP project documentation, see the
|
|
285
317
|
> [main README](https://github.com/IvanMurzak/Godot-MCP/blob/main/README.md). Backed by
|
|
286
318
|
> **[ai-game.dev](https://ai-game.dev)**.
|
|
@@ -12,6 +12,7 @@ export const setupMcpCommand = new Command('setup-mcp')
|
|
|
12
12
|
.argument('[path]', 'Godot project path (defaults to cwd)')
|
|
13
13
|
.option('--url <url>', 'MCP server host override (the <host>/mcp client URL is derived from it)')
|
|
14
14
|
.option('--token <token>', 'Auth token override (defaults to GODOT_MCP_TOKEN)')
|
|
15
|
+
.option('--no-pin', 'Write an unpinned <host>/mcp URL (default pins /p/<pin> to route to this project)')
|
|
15
16
|
.option('--list', 'List all available agent IDs')
|
|
16
17
|
.action(async (agentId, positionalPath, options) => {
|
|
17
18
|
if (options.list) {
|
|
@@ -35,6 +36,7 @@ export const setupMcpCommand = new Command('setup-mcp')
|
|
|
35
36
|
godotProjectPath: positionalPath,
|
|
36
37
|
url: options.url,
|
|
37
38
|
token: options.token,
|
|
39
|
+
noPin: options.pin === false,
|
|
38
40
|
});
|
|
39
41
|
if (result.kind === 'failure') {
|
|
40
42
|
spinner.error('Failed to write config');
|
|
@@ -50,6 +52,7 @@ export const setupMcpCommand = new Command('setup-mcp')
|
|
|
50
52
|
ui.label('Config file', result.configPath);
|
|
51
53
|
ui.label('Server URL', result.serverUrl);
|
|
52
54
|
ui.label('Server name', MCP_SERVER_NAME);
|
|
55
|
+
ui.label('Routing', result.pinned ? 'pinned to this project (/p/<pin>)' : 'unpinned (--no-pin)');
|
|
53
56
|
for (const warning of result.warnings) {
|
|
54
57
|
console.log('');
|
|
55
58
|
ui.warn(warning);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"setup-mcp.js","sourceRoot":"","sources":["../../src/commands/setup-mcp.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACpC,OAAO,KAAK,EAAE,MAAM,gBAAgB,CAAC;AACrC,OAAO,EAAE,OAAO,EAAE,MAAM,gBAAgB,CAAC;AACzC,OAAO,EAAE,YAAY,EAAE,WAAW,EAAE,cAAc,EAAE,eAAe,EAAE,MAAM,oBAAoB,CAAC;AAChG,OAAO,EAAE,QAAQ,EAAE,MAAM,qBAAqB,CAAC;
|
|
1
|
+
{"version":3,"file":"setup-mcp.js","sourceRoot":"","sources":["../../src/commands/setup-mcp.ts"],"names":[],"mappings":"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"}
|
package/dist/lib/enroll.js
CHANGED
|
@@ -3,7 +3,7 @@ import * as path from 'path';
|
|
|
3
3
|
import { DEFAULT_CLOUD_BASE_URL } from '../utils/connection.js';
|
|
4
4
|
import { redeemEnrollment } from '../utils/enroll.js';
|
|
5
5
|
import { writeMachineCredentials, readMachineCredentials, getMachineCredentialsPath, } from '../utils/machine-credentials.js';
|
|
6
|
-
import {
|
|
6
|
+
import { deriveProjectIdentityV2 } from '../utils/project-identity.js';
|
|
7
7
|
import { writeProjectMarker, getProjectMarkerPath, isLocalhostUrl, upsertPinIntoAgentConfigs, } from '../utils/project-marker.js';
|
|
8
8
|
import { emitProgress } from './progress.js';
|
|
9
9
|
/**
|
|
@@ -63,10 +63,14 @@ export async function enrollPlugin(opts) {
|
|
|
63
63
|
}, opts.storeBaseDir);
|
|
64
64
|
const credentialsPath = getMachineCredentialsPath(opts.storeBaseDir);
|
|
65
65
|
emitProgress(opts.onProgress, { phase: 'enroll-redeemed', message: 'Credential planted', serverTarget });
|
|
66
|
-
// 3. Project marker: server target + pin (+ derived port for a localhost target).
|
|
67
|
-
|
|
66
|
+
// 3. Project marker: server target + pin (+ derived port for a localhost target). The pin/port
|
|
67
|
+
// are derived with the cli-core **v2** algorithm (`\`→`/` normalization — defect B5): on a
|
|
68
|
+
// Windows `path.resolve` backslash root the pin now matches the forward-slash hash the plugin
|
|
69
|
+
// sends, so routing works. v1 hashed the backslash form to a DIFFERENT pin (the B5 break).
|
|
70
|
+
// Derive pin + port from ONE v2 hash of the project root (deriveProjectIdentityV2 hashes once).
|
|
71
|
+
const { pin, port: derivedPort } = deriveProjectIdentityV2(projectPath);
|
|
68
72
|
const localhost = isLocalhostUrl(serverTarget);
|
|
69
|
-
const port = localhost ?
|
|
73
|
+
const port = localhost ? derivedPort : undefined;
|
|
70
74
|
writeProjectMarker(projectPath, {
|
|
71
75
|
serverTarget,
|
|
72
76
|
pin,
|
package/dist/lib/enroll.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"enroll.js","sourceRoot":"","sources":["../../src/lib/enroll.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,IAAI,CAAC;AACzB,OAAO,KAAK,IAAI,MAAM,MAAM,CAAC;AAC7B,OAAO,EAAE,sBAAsB,EAAE,MAAM,wBAAwB,CAAC;AAChE,OAAO,EAAE,gBAAgB,EAAE,MAAM,oBAAoB,CAAC;AACtD,OAAO,EACL,uBAAuB,EACvB,sBAAsB,EACtB,yBAAyB,GAC1B,MAAM,iCAAiC,CAAC;AACzC,OAAO,EAAE,
|
|
1
|
+
{"version":3,"file":"enroll.js","sourceRoot":"","sources":["../../src/lib/enroll.ts"],"names":[],"mappings":"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"}
|
package/dist/lib/setup-mcp.js
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import * as path from 'path';
|
|
2
|
+
import { pinUrl } from '@baizor/gamedev-cli-core';
|
|
2
3
|
import { CLOUD_MCP_URL, DEFAULT_CUSTOM_HOST, ENV_HOST, ENV_TOKEN, MCP_HUB_PATH, } from '../utils/connection.js';
|
|
3
4
|
import { getAgentById, getAgentIds, writeJsonAgentConfig, writeTomlAgentConfig, MCP_SERVER_NAME, } from '../utils/agents.js';
|
|
5
|
+
import { derivePinV2 } from '../utils/project-identity.js';
|
|
4
6
|
import { emitProgress } from './progress.js';
|
|
5
7
|
import { requireExistingPath } from './validation.js';
|
|
6
8
|
/**
|
|
@@ -114,7 +116,17 @@ export async function setupMcp(opts) {
|
|
|
114
116
|
phase: 'start',
|
|
115
117
|
message: `Configuring ${agent.name} for ${projectPath}`,
|
|
116
118
|
});
|
|
117
|
-
|
|
119
|
+
// Pin the URL to THIS project's routing segment by DEFAULT (design 02 §T4 / defect B4): the
|
|
120
|
+
// written config points at `<base>/mcp/p/<pin-v2>`, so an agent session launched in this project
|
|
121
|
+
// folder routes strictly to this project's engine instance even when the account has several
|
|
122
|
+
// editors connected. `--no-pin` (opts.noPin) is the escape hatch → the bare `<base>/mcp` URL.
|
|
123
|
+
// The pin is the shared cli-core **v2** pin (`\`→`/` normalization), byte-identical to what the
|
|
124
|
+
// Godot editor's Configure writes — so a CLI-written and an editor-written config route the same
|
|
125
|
+
// (parity with the C# AgentConfigurator; the pin is a ROUTING segment, never the OAuth resource
|
|
126
|
+
// — decision M8).
|
|
127
|
+
const baseClientUrl = resolveMcpClientUrl(opts.url);
|
|
128
|
+
const pinned = opts.noPin !== true;
|
|
129
|
+
const serverUrl = pinned ? pinUrl(baseClientUrl, derivePinV2(projectPath)) : baseClientUrl;
|
|
118
130
|
// A token supplied EXPLICITLY by the caller (`--token` / the library `token`
|
|
119
131
|
// arg) is a deliberate Flow C PAT opt-in; a token that is only present in the
|
|
120
132
|
// ambient `GODOT_MCP_TOKEN` env (e.g. one a prior `login` set) is NOT.
|
|
@@ -162,6 +174,7 @@ export async function setupMcp(opts) {
|
|
|
162
174
|
agentId: agent.id,
|
|
163
175
|
configPath,
|
|
164
176
|
serverUrl,
|
|
177
|
+
pinned,
|
|
165
178
|
warnings,
|
|
166
179
|
};
|
|
167
180
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"setup-mcp.js","sourceRoot":"","sources":["../../src/lib/setup-mcp.ts"],"names":[],"mappings":"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
|
|
1
|
+
{"version":3,"file":"setup-mcp.js","sourceRoot":"","sources":["../../src/lib/setup-mcp.ts"],"names":[],"mappings":"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"}
|
package/dist/lib/types.d.ts
CHANGED
|
@@ -100,6 +100,12 @@ export interface SetupMcpOptions {
|
|
|
100
100
|
url?: string;
|
|
101
101
|
/** Auth token override. */
|
|
102
102
|
token?: string;
|
|
103
|
+
/**
|
|
104
|
+
* The `--no-pin` escape hatch (design 02 §T4 / defect B4). By default the written URL is PINNED to
|
|
105
|
+
* this project's routing segment (`<base>/mcp/p/<pin-v2>`) so an agent session routes strictly to
|
|
106
|
+
* this project's engine instance. Set `true` to write the unpinned `<base>/mcp` URL instead.
|
|
107
|
+
*/
|
|
108
|
+
noPin?: boolean;
|
|
103
109
|
onProgress?: ProgressCallback;
|
|
104
110
|
}
|
|
105
111
|
export interface SetupMcpSuccess {
|
|
@@ -107,7 +113,10 @@ export interface SetupMcpSuccess {
|
|
|
107
113
|
success: true;
|
|
108
114
|
agentId: string;
|
|
109
115
|
configPath: string;
|
|
116
|
+
/** The MCP-client URL written to the config — pinned (`…/mcp/p/<pin-v2>`) unless `noPin`. */
|
|
110
117
|
serverUrl: string;
|
|
118
|
+
/** True when the URL carries the `/p/<pin-v2>` routing segment (the default; false with `--no-pin`). */
|
|
119
|
+
pinned: boolean;
|
|
111
120
|
warnings: string[];
|
|
112
121
|
}
|
|
113
122
|
export interface SetupMcpFailure {
|
package/dist/utils/addon-deps.js
CHANGED
|
@@ -20,7 +20,7 @@
|
|
|
20
20
|
*/
|
|
21
21
|
export const ADDON_PACKAGE_REFERENCES = [
|
|
22
22
|
{ id: 'com.IvanMurzak.ReflectorNet', version: '5.3.2' },
|
|
23
|
-
{ id: 'com.IvanMurzak.McpPlugin', version: '7.
|
|
23
|
+
{ id: 'com.IvanMurzak.McpPlugin', version: '7.3.0' },
|
|
24
24
|
];
|
|
25
25
|
/**
|
|
26
26
|
* The exact `<EmbeddedResource>`s the consumer's project `.csproj` needs, single-sourced
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { type
|
|
1
|
+
import { type DeviceAuthTransport } from '@baizor/gamedev-cli-core';
|
|
2
2
|
/**
|
|
3
3
|
* Where a successful `login` persists the credential.
|
|
4
4
|
*
|
|
@@ -18,19 +18,34 @@ export type CredentialSink = {
|
|
|
18
18
|
export interface CloudLoginOptions {
|
|
19
19
|
/** Cloud base URL to authenticate against (default https://ai-game.dev). */
|
|
20
20
|
baseUrl?: string;
|
|
21
|
-
/** Client label sent to the authorize endpoint. */
|
|
22
|
-
clientLabel?: string;
|
|
23
21
|
/** Browser opener (injectable for tests; defaults to the real opener). */
|
|
24
22
|
openBrowserImpl?: (url: string) => void;
|
|
25
|
-
/** Device-flow dependency injection (fetch/sleep/clock) for tests. */
|
|
26
|
-
authDeps?: DeviceAuthDeps;
|
|
27
23
|
/** Where to persist the credential. Defaults to the shared machine store. */
|
|
28
24
|
sink?: CredentialSink;
|
|
25
|
+
/**
|
|
26
|
+
* Injectable device-authorization transport (tests). Defaults to the real fetch-backed
|
|
27
|
+
* {@link HttpDeviceAuthTransport} which POSTs to `{base}/oauth/device_authorization` +
|
|
28
|
+
* `{base}/oauth/token` (the OAuth 2.1 device grant — NOT the retired legacy JSON device flow).
|
|
29
|
+
*/
|
|
30
|
+
transport?: DeviceAuthTransport;
|
|
31
|
+
/** Injectable fetch for the default transport (tests). */
|
|
32
|
+
fetchImpl?: typeof fetch;
|
|
33
|
+
/** Injectable poll delay (tests) — bypass the real RFC 8628 polling wait. */
|
|
34
|
+
delay?: (ms: number, signal?: AbortSignal) => Promise<void>;
|
|
35
|
+
/** Injectable clock in ms (tests) — for deadline control. */
|
|
36
|
+
now?: () => number;
|
|
29
37
|
}
|
|
30
38
|
/**
|
|
31
|
-
* Run the
|
|
32
|
-
*
|
|
33
|
-
*
|
|
39
|
+
* Run the OAuth 2.1 device-authorization login (RFC 8628) via `@baizor/gamedev-cli-core`
|
|
40
|
+
* {@link deviceLogin}: request a device+user code, display the user code + verification URL, open the
|
|
41
|
+
* browser, poll `/oauth/token` to completion, and persist the **full** {@link MachineCredentials}
|
|
42
|
+
* (access + rotating refresh + expiry + subject) to the resolved {@link CredentialSink}.
|
|
43
|
+
*
|
|
44
|
+
* This REPLACES the retired legacy JSON device flow (which minted a non-expiring PAT and
|
|
45
|
+
* dropped the refresh/expiry — defects B2/B3). The client id is the product id `godot-cli`
|
|
46
|
+
* ({@link godotAdapter.clientId}) with scope `mcp:plugin`, and **no PAT is ever minted**. On any
|
|
47
|
+
* failure NOTHING is written (design 03 F4) — the store survives a denied/expired/network error
|
|
48
|
+
* intact.
|
|
34
49
|
*
|
|
35
50
|
* Returns the access token on success, or null on failure (errors are printed).
|
|
36
51
|
*/
|
|
@@ -2,39 +2,59 @@ import * as ui from './ui.js';
|
|
|
2
2
|
import { DEFAULT_CLOUD_BASE_URL } from './connection.js';
|
|
3
3
|
import { readCredentials, writeCredentials } from './credentials.js';
|
|
4
4
|
import { readMachineCredentials, writeMachineCredentials } from './machine-credentials.js';
|
|
5
|
-
import { deviceAuthFlow } from './auth.js';
|
|
6
5
|
import { openBrowser } from './browser.js';
|
|
6
|
+
import { deviceLogin, HttpDeviceAuthTransport, godotAdapter, DEFAULT_PLUGIN_SCOPE, } from '@baizor/gamedev-cli-core';
|
|
7
7
|
/**
|
|
8
|
-
* Run the
|
|
9
|
-
*
|
|
10
|
-
*
|
|
8
|
+
* Run the OAuth 2.1 device-authorization login (RFC 8628) via `@baizor/gamedev-cli-core`
|
|
9
|
+
* {@link deviceLogin}: request a device+user code, display the user code + verification URL, open the
|
|
10
|
+
* browser, poll `/oauth/token` to completion, and persist the **full** {@link MachineCredentials}
|
|
11
|
+
* (access + rotating refresh + expiry + subject) to the resolved {@link CredentialSink}.
|
|
12
|
+
*
|
|
13
|
+
* This REPLACES the retired legacy JSON device flow (which minted a non-expiring PAT and
|
|
14
|
+
* dropped the refresh/expiry — defects B2/B3). The client id is the product id `godot-cli`
|
|
15
|
+
* ({@link godotAdapter.clientId}) with scope `mcp:plugin`, and **no PAT is ever minted**. On any
|
|
16
|
+
* failure NOTHING is written (design 03 F4) — the store survives a denied/expired/network error
|
|
17
|
+
* intact.
|
|
11
18
|
*
|
|
12
19
|
* Returns the access token on success, or null on failure (errors are printed).
|
|
13
20
|
*/
|
|
14
21
|
export async function runCloudLogin(options = {}) {
|
|
15
22
|
const baseUrl = (options.baseUrl ?? DEFAULT_CLOUD_BASE_URL).replace(/\/$/, '');
|
|
16
|
-
const clientLabel = options.clientLabel ?? 'Godot-MCP CLI';
|
|
17
23
|
const openBrowserImpl = options.openBrowserImpl ?? openBrowser;
|
|
18
24
|
const sink = options.sink ?? { kind: 'machine' };
|
|
19
25
|
let spinner;
|
|
26
|
+
const transport = options.transport ??
|
|
27
|
+
new HttpDeviceAuthTransport({
|
|
28
|
+
serverBaseUrl: baseUrl,
|
|
29
|
+
clientId: godotAdapter.clientId, // 'godot-cli'
|
|
30
|
+
scope: DEFAULT_PLUGIN_SCOPE, // 'mcp:plugin'
|
|
31
|
+
fetchImpl: options.fetchImpl,
|
|
32
|
+
});
|
|
20
33
|
try {
|
|
21
|
-
const result = await
|
|
22
|
-
|
|
34
|
+
const result = await deviceLogin({
|
|
35
|
+
serverBaseUrl: baseUrl,
|
|
36
|
+
clientId: godotAdapter.clientId,
|
|
37
|
+
scope: DEFAULT_PLUGIN_SCOPE,
|
|
38
|
+
serverTarget: baseUrl,
|
|
39
|
+
transport,
|
|
40
|
+
delay: options.delay,
|
|
41
|
+
now: options.now,
|
|
42
|
+
onUserCode: (userCode, verificationUri) => {
|
|
23
43
|
ui.info('Open this URL to authorize:');
|
|
24
44
|
console.log();
|
|
25
|
-
console.log(` ${
|
|
45
|
+
console.log(` ${verificationUri}`);
|
|
26
46
|
console.log();
|
|
27
47
|
ui.label('Code', userCode);
|
|
28
|
-
openBrowserImpl(verificationUrl);
|
|
29
48
|
},
|
|
30
49
|
onPolling: () => {
|
|
31
50
|
spinner = ui.startSpinner('Waiting for authorization...');
|
|
32
51
|
},
|
|
33
|
-
|
|
34
|
-
|
|
52
|
+
openBrowser: openBrowserImpl,
|
|
53
|
+
});
|
|
54
|
+
if (result.ok) {
|
|
35
55
|
spinner?.success('Authorized');
|
|
36
|
-
persistCredential(sink, result.
|
|
37
|
-
return result.accessToken;
|
|
56
|
+
persistCredential(sink, result.credentials, baseUrl);
|
|
57
|
+
return result.credentials.accessToken ?? null;
|
|
38
58
|
}
|
|
39
59
|
spinner?.stop();
|
|
40
60
|
ui.error(result.message);
|
|
@@ -52,9 +72,11 @@ export async function runCloudLogin(options = {}) {
|
|
|
52
72
|
return null;
|
|
53
73
|
}
|
|
54
74
|
}
|
|
55
|
-
function persistCredential(sink,
|
|
75
|
+
function persistCredential(sink, credentials, baseUrl) {
|
|
56
76
|
if (sink.kind === 'project') {
|
|
57
|
-
//
|
|
77
|
+
// Legacy per-project override: keep the project store's `cloudToken`/`cloudBaseUrl` shape a
|
|
78
|
+
// `--project` login and `open --mode Cloud` read back (credentials.ts). A corrupt existing
|
|
79
|
+
// credentials file must not block a fresh login.
|
|
58
80
|
let existing = {};
|
|
59
81
|
try {
|
|
60
82
|
existing = readCredentials(sink.projectPath) ?? {};
|
|
@@ -64,13 +86,14 @@ function persistCredential(sink, accessToken, baseUrl) {
|
|
|
64
86
|
}
|
|
65
87
|
writeCredentials(sink.projectPath, {
|
|
66
88
|
...existing,
|
|
67
|
-
cloudToken: accessToken,
|
|
89
|
+
cloudToken: credentials.accessToken,
|
|
68
90
|
cloudBaseUrl: baseUrl,
|
|
69
91
|
});
|
|
70
92
|
return;
|
|
71
93
|
}
|
|
72
|
-
// Machine store:
|
|
73
|
-
//
|
|
94
|
+
// Machine store: persist the FULL credential set (access + refresh + expiry + subject — the B3
|
|
95
|
+
// fix), preserving any prior identity / forward-compat fields, and recording the server target
|
|
96
|
+
// the credential was issued for.
|
|
74
97
|
let existing = {};
|
|
75
98
|
try {
|
|
76
99
|
existing = readMachineCredentials(sink.storeBaseDir) ?? {};
|
|
@@ -80,8 +103,8 @@ function persistCredential(sink, accessToken, baseUrl) {
|
|
|
80
103
|
}
|
|
81
104
|
writeMachineCredentials({
|
|
82
105
|
...existing,
|
|
106
|
+
...credentials,
|
|
83
107
|
version: 1,
|
|
84
|
-
accessToken,
|
|
85
108
|
serverTarget: baseUrl,
|
|
86
109
|
}, sink.storeBaseDir);
|
|
87
110
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cloud-login.js","sourceRoot":"","sources":["../../src/utils/cloud-login.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,SAAS,CAAC;AAC9B,OAAO,EAAE,sBAAsB,EAAE,MAAM,iBAAiB,CAAC;AACzD,OAAO,EAAE,eAAe,EAAE,gBAAgB,EAAE,MAAM,kBAAkB,CAAC;AACrE,OAAO,EAAE,sBAAsB,EAAE,uBAAuB,EAAE,MAAM,0BAA0B,CAAC;AAC3F,OAAO,EAAE,
|
|
1
|
+
{"version":3,"file":"cloud-login.js","sourceRoot":"","sources":["../../src/utils/cloud-login.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,76 +1,22 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* The single canonical derivation of a project's **routing pin** and its
|
|
3
|
-
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
* so an agent session launched in a project folder routes strictly to that project's engine
|
|
9
|
-
* instance (design `06-engine-plugins.md` § D14/D15).
|
|
2
|
+
* The single canonical derivation of a project's **routing pin** and its **deterministic local
|
|
3
|
+
* port** — now a THIN RE-EXPORT of the shared `@baizor/gamedev-cli-core` `project-identity` module
|
|
4
|
+
* (auth-fixes design 02 §T3 / T7, defect B5). cli-core is the ONE TypeScript source of truth, gated
|
|
5
|
+
* byte-for-byte against the same golden vectors as the C# reference
|
|
6
|
+
* `com.IvanMurzak.McpPlugin.AgentConfig.ProjectIdentity` — so the Godot CLI, the Godot addon, and the
|
|
7
|
+
* other two engine CLIs all derive identical pins/ports.
|
|
10
8
|
*
|
|
11
|
-
*
|
|
12
|
-
*
|
|
13
|
-
*
|
|
14
|
-
*
|
|
15
|
-
*
|
|
16
|
-
*
|
|
17
|
-
*
|
|
9
|
+
* Two normalizations ship side by side:
|
|
10
|
+
* - **v1** (`derivePin` / `derivePort` / `normalize` / …) — the legacy algorithm, kept verbatim so
|
|
11
|
+
* old `.mcp.json` pins keep matching during the dual-hash transition (decision M1). Separators
|
|
12
|
+
* are NOT converted: `C:\a` and `C:/a` hash differently.
|
|
13
|
+
* - **v2** (`derivePinV2` / `derivePortV2` / `normalizeV2` / …) — adds ONE step (convert `\`→`/`)
|
|
14
|
+
* so a Windows root reported with backslashes and the same root with forward slashes hash
|
|
15
|
+
* IDENTICALLY. This is the **B5 fix**: the pin `enroll` / `setup-mcp` write now matches the
|
|
16
|
+
* forward-slash hash the plugin sends, even from a Windows `path.resolve` backslash root.
|
|
18
17
|
*
|
|
19
|
-
*
|
|
20
|
-
*
|
|
21
|
-
* `tests/project-identity.test.ts`.
|
|
18
|
+
* The local reimplementation this file used to carry was removed in the cli-core migration (task
|
|
19
|
+
* i1-godot-cli-migration) so the two can never silently diverge.
|
|
22
20
|
*/
|
|
23
|
-
|
|
24
|
-
export
|
|
25
|
-
/** Inclusive upper bound of the deterministic local-port range. */
|
|
26
|
-
export declare const MAX_PORT = 29999;
|
|
27
|
-
/** Number of ports in the deterministic range (10000). */
|
|
28
|
-
export declare const PORT_RANGE: number;
|
|
29
|
-
/** Number of hex characters in the routing pin (first 4 bytes of the hash). */
|
|
30
|
-
export declare const PIN_LENGTH = 8;
|
|
31
|
-
/** The resolved identity for a project root. */
|
|
32
|
-
export interface ProjectIdentity {
|
|
33
|
-
/** The routing pin: first 8 lowercase hex chars of the SHA-256 of the normalized project root. */
|
|
34
|
-
pin: string;
|
|
35
|
-
/** The resolved local port — the hash-derived port unless an explicit override was supplied. */
|
|
36
|
-
port: number;
|
|
37
|
-
/** True when {@link port} came from an explicit user override rather than the hash. */
|
|
38
|
-
portIsOverridden: boolean;
|
|
39
|
-
}
|
|
40
|
-
/**
|
|
41
|
-
* Invariant lowercasing that matches C# `string.ToLowerInvariant()`.
|
|
42
|
-
*
|
|
43
|
-
* JS `String.prototype.toLowerCase()` and C# `ToLowerInvariant()` disagree on some Unicode
|
|
44
|
-
* characters. The one that matters for real project paths — and the one the golden-vector file
|
|
45
|
-
* explicitly pins (`unicodeDivergence`) — is **U+0130 LATIN CAPITAL LETTER I WITH DOT ABOVE**:
|
|
46
|
-
* C# `ToLowerInvariant` leaves it unchanged, whereas a naive JS `toLowerCase()` folds it to
|
|
47
|
-
* `i` + U+0307 (COMBINING DOT ABOVE), producing a DIFFERENT hash. Preserve U+0130 so the TS
|
|
48
|
-
* derivation reproduces the canonical C# value byte-for-byte; every other character lowercases
|
|
49
|
-
* identically under both implementations.
|
|
50
|
-
*/
|
|
51
|
-
export declare function toLowerInvariant(value: string): string;
|
|
52
|
-
/**
|
|
53
|
-
* The exact string that is UTF-8/SHA-256 hashed: the project root with trailing directory
|
|
54
|
-
* separators trimmed, then invariant-lowercased. Exposed so callers/tests can reproduce the
|
|
55
|
-
* pre-hash string.
|
|
56
|
-
*/
|
|
57
|
-
export declare function normalize(projectRoot: string): string;
|
|
58
|
-
/** The routing pin only (first 8 lowercase hex chars of the hash). Never affected by overrides. */
|
|
59
|
-
export declare function derivePin(projectRoot: string): string;
|
|
60
|
-
/**
|
|
61
|
-
* The pure hash-derived port (ignores any override). Byte-for-byte equivalent of the shipped
|
|
62
|
-
* Unity `GeneratePortFromDirectory` when given the same directory string.
|
|
63
|
-
*/
|
|
64
|
-
export declare function derivePort(projectRoot: string): number;
|
|
65
|
-
/**
|
|
66
|
-
* The FULL project-path hash: the complete 64-char lowercase hex SHA-256 of the normalized
|
|
67
|
-
* project root — the `projectPathHash` an engine plugin sends in its hub instance-metadata
|
|
68
|
-
* handshake. The routing pin is a case-insensitive prefix of this value by construction.
|
|
69
|
-
*/
|
|
70
|
-
export declare function deriveProjectPathHash(projectRoot: string): string;
|
|
71
|
-
/**
|
|
72
|
-
* Derive the identity for `projectRoot`. When `portOverride` is non-null (the user's explicit
|
|
73
|
-
* override from the project marker) it always wins for {@link ProjectIdentity.port}; the
|
|
74
|
-
* {@link ProjectIdentity.pin} is always hash-derived.
|
|
75
|
-
*/
|
|
76
|
-
export declare function deriveProjectIdentity(projectRoot: string, portOverride?: number | null): ProjectIdentity;
|
|
21
|
+
export { MIN_PORT, MAX_PORT, PORT_RANGE, PIN_LENGTH, toLowerInvariant, normalize, derivePin, derivePort, deriveProjectPathHash, deriveProjectIdentity, normalizeV2, derivePinV2, derivePortV2, deriveProjectPathHashV2, deriveProjectIdentityV2, } from '@baizor/gamedev-cli-core';
|
|
22
|
+
export type { ProjectIdentity } from '@baizor/gamedev-cli-core';
|
|
@@ -1,111 +1,26 @@
|
|
|
1
|
-
import { createHash } from 'crypto';
|
|
2
1
|
/**
|
|
3
|
-
* The single canonical derivation of a project's **routing pin** and its
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
* so an agent session launched in a project folder routes strictly to that project's engine
|
|
10
|
-
* instance (design `06-engine-plugins.md` § D14/D15).
|
|
2
|
+
* The single canonical derivation of a project's **routing pin** and its **deterministic local
|
|
3
|
+
* port** — now a THIN RE-EXPORT of the shared `@baizor/gamedev-cli-core` `project-identity` module
|
|
4
|
+
* (auth-fixes design 02 §T3 / T7, defect B5). cli-core is the ONE TypeScript source of truth, gated
|
|
5
|
+
* byte-for-byte against the same golden vectors as the C# reference
|
|
6
|
+
* `com.IvanMurzak.McpPlugin.AgentConfig.ProjectIdentity` — so the Godot CLI, the Godot addon, and the
|
|
7
|
+
* other two engine CLIs all derive identical pins/ports.
|
|
11
8
|
*
|
|
12
|
-
*
|
|
13
|
-
*
|
|
14
|
-
*
|
|
15
|
-
*
|
|
16
|
-
*
|
|
17
|
-
*
|
|
18
|
-
*
|
|
9
|
+
* Two normalizations ship side by side:
|
|
10
|
+
* - **v1** (`derivePin` / `derivePort` / `normalize` / …) — the legacy algorithm, kept verbatim so
|
|
11
|
+
* old `.mcp.json` pins keep matching during the dual-hash transition (decision M1). Separators
|
|
12
|
+
* are NOT converted: `C:\a` and `C:/a` hash differently.
|
|
13
|
+
* - **v2** (`derivePinV2` / `derivePortV2` / `normalizeV2` / …) — adds ONE step (convert `\`→`/`)
|
|
14
|
+
* so a Windows root reported with backslashes and the same root with forward slashes hash
|
|
15
|
+
* IDENTICALLY. This is the **B5 fix**: the pin `enroll` / `setup-mcp` write now matches the
|
|
16
|
+
* forward-slash hash the plugin sends, even from a Windows `path.resolve` backslash root.
|
|
19
17
|
*
|
|
20
|
-
*
|
|
21
|
-
*
|
|
22
|
-
* `tests/project-identity.test.ts`.
|
|
18
|
+
* The local reimplementation this file used to carry was removed in the cli-core migration (task
|
|
19
|
+
* i1-godot-cli-migration) so the two can never silently diverge.
|
|
23
20
|
*/
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
export const PORT_RANGE = MAX_PORT - MIN_PORT + 1;
|
|
30
|
-
/** Number of hex characters in the routing pin (first 4 bytes of the hash). */
|
|
31
|
-
export const PIN_LENGTH = 8;
|
|
32
|
-
const SEPARATOR_FORWARD = '/';
|
|
33
|
-
const SEPARATOR_BACK = String.fromCharCode(92); // backslash
|
|
34
|
-
/**
|
|
35
|
-
* Invariant lowercasing that matches C# `string.ToLowerInvariant()`.
|
|
36
|
-
*
|
|
37
|
-
* JS `String.prototype.toLowerCase()` and C# `ToLowerInvariant()` disagree on some Unicode
|
|
38
|
-
* characters. The one that matters for real project paths — and the one the golden-vector file
|
|
39
|
-
* explicitly pins (`unicodeDivergence`) — is **U+0130 LATIN CAPITAL LETTER I WITH DOT ABOVE**:
|
|
40
|
-
* C# `ToLowerInvariant` leaves it unchanged, whereas a naive JS `toLowerCase()` folds it to
|
|
41
|
-
* `i` + U+0307 (COMBINING DOT ABOVE), producing a DIFFERENT hash. Preserve U+0130 so the TS
|
|
42
|
-
* derivation reproduces the canonical C# value byte-for-byte; every other character lowercases
|
|
43
|
-
* identically under both implementations.
|
|
44
|
-
*/
|
|
45
|
-
export function toLowerInvariant(value) {
|
|
46
|
-
let result = '';
|
|
47
|
-
for (const ch of value) {
|
|
48
|
-
result += ch === 'İ' ? ch : ch.toLowerCase();
|
|
49
|
-
}
|
|
50
|
-
return result;
|
|
51
|
-
}
|
|
52
|
-
/**
|
|
53
|
-
* The exact string that is UTF-8/SHA-256 hashed: the project root with trailing directory
|
|
54
|
-
* separators trimmed, then invariant-lowercased. Exposed so callers/tests can reproduce the
|
|
55
|
-
* pre-hash string.
|
|
56
|
-
*/
|
|
57
|
-
export function normalize(projectRoot) {
|
|
58
|
-
return toLowerInvariant(trimTrailingSeparators(projectRoot));
|
|
59
|
-
}
|
|
60
|
-
/** The routing pin only (first 8 lowercase hex chars of the hash). Never affected by overrides. */
|
|
61
|
-
export function derivePin(projectRoot) {
|
|
62
|
-
return hashOf(projectRoot).subarray(0, PIN_LENGTH / 2).toString('hex');
|
|
63
|
-
}
|
|
64
|
-
/**
|
|
65
|
-
* The pure hash-derived port (ignores any override). Byte-for-byte equivalent of the shipped
|
|
66
|
-
* Unity `GeneratePortFromDirectory` when given the same directory string.
|
|
67
|
-
*/
|
|
68
|
-
export function derivePort(projectRoot) {
|
|
69
|
-
return portFromHash(hashOf(projectRoot));
|
|
70
|
-
}
|
|
71
|
-
/**
|
|
72
|
-
* The FULL project-path hash: the complete 64-char lowercase hex SHA-256 of the normalized
|
|
73
|
-
* project root — the `projectPathHash` an engine plugin sends in its hub instance-metadata
|
|
74
|
-
* handshake. The routing pin is a case-insensitive prefix of this value by construction.
|
|
75
|
-
*/
|
|
76
|
-
export function deriveProjectPathHash(projectRoot) {
|
|
77
|
-
return hashOf(projectRoot).toString('hex');
|
|
78
|
-
}
|
|
79
|
-
/**
|
|
80
|
-
* Derive the identity for `projectRoot`. When `portOverride` is non-null (the user's explicit
|
|
81
|
-
* override from the project marker) it always wins for {@link ProjectIdentity.port}; the
|
|
82
|
-
* {@link ProjectIdentity.pin} is always hash-derived.
|
|
83
|
-
*/
|
|
84
|
-
export function deriveProjectIdentity(projectRoot, portOverride) {
|
|
85
|
-
const hash = hashOf(projectRoot);
|
|
86
|
-
const pin = hash.subarray(0, PIN_LENGTH / 2).toString('hex');
|
|
87
|
-
if (portOverride !== undefined && portOverride !== null) {
|
|
88
|
-
return { pin, port: portOverride, portIsOverridden: true };
|
|
89
|
-
}
|
|
90
|
-
return { pin, port: portFromHash(hash), portIsOverridden: false };
|
|
91
|
-
}
|
|
92
|
-
function hashOf(projectRoot) {
|
|
93
|
-
if (projectRoot === null || projectRoot === undefined) {
|
|
94
|
-
throw new TypeError('projectRoot must be a string');
|
|
95
|
-
}
|
|
96
|
-
return createHash('sha256').update(Buffer.from(normalize(projectRoot), 'utf8')).digest();
|
|
97
|
-
}
|
|
98
|
-
function portFromHash(hash) {
|
|
99
|
-
// First 4 bytes as an explicit little-endian uint32 — matches the C# byte-shift
|
|
100
|
-
// (`hash[0] | hash[1]<<8 | hash[2]<<16 | hash[3]<<24`) and is CPU-endianness independent.
|
|
101
|
-
const value = hash.readUInt32LE(0);
|
|
102
|
-
return MIN_PORT + (value % PORT_RANGE);
|
|
103
|
-
}
|
|
104
|
-
function trimTrailingSeparators(path) {
|
|
105
|
-
let end = path.length;
|
|
106
|
-
while (end > 1 && (path[end - 1] === SEPARATOR_FORWARD || path[end - 1] === SEPARATOR_BACK)) {
|
|
107
|
-
end--;
|
|
108
|
-
}
|
|
109
|
-
return end === path.length ? path : path.slice(0, end);
|
|
110
|
-
}
|
|
21
|
+
export { MIN_PORT, MAX_PORT, PORT_RANGE, PIN_LENGTH, toLowerInvariant,
|
|
22
|
+
// v1 (legacy, separator-sensitive)
|
|
23
|
+
normalize, derivePin, derivePort, deriveProjectPathHash, deriveProjectIdentity,
|
|
24
|
+
// v2 (B5 fix — `\`→`/` normalization; what configurators now emit)
|
|
25
|
+
normalizeV2, derivePinV2, derivePortV2, deriveProjectPathHashV2, deriveProjectIdentityV2, } from '@baizor/gamedev-cli-core';
|
|
111
26
|
//# sourceMappingURL=project-identity.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"project-identity.js","sourceRoot":"","sources":["../../src/utils/project-identity.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"project-identity.js","sourceRoot":"","sources":["../../src/utils/project-identity.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AAEH,OAAO,EACL,QAAQ,EACR,QAAQ,EACR,UAAU,EACV,UAAU,EACV,gBAAgB;AAChB,mCAAmC;AACnC,SAAS,EACT,SAAS,EACT,UAAU,EACV,qBAAqB,EACrB,qBAAqB;AACrB,mEAAmE;AACnE,WAAW,EACX,WAAW,EACX,YAAY,EACZ,uBAAuB,EACvB,uBAAuB,GACxB,MAAM,0BAA0B,CAAC"}
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
* download entirely (offline / CI). Bumping the addon's server pin requires
|
|
6
6
|
* bumping this in the same PR or the parity test goes red.
|
|
7
7
|
*/
|
|
8
|
-
export declare const DEFAULT_SERVER_VERSION = "9.
|
|
8
|
+
export declare const DEFAULT_SERVER_VERSION = "9.2.0";
|
|
9
9
|
/** GitHub-release host the server zip + manifest are downloaded from. NOTHING else is trusted. */
|
|
10
10
|
export declare const TRUSTED_DOWNLOAD_HOST = "github.com";
|
|
11
11
|
/** The `owner/repo` the shared server releases live under. */
|
|
@@ -18,7 +18,7 @@ import { createHash } from 'crypto';
|
|
|
18
18
|
* download entirely (offline / CI). Bumping the addon's server pin requires
|
|
19
19
|
* bumping this in the same PR or the parity test goes red.
|
|
20
20
|
*/
|
|
21
|
-
export const DEFAULT_SERVER_VERSION = '9.
|
|
21
|
+
export const DEFAULT_SERVER_VERSION = '9.2.0';
|
|
22
22
|
/** GitHub-release host the server zip + manifest are downloaded from. NOTHING else is trusted. */
|
|
23
23
|
export const TRUSTED_DOWNLOAD_HOST = 'github.com';
|
|
24
24
|
/** The `owner/repo` the shared server releases live under. */
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "godot-cli",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.19.0",
|
|
4
4
|
"description": "Cross-platform CLI tool for Godot-MCP (Skills & MCP). Resolves and launches the Godot editor with MCP connection env vars, runs MCP/system tools over HTTP, probes server health, configures AI agents (Claude Code, Cursor, VS Code, …), and enables/disables the godot_mcp addon. Works with Claude Code, Cursor, Copilot, and any MCP client.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/lib.js",
|
|
@@ -62,6 +62,7 @@
|
|
|
62
62
|
"node": "^20.19.0 || >=22.12.0"
|
|
63
63
|
},
|
|
64
64
|
"dependencies": {
|
|
65
|
+
"@baizor/gamedev-cli-core": "^0.1.0",
|
|
65
66
|
"chalk": "^5.6.2",
|
|
66
67
|
"commander": "^13.1.0",
|
|
67
68
|
"yocto-spinner": "^1.1.0"
|
package/dist/utils/auth.d.ts
DELETED
|
@@ -1,42 +0,0 @@
|
|
|
1
|
-
export interface DeviceAuthorizeResponse {
|
|
2
|
-
device_code: string;
|
|
3
|
-
user_code: string;
|
|
4
|
-
verification_uri: string;
|
|
5
|
-
verification_uri_complete: string;
|
|
6
|
-
expires_in: number;
|
|
7
|
-
interval: number;
|
|
8
|
-
}
|
|
9
|
-
export type DeviceAuthResult = {
|
|
10
|
-
success: true;
|
|
11
|
-
accessToken: string;
|
|
12
|
-
} | {
|
|
13
|
-
success: false;
|
|
14
|
-
reason: 'expired' | 'denied' | 'error';
|
|
15
|
-
message: string;
|
|
16
|
-
};
|
|
17
|
-
export interface DeviceAuthCallbacks {
|
|
18
|
-
/** Invoked once the device+user codes are issued so the caller can display them / open a browser. */
|
|
19
|
-
onUserCode: (userCode: string, verificationUrl: string) => void;
|
|
20
|
-
/** Invoked once, right before polling begins (e.g. to start a spinner). */
|
|
21
|
-
onPolling?: () => void;
|
|
22
|
-
}
|
|
23
|
-
/** Injectable dependencies — all default to the real implementations. */
|
|
24
|
-
export interface DeviceAuthDeps {
|
|
25
|
-
/** HTTP client (defaults to global `fetch`). */
|
|
26
|
-
fetchImpl?: typeof fetch;
|
|
27
|
-
/** Delay primitive (defaults to a real `setTimeout` sleep). */
|
|
28
|
-
sleepImpl?: (ms: number) => Promise<void>;
|
|
29
|
-
/** Monotonic clock in ms (defaults to `Date.now`). */
|
|
30
|
-
nowImpl?: () => number;
|
|
31
|
-
/** Floor for the poll interval in ms (defaults to 5000). */
|
|
32
|
-
minIntervalMs?: number;
|
|
33
|
-
}
|
|
34
|
-
/**
|
|
35
|
-
* Run the RFC 8628 Device Authorization Grant flow against `baseUrl`.
|
|
36
|
-
*
|
|
37
|
-
* 1. POST `/api/auth/device/authorize` → device_code + user_code.
|
|
38
|
-
* 2. Invoke `onUserCode` so the caller can display instructions / open a browser.
|
|
39
|
-
* 3. Poll `/api/auth/device/token` until success, denial, or expiry, honoring
|
|
40
|
-
* `authorization_pending` / `slow_down` / `access_denied` / `expired_token`.
|
|
41
|
-
*/
|
|
42
|
-
export declare function deviceAuthFlow(baseUrl: string, clientLabel: string, callbacks: DeviceAuthCallbacks, deps?: DeviceAuthDeps): Promise<DeviceAuthResult>;
|
package/dist/utils/auth.js
DELETED
|
@@ -1,113 +0,0 @@
|
|
|
1
|
-
import { verbose } from './ui.js';
|
|
2
|
-
// ─── Flow ────────────────────────────────────────────────────────────────────
|
|
3
|
-
/**
|
|
4
|
-
* Run the RFC 8628 Device Authorization Grant flow against `baseUrl`.
|
|
5
|
-
*
|
|
6
|
-
* 1. POST `/api/auth/device/authorize` → device_code + user_code.
|
|
7
|
-
* 2. Invoke `onUserCode` so the caller can display instructions / open a browser.
|
|
8
|
-
* 3. Poll `/api/auth/device/token` until success, denial, or expiry, honoring
|
|
9
|
-
* `authorization_pending` / `slow_down` / `access_denied` / `expired_token`.
|
|
10
|
-
*/
|
|
11
|
-
export async function deviceAuthFlow(baseUrl, clientLabel, callbacks, deps = {}) {
|
|
12
|
-
const doFetch = deps.fetchImpl ?? fetch;
|
|
13
|
-
const sleep = deps.sleepImpl ?? defaultSleep;
|
|
14
|
-
const now = deps.nowImpl ?? (() => Date.now());
|
|
15
|
-
const minIntervalMs = deps.minIntervalMs ?? 5000;
|
|
16
|
-
const authorizeUrl = `${baseUrl}/api/auth/device/authorize`;
|
|
17
|
-
verbose(`POST ${authorizeUrl}`);
|
|
18
|
-
const initResponse = await fetchWithTimeout(doFetch, authorizeUrl, {
|
|
19
|
-
method: 'POST',
|
|
20
|
-
headers: { 'Content-Type': 'application/json' },
|
|
21
|
-
body: JSON.stringify({ client_label: clientLabel }),
|
|
22
|
-
}, 30000);
|
|
23
|
-
if (!initResponse.ok) {
|
|
24
|
-
const text = await initResponse.text();
|
|
25
|
-
return {
|
|
26
|
-
success: false,
|
|
27
|
-
reason: 'error',
|
|
28
|
-
message: `Failed to initiate device auth (HTTP ${initResponse.status}): ${text}`,
|
|
29
|
-
};
|
|
30
|
-
}
|
|
31
|
-
const auth = (await initResponse.json());
|
|
32
|
-
verbose(`Device code received, user code: ${auth.user_code}, expires in ${auth.expires_in}s`);
|
|
33
|
-
callbacks.onUserCode(auth.user_code, auth.verification_uri_complete);
|
|
34
|
-
callbacks.onPolling?.();
|
|
35
|
-
const tokenUrl = `${baseUrl}/api/auth/device/token`;
|
|
36
|
-
const deadline = now() + auth.expires_in * 1000;
|
|
37
|
-
let interval = Math.max(auth.interval * 1000, minIntervalMs);
|
|
38
|
-
while (now() < deadline) {
|
|
39
|
-
await sleep(interval);
|
|
40
|
-
if (now() >= deadline)
|
|
41
|
-
break;
|
|
42
|
-
verbose(`Polling ${tokenUrl} (interval=${interval / 1000}s)`);
|
|
43
|
-
const pollResponse = await fetchWithTimeout(doFetch, tokenUrl, {
|
|
44
|
-
method: 'POST',
|
|
45
|
-
headers: { 'Content-Type': 'application/json' },
|
|
46
|
-
body: JSON.stringify({
|
|
47
|
-
device_code: auth.device_code,
|
|
48
|
-
grant_type: 'urn:ietf:params:oauth:grant-type:device_code',
|
|
49
|
-
}),
|
|
50
|
-
}, 15000);
|
|
51
|
-
if (pollResponse.ok) {
|
|
52
|
-
const data = (await pollResponse.json());
|
|
53
|
-
return { success: true, accessToken: data.access_token };
|
|
54
|
-
}
|
|
55
|
-
let errorData;
|
|
56
|
-
try {
|
|
57
|
-
errorData = (await pollResponse.json());
|
|
58
|
-
}
|
|
59
|
-
catch {
|
|
60
|
-
return {
|
|
61
|
-
success: false,
|
|
62
|
-
reason: 'error',
|
|
63
|
-
message: `Unexpected response from token endpoint (HTTP ${pollResponse.status})`,
|
|
64
|
-
};
|
|
65
|
-
}
|
|
66
|
-
verbose(`Poll response: ${errorData.error} — ${errorData.error_description ?? ''}`);
|
|
67
|
-
switch (errorData.error) {
|
|
68
|
-
case 'authorization_pending':
|
|
69
|
-
break;
|
|
70
|
-
case 'slow_down':
|
|
71
|
-
interval = Math.min(interval + 5000, 30000);
|
|
72
|
-
verbose(`Slowing down, new interval: ${interval / 1000}s`);
|
|
73
|
-
break;
|
|
74
|
-
case 'expired_token':
|
|
75
|
-
return {
|
|
76
|
-
success: false,
|
|
77
|
-
reason: 'expired',
|
|
78
|
-
message: errorData.error_description ?? 'Device code expired. Please try again.',
|
|
79
|
-
};
|
|
80
|
-
case 'access_denied':
|
|
81
|
-
return {
|
|
82
|
-
success: false,
|
|
83
|
-
reason: 'denied',
|
|
84
|
-
message: errorData.error_description ?? 'Authorization was denied.',
|
|
85
|
-
};
|
|
86
|
-
default:
|
|
87
|
-
return {
|
|
88
|
-
success: false,
|
|
89
|
-
reason: 'error',
|
|
90
|
-
message: errorData.error_description ?? `Unexpected error: ${errorData.error}`,
|
|
91
|
-
};
|
|
92
|
-
}
|
|
93
|
-
}
|
|
94
|
-
return {
|
|
95
|
-
success: false,
|
|
96
|
-
reason: 'expired',
|
|
97
|
-
message: 'Device code expired. Please try again.',
|
|
98
|
-
};
|
|
99
|
-
}
|
|
100
|
-
function defaultSleep(ms) {
|
|
101
|
-
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
102
|
-
}
|
|
103
|
-
async function fetchWithTimeout(doFetch, url, options, timeoutMs) {
|
|
104
|
-
const controller = new AbortController();
|
|
105
|
-
const timer = setTimeout(() => controller.abort(), timeoutMs);
|
|
106
|
-
try {
|
|
107
|
-
return await doFetch(url, { ...options, signal: controller.signal });
|
|
108
|
-
}
|
|
109
|
-
finally {
|
|
110
|
-
clearTimeout(timer);
|
|
111
|
-
}
|
|
112
|
-
}
|
|
113
|
-
//# sourceMappingURL=auth.js.map
|
package/dist/utils/auth.js.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"auth.js","sourceRoot":"","sources":["../../src/utils/auth.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,SAAS,CAAC;AAqDlC,gFAAgF;AAEhF;;;;;;;GAOG;AACH,MAAM,CAAC,KAAK,UAAU,cAAc,CAClC,OAAe,EACf,WAAmB,EACnB,SAA8B,EAC9B,OAAuB,EAAE;IAEzB,MAAM,OAAO,GAAG,IAAI,CAAC,SAAS,IAAI,KAAK,CAAC;IACxC,MAAM,KAAK,GAAG,IAAI,CAAC,SAAS,IAAI,YAAY,CAAC;IAC7C,MAAM,GAAG,GAAG,IAAI,CAAC,OAAO,IAAI,CAAC,GAAG,EAAE,CAAC,IAAI,CAAC,GAAG,EAAE,CAAC,CAAC;IAC/C,MAAM,aAAa,GAAG,IAAI,CAAC,aAAa,IAAI,IAAI,CAAC;IAEjD,MAAM,YAAY,GAAG,GAAG,OAAO,4BAA4B,CAAC;IAC5D,OAAO,CAAC,QAAQ,YAAY,EAAE,CAAC,CAAC;IAEhC,MAAM,YAAY,GAAG,MAAM,gBAAgB,CACzC,OAAO,EACP,YAAY,EACZ;QACE,MAAM,EAAE,MAAM;QACd,OAAO,EAAE,EAAE,cAAc,EAAE,kBAAkB,EAAE;QAC/C,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,EAAE,YAAY,EAAE,WAAW,EAAE,CAAC;KACpD,EACD,KAAM,CACP,CAAC;IAEF,IAAI,CAAC,YAAY,CAAC,EAAE,EAAE,CAAC;QACrB,MAAM,IAAI,GAAG,MAAM,YAAY,CAAC,IAAI,EAAE,CAAC;QACvC,OAAO;YACL,OAAO,EAAE,KAAK;YACd,MAAM,EAAE,OAAO;YACf,OAAO,EAAE,wCAAwC,YAAY,CAAC,MAAM,MAAM,IAAI,EAAE;SACjF,CAAC;IACJ,CAAC;IAED,MAAM,IAAI,GAAG,CAAC,MAAM,YAAY,CAAC,IAAI,EAAE,CAA4B,CAAC;IACpE,OAAO,CAAC,oCAAoC,IAAI,CAAC,SAAS,gBAAgB,IAAI,CAAC,UAAU,GAAG,CAAC,CAAC;IAE9F,SAAS,CAAC,UAAU,CAAC,IAAI,CAAC,SAAS,EAAE,IAAI,CAAC,yBAAyB,CAAC,CAAC;IACrE,SAAS,CAAC,SAAS,EAAE,EAAE,CAAC;IAExB,MAAM,QAAQ,GAAG,GAAG,OAAO,wBAAwB,CAAC;IACpD,MAAM,QAAQ,GAAG,GAAG,EAAE,GAAG,IAAI,CAAC,UAAU,GAAG,IAAI,CAAC;IAChD,IAAI,QAAQ,GAAG,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,QAAQ,GAAG,IAAI,EAAE,aAAa,CAAC,CAAC;IAE7D,OAAO,GAAG,EAAE,GAAG,QAAQ,EAAE,CAAC;QACxB,MAAM,KAAK,CAAC,QAAQ,CAAC,CAAC;QAEtB,IAAI,GAAG,EAAE,IAAI,QAAQ;YAAE,MAAM;QAE7B,OAAO,CAAC,WAAW,QAAQ,cAAc,QAAQ,GAAG,IAAI,IAAI,CAAC,CAAC;QAE9D,MAAM,YAAY,GAAG,MAAM,gBAAgB,CACzC,OAAO,EACP,QAAQ,EACR;YACE,MAAM,EAAE,MAAM;YACd,OAAO,EAAE,EAAE,cAAc,EAAE,kBAAkB,EAAE;YAC/C,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC;gBACnB,WAAW,EAAE,IAAI,CAAC,WAAW;gBAC7B,UAAU,EAAE,8CAA8C;aAC3D,CAAC;SACH,EACD,KAAM,CACP,CAAC;QAEF,IAAI,YAAY,CAAC,EAAE,EAAE,CAAC;YACpB,MAAM,IAAI,GAAG,CAAC,MAAM,YAAY,CAAC,IAAI,EAAE,CAA+B,CAAC;YACvE,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,WAAW,EAAE,IAAI,CAAC,YAAY,EAAE,CAAC;QAC3D,CAAC;QAED,IAAI,SAAmC,CAAC;QACxC,IAAI,CAAC;YACH,SAAS,GAAG,CAAC,MAAM,YAAY,CAAC,IAAI,EAAE,CAA6B,CAAC;QACtE,CAAC;QAAC,MAAM,CAAC;YACP,OAAO;gBACL,OAAO,EAAE,KAAK;gBACd,MAAM,EAAE,OAAO;gBACf,OAAO,EAAE,iDAAiD,YAAY,CAAC,MAAM,GAAG;aACjF,CAAC;QACJ,CAAC;QACD,OAAO,CAAC,kBAAkB,SAAS,CAAC,KAAK,MAAM,SAAS,CAAC,iBAAiB,IAAI,EAAE,EAAE,CAAC,CAAC;QAEpF,QAAQ,SAAS,CAAC,KAAK,EAAE,CAAC;YACxB,KAAK,uBAAuB;gBAC1B,MAAM;YAER,KAAK,WAAW;gBACd,QAAQ,GAAG,IAAI,CAAC,GAAG,CAAC,QAAQ,GAAG,IAAI,EAAE,KAAM,CAAC,CAAC;gBAC7C,OAAO,CAAC,+BAA+B,QAAQ,GAAG,IAAI,GAAG,CAAC,CAAC;gBAC3D,MAAM;YAER,KAAK,eAAe;gBAClB,OAAO;oBACL,OAAO,EAAE,KAAK;oBACd,MAAM,EAAE,SAAS;oBACjB,OAAO,EAAE,SAAS,CAAC,iBAAiB,IAAI,wCAAwC;iBACjF,CAAC;YAEJ,KAAK,eAAe;gBAClB,OAAO;oBACL,OAAO,EAAE,KAAK;oBACd,MAAM,EAAE,QAAQ;oBAChB,OAAO,EAAE,SAAS,CAAC,iBAAiB,IAAI,2BAA2B;iBACpE,CAAC;YAEJ;gBACE,OAAO;oBACL,OAAO,EAAE,KAAK;oBACd,MAAM,EAAE,OAAO;oBACf,OAAO,EAAE,SAAS,CAAC,iBAAiB,IAAI,qBAAqB,SAAS,CAAC,KAAK,EAAE;iBAC/E,CAAC;QACN,CAAC;IACH,CAAC;IAED,OAAO;QACL,OAAO,EAAE,KAAK;QACd,MAAM,EAAE,SAAS;QACjB,OAAO,EAAE,wCAAwC;KAClD,CAAC;AACJ,CAAC;AAED,SAAS,YAAY,CAAC,EAAU;IAC9B,OAAO,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,UAAU,CAAC,OAAO,EAAE,EAAE,CAAC,CAAC,CAAC;AAC3D,CAAC;AAED,KAAK,UAAU,gBAAgB,CAC7B,OAAqB,EACrB,GAAW,EACX,OAAoB,EACpB,SAAiB;IAEjB,MAAM,UAAU,GAAG,IAAI,eAAe,EAAE,CAAC;IACzC,MAAM,KAAK,GAAG,UAAU,CAAC,GAAG,EAAE,CAAC,UAAU,CAAC,KAAK,EAAE,EAAE,SAAS,CAAC,CAAC;IAC9D,IAAI,CAAC;QACH,OAAO,MAAM,OAAO,CAAC,GAAG,EAAE,EAAE,GAAG,OAAO,EAAE,MAAM,EAAE,UAAU,CAAC,MAAM,EAAE,CAAC,CAAC;IACvE,CAAC;YAAS,CAAC;QACT,YAAY,CAAC,KAAK,CAAC,CAAC;IACtB,CAAC;AACH,CAAC"}
|