tide-commander 1.37.2 → 1.38.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/assets/{BossLogsModal-C9c6b6JM.js → BossLogsModal-Bn3z56OP.js} +1 -1
- package/dist/assets/{BossSpawnModal-dSXYldID.js → BossSpawnModal-CWxP-MSv.js} +1 -1
- package/dist/assets/{ControlsModal-C_W0XTlE.js → ControlsModal-B5r9l6t1.js} +1 -1
- package/dist/assets/{DockerLogsModal-CUweGxhz.js → DockerLogsModal-CWwOgxQG.js} +1 -1
- package/dist/assets/{EmbeddedEditor-DU_lcIBD.js → EmbeddedEditor-CePY44FJ.js} +1 -1
- package/dist/assets/{GmailOAuthSetup-DTAkTnzL.js → GmailOAuthSetup-zVmKkZLJ.js} +1 -1
- package/dist/assets/{GoogleOAuthSetup-B83vPcAW.js → GoogleOAuthSetup-DXSCMIOU.js} +1 -1
- package/dist/assets/{IframeModal-3rIUuKKu.js → IframeModal-BsbB4AMI.js} +1 -1
- package/dist/assets/{IntegrationsPanel-CJviFK3q.js → IntegrationsPanel-1xcUvOle.js} +2 -2
- package/dist/assets/{LogViewerModal-C9qFaC8V.js → LogViewerModal-DVS-zxfP.js} +1 -1
- package/dist/assets/{MonitoringModal-DRZrxL7t.js → MonitoringModal-DWk0piqs.js} +1 -1
- package/dist/assets/{PM2LogsModal-BLzKH4ql.js → PM2LogsModal-GxDWxG5c.js} +1 -1
- package/dist/assets/{RestoreArchivedAreaModal-DHH8QKkx.js → RestoreArchivedAreaModal-DvbQVeiA.js} +1 -1
- package/dist/assets/{SaveSnapshotModal-BTvvgNqk.js → SaveSnapshotModal-DpONGDMt.js} +1 -1
- package/dist/assets/{Scene2DCanvas-Gu0Pplgl.js → Scene2DCanvas-DLvMkm2q.js} +1 -1
- package/dist/assets/{SceneManager-BD0UwiqS.js → SceneManager-BgjRQCpO.js} +1 -1
- package/dist/assets/{SkillsPanel-Btfm8DLY.js → SkillsPanel-DqqJjbX5.js} +1 -1
- package/dist/assets/{SnapshotManager-BgCuvtUx.js → SnapshotManager-4ig8bV_o.js} +1 -1
- package/dist/assets/{SpawnModal-ByhvPfN7.js → SpawnModal-D2e4lCt9.js} +1 -1
- package/dist/assets/{SubordinateAssignmentModal-5Re0nGK9.js → SubordinateAssignmentModal-B_rjqVum.js} +1 -1
- package/dist/assets/{SupervisorPanel-DtNzjH1r.js → SupervisorPanel-BAXOUwAk.js} +1 -1
- package/dist/assets/{TriggerManagerPanel-DUXzcjBS.js → TriggerManagerPanel-D48mxJNq.js} +1 -1
- package/dist/assets/{WorkflowEditorPanel-CCauefUO.js → WorkflowEditorPanel-DkzFKMCZ.js} +1 -1
- package/dist/assets/{index-DtDea-zV.js → index-BoWUyxxn.js} +1 -1
- package/dist/assets/{index-CPBP46l5.js → index-BrgM4K8j.js} +1 -1
- package/dist/assets/{index-BG-fI4o6.js → index-CdlqvN8h.js} +1 -1
- package/dist/assets/{index-DTO0LlmC.js → index-CsBPIIxt.js} +1 -1
- package/dist/assets/{index-BakRazz4.js → index-D3k7Y2rz.js} +1 -1
- package/dist/assets/{index-Bma1pZ6y.js → index-DENcwRRk.js} +2 -2
- package/dist/assets/{index-DfBVuA_K.js → index-DHFWfrE-.js} +1 -1
- package/dist/assets/{index-Bl5TthnX.js → index-DWfphUcy.js} +3 -3
- package/dist/assets/{main-lr_lRuBZ.js → main-sZyyVmvr.js} +4 -4
- package/dist/assets/{web-DOowQZI5.js → web-CmQWPACi.js} +1 -1
- package/dist/assets/{web-CV2dsnZj.js → web-GvCQTLgg.js} +1 -1
- package/dist/index.html +1 -1
- package/dist/src/packages/server/integrations/slack/index.js +17 -0
- package/dist/src/packages/server/integrations/slack/slack-client.js +63 -0
- package/dist/src/packages/server/integrations/slack/slack-config.js +1 -1
- package/dist/src/packages/server/integrations/slack/slack-routes.js +49 -1
- package/dist/src/packages/server/integrations/slack/slack-skill.js +31 -0
- package/package.json +1 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
import{bw as s}from"./main-
|
|
1
|
+
import{bw as s}from"./main-sZyyVmvr.js";import"./modulepreload-polyfill-B5Qt9EMX.js";import"./vendor-react--Eh9ivFN.js";import"./vendor-three-Chj50gSY.js";class f extends s{constructor(){super(...arguments),this.pending=[],this.deliveredNotifications=[],this.hasNotificationSupport=()=>{if(!("Notification"in window)||!Notification.requestPermission)return!1;if(Notification.permission!=="granted")try{new Notification("")}catch(i){if(i instanceof Error&&i.name==="TypeError")return!1}return!0}}async getDeliveredNotifications(){const i=[];for(const t of this.deliveredNotifications){const e={title:t.title,id:parseInt(t.tag),body:t.body};i.push(e)}return{notifications:i}}async removeDeliveredNotifications(i){for(const t of i.notifications){const e=this.deliveredNotifications.find(n=>n.tag===String(t.id));e==null||e.close(),this.deliveredNotifications=this.deliveredNotifications.filter(()=>!e)}}async removeAllDeliveredNotifications(){for(const i of this.deliveredNotifications)i.close();this.deliveredNotifications=[]}async createChannel(){throw this.unimplemented("Not implemented on web.")}async deleteChannel(){throw this.unimplemented("Not implemented on web.")}async listChannels(){throw this.unimplemented("Not implemented on web.")}async schedule(i){if(!this.hasNotificationSupport())throw this.unavailable("Notifications not supported in this browser.");for(const t of i.notifications)this.sendNotification(t);return{notifications:i.notifications.map(t=>({id:t.id}))}}async getPending(){return{notifications:this.pending}}async registerActionTypes(){throw this.unimplemented("Not implemented on web.")}async cancel(i){this.pending=this.pending.filter(t=>!i.notifications.find(e=>e.id===t.id))}async areEnabled(){const{display:i}=await this.checkPermissions();return{value:i==="granted"}}async changeExactNotificationSetting(){throw this.unimplemented("Not implemented on web.")}async checkExactNotificationSetting(){throw this.unimplemented("Not implemented on web.")}async requestPermissions(){if(!this.hasNotificationSupport())throw this.unavailable("Notifications not supported in this browser.");return{display:this.transformNotificationPermission(await Notification.requestPermission())}}async checkPermissions(){if(!this.hasNotificationSupport())throw this.unavailable("Notifications not supported in this browser.");return{display:this.transformNotificationPermission(Notification.permission)}}transformNotificationPermission(i){switch(i){case"granted":return"granted";case"denied":return"denied";default:return"prompt"}}sendPending(){var i;const t=[],e=new Date().getTime();for(const n of this.pending)!((i=n.schedule)===null||i===void 0)&&i.at&&n.schedule.at.getTime()<=e&&(this.buildNotification(n),t.push(n));this.pending=this.pending.filter(n=>!t.find(o=>o===n))}sendNotification(i){var t;if(!((t=i.schedule)===null||t===void 0)&&t.at){const e=i.schedule.at.getTime()-new Date().getTime();this.pending.push(i),setTimeout(()=>{this.sendPending()},e);return}this.buildNotification(i)}buildNotification(i){const t=new Notification(i.title,{body:i.body,tag:String(i.id)});return t.addEventListener("click",this.onClick.bind(this,i),!1),t.addEventListener("show",this.onShow.bind(this,i),!1),t.addEventListener("close",()=>{this.deliveredNotifications=this.deliveredNotifications.filter(()=>!this)},!1),this.deliveredNotifications.push(t),t}onClick(i){const t={actionId:"tap",notification:i};this.notifyListeners("localNotificationActionPerformed",t)}onShow(i){this.notifyListeners("localNotificationReceived",i)}}export{f as LocalNotificationsWeb};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{bw as a}from"./main-
|
|
1
|
+
import{bw as a}from"./main-sZyyVmvr.js";import{ImpactStyle as i,NotificationType as r}from"./index-DENcwRRk.js";import"./modulepreload-polyfill-B5Qt9EMX.js";import"./vendor-react--Eh9ivFN.js";import"./vendor-three-Chj50gSY.js";class u extends a{constructor(){super(...arguments),this.selectionStarted=!1}async impact(t){const e=this.patternForImpact(t==null?void 0:t.style);this.vibrateWithPattern(e)}async notification(t){const e=this.patternForNotification(t==null?void 0:t.type);this.vibrateWithPattern(e)}async vibrate(t){const e=(t==null?void 0:t.duration)||300;this.vibrateWithPattern([e])}async selectionStart(){this.selectionStarted=!0}async selectionChanged(){this.selectionStarted&&this.vibrateWithPattern([70])}async selectionEnd(){this.selectionStarted=!1}patternForImpact(t=i.Heavy){return t===i.Medium?[43]:t===i.Light?[20]:[61]}patternForNotification(t=r.Success){return t===r.Warning?[30,40,30,50,60]:t===r.Error?[27,45,50]:[35,65,21]}vibrateWithPattern(t){if(navigator.vibrate)navigator.vibrate(t);else throw this.unavailable("Browser does not support the vibrate API")}}export{u as HapticsWeb};
|
package/dist/index.html
CHANGED
|
@@ -22,7 +22,7 @@
|
|
|
22
22
|
<link rel="icon" type="image/png" sizes="16x16" href="/assets/icons/favicon-16x16.png" />
|
|
23
23
|
<link rel="apple-touch-icon" sizes="180x180" href="/assets/icons/apple-touch-icon.png" />
|
|
24
24
|
<title>Tide Commander</title>
|
|
25
|
-
<script type="module" crossorigin src="/assets/main-
|
|
25
|
+
<script type="module" crossorigin src="/assets/main-sZyyVmvr.js"></script>
|
|
26
26
|
<link rel="modulepreload" crossorigin href="/assets/modulepreload-polyfill-B5Qt9EMX.js">
|
|
27
27
|
<link rel="modulepreload" crossorigin href="/assets/vendor-react--Eh9ivFN.js">
|
|
28
28
|
<link rel="modulepreload" crossorigin href="/assets/vendor-three-Chj50gSY.js">
|
|
@@ -52,5 +52,22 @@ export const slackPlugin = {
|
|
|
52
52
|
if (!integrationCtx)
|
|
53
53
|
throw new Error('Slack not initialized');
|
|
54
54
|
await setConfigValues(config, integrationCtx.secrets);
|
|
55
|
+
// Auto-connect or disconnect based on new config state
|
|
56
|
+
const updated = loadConfig();
|
|
57
|
+
const botToken = integrationCtx.secrets.get('SLACK_BOT_TOKEN');
|
|
58
|
+
const appToken = integrationCtx.secrets.get('SLACK_APP_TOKEN');
|
|
59
|
+
if (updated.enabled && botToken && appToken) {
|
|
60
|
+
// Tokens + enabled: (re)connect
|
|
61
|
+
try {
|
|
62
|
+
await slackClient.reconnect();
|
|
63
|
+
}
|
|
64
|
+
catch {
|
|
65
|
+
// Error is already captured in config status by reconnect()
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
else if (!updated.enabled && slackClient.isConnected()) {
|
|
69
|
+
// Disabled: disconnect
|
|
70
|
+
await slackClient.disconnect();
|
|
71
|
+
}
|
|
55
72
|
},
|
|
56
73
|
};
|
|
@@ -235,6 +235,18 @@ export function waitForReply(params) {
|
|
|
235
235
|
replyWaiters.add(waiter);
|
|
236
236
|
});
|
|
237
237
|
}
|
|
238
|
+
// ─── Channel Management ───
|
|
239
|
+
export async function joinChannel(channel) {
|
|
240
|
+
if (!webClient)
|
|
241
|
+
throw new Error('Slack not connected');
|
|
242
|
+
const result = await webClient.conversations.join({ channel });
|
|
243
|
+
const ch = result.channel;
|
|
244
|
+
if (!ch)
|
|
245
|
+
throw new Error(`Failed to join channel ${channel}`);
|
|
246
|
+
// Update cache
|
|
247
|
+
channelNameCache.set(ch.id, ch.name);
|
|
248
|
+
return { id: ch.id, name: ch.name };
|
|
249
|
+
}
|
|
238
250
|
// ─── Lookup ───
|
|
239
251
|
export async function listChannels() {
|
|
240
252
|
if (!webClient)
|
|
@@ -332,6 +344,57 @@ export async function findUserByName(displayName) {
|
|
|
332
344
|
}
|
|
333
345
|
return null;
|
|
334
346
|
}
|
|
347
|
+
// ─── Direct Messages ───
|
|
348
|
+
export async function openDmChannel(userId) {
|
|
349
|
+
if (!webClient)
|
|
350
|
+
throw new Error('Slack not connected');
|
|
351
|
+
const result = await webClient.conversations.open({ users: userId });
|
|
352
|
+
const channelId = result.channel?.id;
|
|
353
|
+
if (!channelId)
|
|
354
|
+
throw new Error(`Failed to open DM channel with user ${userId}`);
|
|
355
|
+
return channelId;
|
|
356
|
+
}
|
|
357
|
+
export async function sendDm(params) {
|
|
358
|
+
const dmChannel = await openDmChannel(params.userId);
|
|
359
|
+
return sendMessage({
|
|
360
|
+
channel: dmChannel,
|
|
361
|
+
text: params.text,
|
|
362
|
+
agentId: params.agentId,
|
|
363
|
+
workflowInstanceId: params.workflowInstanceId,
|
|
364
|
+
});
|
|
365
|
+
}
|
|
366
|
+
export async function searchUsers(query) {
|
|
367
|
+
if (!webClient)
|
|
368
|
+
throw new Error('Slack not connected');
|
|
369
|
+
const result = await webClient.users.list({ limit: 500 });
|
|
370
|
+
const lower = query.toLowerCase();
|
|
371
|
+
const matches = [];
|
|
372
|
+
for (const u of result.members || []) {
|
|
373
|
+
if (u.deleted || u.is_bot)
|
|
374
|
+
continue;
|
|
375
|
+
const profile = u.profile;
|
|
376
|
+
const name = u.name || '';
|
|
377
|
+
const realName = u.real_name || '';
|
|
378
|
+
const displayName = profile?.display_name || '';
|
|
379
|
+
const email = profile?.email || '';
|
|
380
|
+
if (name.toLowerCase().includes(lower) ||
|
|
381
|
+
realName.toLowerCase().includes(lower) ||
|
|
382
|
+
displayName.toLowerCase().includes(lower) ||
|
|
383
|
+
email.toLowerCase().includes(lower)) {
|
|
384
|
+
const user = {
|
|
385
|
+
id: u.id,
|
|
386
|
+
name,
|
|
387
|
+
realName,
|
|
388
|
+
displayName: displayName || name,
|
|
389
|
+
email,
|
|
390
|
+
isBot: false,
|
|
391
|
+
};
|
|
392
|
+
userCache.set(user.id, user);
|
|
393
|
+
matches.push(user);
|
|
394
|
+
}
|
|
395
|
+
}
|
|
396
|
+
return matches;
|
|
397
|
+
}
|
|
335
398
|
// ─── Event Subscription (for triggers) ───
|
|
336
399
|
export function onMessage(callback) {
|
|
337
400
|
messageListeners.add(callback);
|
|
@@ -62,7 +62,7 @@ export const slackConfigSchema = [
|
|
|
62
62
|
key: 'SLACK_BOT_TOKEN',
|
|
63
63
|
label: 'Bot Token',
|
|
64
64
|
type: 'password',
|
|
65
|
-
description: 'Slack Bot User OAuth Token (xoxb-...)',
|
|
65
|
+
description: 'Slack Bot User OAuth Token (xoxb-...). Found in the "OAuth & Permissions" section of your Slack app. Required scopes: channels:history, channels:read, chat:write, groups:history, groups:read, im:history, im:read, users:read.',
|
|
66
66
|
placeholder: 'xoxb-...',
|
|
67
67
|
required: true,
|
|
68
68
|
secret: true,
|
|
@@ -100,7 +100,39 @@ router.get('/channels', async (_req, res) => {
|
|
|
100
100
|
res.status(500).json({ error: `Failed to list channels: ${err instanceof Error ? err.message : err}` });
|
|
101
101
|
}
|
|
102
102
|
});
|
|
103
|
-
//
|
|
103
|
+
// POST /api/slack/channels/join — Join a channel
|
|
104
|
+
router.post('/channels/join', async (req, res) => {
|
|
105
|
+
try {
|
|
106
|
+
const { channel } = req.body;
|
|
107
|
+
if (!channel) {
|
|
108
|
+
res.status(400).json({ error: 'channel is required' });
|
|
109
|
+
return;
|
|
110
|
+
}
|
|
111
|
+
const result = await slackClient.joinChannel(channel);
|
|
112
|
+
res.json({ success: true, channel: result });
|
|
113
|
+
}
|
|
114
|
+
catch (err) {
|
|
115
|
+
log.error(`Slack join channel error: ${err}`);
|
|
116
|
+
res.status(500).json({ error: `Failed to join channel: ${err instanceof Error ? err.message : err}` });
|
|
117
|
+
}
|
|
118
|
+
});
|
|
119
|
+
// GET /api/slack/users/search?q=... — Search users by name or email
|
|
120
|
+
router.get('/users/search', async (req, res) => {
|
|
121
|
+
try {
|
|
122
|
+
const query = req.query.q;
|
|
123
|
+
if (!query) {
|
|
124
|
+
res.status(400).json({ error: 'q query param is required' });
|
|
125
|
+
return;
|
|
126
|
+
}
|
|
127
|
+
const users = await slackClient.searchUsers(query);
|
|
128
|
+
res.json({ users });
|
|
129
|
+
}
|
|
130
|
+
catch (err) {
|
|
131
|
+
log.error(`Slack user search error: ${err}`);
|
|
132
|
+
res.status(500).json({ error: `Failed to search users: ${err instanceof Error ? err.message : err}` });
|
|
133
|
+
}
|
|
134
|
+
});
|
|
135
|
+
// GET /api/slack/users/:userId — Resolve a user by ID
|
|
104
136
|
router.get('/users/:userId', async (req, res) => {
|
|
105
137
|
try {
|
|
106
138
|
const user = await slackClient.resolveUser(req.params.userId);
|
|
@@ -111,6 +143,22 @@ router.get('/users/:userId', async (req, res) => {
|
|
|
111
143
|
res.status(500).json({ error: `Failed to resolve user: ${err instanceof Error ? err.message : err}` });
|
|
112
144
|
}
|
|
113
145
|
});
|
|
146
|
+
// POST /api/slack/dm — Send a direct message to a user
|
|
147
|
+
router.post('/dm', async (req, res) => {
|
|
148
|
+
try {
|
|
149
|
+
const { userId, text, agentId, workflowInstanceId } = req.body;
|
|
150
|
+
if (!userId || !text) {
|
|
151
|
+
res.status(400).json({ error: 'userId and text are required' });
|
|
152
|
+
return;
|
|
153
|
+
}
|
|
154
|
+
const result = await slackClient.sendDm({ userId, text, agentId, workflowInstanceId });
|
|
155
|
+
res.json({ success: true, ts: result.ts, channel: result.channel });
|
|
156
|
+
}
|
|
157
|
+
catch (err) {
|
|
158
|
+
log.error(`Slack DM error: ${err}`);
|
|
159
|
+
res.status(500).json({ error: `Failed to send DM: ${err instanceof Error ? err.message : err}` });
|
|
160
|
+
}
|
|
161
|
+
});
|
|
114
162
|
// GET /api/slack/status — Get connection status
|
|
115
163
|
router.get('/status', (_req, res) => {
|
|
116
164
|
const config = loadConfig();
|
|
@@ -23,6 +23,27 @@ To reply in a thread, add \`"threadTs":"THREAD_TIMESTAMP"\`.
|
|
|
23
23
|
|
|
24
24
|
To track which agent sent the message, add \`"agentId":"YOUR_AGENT_ID"\`.
|
|
25
25
|
|
|
26
|
+
## Send a Direct Message
|
|
27
|
+
|
|
28
|
+
\`\`\`bash
|
|
29
|
+
curl -s -X POST http://localhost:5174/api/slack/dm \\
|
|
30
|
+
-H "Content-Type: application/json" \\
|
|
31
|
+
-d '{"userId":"U0123456789","text":"Your message here"}'
|
|
32
|
+
\`\`\`
|
|
33
|
+
|
|
34
|
+
Required: \`userId\` (Slack user ID), \`text\`.
|
|
35
|
+
Optional: \`agentId\`, \`workflowInstanceId\`.
|
|
36
|
+
|
|
37
|
+
Use the search endpoint below to find a user's ID by name or email.
|
|
38
|
+
|
|
39
|
+
## Search Users
|
|
40
|
+
|
|
41
|
+
\`\`\`bash
|
|
42
|
+
curl -s "http://localhost:5174/api/slack/users/search?q=john"
|
|
43
|
+
\`\`\`
|
|
44
|
+
|
|
45
|
+
Searches by name, display name, real name, or email. Returns matching users with their IDs.
|
|
46
|
+
|
|
26
47
|
## Read Channel Messages
|
|
27
48
|
|
|
28
49
|
\`\`\`bash
|
|
@@ -52,6 +73,16 @@ Optional body fields:
|
|
|
52
73
|
- \`messagePattern\`: Regex to match reply text
|
|
53
74
|
- \`timeoutMs\`: Timeout in ms (default 300000 = 5 min)
|
|
54
75
|
|
|
76
|
+
## Join a Channel
|
|
77
|
+
|
|
78
|
+
\`\`\`bash
|
|
79
|
+
curl -s -X POST http://localhost:5174/api/slack/channels/join \\
|
|
80
|
+
-H "Content-Type: application/json" \\
|
|
81
|
+
-d '{"channel":"CHANNEL_ID"}'
|
|
82
|
+
\`\`\`
|
|
83
|
+
|
|
84
|
+
The bot must join a channel before it can read messages or post in it. Use the list endpoint to find channel IDs.
|
|
85
|
+
|
|
55
86
|
## List Channels
|
|
56
87
|
|
|
57
88
|
\`\`\`bash
|