swixter 0.1.1 → 0.1.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.
Files changed (3) hide show
  1. package/README.md +55 -1
  2. package/dist/cli/index.js +1258 -162
  3. package/package.json +2 -2
package/dist/cli/index.js CHANGED
@@ -464,7 +464,7 @@ var import_sisteransi, import_picocolors, uD, W, tD, eD, FD = function() {
464
464
  ` && (s && o && (F += z(s)), i && (F += K(i)));
465
465
  }
466
466
  return F;
467
- }, xD, B, AD, S, gD, vD = (e, u, t) => (u in e) ? gD(e, u, { enumerable: true, configurable: true, writable: true, value: t }) : e[u] = t, h = (e, u, t) => (vD(e, typeof u != "symbol" ? u + "" : u, t), t), dD, A, kD, $D = (e, u, t) => (u in e) ? kD(e, u, { enumerable: true, configurable: true, writable: true, value: t }) : e[u] = t, H = (e, u, t) => ($D(e, typeof u != "symbol" ? u + "" : u, t), t), SD, OD, PD = (e, u, t) => (u in e) ? OD(e, u, { enumerable: true, configurable: true, writable: true, value: t }) : e[u] = t, J = (e, u, t) => (PD(e, typeof u != "symbol" ? u + "" : u, t), t), LD, RD;
467
+ }, xD, B, AD, S, gD, vD = (e, u, t) => (u in e) ? gD(e, u, { enumerable: true, configurable: true, writable: true, value: t }) : e[u] = t, h = (e, u, t) => (vD(e, typeof u != "symbol" ? u + "" : u, t), t), dD, A, kD, $D = (e, u, t) => (u in e) ? kD(e, u, { enumerable: true, configurable: true, writable: true, value: t }) : e[u] = t, H = (e, u, t) => ($D(e, typeof u != "symbol" ? u + "" : u, t), t), SD, TD, jD = (e, u, t) => (u in e) ? TD(e, u, { enumerable: true, configurable: true, writable: true, value: t }) : e[u] = t, U = (e, u, t) => (jD(e, typeof u != "symbol" ? u + "" : u, t), t), MD, OD, PD = (e, u, t) => (u in e) ? OD(e, u, { enumerable: true, configurable: true, writable: true, value: t }) : e[u] = t, J = (e, u, t) => (PD(e, typeof u != "symbol" ? u + "" : u, t), t), LD, RD;
468
468
  var init_dist = __esm(() => {
469
469
  import_sisteransi = __toESM(require_src(), 1);
470
470
  import_picocolors = __toESM(require_picocolors(), 1);
@@ -567,6 +567,27 @@ var init_dist = __esm(() => {
567
567
  this.value = u ? this.value.filter((t) => t !== this._value) : [...this.value, this._value];
568
568
  }
569
569
  };
570
+ TD = Object.defineProperty;
571
+ MD = class MD extends x {
572
+ constructor({ mask: u, ...t }) {
573
+ super(t), U(this, "valueWithCursor", ""), U(this, "_mask", "\u2022"), this._mask = u ?? "\u2022", this.on("finalize", () => {
574
+ this.valueWithCursor = this.masked;
575
+ }), this.on("value", () => {
576
+ if (this.cursor >= this.value.length)
577
+ this.valueWithCursor = `${this.masked}${import_picocolors.default.inverse(import_picocolors.default.hidden("_"))}`;
578
+ else {
579
+ const F = this.masked.slice(0, this.cursor), s = this.masked.slice(this.cursor);
580
+ this.valueWithCursor = `${F}${import_picocolors.default.inverse(s[0])}${s.slice(1)}`;
581
+ }
582
+ });
583
+ }
584
+ get cursor() {
585
+ return this._cursor;
586
+ }
587
+ get masked() {
588
+ return this.value.replaceAll(/./g, this._mask);
589
+ }
590
+ };
570
591
  OD = Object.defineProperty;
571
592
  LD = class LD extends x {
572
593
  constructor(u) {
@@ -616,7 +637,7 @@ import y2 from "process";
616
637
  function ce() {
617
638
  return y2.platform !== "win32" ? y2.env.TERM !== "linux" : !!y2.env.CI || !!y2.env.WT_SESSION || !!y2.env.TERMINUS_SUBLIME || y2.env.ConEmuTask === "{cmd::Cmder}" || y2.env.TERM_PROGRAM === "Terminus-Sublime" || y2.env.TERM_PROGRAM === "vscode" || y2.env.TERM === "xterm-256color" || y2.env.TERM === "alacritty" || y2.env.TERMINAL_EMULATOR === "JetBrains-JediTerm";
618
639
  }
619
- var import_picocolors2, import_sisteransi2, V2, u = (t, n) => V2 ? t : n, le, L2, W2, C, ue, o, d2, k2, P2, A2, T, F, $e, _2, me, de, pe, q, D, U, K2, b2 = (t) => {
640
+ var import_picocolors2, import_sisteransi2, V2, u = (t, n) => V2 ? t : n, le, L2, W2, C, ue, o, d2, k2, P2, A2, T, F, $e, _2, me, de, pe, q, D, U2, K2, b2 = (t) => {
620
641
  switch (t) {
621
642
  case "initial":
622
643
  case "active":
@@ -655,6 +676,26 @@ ${import_picocolors2.default.gray(o)}` : ""}`;
655
676
  default:
656
677
  return `${n}${import_picocolors2.default.cyan(o)} ${i}
657
678
  ${import_picocolors2.default.cyan(d2)}
679
+ `;
680
+ }
681
+ } }).prompt(), ge = (t) => new MD({ validate: t.validate, mask: t.mask ?? $e, render() {
682
+ const n = `${import_picocolors2.default.gray(o)}
683
+ ${b2(this.state)} ${t.message}
684
+ `, r2 = this.valueWithCursor, i = this.masked;
685
+ switch (this.state) {
686
+ case "error":
687
+ return `${n.trim()}
688
+ ${import_picocolors2.default.yellow(o)} ${i}
689
+ ${import_picocolors2.default.yellow(d2)} ${import_picocolors2.default.yellow(this.error)}
690
+ `;
691
+ case "submit":
692
+ return `${n}${import_picocolors2.default.gray(o)} ${import_picocolors2.default.dim(i)}`;
693
+ case "cancel":
694
+ return `${n}${import_picocolors2.default.gray(o)} ${import_picocolors2.default.strikethrough(import_picocolors2.default.dim(i ?? ""))}${i ? `
695
+ ${import_picocolors2.default.gray(o)}` : ""}`;
696
+ default:
697
+ return `${n}${import_picocolors2.default.cyan(o)} ${r2}
698
+ ${import_picocolors2.default.cyan(d2)}
658
699
  `;
659
700
  }
660
701
  } }).prompt(), ye = (t) => {
@@ -834,7 +875,7 @@ var init_dist2 = __esm(() => {
834
875
  pe = u("\u256F", "+");
835
876
  q = u("\u25CF", "\u2022");
836
877
  D = u("\u25C6", "*");
837
- U = u("\u25B2", "!");
878
+ U2 = u("\u25B2", "!");
838
879
  K2 = u("\u25A0", "x");
839
880
  J2 = `${import_picocolors2.default.gray(o)} `;
840
881
  });
@@ -13633,7 +13674,7 @@ var init_types = __esm(() => {
13633
13674
  });
13634
13675
 
13635
13676
  // src/types.ts
13636
- var ProviderPresetSchema, ClaudeCodeProfileSchema, CoderConfigSchema, ConfigFileSchema, ExportConfigSchema;
13677
+ var ProviderPresetSchema, ClaudeCodeProfileSchema, CoderConfigSchema, SyncMetaSchema, ConfigFileSchema, ExportConfigSchema;
13637
13678
  var init_types2 = __esm(() => {
13638
13679
  init_zod();
13639
13680
  init_types();
@@ -13681,12 +13722,20 @@ var init_types2 = __esm(() => {
13681
13722
  CoderConfigSchema = exports_external.object({
13682
13723
  activeProfile: exports_external.string()
13683
13724
  });
13725
+ SyncMetaSchema = exports_external.object({
13726
+ lastSyncAt: exports_external.string(),
13727
+ configVersion: exports_external.number(),
13728
+ providersVersion: exports_external.number(),
13729
+ localUpdatedAt: exports_external.string(),
13730
+ dirty: exports_external.boolean().optional()
13731
+ });
13684
13732
  ConfigFileSchema = exports_external.object({
13685
13733
  profiles: exports_external.record(exports_external.string(), ClaudeCodeProfileSchema),
13686
13734
  coders: exports_external.record(exports_external.string(), CoderConfigSchema),
13687
13735
  groups: exports_external.record(exports_external.string(), GroupSchema),
13688
13736
  activeGroup: exports_external.string().optional(),
13689
- version: exports_external.string()
13737
+ version: exports_external.string(),
13738
+ syncMeta: SyncMetaSchema.optional()
13690
13739
  });
13691
13740
  ExportConfigSchema = exports_external.object({
13692
13741
  profiles: exports_external.array(ClaudeCodeProfileSchema),
@@ -13950,7 +13999,7 @@ var CONFIG_VERSION = "2.0.0", EXPORT_VERSION = "1.0.0";
13950
13999
  var init_versions2 = () => {};
13951
14000
 
13952
14001
  // src/constants/meta.ts
13953
- var APP_VERSION = "0.1.1";
14002
+ var APP_VERSION = "0.1.2";
13954
14003
  var init_meta = () => {};
13955
14004
 
13956
14005
  // src/constants/install.ts
@@ -14058,6 +14107,9 @@ var init_install = __esm(() => {
14058
14107
  // src/constants/proxy.ts
14059
14108
  var DEFAULT_PROXY_HOST = "127.0.0.1", DEFAULT_PROXY_PORT = 15721, SWIXTER_PROXY_AUTH_TOKEN = "swixter-local-proxy", SWIXTER_CLAUDE_MODEL = "SWIXTER_CLAUDE_MODEL", SWIXTER_CLAUDE_HAIKU_MODEL = "SWIXTER_CLAUDE_HAIKU_MODEL", SWIXTER_CLAUDE_SONNET_MODEL = "SWIXTER_CLAUDE_SONNET_MODEL", SWIXTER_CLAUDE_OPUS_MODEL = "SWIXTER_CLAUDE_OPUS_MODEL";
14060
14109
 
14110
+ // src/constants/api.ts
14111
+ var API_BASE = "https://api.swixter.com";
14112
+
14061
14113
  // src/constants/index.ts
14062
14114
  var init_constants = __esm(() => {
14063
14115
  init_coders();
@@ -19039,13 +19091,15 @@ __export(exports_manager, {
19039
19091
  saveConfig: () => saveConfig,
19040
19092
  resetAllData: () => resetAllData,
19041
19093
  profileExists: () => profileExists,
19094
+ markDirty: () => markDirty,
19042
19095
  loadConfig: () => loadConfig,
19043
19096
  listProfiles: () => listProfiles,
19044
19097
  getProfile: () => getProfile,
19045
19098
  getConfigPath: () => getConfigPath2,
19046
19099
  getActiveProfileForCoder: () => getActiveProfileForCoder,
19047
19100
  getActiveProfile: () => getActiveProfile,
19048
- deleteProfile: () => deleteProfile
19101
+ deleteProfile: () => deleteProfile,
19102
+ clearSyncMeta: () => clearSyncMeta
19049
19103
  });
19050
19104
  import { existsSync as existsSync5 } from "fs";
19051
19105
  import { mkdir as mkdir5, readFile as readFile5, writeFile as writeFile5, rename } from "fs/promises";
@@ -19109,6 +19163,11 @@ async function saveConfig(config2) {
19109
19163
  throw new Error(`Failed to save configuration: ${error46}`);
19110
19164
  }
19111
19165
  }
19166
+ function markDirty(config2) {
19167
+ if (config2.syncMeta) {
19168
+ config2.syncMeta.dirty = true;
19169
+ }
19170
+ }
19112
19171
  async function getActiveProfile() {
19113
19172
  return getActiveProfileForCoder("claude");
19114
19173
  }
@@ -19132,6 +19191,7 @@ async function setActiveProfileForCoder(coder, profileName) {
19132
19191
  config2.coders[coder] = { activeProfile: "" };
19133
19192
  }
19134
19193
  config2.coders[coder].activeProfile = profileName;
19194
+ markDirty(config2);
19135
19195
  await saveConfig(config2);
19136
19196
  }
19137
19197
  async function upsertProfile(profile, coder) {
@@ -19151,6 +19211,7 @@ async function upsertProfile(profile, coder) {
19151
19211
  config2.coders[coder].activeProfile = profile.name;
19152
19212
  }
19153
19213
  }
19214
+ markDirty(config2);
19154
19215
  await saveConfig(config2);
19155
19216
  }
19156
19217
  async function deleteProfile(profileName) {
@@ -19177,6 +19238,7 @@ async function deleteProfile(profileName) {
19177
19238
  config2.coders[coder].activeProfile = "";
19178
19239
  }
19179
19240
  }
19241
+ markDirty(config2);
19180
19242
  await saveConfig(config2);
19181
19243
  }
19182
19244
  async function listProfiles() {
@@ -19205,8 +19267,16 @@ async function resetAllData() {
19205
19267
  }
19206
19268
  }
19207
19269
  const defaultConfig = createDefaultConfig();
19270
+ markDirty(defaultConfig);
19208
19271
  await saveConfig(defaultConfig);
19209
19272
  }
19273
+ async function clearSyncMeta() {
19274
+ const config2 = await loadConfig();
19275
+ if (config2.syncMeta) {
19276
+ delete config2.syncMeta;
19277
+ await saveConfig(config2);
19278
+ }
19279
+ }
19210
19280
  var init_manager = __esm(() => {
19211
19281
  init_types2();
19212
19282
  init_constants();
@@ -23449,6 +23519,545 @@ var init_export = __esm(() => {
23449
23519
  init_constants();
23450
23520
  });
23451
23521
 
23522
+ // src/auth/client.ts
23523
+ async function apiRequest(path, options = {}) {
23524
+ const url2 = `${API_BASE}${path}`;
23525
+ const response = await fetch(url2, {
23526
+ ...options,
23527
+ headers: {
23528
+ "Content-Type": "application/json",
23529
+ ...options.headers
23530
+ }
23531
+ });
23532
+ if (!response.ok) {
23533
+ const error46 = await response.json().catch(() => ({}));
23534
+ throw new AuthError(response.status, error46.code || "UNKNOWN", error46.message || `HTTP ${response.status}`);
23535
+ }
23536
+ const text = await response.text();
23537
+ if (!text)
23538
+ return;
23539
+ return JSON.parse(text);
23540
+ }
23541
+ async function sendVerificationCode(email3) {
23542
+ return apiRequest("/api/auth/register/send-code", {
23543
+ method: "POST",
23544
+ body: JSON.stringify({ email: email3 })
23545
+ });
23546
+ }
23547
+ async function verifyAndRegister(data) {
23548
+ return apiRequest("/api/auth/register/verify", {
23549
+ method: "POST",
23550
+ body: JSON.stringify(data)
23551
+ });
23552
+ }
23553
+ async function loginUser(credentials) {
23554
+ return apiRequest("/api/auth/login", {
23555
+ method: "POST",
23556
+ body: JSON.stringify(credentials)
23557
+ });
23558
+ }
23559
+ async function refreshToken(refreshToken2) {
23560
+ return apiRequest("/api/auth/refresh", {
23561
+ method: "POST",
23562
+ body: JSON.stringify({ refreshToken: refreshToken2 })
23563
+ });
23564
+ }
23565
+ async function logoutUser(refreshToken2) {
23566
+ await apiRequest("/api/auth/logout", {
23567
+ method: "POST",
23568
+ body: JSON.stringify({ refreshToken: refreshToken2 })
23569
+ });
23570
+ }
23571
+ async function setPassword(accessToken, password) {
23572
+ await apiRequest("/api/auth/set-password", {
23573
+ method: "POST",
23574
+ headers: { Authorization: `Bearer ${accessToken}` },
23575
+ body: JSON.stringify({ password })
23576
+ });
23577
+ }
23578
+ async function deleteAccount(accessToken) {
23579
+ await apiRequest("/api/auth/account", {
23580
+ method: "DELETE",
23581
+ headers: { Authorization: `Bearer ${accessToken}` }
23582
+ });
23583
+ }
23584
+ async function sendMagicLink(email3) {
23585
+ return apiRequest("/api/auth/magic-link/send", {
23586
+ method: "POST",
23587
+ body: JSON.stringify({ email: email3 })
23588
+ });
23589
+ }
23590
+ async function verifyMagicLink(email3, token) {
23591
+ return apiRequest("/api/auth/magic-link/verify", {
23592
+ method: "POST",
23593
+ body: JSON.stringify({ email: email3, token })
23594
+ });
23595
+ }
23596
+ async function checkMagicLinkSession(sessionId) {
23597
+ return apiRequest(`/api/auth/magic-link/session/${encodeURIComponent(sessionId)}`, { method: "GET" });
23598
+ }
23599
+ var AuthError;
23600
+ var init_client = __esm(() => {
23601
+ AuthError = class AuthError extends Error {
23602
+ status;
23603
+ code;
23604
+ constructor(status, code, message) {
23605
+ super(message);
23606
+ this.status = status;
23607
+ this.code = code;
23608
+ this.name = "AuthError";
23609
+ }
23610
+ };
23611
+ });
23612
+
23613
+ // src/auth/token.ts
23614
+ import { existsSync as existsSync11 } from "fs";
23615
+ import { readFile as readFile8, writeFile as writeFile8, unlink as unlink2 } from "fs/promises";
23616
+ import { join as join10 } from "path";
23617
+ function getAuthFilePath() {
23618
+ return join10(getConfigDir("swixter"), AUTH_FILE);
23619
+ }
23620
+ async function loadAuthState() {
23621
+ const authPath = getAuthFilePath();
23622
+ if (!existsSync11(authPath))
23623
+ return null;
23624
+ try {
23625
+ const content = await readFile8(authPath, "utf-8");
23626
+ return JSON.parse(content);
23627
+ } catch {
23628
+ return null;
23629
+ }
23630
+ }
23631
+ async function saveAuthState(state) {
23632
+ const authPath = getAuthFilePath();
23633
+ await writeFile8(authPath, JSON.stringify(state, null, 2), { mode: 384, encoding: "utf-8" });
23634
+ }
23635
+ async function clearAuthState() {
23636
+ const authPath = getAuthFilePath();
23637
+ if (existsSync11(authPath)) {
23638
+ await unlink2(authPath);
23639
+ }
23640
+ }
23641
+ function isExpired(expiresAt) {
23642
+ const expiry = new Date(expiresAt).getTime();
23643
+ const now = Date.now();
23644
+ const bufferMs = 5 * 60 * 1000;
23645
+ return now >= expiry - bufferMs;
23646
+ }
23647
+ async function getAccessToken() {
23648
+ const state = await loadAuthState();
23649
+ if (!state)
23650
+ return null;
23651
+ if (!isExpired(state.expiresAt)) {
23652
+ return state.accessToken;
23653
+ }
23654
+ try {
23655
+ const refreshed = await refreshToken(state.refreshToken);
23656
+ state.accessToken = refreshed.accessToken;
23657
+ state.expiresAt = refreshed.expiresAt;
23658
+ await saveAuthState(state);
23659
+ return refreshed.accessToken;
23660
+ } catch {
23661
+ await clearAuthState();
23662
+ return null;
23663
+ }
23664
+ }
23665
+ async function isLoggedIn() {
23666
+ const token = await getAccessToken();
23667
+ return token !== null;
23668
+ }
23669
+ var AUTH_FILE = "auth.json";
23670
+ var init_token = __esm(() => {
23671
+ init_paths();
23672
+ init_client();
23673
+ });
23674
+
23675
+ // src/crypto/derive.ts
23676
+ async function deriveKey(password, saltBase64) {
23677
+ const salt = Uint8Array.from(atob(saltBase64), (c) => c.charCodeAt(0));
23678
+ const keyMaterial = await crypto.subtle.importKey("raw", new TextEncoder().encode(password), "PBKDF2", false, ["deriveKey"]);
23679
+ return crypto.subtle.deriveKey({
23680
+ name: "PBKDF2",
23681
+ salt,
23682
+ iterations: PBKDF2_ITERATIONS,
23683
+ hash: "SHA-256"
23684
+ }, keyMaterial, { name: "AES-GCM", length: 256 }, true, ["encrypt", "decrypt"]);
23685
+ }
23686
+ async function importKeyFromBase64(keyBase64) {
23687
+ const keyBytes = Uint8Array.from(atob(keyBase64), (c) => c.charCodeAt(0));
23688
+ return crypto.subtle.importKey("raw", keyBytes, { name: "AES-GCM", length: 256 }, false, ["encrypt", "decrypt"]);
23689
+ }
23690
+ async function exportKeyToBase64(key) {
23691
+ const rawKey = await crypto.subtle.exportKey("raw", key);
23692
+ return btoa(String.fromCharCode(...new Uint8Array(rawKey)));
23693
+ }
23694
+ var PBKDF2_ITERATIONS = 1e5;
23695
+
23696
+ // src/sync/client.ts
23697
+ async function syncRequest(path, accessToken, options = {}) {
23698
+ const url2 = `${API_BASE}${path}`;
23699
+ const response = await fetch(url2, {
23700
+ ...options,
23701
+ headers: {
23702
+ "Content-Type": "application/json",
23703
+ Authorization: `Bearer ${accessToken}`,
23704
+ ...options.headers
23705
+ }
23706
+ });
23707
+ if (!response.ok) {
23708
+ const error46 = await response.json().catch(() => ({}));
23709
+ throw new SyncError(response.status, error46.code || "UNKNOWN", error46.message || `HTTP ${response.status}`);
23710
+ }
23711
+ return response.json();
23712
+ }
23713
+ async function getSyncStatus(accessToken) {
23714
+ return syncRequest("/api/sync/status", accessToken);
23715
+ }
23716
+ async function pushData(accessToken, data) {
23717
+ return syncRequest("/api/sync/push", accessToken, {
23718
+ method: "POST",
23719
+ body: JSON.stringify(data)
23720
+ });
23721
+ }
23722
+ async function pullData(accessToken, dataKey) {
23723
+ return syncRequest(`/api/sync/pull?dataKey=${encodeURIComponent(dataKey)}`, accessToken);
23724
+ }
23725
+ var SyncError;
23726
+ var init_client2 = __esm(() => {
23727
+ SyncError = class SyncError extends Error {
23728
+ status;
23729
+ code;
23730
+ constructor(status, code, message) {
23731
+ super(message);
23732
+ this.status = status;
23733
+ this.code = code;
23734
+ this.name = "SyncError";
23735
+ }
23736
+ };
23737
+ });
23738
+
23739
+ // src/sync/merge.ts
23740
+ function getLocalVersion(meta2, dataKey) {
23741
+ if (!meta2)
23742
+ return 0;
23743
+ if (dataKey === "config")
23744
+ return meta2.configVersion;
23745
+ if (dataKey === "providers")
23746
+ return meta2.providersVersion;
23747
+ return 0;
23748
+ }
23749
+ function detectConflict(localMeta, remoteStatuses, dataKey) {
23750
+ const localVersion = getLocalVersion(localMeta, dataKey);
23751
+ const remoteEntry = remoteStatuses.find((s) => s.dataKey === dataKey);
23752
+ const remoteVersion = remoteEntry?.dataVersion ?? 0;
23753
+ if (localVersion === remoteVersion) {
23754
+ return null;
23755
+ }
23756
+ if (localVersion === 0) {
23757
+ return null;
23758
+ }
23759
+ if (remoteVersion === 0) {
23760
+ return null;
23761
+ }
23762
+ return {
23763
+ localVersion,
23764
+ remoteVersion,
23765
+ dataKey
23766
+ };
23767
+ }
23768
+
23769
+ // src/crypto/encrypt.ts
23770
+ async function encrypt(key, plaintext) {
23771
+ const iv = crypto.getRandomValues(new Uint8Array(IV_LENGTH));
23772
+ const encoded = new TextEncoder().encode(plaintext);
23773
+ const encrypted = await crypto.subtle.encrypt({ name: "AES-GCM", iv }, key, encoded);
23774
+ const combined = new Uint8Array(iv.length + encrypted.byteLength);
23775
+ combined.set(iv, 0);
23776
+ combined.set(new Uint8Array(encrypted), iv.length);
23777
+ return btoa(String.fromCharCode(...combined));
23778
+ }
23779
+ async function decrypt(key, encryptedBase64) {
23780
+ const combined = Uint8Array.from(atob(encryptedBase64), (c) => c.charCodeAt(0));
23781
+ const iv = combined.slice(0, IV_LENGTH);
23782
+ const ciphertext = combined.slice(IV_LENGTH);
23783
+ const decrypted = await crypto.subtle.decrypt({ name: "AES-GCM", iv }, key, ciphertext);
23784
+ return new TextDecoder().decode(decrypted);
23785
+ }
23786
+ var IV_LENGTH = 12;
23787
+
23788
+ // src/crypto/fields.ts
23789
+ async function encryptSensitiveFields(key, obj) {
23790
+ const result = { ...obj };
23791
+ for (const field of SENSITIVE_FIELDS) {
23792
+ const value = result[field];
23793
+ if (typeof value === "string") {
23794
+ result[field] = await encrypt(key, value);
23795
+ }
23796
+ }
23797
+ return result;
23798
+ }
23799
+ async function decryptSensitiveFields(key, obj) {
23800
+ const result = { ...obj };
23801
+ for (const field of SENSITIVE_FIELDS) {
23802
+ const value = result[field];
23803
+ if (typeof value === "string") {
23804
+ result[field] = await decrypt(key, value);
23805
+ }
23806
+ }
23807
+ return result;
23808
+ }
23809
+ var SENSITIVE_FIELDS;
23810
+ var init_fields = __esm(() => {
23811
+ SENSITIVE_FIELDS = ["apiKey", "authToken"];
23812
+ });
23813
+
23814
+ // src/sync/auto-sync.ts
23815
+ function setAutoSyncEnabled(enabled) {
23816
+ autoSyncEnabled = enabled;
23817
+ }
23818
+ var autoSyncEnabled = false;
23819
+ var init_auto_sync = __esm(() => {
23820
+ init_token();
23821
+ init_client2();
23822
+ init_manager();
23823
+ init_user_providers();
23824
+ init_fields();
23825
+ });
23826
+
23827
+ // src/cli/sync.ts
23828
+ var exports_sync = {};
23829
+ __export(exports_sync, {
23830
+ handleSyncCommand: () => handleSyncCommand
23831
+ });
23832
+ async function requireAuth() {
23833
+ const token = await getAccessToken();
23834
+ if (!token) {
23835
+ console.log(import_picocolors15.default.yellow("Not logged in."));
23836
+ console.log(import_picocolors15.default.dim("Run 'swixter auth login' first"));
23837
+ process.exit(1);
23838
+ }
23839
+ return token;
23840
+ }
23841
+ async function getEncryptionKey() {
23842
+ const state = await loadAuthState();
23843
+ if (!state)
23844
+ throw new Error("Not authenticated");
23845
+ if (state.encryptionKey) {
23846
+ return importKeyFromBase64(state.encryptionKey);
23847
+ }
23848
+ const masterPassword = await ge({
23849
+ message: "Master password:",
23850
+ validate: (v2) => {
23851
+ if (!v2)
23852
+ return "Master password is required for encryption";
23853
+ }
23854
+ });
23855
+ if (pD(masterPassword)) {
23856
+ process.exit(0);
23857
+ }
23858
+ return deriveKey(masterPassword, state.encryptionSalt);
23859
+ }
23860
+ async function cmdStatus() {
23861
+ const token = await requireAuth();
23862
+ const s = Y2();
23863
+ s.start("Checking sync status...");
23864
+ try {
23865
+ const remote = await getSyncStatus(token);
23866
+ const config2 = await loadConfig();
23867
+ const syncMeta = config2.syncMeta;
23868
+ s.stop("Sync status:");
23869
+ console.log();
23870
+ console.log(import_picocolors15.default.bold(" Remote:"));
23871
+ if (remote.statuses.length === 0) {
23872
+ console.log(import_picocolors15.default.dim(" No data synced"));
23873
+ } else {
23874
+ for (const entry of remote.statuses) {
23875
+ console.log(` ${import_picocolors15.default.cyan(entry.dataKey)}: v${entry.dataVersion} (${import_picocolors15.default.dim(entry.updatedAt)})`);
23876
+ }
23877
+ }
23878
+ console.log();
23879
+ console.log(import_picocolors15.default.bold(" Local:"));
23880
+ if (syncMeta) {
23881
+ console.log(` config: v${syncMeta.configVersion} (${import_picocolors15.default.dim(syncMeta.localUpdatedAt)})`);
23882
+ console.log(` providers: v${syncMeta.providersVersion} (${import_picocolors15.default.dim(syncMeta.lastSyncAt)})`);
23883
+ } else {
23884
+ console.log(import_picocolors15.default.dim(" Never synced"));
23885
+ }
23886
+ console.log();
23887
+ } catch (err) {
23888
+ s.stop(import_picocolors15.default.red("Failed to get sync status"));
23889
+ console.error(import_picocolors15.default.red(err.message || "Unknown error"));
23890
+ process.exit(1);
23891
+ }
23892
+ }
23893
+ async function cmdPush(forceLocal = false) {
23894
+ const token = await requireAuth();
23895
+ const s = Y2();
23896
+ s.start("Pushing config to cloud...");
23897
+ try {
23898
+ const remote = await getSyncStatus(token);
23899
+ const config2 = await loadConfig();
23900
+ const syncMeta = config2.syncMeta;
23901
+ if (!forceLocal) {
23902
+ const configConflict = detectConflict(syncMeta ?? null, remote.statuses, "config");
23903
+ if (configConflict) {
23904
+ s.stop(import_picocolors15.default.yellow("Version conflict detected!"));
23905
+ console.log(` Local version: ${configConflict.localVersion}, Remote version: ${configConflict.remoteVersion}`);
23906
+ console.log(import_picocolors15.default.dim(" Use --force-local to overwrite remote, or pull first"));
23907
+ process.exit(1);
23908
+ }
23909
+ }
23910
+ s.stop(import_picocolors15.default.dim("Requesting master password..."));
23911
+ const key = await getEncryptionKey();
23912
+ s.start("Encrypting and pushing...");
23913
+ const encryptedProfiles = {};
23914
+ for (const [id, profile] of Object.entries(config2.profiles)) {
23915
+ encryptedProfiles[id] = await encryptSensitiveFields(key, profile);
23916
+ }
23917
+ const configRemoteEntry = remote.statuses.find((s2) => s2.dataKey === "config");
23918
+ const configVersion = configRemoteEntry?.dataVersion ?? 0;
23919
+ const pushResult = await pushData(token, {
23920
+ dataKey: "config",
23921
+ encryptedData: JSON.stringify(encryptedProfiles),
23922
+ dataVersion: configVersion,
23923
+ clientTimestamp: new Date().toISOString()
23924
+ });
23925
+ const providers = await loadUserProviders();
23926
+ const providersRemoteEntry = remote.statuses.find((s2) => s2.dataKey === "providers");
23927
+ const providersVersion = providersRemoteEntry?.dataVersion ?? 0;
23928
+ const providersPushResult = await pushData(token, {
23929
+ dataKey: "providers",
23930
+ encryptedData: JSON.stringify(await encryptSensitiveFields(key, { providers })),
23931
+ dataVersion: providersVersion,
23932
+ clientTimestamp: new Date().toISOString()
23933
+ });
23934
+ const now = new Date().toISOString();
23935
+ const updatedMeta = {
23936
+ lastSyncAt: now,
23937
+ configVersion: pushResult.dataVersion,
23938
+ providersVersion: providersPushResult.dataVersion,
23939
+ localUpdatedAt: now
23940
+ };
23941
+ config2.syncMeta = updatedMeta;
23942
+ await saveConfig(config2);
23943
+ s.stop(import_picocolors15.default.green(`Pushed config (v${pushResult.dataVersion}), providers (v${providersPushResult.dataVersion})`));
23944
+ } catch (err) {
23945
+ s.stop(import_picocolors15.default.red("Push failed"));
23946
+ if (err instanceof SyncError && err.status === 409) {
23947
+ console.log(import_picocolors15.default.yellow(" Version conflict. Use --force-local to overwrite."));
23948
+ } else {
23949
+ console.error(import_picocolors15.default.red(err.message || "Unknown error"));
23950
+ }
23951
+ process.exit(1);
23952
+ }
23953
+ }
23954
+ async function cmdPull(forceRemote = false) {
23955
+ const token = await requireAuth();
23956
+ const s = Y2();
23957
+ s.start("Pulling config from cloud...");
23958
+ try {
23959
+ const pullResult = await pullData(token, "config");
23960
+ const config2 = await loadConfig();
23961
+ const syncMeta = config2.syncMeta;
23962
+ if (!forceRemote && syncMeta) {
23963
+ const configConflict = detectConflict(syncMeta, [{ dataKey: "config", dataVersion: pullResult.dataVersion, updatedAt: pullResult.updatedAt }], "config");
23964
+ if (configConflict) {
23965
+ s.stop(import_picocolors15.default.yellow("Version conflict detected!"));
23966
+ console.log(` Local version: ${configConflict.localVersion}, Remote version: ${configConflict.remoteVersion}`);
23967
+ console.log(import_picocolors15.default.dim(" Use --force-remote to overwrite local, or push first"));
23968
+ process.exit(1);
23969
+ }
23970
+ }
23971
+ s.stop(import_picocolors15.default.dim("Requesting master password..."));
23972
+ const key = await getEncryptionKey();
23973
+ s.start("Decrypting...");
23974
+ const encryptedProfiles = JSON.parse(pullResult.encryptedData);
23975
+ for (const [id, profile] of Object.entries(encryptedProfiles)) {
23976
+ config2.profiles[id] = await decryptSensitiveFields(key, profile);
23977
+ }
23978
+ let providersVersion = syncMeta?.providersVersion ?? 0;
23979
+ try {
23980
+ const providersResult = await pullData(token, "providers");
23981
+ const providersEncrypted = JSON.parse(providersResult.encryptedData);
23982
+ const providersDecrypted = await decryptSensitiveFields(key, providersEncrypted);
23983
+ if (providersDecrypted.providers && Array.isArray(providersDecrypted.providers)) {
23984
+ await saveUserProviders(providersDecrypted.providers);
23985
+ providersVersion = providersResult.dataVersion;
23986
+ }
23987
+ } catch (err) {
23988
+ if (err instanceof SyncError && err.status === 404) {} else {
23989
+ throw err;
23990
+ }
23991
+ }
23992
+ const now = new Date().toISOString();
23993
+ config2.syncMeta = {
23994
+ lastSyncAt: now,
23995
+ configVersion: pullResult.dataVersion,
23996
+ providersVersion,
23997
+ localUpdatedAt: now
23998
+ };
23999
+ await saveConfig(config2);
24000
+ s.stop(import_picocolors15.default.green(`Pulled config (v${pullResult.dataVersion}), providers (v${providersVersion})`));
24001
+ } catch (err) {
24002
+ s.stop(import_picocolors15.default.red("Pull failed"));
24003
+ if (err instanceof SyncError && err.status === 404) {
24004
+ console.log(import_picocolors15.default.dim("No remote data found. Push first with 'swixter sync push'"));
24005
+ } else {
24006
+ console.error(import_picocolors15.default.red(err.message || "Unknown error"));
24007
+ }
24008
+ process.exit(1);
24009
+ }
24010
+ }
24011
+ async function handleSyncCommand(args) {
24012
+ const subcommand = args[0];
24013
+ switch (subcommand) {
24014
+ case "push": {
24015
+ const forceLocal = args.includes("--force-local");
24016
+ await cmdPush(forceLocal);
24017
+ break;
24018
+ }
24019
+ case "pull": {
24020
+ const forceRemote = args.includes("--force-remote");
24021
+ await cmdPull(forceRemote);
24022
+ break;
24023
+ }
24024
+ case "status":
24025
+ await cmdStatus();
24026
+ break;
24027
+ case "enable":
24028
+ setAutoSyncEnabled(true);
24029
+ console.log(import_picocolors15.default.green("\u2713 Auto sync enabled"));
24030
+ break;
24031
+ case "disable":
24032
+ setAutoSyncEnabled(false);
24033
+ console.log(import_picocolors15.default.green("\u2713 Auto sync disabled"));
24034
+ break;
24035
+ default:
24036
+ console.log(import_picocolors15.default.red(`Unknown sync subcommand: ${subcommand}`));
24037
+ console.log();
24038
+ console.log(import_picocolors15.default.bold("Available subcommands:"));
24039
+ console.log(` ${import_picocolors15.default.cyan("push")} [--force-local] - Push config to cloud`);
24040
+ console.log(` ${import_picocolors15.default.cyan("pull")} [--force-remote] - Pull config from cloud`);
24041
+ console.log(` ${import_picocolors15.default.cyan("status")} - Show sync state`);
24042
+ console.log(` ${import_picocolors15.default.cyan("enable")} - Enable auto sync`);
24043
+ console.log(` ${import_picocolors15.default.cyan("disable")} - Disable auto sync`);
24044
+ console.log();
24045
+ process.exit(1);
24046
+ }
24047
+ }
24048
+ var import_picocolors15;
24049
+ var init_sync = __esm(() => {
24050
+ init_dist2();
24051
+ init_token();
24052
+ init_client2();
24053
+ init_client2();
24054
+ init_manager();
24055
+ init_user_providers();
24056
+ init_fields();
24057
+ init_auto_sync();
24058
+ import_picocolors15 = __toESM(require_picocolors(), 1);
24059
+ });
24060
+
23452
24061
  // src/cli/group.ts
23453
24062
  var exports_group = {};
23454
24063
  __export(exports_group, {
@@ -23459,7 +24068,7 @@ function cancelAndExit() {
23459
24068
  process.exit(EXIT_CODES.cancelled);
23460
24069
  }
23461
24070
  function exitWithError(message, exitCode = EXIT_CODES.generalError) {
23462
- console.log(import_picocolors15.default.red(message));
24071
+ console.log(import_picocolors17.default.red(message));
23463
24072
  process.exit(exitCode);
23464
24073
  }
23465
24074
  async function getExistingGroupNameSet() {
@@ -23533,7 +24142,7 @@ async function handleGroupCommand(args) {
23533
24142
  await cmdShow(args.slice(1));
23534
24143
  break;
23535
24144
  default:
23536
- console.log(import_picocolors15.default.red(`Unknown subcommand: ${subcommand}`));
24145
+ console.log(import_picocolors17.default.red(`Unknown subcommand: ${subcommand}`));
23537
24146
  console.log(groupHelp());
23538
24147
  process.exit(EXIT_CODES.invalidArgument);
23539
24148
  }
@@ -23541,15 +24150,15 @@ async function handleGroupCommand(args) {
23541
24150
  async function cmdList4() {
23542
24151
  const groups = await listGroups();
23543
24152
  if (groups.length === 0) {
23544
- console.log(import_picocolors15.default.yellow("No groups defined. Create one with: swixter group create"));
24153
+ console.log(import_picocolors17.default.yellow("No groups defined. Create one with: swixter group create"));
23545
24154
  return;
23546
24155
  }
23547
24156
  console.log();
23548
- console.log(import_picocolors15.default.bold("Groups:"));
24157
+ console.log(import_picocolors17.default.bold("Groups:"));
23549
24158
  console.log();
23550
24159
  for (const group of groups) {
23551
- const marker = group.isDefault ? import_picocolors15.default.green("\u2713") : " ";
23552
- const name = group.isDefault ? import_picocolors15.default.green(group.name) : import_picocolors15.default.cyan(group.name);
24160
+ const marker = group.isDefault ? import_picocolors17.default.green("\u2713") : " ";
24161
+ const name = group.isDefault ? import_picocolors17.default.green(group.name) : import_picocolors17.default.cyan(group.name);
23553
24162
  console.log(` ${marker} ${name} - ${group.profiles.length} profiles`);
23554
24163
  }
23555
24164
  console.log();
@@ -23590,9 +24199,9 @@ async function cmdCreate4(args) {
23590
24199
  }
23591
24200
  const group = await createGroup({ name, profiles });
23592
24201
  console.log();
23593
- console.log(import_picocolors15.default.green(`\u2713 Group "${group.name}" created`));
23594
- console.log(import_picocolors15.default.dim(` ID: ${group.id}`));
23595
- console.log(import_picocolors15.default.dim(` Profiles: ${group.profiles.join(", ")}`));
24202
+ console.log(import_picocolors17.default.green(`\u2713 Group "${group.name}" created`));
24203
+ console.log(import_picocolors17.default.dim(` ID: ${group.id}`));
24204
+ console.log(import_picocolors17.default.dim(` Profiles: ${group.profiles.join(", ")}`));
23596
24205
  console.log();
23597
24206
  }
23598
24207
  async function promptForProfiles(initialSelectedProfiles = []) {
@@ -23630,7 +24239,7 @@ async function promptForProfiles(initialSelectedProfiles = []) {
23630
24239
  return selectedProfiles;
23631
24240
  }
23632
24241
  console.log();
23633
- console.log(import_picocolors15.default.dim("Set failover priority. Priority 1 is tried first."));
24242
+ console.log(import_picocolors17.default.dim("Set failover priority. Priority 1 is tried first."));
23634
24243
  return await promptForProfileOrder(selectedProfiles);
23635
24244
  }
23636
24245
  async function promptForProfileOrder(selectedProfiles) {
@@ -23734,9 +24343,9 @@ async function cmdEdit4(args) {
23734
24343
  exitWithError(`Failed to update group: ${group.name}`);
23735
24344
  }
23736
24345
  console.log();
23737
- console.log(import_picocolors15.default.green(`\u2713 Group "${updated.name}" updated`));
23738
- console.log(import_picocolors15.default.dim(` ID: ${updated.id}`));
23739
- console.log(import_picocolors15.default.dim(` Profiles: ${updated.profiles.join(", ")}`));
24346
+ console.log(import_picocolors17.default.green(`\u2713 Group "${updated.name}" updated`));
24347
+ console.log(import_picocolors17.default.dim(` ID: ${updated.id}`));
24348
+ console.log(import_picocolors17.default.dim(` Profiles: ${updated.profiles.join(", ")}`));
23740
24349
  console.log();
23741
24350
  }
23742
24351
  async function cmdDelete4(args) {
@@ -23762,7 +24371,7 @@ async function cmdDelete4(args) {
23762
24371
  }
23763
24372
  if (confirmed) {
23764
24373
  await deleteGroup(group.id);
23765
- console.log(import_picocolors15.default.green(`\u2713 Group "${group.name}" deleted`));
24374
+ console.log(import_picocolors17.default.green(`\u2713 Group "${group.name}" deleted`));
23766
24375
  }
23767
24376
  }
23768
24377
  async function cmdSetDefault(args) {
@@ -23772,7 +24381,7 @@ async function cmdSetDefault(args) {
23772
24381
  }
23773
24382
  const group = await setDefaultGroup(name);
23774
24383
  if (group) {
23775
- console.log(import_picocolors15.default.green(`\u2713 "${group.name}" is now the default group`));
24384
+ console.log(import_picocolors17.default.green(`\u2713 "${group.name}" is now the default group`));
23776
24385
  } else {
23777
24386
  exitWithError(`Group not found: ${name}`, EXIT_CODES.notFound);
23778
24387
  }
@@ -23787,37 +24396,37 @@ async function cmdShow(args) {
23787
24396
  exitWithError(`Group not found: ${name}`, EXIT_CODES.notFound);
23788
24397
  }
23789
24398
  console.log();
23790
- console.log(import_picocolors15.default.bold("Group: ") + import_picocolors15.default.cyan(group.name));
23791
- console.log(import_picocolors15.default.bold("ID: ") + group.id);
23792
- console.log(import_picocolors15.default.bold("Default: ") + (group.isDefault ? import_picocolors15.default.green("yes") : import_picocolors15.default.dim("no")));
23793
- console.log(import_picocolors15.default.bold("Profiles:"));
24399
+ console.log(import_picocolors17.default.bold("Group: ") + import_picocolors17.default.cyan(group.name));
24400
+ console.log(import_picocolors17.default.bold("ID: ") + group.id);
24401
+ console.log(import_picocolors17.default.bold("Default: ") + (group.isDefault ? import_picocolors17.default.green("yes") : import_picocolors17.default.dim("no")));
24402
+ console.log(import_picocolors17.default.bold("Profiles:"));
23794
24403
  group.profiles.forEach((p2, i2) => {
23795
- console.log(import_picocolors15.default.dim(` ${i2 + 1}. ${p2}`));
24404
+ console.log(import_picocolors17.default.dim(` ${i2 + 1}. ${p2}`));
23796
24405
  });
23797
24406
  console.log();
23798
24407
  }
23799
24408
  function groupHelp() {
23800
24409
  return `
23801
- ${import_picocolors15.default.bold("Swixter Group Commands")}
23802
-
23803
- ${import_picocolors15.default.bold("Usage:")}
23804
- ${import_picocolors15.default.green("swixter group <command> [options]")}
23805
-
23806
- ${import_picocolors15.default.bold("Commands:")}
23807
- ${import_picocolors15.default.cyan("list, ls")} List all groups
23808
- ${import_picocolors15.default.cyan("create, new")} Create a new group
23809
- ${import_picocolors15.default.cyan("edit")} Edit a group
23810
- ${import_picocolors15.default.cyan("delete, rm")} Delete a group
23811
- ${import_picocolors15.default.cyan("set-default")} Set default group
23812
- ${import_picocolors15.default.cyan("show, info")} Show group details
23813
-
23814
- ${import_picocolors15.default.bold("Examples:")}
23815
- ${import_picocolors15.default.green("swixter group list")}
23816
- ${import_picocolors15.default.green("swixter group create my-group --profiles profile-a,profile-b")}
23817
- ${import_picocolors15.default.green("swixter group set-default my-group")}
24410
+ ${import_picocolors17.default.bold("Swixter Group Commands")}
24411
+
24412
+ ${import_picocolors17.default.bold("Usage:")}
24413
+ ${import_picocolors17.default.green("swixter group <command> [options]")}
24414
+
24415
+ ${import_picocolors17.default.bold("Commands:")}
24416
+ ${import_picocolors17.default.cyan("list, ls")} List all groups
24417
+ ${import_picocolors17.default.cyan("create, new")} Create a new group
24418
+ ${import_picocolors17.default.cyan("edit")} Edit a group
24419
+ ${import_picocolors17.default.cyan("delete, rm")} Delete a group
24420
+ ${import_picocolors17.default.cyan("set-default")} Set default group
24421
+ ${import_picocolors17.default.cyan("show, info")} Show group details
24422
+
24423
+ ${import_picocolors17.default.bold("Examples:")}
24424
+ ${import_picocolors17.default.green("swixter group list")}
24425
+ ${import_picocolors17.default.green("swixter group create my-group --profiles profile-a,profile-b")}
24426
+ ${import_picocolors17.default.green("swixter group set-default my-group")}
23818
24427
  `;
23819
24428
  }
23820
- var import_picocolors15;
24429
+ var import_picocolors17;
23821
24430
  var init_group = __esm(() => {
23822
24431
  init_dist2();
23823
24432
  init_manager2();
@@ -23826,7 +24435,7 @@ var init_group = __esm(() => {
23826
24435
  init_messages();
23827
24436
  init_formatting();
23828
24437
  init_validation();
23829
- import_picocolors15 = __toESM(require_picocolors(), 1);
24438
+ import_picocolors17 = __toESM(require_picocolors(), 1);
23830
24439
  });
23831
24440
 
23832
24441
  // src/cli/proxy.ts
@@ -23926,13 +24535,13 @@ async function handleProxyCommand(args) {
23926
24535
  await cmdRun4(args.slice(1));
23927
24536
  break;
23928
24537
  case "status":
23929
- await cmdStatus();
24538
+ await cmdStatus3();
23930
24539
  break;
23931
24540
  default:
23932
24541
  if (!subcommand || subcommand === "status") {
23933
- await cmdStatus();
24542
+ await cmdStatus3();
23934
24543
  } else {
23935
- console.log(import_picocolors16.default.red(`Unknown subcommand: ${subcommand}`));
24544
+ console.log(import_picocolors18.default.red(`Unknown subcommand: ${subcommand}`));
23936
24545
  console.log(proxyHelp());
23937
24546
  process.exit(1);
23938
24547
  }
@@ -23964,21 +24573,21 @@ async function cmdStart(args) {
23964
24573
  if (groupName) {
23965
24574
  const group = await getGroup(groupName);
23966
24575
  if (!group) {
23967
- console.log(import_picocolors16.default.red(`Group "${groupName}" not found`));
24576
+ console.log(import_picocolors18.default.red(`Group "${groupName}" not found`));
23968
24577
  process.exit(EXIT_CODES.notFound);
23969
24578
  return;
23970
24579
  }
23971
24580
  }
23972
24581
  const defaultStatus = getProxyStatus("default");
23973
24582
  if (defaultStatus.running) {
23974
- console.log(import_picocolors16.default.yellow(`Default proxy already running on ${defaultStatus.host}:${defaultStatus.port}`));
24583
+ console.log(import_picocolors18.default.yellow(`Default proxy already running on ${defaultStatus.host}:${defaultStatus.port}`));
23975
24584
  return;
23976
24585
  }
23977
24586
  if (!groupName) {
23978
24587
  const activeGroup = await getActiveGroup();
23979
24588
  if (activeGroup) {
23980
24589
  groupName = activeGroup.name;
23981
- console.log(import_picocolors16.default.dim(`Using default group: ${groupName}`));
24590
+ console.log(import_picocolors18.default.dim(`Using default group: ${groupName}`));
23982
24591
  }
23983
24592
  }
23984
24593
  const resolvedPort = port || DEFAULT_PROXY_PORT;
@@ -23989,7 +24598,7 @@ async function cmdStart(args) {
23989
24598
  }
23990
24599
  }
23991
24600
  async function cmdStartBlocking(config2) {
23992
- console.log(import_picocolors16.default.cyan("Starting proxy server..."));
24601
+ console.log(import_picocolors18.default.cyan("Starting proxy server..."));
23993
24602
  await startProxyServer({
23994
24603
  instanceId: "default",
23995
24604
  type: "service",
@@ -23999,30 +24608,28 @@ async function cmdStartBlocking(config2) {
23999
24608
  groupName: config2.groupName
24000
24609
  });
24001
24610
  console.log();
24002
- console.log(import_picocolors16.default.green("\u2713 Proxy server started"));
24611
+ console.log(import_picocolors18.default.green("\u2713 Proxy server started"));
24003
24612
  console.log(` Instance: default (service)`);
24004
24613
  console.log(` Address: ${config2.host}:${config2.port}`);
24005
24614
  console.log(` Timeout: ${config2.timeout}ms`);
24006
- console.log(import_picocolors16.default.dim(` Group: ${config2.groupName || "none"}`));
24007
- console.log(import_picocolors16.default.dim(` Endpoints:`));
24008
- console.log(import_picocolors16.default.dim(` - /v1/chat/completions (OpenAI)`));
24009
- console.log(import_picocolors16.default.dim(` - /v1/messages (Anthropic)`));
24010
- console.log(import_picocolors16.default.dim(` - /v1/responses (Anthropic)`));
24011
- console.log(import_picocolors16.default.dim(` - /anthropic/* (Anthropic)`));
24012
- console.log(import_picocolors16.default.dim(` - /health (Health check)`));
24615
+ console.log(import_picocolors18.default.dim(` Group: ${config2.groupName || "none"}`));
24616
+ console.log(import_picocolors18.default.dim(` Endpoints:`));
24617
+ console.log(import_picocolors18.default.dim(` - /v1/chat/completions (OpenAI)`));
24618
+ console.log(import_picocolors18.default.dim(` - /v1/messages (Anthropic)`));
24619
+ console.log(import_picocolors18.default.dim(` - /v1/responses (Anthropic)`));
24620
+ console.log(import_picocolors18.default.dim(` - /anthropic/* (Anthropic)`));
24621
+ console.log(import_picocolors18.default.dim(` - /health (Health check)`));
24013
24622
  console.log();
24014
- console.log(import_picocolors16.default.dim("Press Ctrl+C to stop"));
24623
+ console.log(import_picocolors18.default.dim("Press Ctrl+C to stop"));
24015
24624
  }
24016
24625
  async function cmdStartDaemon(config2) {
24017
24626
  const defaultStatus = getProxyStatus("default");
24018
24627
  if (defaultStatus.running) {
24019
- console.log(import_picocolors16.default.yellow(`Default proxy already running on ${defaultStatus.host}:${defaultStatus.port}`));
24628
+ console.log(import_picocolors18.default.yellow(`Default proxy already running on ${defaultStatus.host}:${defaultStatus.port}`));
24020
24629
  return;
24021
24630
  }
24022
- console.log(import_picocolors16.default.cyan("Starting proxy server in background..."));
24631
+ console.log(import_picocolors18.default.cyan("Starting proxy server in background..."));
24023
24632
  const args = [
24024
- "run",
24025
- "cli",
24026
24633
  "proxy",
24027
24634
  "start",
24028
24635
  "--host",
@@ -24035,22 +24642,21 @@ async function cmdStartDaemon(config2) {
24035
24642
  if (config2.groupName) {
24036
24643
  args.push("--group", config2.groupName);
24037
24644
  }
24038
- const child = spawn3("bun", args, {
24645
+ const child = spawn3(process.execPath, [process.argv[1], ...args], {
24039
24646
  detached: true,
24040
- stdio: "ignore",
24041
- cwd: process.cwd()
24647
+ stdio: "ignore"
24042
24648
  });
24043
24649
  child.unref();
24044
24650
  const isHealthy = await waitForProxyHealth(config2.host, config2.port);
24045
24651
  const hasRuntime = isHealthy ? await waitForProxyRuntime(config2.host, config2.port) : false;
24046
24652
  if (isHealthy && hasRuntime) {
24047
- console.log(import_picocolors16.default.green("\u2713 Proxy server started in background"));
24653
+ console.log(import_picocolors18.default.green("\u2713 Proxy server started in background"));
24048
24654
  console.log(` Instance: default (service)`);
24049
24655
  console.log(` Address: ${config2.host}:${config2.port}`);
24050
- console.log(import_picocolors16.default.dim(` Use "swixter proxy status" to check status`));
24051
- console.log(import_picocolors16.default.dim(` Use "swixter proxy stop" to stop`));
24656
+ console.log(import_picocolors18.default.dim(` Use "swixter proxy status" to check status`));
24657
+ console.log(import_picocolors18.default.dim(` Use "swixter proxy stop" to stop`));
24052
24658
  } else {
24053
- console.log(import_picocolors16.default.red("\u2717 Failed to start proxy server in background"));
24659
+ console.log(import_picocolors18.default.red("\u2717 Failed to start proxy server in background"));
24054
24660
  process.exit(1);
24055
24661
  }
24056
24662
  }
@@ -24058,11 +24664,11 @@ async function cmdStop(args) {
24058
24664
  const instanceId = args[0] || "default";
24059
24665
  const status = getProxyStatus(instanceId);
24060
24666
  if (!status.running) {
24061
- console.log(import_picocolors16.default.yellow(`Proxy instance "${instanceId}" is not running`));
24667
+ console.log(import_picocolors18.default.yellow(`Proxy instance "${instanceId}" is not running`));
24062
24668
  return;
24063
24669
  }
24064
24670
  await stopProxyServer(instanceId);
24065
- console.log(import_picocolors16.default.green(`\u2713 Proxy instance "${instanceId}" stopped`));
24671
+ console.log(import_picocolors18.default.green(`\u2713 Proxy instance "${instanceId}" stopped`));
24066
24672
  }
24067
24673
  async function cmdRun4(args) {
24068
24674
  const doubleDash = args.indexOf("--");
@@ -24090,17 +24696,17 @@ async function cmdRun4(args) {
24090
24696
  groupName = activeGroup?.name;
24091
24697
  }
24092
24698
  if (!groupName) {
24093
- console.log(import_picocolors16.default.red("No group specified and no default group set"));
24094
- console.log(import_picocolors16.default.dim("Use --group or create a default group first"));
24699
+ console.log(import_picocolors18.default.red("No group specified and no default group set"));
24700
+ console.log(import_picocolors18.default.dim("Use --group or create a default group first"));
24095
24701
  return;
24096
24702
  }
24097
24703
  const group = await getGroup(groupName);
24098
24704
  if (!group) {
24099
- console.log(import_picocolors16.default.red(`Group "${groupName}" not found`));
24705
+ console.log(import_picocolors18.default.red(`Group "${groupName}" not found`));
24100
24706
  process.exit(EXIT_CODES.notFound);
24101
24707
  return;
24102
24708
  }
24103
- console.log(import_picocolors16.default.cyan("Starting proxy server..."));
24709
+ console.log(import_picocolors18.default.cyan("Starting proxy server..."));
24104
24710
  const allInstances = listProxyInstances();
24105
24711
  const runtimeBinding = resolveProxyRuntimeBinding({
24106
24712
  groupName,
@@ -24119,15 +24725,15 @@ async function cmdRun4(args) {
24119
24725
  }
24120
24726
  const coder = coderArgs[0];
24121
24727
  if (!coder) {
24122
- console.log(import_picocolors16.default.red("Coder command required after --"));
24123
- console.log(import_picocolors16.default.dim("Example: swixter proxy run -- claude"));
24728
+ console.log(import_picocolors18.default.red("Coder command required after --"));
24729
+ console.log(import_picocolors18.default.dim("Example: swixter proxy run -- claude"));
24124
24730
  await stopProxyServer(instanceId);
24125
24731
  return;
24126
24732
  }
24127
24733
  const env = buildCoderProxyEnv(coder, process.env, runtimeBinding.port);
24128
- console.log(import_picocolors16.default.green(`\u2713 Running: ${coder} ${coderArgs.slice(1).join(" ")}`));
24129
- console.log(import_picocolors16.default.dim(` Instance: ${instanceId} (run)`));
24130
- console.log(import_picocolors16.default.dim(` Proxy: ${runtimeBinding.host}:${runtimeBinding.port}`));
24734
+ console.log(import_picocolors18.default.green(`\u2713 Running: ${coder} ${coderArgs.slice(1).join(" ")}`));
24735
+ console.log(import_picocolors18.default.dim(` Instance: ${instanceId} (run)`));
24736
+ console.log(import_picocolors18.default.dim(` Proxy: ${runtimeBinding.host}:${runtimeBinding.port}`));
24131
24737
  if (coder === "claude") {
24132
24738
  const firstProfile = group.profiles[0] ? await getProfile(group.profiles[0]) : null;
24133
24739
  const proxyProfile = {
@@ -24162,21 +24768,21 @@ async function cmdRun4(args) {
24162
24768
  process.exit(1);
24163
24769
  });
24164
24770
  }
24165
- async function cmdStatus() {
24771
+ async function cmdStatus3() {
24166
24772
  const instances = listProxyInstances();
24167
24773
  console.log();
24168
- console.log(import_picocolors16.default.bold("Proxy Status:"));
24774
+ console.log(import_picocolors18.default.bold("Proxy Status:"));
24169
24775
  console.log();
24170
24776
  const running = instances.filter((s) => s.running);
24171
24777
  if (running.length === 0) {
24172
- console.log(` ${import_picocolors16.default.red("\u25CF")} No proxy instances running`);
24778
+ console.log(` ${import_picocolors18.default.red("\u25CF")} No proxy instances running`);
24173
24779
  console.log();
24174
- console.log(import_picocolors16.default.dim(" Start with: swixter proxy start"));
24780
+ console.log(import_picocolors18.default.dim(" Start with: swixter proxy start"));
24175
24781
  return;
24176
24782
  }
24177
24783
  for (const status of running) {
24178
- const typeLabel = status.type === "service" ? import_picocolors16.default.cyan("service") : import_picocolors16.default.yellow("run");
24179
- console.log(` ${import_picocolors16.default.green("\u25CF")} ${import_picocolors16.default.bold(status.instanceId)} (${typeLabel})`);
24784
+ const typeLabel = status.type === "service" ? import_picocolors18.default.cyan("service") : import_picocolors18.default.yellow("run");
24785
+ console.log(` ${import_picocolors18.default.green("\u25CF")} ${import_picocolors18.default.bold(status.instanceId)} (${typeLabel})`);
24180
24786
  console.log(` Address: ${status.host}:${status.port}`);
24181
24787
  console.log(` Group: ${status.groupName || "none"}`);
24182
24788
  console.log(` Requests: ${status.requestCount} | Errors: ${status.errorCount}`);
@@ -24188,35 +24794,35 @@ async function cmdStatus() {
24188
24794
  }
24189
24795
  function proxyHelp() {
24190
24796
  return `
24191
- ${import_picocolors16.default.bold("Swixter Proxy Commands")}
24192
-
24193
- ${import_picocolors16.default.bold("Usage:")}
24194
- ${import_picocolors16.default.green("swixter proxy <command> [options]")}
24195
-
24196
- ${import_picocolors16.default.bold("Commands:")}
24197
- ${import_picocolors16.default.cyan("start")} Start proxy server (default instance)
24198
- ${import_picocolors16.default.cyan("stop")} [instanceId] Stop proxy instance (default: "default")
24199
- ${import_picocolors16.default.cyan("run")} Start proxy and run coder with env vars
24200
- ${import_picocolors16.default.cyan("status")} Show all proxy instances
24201
-
24202
- ${import_picocolors16.default.bold("Options:")}
24203
- ${import_picocolors16.default.dim("--group <name>")} Use specified group
24204
- ${import_picocolors16.default.dim("--port <port>")} Proxy port (default: 15721)
24205
- ${import_picocolors16.default.dim("--host <host>")} Proxy host (default: 127.0.0.1)
24206
- ${import_picocolors16.default.dim("--timeout <ms>")} Request timeout in ms (default: 3000000)
24207
- ${import_picocolors16.default.dim("--daemon")} Run in background (detached)
24208
-
24209
- ${import_picocolors16.default.bold("Examples:")}
24210
- ${import_picocolors16.default.green("swixter proxy start")}
24211
- ${import_picocolors16.default.green("swixter proxy start --daemon")}
24212
- ${import_picocolors16.default.green("swixter proxy start --group my-group")}
24213
- ${import_picocolors16.default.green("swixter proxy run --group my-group -- claude")}
24214
- ${import_picocolors16.default.green("swixter proxy stop")}
24215
- ${import_picocolors16.default.green("swixter proxy stop run-15722")}
24216
- ${import_picocolors16.default.dim(" # Stop a specific run instance")}
24797
+ ${import_picocolors18.default.bold("Swixter Proxy Commands")}
24798
+
24799
+ ${import_picocolors18.default.bold("Usage:")}
24800
+ ${import_picocolors18.default.green("swixter proxy <command> [options]")}
24801
+
24802
+ ${import_picocolors18.default.bold("Commands:")}
24803
+ ${import_picocolors18.default.cyan("start")} Start proxy server (default instance)
24804
+ ${import_picocolors18.default.cyan("stop")} [instanceId] Stop proxy instance (default: "default")
24805
+ ${import_picocolors18.default.cyan("run")} Start proxy and run coder with env vars
24806
+ ${import_picocolors18.default.cyan("status")} Show all proxy instances
24807
+
24808
+ ${import_picocolors18.default.bold("Options:")}
24809
+ ${import_picocolors18.default.dim("--group <name>")} Use specified group
24810
+ ${import_picocolors18.default.dim("--port <port>")} Proxy port (default: 15721)
24811
+ ${import_picocolors18.default.dim("--host <host>")} Proxy host (default: 127.0.0.1)
24812
+ ${import_picocolors18.default.dim("--timeout <ms>")} Request timeout in ms (default: 3000000)
24813
+ ${import_picocolors18.default.dim("--daemon")} Run in background (detached)
24814
+
24815
+ ${import_picocolors18.default.bold("Examples:")}
24816
+ ${import_picocolors18.default.green("swixter proxy start")}
24817
+ ${import_picocolors18.default.green("swixter proxy start --daemon")}
24818
+ ${import_picocolors18.default.green("swixter proxy start --group my-group")}
24819
+ ${import_picocolors18.default.green("swixter proxy run --group my-group -- claude")}
24820
+ ${import_picocolors18.default.green("swixter proxy stop")}
24821
+ ${import_picocolors18.default.green("swixter proxy stop run-15722")}
24822
+ ${import_picocolors18.default.dim(" # Stop a specific run instance")}
24217
24823
  `;
24218
24824
  }
24219
- var import_picocolors16;
24825
+ var import_picocolors18;
24220
24826
  var init_proxy = __esm(() => {
24221
24827
  init_manager();
24222
24828
  init_claude2();
@@ -24224,12 +24830,12 @@ var init_proxy = __esm(() => {
24224
24830
  init_formatting();
24225
24831
  init_server();
24226
24832
  init_manager2();
24227
- import_picocolors16 = __toESM(require_picocolors(), 1);
24833
+ import_picocolors18 = __toESM(require_picocolors(), 1);
24228
24834
  });
24229
24835
 
24230
24836
  // src/cli/index.ts
24231
24837
  init_claude2();
24232
- var import_picocolors17 = __toESM(require_picocolors(), 1);
24838
+ var import_picocolors19 = __toESM(require_picocolors(), 1);
24233
24839
 
24234
24840
  // src/cli/qwen.ts
24235
24841
  init_dist2();
@@ -27784,7 +28390,489 @@ async function showProvider(providerId) {
27784
28390
  console.log();
27785
28391
  }
27786
28392
 
28393
+ // src/cli/auth.ts
28394
+ init_dist2();
28395
+ init_client();
28396
+ init_token();
28397
+ init_manager();
28398
+ var import_picocolors16 = __toESM(require_picocolors(), 1);
28399
+ import * as readline from "readline";
28400
+ var MAGIC_LINK_POLL_INTERVAL_MS = 2000;
28401
+ var MAGIC_LINK_MAX_ATTEMPTS = 300;
28402
+ async function cmdRegister() {
28403
+ Ie(import_picocolors16.default.bold(import_picocolors16.default.cyan("Register Swixter Account")));
28404
+ const email3 = await he({
28405
+ message: "Email:",
28406
+ validate: (v2) => {
28407
+ if (!v2)
28408
+ return "Email is required";
28409
+ if (!v2.includes("@"))
28410
+ return "Invalid email format";
28411
+ }
28412
+ });
28413
+ if (pD(email3))
28414
+ return;
28415
+ const s = Y2();
28416
+ s.start("Sending verification code...");
28417
+ try {
28418
+ const sendResult = await sendVerificationCode(email3);
28419
+ s.stop(import_picocolors16.default.green(`\u2713 Verification code sent! (Expires in ${sendResult.expiresIn}s)`));
28420
+ } catch (err) {
28421
+ s.stop(import_picocolors16.default.red("\u2717 Failed to send verification code"));
28422
+ if (err.status === 409) {
28423
+ console.error(import_picocolors16.default.red("This email is already registered. Try logging in instead."));
28424
+ } else if (err.status === 429) {
28425
+ console.error(import_picocolors16.default.red(err.message || "Please wait before requesting a new code."));
28426
+ } else {
28427
+ console.error(import_picocolors16.default.red(err.message || "Unknown error"));
28428
+ }
28429
+ process.exit(1);
28430
+ }
28431
+ const code = await he({
28432
+ message: "Enter the 6-digit verification code sent to your email:",
28433
+ validate: (v2) => {
28434
+ if (!v2)
28435
+ return "Verification code is required";
28436
+ if (!/^\d{6}$/.test(v2))
28437
+ return "Please enter a 6-digit code";
28438
+ }
28439
+ });
28440
+ if (pD(code))
28441
+ return;
28442
+ const password = await ge({
28443
+ message: "Create password:",
28444
+ validate: (v2) => {
28445
+ if (!v2)
28446
+ return "Password is required";
28447
+ if (v2.length < 6)
28448
+ return "Password must be at least 6 characters";
28449
+ }
28450
+ });
28451
+ if (pD(password))
28452
+ return;
28453
+ const displayName = await he({
28454
+ message: "Display name (optional):"
28455
+ });
28456
+ if (pD(displayName))
28457
+ return;
28458
+ s.start("Creating account...");
28459
+ try {
28460
+ const result = await verifyAndRegister({
28461
+ email: email3,
28462
+ code,
28463
+ password,
28464
+ displayName
28465
+ });
28466
+ await persistAuth(result);
28467
+ s.stop(import_picocolors16.default.green("\u2713 Account created and logged in!"));
28468
+ const state = await loadAuthState();
28469
+ await setupEncryptionAfterAuth(state);
28470
+ Se(`Welcome, ${import_picocolors16.default.cyan(result.user.displayName || result.user.email)}!`);
28471
+ } catch (err) {
28472
+ s.stop(import_picocolors16.default.red("\u2717 Registration failed"));
28473
+ if (err.status === 401) {
28474
+ console.error(import_picocolors16.default.red(err.message || "Invalid or expired verification code."));
28475
+ } else if (err.status === 409) {
28476
+ console.error(import_picocolors16.default.red("This email is already registered."));
28477
+ } else {
28478
+ console.error(import_picocolors16.default.red(err.message || "Unknown error"));
28479
+ }
28480
+ process.exit(1);
28481
+ }
28482
+ }
28483
+ async function cmdLogin() {
28484
+ Ie(import_picocolors16.default.bold(import_picocolors16.default.cyan("Login to Swixter")));
28485
+ const email3 = await he({
28486
+ message: "Email:",
28487
+ validate: (v2) => {
28488
+ if (!v2)
28489
+ return "Email is required";
28490
+ if (!v2.includes("@"))
28491
+ return "Invalid email format";
28492
+ }
28493
+ });
28494
+ if (pD(email3))
28495
+ return;
28496
+ const password = await ge({
28497
+ message: "Password:",
28498
+ validate: (v2) => {
28499
+ if (!v2)
28500
+ return "Password is required";
28501
+ }
28502
+ });
28503
+ if (pD(password))
28504
+ return;
28505
+ const s = Y2();
28506
+ s.start("Logging in...");
28507
+ try {
28508
+ const result = await loginUser({
28509
+ email: email3,
28510
+ password
28511
+ });
28512
+ const userChanged = await persistAuth(result);
28513
+ s.stop(import_picocolors16.default.green("\u2713 Logged in successfully!"));
28514
+ const state = await loadAuthState();
28515
+ if (state && !state.encryptionKey) {
28516
+ await setupEncryptionAfterAuth(state);
28517
+ }
28518
+ if (userChanged) {
28519
+ await promptSyncChoice(state);
28520
+ return;
28521
+ }
28522
+ Se(`Welcome back, ${import_picocolors16.default.cyan(result.user.displayName || result.user.email)}!`);
28523
+ } catch (err) {
28524
+ s.stop(import_picocolors16.default.red("\u2717 Login failed"));
28525
+ console.error(import_picocolors16.default.red(err.message || "Invalid email or password"));
28526
+ process.exit(1);
28527
+ }
28528
+ }
28529
+ async function cmdLogout() {
28530
+ const state = await loadAuthState();
28531
+ if (state) {
28532
+ try {
28533
+ await logoutUser(state.refreshToken);
28534
+ } catch {}
28535
+ }
28536
+ await clearAuthState();
28537
+ await clearSyncMeta();
28538
+ console.log(import_picocolors16.default.green("\u2713 Logged out"));
28539
+ }
28540
+ async function cmdStatus2() {
28541
+ const loggedIn = await isLoggedIn();
28542
+ if (!loggedIn) {
28543
+ console.log(import_picocolors16.default.yellow("Not logged in"));
28544
+ console.log(import_picocolors16.default.dim("Run 'swixter auth login' to sign in"));
28545
+ return;
28546
+ }
28547
+ const state = await loadAuthState();
28548
+ if (state) {
28549
+ console.log(import_picocolors16.default.green("\u2713 Logged in"));
28550
+ console.log(` Email: ${import_picocolors16.default.cyan(state.email)}`);
28551
+ console.log(` User ID: ${import_picocolors16.default.dim(state.userId)}`);
28552
+ console.log(` Expires: ${import_picocolors16.default.dim(state.expiresAt)}`);
28553
+ }
28554
+ }
28555
+ async function cmdDeleteAccount() {
28556
+ const state = await loadAuthState();
28557
+ if (!state) {
28558
+ console.log(import_picocolors16.default.yellow("Not logged in"));
28559
+ return;
28560
+ }
28561
+ const confirmed = await ye({
28562
+ message: import_picocolors16.default.red("This will permanently delete your cloud account and all synced data. Continue?"),
28563
+ initialValue: false
28564
+ });
28565
+ if (pD(confirmed) || !confirmed) {
28566
+ console.log(import_picocolors16.default.dim("Cancelled"));
28567
+ return;
28568
+ }
28569
+ const s = Y2();
28570
+ s.start("Deleting account...");
28571
+ try {
28572
+ const token = await getAccessToken();
28573
+ if (!token) {
28574
+ s.stop(import_picocolors16.default.red("Session expired. Please log in again."));
28575
+ process.exit(1);
28576
+ }
28577
+ await deleteAccount(token);
28578
+ await clearAuthState();
28579
+ await clearSyncMeta();
28580
+ s.stop(import_picocolors16.default.green("\u2713 Account deleted"));
28581
+ } catch (err) {
28582
+ s.stop(import_picocolors16.default.red("\u2717 Failed to delete account"));
28583
+ console.error(import_picocolors16.default.red(err.message || "Unknown error"));
28584
+ process.exit(1);
28585
+ }
28586
+ }
28587
+ async function persistAuth(result) {
28588
+ const previousState = await loadAuthState();
28589
+ const userChanged = previousState != null && previousState.email !== result.user.email;
28590
+ await saveAuthState({
28591
+ accessToken: result.accessToken,
28592
+ refreshToken: result.refreshToken,
28593
+ expiresAt: result.expiresAt,
28594
+ encryptionSalt: result.encryptionSalt,
28595
+ authMethod: "password",
28596
+ userId: result.user.id,
28597
+ email: result.user.email
28598
+ });
28599
+ if (userChanged) {
28600
+ await clearSyncMeta();
28601
+ }
28602
+ return userChanged;
28603
+ }
28604
+ async function setupEncryptionAfterAuth(state) {
28605
+ if (!state)
28606
+ return;
28607
+ const setupEncryption = await ye({
28608
+ message: "Set up end-to-end encryption for cloud sync?",
28609
+ initialValue: true
28610
+ });
28611
+ if (pD(setupEncryption) || !setupEncryption) {
28612
+ return;
28613
+ }
28614
+ const masterPassword = await ge({
28615
+ message: "Create master password for encryption (separate from login password):",
28616
+ validate: (v2) => {
28617
+ if (!v2)
28618
+ return "Master password is required";
28619
+ if (v2.length < 8)
28620
+ return "Must be at least 8 characters";
28621
+ }
28622
+ });
28623
+ if (pD(masterPassword))
28624
+ return;
28625
+ const key = await deriveKey(masterPassword, state.encryptionSalt);
28626
+ const keyBase64 = await exportKeyToBase64(key);
28627
+ const remember = await ye({
28628
+ message: "Save encryption key locally for automatic sync? (Less secure but convenient)",
28629
+ initialValue: false
28630
+ });
28631
+ if (!pD(remember) && remember) {
28632
+ state.encryptionKey = keyBase64;
28633
+ await saveAuthState(state);
28634
+ console.log(import_picocolors16.default.dim("Encryption key saved for automatic sync"));
28635
+ }
28636
+ }
28637
+ async function promptSyncChoice(state) {
28638
+ if (!state?.encryptionKey) {
28639
+ console.log(import_picocolors16.default.dim("Cloud sync requires an encryption key. Run 'swixter sync push' after setting one up."));
28640
+ return;
28641
+ }
28642
+ const choice = await ve({
28643
+ message: "Different account detected. How would you like to handle cloud data?",
28644
+ options: [
28645
+ { value: "pull", label: "Pull from cloud", hint: "Replace local profiles with cloud data" },
28646
+ { value: "push", label: "Push to cloud", hint: "Upload local profiles to this account" },
28647
+ { value: "skip", label: "Skip for now", hint: "No sync action" }
28648
+ ]
28649
+ });
28650
+ if (pD(choice))
28651
+ return;
28652
+ if (choice === "pull") {
28653
+ const { handleSyncCommand: handleSyncCommand2 } = await Promise.resolve().then(() => (init_sync(), exports_sync));
28654
+ await handleSyncCommand2(["pull", "--force-remote"]);
28655
+ } else if (choice === "push") {
28656
+ const { handleSyncCommand: handleSyncCommand2 } = await Promise.resolve().then(() => (init_sync(), exports_sync));
28657
+ await handleSyncCommand2(["push", "--force-local"]);
28658
+ }
28659
+ }
28660
+ async function promptSetPassword(state, hasPassword) {
28661
+ if (!state)
28662
+ return;
28663
+ if (hasPassword)
28664
+ return;
28665
+ const setPw = await ye({
28666
+ message: "Set a login password for future sign-ins?",
28667
+ initialValue: true
28668
+ });
28669
+ if (pD(setPw) || !setPw)
28670
+ return;
28671
+ const password = await ge({
28672
+ message: "Create password:",
28673
+ validate: (v2) => {
28674
+ if (!v2)
28675
+ return "Password is required";
28676
+ if (v2.length < 6)
28677
+ return "Password must be at least 6 characters";
28678
+ }
28679
+ });
28680
+ if (pD(password))
28681
+ return;
28682
+ const s = Y2();
28683
+ s.start("Setting password...");
28684
+ try {
28685
+ await setPassword(state.accessToken, password);
28686
+ s.stop(import_picocolors16.default.green("\u2713 Password set! You can now log in with email + password."));
28687
+ } catch (err) {
28688
+ s.stop(import_picocolors16.default.red("\u2717 Failed to set password"));
28689
+ console.error(import_picocolors16.default.red(err.message || "Unknown error"));
28690
+ }
28691
+ }
28692
+ async function completeMagicLinkManual(email3) {
28693
+ const token = await he({
28694
+ message: "Enter the magic link token:",
28695
+ validate: (v2) => {
28696
+ if (!v2)
28697
+ return "Token is required";
28698
+ }
28699
+ });
28700
+ if (pD(token))
28701
+ return;
28702
+ const s = Y2();
28703
+ s.start("Verifying...");
28704
+ try {
28705
+ const result = await verifyMagicLink(email3, token);
28706
+ const userChanged = await persistAuth(result);
28707
+ const state = await loadAuthState();
28708
+ if (state && !state.encryptionKey) {
28709
+ s.stop(import_picocolors16.default.green("\u2713 Logged in!"));
28710
+ await setupEncryptionAfterAuth(state);
28711
+ } else {
28712
+ s.stop(import_picocolors16.default.green("\u2713 Logged in!"));
28713
+ }
28714
+ await promptSetPassword(state, !!result.hasPassword);
28715
+ if (userChanged)
28716
+ await promptSyncChoice(state);
28717
+ } catch (err) {
28718
+ s.stop(import_picocolors16.default.red("\u2717 Invalid or expired token"));
28719
+ console.error(import_picocolors16.default.red(err.message || "Unknown error"));
28720
+ process.exit(1);
28721
+ }
28722
+ }
28723
+ async function cmdMagicLinkLogin() {
28724
+ Ie(import_picocolors16.default.bold(import_picocolors16.default.cyan("Magic Link Login")));
28725
+ const email3 = await he({
28726
+ message: "Email:",
28727
+ validate: (v2) => {
28728
+ if (!v2)
28729
+ return "Email is required";
28730
+ if (!v2.includes("@"))
28731
+ return "Invalid email format";
28732
+ }
28733
+ });
28734
+ if (pD(email3))
28735
+ return;
28736
+ const s = Y2();
28737
+ s.start("Sending magic link...");
28738
+ let sessionId;
28739
+ try {
28740
+ const result = await sendMagicLink(email3);
28741
+ sessionId = result.sessionId;
28742
+ s.stop(import_picocolors16.default.green("\u2713 Magic link sent!"));
28743
+ } catch (err) {
28744
+ s.stop(import_picocolors16.default.red("\u2717 Failed to send magic link"));
28745
+ console.error(import_picocolors16.default.red(err.message || "Unknown error"));
28746
+ process.exit(1);
28747
+ }
28748
+ if (!sessionId) {
28749
+ await completeMagicLinkManual(email3);
28750
+ return;
28751
+ }
28752
+ console.log();
28753
+ console.log(import_picocolors16.default.dim("Check your email and click the magic link to log in."));
28754
+ console.log(import_picocolors16.default.dim("The CLI will detect it automatically. Press Enter to enter the token manually."));
28755
+ console.log();
28756
+ let shouldStop = false;
28757
+ let isManual = false;
28758
+ let authResult = null;
28759
+ let hasPassword = false;
28760
+ readline.emitKeypressEvents(process.stdin);
28761
+ const wasRaw = process.stdin.isTTY;
28762
+ if (wasRaw) {
28763
+ process.stdin.setRawMode(true);
28764
+ }
28765
+ const onKeypress = (_str, key) => {
28766
+ if (key.name === "return" || key.name === "enter") {
28767
+ shouldStop = true;
28768
+ isManual = true;
28769
+ }
28770
+ if (key.ctrl && key.name === "c") {
28771
+ cleanupStdin();
28772
+ process.exit(0);
28773
+ }
28774
+ };
28775
+ const cleanupStdin = () => {
28776
+ process.stdin.removeListener("keypress", onKeypress);
28777
+ if (wasRaw) {
28778
+ process.stdin.setRawMode(false);
28779
+ }
28780
+ };
28781
+ process.stdin.on("keypress", onKeypress);
28782
+ s.start("Waiting for browser confirmation...");
28783
+ try {
28784
+ const maxAttempts = MAGIC_LINK_MAX_ATTEMPTS;
28785
+ for (let i2 = 0;i2 < maxAttempts && !shouldStop; i2++) {
28786
+ await new Promise((resolve) => setTimeout(resolve, MAGIC_LINK_POLL_INTERVAL_MS));
28787
+ try {
28788
+ const session = await checkMagicLinkSession(sessionId);
28789
+ if (session.status === "completed" && session.accessToken) {
28790
+ if (!session.refreshToken || !session.expiresAt || !session.user || !session.encryptionSalt) {
28791
+ s.stop(import_picocolors16.default.red("\u2717 Incomplete session data from server"));
28792
+ process.exit(1);
28793
+ }
28794
+ authResult = {
28795
+ accessToken: session.accessToken,
28796
+ refreshToken: session.refreshToken,
28797
+ expiresAt: session.expiresAt,
28798
+ user: session.user,
28799
+ encryptionSalt: session.encryptionSalt
28800
+ };
28801
+ hasPassword = !!session.hasPassword;
28802
+ shouldStop = true;
28803
+ }
28804
+ } catch (err) {
28805
+ if (err.status === 404) {
28806
+ s.stop(import_picocolors16.default.red("\u2717 Session expired. Please try again."));
28807
+ process.exit(1);
28808
+ }
28809
+ }
28810
+ }
28811
+ } finally {
28812
+ s.stop();
28813
+ cleanupStdin();
28814
+ }
28815
+ if (authResult) {
28816
+ const userChanged = await persistAuth(authResult);
28817
+ const state = await loadAuthState();
28818
+ if (state && !state.encryptionKey) {
28819
+ await setupEncryptionAfterAuth(state);
28820
+ }
28821
+ await promptSetPassword(state, hasPassword);
28822
+ if (userChanged)
28823
+ await promptSyncChoice(state);
28824
+ Se(`Welcome back, ${import_picocolors16.default.cyan(authResult.user.displayName || authResult.user.email)}!`);
28825
+ return;
28826
+ }
28827
+ if (isManual) {
28828
+ console.log();
28829
+ await completeMagicLinkManual(email3);
28830
+ return;
28831
+ }
28832
+ console.log(import_picocolors16.default.red("\u2717 Timed out waiting for magic link confirmation."));
28833
+ console.log(import_picocolors16.default.dim("The magic link may have expired. Please try again."));
28834
+ process.exit(1);
28835
+ }
28836
+ async function handleAuthCommand(args) {
28837
+ const subcommand = args[0];
28838
+ switch (subcommand) {
28839
+ case "register":
28840
+ await cmdRegister();
28841
+ break;
28842
+ case "login": {
28843
+ if (args.includes("--magic-link")) {
28844
+ await cmdMagicLinkLogin();
28845
+ } else {
28846
+ await cmdLogin();
28847
+ }
28848
+ break;
28849
+ }
28850
+ case "logout":
28851
+ await cmdLogout();
28852
+ break;
28853
+ case "status":
28854
+ await cmdStatus2();
28855
+ break;
28856
+ case "delete-account":
28857
+ await cmdDeleteAccount();
28858
+ break;
28859
+ default:
28860
+ console.log(import_picocolors16.default.red(`Unknown auth subcommand: ${subcommand}`));
28861
+ console.log();
28862
+ console.log(import_picocolors16.default.bold("Available subcommands:"));
28863
+ console.log(` ${import_picocolors16.default.cyan("register")} - Create a new cloud account`);
28864
+ console.log(` ${import_picocolors16.default.cyan("login")} - Sign in to your account`);
28865
+ console.log(` ${import_picocolors16.default.cyan("login --magic-link")} - Sign in with a magic link`);
28866
+ console.log(` ${import_picocolors16.default.cyan("logout")} - Sign out`);
28867
+ console.log(` ${import_picocolors16.default.cyan("status")} - Check login status`);
28868
+ console.log(` ${import_picocolors16.default.cyan("delete-account")} - Delete your cloud account`);
28869
+ console.log();
28870
+ process.exit(1);
28871
+ }
28872
+ }
28873
+
27787
28874
  // src/cli/index.ts
28875
+ init_sync();
27788
28876
  init_meta();
27789
28877
  init_versions2();
27790
28878
  async function cmdProviders(subcommand, args) {
@@ -27831,104 +28919,104 @@ async function cmdProviders(subcommand, args) {
27831
28919
  await showProvider(providerId);
27832
28920
  return;
27833
28921
  }
27834
- console.log(import_picocolors17.default.red(`Unknown providers subcommand: ${subcommand}`));
28922
+ console.log(import_picocolors19.default.red(`Unknown providers subcommand: ${subcommand}`));
27835
28923
  console.log();
27836
- console.log(import_picocolors17.default.bold("Available subcommands:"));
27837
- console.log(` ${import_picocolors17.default.cyan("list, ls")} - List all providers`);
27838
- console.log(` ${import_picocolors17.default.cyan("add, new")} - Add a custom provider`);
27839
- console.log(` ${import_picocolors17.default.cyan("remove, rm, delete")} - Remove a custom provider`);
27840
- console.log(` ${import_picocolors17.default.cyan("show, info")} - Show provider details`);
28924
+ console.log(import_picocolors19.default.bold("Available subcommands:"));
28925
+ console.log(` ${import_picocolors19.default.cyan("list, ls")} - List all providers`);
28926
+ console.log(` ${import_picocolors19.default.cyan("add, new")} - Add a custom provider`);
28927
+ console.log(` ${import_picocolors19.default.cyan("remove, rm, delete")} - Remove a custom provider`);
28928
+ console.log(` ${import_picocolors19.default.cyan("show, info")} - Show provider details`);
27841
28929
  console.log();
27842
28930
  process.exit(1);
27843
28931
  }
27844
28932
  async function cmdExport(filePath) {
27845
28933
  if (!filePath) {
27846
- console.log(import_picocolors17.default.red("Error: Please specify export file path"));
27847
- console.log(import_picocolors17.default.dim("Usage: swixter export <file>"));
28934
+ console.log(import_picocolors19.default.red("Error: Please specify export file path"));
28935
+ console.log(import_picocolors19.default.dim("Usage: swixter export <file>"));
27848
28936
  process.exit(1);
27849
28937
  }
27850
28938
  try {
27851
28939
  await exportConfig(filePath, { sanitizeKeys: false });
27852
28940
  console.log();
27853
- console.log(import_picocolors17.default.green("\u2713") + " Export successful!");
27854
- console.log(` File: ${import_picocolors17.default.cyan(filePath)}`);
28941
+ console.log(import_picocolors19.default.green("\u2713") + " Export successful!");
28942
+ console.log(` File: ${import_picocolors19.default.cyan(filePath)}`);
27855
28943
  console.log();
27856
28944
  } catch (error46) {
27857
28945
  console.log();
27858
- console.log(import_picocolors17.default.red(`\u2717 Export failed: ${error46}`));
28946
+ console.log(import_picocolors19.default.red(`\u2717 Export failed: ${error46}`));
27859
28947
  console.log();
27860
28948
  process.exit(1);
27861
28949
  }
27862
28950
  }
27863
28951
  async function cmdCompletion(shell) {
27864
28952
  if (!shell) {
27865
- console.log(import_picocolors17.default.yellow("Please specify shell type"));
28953
+ console.log(import_picocolors19.default.yellow("Please specify shell type"));
27866
28954
  console.log();
27867
- console.log(import_picocolors17.default.bold("Usage:"));
27868
- console.log(` ${import_picocolors17.default.green("swixter completion <shell>")}`);
28955
+ console.log(import_picocolors19.default.bold("Usage:"));
28956
+ console.log(` ${import_picocolors19.default.green("swixter completion <shell>")}`);
27869
28957
  console.log();
27870
- console.log(import_picocolors17.default.bold("Supported shells:"));
27871
- console.log(` ${import_picocolors17.default.cyan("bash")} - Bash shell`);
27872
- console.log(` ${import_picocolors17.default.cyan("zsh")} - Z shell`);
27873
- console.log(` ${import_picocolors17.default.cyan("fish")} - Fish shell`);
28958
+ console.log(import_picocolors19.default.bold("Supported shells:"));
28959
+ console.log(` ${import_picocolors19.default.cyan("bash")} - Bash shell`);
28960
+ console.log(` ${import_picocolors19.default.cyan("zsh")} - Z shell`);
28961
+ console.log(` ${import_picocolors19.default.cyan("fish")} - Fish shell`);
27874
28962
  console.log();
27875
- console.log(import_picocolors17.default.bold("Examples:"));
27876
- console.log(` ${import_picocolors17.default.green("swixter completion bash > ~/.local/share/bash-completion/completions/swixter")}`);
27877
- console.log(` ${import_picocolors17.default.green("swixter completion zsh > ~/.zfunc/_swixter")}`);
27878
- console.log(` ${import_picocolors17.default.green("swixter completion fish > ~/.config/fish/completions/swixter.fish")}`);
28963
+ console.log(import_picocolors19.default.bold("Examples:"));
28964
+ console.log(` ${import_picocolors19.default.green("swixter completion bash > ~/.local/share/bash-completion/completions/swixter")}`);
28965
+ console.log(` ${import_picocolors19.default.green("swixter completion zsh > ~/.zfunc/_swixter")}`);
28966
+ console.log(` ${import_picocolors19.default.green("swixter completion fish > ~/.config/fish/completions/swixter.fish")}`);
27879
28967
  process.exit(1);
27880
28968
  }
27881
28969
  const supportedShells = ["bash", "zsh", "fish"];
27882
28970
  if (!supportedShells.includes(shell)) {
27883
- console.log(import_picocolors17.default.red(`Unsupported shell: ${shell}`));
27884
- console.log(import_picocolors17.default.dim(`Supported shells: ${supportedShells.join(", ")}`));
28971
+ console.log(import_picocolors19.default.red(`Unsupported shell: ${shell}`));
28972
+ console.log(import_picocolors19.default.dim(`Supported shells: ${supportedShells.join(", ")}`));
27885
28973
  process.exit(1);
27886
28974
  }
27887
28975
  try {
27888
28976
  const script = generateCompletion(shell);
27889
28977
  console.log(script);
27890
28978
  } catch (error46) {
27891
- console.error(import_picocolors17.default.red("Failed to generate completion script:"), error46);
28979
+ console.error(import_picocolors19.default.red("Failed to generate completion script:"), error46);
27892
28980
  process.exit(1);
27893
28981
  }
27894
28982
  }
27895
28983
  async function cmdImport(filePath) {
27896
28984
  if (!filePath) {
27897
- console.log(import_picocolors17.default.red("Error: Please specify import file path"));
27898
- console.log(import_picocolors17.default.dim("Usage: swixter import <file>"));
28985
+ console.log(import_picocolors19.default.red("Error: Please specify import file path"));
28986
+ console.log(import_picocolors19.default.dim("Usage: swixter import <file>"));
27899
28987
  process.exit(1);
27900
28988
  }
27901
28989
  try {
27902
28990
  const result = await importConfig(filePath, { overwrite: false });
27903
28991
  console.log();
27904
- console.log(import_picocolors17.default.green("\u2713") + " Import completed!");
28992
+ console.log(import_picocolors19.default.green("\u2713") + " Import completed!");
27905
28993
  console.log();
27906
- console.log(` Successfully imported: ${import_picocolors17.default.green(result.imported)} items`);
27907
- console.log(` Skipped: ${import_picocolors17.default.yellow(result.skipped)} items`);
27908
- console.log(` Errors: ${import_picocolors17.default.red(result.errors.length)} items`);
28994
+ console.log(` Successfully imported: ${import_picocolors19.default.green(result.imported)} items`);
28995
+ console.log(` Skipped: ${import_picocolors19.default.yellow(result.skipped)} items`);
28996
+ console.log(` Errors: ${import_picocolors19.default.red(result.errors.length)} items`);
27909
28997
  console.log();
27910
28998
  if (result.errors.length > 0) {
27911
- console.log(import_picocolors17.default.red("Error details:"));
27912
- result.errors.forEach((err) => console.log(import_picocolors17.default.red(` - ${err}`)));
28999
+ console.log(import_picocolors19.default.red("Error details:"));
29000
+ result.errors.forEach((err) => console.log(import_picocolors19.default.red(` - ${err}`)));
27913
29001
  console.log();
27914
29002
  }
27915
29003
  } catch (error46) {
27916
29004
  console.log();
27917
- console.log(import_picocolors17.default.red(`\u2717 Import failed: ${error46}`));
29005
+ console.log(import_picocolors19.default.red(`\u2717 Import failed: ${error46}`));
27918
29006
  console.log();
27919
29007
  process.exit(1);
27920
29008
  }
27921
29009
  }
27922
29010
  async function cmdVersion() {
27923
29011
  console.log();
27924
- console.log(import_picocolors17.default.bold(import_picocolors17.default.cyan("Swixter")) + import_picocolors17.default.dim(" - AI Coder Configuration Manager"));
29012
+ console.log(import_picocolors19.default.bold(import_picocolors19.default.cyan("Swixter")) + import_picocolors19.default.dim(" - AI Coder Configuration Manager"));
27925
29013
  console.log();
27926
- console.log(` ${import_picocolors17.default.bold("Version:")} ${import_picocolors17.default.green(APP_VERSION)}`);
27927
- console.log(` ${import_picocolors17.default.bold("Config Version:")} ${import_picocolors17.default.cyan(CONFIG_VERSION)}`);
27928
- console.log(` ${import_picocolors17.default.bold("Export Version:")} ${import_picocolors17.default.cyan(EXPORT_VERSION)}`);
29014
+ console.log(` ${import_picocolors19.default.bold("Version:")} ${import_picocolors19.default.green(APP_VERSION)}`);
29015
+ console.log(` ${import_picocolors19.default.bold("Config Version:")} ${import_picocolors19.default.cyan(CONFIG_VERSION)}`);
29016
+ console.log(` ${import_picocolors19.default.bold("Export Version:")} ${import_picocolors19.default.cyan(EXPORT_VERSION)}`);
27929
29017
  console.log();
27930
- console.log(import_picocolors17.default.dim(" GitHub: https://github.com/dawnswwwww/swixter"));
27931
- console.log(import_picocolors17.default.dim(" NPM: https://www.npmjs.com/package/swixter"));
29018
+ console.log(import_picocolors19.default.dim(" GitHub: https://github.com/dawnswwwww/swixter"));
29019
+ console.log(import_picocolors19.default.dim(" NPM: https://www.npmjs.com/package/swixter"));
27932
29020
  console.log();
27933
29021
  }
27934
29022
  async function main() {
@@ -27965,6 +29053,14 @@ async function main() {
27965
29053
  await handleCodexCommand(args.slice(1));
27966
29054
  return;
27967
29055
  }
29056
+ if (firstArg === "auth") {
29057
+ await handleAuthCommand(args.slice(1));
29058
+ return;
29059
+ }
29060
+ if (firstArg === "sync") {
29061
+ await handleSyncCommand(args.slice(1));
29062
+ return;
29063
+ }
27968
29064
  if (firstArg === "providers") {
27969
29065
  await cmdProviders(args[1], args.slice(2));
27970
29066
  return;
@@ -27985,11 +29081,11 @@ async function main() {
27985
29081
  await cmdVersion();
27986
29082
  return;
27987
29083
  }
27988
- console.log(import_picocolors17.default.red(`Unknown command: ${firstArg}`));
27989
- console.log(import_picocolors17.default.dim("Run 'swixter --help' for help"));
29084
+ console.log(import_picocolors19.default.red(`Unknown command: ${firstArg}`));
29085
+ console.log(import_picocolors19.default.dim("Run 'swixter --help' for help"));
27990
29086
  process.exit(1);
27991
29087
  } catch (error46) {
27992
- console.error(import_picocolors17.default.red("An error occurred:"), error46);
29088
+ console.error(import_picocolors19.default.red("An error occurred:"), error46);
27993
29089
  process.exit(1);
27994
29090
  }
27995
29091
  }