cli-jaw 2.2.10 → 2.2.12

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 (124) hide show
  1. package/README.ja.md +15 -0
  2. package/README.ko.md +15 -0
  3. package/README.md +15 -0
  4. package/dist/bin/cli-jaw.js +5 -1
  5. package/dist/bin/cli-jaw.js.map +1 -1
  6. package/dist/bin/commands/doctor.js +22 -2
  7. package/dist/bin/commands/doctor.js.map +1 -1
  8. package/dist/bin/commands/init.js +7 -0
  9. package/dist/bin/commands/init.js.map +1 -1
  10. package/dist/bin/commands/slack.js +232 -0
  11. package/dist/bin/commands/slack.js.map +1 -0
  12. package/dist/bin/commands/tui/slash-command-runner.js +11 -2
  13. package/dist/bin/commands/tui/slash-command-runner.js.map +1 -1
  14. package/dist/server.js +18 -2
  15. package/dist/server.js.map +1 -1
  16. package/dist/src/cli/commands.js +68 -15
  17. package/dist/src/cli/commands.js.map +1 -1
  18. package/dist/src/cli/opencodex-models.js +67 -14
  19. package/dist/src/cli/opencodex-models.js.map +1 -1
  20. package/dist/src/cli/registry-live.js +55 -2
  21. package/dist/src/cli/registry-live.js.map +1 -1
  22. package/dist/src/cli/registry.js +8 -2
  23. package/dist/src/cli/registry.js.map +1 -1
  24. package/dist/src/core/config.js +26 -1
  25. package/dist/src/core/config.js.map +1 -1
  26. package/dist/src/messaging/channel-health.js +6 -0
  27. package/dist/src/messaging/channel-health.js.map +1 -1
  28. package/dist/src/messaging/channel-validate.js +85 -0
  29. package/dist/src/messaging/channel-validate.js.map +1 -0
  30. package/dist/src/routes/messaging.js +9 -0
  31. package/dist/src/routes/messaging.js.map +1 -1
  32. package/dist/src/routes/system.js +5 -0
  33. package/dist/src/routes/system.js.map +1 -1
  34. package/dist/src/slack/api.js +32 -3
  35. package/dist/src/slack/api.js.map +1 -1
  36. package/dist/src/slack/bot.js +37 -1
  37. package/dist/src/slack/bot.js.map +1 -1
  38. package/dist/src/slack/events.js +23 -0
  39. package/dist/src/slack/events.js.map +1 -1
  40. package/dist/src/slack/hot-notify.js +33 -0
  41. package/dist/src/slack/hot-notify.js.map +1 -0
  42. package/dist/src/slack/manifest.js +85 -0
  43. package/dist/src/slack/manifest.js.map +1 -0
  44. package/dist/src/slack/progress.js +98 -0
  45. package/dist/src/slack/progress.js.map +1 -0
  46. package/dist/src/slack/send-only-client.js +3 -2
  47. package/dist/src/slack/send-only-client.js.map +1 -1
  48. package/dist/src/slack/slack-file.js +4 -2
  49. package/dist/src/slack/slack-file.js.map +1 -1
  50. package/package.json +3 -1
  51. package/public/css/sidebar.css +200 -0
  52. package/public/dist/assets/Agent-BjbZVZ9p.js +1 -0
  53. package/public/dist/assets/{Heartbeat-vDGhPfct.js → Heartbeat-DDmZARWw.js} +1 -1
  54. package/public/dist/assets/{Memory-B_UZ-iCA.js → Memory-CUbviW7N.js} +1 -1
  55. package/public/dist/assets/ModelProvider-ngg8xTuB.js +1 -0
  56. package/public/dist/assets/{agent-meta-C8DqYXM6.js → agent-meta-Y3DE6sxh.js} +1 -1
  57. package/public/dist/assets/app-BzD_ZLvt.js +55 -0
  58. package/public/dist/assets/{app-Nu2VPaO5.css → app-DX4e6isX.css} +1 -1
  59. package/public/dist/assets/bgtask-badge-Dqbp-Y4e.js +1 -0
  60. package/public/dist/assets/{bgtask-badge-DXca8E7c.js → bgtask-badge-qMkKOZx0.js} +1 -1
  61. package/public/dist/assets/{constants-CSZ011-j.js → constants-Df7-7Rky.js} +1 -1
  62. package/public/dist/assets/{employees-BL4qMxJj.js → employees-BnobWM_2.js} +1 -1
  63. package/public/dist/assets/{iframe-renderer-DUI0LYBy.js → iframe-renderer-C5vRbbl9.js} +2 -2
  64. package/public/dist/assets/iframe-renderer-Df4KIVyx.js +1 -0
  65. package/public/dist/assets/{manager-B-gpf-3j.js → manager-DL1GygQL.js} +1 -1
  66. package/public/dist/assets/memory-D-bULTHl.js +1 -0
  67. package/public/dist/assets/{memory-B_Y0L0KD.js → memory-DF4kPwwC.js} +1 -1
  68. package/public/dist/assets/{message-history-Dl8dJzez.js → message-history-C4WOisGx.js} +1 -1
  69. package/public/dist/assets/message-history-ZBoza6ew.js +1 -0
  70. package/public/dist/assets/{render-BI1aqKoc.js → render-DYX6GRQ0.js} +1 -1
  71. package/public/dist/assets/{settings-B0Sj4sUH.js → settings-DbJpGoaX.js} +1 -1
  72. package/public/dist/assets/settings-SreihUje.js +1 -0
  73. package/public/dist/assets/settings-core-B1Y5Nb4Z.js +1 -0
  74. package/public/dist/assets/settings-core-DobZWIZ7.js +140 -0
  75. package/public/dist/assets/{sidebar-DOR9fTgN.js → sidebar-Cp22g0Gv.js} +3 -3
  76. package/public/dist/assets/skills-DwPIP08e.js +1 -0
  77. package/public/dist/assets/{skills-wt0fkV9Y.js → skills-IGquen_j.js} +1 -1
  78. package/public/dist/assets/slash-commands-BBVfwcnW.js +1 -0
  79. package/public/dist/assets/{slash-commands-BZbqTg3v.js → slash-commands-ZRDOL99E.js} +1 -1
  80. package/public/dist/assets/{trace-drawer-kMYzaVFW.js → trace-drawer-D8To8fYt.js} +1 -1
  81. package/public/dist/assets/ui-CAicpUZc.js +1 -0
  82. package/public/dist/assets/{ui-D4D5HUfo.js → ui-Hx_mNoDf.js} +1 -1
  83. package/public/dist/index.html +90 -47
  84. package/public/dist/manager/index.html +1 -1
  85. package/public/index.html +88 -45
  86. package/public/js/constants.ts +50 -0
  87. package/public/js/features/channel-onboarding-flow.ts +197 -0
  88. package/public/js/features/channel-onboarding.ts +322 -0
  89. package/public/js/features/channel-setup-guide.ts +26 -0
  90. package/public/js/features/channel-setup-rules.ts +32 -0
  91. package/public/js/features/notifications.ts +38 -0
  92. package/public/js/features/settings-channel.ts +4 -0
  93. package/public/js/features/settings-core.ts +73 -9
  94. package/public/js/features/settings-discord.ts +2 -0
  95. package/public/js/features/settings-slack.ts +60 -2
  96. package/public/js/features/settings-telegram.ts +2 -0
  97. package/public/js/features/settings-types.ts +1 -1
  98. package/public/js/features/settings.ts +1 -1
  99. package/public/js/features/transport-status-row.ts +7 -3
  100. package/public/js/main.ts +5 -1
  101. package/public/locales/en.json +65 -3
  102. package/public/locales/ja.json +76 -3
  103. package/public/locales/ko.json +65 -3
  104. package/public/locales/zh.json +76 -3
  105. package/public/manager/src/settings/pages/Agent.tsx +25 -4
  106. package/public/manager/src/settings/pages/components/PerCliRow.tsx +13 -5
  107. package/public/manager/src/settings/pages/components/agent/agent-meta.ts +98 -0
  108. package/scripts/bundle-sidecar.sh +13 -4
  109. package/scripts/check-sidecar-prune-safety.mjs +167 -0
  110. package/scripts/pick-gyp-python.sh +61 -0
  111. package/scripts/release-gates.mjs +14 -0
  112. package/public/dist/assets/Agent-BgkP4EnS.js +0 -1
  113. package/public/dist/assets/ModelProvider-CgUvs6pE.js +0 -1
  114. package/public/dist/assets/app-BGIAy4he.js +0 -55
  115. package/public/dist/assets/bgtask-badge-GRSiNILQ.js +0 -1
  116. package/public/dist/assets/iframe-renderer-BUSk0Rw9.js +0 -1
  117. package/public/dist/assets/memory-CX6YahTc.js +0 -1
  118. package/public/dist/assets/message-history-Det7u76S.js +0 -1
  119. package/public/dist/assets/settings-C7NutP6X.js +0 -1
  120. package/public/dist/assets/settings-core-CyX-px6_.js +0 -100
  121. package/public/dist/assets/settings-core-DVpFmrhY.js +0 -1
  122. package/public/dist/assets/skills-BWhKreQ2.js +0 -1
  123. package/public/dist/assets/slash-commands-Di4NDA4w.js +0 -1
  124. package/public/dist/assets/ui-BghexWrB.js +0 -1
@@ -1,22 +1,78 @@
1
1
  // ── Slack Settings ──
2
- import { apiJson } from '../api.js';
2
+ import { api, apiJson } from '../api.js';
3
+ import { openSetupGuideIfUnconfigured } from './channel-setup-guide.js';
4
+ import { hasSlackBotTokenPrefix, hasSlackAppTokenPrefix } from './channel-setup-rules.js';
5
+ import { t } from './i18n.js';
3
6
  import type { SettingsData } from './settings-types.js';
4
7
 
8
+ // ── Guided setup card ──
9
+ // One-time bindings for the Slack setup card in settings: manifest copy,
10
+ // app-page shortcut, and inline token-prefix validation (error below the
11
+ // field, cleared once the value looks right — form-patterns on-blur timing).
12
+ let setupGuideBound = false;
13
+
14
+ export function initSlackSetupGuide(): void {
15
+ if (setupGuideBound) return;
16
+ setupGuideBound = true;
17
+
18
+ document.getElementById('slack-copy-manifest')?.addEventListener('click', async (ev) => {
19
+ const btn = ev.currentTarget as HTMLButtonElement;
20
+ try {
21
+ const json = await api<{ yaml?: string }>('/api/slack/manifest');
22
+ const yaml = json?.yaml || '';
23
+ if (!yaml) throw new Error('empty manifest');
24
+ await navigator.clipboard.writeText(yaml);
25
+ const original = btn.textContent || '';
26
+ btn.textContent = t('settings.slack.guide.copyManifestDone');
27
+ setTimeout(() => { btn.textContent = original; }, 2000);
28
+ } catch {
29
+ btn.textContent = t('settings.slack.guide.copyManifestFail');
30
+ setTimeout(() => { btn.textContent = t('settings.slack.guide.copyManifest'); }, 2500);
31
+ }
32
+ });
33
+
34
+ document.getElementById('slack-open-apps')?.addEventListener('click', () => {
35
+ window.open('https://api.slack.com/apps?new_app=1', '_blank', 'noopener');
36
+ });
37
+
38
+ bindPrefixValidation('slBotToken', 'slack-bot-token-error', hasSlackBotTokenPrefix);
39
+ bindPrefixValidation('slAppToken', 'slack-app-token-error', hasSlackAppTokenPrefix);
40
+ }
41
+
42
+ function bindPrefixValidation(inputId: string, errorId: string, valid: (v: string) => boolean): void {
43
+ const input = document.getElementById(inputId) as HTMLInputElement | null;
44
+ const error = document.getElementById(errorId);
45
+ if (!input || !error) return;
46
+ const check = (): void => {
47
+ const bad = input.value.trim().length > 0 && !valid(input.value);
48
+ error.style.display = bad ? '' : 'none';
49
+ input.classList.toggle('input-error', bad);
50
+ };
51
+ // On blur only until the first error appears; then live, so correcting
52
+ // the paste clears the error immediately.
53
+ input.addEventListener('blur', check);
54
+ input.addEventListener('input', () => {
55
+ if (error.style.display !== 'none') check();
56
+ });
57
+ }
58
+
5
59
  export async function saveSlackSettings(): Promise<void> {
6
60
  const botToken = (document.getElementById('slBotToken') as HTMLInputElement)?.value.trim() || '';
7
61
  const appToken = (document.getElementById('slAppToken') as HTMLInputElement)?.value.trim() || '';
8
62
  const teamId = (document.getElementById('slTeamId') as HTMLInputElement)?.value.trim() || '';
9
63
  const channelIdsRaw = (document.getElementById('slChannelIds') as HTMLInputElement)?.value.trim() || '';
64
+ const attachPort = (document.getElementById('slAttachPort') as HTMLInputElement)?.value.trim() || '';
10
65
  const channelIds = channelIdsRaw
11
66
  ? channelIdsRaw.split(',').map(s => s.trim()).filter(Boolean)
12
67
  : [];
13
- await apiJson('/api/settings', 'PUT', { slack: { botToken, appToken, teamId, channelIds } });
68
+ await apiJson('/api/settings', 'PUT', { slack: { botToken, appToken, teamId, channelIds, attachPort } });
14
69
  }
15
70
 
16
71
  export async function setSlack(enabled: boolean): Promise<void> {
17
72
  document.getElementById('slOn')?.classList.toggle('active', enabled);
18
73
  document.getElementById('slOff')?.classList.toggle('active', !enabled);
19
74
  await apiJson('/api/settings', 'PUT', { slack: { enabled } });
75
+ if (enabled) openSetupGuideIfUnconfigured('slack');
20
76
  }
21
77
 
22
78
  export async function setSlackForwardAll(enabled: boolean): Promise<void> {
@@ -56,6 +112,8 @@ export function loadSlackSettings(s: SettingsData): void {
56
112
  if (sc.teamId && teamId) teamId.value = sc.teamId;
57
113
  const channelIds = document.getElementById('slChannelIds') as HTMLInputElement | null;
58
114
  if (sc.channelIds?.length && channelIds) channelIds.value = sc.channelIds.join(', ');
115
+ const attachPort = document.getElementById('slAttachPort') as HTMLInputElement | null;
116
+ if (attachPort) attachPort.value = sc.attachPort || '';
59
117
  const fwdOn = sc.forwardAll !== false;
60
118
  document.getElementById('slForwardOn')?.classList.toggle('active', fwdOn);
61
119
  document.getElementById('slForwardOff')?.classList.toggle('active', !fwdOn);
@@ -1,5 +1,6 @@
1
1
  // ── Telegram Settings ──
2
2
  import { apiJson } from '../api.js';
3
+ import { openSetupGuideIfUnconfigured } from './channel-setup-guide.js';
3
4
  import type { SettingsData } from './settings-types.js';
4
5
 
5
6
  export async function saveTelegramSettings(): Promise<void> {
@@ -15,6 +16,7 @@ export async function setTelegram(enabled: boolean): Promise<void> {
15
16
  document.getElementById('tgOn')?.classList.toggle('active', enabled);
16
17
  document.getElementById('tgOff')?.classList.toggle('active', !enabled);
17
18
  await apiJson('/api/settings', 'PUT', { telegram: { enabled } });
19
+ if (enabled) openSetupGuideIfUnconfigured('telegram');
18
20
  }
19
21
 
20
22
  export async function setForwardAll(enabled: boolean): Promise<void> {
@@ -5,7 +5,7 @@ export interface TelegramConfig { enabled?: boolean; token?: string; allowedChat
5
5
  export interface DiscordConfig { enabled?: boolean; token?: string; guildId?: string; channelIds?: string[]; forwardAll?: boolean; allowBots?: boolean; mentionOnly?: boolean; }
6
6
  // Slack needs TWO distinctly-scoped tokens: the bot token drives the Web API,
7
7
  // the app-level token opens the Socket Mode connection for inbound events.
8
- export interface SlackConfig { enabled?: boolean; botToken?: string; appToken?: string; teamId?: string; channelIds?: string[]; forwardAll?: boolean; allowBots?: boolean; mentionOnly?: boolean; replyInThread?: boolean; }
8
+ export interface SlackConfig { enabled?: boolean; botToken?: string; appToken?: string; teamId?: string; channelIds?: string[]; forwardAll?: boolean; allowBots?: boolean; mentionOnly?: boolean; replyInThread?: boolean; attachPort?: string; }
9
9
  export interface QuotaWindow {
10
10
  label: string;
11
11
  percent: number;
@@ -2,7 +2,7 @@
2
2
  export { loadSettings, updateSettings, setPerm, getModelValue, handleModelSelect, applyCustomModel, onPerCliProviderChange, onCliChange, saveActiveCliSettings, savePerCli, onFlushCliChange, loadFlushAgentSidebar } from './settings-core.js';
3
3
  export { setTelegram, setForwardAll, setTelegramMentionOnly, saveTelegramSettings } from './settings-telegram.js';
4
4
  export { setDiscord, setDiscordForwardAll, setDiscordAllowBots, setDiscordMentionOnly, saveDiscordSettings } from './settings-discord.js';
5
- export { setSlack, setSlackForwardAll, setSlackAllowBots, setSlackMentionOnly, setSlackReplyInThread, saveSlackSettings } from './settings-slack.js';
5
+ export { setSlack, setSlackForwardAll, setSlackAllowBots, setSlackMentionOnly, setSlackReplyInThread, saveSlackSettings, initSlackSetupGuide } from './settings-slack.js';
6
6
  export { setActiveChannel, loadFallbackOrder, saveFallbackOrder } from './settings-channel.js';
7
7
  export { loadMcpServers, syncMcpServers, installMcpGlobal, openMcpModal, initMcpModal } from './settings-mcp.js';
8
8
  export { loadCliStatus, scheduleCliStatusRefresh, setCliStatusInterval } from './settings-cli-status.js';
@@ -64,9 +64,13 @@ function renderTransportBlock(
64
64
  const chips = transportChipLabels(status)
65
65
  .map(text => `<span class="transport-status-chip">${escapeHtml(text)}</span>`)
66
66
  .join('');
67
- const hint = showHint && status.configured && status.sendCapable && !status.activeInbound
68
- ? `<p class="transport-status-hint">${escapeHtml(t('settings.channel.sendOnlyHint'))}</p>`
69
- : '';
67
+ // not_attach_instance: tokens exist but another instance owns the socket —
68
+ // say so, or the guarded instance just reads as silently broken.
69
+ const hint = status.reason === 'not_attach_instance'
70
+ ? `<p class="transport-status-hint">${escapeHtml(t('settings.channel.notAttachInstanceHint'))}</p>`
71
+ : showHint && status.configured && status.sendCapable && !status.activeInbound
72
+ ? `<p class="transport-status-hint">${escapeHtml(t('settings.channel.sendOnlyHint'))}</p>`
73
+ : '';
70
74
  return `
71
75
  <div class="transport-status-block">
72
76
  <div class="transport-status-label">${escapeHtml(label)}</div>
package/public/js/main.ts CHANGED
@@ -56,10 +56,11 @@ import {
56
56
  initCliStatusToggle, initCliStatusPreviewHooks, isCliStatusExpanded, expandCliStatus, isEmbeddedPreviewFrame,
57
57
  setTelegram, setForwardAll, setTelegramMentionOnly, saveTelegramSettings,
58
58
  setDiscord, setDiscordForwardAll, setDiscordAllowBots, setDiscordMentionOnly, saveDiscordSettings, setActiveChannel,
59
- setSlack, setSlackForwardAll, setSlackAllowBots, setSlackMentionOnly, setSlackReplyInThread, saveSlackSettings,
59
+ setSlack, setSlackForwardAll, setSlackAllowBots, setSlackMentionOnly, setSlackReplyInThread, saveSlackSettings, initSlackSetupGuide,
60
60
  saveFallbackOrder,
61
61
  openTemplateModal, saveTemplateFromModal, closeTemplateModal, templateGoBack, toggleDevMode
62
62
  } from './features/settings.js';
63
+ import { initChannelOnboarding } from './features/channel-onboarding.js';
63
64
  const {
64
65
  loadEmployees, addEmployee, deleteEmployee, updateEmployee,
65
66
  onEmpCliChange, onEmpRoleChange
@@ -300,6 +301,9 @@ document.getElementById('slBotToken')?.addEventListener('change', saveSlackSetti
300
301
  document.getElementById('slAppToken')?.addEventListener('change', saveSlackSettings);
301
302
  document.getElementById('slTeamId')?.addEventListener('change', saveSlackSettings);
302
303
  document.getElementById('slChannelIds')?.addEventListener('change', saveSlackSettings);
304
+ document.getElementById('slAttachPort')?.addEventListener('change', saveSlackSettings);
305
+ initChannelOnboarding();
306
+ initSlackSetupGuide();
303
307
 
304
308
  document.getElementById('fallbackOrderList')?.addEventListener('change', saveFallbackOrder);
305
309
 
@@ -516,7 +516,7 @@
516
516
  "help.slack.link.portal": "Open the Slack app dashboard",
517
517
  "help.slack.link.docs": "Socket Mode documentation",
518
518
  "help.slack.example.1": "@cli-jaw run the tests for this repo",
519
- "help.slack.howTo.1": "Create an app at api.slack.com/apps (a manifest is easiest).",
519
+ "help.slack.howTo.1": "Click \"Copy manifest\" in the guide card below, then create the app at api.slack.com/apps Create an App → From an app manifest.",
520
520
  "help.slack.howTo.2": "Enable Socket Mode and generate an app-level token (xapp-) with connections:write.",
521
521
  "help.slack.howTo.3": "Install to the workspace to obtain the bot token (xoxb-).",
522
522
  "help.slack.howTo.4": "Paste each token into its own field below — do not swap them.",
@@ -823,5 +823,67 @@
823
823
  "help.keyboardShortcuts.subcmd.2": "/help <command> — Show detailed help for a specific command",
824
824
  "help.keyboardShortcuts.subcmd.3": "/commands — Open command palette (alias: /cmd)",
825
825
  "bgtask.title": "Background",
826
- "tg.elicitationExpired": "Selection expired. Please reply with text."
827
- }
826
+ "tg.elicitationExpired": "Selection expired. Please reply with text.",
827
+ "settings.slack.guide.step1Title": "Create the Slack app",
828
+ "settings.slack.guide.step1Desc": "Create an App → From an app manifest → pick workspace → paste",
829
+ "settings.slack.guide.copyManifest": "Copy manifest",
830
+ "settings.slack.guide.copyManifestDone": "Copied — paste it into Slack",
831
+ "settings.slack.guide.copyManifestFail": "Copy failed — use jaw slack manifest",
832
+ "settings.slack.guide.openApps": "Open apps page",
833
+ "settings.slack.guide.step2Title": "Paste the bot token",
834
+ "settings.slack.guide.step3Title": "App-level token for inbound (optional)",
835
+ "settings.slack.guide.step3Desc": "Leave empty for outbound-only operation",
836
+ "settings.slack.guide.botTokenError": "Must be a bot token starting with xoxb- — swapped with the app token?",
837
+ "settings.slack.guide.appTokenError": "Must be an app-level token starting with xapp- — swapped with the bot token?",
838
+ "settings.slack.advanced": "Advanced settings",
839
+ "settings.slack.attachPort": "Attach instance port",
840
+ "settings.channel.notAttachInstanceHint": "Another instance owns this Slack connection (check the attach instance port)",
841
+ "settings.slack.guide.step2Desc": "App settings → OAuth & Permissions → Install to Workspace → copy Bot User OAuth Token",
842
+ "settings.slack.guide.step3Location": "App settings → Basic Information → App-Level Tokens → Generate Token (connections:write)",
843
+ "slack.progress.start": "Gathering context...",
844
+ "slack.progress.working": "Working",
845
+ "onboarding.open": "Setup wizard",
846
+ "onboarding.optional": "optional",
847
+ "onboarding.validate": "Validate",
848
+ "onboarding.save": "Save",
849
+ "onboarding.close": "Close",
850
+ "onboarding.valid": "Verified: {identity}",
851
+ "onboarding.title.telegram": "Connect Telegram",
852
+ "onboarding.title.discord": "Connect Discord",
853
+ "onboarding.title.slack": "Connect Slack",
854
+ "onboarding.guide.telegram": "Open @BotFather in Telegram, send /newbot, pick a name and username, and copy the bot token it returns. Paste it below and press Validate.",
855
+ "onboarding.guide.discord": "At discord.com/developers/applications choose New Application, then Bot, then Reset Token. Enable MESSAGE CONTENT INTENT under Privileged Gateway Intents. Get the server ID by enabling Developer Mode in Discord, then right-click the server and copy its ID.",
856
+ "onboarding.guide.slack": "At api.slack.com/apps use Create an App, From an app manifest. Then get the bot token (xoxb-) from OAuth & Permissions, Install to Workspace, and the app-level token (xapp-, connections:write) from Basic Information, App-Level Tokens.",
857
+ "onboarding.token.botToken": "Bot token",
858
+ "onboarding.token.appToken": "App-level token",
859
+ "onboarding.token.guildId": "Server ID",
860
+ "onboarding.next.telegram": "Saved. Send any message to your bot in Telegram and the chat ID is stored automatically.",
861
+ "onboarding.next.discord": "Saved. Invite the bot to your server and confirm MESSAGE CONTENT INTENT is enabled.",
862
+ "onboarding.next.slack": "Saved. Run /invite @cli-jaw in each channel the bot should read.",
863
+ "onboarding.error.token_required": "Enter a token.",
864
+ "onboarding.error.invalid_token": "That token was rejected. Check it and try again.",
865
+ "onboarding.error.invalid_app_token": "The app-level token was rejected (check the connections:write scope).",
866
+ "onboarding.error.guild_required": "Enter the server ID.",
867
+ "onboarding.error.bot_prefix": "The bot token must start with xoxb-.",
868
+ "onboarding.error.app_prefix": "The app-level token must start with xapp-.",
869
+ "onboarding.error.unknown_channel": "Unsupported channel.",
870
+ "onboarding.error.network": "Validation failed because of a network error.",
871
+ "onboarding.next": "Next",
872
+ "onboarding.back": "Back",
873
+ "onboarding.step.1": "1. Where to get it",
874
+ "onboarding.step.2": "2. Paste what you received",
875
+ "onboarding.step.3": "3. Verify the connection",
876
+ "onboarding.step.4": "4. Save",
877
+ "onboarding.validating": "Checking...",
878
+ "onboarding.validateHint": "Press Validate to check the real connection. It must pass before saving.",
879
+ "onboarding.saveHint": "Validation passed. Saving applies the settings and connects the channel right away.",
880
+ "onboarding.error.validation_required": "Validation has to pass first.",
881
+ "onboarding.openIssuer": "Open the issuing page",
882
+ "onboarding.error.missing_scopes": "The token works, but the app is missing OAuth scopes. Add the ones below under OAuth & Permissions, then reinstall the app.",
883
+ "onboarding.hint.telegram.botToken": "The value @BotFather returned after /newbot.",
884
+ "onboarding.hint.discord.botToken": "Generated with Reset Token on the Bot tab.",
885
+ "onboarding.hint.discord.guildId": "Enable Developer Mode, right-click the server, copy ID.",
886
+ "onboarding.hint.slack.botToken": "Bot User OAuth Token from OAuth & Permissions (starts with xoxb-).",
887
+ "onboarding.hint.slack.appToken": "Basic Information, App-Level Tokens (xapp-, connections:write).",
888
+ "onboarding.progressLabel": "Step {step} of {total}"
889
+ }
@@ -505,7 +505,7 @@
505
505
  "help.slack.link.portal": "Open the Slack app dashboard",
506
506
  "help.slack.link.docs": "Socket Mode documentation",
507
507
  "help.slack.example.1": "@cli-jaw run the tests for this repo",
508
- "help.slack.howTo.1": "Create an app at api.slack.com/apps (a manifest is easiest).",
508
+ "help.slack.howTo.1": "下のガイドカードの「マニフェストをコピー」を押し、api.slack.com/apps Create an App → From an app manifest に貼り付けてアプリを作成します。",
509
509
  "help.slack.howTo.2": "Enable Socket Mode and generate an app-level token (xapp-) with connections:write.",
510
510
  "help.slack.howTo.3": "Install to the workspace to obtain the bot token (xoxb-).",
511
511
  "help.slack.howTo.4": "Paste each token into its own field below — do not swap them.",
@@ -812,5 +812,78 @@
812
812
  "help.keyboardShortcuts.subcmd.2": "/help <command> — 特定コマンドの詳細ヘルプを表示します",
813
813
  "help.keyboardShortcuts.subcmd.3": "/commands — コマンドパレットを開きます(エイリアス: /cmd)",
814
814
  "bgtask.title": "バックグラウンド",
815
- "tg.elicitationExpired": "選択セッションの有効期限が切れました。テキストで返信してください。"
816
- }
815
+ "tg.elicitationExpired": "選択セッションの有効期限が切れました。テキストで返信してください。",
816
+ "settings.slack.guide.step1Title": "Slack アプリを作成",
817
+ "settings.slack.guide.step1Desc": "Create an App → From an app manifest → ワークスペース選択 → 貼り付け",
818
+ "settings.slack.guide.copyManifest": "マニフェストをコピー",
819
+ "settings.slack.guide.copyManifestDone": "コピーしました — Slack に貼り付けてください",
820
+ "settings.slack.guide.copyManifestFail": "コピー失敗 — jaw slack manifest を使用",
821
+ "settings.slack.guide.openApps": "アプリページを開く",
822
+ "settings.slack.guide.step2Title": "ボットトークンを貼り付け",
823
+ "settings.slack.guide.step3Title": "受信用アプリトークン(任意)",
824
+ "settings.slack.guide.step3Desc": "空のままなら送信専用(outbound-only)で動作します",
825
+ "settings.slack.guide.botTokenError": "xoxb- で始まるボットトークンが必要です — アプリトークンと入れ替わっていませんか?",
826
+ "settings.slack.guide.appTokenError": "xapp- で始まるアプリレベルトークンが必要です — ボットトークンと入れ替わっていませんか?",
827
+ "settings.slack.advanced": "詳細設定",
828
+ "settings.slack.attachPort": "接続インスタンスのポート",
829
+ "settings.channel.notAttachInstanceHint": "別のインスタンスがこの Slack 接続を所有しています(接続インスタンスのポートを確認)",
830
+ "settings.slack.guide.step2Desc": "アプリ設定 → OAuth & Permissions → Install to Workspace → Bot User OAuth Token をコピー",
831
+ "settings.slack.guide.step3Location": "アプリ設定 → Basic Information → App-Level Tokens → Generate Token (connections:write)",
832
+ "slack.progress.start": "情報を収集中...",
833
+ "slack.progress.working": "作業中",
834
+ "onboarding.open": "セットアップウィザード",
835
+ "onboarding.optional": "任意",
836
+ "onboarding.validate": "検証",
837
+ "onboarding.save": "保存",
838
+ "onboarding.close": "閉じる",
839
+ "onboarding.valid": "確認済み: {identity}",
840
+ "onboarding.title.telegram": "Telegram に接続",
841
+ "onboarding.title.discord": "Discord に接続",
842
+ "onboarding.title.slack": "Slack に接続",
843
+ "onboarding.guide.telegram": "Open @BotFather in Telegram, send /newbot, pick a name and username, and copy the bot token it returns. Paste it below and press Validate.",
844
+ "onboarding.guide.discord": "At discord.com/developers/applications choose New Application, then Bot, then Reset Token. Enable MESSAGE CONTENT INTENT under Privileged Gateway Intents. Get the server ID by enabling Developer Mode in Discord, then right-click the server and copy its ID.",
845
+ "onboarding.guide.slack": "At api.slack.com/apps use Create an App, From an app manifest. Then get the bot token (xoxb-) from OAuth & Permissions, Install to Workspace, and the app-level token (xapp-, connections:write) from Basic Information, App-Level Tokens.",
846
+ "onboarding.token.botToken": "ボットトークン",
847
+ "onboarding.token.appToken": "アプリレベルトークン",
848
+ "onboarding.token.guildId": "サーバー ID",
849
+ "onboarding.next.telegram": "Saved. Send any message to your bot in Telegram and the chat ID is stored automatically.",
850
+ "onboarding.next.discord": "Saved. Invite the bot to your server and confirm MESSAGE CONTENT INTENT is enabled.",
851
+ "onboarding.next.slack": "Saved. Run /invite @cli-jaw in each channel the bot should read.",
852
+ "onboarding.error.token_required": "Enter a token.",
853
+ "onboarding.error.invalid_token": "That token was rejected. Check it and try again.",
854
+ "onboarding.error.invalid_app_token": "The app-level token was rejected (check the connections:write scope).",
855
+ "onboarding.error.guild_required": "Enter the server ID.",
856
+ "onboarding.error.bot_prefix": "The bot token must start with xoxb-.",
857
+ "onboarding.error.app_prefix": "The app-level token must start with xapp-.",
858
+ "onboarding.error.unknown_channel": "Unsupported channel.",
859
+ "onboarding.error.network": "Validation failed because of a network error.",
860
+ "onboarding.next": "次へ",
861
+ "onboarding.back": "戻る",
862
+ "onboarding.step.1": "1. 取得場所の確認",
863
+ "onboarding.step.2": "2. 取得した値を貼り付け",
864
+ "onboarding.step.3": "3. 接続の検証",
865
+ "onboarding.step.4": "4. 保存",
866
+ "onboarding.validating": "確認中...",
867
+ "onboarding.validateHint": "「検証」を押して実際の接続を確認してください。通過しないと保存に進めません。",
868
+ "onboarding.saveHint": "検証に成功しました。保存すると設定に反映され、チャンネルがすぐ接続されます。",
869
+ "onboarding.error.validation_required": "先に検証を通す必要があります。",
870
+ "onboarding.openIssuer": "発行ページを開く",
871
+ "onboarding.error.missing_scopes": "トークンは有効ですが、アプリの権限(スコープ)が不足しています。OAuth & Permissions で以下を追加し、アプリを再インストールしてください。",
872
+ "onboarding.hint.telegram.botToken": "@BotFather が /newbot の後に返した値です。",
873
+ "onboarding.hint.discord.botToken": "Bot タブの Reset Token で生成した値です。",
874
+ "onboarding.hint.discord.guildId": "開発者モードを有効にし、サーバーを右クリックして ID をコピー。",
875
+ "onboarding.hint.slack.botToken": "OAuth & Permissions の Bot User OAuth Token(xoxb- で始まる)。",
876
+ "onboarding.hint.slack.appToken": "Basic Information → App-Level Tokens(xapp-, connections:write)。",
877
+ "cmd.memory.embed.estimate": "埋め込み推定",
878
+ "cmd.memory.embed.status": "埋め込み状態",
879
+ "settings.channel.activeInbound": "受信チャンネル",
880
+ "settings.channel.configured": "設定済み",
881
+ "settings.channel.notConfigured": "未設定",
882
+ "settings.channel.sendCapable": "送信可能",
883
+ "settings.channel.sendOnlyHint": "受信チャンネルではありませんが、設定済みなら送信は可能です。",
884
+ "settings.channel.statusTitle": "送信状態",
885
+ "settings.channel.testSend": "テスト送信",
886
+ "settings.channel.testSendFail": "送信失敗: {error}",
887
+ "settings.channel.testSendOk": "送信成功",
888
+ "onboarding.progressLabel": "{total} ステップ中 {step} ステップ目"
889
+ }
@@ -516,7 +516,7 @@
516
516
  "help.slack.link.portal": "Slack 앱 관리 페이지 열기",
517
517
  "help.slack.link.docs": "Socket Mode 문서",
518
518
  "help.slack.example.1": "@cli-jaw 이 저장소 테스트 돌려줘",
519
- "help.slack.howTo.1": "api.slack.com/apps 에서 앱을 만듭니다 (매니페스트 사용 권장).",
519
+ "help.slack.howTo.1": "아래 가이드 카드의 \"매니페스트 복사\"를 눌러 api.slack.com/apps Create an App From an app manifest에 붙여넣어 앱을 만듭니다.",
520
520
  "help.slack.howTo.2": "Socket Mode를 켜고 connections:write 범위의 app-level 토큰(xapp-)을 발급합니다.",
521
521
  "help.slack.howTo.3": "워크스페이스에 설치해 봇 토큰(xoxb-)을 받습니다.",
522
522
  "help.slack.howTo.4": "두 토큰을 아래 입력란에 각각 붙여넣습니다 — 서로 바꿔 넣지 않도록 주의하세요.",
@@ -823,5 +823,67 @@
823
823
  "help.keyboardShortcuts.subcmd.2": "/help <command> — 특정 명령어의 상세 도움말을 표시합니다",
824
824
  "help.keyboardShortcuts.subcmd.3": "/commands — 명령 팔레트를 엽니다 (별칭: /cmd)",
825
825
  "bgtask.title": "백그라운드",
826
- "tg.elicitationExpired": "선택 세션이 만료되었습니다. 텍스트로 답해주세요."
827
- }
826
+ "tg.elicitationExpired": "선택 세션이 만료되었습니다. 텍스트로 답해주세요.",
827
+ "settings.slack.guide.step1Title": "Slack 앱 만들기",
828
+ "settings.slack.guide.step1Desc": "Create an App → From an app manifest → 워크스페이스 선택 → 붙여넣기",
829
+ "settings.slack.guide.copyManifest": "매니페스트 복사",
830
+ "settings.slack.guide.copyManifestDone": "복사됨 — Slack에 붙여넣으세요",
831
+ "settings.slack.guide.copyManifestFail": "복사 실패 — jaw slack manifest 사용",
832
+ "settings.slack.guide.openApps": "앱 페이지 열기",
833
+ "settings.slack.guide.step2Title": "봇 토큰 붙여넣기",
834
+ "settings.slack.guide.step3Title": "수신용 앱 토큰 (선택)",
835
+ "settings.slack.guide.step3Desc": "비워두면 전송 전용(outbound-only)으로 동작합니다",
836
+ "settings.slack.guide.botTokenError": "xoxb-로 시작하는 봇 토큰이어야 합니다 — 앱 토큰과 바꿔 넣지 않았나요?",
837
+ "settings.slack.guide.appTokenError": "xapp-로 시작하는 앱 레벨 토큰이어야 합니다 — 봇 토큰과 바꿔 넣지 않았나요?",
838
+ "settings.slack.advanced": "고급 설정",
839
+ "settings.slack.attachPort": "연결 인스턴스 포트",
840
+ "settings.channel.notAttachInstanceHint": "다른 인스턴스가 이 Slack 연결을 소유하고 있습니다 (연결 인스턴스 포트 확인)",
841
+ "settings.slack.guide.step2Desc": "앱 설정 → OAuth & Permissions → Install to Workspace → Bot User OAuth Token 복사",
842
+ "settings.slack.guide.step3Location": "앱 설정 → Basic Information → App-Level Tokens → Generate Token (connections:write)",
843
+ "slack.progress.start": "정보 수집 중...",
844
+ "slack.progress.working": "작업 중",
845
+ "onboarding.open": "설정 마법사",
846
+ "onboarding.optional": "선택",
847
+ "onboarding.validate": "검증",
848
+ "onboarding.save": "저장",
849
+ "onboarding.close": "닫기",
850
+ "onboarding.valid": "확인됨: {identity}",
851
+ "onboarding.title.telegram": "Telegram 연결",
852
+ "onboarding.title.discord": "Discord 연결",
853
+ "onboarding.title.slack": "Slack 연결",
854
+ "onboarding.guide.telegram": "텔레그램에서 @BotFather 를 열고 /newbot 을 보낸 뒤, 이름과 username을 정하면 봇 토큰이 나옵니다. 그 토큰을 아래에 붙여넣고 검증을 누르세요.",
855
+ "onboarding.guide.discord": "discord.com/developers/applications 에서 New Application → Bot → Reset Token 으로 토큰을 만들고, Privileged Gateway Intents 의 MESSAGE CONTENT INTENT 를 켜세요. 서버 ID는 Discord 설정에서 개발자 모드를 켠 뒤 서버 우클릭 → ID 복사로 얻습니다.",
856
+ "onboarding.guide.slack": "api.slack.com/apps 에서 Create an App → From an app manifest 로 앱을 만든 뒤, OAuth & Permissions → Install to Workspace 에서 봇 토큰(xoxb-), Basic Information → App-Level Tokens 에서 앱 토큰(xapp-, connections:write)을 발급받으세요.",
857
+ "onboarding.token.botToken": "봇 토큰",
858
+ "onboarding.token.appToken": "앱 레벨 토큰",
859
+ "onboarding.token.guildId": "서버 ID",
860
+ "onboarding.next.telegram": "저장했습니다. 이제 텔레그램에서 봇에게 아무 메시지나 보내면 채팅 ID가 자동 저장됩니다.",
861
+ "onboarding.next.discord": "저장했습니다. 봇을 서버에 초대하고 MESSAGE CONTENT INTENT 가 켜져 있는지 확인하세요.",
862
+ "onboarding.next.slack": "저장했습니다. 봇이 읽을 채널에서 /invite @cli-jaw 로 초대하세요.",
863
+ "onboarding.error.token_required": "토큰을 입력하세요.",
864
+ "onboarding.error.invalid_token": "토큰이 유효하지 않습니다. 다시 확인해 주세요.",
865
+ "onboarding.error.invalid_app_token": "앱 레벨 토큰이 유효하지 않습니다 (connections:write 범위 확인).",
866
+ "onboarding.error.guild_required": "서버 ID를 입력하세요.",
867
+ "onboarding.error.bot_prefix": "봇 토큰은 xoxb- 로 시작해야 합니다.",
868
+ "onboarding.error.app_prefix": "앱 레벨 토큰은 xapp- 로 시작해야 합니다.",
869
+ "onboarding.error.unknown_channel": "지원하지 않는 채널입니다.",
870
+ "onboarding.error.network": "네트워크 오류로 검증하지 못했습니다.",
871
+ "onboarding.next": "다음",
872
+ "onboarding.back": "이전",
873
+ "onboarding.step.1": "1. 어디서 받는지 확인",
874
+ "onboarding.step.2": "2. 발급받은 값 붙여넣기",
875
+ "onboarding.step.3": "3. 연결 검증",
876
+ "onboarding.step.4": "4. 저장",
877
+ "onboarding.validating": "확인하는 중...",
878
+ "onboarding.validateHint": "검증을 눌러 실제 연결을 확인하세요. 통과해야 저장으로 넘어갑니다.",
879
+ "onboarding.saveHint": "검증을 통과했습니다. 저장을 누르면 설정에 반영되고 채널이 바로 연결됩니다.",
880
+ "onboarding.error.validation_required": "먼저 검증을 통과해야 합니다.",
881
+ "onboarding.openIssuer": "발급 페이지 열기",
882
+ "onboarding.error.missing_scopes": "토큰은 유효하지만 앱에 권한(스코프)이 부족합니다. OAuth & Permissions에서 아래 항목을 추가하고 앱을 재설치하세요.",
883
+ "onboarding.hint.telegram.botToken": "@BotFather가 /newbot 후 알려준 값입니다.",
884
+ "onboarding.hint.discord.botToken": "Bot 탭의 Reset Token으로 새로 만든 값입니다.",
885
+ "onboarding.hint.discord.guildId": "개발자 모드를 켠 뒤 서버를 우클릭해 ID 복사.",
886
+ "onboarding.hint.slack.botToken": "OAuth & Permissions의 Bot User OAuth Token (xoxb-로 시작).",
887
+ "onboarding.hint.slack.appToken": "Basic Information → App-Level Tokens (xapp-, connections:write).",
888
+ "onboarding.progressLabel": "{total}단계 중 {step}단계"
889
+ }
@@ -505,7 +505,7 @@
505
505
  "help.slack.link.portal": "Open the Slack app dashboard",
506
506
  "help.slack.link.docs": "Socket Mode documentation",
507
507
  "help.slack.example.1": "@cli-jaw run the tests for this repo",
508
- "help.slack.howTo.1": "Create an app at api.slack.com/apps (a manifest is easiest).",
508
+ "help.slack.howTo.1": "点击下方指南卡中的“复制清单”,然后在 api.slack.com/apps Create an App → From an app manifest 粘贴以创建应用。",
509
509
  "help.slack.howTo.2": "Enable Socket Mode and generate an app-level token (xapp-) with connections:write.",
510
510
  "help.slack.howTo.3": "Install to the workspace to obtain the bot token (xoxb-).",
511
511
  "help.slack.howTo.4": "Paste each token into its own field below — do not swap them.",
@@ -812,5 +812,78 @@
812
812
  "help.keyboardShortcuts.subcmd.2": "/help <command> — 显示特定命令的详细帮助",
813
813
  "help.keyboardShortcuts.subcmd.3": "/commands — 打开命令面板(别名: /cmd)",
814
814
  "bgtask.title": "后台任务",
815
- "tg.elicitationExpired": "选择会话已过期,请用文字回复。"
816
- }
815
+ "tg.elicitationExpired": "选择会话已过期,请用文字回复。",
816
+ "settings.slack.guide.step1Title": "创建 Slack 应用",
817
+ "settings.slack.guide.step1Desc": "Create an App → From an app manifest → 选择工作区 → 粘贴",
818
+ "settings.slack.guide.copyManifest": "复制清单",
819
+ "settings.slack.guide.copyManifestDone": "已复制 — 请粘贴到 Slack",
820
+ "settings.slack.guide.copyManifestFail": "复制失败 — 请使用 jaw slack manifest",
821
+ "settings.slack.guide.openApps": "打开应用页面",
822
+ "settings.slack.guide.step2Title": "粘贴机器人令牌",
823
+ "settings.slack.guide.step3Title": "接收用应用令牌(可选)",
824
+ "settings.slack.guide.step3Desc": "留空则仅可发送(outbound-only)",
825
+ "settings.slack.guide.botTokenError": "需要以 xoxb- 开头的机器人令牌 — 是否与应用令牌填反了?",
826
+ "settings.slack.guide.appTokenError": "需要以 xapp- 开头的应用级令牌 — 是否与机器人令牌填反了?",
827
+ "settings.slack.advanced": "高级设置",
828
+ "settings.slack.attachPort": "连接实例端口",
829
+ "settings.channel.notAttachInstanceHint": "另一个实例拥有此 Slack 连接(请检查连接实例端口)",
830
+ "settings.slack.guide.step2Desc": "应用设置 → OAuth & Permissions → Install to Workspace → 复制 Bot User OAuth Token",
831
+ "settings.slack.guide.step3Location": "应用设置 → Basic Information → App-Level Tokens → Generate Token (connections:write)",
832
+ "slack.progress.start": "正在收集信息...",
833
+ "slack.progress.working": "处理中",
834
+ "onboarding.open": "设置向导",
835
+ "onboarding.optional": "可选",
836
+ "onboarding.validate": "验证",
837
+ "onboarding.save": "保存",
838
+ "onboarding.close": "关闭",
839
+ "onboarding.valid": "已验证:{identity}",
840
+ "onboarding.title.telegram": "连接 Telegram",
841
+ "onboarding.title.discord": "连接 Discord",
842
+ "onboarding.title.slack": "连接 Slack",
843
+ "onboarding.guide.telegram": "Open @BotFather in Telegram, send /newbot, pick a name and username, and copy the bot token it returns. Paste it below and press Validate.",
844
+ "onboarding.guide.discord": "At discord.com/developers/applications choose New Application, then Bot, then Reset Token. Enable MESSAGE CONTENT INTENT under Privileged Gateway Intents. Get the server ID by enabling Developer Mode in Discord, then right-click the server and copy its ID.",
845
+ "onboarding.guide.slack": "At api.slack.com/apps use Create an App, From an app manifest. Then get the bot token (xoxb-) from OAuth & Permissions, Install to Workspace, and the app-level token (xapp-, connections:write) from Basic Information, App-Level Tokens.",
846
+ "onboarding.token.botToken": "机器人令牌",
847
+ "onboarding.token.appToken": "应用级令牌",
848
+ "onboarding.token.guildId": "服务器 ID",
849
+ "onboarding.next.telegram": "Saved. Send any message to your bot in Telegram and the chat ID is stored automatically.",
850
+ "onboarding.next.discord": "Saved. Invite the bot to your server and confirm MESSAGE CONTENT INTENT is enabled.",
851
+ "onboarding.next.slack": "Saved. Run /invite @cli-jaw in each channel the bot should read.",
852
+ "onboarding.error.token_required": "Enter a token.",
853
+ "onboarding.error.invalid_token": "That token was rejected. Check it and try again.",
854
+ "onboarding.error.invalid_app_token": "The app-level token was rejected (check the connections:write scope).",
855
+ "onboarding.error.guild_required": "Enter the server ID.",
856
+ "onboarding.error.bot_prefix": "The bot token must start with xoxb-.",
857
+ "onboarding.error.app_prefix": "The app-level token must start with xapp-.",
858
+ "onboarding.error.unknown_channel": "Unsupported channel.",
859
+ "onboarding.error.network": "Validation failed because of a network error.",
860
+ "onboarding.next": "下一步",
861
+ "onboarding.back": "上一步",
862
+ "onboarding.step.1": "1. 确认获取位置",
863
+ "onboarding.step.2": "2. 粘贴获取的值",
864
+ "onboarding.step.3": "3. 验证连接",
865
+ "onboarding.step.4": "4. 保存",
866
+ "onboarding.validating": "正在检查...",
867
+ "onboarding.validateHint": "点击验证以检查实际连接。通过后才能保存。",
868
+ "onboarding.saveHint": "验证通过。保存后将写入设置并立即连接该渠道。",
869
+ "onboarding.error.validation_required": "需要先通过验证。",
870
+ "onboarding.openIssuer": "打开签发页面",
871
+ "onboarding.error.missing_scopes": "令牌有效,但应用缺少 OAuth 权限(scope)。请在 OAuth & Permissions 中添加下列项并重新安装应用。",
872
+ "onboarding.hint.telegram.botToken": "@BotFather 在 /newbot 之后返回的值。",
873
+ "onboarding.hint.discord.botToken": "在 Bot 标签页用 Reset Token 生成的值。",
874
+ "onboarding.hint.discord.guildId": "启用开发者模式后右键服务器并复制 ID。",
875
+ "onboarding.hint.slack.botToken": "OAuth & Permissions 中的 Bot User OAuth Token(以 xoxb- 开头)。",
876
+ "onboarding.hint.slack.appToken": "Basic Information → App-Level Tokens(xapp-, connections:write)。",
877
+ "cmd.memory.embed.estimate": "嵌入估算",
878
+ "cmd.memory.embed.status": "嵌入状态",
879
+ "settings.channel.activeInbound": "接收渠道",
880
+ "settings.channel.configured": "已配置",
881
+ "settings.channel.notConfigured": "未配置",
882
+ "settings.channel.sendCapable": "可发送",
883
+ "settings.channel.sendOnlyHint": "虽不是接收渠道,但已配置时仍可发送。",
884
+ "settings.channel.statusTitle": "传输状态",
885
+ "settings.channel.testSend": "测试发送",
886
+ "settings.channel.testSendFail": "发送失败:{error}",
887
+ "settings.channel.testSendOk": "发送成功",
888
+ "onboarding.progressLabel": "第 {step} 步,共 {total} 步"
889
+ }
@@ -17,6 +17,8 @@ import {
17
17
  optionList,
18
18
  runtimeEffortFor,
19
19
  runtimeModelFor,
20
+ coerceEffortForModel,
21
+ effortChoicesForModel,
20
22
  type ActiveOverride,
21
23
  type CliMeta,
22
24
  type PerCliEntry,
@@ -193,8 +195,8 @@ export default function Agent({ port, client, dirty, registerSave }: SettingsPag
193
195
  ? optionList(activeMeta.modelsByProvider?.[activeProvider] || activeMeta.models, draft.model)
194
196
  : optionList(activeMeta.models, draft.model);
195
197
  const activeEffortOptions = hasProviders
196
- ? (activeMeta.effortsByProvider?.[activeProvider] || activeMeta.efforts)
197
- : activeMeta.efforts;
198
+ ? effortChoicesForModel(activeMeta, draft.model, activeMeta.effortsByProvider?.[activeProvider] || activeMeta.efforts, activeProvider)
199
+ : effortChoicesForModel(activeMeta, draft.model);
198
200
  const workingDirError = draft.workingDir.trim() ? null : 'Required';
199
201
 
200
202
  if (state.kind === 'loading') return <PageLoading />;
@@ -248,7 +250,9 @@ export default function Agent({ port, client, dirty, registerSave }: SettingsPag
248
250
  const models = activeMeta.modelsByProvider?.[next] || [];
249
251
  const efforts = activeMeta.effortsByProvider?.[next] || [];
250
252
  const nextModel = models.includes(draft.model) ? draft.model : (models[0] || '');
251
- const nextEffort = efforts.includes(draft.effort) ? draft.effort : '';
253
+ // Resolve against the NEW provider+model pair: the same model
254
+ // id can allow different efforts per provider.
255
+ const nextEffort = coerceEffortForModel(activeMeta, nextModel, draft.effort, efforts, next);
252
256
  setRuntimeDraft({ ...draft, provider: next, model: nextModel, effort: nextEffort });
253
257
  setEntry(`perCli.${draft.cli}.provider`, {
254
258
  value: next,
@@ -267,12 +271,29 @@ export default function Agent({ port, client, dirty, registerSave }: SettingsPag
267
271
  });
268
272
  }}
269
273
  onModelChange={(next) => {
270
- setRuntimeDraft({ ...draft, model: next });
274
+ // Efforts are per-model on a live opencodex catalog, so a
275
+ // model switch can strand an effort the new model does not
276
+ // support — and that value would reach the wire verbatim.
277
+ const nextEffort = coerceEffortForModel(
278
+ activeMeta,
279
+ next,
280
+ draft.effort,
281
+ hasProviders ? (activeMeta.effortsByProvider?.[activeProvider] || activeMeta.efforts) : undefined,
282
+ hasProviders ? activeProvider : undefined,
283
+ );
284
+ setRuntimeDraft({ ...draft, model: next, effort: nextEffort });
271
285
  setEntry(`activeOverrides.${draft.cli}.model`, {
272
286
  value: next,
273
287
  original: runtimeModelFor(draft.cli, perCli, activeOverrides),
274
288
  valid: next.trim().length > 0,
275
289
  });
290
+ if (nextEffort !== draft.effort) {
291
+ setEntry(`activeOverrides.${draft.cli}.effort`, {
292
+ value: nextEffort,
293
+ original: runtimeEffortFor(draft.cli, perCli, activeOverrides),
294
+ valid: true,
295
+ });
296
+ }
276
297
  }}
277
298
  onEffortChange={(next) => {
278
299
  setRuntimeDraft({ ...draft, effort: next });