@taptap/instant-games-open-mcp 1.23.6 → 1.23.7-beta.2

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/dist/maker.js CHANGED
@@ -3233,8 +3233,8 @@ var require_utils = __commonJS({
3233
3233
  }
3234
3234
  return ind;
3235
3235
  }
3236
- function removeDotSegments(path10) {
3237
- let input2 = path10;
3236
+ function removeDotSegments(path11) {
3237
+ let input2 = path11;
3238
3238
  const output2 = [];
3239
3239
  let nextSlash = -1;
3240
3240
  let len = 0;
@@ -3433,8 +3433,8 @@ var require_schemes = __commonJS({
3433
3433
  wsComponent.secure = void 0;
3434
3434
  }
3435
3435
  if (wsComponent.resourceName) {
3436
- const [path10, query] = wsComponent.resourceName.split("?");
3437
- wsComponent.path = path10 && path10 !== "/" ? path10 : void 0;
3436
+ const [path11, query] = wsComponent.resourceName.split("?");
3437
+ wsComponent.path = path11 && path11 !== "/" ? path11 : void 0;
3438
3438
  wsComponent.query = query;
3439
3439
  wsComponent.resourceName = void 0;
3440
3440
  }
@@ -12491,12 +12491,12 @@ var require_dist = __commonJS({
12491
12491
  throw new Error(`Unknown format "${name}"`);
12492
12492
  return f;
12493
12493
  };
12494
- function addFormats(ajv, list, fs9, exportName) {
12494
+ function addFormats(ajv, list, fs10, exportName) {
12495
12495
  var _a2;
12496
12496
  var _b;
12497
12497
  (_a2 = (_b = ajv.opts.code).formats) !== null && _a2 !== void 0 ? _a2 : _b.formats = (0, codegen_1._)`require("ajv-formats/dist/formats").${exportName}`;
12498
12498
  for (const f of list)
12499
- ajv.addFormat(f, fs9[f]);
12499
+ ajv.addFormat(f, fs10[f]);
12500
12500
  }
12501
12501
  module.exports = exports = formatsPlugin;
12502
12502
  Object.defineProperty(exports, "__esModule", { value: true });
@@ -12509,8 +12509,8 @@ var require_windows = __commonJS({
12509
12509
  "node_modules/isexe/windows.js"(exports, module) {
12510
12510
  module.exports = isexe;
12511
12511
  isexe.sync = sync;
12512
- var fs9 = __require("fs");
12513
- function checkPathExt(path10, options) {
12512
+ var fs10 = __require("fs");
12513
+ function checkPathExt(path11, options) {
12514
12514
  var pathext = options.pathExt !== void 0 ? options.pathExt : process.env.PATHEXT;
12515
12515
  if (!pathext) {
12516
12516
  return true;
@@ -12521,25 +12521,25 @@ var require_windows = __commonJS({
12521
12521
  }
12522
12522
  for (var i = 0; i < pathext.length; i++) {
12523
12523
  var p = pathext[i].toLowerCase();
12524
- if (p && path10.substr(-p.length).toLowerCase() === p) {
12524
+ if (p && path11.substr(-p.length).toLowerCase() === p) {
12525
12525
  return true;
12526
12526
  }
12527
12527
  }
12528
12528
  return false;
12529
12529
  }
12530
- function checkStat(stat, path10, options) {
12530
+ function checkStat(stat, path11, options) {
12531
12531
  if (!stat.isSymbolicLink() && !stat.isFile()) {
12532
12532
  return false;
12533
12533
  }
12534
- return checkPathExt(path10, options);
12534
+ return checkPathExt(path11, options);
12535
12535
  }
12536
- function isexe(path10, options, cb) {
12537
- fs9.stat(path10, function(er, stat) {
12538
- cb(er, er ? false : checkStat(stat, path10, options));
12536
+ function isexe(path11, options, cb) {
12537
+ fs10.stat(path11, function(er, stat) {
12538
+ cb(er, er ? false : checkStat(stat, path11, options));
12539
12539
  });
12540
12540
  }
12541
- function sync(path10, options) {
12542
- return checkStat(fs9.statSync(path10), path10, options);
12541
+ function sync(path11, options) {
12542
+ return checkStat(fs10.statSync(path11), path11, options);
12543
12543
  }
12544
12544
  }
12545
12545
  });
@@ -12549,14 +12549,14 @@ var require_mode = __commonJS({
12549
12549
  "node_modules/isexe/mode.js"(exports, module) {
12550
12550
  module.exports = isexe;
12551
12551
  isexe.sync = sync;
12552
- var fs9 = __require("fs");
12553
- function isexe(path10, options, cb) {
12554
- fs9.stat(path10, function(er, stat) {
12552
+ var fs10 = __require("fs");
12553
+ function isexe(path11, options, cb) {
12554
+ fs10.stat(path11, function(er, stat) {
12555
12555
  cb(er, er ? false : checkStat(stat, options));
12556
12556
  });
12557
12557
  }
12558
- function sync(path10, options) {
12559
- return checkStat(fs9.statSync(path10), options);
12558
+ function sync(path11, options) {
12559
+ return checkStat(fs10.statSync(path11), options);
12560
12560
  }
12561
12561
  function checkStat(stat, options) {
12562
12562
  return stat.isFile() && checkMode(stat, options);
@@ -12580,7 +12580,7 @@ var require_mode = __commonJS({
12580
12580
  // node_modules/isexe/index.js
12581
12581
  var require_isexe = __commonJS({
12582
12582
  "node_modules/isexe/index.js"(exports, module) {
12583
- var fs9 = __require("fs");
12583
+ var fs10 = __require("fs");
12584
12584
  var core;
12585
12585
  if (process.platform === "win32" || global.TESTING_WINDOWS) {
12586
12586
  core = require_windows();
@@ -12589,7 +12589,7 @@ var require_isexe = __commonJS({
12589
12589
  }
12590
12590
  module.exports = isexe;
12591
12591
  isexe.sync = sync;
12592
- function isexe(path10, options, cb) {
12592
+ function isexe(path11, options, cb) {
12593
12593
  if (typeof options === "function") {
12594
12594
  cb = options;
12595
12595
  options = {};
@@ -12599,7 +12599,7 @@ var require_isexe = __commonJS({
12599
12599
  throw new TypeError("callback not provided");
12600
12600
  }
12601
12601
  return new Promise(function(resolve, reject) {
12602
- isexe(path10, options || {}, function(er, is) {
12602
+ isexe(path11, options || {}, function(er, is) {
12603
12603
  if (er) {
12604
12604
  reject(er);
12605
12605
  } else {
@@ -12608,7 +12608,7 @@ var require_isexe = __commonJS({
12608
12608
  });
12609
12609
  });
12610
12610
  }
12611
- core(path10, options || {}, function(er, is) {
12611
+ core(path11, options || {}, function(er, is) {
12612
12612
  if (er) {
12613
12613
  if (er.code === "EACCES" || options && options.ignoreErrors) {
12614
12614
  er = null;
@@ -12618,9 +12618,9 @@ var require_isexe = __commonJS({
12618
12618
  cb(er, is);
12619
12619
  });
12620
12620
  }
12621
- function sync(path10, options) {
12621
+ function sync(path11, options) {
12622
12622
  try {
12623
- return core.sync(path10, options || {});
12623
+ return core.sync(path11, options || {});
12624
12624
  } catch (er) {
12625
12625
  if (options && options.ignoreErrors || er.code === "EACCES") {
12626
12626
  return false;
@@ -12636,7 +12636,7 @@ var require_isexe = __commonJS({
12636
12636
  var require_which = __commonJS({
12637
12637
  "node_modules/which/which.js"(exports, module) {
12638
12638
  var isWindows = process.platform === "win32" || process.env.OSTYPE === "cygwin" || process.env.OSTYPE === "msys";
12639
- var path10 = __require("path");
12639
+ var path11 = __require("path");
12640
12640
  var COLON = isWindows ? ";" : ":";
12641
12641
  var isexe = require_isexe();
12642
12642
  var getNotFoundError = (cmd) => Object.assign(new Error(`not found: ${cmd}`), { code: "ENOENT" });
@@ -12674,7 +12674,7 @@ var require_which = __commonJS({
12674
12674
  return opt.all && found.length ? resolve(found) : reject(getNotFoundError(cmd));
12675
12675
  const ppRaw = pathEnv[i];
12676
12676
  const pathPart = /^".*"$/.test(ppRaw) ? ppRaw.slice(1, -1) : ppRaw;
12677
- const pCmd = path10.join(pathPart, cmd);
12677
+ const pCmd = path11.join(pathPart, cmd);
12678
12678
  const p = !pathPart && /^\.[\\\/]/.test(cmd) ? cmd.slice(0, 2) + pCmd : pCmd;
12679
12679
  resolve(subStep(p, i, 0));
12680
12680
  });
@@ -12701,7 +12701,7 @@ var require_which = __commonJS({
12701
12701
  for (let i = 0; i < pathEnv.length; i++) {
12702
12702
  const ppRaw = pathEnv[i];
12703
12703
  const pathPart = /^".*"$/.test(ppRaw) ? ppRaw.slice(1, -1) : ppRaw;
12704
- const pCmd = path10.join(pathPart, cmd);
12704
+ const pCmd = path11.join(pathPart, cmd);
12705
12705
  const p = !pathPart && /^\.[\\\/]/.test(cmd) ? cmd.slice(0, 2) + pCmd : pCmd;
12706
12706
  for (let j = 0; j < pathExt.length; j++) {
12707
12707
  const cur = p + pathExt[j];
@@ -12749,7 +12749,7 @@ var require_path_key = __commonJS({
12749
12749
  var require_resolveCommand = __commonJS({
12750
12750
  "node_modules/cross-spawn/lib/util/resolveCommand.js"(exports, module) {
12751
12751
  "use strict";
12752
- var path10 = __require("path");
12752
+ var path11 = __require("path");
12753
12753
  var which = require_which();
12754
12754
  var getPathKey = require_path_key();
12755
12755
  function resolveCommandAttempt(parsed, withoutPathExt) {
@@ -12767,7 +12767,7 @@ var require_resolveCommand = __commonJS({
12767
12767
  try {
12768
12768
  resolved = which.sync(parsed.command, {
12769
12769
  path: env[getPathKey({ env })],
12770
- pathExt: withoutPathExt ? path10.delimiter : void 0
12770
+ pathExt: withoutPathExt ? path11.delimiter : void 0
12771
12771
  });
12772
12772
  } catch (e) {
12773
12773
  } finally {
@@ -12776,7 +12776,7 @@ var require_resolveCommand = __commonJS({
12776
12776
  }
12777
12777
  }
12778
12778
  if (resolved) {
12779
- resolved = path10.resolve(hasCustomCwd ? parsed.options.cwd : "", resolved);
12779
+ resolved = path11.resolve(hasCustomCwd ? parsed.options.cwd : "", resolved);
12780
12780
  }
12781
12781
  return resolved;
12782
12782
  }
@@ -12830,8 +12830,8 @@ var require_shebang_command = __commonJS({
12830
12830
  if (!match) {
12831
12831
  return null;
12832
12832
  }
12833
- const [path10, argument] = match[0].replace(/#! ?/, "").split(" ");
12834
- const binary = path10.split("/").pop();
12833
+ const [path11, argument] = match[0].replace(/#! ?/, "").split(" ");
12834
+ const binary = path11.split("/").pop();
12835
12835
  if (binary === "env") {
12836
12836
  return argument;
12837
12837
  }
@@ -12844,16 +12844,16 @@ var require_shebang_command = __commonJS({
12844
12844
  var require_readShebang = __commonJS({
12845
12845
  "node_modules/cross-spawn/lib/util/readShebang.js"(exports, module) {
12846
12846
  "use strict";
12847
- var fs9 = __require("fs");
12847
+ var fs10 = __require("fs");
12848
12848
  var shebangCommand = require_shebang_command();
12849
12849
  function readShebang(command) {
12850
12850
  const size = 150;
12851
12851
  const buffer = Buffer.alloc(size);
12852
12852
  let fd;
12853
12853
  try {
12854
- fd = fs9.openSync(command, "r");
12855
- fs9.readSync(fd, buffer, 0, size, 0);
12856
- fs9.closeSync(fd);
12854
+ fd = fs10.openSync(command, "r");
12855
+ fs10.readSync(fd, buffer, 0, size, 0);
12856
+ fs10.closeSync(fd);
12857
12857
  } catch (e) {
12858
12858
  }
12859
12859
  return shebangCommand(buffer.toString());
@@ -12866,7 +12866,7 @@ var require_readShebang = __commonJS({
12866
12866
  var require_parse = __commonJS({
12867
12867
  "node_modules/cross-spawn/lib/parse.js"(exports, module) {
12868
12868
  "use strict";
12869
- var path10 = __require("path");
12869
+ var path11 = __require("path");
12870
12870
  var resolveCommand = require_resolveCommand();
12871
12871
  var escape2 = require_escape();
12872
12872
  var readShebang = require_readShebang();
@@ -12891,7 +12891,7 @@ var require_parse = __commonJS({
12891
12891
  const needsShell = !isExecutableRegExp.test(commandFile);
12892
12892
  if (parsed.options.forceShell || needsShell) {
12893
12893
  const needsDoubleEscapeMetaChars = isCmdShimRegExp.test(commandFile);
12894
- parsed.command = path10.normalize(parsed.command);
12894
+ parsed.command = path11.normalize(parsed.command);
12895
12895
  parsed.command = escape2.command(parsed.command);
12896
12896
  parsed.args = parsed.args.map((arg) => escape2.argument(arg, needsDoubleEscapeMetaChars));
12897
12897
  const shellCommand = [parsed.command].concat(parsed.args).join(" ");
@@ -12981,7 +12981,7 @@ var require_cross_spawn = __commonJS({
12981
12981
  var cp = __require("child_process");
12982
12982
  var parse3 = require_parse();
12983
12983
  var enoent = require_enoent();
12984
- function spawn3(command, args, options) {
12984
+ function spawn5(command, args, options) {
12985
12985
  const parsed = parse3(command, args, options);
12986
12986
  const spawned = cp.spawn(parsed.command, parsed.args, parsed.options);
12987
12987
  enoent.hookChildProcess(spawned, parsed);
@@ -12993,8 +12993,8 @@ var require_cross_spawn = __commonJS({
12993
12993
  result.error = result.error || enoent.verifyENOENTSync(result.status, parsed);
12994
12994
  return result;
12995
12995
  }
12996
- module.exports = spawn3;
12997
- module.exports.spawn = spawn3;
12996
+ module.exports = spawn5;
12997
+ module.exports.spawn = spawn5;
12998
12998
  module.exports.sync = spawnSync5;
12999
12999
  module.exports._parse = parse3;
13000
13000
  module.exports._enoent = enoent;
@@ -13002,8 +13002,9 @@ var require_cross_spawn = __commonJS({
13002
13002
  });
13003
13003
 
13004
13004
  // src/maker/server/mcp.ts
13005
- import fs6 from "node:fs";
13006
- import path7 from "node:path";
13005
+ import { execFileSync, spawn as spawn4 } from "node:child_process";
13006
+ import fs7 from "node:fs";
13007
+ import path8 from "node:path";
13007
13008
  import { fileURLToPath as fileURLToPath2 } from "node:url";
13008
13009
 
13009
13010
  // node_modules/zod/v3/helpers/util.js
@@ -13365,8 +13366,8 @@ function getErrorMap() {
13365
13366
 
13366
13367
  // node_modules/zod/v3/helpers/parseUtil.js
13367
13368
  var makeIssue = (params) => {
13368
- const { data, path: path10, errorMaps, issueData } = params;
13369
- const fullPath = [...path10, ...issueData.path || []];
13369
+ const { data, path: path11, errorMaps, issueData } = params;
13370
+ const fullPath = [...path11, ...issueData.path || []];
13370
13371
  const fullIssue = {
13371
13372
  ...issueData,
13372
13373
  path: fullPath
@@ -13481,11 +13482,11 @@ var errorUtil;
13481
13482
 
13482
13483
  // node_modules/zod/v3/types.js
13483
13484
  var ParseInputLazyPath = class {
13484
- constructor(parent, value, path10, key) {
13485
+ constructor(parent, value, path11, key) {
13485
13486
  this._cachedPath = [];
13486
13487
  this.parent = parent;
13487
13488
  this.data = value;
13488
- this._path = path10;
13489
+ this._path = path11;
13489
13490
  this._key = key;
13490
13491
  }
13491
13492
  get path() {
@@ -17134,10 +17135,10 @@ function mergeDefs(...defs) {
17134
17135
  function cloneDef(schema) {
17135
17136
  return mergeDefs(schema._zod.def);
17136
17137
  }
17137
- function getElementAtPath(obj, path10) {
17138
- if (!path10)
17138
+ function getElementAtPath(obj, path11) {
17139
+ if (!path11)
17139
17140
  return obj;
17140
- return path10.reduce((acc, key) => acc == null ? void 0 : acc[key], obj);
17141
+ return path11.reduce((acc, key) => acc == null ? void 0 : acc[key], obj);
17141
17142
  }
17142
17143
  function promiseAllObject(promisesObj) {
17143
17144
  const keys = Object.keys(promisesObj);
@@ -17522,11 +17523,11 @@ function aborted(x, startIndex = 0) {
17522
17523
  }
17523
17524
  return false;
17524
17525
  }
17525
- function prefixIssues(path10, issues) {
17526
+ function prefixIssues(path11, issues) {
17526
17527
  return issues.map((iss) => {
17527
17528
  var _a2;
17528
17529
  (_a2 = iss).path ?? (_a2.path = []);
17529
- iss.path.unshift(path10);
17530
+ iss.path.unshift(path11);
17530
17531
  return iss;
17531
17532
  });
17532
17533
  }
@@ -28436,8 +28437,105 @@ function formatIdentifyHint() {
28436
28437
  ].join("\n");
28437
28438
  }
28438
28439
 
28440
+ // src/maker/server/hiddenStdioTransport.ts
28441
+ import { spawn as spawn2 } from "node:child_process";
28442
+ import { PassThrough as PassThrough2 } from "node:stream";
28443
+ var HiddenStdioClientTransport = class {
28444
+ constructor(serverParams) {
28445
+ this.serverParams = serverParams;
28446
+ this.abortController = new AbortController();
28447
+ this.readBuffer = new ReadBuffer();
28448
+ this.stderrStream = serverParams.stderr === "pipe" || serverParams.stderr === "overlapped" ? new PassThrough2() : null;
28449
+ }
28450
+ async start() {
28451
+ if (this.process) {
28452
+ throw new Error("HiddenStdioClientTransport already started.");
28453
+ }
28454
+ await new Promise((resolve, reject) => {
28455
+ var _a2, _b, _c;
28456
+ this.process = spawn2(this.serverParams.command, this.serverParams.args ?? [], {
28457
+ env: this.serverParams.env,
28458
+ stdio: ["pipe", "pipe", this.serverParams.stderr ?? "inherit"],
28459
+ shell: false,
28460
+ signal: this.abortController.signal,
28461
+ windowsHide: true,
28462
+ cwd: this.serverParams.cwd
28463
+ });
28464
+ this.process.on("error", (error2) => {
28465
+ var _a3, _b2;
28466
+ if (error2.name === "AbortError") {
28467
+ (_a3 = this.onclose) == null ? void 0 : _a3.call(this);
28468
+ return;
28469
+ }
28470
+ reject(error2);
28471
+ (_b2 = this.onerror) == null ? void 0 : _b2.call(this, error2);
28472
+ });
28473
+ this.process.on("spawn", () => resolve());
28474
+ this.process.on("close", () => {
28475
+ var _a3;
28476
+ this.process = void 0;
28477
+ (_a3 = this.onclose) == null ? void 0 : _a3.call(this);
28478
+ });
28479
+ (_a2 = this.process.stdin) == null ? void 0 : _a2.on("error", (error2) => {
28480
+ var _a3;
28481
+ return (_a3 = this.onerror) == null ? void 0 : _a3.call(this, error2);
28482
+ });
28483
+ (_b = this.process.stdout) == null ? void 0 : _b.on("data", (chunk) => {
28484
+ this.readBuffer.append(chunk);
28485
+ this.processReadBuffer();
28486
+ });
28487
+ (_c = this.process.stdout) == null ? void 0 : _c.on("error", (error2) => {
28488
+ var _a3;
28489
+ return (_a3 = this.onerror) == null ? void 0 : _a3.call(this, error2);
28490
+ });
28491
+ if (this.stderrStream && this.process.stderr) {
28492
+ this.process.stderr.pipe(this.stderrStream);
28493
+ }
28494
+ });
28495
+ }
28496
+ get stderr() {
28497
+ var _a2;
28498
+ return this.stderrStream || ((_a2 = this.process) == null ? void 0 : _a2.stderr) || null;
28499
+ }
28500
+ get pid() {
28501
+ var _a2;
28502
+ return ((_a2 = this.process) == null ? void 0 : _a2.pid) ?? null;
28503
+ }
28504
+ async close() {
28505
+ this.abortController.abort();
28506
+ this.process = void 0;
28507
+ this.readBuffer.clear();
28508
+ }
28509
+ async send(message) {
28510
+ var _a2;
28511
+ const stdin = (_a2 = this.process) == null ? void 0 : _a2.stdin;
28512
+ if (!stdin) {
28513
+ throw new Error("Not connected");
28514
+ }
28515
+ const json2 = serializeMessage(message);
28516
+ if (stdin.write(json2)) {
28517
+ return;
28518
+ }
28519
+ await new Promise((resolve) => stdin.once("drain", resolve));
28520
+ }
28521
+ processReadBuffer() {
28522
+ var _a2, _b;
28523
+ for (; ; ) {
28524
+ try {
28525
+ const message = this.readBuffer.readMessage();
28526
+ if (message === null) {
28527
+ break;
28528
+ }
28529
+ (_a2 = this.onmessage) == null ? void 0 : _a2.call(this, message);
28530
+ } catch (error2) {
28531
+ (_b = this.onerror) == null ? void 0 : _b.call(this, error2 instanceof Error ? error2 : new Error(String(error2)));
28532
+ }
28533
+ }
28534
+ }
28535
+ };
28536
+
28439
28537
  // src/maker/cli/projects.ts
28440
- import { spawn as spawn2, spawnSync as spawnSync3 } from "node:child_process";
28538
+ import { spawn as spawn3, spawnSync as spawnSync3 } from "node:child_process";
28441
28539
  import fs4 from "node:fs";
28442
28540
  import path5 from "node:path";
28443
28541
 
@@ -28702,20 +28800,38 @@ import { spawnSync as spawnSync2 } from "node:child_process";
28702
28800
  import fs3 from "node:fs";
28703
28801
  import os3 from "node:os";
28704
28802
  import path4 from "node:path";
28705
- var DEFAULT_AI_DEV_KIT_URL = "https://urhox-demo-platform.spark.xd.com/ai-dev-kit/pd/stable/ai-dev-kit.zip";
28803
+ var AI_DEV_KIT_URLS = {
28804
+ production: "https://urhox-demo-platform.spark.xd.com/ai-dev-kit/pd/stable/ai-dev-kit.zip",
28805
+ rnd: "https://urhox-demo-platform.spark.xd.com/ai-dev-kit/rnd/latest/ai-dev-kit.zip"
28806
+ };
28807
+ var DEFAULT_AI_DEV_KIT_URL = AI_DEV_KIT_URLS.production;
28808
+ function resolveDefaultAiDevKitUrl(environment = EnvConfig.environment) {
28809
+ return AI_DEV_KIT_URLS[environment] || AI_DEV_KIT_URLS.production;
28810
+ }
28706
28811
  var DEV_KIT_IGNORE_BEGIN = "# >>> TapTap Maker AI dev kit (local only) >>>";
28707
28812
  var DEV_KIT_IGNORE_END = "# <<< TapTap Maker AI dev kit (local only) <<<";
28708
28813
  var DEV_KIT_GITIGNORE_STAGING_FILE = ".gitignore.dev-kit-before-clone";
28709
28814
  var DEV_KIT_REQUIRED_ENTRIES = ["CLAUDE.md", "examples", "templates", "urhox-libs"];
28815
+ var ALWAYS_IGNORED_LOCAL_ENTRIES = [".DS_Store", ".maker"];
28710
28816
  var DEV_KIT_MANAGED_ENTRY_CANDIDATES = [
28817
+ ".claude",
28818
+ ".cli",
28819
+ ".codex",
28820
+ ".cursor",
28711
28821
  ".emmylua",
28822
+ ".gemini",
28823
+ "AGENTS.md",
28712
28824
  "CLAUDE.md",
28713
28825
  "engine-docs",
28714
28826
  "examples",
28827
+ "schemas",
28828
+ "skills",
28715
28829
  "templates",
28830
+ "tools",
28716
28831
  "urhox-libs"
28717
28832
  ];
28718
28833
  var SKIPPED_TOP_LEVEL_ENTRIES = /* @__PURE__ */ new Set(["scripts", ".DS_Store", "ai-dev-kit.zip"]);
28834
+ var SKILL_INSTALLER_OUTPUT_ENTRIES = [".claude", ".codex", ".cursor", ".gemini"];
28719
28835
  function inspectAiDevKit(targetDir) {
28720
28836
  const resolvedTargetDir = path4.resolve(targetDir);
28721
28837
  const presentEntries = DEV_KIT_REQUIRED_ENTRIES.filter(
@@ -28741,7 +28857,7 @@ function listPresentDevKitManagedEntries(targetDir) {
28741
28857
  async function installAiDevKit(options = {}) {
28742
28858
  const targetDir = path4.resolve(options.targetDir || ".");
28743
28859
  fs3.mkdirSync(targetDir, { recursive: true });
28744
- const preparedSource = options.sourceDir ? path4.resolve(options.sourceDir) : await downloadAndExtractDevKit(options.url || DEFAULT_AI_DEV_KIT_URL);
28860
+ const preparedSource = options.sourceDir ? path4.resolve(options.sourceDir) : await downloadAndExtractDevKit(options.url || resolveDefaultAiDevKitUrl());
28745
28861
  const sourceDir = resolveDevKitRoot(preparedSource);
28746
28862
  const entries = fs3.readdirSync(sourceDir, { withFileTypes: true });
28747
28863
  const installedEntries = [];
@@ -28756,19 +28872,49 @@ async function installAiDevKit(options = {}) {
28756
28872
  });
28757
28873
  installedEntries.push(entry.name);
28758
28874
  }
28875
+ const skillInstaller = runDevKitSkillInstallerForInstall(targetDir, {
28876
+ onStart: options.onSkillInstallerStart
28877
+ });
28759
28878
  const stagedGitignorePath = path4.join(targetDir, DEV_KIT_GITIGNORE_STAGING_FILE);
28760
- writeDevKitStagedGitignore(stagedGitignorePath, installedEntries);
28879
+ writeDevKitStagedGitignore(stagedGitignorePath, [
28880
+ ...installedEntries,
28881
+ ...listPresentSkillInstallerOutputEntries(targetDir)
28882
+ ]);
28761
28883
  return {
28762
28884
  targetDir,
28763
28885
  sourceDir,
28764
28886
  installedEntries: installedEntries.sort(),
28765
28887
  skippedEntries: skippedEntries.sort(),
28766
28888
  gitignorePath: path4.join(targetDir, ".gitignore"),
28767
- stagedGitignorePath
28889
+ stagedGitignorePath,
28890
+ skillInstaller
28891
+ };
28892
+ }
28893
+ function inspectAiDevKitSkillInstallStatus(targetDir) {
28894
+ const resolvedTargetDir = path4.resolve(targetDir);
28895
+ const targets = SKILL_INSTALLER_OUTPUT_ENTRIES.map((entry) => {
28896
+ const skillsDir = path4.join(resolvedTargetDir, entry, "skills");
28897
+ const present = fs3.existsSync(skillsDir) && fs3.statSync(skillsDir).isDirectory();
28898
+ const skillCount = present ? fs3.readdirSync(skillsDir, { withFileTypes: true }).filter((item) => item.isDirectory() || item.isSymbolicLink()).length : 0;
28899
+ return {
28900
+ name: entry.replace(/^\./, ""),
28901
+ path: skillsDir,
28902
+ present,
28903
+ skillCount
28904
+ };
28905
+ });
28906
+ const installedCount = targets.filter((target) => target.present && target.skillCount > 0).length;
28907
+ const status = installedCount === targets.length ? "installed" : installedCount > 0 ? "partial" : "missing";
28908
+ return {
28909
+ status,
28910
+ summary: targets.map((target) => `${target.name}=${target.skillCount}`).join(", "),
28911
+ targets
28768
28912
  };
28769
28913
  }
28770
28914
  function createDevKitGitignoreBlock(entries) {
28771
- const ignoreEntries = Array.from(/* @__PURE__ */ new Set([".DS_Store", ...entries.map(formatIgnoreEntry)])).filter(Boolean).sort();
28915
+ const ignoreEntries = Array.from(
28916
+ new Set([...ALWAYS_IGNORED_LOCAL_ENTRIES, ...entries].map(formatIgnoreEntry))
28917
+ ).filter(Boolean).sort();
28772
28918
  return [DEV_KIT_IGNORE_BEGIN, ...ignoreEntries, DEV_KIT_IGNORE_END].join("\n");
28773
28919
  }
28774
28920
  function writeDevKitStagedGitignore(stagedGitignorePath, entries) {
@@ -28819,6 +28965,87 @@ function copyEntry(source, target, options = {}) {
28819
28965
  fs3.copyFileSync(source, target);
28820
28966
  }
28821
28967
  }
28968
+ function installAiDevKitSkills(targetDir, options = {}) {
28969
+ var _a2;
28970
+ const toolsDir = path4.join(targetDir, "tools");
28971
+ if (!fs3.existsSync(toolsDir) || !fs3.statSync(toolsDir).isDirectory()) {
28972
+ return {
28973
+ ok: false,
28974
+ status: "skipped",
28975
+ stdout: "",
28976
+ stderr: "",
28977
+ summary: "skipped: tools directory not found",
28978
+ reason: "tools_not_found"
28979
+ };
28980
+ }
28981
+ const isWindows = process.platform === "win32";
28982
+ const scriptName = isWindows ? "install-skills.ps1" : "install-skills.sh";
28983
+ const scriptPath = path4.join(toolsDir, scriptName);
28984
+ if (!fs3.existsSync(scriptPath)) {
28985
+ return {
28986
+ ok: false,
28987
+ status: "skipped",
28988
+ script: scriptPath,
28989
+ stdout: "",
28990
+ stderr: "",
28991
+ summary: `skipped: ${scriptName} not found`,
28992
+ reason: "script_not_found"
28993
+ };
28994
+ }
28995
+ const command = isWindows ? ["powershell.exe", "-NoProfile", "-ExecutionPolicy", "Bypass", "-File", scriptPath, "all"] : ["bash", scriptPath, "all"];
28996
+ (_a2 = options.onStart) == null ? void 0 : _a2.call(options, {
28997
+ platform: process.platform,
28998
+ script: scriptPath,
28999
+ cwd: toolsDir,
29000
+ command
29001
+ });
29002
+ const result = spawnSync2(command[0], command.slice(1), { cwd: toolsDir, encoding: "utf8" });
29003
+ if (result.status !== 0) {
29004
+ throw new AiDevKitSkillInstallerError(
29005
+ formatFailedSkillInstallerResult({
29006
+ platform: process.platform,
29007
+ scriptPath,
29008
+ toolsDir,
29009
+ command,
29010
+ result
29011
+ })
29012
+ );
29013
+ }
29014
+ const stdout = String(result.stdout || "");
29015
+ const stderr = String(result.stderr || "");
29016
+ return {
29017
+ ok: true,
29018
+ status: "installed",
29019
+ script: scriptPath,
29020
+ stdout,
29021
+ stderr,
29022
+ summary: summarizeSkillInstallerOutput(stdout)
29023
+ };
29024
+ }
29025
+ function runDevKitSkillInstallerForInstall(targetDir, options = {}) {
29026
+ try {
29027
+ return installAiDevKitSkills(targetDir, options);
29028
+ } catch (error2) {
29029
+ if (error2 instanceof AiDevKitSkillInstallerError) {
29030
+ return error2.result;
29031
+ }
29032
+ const message = error2 instanceof Error ? error2.message : String(error2);
29033
+ return {
29034
+ ok: false,
29035
+ status: "failed",
29036
+ stdout: "",
29037
+ stderr: "",
29038
+ summary: "failed",
29039
+ reason: "installer_failed",
29040
+ error: message
29041
+ };
29042
+ }
29043
+ }
29044
+ function listPresentSkillInstallerOutputEntries(targetDir) {
29045
+ return SKILL_INSTALLER_OUTPUT_ENTRIES.filter(
29046
+ (entry) => fs3.existsSync(path4.join(targetDir, entry))
29047
+ );
29048
+ }
28822
29049
  async function downloadAndExtractDevKit(url2) {
28823
29050
  const tempDir = fs3.mkdtempSync(path4.join(os3.tmpdir(), "taptap-maker-ai-dev-kit-"));
28824
29051
  const zipPath = path4.join(tempDir, "ai-dev-kit.zip");
@@ -28895,6 +29122,67 @@ function formatSpawnFailure(result) {
28895
29122
  var _a2;
28896
29123
  return ((_a2 = result.error) == null ? void 0 : _a2.message) || String(result.stderr || "").trim() || String(result.stdout || "").trim() || `exit status ${result.status ?? "unknown"}`;
28897
29124
  }
29125
+ function formatDevKitSkillInstallerFailure(options) {
29126
+ return [
29127
+ "Failed to install AI dev kit skills",
29128
+ `platform: ${options.platform}`,
29129
+ `script: ${options.scriptPath}`,
29130
+ `cwd: ${options.toolsDir}`,
29131
+ `command: ${options.command.map(shellQuote).join(" ")}`,
29132
+ `exit_status: ${options.result.status ?? "unknown"}`,
29133
+ options.result.signal ? `signal: ${options.result.signal}` : "",
29134
+ options.result.error ? `spawn_error: ${options.result.error.message}` : "",
29135
+ "stdout:",
29136
+ formatSpawnOutput(options.result.stdout),
29137
+ "stderr:",
29138
+ formatSpawnOutput(options.result.stderr)
29139
+ ].filter((line) => line.length > 0).join("\n");
29140
+ }
29141
+ function formatFailedSkillInstallerResult(options) {
29142
+ return {
29143
+ ok: false,
29144
+ status: "failed",
29145
+ script: options.scriptPath,
29146
+ stdout: String(options.result.stdout || ""),
29147
+ stderr: String(options.result.stderr || ""),
29148
+ summary: summarizeSkillInstallerFailure(options.result),
29149
+ reason: "installer_failed",
29150
+ error: formatDevKitSkillInstallerFailure(options)
29151
+ };
29152
+ }
29153
+ function summarizeSkillInstallerFailure(result) {
29154
+ if (typeof result.status === "number") {
29155
+ return `failed: exit_status=${result.status}`;
29156
+ }
29157
+ if (result.signal) {
29158
+ return `failed: signal=${result.signal}`;
29159
+ }
29160
+ if (result.error) {
29161
+ return `failed: ${result.error.message}`;
29162
+ }
29163
+ return "failed";
29164
+ }
29165
+ var AiDevKitSkillInstallerError = class extends Error {
29166
+ constructor(result) {
29167
+ super(result.error || result.summary);
29168
+ this.result = result;
29169
+ this.name = "AiDevKitSkillInstallerError";
29170
+ }
29171
+ };
29172
+ function formatSpawnOutput(value) {
29173
+ const text = String(value || "").trim();
29174
+ return text.length > 0 ? text : "(empty)";
29175
+ }
29176
+ function summarizeSkillInstallerOutput(stdout) {
29177
+ const entries = stdout.split(/\r?\n/).map((line) => line.match(/\[install-skills\]\s+([^:]+):\s+installed=(\d+)/)).filter((match) => Boolean(match)).map((match) => `${match[1]}=${match[2]}`);
29178
+ return entries.length > 0 ? entries.join(", ") : "completed";
29179
+ }
29180
+ function shellQuote(value) {
29181
+ if (/^[A-Za-z0-9_./:=@-]+$/.test(value)) {
29182
+ return value;
29183
+ }
29184
+ return `'${value.replace(/'/g, "'\\''")}'`;
29185
+ }
28898
29186
  function resolveDevKitRoot(sourceDir) {
28899
29187
  const directEntries = fs3.existsSync(sourceDir) ? fs3.readdirSync(sourceDir) : [];
28900
29188
  if (looksLikeDevKitRoot(sourceDir)) {
@@ -28912,6 +29200,9 @@ function looksLikeDevKitRoot(dir) {
28912
29200
  return fs3.existsSync(path4.join(dir, "engine-docs")) || fs3.existsSync(path4.join(dir, ".emmylua")) || fs3.existsSync(path4.join(dir, "urhox-libs"));
28913
29201
  }
28914
29202
  function formatIgnoreEntry(entry) {
29203
+ if (entry === ".DS_Store") {
29204
+ return entry;
29205
+ }
28915
29206
  return entry.endsWith("/") ? entry : fsSafeDirectoryPattern(entry);
28916
29207
  }
28917
29208
  function fsSafeDirectoryPattern(entry) {
@@ -29413,7 +29704,7 @@ async function inspectMakerRemoteSyncStatus(cwd) {
29413
29704
  }
29414
29705
  function getMakerRemoteSyncFailureNextAction(failure) {
29415
29706
  if (failure.classification === "auth") {
29416
- return "暂时无法检查 Maker 远端是否有新提交:Git 鉴权失败。请先运行 `taptap-maker pat set` 并粘贴新的 Maker PAT 后,再重新读取 maker://status。";
29707
+ return `暂时无法检查 Maker 远端是否有新提交:Git 鉴权失败。PAT 页面:${getMakerPatTokensUrl()}。请在该页面创建新的 Maker PAT,然后运行 \`taptap-maker pat set\` 并粘贴 PAT 后,再重新读取 maker://status。`;
29417
29708
  }
29418
29709
  return "暂时无法检查 Maker 远端是否有新提交。请把 failure 信息反馈给用户;如果只是 503、5xx、超时或网络中断,可稍后重新读取 maker://status。";
29419
29710
  }
@@ -29634,7 +29925,7 @@ function nextActionForFailure(classification) {
29634
29925
  case "git_missing":
29635
29926
  return "本机未检测到可用的 Git。请用户自行安装 Git,并在 `git --version` 可用后重启 MCP 客户端再重试;安装前不要执行 clone、fetch、commit 或 push。";
29636
29927
  case "auth":
29637
- return "运行 `taptap-maker pat set` 并粘贴新的 Maker PAT 后,再重试 maker_build_current_directory;如果仍失败,请确认 PAT 是否过期或缺少 Maker git 权限。";
29928
+ return `Maker Git 鉴权失败。PAT 页面:${getMakerPatTokensUrl()}。请在该页面创建新的 Maker PAT,然后运行 \`taptap-maker pat set\` 并粘贴 PAT 后重试。`;
29638
29929
  case "remote_transient":
29639
29930
  return "远端 Maker git 服务临时不可用。本地 commit 会保留;不要手动执行通用 git push,稍后直接重试 maker_build_current_directory。";
29640
29931
  case "branch_not_allowed":
@@ -29652,7 +29943,7 @@ function nextActionForFailure(classification) {
29652
29943
  function pushGit(args, cwd, onProgress) {
29653
29944
  return new Promise((resolve, reject) => {
29654
29945
  const gitCommand = getGitCommand();
29655
- const child = spawn2(gitCommand, args, {
29946
+ const child = spawn3(gitCommand, args, {
29656
29947
  cwd,
29657
29948
  stdio: ["ignore", "pipe", "pipe"]
29658
29949
  });
@@ -30128,7 +30419,7 @@ function readGitSync(args) {
30128
30419
  function readGit(args, cwd) {
30129
30420
  return new Promise((resolve, reject) => {
30130
30421
  const gitCommand = getGitCommand();
30131
- const child = spawn2(gitCommand, args, {
30422
+ const child = spawn3(gitCommand, args, {
30132
30423
  cwd,
30133
30424
  stdio: ["ignore", "pipe", "pipe"]
30134
30425
  });
@@ -30238,7 +30529,7 @@ function sleep(ms) {
30238
30529
  function runGitCapture(args, options = {}) {
30239
30530
  return new Promise((resolve, reject) => {
30240
30531
  const gitCommand = getGitCommand();
30241
- const child = spawn2(gitCommand, args, {
30532
+ const child = spawn3(gitCommand, args, {
30242
30533
  cwd: options.cwd,
30243
30534
  stdio: ["ignore", "pipe", "pipe"]
30244
30535
  });
@@ -30271,7 +30562,7 @@ function runGitCapture(args, options = {}) {
30271
30562
  function runGit(args, options) {
30272
30563
  return new Promise((resolve, reject) => {
30273
30564
  const gitCommand = getGitCommand();
30274
- const child = spawn2(gitCommand, args, {
30565
+ const child = spawn3(gitCommand, args, {
30275
30566
  cwd: options.cwd,
30276
30567
  stdio: ["ignore", "pipe", "pipe"]
30277
30568
  });
@@ -30436,11 +30727,13 @@ function formatMakerSkillStatus(_options = {}) {
30436
30727
  path: path6.join(resolveMakerSkillSourceDir(skill.name), "SKILL.md")
30437
30728
  }));
30438
30729
  return [
30439
- "TapTap bundled workflow skills",
30730
+ "TapTap Maker workflow guide documents",
30440
30731
  "",
30441
30732
  ...skillDocuments.map((skill) => `- ${skill.name}: ${skill.path}`),
30442
30733
  "",
30443
- "These skill documents are bundled in the package. Let the current AI client decide whether and how to load them."
30734
+ "Use these documents as reading references for Maker local workflows.",
30735
+ "Maker initialization next_step: execute `taptap-maker init`.",
30736
+ "Load these documents when the current AI client supports reading local guide files."
30444
30737
  ].join("\n");
30445
30738
  }
30446
30739
  function resolveMakerSkillSourceDir(skillName = MAKER_LOCAL_SKILL_NAME) {
@@ -30466,10 +30759,386 @@ function getBundledSkillSourceDir(skillName) {
30466
30759
  return path6.join(packageRoot, "skills", skillName);
30467
30760
  }
30468
30761
 
30762
+ // src/maker/server/runtimeLogs.ts
30763
+ import fs6 from "node:fs";
30764
+ import path7 from "node:path";
30765
+ var DEFAULT_RUNTIME_LOG_SINCE_SECONDS = 600;
30766
+ var MAX_RUNTIME_LOG_WINDOW_SECONDS = 3600;
30767
+ var DEFAULT_RUNTIME_LOG_TOPICS = ["user_script", "server_user_script"];
30768
+ var MERGED_RUNTIME_LOG_FILE = "runtime.log";
30769
+ var RUNTIME_LOG_FILES_TO_RESET = [
30770
+ MERGED_RUNTIME_LOG_FILE,
30771
+ "state.json",
30772
+ "last-query-runtime-logs-result.json",
30773
+ "runtime.raw.log",
30774
+ "engine.log",
30775
+ "user_script.log",
30776
+ "server_user_script.log"
30777
+ ];
30778
+ async function watchRuntimeLogs(options) {
30779
+ var _a2, _b;
30780
+ if (options.reset) {
30781
+ resetRuntimeLogs(options.projectRoot);
30782
+ }
30783
+ const intervalMs = options.intervalMs ?? 5e3;
30784
+ const maxConsecutiveFailures = options.maxConsecutiveFailures;
30785
+ const sleep2 = options.sleep || defaultSleep;
30786
+ const nowMs = options.nowMs || Date.now;
30787
+ let polls = 0;
30788
+ let writtenLogs = 0;
30789
+ let lastResult;
30790
+ let consecutiveFailures = 0;
30791
+ for (; ; ) {
30792
+ try {
30793
+ const previousCursor = lastResult == null ? void 0 : lastResult.nextStartTime;
30794
+ const result = await pullRuntimeLogs({
30795
+ projectRoot: options.projectRoot,
30796
+ projectId: options.projectId,
30797
+ sinceSeconds: options.sinceSeconds,
30798
+ limit: options.limit,
30799
+ nowMs: options.nowMs,
30800
+ callRemoteRuntimeLogs: options.callRemoteRuntimeLogs
30801
+ });
30802
+ polls += 1;
30803
+ writtenLogs += result.writtenLogs;
30804
+ lastResult = result;
30805
+ consecutiveFailures = 0;
30806
+ await ((_a2 = options.onPoll) == null ? void 0 : _a2.call(options, result));
30807
+ if (options.maxPolls !== void 0 && polls >= options.maxPolls) {
30808
+ return { projectRoot: options.projectRoot, polls, writtenLogs, lastResult };
30809
+ }
30810
+ const cursorProgressed = previousCursor === void 0 ? result.writtenLogs > 0 : result.nextStartTime !== previousCursor;
30811
+ if (!result.hasMore || !cursorProgressed) {
30812
+ await sleep2(intervalMs);
30813
+ }
30814
+ } catch (error2) {
30815
+ consecutiveFailures += 1;
30816
+ writeRuntimeLogFailureState(options.projectRoot, {
30817
+ nowMs,
30818
+ consecutiveFailures,
30819
+ error: error2
30820
+ });
30821
+ await ((_b = options.onError) == null ? void 0 : _b.call(options, error2, consecutiveFailures));
30822
+ if (isNonRetryableRuntimeLogError(error2)) {
30823
+ throw new Error(
30824
+ `runtime log watch stopped after non-retryable failure: ${error2 instanceof Error ? error2.message : String(error2)}`
30825
+ );
30826
+ }
30827
+ if (maxConsecutiveFailures !== void 0 && consecutiveFailures >= maxConsecutiveFailures) {
30828
+ throw new Error(
30829
+ `runtime log watch stopped after ${consecutiveFailures} consecutive failures: ${error2 instanceof Error ? error2.message : String(error2)}`
30830
+ );
30831
+ }
30832
+ await sleep2(intervalMs);
30833
+ }
30834
+ }
30835
+ }
30836
+ async function pullRuntimeLogs(options) {
30837
+ const nowMs = options.nowMs || Date.now;
30838
+ const state = readRuntimeLogState(options.projectRoot);
30839
+ const nowSeconds = nowMs() / 1e3;
30840
+ const stateFresh = (state == null ? void 0 : state.nextStartTime) !== void 0 && isFreshRuntimeLogCursor(state.nextStartTime, nowSeconds);
30841
+ const queryArgs = {};
30842
+ let cursorExpired = false;
30843
+ if (options.startTime !== void 0) {
30844
+ queryArgs.startTime = options.startTime;
30845
+ } else if (stateFresh && state) {
30846
+ queryArgs.startTime = state.nextStartTime;
30847
+ } else {
30848
+ cursorExpired = Boolean(state == null ? void 0 : state.nextStartTime);
30849
+ queryArgs.sinceSeconds = options.sinceSeconds ?? DEFAULT_RUNTIME_LOG_SINCE_SECONDS;
30850
+ }
30851
+ queryArgs.topics = options.topics && options.topics.length > 0 ? options.topics : DEFAULT_RUNTIME_LOG_TOPICS;
30852
+ if (options.limit !== void 0) {
30853
+ queryArgs.limit = options.limit;
30854
+ }
30855
+ const result = await options.callRemoteRuntimeLogs(queryArgs);
30856
+ const appendResult = appendRuntimeLogs(options.projectRoot, result.logs);
30857
+ const nextStartTime = resolveNextRuntimeLogCursor(result);
30858
+ const nowIso = new Date(nowMs()).toISOString();
30859
+ const nextState = {
30860
+ ...state || {},
30861
+ ...options.projectId ? { appId: options.projectId, projectId: options.projectId } : {},
30862
+ nextStartTime,
30863
+ updatedAt: nowIso,
30864
+ lastPollAt: nowIso,
30865
+ lastSuccessAt: nowIso,
30866
+ lastWrittenLogs: appendResult.written,
30867
+ consecutiveFailures: 0,
30868
+ lastError: null
30869
+ };
30870
+ writeRuntimeLogState(options.projectRoot, nextState);
30871
+ return {
30872
+ projectRoot: options.projectRoot,
30873
+ queryArgs,
30874
+ writtenLogs: appendResult.written,
30875
+ files: appendResult.files,
30876
+ statePath: getRuntimeLogStatePath(options.projectRoot),
30877
+ nextStartTime,
30878
+ serverTime: result.serverTime,
30879
+ hasMore: result.hasMore,
30880
+ cursorExpired
30881
+ };
30882
+ }
30883
+ function resetRuntimeLogs(projectRoot) {
30884
+ const runtimeDir = getRuntimeLogDir(projectRoot);
30885
+ fs6.mkdirSync(runtimeDir, { recursive: true });
30886
+ for (const file2 of RUNTIME_LOG_FILES_TO_RESET) {
30887
+ fs6.rmSync(path7.join(runtimeDir, file2), { force: true });
30888
+ }
30889
+ return runtimeDir;
30890
+ }
30891
+ function readRuntimeLogState(projectRoot) {
30892
+ const statePath = getRuntimeLogStatePath(projectRoot);
30893
+ if (!fs6.existsSync(statePath)) {
30894
+ return null;
30895
+ }
30896
+ try {
30897
+ const state = JSON.parse(fs6.readFileSync(statePath, "utf8"));
30898
+ return isRuntimeLogState(state) ? state : null;
30899
+ } catch {
30900
+ return null;
30901
+ }
30902
+ }
30903
+ function formatRuntimeLogPullResult(result) {
30904
+ return [
30905
+ "✓ Maker runtime logs pulled once",
30906
+ "",
30907
+ `- project_root: ${result.projectRoot}`,
30908
+ `- query_args: ${JSON.stringify(result.queryArgs)}`,
30909
+ `- written_logs: ${result.writtenLogs}`,
30910
+ `- files: ${result.files.length > 0 ? result.files.join(", ") : "(none)"}`,
30911
+ `- state: ${result.statePath}`,
30912
+ `- next_start_time: ${result.nextStartTime}`,
30913
+ `- server_time: ${result.serverTime}`,
30914
+ `- has_more: ${result.hasMore ? "yes" : "no"}`,
30915
+ result.cursorExpired ? "- cursor_expired: yes; old cursor exceeded 1 hour, used default since window" : "- cursor_expired: no",
30916
+ "",
30917
+ result.hasMore ? "next_action: 还有更多远端日志;可以再次调用 maker_pull_runtime_logs 继续拉取。" : "next_action: 日志已写入 .maker/logs/runtime/runtime.log,AI/skill 可直接读取这一份合并日志。"
30918
+ ].join("\n");
30919
+ }
30920
+ function normalizeRuntimeLogQueryResult(result) {
30921
+ const payload = findRuntimeLogPayload(extractRemoteToolPayload(result));
30922
+ if (!payload) {
30923
+ throw new Error("query_runtime_logs result does not contain logs array.");
30924
+ }
30925
+ const nextStartTime = payload.nextStartTime ?? payload.next_start_time;
30926
+ const serverTime = payload.serverTime ?? payload.server_time ?? nextStartTime;
30927
+ const hasMore = payload.hasMore ?? payload.has_more ?? false;
30928
+ if (typeof nextStartTime !== "number") {
30929
+ throw new Error("query_runtime_logs result does not contain nextStartTime.");
30930
+ }
30931
+ if (typeof serverTime !== "number") {
30932
+ throw new Error("query_runtime_logs result does not contain serverTime.");
30933
+ }
30934
+ return {
30935
+ logs: payload.logs.map(normalizeRuntimeLogEntry),
30936
+ nextStartTime,
30937
+ serverTime,
30938
+ hasMore: Boolean(hasMore)
30939
+ };
30940
+ }
30941
+ function writeRuntimeLogRawResponse(projectRoot, raw) {
30942
+ const rawPath = path7.join(getRuntimeLogDir(projectRoot), "last-query-runtime-logs-result.json");
30943
+ fs6.mkdirSync(path7.dirname(rawPath), { recursive: true });
30944
+ fs6.writeFileSync(
30945
+ rawPath,
30946
+ `${JSON.stringify({ capturedAt: (/* @__PURE__ */ new Date()).toISOString(), raw }, null, 2)}
30947
+ `,
30948
+ "utf8"
30949
+ );
30950
+ return rawPath;
30951
+ }
30952
+ function appendRuntimeLogs(projectRoot, logs) {
30953
+ fs6.mkdirSync(getRuntimeLogDir(projectRoot), { recursive: true });
30954
+ if (logs.length === 0) {
30955
+ return { files: [], written: 0 };
30956
+ }
30957
+ const filePath = getRuntimeLogPath(projectRoot);
30958
+ let written = 0;
30959
+ for (const log of logs) {
30960
+ const line = JSON.stringify(compactRuntimeLogEntry(log));
30961
+ fs6.appendFileSync(filePath, `${line}
30962
+ `, "utf8");
30963
+ written += 1;
30964
+ }
30965
+ return { files: written > 0 ? [filePath] : [], written };
30966
+ }
30967
+ function writeRuntimeLogState(projectRoot, state) {
30968
+ const statePath = getRuntimeLogStatePath(projectRoot);
30969
+ fs6.mkdirSync(path7.dirname(statePath), { recursive: true });
30970
+ const tempPath = `${statePath}.${process.pid}.${Date.now()}.tmp`;
30971
+ fs6.writeFileSync(tempPath, `${JSON.stringify(state, null, 2)}
30972
+ `, "utf8");
30973
+ fs6.renameSync(tempPath, statePath);
30974
+ }
30975
+ function writeRuntimeLogFailureState(projectRoot, options) {
30976
+ const nowIso = new Date(options.nowMs()).toISOString();
30977
+ writeRuntimeLogState(projectRoot, {
30978
+ ...readRuntimeLogState(projectRoot) || {},
30979
+ updatedAt: nowIso,
30980
+ lastPollAt: nowIso,
30981
+ consecutiveFailures: options.consecutiveFailures,
30982
+ lastError: options.error instanceof Error ? options.error.message : String(options.error)
30983
+ });
30984
+ }
30985
+ function isRuntimeLogState(state) {
30986
+ return typeof state.nextStartTime === "number" || typeof state.updatedAt === "string" || typeof state.lastPollAt === "string" || typeof state.lastSuccessAt === "string";
30987
+ }
30988
+ function isFreshRuntimeLogCursor(nextStartTime, nowSeconds) {
30989
+ if (!Number.isFinite(nextStartTime) || nextStartTime <= 0) {
30990
+ return false;
30991
+ }
30992
+ if (nextStartTime > 1e10) {
30993
+ return false;
30994
+ }
30995
+ const ageSeconds = nowSeconds - nextStartTime;
30996
+ return ageSeconds >= -300 && ageSeconds <= MAX_RUNTIME_LOG_WINDOW_SECONDS;
30997
+ }
30998
+ function isNonRetryableRuntimeLogError(error2) {
30999
+ const message = error2 instanceof Error ? error2.message : String(error2);
31000
+ return /\b(?:401|403|unauthori[sz]ed|forbidden|pat expired|auth(?:entication|orization)?)\b/i.test(
31001
+ message
31002
+ );
31003
+ }
31004
+ function getRuntimeLogDir(projectRoot) {
31005
+ return path7.join(projectRoot, ".maker", "logs", "runtime");
31006
+ }
31007
+ function getRuntimeLogStatePath(projectRoot) {
31008
+ return path7.join(getRuntimeLogDir(projectRoot), "state.json");
31009
+ }
31010
+ function getRuntimeLogPath(projectRoot) {
31011
+ return path7.join(getRuntimeLogDir(projectRoot), MERGED_RUNTIME_LOG_FILE);
31012
+ }
31013
+ function resolveNextRuntimeLogCursor(result) {
31014
+ const maxLogTime = result.logs.reduce((max, log) => {
31015
+ const value = typeof log.t === "number" ? log.t : typeof log.time === "number" ? log.time : max;
31016
+ return Math.max(max, value);
31017
+ }, Number.NEGATIVE_INFINITY);
31018
+ if (maxLogTime === Number.NEGATIVE_INFINITY) {
31019
+ return result.nextStartTime;
31020
+ }
31021
+ return Math.max(result.nextStartTime, maxLogTime + 1);
31022
+ }
31023
+ function defaultSleep(ms) {
31024
+ return new Promise((resolve) => {
31025
+ setTimeout(resolve, ms);
31026
+ });
31027
+ }
31028
+ function extractRemoteToolPayload(result) {
31029
+ if (!result || typeof result !== "object") {
31030
+ return result;
31031
+ }
31032
+ const structuredContent = result.structuredContent;
31033
+ if (structuredContent) {
31034
+ return structuredContent;
31035
+ }
31036
+ const textItems = extractRemoteToolTextItems(result);
31037
+ if (textItems.length === 0) {
31038
+ return result;
31039
+ }
31040
+ for (const textItem of textItems) {
31041
+ const payload = parseRuntimeLogTextPayload(textItem);
31042
+ if (payload) {
31043
+ return payload;
31044
+ }
31045
+ }
31046
+ const text = textItems.join("\n");
31047
+ return parseRuntimeLogTextPayload(text) || text;
31048
+ }
31049
+ function parseRuntimeLogTextPayload(text) {
31050
+ try {
31051
+ return JSON.parse(text);
31052
+ } catch {
31053
+ return parseRuntimeLogNdjson(text);
31054
+ }
31055
+ }
31056
+ function extractRemoteToolTextItems(result) {
31057
+ if (!result || typeof result !== "object") {
31058
+ return [];
31059
+ }
31060
+ const content = result.content;
31061
+ return (content == null ? void 0 : content.filter((item) => item.type === "text" && typeof item.text === "string").map((item) => item.text)) || [];
31062
+ }
31063
+ function parseRuntimeLogNdjson(text) {
31064
+ const rows = text.split(/\r?\n/).map((line) => line.trim()).filter(Boolean);
31065
+ if (rows.length === 0) {
31066
+ return null;
31067
+ }
31068
+ const parsedRows = [];
31069
+ for (const row of rows) {
31070
+ try {
31071
+ parsedRows.push(JSON.parse(row));
31072
+ } catch {
31073
+ return null;
31074
+ }
31075
+ }
31076
+ const meta3 = parsedRows.find(isRuntimeLogMeta);
31077
+ const logs = parsedRows.filter((row) => !isRuntimeLogMeta(row));
31078
+ if (!meta3 && logs.length === 0) {
31079
+ return null;
31080
+ }
31081
+ return {
31082
+ logs,
31083
+ nextStartTime: meta3 == null ? void 0 : meta3.nextStartTime,
31084
+ next_start_time: meta3 == null ? void 0 : meta3.next_start_time,
31085
+ serverTime: meta3 == null ? void 0 : meta3.serverTime,
31086
+ server_time: meta3 == null ? void 0 : meta3.server_time,
31087
+ hasMore: (meta3 == null ? void 0 : meta3.hasMore) ?? (meta3 == null ? void 0 : meta3.has_more) ?? (meta3 == null ? void 0 : meta3.truncated)
31088
+ };
31089
+ }
31090
+ function findRuntimeLogPayload(value, depth = 0) {
31091
+ if (!value || typeof value !== "object" || depth > 4) {
31092
+ return null;
31093
+ }
31094
+ const candidate = value;
31095
+ if (Array.isArray(candidate.logs)) {
31096
+ return candidate;
31097
+ }
31098
+ if (candidate.type === "meta" && candidate.success === true && typeof candidate.nextStartTime === "number") {
31099
+ return {
31100
+ logs: [],
31101
+ nextStartTime: candidate.nextStartTime,
31102
+ serverTime: candidate.serverTime ?? candidate.nextStartTime,
31103
+ hasMore: Boolean(candidate.truncated)
31104
+ };
31105
+ }
31106
+ const wrappers = value;
31107
+ for (const key of ["data", "result", "payload", "runtimeLogs", "runtime_logs"]) {
31108
+ const nested = findRuntimeLogPayload(wrappers[key], depth + 1);
31109
+ if (nested) {
31110
+ return nested;
31111
+ }
31112
+ }
31113
+ return null;
31114
+ }
31115
+ function isRuntimeLogMeta(value) {
31116
+ return Boolean(value) && typeof value === "object" && value.type === "meta" && value.success === true && (typeof value.nextStartTime === "number" || typeof value.next_start_time === "number");
31117
+ }
31118
+ function normalizeRuntimeLogEntry(entry) {
31119
+ return compactRuntimeLogEntry(entry);
31120
+ }
31121
+ function compactRuntimeLogEntry(entry) {
31122
+ const { id: _id, time: time3, message, ...rest } = entry;
31123
+ const compact = {
31124
+ ...rest
31125
+ };
31126
+ if (compact.t === void 0 && time3 !== void 0) {
31127
+ compact.t = time3;
31128
+ }
31129
+ if (compact.msg === void 0 && message !== void 0) {
31130
+ compact.msg = message;
31131
+ }
31132
+ return compact;
31133
+ }
31134
+
30469
31135
  // src/maker/server/mcp.ts
30470
- var VERSION = true ? "1.23.6" : "dev";
31136
+ var VERSION = true ? "1.23.7-beta.2" : "dev";
30471
31137
  var DEFAULT_PROXY_PACKAGE = "@taptap/instant-games-open-mcp@1.22.0";
30472
31138
  var DEFAULT_BUILD_TIMEOUT_MS = 10 * 60 * 1e3;
31139
+ var PREVIEW_REFRESH_TIMEOUT_MS = 15 * 1e3;
31140
+ var WATCHER_STOP_TIMEOUT_MS = 1500;
31141
+ var WATCHER_PROCESS_PATTERN = /(?:\btaptap-maker\b|\bmaker\.js\b).*\blogs\b.*\bwatch\b/;
30473
31142
  var LONG_OPERATION_HEARTBEAT_MS = 3 * 60 * 1e3;
30474
31143
  var MakerCloneFailedError = class extends Error {
30475
31144
  constructor(targetDir, originalError) {
@@ -30483,7 +31152,7 @@ var MakerCloneFailedError = class extends Error {
30483
31152
  var tools = [
30484
31153
  {
30485
31154
  name: "maker_status_lite",
30486
- description: "Compatibility fallback for clients that cannot read the maker://status resource. Prefer reading maker://status when resources are available. Shows local Maker status for the user current working directory, including Git, PAT/TapTap auth, project binding, AI dev kit status, and bundled workflow skill document paths.",
31155
+ description: "Compatibility status surface for clients using tool output instead of the maker://status resource. Prefer reading maker://status when resources are available. Shows local Maker status for the user current working directory, including Git, PAT/TapTap auth, project binding, AI dev kit status, and bundled workflow guide document paths. Maker initialization next_step: taptap-maker init.",
30487
31156
  inputSchema: {
30488
31157
  type: "object",
30489
31158
  properties: {
@@ -30500,7 +31169,7 @@ var tools = [
30500
31169
  },
30501
31170
  {
30502
31171
  name: "maker_build_current_directory",
30503
- description: 'Sync and build the current Maker game. Use this single tool for user requests like "构建", "build", "跑一下", "预览", "验证一下", "提交", "提交代码", "推送", or "push" in a Maker project. If local changes or committed-but-unpushed commits exist, the tool commits when needed, pushes to Maker remote, then triggers remote Maker build. If push fails, build is not started and the result includes recovery details for the local Agent to handle merge/conflict resolution. If push succeeds but remote build fails, report that code is already on Maker remote and include build failure details. Only set confirm_remote_build_without_submit=true when the user explicitly says they do not want to submit local changes and wants to build the current remote version.',
31172
+ description: 'Sync and build the current Maker game. Use this single tool for user requests like "构建", "build", "跑一下", "预览", "验证一下", "提交", "提交代码", "推送", or "push" in a Maker project. If local changes or committed-but-unpushed commits exist, the tool commits when needed, pushes to Maker remote, then triggers remote Maker build. If push fails, build is not started and the result includes recovery details for the local Agent to handle merge/conflict resolution. If push succeeds but remote build fails, report that code is already on Maker remote and include build failure details. After a successful build, a local runtime log watcher is started; for gameplay/runtime diagnostics, read runtime_logs.local_file, and for watcher health read runtime_logs.state_file. Only set confirm_remote_build_without_submit=true when the user explicitly says they do not want to submit local changes and wants to build the current remote version.',
30504
31173
  inputSchema: {
30505
31174
  type: "object",
30506
31175
  properties: {
@@ -30556,31 +31225,69 @@ var tools = [
30556
31225
  }
30557
31226
  }
30558
31227
  }
30559
- }
30560
- ];
30561
- var resources = [
31228
+ },
30562
31229
  {
30563
- uri: "maker://status",
30564
- name: "Maker status",
30565
- description: "Local TapTap Maker project status, including Git, PAT/TapTap auth, project binding, AI dev kit status, and bundled workflow skill document paths.",
30566
- mimeType: "text/plain"
30567
- }
30568
- ];
30569
- async function startMakerMcpServer() {
30570
- const server = new Server(
30571
- {
30572
- name: "taptap-maker",
30573
- version: VERSION
30574
- },
30575
- {
30576
- capabilities: {
30577
- tools: {},
30578
- resources: {}
30579
- }
30580
- }
30581
- );
30582
- server.setRequestHandler(ListToolsRequestSchema, async () => ({ tools }));
30583
- server.setRequestHandler(ListResourcesRequestSchema, async () => ({ resources }));
31230
+ name: "maker_pull_runtime_logs",
31231
+ description: "Pull Maker Lua runtime logs once from the remote Maker MCP query_runtime_logs tool and write user_script/server_user_script logs to .maker/logs/runtime/runtime.log. This is a one-shot fixed business flow for AI diagnostics; it does not start a watcher, does not keep a long-running MCP call open, and does not clear local logs.",
31232
+ inputSchema: {
31233
+ type: "object",
31234
+ properties: {
31235
+ target_dir: {
31236
+ type: "string",
31237
+ description: "Optional Maker project directory. Defaults to the MCP process cwd. Pass the user current working directory when it differs from the MCP process cwd."
31238
+ },
31239
+ start_time: {
31240
+ type: "number",
31241
+ description: "Optional Unix seconds cursor. If omitted, the tool uses a fresh local state cursor, otherwise defaults to since_seconds."
31242
+ },
31243
+ since_seconds: {
31244
+ type: "number",
31245
+ description: "Optional fallback lookback window in seconds when no fresh cursor exists. Defaults to 600 seconds and is capped by the remote server."
31246
+ },
31247
+ limit: {
31248
+ type: "number",
31249
+ description: "Optional maximum log entries returned by the remote query."
31250
+ },
31251
+ server_url: {
31252
+ type: "string",
31253
+ description: "Optional remote MCP server URL override. Defaults to the Maker endpoint table for TAPTAP_MCP_ENV."
31254
+ },
31255
+ env: {
31256
+ type: "string",
31257
+ enum: ["rnd", "production"],
31258
+ description: "Remote MCP environment. Defaults to TAPTAP_MCP_ENV."
31259
+ },
31260
+ timeout_ms: {
31261
+ type: "number",
31262
+ description: "Optional remote log query timeout in milliseconds. Defaults to 60 seconds."
31263
+ }
31264
+ }
31265
+ }
31266
+ }
31267
+ ];
31268
+ var resources = [
31269
+ {
31270
+ uri: "maker://status",
31271
+ name: "Maker status",
31272
+ description: "Local TapTap Maker project status, including Git, PAT/TapTap auth, project binding, AI dev kit status, and bundled workflow guide document paths. Maker initialization next_step: taptap-maker init.",
31273
+ mimeType: "text/plain"
31274
+ }
31275
+ ];
31276
+ async function startMakerMcpServer() {
31277
+ const server = new Server(
31278
+ {
31279
+ name: "taptap-maker",
31280
+ version: VERSION
31281
+ },
31282
+ {
31283
+ capabilities: {
31284
+ tools: {},
31285
+ resources: {}
31286
+ }
31287
+ }
31288
+ );
31289
+ server.setRequestHandler(ListToolsRequestSchema, async () => ({ tools }));
31290
+ server.setRequestHandler(ListResourcesRequestSchema, async () => ({ resources }));
30584
31291
  server.setRequestHandler(ReadResourceRequestSchema, async (request) => {
30585
31292
  const uri = request.params.uri;
30586
31293
  if (uri !== "maker://status") {
@@ -30653,6 +31360,30 @@ async function startMakerMcpServer() {
30653
31360
  ]
30654
31361
  };
30655
31362
  }
31363
+ if (name === "maker_pull_runtime_logs") {
31364
+ const args = request.params.arguments || {};
31365
+ const proxy = createRemoteProxyContext({
31366
+ targetDir: resolveMakerToolTargetDir(args.target_dir),
31367
+ serverUrl: args.server_url,
31368
+ env: args.env
31369
+ });
31370
+ const result = await pullRuntimeLogs({
31371
+ projectRoot: proxy.projectRoot,
31372
+ projectId: proxy.projectId,
31373
+ startTime: args.start_time,
31374
+ sinceSeconds: args.since_seconds,
31375
+ limit: args.limit,
31376
+ callRemoteRuntimeLogs: (queryArgs) => callRemoteRuntimeLogs(proxy, queryArgs, args.timeout_ms)
31377
+ });
31378
+ return {
31379
+ content: [
31380
+ {
31381
+ type: "text",
31382
+ text: formatRuntimeLogPullResult(result)
31383
+ }
31384
+ ]
31385
+ };
31386
+ }
30656
31387
  throw new McpError(ErrorCode.MethodNotFound, `Unknown Maker tool: ${name}`);
30657
31388
  } catch (error2) {
30658
31389
  return {
@@ -30699,8 +31430,8 @@ async function formatStatus(options = {}) {
30699
31430
  const git = checkGitEnvironment();
30700
31431
  const projectSection = identify.projectId ? [
30701
31432
  "目标目录已绑定 Maker 项目。",
30702
- "请继续在当前绑定项目上执行状态、提交、构建等操作;不要再引导用户 clone,除非用户明确要求切换或重新拉取项目。",
30703
- "本地 Maker 工作流请优先参考 taptap-maker-local skill;CLI 负责初始化/PAT/app/clone,MCP 只保留状态和同步构建。"
31433
+ "请继续在当前绑定项目上执行状态、提交、构建等操作;用户明确要求切换或重新拉取项目时,再进入项目选择流程。",
31434
+ "本地 Maker 工作流请参考 taptap-maker-local workflow guide document;CLI 负责初始化/PAT/app/clone,MCP 只保留状态和同步构建。"
30704
31435
  ].join("\n") : isLikelyAiDialogueDirectory(targetDir) ? formatAiDialogueDirectoryHint(targetDir) : pat ? await formatAutoProjectListFromPat() : formatIdentifyHint();
30705
31436
  return [
30706
31437
  "TapTap Maker MCP status",
@@ -30738,7 +31469,7 @@ function formatMakerRemoteSyncSkipped() {
30738
31469
  "Maker remote sync",
30739
31470
  "",
30740
31471
  "- status: skipped",
30741
- "- next_action: 已跳过远端同步检查;如需确认是否需要 pull,请重新读取 maker_status_lite 且不要设置 skip_remote_sync。"
31472
+ "- next_action: 已跳过远端同步检查;如需确认是否需要 pull,请重新读取 maker_status_lite 并启用远端同步检查。"
30742
31473
  ].join("\n");
30743
31474
  }
30744
31475
  async function formatMakerRemoteSyncStatus(projectRoot) {
@@ -30823,7 +31554,7 @@ function formatAiDialogueDirectoryHint(targetDir) {
30823
31554
  }
30824
31555
  function resolveMakerToolTargetDir(targetDir) {
30825
31556
  if (targetDir) {
30826
- return path7.resolve(targetDir);
31557
+ return path8.resolve(targetDir);
30827
31558
  }
30828
31559
  return process.cwd();
30829
31560
  }
@@ -30838,13 +31569,16 @@ async function formatAiDevKitStatus(projectRoot) {
30838
31569
  ].join("\n");
30839
31570
  }
30840
31571
  function formatAiDevKitStatusLines(status, devKitStatus) {
31572
+ const skillStatus = inspectAiDevKitSkillInstallStatus(devKitStatus.targetDir);
30841
31573
  return [
30842
31574
  "AI dev kit",
30843
31575
  "",
30844
31576
  `- status: ${status}`,
30845
31577
  `- required_entries: ${devKitStatus.requiredEntries.join(", ")}`,
30846
31578
  `- present_entries: ${devKitStatus.presentEntries.join(", ") || "(none)"}`,
30847
- `- missing_entries: ${devKitStatus.missingEntries.join(", ") || "(none)"}`
31579
+ `- missing_entries: ${devKitStatus.missingEntries.join(", ") || "(none)"}`,
31580
+ `- skill_install_status: ${skillStatus.status}`,
31581
+ `- skill_install_summary: ${skillStatus.summary}`
30848
31582
  ];
30849
31583
  }
30850
31584
  async function formatAutoProjectListFromPat() {
@@ -30852,7 +31586,8 @@ async function formatAutoProjectListFromPat() {
30852
31586
  const projects = await listMakerProjects();
30853
31587
  return [
30854
31588
  "本地已有 Maker PAT,当前目录尚未绑定 Maker 项目。",
30855
- "当前目录未绑定时,先展示下面的 Maker Apps 预览和总数,避免长列表刷屏;选择、解释和 clone 顺序请参考 taptap-maker-local skill。",
31589
+ "当前目录未绑定时,先展示下面的 Maker Apps 预览和总数;选择、解释和 clone 顺序请参考 taptap-maker-local workflow guide document。",
31590
+ "用户选择 app 后,next_step: 执行 `taptap-maker init`。",
30856
31591
  "",
30857
31592
  formatStatusProjectList(projects)
30858
31593
  ].join("\n");
@@ -30897,25 +31632,26 @@ function formatStatusProjectList(projects) {
30897
31632
  "",
30898
31633
  hiddenCount > 0 ? `为了保持友好的可读性,默认最多展示 ${visibleProjects.length} 个 app;如需完整列表,可以选择显示全部。` : "已显示全部 app;请询问用户选择。",
30899
31634
  hiddenCount > 0 ? "如需完整列表,请运行 taptap-maker apps --json 查看全部 app。" : void 0,
30900
- "AI 展示建议:如果聊天或客户端宽度足够,可把 app 预览整理成两列紧凑布局;每个 app 保留序号、app_id、名称,以及可用的最近活跃时间或 user_id。窄屏保持单列。不要省略 app_id,也不要在用户确认前自动选择 app。",
31635
+ "AI 展示建议:如果聊天或客户端宽度足够,可把 app 预览整理成两列紧凑布局;每个 app 保留序号、app_id、名称,以及可用的最近活跃时间或 user_id。窄屏保持单列。选择 app 前先获取用户确认。",
30901
31636
  "",
30902
31637
  ...visibleProjects.map(
30903
31638
  (project, index) => `${index + 1}. ${project.id}${project.name ? ` ${project.name}` : ""}${project.user_id ? ` user_id=${project.user_id}` : ""}${project.gameType ? ` gameType=${project.gameType}` : ""}${project.stage ? ` stage=${project.stage}` : ""}${project.createdAt ? ` createdAt=${project.createdAt}` : ""}${project.lastConversationAt ? ` lastConversationAt=${project.lastConversationAt}` : ""}`
30904
31639
  ),
30905
31640
  "",
30906
- "仅当当前目录未绑定且用户要初始化或 clone 时,才让用户选择 app 并继续 taptap-maker init。",
30907
- "如果当前目录已绑定 Maker 项目,这个列表仅作账号项目参考;请继续当前项目,除非用户明确要求切换或重新 clone。"
31641
+ "当前目录未绑定且用户要初始化或 clone 时,让用户选择 app 并继续 taptap-maker init。",
31642
+ "用户回复序号或 app_id 后,next_step: 执行 `taptap-maker init`,或让已经启动的 `taptap-maker init` 交互继续读取该选择。",
31643
+ "如果当前目录已绑定 Maker 项目,这个列表仅作账号项目参考;请继续当前项目。用户明确要求切换或重新 clone 时,再进入项目选择流程。"
30908
31644
  ].filter((line) => line !== void 0).join("\n");
30909
31645
  }
30910
31646
  function formatClonePartialStateLines(targetDir) {
30911
- const resolvedTargetDir = path7.resolve(targetDir);
31647
+ const resolvedTargetDir = path8.resolve(targetDir);
30912
31648
  const identify = identifyMakerProject({ cwd: resolvedTargetDir });
30913
31649
  const gitStatus = inspectMakerDirectoryGitStatus(resolvedTargetDir);
30914
31650
  const devKitStatus = inspectAiDevKit(resolvedTargetDir);
30915
- const stagedDevKitGitignorePath = path7.join(resolvedTargetDir, DEV_KIT_GITIGNORE_STAGING_FILE);
31651
+ const stagedDevKitGitignorePath = path8.join(resolvedTargetDir, DEV_KIT_GITIGNORE_STAGING_FILE);
30916
31652
  const projectBound = Boolean(identify.projectId);
30917
31653
  const gitInitialized = Boolean(
30918
- gitStatus.isOwnGitRoot || fs6.existsSync(path7.join(resolvedTargetDir, ".git"))
31654
+ gitStatus.isOwnGitRoot || fs7.existsSync(path8.join(resolvedTargetDir, ".git"))
30919
31655
  );
30920
31656
  const safeToRetry = !projectBound;
30921
31657
  return [
@@ -30929,9 +31665,9 @@ function formatClonePartialStateLines(targetDir) {
30929
31665
  identify.configPath ? `- config: ${identify.configPath}` : "",
30930
31666
  `- ai_dev_kit_present: ${devKitStatus.ready ? "yes" : "no"}`,
30931
31667
  `- ai_dev_kit_missing_entries: ${devKitStatus.missingEntries.join(", ") || "(none)"}`,
30932
- `- staged_dev_kit_gitignore: ${fs6.existsSync(stagedDevKitGitignorePath) ? "yes" : "no"}`,
31668
+ `- staged_dev_kit_gitignore: ${fs7.existsSync(stagedDevKitGitignorePath) ? "yes" : "no"}`,
30933
31669
  `- safe_to_retry: ${safeToRetry ? "yes" : "no"}`,
30934
- safeToRetry ? "- next_step: 可以直接重试 taptap-maker init;如果连续失败,建议换一个全新的独立目录重新 clone。" : "- next_step: 当前目录已经有 Maker 绑定信息;先运行 taptap-maker doctor 或读取 maker://status 确认状态,不要重复 clone。"
31670
+ safeToRetry ? "- next_step: 可以直接重试 taptap-maker init;如果连续失败,建议换一个全新的独立目录重新 clone。" : "- next_step: 当前目录已经有 Maker 绑定信息;先运行 taptap-maker doctor 或读取 maker://status 确认状态。"
30935
31671
  ].filter(Boolean);
30936
31672
  }
30937
31673
  function createRemoteProxyContext(options) {
@@ -31000,17 +31736,17 @@ function createRemoteProxyContext(options) {
31000
31736
  }
31001
31737
  function resolveLocalProxyBundle(options) {
31002
31738
  const currentModuleUrl = (options == null ? void 0 : options.currentModuleUrl) || (typeof __MAKER_BUNDLE_URL__ !== "undefined" ? __MAKER_BUNDLE_URL__ : void 0);
31003
- const currentModuleDir = currentModuleUrl ? path7.dirname(fileURLToPath2(currentModuleUrl)) : "";
31739
+ const currentModuleDir = currentModuleUrl ? path8.dirname(fileURLToPath2(currentModuleUrl)) : "";
31004
31740
  const makerEntry = (options == null ? void 0 : options.makerEntry) ?? process.argv[1];
31005
- const makerEntryDir = makerEntry ? path7.dirname(path7.resolve(makerEntry)) : "";
31741
+ const makerEntryDir = makerEntry ? path8.dirname(path8.resolve(makerEntry)) : "";
31006
31742
  const cwd = (options == null ? void 0 : options.cwd) ?? process.cwd();
31007
31743
  const candidates = [
31008
- currentModuleDir ? path7.join(currentModuleDir, "proxy.js") : "",
31009
- makerEntryDir ? path7.join(makerEntryDir, "..", "dist", "proxy.js") : "",
31010
- path7.resolve(cwd, "dist", "proxy.js")
31744
+ currentModuleDir ? path8.join(currentModuleDir, "proxy.js") : "",
31745
+ makerEntryDir ? path8.join(makerEntryDir, "..", "dist", "proxy.js") : "",
31746
+ path8.resolve(cwd, "dist", "proxy.js")
31011
31747
  ].filter(Boolean);
31012
31748
  for (const candidate of candidates) {
31013
- if (fs6.existsSync(candidate)) {
31749
+ if (fs7.existsSync(candidate)) {
31014
31750
  return candidate;
31015
31751
  }
31016
31752
  }
@@ -31098,6 +31834,13 @@ function formatMakerAppWebUrl(projectId, env) {
31098
31834
  const makerEnv = env === "rnd" || env === "production" ? env : void 0;
31099
31835
  return `${getMakerWebUrl(makerEnv)}/app/${encodeURIComponent(projectId)}`;
31100
31836
  }
31837
+ var RemoteBuildFailedError = class extends Error {
31838
+ constructor(buildResult) {
31839
+ super(buildResult.resultText);
31840
+ this.name = "RemoteBuildFailedError";
31841
+ this.buildResult = buildResult;
31842
+ }
31843
+ };
31101
31844
  async function buildCurrentDirectory(options) {
31102
31845
  var _a2;
31103
31846
  const localChanges = await readMakerProjectLocalChanges(options.targetDir);
@@ -31140,11 +31883,27 @@ async function buildCurrentDirectory(options) {
31140
31883
  submitResult
31141
31884
  };
31142
31885
  }
31143
- return runRemoteBuildCurrentDirectory(options, options.targetDir);
31886
+ try {
31887
+ return await runRemoteBuildCurrentDirectory(options, options.targetDir);
31888
+ } catch (error2) {
31889
+ if (error2 instanceof RemoteBuildFailedError) {
31890
+ return {
31891
+ mode: "remote_build_failed",
31892
+ projectRoot: error2.buildResult.projectRoot,
31893
+ projectId: error2.buildResult.projectId,
31894
+ buildResult: error2.buildResult,
31895
+ buildFailure: toMakerBuildFailure(error2)
31896
+ };
31897
+ }
31898
+ throw error2;
31899
+ }
31144
31900
  }
31145
31901
  async function runRemoteBuildCurrentDirectory(options, targetDir) {
31146
31902
  if (options.callRemoteBuild) {
31147
- return options.callRemoteBuild(targetDir);
31903
+ return attachBuildSuccessSideEffects(await options.callRemoteBuild(targetDir), {
31904
+ refreshPreview: options.refreshPreview || skipPreviewRefresh,
31905
+ startRuntimeLogWatch: options.startRuntimeLogWatch
31906
+ });
31148
31907
  }
31149
31908
  const proxy = createRemoteProxyContext({
31150
31909
  targetDir,
@@ -31190,22 +31949,446 @@ async function runRemoteBuildCurrentDirectory(options, targetDir) {
31190
31949
  }
31191
31950
  }
31192
31951
  );
31952
+ if (isRemoteToolError(result)) {
31953
+ throw new Error(formatRemoteToolResult(result));
31954
+ }
31955
+ return attachBuildSuccessSideEffects(
31956
+ {
31957
+ mode: "remote_build",
31958
+ projectRoot: proxy.projectRoot,
31959
+ projectId: proxy.projectId,
31960
+ projectPath: proxy.projectPath,
31961
+ serverUrl: proxy.serverUrl,
31962
+ env: proxy.env,
31963
+ makerUrl: formatMakerAppWebUrl(proxy.projectId, proxy.env),
31964
+ timeoutMs,
31965
+ buildArgs,
31966
+ resultText: formatRemoteToolResult(result)
31967
+ },
31968
+ {
31969
+ refreshPreview: options.refreshPreview,
31970
+ startRuntimeLogWatch: options.startRuntimeLogWatch || startRuntimeLogWatch
31971
+ }
31972
+ );
31973
+ } finally {
31974
+ await client.close();
31975
+ }
31976
+ }
31977
+ async function attachBuildSuccessSideEffects(buildResult, options) {
31978
+ if (isRemoteBuildFailureResult(buildResult)) {
31979
+ throw new RemoteBuildFailedError(buildResult);
31980
+ }
31981
+ const withPreview = await attachPreviewRefresh(buildResult, options.refreshPreview);
31982
+ if (!options.startRuntimeLogWatch) {
31983
+ return withPreview;
31984
+ }
31985
+ return attachRuntimeLogWatch(withPreview, options.startRuntimeLogWatch);
31986
+ }
31987
+ function isRemoteToolError(result) {
31988
+ return Boolean(result && typeof result === "object" && result.isError);
31989
+ }
31990
+ function isRemoteBuildFailureResult(buildResult) {
31991
+ return /\bBUILD FAILED\b/i.test(buildResult.resultText);
31992
+ }
31993
+ async function attachPreviewRefresh(buildResult, refreshPreview = refreshMakerPreview) {
31994
+ try {
31193
31995
  return {
31194
- mode: "remote_build",
31195
- projectRoot: proxy.projectRoot,
31196
- projectId: proxy.projectId,
31197
- projectPath: proxy.projectPath,
31198
- serverUrl: proxy.serverUrl,
31199
- env: proxy.env,
31200
- makerUrl: formatMakerAppWebUrl(proxy.projectId, proxy.env),
31201
- timeoutMs,
31202
- buildArgs,
31203
- resultText: formatRemoteToolResult(result)
31996
+ ...buildResult,
31997
+ previewRefresh: await refreshPreview(buildResult)
31998
+ };
31999
+ } catch (error2) {
32000
+ return {
32001
+ ...buildResult,
32002
+ previewRefresh: {
32003
+ ok: false,
32004
+ status: 0,
32005
+ url: "",
32006
+ error: error2 instanceof Error ? error2.message : String(error2)
32007
+ }
32008
+ };
32009
+ }
32010
+ }
32011
+ async function skipPreviewRefresh(buildResult) {
32012
+ return {
32013
+ ok: false,
32014
+ status: 0,
32015
+ url: "",
32016
+ error: `preview refresh skipped for injected remote build: ${buildResult.projectId}`
32017
+ };
32018
+ }
32019
+ async function attachRuntimeLogWatch(buildResult, startWatch) {
32020
+ try {
32021
+ return {
32022
+ ...buildResult,
32023
+ runtimeLogWatch: await startWatch(buildResult)
32024
+ };
32025
+ } catch (error2) {
32026
+ return {
32027
+ ...buildResult,
32028
+ runtimeLogWatch: {
32029
+ started: false,
32030
+ command: formatRuntimeLogWatchCommand(buildResult).text,
32031
+ runtimeLog: getRuntimeLogFilePath(buildResult.projectRoot),
32032
+ error: error2 instanceof Error ? error2.message : String(error2)
32033
+ }
32034
+ };
32035
+ }
32036
+ }
32037
+ async function startRuntimeLogWatch(buildResult) {
32038
+ const runtimeDir = path8.join(buildResult.projectRoot, ".maker", "logs", "runtime");
32039
+ fs7.mkdirSync(runtimeDir, { recursive: true });
32040
+ const stdoutLog = path8.join(runtimeDir, "watcher.out.log");
32041
+ const stderrLog = path8.join(runtimeDir, "watcher.err.log");
32042
+ const pidFile = path8.join(runtimeDir, "watcher.pid");
32043
+ const previous = stopExistingRuntimeLogWatcher(pidFile);
32044
+ const outFd = fs7.openSync(stdoutLog, "a");
32045
+ const errFd = fs7.openSync(stderrLog, "a");
32046
+ const command = formatRuntimeLogWatchCommand(buildResult);
32047
+ if (previous.previousStopError) {
32048
+ fs7.closeSync(outFd);
32049
+ fs7.closeSync(errFd);
32050
+ return {
32051
+ started: false,
32052
+ command: command.text,
32053
+ runtimeLog: getRuntimeLogFilePath(buildResult.projectRoot),
32054
+ stdoutLog,
32055
+ stderrLog,
32056
+ pidFile,
32057
+ ...previous,
32058
+ error: previous.previousStopError
32059
+ };
32060
+ }
32061
+ try {
32062
+ const child = spawn4(command.command, command.args, {
32063
+ cwd: buildResult.projectRoot,
32064
+ detached: true,
32065
+ env: mergeStringEnv(process.env, { TAPTAP_MCP_ENV: buildResult.env }),
32066
+ stdio: ["ignore", outFd, errFd],
32067
+ windowsHide: true
32068
+ });
32069
+ const spawnError = await waitForSpawnError(child);
32070
+ if (spawnError) {
32071
+ return {
32072
+ started: false,
32073
+ command: command.text,
32074
+ runtimeLog: getRuntimeLogFilePath(buildResult.projectRoot),
32075
+ stdoutLog,
32076
+ stderrLog,
32077
+ pidFile,
32078
+ ...previous,
32079
+ error: spawnError.message
32080
+ };
32081
+ }
32082
+ if (!child.pid) {
32083
+ return {
32084
+ started: false,
32085
+ command: command.text,
32086
+ runtimeLog: getRuntimeLogFilePath(buildResult.projectRoot),
32087
+ stdoutLog,
32088
+ stderrLog,
32089
+ pidFile,
32090
+ ...previous,
32091
+ error: "runtime log watcher process did not report a pid"
32092
+ };
32093
+ }
32094
+ child.once("error", () => void 0);
32095
+ child.unref();
32096
+ writeRuntimeLogWatcherPidFile(pidFile, {
32097
+ pid: child.pid,
32098
+ command: command.text,
32099
+ startedAt: (/* @__PURE__ */ new Date()).toISOString()
32100
+ });
32101
+ return {
32102
+ started: true,
32103
+ command: command.text,
32104
+ runtimeLog: getRuntimeLogFilePath(buildResult.projectRoot),
32105
+ stdoutLog,
32106
+ stderrLog,
32107
+ pidFile,
32108
+ pid: child.pid,
32109
+ ...previous
31204
32110
  };
31205
32111
  } finally {
31206
- await client.close();
32112
+ fs7.closeSync(outFd);
32113
+ fs7.closeSync(errFd);
32114
+ }
32115
+ }
32116
+ function stopExistingRuntimeLogWatcher(pidFile, options = {}) {
32117
+ if (!fs7.existsSync(pidFile)) {
32118
+ return {};
32119
+ }
32120
+ const pidState = readRuntimeLogWatcherPidState(pidFile);
32121
+ const pid = pidState == null ? void 0 : pidState.pid;
32122
+ if (!Number.isInteger(pid) || pid <= 0) {
32123
+ fs7.rmSync(pidFile, { force: true });
32124
+ return {};
32125
+ }
32126
+ try {
32127
+ process.kill(pid, 0);
32128
+ const processCommand = (options.getProcessCommand || getProcessCommand)(pid);
32129
+ const verifiedCommand = getVerifiedRuntimeLogWatcherCommand(processCommand, pidState);
32130
+ if (!verifiedCommand) {
32131
+ fs7.rmSync(pidFile, { force: true });
32132
+ return {
32133
+ previousPid: pid,
32134
+ previousStopped: false,
32135
+ previousStopError: processCommand ? `process ${pid} does not look like a Maker log watcher: ${processCommand}` : `process ${pid} could not be verified as a Maker log watcher`
32136
+ };
32137
+ }
32138
+ process.kill(pid, "SIGTERM");
32139
+ const stopped = (options.waitForExit || waitForProcessExit)(pid, WATCHER_STOP_TIMEOUT_MS);
32140
+ if (stopped) {
32141
+ fs7.rmSync(pidFile, { force: true });
32142
+ return { previousPid: pid, previousStopped: true };
32143
+ }
32144
+ return {
32145
+ previousPid: pid,
32146
+ previousStopped: false,
32147
+ previousStopError: `process ${pid} did not exit after SIGTERM within ${WATCHER_STOP_TIMEOUT_MS}ms`
32148
+ };
32149
+ } catch (error2) {
32150
+ fs7.rmSync(pidFile, { force: true });
32151
+ const code = error2 && typeof error2 === "object" && "code" in error2 ? String(error2.code) : "";
32152
+ if (code === "ESRCH") {
32153
+ return { previousPid: pid, previousStopped: false };
32154
+ }
32155
+ return {
32156
+ previousPid: pid,
32157
+ previousStopped: false,
32158
+ previousStopError: error2 instanceof Error ? error2.message : String(error2)
32159
+ };
32160
+ }
32161
+ }
32162
+ function readRuntimeLogWatcherPidState(pidFile) {
32163
+ const raw = fs7.readFileSync(pidFile, "utf8").trim();
32164
+ if (!raw) {
32165
+ return null;
32166
+ }
32167
+ try {
32168
+ const parsed = JSON.parse(raw);
32169
+ if (typeof parsed === "number") {
32170
+ return Number.isInteger(parsed) && parsed > 0 ? { pid: parsed, legacy: true } : null;
32171
+ }
32172
+ return typeof parsed.pid === "number" ? parsed : null;
32173
+ } catch {
32174
+ const pid = Number(raw);
32175
+ return Number.isInteger(pid) && pid > 0 ? { pid, legacy: true } : null;
32176
+ }
32177
+ }
32178
+ function writeRuntimeLogWatcherPidFile(pidFile, state) {
32179
+ fs7.writeFileSync(pidFile, `${JSON.stringify(state, null, 2)}
32180
+ `, "utf8");
32181
+ }
32182
+ function getProcessCommand(pid) {
32183
+ if (process.platform === "win32") {
32184
+ return void 0;
32185
+ }
32186
+ try {
32187
+ return execFileSync("ps", ["-p", String(pid), "-o", "command="], {
32188
+ encoding: "utf8",
32189
+ stdio: ["ignore", "pipe", "ignore"]
32190
+ }).trim();
32191
+ } catch {
32192
+ return void 0;
32193
+ }
32194
+ }
32195
+ function isRuntimeLogWatcherProcess(command) {
32196
+ return Boolean(command && WATCHER_PROCESS_PATTERN.test(command));
32197
+ }
32198
+ function getVerifiedRuntimeLogWatcherCommand(processCommand, pidState) {
32199
+ if (isRuntimeLogWatcherProcess(processCommand)) {
32200
+ return processCommand;
32201
+ }
32202
+ if (!processCommand && !pidState.legacy && isRuntimeLogWatcherProcess(pidState.command)) {
32203
+ return pidState.command;
32204
+ }
32205
+ return void 0;
32206
+ }
32207
+ function waitForProcessExit(pid, timeoutMs) {
32208
+ const deadline = Date.now() + timeoutMs;
32209
+ while (Date.now() < deadline) {
32210
+ try {
32211
+ process.kill(pid, 0);
32212
+ } catch {
32213
+ return true;
32214
+ }
32215
+ sleepSync(50);
32216
+ }
32217
+ try {
32218
+ process.kill(pid, 0);
32219
+ return false;
32220
+ } catch {
32221
+ return true;
31207
32222
  }
31208
32223
  }
32224
+ function sleepSync(ms) {
32225
+ const buffer = new SharedArrayBuffer(4);
32226
+ Atomics.wait(new Int32Array(buffer), 0, 0, ms);
32227
+ }
32228
+ function waitForSpawnError(child) {
32229
+ return new Promise((resolve) => {
32230
+ let done = false;
32231
+ const finish = (error2) => {
32232
+ if (done) {
32233
+ return;
32234
+ }
32235
+ done = true;
32236
+ child.off("error", finish);
32237
+ resolve(error2);
32238
+ };
32239
+ child.once("error", finish);
32240
+ setTimeout(() => finish(), 50);
32241
+ });
32242
+ }
32243
+ function formatRuntimeLogWatchCommand(buildResult) {
32244
+ const args = [
32245
+ resolveMakerCliEntry(),
32246
+ "logs",
32247
+ "watch",
32248
+ "--target-dir",
32249
+ buildResult.projectRoot,
32250
+ "--reset",
32251
+ "--interval",
32252
+ "5s",
32253
+ "--env",
32254
+ buildResult.env,
32255
+ "--server-url",
32256
+ buildResult.serverUrl
32257
+ ];
32258
+ return {
32259
+ command: process.execPath,
32260
+ args,
32261
+ text: formatLocalShellCommand([process.execPath, ...args])
32262
+ };
32263
+ }
32264
+ function resolveMakerCliEntry() {
32265
+ return process.argv[1] || path8.resolve(process.cwd(), "dist", "maker.js");
32266
+ }
32267
+ function getRuntimeLogFilePath(projectRoot) {
32268
+ return path8.join(projectRoot, ".maker", "logs", "runtime", "runtime.log");
32269
+ }
32270
+ async function refreshMakerPreview(buildResult) {
32271
+ const makerEnv = buildResult.env === "rnd" || buildResult.env === "production" ? buildResult.env : void 0;
32272
+ const apiBase = requireMakerEndpoint("apiBase", getMakerEndpoints(makerEnv).apiBase, makerEnv);
32273
+ const url2 = `${apiBase.replace(/\/$/, "")}/apps/${encodeURIComponent(
32274
+ buildResult.projectId
32275
+ )}/preview-refresh`;
32276
+ const pat = loadPat();
32277
+ if (!(pat == null ? void 0 : pat.token)) {
32278
+ return {
32279
+ ok: false,
32280
+ status: 0,
32281
+ url: url2,
32282
+ error: "Maker PAT not found. Run taptap-maker pat set and paste a Maker PAT first."
32283
+ };
32284
+ }
32285
+ const controller = new AbortController();
32286
+ const timeout = setTimeout(() => {
32287
+ controller.abort();
32288
+ }, PREVIEW_REFRESH_TIMEOUT_MS);
32289
+ try {
32290
+ const response = await fetch(url2, {
32291
+ method: "POST",
32292
+ headers: {
32293
+ Authorization: `Bearer ${pat.token}`,
32294
+ "Content-Type": "application/json"
32295
+ },
32296
+ body: "{}",
32297
+ signal: controller.signal
32298
+ });
32299
+ const responseText = await response.text();
32300
+ return {
32301
+ ok: response.ok,
32302
+ status: response.status,
32303
+ url: url2,
32304
+ ...responseText ? { responseText: responseText.slice(0, 2e3) } : {}
32305
+ };
32306
+ } catch (error2) {
32307
+ return {
32308
+ ok: false,
32309
+ status: 0,
32310
+ url: url2,
32311
+ error: error2 instanceof Error && error2.name === "AbortError" ? `preview refresh timed out after ${PREVIEW_REFRESH_TIMEOUT_MS}ms` : error2 instanceof Error ? error2.message : String(error2)
32312
+ };
32313
+ } finally {
32314
+ clearTimeout(timeout);
32315
+ }
32316
+ }
32317
+ async function callRemoteRuntimeLogs(proxy, args, timeoutMs = 60 * 1e3) {
32318
+ const runtimeLogClient = createRemoteRuntimeLogClient(proxy, timeoutMs);
32319
+ try {
32320
+ return await runtimeLogClient.call(args);
32321
+ } finally {
32322
+ await runtimeLogClient.close();
32323
+ }
32324
+ }
32325
+ function createRemoteRuntimeLogClient(proxy, timeoutMs = 60 * 1e3, options = {}) {
32326
+ let client;
32327
+ const createTransport = options.createTransport || (() => new HiddenStdioClientTransport({
32328
+ command: proxy.command,
32329
+ args: proxy.args,
32330
+ env: mergeStringEnv(process.env, proxy.envVars),
32331
+ stderr: "pipe"
32332
+ }));
32333
+ const createClient = options.createClient || (() => new Client(
32334
+ {
32335
+ name: "taptap-maker-runtime-log-forwarder",
32336
+ version: VERSION
32337
+ },
32338
+ {
32339
+ capabilities: {}
32340
+ }
32341
+ ));
32342
+ const ensureClient = async () => {
32343
+ if (client) {
32344
+ return client;
32345
+ }
32346
+ const nextClient = createClient();
32347
+ await nextClient.connect(createTransport());
32348
+ client = nextClient;
32349
+ return nextClient;
32350
+ };
32351
+ const close = async () => {
32352
+ const activeClient = client;
32353
+ client = void 0;
32354
+ if (activeClient) {
32355
+ await activeClient.close();
32356
+ }
32357
+ };
32358
+ return {
32359
+ call: async (args) => {
32360
+ const activeClient = await ensureClient();
32361
+ let result;
32362
+ try {
32363
+ result = await activeClient.callTool(
32364
+ {
32365
+ name: "query_runtime_logs",
32366
+ arguments: { ...args }
32367
+ },
32368
+ void 0,
32369
+ {
32370
+ timeout: timeoutMs
32371
+ }
32372
+ );
32373
+ } catch (error2) {
32374
+ await close();
32375
+ throw error2;
32376
+ }
32377
+ try {
32378
+ return normalizeRuntimeLogQueryResult(result);
32379
+ } catch (error2) {
32380
+ const rawPath = writeRuntimeLogRawResponse(proxy.projectRoot, result);
32381
+ throw new Error(
32382
+ [
32383
+ error2 instanceof Error ? error2.message : String(error2),
32384
+ `Raw query_runtime_logs response saved to ${rawPath}`
32385
+ ].join(" ")
32386
+ );
32387
+ }
32388
+ },
32389
+ close
32390
+ };
32391
+ }
31209
32392
  function toMakerBuildFailure(error2) {
31210
32393
  return {
31211
32394
  name: error2 instanceof Error ? error2.name : typeof error2,
@@ -31221,7 +32404,7 @@ function createBuildArgs(projectRoot, options) {
31221
32404
  if (options.scriptsPath) {
31222
32405
  buildArgs.scriptsPath = options.scriptsPath;
31223
32406
  }
31224
- if (!options.entry && !options.scriptsPath && !options.entryClient && !options.entryServer && !options.multiplayer && fs6.existsSync(path7.join(projectRoot, "scripts", "main.lua"))) {
32407
+ if (!options.entry && !options.scriptsPath && !options.entryClient && !options.entryServer && !options.multiplayer && fs7.existsSync(path8.join(projectRoot, "scripts", "main.lua"))) {
31225
32408
  buildArgs.entry = "main.lua";
31226
32409
  buildArgs.scriptsPath = "scripts";
31227
32410
  }
@@ -31233,7 +32416,7 @@ function createBuildArgs(projectRoot, options) {
31233
32416
  }
31234
32417
  if (options.multiplayer) {
31235
32418
  buildArgs.multiplayer = options.multiplayer;
31236
- } else if (!fs6.existsSync(path7.join(projectRoot, ".project", "settings.json"))) {
32419
+ } else if (!fs7.existsSync(path8.join(projectRoot, ".project", "settings.json"))) {
31237
32420
  buildArgs.multiplayer = { enabled: false };
31238
32421
  }
31239
32422
  return buildArgs;
@@ -31268,6 +32451,26 @@ function formatRemoteToolResult(result) {
31268
32451
  }).join("\n");
31269
32452
  }
31270
32453
  function formatBuildResult(result, progressSummary) {
32454
+ if (result.mode === "remote_build_failed") {
32455
+ return [
32456
+ "✗ Remote Maker build failed",
32457
+ "",
32458
+ `- project_root: ${result.projectRoot}`,
32459
+ `- project_id: ${result.projectId}`,
32460
+ `- maker_url: ${result.buildResult.makerUrl || formatMakerAppWebUrl(result.buildResult.projectId, result.buildResult.env)}`,
32461
+ `- project_path: ${result.buildResult.projectPath}`,
32462
+ `- server_url: ${result.buildResult.serverUrl}`,
32463
+ `- env: ${result.buildResult.env}`,
32464
+ `- timeout_ms: ${result.buildResult.timeoutMs}`,
32465
+ `- build_args: ${JSON.stringify(result.buildResult.buildArgs)}`,
32466
+ ...formatProgressSummary(progressSummary),
32467
+ "",
32468
+ ...formatMakerBuildFailureLines(result.buildFailure),
32469
+ "",
32470
+ "remote_result:",
32471
+ indent(result.buildResult.resultText)
32472
+ ].filter(Boolean).join("\n");
32473
+ }
31271
32474
  if (result.mode === "submit_failed_before_build") {
31272
32475
  return [
31273
32476
  result.submitResult.pushed ? "✓ Maker project submitted; remote build was not started" : result.submitResult.status === "clean" ? "Maker project has no changes to submit; remote build was not started" : "✗ Maker project submit failed; remote build was not started",
@@ -31334,7 +32537,10 @@ function formatBuildResult(result, progressSummary) {
31334
32537
  lines.push(
31335
32538
  `- timeout_ms: ${result.timeoutMs}`,
31336
32539
  `- build_args: ${JSON.stringify(result.buildArgs)}`,
32540
+ ...formatPreviewRefreshLines(result.previewRefresh),
31337
32541
  ...formatProgressSummary(progressSummary),
32542
+ "",
32543
+ ...formatRuntimeLogWatchNextActionLines(result),
31338
32544
  ""
31339
32545
  );
31340
32546
  if (submitLines.length > 0) {
@@ -31343,6 +32549,51 @@ function formatBuildResult(result, progressSummary) {
31343
32549
  lines.push("remote_result:", indent(result.resultText));
31344
32550
  return lines.join("\n");
31345
32551
  }
32552
+ function formatPreviewRefreshLines(result) {
32553
+ if (!result) {
32554
+ return ["- preview_refresh: (not attempted)"];
32555
+ }
32556
+ return [
32557
+ `- preview_refresh: ${result.ok ? "ok" : "failed"}`,
32558
+ `- preview_refresh_status: ${result.status}`,
32559
+ result.url ? `- preview_refresh_url: ${result.url}` : "",
32560
+ result.error ? `- preview_refresh_error: ${result.error}` : ""
32561
+ ].filter(Boolean);
32562
+ }
32563
+ function formatRuntimeLogWatchNextActionLines(result) {
32564
+ var _a2, _b, _c, _d, _e, _f, _g, _h, _i, _j;
32565
+ const command = formatLocalShellCommand([
32566
+ "taptap-maker",
32567
+ "logs",
32568
+ "watch",
32569
+ "--target-dir",
32570
+ result.projectRoot,
32571
+ "--reset",
32572
+ "--interval",
32573
+ "5s"
32574
+ ]);
32575
+ return [
32576
+ "runtime_logs:",
32577
+ result.runtimeLogWatch ? `- watch_started: ${result.runtimeLogWatch.started ? "yes" : "no"}` : "- watch_started: (not attempted)",
32578
+ ((_a2 = result.runtimeLogWatch) == null ? void 0 : _a2.pid) ? `- watch_pid: ${result.runtimeLogWatch.pid}` : "",
32579
+ `- watch_command: ${command}`,
32580
+ ((_b = result.runtimeLogWatch) == null ? void 0 : _b.command) ? `- actual_watch_command: ${result.runtimeLogWatch.command}` : "",
32581
+ `- local_file: ${path8.join(result.projectRoot, ".maker", "logs", "runtime", "runtime.log")}`,
32582
+ ((_c = result.runtimeLogWatch) == null ? void 0 : _c.stdoutLog) ? `- watcher_stdout: ${result.runtimeLogWatch.stdoutLog}` : "",
32583
+ ((_d = result.runtimeLogWatch) == null ? void 0 : _d.stderrLog) ? `- watcher_stderr: ${result.runtimeLogWatch.stderrLog}` : "",
32584
+ ((_e = result.runtimeLogWatch) == null ? void 0 : _e.pidFile) ? `- watcher_pid_file: ${result.runtimeLogWatch.pidFile}` : "",
32585
+ `- state_file: ${path8.join(result.projectRoot, ".maker", "logs", "runtime", "state.json")}`,
32586
+ ((_f = result.runtimeLogWatch) == null ? void 0 : _f.previousPid) ? `- previous_watch_pid: ${result.runtimeLogWatch.previousPid}` : "",
32587
+ ((_g = result.runtimeLogWatch) == null ? void 0 : _g.previousStopped) !== void 0 ? `- previous_watch_stopped: ${result.runtimeLogWatch.previousStopped ? "yes" : "no"}` : "",
32588
+ ((_h = result.runtimeLogWatch) == null ? void 0 : _h.previousStopError) ? `- previous_watch_stop_error: ${result.runtimeLogWatch.previousStopError}` : "",
32589
+ ((_i = result.runtimeLogWatch) == null ? void 0 : _i.error) ? `- watch_error: ${result.runtimeLogWatch.error}` : "",
32590
+ ((_j = result.runtimeLogWatch) == null ? void 0 : _j.started) ? "- note: 构建成功后已启动本地 CLI watcher,正在清理历史日志并每 5 秒持续追加 Lua 运行日志。" : "- note: 构建成功后应由本地 CLI watcher 清理历史日志,并每 5 秒持续追加 Lua 运行日志。",
32591
+ "- next_action: 如需分析游戏运行结果或报错,请读取 runtime_logs.local_file;如需判断 watcher 是否正常,请读取 runtime_logs.state_file。"
32592
+ ].filter(Boolean);
32593
+ }
32594
+ function formatLocalShellCommand(parts) {
32595
+ return parts.map((part) => /\s/.test(part) ? `"${part.replace(/(["\\$`])/g, "\\$1")}"` : part).join(" ");
32596
+ }
31346
32597
  function formatPushRecoveryLines(submitResult) {
31347
32598
  if (submitResult.pushed || submitResult.status !== "failed_after_commit") {
31348
32599
  return [];
@@ -31448,20 +32699,21 @@ function indent(value) {
31448
32699
  // src/maker/cli/commands.ts
31449
32700
  import { spawnSync as spawnSync4 } from "node:child_process";
31450
32701
  import crypto from "node:crypto";
31451
- import fs7 from "node:fs";
32702
+ import fs8 from "node:fs";
31452
32703
  import os4 from "node:os";
31453
- import path8 from "node:path";
32704
+ import path9 from "node:path";
31454
32705
  import readline from "node:readline/promises";
31455
32706
  import { stdin as input, stdout as output } from "node:process";
31456
32707
  var DEFAULT_MCP_NAME = "taptap-maker";
31457
32708
  var DEFAULT_PACKAGE = "@taptap/instant-games-open-mcp";
31458
- var TWO_PART_COMMANDS = /* @__PURE__ */ new Set(["pat", "mcp", "dev-kit"]);
32709
+ var TWO_PART_COMMANDS = /* @__PURE__ */ new Set(["pat", "mcp", "dev-kit", "logs"]);
31459
32710
  var BOOLEAN_OPTIONS = /* @__PURE__ */ new Set([
31460
32711
  "json",
31461
32712
  "skip_confirm",
31462
32713
  "skip_mcp_install",
31463
32714
  "pat_stdin",
31464
32715
  "pat_from_stdin",
32716
+ "reset",
31465
32717
  "all",
31466
32718
  "h",
31467
32719
  "help"
@@ -31503,6 +32755,10 @@ async function runMakerCli(argv) {
31503
32755
  await runDevKitUpdate(parsed, ctx);
31504
32756
  return;
31505
32757
  }
32758
+ if (command === "logs" && subcommand === "watch") {
32759
+ await runLogsWatch(parsed, ctx);
32760
+ return;
32761
+ }
31506
32762
  throw new Error(`Unknown taptap-maker command: ${formatUnknownCommand(parsed.command)}`);
31507
32763
  }
31508
32764
  function parseArgs(argv) {
@@ -31551,7 +32807,7 @@ function toOptionKey(value) {
31551
32807
  return value.replace(/-/g, "_");
31552
32808
  }
31553
32809
  async function runInit(parsed, ctx) {
31554
- const targetDir = path8.resolve(stringOption(parsed, "target_dir") || process.cwd());
32810
+ const targetDir = path9.resolve(stringOption(parsed, "target_dir") || process.cwd());
31555
32811
  const env = makerEnvOption(parsed);
31556
32812
  const skipConfirm = booleanOption(parsed, "skip_confirm");
31557
32813
  const skipMcpInstall = booleanOption(parsed, "skip_mcp_install");
@@ -31569,12 +32825,16 @@ async function runInit(parsed, ctx) {
31569
32825
  emit(ctx, "doctor", "Git is available", { version: git.version });
31570
32826
  const pat = await resolvePat(parsed, ctx);
31571
32827
  emit(ctx, "pat", "Maker PAT ready", { saved: getPatPath() });
31572
- const tapAuth = await requestTapAuthWithPat(pat);
32828
+ const tapAuth = await requestTapAuthWithPat(pat).catch((error2) => {
32829
+ throw appendPatRecoveryUrl(error2, parsed);
32830
+ });
31573
32831
  emit(ctx, "tap_auth", "TapTap token exchanged and saved", {
31574
32832
  kid: mask(tapAuth.kid),
31575
32833
  saved: getTapAuthPath()
31576
32834
  });
31577
- const projects = await listMakerProjects({ pat });
32835
+ const projects = await listMakerProjects({ pat }).catch((error2) => {
32836
+ throw appendPatRecoveryUrl(error2, parsed);
32837
+ });
31578
32838
  const selected = await resolveProjectSelection(parsed, projects, {
31579
32839
  skipConfirm
31580
32840
  });
@@ -31597,6 +32857,8 @@ async function runInit(parsed, ctx) {
31597
32857
  userId: selected.user_id,
31598
32858
  sceEndpoint: selected.sce_endpoint || process.env.SCE_MCP_URL,
31599
32859
  onProgress: (progress) => emitProgress(ctx, "clone", progress)
32860
+ }).catch((error2) => {
32861
+ throw appendPatRecoveryUrl(error2, parsed);
31600
32862
  });
31601
32863
  emit(ctx, "clone", "Maker project cloned or fetched", cloneResult);
31602
32864
  if (!skipMcpInstall) {
@@ -31624,7 +32886,7 @@ async function runInit(parsed, ctx) {
31624
32886
  });
31625
32887
  }
31626
32888
  async function runDoctor(parsed, ctx) {
31627
- const targetDir = path8.resolve(stringOption(parsed, "target_dir") || process.cwd());
32889
+ const targetDir = path9.resolve(stringOption(parsed, "target_dir") || process.cwd());
31628
32890
  const git = checkGitEnvironment();
31629
32891
  const pat = loadPat();
31630
32892
  const tapAuth = loadTapAuth();
@@ -31675,7 +32937,9 @@ async function runApps(parsed, ctx) {
31675
32937
  warnPatArgExposure();
31676
32938
  }
31677
32939
  const showAll = booleanOption(parsed, "all");
31678
- const projects = await listMakerProjects({ pat });
32940
+ const projects = await listMakerProjects({ pat }).catch((error2) => {
32941
+ throw appendPatRecoveryUrl(error2, parsed);
32942
+ });
31679
32943
  if (ctx.json) {
31680
32944
  writeJson(projects);
31681
32945
  return;
@@ -31686,7 +32950,9 @@ async function runApps(parsed, ctx) {
31686
32950
  async function runPatSet(parsed, ctx) {
31687
32951
  const pat = await resolvePatSet(parsed, ctx);
31688
32952
  saveManualMakerPat(pat);
31689
- const tapAuth = await requestTapAuthWithPat(pat);
32953
+ const tapAuth = await requestTapAuthWithPat(pat).catch((error2) => {
32954
+ throw appendPatRecoveryUrl(error2, parsed);
32955
+ });
31690
32956
  emit(ctx, "pat", "Maker PAT and TapTap token saved", {
31691
32957
  pat_path: getPatPath(),
31692
32958
  tap_auth_path: getTapAuthPath(),
@@ -31695,7 +32961,7 @@ async function runPatSet(parsed, ctx) {
31695
32961
  }
31696
32962
  async function resolvePatSet(parsed, ctx) {
31697
32963
  if (booleanOption(parsed, "pat_stdin") || booleanOption(parsed, "pat_from_stdin")) {
31698
- const pat = fs7.readFileSync(0, "utf8").trim();
32964
+ const pat = fs8.readFileSync(0, "utf8").trim();
31699
32965
  if (!pat) {
31700
32966
  throw new Error("No PAT found on stdin.");
31701
32967
  }
@@ -31822,13 +33088,127 @@ function getMcpVerifyNextSteps(mode, commandText) {
31822
33088
  ];
31823
33089
  }
31824
33090
  async function runDevKitUpdate(parsed, ctx) {
31825
- const targetDir = path8.resolve(stringOption(parsed, "target_dir") || process.cwd());
33091
+ const targetDir = path9.resolve(stringOption(parsed, "target_dir") || process.cwd());
31826
33092
  const result = await installAiDevKit({
31827
33093
  targetDir,
31828
33094
  preserveExisting: true
31829
33095
  });
31830
33096
  finalizeStagedDevKitGitignore(targetDir);
31831
- emit(ctx, "dev_kit", "AI dev kit updated", result);
33097
+ emit(ctx, "dev_kit", formatDevKitInstallMessage("AI dev kit updated", result), result);
33098
+ emitDevKitSkillInstallerFailure(ctx, result.skillInstaller, "AI skills install failed");
33099
+ }
33100
+ async function runLogsWatch(parsed, ctx) {
33101
+ const targetDir = path9.resolve(stringOption(parsed, "target_dir") || process.cwd());
33102
+ const intervalMs = parseDurationMs(stringOption(parsed, "interval") || "5s");
33103
+ const timeoutMs = numberOption(parsed, "timeout_ms") ?? 60 * 1e3;
33104
+ const maxPolls = numberOption(parsed, "max_polls");
33105
+ const maxConsecutiveFailures = numberOption(parsed, "max_consecutive_failures");
33106
+ const proxy = createRemoteProxyContext({
33107
+ targetDir,
33108
+ serverUrl: stringOption(parsed, "server_url"),
33109
+ env: makerEnvOption(parsed)
33110
+ });
33111
+ const runtimeDir = path9.join(proxy.projectRoot, ".maker", "logs", "runtime");
33112
+ const runtimeLog = path9.join(runtimeDir, "runtime.log");
33113
+ const pidFile = path9.join(runtimeDir, "watcher.pid");
33114
+ const replacedWatcher = registerRuntimeLogWatcherProcess(pidFile);
33115
+ const runtimeLogClient = createRemoteRuntimeLogClient(proxy, timeoutMs);
33116
+ emit(ctx, "logs_watch_start", "Maker runtime log watcher started", {
33117
+ project_root: proxy.projectRoot,
33118
+ project_id: proxy.projectId,
33119
+ runtime_log: runtimeLog,
33120
+ pid_file: pidFile,
33121
+ pid: process.pid,
33122
+ ...replacedWatcher,
33123
+ reset: booleanOption(parsed, "reset"),
33124
+ interval_ms: intervalMs,
33125
+ topics: ["user_script", "server_user_script"]
33126
+ });
33127
+ try {
33128
+ const result = await watchRuntimeLogs({
33129
+ projectRoot: proxy.projectRoot,
33130
+ projectId: proxy.projectId,
33131
+ reset: booleanOption(parsed, "reset"),
33132
+ intervalMs,
33133
+ limit: numberOption(parsed, "limit"),
33134
+ maxPolls,
33135
+ maxConsecutiveFailures,
33136
+ callRemoteRuntimeLogs: (args) => runtimeLogClient.call(args),
33137
+ onPoll: (pullResult) => {
33138
+ emit(ctx, "logs_poll", `Maker runtime logs pulled: ${pullResult.writtenLogs}`, {
33139
+ written_logs: pullResult.writtenLogs,
33140
+ has_more: pullResult.hasMore,
33141
+ next_start_time: pullResult.nextStartTime,
33142
+ files: pullResult.files
33143
+ });
33144
+ },
33145
+ onError: (error2, consecutiveFailures) => {
33146
+ emit(ctx, "logs_poll_error", "Maker runtime log poll failed; watcher will retry", {
33147
+ consecutive_failures: consecutiveFailures,
33148
+ error: error2 instanceof Error ? error2.message : String(error2)
33149
+ });
33150
+ }
33151
+ });
33152
+ emit(ctx, "logs_watch_stop", "Maker runtime log watcher stopped", result);
33153
+ } finally {
33154
+ await runtimeLogClient.close();
33155
+ }
33156
+ }
33157
+ function registerRuntimeLogWatcherProcess(pidFile) {
33158
+ fs8.mkdirSync(path9.dirname(pidFile), { recursive: true });
33159
+ const existingPid = readPidFile(pidFile);
33160
+ const previous = existingPid && existingPid !== process.pid ? stopExistingRuntimeLogWatcher(pidFile) : {};
33161
+ fs8.writeFileSync(
33162
+ pidFile,
33163
+ `${JSON.stringify(
33164
+ {
33165
+ pid: process.pid,
33166
+ command: formatShellCommand([process.execPath, ...process.argv.slice(1)]),
33167
+ startedAt: (/* @__PURE__ */ new Date()).toISOString()
33168
+ },
33169
+ null,
33170
+ 2
33171
+ )}
33172
+ `,
33173
+ "utf8"
33174
+ );
33175
+ installRuntimeLogWatcherPidCleanup(pidFile);
33176
+ return previous;
33177
+ }
33178
+ function readPidFile(pidFile) {
33179
+ if (!fs8.existsSync(pidFile)) {
33180
+ return void 0;
33181
+ }
33182
+ const raw = fs8.readFileSync(pidFile, "utf8").trim();
33183
+ let pid = Number(raw);
33184
+ if (!Number.isInteger(pid) || pid <= 0) {
33185
+ try {
33186
+ const parsed = JSON.parse(raw);
33187
+ pid = typeof parsed.pid === "number" ? parsed.pid : Number.NaN;
33188
+ } catch {
33189
+ pid = Number.NaN;
33190
+ }
33191
+ }
33192
+ return Number.isInteger(pid) && pid > 0 ? pid : void 0;
33193
+ }
33194
+ function installRuntimeLogWatcherPidCleanup(pidFile) {
33195
+ let cleaned = false;
33196
+ const cleanup = () => {
33197
+ if (cleaned) {
33198
+ return;
33199
+ }
33200
+ cleaned = true;
33201
+ if (readPidFile(pidFile) === process.pid) {
33202
+ fs8.rmSync(pidFile, { force: true });
33203
+ }
33204
+ };
33205
+ process.once("exit", cleanup);
33206
+ for (const signal of ["SIGINT", "SIGTERM"]) {
33207
+ process.once(signal, () => {
33208
+ cleanup();
33209
+ process.exit(0);
33210
+ });
33211
+ }
31832
33212
  }
31833
33213
  async function resolvePat(parsed, ctx) {
31834
33214
  const fromArgs = stringOption(parsed, "pat");
@@ -31900,21 +33280,90 @@ async function prepareDevKit(targetDir, ctx) {
31900
33280
  const before = inspectAiDevKit(targetDir);
31901
33281
  if (before.ready) {
31902
33282
  writeDevKitStagedGitignore(
31903
- path8.join(targetDir, DEV_KIT_GITIGNORE_STAGING_FILE),
33283
+ path9.join(targetDir, DEV_KIT_GITIGNORE_STAGING_FILE),
31904
33284
  listPresentDevKitManagedEntries(targetDir)
31905
33285
  );
31906
- emit(ctx, "dev_kit", "AI dev kit already present", before);
33286
+ try {
33287
+ const skillInstaller = installAiDevKitSkills(targetDir, {
33288
+ onStart: (event) => emitSkillInstallerStart(ctx, event)
33289
+ });
33290
+ emit(
33291
+ ctx,
33292
+ "dev_kit",
33293
+ formatDevKitInstallMessage("AI dev kit already present", { skillInstaller }),
33294
+ {
33295
+ ...before,
33296
+ skillInstaller
33297
+ }
33298
+ );
33299
+ } catch (error2) {
33300
+ const detail = error2 instanceof Error ? error2.message : String(error2);
33301
+ emit(ctx, "dev_kit_warning", `AI skills install failed; clone will continue
33302
+ ${detail}`, {
33303
+ error: detail
33304
+ });
33305
+ }
31907
33306
  return;
31908
33307
  }
31909
33308
  try {
31910
- const result = await installAiDevKit({ targetDir });
31911
- emit(ctx, "dev_kit", "AI dev kit prepared", result);
33309
+ const result = await installAiDevKit({
33310
+ targetDir,
33311
+ onSkillInstallerStart: (event) => emitSkillInstallerStart(ctx, event)
33312
+ });
33313
+ emit(ctx, "dev_kit", formatDevKitInstallMessage("AI dev kit prepared", result), result);
33314
+ emitDevKitSkillInstallerFailure(
33315
+ ctx,
33316
+ result.skillInstaller,
33317
+ "AI skills install failed; clone will continue"
33318
+ );
31912
33319
  } catch (error2) {
31913
- emit(ctx, "dev_kit_warning", "AI dev kit preparation failed; clone will continue", {
31914
- error: error2 instanceof Error ? error2.message : String(error2)
33320
+ const detail = error2 instanceof Error ? error2.message : String(error2);
33321
+ emit(ctx, "dev_kit_warning", `AI dev kit preparation failed; clone will continue
33322
+ ${detail}`, {
33323
+ error: detail
31915
33324
  });
31916
33325
  }
31917
33326
  }
33327
+ function formatDevKitInstallMessage(message, result) {
33328
+ if (!result.skillInstaller) {
33329
+ return message;
33330
+ }
33331
+ return `${message}
33332
+ AI skills install result: ${result.skillInstaller.summary}`;
33333
+ }
33334
+ function emitSkillInstallerStart(ctx, event) {
33335
+ emit(ctx, "dev_kit_skill_install_start", `AI skills install started: ${event.script}`, event);
33336
+ }
33337
+ function emitDevKitSkillInstallerFailure(ctx, result, message) {
33338
+ if (!result || result.ok || result.status !== "failed") {
33339
+ return;
33340
+ }
33341
+ const detail = result.error || "unknown installer failure";
33342
+ emit(ctx, "dev_kit_warning", `${message}
33343
+ ${detail}`, {
33344
+ error: detail
33345
+ });
33346
+ }
33347
+ function appendPatRecoveryUrl(error2, parsed) {
33348
+ const message = error2 instanceof Error ? error2.message : String(error2);
33349
+ if (!isPatValidationFailure(message) || message.includes("/pat-tokens")) {
33350
+ return error2 instanceof Error ? error2 : new Error(message);
33351
+ }
33352
+ const patPage = getMakerPatTokensUrl(makerEnvOption(parsed));
33353
+ return new Error(
33354
+ [
33355
+ message,
33356
+ "",
33357
+ `PAT 页面:${patPage}`,
33358
+ "请在该页面创建新的 Maker PAT,然后运行 `taptap-maker pat set` 并粘贴 PAT。"
33359
+ ].join("\n")
33360
+ );
33361
+ }
33362
+ function isPatValidationFailure(message) {
33363
+ return /\b(?:PAT_INVALID|HTTP\s*40[13]|40[13]|unauthori[sz]ed|invalid\s+PAT|PAT\s+invalid|expired)\b/i.test(
33364
+ message
33365
+ ) || /(?:过期|失效)/.test(message);
33366
+ }
31918
33367
  function installMcpConfigs(options) {
31919
33368
  return options.ides.map((ide) => installMcpConfig(ide, options));
31920
33369
  }
@@ -31931,7 +33380,7 @@ function installMcpConfig(ide, options) {
31931
33380
  }
31932
33381
  function installMcpConfigUnsafe(ide, options) {
31933
33382
  if (ide === "codex") {
31934
- const configPath = path8.join(os4.homedir(), ".codex", "config.toml");
33383
+ const configPath = path9.join(os4.homedir(), ".codex", "config.toml");
31935
33384
  mergeCodexMcpConfig(configPath, options);
31936
33385
  return {
31937
33386
  ide,
@@ -31941,7 +33390,7 @@ function installMcpConfigUnsafe(ide, options) {
31941
33390
  };
31942
33391
  }
31943
33392
  if (ide === "cursor") {
31944
- const configPath = path8.join(os4.homedir(), ".cursor", "mcp.json");
33393
+ const configPath = path9.join(os4.homedir(), ".cursor", "mcp.json");
31945
33394
  mergeJsonMcpConfig(configPath, options);
31946
33395
  return {
31947
33396
  ide,
@@ -31955,7 +33404,7 @@ function installMcpConfigUnsafe(ide, options) {
31955
33404
  if (claudeResult.ok) {
31956
33405
  return { ide, ok: true, message: "✓ Claude Code MCP config updated with claude mcp add" };
31957
33406
  }
31958
- const configPath = path8.join(os4.homedir(), ".claude.json");
33407
+ const configPath = path9.join(os4.homedir(), ".claude.json");
31959
33408
  mergeJsonMcpConfig(configPath, options);
31960
33409
  return {
31961
33410
  ide,
@@ -31972,13 +33421,13 @@ function mergeJsonMcpConfig(configPath, options) {
31972
33421
  const mcpServers = asObject(existing.mcpServers);
31973
33422
  mcpServers[options.mcpName] = createJsonMcpServerConfig(options);
31974
33423
  existing.mcpServers = mcpServers;
31975
- fs7.mkdirSync(path8.dirname(configPath), { recursive: true });
31976
- fs7.writeFileSync(configPath, `${JSON.stringify(existing, null, 2)}
33424
+ fs8.mkdirSync(path9.dirname(configPath), { recursive: true });
33425
+ fs8.writeFileSync(configPath, `${JSON.stringify(existing, null, 2)}
31977
33426
  `, "utf8");
31978
33427
  }
31979
33428
  function mergeCodexMcpConfig(configPath, options) {
31980
33429
  const backupPath = backupIfExists(configPath);
31981
- const existing = fs7.existsSync(configPath) ? fs7.readFileSync(configPath, "utf8") : "";
33430
+ const existing = fs8.existsSync(configPath) ? fs8.readFileSync(configPath, "utf8") : "";
31982
33431
  const sectionPattern = createCodexMcpSectionPattern(options.mcpName);
31983
33432
  const withoutOld = existing.replace(sectionPattern, "").trimEnd();
31984
33433
  const command = getNpxCommand();
@@ -31991,9 +33440,9 @@ function mergeCodexMcpConfig(configPath, options) {
31991
33440
  `TAPTAP_MCP_ENV = "${options.env}"`,
31992
33441
  ""
31993
33442
  ].join("\n");
31994
- fs7.mkdirSync(path8.dirname(configPath), { recursive: true });
31995
- fs7.writeFileSync(configPath, [withoutOld, section].filter(Boolean).join("\n\n"), "utf8");
31996
- const updated = fs7.readFileSync(configPath, "utf8");
33443
+ fs8.mkdirSync(path9.dirname(configPath), { recursive: true });
33444
+ fs8.writeFileSync(configPath, [withoutOld, section].filter(Boolean).join("\n\n"), "utf8");
33445
+ const updated = fs8.readFileSync(configPath, "utf8");
31997
33446
  const duplicates = findCodexMcpTableDuplicates(updated, options.mcpName);
31998
33447
  if (duplicates.length > 0) {
31999
33448
  restoreBackup(configPath, backupPath);
@@ -32091,10 +33540,10 @@ function getNpxCommand() {
32091
33540
  return process.platform === "win32" ? "npx.cmd" : "npx";
32092
33541
  }
32093
33542
  function readJsonObject(filePath) {
32094
- if (!fs7.existsSync(filePath)) {
33543
+ if (!fs8.existsSync(filePath)) {
32095
33544
  return {};
32096
33545
  }
32097
- return JSON.parse(fs7.readFileSync(filePath, "utf8"));
33546
+ return JSON.parse(fs8.readFileSync(filePath, "utf8"));
32098
33547
  }
32099
33548
  function asObject(value) {
32100
33549
  if (value && typeof value === "object" && !Array.isArray(value)) {
@@ -32103,26 +33552,26 @@ function asObject(value) {
32103
33552
  return {};
32104
33553
  }
32105
33554
  function backupIfExists(filePath) {
32106
- if (!fs7.existsSync(filePath)) {
33555
+ if (!fs8.existsSync(filePath)) {
32107
33556
  return void 0;
32108
33557
  }
32109
33558
  const stamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[-:]/g, "").replace(/\..+$/, "");
32110
33559
  const backupPath = `${filePath}.bak.${stamp}`;
32111
- fs7.copyFileSync(filePath, backupPath);
33560
+ fs8.copyFileSync(filePath, backupPath);
32112
33561
  return backupPath;
32113
33562
  }
32114
33563
  function restoreBackup(filePath, backupPath) {
32115
33564
  if (backupPath) {
32116
- fs7.copyFileSync(backupPath, filePath);
33565
+ fs8.copyFileSync(backupPath, filePath);
32117
33566
  return;
32118
33567
  }
32119
- fs7.rmSync(filePath, { force: true });
33568
+ fs8.rmSync(filePath, { force: true });
32120
33569
  }
32121
33570
  function saveInitState(targetDir, state) {
32122
- fs7.mkdirSync(getMakerHome(), { recursive: true });
32123
- const key = crypto.createHash("sha256").update(path8.resolve(targetDir)).digest("hex").slice(0, 16);
32124
- fs7.writeFileSync(
32125
- path8.join(getMakerHome(), `init-state-${key}.json`),
33571
+ fs8.mkdirSync(getMakerHome(), { recursive: true });
33572
+ const key = crypto.createHash("sha256").update(path9.resolve(targetDir)).digest("hex").slice(0, 16);
33573
+ fs8.writeFileSync(
33574
+ path9.join(getMakerHome(), `init-state-${key}.json`),
32126
33575
  `${JSON.stringify({ ...state, updated_at: (/* @__PURE__ */ new Date()).toISOString() }, null, 2)}
32127
33576
  `,
32128
33577
  "utf8"
@@ -32223,7 +33672,7 @@ function formatUnknownCommand(command) {
32223
33672
  return `${primary} ${isKnownSubcommand(primary, secondary) ? secondary : "<redacted>"}`;
32224
33673
  }
32225
33674
  function isKnownSubcommand(command, subcommand) {
32226
- return command === "pat" && subcommand === "set" || command === "mcp" && (subcommand === "install" || subcommand === "verify") || command === "dev-kit" && subcommand === "update";
33675
+ return command === "pat" && subcommand === "set" || command === "mcp" && (subcommand === "install" || subcommand === "verify") || command === "dev-kit" && subcommand === "update" || command === "logs" && subcommand === "watch";
32227
33676
  }
32228
33677
  function stringOption(parsed, key) {
32229
33678
  const value = parsed.options[key];
@@ -32232,6 +33681,26 @@ function stringOption(parsed, key) {
32232
33681
  function booleanOption(parsed, key) {
32233
33682
  return parsed.options[key] === true || parsed.options[key] === "true";
32234
33683
  }
33684
+ function numberOption(parsed, key) {
33685
+ const value = parsed.options[key];
33686
+ if (value === void 0 || value === true || value === false) {
33687
+ return void 0;
33688
+ }
33689
+ const number3 = Number(value);
33690
+ if (!Number.isFinite(number3)) {
33691
+ throw new Error(`Invalid --${key.replace(/_/g, "-")} value: ${value}`);
33692
+ }
33693
+ return number3;
33694
+ }
33695
+ function parseDurationMs(value) {
33696
+ const match = /^(\d+(?:\.\d+)?)(ms|s)?$/.exec(value.trim());
33697
+ if (!match) {
33698
+ throw new Error("Invalid --interval value. Use seconds, or suffix with s/ms.");
33699
+ }
33700
+ const amount = Number(match[1]);
33701
+ const unit = match[2] || "s";
33702
+ return unit === "s" ? amount * 1e3 : amount;
33703
+ }
32235
33704
  function makerEnvOption(parsed) {
32236
33705
  const env = stringOption(parsed, "env");
32237
33706
  if (env === "rnd" || env === "production") {
@@ -32288,6 +33757,7 @@ function printHelp() {
32288
33757
  " taptap-maker mcp verify [--package @taptap/instant-games-open-mcp]",
32289
33758
  " [--mode npx|self] [--json]",
32290
33759
  " taptap-maker dev-kit update [--target-dir DIR] [--json]",
33760
+ " taptap-maker logs watch [--target-dir DIR] [--interval 5s] [--reset] [--json]",
32291
33761
  "",
32292
33762
  "MCP verify defaults to the npx command written into AI client config.",
32293
33763
  "Advanced: init and mcp install accept --package only when testing a different npm package.",
@@ -32306,14 +33776,14 @@ function formatCliError(error2) {
32306
33776
  }
32307
33777
 
32308
33778
  // src/maker/crashLog.ts
32309
- import fs8 from "node:fs";
32310
- import path9 from "node:path";
33779
+ import fs9 from "node:fs";
33780
+ import path10 from "node:path";
32311
33781
  var DEFAULT_MAX_BYTES = 1024 * 1024;
32312
33782
  var DEFAULT_MAX_ENTRY_BYTES = 16 * 1024;
32313
33783
  function appendMakerCrashLog(source, error2, options = {}) {
32314
33784
  const makerHome = getMakerHome();
32315
- fs8.mkdirSync(makerHome, { recursive: true });
32316
- const logPath = path9.join(makerHome, "mcp-crash.log");
33785
+ fs9.mkdirSync(makerHome, { recursive: true });
33786
+ const logPath = path10.join(makerHome, "mcp-crash.log");
32317
33787
  const maxBytes = positiveInteger(options.maxBytes) || resolveEnvBytes("TAPTAP_MAKER_CRASH_LOG_MAX_BYTES", DEFAULT_MAX_BYTES);
32318
33788
  const maxEntryBytes = positiveInteger(options.maxEntryBytes) || resolveEnvBytes("TAPTAP_MAKER_CRASH_LOG_MAX_ENTRY_BYTES", DEFAULT_MAX_ENTRY_BYTES);
32319
33789
  const message = error2 instanceof Error ? error2.stack || error2.message : String(error2);
@@ -32323,31 +33793,31 @@ function appendMakerCrashLog(source, error2, options = {}) {
32323
33793
  );
32324
33794
  const entryBytes = Buffer.byteLength(entry, "utf8");
32325
33795
  rotateIfNeeded(logPath, maxBytes, entryBytes);
32326
- fs8.appendFileSync(logPath, entry, "utf8");
33796
+ fs9.appendFileSync(logPath, entry, "utf8");
32327
33797
  }
32328
33798
  function rotateIfNeeded(logPath, maxBytes, incomingBytes) {
32329
- if (!fs8.existsSync(logPath)) {
33799
+ if (!fs9.existsSync(logPath)) {
32330
33800
  return;
32331
33801
  }
32332
- const currentBytes = fs8.statSync(logPath).size;
33802
+ const currentBytes = fs9.statSync(logPath).size;
32333
33803
  if (currentBytes + incomingBytes <= maxBytes) {
32334
33804
  return;
32335
33805
  }
32336
33806
  const rotatedPath = `${logPath}.1`;
32337
33807
  writeFileTail(logPath, rotatedPath, maxBytes);
32338
- fs8.writeFileSync(logPath, "", "utf8");
33808
+ fs9.writeFileSync(logPath, "", "utf8");
32339
33809
  }
32340
33810
  function writeFileTail(sourcePath, targetPath, maxBytes) {
32341
- const size = fs8.statSync(sourcePath).size;
33811
+ const size = fs9.statSync(sourcePath).size;
32342
33812
  const bytesToRead = Math.min(size, maxBytes);
32343
33813
  const start = Math.max(0, size - bytesToRead);
32344
- const fd = fs8.openSync(sourcePath, "r");
33814
+ const fd = fs9.openSync(sourcePath, "r");
32345
33815
  try {
32346
33816
  const buffer = Buffer.alloc(bytesToRead);
32347
- fs8.readSync(fd, buffer, 0, bytesToRead, start);
32348
- fs8.writeFileSync(targetPath, buffer);
33817
+ fs9.readSync(fd, buffer, 0, bytesToRead, start);
33818
+ fs9.writeFileSync(targetPath, buffer);
32349
33819
  } finally {
32350
- fs8.closeSync(fd);
33820
+ fs9.closeSync(fd);
32351
33821
  }
32352
33822
  }
32353
33823
  function truncateUtf8(value, maxBytes) {
@@ -32400,6 +33870,7 @@ function printHelp2() {
32400
33870
  " taptap-maker mcp verify [--package @taptap/instant-games-open-mcp]",
32401
33871
  " [--mode npx|self] [--json]",
32402
33872
  " taptap-maker dev-kit update [--target-dir DIR] [--json]",
33873
+ " taptap-maker logs watch [--target-dir DIR] [--interval 5s] [--reset] [--json]",
32403
33874
  "",
32404
33875
  "MCP verify defaults to the npx command written into AI client config.",
32405
33876
  "Advanced: init and mcp install accept --package only when testing a different npm package.",