@musistudio/claude-code-router 1.0.45 → 1.0.47

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/Dockerfile ADDED
@@ -0,0 +1,7 @@
1
+ FROM node:20-alpine
2
+
3
+ RUN npm install -g @musistudio/claude-code-router
4
+
5
+ EXPOSE 3456
6
+
7
+ CMD ["ccr", "start"]
package/dist/cli.js CHANGED
@@ -1147,7 +1147,7 @@ var require_lib = __commonJS({
1147
1147
  });
1148
1148
 
1149
1149
  // src/constants.ts
1150
- var import_node_path, import_node_os, HOME_DIR, CONFIG_FILE, PLUGINS_DIR, PID_FILE, REFERENCE_COUNT_FILE, DEFAULT_CONFIG;
1150
+ var import_node_path, import_node_os, HOME_DIR, CONFIG_FILE, PLUGINS_DIR, PID_FILE, REFERENCE_COUNT_FILE;
1151
1151
  var init_constants = __esm({
1152
1152
  "src/constants.ts"() {
1153
1153
  "use strict";
@@ -1158,12 +1158,6 @@ var init_constants = __esm({
1158
1158
  PLUGINS_DIR = import_node_path.default.join(HOME_DIR, "plugins");
1159
1159
  PID_FILE = import_node_path.default.join(HOME_DIR, ".claude-code-router.pid");
1160
1160
  REFERENCE_COUNT_FILE = import_node_path.default.join(import_node_os.default.tmpdir(), "claude-code-reference-count.txt");
1161
- DEFAULT_CONFIG = {
1162
- LOG: false,
1163
- OPENAI_API_KEY: "",
1164
- OPENAI_BASE_URL: "",
1165
- OPENAI_MODEL: ""
1166
- };
1167
1161
  }
1168
1162
  });
1169
1163
 
@@ -1267,12 +1261,11 @@ __export(utils_exports, {
1267
1261
  readConfigFile: () => readConfigFile,
1268
1262
  writeConfigFile: () => writeConfigFile
1269
1263
  });
1270
- var import_promises2, import_node_readline, import_json5, import_node_path3, interpolateEnvVars, ensureDir, initDir, createReadline, question, readConfigFile, backupConfigFile, writeConfigFile, initConfig;
1264
+ var import_promises2, import_json5, import_node_path3, interpolateEnvVars, ensureDir, initDir, readConfigFile, backupConfigFile, writeConfigFile, initConfig;
1271
1265
  var init_utils = __esm({
1272
1266
  "src/utils/index.ts"() {
1273
1267
  "use strict";
1274
1268
  import_promises2 = __toESM(require("node:fs/promises"));
1275
- import_node_readline = __toESM(require("node:readline"));
1276
1269
  import_json5 = __toESM(require_lib());
1277
1270
  import_node_path3 = __toESM(require("node:path"));
1278
1271
  init_constants();
@@ -1307,21 +1300,6 @@ var init_utils = __esm({
1307
1300
  await ensureDir(PLUGINS_DIR);
1308
1301
  await ensureDir(import_node_path3.default.join(HOME_DIR, "logs"));
1309
1302
  };
1310
- createReadline = () => {
1311
- return import_node_readline.default.createInterface({
1312
- input: process.stdin,
1313
- output: process.stdout
1314
- });
1315
- };
1316
- question = (query) => {
1317
- return new Promise((resolve) => {
1318
- const rl = createReadline();
1319
- rl.question(query, (answer) => {
1320
- rl.close();
1321
- resolve(answer);
1322
- });
1323
- });
1324
- };
1325
1303
  readConfigFile = async () => {
1326
1304
  try {
1327
1305
  const config = await import_promises2.default.readFile(CONFIG_FILE, "utf-8");
@@ -1336,25 +1314,34 @@ var init_utils = __esm({
1336
1314
  }
1337
1315
  } catch (readError) {
1338
1316
  if (readError.code === "ENOENT") {
1339
- const name = await question("Enter Provider Name: ");
1340
- const APIKEY = await question("Enter Provider API KEY: ");
1341
- const baseUrl = await question("Enter Provider URL: ");
1342
- const model = await question("Enter MODEL Name: ");
1343
- const config = Object.assign({}, DEFAULT_CONFIG, {
1344
- Providers: [
1345
- {
1346
- name,
1347
- api_base_url: baseUrl,
1348
- api_key: APIKEY,
1349
- models: [model]
1350
- }
1351
- ],
1352
- Router: {
1353
- default: `${name},${model}`
1317
+ try {
1318
+ await initDir();
1319
+ const backupPath = await backupConfigFile();
1320
+ if (backupPath) {
1321
+ console.log(
1322
+ `Backed up existing configuration file to ${backupPath}`
1323
+ );
1354
1324
  }
1355
- });
1356
- await writeConfigFile(config);
1357
- return config;
1325
+ const config = {
1326
+ PORT: 3456,
1327
+ Providers: [],
1328
+ Router: {}
1329
+ };
1330
+ await writeConfigFile(config);
1331
+ console.log(
1332
+ "Created minimal default configuration file at ~/.claude-code-router/config.json"
1333
+ );
1334
+ console.log(
1335
+ "Please edit this file with your actual configuration."
1336
+ );
1337
+ return config;
1338
+ } catch (error) {
1339
+ console.error(
1340
+ "Failed to create default configuration:",
1341
+ error.message
1342
+ );
1343
+ process.exit(1);
1344
+ }
1358
1345
  } else {
1359
1346
  console.error(`Failed to read config file at ${CONFIG_FILE}`);
1360
1347
  console.error("Error details:", readError.message);
@@ -2076,7 +2063,7 @@ var require_is_promise_like = __commonJS({
2076
2063
  var require_plugin = __commonJS({
2077
2064
  "node_modules/.pnpm/avvio@9.1.0/node_modules/avvio/lib/plugin.js"(exports2, module2) {
2078
2065
  "use strict";
2079
- var { EventEmitter } = require("node:events");
2066
+ var { EventEmitter: EventEmitter2 } = require("node:events");
2080
2067
  var { inherits } = require("node:util");
2081
2068
  var { debug } = require_debug();
2082
2069
  var { createPromise } = require_create_promise();
@@ -2097,7 +2084,7 @@ var require_plugin = __commonJS({
2097
2084
  this._promise = null;
2098
2085
  this.startTime = null;
2099
2086
  }
2100
- inherits(Plugin, EventEmitter);
2087
+ inherits(Plugin, EventEmitter2);
2101
2088
  Plugin.prototype.exec = function(server, callback) {
2102
2089
  debug("exec", this.name);
2103
2090
  this.server = server;
@@ -5861,7 +5848,7 @@ var require_sonic_boom = __commonJS({
5861
5848
  "node_modules/.pnpm/sonic-boom@4.2.0/node_modules/sonic-boom/index.js"(exports2, module2) {
5862
5849
  "use strict";
5863
5850
  var fs7 = require("fs");
5864
- var EventEmitter = require("events");
5851
+ var EventEmitter2 = require("events");
5865
5852
  var inherits = require("util").inherits;
5866
5853
  var path7 = require("path");
5867
5854
  var sleep = require_atomic_sleep();
@@ -6088,7 +6075,7 @@ var require_sonic_boom = __commonJS({
6088
6075
  sonic._asyncDrainScheduled = false;
6089
6076
  sonic.emit("drain");
6090
6077
  }
6091
- inherits(SonicBoom, EventEmitter);
6078
+ inherits(SonicBoom, EventEmitter2);
6092
6079
  function mergeBuf(bufs, len) {
6093
6080
  if (bufs.length === 0) {
6094
6081
  return kEmptyBuffer;
@@ -6449,17 +6436,17 @@ var require_on_exit_leak_free = __commonJS({
6449
6436
  registry = new FinalizationRegistry(clear);
6450
6437
  }
6451
6438
  }
6452
- function install(event) {
6453
- if (refs[event].length > 0) {
6439
+ function install(event2) {
6440
+ if (refs[event2].length > 0) {
6454
6441
  return;
6455
6442
  }
6456
- process.on(event, functions[event]);
6443
+ process.on(event2, functions[event2]);
6457
6444
  }
6458
- function uninstall(event) {
6459
- if (refs[event].length > 0) {
6445
+ function uninstall(event2) {
6446
+ if (refs[event2].length > 0) {
6460
6447
  return;
6461
6448
  }
6462
- process.removeListener(event, functions[event]);
6449
+ process.removeListener(event2, functions[event2]);
6463
6450
  if (refs.exit.length === 0 && refs.beforeExit.length === 0) {
6464
6451
  registry = void 0;
6465
6452
  }
@@ -6470,33 +6457,33 @@ var require_on_exit_leak_free = __commonJS({
6470
6457
  function onBeforeExit() {
6471
6458
  callRefs("beforeExit");
6472
6459
  }
6473
- function callRefs(event) {
6474
- for (const ref of refs[event]) {
6460
+ function callRefs(event2) {
6461
+ for (const ref of refs[event2]) {
6475
6462
  const obj = ref.deref();
6476
6463
  const fn = ref.fn;
6477
6464
  if (obj !== void 0) {
6478
- fn(obj, event);
6465
+ fn(obj, event2);
6479
6466
  }
6480
6467
  }
6481
- refs[event] = [];
6468
+ refs[event2] = [];
6482
6469
  }
6483
6470
  function clear(ref) {
6484
- for (const event of ["exit", "beforeExit"]) {
6485
- const index = refs[event].indexOf(ref);
6486
- refs[event].splice(index, index + 1);
6487
- uninstall(event);
6471
+ for (const event2 of ["exit", "beforeExit"]) {
6472
+ const index = refs[event2].indexOf(ref);
6473
+ refs[event2].splice(index, index + 1);
6474
+ uninstall(event2);
6488
6475
  }
6489
6476
  }
6490
- function _register(event, obj, fn) {
6477
+ function _register(event2, obj, fn) {
6491
6478
  if (obj === void 0) {
6492
6479
  throw new Error("the object can't be undefined");
6493
6480
  }
6494
- install(event);
6481
+ install(event2);
6495
6482
  const ref = new WeakRef(obj);
6496
6483
  ref.fn = fn;
6497
6484
  ensureRegistry();
6498
6485
  registry.register(obj, ref);
6499
- refs[event].push(ref);
6486
+ refs[event2].push(ref);
6500
6487
  }
6501
6488
  function register(obj, fn) {
6502
6489
  _register("exit", obj, fn);
@@ -6509,12 +6496,12 @@ var require_on_exit_leak_free = __commonJS({
6509
6496
  return;
6510
6497
  }
6511
6498
  registry.unregister(obj);
6512
- for (const event of ["exit", "beforeExit"]) {
6513
- refs[event] = refs[event].filter((ref) => {
6499
+ for (const event2 of ["exit", "beforeExit"]) {
6500
+ refs[event2] = refs[event2].filter((ref) => {
6514
6501
  const _obj = ref.deref();
6515
6502
  return _obj && _obj !== obj;
6516
6503
  });
6517
- uninstall(event);
6504
+ uninstall(event2);
6518
6505
  }
6519
6506
  }
6520
6507
  module2.exports = {
@@ -6664,7 +6651,7 @@ var require_thread_stream = __commonJS({
6664
6651
  "node_modules/.pnpm/thread-stream@3.1.0/node_modules/thread-stream/index.js"(exports2, module2) {
6665
6652
  "use strict";
6666
6653
  var { version: version2 } = require_package();
6667
- var { EventEmitter } = require("events");
6654
+ var { EventEmitter: EventEmitter2 } = require("events");
6668
6655
  var { Worker } = require("worker_threads");
6669
6656
  var { join: join5 } = require("path");
6670
6657
  var { pathToFileURL } = require("url");
@@ -6820,7 +6807,7 @@ var require_thread_stream = __commonJS({
6820
6807
  stream.worker.off("exit", onWorkerExit);
6821
6808
  destroy(stream, code !== 0 ? new Error("the worker thread exited") : null);
6822
6809
  }
6823
- var ThreadStream = class extends EventEmitter {
6810
+ var ThreadStream = class extends EventEmitter2 {
6824
6811
  constructor(opts = {}) {
6825
6812
  super();
6826
6813
  if (opts.bufferSize < 4) {
@@ -7758,7 +7745,7 @@ var require_meta = __commonJS({
7758
7745
  var require_proto = __commonJS({
7759
7746
  "node_modules/.pnpm/pino@9.9.0/node_modules/pino/lib/proto.js"(exports2, module2) {
7760
7747
  "use strict";
7761
- var { EventEmitter } = require("node:events");
7748
+ var { EventEmitter: EventEmitter2 } = require("node:events");
7762
7749
  var {
7763
7750
  lsCacheSym,
7764
7751
  levelValSym,
@@ -7836,7 +7823,7 @@ var require_proto = __commonJS({
7836
7823
  [getLevelSym]: getLevel,
7837
7824
  [setLevelSym]: setLevel
7838
7825
  };
7839
- Object.setPrototypeOf(prototype, EventEmitter.prototype);
7826
+ Object.setPrototypeOf(prototype, EventEmitter2.prototype);
7840
7827
  module2.exports = function() {
7841
7828
  return Object.create(prototype);
7842
7829
  };
@@ -32316,7 +32303,7 @@ var require_request2 = __commonJS({
32316
32303
  var { createDeprecation } = require_process_warning2();
32317
32304
  var parseURL = require_parse_url();
32318
32305
  var { isFormDataLike, formDataToStream } = require_form_data();
32319
- var { EventEmitter } = require("node:events");
32306
+ var { EventEmitter: EventEmitter2 } = require("node:events");
32320
32307
  var FST_LIGHTMYREQUEST_DEP01 = createDeprecation({
32321
32308
  name: "FastifyDeprecationLightMyRequest",
32322
32309
  code: "FST_LIGHTMYREQUEST_DEP01",
@@ -32325,7 +32312,7 @@ var require_request2 = __commonJS({
32325
32312
  function hostHeaderFromURL(parsedURL) {
32326
32313
  return parsedURL.port ? parsedURL.host : parsedURL.hostname + (parsedURL.protocol === "https:" ? ":443" : ":80");
32327
32314
  }
32328
- var MockSocket = class extends EventEmitter {
32315
+ var MockSocket = class extends EventEmitter2 {
32329
32316
  constructor(remoteAddress) {
32330
32317
  super();
32331
32318
  this.remoteAddress = remoteAddress;
@@ -37404,10 +37391,10 @@ var require_wrap_handler = __commonJS({
37404
37391
  var require_dispatcher = __commonJS({
37405
37392
  "node_modules/.pnpm/undici@7.15.0/node_modules/undici/lib/dispatcher/dispatcher.js"(exports2, module2) {
37406
37393
  "use strict";
37407
- var EventEmitter = require("node:events");
37394
+ var EventEmitter2 = require("node:events");
37408
37395
  var WrapHandler = require_wrap_handler();
37409
37396
  var wrapInterceptor = (dispatch) => (opts, handler) => dispatch(opts, WrapHandler.wrap(handler));
37410
- var Dispatcher = class extends EventEmitter {
37397
+ var Dispatcher = class extends EventEmitter2 {
37411
37398
  dispatch() {
37412
37399
  throw new Error("not implemented");
37413
37400
  }
@@ -44750,29 +44737,29 @@ var require_readable = __commonJS({
44750
44737
  * @param {(...args: any[]) => void} listener
44751
44738
  * @returns {this}
44752
44739
  */
44753
- on(event, listener) {
44754
- if (event === "data" || event === "readable") {
44740
+ on(event2, listener) {
44741
+ if (event2 === "data" || event2 === "readable") {
44755
44742
  this[kReading] = true;
44756
44743
  this[kUsed] = true;
44757
44744
  }
44758
- return super.on(event, listener);
44745
+ return super.on(event2, listener);
44759
44746
  }
44760
44747
  /**
44761
44748
  * @param {string|symbol} event
44762
44749
  * @param {(...args: any[]) => void} listener
44763
44750
  * @returns {this}
44764
44751
  */
44765
- addListener(event, listener) {
44766
- return this.on(event, listener);
44752
+ addListener(event2, listener) {
44753
+ return this.on(event2, listener);
44767
44754
  }
44768
44755
  /**
44769
44756
  * @param {string|symbol} event
44770
44757
  * @param {(...args: any[]) => void} listener
44771
44758
  * @returns {this}
44772
44759
  */
44773
- off(event, listener) {
44774
- const ret = super.off(event, listener);
44775
- if (event === "data" || event === "readable") {
44760
+ off(event2, listener) {
44761
+ const ret = super.off(event2, listener);
44762
+ if (event2 === "data" || event2 === "readable") {
44776
44763
  this[kReading] = this.listenerCount("data") > 0 || this.listenerCount("readable") > 0;
44777
44764
  }
44778
44765
  return ret;
@@ -44782,8 +44769,8 @@ var require_readable = __commonJS({
44782
44769
  * @param {(...args: any[]) => void} listener
44783
44770
  * @returns {this}
44784
44771
  */
44785
- removeListener(event, listener) {
44786
- return this.off(event, listener);
44772
+ removeListener(event2, listener) {
44773
+ return this.off(event2, listener);
44787
44774
  }
44788
44775
  /**
44789
44776
  * @param {Buffer|null} chunk
@@ -49250,9 +49237,9 @@ var require_memory_cache_store = __commonJS({
49250
49237
  "node_modules/.pnpm/undici@7.15.0/node_modules/undici/lib/cache/memory-cache-store.js"(exports2, module2) {
49251
49238
  "use strict";
49252
49239
  var { Writable: Writable2 } = require("node:stream");
49253
- var { EventEmitter } = require("node:events");
49240
+ var { EventEmitter: EventEmitter2 } = require("node:events");
49254
49241
  var { assertCacheKey, assertCacheValue } = require_cache();
49255
- var MemoryCacheStore = class extends EventEmitter {
49242
+ var MemoryCacheStore = class extends EventEmitter2 {
49256
49243
  #maxCount = 1024;
49257
49244
  #maxSize = 104857600;
49258
49245
  // 100MB
@@ -54654,8 +54641,8 @@ var require_util8 = __commonJS({
54654
54641
  return readyState === states.CLOSED;
54655
54642
  }
54656
54643
  function fireEvent(e, target, eventFactory = (type, init) => new Event(type, init), eventInitDict = {}) {
54657
- const event = eventFactory(e, eventInitDict);
54658
- target.dispatchEvent(event);
54644
+ const event2 = eventFactory(e, eventInitDict);
54645
+ target.dispatchEvent(event2);
54659
54646
  }
54660
54647
  function websocketMessageReceived(handler, type, data) {
54661
54648
  handler.onMessage(type, data);
@@ -56551,7 +56538,7 @@ var require_eventsource_stream = __commonJS({
56551
56538
  * @param {Buffer} line
56552
56539
  * @param {EventSourceStreamEvent} event
56553
56540
  */
56554
- parseLine(line, event) {
56541
+ parseLine(line, event2) {
56555
56542
  if (line.length === 0) {
56556
56543
  return;
56557
56544
  }
@@ -56574,26 +56561,26 @@ var require_eventsource_stream = __commonJS({
56574
56561
  }
56575
56562
  switch (field) {
56576
56563
  case "data":
56577
- if (event[field] === void 0) {
56578
- event[field] = value;
56564
+ if (event2[field] === void 0) {
56565
+ event2[field] = value;
56579
56566
  } else {
56580
- event[field] += `
56567
+ event2[field] += `
56581
56568
  ${value}`;
56582
56569
  }
56583
56570
  break;
56584
56571
  case "retry":
56585
56572
  if (isASCIINumber(value)) {
56586
- event[field] = value;
56573
+ event2[field] = value;
56587
56574
  }
56588
56575
  break;
56589
56576
  case "id":
56590
56577
  if (isValidLastEventId(value)) {
56591
- event[field] = value;
56578
+ event2[field] = value;
56592
56579
  }
56593
56580
  break;
56594
56581
  case "event":
56595
56582
  if (value.length > 0) {
56596
- event[field] = value;
56583
+ event2[field] = value;
56597
56584
  }
56598
56585
  break;
56599
56586
  }
@@ -56601,18 +56588,18 @@ ${value}`;
56601
56588
  /**
56602
56589
  * @param {EventSourceStreamEvent} event
56603
56590
  */
56604
- processEvent(event) {
56605
- if (event.retry && isASCIINumber(event.retry)) {
56606
- this.state.reconnectionTime = parseInt(event.retry, 10);
56591
+ processEvent(event2) {
56592
+ if (event2.retry && isASCIINumber(event2.retry)) {
56593
+ this.state.reconnectionTime = parseInt(event2.retry, 10);
56607
56594
  }
56608
- if (event.id && isValidLastEventId(event.id)) {
56609
- this.state.lastEventId = event.id;
56595
+ if (event2.id && isValidLastEventId(event2.id)) {
56596
+ this.state.lastEventId = event2.id;
56610
56597
  }
56611
- if (event.data !== void 0) {
56598
+ if (event2.data !== void 0) {
56612
56599
  this.push({
56613
- type: event.event || "message",
56600
+ type: event2.event || "message",
56614
56601
  options: {
56615
- data: event.data,
56602
+ data: event2.data,
56616
56603
  lastEventId: this.state.lastEventId,
56617
56604
  origin: this.state.origin
56618
56605
  }
@@ -56790,10 +56777,10 @@ var require_eventsource = __commonJS({
56790
56777
  this.#state.origin = response.urlList[response.urlList.length - 1].origin;
56791
56778
  const eventSourceStream = new EventSourceStream({
56792
56779
  eventSourceSettings: this.#state,
56793
- push: (event) => {
56780
+ push: (event2) => {
56794
56781
  this.dispatchEvent(createFastMessageEvent(
56795
- event.type,
56796
- event.options
56782
+ event2.type,
56783
+ event2.options
56797
56784
  ));
56798
56785
  }
56799
56786
  });
@@ -66532,7 +66519,7 @@ var require_package3 = __commonJS({
66532
66519
  "package.json"(exports2, module2) {
66533
66520
  module2.exports = {
66534
66521
  name: "@musistudio/claude-code-router",
66535
- version: "1.0.45",
66522
+ version: "1.0.47",
66536
66523
  description: "Use Claude Code without an Anthropics account and route it to another LLM provider",
66537
66524
  bin: {
66538
66525
  ccr: "./dist/cli.js"
@@ -66552,11 +66539,11 @@ var require_package3 = __commonJS({
66552
66539
  license: "MIT",
66553
66540
  dependencies: {
66554
66541
  "@fastify/static": "^8.2.0",
66555
- "@musistudio/llms": "^1.0.30",
66542
+ "@musistudio/llms": "^1.0.31",
66556
66543
  dotenv: "^16.4.7",
66557
66544
  json5: "^2.2.3",
66558
66545
  openurl: "^1.1.1",
66559
- "pino-rotating-file-stream": "^0.0.2",
66546
+ "rotating-file-stream": "^3.2.7",
66560
66547
  tiktoken: "^1.0.21",
66561
66548
  uuid: "^11.1.0"
66562
66549
  },
@@ -67044,7 +67031,7 @@ var import_os = require("os");
67044
67031
  var import_path4 = require("path");
67045
67032
  init_utils();
67046
67033
 
67047
- // node_modules/.pnpm/@musistudio+llms@1.0.30_ws@8.18.3/node_modules/@musistudio/llms/dist/esm/server.mjs
67034
+ // node_modules/.pnpm/@musistudio+llms@1.0.31_ws@8.18.3/node_modules/@musistudio/llms/dist/esm/server.mjs
67048
67035
  var import_node_fs = require("node:fs");
67049
67036
  var import_node_path4 = require("node:path");
67050
67037
  var import_node_stream = __toESM(require("node:stream"), 1);
@@ -76071,26 +76058,26 @@ function Uf(r, e, t, n) {
76071
76058
  let l = { method: "POST", headers: o, body: JSON.stringify(e), signal: a };
76072
76059
  return t.httpsProxy && (l.dispatcher = new import_undici.ProxyAgent(new URL(t.httpsProxy).toString())), n?.debug({ request: l, headers: Object.fromEntries(o.entries()), requestUrl: typeof r == "string" ? r : r.toString(), useProxy: t.httpsProxy }, "final request"), fetch(typeof r == "string" ? r : r.toString(), l);
76073
76060
  }
76074
- var Mf = "1.0.30";
76061
+ var Mf = "1.0.31";
76075
76062
  async function a0(r, e, t, n) {
76076
76063
  let o = r.body, a = r.provider, u = t._server.providerService.getProvider(a);
76077
76064
  if (!u) throw nt(`Provider '${a}' not found`, 404, "provider_not_found");
76078
- let { requestBody: l, config: f, bypass: h } = await u0(o, u, n, r.headers), d = await l0(l, f, u, t, h, n), _ = await f0(l, d, u, n, h);
76065
+ let { requestBody: l, config: f, bypass: h } = await u0(o, u, n, r.headers, { req: r }), d = await l0(l, f, u, t, h, n), _ = await f0(l, d, u, n, h, { req: r });
76079
76066
  return d0(_, e, o);
76080
76067
  }
76081
- async function u0(r, e, t, n) {
76082
- let o = r, a = {}, u = false;
76083
- if (u = c0(e, t, r), u && (n instanceof Headers ? n.delete("content-length") : delete n["content-length"], a.headers = n), !u && typeof t.transformRequestOut == "function") {
76084
- let l = await t.transformRequestOut(o);
76085
- l.body ? (o = l.body, a = l.config || {}) : o = l;
76086
- }
76087
- if (!u && e.transformer?.use?.length) for (let l of e.transformer.use) {
76088
- if (!l || typeof l.transformRequestIn != "function") continue;
76089
- let f = await l.transformRequestIn(o, e);
76090
- f.body ? (o = f.body, a = { ...a, ...f.config }) : o = f;
76091
- }
76092
- if (!u && e.transformer?.[r.model]?.use?.length) for (let l of e.transformer[r.model].use) !l || typeof l.transformRequestIn != "function" || (o = await l.transformRequestIn(o, e));
76093
- return { requestBody: o, config: a, bypass: u };
76068
+ async function u0(r, e, t, n, o) {
76069
+ let a = r, u = {}, l = false;
76070
+ if (l = c0(e, t, r), l && (n instanceof Headers ? n.delete("content-length") : delete n["content-length"], u.headers = n), !l && typeof t.transformRequestOut == "function") {
76071
+ let f = await t.transformRequestOut(a);
76072
+ f.body ? (a = f.body, u = f.config || {}) : a = f;
76073
+ }
76074
+ if (!l && e.transformer?.use?.length) for (let f of e.transformer.use) {
76075
+ if (!f || typeof f.transformRequestIn != "function") continue;
76076
+ let h = await f.transformRequestIn(a, e, o);
76077
+ h.body ? (a = h.body, u = { ...u, ...h.config }) : a = h;
76078
+ }
76079
+ if (!l && e.transformer?.[r.model]?.use?.length) for (let f of e.transformer[r.model].use) !f || typeof f.transformRequestIn != "function" || (a = await f.transformRequestIn(a, e, o));
76080
+ return { requestBody: a, config: u, bypass: l };
76094
76081
  }
76095
76082
  function c0(r, e, t) {
76096
76083
  return r.transformer?.use?.length === 1 && r.transformer.use[0].name === e.name && (!r.transformer?.[t.model]?.use.length || r.transformer?.[t.model]?.use.length === 1 && r.transformer?.[t.model]?.use[0].name === e.name);
@@ -76112,11 +76099,11 @@ async function l0(r, e, t, n, o, a) {
76112
76099
  }
76113
76100
  return l;
76114
76101
  }
76115
- async function f0(r, e, t, n, o) {
76116
- let a = e;
76117
- if (!o && t.transformer?.use?.length) for (let u of Array.from(t.transformer.use).reverse()) !u || typeof u.transformResponseOut != "function" || (a = await u.transformResponseOut(a));
76118
- if (!o && t.transformer?.[r.model]?.use?.length) for (let u of Array.from(t.transformer[r.model].use).reverse()) !u || typeof u.transformResponseOut != "function" || (a = await u.transformResponseOut(a));
76119
- return !o && n.transformResponseIn && (a = await n.transformResponseIn(a)), a;
76102
+ async function f0(r, e, t, n, o, a) {
76103
+ let u = e;
76104
+ if (!o && t.transformer?.use?.length) for (let l of Array.from(t.transformer.use).reverse()) !l || typeof l.transformResponseOut != "function" || (u = await l.transformResponseOut(u, a));
76105
+ if (!o && t.transformer?.[r.model]?.use?.length) for (let l of Array.from(t.transformer[r.model].use).reverse()) !l || typeof l.transformResponseOut != "function" || (u = await l.transformResponseOut(u, a));
76106
+ return !o && n.transformResponseIn && (u = await n.transformResponseIn(u, a)), u;
76120
76107
  }
76121
76108
  function d0(r, e, t) {
76122
76109
  return r.ok || e.code(r.status), t.stream === true ? (e.header("Content-Type", "text/event-stream"), e.header("Cache-Control", "no-cache"), e.header("Connection", "keep-alive"), e.send(r.body)) : r.json();
@@ -76655,7 +76642,7 @@ data: ${JSON.stringify(Z)}
76655
76642
  l = { text: a.function.arguments || "" };
76656
76643
  }
76657
76644
  n.push({ type: "tool_use", id: a.id, name: a.function.name, input: l });
76658
- });
76645
+ }), t.message?.thinking?.content && n.push({ type: "thinking", thinking: t.message.thinking.content, signature: t.message.thinking.signature });
76659
76646
  let o = { id: e.id, type: "message", role: "assistant", model: e.model, content: n, stop_reason: t.finish_reason === "stop" ? "end_turn" : t.finish_reason === "length" ? "max_tokens" : t.finish_reason === "tool_calls" ? "tool_use" : t.finish_reason === "content_filter" ? "stop_sequence" : "end_turn", stop_sequence: null, usage: { input_tokens: e.usage?.prompt_tokens || 0, output_tokens: e.usage?.completion_tokens || 0 } };
76660
76647
  return this.logger.debug({ result: o }, "Conversion complete, final Anthropic response"), o;
76661
76648
  } catch {
@@ -77826,7 +77813,7 @@ var Zi = class {
77826
77813
  this.options = e;
77827
77814
  this.max_tokens = this.options?.max_tokens, this.temperature = this.options?.temperature, this.top_p = this.options?.top_p, this.top_k = this.options?.top_k, this.repetition_penalty = this.options?.repetition_penalty;
77828
77815
  }
77829
- name = "sampling";
77816
+ static TransformerName = "sampling";
77830
77817
  max_tokens;
77831
77818
  temperature;
77832
77819
  top_p;
@@ -78395,7 +78382,8 @@ var getUseModel = async (req, tokenCount, config, lastUsage) => {
78395
78382
  }
78396
78383
  return config.Router.default;
78397
78384
  };
78398
- var router = async (req, _res, config) => {
78385
+ var router = async (req, _res, context) => {
78386
+ const { config, event: event2 } = context;
78399
78387
  if (req.body.metadata?.user_id) {
78400
78388
  const parts = req.body.metadata.user_id.split("_session_");
78401
78389
  if (parts.length > 1) {
@@ -78415,7 +78403,9 @@ var router = async (req, _res, config) => {
78415
78403
  try {
78416
78404
  const customRouter = require(config.CUSTOM_ROUTER_PATH);
78417
78405
  req.tokenCount = tokenCount;
78418
- model = await customRouter(req, config);
78406
+ model = await customRouter(req, config, {
78407
+ event: event2
78408
+ });
78419
78409
  } catch (e) {
78420
78410
  log2("failed to load custom router", e.message);
78421
78411
  }
@@ -78577,7 +78567,7 @@ async function getServiceInfo() {
78577
78567
  // src/index.ts
78578
78568
  init_constants();
78579
78569
 
78580
- // node_modules/.pnpm/rotating-file-stream@3.2.6/node_modules/rotating-file-stream/dist/esm/index.js
78570
+ // node_modules/.pnpm/rotating-file-stream@3.2.7/node_modules/rotating-file-stream/dist/esm/index.js
78581
78571
  var import_child_process2 = require("child_process");
78582
78572
  var import_zlib = require("zlib");
78583
78573
  var import_stream = require("stream");
@@ -79175,20 +79165,6 @@ function createStream(filename, options) {
79175
79165
  return new RotatingFileStream(generator, opts);
79176
79166
  }
79177
79167
 
79178
- // node_modules/.pnpm/pino-rotating-file-stream@0.0.2/node_modules/pino-rotating-file-stream/dist/mjs/index.js
79179
- var mjs_default = (options) => {
79180
- const { filename, size, maxSize, interval, compress, path: path7, ...otherOptions } = options;
79181
- return createStream(filename, {
79182
- ...otherOptions,
79183
- // Set some default values
79184
- size: size || "100M",
79185
- maxSize: maxSize || "1G",
79186
- interval: interval || "7d",
79187
- compress: compress || "gzip",
79188
- path: path7
79189
- });
79190
- };
79191
-
79192
79168
  // src/index.ts
79193
79169
  init_constants();
79194
79170
 
@@ -79205,9 +79181,9 @@ var SSEParserTransform = class extends TransformStream {
79205
79181
  const lines = this.buffer.split("\n");
79206
79182
  this.buffer = lines.pop() || "";
79207
79183
  for (const line of lines) {
79208
- const event = this.processLine(line);
79209
- if (event) {
79210
- controller.enqueue(event);
79184
+ const event2 = this.processLine(line);
79185
+ if (event2) {
79186
+ controller.enqueue(event2);
79211
79187
  }
79212
79188
  }
79213
79189
  },
@@ -79215,7 +79191,7 @@ var SSEParserTransform = class extends TransformStream {
79215
79191
  if (this.buffer.trim()) {
79216
79192
  const events = [];
79217
79193
  this.processLine(this.buffer.trim(), events);
79218
- events.forEach((event) => controller.enqueue(event));
79194
+ events.forEach((event2) => controller.enqueue(event2));
79219
79195
  }
79220
79196
  if (Object.keys(this.currentEvent).length > 0) {
79221
79197
  controller.enqueue(this.currentEvent);
@@ -79226,13 +79202,13 @@ var SSEParserTransform = class extends TransformStream {
79226
79202
  processLine(line, events) {
79227
79203
  if (!line.trim()) {
79228
79204
  if (Object.keys(this.currentEvent).length > 0) {
79229
- const event = { ...this.currentEvent };
79205
+ const event2 = { ...this.currentEvent };
79230
79206
  this.currentEvent = {};
79231
79207
  if (events) {
79232
- events.push(event);
79208
+ events.push(event2);
79233
79209
  return null;
79234
79210
  }
79235
- return event;
79211
+ return event2;
79236
79212
  }
79237
79213
  return null;
79238
79214
  }
@@ -79262,25 +79238,25 @@ var SSEParserTransform = class extends TransformStream {
79262
79238
  var SSESerializerTransform = class extends TransformStream {
79263
79239
  constructor() {
79264
79240
  super({
79265
- transform: (event, controller) => {
79241
+ transform: (event2, controller) => {
79266
79242
  let output = "";
79267
- if (event.event) {
79268
- output += `event: ${event.event}
79243
+ if (event2.event) {
79244
+ output += `event: ${event2.event}
79269
79245
  `;
79270
79246
  }
79271
- if (event.id) {
79272
- output += `id: ${event.id}
79247
+ if (event2.id) {
79248
+ output += `id: ${event2.id}
79273
79249
  `;
79274
79250
  }
79275
- if (event.retry) {
79276
- output += `retry: ${event.retry}
79251
+ if (event2.retry) {
79252
+ output += `retry: ${event2.retry}
79277
79253
  `;
79278
79254
  }
79279
- if (event.data) {
79280
- if (event.data.type === "done") {
79255
+ if (event2.data) {
79256
+ if (event2.data.type === "done") {
79281
79257
  output += "data: [DONE]\n";
79282
79258
  } else {
79283
- output += `data: ${JSON.stringify(event.data)}
79259
+ output += `data: ${JSON.stringify(event2.data)}
79284
79260
  `;
79285
79261
  }
79286
79262
  }
@@ -80927,6 +80903,8 @@ agentsManager.registerAgent(imageAgent);
80927
80903
  var agents_default = agentsManager;
80928
80904
 
80929
80905
  // src/index.ts
80906
+ var import_node_events = require("node:events");
80907
+ var event = new import_node_events.EventEmitter();
80930
80908
  async function initializeClaudeConfig() {
80931
80909
  const homeDir = (0, import_os.homedir)();
80932
80910
  const configPath = (0, import_path4.join)(homeDir, ".claude.json");
@@ -80973,13 +80951,24 @@ async function run(options = {}) {
80973
80951
  process.exit(0);
80974
80952
  });
80975
80953
  const servicePort = process.env.SERVICE_PORT ? parseInt(process.env.SERVICE_PORT) : port;
80954
+ const pad = (num) => (num > 9 ? "" : "0") + num;
80955
+ const generator = (time, index) => {
80956
+ if (!time) {
80957
+ time = /* @__PURE__ */ new Date();
80958
+ }
80959
+ var month = time.getFullYear() + "" + pad(time.getMonth() + 1);
80960
+ var day = pad(time.getDate());
80961
+ var hour = pad(time.getHours());
80962
+ var minute = pad(time.getMinutes());
80963
+ return `./logs/ccr-${month}${day}${hour}${minute}${pad(time.getSeconds())}${index ? `_${index}` : ""}.log`;
80964
+ };
80976
80965
  const loggerConfig = config.LOG !== false ? {
80977
80966
  level: config.LOG_LEVEL || "debug",
80978
- stream: mjs_default({
80967
+ stream: createStream(generator, {
80979
80968
  path: HOME_DIR,
80980
- filename: config.LOGNAME || `./logs/ccr-${+/* @__PURE__ */ new Date()}.log`,
80981
80969
  maxFiles: 3,
80982
- interval: "1d"
80970
+ interval: "1d",
80971
+ compress: "gzip"
80983
80972
  })
80984
80973
  } : false;
80985
80974
  const server = createServer({
@@ -81030,13 +81019,20 @@ async function run(options = {}) {
81030
81019
  if (useAgents.length) {
81031
81020
  req.agents = useAgents;
81032
81021
  }
81033
- await router(req, reply, config);
81022
+ await router(req, reply, {
81023
+ config,
81024
+ event
81025
+ });
81034
81026
  }
81035
81027
  });
81036
- server.addHook("onSend", async (req, reply, payload) => {
81028
+ server.addHook("onError", async (request, reply, error) => {
81029
+ event.emit("onError", request, reply, error);
81030
+ });
81031
+ server.addHook("onSend", (req, reply, payload, done) => {
81037
81032
  if (req.sessionId && req.url.startsWith("/v1/messages")) {
81038
81033
  if (payload instanceof ReadableStream) {
81039
81034
  if (req.agents) {
81035
+ const abortController = new AbortController();
81040
81036
  const eventStream = payload.pipeThrough(new SSEParserTransform());
81041
81037
  let currentAgent;
81042
81038
  let currentToolIndex = -1;
@@ -81045,110 +81041,151 @@ async function run(options = {}) {
81045
81041
  let currentToolId = "";
81046
81042
  const toolMessages = [];
81047
81043
  const assistantMessages = [];
81048
- return rewriteStream(eventStream, async (data, controller) => {
81049
- if (data.event === "content_block_start" && data?.data?.content_block?.name) {
81050
- const agent = req.agents.find((name) => agents_default.getAgent(name)?.tools.get(data.data.content_block.name));
81051
- if (agent) {
81052
- currentAgent = agents_default.getAgent(agent);
81053
- currentToolIndex = data.data.index;
81054
- currentToolName = data.data.content_block.name;
81055
- currentToolId = data.data.content_block.id;
81044
+ return done(null, rewriteStream(eventStream, async (data, controller) => {
81045
+ try {
81046
+ if (data.event === "content_block_start" && data?.data?.content_block?.name) {
81047
+ const agent = req.agents.find((name) => agents_default.getAgent(name)?.tools.get(data.data.content_block.name));
81048
+ if (agent) {
81049
+ currentAgent = agents_default.getAgent(agent);
81050
+ currentToolIndex = data.data.index;
81051
+ currentToolName = data.data.content_block.name;
81052
+ currentToolId = data.data.content_block.id;
81053
+ return void 0;
81054
+ }
81055
+ }
81056
+ if (currentToolIndex > -1 && data.data.index === currentToolIndex && data.data?.delta?.type === "input_json_delta") {
81057
+ currentToolArgs += data.data?.delta?.partial_json;
81056
81058
  return void 0;
81057
81059
  }
81058
- }
81059
- if (currentToolIndex > -1 && data.data.index === currentToolIndex && data.data?.delta?.type === "input_json_delta") {
81060
- currentToolArgs += data.data?.delta?.partial_json;
81061
- return void 0;
81062
- }
81063
- if (currentToolIndex > -1 && data.data.index === currentToolIndex && data.data.type === "content_block_stop") {
81064
- try {
81065
- const args = import_json52.default.parse(currentToolArgs);
81066
- assistantMessages.push({
81067
- type: "tool_use",
81068
- id: currentToolId,
81069
- name: currentToolName,
81070
- input: args
81060
+ if (currentToolIndex > -1 && data.data.index === currentToolIndex && data.data.type === "content_block_stop") {
81061
+ try {
81062
+ const args = import_json52.default.parse(currentToolArgs);
81063
+ assistantMessages.push({
81064
+ type: "tool_use",
81065
+ id: currentToolId,
81066
+ name: currentToolName,
81067
+ input: args
81068
+ });
81069
+ const toolResult = await currentAgent?.tools.get(currentToolName)?.handler(args, {
81070
+ req,
81071
+ config
81072
+ });
81073
+ console.log("result", toolResult);
81074
+ toolMessages.push({
81075
+ "tool_use_id": currentToolId,
81076
+ "type": "tool_result",
81077
+ "content": toolResult
81078
+ });
81079
+ currentAgent = void 0;
81080
+ currentToolIndex = -1;
81081
+ currentToolName = "";
81082
+ currentToolArgs = "";
81083
+ currentToolId = "";
81084
+ } catch (e) {
81085
+ console.log(e);
81086
+ }
81087
+ return void 0;
81088
+ }
81089
+ if (data.event === "message_delta" && toolMessages.length) {
81090
+ req.body.messages.push({
81091
+ role: "assistant",
81092
+ content: assistantMessages
81071
81093
  });
81072
- const toolResult = await currentAgent?.tools.get(currentToolName)?.handler(args, {
81073
- req,
81074
- config
81094
+ req.body.messages.push({
81095
+ role: "user",
81096
+ content: toolMessages
81075
81097
  });
81076
- console.log("result", toolResult);
81077
- toolMessages.push({
81078
- "tool_use_id": currentToolId,
81079
- "type": "tool_result",
81080
- "content": toolResult
81098
+ const response = await fetch(`http://127.0.0.1:${config.PORT}/v1/messages`, {
81099
+ method: "POST",
81100
+ headers: {
81101
+ "x-api-key": config.APIKEY,
81102
+ "content-type": "application/json"
81103
+ },
81104
+ body: JSON.stringify(req.body)
81081
81105
  });
81082
- currentAgent = void 0;
81083
- currentToolIndex = -1;
81084
- currentToolName = "";
81085
- currentToolArgs = "";
81086
- currentToolId = "";
81087
- } catch (e) {
81088
- console.log(e);
81089
- }
81090
- return void 0;
81091
- }
81092
- if (data.event === "message_delta" && toolMessages.length) {
81093
- req.body.messages.push({
81094
- role: "assistant",
81095
- content: assistantMessages
81096
- });
81097
- req.body.messages.push({
81098
- role: "user",
81099
- content: toolMessages
81100
- });
81101
- const response = await fetch(`http://127.0.0.1:${config.PORT}/v1/messages`, {
81102
- method: "POST",
81103
- headers: {
81104
- "x-api-key": config.APIKEY,
81105
- "content-type": "application/json"
81106
- },
81107
- body: JSON.stringify(req.body)
81108
- });
81109
- if (!response.ok) {
81110
- return void 0;
81111
- }
81112
- const stream = response.body.pipeThrough(new SSEParserTransform());
81113
- const reader = stream.getReader();
81114
- while (true) {
81115
- const { value, done } = await reader.read();
81116
- if (done) {
81117
- break;
81106
+ if (!response.ok) {
81107
+ return void 0;
81118
81108
  }
81119
- if (["message_start", "message_stop"].includes(value.event)) {
81120
- continue;
81109
+ const stream = response.body.pipeThrough(new SSEParserTransform());
81110
+ const reader = stream.getReader();
81111
+ while (true) {
81112
+ try {
81113
+ const { value, done: done2 } = await reader.read();
81114
+ if (done2) {
81115
+ break;
81116
+ }
81117
+ if (["message_start", "message_stop"].includes(value.event)) {
81118
+ continue;
81119
+ }
81120
+ if (!controller.desiredSize) {
81121
+ console.log("Stream backpressure detected");
81122
+ break;
81123
+ }
81124
+ controller.enqueue(value);
81125
+ } catch (readError) {
81126
+ if (readError.name === "AbortError" || readError.code === "ERR_STREAM_PREMATURE_CLOSE") {
81127
+ console.log("Stream reading aborted due to client disconnect");
81128
+ abortController.abort();
81129
+ break;
81130
+ }
81131
+ throw readError;
81132
+ }
81121
81133
  }
81122
- controller.enqueue(value);
81134
+ return void 0;
81123
81135
  }
81124
- return void 0;
81136
+ return data;
81137
+ } catch (error) {
81138
+ console.error("Unexpected error in stream processing:", error);
81139
+ if (error.code === "ERR_STREAM_PREMATURE_CLOSE") {
81140
+ console.log("Stream prematurely closed, aborting operations");
81141
+ abortController.abort();
81142
+ return void 0;
81143
+ }
81144
+ throw error;
81125
81145
  }
81126
- return data;
81127
- }).pipeThrough(new SSESerializerTransform());
81146
+ }).pipeThrough(new SSESerializerTransform()));
81128
81147
  }
81129
81148
  const [originalStream, clonedStream] = payload.tee();
81130
81149
  const read = async (stream) => {
81131
81150
  const reader = stream.getReader();
81132
- while (true) {
81133
- const { done, value } = await reader.read();
81134
- if (done) break;
81135
- const dataStr = new TextDecoder().decode(value);
81136
- if (!dataStr.startsWith("event: message_delta")) {
81137
- continue;
81151
+ try {
81152
+ while (true) {
81153
+ const { done: done2, value } = await reader.read();
81154
+ if (done2) break;
81155
+ const dataStr = new TextDecoder().decode(value);
81156
+ if (!dataStr.startsWith("event: message_delta")) {
81157
+ continue;
81158
+ }
81159
+ const str = dataStr.slice(27);
81160
+ try {
81161
+ const message = JSON.parse(str);
81162
+ sessionUsageCache.put(req.sessionId, message.usage);
81163
+ } catch {
81164
+ }
81138
81165
  }
81139
- const str = dataStr.slice(27);
81140
- try {
81141
- const message = JSON.parse(str);
81142
- sessionUsageCache.put(req.sessionId, message.usage);
81143
- } catch {
81166
+ } catch (readError) {
81167
+ if (readError.name === "AbortError" || readError.code === "ERR_STREAM_PREMATURE_CLOSE") {
81168
+ console.log("Background read stream closed prematurely");
81169
+ } else {
81170
+ console.error("Error in background stream reading:", readError);
81144
81171
  }
81172
+ } finally {
81173
+ reader.releaseLock();
81145
81174
  }
81146
81175
  };
81147
81176
  read(clonedStream);
81148
- return originalStream;
81177
+ return done(null, originalStream);
81149
81178
  }
81150
81179
  sessionUsageCache.put(req.sessionId, payload.usage);
81151
81180
  }
81181
+ if (typeof payload === "object" && payload.error) {
81182
+ return done(payload.error, null);
81183
+ }
81184
+ done(null, payload);
81185
+ });
81186
+ server.addHook("onSend", async (req, reply, payload) => {
81187
+ console.log("\u4E3B\u5E94\u7528onSend");
81188
+ event.emit("onSend", req, reply, payload);
81152
81189
  return payload;
81153
81190
  });
81154
81191
  server.start();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@musistudio/claude-code-router",
3
- "version": "1.0.45",
3
+ "version": "1.0.47",
4
4
  "description": "Use Claude Code without an Anthropics account and route it to another LLM provider",
5
5
  "bin": {
6
6
  "ccr": "./dist/cli.js"
@@ -20,11 +20,11 @@
20
20
  "license": "MIT",
21
21
  "dependencies": {
22
22
  "@fastify/static": "^8.2.0",
23
- "@musistudio/llms": "^1.0.30",
23
+ "@musistudio/llms": "^1.0.31",
24
24
  "dotenv": "^16.4.7",
25
25
  "json5": "^2.2.3",
26
26
  "openurl": "^1.1.1",
27
- "pino-rotating-file-stream": "^0.0.2",
27
+ "rotating-file-stream": "^3.2.7",
28
28
  "tiktoken": "^1.0.21",
29
29
  "uuid": "^11.1.0"
30
30
  },
package/dockerfile DELETED
@@ -1,24 +0,0 @@
1
- FROM node:20-alpine
2
-
3
- WORKDIR /app
4
-
5
- # Copy all files
6
- COPY . .
7
-
8
- # Install pnpm globally
9
- RUN npm install -g pnpm
10
-
11
- # Install dependencies
12
- RUN pnpm install --frozen-lockfile
13
-
14
- # Fix rollup optional dependencies issue
15
- RUN cd ui && npm install
16
-
17
- # Build the entire project including UI
18
- RUN pnpm run build
19
-
20
- # Expose port
21
- EXPOSE 3456
22
-
23
- # Start the router service
24
- CMD ["node", "dist/cli.js", "start"]