tide-commander 1.65.0 → 1.66.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-U4mcLIP3.js → BossLogsModal-D6nnEtFf.js} +1 -1
- package/dist/assets/{BossSpawnModal-CiFvgaQ1.js → BossSpawnModal-DmPw2xym.js} +1 -1
- package/dist/assets/{ControlsModal-Bz4EQ0ii.js → ControlsModal-OEZddGmt.js} +1 -1
- package/dist/assets/{DockerLogsModal-CSMh4uRZ.js → DockerLogsModal-CzpJKdxd.js} +1 -1
- package/dist/assets/{EmbeddedEditor-ecjZKKkD.js → EmbeddedEditor-Dh9neVWW.js} +1 -1
- package/dist/assets/{GmailOAuthSetup-pFwSGVxa.js → GmailOAuthSetup-FtYHQDDw.js} +1 -1
- package/dist/assets/{GoogleOAuthSetup-CPvUA9JE.js → GoogleOAuthSetup-reYSDcFV.js} +1 -1
- package/dist/assets/{IframeModal-DcTPyjLy.js → IframeModal-DbW9WCx6.js} +1 -1
- package/dist/assets/{IntegrationsPanel-DHdp8HO-.js → IntegrationsPanel-DooKtzpO.js} +2 -2
- package/dist/assets/{LogViewerModal-QWGgGmiP.js → LogViewerModal-BamqTQzc.js} +1 -1
- package/dist/assets/{MonitoringModal-C8cfJlg5.js → MonitoringModal-DiOT_xpJ.js} +1 -1
- package/dist/assets/{PM2LogsModal-uUu_bTeC.js → PM2LogsModal-C4hIfwGk.js} +1 -1
- package/dist/assets/{RestoreArchivedAreaModal-C-Zxn6n6.js → RestoreArchivedAreaModal-CQ3NdSvn.js} +1 -1
- package/dist/assets/{Scene2DCanvas-0uRu2G4V.js → Scene2DCanvas-BfcwE6aA.js} +1 -1
- package/dist/assets/{SceneManager-xyitf6BI.js → SceneManager-D409BuL6.js} +1 -1
- package/dist/assets/{SkillsPanel-Bgcx1OI3.js → SkillsPanel-B0V-BFfO.js} +1 -1
- package/dist/assets/{SpawnModal-CS3BMuY9.js → SpawnModal-gPT83rW4.js} +1 -1
- package/dist/assets/{SubordinateAssignmentModal-DNWrbv9p.js → SubordinateAssignmentModal-Dvvqv0yZ.js} +1 -1
- package/dist/assets/{TriggerManagerPanel-D-pLDqne.js → TriggerManagerPanel-BmDBe8xx.js} +1 -1
- package/dist/assets/{WorkflowEditorPanel-Cu7zjFdE.js → WorkflowEditorPanel-PS4W8Q3F.js} +1 -1
- package/dist/assets/{index-cGfzMto2.js → index-BSdgxlrR.js} +1 -1
- package/dist/assets/{index-2BVW4z0_.js → index-BkpkUL3C.js} +1 -1
- package/dist/assets/{index-CZl-6UH7.js → index-C7cIg4BE.js} +1 -1
- package/dist/assets/{index-DEfCPZTr.js → index-CJYuOBJD.js} +3 -3
- package/dist/assets/{index-Yy2LrlI7.js → index-D9NmRir8.js} +2 -2
- package/dist/assets/{index-Dd0cfrn9.js → index-DvbZsLxj.js} +1 -1
- package/dist/assets/index-baPDjRvq.js +1 -0
- package/dist/assets/{index-DM70jqOd.js → index-cCrsvvfk.js} +1 -1
- package/dist/assets/main-B3L4mgQ4.css +1 -0
- package/dist/assets/{main-BNhrjJHj.js → main-BzSUj-VM.js} +5 -5
- package/dist/assets/{web-BnX_BsjB.js → web-DffxvD3t.js} +1 -1
- package/dist/assets/{web-CyBgxhK2.js → web-y3e1lmyW.js} +1 -1
- package/dist/index.html +2 -2
- package/dist/src/packages/server/data/builtin-skills/backup-restore.js +242 -0
- package/dist/src/packages/server/data/builtin-skills/index.js +2 -0
- package/dist/src/packages/server/index.js +4 -0
- package/dist/src/packages/server/routes/agents.js +27 -0
- package/dist/src/packages/server/services/backup-service.js +148 -0
- package/package.json +2 -1
- package/scripts/backup-data.sh +116 -0
- package/scripts/krunner/install-krunner-integration.sh +53 -0
- package/scripts/krunner/org.riven.tide.krunner.service +3 -0
- package/scripts/krunner/plasma-runner-tide-commander.desktop +16 -0
- package/scripts/krunner/tide-krunner-runner.js +335 -0
- package/scripts/recover-agents.ts +623 -0
- package/dist/assets/index-CWyxpmuL.js +0 -1
- package/dist/assets/main-4iQEaD98.css +0 -1
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
set -euo pipefail
|
|
3
|
+
|
|
4
|
+
REPO_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)"
|
|
5
|
+
|
|
6
|
+
RUNNER_SRC="${REPO_ROOT}/scripts/krunner/tide-krunner-runner.js"
|
|
7
|
+
SERVICE_SRC="${REPO_ROOT}/scripts/krunner/org.riven.tide.krunner.service"
|
|
8
|
+
DESKTOP_SRC="${REPO_ROOT}/scripts/krunner/plasma-runner-tide-commander.desktop"
|
|
9
|
+
|
|
10
|
+
BIN_DIR="${HOME}/.local/bin"
|
|
11
|
+
DBUS_DIR="${HOME}/.local/share/dbus-1/services"
|
|
12
|
+
KRUNNER_DIR="${HOME}/.local/share/krunner/dbusplugins"
|
|
13
|
+
|
|
14
|
+
RUNNER_DST="${BIN_DIR}/tide-krunner-runner"
|
|
15
|
+
SERVICE_DST="${DBUS_DIR}/org.riven.tide.krunner.service"
|
|
16
|
+
DESKTOP_DST="${KRUNNER_DIR}/plasma-runner-tide-commander.desktop"
|
|
17
|
+
|
|
18
|
+
mkdir -p "${BIN_DIR}" "${DBUS_DIR}" "${KRUNNER_DIR}"
|
|
19
|
+
|
|
20
|
+
install -m 0755 "${RUNNER_SRC}" "${RUNNER_DST}"
|
|
21
|
+
install -m 0644 "${SERVICE_SRC}" "${SERVICE_DST}"
|
|
22
|
+
install -m 0644 "${DESKTOP_SRC}" "${DESKTOP_DST}"
|
|
23
|
+
|
|
24
|
+
# Ensure ${HOME} in Exec is expanded when installed.
|
|
25
|
+
sed -i "s#\\\${HOME}#${HOME}#g" "${SERVICE_DST}"
|
|
26
|
+
|
|
27
|
+
echo "Installed:"
|
|
28
|
+
echo " ${RUNNER_DST}"
|
|
29
|
+
echo " ${SERVICE_DST}"
|
|
30
|
+
echo " ${DESKTOP_DST}"
|
|
31
|
+
|
|
32
|
+
echo "Reloading DBus user config..."
|
|
33
|
+
dbus-send --session --dest=org.freedesktop.DBus --type=method_call --print-reply \
|
|
34
|
+
/org/freedesktop/DBus org.freedesktop.DBus.ReloadConfig >/dev/null 2>&1 || true
|
|
35
|
+
|
|
36
|
+
echo "Stopping existing Tide runner process..."
|
|
37
|
+
if pgrep -f "${RUNNER_DST}" >/dev/null 2>&1; then
|
|
38
|
+
pgrep -f "${RUNNER_DST}" | xargs -r kill
|
|
39
|
+
sleep 0.2
|
|
40
|
+
fi
|
|
41
|
+
|
|
42
|
+
echo "Restarting KRunner..."
|
|
43
|
+
kquitapp6 krunner >/dev/null 2>&1 || true
|
|
44
|
+
nohup krunner >/dev/null 2>&1 &
|
|
45
|
+
sleep 1
|
|
46
|
+
|
|
47
|
+
echo
|
|
48
|
+
echo "Done. Test with:"
|
|
49
|
+
echo " 1) Open KRunner (Alt+Space)"
|
|
50
|
+
echo " 2) Type: tc <agent or area name>"
|
|
51
|
+
echo
|
|
52
|
+
echo "Direct API check:"
|
|
53
|
+
echo " curl -s -X POST http://localhost:5174/api/focus-agent -H 'Content-Type: application/json' -d '{\"agentId\":\"g3d1jvlr\",\"openTerminal\":true}'"
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
[Desktop Entry]
|
|
2
|
+
Name=Tide Commander
|
|
3
|
+
Comment=Find and focus Tide agents and areas
|
|
4
|
+
Type=Service
|
|
5
|
+
Icon=utilities-terminal
|
|
6
|
+
X-KDE-ServiceTypes=Plasma/Runner
|
|
7
|
+
X-KDE-PluginInfo-Name=tide-commander
|
|
8
|
+
X-KDE-PluginInfo-Version=1.0
|
|
9
|
+
X-KDE-PluginInfo-License=MIT
|
|
10
|
+
X-KDE-PluginInfo-EnabledByDefault=true
|
|
11
|
+
X-Plasma-API=DBus
|
|
12
|
+
X-Plasma-DBusRunner-Service=org.riven.tide.krunner
|
|
13
|
+
X-Plasma-DBusRunner-Path=/runner
|
|
14
|
+
X-Plasma-Request-Actions-Once=true
|
|
15
|
+
X-Plasma-Runner-Syntaxes=tc :q:,tide :q:
|
|
16
|
+
X-Plasma-Runner-Syntax-Descriptions=Find Tide agents and areas by name,Find Tide agents and areas by name
|
|
@@ -0,0 +1,335 @@
|
|
|
1
|
+
#!/usr/bin/env gjs
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Tide Commander KRunner DBus Runner
|
|
5
|
+
*
|
|
6
|
+
* Query examples:
|
|
7
|
+
* tc gus
|
|
8
|
+
* tide area backend
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
imports.gi.versions.Gio = '2.0';
|
|
12
|
+
imports.gi.versions.GLib = '2.0';
|
|
13
|
+
|
|
14
|
+
const { Gio, GLib } = imports.gi;
|
|
15
|
+
|
|
16
|
+
const BUS_NAME = 'org.riven.tide.krunner';
|
|
17
|
+
const OBJECT_PATH = '/runner';
|
|
18
|
+
const API_BASE = GLib.getenv('TIDE_COMMANDER_API') || 'http://localhost:5174/api';
|
|
19
|
+
const APP_CAPTION = GLib.getenv('TIDE_COMMANDER_WINDOW_CAPTION') || 'Tide Commander';
|
|
20
|
+
const APP_DESKTOP_FILE = GLib.getenv('TIDE_COMMANDER_DESKTOP_FILE') || 'brave-idemibpphagihbobmgmaojhjfidlfpdl-Default';
|
|
21
|
+
const APP_KWIN_SHORTCUT = GLib.getenv('TIDE_COMMANDER_KWIN_SHORTCUT') || 'AppToggler25TideCommander';
|
|
22
|
+
let lastActivationToken = '';
|
|
23
|
+
|
|
24
|
+
const IFACE_XML = `
|
|
25
|
+
<node>
|
|
26
|
+
<interface name="org.kde.krunner1">
|
|
27
|
+
<method name="Config">
|
|
28
|
+
<arg name="config" type="a{sv}" direction="out"/>
|
|
29
|
+
</method>
|
|
30
|
+
<method name="Actions">
|
|
31
|
+
<arg name="actions" type="a(sss)" direction="out"/>
|
|
32
|
+
</method>
|
|
33
|
+
<method name="SetActivationToken">
|
|
34
|
+
<arg name="token" type="s" direction="in"/>
|
|
35
|
+
</method>
|
|
36
|
+
<method name="Run">
|
|
37
|
+
<arg name="matchId" type="s" direction="in"/>
|
|
38
|
+
<arg name="actionId" type="s" direction="in"/>
|
|
39
|
+
</method>
|
|
40
|
+
<method name="Match">
|
|
41
|
+
<arg name="query" type="s" direction="in"/>
|
|
42
|
+
<arg name="matches" type="a(sssida{sv})" direction="out"/>
|
|
43
|
+
</method>
|
|
44
|
+
</interface>
|
|
45
|
+
</node>`;
|
|
46
|
+
|
|
47
|
+
function shellEscapeSingleQuotes(str) {
|
|
48
|
+
return str.replace(/'/g, `'\\''`);
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
function runShell(command) {
|
|
52
|
+
try {
|
|
53
|
+
const [ok, stdout, stderr] = GLib.spawn_command_line_sync(command);
|
|
54
|
+
const out = (stdout ? ByteArray.toString(stdout) : '').trim();
|
|
55
|
+
const err = (stderr ? ByteArray.toString(stderr) : '').trim();
|
|
56
|
+
return { ok, out, err };
|
|
57
|
+
} catch (_err) {
|
|
58
|
+
return { ok: false, out: '', err: 'spawn failed' };
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
function fetchJson(url) {
|
|
63
|
+
const cmd = `curl -s --max-time 1 '${shellEscapeSingleQuotes(url)}'`;
|
|
64
|
+
const { out } = runShell(cmd);
|
|
65
|
+
if (!out) return null;
|
|
66
|
+
try {
|
|
67
|
+
return JSON.parse(out);
|
|
68
|
+
} catch (_err) {
|
|
69
|
+
return null;
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
function normalize(text) {
|
|
74
|
+
return (text || '').toString().toLowerCase().trim();
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
function contains(haystack, needle) {
|
|
78
|
+
return normalize(haystack).includes(normalize(needle));
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
function splitQuery(query) {
|
|
82
|
+
const trimmed = normalize(query);
|
|
83
|
+
if (!trimmed) return { isTrigger: true, term: '' };
|
|
84
|
+
|
|
85
|
+
if (trimmed === 'tc' || trimmed === 'tide') {
|
|
86
|
+
return { isTrigger: true, term: '' };
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
if (trimmed.startsWith('tc ')) {
|
|
90
|
+
return { isTrigger: true, term: trimmed.slice(3).trim() };
|
|
91
|
+
}
|
|
92
|
+
if (trimmed.startsWith('tide ')) {
|
|
93
|
+
return { isTrigger: true, term: trimmed.slice(5).trim() };
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
// In many KRunner setups, Match receives only the text after trigger words.
|
|
97
|
+
// Accept plain text as a valid term so autocomplete works reliably.
|
|
98
|
+
return { isTrigger: true, term: trimmed };
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
function scoreText(text, term) {
|
|
102
|
+
const t = normalize(text);
|
|
103
|
+
const q = normalize(term);
|
|
104
|
+
if (!q) return 0.7;
|
|
105
|
+
if (t === q) return 1.0;
|
|
106
|
+
if (t.startsWith(q)) return 0.95;
|
|
107
|
+
if (t.includes(q)) return 0.85;
|
|
108
|
+
return 0.0;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
function isActiveStatus(status) {
|
|
112
|
+
const normalized = normalize(status);
|
|
113
|
+
return normalized === 'working' ||
|
|
114
|
+
normalized === 'waiting' ||
|
|
115
|
+
normalized === 'waiting_permission' ||
|
|
116
|
+
normalized === 'orphaned';
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
function asVariantDict(props) {
|
|
120
|
+
const dict = {};
|
|
121
|
+
for (const [k, v] of Object.entries(props)) {
|
|
122
|
+
if (Array.isArray(v)) {
|
|
123
|
+
dict[k] = GLib.Variant.new_strv(v);
|
|
124
|
+
} else if (typeof v === 'string') {
|
|
125
|
+
dict[k] = new GLib.Variant('s', v);
|
|
126
|
+
} else if (typeof v === 'boolean') {
|
|
127
|
+
dict[k] = new GLib.Variant('b', v);
|
|
128
|
+
} else if (typeof v === 'number') {
|
|
129
|
+
dict[k] = new GLib.Variant('d', v);
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
return dict;
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
function makeMatch(id, text, subtext, relevance) {
|
|
136
|
+
return [
|
|
137
|
+
id,
|
|
138
|
+
text,
|
|
139
|
+
'terminal',
|
|
140
|
+
100,
|
|
141
|
+
relevance,
|
|
142
|
+
asVariantDict({
|
|
143
|
+
subtext,
|
|
144
|
+
category: 'Tide Commander',
|
|
145
|
+
actions: [],
|
|
146
|
+
}),
|
|
147
|
+
];
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
function areaMapFromAreas(areas) {
|
|
151
|
+
const map = new Map();
|
|
152
|
+
for (const area of areas) {
|
|
153
|
+
if (!area || !Array.isArray(area.assignedAgentIds)) continue;
|
|
154
|
+
for (const agentId of area.assignedAgentIds) {
|
|
155
|
+
if (!map.has(agentId)) {
|
|
156
|
+
map.set(agentId, area.name || 'No area');
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
return map;
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
function focusTideWindow() {
|
|
164
|
+
// Invoke the persistent AppToggler KWin shortcut (from kde-toggle-windows-shortcuts).
|
|
165
|
+
// Ephemeral KWin scripts loaded via loadScript/start/unloadScript don't get
|
|
166
|
+
// proper focus privileges on Wayland. The AppToggler runs inside KWin as a
|
|
167
|
+
// registered plugin, so workspace.activeWindow = client actually works.
|
|
168
|
+
runShell(
|
|
169
|
+
`qdbus org.kde.kglobalaccel /component/kwin org.kde.kglobalaccel.Component.invokeShortcut '${shellEscapeSingleQuotes(APP_KWIN_SHORTCUT)}' >/dev/null 2>&1`
|
|
170
|
+
);
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
function focusAgent(agentId) {
|
|
174
|
+
const payload = JSON.stringify({ agentId, openTerminal: true }).replace(/'/g, `'\\''`);
|
|
175
|
+
const activationHeader = lastActivationToken
|
|
176
|
+
? ` -H 'X-KDE-ActivationToken: ${shellEscapeSingleQuotes(lastActivationToken)}'`
|
|
177
|
+
: '';
|
|
178
|
+
runShell(
|
|
179
|
+
`curl -s -X POST '${API_BASE}/focus-agent' -H 'Content-Type: application/json'${activationHeader} -d '${payload}' >/dev/null`
|
|
180
|
+
);
|
|
181
|
+
// Retry focus a few times (Wayland can delay activation).
|
|
182
|
+
focusTideWindow();
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
class TideRunner {
|
|
186
|
+
Config() {
|
|
187
|
+
return {
|
|
188
|
+
TriggerWords: new GLib.Variant('as', ['tc', 'tide']),
|
|
189
|
+
MinLetterCount: new GLib.Variant('i', 0),
|
|
190
|
+
};
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
Actions() {
|
|
194
|
+
return [];
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
SetActivationToken(token) {
|
|
198
|
+
lastActivationToken = String(token || '');
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
Run(matchId, _actionId) {
|
|
202
|
+
if (matchId.startsWith('agent:')) {
|
|
203
|
+
focusAgent(matchId.slice(6));
|
|
204
|
+
return;
|
|
205
|
+
}
|
|
206
|
+
if (matchId.startsWith('area:')) {
|
|
207
|
+
const parts = matchId.slice(5).split(':');
|
|
208
|
+
const fallbackAgentId = parts[1] || '';
|
|
209
|
+
if (fallbackAgentId) {
|
|
210
|
+
focusAgent(fallbackAgentId);
|
|
211
|
+
} else {
|
|
212
|
+
focusTideWindow();
|
|
213
|
+
}
|
|
214
|
+
return;
|
|
215
|
+
}
|
|
216
|
+
focusTideWindow();
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
Match(query) {
|
|
220
|
+
const parsed = splitQuery(query);
|
|
221
|
+
if (!parsed.isTrigger) return [];
|
|
222
|
+
|
|
223
|
+
const agents = fetchJson(`${API_BASE}/agents`) || [];
|
|
224
|
+
const areas = fetchJson(`${API_BASE}/areas`) || [];
|
|
225
|
+
|
|
226
|
+
const term = parsed.term;
|
|
227
|
+
const matches = [];
|
|
228
|
+
|
|
229
|
+
const agentsById = new Map();
|
|
230
|
+
for (const agent of agents) {
|
|
231
|
+
agentsById.set(agent.id, agent);
|
|
232
|
+
}
|
|
233
|
+
const areaByAgentId = areaMapFromAreas(areas);
|
|
234
|
+
|
|
235
|
+
const agentCandidates = [];
|
|
236
|
+
const areaCandidates = [];
|
|
237
|
+
|
|
238
|
+
for (const agent of agents) {
|
|
239
|
+
if (
|
|
240
|
+
term &&
|
|
241
|
+
!contains(agent.name, term) &&
|
|
242
|
+
!contains(agent.class, term) &&
|
|
243
|
+
!contains(areaByAgentId.get(agent.id) || '', term) &&
|
|
244
|
+
!contains(agent.cwd, term) &&
|
|
245
|
+
!contains(agent.status, term)
|
|
246
|
+
) {
|
|
247
|
+
continue;
|
|
248
|
+
}
|
|
249
|
+
const relevance = Math.max(
|
|
250
|
+
scoreText(agent.name, term),
|
|
251
|
+
scoreText(agent.class, term),
|
|
252
|
+
scoreText(areaByAgentId.get(agent.id) || '', term),
|
|
253
|
+
scoreText(agent.cwd, term),
|
|
254
|
+
scoreText(agent.status, term),
|
|
255
|
+
term ? 0.6 : 0.7
|
|
256
|
+
);
|
|
257
|
+
const lastActivity = Number(agent.lastActivity || 0);
|
|
258
|
+
const activeRank = isActiveStatus(agent.status) ? 1 : 0;
|
|
259
|
+
const areaName = areaByAgentId.get(agent.id) || 'No area';
|
|
260
|
+
|
|
261
|
+
agentCandidates.push({
|
|
262
|
+
activeRank,
|
|
263
|
+
lastActivity,
|
|
264
|
+
relevance,
|
|
265
|
+
match: makeMatch(
|
|
266
|
+
`agent:${agent.id}`,
|
|
267
|
+
`${agent.name} [${areaName}]`,
|
|
268
|
+
`${agent.class} • ${agent.status} • Area: ${areaName}`,
|
|
269
|
+
relevance
|
|
270
|
+
),
|
|
271
|
+
});
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
for (const area of areas) {
|
|
275
|
+
if (!area || !area.name) continue;
|
|
276
|
+
if (term && !contains(area.name, term) && !contains('area', term)) continue;
|
|
277
|
+
|
|
278
|
+
const assigned = Array.isArray(area.assignedAgentIds) ? area.assignedAgentIds : [];
|
|
279
|
+
const fallbackAgentId = assigned.find((id) => agentsById.has(id)) || '';
|
|
280
|
+
const previewAgent = fallbackAgentId ? agentsById.get(fallbackAgentId).name : 'no agents';
|
|
281
|
+
|
|
282
|
+
const relevance = Math.max(scoreText(area.name, term), term ? 0.55 : 0.65);
|
|
283
|
+
areaCandidates.push({
|
|
284
|
+
relevance,
|
|
285
|
+
match: makeMatch(
|
|
286
|
+
`area:${area.id}:${fallbackAgentId}`,
|
|
287
|
+
`Area: ${area.name}`,
|
|
288
|
+
`Open ${previewAgent}`,
|
|
289
|
+
relevance
|
|
290
|
+
),
|
|
291
|
+
});
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
agentCandidates.sort((a, b) => {
|
|
295
|
+
if (b.activeRank !== a.activeRank) return b.activeRank - a.activeRank;
|
|
296
|
+
if (b.lastActivity !== a.lastActivity) return b.lastActivity - a.lastActivity;
|
|
297
|
+
return b.relevance - a.relevance;
|
|
298
|
+
});
|
|
299
|
+
areaCandidates.sort((a, b) => b.relevance - a.relevance);
|
|
300
|
+
|
|
301
|
+
for (const candidate of agentCandidates) matches.push(candidate.match);
|
|
302
|
+
for (const candidate of areaCandidates) matches.push(candidate.match);
|
|
303
|
+
|
|
304
|
+
if (matches.length === 0) {
|
|
305
|
+
matches.push(
|
|
306
|
+
makeMatch(
|
|
307
|
+
'tide:open',
|
|
308
|
+
'Tide Commander',
|
|
309
|
+
'No agent or area matched',
|
|
310
|
+
0.6
|
|
311
|
+
)
|
|
312
|
+
);
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
return matches.slice(0, 12);
|
|
316
|
+
}
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
const ByteArray = imports.byteArray;
|
|
320
|
+
const loop = new GLib.MainLoop(null, false);
|
|
321
|
+
const runner = new TideRunner();
|
|
322
|
+
const dbusObject = Gio.DBusExportedObject.wrapJSObject(IFACE_XML, runner);
|
|
323
|
+
|
|
324
|
+
const connection = Gio.bus_get_sync(Gio.BusType.SESSION, null);
|
|
325
|
+
dbusObject.export(connection, OBJECT_PATH);
|
|
326
|
+
|
|
327
|
+
Gio.bus_own_name_on_connection(
|
|
328
|
+
connection,
|
|
329
|
+
BUS_NAME,
|
|
330
|
+
Gio.BusNameOwnerFlags.NONE,
|
|
331
|
+
null,
|
|
332
|
+
null
|
|
333
|
+
);
|
|
334
|
+
|
|
335
|
+
loop.run();
|