@prestyj/boss 4.3.164 → 4.10.1

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.js CHANGED
@@ -3,6 +3,7 @@ import { createRequire as __createRequire } from "node:module"; const require =
3
3
  import {
4
4
  AnimationProvider,
5
5
  AssistantMessage,
6
+ AuthStorage,
6
7
  Box_default,
7
8
  ChatControls,
8
9
  ChatInputStack,
@@ -10,11 +11,12 @@ import {
10
11
  ChatLivePane,
11
12
  CompactionDone,
12
13
  CompactionSpinner,
13
- GGBoss,
14
+ EzBoss,
14
15
  InputArea,
15
16
  MODELS,
16
17
  MessageResponse,
17
18
  RESPONSE_LEFT_PADDING,
19
+ TelegramBot,
18
20
  TerminalSizeProvider,
19
21
  Text,
20
22
  ThemeContext,
@@ -28,17 +30,21 @@ import {
28
30
  buildToolGroupSummary,
29
31
  closeLogger,
30
32
  color,
33
+ createAutoUpdater,
31
34
  dim,
32
35
  formatHistoryWrite,
33
36
  getAppPaths,
34
37
  getBossState,
35
38
  getContextWindow,
39
+ getDefaultModel,
40
+ getModel,
36
41
  getNextThinkingLevel,
37
42
  getSplashAudioDurationMs,
38
43
  getTranscriptItemMarginTop,
39
44
  gradientLine,
40
45
  indent,
41
46
  initLogger,
47
+ isModelLoaded,
42
48
  loadSettings,
43
49
  loadTheme,
44
50
  log,
@@ -48,6 +54,7 @@ import {
48
54
  require_react,
49
55
  saveSettings,
50
56
  serializeCompletedItemToTerminalHistory,
57
+ setProgressCallback,
51
58
  setStreamDiagnostic,
52
59
  shouldSeparateTranscriptItemKinds,
53
60
  shouldTopSpaceStreamingAssistant,
@@ -56,6 +63,7 @@ import {
56
63
  subscribeToBossStore,
57
64
  tasksStore,
58
65
  toolTonePalette,
66
+ transcribeVoice,
59
67
  truncatePlain,
60
68
  useAnimationActive,
61
69
  useAnimationTick,
@@ -68,7 +76,7 @@ import {
68
76
  use_input_default,
69
77
  use_stdout_default,
70
78
  wrapPlain
71
- } from "./chunk-STUHLGRN.js";
79
+ } from "./chunk-72J6ZGVA.js";
72
80
  import "./chunk-7QDS2KEP.js";
73
81
  import {
74
82
  source_default
@@ -116,13 +124,13 @@ import readline from "readline";
116
124
  import os from "os";
117
125
  import path2 from "path";
118
126
  async function discoverProjects() {
119
- const [gg, cc, cx] = await Promise.all([
120
- discoverGgcoderProjects(),
127
+ const [ez, cc, cx] = await Promise.all([
128
+ discoverEzcoderProjects(),
121
129
  discoverClaudeProjects(),
122
130
  discoverCodexProjects()
123
131
  ]);
124
132
  const byPath = /* @__PURE__ */ new Map();
125
- for (const p of [...gg, ...cc, ...cx]) {
133
+ for (const p of [...ez, ...cc, ...cx]) {
126
134
  const existing = byPath.get(p.path);
127
135
  if (!existing) {
128
136
  byPath.set(p.path, p);
@@ -153,7 +161,7 @@ function mergeSources(a, b) {
153
161
  const set = /* @__PURE__ */ new Set([...a, ...b]);
154
162
  return Array.from(set).sort((x, y) => SOURCE_ORDER[x] - SOURCE_ORDER[y]);
155
163
  }
156
- async function discoverGgcoderProjects() {
164
+ async function discoverEzcoderProjects() {
157
165
  const sessionsDir = getAppPaths().sessionsDir;
158
166
  let entries;
159
167
  try {
@@ -361,7 +369,7 @@ init_esm_shims();
361
369
  // package.json
362
370
  var package_default = {
363
371
  name: "@prestyj/boss",
364
- version: "4.3.164",
372
+ version: "4.10.1",
365
373
  type: "module",
366
374
  description: "Orchestrator agent that drives multiple ezcoder sessions across projects from a single chat",
367
375
  license: "MIT",
@@ -387,6 +395,7 @@ var package_default = {
387
395
  devDependencies: {
388
396
  "@prestyj/agent": "workspace:*",
389
397
  "@prestyj/ai": "workspace:*",
398
+ "@prestyj/core": "workspace:*",
390
399
  "@prestyj/cli": "workspace:*",
391
400
  "@types/node": "^25.6.0",
392
401
  "@types/react": "^19.2.14",
@@ -517,7 +526,7 @@ var VISIBLE_ROWS = 12;
517
526
  function sourceBadge(sources) {
518
527
  if (sources.length > 1) return { label: "[mix]", color: COLORS.success };
519
528
  const only = sources[0];
520
- if (only === "ezcoder") return { label: "[gg ]", color: COLORS.accent };
529
+ if (only === "ezcoder") return { label: "[ez ]", color: COLORS.accent };
521
530
  if (only === "claude-code") return { label: "[cc ]", color: COLORS.warning };
522
531
  if (only === "codex") return { label: "[cx ]", color: COLORS.primary };
523
532
  return { label: "[?? ]", color: COLORS.textDim };
@@ -669,317 +678,6 @@ async function runLinkCommand() {
669
678
  init_esm_shims();
670
679
  import path3 from "path";
671
680
  import fs3 from "fs/promises";
672
-
673
- // src/voice-transcriber.ts
674
- init_esm_shims();
675
- var TARGET_SAMPLE_RATE = 16e3;
676
- var MODEL_ID = "Xenova/whisper-tiny.en";
677
- var transcriber = null;
678
- var loadPromise = null;
679
- var onProgress = null;
680
- function setProgressCallback(cb) {
681
- onProgress = cb;
682
- }
683
- function resample(audio, fromRate, toRate) {
684
- if (fromRate === toRate) return audio;
685
- const ratio = fromRate / toRate;
686
- const newLength = Math.round(audio.length / ratio);
687
- const result = new Float32Array(newLength);
688
- for (let i = 0; i < newLength; i++) {
689
- const srcIndex = i * ratio;
690
- const low = Math.floor(srcIndex);
691
- const high = Math.min(low + 1, audio.length - 1);
692
- const frac = srcIndex - low;
693
- result[i] = audio[low] * (1 - frac) + audio[high] * frac;
694
- }
695
- return result;
696
- }
697
- function downmixToMono(channelData) {
698
- if (channelData.length === 0) return new Float32Array();
699
- if (channelData.length === 1) return channelData[0];
700
- const samples = channelData[0].length;
701
- const out = new Float32Array(samples);
702
- const scale = 1 / channelData.length;
703
- for (let i = 0; i < samples; i++) {
704
- let mixed = 0;
705
- for (const channel of channelData) mixed += channel[i] ?? 0;
706
- out[i] = mixed * scale;
707
- }
708
- return out;
709
- }
710
- async function decodeOggOpus(buffer) {
711
- const { OggOpusDecoder } = await import("ogg-opus-decoder");
712
- const decoder = new OggOpusDecoder();
713
- await decoder.ready;
714
- try {
715
- const decoded = await decoder.decodeFile(buffer);
716
- if (!decoded.channelData?.length || !decoded.channelData[0]?.length) {
717
- throw new Error("Decoded audio is empty");
718
- }
719
- const mono = downmixToMono(decoded.channelData);
720
- return resample(mono, decoded.sampleRate, TARGET_SAMPLE_RATE);
721
- } finally {
722
- decoder.free();
723
- }
724
- }
725
- async function getTranscriber() {
726
- if (transcriber) return transcriber;
727
- if (!loadPromise) {
728
- loadPromise = (async () => {
729
- const { pipeline } = await import("@huggingface/transformers");
730
- const instance = await pipeline("automatic-speech-recognition", MODEL_ID, {
731
- dtype: "fp32",
732
- progress_callback: onProgress ?? void 0
733
- });
734
- transcriber = instance;
735
- return instance;
736
- })();
737
- }
738
- return loadPromise;
739
- }
740
- function isModelLoaded() {
741
- return transcriber !== null;
742
- }
743
- async function transcribeVoice(fileUrl) {
744
- const response = await fetch(fileUrl);
745
- if (!response.ok) throw new Error(`Failed to download voice file: ${response.status}`);
746
- const buffer = new Uint8Array(await response.arrayBuffer());
747
- const pcm = await decodeOggOpus(buffer);
748
- const asr = await getTranscriber();
749
- const result = await asr(pcm);
750
- const text = Array.isArray(result) ? result[0]?.text : result.text;
751
- return (text ?? "").trim();
752
- }
753
-
754
- // src/telegram.ts
755
- init_esm_shims();
756
- var TELEGRAM_API = "https://api.telegram.org";
757
- var MAX_MESSAGE_LENGTH = 4096;
758
- var TelegramBot = class {
759
- token;
760
- allowedUserId;
761
- offset = 0;
762
- running = false;
763
- onMessage = null;
764
- onVoiceMessage = null;
765
- onCallback = null;
766
- onBotAdded = null;
767
- onBotRemoved = null;
768
- constructor(config) {
769
- this.token = config.botToken;
770
- this.allowedUserId = config.allowedUserId;
771
- }
772
- /** Register handler for incoming text messages. */
773
- onText(handler) {
774
- this.onMessage = handler;
775
- }
776
- /** Register handler for incoming voice notes. */
777
- onVoice(handler) {
778
- this.onVoiceMessage = handler;
779
- }
780
- /** Register handler for inline keyboard button presses. */
781
- onCallbackQuery(handler) {
782
- this.onCallback = handler;
783
- }
784
- /** Register handler for when the bot is added to a group. */
785
- onAddedToGroup(handler) {
786
- this.onBotAdded = handler;
787
- }
788
- /** Register handler for when the bot is removed from a group. */
789
- onRemovedFromGroup(handler) {
790
- this.onBotRemoved = handler;
791
- }
792
- /** Start long polling. Blocks until stop() is called. */
793
- async start() {
794
- this.running = true;
795
- const me = await this.apiCall("getMe");
796
- if (!me.ok) {
797
- throw new Error(`Invalid bot token: ${JSON.stringify(me)}`);
798
- }
799
- while (this.running) {
800
- try {
801
- const updates = await this.getUpdates();
802
- for (const update of updates) {
803
- await this.handleUpdate(update);
804
- }
805
- } catch (err) {
806
- if (!this.running) break;
807
- console.error(`[telegram] Poll error: ${err instanceof Error ? err.message : err}`);
808
- await sleep(3e3);
809
- }
810
- }
811
- }
812
- /** Stop long polling. */
813
- stop() {
814
- this.running = false;
815
- }
816
- /** Send a text message to a specific chat. Converts markdown and splits long messages. */
817
- async send(chatId, text, buttons) {
818
- const converted = toTelegramMarkdown(text);
819
- const chunks = splitMessage(converted);
820
- for (let i = 0; i < chunks.length; i++) {
821
- const isLast = i === chunks.length - 1;
822
- const replyMarkup = isLast && buttons ? {
823
- inline_keyboard: buttons.map(
824
- (row) => row.map((b) => ({ text: b.text, callback_data: b.callback_data }))
825
- )
826
- } : void 0;
827
- await this.apiCall("sendMessage", {
828
- chat_id: chatId,
829
- text: chunks[i],
830
- parse_mode: "Markdown",
831
- ...replyMarkup ? { reply_markup: replyMarkup } : {}
832
- });
833
- }
834
- }
835
- /** Send a plain text message (no markdown parsing) to a specific chat. */
836
- async sendPlain(chatId, text) {
837
- const chunks = splitMessage(text);
838
- for (const chunk of chunks) {
839
- await this.apiCall("sendMessage", {
840
- chat_id: chatId,
841
- text: chunk
842
- });
843
- }
844
- }
845
- /** Send a typing indicator to a specific chat. */
846
- async sendTyping(chatId) {
847
- await this.apiCall("sendChatAction", {
848
- chat_id: chatId,
849
- action: "typing"
850
- });
851
- }
852
- /** Get a direct download URL for a Telegram file. */
853
- async getFileUrl(fileId) {
854
- const result = await this.apiCall("getFile", { file_id: fileId });
855
- if (!result.ok) throw new Error(`Failed to get file: ${JSON.stringify(result)}`);
856
- const filePath = result.result.file_path;
857
- return `${TELEGRAM_API}/file/bot${this.token}/${filePath}`;
858
- }
859
- // ── Private ───────────────────────────────────────────
860
- async getUpdates() {
861
- const result = await this.apiCall("getUpdates", {
862
- offset: this.offset,
863
- timeout: 30,
864
- allowed_updates: ["message", "callback_query", "my_chat_member"]
865
- });
866
- if (!result.ok || !Array.isArray(result.result)) return [];
867
- const updates = result.result;
868
- if (updates.length > 0) {
869
- this.offset = updates[updates.length - 1].update_id + 1;
870
- }
871
- return updates;
872
- }
873
- async handleUpdate(update) {
874
- if (update.message) {
875
- const msg = update.message;
876
- if (msg.from.id !== this.allowedUserId) {
877
- return;
878
- }
879
- if (msg.text && this.onMessage) {
880
- this.onMessage({
881
- text: msg.text,
882
- chatId: msg.chat.id,
883
- chatType: msg.chat.type,
884
- chatTitle: msg.chat.title
885
- });
886
- } else if (msg.voice && this.onVoiceMessage) {
887
- this.onVoiceMessage({
888
- fileId: msg.voice.file_id,
889
- duration: msg.voice.duration,
890
- chatId: msg.chat.id,
891
- chatType: msg.chat.type,
892
- chatTitle: msg.chat.title
893
- });
894
- }
895
- }
896
- if (update.my_chat_member) {
897
- const member = update.my_chat_member;
898
- const status = member.new_chat_member.status;
899
- if ((status === "member" || status === "administrator") && this.onBotAdded) {
900
- this.onBotAdded(member.chat.id, member.chat.title);
901
- } else if ((status === "left" || status === "kicked") && this.onBotRemoved) {
902
- this.onBotRemoved(member.chat.id);
903
- }
904
- }
905
- if (update.callback_query) {
906
- const cb = update.callback_query;
907
- if (cb.from.id !== this.allowedUserId) return;
908
- await this.apiCall("answerCallbackQuery", { callback_query_id: cb.id });
909
- if (cb.data && this.onCallback) {
910
- this.onCallback(cb.data, cb.message.chat.id);
911
- }
912
- }
913
- }
914
- async apiCall(method, body) {
915
- const url = `${TELEGRAM_API}/bot${this.token}/${method}`;
916
- const response = await fetch(url, {
917
- method: "POST",
918
- headers: { "Content-Type": "application/json" },
919
- body: body ? JSON.stringify(body) : void 0
920
- });
921
- if (!response.ok) {
922
- return { ok: false };
923
- }
924
- return response.json();
925
- }
926
- };
927
- function toTelegramMarkdown(text) {
928
- const lines = text.split("\n");
929
- const result = [];
930
- let inCodeBlock = false;
931
- for (const line of lines) {
932
- if (line.trimStart().startsWith("```")) {
933
- inCodeBlock = !inCodeBlock;
934
- result.push(line);
935
- continue;
936
- }
937
- if (inCodeBlock) {
938
- result.push(line);
939
- continue;
940
- }
941
- let transformed = line;
942
- const headingMatch = transformed.match(/^(#{1,6})\s+(.+)$/);
943
- if (headingMatch) {
944
- transformed = `*${headingMatch[2]}*`;
945
- result.push(transformed);
946
- continue;
947
- }
948
- if (/^(-{3,}|_{3,}|\*{3,})$/.test(transformed.trim())) {
949
- result.push("");
950
- continue;
951
- }
952
- transformed = transformed.replace(/\*\*(.+?)\*\*/g, "*$1*");
953
- result.push(transformed);
954
- }
955
- return result.join("\n");
956
- }
957
- function splitMessage(text) {
958
- if (text.length <= MAX_MESSAGE_LENGTH) return [text];
959
- const chunks = [];
960
- let remaining = text;
961
- while (remaining.length > 0) {
962
- if (remaining.length <= MAX_MESSAGE_LENGTH) {
963
- chunks.push(remaining);
964
- break;
965
- }
966
- let splitAt = remaining.lastIndexOf("\n", MAX_MESSAGE_LENGTH);
967
- if (splitAt === -1 || splitAt < MAX_MESSAGE_LENGTH * 0.5) {
968
- splitAt = remaining.lastIndexOf(" ", MAX_MESSAGE_LENGTH);
969
- }
970
- if (splitAt === -1 || splitAt < MAX_MESSAGE_LENGTH * 0.5) {
971
- splitAt = MAX_MESSAGE_LENGTH;
972
- }
973
- chunks.push(remaining.slice(0, splitAt));
974
- remaining = remaining.slice(splitAt).trimStart();
975
- }
976
- return chunks;
977
- }
978
- function sleep(ms) {
979
- return new Promise((r) => setTimeout(r, ms));
980
- }
981
-
982
- // src/serve-mode.ts
983
681
  function getTelegramConfigPath() {
984
682
  return path3.join(getAppPaths().agentDir, "boss", "telegram.json");
985
683
  }
@@ -1069,7 +767,7 @@ async function runBossServeMode(options) {
1069
767
  botToken: options.telegram.botToken,
1070
768
  allowedUserId: options.telegram.userId
1071
769
  });
1072
- const boss = new GGBoss({
770
+ const boss = new EzBoss({
1073
771
  bossProvider: options.bossProvider,
1074
772
  bossModel: options.bossModel,
1075
773
  bossThinkingLevel: options.bossThinkingLevel,
@@ -1641,6 +1339,8 @@ var PARTIAL_BLOCKS = [" ", "\u258F", "\u258E", "\u258D", "\u258C", "\u258B", "\u
1641
1339
  var LIGHT_SHADE = "\u2591";
1642
1340
  var BAR_WIDTH = 8;
1643
1341
  var SHORT_MODELS = {
1342
+ "claude-fable-5": "Fable",
1343
+ "claude-mythos-5": "Mythos",
1644
1344
  "claude-opus-4-8": "Opus",
1645
1345
  "claude-sonnet-4-6": "Sonnet",
1646
1346
  "claude-haiku-4-5": "Haiku",
@@ -2320,7 +2020,7 @@ function tryPlayOnWindowsHost(station) {
2320
2020
  "Add-Type -AssemblyName presentationCore;",
2321
2021
  "Add-Type -AssemblyName WindowsBase;",
2322
2022
  "$p = New-Object System.Windows.Media.MediaPlayer;",
2323
- "$p.Open([uri]$env:GGBOSS_RADIO_URL);",
2023
+ "$p.Open([uri]$env:EZBOSS_RADIO_URL);",
2324
2024
  "$p.Play();",
2325
2025
  "[System.Windows.Threading.Dispatcher]::Run();"
2326
2026
  ].join(" ");
@@ -2333,8 +2033,8 @@ function tryPlayOnWindowsHost(station) {
2333
2033
  stdio: "ignore",
2334
2034
  env: {
2335
2035
  ...process.env,
2336
- GGBOSS_RADIO_URL: station.url,
2337
- WSLENV: (process.env.WSLENV ? process.env.WSLENV + ":" : "") + "GGBOSS_RADIO_URL"
2036
+ EZBOSS_RADIO_URL: station.url,
2037
+ WSLENV: (process.env.WSLENV ? process.env.WSLENV + ":" : "") + "EZBOSS_RADIO_URL"
2338
2038
  }
2339
2039
  }
2340
2040
  );
@@ -3066,7 +2766,7 @@ function serializeBossItemToTerminalHistory(item, context) {
3066
2766
  case "update_notice":
3067
2767
  return renderUpdateNotice(item, context);
3068
2768
  default:
3069
- return serializeCompletedItemToTerminalHistory(toGGCoderCompletedItem(item), context);
2769
+ return serializeCompletedItemToTerminalHistory(toEzCoderCompletedItem(item), context);
3070
2770
  }
3071
2771
  }
3072
2772
  function renderBanner(context) {
@@ -3153,7 +2853,7 @@ function renderTaskDispatch(item, context) {
3153
2853
  }
3154
2854
  return lines.join("\n");
3155
2855
  }
3156
- function toGGCoderCompletedItem(item) {
2856
+ function toEzCoderCompletedItem(item) {
3157
2857
  switch (item.kind) {
3158
2858
  case "user":
3159
2859
  return { kind: "user", id: item.id, text: item.text };
@@ -3311,170 +3011,17 @@ function renderRoundNoticeBox(lines, context, borderColor) {
3311
3011
 
3312
3012
  // src/auto-update.ts
3313
3013
  init_esm_shims();
3314
- import { spawn as spawn2 } from "child_process";
3315
- import fs4 from "fs";
3316
3014
  import path4 from "path";
3317
3015
  import os2 from "os";
3318
- var PACKAGE_NAME = "@prestyj/boss";
3319
- var REGISTRY_URL = `https://registry.npmjs.org/${PACKAGE_NAME}/latest`;
3320
- var CHECK_INTERVAL_MS = 60 * 60 * 1e3;
3321
- var FETCH_TIMEOUT_MS = 1e4;
3322
- function getStateFilePath() {
3323
- return path4.join(os2.homedir(), ".ezcoder", "boss", "update-state.json");
3324
- }
3325
- function readState() {
3326
- try {
3327
- const raw = fs4.readFileSync(getStateFilePath(), "utf-8");
3328
- return JSON.parse(raw);
3329
- } catch {
3330
- return null;
3331
- }
3332
- }
3333
- function writeState(state) {
3334
- try {
3335
- const dir = path4.dirname(getStateFilePath());
3336
- fs4.mkdirSync(dir, { recursive: true, mode: 448 });
3337
- fs4.writeFileSync(getStateFilePath(), JSON.stringify(state));
3338
- } catch {
3339
- }
3340
- }
3341
- function compareVersions(a, b) {
3342
- const pa = a.split(".").map(Number);
3343
- const pb = b.split(".").map(Number);
3344
- for (let i = 0; i < 3; i++) {
3345
- const diff = (pa[i] ?? 0) - (pb[i] ?? 0);
3346
- if (diff !== 0) return diff;
3347
- }
3348
- return 0;
3349
- }
3350
- function detectInstallInfo() {
3351
- const scriptPath = (process.argv[1] ?? "").replace(/\\/g, "/");
3352
- if (scriptPath.includes("/_npx/")) {
3353
- return { packageManager: "unknown" /* UNKNOWN */, updateCommand: null };
3354
- }
3355
- if (scriptPath.includes("/.pnpm") || scriptPath.includes("/pnpm/global")) {
3356
- return {
3357
- packageManager: "pnpm" /* PNPM */,
3358
- updateCommand: `pnpm add -g ${PACKAGE_NAME}@latest`
3359
- };
3360
- }
3361
- if (scriptPath.includes("/.yarn/") || scriptPath.includes("/yarn/global")) {
3362
- return {
3363
- packageManager: "yarn" /* YARN */,
3364
- updateCommand: `yarn global add ${PACKAGE_NAME}@latest`
3365
- };
3366
- }
3367
- return {
3368
- packageManager: "npm" /* NPM */,
3369
- updateCommand: `npm install -g ${PACKAGE_NAME}@latest`
3370
- };
3371
- }
3372
- async function fetchLatestVersion() {
3373
- try {
3374
- const controller = new AbortController();
3375
- const timeout = setTimeout(() => controller.abort(), FETCH_TIMEOUT_MS);
3376
- const response = await fetch(REGISTRY_URL, { signal: controller.signal });
3377
- clearTimeout(timeout);
3378
- const data = await response.json();
3379
- const version = data.version?.trim();
3380
- return version && /^\d+\.\d+\.\d+/.test(version) ? version : null;
3381
- } catch {
3382
- return null;
3383
- }
3384
- }
3385
- function performUpdateInBackground(command) {
3386
- try {
3387
- const parts = command.split(" ");
3388
- const child = spawn2(parts[0], parts.slice(1), {
3389
- detached: true,
3390
- stdio: "ignore",
3391
- env: { ...process.env, npm_config_loglevel: "silent" }
3392
- });
3393
- child.unref();
3394
- } catch {
3395
- }
3396
- }
3397
- function checkAndAutoUpdate(currentVersion) {
3398
- try {
3399
- const state = readState();
3400
- let message = null;
3401
- if (state?.updatePending && state.latestVersion) {
3402
- if (compareVersions(state.latestVersion, currentVersion) > 0) {
3403
- const info = detectInstallInfo();
3404
- if (info.updateCommand) {
3405
- performUpdateInBackground(info.updateCommand);
3406
- message = `Ken just shipped ${state.latestVersion}! Installing in the background \u2014 takes effect next launch.`;
3407
- writeState({
3408
- ...state,
3409
- lastCheckedAt: Date.now(),
3410
- updatePending: false,
3411
- lastUpdateAttempt: Date.now()
3412
- });
3413
- }
3414
- } else {
3415
- writeState({ ...state, updatePending: false });
3416
- }
3417
- }
3418
- const shouldCheck = !state || Date.now() - state.lastCheckedAt > CHECK_INTERVAL_MS;
3419
- if (shouldCheck) scheduleBackgroundCheck(currentVersion);
3420
- return message;
3421
- } catch {
3422
- return null;
3423
- }
3424
- }
3425
- function getPendingUpdate(currentVersion) {
3426
- try {
3427
- const state = readState();
3428
- if (!state?.latestVersion) return null;
3429
- if (compareVersions(state.latestVersion, currentVersion) <= 0) return null;
3430
- return { latestVersion: state.latestVersion };
3431
- } catch {
3432
- return null;
3433
- }
3434
- }
3435
- function scheduleBackgroundCheck(currentVersion) {
3436
- fetchLatestVersion().then((latestVersion) => {
3437
- const newState = {
3438
- lastCheckedAt: Date.now(),
3439
- latestVersion: latestVersion ?? void 0,
3440
- updatePending: false
3441
- };
3442
- if (latestVersion && compareVersions(latestVersion, currentVersion) > 0) {
3443
- newState.updatePending = true;
3444
- }
3445
- writeState(newState);
3446
- }).catch(() => {
3447
- });
3448
- }
3449
- var periodicTimer = null;
3450
- function startPeriodicUpdateCheck(currentVersion, onUpdate) {
3451
- if (periodicTimer) return;
3452
- periodicTimer = setInterval(() => {
3453
- fetchLatestVersion().then((latestVersion) => {
3454
- if (!latestVersion) return;
3455
- if (compareVersions(latestVersion, currentVersion) <= 0) return;
3456
- const info = detectInstallInfo();
3457
- if (!info.updateCommand) return;
3458
- writeState({
3459
- lastCheckedAt: Date.now(),
3460
- latestVersion,
3461
- updatePending: true
3462
- });
3463
- onUpdate(
3464
- `Ken just pushed a fresh update \u2014 ${currentVersion} \u2192 ${latestVersion}! Restart ezboss to grab it (or run ${info.updateCommand} if you can't wait).`
3465
- );
3466
- stopPeriodicUpdateCheck();
3467
- }).catch(() => {
3468
- });
3469
- }, CHECK_INTERVAL_MS);
3470
- periodicTimer.unref();
3471
- }
3472
- function stopPeriodicUpdateCheck() {
3473
- if (periodicTimer) {
3474
- clearInterval(periodicTimer);
3475
- periodicTimer = null;
3476
- }
3477
- }
3016
+ var updater = createAutoUpdater({
3017
+ packageName: "@prestyj/boss",
3018
+ stateFilePath: () => path4.join(os2.homedir(), ".ezcoder", "boss", "update-state.json"),
3019
+ periodicMessage: ({ currentVersion, latestVersion, updateCommand }) => `Ken just pushed a fresh update \u2014 ${currentVersion} \u2192 ${latestVersion}! Restart ezboss to grab it (or run ${updateCommand} if you can't wait).`
3020
+ });
3021
+ var checkAndAutoUpdate = updater.checkAndAutoUpdate;
3022
+ var getPendingUpdate = updater.getPendingUpdate;
3023
+ var startPeriodicUpdateCheck = updater.startPeriodicUpdateCheck;
3024
+ var stopPeriodicUpdateCheck = updater.stopPeriodicUpdateCheck;
3478
3025
 
3479
3026
  // src/orchestrator-app.tsx
3480
3027
  var import_jsx_runtime15 = __toESM(require_jsx_runtime(), 1);
@@ -4098,10 +3645,12 @@ async function runServeSubcommand(argv) {
4098
3645
  process.exit(1);
4099
3646
  }
4100
3647
  const settings = await loadSettings();
4101
- const bossProvider = settings.bossProvider ?? "anthropic";
4102
- const bossModel = cliBossModel ?? settings.bossModel ?? "claude-opus-4-8";
4103
- const workerProvider = settings.workerProvider ?? "anthropic";
4104
- const workerModel = cliWorkerModel ?? settings.workerModel ?? "claude-sonnet-4-6";
3648
+ const { bossProvider, bossModel, workerProvider, workerModel } = await resolveBossAuth({
3649
+ bossProvider: settings.bossProvider ?? "anthropic",
3650
+ bossModel: cliBossModel ?? settings.bossModel ?? "claude-opus-4-8",
3651
+ workerProvider: settings.workerProvider ?? "anthropic",
3652
+ workerModel: cliWorkerModel ?? settings.workerModel ?? "claude-sonnet-4-6"
3653
+ });
4105
3654
  await runBossServeMode({
4106
3655
  bossProvider,
4107
3656
  bossModel,
@@ -4111,6 +3660,45 @@ async function runServeSubcommand(argv) {
4111
3660
  telegram: { botToken, userId }
4112
3661
  });
4113
3662
  }
3663
+ var ALL_PROVIDERS = [
3664
+ "anthropic",
3665
+ "openai",
3666
+ "xiaomi",
3667
+ "gemini",
3668
+ "glm",
3669
+ "moonshot",
3670
+ "minimax",
3671
+ "deepseek",
3672
+ "openrouter"
3673
+ ];
3674
+ function bossDefaultModel(provider) {
3675
+ return provider === "anthropic" ? "claude-opus-4-8" : getDefaultModel(provider).id;
3676
+ }
3677
+ async function resolveBossAuth(input) {
3678
+ const auth = new AuthStorage();
3679
+ await auth.load();
3680
+ const stored = await auth.listProviders();
3681
+ const loggedIn = ALL_PROVIDERS.filter((p) => stored.includes(p));
3682
+ if (loggedIn.length === 0) {
3683
+ throw new Error('Not logged in to any provider. Run "ezcoder login" to authenticate.');
3684
+ }
3685
+ const fallback = loggedIn[0];
3686
+ const resolve = (preferredProvider, preferredModel, defaultFor) => {
3687
+ const provider = loggedIn.includes(preferredProvider) ? preferredProvider : fallback;
3688
+ const modelFits = getModel(preferredModel)?.provider === provider;
3689
+ return { provider, model: modelFits ? preferredModel : defaultFor(provider) };
3690
+ };
3691
+ const boss = resolve(input.bossProvider, input.bossModel, bossDefaultModel);
3692
+ const worker = resolve(input.workerProvider, input.workerModel, (p) => getDefaultModel(p).id);
3693
+ const fellBack = boss.provider !== input.bossProvider || worker.provider !== input.workerProvider;
3694
+ return {
3695
+ bossProvider: boss.provider,
3696
+ bossModel: boss.model,
3697
+ workerProvider: worker.provider,
3698
+ workerModel: worker.model,
3699
+ fellBack
3700
+ };
3701
+ }
4114
3702
  async function runOrchestrator(args) {
4115
3703
  if (args.projects.length === 0) {
4116
3704
  const links = await loadLinks();
@@ -4127,10 +3715,30 @@ async function runOrchestrator(args) {
4127
3715
  caption: `Spinning up ${args.projects.length} worker${args.projects.length === 1 ? "" : "s"}\u2026`
4128
3716
  });
4129
3717
  const settings = await loadSettings();
4130
- const finalBossProvider = args.bossProvider ?? settings.bossProvider ?? "anthropic";
4131
- const finalBossModel = args.bossModel ?? settings.bossModel ?? "claude-opus-4-8";
4132
- const finalWorkerProvider = args.workerProvider ?? settings.workerProvider ?? "anthropic";
4133
- const finalWorkerModel = args.workerModel ?? settings.workerModel ?? "claude-sonnet-4-6";
3718
+ const preferredBossProvider = args.bossProvider ?? settings.bossProvider ?? "anthropic";
3719
+ const preferredBossModel = args.bossModel ?? settings.bossModel ?? "claude-opus-4-8";
3720
+ const preferredWorkerProvider = args.workerProvider ?? settings.workerProvider ?? "anthropic";
3721
+ const preferredWorkerModel = args.workerModel ?? settings.workerModel ?? "claude-sonnet-4-6";
3722
+ const {
3723
+ bossProvider: finalBossProvider,
3724
+ bossModel: finalBossModel,
3725
+ workerProvider: finalWorkerProvider,
3726
+ workerModel: finalWorkerModel,
3727
+ fellBack
3728
+ } = await resolveBossAuth({
3729
+ bossProvider: preferredBossProvider,
3730
+ bossModel: preferredBossModel,
3731
+ workerProvider: preferredWorkerProvider,
3732
+ workerModel: preferredWorkerModel
3733
+ });
3734
+ if (fellBack) {
3735
+ log("INFO", "cli", "provider fallback", {
3736
+ preferredBoss: preferredBossProvider,
3737
+ boss: finalBossProvider,
3738
+ preferredWorker: preferredWorkerProvider,
3739
+ worker: finalWorkerProvider
3740
+ });
3741
+ }
4134
3742
  initLogger({
4135
3743
  version: VERSION,
4136
3744
  bossProvider: finalBossProvider,
@@ -4148,7 +3756,7 @@ async function runOrchestrator(args) {
4148
3756
  });
4149
3757
  const updateMessage = checkAndAutoUpdate(VERSION);
4150
3758
  if (updateMessage) log("INFO", "auto_update", updateMessage);
4151
- const boss = new GGBoss({
3759
+ const boss = new EzBoss({
4152
3760
  bossProvider: finalBossProvider,
4153
3761
  bossModel: finalBossModel,
4154
3762
  bossThinkingLevel: settings.bossThinkingLevel,
@@ -4202,7 +3810,7 @@ process.on("unhandledRejection", (reason) => {
4202
3810
  main().catch((err) => {
4203
3811
  const message = err instanceof Error ? err.message : String(err);
4204
3812
  process.stderr.write(source_default.hex(COLORS.error)(`
4205
- gg-boss failed: ${message}
3813
+ ez-boss failed: ${message}
4206
3814
  `));
4207
3815
  process.exit(1);
4208
3816
  });