unity-mcp-cli 0.84.3 → 0.85.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.
@@ -1,8 +1,23 @@
1
1
  import { MachineCredentialStore } from './machine-credentials.js';
2
+ import { type DeviceLoginResult, type DeviceLoginOptions } from '@baizor/gamedev-cli-core';
2
3
  /**
3
- * Run the cloud device-auth flow: initiate, display code, open browser, poll,
4
- * and persist the token to the shared machine credential store.
4
+ * The device-flow login now runs on `@baizor/gamedev-cli-core`'s OAuth 2.1 Device Authorization
5
+ * Grant (RFC 8628) — client_id `unity-mcp-cli`, scope `mcp:plugin` — which mints an ES256 hub JWT
6
+ * plus a rotating refresh token and NEVER mints a PAT (auth-fixes T1, closing B2/B3). The FULL
7
+ * credential set (accessToken, refreshToken, expiresAt, serverTarget, subject) is persisted to the
8
+ * shared machine credential store — the legacy flow dropped refresh/expiry/subject on the floor.
9
+ */
10
+ /** Injection seam so the login flow can be exercised offline in tests without the network. */
11
+ export interface RunCloudLoginDeps {
12
+ /** Authorization-server base; defaults to the hosted `CLOUD_SERVER_BASE_URL`. */
13
+ serverBaseUrl?: string;
14
+ /** The device-login implementation; defaults to cli-core's `deviceLogin`. */
15
+ login?: (options: DeviceLoginOptions) => Promise<DeviceLoginResult>;
16
+ }
17
+ /**
18
+ * Run the cloud device-auth flow: initiate, display the user code + verification URL, open the
19
+ * browser, poll, and persist the FULL credential to the shared machine credential store.
5
20
  *
6
21
  * Returns the access token on success, or null on failure (errors are printed).
7
22
  */
8
- export declare function runCloudLogin(store: MachineCredentialStore): Promise<string | null>;
23
+ export declare function runCloudLogin(store: MachineCredentialStore, deps?: RunCloudLoginDeps): Promise<string | null>;
@@ -2,37 +2,42 @@
2
2
  // Licensed under the Apache License, Version 2.0.
3
3
  import * as ui from './ui.js';
4
4
  import { CLOUD_SERVER_BASE_URL } from './config.js';
5
- import { deviceAuthFlow } from './auth.js';
6
5
  import { openBrowser } from './browser.js';
6
+ import { deviceLogin, unityAdapter, DEFAULT_PLUGIN_SCOPE, } from '@baizor/gamedev-cli-core';
7
7
  /**
8
- * Run the cloud device-auth flow: initiate, display code, open browser, poll,
9
- * and persist the token to the shared machine credential store.
8
+ * Run the cloud device-auth flow: initiate, display the user code + verification URL, open the
9
+ * browser, poll, and persist the FULL credential to the shared machine credential store.
10
10
  *
11
11
  * Returns the access token on success, or null on failure (errors are printed).
12
12
  */
13
- export async function runCloudLogin(store) {
13
+ export async function runCloudLogin(store, deps = {}) {
14
+ const serverBaseUrl = deps.serverBaseUrl ?? CLOUD_SERVER_BASE_URL;
15
+ const login = deps.login ?? deviceLogin;
14
16
  let spinner;
15
17
  try {
16
- const result = await deviceAuthFlow(CLOUD_SERVER_BASE_URL, 'Unity-MCP CLI', {
17
- onUserCode: (userCode, verificationUrl) => {
18
+ const result = await login({
19
+ serverBaseUrl,
20
+ clientId: unityAdapter.clientId, // unity-mcp-cli
21
+ scope: DEFAULT_PLUGIN_SCOPE, // mcp:plugin
22
+ onUserCode: (userCode, verificationUri) => {
18
23
  ui.info('Open this URL to authorize:');
19
24
  console.log();
20
- console.log(` ${verificationUrl}`);
25
+ console.log(` ${verificationUri}`);
21
26
  console.log();
22
27
  ui.label('Code', userCode);
23
- openBrowser(verificationUrl);
24
28
  },
25
29
  onPolling: () => {
26
30
  spinner = ui.startSpinner('Waiting for authorization...');
27
31
  },
32
+ openBrowser,
28
33
  });
29
- if (result.success) {
34
+ if (result.ok) {
30
35
  spinner?.success('Authorized');
31
- store.write({
32
- accessToken: result.accessToken,
33
- serverTarget: CLOUD_SERVER_BASE_URL,
34
- });
35
- return result.accessToken;
36
+ // Persist the FULL credential set (accessToken + refreshToken + expiresAt + serverTarget +
37
+ // subject) — never a project config file. This closes B3 (the legacy flow stored only
38
+ // accessToken + serverTarget, losing the refresh/expiry needed for proactive refresh).
39
+ store.write(result.credentials);
40
+ return result.credentials.accessToken ?? null;
36
41
  }
37
42
  spinner?.stop();
38
43
  ui.error(result.message);
@@ -42,7 +47,7 @@ export async function runCloudLogin(store) {
42
47
  spinner?.stop();
43
48
  const message = err instanceof Error ? err.message : String(err);
44
49
  if (message.includes('ECONNREFUSED') || message.includes('fetch failed')) {
45
- ui.error(`Cannot reach cloud server at ${CLOUD_SERVER_BASE_URL}`);
50
+ ui.error(`Cannot reach cloud server at ${serverBaseUrl}`);
46
51
  }
47
52
  else {
48
53
  ui.error(`Authentication failed: ${message}`);
@@ -1 +1 @@
1
- {"version":3,"file":"cloud-login.js","sourceRoot":"","sources":["../../src/utils/cloud-login.ts"],"names":[],"mappings":"AAAA,uDAAuD;AACvD,kDAAkD;AAElD,OAAO,KAAK,EAAE,MAAM,SAAS,CAAC;AAC9B,OAAO,EAAE,qBAAqB,EAAE,MAAM,aAAa,CAAC;AACpD,OAAO,EAAE,cAAc,EAAE,MAAM,WAAW,CAAC;AAC3C,OAAO,EAAE,WAAW,EAAE,MAAM,cAAc,CAAC;AAG3C;;;;;GAKG;AACH,MAAM,CAAC,KAAK,UAAU,aAAa,CAAC,KAA6B;IAC/D,IAAI,OAAuD,CAAC;IAE5D,IAAI,CAAC;QACH,MAAM,MAAM,GAAG,MAAM,cAAc,CACjC,qBAAqB,EACrB,eAAe,EACf;YACE,UAAU,EAAE,CAAC,QAAQ,EAAE,eAAe,EAAE,EAAE;gBACxC,EAAE,CAAC,IAAI,CAAC,6BAA6B,CAAC,CAAC;gBACvC,OAAO,CAAC,GAAG,EAAE,CAAC;gBACd,OAAO,CAAC,GAAG,CAAC,KAAK,eAAe,EAAE,CAAC,CAAC;gBACpC,OAAO,CAAC,GAAG,EAAE,CAAC;gBACd,EAAE,CAAC,KAAK,CAAC,MAAM,EAAE,QAAQ,CAAC,CAAC;gBAC3B,WAAW,CAAC,eAAe,CAAC,CAAC;YAC/B,CAAC;YACD,SAAS,EAAE,GAAG,EAAE;gBACd,OAAO,GAAG,EAAE,CAAC,YAAY,CAAC,8BAA8B,CAAC,CAAC;YAC5D,CAAC;SACF,CACF,CAAC;QAEF,IAAI,MAAM,CAAC,OAAO,EAAE,CAAC;YACnB,OAAO,EAAE,OAAO,CAAC,YAAY,CAAC,CAAC;YAE/B,KAAK,CAAC,KAAK,CAAC;gBACV,WAAW,EAAE,MAAM,CAAC,WAAW;gBAC/B,YAAY,EAAE,qBAAqB;aACpC,CAAC,CAAC;YAEH,OAAO,MAAM,CAAC,WAAW,CAAC;QAC5B,CAAC;QAED,OAAO,EAAE,IAAI,EAAE,CAAC;QAChB,EAAE,CAAC,KAAK,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC;QACzB,OAAO,IAAI,CAAC;IACd,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,OAAO,EAAE,IAAI,EAAE,CAAC;QAChB,MAAM,OAAO,GAAG,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;QACjE,IAAI,OAAO,CAAC,QAAQ,CAAC,cAAc,CAAC,IAAI,OAAO,CAAC,QAAQ,CAAC,cAAc,CAAC,EAAE,CAAC;YACzE,EAAE,CAAC,KAAK,CAAC,gCAAgC,qBAAqB,EAAE,CAAC,CAAC;QACpE,CAAC;aAAM,CAAC;YACN,EAAE,CAAC,KAAK,CAAC,0BAA0B,OAAO,EAAE,CAAC,CAAC;QAChD,CAAC;QACD,OAAO,IAAI,CAAC;IACd,CAAC;AACH,CAAC"}
1
+ {"version":3,"file":"cloud-login.js","sourceRoot":"","sources":["../../src/utils/cloud-login.ts"],"names":[],"mappings":"AAAA,uDAAuD;AACvD,kDAAkD;AAElD,OAAO,KAAK,EAAE,MAAM,SAAS,CAAC;AAC9B,OAAO,EAAE,qBAAqB,EAAE,MAAM,aAAa,CAAC;AACpD,OAAO,EAAE,WAAW,EAAE,MAAM,cAAc,CAAC;AAE3C,OAAO,EACL,WAAW,EACX,YAAY,EACZ,oBAAoB,GAGrB,MAAM,0BAA0B,CAAC;AAkBlC;;;;;GAKG;AACH,MAAM,CAAC,KAAK,UAAU,aAAa,CACjC,KAA6B,EAC7B,OAA0B,EAAE;IAE5B,MAAM,aAAa,GAAG,IAAI,CAAC,aAAa,IAAI,qBAAqB,CAAC;IAClE,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,IAAI,WAAW,CAAC;IACxC,IAAI,OAAuD,CAAC;IAE5D,IAAI,CAAC;QACH,MAAM,MAAM,GAAG,MAAM,KAAK,CAAC;YACzB,aAAa;YACb,QAAQ,EAAE,YAAY,CAAC,QAAQ,EAAE,gBAAgB;YACjD,KAAK,EAAE,oBAAoB,EAAE,aAAa;YAC1C,UAAU,EAAE,CAAC,QAAQ,EAAE,eAAe,EAAE,EAAE;gBACxC,EAAE,CAAC,IAAI,CAAC,6BAA6B,CAAC,CAAC;gBACvC,OAAO,CAAC,GAAG,EAAE,CAAC;gBACd,OAAO,CAAC,GAAG,CAAC,KAAK,eAAe,EAAE,CAAC,CAAC;gBACpC,OAAO,CAAC,GAAG,EAAE,CAAC;gBACd,EAAE,CAAC,KAAK,CAAC,MAAM,EAAE,QAAQ,CAAC,CAAC;YAC7B,CAAC;YACD,SAAS,EAAE,GAAG,EAAE;gBACd,OAAO,GAAG,EAAE,CAAC,YAAY,CAAC,8BAA8B,CAAC,CAAC;YAC5D,CAAC;YACD,WAAW;SACZ,CAAC,CAAC;QAEH,IAAI,MAAM,CAAC,EAAE,EAAE,CAAC;YACd,OAAO,EAAE,OAAO,CAAC,YAAY,CAAC,CAAC;YAE/B,2FAA2F;YAC3F,sFAAsF;YACtF,uFAAuF;YACvF,KAAK,CAAC,KAAK,CAAC,MAAM,CAAC,WAAW,CAAC,CAAC;YAEhC,OAAO,MAAM,CAAC,WAAW,CAAC,WAAW,IAAI,IAAI,CAAC;QAChD,CAAC;QAED,OAAO,EAAE,IAAI,EAAE,CAAC;QAChB,EAAE,CAAC,KAAK,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC;QACzB,OAAO,IAAI,CAAC;IACd,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,OAAO,EAAE,IAAI,EAAE,CAAC;QAChB,MAAM,OAAO,GAAG,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;QACjE,IAAI,OAAO,CAAC,QAAQ,CAAC,cAAc,CAAC,IAAI,OAAO,CAAC,QAAQ,CAAC,cAAc,CAAC,EAAE,CAAC;YACzE,EAAE,CAAC,KAAK,CAAC,gCAAgC,aAAa,EAAE,CAAC,CAAC;QAC5D,CAAC;aAAM,CAAC;YACN,EAAE,CAAC,KAAK,CAAC,0BAA0B,OAAO,EAAE,CAAC,CAAC;QAChD,CAAC;QACD,OAAO,IAAI,CAAC;IACd,CAAC;AACH,CAAC"}
@@ -1,101 +1,16 @@
1
- import type { MachineCredentialStore } from './machine-credentials.js';
2
1
  /**
3
- * Agent-driven enrollment (D13): redeem a one-time enrollment code — minted by the server's
4
- * `enroll_engine_plugin` tool from an already-authorized agent session — for a plugin credential,
5
- * with NO second browser hop. The redeemed credential is planted in the shared machine store, the
6
- * server target it was minted for is recorded in the project marker, and the D14 project pin is
7
- * upserted into any existing project-local agent config so the plugin boots pointed at the right
8
- * hub. Codes are burned server-side on first redeem attempt; a spent/invalid code yields a uniform
9
- * error surfaced here as an actionable message.
10
- */
11
- /** Raised on any enrollment-redeem failure. Carries the HTTP status when one was received. */
12
- export declare class EnrollmentError extends Error {
13
- readonly status?: number;
14
- constructor(message: string, status?: number);
15
- }
16
- /** Credential material returned by a successful `/api/auth/enroll/redeem`. */
17
- export interface RedeemedCredential {
18
- accessToken?: string;
19
- refreshToken?: string;
20
- expiresAt?: string;
21
- serverTarget?: string;
22
- subject?: string;
23
- }
24
- export interface RedeemOptions {
25
- /** Authorization Server base; defaults to the hosted `CLOUD_SERVER_BASE_URL`. */
26
- baseUrl?: string;
27
- /** `fetch` injection (tests). */
28
- fetchImpl?: typeof fetch;
29
- /** Request timeout; defaults to 30s. */
30
- timeoutMs?: number;
31
- }
32
- /**
33
- * Normalize the redeem response. The Authorization Server's JSON key casing is not re-derivable
34
- * from this repo (the AS lives in a separate service), so both snake_case and camelCase variants
35
- * of every field are accepted defensively; `expires_in` seconds are converted to an absolute
36
- * `expiresAt` ISO timestamp when no explicit `expires_at` is present.
37
- */
38
- export declare function normalizeRedeemResponse(data: Record<string, unknown>): RedeemedCredential;
39
- /**
40
- * Redeem an enrollment code against `POST <baseUrl>/api/auth/enroll/redeem` with body
41
- * `{enroll_code}`. The code travels only in the request BODY (never a query string). A non-2xx
42
- * response is surfaced as an actionable `EnrollmentError` (invalid/expired/already-used codes all
43
- * return a uniform server error; burn-on-first-attempt is server-side).
44
- */
45
- export declare function redeemEnrollmentCode(code: string, opts?: RedeemOptions): Promise<RedeemedCredential>;
46
- /**
47
- * Resolve the enrollment code from `--enroll <code>` (argv) or `--enroll-stdin` (stdin), enforcing
48
- * mutual exclusion. `--enroll-stdin` reads via the injected `readStdin` so the code NEVER lands in
49
- * argv / shell history. `readStdin` is only invoked in the stdin mode.
50
- */
51
- export declare function resolveEnrollCode(opts: {
52
- enroll?: string;
53
- enrollStdin?: boolean;
54
- }, readStdin: () => string): string;
55
- /**
56
- * Add (or replace) the `/p/<pin>` routing segment on a config URL so an agent session launched in
57
- * this project folder routes strictly to this project's engine (design 06 D14). Existing `/p/<pin>`
58
- * segments are replaced; the port / host / scheme are preserved.
59
- */
60
- export declare function pinUrl(rawUrl: string, pin: string): string;
61
- /**
62
- * The exact project-root STRING to feed into ProjectIdentity so the CLI-derived pin matches the
63
- * plugin's. The Unity plugin registers with `ProjectIdentity.DerivePin(UnityMcpPluginEditor
64
- * .ProjectRootPath)`, where `ProjectRootPath => Path.GetDirectoryName(Application.dataPath)`.
65
- * `Application.dataPath` is forward-slash on EVERY platform (incl. Windows: `C:/proj/Assets`), so
66
- * the plugin hashes a forward-slash root like `C:/proj`. Node's `path.resolve` yields BACKSLASHES
67
- * on Windows (`C:\proj`), which the ProjectIdentity golden vectors hash to a DIFFERENT pin — so we
68
- * convert separators to `/` here (a no-op on POSIX) to route to the same engine instance.
69
- */
70
- export declare function projectRootForIdentity(projectPath: string): string;
71
- export interface PinUpsertResult {
72
- updatedFiles: string[];
73
- }
74
- /**
75
- * Upsert the project pin into every EXISTING project-local (project-scoped) JSON agent config that
76
- * carries an `ai-game-developer` server entry with a `url` / `serverUrl`. Global client config
77
- * files (Claude Desktop, Antigravity, Cline, the Copilot CLI) are never touched — the golden path
78
- * writes no user-global entry. TOML (Codex) configs are left to the server binary's own
79
- * `configure`. Returns the list of files actually rewritten.
80
- */
81
- export declare function upsertProjectPinIntoConfigs(projectPath: string, pin: string): PinUpsertResult;
82
- export interface RunEnrollOptions {
83
- code: string;
84
- projectPath: string;
85
- store: MachineCredentialStore;
86
- baseUrl?: string;
87
- fetchImpl?: typeof fetch;
88
- }
89
- export interface RunEnrollResult {
90
- serverTarget: string;
91
- pin: string;
92
- credentialPath: string;
93
- markerPath: string;
94
- pinnedConfigs: string[];
95
- }
96
- /**
97
- * Execute the full enrollment side effect: redeem → persist the plugin credential to the SHARED
98
- * machine store → write the project marker with the server target → upsert the D14 pin into
99
- * existing project-local configs. NEVER writes a project token file / `cloudToken` config.
100
- */
101
- export declare function runEnroll(opts: RunEnrollOptions): Promise<RunEnrollResult>;
2
+ * Agent-driven enrollment now lives in `@baizor/gamedev-cli-core` (auth-fixes T3/T7): ONE engine-
3
+ * agnostic `enroll` flow — redeem a one-time code, plant the plugin credential in the SHARED machine
4
+ * store, record the AS-root server target in the committable project marker (MED-2), and upsert the
5
+ * `/p/<pin>` routing segment into existing project-local agent configs.
6
+ *
7
+ * Two things changed vs. the old Unity-CLI-local port:
8
+ * - The pin is derived with **v2** identity (`derivePinV2`, the `\`→`/` normalization). This
9
+ * REPLACES the Unity CLI's local `projectRootForIdentity` `\`→`/` workaround — one algorithm for
10
+ * every engine, so a Windows `path.resolve` backslash root matches the plugin's forward-slash
11
+ * hash. `projectRootForIdentity` is therefore gone.
12
+ * - `runEnroll` takes an `adapter` (pass `unityAdapter`); it records the AS root via
13
+ * `adapter.loginServerTarget`, never a pinned hub URL.
14
+ */
15
+ export { redeemEnrollmentCode, normalizeRedeemResponse, resolveEnrollCode, upsertProjectPinIntoConfigs, runEnroll, EnrollmentError, pinUrl, } from '@baizor/gamedev-cli-core';
16
+ export type { RedeemedCredential, RedeemOptions, RunEnrollOptions, RunEnrollResult, PinUpsertResult, } from '@baizor/gamedev-cli-core';
@@ -1,251 +1,18 @@
1
1
  // Copyright (c) 2024 Ivan Murzak. All rights reserved.
2
2
  // Licensed under the Apache License, Version 2.0.
3
- import * as fs from 'fs';
4
- import * as path from 'path';
5
- import { CLOUD_SERVER_BASE_URL } from './config.js';
6
- import { deriveProjectPin } from './port.js';
7
- import { agentRegistry, MCP_SERVER_NAME } from './agents.js';
8
- import { writeProjectMarker } from './project-marker.js';
9
3
  /**
10
- * Agent-driven enrollment (D13): redeem a one-time enrollment code — minted by the server's
11
- * `enroll_engine_plugin` tool from an already-authorized agent session — for a plugin credential,
12
- * with NO second browser hop. The redeemed credential is planted in the shared machine store, the
13
- * server target it was minted for is recorded in the project marker, and the D14 project pin is
14
- * upserted into any existing project-local agent config so the plugin boots pointed at the right
15
- * hub. Codes are burned server-side on first redeem attempt; a spent/invalid code yields a uniform
16
- * error surfaced here as an actionable message.
17
- */
18
- /** Raised on any enrollment-redeem failure. Carries the HTTP status when one was received. */
19
- export class EnrollmentError extends Error {
20
- constructor(message, status) {
21
- super(message);
22
- this.name = 'EnrollmentError';
23
- this.status = status;
24
- }
25
- }
26
- function nonEmptyString(value) {
27
- return typeof value === 'string' && value.length > 0 ? value : undefined;
28
- }
29
- function numberOrUndefined(value) {
30
- return typeof value === 'number' && Number.isFinite(value) ? value : undefined;
31
- }
32
- /**
33
- * Normalize the redeem response. The Authorization Server's JSON key casing is not re-derivable
34
- * from this repo (the AS lives in a separate service), so both snake_case and camelCase variants
35
- * of every field are accepted defensively; `expires_in` seconds are converted to an absolute
36
- * `expiresAt` ISO timestamp when no explicit `expires_at` is present.
37
- */
38
- export function normalizeRedeemResponse(data) {
39
- const accessToken = nonEmptyString(data.access_token) ?? nonEmptyString(data.accessToken);
40
- const refreshToken = nonEmptyString(data.refresh_token) ?? nonEmptyString(data.refreshToken);
41
- const serverTarget = nonEmptyString(data.server_target) ??
42
- nonEmptyString(data.serverTarget) ??
43
- nonEmptyString(data.server_target_url) ??
44
- nonEmptyString(data.serverTargetUrl);
45
- const subject = nonEmptyString(data.subject) ?? nonEmptyString(data.sub);
46
- let expiresAt = nonEmptyString(data.expires_at) ?? nonEmptyString(data.expiresAt);
47
- const expiresIn = numberOrUndefined(data.expires_in) ?? numberOrUndefined(data.expiresIn);
48
- if (!expiresAt && expiresIn !== undefined) {
49
- expiresAt = new Date(Date.now() + expiresIn * 1000).toISOString();
50
- }
51
- return { accessToken, refreshToken, expiresAt, serverTarget, subject };
52
- }
53
- /**
54
- * Redeem an enrollment code against `POST <baseUrl>/api/auth/enroll/redeem` with body
55
- * `{enroll_code}`. The code travels only in the request BODY (never a query string). A non-2xx
56
- * response is surfaced as an actionable `EnrollmentError` (invalid/expired/already-used codes all
57
- * return a uniform server error; burn-on-first-attempt is server-side).
58
- */
59
- export async function redeemEnrollmentCode(code, opts = {}) {
60
- const baseUrl = opts.baseUrl ?? CLOUD_SERVER_BASE_URL;
61
- const doFetch = opts.fetchImpl ?? fetch;
62
- const url = `${baseUrl}/api/auth/enroll/redeem`;
63
- const controller = new AbortController();
64
- const timer = setTimeout(() => controller.abort(), opts.timeoutMs ?? 30000);
65
- let response;
66
- try {
67
- response = await doFetch(url, {
68
- method: 'POST',
69
- headers: { 'Content-Type': 'application/json' },
70
- body: JSON.stringify({ enroll_code: code }),
71
- signal: controller.signal,
72
- });
73
- }
74
- catch (err) {
75
- throw new EnrollmentError(`Could not reach the enrollment server at ${url}: ${err instanceof Error ? err.message : String(err)}`);
76
- }
77
- finally {
78
- clearTimeout(timer);
79
- }
80
- if (!response.ok) {
81
- throw new EnrollmentError(`Enrollment failed (HTTP ${response.status}). The enrollment code may be invalid, expired, ` +
82
- `or already used — ask the agent to issue a fresh code and try again.`, response.status);
83
- }
84
- let data;
85
- try {
86
- data = (await response.json());
87
- }
88
- catch {
89
- throw new EnrollmentError('Enrollment server returned a malformed (non-JSON) response.');
90
- }
91
- const credential = normalizeRedeemResponse(data);
92
- if (!credential.accessToken) {
93
- throw new EnrollmentError('Enrollment response did not contain an access token.');
94
- }
95
- return credential;
96
- }
97
- // ---------------------------------------------------------------------------
98
- // Enrollment code resolution (--enroll <code> vs --enroll-stdin)
99
- // ---------------------------------------------------------------------------
100
- /**
101
- * Resolve the enrollment code from `--enroll <code>` (argv) or `--enroll-stdin` (stdin), enforcing
102
- * mutual exclusion. `--enroll-stdin` reads via the injected `readStdin` so the code NEVER lands in
103
- * argv / shell history. `readStdin` is only invoked in the stdin mode.
104
- */
105
- export function resolveEnrollCode(opts, readStdin) {
106
- if (opts.enroll && opts.enrollStdin) {
107
- throw new Error('Use either --enroll <code> or --enroll-stdin, not both.');
108
- }
109
- if (opts.enrollStdin) {
110
- const code = readStdin().trim();
111
- if (!code)
112
- throw new Error('No enrollment code received on stdin.');
113
- return code;
114
- }
115
- if (opts.enroll) {
116
- const code = opts.enroll.trim();
117
- if (!code)
118
- throw new Error('Enrollment code (--enroll) is empty.');
119
- return code;
120
- }
121
- throw new Error('An enrollment code is required: pass --enroll <code> or --enroll-stdin.');
122
- }
123
- // ---------------------------------------------------------------------------
124
- // Project pin upsert (D14)
125
- // ---------------------------------------------------------------------------
126
- /**
127
- * Add (or replace) the `/p/<pin>` routing segment on a config URL so an agent session launched in
128
- * this project folder routes strictly to this project's engine (design 06 D14). Existing `/p/<pin>`
129
- * segments are replaced; the port / host / scheme are preserved.
130
- */
131
- export function pinUrl(rawUrl, pin) {
132
- const stripExistingPin = (segments) => {
133
- const idx = segments.findIndex((s) => s === 'p');
134
- if (idx >= 0 && idx === segments.length - 2 && /^[0-9a-f]{8}$/i.test(segments[idx + 1])) {
135
- return segments.slice(0, idx);
136
- }
137
- return segments;
138
- };
139
- try {
140
- const url = new URL(rawUrl);
141
- let segments = stripExistingPin(url.pathname.split('/').filter(Boolean));
142
- segments = [...segments, 'p', pin];
143
- url.pathname = '/' + segments.join('/');
144
- return url.toString().replace(/\/$/, '');
145
- }
146
- catch {
147
- const base = rawUrl.replace(/\/+$/, '').replace(/\/p\/[0-9a-f]{8}$/i, '');
148
- return `${base}/p/${pin}`;
149
- }
150
- }
151
- /**
152
- * The exact project-root STRING to feed into ProjectIdentity so the CLI-derived pin matches the
153
- * plugin's. The Unity plugin registers with `ProjectIdentity.DerivePin(UnityMcpPluginEditor
154
- * .ProjectRootPath)`, where `ProjectRootPath => Path.GetDirectoryName(Application.dataPath)`.
155
- * `Application.dataPath` is forward-slash on EVERY platform (incl. Windows: `C:/proj/Assets`), so
156
- * the plugin hashes a forward-slash root like `C:/proj`. Node's `path.resolve` yields BACKSLASHES
157
- * on Windows (`C:\proj`), which the ProjectIdentity golden vectors hash to a DIFFERENT pin — so we
158
- * convert separators to `/` here (a no-op on POSIX) to route to the same engine instance.
159
- */
160
- export function projectRootForIdentity(projectPath) {
161
- return path.resolve(projectPath).replace(/\\/g, '/');
162
- }
163
- /**
164
- * Upsert the project pin into every EXISTING project-local (project-scoped) JSON agent config that
165
- * carries an `ai-game-developer` server entry with a `url` / `serverUrl`. Global client config
166
- * files (Claude Desktop, Antigravity, Cline, the Copilot CLI) are never touched — the golden path
167
- * writes no user-global entry. TOML (Codex) configs are left to the server binary's own
168
- * `configure`. Returns the list of files actually rewritten.
169
- */
170
- export function upsertProjectPinIntoConfigs(projectPath, pin) {
171
- const resolvedProject = path.resolve(projectPath);
172
- const updatedFiles = [];
173
- for (const agent of agentRegistry) {
174
- if (agent.configFormat !== 'json')
175
- continue;
176
- const configPath = agent.getConfigPath(resolvedProject);
177
- // Project-scoped only: the config path must live inside the project directory.
178
- const relative = path.relative(resolvedProject, configPath);
179
- if (relative.startsWith('..') || path.isAbsolute(relative))
180
- continue;
181
- if (!fs.existsSync(configPath))
182
- continue;
183
- let root;
184
- try {
185
- const parsed = JSON.parse(fs.readFileSync(configPath, 'utf-8'));
186
- if (!parsed || typeof parsed !== 'object' || Array.isArray(parsed))
187
- continue;
188
- root = parsed;
189
- }
190
- catch {
191
- continue;
192
- }
193
- const body = root[agent.bodyPath];
194
- if (!body || typeof body !== 'object' || Array.isArray(body))
195
- continue;
196
- const entry = body[MCP_SERVER_NAME];
197
- if (!entry || typeof entry !== 'object' || Array.isArray(entry))
198
- continue;
199
- const entryRecord = entry;
200
- let changed = false;
201
- for (const key of ['url', 'serverUrl']) {
202
- const current = entryRecord[key];
203
- if (typeof current === 'string' && current.length > 0) {
204
- const pinned = pinUrl(current, pin);
205
- if (pinned !== current) {
206
- entryRecord[key] = pinned;
207
- changed = true;
208
- }
209
- }
210
- }
211
- if (changed) {
212
- fs.writeFileSync(configPath, JSON.stringify(root, null, 2) + '\n');
213
- updatedFiles.push(configPath);
214
- }
215
- }
216
- return { updatedFiles };
217
- }
218
- /**
219
- * Execute the full enrollment side effect: redeem → persist the plugin credential to the SHARED
220
- * machine store → write the project marker with the server target → upsert the D14 pin into
221
- * existing project-local configs. NEVER writes a project token file / `cloudToken` config.
222
- */
223
- export async function runEnroll(opts) {
224
- const credential = await redeemEnrollmentCode(opts.code, {
225
- baseUrl: opts.baseUrl,
226
- fetchImpl: opts.fetchImpl,
227
- });
228
- const serverTarget = credential.serverTarget ?? opts.baseUrl ?? CLOUD_SERVER_BASE_URL;
229
- // Persist to the shared machine credential store (0600 / DPAPI) — never a project file.
230
- opts.store.write({
231
- accessToken: credential.accessToken,
232
- refreshToken: credential.refreshToken,
233
- expiresAt: credential.expiresAt,
234
- serverTarget,
235
- subject: credential.subject,
236
- });
237
- // Record the enrolled server target in the committable project marker.
238
- const markerPath = writeProjectMarker(opts.projectPath, { serverTarget });
239
- // Upsert the D14 pin into existing project-local agent configs. The pin is derived from the
240
- // forward-slash project root so it matches the plugin's pin (see projectRootForIdentity).
241
- const pin = deriveProjectPin(projectRootForIdentity(opts.projectPath));
242
- const { updatedFiles } = upsertProjectPinIntoConfigs(opts.projectPath, pin);
243
- return {
244
- serverTarget,
245
- pin,
246
- credentialPath: opts.store.credentialsPath,
247
- markerPath,
248
- pinnedConfigs: updatedFiles,
249
- };
250
- }
4
+ * Agent-driven enrollment now lives in `@baizor/gamedev-cli-core` (auth-fixes T3/T7): ONE engine-
5
+ * agnostic `enroll` flow — redeem a one-time code, plant the plugin credential in the SHARED machine
6
+ * store, record the AS-root server target in the committable project marker (MED-2), and upsert the
7
+ * `/p/<pin>` routing segment into existing project-local agent configs.
8
+ *
9
+ * Two things changed vs. the old Unity-CLI-local port:
10
+ * - The pin is derived with **v2** identity (`derivePinV2`, the `\`→`/` normalization). This
11
+ * REPLACES the Unity CLI's local `projectRootForIdentity` `\`→`/` workaround — one algorithm for
12
+ * every engine, so a Windows `path.resolve` backslash root matches the plugin's forward-slash
13
+ * hash. `projectRootForIdentity` is therefore gone.
14
+ * - `runEnroll` takes an `adapter` (pass `unityAdapter`); it records the AS root via
15
+ * `adapter.loginServerTarget`, never a pinned hub URL.
16
+ */
17
+ export { redeemEnrollmentCode, normalizeRedeemResponse, resolveEnrollCode, upsertProjectPinIntoConfigs, runEnroll, EnrollmentError, pinUrl, } from '@baizor/gamedev-cli-core';
251
18
  //# sourceMappingURL=enroll.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"enroll.js","sourceRoot":"","sources":["../../src/utils/enroll.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"enroll.js","sourceRoot":"","sources":["../../src/utils/enroll.ts"],"names":[],"mappings":"AAAA,uDAAuD;AACvD,kDAAkD;AAElD;;;;;;;;;;;;;GAaG;AAEH,OAAO,EACL,oBAAoB,EACpB,uBAAuB,EACvB,iBAAiB,EACjB,2BAA2B,EAC3B,SAAS,EACT,eAAe,EACf,MAAM,GACP,MAAM,0BAA0B,CAAC"}
@@ -1,64 +1,9 @@
1
1
  /**
2
- * TypeScript client of the shared machine credential store — the same on-disk contract the
3
- * plugin's C# `MachineCredentialStore` (MCP-Plugin-dotnet, com.IvanMurzak.McpPlugin.AgentConfig)
4
- * reads and writes. A single ai-game.dev account credential lives once per machine at
5
- * `~/.ai-game-dev/credentials.json`, so `login` writes it here and every engine plugin/CLI reads
6
- * it — sign-in happens once per machine, never per project, and the credential is NEVER written
7
- * into a project file / VCS.
8
- *
9
- * At-rest protection matches the C# store byte-for-byte so the plugin can read what the CLI wrote:
10
- * - POSIX — plaintext JSON, file mode 0600, inside a 0700 directory.
11
- * - Windows — DPAPI-encrypted (CurrentUser scope, no entropy) via
12
- * System.Security.Cryptography.ProtectedData, invoked through PowerShell. This is
13
- * interoperable with the C# store's CryptProtectData/CryptUnprotectData (the description
14
- * string and CRYPTPROTECT_UI_FORBIDDEN flag do not affect decryptability).
2
+ * The shared machine credential store now lives in `@baizor/gamedev-cli-core` (auth-fixes T7 / b2):
3
+ * ONE implementation of `~/.ai-game-dev/credentials.json` across the three engine CLIs and the C#
4
+ * plugin — atomic, corruption-safe writes (temp + fsync + rename), DPAPI on Windows / `0600`-`0700`
5
+ * on POSIX, plus the `rotate()` used by the proactive refresh loop. This module is a thin re-export
6
+ * so the rest of the CLI keeps importing from a stable local path.
15
7
  */
16
- /** Directory name under the user home (or a project root) that holds the store. */
17
- export declare const MACHINE_STORE_DIR_NAME = ".ai-game-dev";
18
- /** File name of the secret credential document. */
19
- export declare const CREDENTIALS_FILE_NAME = "credentials.json";
20
- /**
21
- * The secret credential material persisted in the store. Mirrors the C# `MachineCredentials`
22
- * schema (camelCase JSON keys). Unknown fields are preserved on read for forward-compatibility.
23
- */
24
- export interface MachineCredentials {
25
- /** Schema version of the persisted document (currently 1). */
26
- version?: number;
27
- /** The current short-lived JWT access token (MCP audience). */
28
- accessToken?: string;
29
- /** The rotating refresh token used to mint a new access token before `expiresAt`. */
30
- refreshToken?: string;
31
- /** ISO-8601 absolute expiry of `accessToken`; used to schedule proactive refresh. */
32
- expiresAt?: string;
33
- /** The server target the credential was issued for (hosted https://ai-game.dev or a local URL). */
34
- serverTarget?: string;
35
- /** The account id (`sub`) the credential resolves to. Audit/diagnostic only. */
36
- subject?: string;
37
- [key: string]: unknown;
38
- }
39
- /**
40
- * The shared machine credential store. Defaults to `~/.ai-game-dev/`; pass an explicit
41
- * `baseDirectory` for tests or for the `--project` per-project store
42
- * (`<project>/.ai-game-dev/`).
43
- */
44
- export declare class MachineCredentialStore {
45
- private readonly _baseDirectory;
46
- constructor(baseDirectory?: string);
47
- /** Absolute path of the store directory. */
48
- get baseDirectory(): string;
49
- /** Absolute path of the secret credential file. */
50
- get credentialsPath(): string;
51
- /** True when a credential file exists in the store. */
52
- get exists(): boolean;
53
- /**
54
- * Encrypt (Windows) / restrict (POSIX) and write `credentials` to the store, creating the
55
- * store directory with owner-only permissions if needed. `version` is always written as 1;
56
- * undefined fields are omitted (matching the C# `WhenWritingNull` policy).
57
- */
58
- write(credentials: MachineCredentials): void;
59
- /** Read and decrypt the stored credentials, or null when none are present. */
60
- read(): MachineCredentials | null;
61
- /** Delete the stored credentials (sign-out). No-op when none exist. */
62
- delete(): void;
63
- private ensureBaseDirectory;
64
- }
8
+ export { MachineCredentialStore, MACHINE_STORE_DIR_NAME, CREDENTIALS_FILE_NAME, CREDENTIALS_SCHEMA_VERSION, } from '@baizor/gamedev-cli-core';
9
+ export type { MachineCredentials } from '@baizor/gamedev-cli-core';