@jvittechs/jai1-cli 0.1.96 → 0.1.97

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 (135) hide show
  1. package/dist/cli.js +658 -119
  2. package/dist/cli.js.map +1 -1
  3. package/dist/web-chat/README.md +418 -0
  4. package/dist/web-chat/app.js +1527 -0
  5. package/dist/web-chat/index.html +324 -0
  6. package/dist/web-chat/node_modules/.bin/0ecdsa-generate-keypair +15 -0
  7. package/dist/web-chat/node_modules/.bin/0gentesthtml +89 -0
  8. package/dist/web-chat/node_modules/.bin/0serve +97 -0
  9. package/dist/web-chat/node_modules/.bin/acorn +4 -0
  10. package/dist/web-chat/node_modules/.bin/autoprefixer +22 -0
  11. package/dist/web-chat/node_modules/.bin/baseline-browser-mapping +2 -0
  12. package/dist/web-chat/node_modules/.bin/browserslist +156 -0
  13. package/dist/web-chat/node_modules/.bin/esbuild +0 -0
  14. package/dist/web-chat/node_modules/.bin/jiti +34 -0
  15. package/dist/web-chat/node_modules/.bin/js-yaml +126 -0
  16. package/dist/web-chat/node_modules/.bin/jsesc +148 -0
  17. package/dist/web-chat/node_modules/.bin/json5 +152 -0
  18. package/dist/web-chat/node_modules/.bin/katex +112 -0
  19. package/dist/web-chat/node_modules/.bin/loose-envify +16 -0
  20. package/dist/web-chat/node_modules/.bin/lz-string +13 -0
  21. package/dist/web-chat/node_modules/.bin/nanoid +55 -0
  22. package/dist/web-chat/node_modules/.bin/parser +15 -0
  23. package/dist/web-chat/node_modules/.bin/prebuild-install +78 -0
  24. package/dist/web-chat/node_modules/.bin/rc +4 -0
  25. package/dist/web-chat/node_modules/.bin/rollup +1912 -0
  26. package/dist/web-chat/node_modules/.bin/semver +174 -0
  27. package/dist/web-chat/node_modules/.bin/tsc +2 -0
  28. package/dist/web-chat/node_modules/.bin/tsserver +2 -0
  29. package/dist/web-chat/node_modules/.bin/update-browserslist-db +42 -0
  30. package/dist/web-chat/node_modules/.bin/uuid +2 -0
  31. package/dist/web-chat/node_modules/.bin/uvu +35 -0
  32. package/dist/web-chat/node_modules/.bin/vite +61 -0
  33. package/dist/web-chat/node_modules/.package-lock.json +15371 -0
  34. package/dist/web-chat/node_modules/.vite/deps/_metadata.json +67 -0
  35. package/dist/web-chat/node_modules/.vite/deps/chunk-DC5AMYBS.js +39 -0
  36. package/dist/web-chat/node_modules/.vite/deps/chunk-DC5AMYBS.js.map +7 -0
  37. package/dist/web-chat/node_modules/.vite/deps/chunk-NUMECXU6.js +21628 -0
  38. package/dist/web-chat/node_modules/.vite/deps/chunk-NUMECXU6.js.map +7 -0
  39. package/dist/web-chat/node_modules/.vite/deps/chunk-RLJ2RCJQ.js +1906 -0
  40. package/dist/web-chat/node_modules/.vite/deps/chunk-RLJ2RCJQ.js.map +7 -0
  41. package/dist/web-chat/node_modules/.vite/deps/chunk-S725DACQ.js +928 -0
  42. package/dist/web-chat/node_modules/.vite/deps/chunk-S725DACQ.js.map +7 -0
  43. package/dist/web-chat/node_modules/.vite/deps/chunk-XJVUMEYI.js +8578 -0
  44. package/dist/web-chat/node_modules/.vite/deps/chunk-XJVUMEYI.js.map +7 -0
  45. package/dist/web-chat/node_modules/.vite/deps/package.json +3 -0
  46. package/dist/web-chat/node_modules/.vite/deps/react-dom.js +7 -0
  47. package/dist/web-chat/node_modules/.vite/deps/react-dom.js.map +7 -0
  48. package/dist/web-chat/node_modules/.vite/deps/react-dom_client.js +39 -0
  49. package/dist/web-chat/node_modules/.vite/deps/react-dom_client.js.map +7 -0
  50. package/dist/web-chat/node_modules/.vite/deps/react-markdown.js +8931 -0
  51. package/dist/web-chat/node_modules/.vite/deps/react-markdown.js.map +7 -0
  52. package/dist/web-chat/node_modules/.vite/deps/react.js +6 -0
  53. package/dist/web-chat/node_modules/.vite/deps/react.js.map +7 -0
  54. package/dist/web-chat/node_modules/.vite/deps/react_jsx-dev-runtime.js +913 -0
  55. package/dist/web-chat/node_modules/.vite/deps/react_jsx-dev-runtime.js.map +7 -0
  56. package/dist/web-chat/node_modules/.vite/deps/react_jsx-runtime.js +7 -0
  57. package/dist/web-chat/node_modules/.vite/deps/react_jsx-runtime.js.map +7 -0
  58. package/dist/web-chat/node_modules/.vite/deps/remark-gfm.js +7403 -0
  59. package/dist/web-chat/node_modules/.vite/deps/remark-gfm.js.map +7 -0
  60. package/dist/web-chat/node_modules/@codemirror/autocomplete/.github/workflows/dispatch.yml +16 -0
  61. package/dist/web-chat/node_modules/@codemirror/commands/.github/workflows/dispatch.yml +16 -0
  62. package/dist/web-chat/node_modules/@codemirror/lang-cpp/.github/workflows/dispatch.yml +16 -0
  63. package/dist/web-chat/node_modules/@codemirror/lang-css/.github/workflows/dispatch.yml +16 -0
  64. package/dist/web-chat/node_modules/@codemirror/lang-go/.github/workflows/dispatch.yml +16 -0
  65. package/dist/web-chat/node_modules/@codemirror/lang-html/.github/workflows/dispatch.yml +16 -0
  66. package/dist/web-chat/node_modules/@codemirror/lang-java/.github/workflows/dispatch.yml +16 -0
  67. package/dist/web-chat/node_modules/@codemirror/lang-javascript/.github/workflows/dispatch.yml +16 -0
  68. package/dist/web-chat/node_modules/@codemirror/lang-json/.github/workflows/dispatch.yml +16 -0
  69. package/dist/web-chat/node_modules/@codemirror/lang-less/.github/workflows/dispatch.yml +16 -0
  70. package/dist/web-chat/node_modules/@codemirror/lang-markdown/.github/workflows/dispatch.yml +16 -0
  71. package/dist/web-chat/node_modules/@codemirror/lang-python/.github/workflows/dispatch.yml +16 -0
  72. package/dist/web-chat/node_modules/@codemirror/lang-rust/.github/workflows/dispatch.yml +16 -0
  73. package/dist/web-chat/node_modules/@codemirror/lang-sass/.github/workflows/dispatch.yml +16 -0
  74. package/dist/web-chat/node_modules/@codemirror/lang-sql/.github/workflows/dispatch.yml +16 -0
  75. package/dist/web-chat/node_modules/@codemirror/lang-wast/.github/workflows/dispatch.yml +16 -0
  76. package/dist/web-chat/node_modules/@codemirror/lang-xml/.github/workflows/dispatch.yml +16 -0
  77. package/dist/web-chat/node_modules/@codemirror/lang-yaml/.github/workflows/dispatch.yml +16 -0
  78. package/dist/web-chat/node_modules/@codemirror/language/.github/workflows/dispatch.yml +16 -0
  79. package/dist/web-chat/node_modules/@codemirror/language-data/.github/workflows/dispatch.yml +16 -0
  80. package/dist/web-chat/node_modules/@codemirror/legacy-modes/.github/workflows/dispatch.yml +16 -0
  81. package/dist/web-chat/node_modules/@codemirror/lint/.github/workflows/dispatch.yml +16 -0
  82. package/dist/web-chat/node_modules/@codemirror/search/.github/workflows/dispatch.yml +16 -0
  83. package/dist/web-chat/node_modules/@codemirror/state/.github/workflows/dispatch.yml +16 -0
  84. package/dist/web-chat/node_modules/@codemirror/theme-one-dark/.github/workflows/dispatch.yml +16 -0
  85. package/dist/web-chat/node_modules/@codemirror/view/.github/workflows/dispatch.yml +16 -0
  86. package/dist/web-chat/node_modules/@lezer/html/src/.tern-port +1 -0
  87. package/dist/web-chat/node_modules/@lezer/php/.tern-port +1 -0
  88. package/dist/web-chat/node_modules/@lezer/php/test/.tern-port +1 -0
  89. package/dist/web-chat/node_modules/@lezer/sass/src/.tern-port +1 -0
  90. package/dist/web-chat/node_modules/@lezer/yaml/.tern-port +1 -0
  91. package/dist/web-chat/node_modules/@ungap/structured-clone/.github/workflows/node.js.yml +31 -0
  92. package/dist/web-chat/node_modules/bl/.travis.yml +17 -0
  93. package/dist/web-chat/node_modules/clean-set/.prettierrc +4 -0
  94. package/dist/web-chat/node_modules/clean-set/.travis.yml +7 -0
  95. package/dist/web-chat/node_modules/codemirror/.github/workflows/dispatch.yml +16 -0
  96. package/dist/web-chat/node_modules/entities/src/generated/.eslintrc.json +10 -0
  97. package/dist/web-chat/node_modules/es5-ext/promise/.eslintrc.json +1 -0
  98. package/dist/web-chat/node_modules/es6-iterator/.editorconfig +14 -0
  99. package/dist/web-chat/node_modules/es6-iterator/test/.eslintrc.json +5 -0
  100. package/dist/web-chat/node_modules/es6-symbol/.testignore +1 -0
  101. package/dist/web-chat/node_modules/escape-carriage/.github/workflows/node.js.yml +19 -0
  102. package/dist/web-chat/node_modules/esniff/.prettierignore +2 -0
  103. package/dist/web-chat/node_modules/esniff/.testignore +3 -0
  104. package/dist/web-chat/node_modules/event-emitter/.lint +15 -0
  105. package/dist/web-chat/node_modules/event-emitter/.testignore +1 -0
  106. package/dist/web-chat/node_modules/event-emitter/.travis.yml +16 -0
  107. package/dist/web-chat/node_modules/expand-template/.travis.yml +6 -0
  108. package/dist/web-chat/node_modules/extend/.editorconfig +20 -0
  109. package/dist/web-chat/node_modules/extend/.eslintrc +17 -0
  110. package/dist/web-chat/node_modules/extend/.jscs.json +175 -0
  111. package/dist/web-chat/node_modules/extend/.travis.yml +230 -0
  112. package/dist/web-chat/node_modules/gensync/test/.babelrc +5 -0
  113. package/dist/web-chat/node_modules/github-from-package/.travis.yml +4 -0
  114. package/dist/web-chat/node_modules/intersection-observer/.eslintrc +45 -0
  115. package/dist/web-chat/node_modules/lib0/.github/workflows/node.js.yml +24 -0
  116. package/dist/web-chat/node_modules/lib0/.jsdoc.json +18 -0
  117. package/dist/web-chat/node_modules/lib0/.vscode/launch.json +17 -0
  118. package/dist/web-chat/node_modules/minimist/.eslintrc +29 -0
  119. package/dist/web-chat/node_modules/minimist/.github/FUNDING.yml +12 -0
  120. package/dist/web-chat/node_modules/minimist/.nycrc +14 -0
  121. package/dist/web-chat/node_modules/napi-build-utils/.github/workflows/run-npm-tests.yml +31 -0
  122. package/dist/web-chat/node_modules/next-tick/.editorconfig +16 -0
  123. package/dist/web-chat/node_modules/next-tick/.github/FUNDING.yml +1 -0
  124. package/dist/web-chat/node_modules/next-tick/.lint +16 -0
  125. package/dist/web-chat/node_modules/node-abi/node_modules/.bin/semver +191 -0
  126. package/dist/web-chat/node_modules/pump/.github/FUNDING.yml +2 -0
  127. package/dist/web-chat/node_modules/pump/.travis.yml +5 -0
  128. package/dist/web-chat/node_modules/simple-concat/.travis.yml +3 -0
  129. package/dist/web-chat/node_modules/simple-get/.github/dependabot.yml +15 -0
  130. package/dist/web-chat/node_modules/simple-get/.github/workflows/ci.yml +23 -0
  131. package/dist/web-chat/node_modules/tar-fs/.travis.yml +6 -0
  132. package/dist/web-chat/node_modules/tar-fs/test/fixtures/e/directory/.ignore +0 -0
  133. package/dist/web-chat/node_modules/w3c-keyname/.tern-port +1 -0
  134. package/dist/web-chat/style.css +1882 -0
  135. package/package.json +4 -2
package/dist/cli.js CHANGED
@@ -33,7 +33,7 @@ var NetworkError = class extends Jai1Error {
33
33
  // package.json
34
34
  var package_default = {
35
35
  name: "@jvittechs/jai1-cli",
36
- version: "0.1.96",
36
+ version: "0.1.97",
37
37
  description: "A unified CLI tool for JV-IT TECHS developers to manage Jai1 Framework. Please contact TeamAI for usage instructions.",
38
38
  type: "module",
39
39
  bin: {
@@ -70,7 +70,8 @@ var package_default = {
70
70
  "coding"
71
71
  ],
72
72
  scripts: {
73
- build: "tsup src/cli.ts --dts --format esm --target node22 --out-dir dist --sourcemap",
73
+ build: "tsup src/cli.ts --dts --format esm --target node22 --out-dir dist --sourcemap && npm run copy-web-chat",
74
+ "copy-web-chat": "mkdir -p dist/web-chat && cp -r src/web-chat/* dist/web-chat/",
74
75
  dev: "tsx src/cli.ts --help",
75
76
  lint: "eslint .",
76
77
  test: "vitest run",
@@ -92,6 +93,7 @@ var package_default = {
92
93
  "ink-text-input": "^6.0.0",
93
94
  marked: "^12.0.0",
94
95
  "marked-terminal": "^7.0.0",
96
+ open: "^10.1.0",
95
97
  "p-limit": "^5.0.0",
96
98
  "p-queue": "^7.4.1",
97
99
  "p-retry": "^6.2.0",
@@ -798,6 +800,9 @@ var IDE_MIGRATION_CONFIGS = {
798
800
  workflowsPath: null,
799
801
  commandsPath: "commands",
800
802
  fileExtension: ".mdc",
803
+ // For rules
804
+ workflowsExtension: ".md",
805
+ // For workflows/commands
801
806
  generateFrontmatter: (opts) => {
802
807
  const lines = ["---"];
803
808
  if (opts.description) {
@@ -1112,21 +1117,24 @@ ${reference}
1112
1117
  */
1113
1118
  getTargetPath(config, item) {
1114
1119
  let contentPath = null;
1120
+ let extension = config.fileExtension;
1115
1121
  switch (item.type) {
1116
1122
  case "rules":
1117
1123
  contentPath = config.rulesPath;
1118
1124
  break;
1119
1125
  case "workflows":
1120
1126
  contentPath = config.workflowsPath ?? config.commandsPath;
1127
+ extension = config.workflowsExtension ?? config.fileExtension;
1121
1128
  break;
1122
1129
  case "commands":
1123
1130
  contentPath = config.commandsPath;
1131
+ extension = config.workflowsExtension ?? config.fileExtension;
1124
1132
  break;
1125
1133
  }
1126
1134
  if (!contentPath) {
1127
1135
  return null;
1128
1136
  }
1129
- const filename = `${item.name}${config.fileExtension}`;
1137
+ const filename = `${item.name}${extension}`;
1130
1138
  return path.join(this.projectPath, config.basePath, contentPath, filename);
1131
1139
  }
1132
1140
  // Helper methods
@@ -3381,9 +3389,9 @@ var IdeDetectionService = class {
3381
3389
  /**
3382
3390
  * Check if a path exists
3383
3391
  */
3384
- async pathExists(path8) {
3392
+ async pathExists(path9) {
3385
3393
  try {
3386
- await fs7.access(path8);
3394
+ await fs7.access(path9);
3387
3395
  return true;
3388
3396
  } catch {
3389
3397
  return false;
@@ -4578,15 +4586,19 @@ var LlmProxyService = class {
4578
4586
  }
4579
4587
  const reader = response.body.getReader();
4580
4588
  const decoder = new TextDecoder();
4589
+ let buffer = "";
4581
4590
  try {
4582
4591
  while (true) {
4583
4592
  const { done, value } = await reader.read();
4584
4593
  if (done) break;
4585
- const chunk = decoder.decode(value, { stream: true });
4586
- const lines = chunk.split("\n").filter((line) => line.trim() !== "");
4594
+ buffer += decoder.decode(value, { stream: true });
4595
+ const lines = buffer.split("\n");
4596
+ buffer = lines.pop() || "";
4587
4597
  for (const line of lines) {
4588
- if (line.startsWith("data: ")) {
4589
- const data = line.slice(6);
4598
+ const trimmedLine = line.trim();
4599
+ if (!trimmedLine) continue;
4600
+ if (trimmedLine.startsWith("data: ")) {
4601
+ const data = trimmedLine.slice(6);
4590
4602
  if (data === "[DONE]") {
4591
4603
  return;
4592
4604
  }
@@ -4601,6 +4613,22 @@ var LlmProxyService = class {
4601
4613
  }
4602
4614
  }
4603
4615
  }
4616
+ if (buffer.trim()) {
4617
+ const trimmedLine = buffer.trim();
4618
+ if (trimmedLine.startsWith("data: ")) {
4619
+ const data = trimmedLine.slice(6);
4620
+ if (data !== "[DONE]") {
4621
+ try {
4622
+ const parsed = JSON.parse(data);
4623
+ const content = parsed.choices[0]?.delta?.content;
4624
+ if (content) {
4625
+ yield content;
4626
+ }
4627
+ } catch (e) {
4628
+ }
4629
+ }
4630
+ }
4631
+ }
4604
4632
  } finally {
4605
4633
  reader.releaseLock();
4606
4634
  }
@@ -5083,8 +5111,469 @@ var ChatApp = ({ service, initialModel }) => {
5083
5111
  ), /* @__PURE__ */ React25.createElement(Box16, { borderStyle: "single", borderColor: "gray", paddingX: 1 }, /* @__PURE__ */ React25.createElement(Text17, { dimColor: true }, footer)));
5084
5112
  };
5085
5113
 
5114
+ // src/server/web-chat-server.ts
5115
+ import http from "http";
5116
+ import fs8 from "fs";
5117
+ import path4 from "path";
5118
+ import { fileURLToPath } from "url";
5119
+
5120
+ // src/server/session.ts
5121
+ import crypto from "crypto";
5122
+ var SESSION_PREFIX = "wc_";
5123
+ var DEFAULT_TTL_MINUTES = 30;
5124
+ var SessionManager = class {
5125
+ sessions = /* @__PURE__ */ new Map();
5126
+ cleanupInterval = null;
5127
+ constructor() {
5128
+ this.startCleanup();
5129
+ }
5130
+ /**
5131
+ * Generate a new session token
5132
+ */
5133
+ generateToken() {
5134
+ const randomBytes = crypto.randomBytes(16).toString("hex");
5135
+ return `${SESSION_PREFIX}${randomBytes}`;
5136
+ }
5137
+ /**
5138
+ * Create a new session
5139
+ */
5140
+ createSession(config) {
5141
+ const token = this.generateToken();
5142
+ const now = /* @__PURE__ */ new Date();
5143
+ const ttlMinutes = config.ttlMinutes ?? DEFAULT_TTL_MINUTES;
5144
+ const session = {
5145
+ token,
5146
+ accessKey: config.accessKey,
5147
+ apiUrl: config.apiUrl,
5148
+ createdAt: now,
5149
+ expiresAt: new Date(now.getTime() + ttlMinutes * 60 * 1e3),
5150
+ lastActivity: now
5151
+ };
5152
+ this.sessions.set(token, session);
5153
+ return session;
5154
+ }
5155
+ /**
5156
+ * Validate a session token and return session if valid
5157
+ * Also extends the session TTL on successful validation
5158
+ */
5159
+ validateSession(token) {
5160
+ if (!token || !token.startsWith(SESSION_PREFIX)) {
5161
+ return null;
5162
+ }
5163
+ const session = this.sessions.get(token);
5164
+ if (!session) {
5165
+ return null;
5166
+ }
5167
+ if (/* @__PURE__ */ new Date() > session.expiresAt) {
5168
+ this.sessions.delete(token);
5169
+ return null;
5170
+ }
5171
+ session.lastActivity = /* @__PURE__ */ new Date();
5172
+ session.expiresAt = new Date(
5173
+ session.lastActivity.getTime() + DEFAULT_TTL_MINUTES * 60 * 1e3
5174
+ );
5175
+ return session;
5176
+ }
5177
+ /**
5178
+ * Get session without extending TTL
5179
+ */
5180
+ getSession(token) {
5181
+ if (!token || !token.startsWith(SESSION_PREFIX)) {
5182
+ return null;
5183
+ }
5184
+ const session = this.sessions.get(token);
5185
+ if (!session) {
5186
+ return null;
5187
+ }
5188
+ if (/* @__PURE__ */ new Date() > session.expiresAt) {
5189
+ this.sessions.delete(token);
5190
+ return null;
5191
+ }
5192
+ return session;
5193
+ }
5194
+ /**
5195
+ * Invalidate/delete a session
5196
+ */
5197
+ invalidateSession(token) {
5198
+ this.sessions.delete(token);
5199
+ }
5200
+ /**
5201
+ * Get remaining TTL in seconds
5202
+ */
5203
+ getTimeToLive(token) {
5204
+ const session = this.getSession(token);
5205
+ if (!session) {
5206
+ return 0;
5207
+ }
5208
+ const remaining = session.expiresAt.getTime() - Date.now();
5209
+ return Math.max(0, Math.floor(remaining / 1e3));
5210
+ }
5211
+ /**
5212
+ * Start periodic cleanup of expired sessions
5213
+ */
5214
+ startCleanup() {
5215
+ this.cleanupInterval = setInterval(() => {
5216
+ const now = /* @__PURE__ */ new Date();
5217
+ for (const [token, session] of this.sessions.entries()) {
5218
+ if (now > session.expiresAt) {
5219
+ this.sessions.delete(token);
5220
+ }
5221
+ }
5222
+ }, 5 * 60 * 1e3);
5223
+ this.cleanupInterval.unref();
5224
+ }
5225
+ /**
5226
+ * Stop cleanup interval (call when shutting down)
5227
+ */
5228
+ stopCleanup() {
5229
+ if (this.cleanupInterval) {
5230
+ clearInterval(this.cleanupInterval);
5231
+ this.cleanupInterval = null;
5232
+ }
5233
+ }
5234
+ /**
5235
+ * Clear all sessions (for shutdown)
5236
+ */
5237
+ clearAll() {
5238
+ this.sessions.clear();
5239
+ this.stopCleanup();
5240
+ }
5241
+ /**
5242
+ * Get active session count (for debugging)
5243
+ */
5244
+ getActiveSessionCount() {
5245
+ return this.sessions.size;
5246
+ }
5247
+ };
5248
+
5249
+ // src/server/web-chat-server.ts
5250
+ var __filename = fileURLToPath(import.meta.url);
5251
+ var __dirname = path4.dirname(__filename);
5252
+ var MIME_TYPES = {
5253
+ ".html": "text/html",
5254
+ ".css": "text/css",
5255
+ ".js": "application/javascript",
5256
+ ".json": "application/json",
5257
+ ".png": "image/png",
5258
+ ".svg": "image/svg+xml",
5259
+ ".ico": "image/x-icon"
5260
+ };
5261
+ async function findAvailablePort(startPort, endPort) {
5262
+ for (let port = startPort; port <= endPort; port++) {
5263
+ try {
5264
+ await new Promise((resolve4, reject) => {
5265
+ const server = http.createServer();
5266
+ server.listen(port, "127.0.0.1");
5267
+ server.once("listening", () => {
5268
+ server.close(() => resolve4());
5269
+ });
5270
+ server.once("error", reject);
5271
+ });
5272
+ return port;
5273
+ } catch {
5274
+ continue;
5275
+ }
5276
+ }
5277
+ throw new Error(`No available port found in range ${startPort}-${endPort}`);
5278
+ }
5279
+ async function parseJsonBody(req) {
5280
+ return new Promise((resolve4, reject) => {
5281
+ let body = "";
5282
+ req.on("data", (chunk) => {
5283
+ body += chunk.toString();
5284
+ });
5285
+ req.on("end", () => {
5286
+ try {
5287
+ resolve4(body ? JSON.parse(body) : {});
5288
+ } catch (e) {
5289
+ reject(new Error("Invalid JSON body"));
5290
+ }
5291
+ });
5292
+ req.on("error", reject);
5293
+ });
5294
+ }
5295
+ function sendJson(res, statusCode, data) {
5296
+ res.writeHead(statusCode, {
5297
+ "Content-Type": "application/json",
5298
+ "Access-Control-Allow-Origin": "*",
5299
+ "Access-Control-Allow-Methods": "GET, POST, OPTIONS",
5300
+ "Access-Control-Allow-Headers": "Content-Type, X-Session-Token"
5301
+ });
5302
+ res.end(JSON.stringify(data));
5303
+ }
5304
+ function sendError(res, statusCode, code, message) {
5305
+ sendJson(res, statusCode, {
5306
+ success: false,
5307
+ error: { code, message }
5308
+ });
5309
+ }
5310
+ function getSessionToken(req) {
5311
+ const headerToken = req.headers["x-session-token"];
5312
+ if (headerToken && typeof headerToken === "string") {
5313
+ return headerToken;
5314
+ }
5315
+ const url = new URL(req.url || "/", `http://${req.headers.host}`);
5316
+ return url.searchParams.get("session");
5317
+ }
5318
+ function createWebChatServer(options) {
5319
+ const { config } = options;
5320
+ const host = options.host ?? "127.0.0.1";
5321
+ let port = options.port ?? 0;
5322
+ const sessionManager = new SessionManager();
5323
+ let server = null;
5324
+ let session = null;
5325
+ const staticDir = path4.join(__dirname, "web-chat");
5326
+ async function serveStaticFile(res, filePath) {
5327
+ const fullPath = path4.join(staticDir, filePath);
5328
+ if (!fullPath.startsWith(staticDir)) {
5329
+ sendError(res, 403, "ERR-WC-006", "Forbidden");
5330
+ return;
5331
+ }
5332
+ try {
5333
+ const stat = await fs8.promises.stat(fullPath);
5334
+ if (!stat.isFile()) {
5335
+ sendError(res, 404, "ERR-WC-007", "Not found");
5336
+ return;
5337
+ }
5338
+ const ext = path4.extname(fullPath);
5339
+ const contentType = MIME_TYPES[ext] || "application/octet-stream";
5340
+ const content = await fs8.promises.readFile(fullPath);
5341
+ res.writeHead(200, { "Content-Type": contentType });
5342
+ res.end(content);
5343
+ } catch (error) {
5344
+ if (error && typeof error === "object" && "code" in error && error.code === "ENOENT") {
5345
+ sendError(res, 404, "ERR-WC-007", "Not found");
5346
+ } else {
5347
+ sendError(res, 500, "ERR-WC-008", "Internal server error");
5348
+ }
5349
+ }
5350
+ }
5351
+ async function handleApi(req, res, pathname) {
5352
+ if (req.method === "OPTIONS") {
5353
+ res.writeHead(204, {
5354
+ "Access-Control-Allow-Origin": "*",
5355
+ "Access-Control-Allow-Methods": "GET, POST, OPTIONS",
5356
+ "Access-Control-Allow-Headers": "Content-Type, X-Session-Token"
5357
+ });
5358
+ res.end();
5359
+ return;
5360
+ }
5361
+ if (pathname === "/health") {
5362
+ sendJson(res, 200, { status: "ok" });
5363
+ return;
5364
+ }
5365
+ const token = getSessionToken(req);
5366
+ const validSession = token ? sessionManager.validateSession(token) : null;
5367
+ if (!validSession) {
5368
+ sendError(
5369
+ res,
5370
+ 401,
5371
+ "ERR-WC-001",
5372
+ "Session expired. Please run 'jai1 chat --web' again."
5373
+ );
5374
+ return;
5375
+ }
5376
+ const llmService = new LlmProxyService({
5377
+ accessKey: validSession.accessKey,
5378
+ apiUrl: validSession.apiUrl
5379
+ });
5380
+ if (pathname === "/api/session" && req.method === "GET") {
5381
+ try {
5382
+ const models = await llmService.getModelsWithUsage();
5383
+ const allowedModels = models.filter((m) => m.allowed).map((m) => m.id);
5384
+ sendJson(res, 200, {
5385
+ success: true,
5386
+ data: {
5387
+ expiresAt: validSession.expiresAt.toISOString(),
5388
+ models: allowedModels
5389
+ }
5390
+ });
5391
+ } catch (error) {
5392
+ sendError(res, 502, "ERR-WC-004", "Failed to connect to LLM service.");
5393
+ }
5394
+ return;
5395
+ }
5396
+ if (pathname === "/api/models" && req.method === "GET") {
5397
+ try {
5398
+ const models = await llmService.getModelsWithUsage();
5399
+ sendJson(res, 200, {
5400
+ success: true,
5401
+ data: models.filter((m) => m.allowed)
5402
+ });
5403
+ } catch (error) {
5404
+ sendError(res, 502, "ERR-WC-004", "Failed to connect to LLM service.");
5405
+ }
5406
+ return;
5407
+ }
5408
+ if (pathname === "/api/stats" && req.method === "GET") {
5409
+ try {
5410
+ const [limits, usage] = await Promise.all([
5411
+ llmService.getLimits(),
5412
+ llmService.getUsage(1)
5413
+ // Today only
5414
+ ]);
5415
+ const today = (/* @__PURE__ */ new Date()).toLocaleDateString("en-CA", { timeZone: "Asia/Ho_Chi_Minh" });
5416
+ const modelStats = {};
5417
+ const allowedModels = limits.effectiveAllowedModels || [];
5418
+ const rateLimits = limits.effectiveRateLimits || {};
5419
+ allowedModels.forEach((modelId) => {
5420
+ const limit = rateLimits[modelId] || rateLimits[modelId.toLowerCase()] || 0;
5421
+ const usageRecord = (usage.data || []).find(
5422
+ (u) => (u.model === modelId || u.model.toLowerCase() === modelId.toLowerCase()) && u.date === today
5423
+ );
5424
+ const used = usageRecord?.count || 0;
5425
+ modelStats[modelId] = {
5426
+ limit,
5427
+ used,
5428
+ remaining: Math.max(0, limit - used)
5429
+ };
5430
+ });
5431
+ sendJson(res, 200, {
5432
+ success: true,
5433
+ data: {
5434
+ date: today,
5435
+ models: modelStats
5436
+ }
5437
+ });
5438
+ } catch (error) {
5439
+ sendError(res, 502, "ERR-WC-004", "Failed to connect to LLM service.");
5440
+ }
5441
+ return;
5442
+ }
5443
+ if (pathname === "/api/chat" && req.method === "POST") {
5444
+ try {
5445
+ const body = await parseJsonBody(req);
5446
+ if (!body.message || !body.model) {
5447
+ sendError(res, 400, "ERR-WC-009", "Missing message or model");
5448
+ return;
5449
+ }
5450
+ const messages = [];
5451
+ if (body.systemPrompt) {
5452
+ messages.push({ role: "system", content: body.systemPrompt });
5453
+ }
5454
+ messages.push(
5455
+ ...(body.history || []).map((m) => ({
5456
+ role: m.role,
5457
+ content: m.content
5458
+ })),
5459
+ { role: "user", content: body.message }
5460
+ );
5461
+ res.writeHead(200, {
5462
+ "Content-Type": "text/event-stream",
5463
+ "Cache-Control": "no-cache",
5464
+ Connection: "keep-alive",
5465
+ "Access-Control-Allow-Origin": "*"
5466
+ });
5467
+ try {
5468
+ for await (const chunk of llmService.chatStream(
5469
+ messages,
5470
+ body.model
5471
+ )) {
5472
+ res.write(
5473
+ `data: ${JSON.stringify({ type: "chunk", content: chunk })}
5474
+
5475
+ `
5476
+ );
5477
+ }
5478
+ res.write(`data: ${JSON.stringify({ type: "done" })}
5479
+
5480
+ `);
5481
+ } catch (streamError) {
5482
+ const errorMessage = streamError instanceof Error ? streamError.message : "Unknown error";
5483
+ if (errorMessage.includes("not allowed")) {
5484
+ res.write(
5485
+ `data: ${JSON.stringify({ type: "error", code: "ERR-WC-002", message: "You don't have access to this model." })}
5486
+
5487
+ `
5488
+ );
5489
+ } else if (errorMessage.includes("rate limit")) {
5490
+ res.write(
5491
+ `data: ${JSON.stringify({ type: "error", code: "ERR-WC-003", message: "Rate limit exceeded. Please wait." })}
5492
+
5493
+ `
5494
+ );
5495
+ } else {
5496
+ res.write(
5497
+ `data: ${JSON.stringify({ type: "error", code: "ERR-WC-004", message: errorMessage })}
5498
+
5499
+ `
5500
+ );
5501
+ }
5502
+ }
5503
+ res.end();
5504
+ } catch (error) {
5505
+ sendError(res, 500, "ERR-WC-008", "Internal server error");
5506
+ }
5507
+ return;
5508
+ }
5509
+ sendError(res, 404, "ERR-WC-010", "API endpoint not found");
5510
+ }
5511
+ async function handleRequest(req, res) {
5512
+ const url = new URL(req.url || "/", `http://${req.headers.host}`);
5513
+ const pathname = url.pathname;
5514
+ try {
5515
+ if (pathname.startsWith("/api/") || pathname === "/health") {
5516
+ await handleApi(req, res, pathname);
5517
+ return;
5518
+ }
5519
+ let filePath = pathname === "/" ? "/index.html" : pathname;
5520
+ filePath = filePath.replace(/^\//, "");
5521
+ await serveStaticFile(res, filePath);
5522
+ } catch (error) {
5523
+ console.error("[WebChatServer] Error:", error);
5524
+ sendError(res, 500, "ERR-WC-008", "Internal server error");
5525
+ }
5526
+ }
5527
+ return {
5528
+ async start() {
5529
+ if (!port || port === 0) {
5530
+ port = await findAvailablePort(54321, 54399);
5531
+ }
5532
+ session = sessionManager.createSession({
5533
+ accessKey: config.accessKey,
5534
+ apiUrl: config.apiUrl
5535
+ });
5536
+ server = http.createServer(handleRequest);
5537
+ return new Promise((resolve4, reject) => {
5538
+ server.listen(port, host, () => {
5539
+ console.log(`
5540
+ \u{1F310} Web Chat server started at http://${host}:${port}`);
5541
+ console.log(`\u{1F4CB} Session token: ${session.token.slice(0, 12)}...`);
5542
+ console.log(`\u23F0 Session expires in 30 minutes
5543
+ `);
5544
+ resolve4({
5545
+ port,
5546
+ sessionToken: session.token
5547
+ });
5548
+ });
5549
+ server.once("error", (err) => {
5550
+ if (err.code === "EADDRINUSE") {
5551
+ reject(new Error(`Port ${port} is already in use.`));
5552
+ } else {
5553
+ reject(err);
5554
+ }
5555
+ });
5556
+ });
5557
+ },
5558
+ async stop() {
5559
+ if (server) {
5560
+ return new Promise((resolve4) => {
5561
+ server.close(() => {
5562
+ sessionManager.clearAll();
5563
+ console.log("\n\u{1F44B} Web Chat server stopped");
5564
+ resolve4();
5565
+ });
5566
+ });
5567
+ }
5568
+ },
5569
+ getUrl() {
5570
+ return `http://${host}:${port}/?session=${session?.token || ""}`;
5571
+ }
5572
+ };
5573
+ }
5574
+
5086
5575
  // src/commands/chat.ts
5087
- async function handleChatCommand(options) {
5576
+ async function handleTerminalChat(options) {
5088
5577
  const configService = new ConfigService();
5089
5578
  const config = await configService.load();
5090
5579
  if (!config) {
@@ -5105,8 +5594,58 @@ async function handleChatCommand(options) {
5105
5594
  await waitUntilExit();
5106
5595
  clear();
5107
5596
  }
5597
+ async function handleWebChat(options) {
5598
+ const configService = new ConfigService();
5599
+ const config = await configService.load();
5600
+ if (!config) {
5601
+ throw new ValidationError('Not initialized. Run "jai1 auth" first.');
5602
+ }
5603
+ const port = options.port ? parseInt(options.port, 10) : void 0;
5604
+ const server = createWebChatServer({
5605
+ config,
5606
+ port
5607
+ });
5608
+ try {
5609
+ const { port: actualPort, sessionToken } = await server.start();
5610
+ const url = server.getUrl();
5611
+ if (!options.noOpen) {
5612
+ const { default: open } = await import("open");
5613
+ await open(url);
5614
+ console.log("\u{1F310} Browser opened with chat interface");
5615
+ } else {
5616
+ console.log(`
5617
+ \u{1F4CB} Open this URL in your browser:
5618
+ ${url}
5619
+ `);
5620
+ }
5621
+ console.log("Press Ctrl+C to stop the server\n");
5622
+ await new Promise((resolve4) => {
5623
+ process.on("SIGINT", async () => {
5624
+ console.log("\n\n\u{1F6D1} Shutting down...");
5625
+ await server.stop();
5626
+ resolve4();
5627
+ });
5628
+ process.on("SIGTERM", async () => {
5629
+ await server.stop();
5630
+ resolve4();
5631
+ });
5632
+ });
5633
+ } catch (error) {
5634
+ if (error instanceof Error && error.message.includes("Port")) {
5635
+ throw new ValidationError(error.message + " Try specifying a different port with --port.");
5636
+ }
5637
+ throw error;
5638
+ }
5639
+ }
5640
+ async function handleChatCommand(options) {
5641
+ if (options.web) {
5642
+ await handleWebChat(options);
5643
+ } else {
5644
+ await handleTerminalChat(options);
5645
+ }
5646
+ }
5108
5647
  function createChatCommand() {
5109
- const cmd = new Command12("chat").description("Interactive AI chat with Jai1 LLM Proxy").option("--model <model>", "Initial model to use (e.g., gpt-4o, claude-3-opus)").action(async (options) => {
5648
+ const cmd = new Command12("chat").description("Interactive AI chat with Jai1 LLM Proxy").option("--model <model>", "Initial model to use (e.g., gpt-4o, claude-3-opus)").option("--web", "Open chat in web browser instead of terminal").option("--port <port>", "Port for web server (default: auto-find available port)").option("--no-open", "Don't auto-open browser (web mode only)").action(async (options) => {
5110
5649
  await handleChatCommand(options);
5111
5650
  });
5112
5651
  return cmd;
@@ -5254,8 +5793,8 @@ function createStatsCommand() {
5254
5793
  import { Command as Command15 } from "commander";
5255
5794
 
5256
5795
  // src/services/translation.service.ts
5257
- import { promises as fs8 } from "fs";
5258
- import path4 from "path";
5796
+ import { promises as fs9 } from "fs";
5797
+ import path5 from "path";
5259
5798
  import pLimit from "p-limit";
5260
5799
  import pRetry from "p-retry";
5261
5800
  var TRANSLATABLE_EXTS = [".md", ".txt", ".json", ".yaml", ".yml"];
@@ -5273,7 +5812,7 @@ var TranslationService = class {
5273
5812
  */
5274
5813
  async detectInputType(input4) {
5275
5814
  try {
5276
- const stat = await fs8.stat(input4);
5815
+ const stat = await fs9.stat(input4);
5277
5816
  if (stat.isDirectory()) return "folder";
5278
5817
  if (stat.isFile()) return "file";
5279
5818
  } catch {
@@ -5310,13 +5849,13 @@ var TranslationService = class {
5310
5849
  */
5311
5850
  async translateFile(filePath) {
5312
5851
  try {
5313
- const content = await fs8.readFile(filePath, "utf-8");
5314
- const ext = path4.extname(filePath).toLowerCase();
5852
+ const content = await fs9.readFile(filePath, "utf-8");
5853
+ const ext = path5.extname(filePath).toLowerCase();
5315
5854
  const fileType = this.getFileType(ext);
5316
5855
  const translatedContent = await this.translateWithRetry(content, fileType);
5317
5856
  const outputPath = this.generateOutputPath(filePath);
5318
5857
  if (!this.options.dryRun) {
5319
- await fs8.writeFile(outputPath, translatedContent, "utf-8");
5858
+ await fs9.writeFile(outputPath, translatedContent, "utf-8");
5320
5859
  }
5321
5860
  return {
5322
5861
  inputPath: filePath,
@@ -5371,27 +5910,27 @@ var TranslationService = class {
5371
5910
  if (this.options.output) {
5372
5911
  return this.options.output;
5373
5912
  }
5374
- const ext = path4.extname(inputPath);
5375
- const base = path4.basename(inputPath, ext);
5376
- const dir = path4.dirname(inputPath);
5913
+ const ext = path5.extname(inputPath);
5914
+ const base = path5.basename(inputPath, ext);
5915
+ const dir = path5.dirname(inputPath);
5377
5916
  const lang = this.options.to;
5378
- return path4.join(dir, `${base}.${lang}${ext}`);
5917
+ return path5.join(dir, `${base}.${lang}${ext}`);
5379
5918
  }
5380
5919
  /**
5381
5920
  * Discover translatable files in folder recursively
5382
5921
  */
5383
5922
  async discoverFiles(folderPath) {
5384
- const entries = await fs8.readdir(folderPath, { withFileTypes: true });
5923
+ const entries = await fs9.readdir(folderPath, { withFileTypes: true });
5385
5924
  const files = [];
5386
5925
  for (const entry of entries) {
5387
- const fullPath = path4.join(folderPath, entry.name);
5926
+ const fullPath = path5.join(folderPath, entry.name);
5388
5927
  if (entry.isDirectory()) {
5389
5928
  if (!entry.name.startsWith(".") && entry.name !== "node_modules") {
5390
5929
  const subFiles = await this.discoverFiles(fullPath);
5391
5930
  files.push(...subFiles);
5392
5931
  }
5393
5932
  } else if (entry.isFile()) {
5394
- const ext = path4.extname(entry.name).toLowerCase();
5933
+ const ext = path5.extname(entry.name).toLowerCase();
5395
5934
  if (TRANSLATABLE_EXTS.includes(ext)) {
5396
5935
  if (!this.isTranslatedFile(entry.name)) {
5397
5936
  files.push(fullPath);
@@ -5896,7 +6435,7 @@ import { Command as Command34 } from "commander";
5896
6435
  import { Command as Command21 } from "commander";
5897
6436
 
5898
6437
  // src/services/utils.service.ts
5899
- import crypto from "crypto";
6438
+ import crypto2 from "crypto";
5900
6439
  import bcrypt from "bcryptjs";
5901
6440
  import { readFile } from "fs/promises";
5902
6441
  var UtilsService = class {
@@ -5920,7 +6459,7 @@ var UtilsService = class {
5920
6459
  if (charset.length === 0) {
5921
6460
  throw new Error("At least one character type must be enabled");
5922
6461
  }
5923
- const randomBytes = crypto.randomBytes(length);
6462
+ const randomBytes = crypto2.randomBytes(length);
5924
6463
  let password = "";
5925
6464
  for (let i = 0; i < length; i++) {
5926
6465
  const randomIndex = randomBytes[i] % charset.length;
@@ -5932,7 +6471,7 @@ var UtilsService = class {
5932
6471
  * Generate UUID v4
5933
6472
  */
5934
6473
  generateUuid(options) {
5935
- let uuid = crypto.randomUUID();
6474
+ let uuid = crypto2.randomUUID();
5936
6475
  if (options?.uppercase) {
5937
6476
  uuid = uuid.toUpperCase();
5938
6477
  }
@@ -5948,7 +6487,7 @@ var UtilsService = class {
5948
6487
  if (algorithm === "bcrypt") {
5949
6488
  throw new Error("Use hashBcrypt for bcrypt algorithm");
5950
6489
  }
5951
- const hash = crypto.createHash(algorithm);
6490
+ const hash = crypto2.createHash(algorithm);
5952
6491
  hash.update(input4);
5953
6492
  return hash.digest("hex");
5954
6493
  }
@@ -6036,7 +6575,7 @@ var UtilsService = class {
6036
6575
  const headerB64 = this.base64Encode(JSON.stringify(finalHeader), true);
6037
6576
  const payloadB64 = this.base64Encode(JSON.stringify(payload), true);
6038
6577
  const signatureInput = `${headerB64}.${payloadB64}`;
6039
- const signature = crypto.createHmac("sha256", secret).update(signatureInput).digest("base64").replace(/\+/g, "-").replace(/\//g, "_").replace(/=/g, "");
6578
+ const signature = crypto2.createHmac("sha256", secret).update(signatureInput).digest("base64").replace(/\+/g, "-").replace(/\//g, "_").replace(/=/g, "");
6040
6579
  return `${headerB64}.${payloadB64}.${signature}`;
6041
6580
  }
6042
6581
  /**
@@ -7943,8 +8482,8 @@ var HttpView = () => {
7943
8482
  import React37, { useState as useState24 } from "react";
7944
8483
  import { Box as Box27, Text as Text28, useInput as useInput22 } from "ink";
7945
8484
  import TextInput14 from "ink-text-input";
7946
- import * as fs9 from "fs";
7947
- import * as path5 from "path";
8485
+ import * as fs10 from "fs";
8486
+ import * as path6 from "path";
7948
8487
  var MarkdownView = () => {
7949
8488
  const [filePath, setFilePath] = useState24("");
7950
8489
  const [content, setContent] = useState24("");
@@ -7964,12 +8503,12 @@ var MarkdownView = () => {
7964
8503
  }
7965
8504
  try {
7966
8505
  setError("");
7967
- const resolvedPath = path5.resolve(filePath);
7968
- if (!fs9.existsSync(resolvedPath)) {
8506
+ const resolvedPath = path6.resolve(filePath);
8507
+ if (!fs10.existsSync(resolvedPath)) {
7969
8508
  setError(`File not found: ${resolvedPath}`);
7970
8509
  return;
7971
8510
  }
7972
- let fileContent = fs9.readFileSync(resolvedPath, "utf-8");
8511
+ let fileContent = fs10.readFileSync(resolvedPath, "utf-8");
7973
8512
  fileContent = fileContent.replace(/```mermaid\n([\s\S]*?)```/g, (match, code) => {
7974
8513
  return `
7975
8514
  \u{1F3A8} **Mermaid Diagram**
@@ -8250,8 +8789,8 @@ import { Command as Command35 } from "commander";
8250
8789
  import { checkbox as checkbox3, confirm as confirm5 } from "@inquirer/prompts";
8251
8790
 
8252
8791
  // src/services/deps.service.ts
8253
- import { promises as fs10 } from "fs";
8254
- import path6 from "path";
8792
+ import { promises as fs11 } from "fs";
8793
+ import path7 from "path";
8255
8794
  import { execSync } from "child_process";
8256
8795
  import pLimit2 from "p-limit";
8257
8796
  var DepsService = class {
@@ -8260,9 +8799,9 @@ var DepsService = class {
8260
8799
  * Đọc package.json từ thư mục
8261
8800
  */
8262
8801
  async readPackageJson(cwd) {
8263
- const pkgPath = path6.join(cwd, "package.json");
8802
+ const pkgPath = path7.join(cwd, "package.json");
8264
8803
  try {
8265
- const content = await fs10.readFile(pkgPath, "utf-8");
8804
+ const content = await fs11.readFile(pkgPath, "utf-8");
8266
8805
  return JSON.parse(content);
8267
8806
  } catch (error) {
8268
8807
  if (error.code === "ENOENT") {
@@ -8368,7 +8907,7 @@ var DepsService = class {
8368
8907
  ];
8369
8908
  for (const { file, pm } of lockFiles) {
8370
8909
  try {
8371
- await fs10.access(path6.join(cwd, file));
8910
+ await fs11.access(path7.join(cwd, file));
8372
8911
  return pm;
8373
8912
  } catch {
8374
8913
  }
@@ -8652,7 +9191,7 @@ import { Command as Command40 } from "commander";
8652
9191
  import { Command as Command37 } from "commander";
8653
9192
 
8654
9193
  // src/services/starter-kit.service.ts
8655
- import { promises as fs11 } from "fs";
9194
+ import { promises as fs12 } from "fs";
8656
9195
  import { join as join5 } from "path";
8657
9196
  import AdmZip from "adm-zip";
8658
9197
  var StarterKitService = class {
@@ -8701,16 +9240,16 @@ var StarterKitService = class {
8701
9240
  }
8702
9241
  if (onProgress) onProgress(30);
8703
9242
  const tmpDir = join5(process.env.TMPDIR || "/tmp", "jai1-kits");
8704
- await fs11.mkdir(tmpDir, { recursive: true });
9243
+ await fs12.mkdir(tmpDir, { recursive: true });
8705
9244
  const tmpFile = join5(tmpDir, `${slug}.zip`);
8706
9245
  const buffer = await response.arrayBuffer();
8707
- await fs11.writeFile(tmpFile, Buffer.from(buffer));
9246
+ await fs12.writeFile(tmpFile, Buffer.from(buffer));
8708
9247
  if (onProgress) onProgress(60);
8709
9248
  const zip = new AdmZip(tmpFile);
8710
- await fs11.mkdir(targetDir, { recursive: true });
9249
+ await fs12.mkdir(targetDir, { recursive: true });
8711
9250
  zip.extractAllTo(targetDir, true);
8712
9251
  if (onProgress) onProgress(100);
8713
- await fs11.unlink(tmpFile);
9252
+ await fs12.unlink(tmpFile);
8714
9253
  }
8715
9254
  };
8716
9255
 
@@ -8805,7 +9344,7 @@ Post-Init Commands:`);
8805
9344
 
8806
9345
  // src/commands/kit/create.ts
8807
9346
  import { Command as Command39 } from "commander";
8808
- import { promises as fs12 } from "fs";
9347
+ import { promises as fs13 } from "fs";
8809
9348
  import { join as join6 } from "path";
8810
9349
  import { select as select2, input, checkbox as checkbox4 } from "@inquirer/prompts";
8811
9350
  import { execa as execa2 } from "execa";
@@ -8871,7 +9410,7 @@ function createKitCreateCommand() {
8871
9410
  }
8872
9411
  const targetDir = directory || join6(process.cwd(), options.name || slug);
8873
9412
  try {
8874
- await fs12.access(targetDir);
9413
+ await fs13.access(targetDir);
8875
9414
  throw new Error(`Directory already exists: ${targetDir}`);
8876
9415
  } catch (error) {
8877
9416
  if (error.code !== "ENOENT") {
@@ -8989,7 +9528,7 @@ function createKitCreateCommand() {
8989
9528
  async function applyVariableSubstitution(dir, variables) {
8990
9529
  const files = await getAllFiles(dir);
8991
9530
  for (const file of files) {
8992
- let content = await fs12.readFile(file, "utf-8");
9531
+ let content = await fs13.readFile(file, "utf-8");
8993
9532
  let modified = false;
8994
9533
  for (const [key, value] of Object.entries(variables)) {
8995
9534
  const regex = new RegExp(`\\{\\{${key}\\}\\}`, "g");
@@ -8999,13 +9538,13 @@ async function applyVariableSubstitution(dir, variables) {
8999
9538
  }
9000
9539
  }
9001
9540
  if (modified) {
9002
- await fs12.writeFile(file, content, "utf-8");
9541
+ await fs13.writeFile(file, content, "utf-8");
9003
9542
  }
9004
9543
  }
9005
9544
  }
9006
9545
  async function getAllFiles(dir) {
9007
9546
  const files = [];
9008
- const entries = await fs12.readdir(dir, { withFileTypes: true });
9547
+ const entries = await fs13.readdir(dir, { withFileTypes: true });
9009
9548
  for (const entry of entries) {
9010
9549
  const fullPath = join6(dir, entry.name);
9011
9550
  if (entry.isDirectory()) {
@@ -9092,7 +9631,7 @@ function createRulesListCommand() {
9092
9631
 
9093
9632
  // src/commands/rules/init.ts
9094
9633
  import { Command as Command42 } from "commander";
9095
- import { promises as fs13 } from "fs";
9634
+ import { promises as fs14 } from "fs";
9096
9635
  import { join as join7 } from "path";
9097
9636
  import { select as select3, confirm as confirm6 } from "@inquirer/prompts";
9098
9637
  function createRulesInitCommand() {
@@ -9178,7 +9717,7 @@ function createRulesInitCommand() {
9178
9717
  appliedAt: (/* @__PURE__ */ new Date()).toISOString(),
9179
9718
  customContext: "09-custom.mdc"
9180
9719
  };
9181
- await fs13.writeFile("jai1-rules.json", JSON.stringify(projectConfig, null, 2));
9720
+ await fs14.writeFile("jai1-rules.json", JSON.stringify(projectConfig, null, 2));
9182
9721
  console.log("\u2713 Created jai1-rules.json");
9183
9722
  console.log("\n\u2705 Preset applied successfully!\n");
9184
9723
  console.log("Next steps:");
@@ -9189,10 +9728,10 @@ function createRulesInitCommand() {
9189
9728
  }
9190
9729
  async function applyCursorFormat(bundle) {
9191
9730
  const rulesDir = join7(process.cwd(), ".cursor", "rules");
9192
- await fs13.mkdir(rulesDir, { recursive: true });
9731
+ await fs14.mkdir(rulesDir, { recursive: true });
9193
9732
  for (const [filename, content] of Object.entries(bundle.files)) {
9194
9733
  const filePath = join7(rulesDir, filename);
9195
- await fs13.writeFile(filePath, content, "utf-8");
9734
+ await fs14.writeFile(filePath, content, "utf-8");
9196
9735
  console.log(`\u2713 Created .cursor/rules/${filename}`);
9197
9736
  }
9198
9737
  }
@@ -9219,13 +9758,13 @@ async function applyAgentsMdFormat(bundle) {
9219
9758
  }
9220
9759
  }
9221
9760
  const agentsMd = sections.join("\n");
9222
- await fs13.writeFile("AGENTS.md", agentsMd, "utf-8");
9761
+ await fs14.writeFile("AGENTS.md", agentsMd, "utf-8");
9223
9762
  console.log("\u2713 Created AGENTS.md");
9224
9763
  }
9225
9764
 
9226
9765
  // src/commands/rules/apply.ts
9227
9766
  import { Command as Command43 } from "commander";
9228
- import { promises as fs15 } from "fs";
9767
+ import { promises as fs16 } from "fs";
9229
9768
  import { join as join9 } from "path";
9230
9769
  import { select as select4, confirm as confirm7, checkbox as checkbox5 } from "@inquirer/prompts";
9231
9770
 
@@ -9586,7 +10125,7 @@ Follow all instructions and patterns defined in AGENTS.md above.
9586
10125
  };
9587
10126
 
9588
10127
  // src/services/backup.service.ts
9589
- import { promises as fs14 } from "fs";
10128
+ import { promises as fs15 } from "fs";
9590
10129
  import { join as join8, dirname } from "path";
9591
10130
  var BackupService = class {
9592
10131
  backupDir = ".jai1/backups";
@@ -9617,16 +10156,16 @@ var BackupService = class {
9617
10156
  await this.backupSingleFile("GEMINI.md", backupPath, ideId, backedUpFiles);
9618
10157
  hasContent = true;
9619
10158
  } else {
9620
- const stats = await fs14.stat(rulesPath);
10159
+ const stats = await fs15.stat(rulesPath);
9621
10160
  if (stats.isDirectory()) {
9622
- const files = await fs14.readdir(rulesPath);
10161
+ const files = await fs15.readdir(rulesPath);
9623
10162
  for (const file of files) {
9624
10163
  if (file.endsWith(format.fileExtension)) {
9625
10164
  const originalPath = join8(rulesPath, file);
9626
10165
  const relativePath = join8(format.rulesPath, file);
9627
10166
  const destPath = join8(backupPath, ideId, file);
9628
- await fs14.mkdir(dirname(destPath), { recursive: true });
9629
- await fs14.copyFile(originalPath, destPath);
10167
+ await fs15.mkdir(dirname(destPath), { recursive: true });
10168
+ await fs15.copyFile(originalPath, destPath);
9630
10169
  backedUpFiles.push({
9631
10170
  originalPath: relativePath,
9632
10171
  backupPath: join8(ideId, file),
@@ -9650,8 +10189,8 @@ var BackupService = class {
9650
10189
  ides,
9651
10190
  files: backedUpFiles
9652
10191
  };
9653
- await fs14.mkdir(backupPath, { recursive: true });
9654
- await fs14.writeFile(
10192
+ await fs15.mkdir(backupPath, { recursive: true });
10193
+ await fs15.writeFile(
9655
10194
  join8(backupPath, "metadata.json"),
9656
10195
  JSON.stringify(metadata, null, 2),
9657
10196
  "utf-8"
@@ -9669,8 +10208,8 @@ var BackupService = class {
9669
10208
  return;
9670
10209
  }
9671
10210
  const destPath = join8(backupPath, ideId, filename);
9672
- await fs14.mkdir(dirname(destPath), { recursive: true });
9673
- await fs14.copyFile(originalPath, destPath);
10211
+ await fs15.mkdir(dirname(destPath), { recursive: true });
10212
+ await fs15.copyFile(originalPath, destPath);
9674
10213
  backedUpFiles.push({
9675
10214
  originalPath: filename,
9676
10215
  backupPath: join8(ideId, filename),
@@ -9684,15 +10223,15 @@ var BackupService = class {
9684
10223
  */
9685
10224
  async restoreBackup(backupPath) {
9686
10225
  const metadataPath = join8(backupPath, "metadata.json");
9687
- const metadataContent = await fs14.readFile(metadataPath, "utf-8");
10226
+ const metadataContent = await fs15.readFile(metadataPath, "utf-8");
9688
10227
  const metadata = JSON.parse(metadataContent);
9689
10228
  console.log(`
9690
10229
  Restoring backup from ${metadata.timestamp}...`);
9691
10230
  for (const file of metadata.files) {
9692
10231
  const sourcePath = join8(backupPath, file.backupPath);
9693
10232
  const destPath = join8(process.cwd(), file.originalPath);
9694
- await fs14.mkdir(dirname(destPath), { recursive: true });
9695
- await fs14.copyFile(sourcePath, destPath);
10233
+ await fs15.mkdir(dirname(destPath), { recursive: true });
10234
+ await fs15.copyFile(sourcePath, destPath);
9696
10235
  console.log(`\u2713 Restored ${file.originalPath}`);
9697
10236
  }
9698
10237
  console.log("\n\u2705 Backup restored successfully!");
@@ -9707,13 +10246,13 @@ Restoring backup from ${metadata.timestamp}...`);
9707
10246
  if (!exists) {
9708
10247
  return [];
9709
10248
  }
9710
- const entries = await fs14.readdir(backupDirPath, { withFileTypes: true });
10249
+ const entries = await fs15.readdir(backupDirPath, { withFileTypes: true });
9711
10250
  const backups = [];
9712
10251
  for (const entry of entries) {
9713
10252
  if (entry.isDirectory()) {
9714
10253
  const metadataPath = join8(backupDirPath, entry.name, "metadata.json");
9715
10254
  try {
9716
- const metadataContent = await fs14.readFile(metadataPath, "utf-8");
10255
+ const metadataContent = await fs15.readFile(metadataPath, "utf-8");
9717
10256
  const metadata = JSON.parse(metadataContent);
9718
10257
  backups.push(metadata);
9719
10258
  } catch {
@@ -9760,9 +10299,9 @@ Restoring backup from ${metadata.timestamp}...`);
9760
10299
  /**
9761
10300
  * Check if a path exists
9762
10301
  */
9763
- async pathExists(path8) {
10302
+ async pathExists(path9) {
9764
10303
  try {
9765
- await fs14.access(path8);
10304
+ await fs15.access(path9);
9766
10305
  return true;
9767
10306
  } catch {
9768
10307
  return false;
@@ -9771,22 +10310,22 @@ Restoring backup from ${metadata.timestamp}...`);
9771
10310
  /**
9772
10311
  * Recursively delete a directory
9773
10312
  */
9774
- async deleteDirectory(path8) {
10313
+ async deleteDirectory(path9) {
9775
10314
  try {
9776
- const exists = await this.pathExists(path8);
10315
+ const exists = await this.pathExists(path9);
9777
10316
  if (!exists) {
9778
10317
  return;
9779
10318
  }
9780
- const entries = await fs14.readdir(path8, { withFileTypes: true });
10319
+ const entries = await fs15.readdir(path9, { withFileTypes: true });
9781
10320
  for (const entry of entries) {
9782
- const fullPath = join8(path8, entry.name);
10321
+ const fullPath = join8(path9, entry.name);
9783
10322
  if (entry.isDirectory()) {
9784
10323
  await this.deleteDirectory(fullPath);
9785
10324
  } else {
9786
- await fs14.unlink(fullPath);
10325
+ await fs15.unlink(fullPath);
9787
10326
  }
9788
10327
  }
9789
- await fs14.rmdir(path8);
10328
+ await fs15.rmdir(path9);
9790
10329
  } catch (error) {
9791
10330
  }
9792
10331
  }
@@ -9801,7 +10340,7 @@ Restoring backup from ${metadata.timestamp}...`);
9801
10340
  */
9802
10341
  async ensureBackupDir() {
9803
10342
  const backupDirPath = join8(process.cwd(), this.backupDir);
9804
- await fs14.mkdir(backupDirPath, { recursive: true });
10343
+ await fs15.mkdir(backupDirPath, { recursive: true });
9805
10344
  }
9806
10345
  };
9807
10346
 
@@ -9959,19 +10498,19 @@ function createRulesApplyCommand() {
9959
10498
  console.log("\n\u{1F4DD} Applying preset...\n");
9960
10499
  const rulePresetDir = join9(process.cwd(), ".jai1", "rule-preset");
9961
10500
  try {
9962
- await fs15.rm(rulePresetDir, { recursive: true, force: true });
10501
+ await fs16.rm(rulePresetDir, { recursive: true, force: true });
9963
10502
  } catch {
9964
10503
  }
9965
- await fs15.mkdir(rulePresetDir, { recursive: true });
9966
- await fs15.writeFile(
10504
+ await fs16.mkdir(rulePresetDir, { recursive: true });
10505
+ await fs16.writeFile(
9967
10506
  join9(rulePresetDir, "preset.json"),
9968
10507
  JSON.stringify(bundle.preset, null, 2),
9969
10508
  "utf-8"
9970
10509
  );
9971
10510
  for (const [filename, content] of Object.entries(bundle.files)) {
9972
10511
  const filePath = join9(rulePresetDir, filename);
9973
- await fs15.mkdir(join9(filePath, ".."), { recursive: true });
9974
- await fs15.writeFile(filePath, content, "utf-8");
10512
+ await fs16.mkdir(join9(filePath, ".."), { recursive: true });
10513
+ await fs16.writeFile(filePath, content, "utf-8");
9975
10514
  }
9976
10515
  console.log(`\u2713 Saved preset to .jai1/rule-preset/`);
9977
10516
  const allGeneratedFiles = [];
@@ -9980,8 +10519,8 @@ function createRulesApplyCommand() {
9980
10519
  const files = generatorService.generateForIde(bundle, ideId);
9981
10520
  for (const file of files) {
9982
10521
  const fullPath = join9(process.cwd(), file.path);
9983
- await fs15.mkdir(join9(fullPath, ".."), { recursive: true });
9984
- await fs15.writeFile(fullPath, file.content, "utf-8");
10522
+ await fs16.mkdir(join9(fullPath, ".."), { recursive: true });
10523
+ await fs16.writeFile(fullPath, file.content, "utf-8");
9985
10524
  console.log(`\u2713 [${ideId}] ${file.path}`);
9986
10525
  allGeneratedFiles.push({
9987
10526
  ide: ideId,
@@ -10009,7 +10548,7 @@ function createRulesApplyCommand() {
10009
10548
  };
10010
10549
  try {
10011
10550
  const existingConfigPath = join9(process.cwd(), "jai1-rules.json");
10012
- const existingConfigContent = await fs15.readFile(existingConfigPath, "utf-8");
10551
+ const existingConfigContent = await fs16.readFile(existingConfigPath, "utf-8");
10013
10552
  const existingConfig = JSON.parse(existingConfigContent);
10014
10553
  if (existingConfig.backups && existingConfig.backups.length > 0) {
10015
10554
  projectConfig.backups = [
@@ -10020,7 +10559,7 @@ function createRulesApplyCommand() {
10020
10559
  }
10021
10560
  } catch {
10022
10561
  }
10023
- await fs15.writeFile(
10562
+ await fs16.writeFile(
10024
10563
  join9(process.cwd(), "jai1-rules.json"),
10025
10564
  JSON.stringify(projectConfig, null, 2),
10026
10565
  "utf-8"
@@ -10125,7 +10664,7 @@ function formatTimestamp(timestamp) {
10125
10664
 
10126
10665
  // src/commands/rules/sync.ts
10127
10666
  import { Command as Command45 } from "commander";
10128
- import { promises as fs16 } from "fs";
10667
+ import { promises as fs17 } from "fs";
10129
10668
  import { join as join11 } from "path";
10130
10669
  import { confirm as confirm9 } from "@inquirer/prompts";
10131
10670
  function createRulesSyncCommand() {
@@ -10133,7 +10672,7 @@ function createRulesSyncCommand() {
10133
10672
  const configPath = join11(process.cwd(), "jai1-rules.json");
10134
10673
  let projectConfig;
10135
10674
  try {
10136
- const configContent = await fs16.readFile(configPath, "utf-8");
10675
+ const configContent = await fs17.readFile(configPath, "utf-8");
10137
10676
  projectConfig = JSON.parse(configContent);
10138
10677
  } catch {
10139
10678
  throw new ValidationError(
@@ -10216,11 +10755,11 @@ Detected ${detected.length} active IDE(s):
10216
10755
  const rulePresetDir = join11(process.cwd(), ".jai1", "rule-preset");
10217
10756
  const presetExists = await checkPathExists(rulePresetDir);
10218
10757
  if (presetExists) {
10219
- const files = await fs16.readdir(rulePresetDir);
10758
+ const files = await fs17.readdir(rulePresetDir);
10220
10759
  for (const file of files) {
10221
10760
  if (file.endsWith(".mdc")) {
10222
10761
  const filePath = join11(rulePresetDir, file);
10223
- const content = await fs16.readFile(filePath, "utf-8");
10762
+ const content = await fs17.readFile(filePath, "utf-8");
10224
10763
  bundle.files[file] = content;
10225
10764
  }
10226
10765
  }
@@ -10240,8 +10779,8 @@ Detected ${detected.length} active IDE(s):
10240
10779
  const files = generatorService.generateForIde(bundle, ideId);
10241
10780
  for (const file of files) {
10242
10781
  const fullPath = join11(process.cwd(), file.path);
10243
- await fs16.mkdir(join11(fullPath, ".."), { recursive: true });
10244
- await fs16.writeFile(fullPath, file.content, "utf-8");
10782
+ await fs17.mkdir(join11(fullPath, ".."), { recursive: true });
10783
+ await fs17.writeFile(fullPath, file.content, "utf-8");
10245
10784
  }
10246
10785
  console.log(`\u2713 ${format.name} - ${files.length} files regenerated`);
10247
10786
  } catch (error) {
@@ -10250,7 +10789,7 @@ Detected ${detected.length} active IDE(s):
10250
10789
  }
10251
10790
  if (JSON.stringify(projectConfig.ides) !== JSON.stringify(idesToSync)) {
10252
10791
  projectConfig.ides = idesToSync;
10253
- await fs16.writeFile(
10792
+ await fs17.writeFile(
10254
10793
  join11(process.cwd(), "jai1-rules.json"),
10255
10794
  JSON.stringify(projectConfig, null, 2),
10256
10795
  "utf-8"
@@ -10266,7 +10805,7 @@ Detected ${detected.length} active IDE(s):
10266
10805
  }
10267
10806
  async function checkPathExists(absolutePath) {
10268
10807
  try {
10269
- await fs16.access(absolutePath);
10808
+ await fs17.access(absolutePath);
10270
10809
  return true;
10271
10810
  } catch {
10272
10811
  return false;
@@ -10275,14 +10814,14 @@ async function checkPathExists(absolutePath) {
10275
10814
 
10276
10815
  // src/commands/rules/info.ts
10277
10816
  import { Command as Command46 } from "commander";
10278
- import { promises as fs17 } from "fs";
10817
+ import { promises as fs18 } from "fs";
10279
10818
  import { join as join12 } from "path";
10280
10819
  function createRulesInfoCommand() {
10281
10820
  return new Command46("info").description("Show current preset information").option("--json", "Output as JSON").action(async (options) => {
10282
10821
  const configPath = join12(process.cwd(), "jai1-rules.json");
10283
10822
  let projectConfig;
10284
10823
  try {
10285
- const configContent = await fs17.readFile(configPath, "utf-8");
10824
+ const configContent = await fs18.readFile(configPath, "utf-8");
10286
10825
  projectConfig = JSON.parse(configContent);
10287
10826
  } catch {
10288
10827
  throw new ValidationError(
@@ -10299,9 +10838,9 @@ function createRulesInfoCommand() {
10299
10838
  let presetMetadata = null;
10300
10839
  let presetFiles = [];
10301
10840
  try {
10302
- const presetContent = await fs17.readFile(presetJsonPath, "utf-8");
10841
+ const presetContent = await fs18.readFile(presetJsonPath, "utf-8");
10303
10842
  presetMetadata = JSON.parse(presetContent);
10304
- const files = await fs17.readdir(rulePresetDir);
10843
+ const files = await fs18.readdir(rulePresetDir);
10305
10844
  presetFiles = files.filter((f) => f.endsWith(".mdc"));
10306
10845
  } catch {
10307
10846
  }
@@ -10360,9 +10899,9 @@ Available Backups (${projectConfig.backups.length}):`);
10360
10899
  console.log(' \u2022 "jai1 rules apply" - Apply a different preset (replaces current)');
10361
10900
  });
10362
10901
  }
10363
- async function checkPathExists2(path8) {
10902
+ async function checkPathExists2(path9) {
10364
10903
  try {
10365
- await fs17.access(join12(process.cwd(), path8));
10904
+ await fs18.access(join12(process.cwd(), path9));
10366
10905
  return true;
10367
10906
  } catch {
10368
10907
  return false;
@@ -10793,8 +11332,8 @@ var RedmineApiClient = class {
10793
11332
  this.retryConfig = config.defaults.retry;
10794
11333
  this.concurrencyLimit = pLimit3(config.defaults.concurrency);
10795
11334
  }
10796
- async request(path8, options = {}) {
10797
- const url = `${this.baseUrl}${path8}`;
11335
+ async request(path9, options = {}) {
11336
+ const url = `${this.baseUrl}${path9}`;
10798
11337
  const headers = {
10799
11338
  "X-Redmine-API-Key": this.apiAccessToken,
10800
11339
  "Content-Type": "application/json",
@@ -10855,8 +11394,8 @@ var RedmineApiClient = class {
10855
11394
  if (include && include.length > 0) {
10856
11395
  params.append("include", include.join(","));
10857
11396
  }
10858
- const path8 = `/issues/${issueId}.json${params.toString() ? `?${params.toString()}` : ""}`;
10859
- return this.request(path8);
11397
+ const path9 = `/issues/${issueId}.json${params.toString() ? `?${params.toString()}` : ""}`;
11398
+ return this.request(path9);
10860
11399
  }
10861
11400
  async getIssues(projectId, options = {}) {
10862
11401
  const params = new URLSearchParams();
@@ -10876,8 +11415,8 @@ var RedmineApiClient = class {
10876
11415
  if (options.updatedSince) {
10877
11416
  params.append("updated_on", `>=${options.updatedSince}`);
10878
11417
  }
10879
- const path8 = `/issues.json?${params.toString()}`;
10880
- return this.request(path8);
11418
+ const path9 = `/issues.json?${params.toString()}`;
11419
+ return this.request(path9);
10881
11420
  }
10882
11421
  async getAllIssues(projectId, options = {}) {
10883
11422
  const pageSize = options.pageSize || 100;
@@ -11561,7 +12100,7 @@ async function handleSyncProject(options) {
11561
12100
 
11562
12101
  // src/commands/framework/info.ts
11563
12102
  import { Command as Command53 } from "commander";
11564
- import { promises as fs18 } from "fs";
12103
+ import { promises as fs19 } from "fs";
11565
12104
  import { join as join14 } from "path";
11566
12105
  import { homedir as homedir5 } from "os";
11567
12106
  function createInfoCommand() {
@@ -11613,7 +12152,7 @@ function maskKey3(key) {
11613
12152
  async function getProjectStatus2() {
11614
12153
  const projectJai1 = join14(process.cwd(), ".jai1");
11615
12154
  try {
11616
- await fs18.access(projectJai1);
12155
+ await fs19.access(projectJai1);
11617
12156
  return { exists: true, version: "Synced" };
11618
12157
  } catch {
11619
12158
  return { exists: false };
@@ -11803,8 +12342,8 @@ function createClearBackupsCommand() {
11803
12342
  // src/commands/vscode/index.ts
11804
12343
  import { Command as Command56 } from "commander";
11805
12344
  import { checkbox as checkbox7, confirm as confirm14, select as select7 } from "@inquirer/prompts";
11806
- import fs19 from "fs/promises";
11807
- import path7 from "path";
12345
+ import fs20 from "fs/promises";
12346
+ import path8 from "path";
11808
12347
  import { existsSync as existsSync3 } from "fs";
11809
12348
  var PERFORMANCE_GROUPS2 = {
11810
12349
  telemetry: {
@@ -12024,8 +12563,8 @@ async function selectGroupsToApply2(action) {
12024
12563
  }
12025
12564
  }
12026
12565
  async function applyGroups2(groupKeys, action) {
12027
- const vscodeDir = path7.join(process.cwd(), ".vscode");
12028
- const settingsPath = path7.join(vscodeDir, "settings.json");
12566
+ const vscodeDir = path8.join(process.cwd(), ".vscode");
12567
+ const settingsPath = path8.join(vscodeDir, "settings.json");
12029
12568
  const invalidGroups = groupKeys.filter((key) => !PERFORMANCE_GROUPS2[key]);
12030
12569
  if (invalidGroups.length > 0) {
12031
12570
  console.log(`
@@ -12034,13 +12573,13 @@ async function applyGroups2(groupKeys, action) {
12034
12573
  return;
12035
12574
  }
12036
12575
  if (!existsSync3(vscodeDir)) {
12037
- await fs19.mkdir(vscodeDir, { recursive: true });
12576
+ await fs20.mkdir(vscodeDir, { recursive: true });
12038
12577
  console.log("\u{1F4C1} \u0110\xE3 t\u1EA1o th\u01B0 m\u1EE5c .vscode/");
12039
12578
  }
12040
12579
  let currentSettings = {};
12041
12580
  if (existsSync3(settingsPath)) {
12042
12581
  try {
12043
- const content = await fs19.readFile(settingsPath, "utf-8");
12582
+ const content = await fs20.readFile(settingsPath, "utf-8");
12044
12583
  currentSettings = JSON.parse(content);
12045
12584
  console.log("\u{1F4C4} \u0110\xE3 \u0111\u1ECDc c\xE0i \u0111\u1EB7t hi\u1EC7n t\u1EA1i t\u1EEB settings.json");
12046
12585
  } catch {
@@ -12080,14 +12619,14 @@ async function applyGroups2(groupKeys, action) {
12080
12619
  }
12081
12620
  }
12082
12621
  }
12083
- await fs19.writeFile(settingsPath, JSON.stringify(newSettings, null, 2));
12622
+ await fs20.writeFile(settingsPath, JSON.stringify(newSettings, null, 2));
12084
12623
  console.log(`
12085
12624
  \u2705 \u0110\xE3 c\u1EADp nh\u1EADt c\xE0i \u0111\u1EB7t VSCode t\u1EA1i: ${settingsPath}`);
12086
12625
  console.log("\u{1F4A1} M\u1EB9o: Kh\u1EDFi \u0111\u1ED9ng l\u1EA1i VSCode \u0111\u1EC3 \xE1p d\u1EE5ng c\xE1c thay \u0111\u1ED5i.");
12087
12626
  }
12088
12627
  async function resetSettings2(groupKeys) {
12089
- const vscodeDir = path7.join(process.cwd(), ".vscode");
12090
- const settingsPath = path7.join(vscodeDir, "settings.json");
12628
+ const vscodeDir = path8.join(process.cwd(), ".vscode");
12629
+ const settingsPath = path8.join(vscodeDir, "settings.json");
12091
12630
  if (!existsSync3(settingsPath)) {
12092
12631
  console.log("\n\u26A0\uFE0F Kh\xF4ng t\xECm th\u1EA5y file settings.json");
12093
12632
  return;
@@ -12101,7 +12640,7 @@ async function resetSettings2(groupKeys) {
12101
12640
  return;
12102
12641
  }
12103
12642
  if (groupKeys.length === 0) {
12104
- await fs19.unlink(settingsPath);
12643
+ await fs20.unlink(settingsPath);
12105
12644
  console.log("\n\u2705 \u0110\xE3 x\xF3a file settings.json");
12106
12645
  } else {
12107
12646
  await applyGroups2(groupKeys, "disable");