@jer-y/copilot-proxy 0.2.1 → 0.2.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/main.js +6 -6
- package/dist/{pid-DtjYMiQS.js → pid-BQbC5Hf2.js} +6 -2
- package/dist/{pid-DtjYMiQS.js.map → pid-BQbC5Hf2.js.map} +1 -1
- package/dist/{start-BNocR0hU.js → start-BwtzhMKZ.js} +2 -2
- package/dist/{start-BNocR0hU.js.map → start-BwtzhMKZ.js.map} +1 -1
- package/dist/start-CELRn1-3.js +6 -0
- package/dist/{supervisor-wpaa2IAJ.js → supervisor-C2pujTsK.js} +2 -2
- package/dist/{supervisor-wpaa2IAJ.js.map → supervisor-C2pujTsK.js.map} +1 -1
- package/dist/{win32-D1-MlKl7.js → win32-DAStPxcF.js} +63 -5
- package/dist/win32-DAStPxcF.js.map +1 -0
- package/package.json +1 -1
- package/dist/start-CUT1hJrN.js +0 -6
- package/dist/win32-D1-MlKl7.js.map +0 -1
package/dist/main.js
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import { PATHS, ensurePaths } from "./paths-CA6OZ0WA.js";
|
|
3
3
|
import { loadDaemonConfig } from "./config-D1kMGXKU.js";
|
|
4
|
-
import { isDaemonRunning, isProcessRunning, readPid, removePidFile } from "./pid-
|
|
5
|
-
import { daemonStart } from "./start-
|
|
4
|
+
import { isDaemonRunning, isProcessRunning, readPid, removePidFile } from "./pid-BQbC5Hf2.js";
|
|
5
|
+
import { daemonStart } from "./start-BwtzhMKZ.js";
|
|
6
6
|
import { defineCommand, runMain } from "citty";
|
|
7
7
|
import consola from "consola";
|
|
8
8
|
import fs from "node:fs/promises";
|
|
@@ -389,7 +389,7 @@ const disable = defineCommand({
|
|
|
389
389
|
const { uninstallAutoStart } = await import("./darwin-BVmd1DeO.js");
|
|
390
390
|
success = await uninstallAutoStart();
|
|
391
391
|
} else if (platform === "win32") {
|
|
392
|
-
const { uninstallAutoStart } = await import("./win32-
|
|
392
|
+
const { uninstallAutoStart } = await import("./win32-DAStPxcF.js");
|
|
393
393
|
success = await uninstallAutoStart();
|
|
394
394
|
} else {
|
|
395
395
|
consola.error(`Unsupported platform: ${platform}`);
|
|
@@ -426,7 +426,7 @@ const enable = defineCommand({
|
|
|
426
426
|
const { installAutoStart } = await import("./darwin-BVmd1DeO.js");
|
|
427
427
|
success = await installAutoStart(execPath, args);
|
|
428
428
|
} else if (platform === "win32") {
|
|
429
|
-
const { installAutoStart } = await import("./win32-
|
|
429
|
+
const { installAutoStart } = await import("./win32-DAStPxcF.js");
|
|
430
430
|
success = await installAutoStart(execPath, args);
|
|
431
431
|
} else {
|
|
432
432
|
consola.error(`Unsupported platform: ${platform}`);
|
|
@@ -1974,7 +1974,7 @@ const start = defineCommand({
|
|
|
1974
1974
|
consola.error("Supervisor mode: daemon config not found");
|
|
1975
1975
|
process.exit(1);
|
|
1976
1976
|
}
|
|
1977
|
-
const { runAsSupervisor } = await import("./supervisor-
|
|
1977
|
+
const { runAsSupervisor } = await import("./supervisor-C2pujTsK.js");
|
|
1978
1978
|
const options = {
|
|
1979
1979
|
port: config.port,
|
|
1980
1980
|
verbose: config.verbose,
|
|
@@ -1994,7 +1994,7 @@ const start = defineCommand({
|
|
|
1994
1994
|
consola.error("Cannot use --claude-code with --daemon (interactive mode)");
|
|
1995
1995
|
process.exit(1);
|
|
1996
1996
|
}
|
|
1997
|
-
const { daemonStart: daemonStart$1 } = await import("./start-
|
|
1997
|
+
const { daemonStart: daemonStart$1 } = await import("./start-CELRn1-3.js");
|
|
1998
1998
|
daemonStart$1({
|
|
1999
1999
|
port,
|
|
2000
2000
|
verbose: args.verbose,
|
|
@@ -107,7 +107,11 @@ function getProcessStartTime(pid) {
|
|
|
107
107
|
} catch {
|
|
108
108
|
const output = execSync(`ps -p ${pid} -o lstart=`, {
|
|
109
109
|
stdio: "pipe",
|
|
110
|
-
encoding: "utf8"
|
|
110
|
+
encoding: "utf8",
|
|
111
|
+
env: {
|
|
112
|
+
...process.env,
|
|
113
|
+
LC_ALL: "C"
|
|
114
|
+
}
|
|
111
115
|
}).trim();
|
|
112
116
|
if (!output) return null;
|
|
113
117
|
const parsed = Date.parse(output);
|
|
@@ -135,4 +139,4 @@ function isDaemonRunning() {
|
|
|
135
139
|
|
|
136
140
|
//#endregion
|
|
137
141
|
export { isDaemonRunning, isProcessRunning, readPid, removePidFile, writePid };
|
|
138
|
-
//# sourceMappingURL=pid-
|
|
142
|
+
//# sourceMappingURL=pid-BQbC5Hf2.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"pid-
|
|
1
|
+
{"version":3,"file":"pid-BQbC5Hf2.js","names":["pid","error: unknown","cmdline: string"],"sources":["../src/daemon/pid.ts"],"sourcesContent":["import { execSync } from 'node:child_process'\nimport fs from 'node:fs'\nimport process from 'node:process'\n\nimport { PATHS } from '~/lib/paths'\n\nexport interface DaemonPidInfo {\n pid: number\n startTime: number\n}\n\nexport function writePid(pid: number, startTime?: number): void {\n fs.mkdirSync(PATHS.APP_DIR, { recursive: true })\n const pidPath = PATHS.DAEMON_PID\n fs.writeFileSync(pidPath, `${pid}\\n${startTime ?? Date.now()}`, { mode: 0o600 })\n // Ensure permissions are correct even if file already existed with wider perms\n try {\n fs.chmodSync(pidPath, 0o600)\n }\n catch {}\n}\n\nexport function readPid(): DaemonPidInfo | null {\n try {\n const content = fs.readFileSync(PATHS.DAEMON_PID, 'utf8').trim()\n const lines = content.split('\\n')\n if (lines.length < 2) {\n // Legacy format: just PID\n const pid = Number.parseInt(lines[0], 10)\n if (Number.isNaN(pid) || pid <= 0 || String(pid) !== lines[0])\n return null\n return { pid, startTime: 0 }\n }\n const pid = Number.parseInt(lines[0], 10)\n const startTime = Number.parseInt(lines[1], 10)\n if (Number.isNaN(pid) || pid <= 0 || String(pid) !== lines[0])\n return null\n if (Number.isNaN(startTime))\n return null\n return { pid, startTime }\n }\n catch {\n return null\n }\n}\n\nexport function removePidFile(): void {\n try {\n fs.unlinkSync(PATHS.DAEMON_PID)\n }\n catch {}\n}\n\nexport function isProcessRunning(pid: number): boolean {\n try {\n process.kill(pid, 0)\n return true\n }\n catch (error: unknown) {\n if (error instanceof Error && 'code' in error && error.code === 'EPERM') {\n return true\n }\n return false\n }\n}\n\n/**\n * Check if a running process is actually our daemon supervisor.\n * Uses two checks:\n * 1. Command line must contain '_supervisor' flag\n * 2. Process start time must match the recorded startTime in PID file\n * (within tolerance, to prevent spoofing with a fake process)\n */\nfunction isOurDaemonProcess(pid: number, recordedStartTime: number): boolean {\n try {\n // Check command line\n let cmdline: string\n if (process.platform === 'win32') {\n cmdline = execSync(\n `wmic process where ProcessId=${pid} get CommandLine /format:list`,\n { stdio: 'pipe', encoding: 'utf8' },\n )\n }\n else {\n try {\n cmdline = fs.readFileSync(`/proc/${pid}/cmdline`, 'utf8')\n }\n catch {\n cmdline = execSync(`ps -p ${pid} -o command=`, {\n stdio: 'pipe',\n encoding: 'utf8',\n })\n }\n }\n\n if (!cmdline.includes('_supervisor'))\n return false\n\n // If no recorded start time (legacy), command line check alone is sufficient\n if (recordedStartTime === 0)\n return true\n\n // Check process start time matches what we recorded\n const processStartMs = getProcessStartTime(pid)\n if (processStartMs === null)\n return false\n\n // Allow 5s tolerance between when we recorded the time and when the OS recorded process start\n return Math.abs(processStartMs - recordedStartTime) < 5000\n }\n catch {\n return false\n }\n}\n\n/**\n * Get the start time of a process in milliseconds since epoch.\n * Returns null if unavailable.\n */\nfunction getProcessStartTime(pid: number): number | null {\n try {\n if (process.platform === 'win32') {\n const output = execSync(\n `wmic process where ProcessId=${pid} get CreationDate /format:list`,\n { stdio: 'pipe', encoding: 'utf8' },\n )\n // Format: CreationDate=20260302123456.123456+480 (offset is minutes from UTC)\n const match = output.match(/CreationDate=(\\d{4})(\\d{2})(\\d{2})(\\d{2})(\\d{2})(\\d{2})\\.\\d+([+-]\\d+)/)\n if (!match)\n return null\n const [, y, m, d, h, min, s, offsetStr] = match\n const offsetMinutes = Number.parseInt(offsetStr, 10)\n // Parse as local time then adjust by the UTC offset\n const localMs = new Date(`${y}-${m}-${d}T${h}:${min}:${s}Z`).getTime()\n return localMs - offsetMinutes * 60 * 1000\n }\n else {\n // Linux: read /proc/<pid>/stat, field 22 is starttime in clock ticks since boot\n try {\n const stat = fs.readFileSync(`/proc/${pid}/stat`, 'utf8')\n // Fields are space-separated, but field 2 (comm) can contain spaces/parens\n // Find the last ')' to skip past comm field\n const afterComm = stat.substring(stat.lastIndexOf(')') + 2)\n const fields = afterComm.split(' ')\n // starttime is field 22 overall, which is field 20 after comm (0-indexed: index 19)\n const startTicks = Number.parseInt(fields[19], 10)\n if (Number.isNaN(startTicks))\n return null\n\n // Get system boot time and clock ticks per second\n const uptimeStr = fs.readFileSync('/proc/uptime', 'utf8')\n const uptimeSec = Number.parseFloat(uptimeStr.split(' ')[0])\n // CLK_TCK is typically 100 on Linux\n const clkTck = 100\n const processUptimeSec = startTicks / clkTck\n const bootTimeMs = Date.now() - uptimeSec * 1000\n return bootTimeMs + processUptimeSec * 1000\n }\n catch {\n // macOS: use ps -p <pid> -o lstart=\n const output = execSync(`ps -p ${pid} -o lstart=`, {\n stdio: 'pipe',\n encoding: 'utf8',\n env: { ...process.env, LC_ALL: 'C' },\n }).trim()\n if (!output)\n return null\n const parsed = Date.parse(output)\n return Number.isNaN(parsed) ? null : parsed\n }\n }\n }\n catch {\n return null\n }\n}\n\n/**\n * Check if the PID from the PID file is likely still our daemon process.\n * Verifies that the process is alive AND that its command line contains\n * the _supervisor flag AND that its start time matches what we recorded.\n */\nexport function isDaemonRunning(): { running: true, pid: number } | { running: false } {\n const info = readPid()\n if (info === null)\n return { running: false }\n if (!isProcessRunning(info.pid))\n return { running: false }\n\n // Verify the process is actually our daemon\n if (!isOurDaemonProcess(info.pid, info.startTime))\n return { running: false }\n\n return { running: true, pid: info.pid }\n}\n"],"mappings":";;;;;;AAWA,SAAgB,SAAS,KAAa,WAA0B;AAC9D,IAAG,UAAU,MAAM,SAAS,EAAE,WAAW,MAAM,CAAC;CAChD,MAAM,UAAU,MAAM;AACtB,IAAG,cAAc,SAAS,GAAG,IAAI,IAAI,aAAa,KAAK,KAAK,IAAI,EAAE,MAAM,KAAO,CAAC;AAEhF,KAAI;AACF,KAAG,UAAU,SAAS,IAAM;SAExB;;AAGR,SAAgB,UAAgC;AAC9C,KAAI;EAEF,MAAM,QADU,GAAG,aAAa,MAAM,YAAY,OAAO,CAAC,MAAM,CAC1C,MAAM,KAAK;AACjC,MAAI,MAAM,SAAS,GAAG;GAEpB,MAAMA,QAAM,OAAO,SAAS,MAAM,IAAI,GAAG;AACzC,OAAI,OAAO,MAAMA,MAAI,IAAIA,SAAO,KAAK,OAAOA,MAAI,KAAK,MAAM,GACzD,QAAO;AACT,UAAO;IAAE;IAAK,WAAW;IAAG;;EAE9B,MAAM,MAAM,OAAO,SAAS,MAAM,IAAI,GAAG;EACzC,MAAM,YAAY,OAAO,SAAS,MAAM,IAAI,GAAG;AAC/C,MAAI,OAAO,MAAM,IAAI,IAAI,OAAO,KAAK,OAAO,IAAI,KAAK,MAAM,GACzD,QAAO;AACT,MAAI,OAAO,MAAM,UAAU,CACzB,QAAO;AACT,SAAO;GAAE;GAAK;GAAW;SAErB;AACJ,SAAO;;;AAIX,SAAgB,gBAAsB;AACpC,KAAI;AACF,KAAG,WAAW,MAAM,WAAW;SAE3B;;AAGR,SAAgB,iBAAiB,KAAsB;AACrD,KAAI;AACF,UAAQ,KAAK,KAAK,EAAE;AACpB,SAAO;UAEFC,OAAgB;AACrB,MAAI,iBAAiB,SAAS,UAAU,SAAS,MAAM,SAAS,QAC9D,QAAO;AAET,SAAO;;;;;;;;;;AAWX,SAAS,mBAAmB,KAAa,mBAAoC;AAC3E,KAAI;EAEF,IAAIC;AACJ,MAAI,QAAQ,aAAa,QACvB,WAAU,SACR,gCAAgC,IAAI,gCACpC;GAAE,OAAO;GAAQ,UAAU;GAAQ,CACpC;MAGD,KAAI;AACF,aAAU,GAAG,aAAa,SAAS,IAAI,WAAW,OAAO;UAErD;AACJ,aAAU,SAAS,SAAS,IAAI,eAAe;IAC7C,OAAO;IACP,UAAU;IACX,CAAC;;AAIN,MAAI,CAAC,QAAQ,SAAS,cAAc,CAClC,QAAO;AAGT,MAAI,sBAAsB,EACxB,QAAO;EAGT,MAAM,iBAAiB,oBAAoB,IAAI;AAC/C,MAAI,mBAAmB,KACrB,QAAO;AAGT,SAAO,KAAK,IAAI,iBAAiB,kBAAkB,GAAG;SAElD;AACJ,SAAO;;;;;;;AAQX,SAAS,oBAAoB,KAA4B;AACvD,KAAI;AACF,MAAI,QAAQ,aAAa,SAAS;GAMhC,MAAM,QALS,SACb,gCAAgC,IAAI,iCACpC;IAAE,OAAO;IAAQ,UAAU;IAAQ,CACpC,CAEoB,MAAM,wEAAwE;AACnG,OAAI,CAAC,MACH,QAAO;GACT,MAAM,GAAG,GAAG,GAAG,GAAG,GAAG,KAAK,GAAG,aAAa;GAC1C,MAAM,gBAAgB,OAAO,SAAS,WAAW,GAAG;AAGpD,2BADgB,IAAI,KAAK,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,IAAI,GAAG,EAAE,GAAG,EAAC,SAAS,GACrD,gBAAgB,KAAK;QAItC,KAAI;GACF,MAAM,OAAO,GAAG,aAAa,SAAS,IAAI,QAAQ,OAAO;GAIzD,MAAM,SADY,KAAK,UAAU,KAAK,YAAY,IAAI,GAAG,EAAE,CAClC,MAAM,IAAI;GAEnC,MAAM,aAAa,OAAO,SAAS,OAAO,KAAK,GAAG;AAClD,OAAI,OAAO,MAAM,WAAW,CAC1B,QAAO;GAGT,MAAM,YAAY,GAAG,aAAa,gBAAgB,OAAO;GACzD,MAAM,YAAY,OAAO,WAAW,UAAU,MAAM,IAAI,CAAC,GAAG;GAG5D,MAAM,mBAAmB,aADV;AAGf,UADmB,KAAK,KAAK,GAAG,YAAY,MACxB,mBAAmB;UAEnC;GAEJ,MAAM,SAAS,SAAS,SAAS,IAAI,cAAc;IACjD,OAAO;IACP,UAAU;IACV,KAAK;KAAE,GAAG,QAAQ;KAAK,QAAQ;KAAK;IACrC,CAAC,CAAC,MAAM;AACT,OAAI,CAAC,OACH,QAAO;GACT,MAAM,SAAS,KAAK,MAAM,OAAO;AACjC,UAAO,OAAO,MAAM,OAAO,GAAG,OAAO;;SAIrC;AACJ,SAAO;;;;;;;;AASX,SAAgB,kBAAuE;CACrF,MAAM,OAAO,SAAS;AACtB,KAAI,SAAS,KACX,QAAO,EAAE,SAAS,OAAO;AAC3B,KAAI,CAAC,iBAAiB,KAAK,IAAI,CAC7B,QAAO,EAAE,SAAS,OAAO;AAG3B,KAAI,CAAC,mBAAmB,KAAK,KAAK,KAAK,UAAU,CAC/C,QAAO,EAAE,SAAS,OAAO;AAE3B,QAAO;EAAE,SAAS;EAAM,KAAK,KAAK;EAAK"}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { PATHS } from "./paths-CA6OZ0WA.js";
|
|
2
2
|
import { saveDaemonConfig } from "./config-D1kMGXKU.js";
|
|
3
|
-
import { isDaemonRunning, removePidFile, writePid } from "./pid-
|
|
3
|
+
import { isDaemonRunning, removePidFile, writePid } from "./pid-BQbC5Hf2.js";
|
|
4
4
|
import consola from "consola";
|
|
5
5
|
import process from "node:process";
|
|
6
6
|
import fs from "node:fs";
|
|
@@ -104,4 +104,4 @@ function daemonStart(config) {
|
|
|
104
104
|
|
|
105
105
|
//#endregion
|
|
106
106
|
export { daemonStart };
|
|
107
|
-
//# sourceMappingURL=start-
|
|
107
|
+
//# sourceMappingURL=start-BwtzhMKZ.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"start-
|
|
1
|
+
{"version":3,"file":"start-BwtzhMKZ.js","names":[],"sources":["../src/daemon/start.ts"],"sourcesContent":["import type { DaemonConfig } from '~/daemon/config'\nimport { spawn } from 'node:child_process'\nimport fs from 'node:fs'\nimport process from 'node:process'\n\nimport consola from 'consola'\nimport { saveDaemonConfig } from '~/daemon/config'\nimport { isDaemonRunning, removePidFile, writePid } from '~/daemon/pid'\nimport { PATHS } from '~/lib/paths'\n\nconst LOCK_PATH = `${PATHS.DAEMON_PID}.lock`\n\nfunction acquireLock(): boolean {\n try {\n fs.mkdirSync(PATHS.APP_DIR, { recursive: true })\n // O_CREAT | O_EXCL — fails if file already exists (atomic)\n const fd = fs.openSync(LOCK_PATH, 'wx')\n fs.writeSync(fd, String(process.pid))\n fs.closeSync(fd)\n return true\n }\n catch {\n return false\n }\n}\n\nfunction releaseLock(): void {\n try {\n fs.unlinkSync(LOCK_PATH)\n }\n catch {}\n}\n\nfunction ensureLock(): void {\n if (acquireLock())\n return\n\n // Check if the lock is stale (owner process dead)\n try {\n const lockPid = Number.parseInt(fs.readFileSync(LOCK_PATH, 'utf8').trim(), 10)\n if (!Number.isNaN(lockPid) && lockPid > 0) {\n try {\n process.kill(lockPid, 0)\n // Lock holder is alive — genuine concurrent start\n consola.error('Another start operation is in progress')\n process.exit(1)\n }\n catch {\n // Lock holder is dead — stale lock, remove and retry\n releaseLock()\n if (!acquireLock()) {\n consola.error('Failed to acquire start lock')\n process.exit(1)\n }\n }\n }\n else {\n releaseLock()\n if (!acquireLock()) {\n consola.error('Failed to acquire start lock')\n process.exit(1)\n }\n }\n }\n catch {\n consola.error('Failed to acquire start lock')\n process.exit(1)\n }\n}\n\nexport function daemonStart(config: DaemonConfig): void {\n // Acquire lock to prevent concurrent starts.\n // ensureLock() calls process.exit() before lock is held,\n // so no cleanup needed in that path.\n ensureLock()\n\n // From here on, we hold the lock. Always release before exiting.\n const exitWithLock = (code: number): never => {\n releaseLock()\n process.exit(code)\n throw new Error('unreachable')\n }\n\n // Check if already running\n const daemon = isDaemonRunning()\n if (daemon.running) {\n consola.error(`Daemon is already running (PID: ${daemon.pid})`)\n exitWithLock(1)\n }\n\n // Save config for restart/enable\n saveDaemonConfig(config)\n\n // If a github token was provided, persist it to the token file\n // so the supervisor can use it (we don't store tokens in daemon.json)\n if (config.githubToken) {\n fs.mkdirSync(PATHS.APP_DIR, { recursive: true })\n fs.writeFileSync(PATHS.GITHUB_TOKEN_PATH, config.githubToken, { mode: 0o600 })\n try {\n fs.chmodSync(PATHS.GITHUB_TOKEN_PATH, 0o600)\n }\n catch {}\n }\n\n // Resolve the executable path\n const execPath = process.argv[0]\n const scriptPath = process.argv[1]\n\n const logStream = fs.openSync(PATHS.DAEMON_LOG, 'a', 0o600)\n // Ensure permissions are correct even if file already existed with wider perms\n try {\n fs.fchmodSync(logStream, 0o600)\n }\n catch {}\n\n const child = spawn(execPath, [scriptPath, 'start', '--_supervisor'], {\n detached: true,\n stdio: ['ignore', logStream, logStream],\n env: process.env,\n })\n\n if (child.pid === undefined) {\n consola.error('Failed to start daemon process')\n removePidFile()\n return exitWithLock(1)\n }\n\n writePid(child.pid)\n child.unref()\n\n consola.success(`Daemon started (PID: ${child.pid})`)\n consola.info(`Logs: ${PATHS.DAEMON_LOG}`)\n exitWithLock(0)\n}\n"],"mappings":";;;;;;;;;AAUA,MAAM,YAAY,GAAG,MAAM,WAAW;AAEtC,SAAS,cAAuB;AAC9B,KAAI;AACF,KAAG,UAAU,MAAM,SAAS,EAAE,WAAW,MAAM,CAAC;EAEhD,MAAM,KAAK,GAAG,SAAS,WAAW,KAAK;AACvC,KAAG,UAAU,IAAI,OAAO,QAAQ,IAAI,CAAC;AACrC,KAAG,UAAU,GAAG;AAChB,SAAO;SAEH;AACJ,SAAO;;;AAIX,SAAS,cAAoB;AAC3B,KAAI;AACF,KAAG,WAAW,UAAU;SAEpB;;AAGR,SAAS,aAAmB;AAC1B,KAAI,aAAa,CACf;AAGF,KAAI;EACF,MAAM,UAAU,OAAO,SAAS,GAAG,aAAa,WAAW,OAAO,CAAC,MAAM,EAAE,GAAG;AAC9E,MAAI,CAAC,OAAO,MAAM,QAAQ,IAAI,UAAU,EACtC,KAAI;AACF,WAAQ,KAAK,SAAS,EAAE;AAExB,WAAQ,MAAM,yCAAyC;AACvD,WAAQ,KAAK,EAAE;UAEX;AAEJ,gBAAa;AACb,OAAI,CAAC,aAAa,EAAE;AAClB,YAAQ,MAAM,+BAA+B;AAC7C,YAAQ,KAAK,EAAE;;;OAIhB;AACH,gBAAa;AACb,OAAI,CAAC,aAAa,EAAE;AAClB,YAAQ,MAAM,+BAA+B;AAC7C,YAAQ,KAAK,EAAE;;;SAIf;AACJ,UAAQ,MAAM,+BAA+B;AAC7C,UAAQ,KAAK,EAAE;;;AAInB,SAAgB,YAAY,QAA4B;AAItD,aAAY;CAGZ,MAAM,gBAAgB,SAAwB;AAC5C,eAAa;AACb,UAAQ,KAAK,KAAK;AAClB,QAAM,IAAI,MAAM,cAAc;;CAIhC,MAAM,SAAS,iBAAiB;AAChC,KAAI,OAAO,SAAS;AAClB,UAAQ,MAAM,mCAAmC,OAAO,IAAI,GAAG;AAC/D,eAAa,EAAE;;AAIjB,kBAAiB,OAAO;AAIxB,KAAI,OAAO,aAAa;AACtB,KAAG,UAAU,MAAM,SAAS,EAAE,WAAW,MAAM,CAAC;AAChD,KAAG,cAAc,MAAM,mBAAmB,OAAO,aAAa,EAAE,MAAM,KAAO,CAAC;AAC9E,MAAI;AACF,MAAG,UAAU,MAAM,mBAAmB,IAAM;UAExC;;CAIR,MAAM,WAAW,QAAQ,KAAK;CAC9B,MAAM,aAAa,QAAQ,KAAK;CAEhC,MAAM,YAAY,GAAG,SAAS,MAAM,YAAY,KAAK,IAAM;AAE3D,KAAI;AACF,KAAG,WAAW,WAAW,IAAM;SAE3B;CAEN,MAAM,QAAQ,MAAM,UAAU;EAAC;EAAY;EAAS;EAAgB,EAAE;EACpE,UAAU;EACV,OAAO;GAAC;GAAU;GAAW;GAAU;EACvC,KAAK,QAAQ;EACd,CAAC;AAEF,KAAI,MAAM,QAAQ,QAAW;AAC3B,UAAQ,MAAM,iCAAiC;AAC/C,iBAAe;AACf,SAAO,aAAa,EAAE;;AAGxB,UAAS,MAAM,IAAI;AACnB,OAAM,OAAO;AAEb,SAAQ,QAAQ,wBAAwB,MAAM,IAAI,GAAG;AACrD,SAAQ,KAAK,SAAS,MAAM,aAAa;AACzC,cAAa,EAAE"}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import "./paths-CA6OZ0WA.js";
|
|
2
|
-
import { readPid, removePidFile, writePid } from "./pid-
|
|
2
|
+
import { readPid, removePidFile, writePid } from "./pid-BQbC5Hf2.js";
|
|
3
3
|
import consola from "consola";
|
|
4
4
|
import process from "node:process";
|
|
5
5
|
|
|
@@ -40,4 +40,4 @@ async function runAsSupervisor(runFn) {
|
|
|
40
40
|
|
|
41
41
|
//#endregion
|
|
42
42
|
export { runAsSupervisor };
|
|
43
|
-
//# sourceMappingURL=supervisor-
|
|
43
|
+
//# sourceMappingURL=supervisor-C2pujTsK.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"supervisor-
|
|
1
|
+
{"version":3,"file":"supervisor-C2pujTsK.js","names":[],"sources":["../src/daemon/supervisor.ts"],"sourcesContent":["import process from 'node:process'\nimport consola from 'consola'\n\nimport { readPid, removePidFile, writePid } from '~/daemon/pid'\n\nconst MAX_BACKOFF_MS = 60_000\nconst STABLE_THRESHOLD_MS = 60_000\n\nexport async function runAsSupervisor(runFn: () => Promise<void>): Promise<void> {\n let backoffMs = 1000\n let lastStartTime = Date.now()\n\n // Capture a fixed start time once. All subsequent writePid calls\n // reuse this value so it stays close to the OS process start time,\n // preventing isDaemonRunning() from rejecting us after crash-restarts\n // or PID file self-healing.\n const supervisorStartTime = Date.now()\n\n // Write PID file so status/stop/restart can find us.\n // This covers both the start -d path (where parent already wrote it)\n // and the enable path (where _supervisor is launched directly by the OS).\n writePid(process.pid, supervisorStartTime)\n\n const cleanup = () => {\n removePidFile()\n process.exit(0)\n }\n\n process.on('SIGTERM', cleanup)\n process.on('SIGINT', cleanup)\n\n // On Windows, SIGTERM doesn't fire - use 'exit' as fallback to clean up PID file\n if (process.platform === 'win32') {\n process.on('exit', () => {\n removePidFile()\n })\n }\n\n while (true) {\n // Self-heal: restore PID file if it was deleted externally.\n // Uses the fixed supervisorStartTime so the timestamp stays consistent\n // with the OS process start time.\n const existing = readPid()\n if (existing === null || existing.pid !== process.pid) {\n writePid(process.pid, supervisorStartTime)\n }\n lastStartTime = Date.now()\n try {\n await runFn()\n // runFn resolved normally — shouldn't happen for a long-running server,\n // but if it does, break the loop\n break\n }\n catch (error) {\n const uptime = Date.now() - lastStartTime\n consola.error('Server crashed:', error)\n\n if (uptime > STABLE_THRESHOLD_MS) {\n backoffMs = 1000\n }\n\n consola.info(`Restarting in ${backoffMs / 1000}s...`)\n await new Promise(resolve => setTimeout(resolve, backoffMs))\n backoffMs = Math.min(backoffMs * 2, MAX_BACKOFF_MS)\n }\n }\n}\n"],"mappings":";;;;;;AAKA,MAAM,iBAAiB;AACvB,MAAM,sBAAsB;AAE5B,eAAsB,gBAAgB,OAA2C;CAC/E,IAAI,YAAY;CAChB,IAAI,gBAAgB,KAAK,KAAK;CAM9B,MAAM,sBAAsB,KAAK,KAAK;AAKtC,UAAS,QAAQ,KAAK,oBAAoB;CAE1C,MAAM,gBAAgB;AACpB,iBAAe;AACf,UAAQ,KAAK,EAAE;;AAGjB,SAAQ,GAAG,WAAW,QAAQ;AAC9B,SAAQ,GAAG,UAAU,QAAQ;AAG7B,KAAI,QAAQ,aAAa,QACvB,SAAQ,GAAG,cAAc;AACvB,iBAAe;GACf;AAGJ,QAAO,MAAM;EAIX,MAAM,WAAW,SAAS;AAC1B,MAAI,aAAa,QAAQ,SAAS,QAAQ,QAAQ,IAChD,UAAS,QAAQ,KAAK,oBAAoB;AAE5C,kBAAgB,KAAK,KAAK;AAC1B,MAAI;AACF,SAAM,OAAO;AAGb;WAEK,OAAO;GACZ,MAAM,SAAS,KAAK,KAAK,GAAG;AAC5B,WAAQ,MAAM,mBAAmB,MAAM;AAEvC,OAAI,SAAS,oBACX,aAAY;AAGd,WAAQ,KAAK,iBAAiB,YAAY,IAAK,MAAM;AACrD,SAAM,IAAI,SAAQ,YAAW,WAAW,SAAS,UAAU,CAAC;AAC5D,eAAY,KAAK,IAAI,YAAY,GAAG,eAAe"}
|
|
@@ -34,13 +34,46 @@ function winQuoteArg(arg) {
|
|
|
34
34
|
result += "\"";
|
|
35
35
|
return result;
|
|
36
36
|
}
|
|
37
|
-
|
|
37
|
+
/**
|
|
38
|
+
* Detect whether conhost --headless is available (Win10 1809+).
|
|
39
|
+
* Falls back to direct execution on older Windows versions.
|
|
40
|
+
*/
|
|
41
|
+
function supportsHeadlessConhost() {
|
|
42
|
+
try {
|
|
43
|
+
execFileSync("conhost.exe", [
|
|
44
|
+
"--headless",
|
|
45
|
+
"cmd.exe",
|
|
46
|
+
"/c",
|
|
47
|
+
"exit",
|
|
48
|
+
"0"
|
|
49
|
+
], {
|
|
50
|
+
stdio: "pipe",
|
|
51
|
+
timeout: 5e3
|
|
52
|
+
});
|
|
53
|
+
return true;
|
|
54
|
+
} catch {
|
|
55
|
+
return false;
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
function buildTaskXml(execPath, args, options = {}) {
|
|
38
59
|
const xmlArgs = args.map((a) => winQuoteArg(a)).join(" ");
|
|
39
|
-
const
|
|
60
|
+
const headless = options.useHeadlessConhost ?? supportsHeadlessConhost();
|
|
61
|
+
let command;
|
|
62
|
+
let commandArgs;
|
|
63
|
+
if (headless) {
|
|
64
|
+
command = "conhost.exe";
|
|
65
|
+
commandArgs = `--headless ${winQuoteArg(execPath)} ${xmlArgs}`;
|
|
66
|
+
} else {
|
|
67
|
+
if (options.useHeadlessConhost === void 0) consola.warn("conhost --headless not supported, console window may briefly appear on startup");
|
|
68
|
+
command = execPath;
|
|
69
|
+
commandArgs = xmlArgs;
|
|
70
|
+
}
|
|
71
|
+
return `<?xml version="1.0" encoding="UTF-16"?>
|
|
40
72
|
<Task version="1.2" xmlns="http://schemas.microsoft.com/windows/2004/02/mit/task">
|
|
41
73
|
<Triggers>
|
|
42
74
|
<LogonTrigger>
|
|
43
75
|
<Enabled>true</Enabled>
|
|
76
|
+
<Delay>PT30S</Delay>
|
|
44
77
|
</LogonTrigger>
|
|
45
78
|
</Triggers>
|
|
46
79
|
<Principals>
|
|
@@ -49,13 +82,38 @@ async function installAutoStart(execPath, args) {
|
|
|
49
82
|
<RunLevel>LeastPrivilege</RunLevel>
|
|
50
83
|
</Principal>
|
|
51
84
|
</Principals>
|
|
85
|
+
<Settings>
|
|
86
|
+
<MultipleInstancesPolicy>IgnoreNew</MultipleInstancesPolicy>
|
|
87
|
+
<DisallowStartIfOnBatteries>false</DisallowStartIfOnBatteries>
|
|
88
|
+
<StopIfGoingOnBatteries>false</StopIfGoingOnBatteries>
|
|
89
|
+
<AllowHardTerminate>true</AllowHardTerminate>
|
|
90
|
+
<StartWhenAvailable>true</StartWhenAvailable>
|
|
91
|
+
<RunOnlyIfNetworkAvailable>false</RunOnlyIfNetworkAvailable>
|
|
92
|
+
<IdleSettings>
|
|
93
|
+
<StopOnIdleEnd>false</StopOnIdleEnd>
|
|
94
|
+
<RestartOnIdle>false</RestartOnIdle>
|
|
95
|
+
</IdleSettings>
|
|
96
|
+
<AllowStartOnDemand>true</AllowStartOnDemand>
|
|
97
|
+
<Enabled>true</Enabled>
|
|
98
|
+
<Hidden>true</Hidden>
|
|
99
|
+
<RunOnlyIfIdle>false</RunOnlyIfIdle>
|
|
100
|
+
<ExecutionTimeLimit>PT0S</ExecutionTimeLimit>
|
|
101
|
+
<Priority>7</Priority>
|
|
102
|
+
<RestartOnFailure>
|
|
103
|
+
<Interval>PT1M</Interval>
|
|
104
|
+
<Count>3</Count>
|
|
105
|
+
</RestartOnFailure>
|
|
106
|
+
</Settings>
|
|
52
107
|
<Actions>
|
|
53
108
|
<Exec>
|
|
54
|
-
<Command>${escapeXmlAttr(
|
|
55
|
-
<Arguments>${escapeXmlAttr(
|
|
109
|
+
<Command>${escapeXmlAttr(command)}</Command>
|
|
110
|
+
<Arguments>${escapeXmlAttr(commandArgs)}</Arguments>
|
|
56
111
|
</Exec>
|
|
57
112
|
</Actions>
|
|
58
113
|
</Task>`;
|
|
114
|
+
}
|
|
115
|
+
async function installAutoStart(execPath, args) {
|
|
116
|
+
const taskXml = buildTaskXml(execPath, args);
|
|
59
117
|
const tmpDir = os.tmpdir();
|
|
60
118
|
const xmlPath = path.join(tmpDir, "copilot-proxy-task.xml");
|
|
61
119
|
try {
|
|
@@ -96,4 +154,4 @@ async function uninstallAutoStart() {
|
|
|
96
154
|
|
|
97
155
|
//#endregion
|
|
98
156
|
export { installAutoStart, uninstallAutoStart };
|
|
99
|
-
//# sourceMappingURL=win32-
|
|
157
|
+
//# sourceMappingURL=win32-DAStPxcF.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"win32-DAStPxcF.js","names":["command: string","commandArgs: string"],"sources":["../src/daemon/platform/win32.ts"],"sourcesContent":["import { execFileSync } from 'node:child_process'\nimport fs from 'node:fs'\nimport os from 'node:os'\nimport path from 'node:path'\nimport consola from 'consola'\n\nconst TASK_NAME = 'CopilotProxy'\n\nfunction escapeXmlAttr(s: string): string {\n return s.replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>').replace(/\"/g, '"').replace(/'/g, ''')\n}\n\n/**\n * Quote a single argument for Windows CommandLineToArgvW parsing.\n * See: https://learn.microsoft.com/en-us/cpp/c-language/parsing-c-command-line-arguments\n */\nfunction winQuoteArg(arg: string): string {\n if (arg.length > 0 && !/[\\s\"\\\\]/.test(arg))\n return arg\n\n let result = '\"'\n for (let i = 0; i < arg.length; i++) {\n let numBackslashes = 0\n while (i < arg.length && arg[i] === '\\\\') {\n numBackslashes++\n i++\n }\n if (i >= arg.length) {\n // End of string: double all backslashes\n result += '\\\\'.repeat(numBackslashes * 2)\n }\n else if (arg[i] === '\"') {\n // Before a quote: double backslashes, then escape the quote\n result += '\\\\'.repeat(numBackslashes * 2 + 1)\n result += '\"'\n }\n else {\n // Not before a quote: keep backslashes as-is\n result += '\\\\'.repeat(numBackslashes)\n result += arg[i]\n }\n }\n result += '\"'\n return result\n}\n\n/**\n * Detect whether conhost --headless is available (Win10 1809+).\n * Falls back to direct execution on older Windows versions.\n */\nfunction supportsHeadlessConhost(): boolean {\n try {\n execFileSync('conhost.exe', ['--headless', 'cmd.exe', '/c', 'exit', '0'], {\n stdio: 'pipe',\n timeout: 5000,\n })\n return true\n }\n catch {\n return false\n }\n}\n\ninterface BuildTaskXmlOptions {\n // Explicitly set for deterministic tests; undefined keeps runtime detection.\n useHeadlessConhost?: boolean\n}\n\nexport function buildTaskXml(execPath: string, args: string[], options: BuildTaskXmlOptions = {}): string {\n // Each arg is individually quoted for CommandLineToArgvW parsing,\n // then the whole string is XML-escaped for the task XML.\n const xmlArgs = args.map(a => winQuoteArg(a)).join(' ')\n\n // Use conhost --headless to hide console window when available\n const headless = options.useHeadlessConhost ?? supportsHeadlessConhost()\n let command: string\n let commandArgs: string\n if (headless) {\n command = 'conhost.exe'\n commandArgs = `--headless ${winQuoteArg(execPath)} ${xmlArgs}`\n }\n else {\n if (options.useHeadlessConhost === undefined) {\n consola.warn('conhost --headless not supported, console window may briefly appear on startup')\n }\n command = execPath\n commandArgs = xmlArgs\n }\n\n return `<?xml version=\"1.0\" encoding=\"UTF-16\"?>\n<Task version=\"1.2\" xmlns=\"http://schemas.microsoft.com/windows/2004/02/mit/task\">\n <Triggers>\n <LogonTrigger>\n <Enabled>true</Enabled>\n <Delay>PT30S</Delay>\n </LogonTrigger>\n </Triggers>\n <Principals>\n <Principal>\n <LogonType>InteractiveToken</LogonType>\n <RunLevel>LeastPrivilege</RunLevel>\n </Principal>\n </Principals>\n <Settings>\n <MultipleInstancesPolicy>IgnoreNew</MultipleInstancesPolicy>\n <DisallowStartIfOnBatteries>false</DisallowStartIfOnBatteries>\n <StopIfGoingOnBatteries>false</StopIfGoingOnBatteries>\n <AllowHardTerminate>true</AllowHardTerminate>\n <StartWhenAvailable>true</StartWhenAvailable>\n <RunOnlyIfNetworkAvailable>false</RunOnlyIfNetworkAvailable>\n <IdleSettings>\n <StopOnIdleEnd>false</StopOnIdleEnd>\n <RestartOnIdle>false</RestartOnIdle>\n </IdleSettings>\n <AllowStartOnDemand>true</AllowStartOnDemand>\n <Enabled>true</Enabled>\n <Hidden>true</Hidden>\n <RunOnlyIfIdle>false</RunOnlyIfIdle>\n <ExecutionTimeLimit>PT0S</ExecutionTimeLimit>\n <Priority>7</Priority>\n <RestartOnFailure>\n <Interval>PT1M</Interval>\n <Count>3</Count>\n </RestartOnFailure>\n </Settings>\n <Actions>\n <Exec>\n <Command>${escapeXmlAttr(command)}</Command>\n <Arguments>${escapeXmlAttr(commandArgs)}</Arguments>\n </Exec>\n </Actions>\n</Task>`\n}\n\nexport async function installAutoStart(execPath: string, args: string[]): Promise<boolean> {\n const taskXml = buildTaskXml(execPath, args)\n\n const tmpDir = os.tmpdir()\n const xmlPath = path.join(tmpDir, 'copilot-proxy-task.xml')\n\n try {\n fs.writeFileSync(xmlPath, taskXml, { encoding: 'utf16le' })\n\n execFileSync('schtasks', [\n '/create',\n '/tn',\n TASK_NAME,\n '/xml',\n xmlPath,\n '/f',\n ], { stdio: 'pipe' })\n }\n catch (error) {\n consola.error('Failed to create scheduled task:', error instanceof Error ? error.message : error)\n return false\n }\n finally {\n try {\n fs.unlinkSync(xmlPath)\n }\n catch {}\n }\n\n consola.success('Auto-start enabled via Task Scheduler')\n return true\n}\n\nexport async function uninstallAutoStart(): Promise<boolean> {\n try {\n execFileSync('schtasks', ['/delete', '/tn', TASK_NAME, '/f'], { stdio: 'pipe' })\n }\n catch (error) {\n consola.warn('Failed to delete scheduled task:', error instanceof Error ? error.message : error)\n }\n\n consola.success('Auto-start disabled')\n return true\n}\n"],"mappings":";;;;;;;AAMA,MAAM,YAAY;AAElB,SAAS,cAAc,GAAmB;AACxC,QAAO,EAAE,QAAQ,MAAM,QAAQ,CAAC,QAAQ,MAAM,OAAO,CAAC,QAAQ,MAAM,OAAO,CAAC,QAAQ,MAAM,SAAS,CAAC,QAAQ,MAAM,SAAS;;;;;;AAO7H,SAAS,YAAY,KAAqB;AACxC,KAAI,IAAI,SAAS,KAAK,CAAC,UAAU,KAAK,IAAI,CACxC,QAAO;CAET,IAAI,SAAS;AACb,MAAK,IAAI,IAAI,GAAG,IAAI,IAAI,QAAQ,KAAK;EACnC,IAAI,iBAAiB;AACrB,SAAO,IAAI,IAAI,UAAU,IAAI,OAAO,MAAM;AACxC;AACA;;AAEF,MAAI,KAAK,IAAI,OAEX,WAAU,KAAK,OAAO,iBAAiB,EAAE;WAElC,IAAI,OAAO,MAAK;AAEvB,aAAU,KAAK,OAAO,iBAAiB,IAAI,EAAE;AAC7C,aAAU;SAEP;AAEH,aAAU,KAAK,OAAO,eAAe;AACrC,aAAU,IAAI;;;AAGlB,WAAU;AACV,QAAO;;;;;;AAOT,SAAS,0BAAmC;AAC1C,KAAI;AACF,eAAa,eAAe;GAAC;GAAc;GAAW;GAAM;GAAQ;GAAI,EAAE;GACxE,OAAO;GACP,SAAS;GACV,CAAC;AACF,SAAO;SAEH;AACJ,SAAO;;;AASX,SAAgB,aAAa,UAAkB,MAAgB,UAA+B,EAAE,EAAU;CAGxG,MAAM,UAAU,KAAK,KAAI,MAAK,YAAY,EAAE,CAAC,CAAC,KAAK,IAAI;CAGvD,MAAM,WAAW,QAAQ,sBAAsB,yBAAyB;CACxE,IAAIA;CACJ,IAAIC;AACJ,KAAI,UAAU;AACZ,YAAU;AACV,gBAAc,cAAc,YAAY,SAAS,CAAC,GAAG;QAElD;AACH,MAAI,QAAQ,uBAAuB,OACjC,SAAQ,KAAK,iFAAiF;AAEhG,YAAU;AACV,gBAAc;;AAGhB,QAAO;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBAsCQ,cAAc,QAAQ,CAAC;mBACrB,cAAc,YAAY,CAAC;;;;;AAM9C,eAAsB,iBAAiB,UAAkB,MAAkC;CACzF,MAAM,UAAU,aAAa,UAAU,KAAK;CAE5C,MAAM,SAAS,GAAG,QAAQ;CAC1B,MAAM,UAAU,KAAK,KAAK,QAAQ,yBAAyB;AAE3D,KAAI;AACF,KAAG,cAAc,SAAS,SAAS,EAAE,UAAU,WAAW,CAAC;AAE3D,eAAa,YAAY;GACvB;GACA;GACA;GACA;GACA;GACA;GACD,EAAE,EAAE,OAAO,QAAQ,CAAC;UAEhB,OAAO;AACZ,UAAQ,MAAM,oCAAoC,iBAAiB,QAAQ,MAAM,UAAU,MAAM;AACjG,SAAO;WAED;AACN,MAAI;AACF,MAAG,WAAW,QAAQ;UAElB;;AAGR,SAAQ,QAAQ,wCAAwC;AACxD,QAAO;;AAGT,eAAsB,qBAAuC;AAC3D,KAAI;AACF,eAAa,YAAY;GAAC;GAAW;GAAO;GAAW;GAAK,EAAE,EAAE,OAAO,QAAQ,CAAC;UAE3E,OAAO;AACZ,UAAQ,KAAK,oCAAoC,iBAAiB,QAAQ,MAAM,UAAU,MAAM;;AAGlG,SAAQ,QAAQ,sBAAsB;AACtC,QAAO"}
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@jer-y/copilot-proxy",
|
|
3
3
|
"type": "module",
|
|
4
|
-
"version": "0.2.
|
|
4
|
+
"version": "0.2.2",
|
|
5
5
|
"description": "Turn GitHub Copilot into an OpenAI/Anthropic-compatible server with Claude Code and Codex support.",
|
|
6
6
|
"author": "jer-y",
|
|
7
7
|
"homepage": "https://github.com/jer-y/copilot-proxy",
|
package/dist/start-CUT1hJrN.js
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"win32-D1-MlKl7.js","names":[],"sources":["../src/daemon/platform/win32.ts"],"sourcesContent":["import { execFileSync } from 'node:child_process'\nimport fs from 'node:fs'\nimport os from 'node:os'\nimport path from 'node:path'\nimport consola from 'consola'\n\nconst TASK_NAME = 'CopilotProxy'\n\nfunction escapeXmlAttr(s: string): string {\n return s.replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>').replace(/\"/g, '"').replace(/'/g, ''')\n}\n\n/**\n * Quote a single argument for Windows CommandLineToArgvW parsing.\n * See: https://learn.microsoft.com/en-us/cpp/c-language/parsing-c-command-line-arguments\n */\nfunction winQuoteArg(arg: string): string {\n if (arg.length > 0 && !/[\\s\"\\\\]/.test(arg))\n return arg\n\n let result = '\"'\n for (let i = 0; i < arg.length; i++) {\n let numBackslashes = 0\n while (i < arg.length && arg[i] === '\\\\') {\n numBackslashes++\n i++\n }\n if (i >= arg.length) {\n // End of string: double all backslashes\n result += '\\\\'.repeat(numBackslashes * 2)\n }\n else if (arg[i] === '\"') {\n // Before a quote: double backslashes, then escape the quote\n result += '\\\\'.repeat(numBackslashes * 2 + 1)\n result += '\"'\n }\n else {\n // Not before a quote: keep backslashes as-is\n result += '\\\\'.repeat(numBackslashes)\n result += arg[i]\n }\n }\n result += '\"'\n return result\n}\n\nexport async function installAutoStart(execPath: string, args: string[]): Promise<boolean> {\n // Use XML task definition for reliable argument handling\n // Each arg is individually quoted for CommandLineToArgvW parsing,\n // then the whole string is XML-escaped for the task XML.\n const xmlArgs = args.map(a => winQuoteArg(a)).join(' ')\n const taskXml = `<?xml version=\"1.0\" encoding=\"UTF-16\"?>\n<Task version=\"1.2\" xmlns=\"http://schemas.microsoft.com/windows/2004/02/mit/task\">\n <Triggers>\n <LogonTrigger>\n <Enabled>true</Enabled>\n </LogonTrigger>\n </Triggers>\n <Principals>\n <Principal>\n <LogonType>InteractiveToken</LogonType>\n <RunLevel>LeastPrivilege</RunLevel>\n </Principal>\n </Principals>\n <Actions>\n <Exec>\n <Command>${escapeXmlAttr(execPath)}</Command>\n <Arguments>${escapeXmlAttr(xmlArgs)}</Arguments>\n </Exec>\n </Actions>\n</Task>`\n\n const tmpDir = os.tmpdir()\n const xmlPath = path.join(tmpDir, 'copilot-proxy-task.xml')\n\n try {\n fs.writeFileSync(xmlPath, taskXml, { encoding: 'utf16le' })\n\n execFileSync('schtasks', [\n '/create',\n '/tn',\n TASK_NAME,\n '/xml',\n xmlPath,\n '/f',\n ], { stdio: 'pipe' })\n }\n catch (error) {\n consola.error('Failed to create scheduled task:', error instanceof Error ? error.message : error)\n return false\n }\n finally {\n try {\n fs.unlinkSync(xmlPath)\n }\n catch {}\n }\n\n consola.success('Auto-start enabled via Task Scheduler')\n return true\n}\n\nexport async function uninstallAutoStart(): Promise<boolean> {\n try {\n execFileSync('schtasks', ['/delete', '/tn', TASK_NAME, '/f'], { stdio: 'pipe' })\n }\n catch (error) {\n consola.warn('Failed to delete scheduled task:', error instanceof Error ? error.message : error)\n }\n\n consola.success('Auto-start disabled')\n return true\n}\n"],"mappings":";;;;;;;AAMA,MAAM,YAAY;AAElB,SAAS,cAAc,GAAmB;AACxC,QAAO,EAAE,QAAQ,MAAM,QAAQ,CAAC,QAAQ,MAAM,OAAO,CAAC,QAAQ,MAAM,OAAO,CAAC,QAAQ,MAAM,SAAS,CAAC,QAAQ,MAAM,SAAS;;;;;;AAO7H,SAAS,YAAY,KAAqB;AACxC,KAAI,IAAI,SAAS,KAAK,CAAC,UAAU,KAAK,IAAI,CACxC,QAAO;CAET,IAAI,SAAS;AACb,MAAK,IAAI,IAAI,GAAG,IAAI,IAAI,QAAQ,KAAK;EACnC,IAAI,iBAAiB;AACrB,SAAO,IAAI,IAAI,UAAU,IAAI,OAAO,MAAM;AACxC;AACA;;AAEF,MAAI,KAAK,IAAI,OAEX,WAAU,KAAK,OAAO,iBAAiB,EAAE;WAElC,IAAI,OAAO,MAAK;AAEvB,aAAU,KAAK,OAAO,iBAAiB,IAAI,EAAE;AAC7C,aAAU;SAEP;AAEH,aAAU,KAAK,OAAO,eAAe;AACrC,aAAU,IAAI;;;AAGlB,WAAU;AACV,QAAO;;AAGT,eAAsB,iBAAiB,UAAkB,MAAkC;CAIzF,MAAM,UAAU,KAAK,KAAI,MAAK,YAAY,EAAE,CAAC,CAAC,KAAK,IAAI;CACvD,MAAM,UAAU;;;;;;;;;;;;;;;iBAeD,cAAc,SAAS,CAAC;mBACtB,cAAc,QAAQ,CAAC;;;;CAKxC,MAAM,SAAS,GAAG,QAAQ;CAC1B,MAAM,UAAU,KAAK,KAAK,QAAQ,yBAAyB;AAE3D,KAAI;AACF,KAAG,cAAc,SAAS,SAAS,EAAE,UAAU,WAAW,CAAC;AAE3D,eAAa,YAAY;GACvB;GACA;GACA;GACA;GACA;GACA;GACD,EAAE,EAAE,OAAO,QAAQ,CAAC;UAEhB,OAAO;AACZ,UAAQ,MAAM,oCAAoC,iBAAiB,QAAQ,MAAM,UAAU,MAAM;AACjG,SAAO;WAED;AACN,MAAI;AACF,MAAG,WAAW,QAAQ;UAElB;;AAGR,SAAQ,QAAQ,wCAAwC;AACxD,QAAO;;AAGT,eAAsB,qBAAuC;AAC3D,KAAI;AACF,eAAa,YAAY;GAAC;GAAW;GAAO;GAAW;GAAK,EAAE,EAAE,OAAO,QAAQ,CAAC;UAE3E,OAAO;AACZ,UAAQ,KAAK,oCAAoC,iBAAiB,QAAQ,MAAM,UAAU,MAAM;;AAGlG,SAAQ,QAAQ,sBAAsB;AACtC,QAAO"}
|