kantban-cli 0.1.35 → 0.1.37

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (32) hide show
  1. package/dist/{chunk-7HJZFR7Y.js → chunk-AF72765A.js} +22 -14
  2. package/dist/chunk-AF72765A.js.map +1 -0
  3. package/dist/{chunk-ZTQJMXJM.js → chunk-DAFLEMLK.js} +1 -1
  4. package/dist/chunk-DAFLEMLK.js.map +1 -0
  5. package/dist/chunk-DGUM43GV.js +11 -0
  6. package/dist/chunk-DGUM43GV.js.map +1 -0
  7. package/dist/{chunk-2P25AHSD.js → chunk-MIL7SIPJ.js} +12 -7
  8. package/dist/chunk-MIL7SIPJ.js.map +1 -0
  9. package/dist/chunk-UNSNCYTR.js +96 -0
  10. package/dist/chunk-UNSNCYTR.js.map +1 -0
  11. package/dist/{context-7YDNTI3P.js → context-IRTNYVN6.js} +3 -1
  12. package/dist/{context-7YDNTI3P.js.map → context-IRTNYVN6.js.map} +1 -1
  13. package/dist/{cron-RG4VCGME.js → cron-PS2IEPH2.js} +5 -3
  14. package/dist/{cron-RG4VCGME.js.map → cron-PS2IEPH2.js.map} +1 -1
  15. package/dist/index.js +7 -6
  16. package/dist/index.js.map +1 -1
  17. package/dist/lib/gate-proxy-server.js +4 -2
  18. package/dist/lib/gate-proxy-server.js.map +1 -1
  19. package/dist/{pipeline-ZI7Y25IA.js → pipeline-76ZTI4IN.js} +79 -56
  20. package/dist/pipeline-76ZTI4IN.js.map +1 -0
  21. package/dist/{pipeline-init-IGZZOOLK.js → pipeline-init-QDHBJGWY.js} +3 -1
  22. package/dist/{pipeline-init-IGZZOOLK.js.map → pipeline-init-QDHBJGWY.js.map} +1 -1
  23. package/dist/{status-4GFXMVIM.js → status-3B3BSJBJ.js} +3 -1
  24. package/dist/{status-4GFXMVIM.js.map → status-3B3BSJBJ.js.map} +1 -1
  25. package/dist/{work-2V33NZAT.js → work-HZVJJG4A.js} +11 -3
  26. package/dist/work-HZVJJG4A.js.map +1 -0
  27. package/package.json +4 -3
  28. package/dist/chunk-2P25AHSD.js.map +0 -1
  29. package/dist/chunk-7HJZFR7Y.js.map +0 -1
  30. package/dist/chunk-ZTQJMXJM.js.map +0 -1
  31. package/dist/pipeline-ZI7Y25IA.js.map +0 -1
  32. package/dist/work-2V33NZAT.js.map +0 -1
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  runGates
3
- } from "./chunk-2P25AHSD.js";
3
+ } from "./chunk-MIL7SIPJ.js";
4
4
  import {
5
5
  ClaudeProvider,
6
6
  RalphLoop,
@@ -12,14 +12,22 @@ import {
12
12
  generateMcpConfig,
13
13
  parseJsonFromLlmOutput,
14
14
  parseStuckDetectionResponse
15
- } from "./chunk-7HJZFR7Y.js";
15
+ } from "./chunk-AF72765A.js";
16
16
  import {
17
17
  LoopCheckpointSchema,
18
18
  VerdictSchema,
19
19
  parseGateConfig,
20
20
  parseTimeout,
21
21
  resolveGatesForColumn
22
- } from "./chunk-ZTQJMXJM.js";
22
+ } from "./chunk-DAFLEMLK.js";
23
+ import {
24
+ IS_WINDOWS,
25
+ crossSpawnOptions,
26
+ killProcessTree,
27
+ normalizeEol,
28
+ resolveCommand
29
+ } from "./chunk-UNSNCYTR.js";
30
+ import "./chunk-DGUM43GV.js";
23
31
 
24
32
  // src/commands/pipeline.ts
25
33
  import { mkdirSync as mkdirSync3, writeFileSync as writeFileSync4, readFileSync as readFileSync3, unlinkSync as unlinkSync3, existsSync as existsSync3, appendFileSync as appendFileSync2 } from "fs";
@@ -51,25 +59,25 @@ function isPlausibleRemoteUrl(url) {
51
59
  }
52
60
  function ensureWorktreeRemote(worktreePath) {
53
61
  try {
54
- const remotes = execFileSync("git", ["-C", worktreePath, "remote"], {
62
+ const remotes = normalizeEol(execFileSync("git", ["-C", worktreePath, "remote"], {
55
63
  stdio: "pipe",
56
64
  encoding: "utf-8"
57
- }).trim();
65
+ })).trim();
58
66
  if (remotes.split("\n").includes("origin")) {
59
- const currentUrl = execFileSync("git", ["-C", worktreePath, "remote", "get-url", "origin"], {
67
+ const currentUrl = normalizeEol(execFileSync("git", ["-C", worktreePath, "remote", "get-url", "origin"], {
60
68
  stdio: "pipe",
61
69
  encoding: "utf-8"
62
- }).trim();
70
+ })).trim();
63
71
  if (isPlausibleRemoteUrl(currentUrl)) return;
64
72
  console.error(`[worktree] Invalid origin URL in ${worktreePath}: "${currentUrl}" \u2014 fixing`);
65
73
  execFileSync("git", ["-C", worktreePath, "remote", "remove", "origin"], {
66
74
  stdio: "pipe"
67
75
  });
68
76
  }
69
- const originUrl = execFileSync("git", ["remote", "get-url", "origin"], {
77
+ const originUrl = normalizeEol(execFileSync("git", ["remote", "get-url", "origin"], {
70
78
  stdio: "pipe",
71
79
  encoding: "utf-8"
72
- }).trim();
80
+ })).trim();
73
81
  if (originUrl && isPlausibleRemoteUrl(originUrl)) {
74
82
  execFileSync("git", ["-C", worktreePath, "remote", "add", "origin", originUrl], {
75
83
  stdio: "pipe"
@@ -114,7 +122,7 @@ async function findWorktreeForBranch(exec, branch) {
114
122
  const { stdout } = await execPromise(exec, "git", ["worktree", "list", "--porcelain"]);
115
123
  const targetRef = `refs/heads/${branch}`;
116
124
  let currentPath = null;
117
- for (const line of stdout.split("\n")) {
125
+ for (const line of normalizeEol(stdout).split("\n")) {
118
126
  if (line.startsWith("worktree ")) currentPath = line.slice("worktree ".length);
119
127
  if (line.startsWith("branch ") && line.slice("branch ".length) === targetRef && currentPath) {
120
128
  return currentPath;
@@ -2116,7 +2124,7 @@ var RunMemory = class {
2116
2124
  }
2117
2125
  try {
2118
2126
  const content = await this.deps.getDocument(this._documentId);
2119
- const lines = content.split("\n");
2127
+ const lines = normalizeEol(content).split("\n");
2120
2128
  if (lines.length > DEFAULT_COMPACTION_THRESHOLD) {
2121
2129
  const truncated = lines.slice(-DEFAULT_COMPACTION_THRESHOLD);
2122
2130
  return "[Run memory truncated \u2014 compaction needed]\n" + truncated.join("\n");
@@ -2137,7 +2145,7 @@ var RunMemory = class {
2137
2145
  try {
2138
2146
  const content = await this.deps.getDocument(this._documentId);
2139
2147
  if (!content) return false;
2140
- return content.split("\n").length > threshold;
2148
+ return normalizeEol(content).split("\n").length > threshold;
2141
2149
  } catch {
2142
2150
  return false;
2143
2151
  }
@@ -2768,13 +2776,16 @@ function isAlive(pid) {
2768
2776
  function readManifest() {
2769
2777
  try {
2770
2778
  return fs.readFileSync(MANIFEST_PATH, 'utf-8')
2771
- .split('\\n')
2779
+ .split(/\\r?\\n/)
2772
2780
  .map(l => parseInt(l.trim(), 10))
2773
2781
  .filter(p => !isNaN(p) && p > 0);
2774
2782
  } catch { return []; }
2775
2783
  }
2776
2784
 
2777
2785
  function killPid(pid, signal) {
2786
+ if (process.platform === 'win32') {
2787
+ try { require('child_process').execFileSync('taskkill', ['/pid', String(pid), '/t', '/f'], { stdio: 'pipe' }); return; } catch { /* fall through */ }
2788
+ }
2778
2789
  try { process.kill(pid, signal); } catch { /* already dead */ }
2779
2790
  }
2780
2791
 
@@ -2782,14 +2793,14 @@ function cleanup() {
2782
2793
  const pids = readManifest();
2783
2794
  for (const pid of pids) {
2784
2795
  killPid(pid, 'SIGTERM');
2785
- try { process.kill(-pid, 'SIGTERM'); } catch { /* ignore */ }
2796
+ if (process.platform !== 'win32') { try { process.kill(-pid, 'SIGTERM'); } catch { /* ignore */ } }
2786
2797
  }
2787
2798
 
2788
2799
  setTimeout(() => {
2789
2800
  for (const pid of pids) {
2790
2801
  if (isAlive(pid)) {
2791
2802
  killPid(pid, 'SIGKILL');
2792
- try { process.kill(-pid, 'SIGKILL'); } catch { /* ignore */ }
2803
+ if (process.platform !== 'win32') { try { process.kill(-pid, 'SIGKILL'); } catch { /* ignore */ } }
2793
2804
  }
2794
2805
  }
2795
2806
 
@@ -2833,7 +2844,8 @@ function spawnReaper(config) {
2833
2844
  const script = buildReaperScript(config);
2834
2845
  const child = spawn(process.execPath, ["-e", script], {
2835
2846
  detached: true,
2836
- stdio: "ignore"
2847
+ stdio: "ignore",
2848
+ windowsHide: true
2837
2849
  });
2838
2850
  child.unref();
2839
2851
  if (child.pid) {
@@ -2844,7 +2856,7 @@ function spawnReaper(config) {
2844
2856
  function killReaper(reaperPidPath) {
2845
2857
  try {
2846
2858
  const pid = parseInt(
2847
- readFileSync(reaperPidPath, "utf-8").trim(),
2859
+ readFileSync(reaperPidPath, "utf-8").replace(/\r\n/g, "\n").trim(),
2848
2860
  10
2849
2861
  );
2850
2862
  if (pid && !isNaN(pid)) {
@@ -3153,7 +3165,7 @@ var ProviderRegistry = class {
3153
3165
 
3154
3166
  // src/providers/codex-provider.ts
3155
3167
  import { spawn as spawn2, execFileSync as execFileSync2 } from "child_process";
3156
- import { existsSync } from "fs";
3168
+ import { existsSync, rmSync as rmSync2 } from "fs";
3157
3169
 
3158
3170
  // src/providers/codex-jsonl-parser.ts
3159
3171
  var CodexJsonlParser = class {
@@ -3311,7 +3323,7 @@ var CodexProvider = class {
3311
3323
  });
3312
3324
  } catch {
3313
3325
  try {
3314
- execFileSync2("rm", ["-rf", request.workingDirectory], { stdio: "pipe" });
3326
+ rmSync2(request.workingDirectory, { recursive: true, force: true });
3315
3327
  execFileSync2("git", ["worktree", "add", "-b", request.workingDirectory, request.workingDirectory, "HEAD"], {
3316
3328
  stdio: "pipe"
3317
3329
  });
@@ -3331,9 +3343,12 @@ var CodexProvider = class {
3331
3343
  }
3332
3344
  }
3333
3345
  return new Promise((resolve) => {
3334
- const child = spawn2("codex", args, {
3346
+ const [cmd, prefixArgs] = resolveCommand("codex");
3347
+ const resolvedArgs = [...prefixArgs, ...args];
3348
+ const child = spawn2(cmd, resolvedArgs, {
3335
3349
  stdio: ["ignore", "pipe", "pipe"],
3336
- env: { ...process.env, ...env }
3350
+ env: { ...process.env, ...env },
3351
+ ...prefixArgs.length > 0 ? {} : crossSpawnOptions()
3337
3352
  });
3338
3353
  const parser = new CodexJsonlParser();
3339
3354
  parser.onEvent = (event) => request.onStreamEvent?.(event);
@@ -3359,10 +3374,7 @@ var CodexProvider = class {
3359
3374
  } catch {
3360
3375
  }
3361
3376
  killTimer = setTimeout(() => {
3362
- try {
3363
- child.kill("SIGKILL");
3364
- } catch {
3365
- }
3377
+ if (child.pid) killProcessTree(child.pid, "SIGKILL");
3366
3378
  }, 5e3);
3367
3379
  }, CODEX_TIMEOUT_MS);
3368
3380
  let resolved = false;
@@ -3451,7 +3463,7 @@ var CodexProvider = class {
3451
3463
 
3452
3464
  // src/providers/gemini-provider.ts
3453
3465
  import { spawn as spawn3, execFileSync as execFileSync3 } from "child_process";
3454
- import { existsSync as existsSync2, writeFileSync as writeFileSync3, mkdirSync as mkdirSync2 } from "fs";
3466
+ import { existsSync as existsSync2, writeFileSync as writeFileSync3, mkdirSync as mkdirSync2, rmSync as rmSync3 } from "fs";
3455
3467
  import { join as join2, dirname } from "path";
3456
3468
  import { homedir } from "os";
3457
3469
  import { fileURLToPath } from "url";
@@ -3681,7 +3693,7 @@ var GeminiProvider = class _GeminiProvider {
3681
3693
  });
3682
3694
  } catch {
3683
3695
  try {
3684
- execFileSync3("rm", ["-rf", request.workingDirectory], { stdio: "pipe" });
3696
+ rmSync3(request.workingDirectory, { recursive: true, force: true });
3685
3697
  execFileSync3("git", ["worktree", "add", "-b", request.workingDirectory, request.workingDirectory, "HEAD"], {
3686
3698
  stdio: "pipe"
3687
3699
  });
@@ -3706,9 +3718,12 @@ var GeminiProvider = class _GeminiProvider {
3706
3718
  this.copySettingsToDir(settingsDir, request.workingDirectory);
3707
3719
  }
3708
3720
  return new Promise((resolve) => {
3709
- const child = spawn3("gemini", args, {
3721
+ const [cmd, prefixArgs] = resolveCommand("gemini");
3722
+ const resolvedArgs = [...prefixArgs, ...args];
3723
+ const child = spawn3(cmd, resolvedArgs, {
3710
3724
  stdio: ["pipe", "pipe", "pipe"],
3711
- cwd: cwd || void 0
3725
+ cwd: cwd || void 0,
3726
+ ...prefixArgs.length > 0 ? {} : crossSpawnOptions()
3712
3727
  });
3713
3728
  const parser = new GeminiJsonlParser();
3714
3729
  parser.onEvent = (event) => request.onStreamEvent?.(event);
@@ -3724,7 +3739,7 @@ var GeminiProvider = class _GeminiProvider {
3724
3739
  child.stderr?.on("data", (chunk) => {
3725
3740
  const text = chunk.toString();
3726
3741
  stderr += text;
3727
- const lines = text.split("\n").filter((l) => l.trim());
3742
+ const lines = normalizeEol(text).split("\n").filter((l) => l.trim());
3728
3743
  if (lines.length) lastStderrLine = lines[lines.length - 1];
3729
3744
  });
3730
3745
  child.stdin?.end();
@@ -3754,10 +3769,7 @@ var GeminiProvider = class _GeminiProvider {
3754
3769
  } catch {
3755
3770
  }
3756
3771
  killTimer = setTimeout(() => {
3757
- try {
3758
- child.kill("SIGKILL");
3759
- } catch {
3760
- }
3772
+ if (child.pid) killProcessTree(child.pid, "SIGKILL");
3761
3773
  }, 5e3);
3762
3774
  }, GEMINI_TIMEOUT_MS);
3763
3775
  let resolved = false;
@@ -3841,14 +3853,16 @@ var GeminiProvider = class _GeminiProvider {
3841
3853
  hookConfig.allowedTools = tr.allowedTools ? translateToolNames(tr.allowedTools) : null;
3842
3854
  hookConfig.disallowedTools = tr.disallowedTools ? translateToolNames(tr.disallowedTools) : null;
3843
3855
  if (tr.tools !== void 0) hookConfig.builtinToolsMode = tr.tools;
3844
- const wrapperPath = join2(dir, ".kantban-hook-before-tool.sh");
3856
+ const ext = IS_WINDOWS ? ".cmd" : ".sh";
3857
+ const wrapperPath = join2(dir, `.kantban-hook-before-tool${ext}`);
3845
3858
  writeFileSync3(wrapperPath, this.generateHookWrapper(hookPath, "BeforeToolSelection", join2(dir, ".kantban-hook-config.json")), { mode: 493 });
3846
3859
  hooks.BeforeToolSelection = [{ matcher: "*", hooks: [{ type: "command", command: wrapperPath, timeout: 3e4 }] }];
3847
3860
  }
3848
3861
  if (request.maxTurns && request.mcpConfig) {
3849
3862
  hookConfig.maxTurns = request.maxTurns;
3850
3863
  hookConfig.turnFile = join2(dir, ".kantban-turn-counter");
3851
- const wrapperPath = join2(dir, ".kantban-hook-after-agent.sh");
3864
+ const ext = IS_WINDOWS ? ".cmd" : ".sh";
3865
+ const wrapperPath = join2(dir, `.kantban-hook-after-agent${ext}`);
3852
3866
  writeFileSync3(wrapperPath, this.generateHookWrapper(hookPath, "AfterAgent", join2(dir, ".kantban-hook-config.json")), { mode: 493 });
3853
3867
  hooks.AfterAgent = [{ matcher: "*", hooks: [{ type: "command", command: wrapperPath, timeout: 3e4 }] }];
3854
3868
  }
@@ -3881,6 +3895,18 @@ var GeminiProvider = class _GeminiProvider {
3881
3895
  /** Generate a per-session bash wrapper that invokes the Node hook script.
3882
3896
  * Gemini CLI ignores `args` in hook definitions, so we embed all arguments. */
3883
3897
  generateHookWrapper(hookScript, event, configFile) {
3898
+ if (IS_WINDOWS) {
3899
+ return [
3900
+ "@echo off",
3901
+ 'set "STDIN_FILE=%TEMP%\\kantban-hook-%RANDOM%%RANDOM%.tmp"',
3902
+ 'findstr "^" > "%STDIN_FILE%"',
3903
+ `node "${hookScript}" "${event}" "${configFile}" < "%STDIN_FILE%"`,
3904
+ 'set "EXIT_CODE=%ERRORLEVEL%"',
3905
+ 'del /f /q "%STDIN_FILE%" >nul 2>&1',
3906
+ "exit /b %EXIT_CODE%",
3907
+ ""
3908
+ ].join("\r\n");
3909
+ }
3884
3910
  return `#!/bin/bash
3885
3911
  # KantBan pipeline hook wrapper \u2014 Gemini CLI does not pass args, so we embed them.
3886
3912
  STDIN_FILE=$(mktemp)
@@ -3894,7 +3920,7 @@ node "${hookScript}" "${event}" "${configFile}" < "$STDIN_FILE"
3894
3920
  * which contains lines like "YOLO mode is enabled..." and "Loaded cached credentials."
3895
3921
  * that corrupt JSON parsing in downstream consumers (advisor, light-call). */
3896
3922
  static stripCliPreamble(text) {
3897
- const lines = text.split("\n");
3923
+ const lines = normalizeEol(text).split("\n");
3898
3924
  const jsonStart = lines.findIndex((line) => {
3899
3925
  const trimmed = line.trim();
3900
3926
  return trimmed.startsWith("{") || trimmed.startsWith("[");
@@ -4057,7 +4083,7 @@ function childManifestPath(boardId) {
4057
4083
  }
4058
4084
  function readChildManifest(boardId) {
4059
4085
  try {
4060
- const contents = readFileSync3(childManifestPath(boardId), "utf-8");
4086
+ const contents = normalizeEol(readFileSync3(childManifestPath(boardId), "utf-8"));
4061
4087
  return contents.split("\n").map((l) => l.trim()).filter((l) => l !== "").map(Number).filter((n) => !isNaN(n) && n > 0);
4062
4088
  } catch {
4063
4089
  return [];
@@ -4077,7 +4103,7 @@ function cleanupOrphanedProcesses(boardId) {
4077
4103
  if (stalePid && stalePid !== process.pid) {
4078
4104
  try {
4079
4105
  process.kill(stalePid, 0);
4080
- process.kill(stalePid, "SIGTERM");
4106
+ killProcessTree(stalePid, "SIGTERM");
4081
4107
  console.log(`Killed stale orchestrator (PID ${String(stalePid)})`);
4082
4108
  } catch {
4083
4109
  }
@@ -4091,7 +4117,7 @@ function cleanupOrphanedProcesses(boardId) {
4091
4117
  for (const pid of manifestPids) {
4092
4118
  try {
4093
4119
  process.kill(pid, 0);
4094
- process.kill(pid, "SIGTERM");
4120
+ killProcessTree(pid, "SIGTERM");
4095
4121
  } catch {
4096
4122
  }
4097
4123
  }
@@ -4105,7 +4131,7 @@ function cleanupOrphanedProcesses(boardId) {
4105
4131
  if (reaperPid && !isNaN(reaperPid)) {
4106
4132
  try {
4107
4133
  process.kill(reaperPid, 0);
4108
- process.kill(reaperPid, "SIGTERM");
4134
+ killProcessTree(reaperPid, "SIGTERM");
4109
4135
  console.log(`Killed stale reaper (PID ${String(reaperPid)})`);
4110
4136
  } catch {
4111
4137
  }
@@ -4143,7 +4169,7 @@ function waitForConfirmation() {
4143
4169
  }
4144
4170
  async function runPipeline(client, args) {
4145
4171
  if (args[0] === "init") {
4146
- const { runPipelineInit } = await import("./pipeline-init-IGZZOOLK.js");
4172
+ const { runPipelineInit } = await import("./pipeline-init-QDHBJGWY.js");
4147
4173
  await runPipelineInit();
4148
4174
  return;
4149
4175
  }
@@ -4257,7 +4283,7 @@ async function runPipeline(client, args) {
4257
4283
  // Run memory enrichment — inject accumulated discoveries into each iteration prompt
4258
4284
  fetchRunMemoryContent: mem ? async () => {
4259
4285
  const content = await mem.getContent();
4260
- const lines = content.split("\n");
4286
+ const lines = normalizeEol(content).split("\n");
4261
4287
  if (lines.length > 500) {
4262
4288
  return lines.slice(-500).join("\n");
4263
4289
  }
@@ -4671,12 +4697,14 @@ Received ${signal}. Shutting down gracefully...`);
4671
4697
  process.exit(1);
4672
4698
  });
4673
4699
  });
4674
- process.on("SIGHUP", () => {
4675
- shutdown("SIGHUP").catch((err) => {
4676
- console.error("Error during shutdown:", err);
4677
- process.exit(1);
4700
+ if (!IS_WINDOWS) {
4701
+ process.on("SIGHUP", () => {
4702
+ shutdown("SIGHUP").catch((err) => {
4703
+ console.error("Error during shutdown:", err);
4704
+ process.exit(1);
4705
+ });
4678
4706
  });
4679
- });
4707
+ }
4680
4708
  cleanupOrphanedProcesses(opts.boardId);
4681
4709
  writePidFile(opts.boardId);
4682
4710
  logger.orchestrator(`PID file written: ${String(process.pid)}`);
@@ -4855,13 +4883,8 @@ async function stopPipeline(args) {
4855
4883
  return;
4856
4884
  }
4857
4885
  try {
4858
- try {
4859
- process.kill(-pid, "SIGTERM");
4860
- console.log(`Sent SIGTERM to pipeline process group (pgid ${String(pid)}) for board ${boardId}.`);
4861
- } catch {
4862
- process.kill(pid, "SIGTERM");
4863
- console.log(`Sent SIGTERM to pipeline process ${String(pid)} for board ${boardId}.`);
4864
- }
4886
+ killProcessTree(pid, "SIGTERM");
4887
+ console.log(`Sent SIGTERM to pipeline process ${String(pid)} for board ${boardId}.`);
4865
4888
  } catch (err) {
4866
4889
  const message = err instanceof Error ? err.message : String(err);
4867
4890
  console.error(`Failed to stop pipeline (PID ${String(pid)}): ${message}`);
@@ -4874,4 +4897,4 @@ export {
4874
4897
  runPipeline,
4875
4898
  stopPipeline
4876
4899
  };
4877
- //# sourceMappingURL=pipeline-ZI7Y25IA.js.map
4900
+ //# sourceMappingURL=pipeline-76ZTI4IN.js.map