cli-jaw 2.2.17 → 2.2.18

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 (135) hide show
  1. package/README.md +4 -0
  2. package/dist/bin/cli-jaw.js +9 -1
  3. package/dist/bin/cli-jaw.js.map +1 -1
  4. package/dist/bin/commands/ask.js +245 -0
  5. package/dist/bin/commands/ask.js.map +1 -0
  6. package/dist/bin/commands/chat.js +41 -3
  7. package/dist/bin/commands/chat.js.map +1 -1
  8. package/dist/bin/commands/dispatch.js +210 -167
  9. package/dist/bin/commands/dispatch.js.map +1 -1
  10. package/dist/bin/commands/init.js +33 -4
  11. package/dist/bin/commands/init.js.map +1 -1
  12. package/dist/bin/commands/slack.js +73 -3
  13. package/dist/bin/commands/slack.js.map +1 -1
  14. package/dist/bin/commands/tui/raw-pipe-mode.js +90 -0
  15. package/dist/bin/commands/tui/raw-pipe-mode.js.map +1 -0
  16. package/dist/bin/commands/tui/simple-mode.js +11 -2
  17. package/dist/bin/commands/tui/simple-mode.js.map +1 -1
  18. package/dist/bin/postinstall.js +54 -30
  19. package/dist/bin/postinstall.js.map +1 -1
  20. package/dist/server.js +10 -0
  21. package/dist/server.js.map +1 -1
  22. package/dist/src/agent/pi-runtime.js +10 -2
  23. package/dist/src/agent/pi-runtime.js.map +1 -1
  24. package/dist/src/agent/spawn/queue.js +21 -0
  25. package/dist/src/agent/spawn/queue.js.map +1 -1
  26. package/dist/src/agent/spawn.js +19 -6
  27. package/dist/src/agent/spawn.js.map +1 -1
  28. package/dist/src/cli/cli-status.js +65 -5
  29. package/dist/src/cli/cli-status.js.map +1 -1
  30. package/dist/src/cli/handlers.js +9 -1
  31. package/dist/src/cli/handlers.js.map +1 -1
  32. package/dist/src/cli/tui/jawcode-render.js +18 -5
  33. package/dist/src/cli/tui/jawcode-render.js.map +1 -1
  34. package/dist/src/core/config.js +85 -0
  35. package/dist/src/core/config.js.map +1 -1
  36. package/dist/src/core/exec-name.js +55 -0
  37. package/dist/src/core/exec-name.js.map +1 -0
  38. package/dist/src/core/runtime-settings.js +9 -1
  39. package/dist/src/core/runtime-settings.js.map +1 -1
  40. package/dist/src/core/settings-watch.js +10 -1
  41. package/dist/src/core/settings-watch.js.map +1 -1
  42. package/dist/src/lib/tui/bun-shim.mjs +75 -0
  43. package/dist/src/memory/bootstrap.js +8 -1
  44. package/dist/src/memory/bootstrap.js.map +1 -1
  45. package/dist/src/messaging/channel-validate.js +11 -1
  46. package/dist/src/messaging/channel-validate.js.map +1 -1
  47. package/dist/src/orchestrator/gateway.js +23 -2
  48. package/dist/src/orchestrator/gateway.js.map +1 -1
  49. package/dist/src/orchestrator/pipeline.js +6 -0
  50. package/dist/src/orchestrator/pipeline.js.map +1 -1
  51. package/dist/src/orchestrator/request-registry.js +116 -0
  52. package/dist/src/orchestrator/request-registry.js.map +1 -0
  53. package/dist/src/prompt/templates/a1-system.md +5 -5
  54. package/dist/src/routes/messaging.js +94 -1
  55. package/dist/src/routes/messaging.js.map +1 -1
  56. package/dist/src/routes/settings.js +61 -4
  57. package/dist/src/routes/settings.js.map +1 -1
  58. package/dist/src/routes/system.js +23 -3
  59. package/dist/src/routes/system.js.map +1 -1
  60. package/dist/src/slack/bot.js +36 -1
  61. package/dist/src/slack/bot.js.map +1 -1
  62. package/dist/src/slack/commands.js +29 -1
  63. package/dist/src/slack/commands.js.map +1 -1
  64. package/dist/src/slack/events.js +4 -1
  65. package/dist/src/slack/events.js.map +1 -1
  66. package/dist/src/slack/history.js +10 -3
  67. package/dist/src/slack/history.js.map +1 -1
  68. package/dist/src/slack/identity.js +553 -0
  69. package/dist/src/slack/identity.js.map +1 -0
  70. package/dist/src/slack/ingress.js +42 -0
  71. package/dist/src/slack/ingress.js.map +1 -1
  72. package/dist/src/slack/manifest.js +95 -71
  73. package/dist/src/slack/manifest.js.map +1 -1
  74. package/dist/src/slack/roster.js +180 -0
  75. package/dist/src/slack/roster.js.map +1 -0
  76. package/package.json +4 -3
  77. package/public/css/sidebar.css +29 -51
  78. package/public/dist/assets/AdvancedExport-c41zCkwB.js +1 -0
  79. package/public/dist/assets/{Agent-CNNQ7KkJ.js → Agent-nMPZsMfN.js} +1 -1
  80. package/public/dist/assets/ChannelsSlack-CVNKtoo4.js +1 -0
  81. package/public/dist/assets/{app-Dsgp4xpU.js → app-DqSJmDwP.js} +4 -4
  82. package/public/dist/assets/{app-DjgGeXhJ.css → app-DuyLYRC6.css} +1 -1
  83. package/public/dist/assets/bgtask-badge-CjWz0F6U.js +1 -0
  84. package/public/dist/assets/{bgtask-badge-CpavV0Jc.js → bgtask-badge-D6XYRFI5.js} +1 -1
  85. package/public/dist/assets/{employees-BJyEgim7.js → employees-B_wKPfTO.js} +1 -1
  86. package/public/dist/assets/{iframe-renderer-B8k3AvTv.js → iframe-renderer-D1-zDg8V.js} +2 -2
  87. package/public/dist/assets/iframe-renderer-qRVLHPnT.js +1 -0
  88. package/public/dist/assets/{manager-kXYQyMhR.js → manager-Bjuh1jpq.js} +1 -1
  89. package/public/dist/assets/{memory-D2RbBVO-.js → memory-CNUpXWEC.js} +1 -1
  90. package/public/dist/assets/memory-CcWGiPy_.js +1 -0
  91. package/public/dist/assets/message-history-BamMPUnL.js +1 -0
  92. package/public/dist/assets/{message-history-Bw2E_b8L.js → message-history-DB1rynP-.js} +1 -1
  93. package/public/dist/assets/{render-BkGcpH7z.js → render-DXwQzjsJ.js} +1 -1
  94. package/public/dist/assets/{settings-B1FPORO2.js → settings-CXndjCad.js} +14 -14
  95. package/public/dist/assets/settings-RGpifFHy.js +1 -0
  96. package/public/dist/assets/settings-core-X_5UHfr3.js +1 -0
  97. package/public/dist/assets/settings-core-t5yYVRrv.js +162 -0
  98. package/public/dist/assets/{sidebar-DW78DANx.js → sidebar-DgIj-LuT.js} +3 -3
  99. package/public/dist/assets/skills-CbwIimit.js +1 -0
  100. package/public/dist/assets/{skills-lWi0ZFiV.js → skills-QqxjqDHg.js} +1 -1
  101. package/public/dist/assets/{slash-commands-DYOI6nKj.js → slash-commands-B2V_jngP.js} +1 -1
  102. package/public/dist/assets/slash-commands-tCNRlNTC.js +1 -0
  103. package/public/dist/assets/{trace-drawer-DBX9xzmt.js → trace-drawer-CBe9nRjS.js} +1 -1
  104. package/public/dist/assets/{ui-Bx7eqnXt.js → ui-CDozpEQt.js} +1 -1
  105. package/public/dist/assets/ui-o-VpvrSv.js +1 -0
  106. package/public/dist/index.html +15 -34
  107. package/public/dist/manager/index.html +1 -1
  108. package/public/index.html +13 -32
  109. package/public/js/features/channel-onboarding-flow.ts +25 -2
  110. package/public/js/features/channel-onboarding.ts +111 -8
  111. package/public/js/features/settings-cli-status.ts +18 -4
  112. package/public/js/features/settings-slack.ts +127 -29
  113. package/public/js/features/settings-types.ts +11 -2
  114. package/public/locales/en.json +17 -8
  115. package/public/locales/ja.json +17 -8
  116. package/public/locales/ko.json +17 -8
  117. package/public/locales/zh.json +17 -8
  118. package/public/manager/src/settings/pages/AdvancedExport.tsx +1 -0
  119. package/public/manager/src/settings/pages/Agent.tsx +10 -1
  120. package/public/manager/src/settings/pages/ChannelsSlack.tsx +13 -1
  121. package/scripts/atomic-build.sh +10 -3
  122. package/scripts/release-preview.sh +16 -1
  123. package/scripts/verify-dist-assets.sh +53 -0
  124. package/public/dist/assets/AdvancedExport-DYGcCAUq.js +0 -1
  125. package/public/dist/assets/ChannelsSlack-ybvupgdK.js +0 -1
  126. package/public/dist/assets/bgtask-badge-C6h5C-o4.js +0 -1
  127. package/public/dist/assets/iframe-renderer-BO_GgCTR.js +0 -1
  128. package/public/dist/assets/memory-amsvg4Ey.js +0 -1
  129. package/public/dist/assets/message-history-BGHZxvgV.js +0 -1
  130. package/public/dist/assets/settings-CMN-zN4Q.js +0 -1
  131. package/public/dist/assets/settings-core-B_OjGYVN.js +0 -1
  132. package/public/dist/assets/settings-core-CxRwqx86.js +0 -144
  133. package/public/dist/assets/skills-Dz-9Yrxr.js +0 -1
  134. package/public/dist/assets/slash-commands-xJFCdTTM.js +0 -1
  135. package/public/dist/assets/ui-JOcrYeG-.js +0 -1
@@ -1,42 +1,113 @@
1
1
  // ── Slack Settings ──
2
- import { api, apiJson } from '../api.js';
2
+ import { API_BASE, api, apiJson, getAuthToken } from '../api.js';
3
3
  import { openSetupGuideIfUnconfigured } from './channel-setup-guide.js';
4
4
  import { hasSlackBotTokenPrefix, hasSlackAppTokenPrefix } from './channel-setup-rules.js';
5
5
  import { t } from './i18n.js';
6
+ import { refreshTransportStatusRow } from './transport-status-row.js';
6
7
  import type { SettingsData } from './settings-types.js';
7
8
 
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).
9
+ // One-time inline token-prefix validation. Errors appear below each field on
10
+ // blur and clear live once the token prefix is corrected.
12
11
  let setupGuideBound = false;
12
+ let slackResetting = false;
13
+ let slackEnvironmentVariables: string[] = [];
14
+
15
+ type SlackResetResponse = {
16
+ ok?: boolean;
17
+ error?: string;
18
+ environmentVariables?: string[];
19
+ };
20
+
21
+ async function requestSlackConnectionReset(): Promise<SlackResetResponse | null> {
22
+ try {
23
+ const token = await getAuthToken();
24
+ const response = await fetch(`${API_BASE}/api/settings/slack/reset`, {
25
+ method: 'POST',
26
+ headers: token ? { Authorization: `Bearer ${token}` } : {},
27
+ });
28
+ return await response.json().catch(() => null) as SlackResetResponse | null;
29
+ } catch {
30
+ // The write may have committed even if its response was lost. The
31
+ // authoritative GET below decides whether the reset actually landed.
32
+ return null;
33
+ }
34
+ }
35
+
36
+ function slackConnectionIsCleared(snapshot: SettingsData | null): boolean {
37
+ const slack = snapshot?.slack;
38
+ return !slack?.enabled
39
+ && !slack?.botToken
40
+ && !slack?.appToken
41
+ && !slack?.teamId
42
+ && !(slack?.channelIds?.length)
43
+ && !slack?.attachPort;
44
+ }
45
+
46
+ function slackEnvironmentMessage(): string {
47
+ return t('settings.slack.resetManagedByEnvironment', {
48
+ variables: slackEnvironmentVariables.join(', ') || 'SLACK_*',
49
+ });
50
+ }
13
51
 
14
52
  export function initSlackSetupGuide(): void {
15
53
  if (setupGuideBound) return;
16
54
  setupGuideBound = true;
17
55
 
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
- }
56
+ bindPrefixValidation('slBotToken', 'slack-bot-token-error', hasSlackBotTokenPrefix);
57
+ bindPrefixValidation('slAppToken', 'slack-app-token-error', hasSlackAppTokenPrefix);
58
+ document.getElementById('slack-reset-connection')?.addEventListener('click', () => {
59
+ void resetSlackConnection();
32
60
  });
61
+ }
33
62
 
34
- document.getElementById('slack-open-apps')?.addEventListener('click', () => {
35
- window.open('https://api.slack.com/apps?new_app=1', '_blank', 'noopener');
36
- });
63
+ export async function resetSlackConnection(): Promise<void> {
64
+ if (slackResetting) return;
65
+ if (slackEnvironmentVariables.length > 0) {
66
+ window.alert(slackEnvironmentMessage());
67
+ return;
68
+ }
69
+ const botToken = document.getElementById('slBotToken') as HTMLInputElement | null;
70
+ const appToken = document.getElementById('slAppToken') as HTMLInputElement | null;
71
+ if (!botToken?.value.trim() && !appToken?.value.trim()) {
72
+ window.alert(t('settings.slack.resetEmpty'));
73
+ return;
74
+ }
75
+ if (!window.confirm(t('settings.slack.resetConfirm'))) return;
37
76
 
38
- bindPrefixValidation('slBotToken', 'slack-bot-token-error', hasSlackBotTokenPrefix);
39
- bindPrefixValidation('slAppToken', 'slack-app-token-error', hasSlackAppTokenPrefix);
77
+ const resetButton = document.getElementById('slack-reset-connection') as HTMLButtonElement | null;
78
+ slackResetting = true;
79
+ if (resetButton) resetButton.disabled = true;
80
+ try {
81
+ const result = await requestSlackConnectionReset();
82
+ const authoritative = await api<SettingsData>('/api/settings');
83
+ if (authoritative) loadSlackSettings(authoritative);
84
+ await refreshTransportStatusRow();
85
+
86
+ if (result?.error === 'slack_connection_managed_by_environment') {
87
+ const variables = Array.isArray(result.environmentVariables)
88
+ ? result.environmentVariables.join(', ')
89
+ : 'SLACK_*';
90
+ window.alert(t('settings.slack.resetManagedByEnvironment', { variables }));
91
+ return;
92
+ }
93
+ if (!slackConnectionIsCleared(authoritative)) {
94
+ window.alert(t('settings.slack.resetFailed'));
95
+ return;
96
+ }
97
+
98
+ for (const id of ['slBotToken', 'slAppToken', 'slTeamId', 'slChannelIds', 'slAttachPort']) {
99
+ document.getElementById(id)?.classList.remove('input-error');
100
+ }
101
+ for (const id of ['slack-bot-token-error', 'slack-app-token-error']) {
102
+ const error = document.getElementById(id);
103
+ if (error) error.style.display = 'none';
104
+ }
105
+ document.getElementById('slOff')?.classList.add('active');
106
+ document.getElementById('slOn')?.classList.remove('active');
107
+ } finally {
108
+ slackResetting = false;
109
+ if (resetButton) resetButton.disabled = slackEnvironmentVariables.length > 0;
110
+ }
40
111
  }
41
112
 
42
113
  function bindPrefixValidation(inputId: string, errorId: string, valid: (v: string) => boolean): void {
@@ -57,6 +128,10 @@ function bindPrefixValidation(inputId: string, errorId: string, valid: (v: strin
57
128
  }
58
129
 
59
130
  export async function saveSlackSettings(): Promise<void> {
131
+ if (slackEnvironmentVariables.length > 0) {
132
+ window.alert(slackEnvironmentMessage());
133
+ return;
134
+ }
60
135
  const botToken = (document.getElementById('slBotToken') as HTMLInputElement)?.value.trim() || '';
61
136
  const appToken = (document.getElementById('slAppToken') as HTMLInputElement)?.value.trim() || '';
62
137
  const teamId = (document.getElementById('slTeamId') as HTMLInputElement)?.value.trim() || '';
@@ -69,6 +144,10 @@ export async function saveSlackSettings(): Promise<void> {
69
144
  }
70
145
 
71
146
  export async function setSlack(enabled: boolean): Promise<void> {
147
+ if (slackEnvironmentVariables.length > 0) {
148
+ window.alert(slackEnvironmentMessage());
149
+ return;
150
+ }
72
151
  document.getElementById('slOn')?.classList.toggle('active', enabled);
73
152
  document.getElementById('slOff')?.classList.toggle('active', !enabled);
74
153
  await apiJson('/api/settings', 'PUT', { slack: { enabled } });
@@ -102,18 +181,37 @@ export async function setSlackReplyInThread(enabled: boolean): Promise<void> {
102
181
  export function loadSlackSettings(s: SettingsData): void {
103
182
  if (!s.slack) return;
104
183
  const sc = s.slack;
184
+ slackEnvironmentVariables = Array.isArray(s.slackEnvironmentVariables)
185
+ ? s.slackEnvironmentVariables
186
+ : [];
187
+ const environmentManaged = slackEnvironmentVariables.length > 0;
188
+ const notice = document.getElementById('slack-environment-managed');
189
+ if (notice) {
190
+ notice.style.display = environmentManaged ? '' : 'none';
191
+ notice.textContent = environmentManaged
192
+ ? t('settings.slack.managedByEnvironment', { variables: slackEnvironmentVariables.join(', ') })
193
+ : '';
194
+ }
195
+ for (const id of ['slBotToken', 'slAppToken', 'slTeamId', 'slChannelIds', 'slAttachPort']) {
196
+ const input = document.getElementById(id) as HTMLInputElement | null;
197
+ if (input) input.disabled = environmentManaged;
198
+ }
199
+ for (const id of ['slOff', 'slOn', 'slack-reset-connection', 'slack-onboarding-trigger']) {
200
+ const button = document.getElementById(id) as HTMLButtonElement | null;
201
+ if (button) button.disabled = environmentManaged;
202
+ }
105
203
  document.getElementById('slOn')?.classList.toggle('active', !!sc.enabled);
106
204
  document.getElementById('slOff')?.classList.toggle('active', !sc.enabled);
107
205
  const botToken = document.getElementById('slBotToken') as HTMLInputElement | null;
108
- if (sc.botToken && botToken) botToken.value = sc.botToken;
206
+ if (botToken) botToken.value = environmentManaged ? '' : sc.botToken || '';
109
207
  const appToken = document.getElementById('slAppToken') as HTMLInputElement | null;
110
- if (sc.appToken && appToken) appToken.value = sc.appToken;
208
+ if (appToken) appToken.value = environmentManaged ? '' : sc.appToken || '';
111
209
  const teamId = document.getElementById('slTeamId') as HTMLInputElement | null;
112
- if (sc.teamId && teamId) teamId.value = sc.teamId;
210
+ if (teamId) teamId.value = environmentManaged ? '' : sc.teamId || '';
113
211
  const channelIds = document.getElementById('slChannelIds') as HTMLInputElement | null;
114
- if (sc.channelIds?.length && channelIds) channelIds.value = sc.channelIds.join(', ');
212
+ if (channelIds) channelIds.value = environmentManaged ? '' : sc.channelIds?.join(', ') || '';
115
213
  const attachPort = document.getElementById('slAttachPort') as HTMLInputElement | null;
116
- if (attachPort) attachPort.value = sc.attachPort || '';
214
+ if (attachPort) attachPort.value = environmentManaged ? '' : sc.attachPort || '';
117
215
  const fwdOn = sc.forwardAll !== false;
118
216
  document.getElementById('slForwardOn')?.classList.toggle('active', fwdOn);
119
217
  document.getElementById('slForwardOff')?.classList.toggle('active', !fwdOn);
@@ -63,11 +63,19 @@ export interface CliStatusInfo {
63
63
  authenticated: boolean | null;
64
64
  path: string | null;
65
65
  source: string;
66
- probeState: 'checking' | 'fresh' | 'stale';
66
+ probeState: 'checking' | 'fresh' | 'stale' | 'failing';
67
67
  reason?: string;
68
+ /** Underlying probe error, present only while `failing`. */
69
+ probeError?: string;
70
+ probeFailures?: number;
71
+ nextRetryAt?: number;
68
72
  }
69
- export function describeCliProbe(info: CliStatusInfo): 'checking' | 'capability-failed' | 'stale' | 'ready' | 'unavailable' {
73
+ export function describeCliProbe(info: CliStatusInfo): 'checking' | 'probe-failing' | 'capability-failed' | 'stale' | 'ready' | 'unavailable' {
70
74
  if (info.probeState === 'checking') return 'checking';
75
+ // Ahead of every other branch: while probes keep failing we know nothing
76
+ // current about the runtime, so a preserved snapshot must not be rendered
77
+ // as ready or merely stale (#277).
78
+ if (info.probeState === 'failing') return 'probe-failing';
71
79
  if (info.binaryInstalled === true && info.capabilityReady === false) return 'capability-failed';
72
80
  if (info.probeState === 'stale') return 'stale';
73
81
  return info.available === true && info.capabilityReady !== false ? 'ready' : 'unavailable';
@@ -82,6 +90,7 @@ export interface SettingsData {
82
90
  telegram?: TelegramConfig;
83
91
  discord?: DiscordConfig;
84
92
  slack?: SlackConfig;
93
+ slackEnvironmentVariables?: string[];
85
94
  channel?: 'telegram' | 'discord' | 'slack';
86
95
  fallbackOrder?: string[];
87
96
  memory?: { cli?: string };
@@ -545,7 +545,7 @@
545
545
  "help.slack.link.portal": "Open the Slack app dashboard",
546
546
  "help.slack.link.docs": "Socket Mode documentation",
547
547
  "help.slack.example.1": "@cli-jaw run the tests for this repo",
548
- "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.",
548
+ "help.slack.howTo.1": "Click \"Copy manifest\" in the guide card below, then create the app at api.slack.com/apps → Create New App → From a manifest.",
549
549
  "help.slack.howTo.2": "Enable Socket Mode and generate an app-level token (xapp-) with connections:write.",
550
550
  "help.slack.howTo.3": "Install to the workspace to obtain the bot token (xoxb-).",
551
551
  "help.slack.howTo.4": "Paste each token into its own field below — do not swap them.",
@@ -854,10 +854,6 @@
854
854
  "bgtask.title": "Background",
855
855
  "tg.elicitationExpired": "Selection expired. Please reply with text.",
856
856
  "settings.slack.guide.step1Title": "Create the Slack app",
857
- "settings.slack.guide.step1Desc": "Create an App → From an app manifest → pick workspace → paste. For an existing app, add files:read.",
858
- "settings.slack.guide.copyManifest": "Copy manifest",
859
- "settings.slack.guide.copyManifestDone": "Copied — paste the files:read manifest into Slack",
860
- "settings.slack.guide.copyManifestFail": "Copy failed — use jaw slack manifest",
861
857
  "settings.slack.guide.openApps": "Open apps page",
862
858
  "settings.slack.guide.step2Title": "Paste the bot token",
863
859
  "settings.slack.guide.step3Title": "App-level token for inbound (optional)",
@@ -865,6 +861,12 @@
865
861
  "settings.slack.guide.botTokenError": "Must be a bot token starting with xoxb- — swapped with the app token?",
866
862
  "settings.slack.guide.appTokenError": "Must be an app-level token starting with xapp- — swapped with the bot token?",
867
863
  "settings.slack.advanced": "Advanced settings",
864
+ "settings.slack.resetConnection": "Reset connection",
865
+ "settings.slack.resetConfirm": "The Slack app connection will be reset. Continue?",
866
+ "settings.slack.resetEmpty": "No Slack app is connected.",
867
+ "settings.slack.resetFailed": "Could not reset the Slack app connection. Try again.",
868
+ "settings.slack.resetManagedByEnvironment": "This Slack connection is managed by environment variables ({variables}). Remove them and restart cli-jaw before resetting the connection.",
869
+ "settings.slack.managedByEnvironment": "Connection settings are read-only because they are managed by environment variables: {variables}. Remove them and restart cli-jaw to edit here.",
868
870
  "settings.slack.attachPort": "Attach instance port",
869
871
  "settings.channel.notAttachInstanceHint": "Another instance owns this Slack connection (check the attach instance port)",
870
872
  "settings.slack.guide.step2Desc": "App settings → OAuth & Permissions → confirm files:read → Reinstall to Workspace → copy Bot User OAuth Token",
@@ -893,9 +895,15 @@
893
895
  "onboarding.title.slack": "Connect Slack",
894
896
  "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.",
895
897
  "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.",
896
- "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.",
897
- "onboarding.slackExistingApp": "For an existing app, add files:read under OAuth & Permissions, then choose Reinstall to Workspace.",
898
- "onboarding.warning.missingCapabilities": "Optional capability {capabilities} is missing, so inbound attachments are disabled. Add it and reinstall the app. Text messages still work.",
898
+ "onboarding.guide.slack": "At api.slack.com/apps, choose Create New App From a manifest, paste the copied manifest into the JSON tab, select the workspace where you want to add the app, and create it.",
899
+ "onboarding.slackAppName": "App name",
900
+ "onboarding.slackAppNameHint": "Used for both the app and bot. Enter up to 35 lowercase letters, numbers, hyphens, underscores, or dots.",
901
+ "onboarding.slackGenerateManifest": "Generate manifest",
902
+ "onboarding.slackManifestGenerating": "Generating...",
903
+ "onboarding.slackManifestReady": "Copied",
904
+ "onboarding.slackManifestError": "Could not generate or copy the manifest. Try again.",
905
+ "onboarding.slackAppNameError": "Use 1–35 lowercase letters, numbers, hyphens, underscores, or dots.",
906
+ "onboarding.warning.missingCapabilities": "Optional scopes {capabilities} are missing, so the features they cover are turned off. Add them and reinstall the app. Text messages still work.",
899
907
  "onboarding.token.botToken": "Bot token",
900
908
  "onboarding.token.appToken": "App-level token",
901
909
  "onboarding.token.guildId": "Server ID",
@@ -920,6 +928,7 @@
920
928
  "onboarding.validateHint": "Press Validate to check the real connection. It must pass before saving.",
921
929
  "onboarding.saveHint": "Validation passed. Saving applies the settings and connects the channel right away.",
922
930
  "onboarding.error.validation_required": "Validation has to pass first.",
931
+ "onboarding.error.slack_setup_required": "Generate the manifest and open the issuing page before continuing.",
923
932
  "onboarding.openIssuer": "Open the issuing page",
924
933
  "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.",
925
934
  "onboarding.hint.telegram.botToken": "The value @BotFather returned after /newbot.",
@@ -534,7 +534,7 @@
534
534
  "help.slack.link.portal": "Open the Slack app dashboard",
535
535
  "help.slack.link.docs": "Socket Mode documentation",
536
536
  "help.slack.example.1": "@cli-jaw run the tests for this repo",
537
- "help.slack.howTo.1": "下のガイドカードの「マニフェストをコピー」を押し、api.slack.com/apps → Create an App → From an app manifest に貼り付けてアプリを作成します。",
537
+ "help.slack.howTo.1": "下のガイドカードの「マニフェストをコピー」を押し、api.slack.com/apps → Create New App → From a manifest に貼り付けてアプリを作成します。",
538
538
  "help.slack.howTo.2": "Enable Socket Mode and generate an app-level token (xapp-) with connections:write.",
539
539
  "help.slack.howTo.3": "Install to the workspace to obtain the bot token (xoxb-).",
540
540
  "help.slack.howTo.4": "Paste each token into its own field below — do not swap them.",
@@ -843,10 +843,6 @@
843
843
  "bgtask.title": "バックグラウンド",
844
844
  "tg.elicitationExpired": "選択セッションの有効期限が切れました。テキストで返信してください。",
845
845
  "settings.slack.guide.step1Title": "Slack アプリを作成",
846
- "settings.slack.guide.step1Desc": "Create an App → From an app manifest → ワークスペース選択 → 貼り付け。既存アプリには files:read を追加します。",
847
- "settings.slack.guide.copyManifest": "マニフェストをコピー",
848
- "settings.slack.guide.copyManifestDone": "コピーしました — files:read を含むマニフェストを Slack に貼り付けてください",
849
- "settings.slack.guide.copyManifestFail": "コピー失敗 — jaw slack manifest を使用",
850
846
  "settings.slack.guide.openApps": "アプリページを開く",
851
847
  "settings.slack.guide.step2Title": "ボットトークンを貼り付け",
852
848
  "settings.slack.guide.step3Title": "受信用アプリトークン(任意)",
@@ -854,6 +850,12 @@
854
850
  "settings.slack.guide.botTokenError": "xoxb- で始まるボットトークンが必要です — アプリトークンと入れ替わっていませんか?",
855
851
  "settings.slack.guide.appTokenError": "xapp- で始まるアプリレベルトークンが必要です — ボットトークンと入れ替わっていませんか?",
856
852
  "settings.slack.advanced": "詳細設定",
853
+ "settings.slack.resetConnection": "接続をリセット",
854
+ "settings.slack.resetConfirm": "Slack アプリの接続がリセットされます。続行しますか?",
855
+ "settings.slack.resetEmpty": "接続されている Slack アプリはありません。",
856
+ "settings.slack.resetFailed": "Slack アプリの接続をリセットできませんでした。もう一度お試しください。",
857
+ "settings.slack.resetManagedByEnvironment": "この Slack 接続は環境変数({variables})で管理されています。環境変数を削除して cli-jaw を再起動してから、接続をリセットしてください。",
858
+ "settings.slack.managedByEnvironment": "接続設定は環境変数({variables})で管理されているため読み取り専用です。ここで編集するには環境変数を削除し、cli-jaw を再起動してください。",
857
859
  "settings.slack.attachPort": "接続インスタンスのポート",
858
860
  "settings.channel.notAttachInstanceHint": "別のインスタンスがこの Slack 接続を所有しています(接続インスタンスのポートを確認)",
859
861
  "settings.slack.guide.step2Desc": "アプリ設定 → OAuth & Permissions → files:read を確認 → Reinstall to Workspace → Bot User OAuth Token をコピー",
@@ -882,9 +884,15 @@
882
884
  "onboarding.title.slack": "Slack に接続",
883
885
  "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.",
884
886
  "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.",
885
- "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.",
886
- "onboarding.slackExistingApp": "既存アプリでは OAuth & Permissions に files:read を追加し、Reinstall to Workspace を実行してください。",
887
- "onboarding.warning.missingCapabilities": "任意機能の権限 {capabilities} がないため、添付ファイル受信は無効です。権限を追加してアプリを再インストールしてください。テキストは引き続き利用できます。",
887
+ "onboarding.guide.slack": "api.slack.com/apps Create New App From a manifest を選び、JSON タブにコピーしたマニフェストを貼り付け、アプリを追加するワークスペースを選択して作成してください。",
888
+ "onboarding.slackAppName": "アプリ名",
889
+ "onboarding.slackAppNameHint": "アプリとボットの両方に使用します。英小文字、数字、-、_、. のみで35文字以内にしてください。",
890
+ "onboarding.slackGenerateManifest": "マニフェストを生成",
891
+ "onboarding.slackManifestGenerating": "生成中...",
892
+ "onboarding.slackManifestReady": "コピーしました",
893
+ "onboarding.slackManifestError": "マニフェストを生成またはコピーできませんでした。もう一度お試しください。",
894
+ "onboarding.slackAppNameError": "英小文字、数字、-、_、. のみで1〜35文字入力してください。",
895
+ "onboarding.warning.missingCapabilities": "任意の権限 {capabilities} がないため、その機能のみ無効です。権限を追加してアプリを再インストールしてください。テキストは引き続き利用できます。",
888
896
  "onboarding.token.botToken": "ボットトークン",
889
897
  "onboarding.token.appToken": "アプリレベルトークン",
890
898
  "onboarding.token.guildId": "サーバー ID",
@@ -909,6 +917,7 @@
909
917
  "onboarding.validateHint": "「検証」を押して実際の接続を確認してください。通過しないと保存に進めません。",
910
918
  "onboarding.saveHint": "検証に成功しました。保存すると設定に反映され、チャンネルがすぐ接続されます。",
911
919
  "onboarding.error.validation_required": "先に検証を通す必要があります。",
920
+ "onboarding.error.slack_setup_required": "マニフェストを生成し、発行ページを開いてから次へ進んでください。",
912
921
  "onboarding.openIssuer": "発行ページを開く",
913
922
  "onboarding.error.missing_scopes": "トークンは有効ですが、アプリの権限(スコープ)が不足しています。OAuth & Permissions で以下を追加し、アプリを再インストールしてください。",
914
923
  "onboarding.hint.telegram.botToken": "@BotFather が /newbot の後に返した値です。",
@@ -545,7 +545,7 @@
545
545
  "help.slack.link.portal": "Slack 앱 관리 페이지 열기",
546
546
  "help.slack.link.docs": "Socket Mode 문서",
547
547
  "help.slack.example.1": "@cli-jaw 이 저장소 테스트 돌려줘",
548
- "help.slack.howTo.1": "아래 가이드 카드의 \"매니페스트 복사\"를 눌러 api.slack.com/apps → Create an App → From an app manifest에 붙여넣어 앱을 만듭니다.",
548
+ "help.slack.howTo.1": "아래 가이드 카드의 \"매니페스트 복사\"를 눌러 api.slack.com/apps → Create New App → From a manifest에 붙여넣어 앱을 만듭니다.",
549
549
  "help.slack.howTo.2": "Socket Mode를 켜고 connections:write 범위의 app-level 토큰(xapp-)을 발급합니다.",
550
550
  "help.slack.howTo.3": "워크스페이스에 설치해 봇 토큰(xoxb-)을 받습니다.",
551
551
  "help.slack.howTo.4": "두 토큰을 아래 입력란에 각각 붙여넣습니다 — 서로 바꿔 넣지 않도록 주의하세요.",
@@ -854,10 +854,6 @@
854
854
  "bgtask.title": "백그라운드",
855
855
  "tg.elicitationExpired": "선택 세션이 만료되었습니다. 텍스트로 답해주세요.",
856
856
  "settings.slack.guide.step1Title": "Slack 앱 만들기",
857
- "settings.slack.guide.step1Desc": "Create an App → From an app manifest → 워크스페이스 선택 → 붙여넣기. 기존 앱은 files:read를 추가하세요.",
858
- "settings.slack.guide.copyManifest": "매니페스트 복사",
859
- "settings.slack.guide.copyManifestDone": "복사됨 — files:read 포함 매니페스트를 Slack에 붙여넣으세요",
860
- "settings.slack.guide.copyManifestFail": "복사 실패 — jaw slack manifest 사용",
861
857
  "settings.slack.guide.openApps": "앱 페이지 열기",
862
858
  "settings.slack.guide.step2Title": "봇 토큰 붙여넣기",
863
859
  "settings.slack.guide.step3Title": "수신용 앱 토큰 (선택)",
@@ -865,6 +861,12 @@
865
861
  "settings.slack.guide.botTokenError": "xoxb-로 시작하는 봇 토큰이어야 합니다 — 앱 토큰과 바꿔 넣지 않았나요?",
866
862
  "settings.slack.guide.appTokenError": "xapp-로 시작하는 앱 레벨 토큰이어야 합니다 — 봇 토큰과 바꿔 넣지 않았나요?",
867
863
  "settings.slack.advanced": "고급 설정",
864
+ "settings.slack.resetConnection": "연결 초기화",
865
+ "settings.slack.resetConfirm": "Slack 앱 연결이 초기화됩니다. 계속하시겠습니까?",
866
+ "settings.slack.resetEmpty": "연결된 Slack 앱이 없습니다.",
867
+ "settings.slack.resetFailed": "Slack 앱 연결을 초기화하지 못했습니다. 다시 시도해주세요.",
868
+ "settings.slack.resetManagedByEnvironment": "이 Slack 연결은 환경변수({variables})로 관리되고 있습니다. 해당 환경변수를 제거하고 cli-jaw를 재시작한 후 연결을 초기화하세요.",
869
+ "settings.slack.managedByEnvironment": "연결 설정은 환경변수({variables})로 관리되어 읽기 전용입니다. 여기서 수정하려면 환경변수를 제거하고 cli-jaw를 재시작하세요.",
868
870
  "settings.slack.attachPort": "연결 인스턴스 포트",
869
871
  "settings.channel.notAttachInstanceHint": "다른 인스턴스가 이 Slack 연결을 소유하고 있습니다 (연결 인스턴스 포트 확인)",
870
872
  "settings.slack.guide.step2Desc": "앱 설정 → OAuth & Permissions → files:read 확인 → Reinstall to Workspace → Bot User OAuth Token 복사",
@@ -893,9 +895,15 @@
893
895
  "onboarding.title.slack": "Slack 연결",
894
896
  "onboarding.guide.telegram": "텔레그램에서 @BotFather 를 열고 /newbot 을 보낸 뒤, 이름과 username을 정하면 봇 토큰이 나옵니다. 그 토큰을 아래에 붙여넣고 검증을 누르세요.",
895
897
  "onboarding.guide.discord": "discord.com/developers/applications 에서 New Application → Bot → Reset Token 으로 토큰을 만들고, Privileged Gateway Intents 의 MESSAGE CONTENT INTENT 를 켜세요. 서버 ID는 Discord 설정에서 개발자 모드를 켠 뒤 서버 우클릭 → ID 복사로 얻습니다.",
896
- "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)을 발급받으세요.",
897
- "onboarding.slackExistingApp": "기존 앱은 OAuth & Permissions에서 files:read를 추가한 뒤 Reinstall to Workspace를 실행하세요.",
898
- "onboarding.warning.missingCapabilities": "선택 기능 권한({capabilities})이 없어 첨부파일 수신은 꺼져 있습니다. 권한을 추가하고 앱을 재설치하세요. 텍스트 메시지는 계속 사용할 수 있습니다.",
898
+ "onboarding.guide.slack": "api.slack.com/apps에서 Create New App → From a manifest JSON 탭에 복사된 매니페스트를 붙여넣고, 추가하려는 워크스페이스를 선택하여 앱을 생성해 주세요.",
899
+ "onboarding.slackAppName": " 이름",
900
+ "onboarding.slackAppNameHint": "앱과 봇에 함께 사용할 이름입니다. 영문 소문자, 숫자, -, _, .만 35자 이내로 입력하세요.",
901
+ "onboarding.slackGenerateManifest": "매니페스트 생성",
902
+ "onboarding.slackManifestGenerating": "생성 중...",
903
+ "onboarding.slackManifestReady": "복사되었습니다",
904
+ "onboarding.slackManifestError": "매니페스트를 생성하거나 복사하지 못했어요. 다시 시도해주세요.",
905
+ "onboarding.slackAppNameError": "영문 소문자, 숫자, -, _, .만 사용해 1~35자로 입력하세요.",
906
+ "onboarding.warning.missingCapabilities": "선택 권한({capabilities})이 없어 해당 기능만 꺼져 있습니다. 권한을 추가하고 앱을 재설치하세요. 텍스트 메시지는 계속 사용할 수 있습니다.",
899
907
  "onboarding.token.botToken": "봇 토큰",
900
908
  "onboarding.token.appToken": "앱 레벨 토큰",
901
909
  "onboarding.token.guildId": "서버 ID",
@@ -920,6 +928,7 @@
920
928
  "onboarding.validateHint": "검증을 눌러 실제 연결을 확인하세요. 통과해야 저장으로 넘어갑니다.",
921
929
  "onboarding.saveHint": "검증을 통과했습니다. 저장을 누르면 설정에 반영되고 채널이 바로 연결됩니다.",
922
930
  "onboarding.error.validation_required": "먼저 검증을 통과해야 합니다.",
931
+ "onboarding.error.slack_setup_required": "매니페스트를 생성하고 발급 페이지를 연 뒤 다음 단계로 진행하세요.",
923
932
  "onboarding.openIssuer": "발급 페이지 열기",
924
933
  "onboarding.error.missing_scopes": "토큰은 유효하지만 앱에 권한(스코프)이 부족합니다. OAuth & Permissions에서 아래 항목을 추가하고 앱을 재설치하세요.",
925
934
  "onboarding.hint.telegram.botToken": "@BotFather가 /newbot 후 알려준 값입니다.",
@@ -534,7 +534,7 @@
534
534
  "help.slack.link.portal": "Open the Slack app dashboard",
535
535
  "help.slack.link.docs": "Socket Mode documentation",
536
536
  "help.slack.example.1": "@cli-jaw run the tests for this repo",
537
- "help.slack.howTo.1": "点击下方指南卡中的“复制清单”,然后在 api.slack.com/apps → Create an App → From an app manifest 粘贴以创建应用。",
537
+ "help.slack.howTo.1": "点击下方指南卡中的“复制清单”,然后在 api.slack.com/apps → Create New App → From a manifest 粘贴以创建应用。",
538
538
  "help.slack.howTo.2": "Enable Socket Mode and generate an app-level token (xapp-) with connections:write.",
539
539
  "help.slack.howTo.3": "Install to the workspace to obtain the bot token (xoxb-).",
540
540
  "help.slack.howTo.4": "Paste each token into its own field below — do not swap them.",
@@ -843,10 +843,6 @@
843
843
  "bgtask.title": "后台任务",
844
844
  "tg.elicitationExpired": "选择会话已过期,请用文字回复。",
845
845
  "settings.slack.guide.step1Title": "创建 Slack 应用",
846
- "settings.slack.guide.step1Desc": "Create an App → From an app manifest → 选择工作区 → 粘贴。现有应用需添加 files:read。",
847
- "settings.slack.guide.copyManifest": "复制清单",
848
- "settings.slack.guide.copyManifestDone": "已复制 — 请将含 files:read 的 manifest 粘贴到 Slack",
849
- "settings.slack.guide.copyManifestFail": "复制失败 — 请使用 jaw slack manifest",
850
846
  "settings.slack.guide.openApps": "打开应用页面",
851
847
  "settings.slack.guide.step2Title": "粘贴机器人令牌",
852
848
  "settings.slack.guide.step3Title": "接收用应用令牌(可选)",
@@ -854,6 +850,12 @@
854
850
  "settings.slack.guide.botTokenError": "需要以 xoxb- 开头的机器人令牌 — 是否与应用令牌填反了?",
855
851
  "settings.slack.guide.appTokenError": "需要以 xapp- 开头的应用级令牌 — 是否与机器人令牌填反了?",
856
852
  "settings.slack.advanced": "高级设置",
853
+ "settings.slack.resetConnection": "重置连接",
854
+ "settings.slack.resetConfirm": "Slack 应用连接将被重置。是否继续?",
855
+ "settings.slack.resetEmpty": "当前没有已连接的 Slack 应用。",
856
+ "settings.slack.resetFailed": "无法重置 Slack 应用连接,请重试。",
857
+ "settings.slack.resetManagedByEnvironment": "此 Slack 连接由环境变量({variables})管理。请删除这些环境变量并重启 cli-jaw,然后再重置连接。",
858
+ "settings.slack.managedByEnvironment": "连接设置由环境变量({variables})管理,因此为只读。若要在此编辑,请删除这些变量并重启 cli-jaw。",
857
859
  "settings.slack.attachPort": "连接实例端口",
858
860
  "settings.channel.notAttachInstanceHint": "另一个实例拥有此 Slack 连接(请检查连接实例端口)",
859
861
  "settings.slack.guide.step2Desc": "应用设置 → OAuth & Permissions → 确认 files:read → Reinstall to Workspace → 复制 Bot User OAuth Token",
@@ -882,9 +884,15 @@
882
884
  "onboarding.title.slack": "连接 Slack",
883
885
  "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.",
884
886
  "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.",
885
- "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.",
886
- "onboarding.slackExistingApp": "对于现有应用,请在 OAuth & Permissions 中添加 files:read,然后选择 Reinstall to Workspace。",
887
- "onboarding.warning.missingCapabilities": "缺少可选权限 {capabilities},因此无法接收附件。请添加权限并重新安装应用。文本消息仍可使用。",
887
+ "onboarding.guide.slack": " api.slack.com/apps 中选择 Create New App From a manifest,将复制的清单粘贴到 JSON 标签页,选择要添加应用的工作区,然后创建应用。",
888
+ "onboarding.slackAppName": "应用名称",
889
+ "onboarding.slackAppNameHint": "该名称同时用于应用和机器人。请使用小写英文字母、数字、-、_、.,最多35个字符。",
890
+ "onboarding.slackGenerateManifest": "生成清单",
891
+ "onboarding.slackManifestGenerating": "正在生成...",
892
+ "onboarding.slackManifestReady": "已复制",
893
+ "onboarding.slackManifestError": "无法生成或复制清单,请重试。",
894
+ "onboarding.slackAppNameError": "请使用小写英文字母、数字、-、_、.,输入1到35个字符。",
895
+ "onboarding.warning.missingCapabilities": "缺少可选权限 {capabilities},相应功能已关闭。请添加权限并重新安装应用。文本消息仍可使用。",
888
896
  "onboarding.token.botToken": "机器人令牌",
889
897
  "onboarding.token.appToken": "应用级令牌",
890
898
  "onboarding.token.guildId": "服务器 ID",
@@ -909,6 +917,7 @@
909
917
  "onboarding.validateHint": "点击验证以检查实际连接。通过后才能保存。",
910
918
  "onboarding.saveHint": "验证通过。保存后将写入设置并立即连接该渠道。",
911
919
  "onboarding.error.validation_required": "需要先通过验证。",
920
+ "onboarding.error.slack_setup_required": "请先生成清单并打开发放页面,然后再继续。",
912
921
  "onboarding.openIssuer": "打开签发页面",
913
922
  "onboarding.error.missing_scopes": "令牌有效,但应用缺少 OAuth 权限(scope)。请在 OAuth & Permissions 中添加下列项并重新安装应用。",
914
923
  "onboarding.hint.telegram.botToken": "@BotFather 在 /newbot 之后返回的值。",
@@ -42,6 +42,7 @@ export function stripServerOwnedSettingsFields(settings: Record<string, unknown>
42
42
  settingsSchemaVersion: _schema,
43
43
  runtimeDefaultMigration: _migration,
44
44
  multiSessionDefaultMigration: _sessionMigration,
45
+ slackEnvironmentVariables: _slackEnvironmentVariables,
45
46
  ...userOwned
46
47
  } = settings;
47
48
  return userOwned;
@@ -57,7 +57,8 @@ type AgentSnapshot = AgentSettingsSnapshot & {
57
57
  type CliStatusInfo = {
58
58
  available: boolean | null;
59
59
  capabilityReady: boolean | null;
60
- probeState: 'checking' | 'fresh' | 'stale';
60
+ probeState: 'checking' | 'fresh' | 'stale' | 'failing';
61
+ probeError?: string;
61
62
  };
62
63
 
63
64
  export function conflictSettingsFromError(error: unknown): AgentSnapshot | null {
@@ -333,6 +334,14 @@ export default function Agent({ port, client, dirty, registerSave }: SettingsPag
333
334
  {cliStatus[draft.cli]?.probeState === 'checking' ? (
334
335
  <div className="settings-inline-notice" role="status" aria-live="polite">상태 확인 중</div>
335
336
  ) : null}
337
+ {cliStatus[draft.cli]?.probeState === 'failing' ? (
338
+ // Without this the panel stays silent while every probe errors,
339
+ // which is the state #277 reported as an endless "stale".
340
+ <div className="settings-inline-notice" role="alert">
341
+ 상태 확인 실패 (재시도 중)
342
+ {cliStatus[draft.cli]?.probeError ? `: ${cliStatus[draft.cli]!.probeError}` : ''}
343
+ </div>
344
+ ) : null}
336
345
  <RuntimeHeader
337
346
  cli={draft.cli}
338
347
  cliOptions={cliOptions.length > 0 ? cliOptions : [draft.cli || 'claude']}
@@ -42,6 +42,7 @@ type SlackBlock = {
42
42
  type SlackSnapshot = {
43
43
  channel?: ActiveChannel;
44
44
  slack?: SlackBlock;
45
+ slackEnvironmentVariables?: string[];
45
46
  [key: string]: unknown;
46
47
  };
47
48
 
@@ -117,6 +118,10 @@ export default function ChannelsSlack({ port, client, dirty, registerSave }: Set
117
118
  }, [state]);
118
119
 
119
120
  const originalChannel = state.kind === 'ready' ? state.data.channel : undefined;
121
+ const environmentVariables = state.kind === 'ready' && Array.isArray(state.data.slackEnvironmentVariables)
122
+ ? state.data.slackEnvironmentVariables
123
+ : [];
124
+ const environmentManaged = environmentVariables.length > 0;
120
125
 
121
126
  const onSave = useCallback(async () => {
122
127
  const bundle = dirty.saveBundle();
@@ -157,7 +162,9 @@ export default function ChannelsSlack({ port, client, dirty, registerSave }: Set
157
162
  // Surface the outbound-only state on THIS page rather than changing the
158
163
  // shared status chips, which are frozen for cross-channel changes.
159
164
  const outboundOnly = Boolean(original.enabled) && Boolean(original.botToken) && !original.appToken;
160
- const slackHint = outboundOnly
165
+ const slackHint = environmentManaged
166
+ ? `Connection settings are read-only because they are managed by environment variables: ${environmentVariables.join(', ')}.`
167
+ : outboundOnly
161
168
  ? 'Currently OUTBOUND-ONLY: the app-level token (xapp-) is missing, so no inbound Slack events can arrive.'
162
169
  : 'Slack needs TWO tokens: a bot token (xoxb-) for the Web API and an app-level token (xapp-) for Socket Mode.';
163
170
 
@@ -189,6 +196,7 @@ export default function ChannelsSlack({ port, client, dirty, registerSave }: Set
189
196
  id="sl-enabled"
190
197
  label="Slack enabled"
191
198
  value={enabled}
199
+ disabled={environmentManaged}
192
200
  onChange={(next) => {
193
201
  setEnabled(next);
194
202
  setEntry('slack.enabled', {
@@ -204,6 +212,7 @@ export default function ChannelsSlack({ port, client, dirty, registerSave }: Set
204
212
  value={botToken}
205
213
  placeholder={maskOf(original.botToken)}
206
214
  error={botTokenError}
215
+ disabled={environmentManaged}
207
216
  onChange={(next) => {
208
217
  setBotToken(next);
209
218
  if (next.length === 0) {
@@ -223,6 +232,7 @@ export default function ChannelsSlack({ port, client, dirty, registerSave }: Set
223
232
  value={appToken}
224
233
  placeholder={maskOf(original.appToken)}
225
234
  error={appTokenError}
235
+ disabled={environmentManaged}
226
236
  onChange={(next) => {
227
237
  setAppToken(next);
228
238
  if (next.length === 0) {
@@ -241,6 +251,7 @@ export default function ChannelsSlack({ port, client, dirty, registerSave }: Set
241
251
  label="Team ID"
242
252
  value={teamId}
243
253
  placeholder="T01234567 (optional)"
254
+ disabled={environmentManaged}
244
255
  onChange={(next) => {
245
256
  setTeamId(next);
246
257
  setEntry('slack.teamId', {
@@ -256,6 +267,7 @@ export default function ChannelsSlack({ port, client, dirty, registerSave }: Set
256
267
  value={channelIds}
257
268
  placeholder="C01234567"
258
269
  error={channelIdsError}
270
+ disabled={environmentManaged}
259
271
  onChange={(next) => {
260
272
  setChannelIds(next);
261
273
  setEntry('slack.channelIds', {
@@ -18,10 +18,17 @@ cp -R src/prompt/templates/. "$STAGING/src/prompt/templates/"
18
18
  cp -R prompts/. "$STAGING/prompts/"
19
19
  cp -R src/browser/adaptive-fetch/vendor/. "$STAGING/src/browser/adaptive-fetch/vendor/"
20
20
 
21
- # jawcode TUI bundle + Bun shim + native addon (macOS only)
21
+ # bun-shim.mjs is a COMMITTED source asset, unlike the jawcode bundles below
22
+ # which are generated and gitignored. jawcode-render.ts imports the shim
23
+ # unconditionally, so gating its copy on a generated artifact made every clean
24
+ # build (CI, release, any fresh checkout) publish a dist that cannot load its
25
+ # own renderer — `jaw chat --raw` then died with ERR_MODULE_NOT_FOUND (#275).
26
+ mkdir -p "$STAGING/src/lib/tui"
27
+ cp src/lib/tui/bun-shim.mjs "$STAGING/src/lib/tui/"
28
+
29
+ # jawcode TUI bundle + native addon (macOS only)
22
30
  if [ -f src/lib/tui/jawcode-tui-bundle.mjs ]; then
23
- mkdir -p "$STAGING/src/lib/tui" "$STAGING/src/lib/native"
24
- cp src/lib/tui/bun-shim.mjs "$STAGING/src/lib/tui/"
31
+ mkdir -p "$STAGING/src/lib/native"
25
32
  cp src/lib/tui/jawcode-tui-bundle.mjs "$STAGING/src/lib/tui/"
26
33
  [ -f src/lib/tui/jawcode-interactive-bundle.mjs ] && cp src/lib/tui/jawcode-interactive-bundle.mjs "$STAGING/src/lib/tui/"
27
34
  NATIVE_TAG="$(node -p '`${process.platform}-${process.arch}`')"