diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index ea1c4bb0..c2b9df2a 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -8,49 +8,40 @@ on: - main jobs: - unit-test: + linux-tests: runs-on: ubuntu-latest - strategy: - matrix: - node-version: [20.x] steps: - uses: actions/checkout@v4 - - name: Use Node.js ${{ matrix.node-version }} - uses: actions/setup-node@v3 + - name: Use Node.js + uses: actions/setup-node@v4 with: - node-version: ${{ matrix.node-version }} + node-version: '20.x' cache: 'npm' + - name: Setup git + run: | + git config --global user.email "test@example.com" + git config --global user.name "Test User" - name: Install dependencies - run: npm install + run: npm ci - name: Run Unit Tests run: npm run test:unit - e2e-test: - runs-on: ubuntu-latest - strategy: - matrix: - node-version: [20.x] + - name: Run Core E2E Tests + run: npm run test:e2e:core + - name: Run Prompt Module E2E Tests + run: npm run test:e2e:prompt-module + macos-smoke: + runs-on: macos-latest steps: - uses: actions/checkout@v4 - - name: Use Node.js ${{ matrix.node-version }} - uses: actions/setup-node@v3 + - name: Use Node.js + uses: actions/setup-node@v4 with: - node-version: ${{ matrix.node-version }} + node-version: '20.x' cache: 'npm' - - name: Install git - run: | - sudo apt-get update - sudo apt-get install -y git - git --version - - name: Setup git - run: | - git config --global user.email "test@example.com" - git config --global user.name "Test User" - name: Install dependencies - run: npm install - - name: Build - run: npm run build - - name: Run E2E Tests - run: npm run test:e2e + run: npm ci + - name: Run Smoke E2E Tests + run: npm run test:e2e:smoke prettier: runs-on: ubuntu-latest steps: diff --git a/README.md b/README.md index bb0fafe1..d2cbe375 100644 --- a/README.md +++ b/README.md @@ -245,7 +245,13 @@ If you are behind a proxy, you can set it in the config: oco config set OCO_PROXY=http://127.0.0.1:7890 ``` -Or it will automatically use `HTTPS_PROXY` or `HTTP_PROXY` environment variables. +If `OCO_PROXY` is unset, OpenCommit will automatically use `HTTPS_PROXY` or `HTTP_PROXY` environment variables. + +To explicitly disable proxy use for OpenCommit, even when those environment variables are set: + +```sh +oco config set OCO_PROXY=null +``` ### Locale configuration diff --git a/out/cli.cjs b/out/cli.cjs index 49094676..49706822 100755 --- a/out/cli.cjs +++ b/out/cli.cjs @@ -1545,7 +1545,7 @@ var require_signal_exit = __commonJS({ }; }; } else { - assert2 = require("assert"); + assert = require("assert"); signals = require_signals(); isWin = /^win/i.test(process9.platform); EE = require("events"); @@ -1568,7 +1568,7 @@ var require_signal_exit = __commonJS({ return function() { }; } - assert2.equal(typeof cb, "function", "a callback must be provided for exit handler"); + assert.equal(typeof cb, "function", "a callback must be provided for exit handler"); if (loaded === false) { load(); } @@ -1674,7 +1674,7 @@ var require_signal_exit = __commonJS({ } }; } - var assert2; + var assert; var signals; var isWin; var EE; @@ -2043,11 +2043,11 @@ var require_main = __commonJS({ function configDotenv(options) { const dotenvPath = path5.resolve(process.cwd(), ".env"); let encoding = "utf8"; - const debug5 = Boolean(options && options.debug); + const debug3 = Boolean(options && options.debug); if (options && options.encoding) { encoding = options.encoding; } else { - if (debug5) { + if (debug3) { _debug("No encoding is specified. UTF-8 is used by default"); } } @@ -2069,7 +2069,7 @@ var require_main = __commonJS({ const parsed = DotenvModule.parse(fs7.readFileSync(path6, { encoding })); DotenvModule.populate(parsedAll, parsed, options); } catch (e3) { - if (debug5) { + if (debug3) { _debug(`Failed to load ${path6} ${e3.message}`); } lastError = e3; @@ -2125,7 +2125,7 @@ var require_main = __commonJS({ } } function populate(processEnv, parsed, options = {}) { - const debug5 = Boolean(options && options.debug); + const debug3 = Boolean(options && options.debug); const override = Boolean(options && options.override); if (typeof parsed !== "object") { const err = new Error("OBJECT_REQUIRED: Please check the processEnv argument being passed to populate"); @@ -2137,7 +2137,7 @@ var require_main = __commonJS({ if (override === true) { processEnv[key] = parsed[key]; } - if (debug5) { + if (debug3) { if (override === true) { _debug(`"${key}" is already defined and WAS overwritten`); } else { @@ -5581,7 +5581,7 @@ var require_URL = __commonJS({ return; } const ctorRegistry = utils.initCtorRegistry(globalObject); - class URL3 { + class URL2 { constructor(url2) { if (arguments.length < 1) { throw new globalObject.TypeError( @@ -5874,7 +5874,7 @@ var require_URL = __commonJS({ return Impl.implementation.canParse(...args); } } - Object.defineProperties(URL3.prototype, { + Object.defineProperties(URL2.prototype, { toJSON: { enumerable: true }, href: { enumerable: true }, toString: { enumerable: true }, @@ -5891,18 +5891,18 @@ var require_URL = __commonJS({ hash: { enumerable: true }, [Symbol.toStringTag]: { value: "URL", configurable: true } }); - Object.defineProperties(URL3, { parse: { enumerable: true }, canParse: { enumerable: true } }); - ctorRegistry[interfaceName] = URL3; + Object.defineProperties(URL2, { parse: { enumerable: true }, canParse: { enumerable: true } }); + ctorRegistry[interfaceName] = URL2; Object.defineProperty(globalObject, interfaceName, { configurable: true, writable: true, - value: URL3 + value: URL2 }); if (globalNames.includes("Window")) { Object.defineProperty(globalObject, "webkitURL", { configurable: true, writable: true, - value: URL3 + value: URL2 }); } }; @@ -5914,9 +5914,9 @@ var require_URL = __commonJS({ var require_webidl2js_wrapper = __commonJS({ "node_modules/whatwg-url/webidl2js-wrapper.js"(exports2) { "use strict"; - var URL3 = require_URL(); + var URL2 = require_URL(); var URLSearchParams2 = require_URLSearchParams(); - exports2.URL = URL3; + exports2.URL = URL2; exports2.URLSearchParams = URLSearchParams2; } }); @@ -5925,11 +5925,11 @@ var require_webidl2js_wrapper = __commonJS({ var require_whatwg_url = __commonJS({ "node_modules/whatwg-url/index.js"(exports2) { "use strict"; - var { URL: URL3, URLSearchParams: URLSearchParams2 } = require_webidl2js_wrapper(); + var { URL: URL2, URLSearchParams: URLSearchParams2 } = require_webidl2js_wrapper(); var urlStateMachine = require_url_state_machine(); var percentEncoding = require_percent_encoding(); var sharedGlobalObject = { Array, Object, Promise, String, TypeError }; - URL3.install(sharedGlobalObject, ["Window"]); + URL2.install(sharedGlobalObject, ["Window"]); URLSearchParams2.install(sharedGlobalObject, ["Window"]); exports2.URL = sharedGlobalObject.URL; exports2.URLSearchParams = sharedGlobalObject.URLSearchParams; @@ -5958,7 +5958,7 @@ var require_lib2 = __commonJS({ return ex && typeof ex === "object" && "default" in ex ? ex["default"] : ex; } var Stream3 = _interopDefault(require("stream")); - var http4 = _interopDefault(require("http")); + var http3 = _interopDefault(require("http")); var Url = _interopDefault(require("url")); var whatwgUrl = _interopDefault(require_whatwg_url()); var https3 = _interopDefault(require("https")); @@ -6628,10 +6628,10 @@ var require_lib2 = __commonJS({ return [k7.toLowerCase(), headers[MAP][k7].join(", ")]; }); } - var INTERNAL2 = Symbol("internal"); + var INTERNAL = Symbol("internal"); function createHeadersIterator(target, kind3) { const iterator2 = Object.create(HeadersIteratorPrototype); - iterator2[INTERNAL2] = { + iterator2[INTERNAL] = { target, kind: kind3, index: 0 @@ -6643,7 +6643,7 @@ var require_lib2 = __commonJS({ if (!this || Object.getPrototypeOf(this) !== HeadersIteratorPrototype) { throw new TypeError("Value of `this` is not a HeadersIterator"); } - var _INTERNAL = this[INTERNAL2]; + var _INTERNAL = this[INTERNAL]; const target = _INTERNAL.target, kind3 = _INTERNAL.kind, index = _INTERNAL.index; const values = getHeaders2(target, kind3); const len = values.length; @@ -6653,7 +6653,7 @@ var require_lib2 = __commonJS({ done: true }; } - this[INTERNAL2].index = index + 1; + this[INTERNAL].index = index + 1; return { value: values[index], done: false @@ -6698,7 +6698,7 @@ var require_lib2 = __commonJS({ return headers; } var INTERNALS$1 = Symbol("Response internals"); - var STATUS_CODES = http4.STATUS_CODES; + var STATUS_CODES = http3.STATUS_CODES; var Response6 = class _Response { constructor() { let body = arguments.length > 0 && arguments[0] !== void 0 ? arguments[0] : null; @@ -6774,12 +6774,12 @@ var require_lib2 = __commonJS({ configurable: true }); var INTERNALS$2 = Symbol("Request internals"); - var URL3 = Url.URL || whatwgUrl.URL; + var URL2 = Url.URL || whatwgUrl.URL; var parse_url = Url.parse; var format_url = Url.format; function parseURL(urlStr) { if (/^[a-zA-Z][a-zA-Z\d+\-.]*:/.exec(urlStr)) { - urlStr = new URL3(urlStr).toString(); + urlStr = new URL2(urlStr).toString(); } return parse_url(urlStr); } @@ -6951,7 +6951,7 @@ var require_lib2 = __commonJS({ return new fetch4.Promise(function(resolve, reject) { const request3 = new Request6(url2, opts); const options = getNodeRequestOptions(request3); - const send = (options.protocol === "https:" ? https3 : http4).request; + const send = (options.protocol === "https:" ? https3 : http3).request; const signal = request3.signal; let response = null; const abort = function abort2() { @@ -9409,7 +9409,7 @@ var require_agent = __commonJS({ "use strict"; var OriginalAgent = require("http").Agent; var ms = require_humanize_ms(); - var debug5 = require("util").debuglog("agentkeepalive"); + var debug3 = require("util").debuglog("agentkeepalive"); var { INIT_SOCKET, CURRENT_ID, @@ -9429,7 +9429,7 @@ var require_agent = __commonJS({ function deprecate2(message) { console.log("[agentkeepalive:deprecated] %s", message); } - var Agent5 = class extends OriginalAgent { + var Agent4 = class extends OriginalAgent { constructor(options) { options = options || {}; options.keepAlive = options.keepAlive !== false; @@ -9511,7 +9511,7 @@ var require_agent = __commonJS({ return true; } if (customTimeout <= 0) { - debug5( + debug3( "%s(requests: %s, finished: %s) free but need to destroy by TTL, request count %s, diff is %s", socket[SOCKET_NAME], socket[SOCKET_REQUEST_COUNT], @@ -9534,10 +9534,10 @@ var require_agent = __commonJS({ const agentTimeout = this.options.timeout; if (getSocketTimeout(socket) !== agentTimeout) { socket.setTimeout(agentTimeout); - debug5("%s reset timeout to %sms", socket[SOCKET_NAME], agentTimeout); + debug3("%s reset timeout to %sms", socket[SOCKET_NAME], agentTimeout); } socket[SOCKET_REQUEST_COUNT]++; - debug5( + debug3( "%s(requests: %s, finished: %s) reuse on addRequest, timeout %sms", socket[SOCKET_NAME], socket[SOCKET_REQUEST_COUNT], @@ -9615,12 +9615,12 @@ var require_agent = __commonJS({ return socket.timeout || socket._idleTimeout; } function installListeners(agent, socket, options) { - debug5("%s create, timeout %sms", socket[SOCKET_NAME], getSocketTimeout(socket)); + debug3("%s create, timeout %sms", socket[SOCKET_NAME], getSocketTimeout(socket)); function onFree() { if (!socket._httpMessage && socket[SOCKET_REQUEST_COUNT] === 1) return; socket[SOCKET_REQUEST_FINISHED_COUNT]++; agent.requestCount++; - debug5( + debug3( "%s(requests: %s, finished: %s) free", socket[SOCKET_NAME], socket[SOCKET_REQUEST_COUNT], @@ -9629,7 +9629,7 @@ var require_agent = __commonJS({ const name = agent.getName(options); if (socket.writable && agent.requests[name] && agent.requests[name].length) { socket[SOCKET_REQUEST_COUNT]++; - debug5( + debug3( "%s(requests: %s, finished: %s) will be reuse on agent free event", socket[SOCKET_NAME], socket[SOCKET_REQUEST_COUNT], @@ -9639,7 +9639,7 @@ var require_agent = __commonJS({ } socket.on("free", onFree); function onClose(isError2) { - debug5( + debug3( "%s(requests: %s, finished: %s) close, isError: %s", socket[SOCKET_NAME], socket[SOCKET_REQUEST_COUNT], @@ -9654,7 +9654,7 @@ var require_agent = __commonJS({ const timeout = getSocketTimeout(socket); const req = socket._httpMessage; const reqTimeoutListenerCount = req && req.listeners("timeout").length || 0; - debug5( + debug3( "%s(requests: %s, finished: %s) timeout after %sms, listeners %s, defaultTimeoutListenerCount %s, hasHttpRequest %s, HttpRequest timeoutListenerCount %s", socket[SOCKET_NAME], socket[SOCKET_REQUEST_COUNT], @@ -9665,15 +9665,15 @@ var require_agent = __commonJS({ !!req, reqTimeoutListenerCount ); - if (debug5.enabled) { - debug5("timeout listeners: %s", socket.listeners("timeout").map((f4) => f4.name).join(", ")); + if (debug3.enabled) { + debug3("timeout listeners: %s", socket.listeners("timeout").map((f4) => f4.name).join(", ")); } agent.timeoutSocketCount++; const name = agent.getName(options); if (agent.freeSockets[name] && agent.freeSockets[name].indexOf(socket) !== -1) { socket.destroy(); agent.removeSocket(socket, options); - debug5("%s is free, destroy quietly", socket[SOCKET_NAME]); + debug3("%s is free, destroy quietly", socket[SOCKET_NAME]); } else { if (reqTimeoutListenerCount === 0) { const error = new Error("Socket timeout"); @@ -9681,14 +9681,14 @@ var require_agent = __commonJS({ error.timeout = timeout; socket.destroy(error); agent.removeSocket(socket, options); - debug5("%s destroy with timeout error", socket[SOCKET_NAME]); + debug3("%s destroy with timeout error", socket[SOCKET_NAME]); } } } socket.on("timeout", onTimeout); function onError(err) { const listenerCount = socket.listeners("error").length; - debug5( + debug3( "%s(requests: %s, finished: %s) error: %s, listenerCount: %s", socket[SOCKET_NAME], socket[SOCKET_REQUEST_COUNT], @@ -9698,14 +9698,14 @@ var require_agent = __commonJS({ ); agent.errorSocketCount++; if (listenerCount === 1) { - debug5("%s emit uncaught error event", socket[SOCKET_NAME]); + debug3("%s emit uncaught error event", socket[SOCKET_NAME]); socket.removeListener("error", onError); socket.emit("error", err); } } socket.on("error", onError); function onRemove() { - debug5( + debug3( "%s(requests: %s, finished: %s) agentRemove", socket[SOCKET_NAME], socket[SOCKET_REQUEST_COUNT], @@ -9719,7 +9719,7 @@ var require_agent = __commonJS({ } socket.on("agentRemove", onRemove); } - module2.exports = Agent5; + module2.exports = Agent4; function inspect3(obj) { const res = {}; for (const key in obj) { @@ -9740,7 +9740,7 @@ var require_https_agent = __commonJS({ INIT_SOCKET, CREATE_HTTPS_CONNECTION } = require_constants(); - var HttpsAgent2 = class extends HttpAgent { + var HttpsAgent = class extends HttpAgent { constructor(options) { super(options); this.defaultPort = 443; @@ -9760,7 +9760,7 @@ var require_https_agent = __commonJS({ return socket; } }; - HttpsAgent2.prototype[CREATE_HTTPS_CONNECTION] = OriginalHttpsAgent.prototype.createConnection; + HttpsAgent.prototype[CREATE_HTTPS_CONNECTION] = OriginalHttpsAgent.prototype.createConnection; [ "getName", "_getSession", @@ -9769,10 +9769,10 @@ var require_https_agent = __commonJS({ "_evictSession" ].forEach(function(method) { if (typeof OriginalHttpsAgent.prototype[method] === "function") { - HttpsAgent2.prototype[method] = OriginalHttpsAgent.prototype[method]; + HttpsAgent.prototype[method] = OriginalHttpsAgent.prototype[method]; } }); - module2.exports = HttpsAgent2; + module2.exports = HttpsAgent; } }); @@ -14857,46 +14857,46 @@ var init_fileFromPath = __esm({ var require_common = __commonJS({ "node_modules/debug/src/common.js"(exports2, module2) { function setup(env2) { - createDebug3.debug = createDebug3; - createDebug3.default = createDebug3; - createDebug3.coerce = coerce; - createDebug3.disable = disable2; - createDebug3.enable = enable2; - createDebug3.enabled = enabled2; - createDebug3.humanize = require_ms(); - createDebug3.destroy = destroy2; + createDebug.debug = createDebug; + createDebug.default = createDebug; + createDebug.coerce = coerce; + createDebug.disable = disable2; + createDebug.enable = enable2; + createDebug.enabled = enabled2; + createDebug.humanize = require_ms(); + createDebug.destroy = destroy2; Object.keys(env2).forEach((key) => { - createDebug3[key] = env2[key]; + createDebug[key] = env2[key]; }); - createDebug3.names = []; - createDebug3.skips = []; - createDebug3.formatters = {}; + createDebug.names = []; + createDebug.skips = []; + createDebug.formatters = {}; function selectColor(namespace) { let hash = 0; for (let i3 = 0; i3 < namespace.length; i3++) { hash = (hash << 5) - hash + namespace.charCodeAt(i3); hash |= 0; } - return createDebug3.colors[Math.abs(hash) % createDebug3.colors.length]; + return createDebug.colors[Math.abs(hash) % createDebug.colors.length]; } - createDebug3.selectColor = selectColor; - function createDebug3(namespace) { + createDebug.selectColor = selectColor; + function createDebug(namespace) { let prevTime; let enableOverride = null; let namespacesCache; let enabledCache; - function debug5(...args) { - if (!debug5.enabled) { + function debug3(...args) { + if (!debug3.enabled) { return; } - const self2 = debug5; + const self2 = debug3; const curr = Number(/* @__PURE__ */ new Date()); const ms = curr - (prevTime || curr); self2.diff = ms; self2.prev = prevTime; self2.curr = curr; prevTime = curr; - args[0] = createDebug3.coerce(args[0]); + args[0] = createDebug.coerce(args[0]); if (typeof args[0] !== "string") { args.unshift("%O"); } @@ -14906,7 +14906,7 @@ var require_common = __commonJS({ return "%"; } index++; - const formatter = createDebug3.formatters[format]; + const formatter = createDebug.formatters[format]; if (typeof formatter === "function") { const val = args[index]; match = formatter.call(self2, val); @@ -14915,25 +14915,25 @@ var require_common = __commonJS({ } return match; }); - createDebug3.formatArgs.call(self2, args); - const logFn = self2.log || createDebug3.log; + createDebug.formatArgs.call(self2, args); + const logFn = self2.log || createDebug.log; logFn.apply(self2, args); } - debug5.namespace = namespace; - debug5.useColors = createDebug3.useColors(); - debug5.color = createDebug3.selectColor(namespace); - debug5.extend = extend3; - debug5.destroy = createDebug3.destroy; - Object.defineProperty(debug5, "enabled", { + debug3.namespace = namespace; + debug3.useColors = createDebug.useColors(); + debug3.color = createDebug.selectColor(namespace); + debug3.extend = extend3; + debug3.destroy = createDebug.destroy; + Object.defineProperty(debug3, "enabled", { enumerable: true, configurable: false, get: () => { if (enableOverride !== null) { return enableOverride; } - if (namespacesCache !== createDebug3.namespaces) { - namespacesCache = createDebug3.namespaces; - enabledCache = createDebug3.enabled(namespace); + if (namespacesCache !== createDebug.namespaces) { + namespacesCache = createDebug.namespaces; + enabledCache = createDebug.enabled(namespace); } return enabledCache; }, @@ -14941,21 +14941,21 @@ var require_common = __commonJS({ enableOverride = v5; } }); - if (typeof createDebug3.init === "function") { - createDebug3.init(debug5); + if (typeof createDebug.init === "function") { + createDebug.init(debug3); } - return debug5; + return debug3; } function extend3(namespace, delimiter) { - const newDebug = createDebug3(this.namespace + (typeof delimiter === "undefined" ? ":" : delimiter) + namespace); + const newDebug = createDebug(this.namespace + (typeof delimiter === "undefined" ? ":" : delimiter) + namespace); newDebug.log = this.log; return newDebug; } function enable2(namespaces) { - createDebug3.save(namespaces); - createDebug3.namespaces = namespaces; - createDebug3.names = []; - createDebug3.skips = []; + createDebug.save(namespaces); + createDebug.namespaces = namespaces; + createDebug.names = []; + createDebug.skips = []; let i3; const split = (typeof namespaces === "string" ? namespaces : "").split(/[\s,]+/); const len = split.length; @@ -14965,18 +14965,18 @@ var require_common = __commonJS({ } namespaces = split[i3].replace(/\*/g, ".*?"); if (namespaces[0] === "-") { - createDebug3.skips.push(new RegExp("^" + namespaces.slice(1) + "$")); + createDebug.skips.push(new RegExp("^" + namespaces.slice(1) + "$")); } else { - createDebug3.names.push(new RegExp("^" + namespaces + "$")); + createDebug.names.push(new RegExp("^" + namespaces + "$")); } } } function disable2() { const namespaces = [ - ...createDebug3.names.map(toNamespace), - ...createDebug3.skips.map(toNamespace).map((namespace) => "-" + namespace) + ...createDebug.names.map(toNamespace), + ...createDebug.skips.map(toNamespace).map((namespace) => "-" + namespace) ].join(","); - createDebug3.enable(""); + createDebug.enable(""); return namespaces; } function enabled2(name) { @@ -14985,13 +14985,13 @@ var require_common = __commonJS({ } let i3; let len; - for (i3 = 0, len = createDebug3.skips.length; i3 < len; i3++) { - if (createDebug3.skips[i3].test(name)) { + for (i3 = 0, len = createDebug.skips.length; i3 < len; i3++) { + if (createDebug.skips[i3].test(name)) { return false; } } - for (i3 = 0, len = createDebug3.names.length; i3 < len; i3++) { - if (createDebug3.names[i3].test(name)) { + for (i3 = 0, len = createDebug.names.length; i3 < len; i3++) { + if (createDebug.names[i3].test(name)) { return true; } } @@ -15009,8 +15009,8 @@ var require_common = __commonJS({ function destroy2() { console.warn("Instance method `debug.destroy()` is deprecated and no longer does anything. It will be removed in the next major version of `debug`."); } - createDebug3.enable(createDebug3.load()); - return createDebug3; + createDebug.enable(createDebug.load()); + return createDebug; } module2.exports = setup; } @@ -15454,11 +15454,11 @@ var require_node = __commonJS({ function load() { return process.env.DEBUG; } - function init(debug5) { - debug5.inspectOpts = {}; + function init(debug3) { + debug3.inspectOpts = {}; const keys = Object.keys(exports2.inspectOpts); for (let i3 = 0; i3 < keys.length; i3++) { - debug5.inspectOpts[keys[i3]] = exports2.inspectOpts[keys[i3]]; + debug3.inspectOpts[keys[i3]] = exports2.inspectOpts[keys[i3]]; } } module2.exports = require_common()(exports2); @@ -15485,6 +15485,471 @@ var require_src2 = __commonJS({ } }); +// node_modules/agent-base/dist/helpers.js +var require_helpers = __commonJS({ + "node_modules/agent-base/dist/helpers.js"(exports2) { + "use strict"; + var __createBinding = exports2 && exports2.__createBinding || (Object.create ? function(o3, m5, k7, k22) { + if (k22 === void 0) k22 = k7; + var desc = Object.getOwnPropertyDescriptor(m5, k7); + if (!desc || ("get" in desc ? !m5.__esModule : desc.writable || desc.configurable)) { + desc = { enumerable: true, get: function() { + return m5[k7]; + } }; + } + Object.defineProperty(o3, k22, desc); + } : function(o3, m5, k7, k22) { + if (k22 === void 0) k22 = k7; + o3[k22] = m5[k7]; + }); + var __setModuleDefault = exports2 && exports2.__setModuleDefault || (Object.create ? function(o3, v5) { + Object.defineProperty(o3, "default", { enumerable: true, value: v5 }); + } : function(o3, v5) { + o3["default"] = v5; + }); + var __importStar = exports2 && exports2.__importStar || function(mod) { + if (mod && mod.__esModule) return mod; + var result = {}; + if (mod != null) { + for (var k7 in mod) if (k7 !== "default" && Object.prototype.hasOwnProperty.call(mod, k7)) __createBinding(result, mod, k7); + } + __setModuleDefault(result, mod); + return result; + }; + Object.defineProperty(exports2, "__esModule", { value: true }); + exports2.req = exports2.json = exports2.toBuffer = void 0; + var http3 = __importStar(require("http")); + var https3 = __importStar(require("https")); + async function toBuffer(stream4) { + let length = 0; + const chunks = []; + for await (const chunk of stream4) { + length += chunk.length; + chunks.push(chunk); + } + return Buffer.concat(chunks, length); + } + exports2.toBuffer = toBuffer; + async function json(stream4) { + const buf = await toBuffer(stream4); + const str2 = buf.toString("utf8"); + try { + return JSON.parse(str2); + } catch (_err) { + const err = _err; + err.message += ` (input: ${str2})`; + throw err; + } + } + exports2.json = json; + function req(url2, opts = {}) { + const href = typeof url2 === "string" ? url2 : url2.href; + const req2 = (href.startsWith("https:") ? https3 : http3).request(url2, opts); + const promise = new Promise((resolve, reject) => { + req2.once("response", resolve).once("error", reject).end(); + }); + req2.then = promise.then.bind(promise); + return req2; + } + exports2.req = req; + } +}); + +// node_modules/agent-base/dist/index.js +var require_dist = __commonJS({ + "node_modules/agent-base/dist/index.js"(exports2) { + "use strict"; + var __createBinding = exports2 && exports2.__createBinding || (Object.create ? function(o3, m5, k7, k22) { + if (k22 === void 0) k22 = k7; + var desc = Object.getOwnPropertyDescriptor(m5, k7); + if (!desc || ("get" in desc ? !m5.__esModule : desc.writable || desc.configurable)) { + desc = { enumerable: true, get: function() { + return m5[k7]; + } }; + } + Object.defineProperty(o3, k22, desc); + } : function(o3, m5, k7, k22) { + if (k22 === void 0) k22 = k7; + o3[k22] = m5[k7]; + }); + var __setModuleDefault = exports2 && exports2.__setModuleDefault || (Object.create ? function(o3, v5) { + Object.defineProperty(o3, "default", { enumerable: true, value: v5 }); + } : function(o3, v5) { + o3["default"] = v5; + }); + var __importStar = exports2 && exports2.__importStar || function(mod) { + if (mod && mod.__esModule) return mod; + var result = {}; + if (mod != null) { + for (var k7 in mod) if (k7 !== "default" && Object.prototype.hasOwnProperty.call(mod, k7)) __createBinding(result, mod, k7); + } + __setModuleDefault(result, mod); + return result; + }; + var __exportStar = exports2 && exports2.__exportStar || function(m5, exports3) { + for (var p4 in m5) if (p4 !== "default" && !Object.prototype.hasOwnProperty.call(exports3, p4)) __createBinding(exports3, m5, p4); + }; + Object.defineProperty(exports2, "__esModule", { value: true }); + exports2.Agent = void 0; + var net = __importStar(require("net")); + var http3 = __importStar(require("http")); + var https_1 = require("https"); + __exportStar(require_helpers(), exports2); + var INTERNAL = Symbol("AgentBaseInternalState"); + var Agent4 = class extends http3.Agent { + constructor(opts) { + super(opts); + this[INTERNAL] = {}; + } + /** + * Determine whether this is an `http` or `https` request. + */ + isSecureEndpoint(options) { + if (options) { + if (typeof options.secureEndpoint === "boolean") { + return options.secureEndpoint; + } + if (typeof options.protocol === "string") { + return options.protocol === "https:"; + } + } + const { stack } = new Error(); + if (typeof stack !== "string") + return false; + return stack.split("\n").some((l3) => l3.indexOf("(https.js:") !== -1 || l3.indexOf("node:https:") !== -1); + } + // In order to support async signatures in `connect()` and Node's native + // connection pooling in `http.Agent`, the array of sockets for each origin + // has to be updated synchronously. This is so the length of the array is + // accurate when `addRequest()` is next called. We achieve this by creating a + // fake socket and adding it to `sockets[origin]` and incrementing + // `totalSocketCount`. + incrementSockets(name) { + if (this.maxSockets === Infinity && this.maxTotalSockets === Infinity) { + return null; + } + if (!this.sockets[name]) { + this.sockets[name] = []; + } + const fakeSocket = new net.Socket({ writable: false }); + this.sockets[name].push(fakeSocket); + this.totalSocketCount++; + return fakeSocket; + } + decrementSockets(name, socket) { + if (!this.sockets[name] || socket === null) { + return; + } + const sockets = this.sockets[name]; + const index = sockets.indexOf(socket); + if (index !== -1) { + sockets.splice(index, 1); + this.totalSocketCount--; + if (sockets.length === 0) { + delete this.sockets[name]; + } + } + } + // In order to properly update the socket pool, we need to call `getName()` on + // the core `https.Agent` if it is a secureEndpoint. + getName(options) { + const secureEndpoint = typeof options.secureEndpoint === "boolean" ? options.secureEndpoint : this.isSecureEndpoint(options); + if (secureEndpoint) { + return https_1.Agent.prototype.getName.call(this, options); + } + return super.getName(options); + } + createSocket(req, options, cb) { + const connectOpts = { + ...options, + secureEndpoint: this.isSecureEndpoint(options) + }; + const name = this.getName(connectOpts); + const fakeSocket = this.incrementSockets(name); + Promise.resolve().then(() => this.connect(req, connectOpts)).then((socket) => { + this.decrementSockets(name, fakeSocket); + if (socket instanceof http3.Agent) { + return socket.addRequest(req, connectOpts); + } + this[INTERNAL].currentSocket = socket; + super.createSocket(req, options, cb); + }, (err) => { + this.decrementSockets(name, fakeSocket); + cb(err); + }); + } + createConnection() { + const socket = this[INTERNAL].currentSocket; + this[INTERNAL].currentSocket = void 0; + if (!socket) { + throw new Error("No socket was returned in the `connect()` function"); + } + return socket; + } + get defaultPort() { + return this[INTERNAL].defaultPort ?? (this.protocol === "https:" ? 443 : 80); + } + set defaultPort(v5) { + if (this[INTERNAL]) { + this[INTERNAL].defaultPort = v5; + } + } + get protocol() { + return this[INTERNAL].protocol ?? (this.isSecureEndpoint() ? "https:" : "http:"); + } + set protocol(v5) { + if (this[INTERNAL]) { + this[INTERNAL].protocol = v5; + } + } + }; + exports2.Agent = Agent4; + } +}); + +// node_modules/https-proxy-agent/dist/parse-proxy-response.js +var require_parse_proxy_response = __commonJS({ + "node_modules/https-proxy-agent/dist/parse-proxy-response.js"(exports2) { + "use strict"; + var __importDefault = exports2 && exports2.__importDefault || function(mod) { + return mod && mod.__esModule ? mod : { "default": mod }; + }; + Object.defineProperty(exports2, "__esModule", { value: true }); + exports2.parseProxyResponse = void 0; + var debug_1 = __importDefault(require_src2()); + var debug3 = (0, debug_1.default)("https-proxy-agent:parse-proxy-response"); + function parseProxyResponse(socket) { + return new Promise((resolve, reject) => { + let buffersLength = 0; + const buffers = []; + function read() { + const b7 = socket.read(); + if (b7) + ondata(b7); + else + socket.once("readable", read); + } + function cleanup() { + socket.removeListener("end", onend); + socket.removeListener("error", onerror); + socket.removeListener("readable", read); + } + function onend() { + cleanup(); + debug3("onend"); + reject(new Error("Proxy connection ended before receiving CONNECT response")); + } + function onerror(err) { + cleanup(); + debug3("onerror %o", err); + reject(err); + } + function ondata(b7) { + buffers.push(b7); + buffersLength += b7.length; + const buffered = Buffer.concat(buffers, buffersLength); + const endOfHeaders = buffered.indexOf("\r\n\r\n"); + if (endOfHeaders === -1) { + debug3("have not received end of HTTP headers yet..."); + read(); + return; + } + const headerParts = buffered.slice(0, endOfHeaders).toString("ascii").split("\r\n"); + const firstLine = headerParts.shift(); + if (!firstLine) { + socket.destroy(); + return reject(new Error("No header received from proxy CONNECT response")); + } + const firstLineParts = firstLine.split(" "); + const statusCode = +firstLineParts[1]; + const statusText = firstLineParts.slice(2).join(" "); + const headers = {}; + for (const header of headerParts) { + if (!header) + continue; + const firstColon = header.indexOf(":"); + if (firstColon === -1) { + socket.destroy(); + return reject(new Error(`Invalid header from proxy CONNECT response: "${header}"`)); + } + const key = header.slice(0, firstColon).toLowerCase(); + const value = header.slice(firstColon + 1).trimStart(); + const current = headers[key]; + if (typeof current === "string") { + headers[key] = [current, value]; + } else if (Array.isArray(current)) { + current.push(value); + } else { + headers[key] = value; + } + } + debug3("got proxy server response: %o %o", firstLine, headers); + cleanup(); + resolve({ + connect: { + statusCode, + statusText, + headers + }, + buffered + }); + } + socket.on("error", onerror); + socket.on("end", onend); + read(); + }); + } + exports2.parseProxyResponse = parseProxyResponse; + } +}); + +// node_modules/https-proxy-agent/dist/index.js +var require_dist2 = __commonJS({ + "node_modules/https-proxy-agent/dist/index.js"(exports2) { + "use strict"; + var __createBinding = exports2 && exports2.__createBinding || (Object.create ? function(o3, m5, k7, k22) { + if (k22 === void 0) k22 = k7; + var desc = Object.getOwnPropertyDescriptor(m5, k7); + if (!desc || ("get" in desc ? !m5.__esModule : desc.writable || desc.configurable)) { + desc = { enumerable: true, get: function() { + return m5[k7]; + } }; + } + Object.defineProperty(o3, k22, desc); + } : function(o3, m5, k7, k22) { + if (k22 === void 0) k22 = k7; + o3[k22] = m5[k7]; + }); + var __setModuleDefault = exports2 && exports2.__setModuleDefault || (Object.create ? function(o3, v5) { + Object.defineProperty(o3, "default", { enumerable: true, value: v5 }); + } : function(o3, v5) { + o3["default"] = v5; + }); + var __importStar = exports2 && exports2.__importStar || function(mod) { + if (mod && mod.__esModule) return mod; + var result = {}; + if (mod != null) { + for (var k7 in mod) if (k7 !== "default" && Object.prototype.hasOwnProperty.call(mod, k7)) __createBinding(result, mod, k7); + } + __setModuleDefault(result, mod); + return result; + }; + var __importDefault = exports2 && exports2.__importDefault || function(mod) { + return mod && mod.__esModule ? mod : { "default": mod }; + }; + Object.defineProperty(exports2, "__esModule", { value: true }); + exports2.HttpsProxyAgent = void 0; + var net = __importStar(require("net")); + var tls = __importStar(require("tls")); + var assert_1 = __importDefault(require("assert")); + var debug_1 = __importDefault(require_src2()); + var agent_base_1 = require_dist(); + var url_1 = require("url"); + var parse_proxy_response_1 = require_parse_proxy_response(); + var debug3 = (0, debug_1.default)("https-proxy-agent"); + var HttpsProxyAgent5 = class extends agent_base_1.Agent { + constructor(proxy, opts) { + super(opts); + this.options = { path: void 0 }; + this.proxy = typeof proxy === "string" ? new url_1.URL(proxy) : proxy; + this.proxyHeaders = opts?.headers ?? {}; + debug3("Creating new HttpsProxyAgent instance: %o", this.proxy.href); + const host = (this.proxy.hostname || this.proxy.host).replace(/^\[|\]$/g, ""); + const port = this.proxy.port ? parseInt(this.proxy.port, 10) : this.proxy.protocol === "https:" ? 443 : 80; + this.connectOpts = { + // Attempt to negotiate http/1.1 for proxy servers that support http/2 + ALPNProtocols: ["http/1.1"], + ...opts ? omit(opts, "headers") : null, + host, + port + }; + } + /** + * Called when the node-core HTTP client library is creating a + * new HTTP request. + */ + async connect(req, opts) { + const { proxy } = this; + if (!opts.host) { + throw new TypeError('No "host" provided'); + } + let socket; + if (proxy.protocol === "https:") { + debug3("Creating `tls.Socket`: %o", this.connectOpts); + const servername = this.connectOpts.servername || this.connectOpts.host; + socket = tls.connect({ + ...this.connectOpts, + servername: servername && net.isIP(servername) ? void 0 : servername + }); + } else { + debug3("Creating `net.Socket`: %o", this.connectOpts); + socket = net.connect(this.connectOpts); + } + const headers = typeof this.proxyHeaders === "function" ? this.proxyHeaders() : { ...this.proxyHeaders }; + const host = net.isIPv6(opts.host) ? `[${opts.host}]` : opts.host; + let payload = `CONNECT ${host}:${opts.port} HTTP/1.1\r +`; + if (proxy.username || proxy.password) { + const auth = `${decodeURIComponent(proxy.username)}:${decodeURIComponent(proxy.password)}`; + headers["Proxy-Authorization"] = `Basic ${Buffer.from(auth).toString("base64")}`; + } + headers.Host = `${host}:${opts.port}`; + if (!headers["Proxy-Connection"]) { + headers["Proxy-Connection"] = this.keepAlive ? "Keep-Alive" : "close"; + } + for (const name of Object.keys(headers)) { + payload += `${name}: ${headers[name]}\r +`; + } + const proxyResponsePromise = (0, parse_proxy_response_1.parseProxyResponse)(socket); + socket.write(`${payload}\r +`); + const { connect, buffered } = await proxyResponsePromise; + req.emit("proxyConnect", connect); + this.emit("proxyConnect", connect, req); + if (connect.statusCode === 200) { + req.once("socket", resume); + if (opts.secureEndpoint) { + debug3("Upgrading socket connection to TLS"); + const servername = opts.servername || opts.host; + return tls.connect({ + ...omit(opts, "host", "path", "port"), + socket, + servername: net.isIP(servername) ? void 0 : servername + }); + } + return socket; + } + socket.destroy(); + const fakeSocket = new net.Socket({ writable: false }); + fakeSocket.readable = true; + req.once("socket", (s2) => { + debug3("Replaying proxy buffer for failed request"); + (0, assert_1.default)(s2.listenerCount("data") > 0); + s2.push(buffered); + s2.push(null); + }); + return fakeSocket; + } + }; + HttpsProxyAgent5.protocols = ["http", "https"]; + exports2.HttpsProxyAgent = HttpsProxyAgent5; + function resume(socket) { + socket.resume(); + } + function omit(obj, ...keys) { + const ret = {}; + let key; + for (key in obj) { + if (!keys.includes(key)) { + ret[key] = obj[key]; + } + } + return ret; + } + } +}); + // node_modules/delayed-stream/lib/delayed_stream.js var require_delayed_stream = __commonJS({ "node_modules/delayed-stream/lib/delayed_stream.js"(exports2, module2) { @@ -24594,7 +25059,7 @@ var require_form_data = __commonJS({ var CombinedStream = require_combined_stream(); var util4 = require("util"); var path5 = require("path"); - var http4 = require("http"); + var http3 = require("http"); var https3 = require("https"); var parseUrl = require("url").parse; var fs7 = require("fs"); @@ -24864,7 +25329,7 @@ var require_form_data = __commonJS({ if (options.protocol == "https:") { request3 = https3.request(options); } else { - request3 = http4.request(options); + request3 = http3.request(options); } this.getLength(function(err, length) { if (err && err !== "Unknown stream") { @@ -24975,19 +25440,19 @@ var require_proxy_from_env = __commonJS({ // node_modules/follow-redirects/debug.js var require_debug = __commonJS({ "node_modules/follow-redirects/debug.js"(exports2, module2) { - var debug5; + var debug3; module2.exports = function() { - if (!debug5) { + if (!debug3) { try { - debug5 = require_src2()("follow-redirects"); + debug3 = require_src2()("follow-redirects"); } catch (error) { } - if (typeof debug5 !== "function") { - debug5 = function() { + if (typeof debug3 !== "function") { + debug3 = function() { }; } } - debug5.apply(null, arguments); + debug3.apply(null, arguments); }; } }); @@ -24996,15 +25461,15 @@ var require_debug = __commonJS({ var require_follow_redirects = __commonJS({ "node_modules/follow-redirects/index.js"(exports2, module2) { var url2 = require("url"); - var URL3 = url2.URL; - var http4 = require("http"); + var URL2 = url2.URL; + var http3 = require("http"); var https3 = require("https"); var Writable = require("stream").Writable; - var assert2 = require("assert"); - var debug5 = require_debug(); + var assert = require("assert"); + var debug3 = require_debug(); var useNativeURL = false; try { - assert2(new URL3()); + assert(new URL2()); } catch (error) { useNativeURL = error.code === "ERR_INVALID_URL"; } @@ -25310,7 +25775,7 @@ var require_follow_redirects = __commonJS({ var currentHost = currentHostHeader || currentUrlParts.host; var currentUrl = /^\w+:/.test(location) ? this._currentUrl : url2.format(Object.assign(currentUrlParts, { host: currentHost })); var redirectUrl = resolveUrl(location, currentUrl); - debug5("redirecting to", redirectUrl.href); + debug3("redirecting to", redirectUrl.href); this._isRedirect = true; spreadUrlObject(redirectUrl, this._options); if (redirectUrl.protocol !== currentUrlParts.protocol && redirectUrl.protocol !== "https:" || redirectUrl.host !== currentHost && !isSubdomain(redirectUrl.host, currentHost)) { @@ -25363,8 +25828,8 @@ var require_follow_redirects = __commonJS({ if (!isString2(options.host) && !isString2(options.hostname)) { options.hostname = "::1"; } - assert2.equal(options.protocol, protocol, "protocol mismatch"); - debug5("options", options); + assert.equal(options.protocol, protocol, "protocol mismatch"); + debug3("options", options); return new RedirectableRequest(options, callback); } function get(input, options, callback) { @@ -25384,7 +25849,7 @@ var require_follow_redirects = __commonJS({ function parseUrl(input) { var parsed; if (useNativeURL) { - parsed = new URL3(input); + parsed = new URL2(input); } else { parsed = validateUrl(url2.parse(input)); if (!isString2(parsed.protocol)) { @@ -25394,7 +25859,7 @@ var require_follow_redirects = __commonJS({ return parsed; } function resolveUrl(relative, base) { - return useNativeURL ? new URL3(relative, base) : parseUrl(url2.resolve(base, relative)); + return useNativeURL ? new URL2(relative, base) : parseUrl(url2.resolve(base, relative)); } function validateUrl(input) { if (/^\[/.test(input.hostname) && !/^\[[:0-9a-f]+\]$/i.test(input.hostname)) { @@ -25457,7 +25922,7 @@ var require_follow_redirects = __commonJS({ request3.destroy(error); } function isSubdomain(subdomain, domain) { - assert2(isString2(subdomain) && isString2(domain)); + assert(isString2(subdomain) && isString2(domain)); var dot = subdomain.length - domain.length - 1; return dot > 0 && subdomain[dot] === "." && subdomain.endsWith(domain); } @@ -25471,9 +25936,9 @@ var require_follow_redirects = __commonJS({ return typeof value === "object" && "length" in value; } function isURL(value) { - return URL3 && value instanceof URL3; + return URL2 && value instanceof URL2; } - module2.exports = wrap({ http: http4, https: https3 }); + module2.exports = wrap({ http: http3, https: https3 }); module2.exports.wrap = wrap; } }); @@ -25859,478 +26324,6 @@ var require_tiktoken = __commonJS({ } }); -// node_modules/agent-base/dist/helpers.js -var require_helpers = __commonJS({ - "node_modules/agent-base/dist/helpers.js"(exports2) { - "use strict"; - var __createBinding = exports2 && exports2.__createBinding || (Object.create ? function(o3, m5, k7, k22) { - if (k22 === void 0) k22 = k7; - var desc = Object.getOwnPropertyDescriptor(m5, k7); - if (!desc || ("get" in desc ? !m5.__esModule : desc.writable || desc.configurable)) { - desc = { enumerable: true, get: function() { - return m5[k7]; - } }; - } - Object.defineProperty(o3, k22, desc); - } : function(o3, m5, k7, k22) { - if (k22 === void 0) k22 = k7; - o3[k22] = m5[k7]; - }); - var __setModuleDefault = exports2 && exports2.__setModuleDefault || (Object.create ? function(o3, v5) { - Object.defineProperty(o3, "default", { enumerable: true, value: v5 }); - } : function(o3, v5) { - o3["default"] = v5; - }); - var __importStar = exports2 && exports2.__importStar || function(mod) { - if (mod && mod.__esModule) return mod; - var result = {}; - if (mod != null) { - for (var k7 in mod) if (k7 !== "default" && Object.prototype.hasOwnProperty.call(mod, k7)) __createBinding(result, mod, k7); - } - __setModuleDefault(result, mod); - return result; - }; - Object.defineProperty(exports2, "__esModule", { value: true }); - exports2.req = exports2.json = exports2.toBuffer = void 0; - var http4 = __importStar(require("http")); - var https3 = __importStar(require("https")); - async function toBuffer(stream4) { - let length = 0; - const chunks = []; - for await (const chunk of stream4) { - length += chunk.length; - chunks.push(chunk); - } - return Buffer.concat(chunks, length); - } - exports2.toBuffer = toBuffer; - async function json(stream4) { - const buf = await toBuffer(stream4); - const str2 = buf.toString("utf8"); - try { - return JSON.parse(str2); - } catch (_err) { - const err = _err; - err.message += ` (input: ${str2})`; - throw err; - } - } - exports2.json = json; - function req(url2, opts = {}) { - const href = typeof url2 === "string" ? url2 : url2.href; - const req2 = (href.startsWith("https:") ? https3 : http4).request(url2, opts); - const promise = new Promise((resolve, reject) => { - req2.once("response", resolve).once("error", reject).end(); - }); - req2.then = promise.then.bind(promise); - return req2; - } - exports2.req = req; - } -}); - -// node_modules/agent-base/dist/index.js -var require_dist = __commonJS({ - "node_modules/agent-base/dist/index.js"(exports2) { - "use strict"; - var __createBinding = exports2 && exports2.__createBinding || (Object.create ? function(o3, m5, k7, k22) { - if (k22 === void 0) k22 = k7; - var desc = Object.getOwnPropertyDescriptor(m5, k7); - if (!desc || ("get" in desc ? !m5.__esModule : desc.writable || desc.configurable)) { - desc = { enumerable: true, get: function() { - return m5[k7]; - } }; - } - Object.defineProperty(o3, k22, desc); - } : function(o3, m5, k7, k22) { - if (k22 === void 0) k22 = k7; - o3[k22] = m5[k7]; - }); - var __setModuleDefault = exports2 && exports2.__setModuleDefault || (Object.create ? function(o3, v5) { - Object.defineProperty(o3, "default", { enumerable: true, value: v5 }); - } : function(o3, v5) { - o3["default"] = v5; - }); - var __importStar = exports2 && exports2.__importStar || function(mod) { - if (mod && mod.__esModule) return mod; - var result = {}; - if (mod != null) { - for (var k7 in mod) if (k7 !== "default" && Object.prototype.hasOwnProperty.call(mod, k7)) __createBinding(result, mod, k7); - } - __setModuleDefault(result, mod); - return result; - }; - var __exportStar = exports2 && exports2.__exportStar || function(m5, exports3) { - for (var p4 in m5) if (p4 !== "default" && !Object.prototype.hasOwnProperty.call(exports3, p4)) __createBinding(exports3, m5, p4); - }; - Object.defineProperty(exports2, "__esModule", { value: true }); - exports2.Agent = void 0; - var net3 = __importStar(require("net")); - var http4 = __importStar(require("http")); - var https_1 = require("https"); - __exportStar(require_helpers(), exports2); - var INTERNAL2 = Symbol("AgentBaseInternalState"); - var Agent5 = class extends http4.Agent { - constructor(opts) { - super(opts); - this[INTERNAL2] = {}; - } - /** - * Determine whether this is an `http` or `https` request. - */ - isSecureEndpoint(options) { - if (options) { - if (typeof options.secureEndpoint === "boolean") { - return options.secureEndpoint; - } - if (typeof options.protocol === "string") { - return options.protocol === "https:"; - } - } - const { stack } = new Error(); - if (typeof stack !== "string") - return false; - return stack.split("\n").some((l3) => l3.indexOf("(https.js:") !== -1 || l3.indexOf("node:https:") !== -1); - } - // In order to support async signatures in `connect()` and Node's native - // connection pooling in `http.Agent`, the array of sockets for each origin - // has to be updated synchronously. This is so the length of the array is - // accurate when `addRequest()` is next called. We achieve this by creating a - // fake socket and adding it to `sockets[origin]` and incrementing - // `totalSocketCount`. - incrementSockets(name) { - if (this.maxSockets === Infinity && this.maxTotalSockets === Infinity) { - return null; - } - if (!this.sockets[name]) { - this.sockets[name] = []; - } - const fakeSocket = new net3.Socket({ writable: false }); - this.sockets[name].push(fakeSocket); - this.totalSocketCount++; - return fakeSocket; - } - decrementSockets(name, socket) { - if (!this.sockets[name] || socket === null) { - return; - } - const sockets = this.sockets[name]; - const index = sockets.indexOf(socket); - if (index !== -1) { - sockets.splice(index, 1); - this.totalSocketCount--; - if (sockets.length === 0) { - delete this.sockets[name]; - } - } - } - // In order to properly update the socket pool, we need to call `getName()` on - // the core `https.Agent` if it is a secureEndpoint. - getName(options) { - const secureEndpoint = this.isSecureEndpoint(options); - if (secureEndpoint) { - return https_1.Agent.prototype.getName.call(this, options); - } - return super.getName(options); - } - createSocket(req, options, cb) { - const connectOpts = { - ...options, - secureEndpoint: this.isSecureEndpoint(options) - }; - const name = this.getName(connectOpts); - const fakeSocket = this.incrementSockets(name); - Promise.resolve().then(() => this.connect(req, connectOpts)).then((socket) => { - this.decrementSockets(name, fakeSocket); - if (socket instanceof http4.Agent) { - try { - return socket.addRequest(req, connectOpts); - } catch (err) { - return cb(err); - } - } - this[INTERNAL2].currentSocket = socket; - super.createSocket(req, options, cb); - }, (err) => { - this.decrementSockets(name, fakeSocket); - cb(err); - }); - } - createConnection() { - const socket = this[INTERNAL2].currentSocket; - this[INTERNAL2].currentSocket = void 0; - if (!socket) { - throw new Error("No socket was returned in the `connect()` function"); - } - return socket; - } - get defaultPort() { - return this[INTERNAL2].defaultPort ?? (this.protocol === "https:" ? 443 : 80); - } - set defaultPort(v5) { - if (this[INTERNAL2]) { - this[INTERNAL2].defaultPort = v5; - } - } - get protocol() { - return this[INTERNAL2].protocol ?? (this.isSecureEndpoint() ? "https:" : "http:"); - } - set protocol(v5) { - if (this[INTERNAL2]) { - this[INTERNAL2].protocol = v5; - } - } - }; - exports2.Agent = Agent5; - } -}); - -// node_modules/@azure/core-rest-pipeline/node_modules/https-proxy-agent/dist/parse-proxy-response.js -var require_parse_proxy_response = __commonJS({ - "node_modules/@azure/core-rest-pipeline/node_modules/https-proxy-agent/dist/parse-proxy-response.js"(exports2) { - "use strict"; - var __importDefault = exports2 && exports2.__importDefault || function(mod) { - return mod && mod.__esModule ? mod : { "default": mod }; - }; - Object.defineProperty(exports2, "__esModule", { value: true }); - exports2.parseProxyResponse = void 0; - var debug_1 = __importDefault(require_src2()); - var debug5 = (0, debug_1.default)("https-proxy-agent:parse-proxy-response"); - function parseProxyResponse2(socket) { - return new Promise((resolve, reject) => { - let buffersLength = 0; - const buffers = []; - function read() { - const b7 = socket.read(); - if (b7) - ondata(b7); - else - socket.once("readable", read); - } - function cleanup() { - socket.removeListener("end", onend); - socket.removeListener("error", onerror); - socket.removeListener("readable", read); - } - function onend() { - cleanup(); - debug5("onend"); - reject(new Error("Proxy connection ended before receiving CONNECT response")); - } - function onerror(err) { - cleanup(); - debug5("onerror %o", err); - reject(err); - } - function ondata(b7) { - buffers.push(b7); - buffersLength += b7.length; - const buffered = Buffer.concat(buffers, buffersLength); - const endOfHeaders = buffered.indexOf("\r\n\r\n"); - if (endOfHeaders === -1) { - debug5("have not received end of HTTP headers yet..."); - read(); - return; - } - const headerParts = buffered.slice(0, endOfHeaders).toString("ascii").split("\r\n"); - const firstLine = headerParts.shift(); - if (!firstLine) { - socket.destroy(); - return reject(new Error("No header received from proxy CONNECT response")); - } - const firstLineParts = firstLine.split(" "); - const statusCode = +firstLineParts[1]; - const statusText = firstLineParts.slice(2).join(" "); - const headers = {}; - for (const header of headerParts) { - if (!header) - continue; - const firstColon = header.indexOf(":"); - if (firstColon === -1) { - socket.destroy(); - return reject(new Error(`Invalid header from proxy CONNECT response: "${header}"`)); - } - const key = header.slice(0, firstColon).toLowerCase(); - const value = header.slice(firstColon + 1).trimStart(); - const current = headers[key]; - if (typeof current === "string") { - headers[key] = [current, value]; - } else if (Array.isArray(current)) { - current.push(value); - } else { - headers[key] = value; - } - } - debug5("got proxy server response: %o %o", firstLine, headers); - cleanup(); - resolve({ - connect: { - statusCode, - statusText, - headers - }, - buffered - }); - } - socket.on("error", onerror); - socket.on("end", onend); - read(); - }); - } - exports2.parseProxyResponse = parseProxyResponse2; - } -}); - -// node_modules/@azure/core-rest-pipeline/node_modules/https-proxy-agent/dist/index.js -var require_dist2 = __commonJS({ - "node_modules/@azure/core-rest-pipeline/node_modules/https-proxy-agent/dist/index.js"(exports2) { - "use strict"; - var __createBinding = exports2 && exports2.__createBinding || (Object.create ? function(o3, m5, k7, k22) { - if (k22 === void 0) k22 = k7; - var desc = Object.getOwnPropertyDescriptor(m5, k7); - if (!desc || ("get" in desc ? !m5.__esModule : desc.writable || desc.configurable)) { - desc = { enumerable: true, get: function() { - return m5[k7]; - } }; - } - Object.defineProperty(o3, k22, desc); - } : function(o3, m5, k7, k22) { - if (k22 === void 0) k22 = k7; - o3[k22] = m5[k7]; - }); - var __setModuleDefault = exports2 && exports2.__setModuleDefault || (Object.create ? function(o3, v5) { - Object.defineProperty(o3, "default", { enumerable: true, value: v5 }); - } : function(o3, v5) { - o3["default"] = v5; - }); - var __importStar = exports2 && exports2.__importStar || function(mod) { - if (mod && mod.__esModule) return mod; - var result = {}; - if (mod != null) { - for (var k7 in mod) if (k7 !== "default" && Object.prototype.hasOwnProperty.call(mod, k7)) __createBinding(result, mod, k7); - } - __setModuleDefault(result, mod); - return result; - }; - var __importDefault = exports2 && exports2.__importDefault || function(mod) { - return mod && mod.__esModule ? mod : { "default": mod }; - }; - Object.defineProperty(exports2, "__esModule", { value: true }); - exports2.HttpsProxyAgent = void 0; - var net3 = __importStar(require("net")); - var tls2 = __importStar(require("tls")); - var assert_1 = __importDefault(require("assert")); - var debug_1 = __importDefault(require_src2()); - var agent_base_1 = require_dist(); - var url_1 = require("url"); - var parse_proxy_response_1 = require_parse_proxy_response(); - var debug5 = (0, debug_1.default)("https-proxy-agent"); - var setServernameFromNonIpHost2 = (options) => { - if (options.servername === void 0 && options.host && !net3.isIP(options.host)) { - return { - ...options, - servername: options.host - }; - } - return options; - }; - var HttpsProxyAgent3 = class extends agent_base_1.Agent { - constructor(proxy, opts) { - super(opts); - this.options = { path: void 0 }; - this.proxy = typeof proxy === "string" ? new url_1.URL(proxy) : proxy; - this.proxyHeaders = opts?.headers ?? {}; - debug5("Creating new HttpsProxyAgent instance: %o", this.proxy.href); - const host = (this.proxy.hostname || this.proxy.host).replace(/^\[|\]$/g, ""); - const port = this.proxy.port ? parseInt(this.proxy.port, 10) : this.proxy.protocol === "https:" ? 443 : 80; - this.connectOpts = { - // Attempt to negotiate http/1.1 for proxy servers that support http/2 - ALPNProtocols: ["http/1.1"], - ...opts ? omit2(opts, "headers") : null, - host, - port - }; - } - /** - * Called when the node-core HTTP client library is creating a - * new HTTP request. - */ - async connect(req, opts) { - const { proxy } = this; - if (!opts.host) { - throw new TypeError('No "host" provided'); - } - let socket; - if (proxy.protocol === "https:") { - debug5("Creating `tls.Socket`: %o", this.connectOpts); - socket = tls2.connect(setServernameFromNonIpHost2(this.connectOpts)); - } else { - debug5("Creating `net.Socket`: %o", this.connectOpts); - socket = net3.connect(this.connectOpts); - } - const headers = typeof this.proxyHeaders === "function" ? this.proxyHeaders() : { ...this.proxyHeaders }; - const host = net3.isIPv6(opts.host) ? `[${opts.host}]` : opts.host; - let payload = `CONNECT ${host}:${opts.port} HTTP/1.1\r -`; - if (proxy.username || proxy.password) { - const auth = `${decodeURIComponent(proxy.username)}:${decodeURIComponent(proxy.password)}`; - headers["Proxy-Authorization"] = `Basic ${Buffer.from(auth).toString("base64")}`; - } - headers.Host = `${host}:${opts.port}`; - if (!headers["Proxy-Connection"]) { - headers["Proxy-Connection"] = this.keepAlive ? "Keep-Alive" : "close"; - } - for (const name of Object.keys(headers)) { - payload += `${name}: ${headers[name]}\r -`; - } - const proxyResponsePromise = (0, parse_proxy_response_1.parseProxyResponse)(socket); - socket.write(`${payload}\r -`); - const { connect: connect3, buffered } = await proxyResponsePromise; - req.emit("proxyConnect", connect3); - this.emit("proxyConnect", connect3, req); - if (connect3.statusCode === 200) { - req.once("socket", resume2); - if (opts.secureEndpoint) { - debug5("Upgrading socket connection to TLS"); - return tls2.connect({ - ...omit2(setServernameFromNonIpHost2(opts), "host", "path", "port"), - socket - }); - } - return socket; - } - socket.destroy(); - const fakeSocket = new net3.Socket({ writable: false }); - fakeSocket.readable = true; - req.once("socket", (s2) => { - debug5("Replaying proxy buffer for failed request"); - (0, assert_1.default)(s2.listenerCount("data") > 0); - s2.push(buffered); - s2.push(null); - }); - return fakeSocket; - } - }; - HttpsProxyAgent3.protocols = ["http", "https"]; - exports2.HttpsProxyAgent = HttpsProxyAgent3; - function resume2(socket) { - socket.resume(); - } - function omit2(obj, ...keys) { - const ret = {}; - let key; - for (key in obj) { - if (!keys.includes(key)) { - ret[key] = obj[key]; - } - } - return ret; - } - } -}); - // node_modules/http-proxy-agent/dist/index.js var require_dist3 = __commonJS({ "node_modules/http-proxy-agent/dist/index.js"(exports2) { @@ -26367,23 +26360,23 @@ var require_dist3 = __commonJS({ }; Object.defineProperty(exports2, "__esModule", { value: true }); exports2.HttpProxyAgent = void 0; - var net3 = __importStar(require("net")); - var tls2 = __importStar(require("tls")); + var net = __importStar(require("net")); + var tls = __importStar(require("tls")); var debug_1 = __importDefault(require_src2()); var events_1 = require("events"); var agent_base_1 = require_dist(); var url_1 = require("url"); - var debug5 = (0, debug_1.default)("http-proxy-agent"); + var debug3 = (0, debug_1.default)("http-proxy-agent"); var HttpProxyAgent2 = class extends agent_base_1.Agent { constructor(proxy, opts) { super(opts); this.proxy = typeof proxy === "string" ? new url_1.URL(proxy) : proxy; this.proxyHeaders = opts?.headers ?? {}; - debug5("Creating new HttpProxyAgent instance: %o", this.proxy.href); + debug3("Creating new HttpProxyAgent instance: %o", this.proxy.href); const host = (this.proxy.hostname || this.proxy.host).replace(/^\[|\]$/g, ""); const port = this.proxy.port ? parseInt(this.proxy.port, 10) : this.proxy.protocol === "https:" ? 443 : 80; this.connectOpts = { - ...opts ? omit2(opts, "headers") : null, + ...opts ? omit(opts, "headers") : null, host, port }; @@ -26425,22 +26418,22 @@ var require_dist3 = __commonJS({ } let first; let endOfHeaders; - debug5("Regenerating stored HTTP header string for request"); + debug3("Regenerating stored HTTP header string for request"); req._implicitHeader(); if (req.outputData && req.outputData.length > 0) { - debug5("Patching connection write() output buffer with updated header"); + debug3("Patching connection write() output buffer with updated header"); first = req.outputData[0].data; endOfHeaders = first.indexOf("\r\n\r\n") + 4; req.outputData[0].data = req._header + first.substring(endOfHeaders); - debug5("Output buffer: %o", req.outputData[0].data); + debug3("Output buffer: %o", req.outputData[0].data); } let socket; if (this.proxy.protocol === "https:") { - debug5("Creating `tls.Socket`: %o", this.connectOpts); - socket = tls2.connect(this.connectOpts); + debug3("Creating `tls.Socket`: %o", this.connectOpts); + socket = tls.connect(this.connectOpts); } else { - debug5("Creating `net.Socket`: %o", this.connectOpts); - socket = net3.connect(this.connectOpts); + debug3("Creating `net.Socket`: %o", this.connectOpts); + socket = net.connect(this.connectOpts); } await (0, events_1.once)(socket, "connect"); return socket; @@ -26448,7 +26441,7 @@ var require_dist3 = __commonJS({ }; HttpProxyAgent2.protocols = ["http", "https"]; exports2.HttpProxyAgent = HttpProxyAgent2; - function omit2(obj, ...keys) { + function omit(obj, ...keys) { const ret = {}; let key; for (key in obj) { @@ -47356,494 +47349,6 @@ var require_mistralai = __commonJS({ } }); -// node_modules/@commitlint/types/lib/ensure.js -var require_ensure = __commonJS({ - "node_modules/@commitlint/types/lib/ensure.js"(exports2) { - "use strict"; - Object.defineProperty(exports2, "__esModule", { value: true }); - } -}); - -// node_modules/@commitlint/types/lib/format.js -var require_format = __commonJS({ - "node_modules/@commitlint/types/lib/format.js"(exports2) { - "use strict"; - Object.defineProperty(exports2, "__esModule", { value: true }); - } -}); - -// node_modules/@commitlint/types/lib/is-ignored.js -var require_is_ignored = __commonJS({ - "node_modules/@commitlint/types/lib/is-ignored.js"(exports2) { - "use strict"; - Object.defineProperty(exports2, "__esModule", { value: true }); - } -}); - -// node_modules/@commitlint/types/lib/lint.js -var require_lint = __commonJS({ - "node_modules/@commitlint/types/lib/lint.js"(exports2) { - "use strict"; - Object.defineProperty(exports2, "__esModule", { value: true }); - } -}); - -// node_modules/@commitlint/types/lib/load.js -var require_load = __commonJS({ - "node_modules/@commitlint/types/lib/load.js"(exports2) { - "use strict"; - Object.defineProperty(exports2, "__esModule", { value: true }); - } -}); - -// node_modules/@commitlint/types/lib/parse.js -var require_parse3 = __commonJS({ - "node_modules/@commitlint/types/lib/parse.js"(exports2) { - "use strict"; - Object.defineProperty(exports2, "__esModule", { value: true }); - } -}); - -// node_modules/@commitlint/types/lib/prompt.js -var require_prompt = __commonJS({ - "node_modules/@commitlint/types/lib/prompt.js"(exports2) { - "use strict"; - Object.defineProperty(exports2, "__esModule", { value: true }); - } -}); - -// node_modules/@commitlint/types/lib/rules.js -var require_rules = __commonJS({ - "node_modules/@commitlint/types/lib/rules.js"(exports2) { - "use strict"; - Object.defineProperty(exports2, "__esModule", { value: true }); - exports2.RuleConfigQuality = exports2.RuleConfigSeverity = void 0; - var RuleConfigSeverity2; - (function(RuleConfigSeverity3) { - RuleConfigSeverity3[RuleConfigSeverity3["Disabled"] = 0] = "Disabled"; - RuleConfigSeverity3[RuleConfigSeverity3["Warning"] = 1] = "Warning"; - RuleConfigSeverity3[RuleConfigSeverity3["Error"] = 2] = "Error"; - })(RuleConfigSeverity2 || (exports2.RuleConfigSeverity = RuleConfigSeverity2 = {})); - var RuleConfigQuality; - (function(RuleConfigQuality2) { - RuleConfigQuality2[RuleConfigQuality2["User"] = 0] = "User"; - RuleConfigQuality2[RuleConfigQuality2["Qualified"] = 1] = "Qualified"; - })(RuleConfigQuality || (exports2.RuleConfigQuality = RuleConfigQuality = {})); - } -}); - -// node_modules/@commitlint/types/lib/index.js -var require_lib5 = __commonJS({ - "node_modules/@commitlint/types/lib/index.js"(exports2) { - "use strict"; - var __createBinding = exports2 && exports2.__createBinding || (Object.create ? function(o3, m5, k7, k22) { - if (k22 === void 0) k22 = k7; - var desc = Object.getOwnPropertyDescriptor(m5, k7); - if (!desc || ("get" in desc ? !m5.__esModule : desc.writable || desc.configurable)) { - desc = { enumerable: true, get: function() { - return m5[k7]; - } }; - } - Object.defineProperty(o3, k22, desc); - } : function(o3, m5, k7, k22) { - if (k22 === void 0) k22 = k7; - o3[k22] = m5[k7]; - }); - var __exportStar = exports2 && exports2.__exportStar || function(m5, exports3) { - for (var p4 in m5) if (p4 !== "default" && !Object.prototype.hasOwnProperty.call(exports3, p4)) __createBinding(exports3, m5, p4); - }; - Object.defineProperty(exports2, "__esModule", { value: true }); - __exportStar(require_ensure(), exports2); - __exportStar(require_format(), exports2); - __exportStar(require_is_ignored(), exports2); - __exportStar(require_lint(), exports2); - __exportStar(require_load(), exports2); - __exportStar(require_parse3(), exports2); - __exportStar(require_prompt(), exports2); - __exportStar(require_rules(), exports2); - } -}); - -// node_modules/ignore/index.js -var require_ignore = __commonJS({ - "node_modules/ignore/index.js"(exports2, module2) { - function makeArray(subject) { - return Array.isArray(subject) ? subject : [subject]; - } - var EMPTY = ""; - var SPACE = " "; - var ESCAPE = "\\"; - var REGEX_TEST_BLANK_LINE = /^\s+$/; - var REGEX_INVALID_TRAILING_BACKSLASH = /(?:[^\\]|^)\\$/; - var REGEX_REPLACE_LEADING_EXCAPED_EXCLAMATION = /^\\!/; - var REGEX_REPLACE_LEADING_EXCAPED_HASH = /^\\#/; - var REGEX_SPLITALL_CRLF = /\r?\n/g; - var REGEX_TEST_INVALID_PATH = /^\.*\/|^\.+$/; - var SLASH = "/"; - var TMP_KEY_IGNORE = "node-ignore"; - if (typeof Symbol !== "undefined") { - TMP_KEY_IGNORE = Symbol.for("node-ignore"); - } - var KEY_IGNORE = TMP_KEY_IGNORE; - var define2 = (object, key, value) => Object.defineProperty(object, key, { value }); - var REGEX_REGEXP_RANGE = /([0-z])-([0-z])/g; - var RETURN_FALSE = () => false; - var sanitizeRange = (range) => range.replace( - REGEX_REGEXP_RANGE, - (match, from, to) => from.charCodeAt(0) <= to.charCodeAt(0) ? match : EMPTY - ); - var cleanRangeBackSlash = (slashes) => { - const { length } = slashes; - return slashes.slice(0, length - length % 2); - }; - var REPLACERS = [ - [ - // remove BOM - // TODO: - // Other similar zero-width characters? - /^\uFEFF/, - () => EMPTY - ], - // > Trailing spaces are ignored unless they are quoted with backslash ("\") - [ - // (a\ ) -> (a ) - // (a ) -> (a) - // (a \ ) -> (a ) - /\\?\s+$/, - (match) => match.indexOf("\\") === 0 ? SPACE : EMPTY - ], - // replace (\ ) with ' ' - [ - /\\\s/g, - () => SPACE - ], - // Escape metacharacters - // which is written down by users but means special for regular expressions. - // > There are 12 characters with special meanings: - // > - the backslash \, - // > - the caret ^, - // > - the dollar sign $, - // > - the period or dot ., - // > - the vertical bar or pipe symbol |, - // > - the question mark ?, - // > - the asterisk or star *, - // > - the plus sign +, - // > - the opening parenthesis (, - // > - the closing parenthesis ), - // > - and the opening square bracket [, - // > - the opening curly brace {, - // > These special characters are often called "metacharacters". - [ - /[\\$.|*+(){^]/g, - (match) => `\\${match}` - ], - [ - // > a question mark (?) matches a single character - /(?!\\)\?/g, - () => "[^/]" - ], - // leading slash - [ - // > A leading slash matches the beginning of the pathname. - // > For example, "/*.c" matches "cat-file.c" but not "mozilla-sha1/sha1.c". - // A leading slash matches the beginning of the pathname - /^\//, - () => "^" - ], - // replace special metacharacter slash after the leading slash - [ - /\//g, - () => "\\/" - ], - [ - // > A leading "**" followed by a slash means match in all directories. - // > For example, "**/foo" matches file or directory "foo" anywhere, - // > the same as pattern "foo". - // > "**/foo/bar" matches file or directory "bar" anywhere that is directly - // > under directory "foo". - // Notice that the '*'s have been replaced as '\\*' - /^\^*\\\*\\\*\\\//, - // '**/foo' <-> 'foo' - () => "^(?:.*\\/)?" - ], - // starting - [ - // there will be no leading '/' - // (which has been replaced by section "leading slash") - // If starts with '**', adding a '^' to the regular expression also works - /^(?=[^^])/, - function startingReplacer() { - return !/\/(?!$)/.test(this) ? "(?:^|\\/)" : "^"; - } - ], - // two globstars - [ - // Use lookahead assertions so that we could match more than one `'/**'` - /\\\/\\\*\\\*(?=\\\/|$)/g, - // Zero, one or several directories - // should not use '*', or it will be replaced by the next replacer - // Check if it is not the last `'/**'` - (_7, index, str2) => index + 6 < str2.length ? "(?:\\/[^\\/]+)*" : "\\/.+" - ], - // normal intermediate wildcards - [ - // Never replace escaped '*' - // ignore rule '\*' will match the path '*' - // 'abc.*/' -> go - // 'abc.*' -> skip this rule, - // coz trailing single wildcard will be handed by [trailing wildcard] - /(^|[^\\]+)(\\\*)+(?=.+)/g, - // '*.js' matches '.js' - // '*.js' doesn't match 'abc' - (_7, p1, p22) => { - const unescaped = p22.replace(/\\\*/g, "[^\\/]*"); - return p1 + unescaped; - } - ], - [ - // unescape, revert step 3 except for back slash - // For example, if a user escape a '\\*', - // after step 3, the result will be '\\\\\\*' - /\\\\\\(?=[$.|*+(){^])/g, - () => ESCAPE - ], - [ - // '\\\\' -> '\\' - /\\\\/g, - () => ESCAPE - ], - [ - // > The range notation, e.g. [a-zA-Z], - // > can be used to match one of the characters in a range. - // `\` is escaped by step 3 - /(\\)?\[([^\]/]*?)(\\*)($|\])/g, - (match, leadEscape, range, endEscape, close) => leadEscape === ESCAPE ? `\\[${range}${cleanRangeBackSlash(endEscape)}${close}` : close === "]" ? endEscape.length % 2 === 0 ? `[${sanitizeRange(range)}${endEscape}]` : "[]" : "[]" - ], - // ending - [ - // 'js' will not match 'js.' - // 'ab' will not match 'abc' - /(?:[^*])$/, - // WTF! - // https://git-scm.com/docs/gitignore - // changes in [2.22.1](https://git-scm.com/docs/gitignore/2.22.1) - // which re-fixes #24, #38 - // > If there is a separator at the end of the pattern then the pattern - // > will only match directories, otherwise the pattern can match both - // > files and directories. - // 'js*' will not match 'a.js' - // 'js/' will not match 'a.js' - // 'js' will match 'a.js' and 'a.js/' - (match) => /\/$/.test(match) ? `${match}$` : `${match}(?=$|\\/$)` - ], - // trailing wildcard - [ - /(\^|\\\/)?\\\*$/, - (_7, p1) => { - const prefix = p1 ? `${p1}[^/]+` : "[^/]*"; - return `${prefix}(?=$|\\/$)`; - } - ] - ]; - var regexCache = /* @__PURE__ */ Object.create(null); - var makeRegex = (pattern, ignoreCase) => { - let source = regexCache[pattern]; - if (!source) { - source = REPLACERS.reduce( - (prev, current) => prev.replace(current[0], current[1].bind(pattern)), - pattern - ); - regexCache[pattern] = source; - } - return ignoreCase ? new RegExp(source, "i") : new RegExp(source); - }; - var isString2 = (subject) => typeof subject === "string"; - var checkPattern = (pattern) => pattern && isString2(pattern) && !REGEX_TEST_BLANK_LINE.test(pattern) && !REGEX_INVALID_TRAILING_BACKSLASH.test(pattern) && pattern.indexOf("#") !== 0; - var splitPattern = (pattern) => pattern.split(REGEX_SPLITALL_CRLF); - var IgnoreRule = class { - constructor(origin2, pattern, negative, regex) { - this.origin = origin2; - this.pattern = pattern; - this.negative = negative; - this.regex = regex; - } - }; - var createRule = (pattern, ignoreCase) => { - const origin2 = pattern; - let negative = false; - if (pattern.indexOf("!") === 0) { - negative = true; - pattern = pattern.substr(1); - } - pattern = pattern.replace(REGEX_REPLACE_LEADING_EXCAPED_EXCLAMATION, "!").replace(REGEX_REPLACE_LEADING_EXCAPED_HASH, "#"); - const regex = makeRegex(pattern, ignoreCase); - return new IgnoreRule( - origin2, - pattern, - negative, - regex - ); - }; - var throwError = (message, Ctor) => { - throw new Ctor(message); - }; - var checkPath = (path5, originalPath, doThrow) => { - if (!isString2(path5)) { - return doThrow( - `path must be a string, but got \`${originalPath}\``, - TypeError - ); - } - if (!path5) { - return doThrow(`path must not be empty`, TypeError); - } - if (checkPath.isNotRelative(path5)) { - const r3 = "`path.relative()`d"; - return doThrow( - `path should be a ${r3} string, but got "${originalPath}"`, - RangeError - ); - } - return true; - }; - var isNotRelative = (path5) => REGEX_TEST_INVALID_PATH.test(path5); - checkPath.isNotRelative = isNotRelative; - checkPath.convert = (p4) => p4; - var Ignore2 = class { - constructor({ - ignorecase = true, - ignoreCase = ignorecase, - allowRelativePaths = false - } = {}) { - define2(this, KEY_IGNORE, true); - this._rules = []; - this._ignoreCase = ignoreCase; - this._allowRelativePaths = allowRelativePaths; - this._initCache(); - } - _initCache() { - this._ignoreCache = /* @__PURE__ */ Object.create(null); - this._testCache = /* @__PURE__ */ Object.create(null); - } - _addPattern(pattern) { - if (pattern && pattern[KEY_IGNORE]) { - this._rules = this._rules.concat(pattern._rules); - this._added = true; - return; - } - if (checkPattern(pattern)) { - const rule = createRule(pattern, this._ignoreCase); - this._added = true; - this._rules.push(rule); - } - } - // @param {Array | string | Ignore} pattern - add(pattern) { - this._added = false; - makeArray( - isString2(pattern) ? splitPattern(pattern) : pattern - ).forEach(this._addPattern, this); - if (this._added) { - this._initCache(); - } - return this; - } - // legacy - addPattern(pattern) { - return this.add(pattern); - } - // | ignored : unignored - // negative | 0:0 | 0:1 | 1:0 | 1:1 - // -------- | ------- | ------- | ------- | -------- - // 0 | TEST | TEST | SKIP | X - // 1 | TESTIF | SKIP | TEST | X - // - SKIP: always skip - // - TEST: always test - // - TESTIF: only test if checkUnignored - // - X: that never happen - // @param {boolean} whether should check if the path is unignored, - // setting `checkUnignored` to `false` could reduce additional - // path matching. - // @returns {TestResult} true if a file is ignored - _testOne(path5, checkUnignored) { - let ignored = false; - let unignored = false; - this._rules.forEach((rule) => { - const { negative } = rule; - if (unignored === negative && ignored !== unignored || negative && !ignored && !unignored && !checkUnignored) { - return; - } - const matched = rule.regex.test(path5); - if (matched) { - ignored = !negative; - unignored = negative; - } - }); - return { - ignored, - unignored - }; - } - // @returns {TestResult} - _test(originalPath, cache, checkUnignored, slices) { - const path5 = originalPath && checkPath.convert(originalPath); - checkPath( - path5, - originalPath, - this._allowRelativePaths ? RETURN_FALSE : throwError - ); - return this._t(path5, cache, checkUnignored, slices); - } - _t(path5, cache, checkUnignored, slices) { - if (path5 in cache) { - return cache[path5]; - } - if (!slices) { - slices = path5.split(SLASH); - } - slices.pop(); - if (!slices.length) { - return cache[path5] = this._testOne(path5, checkUnignored); - } - const parent = this._t( - slices.join(SLASH) + SLASH, - cache, - checkUnignored, - slices - ); - return cache[path5] = parent.ignored ? parent : this._testOne(path5, checkUnignored); - } - ignores(path5) { - return this._test(path5, this._ignoreCache, false).ignored; - } - createFilter() { - return (path5) => !this.ignores(path5); - } - filter(paths) { - return makeArray(paths).filter(this.createFilter()); - } - // @returns {TestResult} - test(path5) { - return this._test(path5, this._testCache, true); - } - }; - var factory = (options) => new Ignore2(options); - var isPathValid = (path5) => checkPath(path5 && checkPath.convert(path5), path5, RETURN_FALSE); - factory.isPathValid = isPathValid; - factory.default = factory; - module2.exports = factory; - if ( - // Detect `process` so that it can run in browsers. - typeof process !== "undefined" && (process.env && process.env.IGNORE_TEST_WIN32 || process.platform === "win32") - ) { - const makePosix = (str2) => /^\\\\\?\\/.test(str2) || /["<>|\u0000-\u001F]+/u.test(str2) ? str2 : str2.replace(/\\/g, "/"); - checkPath.convert = makePosix; - const REGIX_IS_WINDOWS_PATH_ABSOLUTE = /^[a-z]:\//i; - checkPath.isNotRelative = (path5) => REGIX_IS_WINDOWS_PATH_ABSOLUTE.test(path5) || isNotRelative(path5); - } - } -}); - // node_modules/undici/lib/core/symbols.js var require_symbols = __commonJS({ "node_modules/undici/lib/core/symbols.js"(exports2, module2) { @@ -48247,11 +47752,11 @@ var require_constants2 = __commonJS({ var require_util2 = __commonJS({ "node_modules/undici/lib/core/util.js"(exports2, module2) { "use strict"; - var assert2 = require("assert"); + var assert = require("assert"); var { kDestroyed, kBodyUsed } = require_symbols(); var { IncomingMessage } = require("http"); var stream4 = require("stream"); - var net3 = require("net"); + var net = require("net"); var { InvalidArgumentError } = require_errors3(); var { Blob: Blob5 } = require("buffer"); var nodeUtil = require("util"); @@ -48329,7 +47834,7 @@ var require_util2 = __commonJS({ function getHostname(host) { if (host[0] === "[") { const idx2 = host.indexOf("]"); - assert2(idx2 !== -1); + assert(idx2 !== -1); return host.substring(1, idx2); } const idx = host.indexOf(":"); @@ -48340,9 +47845,9 @@ var require_util2 = __commonJS({ if (!host) { return null; } - assert2.strictEqual(typeof host, "string"); + assert.strictEqual(typeof host, "string"); const servername = getHostname(host); - if (net3.isIP(servername)) { + if (net.isIP(servername)) { return ""; } return servername; @@ -50943,7 +50448,7 @@ var require_util3 = __commonJS({ var { getGlobalOrigin } = require_global(); var { performance: performance2 } = require("perf_hooks"); var { isBlobLike: isBlobLike3, toUSVString, ReadableStreamFrom } = require_util2(); - var assert2 = require("assert"); + var assert = require("assert"); var { isUint8Array } = require("util/types"); var supportedHashes = []; var crypto3; @@ -51133,7 +50638,7 @@ var require_util3 = __commonJS({ } function determineRequestsReferrer(request3) { const policy = request3.referrerPolicy; - assert2(policy); + assert(policy); let referrerSource = null; if (request3.referrer === "client") { const globalOrigin = getGlobalOrigin(); @@ -51191,7 +50696,7 @@ var require_util3 = __commonJS({ } } function stripURLForReferrer(url2, originOnly) { - assert2(url2 instanceof URL); + assert(url2 instanceof URL); if (url2.protocol === "file:" || url2.protocol === "about:" || url2.protocol === "blank:") { return "no-referrer"; } @@ -51370,7 +50875,7 @@ var require_util3 = __commonJS({ if (result === void 0) { throw new TypeError("Value is not JSON serializable"); } - assert2(typeof result === "string"); + assert(typeof result === "string"); return result; } var esIteratorPrototype = Object.getPrototypeOf(Object.getPrototypeOf([][Symbol.iterator]())); @@ -51464,7 +50969,7 @@ var require_util3 = __commonJS({ } function isomorphicEncode(input) { for (let i3 = 0; i3 < input.length; i3++) { - assert2(input.charCodeAt(i3) <= 255); + assert(input.charCodeAt(i3) <= 255); } return input; } @@ -51484,7 +50989,7 @@ var require_util3 = __commonJS({ } } function urlIsLocal(url2) { - assert2("protocol" in url2); + assert("protocol" in url2); const protocol = url2.protocol; return protocol === "about:" || protocol === "blob:" || protocol === "data:"; } @@ -51495,7 +51000,7 @@ var require_util3 = __commonJS({ return url2.protocol === "https:"; } function urlIsHttpHttpsScheme(url2) { - assert2("protocol" in url2); + assert("protocol" in url2); const protocol = url2.protocol; return protocol === "http:" || protocol === "https:"; } @@ -51937,7 +51442,7 @@ var require_webidl = __commonJS({ // node_modules/undici/lib/fetch/dataURL.js var require_dataURL = __commonJS({ "node_modules/undici/lib/fetch/dataURL.js"(exports2, module2) { - var assert2 = require("assert"); + var assert = require("assert"); var { atob: atob2 } = require("buffer"); var { isomorphicDecode } = require_util3(); var encoder = new TextEncoder(); @@ -51945,7 +51450,7 @@ var require_dataURL = __commonJS({ var HTTP_WHITESPACE_REGEX = /(\u000A|\u000D|\u0009|\u0020)/; var HTTP_QUOTED_STRING_TOKENS = /[\u0009|\u0020-\u007E|\u0080-\u00FF]/; function dataURLProcessor(dataURL) { - assert2(dataURL.protocol === "data:"); + assert(dataURL.protocol === "data:"); let input = URLSerializer(dataURL, true); input = input.slice(5); const position = { position: 0 }; @@ -52131,7 +51636,7 @@ var require_dataURL = __commonJS({ function collectAnHTTPQuotedString(input, position, extractValue) { const positionStart = position.position; let value = ""; - assert2(input[position.position] === '"'); + assert(input[position.position] === '"'); position.position++; while (true) { value += collectASequenceOfCodePoints( @@ -52152,7 +51657,7 @@ var require_dataURL = __commonJS({ value += input[position.position]; position.position++; } else { - assert2(quoteOrBackslash === '"'); + assert(quoteOrBackslash === '"'); break; } } @@ -52162,7 +51667,7 @@ var require_dataURL = __commonJS({ return input.slice(positionStart, position.position); } function serializeAMimeType(mimeType) { - assert2(mimeType !== "failure"); + assert(mimeType !== "failure"); const { parameters, essence } = mimeType; let serialization = essence; for (let [name, value] of parameters.entries()) { @@ -52581,7 +52086,7 @@ var require_body = __commonJS({ var { DOMException: DOMException3, structuredClone: structuredClone2 } = require_constants3(); var { Blob: Blob5, File: NativeFile } = require("buffer"); var { kBodyUsed } = require_symbols(); - var assert2 = require("assert"); + var assert = require("assert"); var { isErrored } = require_util2(); var { isUint8Array, isArrayBuffer: isArrayBuffer3 } = require("util/types"); var { File: UndiciFile } = require_file(); @@ -52619,7 +52124,7 @@ var require_body = __commonJS({ type: void 0 }); } - assert2(isReadableStreamLike(stream4)); + assert(isReadableStreamLike(stream4)); let action = null; let source = null; let length = null; @@ -52735,8 +52240,8 @@ Content-Type: ${value.type || "application/octet-stream"}\r ReadableStream7 = require("stream/web").ReadableStream; } if (object instanceof ReadableStream7) { - assert2(!util4.isDisturbed(object), "The body has already been consumed."); - assert2(!object.locked, "The stream is locked."); + assert(!util4.isDisturbed(object), "The body has already been consumed."); + assert(!object.locked, "The stream is locked."); } return extractBody(object, keepalive); } @@ -52947,7 +52452,7 @@ var require_request = __commonJS({ InvalidArgumentError, NotSupportedError } = require_errors3(); - var assert2 = require("assert"); + var assert = require("assert"); var { kHTTP2BuildRequest, kHTTP2CopyHeaders, kHTTP1BuildRequest } = require_symbols(); var util4 = require_util2(); var tokenRegExp = /^[\^_`a-zA-Z\-0-9!#$%&'*+.|~]+$/; @@ -53128,8 +52633,8 @@ var require_request = __commonJS({ } } onConnect(abort) { - assert2(!this.aborted); - assert2(!this.completed); + assert(!this.aborted); + assert(!this.completed); if (this.error) { abort(this.error); } else { @@ -53137,21 +52642,21 @@ var require_request = __commonJS({ return this[kHandler].onConnect(abort); } } - onHeaders(statusCode, headers, resume2, statusText) { - assert2(!this.aborted); - assert2(!this.completed); + onHeaders(statusCode, headers, resume, statusText) { + assert(!this.aborted); + assert(!this.completed); if (channels.headers.hasSubscribers) { channels.headers.publish({ request: this, response: { statusCode, headers, statusText } }); } try { - return this[kHandler].onHeaders(statusCode, headers, resume2, statusText); + return this[kHandler].onHeaders(statusCode, headers, resume, statusText); } catch (err) { this.abort(err); } } onData(chunk) { - assert2(!this.aborted); - assert2(!this.completed); + assert(!this.aborted); + assert(!this.completed); try { return this[kHandler].onData(chunk); } catch (err) { @@ -53160,13 +52665,13 @@ var require_request = __commonJS({ } } onUpgrade(statusCode, headers, socket) { - assert2(!this.aborted); - assert2(!this.completed); + assert(!this.aborted); + assert(!this.completed); return this[kHandler].onUpgrade(statusCode, headers, socket); } onComplete(trailers) { this.onFinally(); - assert2(!this.aborted); + assert(!this.aborted); this.completed = true; if (channels.trailers.hasSubscribers) { channels.trailers.publish({ request: this, trailers }); @@ -53496,11 +53001,11 @@ var require_dispatcher_base = __commonJS({ var require_connect = __commonJS({ "node_modules/undici/lib/core/connect.js"(exports2, module2) { "use strict"; - var net3 = require("net"); - var assert2 = require("assert"); + var net = require("net"); + var assert = require("assert"); var util4 = require_util2(); var { InvalidArgumentError, ConnectTimeoutError } = require_errors3(); - var tls2; + var tls; var SessionCache; if (global.FinalizationRegistry && !process.env.NODE_V8_COVERAGE) { SessionCache = class WeakSessionCache { @@ -53558,17 +53063,17 @@ var require_connect = __commonJS({ const sessionCache = new SessionCache(maxCachedSessions == null ? 100 : maxCachedSessions); timeout = timeout == null ? 1e4 : timeout; allowH2 = allowH2 != null ? allowH2 : false; - return function connect3({ hostname, host, protocol, port, servername, localAddress, httpSocket }, callback) { + return function connect({ hostname, host, protocol, port, servername, localAddress, httpSocket }, callback) { let socket; if (protocol === "https:") { - if (!tls2) { - tls2 = require("tls"); + if (!tls) { + tls = require("tls"); } servername = servername || options.servername || util4.getServerName(host) || null; const sessionKey = servername || hostname; const session = sessionCache.get(sessionKey) || null; - assert2(sessionKey); - socket = tls2.connect({ + assert(sessionKey); + socket = tls.connect({ highWaterMark: 16384, // TLS in node can't have bigger HWM anyway... ...options, @@ -53586,8 +53091,8 @@ var require_connect = __commonJS({ sessionCache.set(sessionKey, session2); }); } else { - assert2(!httpSocket, "httpSocket can only be sent on TLS update"); - socket = net3.connect({ + assert(!httpSocket, "httpSocket can only be sent on TLS update"); + socket = net.connect({ highWaterMark: 64 * 1024, // Same as nodejs fs streams. ...options, @@ -53995,7 +53500,7 @@ var require_RedirectHandler = __commonJS({ "use strict"; var util4 = require_util2(); var { kBodyUsed } = require_symbols(); - var assert2 = require("assert"); + var assert = require("assert"); var { InvalidArgumentError } = require_errors3(); var EE = require("events"); var redirectableStatusCodes = [300, 301, 302, 303, 307, 308]; @@ -54006,7 +53511,7 @@ var require_RedirectHandler = __commonJS({ this[kBodyUsed] = false; } async *[Symbol.asyncIterator]() { - assert2(!this[kBodyUsed], "disturbed"); + assert(!this[kBodyUsed], "disturbed"); this[kBodyUsed] = true; yield* this[kBody]; } @@ -54027,7 +53532,7 @@ var require_RedirectHandler = __commonJS({ if (util4.isStream(this.opts.body)) { if (util4.bodyLength(this.opts.body) === 0) { this.opts.body.on("data", function() { - assert2(false); + assert(false); }); } if (typeof this.opts.body.readableDidRead !== "boolean") { @@ -54052,13 +53557,13 @@ var require_RedirectHandler = __commonJS({ onError(error) { this.handler.onError(error); } - onHeaders(statusCode, headers, resume2, statusText) { + onHeaders(statusCode, headers, resume, statusText) { this.location = this.history.length >= this.maxRedirections || util4.isDisturbed(this.opts.body) ? null : parseLocation(statusCode, headers); if (this.opts.origin) { this.history.push(new URL(this.opts.path, this.opts.origin)); } if (!this.location) { - return this.handler.onHeaders(statusCode, headers, resume2, statusText); + return this.handler.onHeaders(statusCode, headers, resume, statusText); } const { origin: origin2, pathname, search } = util4.parseURL(new URL(this.location, this.opts.origin && new URL(this.opts.path, this.opts.origin))); const path5 = search ? `${pathname}${search}` : pathname; @@ -54131,7 +53636,7 @@ var require_RedirectHandler = __commonJS({ } } } else { - assert2(headers == null, "headers must be an object or an array"); + assert(headers == null, "headers must be an object or an array"); } return ret; } @@ -54179,9 +53684,9 @@ var require_llhttp_simd_wasm = __commonJS({ var require_client = __commonJS({ "node_modules/undici/lib/client.js"(exports2, module2) { "use strict"; - var assert2 = require("assert"); - var net3 = require("net"); - var http4 = require("http"); + var assert = require("assert"); + var net = require("net"); + var http3 = require("http"); var { pipeline } = require("stream"); var util4 = require_util2(); var timers = require_timers(); @@ -54309,11 +53814,11 @@ var require_client = __commonJS({ keepAliveTimeoutThreshold, socketPath, pipelining, - tls: tls2, + tls, strictContentLength, maxCachedSessions, maxRedirections, - connect: connect4, + connect: connect2, maxRequestsPerClient, localAddress, maxResponseSize, @@ -54363,7 +53868,7 @@ var require_client = __commonJS({ if (bodyTimeout != null && (!Number.isInteger(bodyTimeout) || bodyTimeout < 0)) { throw new InvalidArgumentError("bodyTimeout must be a positive integer or zero"); } - if (connect4 != null && typeof connect4 !== "function" && typeof connect4 !== "object") { + if (connect2 != null && typeof connect2 !== "function" && typeof connect2 !== "object") { throw new InvalidArgumentError("connect must be a function or an object"); } if (maxRedirections != null && (!Number.isInteger(maxRedirections) || maxRedirections < 0)) { @@ -54372,7 +53877,7 @@ var require_client = __commonJS({ if (maxRequestsPerClient != null && (!Number.isInteger(maxRequestsPerClient) || maxRequestsPerClient < 0)) { throw new InvalidArgumentError("maxRequestsPerClient must be a positive number"); } - if (localAddress != null && (typeof localAddress !== "string" || net3.isIP(localAddress) === 0)) { + if (localAddress != null && (typeof localAddress !== "string" || net.isIP(localAddress) === 0)) { throw new InvalidArgumentError("localAddress must be valid string IP address"); } if (maxResponseSize != null && (!Number.isInteger(maxResponseSize) || maxResponseSize < -1)) { @@ -54387,23 +53892,23 @@ var require_client = __commonJS({ if (maxConcurrentStreams != null && (typeof maxConcurrentStreams !== "number" || maxConcurrentStreams < 1)) { throw new InvalidArgumentError("maxConcurrentStreams must be a possitive integer, greater than 0"); } - if (typeof connect4 !== "function") { - connect4 = buildConnector({ - ...tls2, + if (typeof connect2 !== "function") { + connect2 = buildConnector({ + ...tls, maxCachedSessions, allowH2, socketPath, timeout: connectTimeout, ...util4.nodeHasAutoSelectFamily && autoSelectFamily ? { autoSelectFamily, autoSelectFamilyAttemptTimeout } : void 0, - ...connect4 + ...connect2 }); } this[kInterceptors] = interceptors && interceptors.Client && Array.isArray(interceptors.Client) ? interceptors.Client : [createRedirectInterceptor({ maxRedirections })]; this[kUrl] = util4.parseOrigin(url2); - this[kConnector] = connect4; + this[kConnector] = connect2; this[kSocket] = null; this[kPipelining] = pipelining != null ? pipelining : 1; - this[kMaxHeadersSize] = maxHeaderSize || http4.maxHeaderSize; + this[kMaxHeadersSize] = maxHeaderSize || http3.maxHeaderSize; this[kKeepAliveDefaultTimeout] = keepAliveTimeout == null ? 4e3 : keepAliveTimeout; this[kKeepAliveMaxTimeout] = keepAliveMaxTimeout == null ? 6e5 : keepAliveMaxTimeout; this[kKeepAliveTimeoutThreshold] = keepAliveTimeoutThreshold == null ? 1e3 : keepAliveTimeoutThreshold; @@ -54440,7 +53945,7 @@ var require_client = __commonJS({ } set pipelining(value) { this[kPipelining] = value; - resume2(this, true); + resume(this, true); } get [kPending]() { return this[kQueue].length - this[kPendingIdx]; @@ -54460,7 +53965,7 @@ var require_client = __commonJS({ } /* istanbul ignore: only used for test */ [kConnect](cb) { - connect3(this); + connect(this); this.once("connect", cb); } [kDispatch](opts, handler) { @@ -54470,9 +53975,9 @@ var require_client = __commonJS({ if (this[kResuming]) { } else if (util4.bodyLength(request3.body) == null && util4.isIterable(request3.body)) { this[kResuming] = 1; - process.nextTick(resume2, this); + process.nextTick(resume, this); } else { - resume2(this, true); + resume(this, true); } if (this[kResuming] && this[kNeedDrain] !== 2 && this[kBusy]) { this[kNeedDrain] = 2; @@ -54512,12 +54017,12 @@ var require_client = __commonJS({ } else { util4.destroy(this[kSocket].on("close", callback), err); } - resume2(this); + resume(this); }); } }; function onHttp2SessionError(err) { - assert2(err.code !== "ERR_TLS_CERT_ALTNAME_INVALID"); + assert(err.code !== "ERR_TLS_CERT_ALTNAME_INVALID"); this[kSocket][kError] = err; onError(this[kClient], err); } @@ -54538,7 +54043,7 @@ var require_client = __commonJS({ client[kSocket] = null; client[kHTTP2Session] = null; if (client.destroyed) { - assert2(this[kPending] === 0); + assert(this[kPending] === 0); const requests = client[kQueue].splice(client[kRunningIdx]); for (let i3 = 0; i3 < requests.length; i3++) { const request3 = requests[i3]; @@ -54550,14 +54055,14 @@ var require_client = __commonJS({ errorRequest(client, request3, err); } client[kPendingIdx] = client[kRunningIdx]; - assert2(client[kRunning] === 0); + assert(client[kRunning] === 0); client.emit( "disconnect", client[kUrl], [client], err ); - resume2(client); + resume(client); } var constants3 = require_constants4(); var createRedirectInterceptor = require_redirectInterceptor(); @@ -54577,35 +54082,35 @@ var require_client = __commonJS({ return 0; }, wasm_on_status: (p4, at2, len) => { - assert2.strictEqual(currentParser.ptr, p4); + assert.strictEqual(currentParser.ptr, p4); const start = at2 - currentBufferPtr + currentBufferRef.byteOffset; return currentParser.onStatus(new FastBuffer(currentBufferRef.buffer, start, len)) || 0; }, wasm_on_message_begin: (p4) => { - assert2.strictEqual(currentParser.ptr, p4); + assert.strictEqual(currentParser.ptr, p4); return currentParser.onMessageBegin() || 0; }, wasm_on_header_field: (p4, at2, len) => { - assert2.strictEqual(currentParser.ptr, p4); + assert.strictEqual(currentParser.ptr, p4); const start = at2 - currentBufferPtr + currentBufferRef.byteOffset; return currentParser.onHeaderField(new FastBuffer(currentBufferRef.buffer, start, len)) || 0; }, wasm_on_header_value: (p4, at2, len) => { - assert2.strictEqual(currentParser.ptr, p4); + assert.strictEqual(currentParser.ptr, p4); const start = at2 - currentBufferPtr + currentBufferRef.byteOffset; return currentParser.onHeaderValue(new FastBuffer(currentBufferRef.buffer, start, len)) || 0; }, wasm_on_headers_complete: (p4, statusCode, upgrade, shouldKeepAlive) => { - assert2.strictEqual(currentParser.ptr, p4); + assert.strictEqual(currentParser.ptr, p4); return currentParser.onHeadersComplete(statusCode, Boolean(upgrade), Boolean(shouldKeepAlive)) || 0; }, wasm_on_body: (p4, at2, len) => { - assert2.strictEqual(currentParser.ptr, p4); + assert.strictEqual(currentParser.ptr, p4); const start = at2 - currentBufferPtr + currentBufferRef.byteOffset; return currentParser.onBody(new FastBuffer(currentBufferRef.buffer, start, len)) || 0; }, wasm_on_message_complete: (p4) => { - assert2.strictEqual(currentParser.ptr, p4); + assert.strictEqual(currentParser.ptr, p4); return currentParser.onMessageComplete() || 0; } /* eslint-enable camelcase */ @@ -54624,7 +54129,7 @@ var require_client = __commonJS({ var TIMEOUT_IDLE = 3; var Parser = class { constructor(client, socket, { exports: exports3 }) { - assert2(Number.isFinite(client[kMaxHeadersSize]) && client[kMaxHeadersSize] > 0); + assert(Number.isFinite(client[kMaxHeadersSize]) && client[kMaxHeadersSize] > 0); this.llhttp = exports3; this.ptr = this.llhttp.llhttp_alloc(constants3.TYPE.RESPONSE); this.client = client; @@ -54670,10 +54175,10 @@ var require_client = __commonJS({ if (this.socket.destroyed || !this.paused) { return; } - assert2(this.ptr != null); - assert2(currentParser == null); + assert(this.ptr != null); + assert(currentParser == null); this.llhttp.llhttp_resume(this.ptr); - assert2(this.timeoutType === TIMEOUT_BODY); + assert(this.timeoutType === TIMEOUT_BODY); if (this.timeout) { if (this.timeout.refresh) { this.timeout.refresh(); @@ -54693,9 +54198,9 @@ var require_client = __commonJS({ } } execute(data) { - assert2(this.ptr != null); - assert2(currentParser == null); - assert2(!this.paused); + assert(this.ptr != null); + assert(currentParser == null); + assert(!this.paused); const { socket, llhttp } = this; if (data.length > currentBufferSize) { if (currentBufferPtr) { @@ -54737,8 +54242,8 @@ var require_client = __commonJS({ } } destroy() { - assert2(this.ptr != null); - assert2(currentParser == null); + assert(this.ptr != null); + assert(currentParser == null); this.llhttp.llhttp_free(this.ptr); this.ptr = null; timers.clearTimeout(this.timeout); @@ -54795,17 +54300,17 @@ var require_client = __commonJS({ } onUpgrade(head) { const { upgrade, client, socket, headers, statusCode } = this; - assert2(upgrade); + assert(upgrade); const request3 = client[kQueue][client[kRunningIdx]]; - assert2(request3); - assert2(!socket.destroyed); - assert2(socket === client[kSocket]); - assert2(!this.paused); - assert2(request3.upgrade || request3.method === "CONNECT"); + assert(request3); + assert(!socket.destroyed); + assert(socket === client[kSocket]); + assert(!this.paused); + assert(request3.upgrade || request3.method === "CONNECT"); this.statusCode = null; this.statusText = ""; this.shouldKeepAlive = null; - assert2(this.headers.length % 2 === 0); + assert(this.headers.length % 2 === 0); this.headers = []; this.headersSize = 0; socket.unshift(head); @@ -54822,7 +54327,7 @@ var require_client = __commonJS({ } catch (err) { util4.destroy(socket, err); } - resume2(client); + resume(client); } onHeadersComplete(statusCode, upgrade, shouldKeepAlive) { const { client, socket, headers, statusText } = this; @@ -54833,8 +54338,8 @@ var require_client = __commonJS({ if (!request3) { return -1; } - assert2(!this.upgrade); - assert2(this.statusCode < 200); + assert(!this.upgrade); + assert(this.statusCode < 200); if (statusCode === 100) { util4.destroy(socket, new SocketError("bad response", util4.getSocketInfo(socket))); return -1; @@ -54843,7 +54348,7 @@ var require_client = __commonJS({ util4.destroy(socket, new SocketError("bad upgrade", util4.getSocketInfo(socket))); return -1; } - assert2.strictEqual(this.timeoutType, TIMEOUT_HEADERS); + assert.strictEqual(this.timeoutType, TIMEOUT_HEADERS); this.statusCode = statusCode; this.shouldKeepAlive = shouldKeepAlive || // Override llhttp value which does not allow keepAlive for HEAD. request3.method === "HEAD" && !socket[kReset] && this.connection.toLowerCase() === "keep-alive"; @@ -54856,16 +54361,16 @@ var require_client = __commonJS({ } } if (request3.method === "CONNECT") { - assert2(client[kRunning] === 1); + assert(client[kRunning] === 1); this.upgrade = true; return 2; } if (upgrade) { - assert2(client[kRunning] === 1); + assert(client[kRunning] === 1); this.upgrade = true; return 2; } - assert2(this.headers.length % 2 === 0); + assert(this.headers.length % 2 === 0); this.headers = []; this.headersSize = 0; if (this.shouldKeepAlive && client[kPipelining]) { @@ -54898,7 +54403,7 @@ var require_client = __commonJS({ } if (socket[kBlocking]) { socket[kBlocking] = false; - resume2(client); + resume(client); } return pause ? constants3.ERROR.PAUSED : 0; } @@ -54908,14 +54413,14 @@ var require_client = __commonJS({ return -1; } const request3 = client[kQueue][client[kRunningIdx]]; - assert2(request3); - assert2.strictEqual(this.timeoutType, TIMEOUT_BODY); + assert(request3); + assert.strictEqual(this.timeoutType, TIMEOUT_BODY); if (this.timeout) { if (this.timeout.refresh) { this.timeout.refresh(); } } - assert2(statusCode >= 200); + assert(statusCode >= 200); if (maxResponseSize > -1 && this.bytesRead + buf.length > maxResponseSize) { util4.destroy(socket, new ResponseExceededMaxSizeError()); return -1; @@ -54934,15 +54439,15 @@ var require_client = __commonJS({ return; } const request3 = client[kQueue][client[kRunningIdx]]; - assert2(request3); - assert2(statusCode >= 100); + assert(request3); + assert(statusCode >= 100); this.statusCode = null; this.statusText = ""; this.bytesRead = 0; this.contentLength = ""; this.keepAlive = ""; this.connection = ""; - assert2(this.headers.length % 2 === 0); + assert(this.headers.length % 2 === 0); this.headers = []; this.headersSize = 0; if (statusCode < 200) { @@ -54955,7 +54460,7 @@ var require_client = __commonJS({ request3.onComplete(headers); client[kQueue][client[kRunningIdx]++] = null; if (socket[kWriting]) { - assert2.strictEqual(client[kRunning], 0); + assert.strictEqual(client[kRunning], 0); util4.destroy(socket, new InformationalError("reset")); return constants3.ERROR.PAUSED; } else if (!shouldKeepAlive) { @@ -54965,9 +54470,9 @@ var require_client = __commonJS({ util4.destroy(socket, new InformationalError("reset")); return constants3.ERROR.PAUSED; } else if (client[kPipelining] === 1) { - setImmediate(resume2, client); + setImmediate(resume, client); } else { - resume2(client); + resume(client); } } }; @@ -54975,7 +54480,7 @@ var require_client = __commonJS({ const { socket, timeoutType, client } = parser; if (timeoutType === TIMEOUT_HEADERS) { if (!socket[kWriting] || socket.writableNeedDrain || client[kRunning] > 1) { - assert2(!parser.paused, "cannot be paused while waiting for headers"); + assert(!parser.paused, "cannot be paused while waiting for headers"); util4.destroy(socket, new HeadersTimeoutError()); } } else if (timeoutType === TIMEOUT_BODY) { @@ -54983,7 +54488,7 @@ var require_client = __commonJS({ util4.destroy(socket, new BodyTimeoutError()); } } else if (timeoutType === TIMEOUT_IDLE) { - assert2(client[kRunning] === 0 && client[kKeepAliveTimeoutValue]); + assert(client[kRunning] === 0 && client[kKeepAliveTimeoutValue]); util4.destroy(socket, new InformationalError("socket idle timeout")); } } @@ -54995,7 +54500,7 @@ var require_client = __commonJS({ } function onSocketError(err) { const { [kClient]: client, [kParser]: parser } = this; - assert2(err.code !== "ERR_TLS_CERT_ALTNAME_INVALID"); + assert(err.code !== "ERR_TLS_CERT_ALTNAME_INVALID"); if (client[kHTTPConnVersion] !== "h2") { if (err.code === "ECONNRESET" && parser.statusCode && !parser.shouldKeepAlive) { parser.onMessageComplete(); @@ -55007,13 +54512,13 @@ var require_client = __commonJS({ } function onError(client, err) { if (client[kRunning] === 0 && err.code !== "UND_ERR_INFO" && err.code !== "UND_ERR_SOCKET") { - assert2(client[kPendingIdx] === client[kRunningIdx]); + assert(client[kPendingIdx] === client[kRunningIdx]); const requests = client[kQueue].splice(client[kRunningIdx]); for (let i3 = 0; i3 < requests.length; i3++) { const request3 = requests[i3]; errorRequest(client, request3, err); } - assert2(client[kSize] === 0); + assert(client[kSize] === 0); } } function onSocketEnd() { @@ -55038,7 +54543,7 @@ var require_client = __commonJS({ const err = this[kError] || new SocketError("closed", util4.getSocketInfo(this)); client[kSocket] = null; if (client.destroyed) { - assert2(client[kPending] === 0); + assert(client[kPending] === 0); const requests = client[kQueue].splice(client[kRunningIdx]); for (let i3 = 0; i3 < requests.length; i3++) { const request3 = requests[i3]; @@ -55050,19 +54555,19 @@ var require_client = __commonJS({ errorRequest(client, request3, err); } client[kPendingIdx] = client[kRunningIdx]; - assert2(client[kRunning] === 0); + assert(client[kRunning] === 0); client.emit("disconnect", client[kUrl], [client], err); - resume2(client); + resume(client); } - async function connect3(client) { - assert2(!client[kConnecting]); - assert2(!client[kSocket]); + async function connect(client) { + assert(!client[kConnecting]); + assert(!client[kSocket]); let { host, hostname, protocol, port } = client[kUrl]; if (hostname[0] === "[") { const idx = hostname.indexOf("]"); - assert2(idx !== -1); + assert(idx !== -1); const ip = hostname.substring(1, idx); - assert2(net3.isIP(ip)); + assert(net.isIP(ip)); hostname = ip; } client[kConnecting] = true; @@ -55102,7 +54607,7 @@ var require_client = __commonJS({ return; } client[kConnecting] = false; - assert2(socket); + assert(socket); const isH2 = socket.alpnProtocol === "h2"; if (isH2) { if (!h2ExperimentalWarned) { @@ -55178,7 +54683,7 @@ var require_client = __commonJS({ }); } if (err.code === "ERR_TLS_CERT_ALTNAME_INVALID") { - assert2(client[kRunning] === 0); + assert(client[kRunning] === 0); while (client[kPending] > 0 && client[kQueue][client[kPendingIdx]].servername === client[kServerName]) { const request3 = client[kQueue][client[kPendingIdx]++]; errorRequest(client, request3, err); @@ -55188,13 +54693,13 @@ var require_client = __commonJS({ } client.emit("connectionError", client[kUrl], [client], err); } - resume2(client); + resume(client); } function emitDrain(client) { client[kNeedDrain] = 0; client.emit("drain", client[kUrl], [client]); } - function resume2(client, sync) { + function resume(client, sync) { if (client[kResuming] === 2) { return; } @@ -55210,7 +54715,7 @@ var require_client = __commonJS({ function _resume(client, sync) { while (true) { if (client.destroyed) { - assert2(client[kPending] === 0); + assert(client[kPending] === 0); return; } if (client[kClosedResolve] && !client[kSize]) { @@ -55273,7 +54778,7 @@ var require_client = __commonJS({ return; } if (!socket && !client[kHTTP2Session]) { - connect3(client); + connect(client); return; } if (socket.destroyed || socket[kWriting] || socket[kReset] || socket[kBlocking]) { @@ -55382,13 +54887,13 @@ upgrade: ${upgrade}\r \r `, "latin1"); } else { - assert2(contentLength === null, "no body must not have content length"); + assert(contentLength === null, "no body must not have content length"); socket.write(`${header}\r `, "latin1"); } request3.onRequestSent(); } else if (util4.isBuffer(body)) { - assert2(contentLength === body.byteLength, "buffer body must have content length"); + assert(contentLength === body.byteLength, "buffer body must have content length"); socket.cork(); socket.write(`${header}content-length: ${contentLength}\r \r @@ -55411,7 +54916,7 @@ upgrade: ${upgrade}\r } else if (util4.isIterable(body)) { writeIterable({ body, client, request: request3, socket, contentLength, header, expectsPayload }); } else { - assert2(false); + assert(false); } return true; } @@ -55480,7 +54985,7 @@ upgrade: ${upgrade}\r process.emitWarning(new RequestContentLengthMismatchError()); } if (contentLength != null) { - assert2(body, "no body must not have content length"); + assert(body, "no body must not have content length"); headers[HTTP2_HEADER_CONTENT_LENGTH] = `${contentLength}`; } session.ref(); @@ -55536,7 +55041,7 @@ upgrade: ${upgrade}\r if (!body) { request3.onRequestSent(); } else if (util4.isBuffer(body)) { - assert2(contentLength === body.byteLength, "buffer body must have content length"); + assert(contentLength === body.byteLength, "buffer body must have content length"); stream4.cork(); stream4.write(body); stream4.uncork(); @@ -55590,12 +55095,12 @@ upgrade: ${upgrade}\r socket: client[kSocket] }); } else { - assert2(false); + assert(false); } } } function writeStream({ h2stream, body, client, request: request3, socket, contentLength, header, expectsPayload }) { - assert2(contentLength !== 0 || client[kRunning] === 0, "stream body cannot be pipelined"); + assert(contentLength !== 0 || client[kRunning] === 0, "stream body cannot be pipelined"); if (client[kHTTPConnVersion] === "h2") { let onPipeData = function(chunk) { request3.onBodySent(chunk); @@ -55653,7 +55158,7 @@ upgrade: ${upgrade}\r return; } finished = true; - assert2(socket.destroyed || socket[kWriting] && client[kRunning] <= 1); + assert(socket.destroyed || socket[kWriting] && client[kRunning] <= 1); socket.off("drain", onDrain).off("error", onFinished); body.removeListener("data", onData).removeListener("end", onFinished).removeListener("error", onFinished).removeListener("close", onAbort); if (!err) { @@ -55677,7 +55182,7 @@ upgrade: ${upgrade}\r socket.on("drain", onDrain).on("error", onFinished); } async function writeBlob({ h2stream, body, client, request: request3, socket, contentLength, header, expectsPayload }) { - assert2(contentLength === body.size, "blob body must have content length"); + assert(contentLength === body.size, "blob body must have content length"); const isH2 = client[kHTTPConnVersion] === "h2"; try { if (contentLength != null && contentLength !== body.size) { @@ -55701,13 +55206,13 @@ upgrade: ${upgrade}\r if (!expectsPayload) { socket[kReset] = true; } - resume2(client); + resume(client); } catch (err) { util4.destroy(isH2 ? h2stream : socket, err); } } async function writeIterable({ h2stream, body, client, request: request3, socket, contentLength, header, expectsPayload }) { - assert2(contentLength !== 0 || client[kRunning] === 0, "iterator body cannot be pipelined"); + assert(contentLength !== 0 || client[kRunning] === 0, "iterator body cannot be pipelined"); let callback = null; function onDrain() { if (callback) { @@ -55717,7 +55222,7 @@ upgrade: ${upgrade}\r } } const waitForDrain = () => new Promise((resolve, reject) => { - assert2(callback === null); + assert(callback === null); if (socket[kError]) { reject(socket[kError]); } else { @@ -55859,13 +55364,13 @@ ${len.toString(16)}\r socket[kParser].timeout.refresh(); } } - resume2(client); + resume(client); } destroy(err) { const { socket, client } = this; socket[kWriting] = false; if (err) { - assert2(client[kRunning] <= 1, "pipeline should only contain this request"); + assert(client[kRunning] <= 1, "pipeline should only contain this request"); util4.destroy(socket, err); } } @@ -55873,7 +55378,7 @@ ${len.toString(16)}\r function errorRequest(client, request3, err) { try { request3.onError(err); - assert2(request3.aborted); + assert(request3.aborted); } catch (err2) { client.emit("error", err2); } @@ -56154,9 +55659,9 @@ var require_pool = __commonJS({ constructor(origin2, { connections, factory = defaultFactory, - connect: connect3, + connect, connectTimeout, - tls: tls2, + tls, maxCachedSessions, socketPath, autoSelectFamily, @@ -56171,24 +55676,24 @@ var require_pool = __commonJS({ if (typeof factory !== "function") { throw new InvalidArgumentError("factory must be a function."); } - if (connect3 != null && typeof connect3 !== "function" && typeof connect3 !== "object") { + if (connect != null && typeof connect !== "function" && typeof connect !== "object") { throw new InvalidArgumentError("connect must be a function or an object"); } - if (typeof connect3 !== "function") { - connect3 = buildConnector({ - ...tls2, + if (typeof connect !== "function") { + connect = buildConnector({ + ...tls, maxCachedSessions, allowH2, socketPath, timeout: connectTimeout, ...util4.nodeHasAutoSelectFamily && autoSelectFamily ? { autoSelectFamily, autoSelectFamilyAttemptTimeout } : void 0, - ...connect3 + ...connect }); } this[kInterceptors] = options.interceptors && options.interceptors.Pool && Array.isArray(options.interceptors.Pool) ? options.interceptors.Pool : []; this[kConnections] = connections || null; this[kUrl] = util4.parseOrigin(origin2); - this[kOptions] = { ...util4.deepClone(options), connect: connect3, allowH2 }; + this[kOptions] = { ...util4.deepClone(options), connect, allowH2 }; this[kOptions].interceptors = options.interceptors ? { ...options.interceptors } : void 0; this[kFactory] = factory; this.on("connectionError", (origin3, targets, error) => { @@ -56416,23 +55921,23 @@ var require_agent2 = __commonJS({ function defaultFactory(origin2, opts) { return opts && opts.connections === 1 ? new Client(origin2, opts) : new Pool(origin2, opts); } - var Agent5 = class extends DispatcherBase { - constructor({ factory = defaultFactory, maxRedirections = 0, connect: connect3, ...options } = {}) { + var Agent4 = class extends DispatcherBase { + constructor({ factory = defaultFactory, maxRedirections = 0, connect, ...options } = {}) { super(); if (typeof factory !== "function") { throw new InvalidArgumentError("factory must be a function."); } - if (connect3 != null && typeof connect3 !== "function" && typeof connect3 !== "object") { + if (connect != null && typeof connect !== "function" && typeof connect !== "object") { throw new InvalidArgumentError("connect must be a function or an object"); } if (!Number.isInteger(maxRedirections) || maxRedirections < 0) { throw new InvalidArgumentError("maxRedirections must be a positive number"); } - if (connect3 && typeof connect3 !== "function") { - connect3 = { ...connect3 }; + if (connect && typeof connect !== "function") { + connect = { ...connect }; } this[kInterceptors] = options.interceptors && options.interceptors.Agent && Array.isArray(options.interceptors.Agent) ? options.interceptors.Agent : [createRedirectInterceptor({ maxRedirections })]; - this[kOptions] = { ...util4.deepClone(options), connect: connect3 }; + this[kOptions] = { ...util4.deepClone(options), connect }; this[kOptions].interceptors = options.interceptors ? { ...options.interceptors } : void 0; this[kMaxRedirections] = maxRedirections; this[kFactory] = factory; @@ -56507,7 +56012,7 @@ var require_agent2 = __commonJS({ await Promise.all(destroyPromises); } }; - module2.exports = Agent5; + module2.exports = Agent4; } }); @@ -56515,7 +56020,7 @@ var require_agent2 = __commonJS({ var require_readable = __commonJS({ "node_modules/undici/lib/api/readable.js"(exports2, module2) { "use strict"; - var assert2 = require("assert"); + var assert = require("assert"); var { Readable: Readable5 } = require("stream"); var { RequestAbortedError, NotSupportedError, InvalidArgumentError } = require_errors3(); var util4 = require_util2(); @@ -56530,7 +56035,7 @@ var require_readable = __commonJS({ }; module2.exports = class BodyReadable extends Readable5 { constructor({ - resume: resume2, + resume, abort, contentType = "", highWaterMark = 64 * 1024 @@ -56538,7 +56043,7 @@ var require_readable = __commonJS({ }) { super({ autoDestroy: true, - read: resume2, + read: resume, highWaterMark }); this._readableState.dataEmitted = false; @@ -56624,7 +56129,7 @@ var require_readable = __commonJS({ this[kBody] = ReadableStreamFrom(this); if (this[kConsume]) { this[kBody].getReader(); - assert2(this[kBody].locked); + assert(this[kBody].locked); } } return this[kBody]; @@ -56675,7 +56180,7 @@ var require_readable = __commonJS({ if (isUnusable(stream4)) { throw new TypeError("unusable"); } - assert2(!stream4[kConsume]); + assert(!stream4[kConsume]); return new Promise((resolve, reject) => { stream4[kConsume] = { type: type2, @@ -56766,13 +56271,13 @@ var require_readable = __commonJS({ // node_modules/undici/lib/api/util.js var require_util4 = __commonJS({ "node_modules/undici/lib/api/util.js"(exports2, module2) { - var assert2 = require("assert"); + var assert = require("assert"); var { ResponseStatusCodeError } = require_errors3(); var { toUSVString } = require_util2(); async function getResolveErrorBodyCallback({ callback, body, contentType, statusCode, statusMessage, headers }) { - assert2(body); + assert(body); let chunks = []; let limit = 0; for await (const chunk of body) { @@ -56922,7 +56427,7 @@ var require_api_request = __commonJS({ this.abort = abort; this.context = context; } - onHeaders(statusCode, rawHeaders, resume2, statusMessage) { + onHeaders(statusCode, rawHeaders, resume, statusMessage) { const { callback, opaque, abort, context, responseHeaders, highWaterMark } = this; const headers = responseHeaders === "raw" ? util4.parseRawHeaders(rawHeaders) : util4.parseHeaders(rawHeaders); if (statusCode < 200) { @@ -56933,7 +56438,7 @@ var require_api_request = __commonJS({ } const parsedHeaders = responseHeaders === "raw" ? util4.parseHeaders(rawHeaders) : headers; const contentType = parsedHeaders["content-type"]; - const body = new Readable5({ resume: resume2, abort, contentType, highWaterMark }); + const body = new Readable5({ resume, abort, contentType, highWaterMark }); this.callback = null; this.res = body; if (callback !== null) { @@ -57077,7 +56582,7 @@ var require_api_stream = __commonJS({ this.abort = abort; this.context = context; } - onHeaders(statusCode, rawHeaders, resume2, statusMessage) { + onHeaders(statusCode, rawHeaders, resume, statusMessage) { const { factory, opaque, context, callback, responseHeaders } = this; const headers = responseHeaders === "raw" ? util4.parseRawHeaders(rawHeaders) : util4.parseHeaders(rawHeaders); if (statusCode < 200) { @@ -57124,7 +56629,7 @@ var require_api_stream = __commonJS({ } }); } - res.on("drain", resume2); + res.on("drain", resume); this.res = res; const needDrain = res.writableNeedDrain !== void 0 ? res.writableNeedDrain : res._writableState && res._writableState.needDrain; return needDrain !== true; @@ -57200,7 +56705,7 @@ var require_api_pipeline = __commonJS({ var util4 = require_util2(); var { AsyncResource } = require("async_hooks"); var { addSignal, removeSignal } = require_abort_signal(); - var assert2 = require("assert"); + var assert = require("assert"); var kResume = Symbol("resume"); var PipelineRequest = class extends Readable5 { constructor() { @@ -57208,10 +56713,10 @@ var require_api_pipeline = __commonJS({ this[kResume] = null; } _read() { - const { [kResume]: resume2 } = this; - if (resume2) { + const { [kResume]: resume } = this; + if (resume) { this[kResume] = null; - resume2(); + resume(); } } _destroy(err, callback) { @@ -57220,9 +56725,9 @@ var require_api_pipeline = __commonJS({ } }; var PipelineResponse = class extends Readable5 { - constructor(resume2) { + constructor(resume) { super({ autoDestroy: true }); - this[kResume] = resume2; + this[kResume] = resume; } _read() { this[kResume](); @@ -57300,14 +56805,14 @@ var require_api_pipeline = __commonJS({ } onConnect(abort, context) { const { ret, res } = this; - assert2(!res, "pipeline cannot be retried"); + assert(!res, "pipeline cannot be retried"); if (ret.destroyed) { throw new RequestAbortedError(); } this.abort = abort; this.context = context; } - onHeaders(statusCode, rawHeaders, resume2) { + onHeaders(statusCode, rawHeaders, resume) { const { opaque, handler, context } = this; if (statusCode < 200) { if (this.onInfo) { @@ -57316,7 +56821,7 @@ var require_api_pipeline = __commonJS({ } return; } - this.res = new PipelineResponse(resume2); + this.res = new PipelineResponse(resume); let body; try { this.handler = null; @@ -57389,7 +56894,7 @@ var require_api_upgrade = __commonJS({ var { AsyncResource } = require("async_hooks"); var util4 = require_util2(); var { addSignal, removeSignal } = require_abort_signal(); - var assert2 = require("assert"); + var assert = require("assert"); var UpgradeHandler = class extends AsyncResource { constructor(opts, callback) { if (!opts || typeof opts !== "object") { @@ -57422,7 +56927,7 @@ var require_api_upgrade = __commonJS({ } onUpgrade(statusCode, rawHeaders, socket) { const { callback, opaque, context } = this; - assert2.strictEqual(statusCode, 101); + assert.strictEqual(statusCode, 101); removeSignal(this); this.callback = null; const headers = this.responseHeaders === "raw" ? util4.parseRawHeaders(rawHeaders) : util4.parseHeaders(rawHeaders); @@ -57535,10 +57040,10 @@ var require_api_connect = __commonJS({ } } }; - function connect3(opts, callback) { + function connect(opts, callback) { if (callback === void 0) { return new Promise((resolve, reject) => { - connect3.call(this, opts, (err, data) => { + connect.call(this, opts, (err, data) => { return err ? reject(err) : resolve(data); }); }); @@ -57554,7 +57059,7 @@ var require_api_connect = __commonJS({ queueMicrotask(() => callback(err, { opaque })); } } - module2.exports = connect3; + module2.exports = connect; } }); @@ -57827,12 +57332,12 @@ var require_mock_utils = __commonJS({ const responseHeaders = generateKeyValues(headers); const responseTrailers = generateKeyValues(trailers); handler.abort = nop; - handler.onHeaders(statusCode, responseHeaders, resume2, getStatusText(statusCode)); + handler.onHeaders(statusCode, responseHeaders, resume, getStatusText(statusCode)); handler.onData(Buffer.from(responseData)); handler.onComplete(responseTrailers); deleteMockDispatch(mockDispatches, key); } - function resume2() { + function resume() { } return true; } @@ -58240,7 +57745,7 @@ var require_mock_agent = __commonJS({ "node_modules/undici/lib/mock/mock-agent.js"(exports2, module2) { "use strict"; var { kClients } = require_symbols(); - var Agent5 = require_agent2(); + var Agent4 = require_agent2(); var { kAgent, kMockAgentSet, @@ -58275,7 +57780,7 @@ var require_mock_agent = __commonJS({ if (opts && opts.agent && typeof opts.agent.dispatch !== "function") { throw new InvalidArgumentError("Argument opts.agent must implement Agent"); } - const agent = opts && opts.agent ? opts.agent : new Agent5(opts); + const agent = opts && opts.agent ? opts.agent : new Agent4(opts); this[kAgent] = agent; this[kClients] = agent[kClients]; this[kOptions] = buildMockOptions(opts); @@ -58379,8 +57884,8 @@ var require_proxy_agent = __commonJS({ "node_modules/undici/lib/proxy-agent.js"(exports2, module2) { "use strict"; var { kProxy, kClose, kDestroy, kInterceptors } = require_symbols(); - var { URL: URL3 } = require("url"); - var Agent5 = require_agent2(); + var { URL: URL2 } = require("url"); + var Agent4 = require_agent2(); var Pool = require_pool(); var DispatcherBase = require_dispatcher_base(); var { InvalidArgumentError, RequestAbortedError } = require_errors3(); @@ -58413,7 +57918,7 @@ var require_proxy_agent = __commonJS({ constructor(opts) { super(opts); this[kProxy] = buildProxyOptions(opts); - this[kAgent] = new Agent5(opts); + this[kAgent] = new Agent4(opts); this[kInterceptors] = opts.interceptors && opts.interceptors.ProxyAgent && Array.isArray(opts.interceptors.ProxyAgent) ? opts.interceptors.ProxyAgent : []; if (typeof opts === "string") { opts = { uri: opts }; @@ -58428,7 +57933,7 @@ var require_proxy_agent = __commonJS({ this[kRequestTls] = opts.requestTls; this[kProxyTls] = opts.proxyTls; this[kProxyHeaders] = opts.headers || {}; - const resolvedUrl = new URL3(opts.uri); + const resolvedUrl = new URL2(opts.uri); const { origin: origin2, port, host, username, password } = resolvedUrl; if (opts.auth && opts.token) { throw new InvalidArgumentError("opts.auth cannot be used in combination with opts.token"); @@ -58439,10 +57944,10 @@ var require_proxy_agent = __commonJS({ } else if (username && password) { this[kProxyHeaders]["proxy-authorization"] = `Basic ${Buffer.from(`${decodeURIComponent(username)}:${decodeURIComponent(password)}`).toString("base64")}`; } - const connect3 = buildConnector({ ...opts.proxyTls }); + const connect = buildConnector({ ...opts.proxyTls }); this[kConnectEndpoint] = buildConnector({ ...opts.requestTls }); - this[kClient] = clientFactory(resolvedUrl, { connect: connect3 }); - this[kAgent] = new Agent5({ + this[kClient] = clientFactory(resolvedUrl, { connect }); + this[kAgent] = new Agent4({ ...opts, connect: async (opts2, callback) => { let requestedHost = opts2.host; @@ -58483,7 +57988,7 @@ var require_proxy_agent = __commonJS({ }); } dispatch(opts, handler) { - const { host } = new URL3(opts.origin); + const { host } = new URL2(opts.origin); const headers = buildHeaders(opts.headers); throwIfProxyAuthIsSent(headers); return this[kAgent].dispatch( @@ -58529,7 +58034,7 @@ var require_proxy_agent = __commonJS({ // node_modules/undici/lib/handler/RetryHandler.js var require_RetryHandler = __commonJS({ "node_modules/undici/lib/handler/RetryHandler.js"(exports2, module2) { - var assert2 = require("assert"); + var assert = require("assert"); var { kRetryHandlerDefaultRetry } = require_symbols(); var { RequestRetryError } = require_errors3(); var { isDisturbed, parseHeaders, parseRangeHeader } = require_util2(); @@ -58657,7 +58162,7 @@ var require_RetryHandler = __commonJS({ state2.currentTimeout = retryTimeout; setTimeout(() => cb(null), retryTimeout); } - onHeaders(statusCode, rawHeaders, resume2, statusMessage) { + onHeaders(statusCode, rawHeaders, resume, statusMessage) { const headers = parseHeaders(rawHeaders); this.retryCount += 1; if (statusCode >= 300) { @@ -58694,9 +58199,9 @@ var require_RetryHandler = __commonJS({ return false; } const { start, size, end = size } = contentRange; - assert2(this.start === start, "content-range mismatch"); - assert2(this.end == null || this.end === end, "content-range mismatch"); - this.resume = resume2; + assert(this.start === start, "content-range mismatch"); + assert(this.end == null || this.end === end, "content-range mismatch"); + this.resume = resume; return true; } if (this.end == null) { @@ -58706,17 +58211,17 @@ var require_RetryHandler = __commonJS({ return this.handler.onHeaders( statusCode, rawHeaders, - resume2, + resume, statusMessage ); } const { start, size, end = size } = range; - assert2( + assert( start != null && Number.isFinite(start) && this.start !== start, "content-range mismatch" ); - assert2(Number.isFinite(start)); - assert2( + assert(Number.isFinite(start)); + assert( end != null && Number.isFinite(end) && this.end !== end, "invalid content-length" ); @@ -58727,17 +58232,17 @@ var require_RetryHandler = __commonJS({ const contentLength = headers["content-length"]; this.end = contentLength != null ? Number(contentLength) : null; } - assert2(Number.isFinite(this.start)); - assert2( + assert(Number.isFinite(this.start)); + assert( this.end == null || Number.isFinite(this.end), "invalid content-length" ); - this.resume = resume2; + this.resume = resume; this.etag = headers.etag != null ? headers.etag : null; return this.handler.onHeaders( statusCode, rawHeaders, - resume2, + resume, statusMessage ); } @@ -58799,9 +58304,9 @@ var require_global2 = __commonJS({ "use strict"; var globalDispatcher = Symbol.for("undici.globalDispatcher.1"); var { InvalidArgumentError } = require_errors3(); - var Agent5 = require_agent2(); + var Agent4 = require_agent2(); if (getGlobalDispatcher() === void 0) { - setGlobalDispatcher2(new Agent5()); + setGlobalDispatcher2(new Agent4()); } function setGlobalDispatcher2(agent) { if (!agent || typeof agent.dispatch !== "function") { @@ -58871,7 +58376,7 @@ var require_headers = __commonJS({ } = require_util3(); var util4 = require("util"); var { webidl } = require_webidl(); - var assert2 = require("assert"); + var assert = require("assert"); var kHeadersMap = Symbol("headers map"); var kHeadersSortedMap = Symbol("headers map sorted"); function isHTTPWhiteSpaceCharCode(code) { @@ -59129,7 +58634,7 @@ var require_headers = __commonJS({ headers.push([name, cookies[j4]]); } } else { - assert2(value !== null); + assert(value !== null); headers.push([name, value]); } } @@ -59275,7 +58780,7 @@ var require_response = __commonJS({ var { getGlobalOrigin } = require_global(); var { URLSerializer } = require_dataURL(); var { kHeadersList, kConstruct } = require_symbols(); - var assert2 = require("assert"); + var assert = require("assert"); var { types } = require("util"); var ReadableStream7 = globalThis.ReadableStream || require("stream/web").ReadableStream; var textEncoder2 = new TextEncoder("utf-8"); @@ -59491,7 +58996,7 @@ var require_response = __commonJS({ return p4 in state2 ? state2[p4] : target[p4]; }, set(target, p4, value) { - assert2(!(p4 in state2)); + assert(!(p4 in state2)); target[p4] = value; return true; } @@ -59525,11 +59030,11 @@ var require_response = __commonJS({ body: null }); } else { - assert2(false); + assert(false); } } function makeAppropriateNetworkError(fetchParams, err = null) { - assert2(isCancelled(fetchParams)); + assert(isCancelled(fetchParams)); return isAborted(fetchParams) ? makeNetworkError(Object.assign(new DOMException3("The operation was aborted.", "AbortError"), { cause: err })) : makeNetworkError(Object.assign(new DOMException3("Request was cancelled."), { cause: err })); } function initializeResponse(response, init, body) { @@ -59657,7 +59162,7 @@ var require_request2 = __commonJS({ var { getGlobalOrigin } = require_global(); var { URLSerializer } = require_dataURL(); var { kHeadersList, kConstruct } = require_symbols(); - var assert2 = require("assert"); + var assert = require("assert"); var { getMaxListeners, setMaxListeners, getEventListeners, defaultMaxListeners } = require("events"); var TransformStream3 = globalThis.TransformStream; var kAbortController = Symbol("abortController"); @@ -59701,7 +59206,7 @@ var require_request2 = __commonJS({ request3 = makeRequest2({ urlList: [parsedURL] }); fallbackMode = "cors"; } else { - assert2(input instanceof _Request); + assert(input instanceof _Request); request3 = input[kState]; signal = input[kSignal]; } @@ -60310,7 +59815,7 @@ var require_fetch = __commonJS({ urlHasHttpsScheme } = require_util3(); var { kState, kHeaders, kGuard, kRealm } = require_symbols2(); - var assert2 = require("assert"); + var assert = require("assert"); var { safelyExtractBody } = require_body(); var { redirectStatusSet, @@ -60390,7 +59895,7 @@ var require_fetch = __commonJS({ requestObject.signal, () => { locallyAborted = true; - assert2(controller != null); + assert(controller != null); controller.abort(requestObject.signal.reason); abortFetch(p4, request3, responseObject, requestObject.signal.reason); } @@ -60523,7 +60028,7 @@ var require_fetch = __commonJS({ taskDestination, crossOriginIsolatedCapability }; - assert2(!request3.body || request3.body.stream); + assert(!request3.body || request3.body.stream); if (request3.window === "client") { request3.window = request3.client?.globalObject?.constructor?.name === "Window" ? request3.client : "no-window"; } @@ -60616,7 +60121,7 @@ var require_fetch = __commonJS({ } else if (request3.responseTainting === "opaque") { response = filterResponse(response, "opaque"); } else { - assert2(false); + assert(false); } } let internalResponse = response.status === 0 ? response : response.internalResponse; @@ -60808,7 +60313,7 @@ var require_fetch = __commonJS({ } else if (request3.redirect === "follow") { response = await httpRedirectFetch(fetchParams, response); } else { - assert2(false); + assert(false); } } response.timingInfo = timingInfo; @@ -60861,7 +60366,7 @@ var require_fetch = __commonJS({ request3.headersList.delete("host"); } if (request3.body != null) { - assert2(request3.body.source != null); + assert(request3.body.source != null); request3.body = safelyExtractBody(request3.body.source)[0]; } const timingInfo = fetchParams.timingInfo; @@ -60994,7 +60499,7 @@ var require_fetch = __commonJS({ return response; } async function httpNetworkFetch(fetchParams, includeCredentials = false, forceNewConnection = false) { - assert2(!fetchParams.controller.connection || fetchParams.controller.connection.destroyed); + assert(!fetchParams.controller.connection || fetchParams.controller.connection.destroyed); fetchParams.controller.connection = { abort: null, destroyed: false, @@ -61183,7 +60688,7 @@ var require_fetch = __commonJS({ this.abort = connection.abort = abort; } }, - onHeaders(status, headersList, resume2, statusText) { + onHeaders(status, headersList, resume, statusText) { if (status < 200) { return; } @@ -61213,7 +60718,7 @@ var require_fetch = __commonJS({ headers[kHeadersList].append(key, val); } } - this.body = new Readable5({ read: resume2 }); + this.body = new Readable5({ read: resume }); const decoders = []; const willFollow = request3.redirect === "follow" && location && redirectStatusSet.has(status); if (request3.method !== "HEAD" && request3.method !== "CONNECT" && !nullBodyStatus.includes(status) && !willFollow) { @@ -62129,7 +61634,7 @@ var require_symbols4 = __commonJS({ var require_util6 = __commonJS({ "node_modules/undici/lib/cache/util.js"(exports2, module2) { "use strict"; - var assert2 = require("assert"); + var assert = require("assert"); var { URLSerializer } = require_dataURL(); var { isValidHeaderName: isValidHeaderName2 } = require_util3(); function urlEquals(A5, B3, excludeFragment = false) { @@ -62138,7 +61643,7 @@ var require_util6 = __commonJS({ return serializedA === serializedB; } function fieldValues(header) { - assert2(header !== null); + assert(header !== null); const values = []; for (let value of header.split(",")) { value = value.trim(); @@ -62172,7 +61677,7 @@ var require_cache = __commonJS({ var { kState, kHeaders, kGuard, kRealm } = require_symbols2(); var { fetching } = require_fetch(); var { urlIsHttpHttpsScheme, createDeferredPromise, readAllBytes } = require_util3(); - var assert2 = require("assert"); + var assert = require("assert"); var { getGlobalDispatcher } = require_global2(); var Cache = class _Cache { /** @@ -62433,7 +61938,7 @@ var require_cache = __commonJS({ return false; } } else { - assert2(typeof request3 === "string"); + assert(typeof request3 === "string"); r3 = new Request6(request3)[kState]; } const operations = []; @@ -62542,7 +62047,7 @@ var require_cache = __commonJS({ } for (const requestResponse of requestResponses) { const idx = cache.indexOf(requestResponse); - assert2(idx !== -1); + assert(idx !== -1); cache.splice(idx, 1); } } else if (operation.type === "put") { @@ -62574,7 +62079,7 @@ var require_cache = __commonJS({ requestResponses = this.#queryCache(operation.request); for (const requestResponse of requestResponses) { const idx = cache.indexOf(requestResponse); - assert2(idx !== -1); + assert(idx !== -1); cache.splice(idx, 1); } cache.push([operation.request, operation.response]); @@ -62955,13 +62460,13 @@ var require_util7 = __commonJS({ }); // node_modules/undici/lib/cookies/parse.js -var require_parse4 = __commonJS({ +var require_parse3 = __commonJS({ "node_modules/undici/lib/cookies/parse.js"(exports2, module2) { "use strict"; var { maxNameValuePairSize, maxAttributeValueSize } = require_constants5(); var { isCTLExcludingHtab } = require_util7(); var { collectASequenceOfCodePointsFast } = require_dataURL(); - var assert2 = require("assert"); + var assert = require("assert"); function parseSetCookie(header) { if (isCTLExcludingHtab(header)) { return null; @@ -63003,7 +62508,7 @@ var require_parse4 = __commonJS({ if (unparsedAttributes.length === 0) { return cookieAttributeList; } - assert2(unparsedAttributes[0] === ";"); + assert(unparsedAttributes[0] === ";"); unparsedAttributes = unparsedAttributes.slice(1); let cookieAv = ""; if (unparsedAttributes.includes(";")) { @@ -63098,7 +62603,7 @@ var require_parse4 = __commonJS({ var require_cookies = __commonJS({ "node_modules/undici/lib/cookies/index.js"(exports2, module2) { "use strict"; - var { parseSetCookie } = require_parse4(); + var { parseSetCookie } = require_parse3(); var { stringify: stringify2 } = require_util7(); var { webidl } = require_webidl(); var { Headers: Headers6 } = require_headers(); @@ -64471,7 +63976,7 @@ var require_undici = __commonJS({ var errors = require_errors3(); var Pool = require_pool(); var BalancedPool = require_balanced_pool(); - var Agent5 = require_agent2(); + var Agent4 = require_agent2(); var util4 = require_util2(); var { InvalidArgumentError } = errors; var api = require_api(); @@ -64498,7 +64003,7 @@ var require_undici = __commonJS({ module2.exports.Client = Client; module2.exports.Pool = Pool; module2.exports.BalancedPool = BalancedPool; - module2.exports.Agent = Agent5; + module2.exports.Agent = Agent4; module2.exports.ProxyAgent = ProxyAgent2; module2.exports.RetryHandler = RetryHandler; module2.exports.DecoratorHandler = DecoratorHandler; @@ -64601,6 +64106,494 @@ var require_undici = __commonJS({ } }); +// node_modules/@commitlint/types/lib/ensure.js +var require_ensure = __commonJS({ + "node_modules/@commitlint/types/lib/ensure.js"(exports2) { + "use strict"; + Object.defineProperty(exports2, "__esModule", { value: true }); + } +}); + +// node_modules/@commitlint/types/lib/format.js +var require_format = __commonJS({ + "node_modules/@commitlint/types/lib/format.js"(exports2) { + "use strict"; + Object.defineProperty(exports2, "__esModule", { value: true }); + } +}); + +// node_modules/@commitlint/types/lib/is-ignored.js +var require_is_ignored = __commonJS({ + "node_modules/@commitlint/types/lib/is-ignored.js"(exports2) { + "use strict"; + Object.defineProperty(exports2, "__esModule", { value: true }); + } +}); + +// node_modules/@commitlint/types/lib/lint.js +var require_lint = __commonJS({ + "node_modules/@commitlint/types/lib/lint.js"(exports2) { + "use strict"; + Object.defineProperty(exports2, "__esModule", { value: true }); + } +}); + +// node_modules/@commitlint/types/lib/load.js +var require_load = __commonJS({ + "node_modules/@commitlint/types/lib/load.js"(exports2) { + "use strict"; + Object.defineProperty(exports2, "__esModule", { value: true }); + } +}); + +// node_modules/@commitlint/types/lib/parse.js +var require_parse4 = __commonJS({ + "node_modules/@commitlint/types/lib/parse.js"(exports2) { + "use strict"; + Object.defineProperty(exports2, "__esModule", { value: true }); + } +}); + +// node_modules/@commitlint/types/lib/prompt.js +var require_prompt = __commonJS({ + "node_modules/@commitlint/types/lib/prompt.js"(exports2) { + "use strict"; + Object.defineProperty(exports2, "__esModule", { value: true }); + } +}); + +// node_modules/@commitlint/types/lib/rules.js +var require_rules = __commonJS({ + "node_modules/@commitlint/types/lib/rules.js"(exports2) { + "use strict"; + Object.defineProperty(exports2, "__esModule", { value: true }); + exports2.RuleConfigQuality = exports2.RuleConfigSeverity = void 0; + var RuleConfigSeverity2; + (function(RuleConfigSeverity3) { + RuleConfigSeverity3[RuleConfigSeverity3["Disabled"] = 0] = "Disabled"; + RuleConfigSeverity3[RuleConfigSeverity3["Warning"] = 1] = "Warning"; + RuleConfigSeverity3[RuleConfigSeverity3["Error"] = 2] = "Error"; + })(RuleConfigSeverity2 || (exports2.RuleConfigSeverity = RuleConfigSeverity2 = {})); + var RuleConfigQuality; + (function(RuleConfigQuality2) { + RuleConfigQuality2[RuleConfigQuality2["User"] = 0] = "User"; + RuleConfigQuality2[RuleConfigQuality2["Qualified"] = 1] = "Qualified"; + })(RuleConfigQuality || (exports2.RuleConfigQuality = RuleConfigQuality = {})); + } +}); + +// node_modules/@commitlint/types/lib/index.js +var require_lib5 = __commonJS({ + "node_modules/@commitlint/types/lib/index.js"(exports2) { + "use strict"; + var __createBinding = exports2 && exports2.__createBinding || (Object.create ? function(o3, m5, k7, k22) { + if (k22 === void 0) k22 = k7; + var desc = Object.getOwnPropertyDescriptor(m5, k7); + if (!desc || ("get" in desc ? !m5.__esModule : desc.writable || desc.configurable)) { + desc = { enumerable: true, get: function() { + return m5[k7]; + } }; + } + Object.defineProperty(o3, k22, desc); + } : function(o3, m5, k7, k22) { + if (k22 === void 0) k22 = k7; + o3[k22] = m5[k7]; + }); + var __exportStar = exports2 && exports2.__exportStar || function(m5, exports3) { + for (var p4 in m5) if (p4 !== "default" && !Object.prototype.hasOwnProperty.call(exports3, p4)) __createBinding(exports3, m5, p4); + }; + Object.defineProperty(exports2, "__esModule", { value: true }); + __exportStar(require_ensure(), exports2); + __exportStar(require_format(), exports2); + __exportStar(require_is_ignored(), exports2); + __exportStar(require_lint(), exports2); + __exportStar(require_load(), exports2); + __exportStar(require_parse4(), exports2); + __exportStar(require_prompt(), exports2); + __exportStar(require_rules(), exports2); + } +}); + +// node_modules/ignore/index.js +var require_ignore = __commonJS({ + "node_modules/ignore/index.js"(exports2, module2) { + function makeArray(subject) { + return Array.isArray(subject) ? subject : [subject]; + } + var EMPTY = ""; + var SPACE = " "; + var ESCAPE = "\\"; + var REGEX_TEST_BLANK_LINE = /^\s+$/; + var REGEX_INVALID_TRAILING_BACKSLASH = /(?:[^\\]|^)\\$/; + var REGEX_REPLACE_LEADING_EXCAPED_EXCLAMATION = /^\\!/; + var REGEX_REPLACE_LEADING_EXCAPED_HASH = /^\\#/; + var REGEX_SPLITALL_CRLF = /\r?\n/g; + var REGEX_TEST_INVALID_PATH = /^\.*\/|^\.+$/; + var SLASH = "/"; + var TMP_KEY_IGNORE = "node-ignore"; + if (typeof Symbol !== "undefined") { + TMP_KEY_IGNORE = Symbol.for("node-ignore"); + } + var KEY_IGNORE = TMP_KEY_IGNORE; + var define2 = (object, key, value) => Object.defineProperty(object, key, { value }); + var REGEX_REGEXP_RANGE = /([0-z])-([0-z])/g; + var RETURN_FALSE = () => false; + var sanitizeRange = (range) => range.replace( + REGEX_REGEXP_RANGE, + (match, from, to) => from.charCodeAt(0) <= to.charCodeAt(0) ? match : EMPTY + ); + var cleanRangeBackSlash = (slashes) => { + const { length } = slashes; + return slashes.slice(0, length - length % 2); + }; + var REPLACERS = [ + [ + // remove BOM + // TODO: + // Other similar zero-width characters? + /^\uFEFF/, + () => EMPTY + ], + // > Trailing spaces are ignored unless they are quoted with backslash ("\") + [ + // (a\ ) -> (a ) + // (a ) -> (a) + // (a \ ) -> (a ) + /\\?\s+$/, + (match) => match.indexOf("\\") === 0 ? SPACE : EMPTY + ], + // replace (\ ) with ' ' + [ + /\\\s/g, + () => SPACE + ], + // Escape metacharacters + // which is written down by users but means special for regular expressions. + // > There are 12 characters with special meanings: + // > - the backslash \, + // > - the caret ^, + // > - the dollar sign $, + // > - the period or dot ., + // > - the vertical bar or pipe symbol |, + // > - the question mark ?, + // > - the asterisk or star *, + // > - the plus sign +, + // > - the opening parenthesis (, + // > - the closing parenthesis ), + // > - and the opening square bracket [, + // > - the opening curly brace {, + // > These special characters are often called "metacharacters". + [ + /[\\$.|*+(){^]/g, + (match) => `\\${match}` + ], + [ + // > a question mark (?) matches a single character + /(?!\\)\?/g, + () => "[^/]" + ], + // leading slash + [ + // > A leading slash matches the beginning of the pathname. + // > For example, "/*.c" matches "cat-file.c" but not "mozilla-sha1/sha1.c". + // A leading slash matches the beginning of the pathname + /^\//, + () => "^" + ], + // replace special metacharacter slash after the leading slash + [ + /\//g, + () => "\\/" + ], + [ + // > A leading "**" followed by a slash means match in all directories. + // > For example, "**/foo" matches file or directory "foo" anywhere, + // > the same as pattern "foo". + // > "**/foo/bar" matches file or directory "bar" anywhere that is directly + // > under directory "foo". + // Notice that the '*'s have been replaced as '\\*' + /^\^*\\\*\\\*\\\//, + // '**/foo' <-> 'foo' + () => "^(?:.*\\/)?" + ], + // starting + [ + // there will be no leading '/' + // (which has been replaced by section "leading slash") + // If starts with '**', adding a '^' to the regular expression also works + /^(?=[^^])/, + function startingReplacer() { + return !/\/(?!$)/.test(this) ? "(?:^|\\/)" : "^"; + } + ], + // two globstars + [ + // Use lookahead assertions so that we could match more than one `'/**'` + /\\\/\\\*\\\*(?=\\\/|$)/g, + // Zero, one or several directories + // should not use '*', or it will be replaced by the next replacer + // Check if it is not the last `'/**'` + (_7, index, str2) => index + 6 < str2.length ? "(?:\\/[^\\/]+)*" : "\\/.+" + ], + // normal intermediate wildcards + [ + // Never replace escaped '*' + // ignore rule '\*' will match the path '*' + // 'abc.*/' -> go + // 'abc.*' -> skip this rule, + // coz trailing single wildcard will be handed by [trailing wildcard] + /(^|[^\\]+)(\\\*)+(?=.+)/g, + // '*.js' matches '.js' + // '*.js' doesn't match 'abc' + (_7, p1, p22) => { + const unescaped = p22.replace(/\\\*/g, "[^\\/]*"); + return p1 + unescaped; + } + ], + [ + // unescape, revert step 3 except for back slash + // For example, if a user escape a '\\*', + // after step 3, the result will be '\\\\\\*' + /\\\\\\(?=[$.|*+(){^])/g, + () => ESCAPE + ], + [ + // '\\\\' -> '\\' + /\\\\/g, + () => ESCAPE + ], + [ + // > The range notation, e.g. [a-zA-Z], + // > can be used to match one of the characters in a range. + // `\` is escaped by step 3 + /(\\)?\[([^\]/]*?)(\\*)($|\])/g, + (match, leadEscape, range, endEscape, close) => leadEscape === ESCAPE ? `\\[${range}${cleanRangeBackSlash(endEscape)}${close}` : close === "]" ? endEscape.length % 2 === 0 ? `[${sanitizeRange(range)}${endEscape}]` : "[]" : "[]" + ], + // ending + [ + // 'js' will not match 'js.' + // 'ab' will not match 'abc' + /(?:[^*])$/, + // WTF! + // https://git-scm.com/docs/gitignore + // changes in [2.22.1](https://git-scm.com/docs/gitignore/2.22.1) + // which re-fixes #24, #38 + // > If there is a separator at the end of the pattern then the pattern + // > will only match directories, otherwise the pattern can match both + // > files and directories. + // 'js*' will not match 'a.js' + // 'js/' will not match 'a.js' + // 'js' will match 'a.js' and 'a.js/' + (match) => /\/$/.test(match) ? `${match}$` : `${match}(?=$|\\/$)` + ], + // trailing wildcard + [ + /(\^|\\\/)?\\\*$/, + (_7, p1) => { + const prefix = p1 ? `${p1}[^/]+` : "[^/]*"; + return `${prefix}(?=$|\\/$)`; + } + ] + ]; + var regexCache = /* @__PURE__ */ Object.create(null); + var makeRegex = (pattern, ignoreCase) => { + let source = regexCache[pattern]; + if (!source) { + source = REPLACERS.reduce( + (prev, current) => prev.replace(current[0], current[1].bind(pattern)), + pattern + ); + regexCache[pattern] = source; + } + return ignoreCase ? new RegExp(source, "i") : new RegExp(source); + }; + var isString2 = (subject) => typeof subject === "string"; + var checkPattern = (pattern) => pattern && isString2(pattern) && !REGEX_TEST_BLANK_LINE.test(pattern) && !REGEX_INVALID_TRAILING_BACKSLASH.test(pattern) && pattern.indexOf("#") !== 0; + var splitPattern = (pattern) => pattern.split(REGEX_SPLITALL_CRLF); + var IgnoreRule = class { + constructor(origin2, pattern, negative, regex) { + this.origin = origin2; + this.pattern = pattern; + this.negative = negative; + this.regex = regex; + } + }; + var createRule = (pattern, ignoreCase) => { + const origin2 = pattern; + let negative = false; + if (pattern.indexOf("!") === 0) { + negative = true; + pattern = pattern.substr(1); + } + pattern = pattern.replace(REGEX_REPLACE_LEADING_EXCAPED_EXCLAMATION, "!").replace(REGEX_REPLACE_LEADING_EXCAPED_HASH, "#"); + const regex = makeRegex(pattern, ignoreCase); + return new IgnoreRule( + origin2, + pattern, + negative, + regex + ); + }; + var throwError = (message, Ctor) => { + throw new Ctor(message); + }; + var checkPath = (path5, originalPath, doThrow) => { + if (!isString2(path5)) { + return doThrow( + `path must be a string, but got \`${originalPath}\``, + TypeError + ); + } + if (!path5) { + return doThrow(`path must not be empty`, TypeError); + } + if (checkPath.isNotRelative(path5)) { + const r3 = "`path.relative()`d"; + return doThrow( + `path should be a ${r3} string, but got "${originalPath}"`, + RangeError + ); + } + return true; + }; + var isNotRelative = (path5) => REGEX_TEST_INVALID_PATH.test(path5); + checkPath.isNotRelative = isNotRelative; + checkPath.convert = (p4) => p4; + var Ignore2 = class { + constructor({ + ignorecase = true, + ignoreCase = ignorecase, + allowRelativePaths = false + } = {}) { + define2(this, KEY_IGNORE, true); + this._rules = []; + this._ignoreCase = ignoreCase; + this._allowRelativePaths = allowRelativePaths; + this._initCache(); + } + _initCache() { + this._ignoreCache = /* @__PURE__ */ Object.create(null); + this._testCache = /* @__PURE__ */ Object.create(null); + } + _addPattern(pattern) { + if (pattern && pattern[KEY_IGNORE]) { + this._rules = this._rules.concat(pattern._rules); + this._added = true; + return; + } + if (checkPattern(pattern)) { + const rule = createRule(pattern, this._ignoreCase); + this._added = true; + this._rules.push(rule); + } + } + // @param {Array | string | Ignore} pattern + add(pattern) { + this._added = false; + makeArray( + isString2(pattern) ? splitPattern(pattern) : pattern + ).forEach(this._addPattern, this); + if (this._added) { + this._initCache(); + } + return this; + } + // legacy + addPattern(pattern) { + return this.add(pattern); + } + // | ignored : unignored + // negative | 0:0 | 0:1 | 1:0 | 1:1 + // -------- | ------- | ------- | ------- | -------- + // 0 | TEST | TEST | SKIP | X + // 1 | TESTIF | SKIP | TEST | X + // - SKIP: always skip + // - TEST: always test + // - TESTIF: only test if checkUnignored + // - X: that never happen + // @param {boolean} whether should check if the path is unignored, + // setting `checkUnignored` to `false` could reduce additional + // path matching. + // @returns {TestResult} true if a file is ignored + _testOne(path5, checkUnignored) { + let ignored = false; + let unignored = false; + this._rules.forEach((rule) => { + const { negative } = rule; + if (unignored === negative && ignored !== unignored || negative && !ignored && !unignored && !checkUnignored) { + return; + } + const matched = rule.regex.test(path5); + if (matched) { + ignored = !negative; + unignored = negative; + } + }); + return { + ignored, + unignored + }; + } + // @returns {TestResult} + _test(originalPath, cache, checkUnignored, slices) { + const path5 = originalPath && checkPath.convert(originalPath); + checkPath( + path5, + originalPath, + this._allowRelativePaths ? RETURN_FALSE : throwError + ); + return this._t(path5, cache, checkUnignored, slices); + } + _t(path5, cache, checkUnignored, slices) { + if (path5 in cache) { + return cache[path5]; + } + if (!slices) { + slices = path5.split(SLASH); + } + slices.pop(); + if (!slices.length) { + return cache[path5] = this._testOne(path5, checkUnignored); + } + const parent = this._t( + slices.join(SLASH) + SLASH, + cache, + checkUnignored, + slices + ); + return cache[path5] = parent.ignored ? parent : this._testOne(path5, checkUnignored); + } + ignores(path5) { + return this._test(path5, this._ignoreCache, false).ignored; + } + createFilter() { + return (path5) => !this.ignores(path5); + } + filter(paths) { + return makeArray(paths).filter(this.createFilter()); + } + // @returns {TestResult} + test(path5) { + return this._test(path5, this._testCache, true); + } + }; + var factory = (options) => new Ignore2(options); + var isPathValid = (path5) => checkPath(path5 && checkPath.convert(path5), path5, RETURN_FALSE); + factory.isPathValid = isPathValid; + factory.default = factory; + module2.exports = factory; + if ( + // Detect `process` so that it can run in browsers. + typeof process !== "undefined" && (process.env && process.env.IGNORE_TEST_WIN32 || process.platform === "win32") + ) { + const makePosix = (str2) => /^\\\\\?\\/.test(str2) || /["<>|\u0000-\u001F]+/u.test(str2) ? str2 : str2.replace(/\\/g, "/"); + checkPath.convert = makePosix; + const REGIX_IS_WINDOWS_PATH_ABSOLUTE = /^[a-z]:\//i; + checkPath.isNotRelative = (path5) => REGIX_IS_WINDOWS_PATH_ABSOLUTE.test(path5) || isNotRelative(path5); + } + } +}); + // node_modules/type-flag/dist/index.mjs var V = "known-flag"; var k = "unknown-flag"; @@ -65330,8 +65323,15 @@ var package_default = { "test:docker-build": "docker build -t oco-test -f test/Dockerfile .", "test:unit": "NODE_OPTIONS=--experimental-vm-modules jest test/unit", "test:unit:docker": "npm run test:docker-build && DOCKER_CONTENT_TRUST=0 docker run --rm oco-test npm run test:unit", - "test:e2e": "npm run test:e2e:setup && jest test/e2e", - "test:e2e:setup": "sh test/e2e/setup.sh", + "test:e2e": "npm run build && npm run test:e2e:smoke:run && npm run test:e2e:core:run && npm run test:e2e:prompt-module:run", + "test:e2e:smoke": "npm run build && npm run test:e2e:smoke:run", + "test:e2e:smoke:run": "OCO_TEST_SKIP_VERSION_CHECK=true jest test/e2e/smoke.test.ts", + "test:e2e:core": "npm run build && npm run test:e2e:core:run", + "test:e2e:core:run": "OCO_TEST_SKIP_VERSION_CHECK=true jest test/e2e/cliBehavior.test.ts test/e2e/gitPush.test.ts test/e2e/oneFile.test.ts test/e2e/noChanges.test.ts", + "test:e2e:setup": "npm run test:e2e:prompt-module:setup", + "test:e2e:prompt-module:setup": "sh test/e2e/setup.sh", + "test:e2e:prompt-module": "npm run build && npm run test:e2e:prompt-module:run", + "test:e2e:prompt-module:run": "npm run test:e2e:prompt-module:setup && OCO_TEST_SKIP_VERSION_CHECK=true jest test/e2e/prompt-module", "test:e2e:docker": "npm run test:docker-build && DOCKER_CONTENT_TRUST=0 docker run --rm oco-test npm run test:e2e", "mlx:start": "OCO_AI_PROVIDER='mlx' node ./out/cli.cjs" }, @@ -67321,6 +67321,7 @@ var CONFIG_KEYS = /* @__PURE__ */ ((CONFIG_KEYS3) => { CONFIG_KEYS3["OCO_OMIT_SCOPE"] = "OCO_OMIT_SCOPE"; CONFIG_KEYS3["OCO_GITPUSH"] = "OCO_GITPUSH"; CONFIG_KEYS3["OCO_HOOK_AUTO_UNCOMMENT"] = "OCO_HOOK_AUTO_UNCOMMENT"; + CONFIG_KEYS3["OCO_OLLAMA_THINK"] = "OCO_OLLAMA_THINK"; return CONFIG_KEYS3; })(CONFIG_KEYS || {}); var MODEL_LIST = { @@ -67976,7 +67977,7 @@ var configValidators = { ["OCO_API_URL" /* OCO_API_URL */](value) { validateConfig( "OCO_API_URL" /* OCO_API_URL */, - typeof value === "string", + typeof value === "string" && /^(https?:\/\/)/.test(value), `${value} is not a valid URL. It should start with 'http://' or 'https://'.` ); return value; @@ -67984,7 +67985,7 @@ var configValidators = { ["OCO_PROXY" /* OCO_PROXY */](value) { validateConfig( "OCO_PROXY" /* OCO_PROXY */, - typeof value === "string", + value === null || typeof value === "string" && /^(https?:\/\/)/.test(value), `${value} is not a valid URL. It should start with 'http://' or 'https://'.` ); return value; @@ -68081,6 +68082,14 @@ var configValidators = { typeof value === "boolean", "Must be true or false" ); + }, + ["OCO_OLLAMA_THINK" /* OCO_OLLAMA_THINK */](value) { + validateConfig( + "OCO_OLLAMA_THINK" /* OCO_OLLAMA_THINK */, + typeof value === "boolean", + "Must be true or false" + ); + return value; } }; var OCO_AI_PROVIDER_ENUM = /* @__PURE__ */ ((OCO_AI_PROVIDER_ENUM2) => { @@ -68149,10 +68158,6 @@ var DEFAULT_CONFIG = { // todo: deprecate OCO_HOOK_AUTO_UNCOMMENT: false }; -var initGlobalConfig = (configPath = defaultConfigPath) => { - (0, import_fs.writeFileSync)(configPath, (0, import_ini.stringify)(DEFAULT_CONFIG), "utf8"); - return DEFAULT_CONFIG; -}; var parseConfigVarValue = (value) => { try { return JSON.parse(value); @@ -68165,7 +68170,7 @@ var getEnvConfig = (envPath) => { return { OCO_MODEL: process.env.OCO_MODEL, OCO_API_URL: process.env.OCO_API_URL, - OCO_PROXY: process.env.OCO_PROXY || process.env.HTTPS_PROXY || process.env.HTTP_PROXY, + OCO_PROXY: process.env.OCO_PROXY, OCO_API_KEY: process.env.OCO_API_KEY, OCO_API_CUSTOM_HEADERS: process.env.OCO_API_CUSTOM_HEADERS, OCO_AI_PROVIDER: process.env.OCO_AI_PROVIDER, @@ -68192,19 +68197,20 @@ var getIsGlobalConfigFileExist = (configPath = defaultConfigPath) => { return (0, import_fs.existsSync)(configPath); }; var getGlobalConfig = (configPath = defaultConfigPath) => { - let globalConfig; const isGlobalConfigFileExist = getIsGlobalConfigFileExist(configPath); - if (!isGlobalConfigFileExist) globalConfig = initGlobalConfig(configPath); - else { - const configFile = (0, import_fs.readFileSync)(configPath, "utf8"); - globalConfig = (0, import_ini.parse)(configFile); + if (!isGlobalConfigFileExist) { + return { ...DEFAULT_CONFIG }; } - return globalConfig; + const configFile = (0, import_fs.readFileSync)(configPath, "utf8"); + return (0, import_ini.parse)(configFile); }; var mergeConfigs = (main, fallback) => { const allKeys = /* @__PURE__ */ new Set([...Object.keys(main), ...Object.keys(fallback)]); return Array.from(allKeys).reduce((acc, key) => { - acc[key] = parseConfigVarValue(main[key] ?? fallback[key]); + const mainValue = main[key]; + acc[key] = parseConfigVarValue( + mainValue !== void 0 ? mainValue : fallback[key] + ); return acc; }, {}); }; @@ -68347,7 +68353,10 @@ function getConfigKeyDetails(key) { case "OCO_PROXY" /* OCO_PROXY */: return { description: "HTTP/HTTPS Proxy URL", - values: ["URL string (must start with 'http://' or 'https://')"] + values: [ + "URL string (must start with 'http://' or 'https://')", + "null (disable proxy even when HTTP_PROXY/HTTPS_PROXY are set)" + ] }; case "OCO_MESSAGE_TEMPLATE_PLACEHOLDER" /* OCO_MESSAGE_TEMPLATE_PLACEHOLDER */: return { @@ -70691,321 +70700,8 @@ var { AnthropicError: AnthropicError2, APIError: APIError2, APIConnectionError: })(Anthropic || (Anthropic = {})); var sdk_default = Anthropic; -// node_modules/https-proxy-agent/dist/index.js -var net2 = __toESM(require("net"), 1); -var tls = __toESM(require("tls"), 1); -var import_assert = __toESM(require("assert"), 1); -var import_debug2 = __toESM(require_src2(), 1); - -// node_modules/https-proxy-agent/node_modules/agent-base/dist/index.js -var net = __toESM(require("net"), 1); -var http = __toESM(require("http"), 1); -var import_https = require("https"); -var INTERNAL = Symbol("AgentBaseInternalState"); -var Agent2 = class extends http.Agent { - constructor(opts) { - super(opts); - this[INTERNAL] = {}; - } - /** - * Determine whether this is an `http` or `https` request. - */ - isSecureEndpoint(options) { - if (options) { - if (typeof options.secureEndpoint === "boolean") { - return options.secureEndpoint; - } - if (typeof options.protocol === "string") { - return options.protocol === "https:"; - } - } - const { stack } = new Error(); - if (typeof stack !== "string") - return false; - return stack.split("\n").some((l3) => l3.indexOf("(https.js:") !== -1 || l3.indexOf("node:https:") !== -1); - } - // In order to support async signatures in `connect()` and Node's native - // connection pooling in `http.Agent`, the array of sockets for each origin - // has to be updated synchronously. This is so the length of the array is - // accurate when `addRequest()` is next called. We achieve this by creating a - // fake socket and adding it to `sockets[origin]` and incrementing - // `totalSocketCount`. - incrementSockets(name) { - if (this.maxSockets === Infinity && this.maxTotalSockets === Infinity) { - return null; - } - if (!this.sockets[name]) { - this.sockets[name] = []; - } - const fakeSocket = new net.Socket({ writable: false }); - this.sockets[name].push(fakeSocket); - this.totalSocketCount++; - return fakeSocket; - } - decrementSockets(name, socket) { - if (!this.sockets[name] || socket === null) { - return; - } - const sockets = this.sockets[name]; - const index = sockets.indexOf(socket); - if (index !== -1) { - sockets.splice(index, 1); - this.totalSocketCount--; - if (sockets.length === 0) { - delete this.sockets[name]; - } - } - } - // In order to properly update the socket pool, we need to call `getName()` on - // the core `https.Agent` if it is a secureEndpoint. - getName(options) { - const secureEndpoint = this.isSecureEndpoint(options); - if (secureEndpoint) { - return import_https.Agent.prototype.getName.call(this, options); - } - return super.getName(options); - } - createSocket(req, options, cb) { - const connectOpts = { - ...options, - secureEndpoint: this.isSecureEndpoint(options) - }; - const name = this.getName(connectOpts); - const fakeSocket = this.incrementSockets(name); - Promise.resolve().then(() => this.connect(req, connectOpts)).then((socket) => { - this.decrementSockets(name, fakeSocket); - if (socket instanceof http.Agent) { - try { - return socket.addRequest(req, connectOpts); - } catch (err) { - return cb(err); - } - } - this[INTERNAL].currentSocket = socket; - super.createSocket(req, options, cb); - }, (err) => { - this.decrementSockets(name, fakeSocket); - cb(err); - }); - } - createConnection() { - const socket = this[INTERNAL].currentSocket; - this[INTERNAL].currentSocket = void 0; - if (!socket) { - throw new Error("No socket was returned in the `connect()` function"); - } - return socket; - } - get defaultPort() { - return this[INTERNAL].defaultPort ?? (this.protocol === "https:" ? 443 : 80); - } - set defaultPort(v5) { - if (this[INTERNAL]) { - this[INTERNAL].defaultPort = v5; - } - } - get protocol() { - return this[INTERNAL].protocol ?? (this.isSecureEndpoint() ? "https:" : "http:"); - } - set protocol(v5) { - if (this[INTERNAL]) { - this[INTERNAL].protocol = v5; - } - } -}; - -// node_modules/https-proxy-agent/dist/index.js -var import_url = require("url"); - -// node_modules/https-proxy-agent/dist/parse-proxy-response.js -var import_debug = __toESM(require_src2(), 1); -var debug2 = (0, import_debug.default)("https-proxy-agent:parse-proxy-response"); -function parseProxyResponse(socket) { - return new Promise((resolve, reject) => { - let buffersLength = 0; - const buffers = []; - function read() { - const b7 = socket.read(); - if (b7) - ondata(b7); - else - socket.once("readable", read); - } - function cleanup() { - socket.removeListener("end", onend); - socket.removeListener("error", onerror); - socket.removeListener("readable", read); - } - function onend() { - cleanup(); - debug2("onend"); - reject(new Error("Proxy connection ended before receiving CONNECT response")); - } - function onerror(err) { - cleanup(); - debug2("onerror %o", err); - reject(err); - } - function ondata(b7) { - buffers.push(b7); - buffersLength += b7.length; - const buffered = Buffer.concat(buffers, buffersLength); - const endOfHeaders = buffered.indexOf("\r\n\r\n"); - if (endOfHeaders === -1) { - debug2("have not received end of HTTP headers yet..."); - read(); - return; - } - const headerParts = buffered.slice(0, endOfHeaders).toString("ascii").split("\r\n"); - const firstLine = headerParts.shift(); - if (!firstLine) { - socket.destroy(); - return reject(new Error("No header received from proxy CONNECT response")); - } - const firstLineParts = firstLine.split(" "); - const statusCode = +firstLineParts[1]; - const statusText = firstLineParts.slice(2).join(" "); - const headers = {}; - for (const header of headerParts) { - if (!header) - continue; - const firstColon = header.indexOf(":"); - if (firstColon === -1) { - socket.destroy(); - return reject(new Error(`Invalid header from proxy CONNECT response: "${header}"`)); - } - const key = header.slice(0, firstColon).toLowerCase(); - const value = header.slice(firstColon + 1).trimStart(); - const current = headers[key]; - if (typeof current === "string") { - headers[key] = [current, value]; - } else if (Array.isArray(current)) { - current.push(value); - } else { - headers[key] = value; - } - } - debug2("got proxy server response: %o %o", firstLine, headers); - cleanup(); - resolve({ - connect: { - statusCode, - statusText, - headers - }, - buffered - }); - } - socket.on("error", onerror); - socket.on("end", onend); - read(); - }); -} - -// node_modules/https-proxy-agent/dist/index.js -var debug3 = (0, import_debug2.default)("https-proxy-agent"); -var setServernameFromNonIpHost = (options) => { - if (options.servername === void 0 && options.host && !net2.isIP(options.host)) { - return { - ...options, - servername: options.host - }; - } - return options; -}; -var HttpsProxyAgent = class extends Agent2 { - constructor(proxy, opts) { - super(opts); - this.options = { path: void 0 }; - this.proxy = typeof proxy === "string" ? new import_url.URL(proxy) : proxy; - this.proxyHeaders = opts?.headers ?? {}; - debug3("Creating new HttpsProxyAgent instance: %o", this.proxy.href); - const host = (this.proxy.hostname || this.proxy.host).replace(/^\[|\]$/g, ""); - const port = this.proxy.port ? parseInt(this.proxy.port, 10) : this.proxy.protocol === "https:" ? 443 : 80; - this.connectOpts = { - // Attempt to negotiate http/1.1 for proxy servers that support http/2 - ALPNProtocols: ["http/1.1"], - ...opts ? omit(opts, "headers") : null, - host, - port - }; - } - /** - * Called when the node-core HTTP client library is creating a - * new HTTP request. - */ - async connect(req, opts) { - const { proxy } = this; - if (!opts.host) { - throw new TypeError('No "host" provided'); - } - let socket; - if (proxy.protocol === "https:") { - debug3("Creating `tls.Socket`: %o", this.connectOpts); - socket = tls.connect(setServernameFromNonIpHost(this.connectOpts)); - } else { - debug3("Creating `net.Socket`: %o", this.connectOpts); - socket = net2.connect(this.connectOpts); - } - const headers = typeof this.proxyHeaders === "function" ? this.proxyHeaders() : { ...this.proxyHeaders }; - const host = net2.isIPv6(opts.host) ? `[${opts.host}]` : opts.host; - let payload = `CONNECT ${host}:${opts.port} HTTP/1.1\r -`; - if (proxy.username || proxy.password) { - const auth = `${decodeURIComponent(proxy.username)}:${decodeURIComponent(proxy.password)}`; - headers["Proxy-Authorization"] = `Basic ${Buffer.from(auth).toString("base64")}`; - } - headers.Host = `${host}:${opts.port}`; - if (!headers["Proxy-Connection"]) { - headers["Proxy-Connection"] = this.keepAlive ? "Keep-Alive" : "close"; - } - for (const name of Object.keys(headers)) { - payload += `${name}: ${headers[name]}\r -`; - } - const proxyResponsePromise = parseProxyResponse(socket); - socket.write(`${payload}\r -`); - const { connect: connect3, buffered } = await proxyResponsePromise; - req.emit("proxyConnect", connect3); - this.emit("proxyConnect", connect3, req); - if (connect3.statusCode === 200) { - req.once("socket", resume); - if (opts.secureEndpoint) { - debug3("Upgrading socket connection to TLS"); - return tls.connect({ - ...omit(setServernameFromNonIpHost(opts), "host", "path", "port"), - socket - }); - } - return socket; - } - socket.destroy(); - const fakeSocket = new net2.Socket({ writable: false }); - fakeSocket.readable = true; - req.once("socket", (s2) => { - debug3("Replaying proxy buffer for failed request"); - (0, import_assert.default)(s2.listenerCount("data") > 0); - s2.push(buffered); - s2.push(null); - }); - return fakeSocket; - } -}; -HttpsProxyAgent.protocols = ["http", "https"]; -function resume(socket) { - socket.resume(); -} -function omit(obj, ...keys) { - const ret = {}; - let key; - for (key in obj) { - if (!keys.includes(key)) { - ret[key] = obj[key]; - } - } - return ret; -} +// src/engine/anthropic.ts +var import_https_proxy_agent = __toESM(require_dist2(), 1); // node_modules/axios/lib/helpers/bind.js function bind(fn, thisArg) { @@ -71709,8 +71405,8 @@ var transitional_default = { var import_crypto = __toESM(require("crypto"), 1); // node_modules/axios/lib/platform/node/classes/URLSearchParams.js -var import_url2 = __toESM(require("url"), 1); -var URLSearchParams_default = import_url2.default.URLSearchParams; +var import_url = __toESM(require("url"), 1); +var URLSearchParams_default = import_url.default.URLSearchParams; // node_modules/axios/lib/platform/node/index.js var ALPHA = "abcdefghijklmnopqrstuvwxyz"; @@ -72281,7 +71977,7 @@ function buildFullPath(baseURL, requestedURL, allowAbsoluteUrls) { // node_modules/axios/lib/adapters/http.js var import_proxy_from_env = __toESM(require_proxy_from_env(), 1); var import_http = __toESM(require("http"), 1); -var import_https2 = __toESM(require("https"), 1); +var import_https = __toESM(require("https"), 1); var import_util4 = __toESM(require("util"), 1); var import_follow_redirects = __toESM(require_follow_redirects(), 1); var import_zlib = __toESM(require("zlib"), 1); @@ -72994,7 +72690,7 @@ var http_default = isHttpAdapterSupported && function httpAdapter(config8) { if (config8.transport) { transport = config8.transport; } else if (config8.maxRedirects === 0) { - transport = isHttpsRequest ? import_https2.default : import_http.default; + transport = isHttpsRequest ? import_https.default : import_http.default; } else { if (config8.maxRedirects) { options.maxRedirects = config8.maxRedirects; @@ -74488,7 +74184,27 @@ function isServiceUnavailableError(error) { } return false; } -function formatUserFriendlyError(error, provider) { +function getCustomEndpointLabel(baseURL) { + if (!baseURL) { + return null; + } + try { + return new URL(baseURL).host; + } catch { + return null; + } +} +function getServiceUnavailableMessage(provider, context) { + const endpointLabel = getCustomEndpointLabel(context?.baseURL); + if (endpointLabel) { + return `The configured API endpoint (${endpointLabel}) is temporarily unavailable.`; + } + if (context?.baseURL) { + return "The configured API endpoint is temporarily unavailable."; + } + return `The ${provider} service is temporarily unavailable.`; +} +function formatUserFriendlyError(error, provider, context) { const billingUrl = PROVIDER_BILLING_URLS[provider] || null; if (error instanceof InsufficientCreditsError) { return { @@ -74510,7 +74226,7 @@ function formatUserFriendlyError(error, provider) { if (error instanceof ServiceUnavailableError) { return { title: "Service Unavailable", - message: `The ${provider} service is temporarily unavailable.`, + message: getServiceUnavailableMessage(provider, context), helpUrl: null, suggestion: "Please try again in a few moments." }; @@ -74550,7 +74266,7 @@ function formatUserFriendlyError(error, provider) { if (isServiceUnavailableError(error)) { return { title: "Service Unavailable", - message: `The ${provider} service is temporarily unavailable.`, + message: getServiceUnavailableMessage(provider, context), helpUrl: null, suggestion: "Please try again in a few moments." }; @@ -74703,6 +74419,15 @@ function normalizeEngineError(error, provider, model) { return error instanceof Error ? error : new Error(message); } +// src/utils/generateCommitMessageErrors.ts +var GenerateCommitMessageErrorEnum = ((GenerateCommitMessageErrorEnum2) => { + GenerateCommitMessageErrorEnum2["tooMuchTokens"] = "TOO_MUCH_TOKENS"; + GenerateCommitMessageErrorEnum2["internalError"] = "INTERNAL_ERROR"; + GenerateCommitMessageErrorEnum2["emptyMessage"] = "EMPTY_MESSAGE"; + GenerateCommitMessageErrorEnum2["outputTokensTooHigh"] = `Token limit exceeded, OCO_TOKENS_MAX_OUTPUT must not be much higher than the default ${500 /* DEFAULT_MAX_TOKENS_OUTPUT */} tokens.`; + return GenerateCommitMessageErrorEnum2; +})(GenerateCommitMessageErrorEnum || {}); + // src/utils/removeContentTags.ts function removeContentTags(content, tag) { if (!content || typeof content !== "string") { @@ -74786,9 +74511,9 @@ var AnthropicEngine = class { }; this.config = config8; const clientOptions = { apiKey: this.config.apiKey }; - const proxy = config8.proxy || process.env.HTTPS_PROXY || process.env.HTTP_PROXY; + const proxy = config8.proxy; if (proxy) { - clientOptions.httpAgent = new HttpsProxyAgent(proxy); + clientOptions.httpAgent = new import_https_proxy_agent.HttpsProxyAgent(proxy); } this.client = new sdk_default(clientOptions); } @@ -74942,11 +74667,11 @@ function __asyncGenerator(thisArg, _arguments, generator) { function verb(n2) { if (g5[n2]) i3[n2] = function(v5) { return new Promise(function(a4, b7) { - q6.push([n2, v5, a4, b7]) > 1 || resume2(n2, v5); + q6.push([n2, v5, a4, b7]) > 1 || resume(n2, v5); }); }; } - function resume2(n2, v5) { + function resume(n2, v5) { try { step(g5[n2](v5)); } catch (e3) { @@ -74957,13 +74682,13 @@ function __asyncGenerator(thisArg, _arguments, generator) { r3.value instanceof __await ? Promise.resolve(r3.value.v).then(fulfill, reject) : settle2(q6[0][2], r3); } function fulfill(value) { - resume2("next", value); + resume("next", value); } function reject(value) { - resume2("throw", value); + resume("throw", value); } function settle2(f4, v5) { - if (f4(v5), q6.shift(), q6.length) resume2(q6[0][0], q6[0][1]); + if (f4(v5), q6.shift(), q6.length) resume(q6[0][0], q6[0][1]); } } function __asyncValues(o3) { @@ -75231,14 +74956,14 @@ function disable() { return result; } function createDebugger(namespace) { - const newDebugger = Object.assign(debug5, { + const newDebugger = Object.assign(debug3, { enabled: enabled(namespace), destroy, log: debugObj.log, namespace, extend: extend2 }); - function debug5(...args) { + function debug3(...args) { if (!newDebugger.enabled) { return; } @@ -76599,7 +76324,7 @@ function createPipelineFromOptions(options) { } // node_modules/@azure/core-rest-pipeline/dist/esm/nodeHttpClient.js -var http3 = __toESM(require("node:http"), 1); +var http2 = __toESM(require("node:http"), 1); var https2 = __toESM(require("node:https"), 1); var zlib2 = __toESM(require("node:zlib"), 1); var import_node_stream3 = require("node:stream"); @@ -76756,7 +76481,7 @@ var NodeHttpClient = class { headers: request3.headers.toJSON({ preserveCase: true }) }; return new Promise((resolve, reject) => { - const req = isInsecure ? http3.request(options, resolve) : https2.request(options, resolve); + const req = isInsecure ? http2.request(options, resolve) : https2.request(options, resolve); req.once("error", (err) => { var _a6; reject(new RestError(err.message, { code: (_a6 = err.code) !== null && _a6 !== void 0 ? _a6 : RestError.REQUEST_SEND_ERROR, request: request3 })); @@ -76787,10 +76512,10 @@ var NodeHttpClient = class { const disableKeepAlive = request3.disableKeepAlive; if (isInsecure) { if (disableKeepAlive) { - return http3.globalAgent; + return http2.globalAgent; } if (!this.cachedHttpAgent) { - this.cachedHttpAgent = new http3.Agent({ keepAlive: true }); + this.cachedHttpAgent = new http2.Agent({ keepAlive: true }); } return this.cachedHttpAgent; } else { @@ -78662,27 +78387,6 @@ var FlowiseEngine = class { }; // node_modules/@google/generative-ai/dist/index.mjs -var SchemaType; -(function(SchemaType2) { - SchemaType2["STRING"] = "string"; - SchemaType2["NUMBER"] = "number"; - SchemaType2["INTEGER"] = "integer"; - SchemaType2["BOOLEAN"] = "boolean"; - SchemaType2["ARRAY"] = "array"; - SchemaType2["OBJECT"] = "object"; -})(SchemaType || (SchemaType = {})); -var ExecutableCodeLanguage; -(function(ExecutableCodeLanguage2) { - ExecutableCodeLanguage2["LANGUAGE_UNSPECIFIED"] = "language_unspecified"; - ExecutableCodeLanguage2["PYTHON"] = "python"; -})(ExecutableCodeLanguage || (ExecutableCodeLanguage = {})); -var Outcome; -(function(Outcome2) { - Outcome2["OUTCOME_UNSPECIFIED"] = "outcome_unspecified"; - Outcome2["OUTCOME_OK"] = "outcome_ok"; - Outcome2["OUTCOME_FAILED"] = "outcome_failed"; - Outcome2["OUTCOME_DEADLINE_EXCEEDED"] = "outcome_deadline_exceeded"; -})(Outcome || (Outcome = {})); var POSSIBLE_ROLES = ["user", "model", "function", "system"]; var HarmCategory; (function(HarmCategory2) { @@ -78691,7 +78395,6 @@ var HarmCategory; HarmCategory2["HARM_CATEGORY_SEXUALLY_EXPLICIT"] = "HARM_CATEGORY_SEXUALLY_EXPLICIT"; HarmCategory2["HARM_CATEGORY_HARASSMENT"] = "HARM_CATEGORY_HARASSMENT"; HarmCategory2["HARM_CATEGORY_DANGEROUS_CONTENT"] = "HARM_CATEGORY_DANGEROUS_CONTENT"; - HarmCategory2["HARM_CATEGORY_CIVIC_INTEGRITY"] = "HARM_CATEGORY_CIVIC_INTEGRITY"; })(HarmCategory || (HarmCategory = {})); var HarmBlockThreshold; (function(HarmBlockThreshold2) { @@ -78722,11 +78425,6 @@ var FinishReason; FinishReason2["MAX_TOKENS"] = "MAX_TOKENS"; FinishReason2["SAFETY"] = "SAFETY"; FinishReason2["RECITATION"] = "RECITATION"; - FinishReason2["LANGUAGE"] = "LANGUAGE"; - FinishReason2["BLOCKLIST"] = "BLOCKLIST"; - FinishReason2["PROHIBITED_CONTENT"] = "PROHIBITED_CONTENT"; - FinishReason2["SPII"] = "SPII"; - FinishReason2["MALFORMED_FUNCTION_CALL"] = "MALFORMED_FUNCTION_CALL"; FinishReason2["OTHER"] = "OTHER"; })(FinishReason || (FinishReason = {})); var TaskType; @@ -78745,11 +78443,15 @@ var FunctionCallingMode; FunctionCallingMode2["ANY"] = "ANY"; FunctionCallingMode2["NONE"] = "NONE"; })(FunctionCallingMode || (FunctionCallingMode = {})); -var DynamicRetrievalMode; -(function(DynamicRetrievalMode2) { - DynamicRetrievalMode2["MODE_UNSPECIFIED"] = "MODE_UNSPECIFIED"; - DynamicRetrievalMode2["MODE_DYNAMIC"] = "MODE_DYNAMIC"; -})(DynamicRetrievalMode || (DynamicRetrievalMode = {})); +var FunctionDeclarationSchemaType; +(function(FunctionDeclarationSchemaType2) { + FunctionDeclarationSchemaType2["STRING"] = "STRING"; + FunctionDeclarationSchemaType2["NUMBER"] = "NUMBER"; + FunctionDeclarationSchemaType2["INTEGER"] = "INTEGER"; + FunctionDeclarationSchemaType2["BOOLEAN"] = "BOOLEAN"; + FunctionDeclarationSchemaType2["ARRAY"] = "ARRAY"; + FunctionDeclarationSchemaType2["OBJECT"] = "OBJECT"; +})(FunctionDeclarationSchemaType || (FunctionDeclarationSchemaType = {})); var GoogleGenerativeAIError = class extends Error { constructor(message) { super(`[GoogleGenerativeAI Error]: ${message}`); @@ -78771,11 +78473,9 @@ var GoogleGenerativeAIFetchError = class extends GoogleGenerativeAIError { }; var GoogleGenerativeAIRequestInputError = class extends GoogleGenerativeAIError { }; -var GoogleGenerativeAIAbortError = class extends GoogleGenerativeAIError { -}; var DEFAULT_BASE_URL = "https://generativelanguage.googleapis.com"; var DEFAULT_API_VERSION = "v1beta"; -var PACKAGE_VERSION = "0.24.1"; +var PACKAGE_VERSION = "0.11.4"; var PACKAGE_LOG_HEADER = "genai-js"; var Task; (function(Task2) { @@ -78813,12 +78513,11 @@ function getClientHeaders(requestOptions) { return clientHeaders.join(" "); } async function getHeaders(url2) { - var _a5; const headers = new Headers(); headers.append("Content-Type", "application/json"); headers.append("x-goog-api-client", getClientHeaders(url2.requestOptions)); headers.append("x-goog-api-key", url2.apiKey); - let customHeaders = (_a5 = url2.requestOptions) === null || _a5 === void 0 ? void 0 : _a5.customHeaders; + let customHeaders = url2.requestOptions.customHeaders; if (customHeaders) { if (!(customHeaders instanceof Headers)) { try { @@ -78838,67 +78537,53 @@ async function getHeaders(url2) { } return headers; } -async function constructModelRequest(model, task, apiKey, stream4, body, requestOptions) { +async function constructRequest(model, task, apiKey, stream4, body, requestOptions) { const url2 = new RequestUrl(model, task, apiKey, stream4, requestOptions); return { url: url2.toString(), fetchOptions: Object.assign(Object.assign({}, buildFetchOptions(requestOptions)), { method: "POST", headers: await getHeaders(url2), body }) }; } -async function makeModelRequest(model, task, apiKey, stream4, body, requestOptions = {}, fetchFn = fetch) { - const { url: url2, fetchOptions } = await constructModelRequest(model, task, apiKey, stream4, body, requestOptions); - return makeRequest(url2, fetchOptions, fetchFn); +async function makeRequest(model, task, apiKey, stream4, body, requestOptions) { + return _makeRequestInternal(model, task, apiKey, stream4, body, requestOptions, fetch); } -async function makeRequest(url2, fetchOptions, fetchFn = fetch) { +async function _makeRequestInternal(model, task, apiKey, stream4, body, requestOptions, fetchFn = fetch) { + const url2 = new RequestUrl(model, task, apiKey, stream4, requestOptions); let response; try { - response = await fetchFn(url2, fetchOptions); - } catch (e3) { - handleResponseError(e3, url2); - } - if (!response.ok) { - await handleResponseNotOk(response, url2); - } - return response; -} -function handleResponseError(e3, url2) { - let err = e3; - if (err.name === "AbortError") { - err = new GoogleGenerativeAIAbortError(`Request aborted when fetching ${url2.toString()}: ${e3.message}`); - err.stack = e3.stack; - } else if (!(e3 instanceof GoogleGenerativeAIFetchError || e3 instanceof GoogleGenerativeAIRequestInputError)) { - err = new GoogleGenerativeAIError(`Error fetching from ${url2.toString()}: ${e3.message}`); - err.stack = e3.stack; - } - throw err; -} -async function handleResponseNotOk(response, url2) { - let message = ""; - let errorDetails; - try { - const json = await response.json(); - message = json.error.message; - if (json.error.details) { - message += ` ${JSON.stringify(json.error.details)}`; - errorDetails = json.error.details; + const request3 = await constructRequest(model, task, apiKey, stream4, body, requestOptions); + response = await fetchFn(request3.url, request3.fetchOptions); + if (!response.ok) { + let message = ""; + let errorDetails; + try { + const json = await response.json(); + message = json.error.message; + if (json.error.details) { + message += ` ${JSON.stringify(json.error.details)}`; + errorDetails = json.error.details; + } + } catch (e3) { + } + throw new GoogleGenerativeAIFetchError(`Error fetching from ${url2.toString()}: [${response.status} ${response.statusText}] ${message}`, response.status, response.statusText, errorDetails); } } catch (e3) { + let err = e3; + if (!(e3 instanceof GoogleGenerativeAIFetchError || e3 instanceof GoogleGenerativeAIRequestInputError)) { + err = new GoogleGenerativeAIError(`Error fetching from ${url2.toString()}: ${e3.message}`); + err.stack = e3.stack; + } + throw err; } - throw new GoogleGenerativeAIFetchError(`Error fetching from ${url2.toString()}: [${response.status} ${response.statusText}] ${message}`, response.status, response.statusText, errorDetails); + return response; } function buildFetchOptions(requestOptions) { const fetchOptions = {}; - if ((requestOptions === null || requestOptions === void 0 ? void 0 : requestOptions.signal) !== void 0 || (requestOptions === null || requestOptions === void 0 ? void 0 : requestOptions.timeout) >= 0) { - const controller = new AbortController(); - if ((requestOptions === null || requestOptions === void 0 ? void 0 : requestOptions.timeout) >= 0) { - setTimeout(() => controller.abort(), requestOptions.timeout); - } - if (requestOptions === null || requestOptions === void 0 ? void 0 : requestOptions.signal) { - requestOptions.signal.addEventListener("abort", () => { - controller.abort(); - }); - } - fetchOptions.signal = controller.signal; + if ((requestOptions === null || requestOptions === void 0 ? void 0 : requestOptions.timeout) >= 0) { + const abortController = new AbortController(); + const signal = abortController.signal; + setTimeout(() => abortController.abort(), requestOptions.timeout); + fetchOptions.signal = signal; } return fetchOptions; } @@ -78956,12 +78641,6 @@ function getText(response) { if (part.text) { textStrings.push(part.text); } - if (part.executableCode) { - textStrings.push("\n```" + part.executableCode.language + "\n" + part.executableCode.code + "\n```\n"); - } - if (part.codeExecutionResult) { - textStrings.push("\n```\n" + part.codeExecutionResult.output + "\n```\n"); - } } } if (textStrings.length > 0) { @@ -78986,11 +78665,7 @@ function getFunctionCalls(response) { return void 0; } } -var badFinishReasons = [ - FinishReason.RECITATION, - FinishReason.SAFETY, - FinishReason.LANGUAGE -]; +var badFinishReasons = [FinishReason.RECITATION, FinishReason.SAFETY]; function hadBadFinishReason(candidate) { return !!candidate.finishReason && badFinishReasons.includes(candidate.finishReason); } @@ -79028,11 +78703,11 @@ function __asyncGenerator2(thisArg, _arguments, generator) { function verb(n2) { if (g5[n2]) i3[n2] = function(v5) { return new Promise(function(a4, b7) { - q6.push([n2, v5, a4, b7]) > 1 || resume2(n2, v5); + q6.push([n2, v5, a4, b7]) > 1 || resume(n2, v5); }); }; } - function resume2(n2, v5) { + function resume(n2, v5) { try { step(g5[n2](v5)); } catch (e3) { @@ -79043,13 +78718,13 @@ function __asyncGenerator2(thisArg, _arguments, generator) { r3.value instanceof __await2 ? Promise.resolve(r3.value.v).then(fulfill, reject) : settle2(q6[0][2], r3); } function fulfill(value) { - resume2("next", value); + resume("next", value); } function reject(value) { - resume2("throw", value); + resume("throw", value); } function settle2(f4, v5) { - if (f4(v5), q6.shift(), q6.length) resume2(q6[0][0], q6[0][1]); + if (f4(v5), q6.shift(), q6.length) resume(q6[0][0], q6[0][1]); } } var responseLineRE = /^data\: (.*)(?:\n\n|\r\r|\r\n\r\n)/; @@ -79116,15 +78791,6 @@ function getResponseStream(inputStream) { match = currentText.match(responseLineRE); } return pump(); - }).catch((e3) => { - let err = e3; - err.stack = e3.stack; - if (err.name === "AbortError") { - err = new GoogleGenerativeAIAbortError("Request aborted when reading from the stream"); - } else { - err = new GoogleGenerativeAIError("Error reading from the stream"); - } - throw err; }); } } @@ -79138,24 +78804,23 @@ function aggregateResponses(responses) { }; for (const response of responses) { if (response.candidates) { - let candidateIndex = 0; for (const candidate of response.candidates) { + const i3 = candidate.index; if (!aggregatedResponse.candidates) { aggregatedResponse.candidates = []; } - if (!aggregatedResponse.candidates[candidateIndex]) { - aggregatedResponse.candidates[candidateIndex] = { - index: candidateIndex + if (!aggregatedResponse.candidates[i3]) { + aggregatedResponse.candidates[i3] = { + index: candidate.index }; } - aggregatedResponse.candidates[candidateIndex].citationMetadata = candidate.citationMetadata; - aggregatedResponse.candidates[candidateIndex].groundingMetadata = candidate.groundingMetadata; - aggregatedResponse.candidates[candidateIndex].finishReason = candidate.finishReason; - aggregatedResponse.candidates[candidateIndex].finishMessage = candidate.finishMessage; - aggregatedResponse.candidates[candidateIndex].safetyRatings = candidate.safetyRatings; + aggregatedResponse.candidates[i3].citationMetadata = candidate.citationMetadata; + aggregatedResponse.candidates[i3].finishReason = candidate.finishReason; + aggregatedResponse.candidates[i3].finishMessage = candidate.finishMessage; + aggregatedResponse.candidates[i3].safetyRatings = candidate.safetyRatings; if (candidate.content && candidate.content.parts) { - if (!aggregatedResponse.candidates[candidateIndex].content) { - aggregatedResponse.candidates[candidateIndex].content = { + if (!aggregatedResponse.candidates[i3].content) { + aggregatedResponse.candidates[i3].content = { role: candidate.content.role || "user", parts: [] }; @@ -79168,29 +78833,19 @@ function aggregateResponses(responses) { if (part.functionCall) { newPart.functionCall = part.functionCall; } - if (part.executableCode) { - newPart.executableCode = part.executableCode; - } - if (part.codeExecutionResult) { - newPart.codeExecutionResult = part.codeExecutionResult; - } if (Object.keys(newPart).length === 0) { newPart.text = ""; } - aggregatedResponse.candidates[candidateIndex].content.parts.push(newPart); + aggregatedResponse.candidates[i3].content.parts.push(newPart); } } } - candidateIndex++; - } - if (response.usageMetadata) { - aggregatedResponse.usageMetadata = response.usageMetadata; } } return aggregatedResponse; } async function generateContentStream(apiKey, model, params, requestOptions) { - const response = await makeModelRequest( + const response = await makeRequest( model, Task.STREAM_GENERATE_CONTENT, apiKey, @@ -79202,7 +78857,7 @@ async function generateContentStream(apiKey, model, params, requestOptions) { return processStream(response); } async function generateContent(apiKey, model, params, requestOptions) { - const response = await makeModelRequest( + const response = await makeRequest( model, Task.GENERATE_CONTENT, apiKey, @@ -79272,32 +78927,6 @@ function assignRoleToPartsAndValidateSendMessageRequest(parts) { } return functionContent; } -function formatCountTokensInput(params, modelParams) { - var _a5; - let formattedGenerateContentRequest = { - model: modelParams === null || modelParams === void 0 ? void 0 : modelParams.model, - generationConfig: modelParams === null || modelParams === void 0 ? void 0 : modelParams.generationConfig, - safetySettings: modelParams === null || modelParams === void 0 ? void 0 : modelParams.safetySettings, - tools: modelParams === null || modelParams === void 0 ? void 0 : modelParams.tools, - toolConfig: modelParams === null || modelParams === void 0 ? void 0 : modelParams.toolConfig, - systemInstruction: modelParams === null || modelParams === void 0 ? void 0 : modelParams.systemInstruction, - cachedContent: (_a5 = modelParams === null || modelParams === void 0 ? void 0 : modelParams.cachedContent) === null || _a5 === void 0 ? void 0 : _a5.name, - contents: [] - }; - const containsGenerateContentRequest = params.generateContentRequest != null; - if (params.contents) { - if (containsGenerateContentRequest) { - throw new GoogleGenerativeAIRequestInputError("CountTokensRequest must have one of contents or generateContentRequest, not both."); - } - formattedGenerateContentRequest.contents = params.contents; - } else if (containsGenerateContentRequest) { - formattedGenerateContentRequest = Object.assign(Object.assign({}, formattedGenerateContentRequest), params.generateContentRequest); - } else { - const content = formatNewContent(params); - formattedGenerateContentRequest.contents = [content]; - } - return { generateContentRequest: formattedGenerateContentRequest }; -} function formatGenerateContentInput(params) { let formattedRequest; if (params.contents) { @@ -79322,14 +78951,12 @@ var VALID_PART_FIELDS = [ "text", "inlineData", "functionCall", - "functionResponse", - "executableCode", - "codeExecutionResult" + "functionResponse" ]; var VALID_PARTS_PER_ROLE = { user: ["text", "inlineData"], function: ["functionResponse"], - model: ["text", "functionCall", "executableCode", "codeExecutionResult"], + model: ["text", "functionCall"], // System instructions shouldn't be in history anyway. system: ["text"] }; @@ -79354,9 +78981,7 @@ function validateChatHistory(history) { inlineData: 0, functionCall: 0, functionResponse: 0, - fileData: 0, - executableCode: 0, - codeExecutionResult: 0 + fileData: 0 }; for (const part of parts) { for (const key of VALID_PART_FIELDS) { @@ -79374,34 +78999,12 @@ function validateChatHistory(history) { prevContent = true; } } -function isValidResponse(response) { - var _a5; - if (response.candidates === void 0 || response.candidates.length === 0) { - return false; - } - const content = (_a5 = response.candidates[0]) === null || _a5 === void 0 ? void 0 : _a5.content; - if (content === void 0) { - return false; - } - if (content.parts === void 0 || content.parts.length === 0) { - return false; - } - for (const part of content.parts) { - if (part === void 0 || Object.keys(part).length === 0) { - return false; - } - if (part.text !== void 0 && part.text === "") { - return false; - } - } - return true; -} var SILENT_ERROR = "SILENT_ERROR"; var ChatSession = class { - constructor(apiKey, model, params, _requestOptions = {}) { + constructor(apiKey, model, params, requestOptions) { this.model = model; this.params = params; - this._requestOptions = _requestOptions; + this.requestOptions = requestOptions; this._history = []; this._sendPromise = Promise.resolve(); this._apiKey = apiKey; @@ -79421,14 +79024,10 @@ var ChatSession = class { } /** * Sends a chat message and receives a non-streaming - * {@link GenerateContentResult}. - * - * Fields set in the optional {@link SingleRequestOptions} parameter will - * take precedence over the {@link RequestOptions} values provided to - * {@link GoogleGenerativeAI.getGenerativeModel }. + * {@link GenerateContentResult} */ - async sendMessage(request3, requestOptions = {}) { - var _a5, _b2, _c2, _d2, _e2, _f; + async sendMessage(request3) { + var _a5, _b2, _c2, _d2, _e2; await this._sendPromise; const newContent = formatNewContent(request3); const generateContentRequest = { @@ -79437,14 +79036,12 @@ var ChatSession = class { tools: (_c2 = this.params) === null || _c2 === void 0 ? void 0 : _c2.tools, toolConfig: (_d2 = this.params) === null || _d2 === void 0 ? void 0 : _d2.toolConfig, systemInstruction: (_e2 = this.params) === null || _e2 === void 0 ? void 0 : _e2.systemInstruction, - cachedContent: (_f = this.params) === null || _f === void 0 ? void 0 : _f.cachedContent, contents: [...this._history, newContent] }; - const chatSessionRequestOptions = Object.assign(Object.assign({}, this._requestOptions), requestOptions); let finalResult; - this._sendPromise = this._sendPromise.then(() => generateContent(this._apiKey, this.model, generateContentRequest, chatSessionRequestOptions)).then((result) => { + this._sendPromise = this._sendPromise.then(() => generateContent(this._apiKey, this.model, generateContentRequest, this.requestOptions)).then((result) => { var _a6; - if (isValidResponse(result.response)) { + if (result.response.candidates && result.response.candidates.length > 0) { this._history.push(newContent); const responseContent = Object.assign({ parts: [], @@ -79459,9 +79056,6 @@ var ChatSession = class { } } finalResult = result; - }).catch((e3) => { - this._sendPromise = Promise.resolve(); - throw e3; }); await this._sendPromise; return finalResult; @@ -79470,13 +79064,9 @@ var ChatSession = class { * Sends a chat message and receives the response as a * {@link GenerateContentStreamResult} containing an iterable stream * and a response promise. - * - * Fields set in the optional {@link SingleRequestOptions} parameter will - * take precedence over the {@link RequestOptions} values provided to - * {@link GoogleGenerativeAI.getGenerativeModel }. */ - async sendMessageStream(request3, requestOptions = {}) { - var _a5, _b2, _c2, _d2, _e2, _f; + async sendMessageStream(request3) { + var _a5, _b2, _c2, _d2, _e2; await this._sendPromise; const newContent = formatNewContent(request3); const generateContentRequest = { @@ -79485,15 +79075,13 @@ var ChatSession = class { tools: (_c2 = this.params) === null || _c2 === void 0 ? void 0 : _c2.tools, toolConfig: (_d2 = this.params) === null || _d2 === void 0 ? void 0 : _d2.toolConfig, systemInstruction: (_e2 = this.params) === null || _e2 === void 0 ? void 0 : _e2.systemInstruction, - cachedContent: (_f = this.params) === null || _f === void 0 ? void 0 : _f.cachedContent, contents: [...this._history, newContent] }; - const chatSessionRequestOptions = Object.assign(Object.assign({}, this._requestOptions), requestOptions); - const streamPromise = generateContentStream(this._apiKey, this.model, generateContentRequest, chatSessionRequestOptions); + const streamPromise = generateContentStream(this._apiKey, this.model, generateContentRequest, this.requestOptions); this._sendPromise = this._sendPromise.then(() => streamPromise).catch((_ignored) => { throw new Error(SILENT_ERROR); }).then((streamResult) => streamResult.response).then((response) => { - if (isValidResponse(response)) { + if (response.candidates && response.candidates.length > 0) { this._history.push(newContent); const responseContent = Object.assign({}, response.candidates[0].content); if (!responseContent.role) { @@ -79514,25 +79102,24 @@ var ChatSession = class { return streamPromise; } }; -async function countTokens(apiKey, model, params, singleRequestOptions) { - const response = await makeModelRequest(model, Task.COUNT_TOKENS, apiKey, false, JSON.stringify(params), singleRequestOptions); +async function countTokens(apiKey, model, params, requestOptions) { + const response = await makeRequest(model, Task.COUNT_TOKENS, apiKey, false, JSON.stringify(Object.assign(Object.assign({}, params), { model })), requestOptions); return response.json(); } async function embedContent(apiKey, model, params, requestOptions) { - const response = await makeModelRequest(model, Task.EMBED_CONTENT, apiKey, false, JSON.stringify(params), requestOptions); + const response = await makeRequest(model, Task.EMBED_CONTENT, apiKey, false, JSON.stringify(params), requestOptions); return response.json(); } async function batchEmbedContents(apiKey, model, params, requestOptions) { const requestsWithModel = params.requests.map((request3) => { return Object.assign(Object.assign({}, request3), { model }); }); - const response = await makeModelRequest(model, Task.BATCH_EMBED_CONTENTS, apiKey, false, JSON.stringify({ requests: requestsWithModel }), requestOptions); + const response = await makeRequest(model, Task.BATCH_EMBED_CONTENTS, apiKey, false, JSON.stringify({ requests: requestsWithModel }), requestOptions); return response.json(); } var GenerativeModel = class { - constructor(apiKey, modelParams, _requestOptions = {}) { + constructor(apiKey, modelParams, requestOptions) { this.apiKey = apiKey; - this._requestOptions = _requestOptions; if (modelParams.model.includes("/")) { this.model = modelParams.model; } else { @@ -79543,88 +79130,52 @@ var GenerativeModel = class { this.tools = modelParams.tools; this.toolConfig = modelParams.toolConfig; this.systemInstruction = formatSystemInstruction(modelParams.systemInstruction); - this.cachedContent = modelParams.cachedContent; + this.requestOptions = requestOptions || {}; } /** * Makes a single non-streaming call to the model * and returns an object containing a single {@link GenerateContentResponse}. - * - * Fields set in the optional {@link SingleRequestOptions} parameter will - * take precedence over the {@link RequestOptions} values provided to - * {@link GoogleGenerativeAI.getGenerativeModel }. */ - async generateContent(request3, requestOptions = {}) { - var _a5; + async generateContent(request3) { const formattedParams = formatGenerateContentInput(request3); - const generativeModelRequestOptions = Object.assign(Object.assign({}, this._requestOptions), requestOptions); - return generateContent(this.apiKey, this.model, Object.assign({ generationConfig: this.generationConfig, safetySettings: this.safetySettings, tools: this.tools, toolConfig: this.toolConfig, systemInstruction: this.systemInstruction, cachedContent: (_a5 = this.cachedContent) === null || _a5 === void 0 ? void 0 : _a5.name }, formattedParams), generativeModelRequestOptions); + return generateContent(this.apiKey, this.model, Object.assign({ generationConfig: this.generationConfig, safetySettings: this.safetySettings, tools: this.tools, toolConfig: this.toolConfig, systemInstruction: this.systemInstruction }, formattedParams), this.requestOptions); } /** - * Makes a single streaming call to the model and returns an object - * containing an iterable stream that iterates over all chunks in the - * streaming response as well as a promise that returns the final - * aggregated response. - * - * Fields set in the optional {@link SingleRequestOptions} parameter will - * take precedence over the {@link RequestOptions} values provided to - * {@link GoogleGenerativeAI.getGenerativeModel }. + * Makes a single streaming call to the model + * and returns an object containing an iterable stream that iterates + * over all chunks in the streaming response as well as + * a promise that returns the final aggregated response. */ - async generateContentStream(request3, requestOptions = {}) { - var _a5; + async generateContentStream(request3) { const formattedParams = formatGenerateContentInput(request3); - const generativeModelRequestOptions = Object.assign(Object.assign({}, this._requestOptions), requestOptions); - return generateContentStream(this.apiKey, this.model, Object.assign({ generationConfig: this.generationConfig, safetySettings: this.safetySettings, tools: this.tools, toolConfig: this.toolConfig, systemInstruction: this.systemInstruction, cachedContent: (_a5 = this.cachedContent) === null || _a5 === void 0 ? void 0 : _a5.name }, formattedParams), generativeModelRequestOptions); + return generateContentStream(this.apiKey, this.model, Object.assign({ generationConfig: this.generationConfig, safetySettings: this.safetySettings, tools: this.tools, toolConfig: this.toolConfig, systemInstruction: this.systemInstruction }, formattedParams), this.requestOptions); } /** * Gets a new {@link ChatSession} instance which can be used for * multi-turn chats. */ startChat(startChatParams) { - var _a5; - return new ChatSession(this.apiKey, this.model, Object.assign({ generationConfig: this.generationConfig, safetySettings: this.safetySettings, tools: this.tools, toolConfig: this.toolConfig, systemInstruction: this.systemInstruction, cachedContent: (_a5 = this.cachedContent) === null || _a5 === void 0 ? void 0 : _a5.name }, startChatParams), this._requestOptions); + return new ChatSession(this.apiKey, this.model, Object.assign({ generationConfig: this.generationConfig, safetySettings: this.safetySettings, tools: this.tools, toolConfig: this.toolConfig, systemInstruction: this.systemInstruction }, startChatParams), this.requestOptions); } /** * Counts the tokens in the provided request. - * - * Fields set in the optional {@link SingleRequestOptions} parameter will - * take precedence over the {@link RequestOptions} values provided to - * {@link GoogleGenerativeAI.getGenerativeModel }. */ - async countTokens(request3, requestOptions = {}) { - const formattedParams = formatCountTokensInput(request3, { - model: this.model, - generationConfig: this.generationConfig, - safetySettings: this.safetySettings, - tools: this.tools, - toolConfig: this.toolConfig, - systemInstruction: this.systemInstruction, - cachedContent: this.cachedContent - }); - const generativeModelRequestOptions = Object.assign(Object.assign({}, this._requestOptions), requestOptions); - return countTokens(this.apiKey, this.model, formattedParams, generativeModelRequestOptions); + async countTokens(request3) { + const formattedParams = formatGenerateContentInput(request3); + return countTokens(this.apiKey, this.model, formattedParams, this.requestOptions); } /** * Embeds the provided content. - * - * Fields set in the optional {@link SingleRequestOptions} parameter will - * take precedence over the {@link RequestOptions} values provided to - * {@link GoogleGenerativeAI.getGenerativeModel }. */ - async embedContent(request3, requestOptions = {}) { + async embedContent(request3) { const formattedParams = formatEmbedContentInput(request3); - const generativeModelRequestOptions = Object.assign(Object.assign({}, this._requestOptions), requestOptions); - return embedContent(this.apiKey, this.model, formattedParams, generativeModelRequestOptions); + return embedContent(this.apiKey, this.model, formattedParams, this.requestOptions); } /** * Embeds an array of {@link EmbedContentRequest}s. - * - * Fields set in the optional {@link SingleRequestOptions} parameter will - * take precedence over the {@link RequestOptions} values provided to - * {@link GoogleGenerativeAI.getGenerativeModel }. */ - async batchEmbedContents(batchEmbedContentRequest, requestOptions = {}) { - const generativeModelRequestOptions = Object.assign(Object.assign({}, this._requestOptions), requestOptions); - return batchEmbedContents(this.apiKey, this.model, batchEmbedContentRequest, generativeModelRequestOptions); + async batchEmbedContents(batchEmbedContentRequest) { + return batchEmbedContents(this.apiKey, this.model, batchEmbedContentRequest, this.requestOptions); } }; var GoogleGenerativeAI = class { @@ -79640,32 +79191,6 @@ var GoogleGenerativeAI = class { } return new GenerativeModel(this.apiKey, modelParams, requestOptions); } - /** - * Creates a {@link GenerativeModel} instance from provided content cache. - */ - getGenerativeModelFromCachedContent(cachedContent, modelParams, requestOptions) { - if (!cachedContent.name) { - throw new GoogleGenerativeAIRequestInputError("Cached content must contain a `name` field."); - } - if (!cachedContent.model) { - throw new GoogleGenerativeAIRequestInputError("Cached content must contain a `model` field."); - } - const disallowedDuplicates = ["model", "systemInstruction"]; - for (const key of disallowedDuplicates) { - if ((modelParams === null || modelParams === void 0 ? void 0 : modelParams[key]) && cachedContent[key] && (modelParams === null || modelParams === void 0 ? void 0 : modelParams[key]) !== cachedContent[key]) { - if (key === "model") { - const modelParamsComp = modelParams.model.startsWith("models/") ? modelParams.model.replace("models/", "") : modelParams.model; - const cachedContentComp = cachedContent.model.startsWith("models/") ? cachedContent.model.replace("models/", "") : cachedContent.model; - if (modelParamsComp === cachedContentComp) { - continue; - } - } - throw new GoogleGenerativeAIRequestInputError(`Different value for "${key}" specified in modelParams (${modelParams[key]}) and cachedContent (${cachedContent[key]})`); - } - } - const modelParamsFromCache = Object.assign(Object.assign({}, modelParams), { model: cachedContent.model, tools: cachedContent.tools, toolConfig: cachedContent.toolConfig, systemInstruction: cachedContent.systemInstruction, cachedContent }); - return new GenerativeModel(this.apiKey, modelParamsFromCache, requestOptions); - } }; // src/engine/gemini.ts @@ -79747,6 +79272,9 @@ var OllamaEngine = class { options: { temperature: 0, top_p: 0.1 }, stream: false }; + if (typeof this.config.ollamaThink === "boolean") { + params.think = this.config.ollamaThink; + } try { const response = await this.client.post(this.chatUrl, params); const { message } = response.data; @@ -80353,7 +79881,7 @@ var _AbstractPage_client2; async function defaultParseResponse2(props) { const { response } = props; if (props.options.stream) { - debug4("response", response.status, response.url, response.headers, response.body); + debug2("response", response.status, response.url, response.headers, response.body); if (props.options.__streamClass) { return props.options.__streamClass.fromSSEResponse(response, props.controller); } @@ -80369,11 +79897,11 @@ async function defaultParseResponse2(props) { const isJSON = contentType?.includes("application/json") || contentType?.includes("application/vnd.api+json"); if (isJSON) { const json = await response.json(); - debug4("response", response.status, response.url, response.headers, json); + debug2("response", response.status, response.url, response.headers, json); return json; } const text = await response.text(); - debug4("response", response.status, response.url, response.headers, text); + debug2("response", response.status, response.url, response.headers, text); return text; } var APIPromise2 = class _APIPromise extends Promise { @@ -80592,7 +80120,7 @@ var APIClient2 = class { await this.prepareOptions(options); const { req, url: url2, timeout } = this.buildRequest(options); await this.prepareRequest(req, { url: url2, options }); - debug4("request", url2, options, req.headers); + debug2("request", url2, options, req.headers); if (options.signal?.aborted) { throw new APIUserAbortError3(); } @@ -80614,14 +80142,14 @@ var APIClient2 = class { if (!response.ok) { if (retriesRemaining && this.shouldRetry(response)) { const retryMessage2 = `retrying, ${retriesRemaining} attempts remaining`; - debug4(`response (error; ${retryMessage2})`, response.status, url2, responseHeaders); + debug2(`response (error; ${retryMessage2})`, response.status, url2, responseHeaders); return this.retryRequest(options, retriesRemaining, responseHeaders); } const errText = await response.text().catch((e3) => castToError2(e3).message); const errJSON = safeJSON2(errText); const errMessage = errJSON ? void 0 : errText; const retryMessage = retriesRemaining ? `(error; no more retries left)` : `(error; not retryable)`; - debug4(`response (error; ${retryMessage})`, response.status, url2, responseHeaders, errMessage); + debug2(`response (error; ${retryMessage})`, response.status, url2, responseHeaders, errMessage); const err = this.makeStatusError(response.status, errJSON, errMessage, responseHeaders); throw err; } @@ -80985,7 +80513,7 @@ function applyHeadersMut2(targetHeaders, newHeaders) { } } } -function debug4(action, ...args) { +function debug2(action, ...args) { if (typeof process !== "undefined" && process?.env?.["DEBUG"] === "true") { console.log(`OpenAI:DEBUG:${action}`, ...args); } @@ -84462,6 +83990,29 @@ var { OpenAIError: OpenAIError2, APIError: APIError4, APIConnectionError: APICon OpenAI2.Uploads = Uploads; })(OpenAI || (OpenAI = {})); +// src/engine/openAi.ts +var import_https_proxy_agent3 = __toESM(require_dist2(), 1); + +// src/utils/customHeaders.ts +function parseCustomHeaders(headers) { + let parsedHeaders = {}; + if (!headers) { + return parsedHeaders; + } + try { + if (typeof headers === "object" && !Array.isArray(headers)) { + parsedHeaders = headers; + } else { + parsedHeaders = JSON.parse(headers); + } + } catch { + console.warn( + "Invalid OCO_API_CUSTOM_HEADERS format, ignoring custom headers" + ); + } + return parsedHeaders; +} + // src/engine/openAi.ts var OpenAiEngine = class { constructor(config8) { @@ -84497,9 +84048,9 @@ var OpenAiEngine = class { if (config8.baseURL) { clientOptions.baseURL = config8.baseURL; } - const proxy = config8.proxy || process.env.HTTPS_PROXY || process.env.HTTP_PROXY; + const proxy = config8.proxy; if (proxy) { - clientOptions.httpAgent = new HttpsProxyAgent(proxy); + clientOptions.httpAgent = new import_https_proxy_agent3.HttpsProxyAgent(proxy); } if (config8.customHeaders) { const headers = parseCustomHeaders(config8.customHeaders); @@ -84512,7 +84063,7 @@ var OpenAiEngine = class { }; // src/engine/mistral.ts -var Mistral = require_mistralai().Mistral; +var import_mistralai = __toESM(require_mistralai(), 1); var MistralAiEngine = class { // Using any type for Mistral client to avoid TS errors constructor(config8) { @@ -84540,9 +84091,9 @@ var MistralAiEngine = class { }; this.config = config8; if (!config8.baseURL) { - this.client = new Mistral({ apiKey: config8.apiKey }); + this.client = new import_mistralai.Mistral({ apiKey: config8.apiKey }); } else { - this.client = new Mistral({ + this.client = new import_mistralai.Mistral({ apiKey: config8.apiKey, serverURL: config8.baseURL }); @@ -84679,41 +84230,67 @@ var OpenRouterEngine = class { } }; -// src/utils/engine.ts -function parseCustomHeaders(headers) { - let parsedHeaders = {}; - if (!headers) { - return parsedHeaders; +// src/utils/proxy.ts +var import_https_proxy_agent4 = __toESM(require_dist2(), 1); +var import_undici = __toESM(require_undici(), 1); +function resolveProxy(proxySetting) { + if (proxySetting === null) { + return null; + } + if (typeof proxySetting === "string" && proxySetting.trim().length > 0) { + return proxySetting; } + return process.env.HTTPS_PROXY || process.env.HTTP_PROXY; +} +function resetProxySetup(disableEnvProxy) { + (0, import_undici.setGlobalDispatcher)(new import_undici.Agent()); + axios_default.defaults.httpAgent = void 0; + axios_default.defaults.httpsAgent = void 0; + axios_default.defaults.proxy = disableEnvProxy ? false : void 0; +} +function setupProxy(proxySetting) { try { - if (typeof headers === "object" && !Array.isArray(headers)) { - parsedHeaders = headers; - } else { - parsedHeaders = JSON.parse(headers); + if (proxySetting === null) { + resetProxySetup(true); + return; + } + resetProxySetup(false); + if (!proxySetting) { + return; } + const dispatcher = new import_undici.ProxyAgent(proxySetting); + (0, import_undici.setGlobalDispatcher)(dispatcher); + const agent = new import_https_proxy_agent4.HttpsProxyAgent(proxySetting); + axios_default.defaults.httpAgent = agent; + axios_default.defaults.httpsAgent = agent; + axios_default.defaults.proxy = false; } catch (error) { - console.warn( - "Invalid OCO_API_CUSTOM_HEADERS format, ignoring custom headers" - ); + const message = error instanceof Error ? error.message : String(error); + console.warn(`[Proxy Error] Failed to set proxy: ${message}`); } - return parsedHeaders; } + +// src/utils/engine.ts function getEngine() { const config8 = getConfig(); const provider = config8.OCO_AI_PROVIDER; const customHeaders = parseCustomHeaders(config8.OCO_API_CUSTOM_HEADERS); + const resolvedProxy = resolveProxy(config8.OCO_PROXY); const DEFAULT_CONFIG2 = { model: config8.OCO_MODEL, maxTokensOutput: config8.OCO_TOKENS_MAX_OUTPUT, maxTokensInput: config8.OCO_TOKENS_MAX_INPUT, baseURL: config8.OCO_API_URL, - proxy: config8.OCO_PROXY, + proxy: resolvedProxy, apiKey: config8.OCO_API_KEY, customHeaders }; switch (provider) { case "ollama" /* OLLAMA */: - return new OllamaEngine(DEFAULT_CONFIG2); + return new OllamaEngine({ + ...DEFAULT_CONFIG2, + ollamaThink: config8.OCO_OLLAMA_THINK + }); case "anthropic" /* ANTHROPIC */: return new AnthropicEngine(DEFAULT_CONFIG2); case "test" /* TEST */: @@ -85258,16 +84835,9 @@ var generateCommitMessageChatCompletionPrompt = async (diff, fullGitMojiSpec, co }); return chatContextAsCompletionRequest; }; -var GenerateCommitMessageErrorEnum = ((GenerateCommitMessageErrorEnum2) => { - GenerateCommitMessageErrorEnum2["tooMuchTokens"] = "TOO_MUCH_TOKENS"; - GenerateCommitMessageErrorEnum2["internalError"] = "INTERNAL_ERROR"; - GenerateCommitMessageErrorEnum2["emptyMessage"] = "EMPTY_MESSAGE"; - GenerateCommitMessageErrorEnum2["outputTokensTooHigh"] = `Token limit exceeded, OCO_TOKENS_MAX_OUTPUT must not be much higher than the default ${500 /* DEFAULT_MAX_TOKENS_OUTPUT */} tokens.`; - return GenerateCommitMessageErrorEnum2; -})(GenerateCommitMessageErrorEnum || {}); async function handleModelNotFoundError(error, provider, currentModel) { console.log(source_default.red(` -\xD7 Model '${currentModel}' not found +\u2716 Model '${currentModel}' not found `)); const suggestedModels = getSuggestedModels(provider, currentModel); const recommended = RECOMMENDED_MODELS[provider]; @@ -85545,25 +85115,22 @@ var gitAdd = async ({ files }) => { await execa("git", ["add", ...files], { cwd: gitDir }); gitAddSpinner.stop(`Staged ${files.length} files`); }; +var isFileExcludedFromDiff = (file) => file.includes(".lock") || file.includes("-lock.") || file.includes(".svg") || file.includes(".png") || file.includes(".jpg") || file.includes(".jpeg") || file.includes(".webp") || file.includes(".gif"); var getDiff = async ({ files }) => { const gitDir = await getGitDir(); - const lockFiles = files.filter( - (file) => file.includes(".lock") || file.includes("-lock.") || file.includes(".svg") || file.includes(".png") || file.includes(".jpg") || file.includes(".jpeg") || file.includes(".webp") || file.includes(".gif") - ); - if (lockFiles.length) { + const excludedFiles = files.filter(isFileExcludedFromDiff); + if (excludedFiles.length) { ce( `Some files are excluded by default from 'git diff'. No commit messages are generated for this files: -${lockFiles.join( +${excludedFiles.join( "\n" )}` ); } - const filesWithoutLocks = files.filter( - (file) => !file.includes(".lock") && !file.includes("-lock.") - ); + const diffableFiles = files.filter((file) => !isFileExcludedFromDiff(file)); const { stdout: diff } = await execa( "git", - ["diff", "--staged", "--", ...filesWithoutLocks], + ["diff", "--staged", "--", ...diffableFiles], { cwd: gitDir } ); return diff; @@ -85595,7 +85162,12 @@ var getGitRemotes = async () => { }; var hasUpstreamBranch = async () => { try { - await execa("git", ["rev-parse", "--abbrev-ref", "--symbolic-full-name", "@{u}"]); + await execa("git", [ + "rev-parse", + "--abbrev-ref", + "--symbolic-full-name", + "@{u}" + ]); return true; } catch { return false; @@ -85676,7 +85248,7 @@ ${source_default.grey("\u2014\u2014\u2014\u2014\u2014\u2014\u2014\u2014\u2014\u2 ...extraArgs2 ]); committingChangesSpinner.stop( - `${source_default.green("\u221A")} Successfully committed` + `${source_default.green("\u2714")} Successfully committed` ); ce(stdout); const remotes = await getGitRemotes(); @@ -85705,7 +85277,7 @@ ${source_default.grey("\u2014\u2014\u2014\u2014\u2014\u2014\u2014\u2014\u2014\u2 } const { stdout: stdout2, stderr: stderr2 } = await execa("git", pushArgs); pushSpinner.stop( - `${source_default.green("\u221A")} Successfully pushed all commits to ${remotes[0]}` + `${source_default.green("\u2714")} Successfully pushed all commits to ${remotes[0]}` ); if (stdout2) ce(stdout2); displayPushUrl(stderr2); @@ -85734,7 +85306,7 @@ ${source_default.grey("\u2014\u2014\u2014\u2014\u2014\u2014\u2014\u2014\u2014\u2 if (stdout2) ce(stdout2); pushSpinner.stop( `${source_default.green( - "\u221A" + "\u2714" )} successfully pushed all commits to ${selectedRemote}` ); displayPushUrl(stderr2); @@ -85757,11 +85329,13 @@ ${source_default.grey("\u2014\u2014\u2014\u2014\u2014\u2014\u2014\u2014\u2014\u2 } } catch (error) { commitGenerationSpinner.stop( - `${source_default.red("\xD7")} Failed to generate the commit message` + `${source_default.red("\u2716")} Failed to generate the commit message` ); const errorConfig = getConfig(); const provider = errorConfig.OCO_AI_PROVIDER || "openai"; - const formatted = formatUserFriendlyError(error, provider); + const formatted = formatUserFriendlyError(error, provider, { + baseURL: errorConfig.OCO_API_URL + }); ce(printFormattedError(formatted)); process.exit(1); } @@ -85783,7 +85357,7 @@ async function commit(extraArgs2 = [], context = "", isStageAllFlag = false, ful } ae("open-commit"); if (errorChangedFiles ?? errorStagedFiles) { - ce(`${source_default.red("\xD7")} ${errorChangedFiles ?? errorStagedFiles}`); + ce(`${source_default.red("\u2716")} ${errorChangedFiles ?? errorStagedFiles}`); process.exit(1); } const stagedFilesSpinner = le(); @@ -85826,7 +85400,7 @@ ${stagedFiles.map((file) => ` ${file}`).join("\n")}` }) ); if (generateCommitError) { - ce(`${source_default.red("\xD7")} ${generateCommitError}`); + ce(`${source_default.red("\u2716")} ${generateCommitError}`); process.exit(1); } process.exit(0); @@ -86000,23 +85574,6 @@ ${fileContent.toString()}`; } }; -// src/utils/proxy.ts -var import_undici = __toESM(require_undici(), 1); -function setupProxy(proxyUrl) { - const proxy = proxyUrl || process.env.HTTPS_PROXY || process.env.HTTP_PROXY; - if (proxy) { - try { - const dispatcher = new import_undici.ProxyAgent(proxy); - (0, import_undici.setGlobalDispatcher)(dispatcher); - const agent = new HttpsProxyAgent(proxy); - axios_default.defaults.httpsAgent = agent; - axios_default.defaults.proxy = false; - } catch (error) { - console.warn(`[Proxy Error] Failed to set proxy: ${error.message}`); - } - } -} - // src/commands/setup.ts init_dist2(); @@ -86585,15 +86142,16 @@ async function runSetup() { return true; } function isFirstRun() { + const hasGlobalConfig = getIsGlobalConfigFileExist(); const config8 = getConfig(); const provider = config8.OCO_AI_PROVIDER || "openai" /* OPENAI */; - if (MODEL_REQUIRED_PROVIDERS.includes(provider)) { - return !config8.OCO_MODEL; - } if (provider === "test" /* TEST */) { return false; } - return !config8.OCO_API_KEY; + const hasRequiredConfig = MODEL_REQUIRED_PROVIDERS.includes( + provider + ) ? Boolean(config8.OCO_MODEL) : Boolean(config8.OCO_API_KEY); + return !hasGlobalConfig && !hasRequiredConfig; } async function promptForMissingApiKey() { const config8 = getConfig(); @@ -86771,6 +86329,9 @@ var getOpenCommitLatestVersion = async () => { // src/utils/checkIsLatestVersion.ts var checkIsLatestVersion = async () => { + if (process.env.OCO_TEST_SKIP_VERSION_CHECK === "true") { + return; + } const latestVersion = await getOpenCommitLatestVersion(); if (latestVersion) { const currentVersion = package_default.version; @@ -86941,15 +86502,8 @@ var runMigrations = async () => { }; // src/cli.ts -var __origEmit = process.emit.bind(process); -process.emit = function(name, ...args) { - if (name === "warning" && args[0]?.code === "DEP0169") { - return false; - } - return __origEmit(name, ...args); -}; var config7 = getConfig(); -setupProxy(config7.OCO_PROXY); +setupProxy(resolveProxy(config7.OCO_PROXY)); var OCO_FLAGS_WITH_VALUE = /* @__PURE__ */ new Set(["-c", "--context"]); var OCO_BOOLEAN_FLAGS = /* @__PURE__ */ new Set(["-y", "--yes", "--fgm"]); var OCO_EQUALS_PREFIXES = ["-c=", "--context=", "-y=", "--yes=", "--fgm="]; diff --git a/package.json b/package.json index f282ced2..94bbf1bf 100644 --- a/package.json +++ b/package.json @@ -57,8 +57,15 @@ "test:docker-build": "docker build -t oco-test -f test/Dockerfile .", "test:unit": "NODE_OPTIONS=--experimental-vm-modules jest test/unit", "test:unit:docker": "npm run test:docker-build && DOCKER_CONTENT_TRUST=0 docker run --rm oco-test npm run test:unit", - "test:e2e": "npm run test:e2e:setup && jest test/e2e", - "test:e2e:setup": "sh test/e2e/setup.sh", + "test:e2e": "npm run build && npm run test:e2e:smoke:run && npm run test:e2e:core:run && npm run test:e2e:prompt-module:run", + "test:e2e:smoke": "npm run build && npm run test:e2e:smoke:run", + "test:e2e:smoke:run": "OCO_TEST_SKIP_VERSION_CHECK=true jest test/e2e/smoke.test.ts", + "test:e2e:core": "npm run build && npm run test:e2e:core:run", + "test:e2e:core:run": "OCO_TEST_SKIP_VERSION_CHECK=true jest test/e2e/cliBehavior.test.ts test/e2e/gitPush.test.ts test/e2e/oneFile.test.ts test/e2e/noChanges.test.ts", + "test:e2e:setup": "npm run test:e2e:prompt-module:setup", + "test:e2e:prompt-module:setup": "sh test/e2e/setup.sh", + "test:e2e:prompt-module": "npm run build && npm run test:e2e:prompt-module:run", + "test:e2e:prompt-module:run": "npm run test:e2e:prompt-module:setup && OCO_TEST_SKIP_VERSION_CHECK=true jest test/e2e/prompt-module", "test:e2e:docker": "npm run test:docker-build && DOCKER_CONTENT_TRUST=0 docker run --rm oco-test npm run test:e2e", "mlx:start": "OCO_AI_PROVIDER='mlx' node ./out/cli.cjs" }, diff --git a/src/cli.ts b/src/cli.ts index 80b1e227..05221298 100755 --- a/src/cli.ts +++ b/src/cli.ts @@ -8,7 +8,7 @@ import { commitlintConfigCommand } from './commands/commitlint'; import { configCommand, getConfig } from './commands/config'; import { hookCommand, isHookCalled } from './commands/githook.js'; import { prepareCommitMessageHook } from './commands/prepare-commit-msg-hook'; -import { setupProxy } from './utils/proxy'; +import { resolveProxy, setupProxy } from './utils/proxy'; import { setupCommand, isFirstRun, @@ -20,7 +20,7 @@ import { checkIsLatestVersion } from './utils/checkIsLatestVersion'; import { runMigrations } from './migrations/_run.js'; const config = getConfig(); -setupProxy(config.OCO_PROXY); +setupProxy(resolveProxy(config.OCO_PROXY)); const OCO_FLAGS_WITH_VALUE = new Set(['-c', '--context']); const OCO_BOOLEAN_FLAGS = new Set(['-y', '--yes', '--fgm']); diff --git a/src/commands/commit.ts b/src/commands/commit.ts index 98047d90..9618cef5 100644 --- a/src/commands/commit.ts +++ b/src/commands/commit.ts @@ -31,7 +31,12 @@ const getGitRemotes = async () => { const hasUpstreamBranch = async (): Promise => { try { - await execa('git', ['rev-parse', '--abbrev-ref', '--symbolic-full-name', '@{u}']); + await execa('git', [ + 'rev-parse', + '--abbrev-ref', + '--symbolic-full-name', + '@{u}' + ]); return true; } catch { return false; @@ -249,7 +254,9 @@ ${chalk.grey('——————————————————')}` const errorConfig = getConfig(); const provider = errorConfig.OCO_AI_PROVIDER || 'openai'; - const formatted = formatUserFriendlyError(error, provider); + const formatted = formatUserFriendlyError(error, provider, { + baseURL: errorConfig.OCO_API_URL + }); outro(printFormattedError(formatted)); process.exit(1); diff --git a/src/commands/config.ts b/src/commands/config.ts index 95fc8d7a..f95e1f39 100644 --- a/src/commands/config.ts +++ b/src/commands/config.ts @@ -723,7 +723,7 @@ export const configValidators = { [CONFIG_KEYS.OCO_API_URL](value: any) { validateConfig( CONFIG_KEYS.OCO_API_URL, - typeof value === 'string', + typeof value === 'string' && /^(https?:\/\/)/.test(value), `${value} is not a valid URL. It should start with 'http://' or 'https://'.` ); return value; @@ -732,7 +732,8 @@ export const configValidators = { [CONFIG_KEYS.OCO_PROXY](value: any) { validateConfig( CONFIG_KEYS.OCO_PROXY, - typeof value === 'string', + value === null || + (typeof value === 'string' && /^(https?:\/\/)/.test(value)), `${value} is not a valid URL. It should start with 'http://' or 'https://'.` ); return value; @@ -900,7 +901,7 @@ export type ConfigType = { [CONFIG_KEYS.OCO_TOKENS_MAX_INPUT]: number; [CONFIG_KEYS.OCO_TOKENS_MAX_OUTPUT]: number; [CONFIG_KEYS.OCO_API_URL]?: string; - [CONFIG_KEYS.OCO_PROXY]?: string; + [CONFIG_KEYS.OCO_PROXY]?: string | null; [CONFIG_KEYS.OCO_API_CUSTOM_HEADERS]?: string; [CONFIG_KEYS.OCO_DESCRIPTION]: boolean; [CONFIG_KEYS.OCO_EMOJI]: boolean; @@ -986,10 +987,7 @@ const getEnvConfig = (envPath: string) => { return { OCO_MODEL: process.env.OCO_MODEL, OCO_API_URL: process.env.OCO_API_URL, - OCO_PROXY: - process.env.OCO_PROXY || - process.env.HTTPS_PROXY || - process.env.HTTP_PROXY, + OCO_PROXY: process.env.OCO_PROXY, OCO_API_KEY: process.env.OCO_API_KEY, OCO_API_CUSTOM_HEADERS: process.env.OCO_API_CUSTOM_HEADERS, OCO_AI_PROVIDER: process.env.OCO_AI_PROVIDER as OCO_AI_PROVIDER_ENUM, @@ -1027,16 +1025,13 @@ export const getIsGlobalConfigFileExist = ( }; export const getGlobalConfig = (configPath: string = defaultConfigPath) => { - let globalConfig: ConfigType; - const isGlobalConfigFileExist = getIsGlobalConfigFileExist(configPath); - if (!isGlobalConfigFileExist) globalConfig = initGlobalConfig(configPath); - else { - const configFile = readFileSync(configPath, 'utf8'); - globalConfig = iniParse(configFile) as ConfigType; + if (!isGlobalConfigFileExist) { + return { ...DEFAULT_CONFIG }; } - return globalConfig; + const configFile = readFileSync(configPath, 'utf8'); + return iniParse(configFile) as ConfigType; }; /** @@ -1049,7 +1044,10 @@ export const getGlobalConfig = (configPath: string = defaultConfigPath) => { const mergeConfigs = (main: Partial, fallback: ConfigType) => { const allKeys = new Set([...Object.keys(main), ...Object.keys(fallback)]); return Array.from(allKeys).reduce((acc, key) => { - acc[key] = parseConfigVarValue(main[key] ?? fallback[key]); + const mainValue = main[key]; + acc[key] = parseConfigVarValue( + mainValue !== undefined ? mainValue : fallback[key] + ); return acc; }, {} as ConfigType); }; @@ -1218,7 +1216,10 @@ function getConfigKeyDetails(key) { case CONFIG_KEYS.OCO_PROXY: return { description: 'HTTP/HTTPS Proxy URL', - values: ["URL string (must start with 'http://' or 'https://')"] + values: [ + "URL string (must start with 'http://' or 'https://')", + 'null (disable proxy even when HTTP_PROXY/HTTPS_PROXY are set)' + ] }; case CONFIG_KEYS.OCO_MESSAGE_TEMPLATE_PLACEHOLDER: return { diff --git a/src/commands/setup.ts b/src/commands/setup.ts index 7f044e51..28d40295 100644 --- a/src/commands/setup.ts +++ b/src/commands/setup.ts @@ -427,22 +427,23 @@ export async function runSetup(): Promise { } export function isFirstRun(): boolean { + const hasGlobalConfig = getIsGlobalConfigFileExist(); const config = getConfig(); - // Check if API key is missing for providers that need it const provider = config.OCO_AI_PROVIDER || OCO_AI_PROVIDER_ENUM.OPENAI; - if (MODEL_REQUIRED_PROVIDERS.includes(provider as OCO_AI_PROVIDER_ENUM)) { - // For Ollama/MLX, check if model is set - return !config.OCO_MODEL; - } - if (provider === OCO_AI_PROVIDER_ENUM.TEST) { return false; } - // For other providers, check if API key is set - return !config.OCO_API_KEY; + const hasRequiredConfig = MODEL_REQUIRED_PROVIDERS.includes( + provider as OCO_AI_PROVIDER_ENUM + ) + ? Boolean(config.OCO_MODEL) + : Boolean(config.OCO_API_KEY); + + // Trigger the full setup wizard only when nothing usable was configured yet. + return !hasGlobalConfig && !hasRequiredConfig; } export async function promptForMissingApiKey(): Promise { diff --git a/src/engine/Engine.ts b/src/engine/Engine.ts index 0ddaa8d2..95fbc5d9 100644 --- a/src/engine/Engine.ts +++ b/src/engine/Engine.ts @@ -11,7 +11,7 @@ export interface AiEngineConfig { maxTokensOutput: number; maxTokensInput: number; baseURL?: string; - proxy?: string; + proxy?: string | null; customHeaders?: Record; ollamaThink?: boolean; } diff --git a/src/engine/anthropic.ts b/src/engine/anthropic.ts index 0ceee8d8..3716a24e 100644 --- a/src/engine/anthropic.ts +++ b/src/engine/anthropic.ts @@ -5,8 +5,8 @@ import { MessageParam } from '@anthropic-ai/sdk/resources/messages.mjs'; import { OpenAI } from 'openai'; -import { GenerateCommitMessageErrorEnum } from '../generateCommitMessageFromGitDiff'; import { normalizeEngineError } from '../utils/engineErrorHandler'; +import { GenerateCommitMessageErrorEnum } from '../utils/generateCommitMessageErrors'; import { removeContentTags } from '../utils/removeContentTags'; import { tokenCount } from '../utils/tokenCount'; import { AiEngine, AiEngineConfig } from './Engine'; @@ -21,8 +21,7 @@ export class AnthropicEngine implements AiEngine { this.config = config; const clientOptions: any = { apiKey: this.config.apiKey }; - const proxy = - config.proxy || process.env.HTTPS_PROXY || process.env.HTTP_PROXY; + const proxy = config.proxy; if (proxy) { clientOptions.httpAgent = new HttpsProxyAgent(proxy); } diff --git a/src/engine/azure.ts b/src/engine/azure.ts index d7eec216..b9bf2811 100644 --- a/src/engine/azure.ts +++ b/src/engine/azure.ts @@ -3,8 +3,8 @@ import { OpenAIClient as AzureOpenAIClient } from '@azure/openai'; import { OpenAI } from 'openai'; -import { GenerateCommitMessageErrorEnum } from '../generateCommitMessageFromGitDiff'; import { normalizeEngineError } from '../utils/engineErrorHandler'; +import { GenerateCommitMessageErrorEnum } from '../utils/generateCommitMessageErrors'; import { removeContentTags } from '../utils/removeContentTags'; import { tokenCount } from '../utils/tokenCount'; import { AiEngine, AiEngineConfig } from './Engine'; diff --git a/src/engine/deepseek.ts b/src/engine/deepseek.ts index c1794d3d..cec122b9 100644 --- a/src/engine/deepseek.ts +++ b/src/engine/deepseek.ts @@ -1,6 +1,6 @@ import { OpenAI } from 'openai'; -import { GenerateCommitMessageErrorEnum } from '../generateCommitMessageFromGitDiff'; import { normalizeEngineError } from '../utils/engineErrorHandler'; +import { GenerateCommitMessageErrorEnum } from '../utils/generateCommitMessageErrors'; import { removeContentTags } from '../utils/removeContentTags'; import { tokenCount } from '../utils/tokenCount'; import { OpenAiEngine, OpenAiConfig } from './openAi'; diff --git a/src/engine/mistral.ts b/src/engine/mistral.ts index ebdfa54f..e47bc8f5 100644 --- a/src/engine/mistral.ts +++ b/src/engine/mistral.ts @@ -1,7 +1,7 @@ +import { Mistral } from '@mistralai/mistralai'; import { OpenAI } from 'openai'; -import { HttpsProxyAgent } from 'https-proxy-agent'; -import { GenerateCommitMessageErrorEnum } from '../generateCommitMessageFromGitDiff'; import { normalizeEngineError } from '../utils/engineErrorHandler'; +import { GenerateCommitMessageErrorEnum } from '../utils/generateCommitMessageErrors'; import { removeContentTags } from '../utils/removeContentTags'; import { tokenCount } from '../utils/tokenCount'; import { AiEngine, AiEngineConfig } from './Engine'; @@ -10,10 +10,6 @@ import { AiEngine, AiEngineConfig } from './Engine'; export interface MistralAiConfig extends AiEngineConfig {} export type MistralCompletionMessageParam = Array; -// Import Mistral dynamically to avoid TS errors -// eslint-disable-next-line @typescript-eslint/no-var-requires -const Mistral = require('@mistralai/mistralai').Mistral; - export class MistralAiEngine implements AiEngine { config: MistralAiConfig; client: any; // Using any type for Mistral client to avoid TS errors diff --git a/src/engine/openAi.ts b/src/engine/openAi.ts index 3c407c32..f057e4c7 100644 --- a/src/engine/openAi.ts +++ b/src/engine/openAi.ts @@ -1,8 +1,8 @@ import { OpenAI } from 'openai'; import { HttpsProxyAgent } from 'https-proxy-agent'; -import { GenerateCommitMessageErrorEnum } from '../generateCommitMessageFromGitDiff'; -import { parseCustomHeaders } from '../utils/engine'; +import { parseCustomHeaders } from '../utils/customHeaders'; import { normalizeEngineError } from '../utils/engineErrorHandler'; +import { GenerateCommitMessageErrorEnum } from '../utils/generateCommitMessageErrors'; import { removeContentTags } from '../utils/removeContentTags'; import { tokenCount } from '../utils/tokenCount'; import { AiEngine, AiEngineConfig } from './Engine'; @@ -24,8 +24,7 @@ export class OpenAiEngine implements AiEngine { clientOptions.baseURL = config.baseURL; } - const proxy = - config.proxy || process.env.HTTPS_PROXY || process.env.HTTP_PROXY; + const proxy = config.proxy; if (proxy) { clientOptions.httpAgent = new HttpsProxyAgent(proxy); } diff --git a/src/generateCommitMessageFromGitDiff.ts b/src/generateCommitMessageFromGitDiff.ts index 9e5f071c..2f1f7c6e 100644 --- a/src/generateCommitMessageFromGitDiff.ts +++ b/src/generateCommitMessageFromGitDiff.ts @@ -16,6 +16,7 @@ import { getSuggestedModels, ModelNotFoundError } from './utils/errors'; +import { GenerateCommitMessageErrorEnum } from './utils/generateCommitMessageErrors'; import { mergeDiffs } from './utils/mergeDiffs'; import { tokenCount } from './utils/tokenCount'; @@ -43,13 +44,6 @@ const generateCommitMessageChatCompletionPrompt = async ( return chatContextAsCompletionRequest; }; -export enum GenerateCommitMessageErrorEnum { - tooMuchTokens = 'TOO_MUCH_TOKENS', - internalError = 'INTERNAL_ERROR', - emptyMessage = 'EMPTY_MESSAGE', - outputTokensTooHigh = `Token limit exceeded, OCO_TOKENS_MAX_OUTPUT must not be much higher than the default ${DEFAULT_TOKEN_LIMITS.DEFAULT_MAX_TOKENS_OUTPUT} tokens.` -} - async function handleModelNotFoundError( error: Error, provider: string, diff --git a/src/prompts.ts b/src/prompts.ts index 8d831ad3..0f2f7281 100644 --- a/src/prompts.ts +++ b/src/prompts.ts @@ -98,8 +98,8 @@ const getCommitConvention = (fullGitMojiSpec: boolean) => fullGitMojiSpec ? FULL_GITMOJI_SPEC : config.OCO_EMOJI - ? GITMOJI_HELP - : CONVENTIONAL_COMMIT_KEYWORDS; + ? GITMOJI_HELP + : CONVENTIONAL_COMMIT_KEYWORDS; const getDescriptionInstruction = () => config.OCO_DESCRIPTION diff --git a/src/utils/checkIsLatestVersion.ts b/src/utils/checkIsLatestVersion.ts index f4601a1f..d8c0435d 100644 --- a/src/utils/checkIsLatestVersion.ts +++ b/src/utils/checkIsLatestVersion.ts @@ -6,6 +6,10 @@ import currentPackage from '../../package.json'; import { getOpenCommitLatestVersion } from '../version'; export const checkIsLatestVersion = async () => { + if (process.env.OCO_TEST_SKIP_VERSION_CHECK === 'true') { + return; + } + const latestVersion = await getOpenCommitLatestVersion(); if (latestVersion) { diff --git a/src/utils/customHeaders.ts b/src/utils/customHeaders.ts new file mode 100644 index 00000000..6ec86e3d --- /dev/null +++ b/src/utils/customHeaders.ts @@ -0,0 +1,21 @@ +export function parseCustomHeaders(headers: any): Record { + let parsedHeaders = {}; + + if (!headers) { + return parsedHeaders; + } + + try { + if (typeof headers === 'object' && !Array.isArray(headers)) { + parsedHeaders = headers; + } else { + parsedHeaders = JSON.parse(headers); + } + } catch { + console.warn( + 'Invalid OCO_API_CUSTOM_HEADERS format, ignoring custom headers' + ); + } + + return parsedHeaders; +} diff --git a/src/utils/engine.ts b/src/utils/engine.ts index 436f7a11..0a07fe4c 100644 --- a/src/utils/engine.ts +++ b/src/utils/engine.ts @@ -13,41 +13,22 @@ import { MLXEngine } from '../engine/mlx'; import { DeepseekEngine } from '../engine/deepseek'; import { AimlApiEngine } from '../engine/aimlapi'; import { OpenRouterEngine } from '../engine/openrouter'; - -export function parseCustomHeaders(headers: any): Record { - let parsedHeaders = {}; - - if (!headers) { - return parsedHeaders; - } - - try { - if (typeof headers === 'object' && !Array.isArray(headers)) { - parsedHeaders = headers; - } else { - parsedHeaders = JSON.parse(headers); - } - } catch (error) { - console.warn( - 'Invalid OCO_API_CUSTOM_HEADERS format, ignoring custom headers' - ); - } - - return parsedHeaders; -} +import { parseCustomHeaders } from './customHeaders'; +import { resolveProxy } from './proxy'; export function getEngine(): AiEngine { const config = getConfig(); const provider = config.OCO_AI_PROVIDER; const customHeaders = parseCustomHeaders(config.OCO_API_CUSTOM_HEADERS); + const resolvedProxy = resolveProxy(config.OCO_PROXY); const DEFAULT_CONFIG = { model: config.OCO_MODEL!, maxTokensOutput: config.OCO_TOKENS_MAX_OUTPUT!, maxTokensInput: config.OCO_TOKENS_MAX_INPUT!, baseURL: config.OCO_API_URL!, - proxy: config.OCO_PROXY!, + proxy: resolvedProxy, apiKey: config.OCO_API_KEY!, customHeaders }; diff --git a/src/utils/errors.ts b/src/utils/errors.ts index 1fd9887d..ae988e3f 100644 --- a/src/utils/errors.ts +++ b/src/utils/errors.ts @@ -349,10 +349,44 @@ export interface FormattedError { suggestion: string | null; } +export interface ErrorFormattingContext { + baseURL?: string; +} + +function getCustomEndpointLabel(baseURL?: string): string | null { + if (!baseURL) { + return null; + } + + try { + return new URL(baseURL).host; + } catch { + return null; + } +} + +function getServiceUnavailableMessage( + provider: string, + context?: ErrorFormattingContext +): string { + const endpointLabel = getCustomEndpointLabel(context?.baseURL); + + if (endpointLabel) { + return `The configured API endpoint (${endpointLabel}) is temporarily unavailable.`; + } + + if (context?.baseURL) { + return 'The configured API endpoint is temporarily unavailable.'; + } + + return `The ${provider} service is temporarily unavailable.`; +} + // Format an error into a user-friendly structure export function formatUserFriendlyError( error: unknown, - provider: string + provider: string, + context?: ErrorFormattingContext ): FormattedError { const billingUrl = PROVIDER_BILLING_URLS[provider] || null; @@ -381,7 +415,7 @@ export function formatUserFriendlyError( if (error instanceof ServiceUnavailableError) { return { title: 'Service Unavailable', - message: `The ${provider} service is temporarily unavailable.`, + message: getServiceUnavailableMessage(provider, context), helpUrl: null, suggestion: 'Please try again in a few moments.' }; @@ -427,7 +461,7 @@ export function formatUserFriendlyError( if (isServiceUnavailableError(error)) { return { title: 'Service Unavailable', - message: `The ${provider} service is temporarily unavailable.`, + message: getServiceUnavailableMessage(provider, context), helpUrl: null, suggestion: 'Please try again in a few moments.' }; diff --git a/src/utils/generateCommitMessageErrors.ts b/src/utils/generateCommitMessageErrors.ts new file mode 100644 index 00000000..2400b0e7 --- /dev/null +++ b/src/utils/generateCommitMessageErrors.ts @@ -0,0 +1,8 @@ +import { DEFAULT_TOKEN_LIMITS } from '../commands/config'; + +export enum GenerateCommitMessageErrorEnum { + tooMuchTokens = 'TOO_MUCH_TOKENS', + internalError = 'INTERNAL_ERROR', + emptyMessage = 'EMPTY_MESSAGE', + outputTokensTooHigh = `Token limit exceeded, OCO_TOKENS_MAX_OUTPUT must not be much higher than the default ${DEFAULT_TOKEN_LIMITS.DEFAULT_MAX_TOKENS_OUTPUT} tokens.` +} diff --git a/src/utils/git.ts b/src/utils/git.ts index 902d61a8..c8341517 100644 --- a/src/utils/git.ts +++ b/src/utils/git.ts @@ -93,36 +93,34 @@ export const gitAdd = async ({ files }: { files: string[] }) => { gitAddSpinner.stop(`Staged ${files.length} files`); }; +const isFileExcludedFromDiff = (file: string) => + file.includes('.lock') || + file.includes('-lock.') || + file.includes('.svg') || + file.includes('.png') || + file.includes('.jpg') || + file.includes('.jpeg') || + file.includes('.webp') || + file.includes('.gif'); + export const getDiff = async ({ files }: { files: string[] }) => { const gitDir = await getGitDir(); - const lockFiles = files.filter( - (file) => - file.includes('.lock') || - file.includes('-lock.') || - file.includes('.svg') || - file.includes('.png') || - file.includes('.jpg') || - file.includes('.jpeg') || - file.includes('.webp') || - file.includes('.gif') - ); + const excludedFiles = files.filter(isFileExcludedFromDiff); - if (lockFiles.length) { + if (excludedFiles.length) { outro( - `Some files are excluded by default from 'git diff'. No commit messages are generated for this files:\n${lockFiles.join( + `Some files are excluded by default from 'git diff'. No commit messages are generated for this files:\n${excludedFiles.join( '\n' )}` ); } - const filesWithoutLocks = files.filter( - (file) => !file.includes('.lock') && !file.includes('-lock.') - ); + const diffableFiles = files.filter((file) => !isFileExcludedFromDiff(file)); const { stdout: diff } = await execa( 'git', - ['diff', '--staged', '--', ...filesWithoutLocks], + ['diff', '--staged', '--', ...diffableFiles], { cwd: gitDir } ); diff --git a/src/utils/proxy.ts b/src/utils/proxy.ts index dea840a5..c0b58439 100644 --- a/src/utils/proxy.ts +++ b/src/utils/proxy.ts @@ -1,21 +1,52 @@ -import { setGlobalDispatcher, ProxyAgent } from 'undici'; import axios from 'axios'; import { HttpsProxyAgent } from 'https-proxy-agent'; +import { Agent, ProxyAgent, setGlobalDispatcher } from 'undici'; -export function setupProxy(proxyUrl?: string) { - const proxy = proxyUrl || process.env.HTTPS_PROXY || process.env.HTTP_PROXY; - if (proxy) { - try { - // Set global dispatcher for undici (affects globalThis.fetch used by Gemini and others) - const dispatcher = new ProxyAgent(proxy); - setGlobalDispatcher(dispatcher); - - // Set axios global agent - const agent = new HttpsProxyAgent(proxy); - axios.defaults.httpsAgent = agent; - axios.defaults.proxy = false; // Disable axios built-in proxy handling to use agent - } catch (error) { - console.warn(`[Proxy Error] Failed to set proxy: ${error.message}`); +export type ProxySetting = string | null | undefined; + +export function resolveProxy(proxySetting?: ProxySetting): ProxySetting { + if (proxySetting === null) { + return null; + } + + if (typeof proxySetting === 'string' && proxySetting.trim().length > 0) { + return proxySetting; + } + + return process.env.HTTPS_PROXY || process.env.HTTP_PROXY; +} + +function resetProxySetup(disableEnvProxy: boolean) { + setGlobalDispatcher(new Agent()); + axios.defaults.httpAgent = undefined; + axios.defaults.httpsAgent = undefined; + axios.defaults.proxy = disableEnvProxy ? false : undefined; +} + +export function setupProxy(proxySetting?: ProxySetting) { + try { + if (proxySetting === null) { + resetProxySetup(true); + return; } + + resetProxySetup(false); + + if (!proxySetting) { + return; + } + + // Set global dispatcher for undici (affects globalThis.fetch used by Gemini and others) + const dispatcher = new ProxyAgent(proxySetting); + setGlobalDispatcher(dispatcher); + + // Set axios global agents and disable axios built-in proxy handling. + const agent = new HttpsProxyAgent(proxySetting); + axios.defaults.httpAgent = agent; + axios.defaults.httpsAgent = agent; + axios.defaults.proxy = false; + } catch (error) { + const message = error instanceof Error ? error.message : String(error); + console.warn(`[Proxy Error] Failed to set proxy: ${message}`); } } diff --git a/test/e2e/cliBehavior.test.ts b/test/e2e/cliBehavior.test.ts new file mode 100644 index 00000000..f1d6d8e4 --- /dev/null +++ b/test/e2e/cliBehavior.test.ts @@ -0,0 +1,736 @@ +import { + existsSync, + lstatSync, + readFileSync, + realpathSync, + rmSync, + writeFileSync +} from 'fs'; +import { resolve } from 'path'; +import 'cli-testing-library/extend-expect'; +import { + assertGitStatus, + assertHeadCommit, + getHeadCommitFiles, + getMockOpenAiEnv, + prepareEnvironment, + prepareRepo, + prepareTempDir, + runCli, + runGit, + runProcess, + seedMigrations, + seedModelCache, + startMockOpenAiServer, + waitForExit, + writeGlobalConfig, + writeRepoFile +} from './utils'; + +it('cli flow passes --context through to the model prompt and skips confirmation with --yes', async () => { + const { gitDir, cleanup } = await prepareEnvironment(); + const server = await startMockOpenAiServer( + 'fix(context): handle production incident' + ); + + try { + await prepareRepo( + gitDir, + { + 'index.ts': 'console.log("Hello World");\n' + }, + { stage: true } + ); + + const oco = await runCli(['--yes', '--context=production-incident'], { + cwd: gitDir, + env: getMockOpenAiEnv(server.baseUrl) + }); + + expect( + await oco.queryByText('Confirm the commit message?') + ).not.toBeInTheConsole(); + expect( + await oco.queryByText('Do you want to run `git push`?') + ).not.toBeInTheConsole(); + expect(await oco.findByText('Successfully committed')).toBeInTheConsole(); + expect(await waitForExit(oco)).toBe(0); + await assertHeadCommit(gitDir, 'fix(context): handle production incident'); + + const requestPayload = server.requestBodies[ + server.requestBodies.length - 1 + ] as { messages: Array<{ content: string }> }; + const requestContents = requestPayload.messages + .map((message) => message.content) + .join('\n'); + + expect(requestContents).toContain( + 'production-incident' + ); + expect(requestContents).toContain('console.log("Hello World");'); + expect(server.authHeaders).toContain('Bearer test-openai-key'); + } finally { + await server.cleanup(); + await cleanup(); + } +}); + +it('cli flow passes --fgm through to the full GitMoji prompt', async () => { + const { gitDir, cleanup } = await prepareEnvironment(); + const server = await startMockOpenAiServer( + 'feat(fgm): use the extended gitmoji specification' + ); + + try { + await prepareRepo( + gitDir, + { + 'index.ts': 'console.log("Hello World");\n' + }, + { stage: true } + ); + + const oco = await runCli(['--fgm', '--yes'], { + cwd: gitDir, + env: getMockOpenAiEnv(server.baseUrl) + }); + + expect(await oco.findByText('Successfully committed')).toBeInTheConsole(); + expect(await waitForExit(oco)).toBe(0); + await assertHeadCommit( + gitDir, + 'feat(fgm): use the extended gitmoji specification' + ); + + const requestPayload = server.requestBodies[ + server.requestBodies.length - 1 + ] as { messages: Array<{ content: string }> }; + const requestContents = requestPayload.messages + .map((message) => message.content) + .join('\n'); + + expect(requestContents).toContain( + '🎨, Improve structure / format of the code;' + ); + expect(requestContents).toContain('GitMoji specification'); + } finally { + await server.cleanup(); + await cleanup(); + } +}); + +it('cli flow allows editing the generated commit message before committing', async () => { + const { gitDir, cleanup } = await prepareEnvironment(); + const server = await startMockOpenAiServer( + 'fix(cli): allow editing the generated message' + ); + + try { + await prepareRepo( + gitDir, + { + 'index.ts': 'console.log("Hello World");\n' + }, + { stage: true } + ); + + const oco = await runCli([], { + cwd: gitDir, + env: getMockOpenAiEnv(server.baseUrl) + }); + + expect(await oco.findByText('Confirm the commit message?')).toBeInTheConsole(); + oco.userEvent.keyboard('[ArrowDown][ArrowDown][Enter]'); + + expect( + await oco.findByText( + 'Please edit the commit message: (press Enter to continue)' + ) + ).toBeInTheConsole(); + oco.userEvent.keyboard(' before commit[Enter]'); + + expect(await oco.findByText('Successfully committed')).toBeInTheConsole(); + expect(await waitForExit(oco)).toBe(0); + await assertHeadCommit( + gitDir, + 'fix(cli): allow editing the generated message before commit' + ); + } finally { + await server.cleanup(); + await cleanup(); + } +}); + +it('cli flow regenerates the message when the user rejects the first suggestion', async () => { + const { gitDir, cleanup } = await prepareEnvironment(); + const server = await startMockOpenAiServer(({ requestIndex }) => ({ + body: { + choices: [ + { + message: { + content: + requestIndex === 0 + ? 'fix(cli): first generated message' + : 'fix(cli): regenerated message after retry' + } + } + ] + } + })); + + try { + await prepareRepo( + gitDir, + { + 'index.ts': 'console.log("Hello World");\n' + }, + { stage: true } + ); + + const oco = await runCli([], { + cwd: gitDir, + env: getMockOpenAiEnv(server.baseUrl) + }); + + expect(await oco.findByText('Confirm the commit message?')).toBeInTheConsole(); + oco.userEvent.keyboard('[ArrowDown][Enter]'); + + expect( + await oco.findByText('Do you want to regenerate the message?') + ).toBeInTheConsole(); + oco.userEvent.keyboard('[Enter]'); + + oco.clear(); + expect( + await oco.findByText('fix(cli): regenerated message after retry') + ).toBeInTheConsole(); + expect(await oco.findByText('Confirm the commit message?')).toBeInTheConsole(); + oco.userEvent.keyboard('[Enter]'); + + expect(await oco.findByText('Successfully committed')).toBeInTheConsole(); + expect(await waitForExit(oco)).toBe(0); + await assertHeadCommit( + gitDir, + 'fix(cli): regenerated message after retry' + ); + expect(server.requestBodies).toHaveLength(2); + } finally { + await server.cleanup(); + await cleanup(); + } +}); + +it('cli flow lets the user select only specific unstaged files', async () => { + const { gitDir, cleanup } = await prepareEnvironment(); + const server = await startMockOpenAiServer( + 'fix(cli): commit only the selected files' + ); + + try { + await prepareRepo(gitDir, { + 'alpha.ts': 'console.log("alpha");\n', + 'beta.ts': 'console.log("beta");\n' + }); + + const oco = await runCli([], { + cwd: gitDir, + env: getMockOpenAiEnv(server.baseUrl) + }); + + expect(await oco.findByText('No files are staged')).toBeInTheConsole(); + expect( + await oco.findByText( + 'Do you want to stage all files and generate commit message?' + ) + ).toBeInTheConsole(); + oco.userEvent.keyboard('[ArrowDown][Enter]'); + + expect( + await oco.findByText('Select the files you want to add to the commit:') + ).toBeInTheConsole(); + oco.userEvent.keyboard('[Space][Enter]'); + + expect(await oco.findByText('Confirm the commit message?')).toBeInTheConsole(); + oco.userEvent.keyboard('[Enter]'); + + expect(await oco.findByText('Successfully committed')).toBeInTheConsole(); + expect(await waitForExit(oco)).toBe(0); + expect(await getHeadCommitFiles(gitDir)).toEqual(['alpha.ts']); + await assertGitStatus(gitDir, '?? beta.ts'); + } finally { + await server.cleanup(); + await cleanup(); + } +}); + +it('cli applies the documented message template placeholder from extra args', async () => { + const { gitDir, cleanup } = await prepareEnvironment(); + const server = await startMockOpenAiServer( + 'feat(template): keep generated subject' + ); + + try { + await prepareRepo( + gitDir, + { + 'index.ts': 'console.log("Hello World");\n' + }, + { stage: true } + ); + + const oco = await runCli(["'$msg #205'"], { + cwd: gitDir, + env: getMockOpenAiEnv(server.baseUrl) + }); + + expect(await oco.findByText('Confirm the commit message?')).toBeInTheConsole(); + oco.userEvent.keyboard('[Enter]'); + + expect(await oco.findByText('Successfully committed')).toBeInTheConsole(); + expect(await waitForExit(oco)).toBe(0); + await assertHeadCommit(gitDir, 'feat(template): keep generated subject #205'); + } finally { + await server.cleanup(); + await cleanup(); + } +}); + +it('hook command sets and unsets the prepare-commit-msg symlink', async () => { + const { gitDir, cleanup } = await prepareEnvironment(); + const hookPath = resolve(gitDir, '.git/hooks/prepare-commit-msg'); + const cliPath = resolve('./out/cli.cjs'); + + try { + const setHook = await runCli(['hook', 'set'], { + cwd: gitDir + }); + + expect(await setHook.findByText('Hook set')).toBeInTheConsole(); + expect(await waitForExit(setHook)).toBe(0); + expect(existsSync(hookPath)).toBe(true); + expect(lstatSync(hookPath).isSymbolicLink()).toBe(true); + expect(realpathSync(hookPath)).toBe(cliPath); + + const unsetHook = await runCli(['hook', 'unset'], { + cwd: gitDir + }); + + expect(await unsetHook.findByText('Hook is removed')).toBeInTheConsole(); + expect(await waitForExit(unsetHook)).toBe(0); + expect(existsSync(hookPath)).toBe(false); + } finally { + await cleanup(); + } +}); + +it('prepare-commit-msg hook writes the generated message into the commit message file', async () => { + const { gitDir, cleanup } = await prepareEnvironment(); + const server = await startMockOpenAiServer( + 'fix(hook): populate the commit message file' + ); + const hookPath = resolve(gitDir, '.git/hooks/prepare-commit-msg'); + const messageFile = resolve(gitDir, '.git/COMMIT_EDITMSG'); + + try { + await prepareRepo( + gitDir, + { + 'index.ts': 'console.log("Hello World");\n' + }, + { stage: true } + ); + + const setHook = await runCli(['hook', 'set'], { + cwd: gitDir + }); + expect(await setHook.findByText('Hook set')).toBeInTheConsole(); + expect(await waitForExit(setHook)).toBe(0); + + writeFileSync(messageFile, '# existing\n'); + + const hookRun = await runProcess(hookPath, [messageFile], { + cwd: gitDir, + env: getMockOpenAiEnv(server.baseUrl) + }); + + expect(await hookRun.findByText('Done')).toBeInTheConsole(); + expect(await waitForExit(hookRun)).toBe(0); + + const commitMessage = readFileSync(messageFile, 'utf8'); + expect(commitMessage).toContain( + '# fix(hook): populate the commit message file' + ); + expect(commitMessage).toContain('# ---------- [OpenCommit] ---------- #'); + expect(commitMessage).toContain('# existing'); + } finally { + await server.cleanup(); + await cleanup(); + } +}); + +it('cli flow prompts for a missing API key, saves it, and completes the commit', async () => { + const { gitDir, cleanup } = await prepareEnvironment(); + const homeDir = await prepareTempDir(); + const server = await startMockOpenAiServer( + 'fix(api): recovered after prompt' + ); + + try { + const configPath = writeGlobalConfig(homeDir, [ + 'OCO_AI_PROVIDER=openai', + 'OCO_MODEL=gpt-4o-mini', + `OCO_API_URL=${server.baseUrl}`, + 'OCO_GITPUSH=false' + ]); + seedMigrations(homeDir); + + await prepareRepo( + gitDir, + { + 'index.ts': 'console.log("Hello World");\n' + }, + { stage: true } + ); + + const oco = await runCli([], { + cwd: gitDir, + env: { + HOME: homeDir + } + }); + + expect( + await oco.findByText("API key missing for openai. Let's set it up.") + ).toBeInTheConsole(); + expect(await oco.findByText('Enter your API key:')).toBeInTheConsole(); + oco.userEvent.keyboard('test-openai-key[Enter]'); + + expect(await oco.findByText('API key saved')).toBeInTheConsole(); + expect(await oco.findByText('Confirm the commit message?')).toBeInTheConsole(); + oco.userEvent.keyboard('[Enter]'); + + expect(await oco.findByText('Successfully committed')).toBeInTheConsole(); + expect(await waitForExit(oco)).toBe(0); + await assertHeadCommit(gitDir, 'fix(api): recovered after prompt'); + expect(server.authHeaders).toContain('Bearer test-openai-key'); + expect(readFileSync(configPath, 'utf8')).toContain( + 'OCO_API_KEY=test-openai-key' + ); + } finally { + await server.cleanup(); + rmSync(homeDir, { force: true, recursive: true }); + await cleanup(); + } +}); + +it('cli ignores files listed in .opencommitignore when they are the only staged changes', async () => { + const { gitDir, cleanup } = await prepareEnvironment(); + + try { + await prepareRepo( + gitDir, + { + '.opencommitignore': 'ignored.ts\n' + }, + { + stage: true, + commitMessage: 'add opencommit ignore' + } + ); + + writeRepoFile(gitDir, 'ignored.ts', 'console.log("ignored");\n'); + await runGit(['add', 'ignored.ts'], gitDir); + + const oco = await runCli([], { + cwd: gitDir, + env: { + OCO_AI_PROVIDER: 'openai', + OCO_API_KEY: 'dummy-openai-key', + OCO_GITPUSH: 'false' + } + }); + + expect(await oco.findByText('No changes detected')).toBeInTheConsole(); + expect(await waitForExit(oco)).toBe(1); + await assertHeadCommit(gitDir, 'add opencommit ignore'); + } finally { + await cleanup(); + } +}); + +it('cli excludes .opencommitignore files from the generated prompt while still committing staged changes', async () => { + const { gitDir, cleanup } = await prepareEnvironment(); + const server = await startMockOpenAiServer( + 'fix(ignore): keep only relevant diff context' + ); + + try { + await prepareRepo( + gitDir, + { + '.opencommitignore': 'ignored.ts\n' + }, + { + stage: true, + commitMessage: 'add opencommit ignore' + } + ); + + writeRepoFile(gitDir, 'kept.ts', 'console.log("kept");\n'); + writeRepoFile(gitDir, 'ignored.ts', 'console.log("ignored");\n'); + await runGit(['add', 'kept.ts', 'ignored.ts'], gitDir); + + const oco = await runCli([], { + cwd: gitDir, + env: getMockOpenAiEnv(server.baseUrl) + }); + + expect(await oco.findByText('Confirm the commit message?')).toBeInTheConsole(); + oco.userEvent.keyboard('[Enter]'); + + expect(await oco.findByText('Successfully committed')).toBeInTheConsole(); + expect(await waitForExit(oco)).toBe(0); + + const requestPayload = server.requestBodies[ + server.requestBodies.length - 1 + ] as { messages: Array<{ content: string }> }; + const requestContents = requestPayload.messages + .map((message) => message.content) + .join('\n'); + + expect(requestContents).toContain('kept.ts'); + expect(requestContents).toContain('console.log("kept");'); + expect(requestContents).not.toContain('ignored.ts'); + expect(requestContents).not.toContain('console.log("ignored");'); + expect(await getHeadCommitFiles(gitDir)).toEqual(['ignored.ts', 'kept.ts']); + } finally { + await server.cleanup(); + await cleanup(); + } +}); + +it('first run launches setup, saves config, and completes a commit with the configured provider', async () => { + const { gitDir, cleanup } = await prepareEnvironment(); + const homeDir = await prepareTempDir(); + const server = await startMockOpenAiServer( + 'feat(setup): finish first run successfully' + ); + + try { + const configPath = resolve(homeDir, '.opencommit'); + + await seedModelCache(homeDir, { + openai: ['gpt-4o-mini', 'gpt-4o'] + }); + seedMigrations(homeDir); + + await prepareRepo( + gitDir, + { + 'index.ts': 'console.log("Hello World");\n' + }, + { stage: true } + ); + + const oco = await runCli([], { + cwd: gitDir, + env: { + HOME: homeDir, + OCO_API_URL: server.baseUrl, + OCO_GITPUSH: 'false' + } + }); + + expect(await oco.findByText('Select your AI provider:')).toBeInTheConsole(); + oco.userEvent.keyboard('[Enter]'); + + expect(await oco.findByText('Enter your API key:')).toBeInTheConsole(); + oco.userEvent.keyboard('first-run-openai-key[Enter]'); + + expect(await oco.findByText('Select a model:')).toBeInTheConsole(); + oco.userEvent.keyboard('[Enter]'); + + expect( + await oco.findByText('Configuration saved to ~/.opencommit') + ).toBeInTheConsole(); + expect(await oco.findByText('Confirm the commit message?')).toBeInTheConsole(); + oco.userEvent.keyboard('[Enter]'); + + expect(await oco.findByText('Successfully committed')).toBeInTheConsole(); + expect(await waitForExit(oco)).toBe(0); + await assertHeadCommit( + gitDir, + 'feat(setup): finish first run successfully' + ); + expect(readFileSync(configPath, 'utf8')).toContain('OCO_AI_PROVIDER=openai'); + expect(readFileSync(configPath, 'utf8')).toContain( + 'OCO_API_KEY=first-run-openai-key' + ); + expect(readFileSync(configPath, 'utf8')).toContain('OCO_MODEL=gpt-4o-mini'); + expect(server.authHeaders).toContain('Bearer first-run-openai-key'); + } finally { + await server.cleanup(); + rmSync(homeDir, { force: true, recursive: true }); + await cleanup(); + } +}); + +it('cli recovers from a missing model by prompting for an alternative and retrying', async () => { + const { gitDir, cleanup } = await prepareEnvironment(); + const homeDir = await prepareTempDir(); + const server = await startMockOpenAiServer(({ requestIndex, body }) => { + if (requestIndex === 0) { + return { + status: 404, + body: { + error: { + message: `The model '${body?.model}' does not exist`, + type: 'invalid_request_error', + code: 'model_not_found' + } + } + }; + } + + return { + body: { + choices: [ + { + message: { + content: 'fix(model): recover from invalid default model' + } + } + ] + } + }; + }); + + try { + const configPath = writeGlobalConfig(homeDir, [ + 'OCO_AI_PROVIDER=openai', + 'OCO_API_KEY=test-openai-key', + 'OCO_MODEL=missing-model', + `OCO_API_URL=${server.baseUrl}`, + 'OCO_GITPUSH=false' + ]); + seedMigrations(homeDir); + + await prepareRepo( + gitDir, + { + 'index.ts': 'console.log("Hello World");\n' + }, + { stage: true } + ); + + const oco = await runCli([], { + cwd: gitDir, + env: { + HOME: homeDir + } + }); + + expect( + await oco.findByText("Model 'missing-model' not found") + ).toBeInTheConsole(); + expect( + await oco.findByText('Select an alternative model:') + ).toBeInTheConsole(); + oco.userEvent.keyboard('[Enter]'); + + expect(await oco.findByText('Save as default model?')).toBeInTheConsole(); + oco.userEvent.keyboard('[Enter]'); + + expect(await oco.findByText('Model saved as default')).toBeInTheConsole(); + expect(await oco.findByText('Confirm the commit message?')).toBeInTheConsole(); + oco.userEvent.keyboard('[Enter]'); + + expect(await oco.findByText('Successfully committed')).toBeInTheConsole(); + expect(await waitForExit(oco)).toBe(0); + await assertHeadCommit( + gitDir, + 'fix(model): recover from invalid default model' + ); + expect(server.requestBodies.map((request) => request.model)).toEqual([ + 'missing-model', + 'gpt-4o-mini' + ]); + expect(readFileSync(configPath, 'utf8')).toContain('OCO_MODEL=gpt-4o-mini'); + } finally { + await server.cleanup(); + rmSync(homeDir, { force: true, recursive: true }); + await cleanup(); + } +}); + +it('cli excludes lockfiles and assets from the generated prompt while still committing them', async () => { + const { gitDir, cleanup } = await prepareEnvironment(); + const server = await startMockOpenAiServer( + 'fix(diff): focus prompt on meaningful source changes' + ); + + try { + await prepareRepo( + gitDir, + { + 'kept.ts': 'console.log("kept");\n', + 'package-lock.json': '{"name":"opencommit","lockfileVersion":3}\n', + 'logo.svg': '\n' + }, + { stage: true } + ); + + const oco = await runCli([], { + cwd: gitDir, + env: getMockOpenAiEnv(server.baseUrl) + }); + + expect(await oco.findByText('Confirm the commit message?')).toBeInTheConsole(); + oco.userEvent.keyboard('[Enter]'); + + expect(await oco.findByText('Successfully committed')).toBeInTheConsole(); + expect(await waitForExit(oco)).toBe(0); + + const requestPayload = server.requestBodies[ + server.requestBodies.length - 1 + ] as { messages: Array<{ content: string }> }; + const requestContents = requestPayload.messages + .map((message) => message.content) + .join('\n'); + + expect(requestContents).toContain('kept.ts'); + expect(requestContents).toContain('console.log("kept");'); + expect(requestContents).not.toContain('package-lock.json'); + expect(requestContents).not.toContain('lockfileVersion'); + expect(requestContents).not.toContain('logo.svg'); + expect(requestContents).not.toContain(' { + const tempDir = await prepareTempDir(); + + try { + const oco = await runCli([], { + cwd: tempDir, + env: { + OCO_AI_PROVIDER: 'openai', + OCO_API_KEY: 'dummy-openai-key', + OCO_GITPUSH: 'false' + } + }); + + expect(await waitForExit(oco)).toBe(1); + expect(oco.getStdallStr()).toMatch(/No changes detected|not a git repository/); + } finally { + rmSync(tempDir, { force: true, recursive: true }); + } +}); diff --git a/test/e2e/gitPush.test.ts b/test/e2e/gitPush.test.ts index 714254ed..143ab868 100644 --- a/test/e2e/gitPush.test.ts +++ b/test/e2e/gitPush.test.ts @@ -1,210 +1,268 @@ -import path from 'path'; import 'cli-testing-library/extend-expect'; -import { exec } from 'child_process'; -import { prepareTempDir } from './utils'; -import { promisify } from 'util'; -import { render } from 'cli-testing-library'; -import { resolve } from 'path'; -import { rm } from 'fs'; -const fsExec = promisify(exec); -const fsRemove = promisify(rm); - -const waitForCommitConfirmation = async (findByText: any) => { +import { + assertHeadCommit, + getCurrentBranchName, + getMockOpenAiEnv, + getRemoteBranchHeadSubject, + prepareEnvironment, + prepareRepo, + remoteBranchExists, + runCli, + startMockOpenAiServer, + waitForExit +} from './utils'; + +const waitForCommitConfirmation = async ( + findByText: (text: string) => Promise +) => { expect(await findByText('Generating the commit message')).toBeInTheConsole(); expect(await findByText('Confirm the commit message?')).toBeInTheConsole(); }; -/** - * git remote -v - * - * [no remotes] - */ -const prepareNoRemoteGitRepository = async (): Promise<{ - gitDir: string; - cleanup: () => Promise; -}> => { - const tempDir = await prepareTempDir(); - await fsExec('git init test', { cwd: tempDir }); - const gitDir = path.resolve(tempDir, 'test'); - - const cleanup = async () => { - return fsRemove(tempDir, { recursive: true }); - }; - return { - gitDir, - cleanup - }; -}; +describe('cli flow to push git branch', () => { + it('does nothing when OCO_GITPUSH is set to false', async () => { + const { gitDir, cleanup } = await prepareEnvironment({ remotes: 0 }); + const server = await startMockOpenAiServer( + 'fix(push): keep the commit local when push is disabled' + ); -/** - * git remote -v - * - * origin /tmp/remote.git (fetch) - * origin /tmp/remote.git (push) - */ -const prepareOneRemoteGitRepository = async (): Promise<{ - gitDir: string; - cleanup: () => Promise; -}> => { - const tempDir = await prepareTempDir(); - await fsExec('git init --bare remote.git', { cwd: tempDir }); - await fsExec('git clone remote.git test', { cwd: tempDir }); - const gitDir = path.resolve(tempDir, 'test'); - - const cleanup = async () => { - return fsRemove(tempDir, { recursive: true }); - }; - return { - gitDir, - cleanup - }; -}; + try { + await prepareRepo( + gitDir, + { + 'index.ts': 'console.log("Hello World");\n' + }, + { stage: true } + ); -/** - * git remote -v - * - * origin /tmp/remote.git (fetch) - * origin /tmp/remote.git (push) - * other ../remote2.git (fetch) - * other ../remote2.git (push) - */ -const prepareTwoRemotesGitRepository = async (): Promise<{ - gitDir: string; - cleanup: () => Promise; -}> => { - const tempDir = await prepareTempDir(); - await fsExec('git init --bare remote.git', { cwd: tempDir }); - await fsExec('git init --bare other.git', { cwd: tempDir }); - await fsExec('git clone remote.git test', { cwd: tempDir }); - const gitDir = path.resolve(tempDir, 'test'); - await fsExec('git remote add other ../other.git', { cwd: gitDir }); - - const cleanup = async () => { - return fsRemove(tempDir, { recursive: true }); - }; - return { - gitDir, - cleanup - }; -}; + const oco = await runCli([], { + cwd: gitDir, + env: getMockOpenAiEnv(server.baseUrl, { + OCO_GITPUSH: 'false' + }) + }); -describe('cli flow to push git branch', () => { - it('do nothing when OCO_GITPUSH is set to false', async () => { - const { gitDir, cleanup } = await prepareNoRemoteGitRepository(); + await waitForCommitConfirmation(oco.findByText); + oco.userEvent.keyboard('[Enter]'); - await render('echo', [`'console.log("Hello World");' > index.ts`], { - cwd: gitDir - }); - await render('git', ['add index.ts'], { cwd: gitDir }); + expect(await oco.findByText('Successfully committed')).toBeInTheConsole(); + expect( + await oco.queryByText('Choose a remote to push to') + ).not.toBeInTheConsole(); + expect( + await oco.queryByText('Do you want to run `git push`?') + ).not.toBeInTheConsole(); + expect( + await oco.queryByText('Successfully pushed all commits to origin') + ).not.toBeInTheConsole(); + expect( + await oco.queryByText('Command failed with exit code 1') + ).not.toBeInTheConsole(); + expect(await waitForExit(oco)).toBe(0); + await assertHeadCommit( + gitDir, + 'fix(push): keep the commit local when push is disabled' + ); + } finally { + await server.cleanup(); + await cleanup(); + } + }); - const { queryByText, findByText, userEvent } = await render( - `OCO_AI_PROVIDER='test' OCO_GITPUSH='false' node`, - [resolve('./out/cli.cjs')], - { cwd: gitDir } + it('fails after committing when push is enabled but there is no remote', async () => { + const { gitDir, cleanup } = await prepareEnvironment({ remotes: 0 }); + const server = await startMockOpenAiServer( + 'fix(push): commit even when the push later fails' ); - await waitForCommitConfirmation(findByText); - userEvent.keyboard('[Enter]'); - - expect( - await queryByText('Choose a remote to push to') - ).not.toBeInTheConsole(); - expect( - await queryByText('Do you want to run `git push`?') - ).not.toBeInTheConsole(); - expect( - await queryByText('Successfully pushed all commits to origin') - ).not.toBeInTheConsole(); - expect( - await queryByText('Command failed with exit code 1') - ).not.toBeInTheConsole(); - - await cleanup(); - }); - it('push and cause error when there is no remote', async () => { - const { gitDir, cleanup } = await prepareNoRemoteGitRepository(); + try { + await prepareRepo( + gitDir, + { + 'index.ts': 'console.log("Hello World");\n' + }, + { stage: true } + ); - await render('echo', [`'console.log("Hello World");' > index.ts`], { - cwd: gitDir - }); - await render('git', ['add index.ts'], { cwd: gitDir }); + const oco = await runCli([], { + cwd: gitDir, + env: getMockOpenAiEnv(server.baseUrl, { + OCO_GITPUSH: 'true' + }) + }); - const { queryByText, findByText, userEvent } = await render( - `OCO_AI_PROVIDER='test' OCO_GITPUSH='true' node`, - [resolve('./out/cli.cjs')], - { cwd: gitDir } - ); - await waitForCommitConfirmation(findByText); - userEvent.keyboard('[Enter]'); - - expect( - await queryByText('Choose a remote to push to') - ).not.toBeInTheConsole(); - expect( - await queryByText('Do you want to run `git push`?') - ).not.toBeInTheConsole(); - expect( - await queryByText('Successfully pushed all commits to origin') - ).not.toBeInTheConsole(); - - expect( - await findByText('Command failed with exit code 1') - ).toBeInTheConsole(); - - await cleanup(); - }); + await waitForCommitConfirmation(oco.findByText); + oco.userEvent.keyboard('[Enter]'); - it('push when one remote is set', async () => { - const { gitDir, cleanup } = await prepareOneRemoteGitRepository(); + expect( + await oco.queryByText('Choose a remote to push to') + ).not.toBeInTheConsole(); + expect( + await oco.queryByText('Do you want to run `git push`?') + ).not.toBeInTheConsole(); + expect( + await oco.queryByText('Successfully pushed all commits to origin') + ).not.toBeInTheConsole(); + expect( + await oco.findByText('Command failed with exit code 1') + ).toBeInTheConsole(); + expect(await waitForExit(oco)).toBe(1); + await assertHeadCommit( + gitDir, + 'fix(push): commit even when the push later fails' + ); + } finally { + await server.cleanup(); + await cleanup(); + } + }); - await render('echo', [`'console.log("Hello World");' > index.ts`], { - cwd: gitDir + it('pushes to the only configured remote', async () => { + const { gitDir, remoteDir, cleanup } = await prepareEnvironment({ + remotes: 1 }); - await render('git', ['add index.ts'], { cwd: gitDir }); - - const { findByText, userEvent } = await render( - `OCO_AI_PROVIDER='test' OCO_GITPUSH='true' node`, - [resolve('./out/cli.cjs')], - { cwd: gitDir } + const server = await startMockOpenAiServer( + 'feat(push): publish the commit to the only remote' ); - await waitForCommitConfirmation(findByText); - userEvent.keyboard('[Enter]'); - expect( - await findByText('Do you want to run `git push`?') - ).toBeInTheConsole(); - userEvent.keyboard('[Enter]'); + try { + await prepareRepo( + gitDir, + { + 'index.ts': 'console.log("Hello World");\n' + }, + { stage: true } + ); - expect( - await findByText('Successfully pushed all commits to origin') - ).toBeInTheConsole(); + const oco = await runCli([], { + cwd: gitDir, + env: getMockOpenAiEnv(server.baseUrl, { + OCO_GITPUSH: 'true' + }) + }); - await cleanup(); - }); + await waitForCommitConfirmation(oco.findByText); + oco.userEvent.keyboard('[Enter]'); - it('push when two remotes are set', async () => { - const { gitDir, cleanup } = await prepareTwoRemotesGitRepository(); + expect( + await oco.findByText('Do you want to run `git push`?') + ).toBeInTheConsole(); + oco.userEvent.keyboard('[Enter]'); + + expect( + await oco.findByText('Successfully pushed all commits to origin') + ).toBeInTheConsole(); + expect(await waitForExit(oco)).toBe(0); + await assertHeadCommit( + gitDir, + 'feat(push): publish the commit to the only remote' + ); + expect( + await getRemoteBranchHeadSubject( + remoteDir!, + await getCurrentBranchName(gitDir) + ) + ).toBe('feat(push): publish the commit to the only remote'); + } finally { + await server.cleanup(); + await cleanup(); + } + }); - await render('echo', [`'console.log("Hello World");' > index.ts`], { - cwd: gitDir + it('pushes to the selected remote when multiple remotes are configured', async () => { + const { gitDir, remoteDir, cleanup } = await prepareEnvironment({ + remotes: 2 }); - await render('git', ['add index.ts'], { cwd: gitDir }); + const server = await startMockOpenAiServer( + 'feat(push): choose a remote explicitly when several exist' + ); + + try { + await prepareRepo( + gitDir, + { + 'index.ts': 'console.log("Hello World");\n' + }, + { stage: true } + ); + + const oco = await runCli([], { + cwd: gitDir, + env: getMockOpenAiEnv(server.baseUrl, { + OCO_GITPUSH: 'true' + }) + }); - const { findByText, userEvent } = await render( - `OCO_AI_PROVIDER='test' OCO_GITPUSH='true' node`, - [resolve('./out/cli.cjs')], - { cwd: gitDir } + await waitForCommitConfirmation(oco.findByText); + oco.userEvent.keyboard('[Enter]'); + + expect(await oco.findByText('Choose a remote to push to')).toBeInTheConsole(); + oco.userEvent.keyboard('[Enter]'); + + expect( + await oco.findByText('Successfully pushed all commits to origin') + ).toBeInTheConsole(); + expect(await waitForExit(oco)).toBe(0); + await assertHeadCommit( + gitDir, + 'feat(push): choose a remote explicitly when several exist' + ); + expect( + await getRemoteBranchHeadSubject( + remoteDir!, + await getCurrentBranchName(gitDir) + ) + ).toBe('feat(push): choose a remote explicitly when several exist'); + } finally { + await server.cleanup(); + await cleanup(); + } + }); + + it("keeps the commit local when the user chooses 'don't push'", async () => { + const { gitDir, remoteDir, otherRemoteDir, cleanup } = + await prepareEnvironment({ remotes: 2 }); + const server = await startMockOpenAiServer( + "fix(push): skip the remote step when the user chooses don't push" ); - await waitForCommitConfirmation(findByText); - userEvent.keyboard('[Enter]'); - expect(await findByText('Choose a remote to push to')).toBeInTheConsole(); - userEvent.keyboard('[Enter]'); + try { + await prepareRepo( + gitDir, + { + 'index.ts': 'console.log("Hello World");\n' + }, + { stage: true } + ); + + const oco = await runCli([], { + cwd: gitDir, + env: getMockOpenAiEnv(server.baseUrl, { + OCO_GITPUSH: 'true' + }) + }); + + await waitForCommitConfirmation(oco.findByText); + oco.userEvent.keyboard('[Enter]'); + + expect(await oco.findByText('Choose a remote to push to')).toBeInTheConsole(); + oco.userEvent.keyboard('[ArrowDown][ArrowDown][Enter]'); - expect( - await findByText('Successfully pushed all commits to origin') - ).toBeInTheConsole(); + expect( + await oco.queryByText('Successfully pushed all commits to origin') + ).not.toBeInTheConsole(); + expect(await waitForExit(oco)).toBe(0); + await assertHeadCommit( + gitDir, + "fix(push): skip the remote step when the user chooses don't push" + ); - await cleanup(); + const branchName = await getCurrentBranchName(gitDir); + expect(await remoteBranchExists(remoteDir!, branchName)).toBe(false); + expect(await remoteBranchExists(otherRemoteDir!, branchName)).toBe(false); + } finally { + await server.cleanup(); + await cleanup(); + } }); }); diff --git a/test/e2e/noChanges.test.ts b/test/e2e/noChanges.test.ts index b50e740d..d1603338 100644 --- a/test/e2e/noChanges.test.ts +++ b/test/e2e/noChanges.test.ts @@ -1,12 +1,22 @@ -import { resolve } from 'path' -import { render } from 'cli-testing-library' import 'cli-testing-library/extend-expect'; -import { prepareEnvironment } from './utils'; +import { prepareEnvironment, runCli, waitForExit } from './utils'; it('cli flow when there are no changes', async () => { const { gitDir, cleanup } = await prepareEnvironment(); - const { findByText } = await render(`OCO_AI_PROVIDER='test' node`, [resolve('./out/cli.cjs')], { cwd: gitDir }); - expect(await findByText('No changes detected')).toBeInTheConsole(); - await cleanup(); + try { + const oco = await runCli([], { + cwd: gitDir, + env: { + OCO_AI_PROVIDER: 'openai', + OCO_API_KEY: 'dummy-openai-key', + OCO_GITPUSH: 'false' + } + }); + + expect(await oco.findByText('No changes detected')).toBeInTheConsole(); + expect(await waitForExit(oco)).toBe(1); + } finally { + await cleanup(); + } }); diff --git a/test/e2e/oneFile.test.ts b/test/e2e/oneFile.test.ts index 1e6c4d46..417a44bf 100644 --- a/test/e2e/oneFile.test.ts +++ b/test/e2e/oneFile.test.ts @@ -1,55 +1,125 @@ -import { resolve } from 'path' -import { render } from 'cli-testing-library' import 'cli-testing-library/extend-expect'; -import { prepareEnvironment } from './utils'; +import { + assertHeadCommit, + getCurrentBranchName, + getMockOpenAiEnv, + getRemoteBranchHeadSubject, + prepareEnvironment, + prepareRepo, + runCli, + startMockOpenAiServer, + appendRepoFile, + waitForExit +} from './utils'; it('cli flow to generate commit message for 1 new file (staged)', async () => { - const { gitDir, cleanup } = await prepareEnvironment(); - - await render('echo' ,[`'console.log("Hello World");' > index.ts`], { cwd: gitDir }); - await render('git' ,['add index.ts'], { cwd: gitDir }); - - const { queryByText, findByText, userEvent } = await render(`OCO_AI_PROVIDER='test' OCO_GITPUSH='true' node`, [resolve('./out/cli.cjs')], { cwd: gitDir }); - expect(await queryByText('No files are staged')).not.toBeInTheConsole(); - expect(await queryByText('Do you want to stage all files and generate commit message?')).not.toBeInTheConsole(); - - expect(await findByText('Generating the commit message')).toBeInTheConsole(); - expect(await findByText('Confirm the commit message?')).toBeInTheConsole(); - userEvent.keyboard('[Enter]'); - - expect(await findByText('Do you want to run `git push`?')).toBeInTheConsole(); - userEvent.keyboard('[Enter]'); - - expect(await findByText('Successfully pushed all commits to origin')).toBeInTheConsole(); - - await cleanup(); + const { gitDir, remoteDir, cleanup } = await prepareEnvironment(); + const server = await startMockOpenAiServer( + 'feat(cli): commit one staged file through the CLI' + ); + + try { + await prepareRepo( + gitDir, + { + 'index.ts': 'console.log("Hello World");\n' + }, + { stage: true } + ); + + const oco = await runCli([], { + cwd: gitDir, + env: getMockOpenAiEnv(server.baseUrl, { + OCO_GITPUSH: 'true' + }) + }); + + expect(await oco.queryByText('No files are staged')).not.toBeInTheConsole(); + expect( + await oco.queryByText( + 'Do you want to stage all files and generate commit message?' + ) + ).not.toBeInTheConsole(); + + expect(await oco.findByText('Generating the commit message')).toBeInTheConsole(); + expect(await oco.findByText('Confirm the commit message?')).toBeInTheConsole(); + oco.userEvent.keyboard('[Enter]'); + + expect(await oco.findByText('Do you want to run `git push`?')).toBeInTheConsole(); + oco.userEvent.keyboard('[Enter]'); + + expect( + await oco.findByText('Successfully pushed all commits to origin') + ).toBeInTheConsole(); + expect(await waitForExit(oco)).toBe(0); + await assertHeadCommit( + gitDir, + 'feat(cli): commit one staged file through the CLI' + ); + expect( + await getRemoteBranchHeadSubject( + remoteDir!, + await getCurrentBranchName(gitDir) + ) + ).toBe('feat(cli): commit one staged file through the CLI'); + } finally { + await server.cleanup(); + await cleanup(); + } }); it('cli flow to generate commit message for 1 changed file (not staged)', async () => { const { gitDir, cleanup } = await prepareEnvironment(); - - await render('echo' ,[`'console.log("Hello World");' > index.ts`], { cwd: gitDir }); - await render('git' ,['add index.ts'], { cwd: gitDir }); - await render('git' ,[`commit -m 'add new file'`], { cwd: gitDir }); - - await render('echo' ,[`'console.log("Good night World");' >> index.ts`], { cwd: gitDir }); - - const { findByText, userEvent } = await render(`OCO_AI_PROVIDER='test' OCO_GITPUSH='true' node`, [resolve('./out/cli.cjs')], { cwd: gitDir }); - - expect(await findByText('No files are staged')).toBeInTheConsole(); - expect(await findByText('Do you want to stage all files and generate commit message?')).toBeInTheConsole(); - userEvent.keyboard('[Enter]'); - - expect(await findByText('Generating the commit message')).toBeInTheConsole(); - expect(await findByText('Confirm the commit message?')).toBeInTheConsole(); - userEvent.keyboard('[Enter]'); - - expect(await findByText('Successfully committed')).toBeInTheConsole(); - - expect(await findByText('Do you want to run `git push`?')).toBeInTheConsole(); - userEvent.keyboard('[Enter]'); - - expect(await findByText('Successfully pushed all commits to origin')).toBeInTheConsole(); - - await cleanup(); + const server = await startMockOpenAiServer( + 'fix(cli): stage modified files before committing' + ); + + try { + await prepareRepo( + gitDir, + { + 'index.ts': 'console.log("Hello World");\n' + }, + { + stage: true, + commitMessage: 'add new file' + } + ); + appendRepoFile(gitDir, 'index.ts', 'console.log("Good night World");\n'); + + const oco = await runCli([], { + cwd: gitDir, + env: getMockOpenAiEnv(server.baseUrl, { + OCO_GITPUSH: 'true' + }) + }); + + expect(await oco.findByText('No files are staged')).toBeInTheConsole(); + expect( + await oco.findByText( + 'Do you want to stage all files and generate commit message?' + ) + ).toBeInTheConsole(); + oco.userEvent.keyboard('[Enter]'); + + expect(await oco.findByText('Generating the commit message')).toBeInTheConsole(); + expect(await oco.findByText('Confirm the commit message?')).toBeInTheConsole(); + oco.userEvent.keyboard('[Enter]'); + + expect(await oco.findByText('Successfully committed')).toBeInTheConsole(); + expect(await oco.findByText('Do you want to run `git push`?')).toBeInTheConsole(); + oco.userEvent.keyboard('[Enter]'); + + expect( + await oco.findByText('Successfully pushed all commits to origin') + ).toBeInTheConsole(); + expect(await waitForExit(oco)).toBe(0); + await assertHeadCommit( + gitDir, + 'fix(cli): stage modified files before committing' + ); + } finally { + await server.cleanup(); + await cleanup(); + } }); diff --git a/test/e2e/prompt-module/commitlint.test.ts b/test/e2e/prompt-module/commitlint.test.ts index 680203f7..e38032bb 100644 --- a/test/e2e/prompt-module/commitlint.test.ts +++ b/test/e2e/prompt-module/commitlint.test.ts @@ -1,151 +1,145 @@ -import { resolve } from 'path'; -import { render } from 'cli-testing-library'; -import 'cli-testing-library/extend-expect'; -import { prepareEnvironment, wait } from '../utils'; +import { cpSync } from 'fs'; import path from 'path'; import { execFile } from 'child_process'; import { promisify } from 'util'; +import 'cli-testing-library/extend-expect'; +import { + assertHeadCommit, + prepareEnvironment, + prepareRepo, + runCli, + waitForExit +} from '../utils'; const execFileAsync = promisify(execFile); -function getAbsolutePath(relativePath: string) { - // Use process.cwd() which should be the project root during test execution - return path.resolve(process.cwd(), 'test/e2e/prompt-module', relativePath); +const getFixturePath = (version: 9 | 18 | 19, fileName: string) => + path.resolve( + process.cwd(), + `test/e2e/prompt-module/data/commitlint_${version}/${fileName}` + ); + +const getPromptModuleEnv = ( + mockType: 'commit-message' | 'prompt-module-commitlint-config' +): NodeJS.ProcessEnv => ({ + OCO_TEST_MOCK_TYPE: mockType, + OCO_PROMPT_MODULE: '@commitlint', + OCO_AI_PROVIDER: 'test', + OCO_GITPUSH: 'true' +}); + +async function setupCommitlint(dir: string, version: 9 | 18 | 19) { + cpSync(getFixturePath(version, 'node_modules'), path.join(dir, 'node_modules'), { + recursive: true + }); + cpSync(getFixturePath(version, 'package.json'), path.join(dir, 'package.json')); + cpSync( + getFixturePath(version, 'commitlint.config.js'), + path.join(dir, 'commitlint.config.js') + ); } -async function setupCommitlint(dir: string, ver: 9 | 18 | 19) { - let packagePath, packageJsonPath, configPath; - switch (ver) { - case 9: - packagePath = getAbsolutePath('./data/commitlint_9/node_modules'); - packageJsonPath = getAbsolutePath('./data/commitlint_9/package.json'); - configPath = getAbsolutePath('./data/commitlint_9/commitlint.config.js'); - break; - case 18: - packagePath = getAbsolutePath('./data/commitlint_18/node_modules'); - packageJsonPath = getAbsolutePath('./data/commitlint_18/package.json'); - configPath = getAbsolutePath('./data/commitlint_18/commitlint.config.js'); - break; - case 19: - packagePath = getAbsolutePath('./data/commitlint_19/node_modules'); - packageJsonPath = getAbsolutePath('./data/commitlint_19/package.json'); - configPath = getAbsolutePath('./data/commitlint_19/commitlint.config.js'); - break; - } - - await execFileAsync('cp', ['-R', packagePath, path.join(dir, 'node_modules')]); - await execFileAsync('cp', [packageJsonPath, path.join(dir, 'package.json')]); - await execFileAsync('cp', [configPath, path.join(dir, 'commitlint.config.js')]); - await wait(3000); // Avoid flakiness by waiting + +async function assertInstalledCommitlintVersion( + cwd: string, + version: string +): Promise { + const { stdout = '', stderr = '' } = await execFileAsync( + 'npm', + ['list', '@commitlint/load'], + { cwd } + ); + expect(`${stdout}\n${stderr}`).toContain(`@commitlint/load@${version}`); } describe('cli flow to run "oco commitlint force"', () => { it('on commitlint@9 using CJS', async () => { const { gitDir, cleanup } = await prepareEnvironment(); - await setupCommitlint(gitDir, 9); - const npmList = await render('npm', ['list', '@commitlint/load'], { - cwd: gitDir - }); - expect(await npmList.findByText('@commitlint/load@9')).toBeInTheConsole(); - - const { findByText } = await render( - ` - OCO_TEST_MOCK_TYPE='prompt-module-commitlint-config' \ - OCO_PROMPT_MODULE='@commitlint' \ - OCO_AI_PROVIDER='test' OCO_GITPUSH='true' \ - node ${resolve('./out/cli.cjs')} commitlint force \ - `, - [], - { cwd: gitDir } - ); - - expect( - await findByText('opencommit — configure @commitlint') - ).toBeInTheConsole(); - expect( - await findByText('Read @commitlint configuration') - ).toBeInTheConsole(); - - expect( - await findByText('Generating consistency with given @commitlint rules') - ).toBeInTheConsole(); - expect( - await findByText('Done - please review contents of') - ).toBeInTheConsole(); - - await cleanup(); + try { + await setupCommitlint(gitDir, 9); + await assertInstalledCommitlintVersion(gitDir, '9'); + + const oco = await runCli(['commitlint', 'force'], { + cwd: gitDir, + env: getPromptModuleEnv('prompt-module-commitlint-config') + }); + + expect( + await oco.findByText('opencommit — configure @commitlint') + ).toBeInTheConsole(); + expect( + await oco.findByText('Read @commitlint configuration') + ).toBeInTheConsole(); + expect( + await oco.findByText('Generating consistency with given @commitlint rules') + ).toBeInTheConsole(); + expect( + await oco.findByText('Done - please review contents of') + ).toBeInTheConsole(); + expect(await waitForExit(oco)).toBe(0); + } finally { + await cleanup(); + } }); + it('on commitlint@18 using CJS', async () => { const { gitDir, cleanup } = await prepareEnvironment(); - await setupCommitlint(gitDir, 18); - const npmList = await render('npm', ['list', '@commitlint/load'], { - cwd: gitDir - }); - expect(await npmList.findByText('@commitlint/load@18')).toBeInTheConsole(); - - const { findByText } = await render( - ` - OCO_TEST_MOCK_TYPE='prompt-module-commitlint-config' \ - OCO_PROMPT_MODULE='@commitlint' \ - OCO_AI_PROVIDER='test' OCO_GITPUSH='true' \ - node ${resolve('./out/cli.cjs')} commitlint force \ - `, - [], - { cwd: gitDir } - ); - - expect( - await findByText('opencommit — configure @commitlint') - ).toBeInTheConsole(); - expect( - await findByText('Read @commitlint configuration') - ).toBeInTheConsole(); - - expect( - await findByText('Generating consistency with given @commitlint rules') - ).toBeInTheConsole(); - expect( - await findByText('Done - please review contents of') - ).toBeInTheConsole(); - - await cleanup(); + try { + await setupCommitlint(gitDir, 18); + await assertInstalledCommitlintVersion(gitDir, '18'); + + const oco = await runCli(['commitlint', 'force'], { + cwd: gitDir, + env: getPromptModuleEnv('prompt-module-commitlint-config') + }); + + expect( + await oco.findByText('opencommit — configure @commitlint') + ).toBeInTheConsole(); + expect( + await oco.findByText('Read @commitlint configuration') + ).toBeInTheConsole(); + expect( + await oco.findByText('Generating consistency with given @commitlint rules') + ).toBeInTheConsole(); + expect( + await oco.findByText('Done - please review contents of') + ).toBeInTheConsole(); + expect(await waitForExit(oco)).toBe(0); + } finally { + await cleanup(); + } }); + it('on commitlint@19 using ESM', async () => { const { gitDir, cleanup } = await prepareEnvironment(); - await setupCommitlint(gitDir, 19); - const npmList = await render('npm', ['list', '@commitlint/load'], { - cwd: gitDir - }); - expect(await npmList.findByText('@commitlint/load@19')).toBeInTheConsole(); - - const { findByText } = await render( - ` - OCO_TEST_MOCK_TYPE='prompt-module-commitlint-config' \ - OCO_PROMPT_MODULE='@commitlint' \ - OCO_AI_PROVIDER='test' OCO_GITPUSH='true' \ - node ${resolve('./out/cli.cjs')} commitlint force \ - `, - [], - { cwd: gitDir } - ); - - expect( - await findByText('opencommit — configure @commitlint') - ).toBeInTheConsole(); - expect( - await findByText('Read @commitlint configuration') - ).toBeInTheConsole(); - - expect( - await findByText('Generating consistency with given @commitlint rules') - ).toBeInTheConsole(); - expect( - await findByText('Done - please review contents of') - ).toBeInTheConsole(); - - await cleanup(); + try { + await setupCommitlint(gitDir, 19); + await assertInstalledCommitlintVersion(gitDir, '19'); + + const oco = await runCli(['commitlint', 'force'], { + cwd: gitDir, + env: getPromptModuleEnv('prompt-module-commitlint-config') + }); + + expect( + await oco.findByText('opencommit — configure @commitlint') + ).toBeInTheConsole(); + expect( + await oco.findByText('Read @commitlint configuration') + ).toBeInTheConsole(); + expect( + await oco.findByText('Generating consistency with given @commitlint rules') + ).toBeInTheConsole(); + expect( + await oco.findByText('Done - please review contents of') + ).toBeInTheConsole(); + expect(await waitForExit(oco)).toBe(0); + } finally { + await cleanup(); + } }); }); @@ -153,75 +147,57 @@ describe('cli flow to generate commit message using @commitlint prompt-module', it('on commitlint@19 using ESM', async () => { const { gitDir, cleanup } = await prepareEnvironment(); - // Setup commitlint@19 - await setupCommitlint(gitDir, 19); - const npmList = await render('npm', ['list', '@commitlint/load'], { - cwd: gitDir - }); - expect(await npmList.findByText('@commitlint/load@19')).toBeInTheConsole(); - - // Run `oco commitlint force` - const commitlintForce = await render( - ` - OCO_TEST_MOCK_TYPE='prompt-module-commitlint-config' \ - OCO_PROMPT_MODULE='@commitlint' \ - OCO_AI_PROVIDER='test' OCO_GITPUSH='true' \ - node ${resolve('./out/cli.cjs')} commitlint force \ - `, - [], - { cwd: gitDir } - ); - expect( - await commitlintForce.findByText('Done - please review contents of') - ).toBeInTheConsole(); - - // Run `oco commitlint get` - const commitlintGet = await render( - ` - OCO_TEST_MOCK_TYPE='prompt-module-commitlint-config' \ - OCO_PROMPT_MODULE='@commitlint' \ - OCO_AI_PROVIDER='test' OCO_GITPUSH='true' \ - node ${resolve('./out/cli.cjs')} commitlint get \ - `, - [], - { cwd: gitDir } - ); - expect(await commitlintGet.findByText('consistency')).toBeInTheConsole(); - - // Run 'oco' using .opencommit-commitlint - await render('echo', [`'console.log("Hello World");' > index.ts`], { - cwd: gitDir - }); - await render('git', ['add index.ts'], { cwd: gitDir }); - - const oco = await render( - ` - OCO_TEST_MOCK_TYPE='commit-message' \ - OCO_PROMPT_MODULE='@commitlint' \ - OCO_AI_PROVIDER='test' OCO_GITPUSH='true' \ - node ${resolve('./out/cli.cjs')} \ - `, - [], - { cwd: gitDir } - ); - - expect( - await oco.findByText('Generating the commit message') - ).toBeInTheConsole(); - expect( - await oco.findByText('Confirm the commit message?') - ).toBeInTheConsole(); - oco.userEvent.keyboard('[Enter]'); - - expect( - await oco.findByText('Do you want to run `git push`?') - ).toBeInTheConsole(); - oco.userEvent.keyboard('[Enter]'); - - expect( - await oco.findByText('Successfully pushed all commits to origin') - ).toBeInTheConsole(); - - await cleanup(); + try { + await setupCommitlint(gitDir, 19); + await assertInstalledCommitlintVersion(gitDir, '19'); + + const commitlintForce = await runCli(['commitlint', 'force'], { + cwd: gitDir, + env: getPromptModuleEnv('prompt-module-commitlint-config') + }); + expect( + await commitlintForce.findByText('Done - please review contents of') + ).toBeInTheConsole(); + expect(await waitForExit(commitlintForce)).toBe(0); + + const commitlintGet = await runCli(['commitlint', 'get'], { + cwd: gitDir, + env: getPromptModuleEnv('prompt-module-commitlint-config') + }); + expect(await commitlintGet.findByText('consistency')).toBeInTheConsole(); + expect(await waitForExit(commitlintGet)).toBe(0); + + await prepareRepo( + gitDir, + { + 'index.ts': 'console.log("Hello World");\n' + }, + { stage: true } + ); + + const oco = await runCli([], { + cwd: gitDir, + env: getPromptModuleEnv('commit-message') + }); + + expect( + await oco.findByText('Generating the commit message') + ).toBeInTheConsole(); + expect(await oco.findByText('Confirm the commit message?')).toBeInTheConsole(); + oco.userEvent.keyboard('[Enter]'); + + expect( + await oco.findByText('Do you want to run `git push`?') + ).toBeInTheConsole(); + oco.userEvent.keyboard('[Enter]'); + + expect( + await oco.findByText('Successfully pushed all commits to origin') + ).toBeInTheConsole(); + expect(await waitForExit(oco)).toBe(0); + await assertHeadCommit(gitDir, 'fix(testAi.ts): test commit message'); + } finally { + await cleanup(); + } }); }); diff --git a/test/e2e/setup.sh b/test/e2e/setup.sh index 83bf72f7..ab1224ac 100755 --- a/test/e2e/setup.sh +++ b/test/e2e/setup.sh @@ -1,5 +1,7 @@ #!/bin/sh +set -eu + current_dir=$(pwd) setup_dir="$(cd "$(dirname "$0")" && pwd)" diff --git a/test/e2e/smoke.test.ts b/test/e2e/smoke.test.ts new file mode 100644 index 00000000..4f4308cd --- /dev/null +++ b/test/e2e/smoke.test.ts @@ -0,0 +1,26 @@ +import packageJson from '../../package.json'; +import 'cli-testing-library/extend-expect'; +import { runCli, waitForExit } from './utils'; + +it('prints help without entering the interactive flow', async () => { + const help = await runCli(['--help'], { + cwd: process.cwd() + }); + + expect(await help.findByText('opencommit')).toBeInTheConsole(); + expect(await help.findByText('--context')).toBeInTheConsole(); + expect(await help.findByText('--yes')).toBeInTheConsole(); + expect(await help.queryByText('Select your AI provider:')).not.toBeInTheConsole(); + expect(await help.queryByText('Enter your API key:')).not.toBeInTheConsole(); + expect(await waitForExit(help)).toBe(0); +}); + +it('prints the current version without booting the CLI runtime', async () => { + const version = await runCli(['--version'], { + cwd: process.cwd() + }); + + expect(await version.findByText(packageJson.version)).toBeInTheConsole(); + expect(await version.queryByText('Generating the commit message')).not.toBeInTheConsole(); + expect(await waitForExit(version)).toBe(0); +}); diff --git a/test/e2e/utils.ts b/test/e2e/utils.ts index 6ae56633..8ac935cf 100644 --- a/test/e2e/utils.ts +++ b/test/e2e/utils.ts @@ -1,37 +1,441 @@ -import path from 'path' -import { mkdtemp, rm } from 'fs' -import { promisify } from 'util'; +import path from 'path'; +import { + appendFileSync, + existsSync, + mkdirSync, + mkdtemp, + rm, + writeFileSync +} from 'fs'; +import http from 'http'; import { tmpdir } from 'os'; -import { exec } from 'child_process'; +import { execFile } from 'child_process'; +import { promisify } from 'util'; +import type { AddressInfo } from 'net'; +import { render } from 'cli-testing-library'; +import type { RenderResult } from 'cli-testing-library'; + const fsMakeTempDir = promisify(mkdtemp); -const fsExec = promisify(exec); +const fsExecFile = promisify(execFile); const fsRemove = promisify(rm); -/** - * Prepare the environment for the test - * Create a temporary git repository in the temp directory - */ -export const prepareEnvironment = async (): Promise<{ +const CLI_PATH = path.resolve(process.cwd(), 'out/cli.cjs'); +const DEFAULT_TEST_ENV = { + OCO_TEST_SKIP_VERSION_CHECK: 'true' +}; +const COMPLETED_MIGRATIONS = [ + '00_use_single_api_key_and_url', + '01_remove_obsolete_config_keys_from_global_file', + '02_set_missing_default_values' +]; + +type ProcessOptions = { + cwd: string; + env?: NodeJS.ProcessEnv; +}; + +type PrepareEnvironmentOptions = { + remotes?: 0 | 1 | 2; +}; + +export const getCliPath = () => CLI_PATH; + +export const runProcess = async ( + command: string, + args: string[] = [], + { cwd, env = {} }: ProcessOptions +): Promise => { + return render(command, args, { + cwd, + spawnOpts: { + env: { + ...process.env, + ...DEFAULT_TEST_ENV, + ...env + } + } + }); +}; + +export const runCli = async ( + args: string[] = [], + options: ProcessOptions +): Promise => { + return runProcess(process.execPath, [getCliPath(), ...args], options); +}; + +export const runGit = async ( + args: string[], + cwd: string +): Promise<{ stdout: string; stderr: string }> => { + const { stdout = '', stderr = '' } = await fsExecFile('git', args, { cwd }); + return { stdout, stderr }; +}; + +export const configureGitUser = async (gitDir: string): Promise => { + await runGit(['config', 'user.email', 'test@example.com'], gitDir); + await runGit(['config', 'user.name', 'Test User'], gitDir); +}; + +export const prepareEnvironment = async ({ + remotes = 1 +}: PrepareEnvironmentOptions = {}): Promise<{ + tempDir: string; gitDir: string; + remoteDir?: string; + otherRemoteDir?: string; cleanup: () => Promise; }> => { const tempDir = await prepareTempDir(); - // Create a remote git repository int the temp directory. This is necessary to execute the `git push` command - await fsExec('git init --bare remote.git', { cwd: tempDir }); - await fsExec('git clone remote.git test', { cwd: tempDir }); const gitDir = path.resolve(tempDir, 'test'); + let remoteDir: string | undefined; + let otherRemoteDir: string | undefined; - const cleanup = async () => { - return fsRemove(tempDir, { recursive: true }); + if (remotes === 0) { + await fsExecFile('git', ['init', 'test'], { cwd: tempDir }); + } else { + await fsExecFile('git', ['init', '--bare', 'remote.git'], { + cwd: tempDir + }); + remoteDir = path.resolve(tempDir, 'remote.git'); + + if (remotes === 2) { + await fsExecFile('git', ['init', '--bare', 'other.git'], { + cwd: tempDir + }); + otherRemoteDir = path.resolve(tempDir, 'other.git'); + } + + await fsExecFile('git', ['clone', 'remote.git', 'test'], { cwd: tempDir }); + + if (remotes === 2) { + await runGit(['remote', 'add', 'other', '../other.git'], gitDir); + } } + + await configureGitUser(gitDir); + + const cleanup = async () => { + if (existsSync(tempDir)) { + await fsRemove(tempDir, { force: true, recursive: true }); + } + }; + return { + tempDir, gitDir, - cleanup, + remoteDir, + otherRemoteDir, + cleanup + }; +}; + +export const prepareTempDir = async (): Promise => { + return fsMakeTempDir(path.join(tmpdir(), 'opencommit-test-')); +}; + +export const prepareRepo = async ( + gitDir: string, + files: Record, + options: { + stage?: string[] | true; + commitMessage?: string; + } = {} +): Promise => { + for (const [relativePath, content] of Object.entries(files)) { + writeRepoFile(gitDir, relativePath, content); + } + + const stageFiles = + options.stage === true + ? Object.keys(files) + : Array.isArray(options.stage) + ? options.stage + : options.commitMessage + ? Object.keys(files) + : []; + + if (stageFiles.length > 0) { + await runGit(['add', ...stageFiles], gitDir); + } + + if (options.commitMessage) { + await runGit(['commit', '-m', options.commitMessage], gitDir); + } +}; + +export const writeRepoFile = ( + gitDir: string, + relativePath: string, + content: string +): void => { + const filePath = path.resolve(gitDir, relativePath); + mkdirSync(path.dirname(filePath), { recursive: true }); + writeFileSync(filePath, content); +}; + +export const appendRepoFile = ( + gitDir: string, + relativePath: string, + content: string +): void => { + const filePath = path.resolve(gitDir, relativePath); + mkdirSync(path.dirname(filePath), { recursive: true }); + appendFileSync(filePath, content); +}; + +export const writeGlobalConfig = ( + homeDir: string, + lines: string[] +): string => { + const configPath = path.resolve(homeDir, '.opencommit'); + writeFileSync(configPath, lines.join('\n')); + return configPath; +}; + +export const seedMigrations = ( + homeDir: string, + completedMigrations: string[] = COMPLETED_MIGRATIONS +): string => { + const migrationsPath = path.resolve(homeDir, '.opencommit_migrations'); + writeFileSync(migrationsPath, JSON.stringify(completedMigrations)); + return migrationsPath; +}; + +export const seedModelCache = async ( + homeDir: string, + models: Record +): Promise => { + const modelCachePath = path.resolve(homeDir, '.opencommit-models.json'); + writeFileSync( + modelCachePath, + JSON.stringify( + { + timestamp: Date.now(), + models + }, + null, + 2 + ) + ); +}; + +export const getMockOpenAiEnv = ( + baseUrl: string, + overrides: NodeJS.ProcessEnv = {} +): NodeJS.ProcessEnv => ({ + OCO_AI_PROVIDER: 'openai', + OCO_API_KEY: 'test-openai-key', + OCO_MODEL: 'gpt-4o-mini', + OCO_API_URL: baseUrl, + OCO_GITPUSH: 'false', + ...overrides +}); + +export const wait = (ms: number) => + new Promise((resolve) => setTimeout(resolve, ms)); + +export const waitForExit = async ( + instance: RenderResult, + timeoutMs: number = 10_000 +): Promise => { + const startedAt = Date.now(); + + while (Date.now() - startedAt < timeoutMs) { + const exit = instance.hasExit(); + if (exit) { + return exit.exitCode; + } + await wait(25); + } + + throw new Error('Process did not exit within the expected timeout'); +}; + +export const getHeadCommitSubject = async (gitDir: string): Promise => { + const { stdout } = await runGit(['log', '-1', '--pretty=%s'], gitDir); + return stdout.trim(); +}; + +export const getHeadCommitFiles = async (gitDir: string): Promise => { + const { stdout } = await runGit( + ['diff-tree', '--root', '--no-commit-id', '--name-only', '-r', 'HEAD'], + gitDir + ); + + return stdout + .split('\n') + .map((file) => file.trim()) + .filter(Boolean) + .sort(); +}; + +export const getShortGitStatus = async (gitDir: string): Promise => { + const { stdout } = await runGit(['status', '--short'], gitDir); + return stdout.trim(); +}; + +export const getCurrentBranchName = async (gitDir: string): Promise => { + const { stdout } = await runGit(['branch', '--show-current'], gitDir); + return stdout.trim(); +}; + +export const getRemoteBranchHeadSubject = async ( + remoteGitDir: string, + branchName: string +): Promise => { + const { stdout = '' } = await fsExecFile( + 'git', + ['--git-dir', remoteGitDir, 'log', '-1', '--pretty=%s', `refs/heads/${branchName}`], + { cwd: process.cwd() } + ); + + return stdout.trim(); +}; + +export const remoteBranchExists = async ( + remoteGitDir: string, + branchName: string +): Promise => { + try { + await fsExecFile( + 'git', + [ + '--git-dir', + remoteGitDir, + 'rev-parse', + '--verify', + '--quiet', + `refs/heads/${branchName}` + ], + { cwd: process.cwd() } + ); + return true; + } catch { + return false; + } +}; + +export const assertHeadCommit = async ( + gitDir: string, + expectedSubject: string +): Promise => { + expect(await getHeadCommitSubject(gitDir)).toBe(expectedSubject); +}; + +export const assertGitStatus = async ( + gitDir: string, + expected: string | RegExp +): Promise => { + const status = await getShortGitStatus(gitDir); + if (typeof expected === 'string') { + expect(status).toContain(expected); + return; } -} -export const prepareTempDir = async(): Promise => { - return await fsMakeTempDir(path.join(tmpdir(), 'opencommit-test-')); -} + expect(status).toMatch(expected); +}; + +export const startMockOpenAiServer = async ( + response: + | string + | ((request: { + authorization?: string; + body: Record | undefined; + requestIndex: number; + }) => { + status?: number; + body: Record; + headers?: Record; + }) +): Promise<{ + authHeaders: string[]; + requestBodies: Array>; + baseUrl: string; + cleanup: () => Promise; +}> => { + const authHeaders: string[] = []; + const requestBodies: Array> = []; + + const server = http.createServer((req, res) => { + const authorization = req.headers.authorization; + if (authorization) { + authHeaders.push( + Array.isArray(authorization) ? authorization[0] : authorization + ); + } -export const wait = (ms: number) => new Promise(resolve => setTimeout(resolve, ms)); + const chunks: Buffer[] = []; + req.on('data', (chunk) => { + chunks.push(Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk)); + }); + req.on('end', () => { + const rawBody = Buffer.concat(chunks).toString('utf8'); + let parsedBody: Record | undefined; + if (rawBody) { + try { + parsedBody = JSON.parse(rawBody); + requestBodies.push(parsedBody); + } catch { + requestBodies.push({ rawBody }); + } + } + + if (req.method === 'POST' && req.url?.includes('/chat/completions')) { + const payload = + typeof response === 'string' + ? { + status: 200, + body: { + choices: [ + { + message: { + content: response + } + } + ] + } + } + : response({ + authorization: Array.isArray(authorization) + ? authorization[0] + : authorization, + body: parsedBody, + requestIndex: requestBodies.length - 1 + }); + + res.writeHead(payload.status ?? 200, { + 'Content-Type': 'application/json', + ...payload.headers + }); + res.end(JSON.stringify(payload.body)); + return; + } + + res.writeHead(404, { 'Content-Type': 'application/json' }); + res.end(JSON.stringify({ error: 'not found' })); + }); + }); + + await new Promise((resolve) => { + server.listen(0, '127.0.0.1', () => resolve()); + }); + + const { port } = server.address() as AddressInfo; + + return { + authHeaders, + requestBodies, + baseUrl: `http://127.0.0.1:${port}/v1`, + cleanup: () => + new Promise((resolve, reject) => { + server.close((error) => { + if (error) { + reject(error); + return; + } + resolve(); + }); + }) + }; +}; diff --git a/test/jest-setup.ts b/test/jest-setup.ts index 392354c1..e524b92d 100644 --- a/test/jest-setup.ts +++ b/test/jest-setup.ts @@ -6,6 +6,7 @@ import { configure } from 'cli-testing-library'; global.jest = jest; /** - * Adjusted the wait time for waitFor/findByText to 2000ms, because the default 1000ms makes the test results flaky + * CLI rendering gets noticeably slower under coverage and on CI, so keep a + * slightly roomier timeout than the library default. */ -configure({ asyncUtilTimeout: 2000 }); +configure({ asyncUtilTimeout: 5000 }); diff --git a/test/unit/config.test.ts b/test/unit/config.test.ts index fc4709dc..5b3e44fc 100644 --- a/test/unit/config.test.ts +++ b/test/unit/config.test.ts @@ -199,6 +199,48 @@ describe('config', () => { expect(config).not.toEqual(null); expect(config.OCO_API_KEY).toEqual(undefined); }); + + it('should not create a global config file when only reading defaults', async () => { + globalConfigFile = await generateConfig('.opencommit', {}); + rmSync(globalConfigFile.filePath); + + const config = getConfig({ + globalPath: globalConfigFile.filePath + }); + + expect(config.OCO_MODEL).toEqual(DEFAULT_CONFIG.OCO_MODEL); + expect(existsSync(globalConfigFile.filePath)).toBe(false); + }); + + it('should not materialize ambient proxy env vars into OCO_PROXY', async () => { + process.env.HTTPS_PROXY = 'http://127.0.0.1:7890'; + + globalConfigFile = await generateConfig('.opencommit', {}); + envConfigFile = await generateConfig('.env', {}); + + const config = getConfig({ + globalPath: globalConfigFile.filePath, + envPath: envConfigFile.filePath + }); + + expect(config.OCO_PROXY).toEqual(undefined); + }); + + it('should parse OCO_PROXY=null from local .env as explicit disable', async () => { + globalConfigFile = await generateConfig('.opencommit', { + OCO_PROXY: 'http://global-proxy:8080' + }); + envConfigFile = await generateConfig('.env', { + OCO_PROXY: 'null' + }); + + const config = getConfig({ + globalPath: globalConfigFile.filePath, + envPath: envConfigFile.filePath + }); + + expect(config.OCO_PROXY).toEqual(null); + }); }); describe('setConfig', () => { @@ -325,5 +367,20 @@ describe('config', () => { const fileContent2 = readFileSync(globalConfigFile.filePath, 'utf8'); expect(fileContent2).toContain('OCO_MODEL=gpt-4'); }); + + it('should persist OCO_PROXY=null as an explicit disable', async () => { + await setConfig( + [[CONFIG_KEYS.OCO_PROXY, null]], + globalConfigFile.filePath + ); + + const config = getConfig({ + globalPath: globalConfigFile.filePath + }); + const fileContent = readFileSync(globalConfigFile.filePath, 'utf8'); + + expect(config.OCO_PROXY).toEqual(null); + expect(fileContent).toContain('OCO_PROXY=null'); + }); }); }); diff --git a/test/unit/errors.test.ts b/test/unit/errors.test.ts new file mode 100644 index 00000000..331a71b6 --- /dev/null +++ b/test/unit/errors.test.ts @@ -0,0 +1,29 @@ +import { + formatUserFriendlyError, + ServiceUnavailableError +} from '../../src/utils/errors'; + +describe('formatUserFriendlyError', () => { + it('should keep provider wording when no custom API URL is configured', () => { + const formatted = formatUserFriendlyError( + new ServiceUnavailableError('openai'), + 'openai' + ); + + expect(formatted.message).toEqual( + 'The openai service is temporarily unavailable.' + ); + }); + + it('should use configured endpoint wording when a custom API URL is provided', () => { + const formatted = formatUserFriendlyError( + new ServiceUnavailableError('openai'), + 'openai', + { baseURL: 'http://127.0.0.1:1234/v1' } + ); + + expect(formatted.message).toContain('configured API endpoint'); + expect(formatted.message).toContain('127.0.0.1:1234'); + expect(formatted.message).not.toContain('openai service'); + }); +}); diff --git a/test/unit/gemini.test.ts b/test/unit/gemini.test.ts index d7024f0b..ed1d6a94 100644 --- a/test/unit/gemini.test.ts +++ b/test/unit/gemini.test.ts @@ -1,96 +1,65 @@ -import { GeminiEngine } from '../../src/engine/gemini'; - -import { GenerativeModel, GoogleGenerativeAI } from '@google/generative-ai'; -import { - ConfigType, - getConfig, - OCO_AI_PROVIDER_ENUM -} from '../../src/commands/config'; import { OpenAI } from 'openai'; +import { GeminiEngine } from '../../src/engine/gemini'; -describe('Gemini', () => { - let gemini: GeminiEngine; - let mockConfig: ConfigType; - let mockGoogleGenerativeAi: GoogleGenerativeAI; - let mockGenerativeModel: GenerativeModel; - let mockExit: jest.SpyInstance; - - const noop: (...args: any[]) => any = (...args: any[]) => {}; - - const mockGemini = () => { - mockConfig = getConfig() as ConfigType; - - gemini = new GeminiEngine({ - apiKey: mockConfig.OCO_API_KEY, - model: mockConfig.OCO_MODEL +describe('GeminiEngine', () => { + it('maps OpenAI-style chat messages into Gemini request payloads', async () => { + const engine = new GeminiEngine({ + apiKey: 'mock-api-key', + model: 'gemini-1.5-flash', + baseURL: 'http://127.0.0.1:8080/v1', + maxTokensOutput: 256, + maxTokensInput: 4096 }); - }; - - const oldEnv = process.env; - beforeEach(() => { - jest.resetModules(); - process.env = { ...oldEnv }; - - jest.mock('@google/generative-ai'); - jest.mock('../src/commands/config'); - - jest.mock('@clack/prompts', () => ({ - intro: jest.fn(), - outro: jest.fn() - })); - - mockExit = jest.spyOn(process, 'exit').mockImplementation(); - - mockConfig = getConfig() as ConfigType; - - mockConfig.OCO_AI_PROVIDER = OCO_AI_PROVIDER_ENUM.GEMINI; - mockConfig.OCO_API_KEY = 'mock-api-key'; - mockConfig.OCO_MODEL = 'gemini-1.5-flash'; - - mockGoogleGenerativeAi = new GoogleGenerativeAI(mockConfig.OCO_API_KEY); - mockGenerativeModel = mockGoogleGenerativeAi.getGenerativeModel({ - model: mockConfig.OCO_MODEL + const generateContent = jest.fn().mockResolvedValue({ + response: { + text: () => 'feat(gemini): translate the diffhidden' + } + }); + const getGenerativeModel = jest.fn().mockReturnValue({ + generateContent }); - }); - - afterEach(() => { - gemini = undefined as any; - }); - - afterAll(() => { - mockExit.mockRestore(); - process.env = oldEnv; - }); - - it.skip('should exit process if OCO_GEMINI_API_KEY is not set and command is not config', () => { - process.env.OCO_GEMINI_API_KEY = undefined; - process.env.OCO_AI_PROVIDER = 'gemini'; - - mockGemini(); - - expect(mockExit).toHaveBeenCalledWith(1); - }); - - it('should generate commit message', async () => { - const mockGenerateContent = jest - .fn() - .mockResolvedValue({ response: { text: () => 'generated content' } }); - mockGenerativeModel.generateContent = mockGenerateContent; - - mockGemini(); - - const messages: Array = - [ - { role: 'system', content: 'system message' }, - { role: 'assistant', content: 'assistant message' } - ]; - - jest - .spyOn(gemini, 'generateCommitMessage') - .mockImplementation(async () => 'generated content'); - const result = await gemini.generateCommitMessage(messages); - expect(result).toEqual('generated content'); + engine.client = { + getGenerativeModel + } as any; + + const messages: Array = [ + { role: 'system', content: 'system message' }, + { role: 'assistant', content: 'assistant guidance' }, + { role: 'user', content: 'diff --git a/file b/file' } + ]; + + const result = await engine.generateCommitMessage(messages); + + expect(result).toEqual('feat(gemini): translate the diff'); + expect(getGenerativeModel).toHaveBeenCalledWith( + { + model: 'gemini-1.5-flash', + systemInstruction: 'system message' + }, + { + baseUrl: 'http://127.0.0.1:8080/v1' + } + ); + expect(generateContent).toHaveBeenCalledWith( + expect.objectContaining({ + contents: [ + { + parts: [{ text: 'assistant guidance' }], + role: 'model' + }, + { + parts: [{ text: 'diff --git a/file b/file' }], + role: 'user' + } + ], + generationConfig: expect.objectContaining({ + maxOutputTokens: 256, + temperature: 0, + topP: 0.1 + }) + }) + ); }); }); diff --git a/test/unit/openAi.test.ts b/test/unit/openAi.test.ts index c1defd5f..2ebeeb39 100644 --- a/test/unit/openAi.test.ts +++ b/test/unit/openAi.test.ts @@ -1,26 +1,71 @@ -// Test the reasoning model detection regex used in OpenAiEngine. -// Integration test with the engine is not possible because mistral.ts -// uses require() which is unavailable in the ESM test environment. -const REASONING_MODEL_RE = /^(o[1-9]|gpt-5)/; - -describe('OpenAiEngine reasoning model detection', () => { - it.each([ - ['o1', true], - ['o1-preview', true], - ['o1-mini', true], - ['o3', true], - ['o3-mini', true], - ['o4-mini', true], - ['gpt-5', true], - ['gpt-5-nano', true], - ['gpt-4o', false], - ['gpt-4o-mini', false], - ['gpt-4', false], - ['gpt-3.5-turbo', false] - ])( - 'model "%s" isReasoning=%s', - (model, expected) => { - expect(REASONING_MODEL_RE.test(model)).toBe(expected); - } - ); +import { OpenAI } from 'openai'; +import { OpenAiEngine } from '../../src/engine/openAi'; + +describe('OpenAiEngine', () => { + const baseConfig = { + apiKey: 'test-openai-key', + maxTokensInput: 4096, + maxTokensOutput: 256 + }; + + const messages: Array = [ + { role: 'system', content: 'system message' }, + { role: 'user', content: 'diff --git a/file b/file' } + ]; + + it('uses max_completion_tokens for reasoning models', async () => { + const engine = new OpenAiEngine({ + ...baseConfig, + model: 'o3-mini' + }); + + const create = jest + .spyOn(engine.client.chat.completions, 'create') + .mockResolvedValue({ + choices: [{ message: { content: 'feat(openai): reasoning path' } }] + } as any); + + await engine.generateCommitMessage(messages); + + expect(create).toHaveBeenCalledWith( + expect.objectContaining({ + model: 'o3-mini', + max_completion_tokens: 256 + }) + ); + expect(create).toHaveBeenCalledWith( + expect.not.objectContaining({ + max_tokens: expect.anything() + }) + ); + }); + + it('uses max_tokens and sampling params for non-reasoning models', async () => { + const engine = new OpenAiEngine({ + ...baseConfig, + model: 'gpt-4o-mini' + }); + + const create = jest + .spyOn(engine.client.chat.completions, 'create') + .mockResolvedValue({ + choices: [{ message: { content: 'feat(openai): standard path' } }] + } as any); + + await engine.generateCommitMessage(messages); + + expect(create).toHaveBeenCalledWith( + expect.objectContaining({ + model: 'gpt-4o-mini', + max_tokens: 256, + temperature: 0, + top_p: 0.1 + }) + ); + expect(create).toHaveBeenCalledWith( + expect.not.objectContaining({ + max_completion_tokens: expect.anything() + }) + ); + }); }); diff --git a/test/unit/proxy.test.ts b/test/unit/proxy.test.ts new file mode 100644 index 00000000..5b8a126a --- /dev/null +++ b/test/unit/proxy.test.ts @@ -0,0 +1,126 @@ +import axios from 'axios'; +import { getGlobalDispatcher } from 'undici'; +import { AnthropicEngine } from '../../src/engine/anthropic'; +import { OpenAiEngine } from '../../src/engine/openAi'; +import { resolveProxy, setupProxy } from '../../src/utils/proxy'; + +describe('proxy utilities', () => { + const originalEnv = { ...process.env }; + const originalAxiosProxy = axios.defaults.proxy; + const originalAxiosHttpAgent = axios.defaults.httpAgent; + const originalAxiosHttpsAgent = axios.defaults.httpsAgent; + + function resetEnv(env: NodeJS.ProcessEnv) { + Object.keys(process.env).forEach((key) => { + if (!(key in env)) { + delete process.env[key]; + } else { + process.env[key] = env[key]; + } + }); + } + + beforeEach(() => { + resetEnv(originalEnv); + setupProxy(undefined); + }); + + afterEach(() => { + resetEnv(originalEnv); + setupProxy(undefined); + axios.defaults.proxy = originalAxiosProxy; + axios.defaults.httpAgent = originalAxiosHttpAgent; + axios.defaults.httpsAgent = originalAxiosHttpsAgent; + }); + + it('should prefer an explicit proxy URL over ambient proxy env vars', () => { + process.env.HTTPS_PROXY = 'http://ambient-proxy:8080'; + + expect(resolveProxy('http://explicit-proxy:8080')).toEqual( + 'http://explicit-proxy:8080' + ); + }); + + it('should return null when proxy is explicitly disabled', () => { + process.env.HTTPS_PROXY = 'http://ambient-proxy:8080'; + + expect(resolveProxy(null)).toEqual(null); + }); + + it('should fall back to ambient proxy env vars when proxy is unset', () => { + process.env.HTTPS_PROXY = 'http://ambient-proxy:8080'; + + expect(resolveProxy(undefined)).toEqual('http://ambient-proxy:8080'); + }); + + it('should disable proxy usage when setupProxy receives null', () => { + process.env.HTTPS_PROXY = 'http://ambient-proxy:8080'; + + setupProxy(null); + + expect(getGlobalDispatcher().constructor.name).toEqual('Agent'); + expect(axios.defaults.proxy).toEqual(false); + expect(axios.defaults.httpAgent).toBeUndefined(); + expect(axios.defaults.httpsAgent).toBeUndefined(); + }); + + it('should install proxy agents when setupProxy receives a proxy URL', () => { + setupProxy('http://127.0.0.1:7890'); + + expect(getGlobalDispatcher().constructor.name).toEqual('ProxyAgent'); + expect(axios.defaults.proxy).toEqual(false); + expect(axios.defaults.httpAgent).toBeDefined(); + expect(axios.defaults.httpsAgent).toBeDefined(); + }); +}); + +describe('engine proxy handling', () => { + const originalEnv = { ...process.env }; + const baseConfig = { + apiKey: 'test-key', + model: 'gpt-4o-mini', + maxTokensInput: 4096, + maxTokensOutput: 256 + }; + + function resetEnv(env: NodeJS.ProcessEnv) { + Object.keys(process.env).forEach((key) => { + if (!(key in env)) { + delete process.env[key]; + } else { + process.env[key] = env[key]; + } + }); + } + + beforeEach(() => { + resetEnv(originalEnv); + }); + + afterEach(() => { + resetEnv(originalEnv); + }); + + it('should not let OpenAI engine re-read proxy env vars when proxy is unset', () => { + process.env.HTTPS_PROXY = 'http://ambient-proxy:8080'; + + const engine = new OpenAiEngine({ + ...baseConfig, + proxy: undefined + }); + + expect(engine.client.httpAgent).toBeUndefined(); + }); + + it('should not let Anthropic engine re-read proxy env vars when proxy is unset', () => { + process.env.HTTPS_PROXY = 'http://ambient-proxy:8080'; + + const engine = new AnthropicEngine({ + ...baseConfig, + model: 'claude-sonnet-4-20250514', + proxy: undefined + }); + + expect(engine.client.httpAgent).toBeUndefined(); + }); +});