@leonxin/meetgames 0.1.19 → 0.1.20

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.
Files changed (47) hide show
  1. package/README.md +11 -2
  2. package/dist/android/detect.d.ts +6 -1
  3. package/dist/android/detect.d.ts.map +1 -1
  4. package/dist/android/detect.js +41 -17
  5. package/dist/android/detect.js.map +1 -1
  6. package/dist/android/gradle.d.ts.map +1 -1
  7. package/dist/android/gradle.js +8 -1
  8. package/dist/android/gradle.js.map +1 -1
  9. package/dist/android/meetSdkRemoteGradle.d.ts +1 -0
  10. package/dist/android/meetSdkRemoteGradle.d.ts.map +1 -1
  11. package/dist/android/meetSdkRemoteGradle.js +44 -21
  12. package/dist/android/meetSdkRemoteGradle.js.map +1 -1
  13. package/dist/cli.d.ts.map +1 -1
  14. package/dist/cli.js +32 -7
  15. package/dist/cli.js.map +1 -1
  16. package/dist/contracts/types.d.ts +14 -0
  17. package/dist/contracts/types.d.ts.map +1 -1
  18. package/dist/core/doctor.d.ts.map +1 -1
  19. package/dist/core/doctor.js +16 -1
  20. package/dist/core/doctor.js.map +1 -1
  21. package/dist/core/platform.js +1 -1
  22. package/dist/core/platform.js.map +1 -1
  23. package/dist/core/workspace.d.ts.map +1 -1
  24. package/dist/core/workspace.js +2 -1
  25. package/dist/core/workspace.js.map +1 -1
  26. package/dist/mcp/server.d.ts.map +1 -1
  27. package/dist/mcp/server.js +12 -7
  28. package/dist/mcp/server.js.map +1 -1
  29. package/dist/mcp/service.d.ts +4 -0
  30. package/dist/mcp/service.d.ts.map +1 -1
  31. package/dist/mcp/service.js +13 -6
  32. package/dist/mcp/service.js.map +1 -1
  33. package/dist/ops/handlers.d.ts.map +1 -1
  34. package/dist/ops/handlers.js +21 -3
  35. package/dist/ops/handlers.js.map +1 -1
  36. package/package.json +1 -1
  37. package/src/android/detect.ts +47 -17
  38. package/src/android/gradle.ts +16 -1
  39. package/src/android/meetSdkRemoteGradle.ts +50 -22
  40. package/src/cli.ts +30 -7
  41. package/src/contracts/types.ts +17 -0
  42. package/src/core/doctor.ts +15 -1
  43. package/src/core/platform.ts +1 -1
  44. package/src/core/workspace.ts +2 -1
  45. package/src/mcp/server.ts +12 -7
  46. package/src/mcp/service.ts +21 -5
  47. package/src/ops/handlers.ts +23 -3
@@ -12,6 +12,19 @@ export interface AndroidDetectionError {
12
12
 
13
13
  export type AndroidDetectResult = AndroidDetection | AndroidDetectionError;
14
14
 
15
+ export interface AndroidSdkModule {
16
+ ok: true;
17
+ moduleName: string;
18
+ moduleDir: string;
19
+ }
20
+
21
+ export interface AndroidSdkModuleError {
22
+ ok: false;
23
+ error: string;
24
+ }
25
+
26
+ export type AndroidSdkModuleResult = AndroidSdkModule | AndroidSdkModuleError;
27
+
15
28
  export interface AndroidDetectOptions {
16
29
  /** Unified app target selector; for Android this is the application Gradle module, e.g. `:app` or `launcher`. */
17
30
  appTarget?: string;
@@ -60,6 +73,8 @@ export interface WorkspaceContext {
60
73
  /** Optional cache root override, primarily used by tests. */
61
74
  cacheRoot?: string;
62
75
  android?: AndroidDetectResult;
76
+ /** Android Gradle module where SDK resValue/dependencies are written. Defaults to `:unityLibrary`. */
77
+ androidSdkModule?: AndroidSdkModuleResult;
63
78
  /** Reserved: populated via placeholder until iOS tooling ships. */
64
79
  ios?: IOSDetectResult;
65
80
  iosReserved?: IosToolingReserved;
@@ -68,6 +83,8 @@ export interface WorkspaceContext {
68
83
  export interface WorkspaceContextOptions {
69
84
  /** Unified app target selector: Android application module or iOS application target. */
70
85
  appTarget?: string;
86
+ /** Android Gradle module where SDK resValue/dependencies are written. Defaults to `unityLibrary`. */
87
+ sdkModuleName?: string;
71
88
  /** sdk-home API root; defaults to the production MeetGames business API. */
72
89
  sdkHomeApiBaseUrl?: string;
73
90
  /** Override the cached remote config path. */
@@ -84,6 +84,14 @@ function fileContainsRepository(text: string, repo: string): boolean {
84
84
  }
85
85
 
86
86
  function moduleBuildGradlePath(ctx: WorkspaceContext): string | null {
87
+ if (!ctx.androidSdkModule?.ok) return null;
88
+ const groovy = path.join(ctx.androidSdkModule.moduleDir, "build.gradle");
89
+ if (fs.existsSync(groovy)) return groovy;
90
+ const kts = path.join(ctx.androidSdkModule.moduleDir, "build.gradle.kts");
91
+ return fs.existsSync(kts) ? kts : null;
92
+ }
93
+
94
+ function applicationBuildGradlePath(ctx: WorkspaceContext): string | null {
87
95
  if (!ctx.android?.ok) return null;
88
96
  const groovy = path.join(ctx.android.moduleDir, "build.gradle");
89
97
  if (fs.existsSync(groovy)) return groovy;
@@ -193,6 +201,10 @@ async function checkAndroid(ctx: WorkspaceContext, report: DoctorReport, config:
193
201
  addCheck(report, "android.detect", false, "android application module not detected");
194
202
  return;
195
203
  }
204
+ if (!ctx.androidSdkModule?.ok) {
205
+ addCheck(report, "android.sdkModule", false, ctx.androidSdkModule?.error ?? "android SDK module not resolved");
206
+ return;
207
+ }
196
208
  const resolved = applyMeetSdkDefaultConfig(
197
209
  config,
198
210
  await loadMeetSdkDefaultConfigWithLatestAndroidVersion({
@@ -203,14 +215,16 @@ async function checkAndroid(ctx: WorkspaceContext, report: DoctorReport, config:
203
215
  })
204
216
  );
205
217
  const moduleGradleAbs = moduleBuildGradlePath(ctx);
218
+ const applicationGradleAbs = applicationBuildGradlePath(ctx);
206
219
  const rootGradleAbs = rootBuildGradlePath(ctx.projectRoot);
207
220
  const settingsGradleAbs = settingsGradlePath(ctx.projectRoot);
208
221
  const moduleGradle = moduleGradleAbs ? readFileIfExists(moduleGradleAbs) : "";
222
+ const applicationGradle = applicationGradleAbs ? readFileIfExists(applicationGradleAbs) : "";
209
223
  const rootGradle = rootGradleAbs ? readFileIfExists(rootGradleAbs) : "";
210
224
  const settingsGradle = settingsGradleAbs ? readFileIfExists(settingsGradleAbs) : "";
211
225
 
212
226
  addCheck(report, "android.moduleGradle.exists", Boolean(moduleGradleAbs), moduleGradleAbs ?? "module build.gradle not found");
213
- addCheck(report, "android.applicationId", moduleGradle.includes(`applicationId "${resolved.packageName}"`) || moduleGradle.includes(`applicationId '${resolved.packageName}'`), resolved.packageName);
227
+ addCheck(report, "android.applicationId", applicationGradle.includes(`applicationId "${resolved.packageName}"`) || applicationGradle.includes(`applicationId '${resolved.packageName}'`), resolved.packageName);
214
228
 
215
229
  const repoText = `${settingsGradle}\n${rootGradle}`;
216
230
  const missingRepos = collectMeetSdkRemoteRepositories(resolved).filter((repo) => !fileContainsRepository(repoText, repo));
@@ -35,7 +35,7 @@ export function platformContext(ctx: WorkspaceContext, platform: DetectedPlatfor
35
35
  if (platform === "android") {
36
36
  return { ...ctx, ios: undefined, iosReserved: undefined };
37
37
  }
38
- return { ...ctx, android: undefined };
38
+ return { ...ctx, android: undefined, androidSdkModule: undefined };
39
39
  }
40
40
 
41
41
  export function manifestForPlatform(manifest: Manifest, platform: DetectedPlatform): Manifest {
@@ -1,7 +1,7 @@
1
1
  import path from "node:path";
2
2
  import { fileURLToPath } from "node:url";
3
3
  import type { WorkspaceContext, WorkspaceContextOptions } from "../contracts/types.js";
4
- import { detectAndroid } from "../android/detect.js";
4
+ import { detectAndroid, resolveAndroidSdkModule } from "../android/detect.js";
5
5
  import { detectIOS } from "../ios/detect.js";
6
6
  import { iosToolingReserved } from "../ios/reserved.js";
7
7
 
@@ -26,6 +26,7 @@ export function buildWorkspaceContext(
26
26
  iosSdkRoot: options.iosSdkRoot,
27
27
  cacheRoot: options.cacheRoot,
28
28
  android: detectAndroid(root, { appTarget: options.appTarget }),
29
+ androidSdkModule: resolveAndroidSdkModule(root, options.sdkModuleName),
29
30
  ios: detectIOS(root, { appTarget: options.appTarget }),
30
31
  iosReserved: iosToolingReserved,
31
32
  };
package/src/mcp/server.ts CHANGED
@@ -31,11 +31,12 @@ export function createMeetgamesMcpServer(): McpServer {
31
31
  inputSchema: {
32
32
  projectRoot: z.string().optional().describe("Absolute or relative project root; default is current working directory."),
33
33
  appTarget: z.string().optional().describe("App target to integrate/check: Android application Gradle module or iOS App target name."),
34
+ sdkModuleName: z.string().optional().describe("Android SDK integration Gradle module for resValue/dependencies; default is unityLibrary."),
34
35
  },
35
36
  },
36
- async ({ projectRoot, appTarget }) => {
37
+ async ({ projectRoot, appTarget, sdkModuleName }) => {
37
38
  try {
38
- return toSuccessResult(meetgamesDoctor({ projectRoot, appTarget }));
39
+ return toSuccessResult(meetgamesDoctor({ projectRoot, appTarget, sdkModuleName }));
39
40
  } catch (error) {
40
41
  return toErrorResult(error);
41
42
  }
@@ -78,14 +79,15 @@ export function createMeetgamesMcpServer(): McpServer {
78
79
  inputSchema: {
79
80
  projectRoot: z.string().optional().describe("Absolute or relative project root; default is current working directory."),
80
81
  appTarget: z.string().optional().describe("App target to integrate/check: Android application Gradle module or iOS App target name."),
82
+ sdkModuleName: z.string().optional().describe("Android SDK integration Gradle module for resValue/dependencies; default is unityLibrary."),
81
83
  env: z.enum(["prod", "pre", "test"]).optional().describe("TOPSDK API environment for selecting cached config."),
82
84
  appId: z.string().optional().describe("Application id for selecting cached config."),
83
85
  channelType: z.string().optional().describe("Channel type for selecting cached config."),
84
86
  },
85
87
  },
86
- async ({ projectRoot, appTarget, env, appId, channelType }) => {
88
+ async ({ projectRoot, appTarget, sdkModuleName, env, appId, channelType }) => {
87
89
  try {
88
- const result = await meetgamesIntegrate({ projectRoot, appTarget, env, appId, channelType, dryRun: true });
90
+ const result = await meetgamesIntegrate({ projectRoot, appTarget, sdkModuleName, env, appId, channelType, dryRun: true });
89
91
  if (result.hasErrors) {
90
92
  return {
91
93
  ...toSuccessResult(result),
@@ -106,14 +108,15 @@ export function createMeetgamesMcpServer(): McpServer {
106
108
  inputSchema: {
107
109
  projectRoot: z.string().optional().describe("Absolute or relative project root; default is current working directory."),
108
110
  appTarget: z.string().optional().describe("App target to integrate/check: Android application Gradle module or iOS App target name."),
111
+ sdkModuleName: z.string().optional().describe("Android SDK integration Gradle module for resValue/dependencies; default is unityLibrary."),
109
112
  env: z.enum(["prod", "pre", "test"]).optional().describe("TOPSDK API environment for selecting cached config."),
110
113
  appId: z.string().optional().describe("Application id for selecting cached config."),
111
114
  channelType: z.string().optional().describe("Channel type for selecting cached config."),
112
115
  },
113
116
  },
114
- async ({ projectRoot, appTarget, env, appId, channelType }) => {
117
+ async ({ projectRoot, appTarget, sdkModuleName, env, appId, channelType }) => {
115
118
  try {
116
- const result = await meetgamesIntegrate({ projectRoot, appTarget, env, appId, channelType, dryRun: false });
119
+ const result = await meetgamesIntegrate({ projectRoot, appTarget, sdkModuleName, env, appId, channelType, dryRun: false });
117
120
  if (result.hasErrors) {
118
121
  return {
119
122
  ...toSuccessResult(result),
@@ -139,14 +142,16 @@ export function createMeetgamesMcpServer(): McpServer {
139
142
  appSecret: z.string().optional().describe("Application secret for downloadSDKConfig sign (required unless TOPSDK_APP_SECRET is set)."),
140
143
  channelType: z.string().optional().describe("Channel type e.g. GOOGLE; required if not in local config."),
141
144
  appTarget: z.string().optional().describe("App target to integrate/check: Android application Gradle module or iOS App target name."),
145
+ sdkModuleName: z.string().optional().describe("Android SDK integration Gradle module for resValue/dependencies; default is unityLibrary."),
142
146
  dryRun: z.boolean().optional().describe("If true, integrate step previews only; fetch always writes config."),
143
147
  },
144
148
  },
145
- async ({ projectRoot, env, appId, appSecret, channelType, appTarget, dryRun }) => {
149
+ async ({ projectRoot, env, appId, appSecret, channelType, appTarget, sdkModuleName, dryRun }) => {
146
150
  try {
147
151
  const result = await meetgamesSetup({
148
152
  projectRoot,
149
153
  appTarget,
154
+ sdkModuleName,
150
155
  env,
151
156
  appId,
152
157
  appSecret,
@@ -70,13 +70,14 @@ function ensureSinglePlatformContext(
70
70
  projectRoot: string,
71
71
  packageRoot: string,
72
72
  appTarget?: string,
73
+ sdkModuleName?: string,
73
74
  remoteConfigPath?: string,
74
75
  cacheRoot?: string
75
76
  ): {
76
77
  platform: DetectedPlatform;
77
78
  ctx: ReturnType<typeof buildWorkspaceContext>;
78
79
  } {
79
- const detectedCtx = buildWorkspaceContext(projectRoot, packageRoot, { appTarget, remoteConfigPath, cacheRoot });
80
+ const detectedCtx = buildWorkspaceContext(projectRoot, packageRoot, { appTarget, sdkModuleName, remoteConfigPath, cacheRoot });
80
81
  const detected = detectSinglePlatform(detectedCtx);
81
82
  if (!detected.ok) throw new Error(detected.error);
82
83
  const platformCtx = platformContext(detectedCtx, detected.platform);
@@ -90,11 +91,16 @@ function resolveAppTarget(params: { appTarget?: string }): string | undefined {
90
91
  return params.appTarget?.trim() || undefined;
91
92
  }
92
93
 
93
- export function meetgamesDoctor(params: { projectRoot?: string; appTarget?: string }): {
94
+ function resolveSdkModuleName(params: { sdkModuleName?: string }): string | undefined {
95
+ return params.sdkModuleName?.trim() || undefined;
96
+ }
97
+
98
+ export function meetgamesDoctor(params: { projectRoot?: string; appTarget?: string; sdkModuleName?: string }): {
94
99
  projectRoot: string;
95
100
  packageRoot: string;
96
101
  platform: DetectedPlatform;
97
102
  android?: object;
103
+ androidSdkModule?: object;
98
104
  ios?: object;
99
105
  iosReserved?: object;
100
106
  } {
@@ -103,13 +109,15 @@ export function meetgamesDoctor(params: { projectRoot?: string; appTarget?: stri
103
109
  const { platform, ctx } = ensureSinglePlatformContext(
104
110
  projectRoot,
105
111
  packageRoot,
106
- resolveAppTarget(params)
112
+ resolveAppTarget(params),
113
+ resolveSdkModuleName(params)
107
114
  );
108
115
  const out: {
109
116
  projectRoot: string;
110
117
  packageRoot: string;
111
118
  platform: DetectedPlatform;
112
119
  android?: object;
120
+ androidSdkModule?: object;
113
121
  ios?: object;
114
122
  iosReserved?: object;
115
123
  } = {
@@ -117,7 +125,10 @@ export function meetgamesDoctor(params: { projectRoot?: string; appTarget?: stri
117
125
  packageRoot: ctx.packageRoot,
118
126
  platform,
119
127
  };
120
- if (platform === "android") out.android = ctx.android;
128
+ if (platform === "android") {
129
+ out.android = ctx.android;
130
+ out.androidSdkModule = ctx.androidSdkModule;
131
+ }
121
132
  if (platform === "ios") {
122
133
  out.ios = ctx.ios;
123
134
  out.iosReserved = ctx.iosReserved;
@@ -183,6 +194,7 @@ export async function meetgamesFetchConfig(params: {
183
194
  export async function meetgamesIntegrate(params: {
184
195
  projectRoot?: string;
185
196
  appTarget?: string;
197
+ sdkModuleName?: string;
186
198
  env?: TopSdkApiEnv;
187
199
  appId?: string;
188
200
  channelType?: string;
@@ -204,6 +216,7 @@ export async function meetgamesIntegrate(params: {
204
216
  projectRoot,
205
217
  packageRoot,
206
218
  resolveAppTarget(params),
219
+ resolveSdkModuleName(params),
207
220
  resolveCachedRemoteConfigPath(params),
208
221
  params.cacheRoot
209
222
  );
@@ -222,6 +235,7 @@ export async function meetgamesIntegrate(params: {
222
235
  export async function meetgamesSetup(params: {
223
236
  projectRoot?: string;
224
237
  appTarget?: string;
238
+ sdkModuleName?: string;
225
239
  env?: TopSdkApiEnv;
226
240
  appId?: string;
227
241
  appSecret?: string;
@@ -235,7 +249,8 @@ export async function meetgamesSetup(params: {
235
249
  const projectRoot = ensureProjectRoot(params.projectRoot);
236
250
  const packageRoot = resolvePackageRoot();
237
251
  const appTarget = resolveAppTarget(params);
238
- const { platform } = ensureSinglePlatformContext(projectRoot, packageRoot, appTarget, undefined, params.cacheRoot);
252
+ const sdkModuleName = resolveSdkModuleName(params);
253
+ const { platform } = ensureSinglePlatformContext(projectRoot, packageRoot, appTarget, sdkModuleName, undefined, params.cacheRoot);
239
254
  const fetch = await meetgamesFetchConfig({
240
255
  projectRoot,
241
256
  env: params.env,
@@ -253,6 +268,7 @@ export async function meetgamesSetup(params: {
253
268
  const integrate = await meetgamesIntegrate({
254
269
  projectRoot,
255
270
  appTarget,
271
+ sdkModuleName,
256
272
  env: params.env,
257
273
  appId: params.appId,
258
274
  channelType: params.channelType,
@@ -85,8 +85,8 @@ function readSettingsGradle(projectRoot: string): { rel: string; abs: string } |
85
85
  }
86
86
 
87
87
  function moduleGradleRel(ctx: WorkspaceContext): string | null {
88
- if (!ctx.android?.ok) return null;
89
- const moduleDir = ctx.android.moduleDir;
88
+ if (!ctx.androidSdkModule?.ok) return null;
89
+ const moduleDir = ctx.androidSdkModule.moduleDir;
90
90
  const a = path.join(moduleDir, "build.gradle");
91
91
  if (fs.existsSync(a)) return path.relative(ctx.projectRoot, a).split(path.sep).join("/");
92
92
  const b = path.join(moduleDir, "build.gradle.kts");
@@ -94,6 +94,20 @@ function moduleGradleRel(ctx: WorkspaceContext): string | null {
94
94
  return null;
95
95
  }
96
96
 
97
+ function sdkModuleError(ctx: WorkspaceContext): string {
98
+ return ctx.androidSdkModule?.ok === false
99
+ ? ctx.androidSdkModule.error
100
+ : "android SDK module not resolved";
101
+ }
102
+
103
+ function sdkModuleIsApplicationModule(ctx: WorkspaceContext): boolean {
104
+ return Boolean(
105
+ ctx.android?.ok &&
106
+ ctx.androidSdkModule?.ok &&
107
+ path.resolve(ctx.android.moduleDir) === path.resolve(ctx.androidSdkModule.moduleDir)
108
+ );
109
+ }
110
+
97
111
  function resolveRemoteConfigInput(ctx: WorkspaceContext, configFile: unknown): { abs: string; label: string } {
98
112
  if (typeof configFile === "string" && configFile.trim()) {
99
113
  const normalized = configFile.trim().replace(/\\/g, "/");
@@ -127,6 +141,7 @@ export const opHandlers: Record<string, OpHandler> = {
127
141
  */
128
142
  "gradle.applyMeetSdkRemoteConfig": async (ctx, store, args, _dry, _bc) => {
129
143
  if (!ctx.android?.ok) return fail(["android application module not detected"]);
144
+ if (!ctx.androidSdkModule?.ok) return fail([sdkModuleError(ctx)]);
130
145
  const config = resolveRemoteConfigInput(ctx, args.configFile);
131
146
  if (!fs.existsSync(config.abs)) return fail([`meet sdk remote config not found: ${config.label}`]);
132
147
  let raw: unknown;
@@ -197,9 +212,12 @@ export const opHandlers: Record<string, OpHandler> = {
197
212
  return fail(["gradle.applyMeetSdkRemoteConfig: root build.gradle.kts not supported in MVP"]);
198
213
  }
199
214
  const rootBefore = store.read(root.rel);
215
+ const shouldMirrorRepositoriesToBuildscript = /\bbuildscript\s*\{/.test(rootBefore) || buildscriptClasspaths.length > 0;
200
216
  const rootU = updateRootBuildGradleMeetSdkRemote(rootBefore, {
201
217
  repositories: repositoriesWrittenInSettings ? [] : repositories,
202
- buildscriptRepositories: usesPluginsDsl ? [] : buildscriptRepositories,
218
+ buildscriptRepositories: usesPluginsDsl
219
+ ? []
220
+ : [...buildscriptRepositories, ...(shouldMirrorRepositoriesToBuildscript ? repositories : [])],
203
221
  buildscriptClasspaths: usesPluginsDsl ? [] : buildscriptClasspaths,
204
222
  rootPluginsDsl: usesPluginsDsl ? collectMeetSdkRemotePluginsDslForRoot(resolved) : [],
205
223
  stripBuildscriptClasspaths: usesPluginsDsl ? buildscriptClasspaths : [],
@@ -215,6 +233,7 @@ export const opHandlers: Record<string, OpHandler> = {
215
233
  style: modulePluginStyle,
216
234
  applyPlugins: usesPluginsDsl ? [] : collectMeetSdkRemoteApplyPlugins(resolved),
217
235
  pluginsDsl: usesPluginsDsl ? collectMeetSdkRemotePluginsDslForModule(resolved) : [],
236
+ writeApplicationId: sdkModuleIsApplicationModule(ctx),
218
237
  });
219
238
  if (!modU.ok) return fail([modU.error]);
220
239
 
@@ -243,6 +262,7 @@ export const opHandlers: Record<string, OpHandler> = {
243
262
 
244
263
  "gradle.addDependency": (ctx, store, args, _dry, _bc) => {
245
264
  if (!ctx.android?.ok) return fail(["android application module not detected"]);
265
+ if (!ctx.androidSdkModule?.ok) return fail([sdkModuleError(ctx)]);
246
266
  const modRel = moduleGradleRel(ctx);
247
267
  if (!modRel) return fail(["module build.gradle not found"]);
248
268
  if (modRel.endsWith(".kts")) return fail(["gradle.addDependency: build.gradle.kts not supported in MVP"]);