swixter 0.1.4 → 0.1.6
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/cli/index.js +307 -71
- package/package.json +1 -1
package/dist/cli/index.js
CHANGED
|
@@ -13999,7 +13999,7 @@ var CONFIG_VERSION = "2.0.0", EXPORT_VERSION = "1.0.0";
|
|
|
13999
13999
|
var init_versions2 = () => {};
|
|
14000
14000
|
|
|
14001
14001
|
// src/constants/meta.ts
|
|
14002
|
-
var APP_VERSION = "0.1.
|
|
14002
|
+
var APP_VERSION = "0.1.6";
|
|
14003
14003
|
var init_meta = () => {};
|
|
14004
14004
|
|
|
14005
14005
|
// src/constants/install.ts
|
|
@@ -14359,7 +14359,8 @@ var init_presets = __esm(() => {
|
|
|
14359
14359
|
id: "deepseek",
|
|
14360
14360
|
name: "DeepSeek",
|
|
14361
14361
|
displayName: "DeepSeek",
|
|
14362
|
-
baseURL: "https://api.deepseek.com",
|
|
14362
|
+
baseURL: "https://api.deepseek.com/anthropic",
|
|
14363
|
+
baseURLChat: "https://api.deepseek.com",
|
|
14363
14364
|
defaultModels: [
|
|
14364
14365
|
"deepseek-chat",
|
|
14365
14366
|
"deepseek-coder"
|
|
@@ -18914,7 +18915,7 @@ var init_env_key_helper = __esm(() => {
|
|
|
18914
18915
|
});
|
|
18915
18916
|
|
|
18916
18917
|
// src/adapters/codex.ts
|
|
18917
|
-
import { mkdir as mkdir4, readFile as readFile4, writeFile as writeFile4 } from "node:fs/promises";
|
|
18918
|
+
import { mkdir as mkdir4, readFile as readFile4, writeFile as writeFile4, unlink } from "node:fs/promises";
|
|
18918
18919
|
import { existsSync as existsSync4 } from "node:fs";
|
|
18919
18920
|
import { homedir as homedir2 } from "node:os";
|
|
18920
18921
|
import { join as join3, dirname as dirname4 } from "node:path";
|
|
@@ -18922,8 +18923,10 @@ import { join as join3, dirname as dirname4 } from "node:path";
|
|
|
18922
18923
|
class CodexAdapter {
|
|
18923
18924
|
name = "codex";
|
|
18924
18925
|
configPath;
|
|
18926
|
+
authPath;
|
|
18925
18927
|
constructor() {
|
|
18926
18928
|
this.configPath = join3(homedir2(), ".codex", "config.toml");
|
|
18929
|
+
this.authPath = join3(homedir2(), ".codex", "auth.json");
|
|
18927
18930
|
}
|
|
18928
18931
|
async apply(profile) {
|
|
18929
18932
|
try {
|
|
@@ -18961,6 +18964,7 @@ class CodexAdapter {
|
|
|
18961
18964
|
config2.model_provider = providerName;
|
|
18962
18965
|
const tomlContent = stringify(config2);
|
|
18963
18966
|
await writeFile4(this.configPath, tomlContent, "utf-8");
|
|
18967
|
+
await this.writeAuthJson(profile);
|
|
18964
18968
|
} catch (error46) {
|
|
18965
18969
|
throw new Error(`Failed to apply Codex configuration: ${error46 instanceof Error ? error46.message : String(error46)}`);
|
|
18966
18970
|
}
|
|
@@ -18983,6 +18987,22 @@ class CodexAdapter {
|
|
|
18983
18987
|
if (!config2.model_providers || !config2.model_providers[providerName]) {
|
|
18984
18988
|
return false;
|
|
18985
18989
|
}
|
|
18990
|
+
if (profile.apiKey) {
|
|
18991
|
+
const envKey = await getEnvKey(profile);
|
|
18992
|
+
if (existsSync4(this.authPath)) {
|
|
18993
|
+
try {
|
|
18994
|
+
const authContent = await readFile4(this.authPath, "utf-8");
|
|
18995
|
+
const auth = JSON.parse(authContent);
|
|
18996
|
+
if (auth[envKey] !== profile.apiKey) {
|
|
18997
|
+
return false;
|
|
18998
|
+
}
|
|
18999
|
+
} catch {
|
|
19000
|
+
return false;
|
|
19001
|
+
}
|
|
19002
|
+
} else {
|
|
19003
|
+
return false;
|
|
19004
|
+
}
|
|
19005
|
+
}
|
|
18986
19006
|
return true;
|
|
18987
19007
|
} catch (error46) {
|
|
18988
19008
|
return false;
|
|
@@ -18993,7 +19013,8 @@ class CodexAdapter {
|
|
|
18993
19013
|
const providerTable = {
|
|
18994
19014
|
name: preset.displayName,
|
|
18995
19015
|
base_url: profile.baseURL || baseUrl,
|
|
18996
|
-
wire_api:
|
|
19016
|
+
wire_api: "responses",
|
|
19017
|
+
requires_openai_auth: true
|
|
18997
19018
|
};
|
|
18998
19019
|
providerTable.env_key = await getEnvKey(profile);
|
|
18999
19020
|
if (preset.headers) {
|
|
@@ -19016,6 +19037,31 @@ class CodexAdapter {
|
|
|
19016
19037
|
}
|
|
19017
19038
|
return profileTable;
|
|
19018
19039
|
}
|
|
19040
|
+
async writeAuthJson(profile) {
|
|
19041
|
+
const envKey = await getEnvKey(profile);
|
|
19042
|
+
let auth = {};
|
|
19043
|
+
if (existsSync4(this.authPath)) {
|
|
19044
|
+
try {
|
|
19045
|
+
const content = await readFile4(this.authPath, "utf-8");
|
|
19046
|
+
const parsed = JSON.parse(content);
|
|
19047
|
+
if (parsed && typeof parsed === "object" && !Array.isArray(parsed)) {
|
|
19048
|
+
auth = parsed;
|
|
19049
|
+
}
|
|
19050
|
+
} catch {
|
|
19051
|
+
auth = {};
|
|
19052
|
+
}
|
|
19053
|
+
}
|
|
19054
|
+
if (profile.apiKey) {
|
|
19055
|
+
auth[envKey] = profile.apiKey;
|
|
19056
|
+
} else {
|
|
19057
|
+
delete auth[envKey];
|
|
19058
|
+
}
|
|
19059
|
+
if (Object.keys(auth).length > 0) {
|
|
19060
|
+
await writeFile4(this.authPath, JSON.stringify(auth, null, 2), "utf-8");
|
|
19061
|
+
} else if (existsSync4(this.authPath)) {
|
|
19062
|
+
await unlink(this.authPath);
|
|
19063
|
+
}
|
|
19064
|
+
}
|
|
19019
19065
|
async getEnvExportCommands(profile) {
|
|
19020
19066
|
const commands = await getEnvExportCommands(profile);
|
|
19021
19067
|
const modelValue = getOpenAIModel(profile);
|
|
@@ -19034,7 +19080,9 @@ class CodexAdapter {
|
|
|
19034
19080
|
const providerKey = `swixter-${profileName}`;
|
|
19035
19081
|
const profileKey = `swixter-${profileName}`;
|
|
19036
19082
|
let modified = false;
|
|
19083
|
+
let envKeyToRemove;
|
|
19037
19084
|
if (config2.model_providers && config2.model_providers[providerKey]) {
|
|
19085
|
+
envKeyToRemove = config2.model_providers[providerKey].env_key;
|
|
19038
19086
|
delete config2.model_providers[providerKey];
|
|
19039
19087
|
modified = true;
|
|
19040
19088
|
}
|
|
@@ -19051,6 +19099,14 @@ class CodexAdapter {
|
|
|
19051
19099
|
const tomlContent = stringify(config2);
|
|
19052
19100
|
await writeFile4(this.configPath, tomlContent, "utf-8");
|
|
19053
19101
|
}
|
|
19102
|
+
if (envKeyToRemove && existsSync4(this.authPath)) {
|
|
19103
|
+
try {
|
|
19104
|
+
const authContent = await readFile4(this.authPath, "utf-8");
|
|
19105
|
+
const auth = JSON.parse(authContent);
|
|
19106
|
+
delete auth[envKeyToRemove];
|
|
19107
|
+
await writeFile4(this.authPath, JSON.stringify(auth, null, 2), "utf-8");
|
|
19108
|
+
} catch {}
|
|
19109
|
+
}
|
|
19054
19110
|
} catch (error46) {
|
|
19055
19111
|
console.warn(`Failed to remove profile from Codex config: ${error46}`);
|
|
19056
19112
|
}
|
|
@@ -26527,18 +26583,18 @@ var init_client = __esm(() => {
|
|
|
26527
26583
|
});
|
|
26528
26584
|
|
|
26529
26585
|
// src/auth/token.ts
|
|
26530
|
-
import { existsSync as
|
|
26531
|
-
import { readFile as
|
|
26532
|
-
import { join as
|
|
26586
|
+
import { existsSync as existsSync13 } from "node:fs";
|
|
26587
|
+
import { readFile as readFile10, writeFile as writeFile9, unlink as unlink4 } from "node:fs/promises";
|
|
26588
|
+
import { join as join11 } from "node:path";
|
|
26533
26589
|
function getAuthFilePath() {
|
|
26534
|
-
return
|
|
26590
|
+
return join11(getConfigDir("swixter"), AUTH_FILE);
|
|
26535
26591
|
}
|
|
26536
26592
|
async function loadAuthState() {
|
|
26537
26593
|
const authPath = getAuthFilePath();
|
|
26538
|
-
if (!
|
|
26594
|
+
if (!existsSync13(authPath))
|
|
26539
26595
|
return null;
|
|
26540
26596
|
try {
|
|
26541
|
-
const content = await
|
|
26597
|
+
const content = await readFile10(authPath, "utf-8");
|
|
26542
26598
|
return JSON.parse(content);
|
|
26543
26599
|
} catch {
|
|
26544
26600
|
return null;
|
|
@@ -26546,12 +26602,12 @@ async function loadAuthState() {
|
|
|
26546
26602
|
}
|
|
26547
26603
|
async function saveAuthState(state) {
|
|
26548
26604
|
const authPath = getAuthFilePath();
|
|
26549
|
-
await
|
|
26605
|
+
await writeFile9(authPath, JSON.stringify(state, null, 2), { mode: 384, encoding: "utf-8" });
|
|
26550
26606
|
}
|
|
26551
26607
|
async function clearAuthState() {
|
|
26552
26608
|
const authPath = getAuthFilePath();
|
|
26553
|
-
if (
|
|
26554
|
-
await
|
|
26609
|
+
if (existsSync13(authPath)) {
|
|
26610
|
+
await unlink4(authPath);
|
|
26555
26611
|
}
|
|
26556
26612
|
}
|
|
26557
26613
|
function isExpired(expiresAt) {
|
|
@@ -27364,7 +27420,7 @@ __export(exports_proxy, {
|
|
|
27364
27420
|
buildCoderProxyEnv: () => buildCoderProxyEnv,
|
|
27365
27421
|
buildClaudeProxyEnv: () => buildClaudeProxyEnv
|
|
27366
27422
|
});
|
|
27367
|
-
import { spawn as
|
|
27423
|
+
import { spawn as spawn4 } from "node:child_process";
|
|
27368
27424
|
function resolveProxyRuntimeBinding(input) {
|
|
27369
27425
|
const occupiedPorts = new Set(input.allInstances.filter((s) => s.running).map((s) => s.port));
|
|
27370
27426
|
if (input.requestedPort) {
|
|
@@ -27558,7 +27614,7 @@ async function cmdStartDaemon(config2) {
|
|
|
27558
27614
|
if (config2.groupName) {
|
|
27559
27615
|
args.push("--group", config2.groupName);
|
|
27560
27616
|
}
|
|
27561
|
-
const child =
|
|
27617
|
+
const child = spawn4(process.execPath, [process.argv[1], ...args], {
|
|
27562
27618
|
detached: true,
|
|
27563
27619
|
stdio: "ignore"
|
|
27564
27620
|
});
|
|
@@ -27670,7 +27726,7 @@ async function cmdRun4(args) {
|
|
|
27670
27726
|
});
|
|
27671
27727
|
return;
|
|
27672
27728
|
}
|
|
27673
|
-
const child =
|
|
27729
|
+
const child = spawn4(coder, coderArgs.slice(1), {
|
|
27674
27730
|
env,
|
|
27675
27731
|
stdio: "inherit"
|
|
27676
27732
|
});
|
|
@@ -28654,8 +28710,8 @@ async function cmdCreateInteractive3() {
|
|
|
28654
28710
|
console.log();
|
|
28655
28711
|
console.log(import_picocolors10.default.bold(import_picocolors10.default.cyan(PROMPTS.createProfile(CODER_CONFIG3.displayName))));
|
|
28656
28712
|
console.log();
|
|
28657
|
-
const {
|
|
28658
|
-
const presets = await
|
|
28713
|
+
const { getAllPresets: getAllPresets2 } = await Promise.resolve().then(() => (init_presets(), exports_presets));
|
|
28714
|
+
const presets = await getAllPresets2();
|
|
28659
28715
|
const name = await he({
|
|
28660
28716
|
message: PROMPTS.configName,
|
|
28661
28717
|
placeholder: DEFAULT_PLACEHOLDERS.configName,
|
|
@@ -28823,12 +28879,6 @@ async function cmdCreateQuiet3(params) {
|
|
|
28823
28879
|
console.log(import_picocolors10.default.dim("Run 'swixter providers' to see all supported providers"));
|
|
28824
28880
|
process.exit(1);
|
|
28825
28881
|
}
|
|
28826
|
-
if (preset.wire_api !== "chat") {
|
|
28827
|
-
console.log(import_picocolors10.default.red(`Error: Provider "${preset.displayName}" is not compatible with ${CODER_CONFIG3.displayName}`));
|
|
28828
|
-
console.log(import_picocolors10.default.dim(`${CODER_CONFIG3.displayName} only supports OpenAI-compatible providers (chat API).`));
|
|
28829
|
-
console.log(import_picocolors10.default.dim("Use 'ollama' or 'custom' provider instead."));
|
|
28830
|
-
process.exit(1);
|
|
28831
|
-
}
|
|
28832
28882
|
if (params.provider !== "ollama" && !params["api-key"]) {
|
|
28833
28883
|
console.log(import_picocolors10.default.red("Error: This provider requires --api-key parameter"));
|
|
28834
28884
|
process.exit(1);
|
|
@@ -28992,8 +29042,8 @@ async function cmdEdit3(profileName) {
|
|
|
28992
29042
|
console.log();
|
|
28993
29043
|
console.log(import_picocolors10.default.bold(import_picocolors10.default.cyan(`Edit profile: ${profileName}`)));
|
|
28994
29044
|
console.log();
|
|
28995
|
-
const {
|
|
28996
|
-
const presets = await
|
|
29045
|
+
const { getAllPresets: getAllPresets2 } = await Promise.resolve().then(() => (init_presets(), exports_presets));
|
|
29046
|
+
const presets = await getAllPresets2();
|
|
28997
29047
|
const currentPreset = await getPresetByIdAsync(profile.providerId);
|
|
28998
29048
|
const shouldChangeProvider = await ye({
|
|
28999
29049
|
message: `Change provider? Current: ${currentPreset?.displayName}`,
|
|
@@ -29183,19 +29233,10 @@ async function cmdApply3() {
|
|
|
29183
29233
|
console.log(` Provider: ${import_picocolors10.default.yellow(preset?.displayName)}`);
|
|
29184
29234
|
console.log(` Config file: ${import_picocolors10.default.dim(adapter.configPath)}`);
|
|
29185
29235
|
console.log();
|
|
29186
|
-
|
|
29187
|
-
|
|
29188
|
-
|
|
29189
|
-
|
|
29190
|
-
console.log();
|
|
29191
|
-
envCommands.forEach((cmd) => {
|
|
29192
|
-
console.log(` ${import_picocolors10.default.green(cmd)}`);
|
|
29193
|
-
});
|
|
29194
|
-
console.log();
|
|
29195
|
-
console.log(import_picocolors10.default.dim(`Then run: ${import_picocolors10.default.cyan("codex")}`));
|
|
29196
|
-
console.log();
|
|
29197
|
-
}
|
|
29198
|
-
}
|
|
29236
|
+
console.log(import_picocolors10.default.bold("Run Codex now: ") + import_picocolors10.default.cyan("codex"));
|
|
29237
|
+
console.log();
|
|
29238
|
+
console.log(import_picocolors10.default.dim("Environment variables are automatically managed via auth.json."));
|
|
29239
|
+
console.log();
|
|
29199
29240
|
} else {
|
|
29200
29241
|
console.log(import_picocolors10.default.yellow("⚠ Profile written, but verification failed"));
|
|
29201
29242
|
console.log(import_picocolors10.default.dim("Please check config file format"));
|
|
@@ -29426,6 +29467,8 @@ async function cmdUpdate3(args) {
|
|
|
29426
29467
|
|
|
29427
29468
|
// src/cli/ui.ts
|
|
29428
29469
|
var import_picocolors12 = __toESM(require_picocolors(), 1);
|
|
29470
|
+
import { spawn as spawn3 } from "node:child_process";
|
|
29471
|
+
import { open } from "node:fs/promises";
|
|
29429
29472
|
|
|
29430
29473
|
// src/server/index.ts
|
|
29431
29474
|
import http2 from "node:http";
|
|
@@ -30109,7 +30152,7 @@ init_versions2();
|
|
|
30109
30152
|
init_paths();
|
|
30110
30153
|
init_export();
|
|
30111
30154
|
import { existsSync as existsSync9, statSync as statSync2 } from "node:fs";
|
|
30112
|
-
import { readFile as readFile8, writeFile as writeFile7, unlink } from "node:fs/promises";
|
|
30155
|
+
import { readFile as readFile8, writeFile as writeFile7, unlink as unlink2 } from "node:fs/promises";
|
|
30113
30156
|
import { join as join8 } from "node:path";
|
|
30114
30157
|
async function getVersion(req, res) {
|
|
30115
30158
|
sendJson(res, {
|
|
@@ -30172,7 +30215,7 @@ async function exportConfigFile(req, res) {
|
|
|
30172
30215
|
res.end(content);
|
|
30173
30216
|
} finally {
|
|
30174
30217
|
try {
|
|
30175
|
-
await
|
|
30218
|
+
await unlink2(tempPath);
|
|
30176
30219
|
} catch {}
|
|
30177
30220
|
}
|
|
30178
30221
|
} catch (error46) {
|
|
@@ -30194,7 +30237,7 @@ async function importConfigFile(req, res) {
|
|
|
30194
30237
|
sendJson(res, { success: true, ...result });
|
|
30195
30238
|
} finally {
|
|
30196
30239
|
try {
|
|
30197
|
-
await
|
|
30240
|
+
await unlink2(tempPath);
|
|
30198
30241
|
} catch {}
|
|
30199
30242
|
}
|
|
30200
30243
|
} catch (error46) {
|
|
@@ -30453,7 +30496,7 @@ function getUiDir() {
|
|
|
30453
30496
|
}
|
|
30454
30497
|
return join9(__dirname2, "..", "..", "ui", "dist");
|
|
30455
30498
|
}
|
|
30456
|
-
async function startServer(portArg) {
|
|
30499
|
+
async function startServer(portArg, options) {
|
|
30457
30500
|
const port = portArg || await findAvailablePort(3141);
|
|
30458
30501
|
const host = "127.0.0.1";
|
|
30459
30502
|
const router = new Router;
|
|
@@ -30521,7 +30564,9 @@ async function startServer(portArg) {
|
|
|
30521
30564
|
console.log(` Server: ${import_picocolors11.default.cyan(url2)}`);
|
|
30522
30565
|
console.log(` Press ${import_picocolors11.default.bold("Ctrl+C")} to stop`);
|
|
30523
30566
|
console.log();
|
|
30524
|
-
|
|
30567
|
+
if (!options?.noBrowser) {
|
|
30568
|
+
openBrowser(url2);
|
|
30569
|
+
}
|
|
30525
30570
|
const handle = {
|
|
30526
30571
|
host,
|
|
30527
30572
|
port,
|
|
@@ -30538,45 +30583,230 @@ async function startServer(portArg) {
|
|
|
30538
30583
|
return handle;
|
|
30539
30584
|
}
|
|
30540
30585
|
|
|
30541
|
-
// src/
|
|
30542
|
-
|
|
30543
|
-
|
|
30586
|
+
// src/utils/daemon.ts
|
|
30587
|
+
init_paths();
|
|
30588
|
+
import { existsSync as existsSync12 } from "node:fs";
|
|
30589
|
+
import { readFile as readFile9, writeFile as writeFile8, unlink as unlink3 } from "node:fs/promises";
|
|
30590
|
+
import { join as join10 } from "node:path";
|
|
30591
|
+
function getPidFilePath() {
|
|
30592
|
+
return join10(getConfigDir("swixter"), "ui.pid");
|
|
30593
|
+
}
|
|
30594
|
+
function getLogFilePath() {
|
|
30595
|
+
return join10(getConfigDir("swixter"), "ui.log");
|
|
30596
|
+
}
|
|
30597
|
+
async function readPidFile() {
|
|
30598
|
+
const path = getPidFilePath();
|
|
30599
|
+
if (!existsSync12(path))
|
|
30600
|
+
return null;
|
|
30544
30601
|
try {
|
|
30545
|
-
const
|
|
30546
|
-
|
|
30547
|
-
|
|
30548
|
-
|
|
30549
|
-
|
|
30550
|
-
|
|
30551
|
-
|
|
30602
|
+
const content = await readFile9(path, "utf-8");
|
|
30603
|
+
return JSON.parse(content);
|
|
30604
|
+
} catch {
|
|
30605
|
+
return null;
|
|
30606
|
+
}
|
|
30607
|
+
}
|
|
30608
|
+
async function writePidFile(pid, port) {
|
|
30609
|
+
const path = getPidFilePath();
|
|
30610
|
+
const data = { pid, port, startTime: new Date().toISOString() };
|
|
30611
|
+
await writeFile8(path, JSON.stringify(data, null, 2), "utf-8");
|
|
30612
|
+
}
|
|
30613
|
+
async function removePidFile() {
|
|
30614
|
+
const path = getPidFilePath();
|
|
30615
|
+
if (existsSync12(path)) {
|
|
30616
|
+
await unlink3(path).catch(() => {});
|
|
30617
|
+
}
|
|
30618
|
+
}
|
|
30619
|
+
function isProcessAlive2(pid) {
|
|
30620
|
+
try {
|
|
30621
|
+
process.kill(pid, 0);
|
|
30622
|
+
return true;
|
|
30623
|
+
} catch {
|
|
30624
|
+
return false;
|
|
30625
|
+
}
|
|
30626
|
+
}
|
|
30627
|
+
async function isSwixterUiRunning(pid, port) {
|
|
30628
|
+
if (!isProcessAlive2(pid))
|
|
30629
|
+
return false;
|
|
30630
|
+
try {
|
|
30631
|
+
const res = await fetch(`http://127.0.0.1:${port}/api/version`, {
|
|
30632
|
+
signal: AbortSignal.timeout(3000)
|
|
30552
30633
|
});
|
|
30553
|
-
|
|
30554
|
-
} catch
|
|
30555
|
-
|
|
30634
|
+
return res.ok;
|
|
30635
|
+
} catch {
|
|
30636
|
+
return false;
|
|
30637
|
+
}
|
|
30638
|
+
}
|
|
30639
|
+
async function cleanupStalePidFile() {
|
|
30640
|
+
const data = await readPidFile();
|
|
30641
|
+
if (data && !isProcessAlive2(data.pid)) {
|
|
30642
|
+
await removePidFile();
|
|
30643
|
+
}
|
|
30644
|
+
}
|
|
30645
|
+
async function stopDaemon() {
|
|
30646
|
+
const data = await readPidFile();
|
|
30647
|
+
if (!data) {
|
|
30648
|
+
return { success: false, message: "No daemon process is running." };
|
|
30649
|
+
}
|
|
30650
|
+
if (!isProcessAlive2(data.pid)) {
|
|
30651
|
+
await removePidFile();
|
|
30652
|
+
return { success: false, message: "Daemon process is not running (stale PID file removed)." };
|
|
30653
|
+
}
|
|
30654
|
+
try {
|
|
30655
|
+
process.kill(data.pid, "SIGTERM");
|
|
30656
|
+
await removePidFile();
|
|
30657
|
+
return { success: true, message: `Daemon process ${data.pid} stopped.` };
|
|
30658
|
+
} catch {
|
|
30659
|
+
await removePidFile();
|
|
30660
|
+
return { success: false, message: "Failed to stop daemon process (PID file removed)." };
|
|
30661
|
+
}
|
|
30662
|
+
}
|
|
30663
|
+
|
|
30664
|
+
// src/cli/ui.ts
|
|
30665
|
+
async function handleUiCommand(args) {
|
|
30666
|
+
const flags = parseFlags2(args);
|
|
30667
|
+
if (flags.stop) {
|
|
30668
|
+
const result = await stopDaemon();
|
|
30669
|
+
console.log();
|
|
30670
|
+
console.log(result.success ? import_picocolors12.default.green("✓") + " " + result.message : import_picocolors12.default.yellow("⚠") + " " + result.message);
|
|
30671
|
+
console.log();
|
|
30672
|
+
process.exit(result.success ? 0 : 1);
|
|
30673
|
+
}
|
|
30674
|
+
if (flags.status) {
|
|
30675
|
+
await showStatus();
|
|
30676
|
+
return;
|
|
30677
|
+
}
|
|
30678
|
+
if (flags.daemon) {
|
|
30679
|
+
await startDaemon(flags.port);
|
|
30680
|
+
return;
|
|
30681
|
+
}
|
|
30682
|
+
await runForeground(flags.port);
|
|
30683
|
+
}
|
|
30684
|
+
async function showStatus() {
|
|
30685
|
+
await cleanupStalePidFile();
|
|
30686
|
+
const data = await readPidFile();
|
|
30687
|
+
console.log();
|
|
30688
|
+
if (!data) {
|
|
30689
|
+
console.log(import_picocolors12.default.yellow("Swixter UI is not running."));
|
|
30690
|
+
console.log(import_picocolors12.default.dim("Run 'swixter ui --daemon' to start in background."));
|
|
30691
|
+
console.log();
|
|
30692
|
+
return;
|
|
30693
|
+
}
|
|
30694
|
+
const isRunning = await isSwixterUiRunning(data.pid, data.port);
|
|
30695
|
+
if (isRunning) {
|
|
30696
|
+
console.log(import_picocolors12.default.green("✓ Swixter UI is running"));
|
|
30697
|
+
console.log(` PID: ${import_picocolors12.default.cyan(String(data.pid))}`);
|
|
30698
|
+
console.log(` URL: ${import_picocolors12.default.cyan(`http://127.0.0.1:${data.port}`)}`);
|
|
30699
|
+
console.log(` Started: ${import_picocolors12.default.dim(data.startTime)}`);
|
|
30700
|
+
} else {
|
|
30701
|
+
console.log(import_picocolors12.default.yellow("⚠ Swixter UI is not running (stale PID file removed)."));
|
|
30702
|
+
}
|
|
30703
|
+
console.log();
|
|
30704
|
+
}
|
|
30705
|
+
async function startDaemon(portArg) {
|
|
30706
|
+
await cleanupStalePidFile();
|
|
30707
|
+
const existing = await readPidFile();
|
|
30708
|
+
if (existing && await isSwixterUiRunning(existing.pid, existing.port)) {
|
|
30709
|
+
console.log();
|
|
30710
|
+
console.log(import_picocolors12.default.yellow("Swixter UI is already running."));
|
|
30711
|
+
console.log(` PID: ${import_picocolors12.default.cyan(String(existing.pid))}`);
|
|
30712
|
+
console.log(` URL: ${import_picocolors12.default.cyan(`http://127.0.0.1:${existing.port}`)}`);
|
|
30713
|
+
console.log();
|
|
30714
|
+
openBrowser(`http://127.0.0.1:${existing.port}`);
|
|
30715
|
+
return;
|
|
30716
|
+
}
|
|
30717
|
+
const port = portArg || await findAvailablePort(3141);
|
|
30718
|
+
const childArgs = process.argv.slice(1).filter((arg) => arg !== "--daemon" && arg !== "--stop" && arg !== "--status");
|
|
30719
|
+
const logPath = getLogFilePath();
|
|
30720
|
+
const logFile = await open(logPath, "a");
|
|
30721
|
+
const child = spawn3(process.argv0, childArgs, {
|
|
30722
|
+
detached: true,
|
|
30723
|
+
stdio: ["ignore", logFile.fd, logFile.fd],
|
|
30724
|
+
env: { ...process.env, SWIXTER_UI_DAEMON: "1" }
|
|
30725
|
+
});
|
|
30726
|
+
child.unref();
|
|
30727
|
+
const url2 = `http://127.0.0.1:${port}`;
|
|
30728
|
+
const started = await waitForServer(url2, 1e4);
|
|
30729
|
+
if (!started) {
|
|
30730
|
+
try {
|
|
30731
|
+
process.kill(child.pid, "SIGTERM");
|
|
30732
|
+
} catch {}
|
|
30733
|
+
console.log();
|
|
30734
|
+
console.log(import_picocolors12.default.red("✗ Failed to start daemon (timed out waiting for server)."));
|
|
30735
|
+
console.log();
|
|
30556
30736
|
process.exit(1);
|
|
30557
30737
|
}
|
|
30738
|
+
await writePidFile(child.pid, port);
|
|
30739
|
+
console.log();
|
|
30740
|
+
console.log(import_picocolors12.default.green("✓ Swixter UI daemon started"));
|
|
30741
|
+
console.log(` PID: ${import_picocolors12.default.cyan(String(child.pid))}`);
|
|
30742
|
+
console.log(` URL: ${import_picocolors12.default.cyan(url2)}`);
|
|
30743
|
+
console.log(` Log: ${import_picocolors12.default.dim(logPath)}`);
|
|
30744
|
+
console.log();
|
|
30745
|
+
console.log(import_picocolors12.default.dim("Run 'swixter ui --stop' to stop."));
|
|
30746
|
+
console.log();
|
|
30558
30747
|
}
|
|
30559
|
-
function
|
|
30560
|
-
|
|
30748
|
+
async function runForeground(portArg) {
|
|
30749
|
+
await cleanupStalePidFile();
|
|
30750
|
+
const existing = await readPidFile();
|
|
30751
|
+
if (existing && await isSwixterUiRunning(existing.pid, existing.port)) {
|
|
30752
|
+
const url2 = `http://127.0.0.1:${existing.port}`;
|
|
30753
|
+
console.log();
|
|
30754
|
+
console.log(import_picocolors12.default.green("✓ Swixter UI is already running"));
|
|
30755
|
+
console.log(` URL: ${import_picocolors12.default.cyan(url2)}`);
|
|
30756
|
+
console.log();
|
|
30757
|
+
openBrowser(url2);
|
|
30561
30758
|
return;
|
|
30562
30759
|
}
|
|
30760
|
+
const port = portArg || await findAvailablePort(3141);
|
|
30761
|
+
const noBrowser = process.env.SWIXTER_UI_DAEMON === "1";
|
|
30762
|
+
const server = await startServer(port, { noBrowser });
|
|
30763
|
+
await writePidFile(process.pid, port);
|
|
30764
|
+
const shutdown = () => {
|
|
30765
|
+
console.log();
|
|
30766
|
+
console.log(import_picocolors12.default.dim("Shutting down..."));
|
|
30767
|
+
server.close(() => {
|
|
30768
|
+
removePidFile().catch(() => {});
|
|
30769
|
+
process.exit(0);
|
|
30770
|
+
});
|
|
30771
|
+
};
|
|
30772
|
+
process.on("SIGINT", shutdown);
|
|
30773
|
+
process.on("SIGTERM", shutdown);
|
|
30774
|
+
process.stdin.resume();
|
|
30775
|
+
}
|
|
30776
|
+
async function waitForServer(url2, timeoutMs) {
|
|
30777
|
+
const start = Date.now();
|
|
30778
|
+
const interval = 200;
|
|
30779
|
+
while (Date.now() - start < timeoutMs) {
|
|
30780
|
+
try {
|
|
30781
|
+
const res = await fetch(`${url2}/api/version`);
|
|
30782
|
+
if (res.ok)
|
|
30783
|
+
return true;
|
|
30784
|
+
} catch {}
|
|
30785
|
+
await new Promise((r2) => setTimeout(r2, interval));
|
|
30786
|
+
}
|
|
30787
|
+
return false;
|
|
30788
|
+
}
|
|
30789
|
+
function parseFlags2(args) {
|
|
30790
|
+
const result = { daemon: false, stop: false, status: false, port: undefined };
|
|
30791
|
+
if (!args)
|
|
30792
|
+
return result;
|
|
30563
30793
|
for (let i2 = 0;i2 < args.length; i2++) {
|
|
30564
30794
|
const arg = args[i2];
|
|
30565
|
-
if (arg === "--
|
|
30566
|
-
|
|
30567
|
-
|
|
30568
|
-
|
|
30569
|
-
|
|
30570
|
-
|
|
30571
|
-
|
|
30572
|
-
|
|
30573
|
-
|
|
30574
|
-
|
|
30795
|
+
if (arg === "--daemon") {
|
|
30796
|
+
result.daemon = true;
|
|
30797
|
+
} else if (arg === "--stop") {
|
|
30798
|
+
result.stop = true;
|
|
30799
|
+
} else if (arg === "--status") {
|
|
30800
|
+
result.status = true;
|
|
30801
|
+
} else if ((arg === "--port" || arg === "-p") && args[i2 + 1]) {
|
|
30802
|
+
const port = parseInt(args[i2 + 1], 10);
|
|
30803
|
+
if (!isNaN(port) && port >= 1 && port <= 65535) {
|
|
30804
|
+
result.port = port;
|
|
30575
30805
|
}
|
|
30576
|
-
|
|
30806
|
+
i2++;
|
|
30577
30807
|
}
|
|
30578
30808
|
}
|
|
30579
|
-
return;
|
|
30809
|
+
return result;
|
|
30580
30810
|
}
|
|
30581
30811
|
|
|
30582
30812
|
// src/cli/index.ts
|
|
@@ -30601,6 +30831,12 @@ ${import_picocolors13.default.bold("Web UI:")}
|
|
|
30601
30831
|
${import_picocolors13.default.cyan("ui")} ${import_picocolors13.default.dim("Launch local Web UI")}
|
|
30602
30832
|
${import_picocolors13.default.dim("swixter ui [--port <port>]")}
|
|
30603
30833
|
${import_picocolors13.default.dim("Start local HTTP server and open browser")}
|
|
30834
|
+
${import_picocolors13.default.dim("swixter ui --daemon [--port <port>]")}
|
|
30835
|
+
${import_picocolors13.default.dim("Start server in background")}
|
|
30836
|
+
${import_picocolors13.default.dim("swixter ui --stop")}
|
|
30837
|
+
${import_picocolors13.default.dim("Stop background server")}
|
|
30838
|
+
${import_picocolors13.default.dim("swixter ui --status")}
|
|
30839
|
+
${import_picocolors13.default.dim("Show background server status")}
|
|
30604
30840
|
|
|
30605
30841
|
${import_picocolors13.default.bold("Proxy Commands:")}
|
|
30606
30842
|
${import_picocolors13.default.cyan("proxy")} ${import_picocolors13.default.dim("Start proxy server or run with proxy")}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "swixter",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.6",
|
|
4
4
|
"description": "CLI tool for managing AI coding assistant configurations - easily switch between providers (Claude Code, Codex, Continue) with Anthropic, Ollama, or custom APIs",
|
|
5
5
|
"main": "dist/cli/index.js",
|
|
6
6
|
"module": "dist/cli/index.js",
|