tide-commander 1.35.0 → 1.36.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-B_dgVF7L.js → BossLogsModal-B6meUM1T.js} +1 -1
- package/dist/assets/BossSpawnModal-DzWs1NNZ.js +1 -0
- package/dist/assets/{ControlsModal-WMTTqbca.js → ControlsModal-D2QDZ20h.js} +1 -1
- package/dist/assets/{DockerLogsModal-THzhLHch.js → DockerLogsModal-Bh1ghgFV.js} +1 -1
- package/dist/assets/{EmbeddedEditor-DWLKJYav.js → EmbeddedEditor-C4rKc9Ys.js} +1 -1
- package/dist/assets/{GmailOAuthSetup-DN9ceaS6.js → GmailOAuthSetup-E5_WFtyX.js} +1 -1
- package/dist/assets/{GoogleOAuthSetup-bVST2EOB.js → GoogleOAuthSetup-B7NLxSSp.js} +1 -1
- package/dist/assets/{IframeModal-BELsjvgi.js → IframeModal-CGbINL-o.js} +1 -1
- package/dist/assets/{IntegrationsPanel-DwDr4BRt.js → IntegrationsPanel-DZmbjiXV.js} +2 -2
- package/dist/assets/{LogViewerModal-CMe04PO5.js → LogViewerModal-KI2L074n.js} +1 -1
- package/dist/assets/{MonitoringModal-CqSalNeY.js → MonitoringModal-CWqjvTra.js} +1 -1
- package/dist/assets/{PM2LogsModal-CCmCDxVt.js → PM2LogsModal-DRHybuUO.js} +1 -1
- package/dist/assets/{RestoreArchivedAreaModal-IfzPidIv.js → RestoreArchivedAreaModal-DjpKSz7P.js} +1 -1
- package/dist/assets/{SaveSnapshotModal-DUhrVD5l.js → SaveSnapshotModal-CWtf3rgg.js} +1 -1
- package/dist/assets/Scene2DCanvas-CSfRDtXw.js +1 -0
- package/dist/assets/SceneManager-Do4NpOsy.js +104 -0
- package/dist/assets/{SkillsPanel-CPFOI4Tl.js → SkillsPanel-BqZSNfNW.js} +3 -3
- package/dist/assets/{SnapshotManager-Cbu0tJBz.js → SnapshotManager-DyqyIhWz.js} +1 -1
- package/dist/assets/SpawnModal-CGFj_b9I.js +1 -0
- package/dist/assets/SubordinateAssignmentModal-DGFoupVt.js +1 -0
- package/dist/assets/{SupervisorPanel-BvX-dlk_.js → SupervisorPanel-B1PVopnm.js} +1 -1
- package/dist/assets/{TriggerManagerPanel-RUVFmKmf.js → TriggerManagerPanel-CFez7_vm.js} +2 -2
- package/dist/assets/{WorkflowEditorPanel-CwZpEqzM.js → WorkflowEditorPanel-ClMl5U8g.js} +1 -1
- package/dist/assets/{index-DDPUtz8-.js → index-B4SUGrVB.js} +1 -1
- package/dist/assets/{index-CiD1Rwaq.js → index-BK3kI8Cb.js} +1 -1
- package/dist/assets/{index-BFguOWBW.js → index-Bf3KKyiV.js} +2 -2
- package/dist/assets/{index-D4nfDvz4.js → index-Bmu_CPsU.js} +3 -3
- package/dist/assets/index-CbzHGOPP.js +1 -0
- package/dist/assets/index-CpZt7iSV.js +1 -0
- package/dist/assets/{index-B-wV06cR.js → index-CxtzptPF.js} +5 -5
- package/dist/assets/{index-EH8IBvSU.js → index-D2_-BBP8.js} +3 -3
- package/dist/assets/main-C6uuUPJ0.css +1 -0
- package/dist/assets/main-CtGyoZXd.js +152 -0
- package/dist/assets/{web-D1vWYL8u.js → web-C2JUUvGB.js} +1 -1
- package/dist/assets/{web-DUq3Undh.js → web-D4EcUHSn.js} +1 -1
- package/dist/index.html +2 -2
- package/dist/src/packages/server/routes/agents.js +209 -1
- package/dist/src/packages/server/routes/areas.js +25 -0
- package/dist/src/packages/server/routes/index.js +2 -0
- package/dist/src/packages/server/routes/workspaces.js +123 -0
- package/dist/src/packages/server/services/agent-service.js +87 -1
- package/dist/src/packages/server/services/area-layout-service.js +260 -0
- package/dist/src/packages/server/services/workspace-service.js +104 -0
- package/package.json +1 -1
- package/dist/assets/BossSpawnModal-CSO1bYxA.js +0 -1
- package/dist/assets/Scene2DCanvas-Bl5DUC7w.js +0 -1
- package/dist/assets/SceneManager-BGO9tiaI.js +0 -104
- package/dist/assets/SpawnModal-BqDbsYLY.js +0 -1
- package/dist/assets/SubordinateAssignmentModal-DOqkhL_L.js +0 -1
- package/dist/assets/camera-D_KeL_pz.js +0 -1
- package/dist/assets/index-C7gqY2AA.js +0 -1
- package/dist/assets/index-H0PzHVFw.js +0 -1
- package/dist/assets/main-Cjm0d8dZ.js +0 -152
- package/dist/assets/main-DqC9_fF4.css +0 -1
|
@@ -0,0 +1,260 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Area Layout Service
|
|
3
|
+
* Organizes agents and buildings within areas using a hierarchy-aware grid layout
|
|
4
|
+
*/
|
|
5
|
+
import { loadAreas, loadBuildings, saveBuildings } from '../data/index.js';
|
|
6
|
+
import * as agentService from './agent-service.js';
|
|
7
|
+
import { createLogger } from '../utils/logger.js';
|
|
8
|
+
const log = createLogger('AreaLayout');
|
|
9
|
+
const PADDING = 1.5;
|
|
10
|
+
const PREFERRED_SPACING = 2.5;
|
|
11
|
+
const MIN_SPACING = 1.0;
|
|
12
|
+
/**
|
|
13
|
+
* Get usable bounds for an area (with padding), clamped to positive dimensions.
|
|
14
|
+
*/
|
|
15
|
+
function getAreaBounds(area) {
|
|
16
|
+
let bounds;
|
|
17
|
+
if (area.type === 'rectangle' && area.width && area.height) {
|
|
18
|
+
bounds = {
|
|
19
|
+
minX: area.center.x - area.width / 2 + PADDING,
|
|
20
|
+
maxX: area.center.x + area.width / 2 - PADDING,
|
|
21
|
+
minZ: area.center.z - area.height / 2 + PADDING,
|
|
22
|
+
maxZ: area.center.z + area.height / 2 - PADDING,
|
|
23
|
+
};
|
|
24
|
+
}
|
|
25
|
+
else if (area.type === 'circle' && area.radius) {
|
|
26
|
+
const r = Math.max(0, area.radius - PADDING);
|
|
27
|
+
bounds = {
|
|
28
|
+
minX: area.center.x - r,
|
|
29
|
+
maxX: area.center.x + r,
|
|
30
|
+
minZ: area.center.z - r,
|
|
31
|
+
maxZ: area.center.z + r,
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
else {
|
|
35
|
+
bounds = { minX: area.center.x, maxX: area.center.x, minZ: area.center.z, maxZ: area.center.z };
|
|
36
|
+
}
|
|
37
|
+
// Ensure non-negative dimensions
|
|
38
|
+
if (bounds.maxX < bounds.minX)
|
|
39
|
+
bounds.maxX = bounds.minX = area.center.x;
|
|
40
|
+
if (bounds.maxZ < bounds.minZ)
|
|
41
|
+
bounds.maxZ = bounds.minZ = area.center.z;
|
|
42
|
+
return bounds;
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* Check if a position is inside an area's bounds.
|
|
46
|
+
*/
|
|
47
|
+
function isPositionInArea(pos, area) {
|
|
48
|
+
if (area.type === 'rectangle' && area.width && area.height) {
|
|
49
|
+
const halfW = area.width / 2;
|
|
50
|
+
const halfH = area.height / 2;
|
|
51
|
+
return (pos.x >= area.center.x - halfW &&
|
|
52
|
+
pos.x <= area.center.x + halfW &&
|
|
53
|
+
pos.z >= area.center.z - halfH &&
|
|
54
|
+
pos.z <= area.center.z + halfH);
|
|
55
|
+
}
|
|
56
|
+
if (area.type === 'circle' && area.radius) {
|
|
57
|
+
const dx = pos.x - area.center.x;
|
|
58
|
+
const dz = pos.z - area.center.z;
|
|
59
|
+
return dx * dx + dz * dz <= area.radius * area.radius;
|
|
60
|
+
}
|
|
61
|
+
return false;
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* Clamp a value to [min, max].
|
|
65
|
+
*/
|
|
66
|
+
function clamp(value, min, max) {
|
|
67
|
+
return Math.max(min, Math.min(max, value));
|
|
68
|
+
}
|
|
69
|
+
/**
|
|
70
|
+
* Place a row of agents centered horizontally within bounds, clamped.
|
|
71
|
+
*/
|
|
72
|
+
function placeRow(agents, z, bounds) {
|
|
73
|
+
if (agents.length === 0)
|
|
74
|
+
return [];
|
|
75
|
+
const width = bounds.maxX - bounds.minX;
|
|
76
|
+
let spacing;
|
|
77
|
+
if (agents.length === 1) {
|
|
78
|
+
spacing = 0;
|
|
79
|
+
}
|
|
80
|
+
else {
|
|
81
|
+
// Calculate ideal spacing, shrink to fit if needed
|
|
82
|
+
spacing = Math.max(MIN_SPACING, Math.min(PREFERRED_SPACING, width / (agents.length - 1)));
|
|
83
|
+
}
|
|
84
|
+
const totalWidth = spacing * (agents.length - 1);
|
|
85
|
+
const startX = (bounds.minX + bounds.maxX) / 2 - totalWidth / 2;
|
|
86
|
+
const clampedZ = clamp(z, bounds.minZ, bounds.maxZ);
|
|
87
|
+
return agents.map((agent, i) => ({
|
|
88
|
+
agentId: agent.id,
|
|
89
|
+
position: {
|
|
90
|
+
x: clamp(startX + i * spacing, bounds.minX, bounds.maxX),
|
|
91
|
+
y: 0,
|
|
92
|
+
z: clampedZ,
|
|
93
|
+
},
|
|
94
|
+
}));
|
|
95
|
+
}
|
|
96
|
+
/**
|
|
97
|
+
* Find buildings that are physically inside an area.
|
|
98
|
+
*/
|
|
99
|
+
function getBuildingsInArea(area) {
|
|
100
|
+
const allBuildings = loadBuildings();
|
|
101
|
+
return allBuildings.filter(b => isPositionInArea(b.position, area));
|
|
102
|
+
}
|
|
103
|
+
/**
|
|
104
|
+
* Place buildings along the top-right edge of the bounds.
|
|
105
|
+
* Returns the building positions and the remaining agent bounds (with building space excluded).
|
|
106
|
+
*/
|
|
107
|
+
function placeBuildingsAndShrinkBounds(buildings, bounds) {
|
|
108
|
+
if (buildings.length === 0) {
|
|
109
|
+
return { buildingLayout: [], agentBounds: bounds };
|
|
110
|
+
}
|
|
111
|
+
const width = bounds.maxX - bounds.minX;
|
|
112
|
+
const buildingSpacing = Math.max(MIN_SPACING, Math.min(PREFERRED_SPACING, width / buildings.length));
|
|
113
|
+
const buildingRowHeight = PREFERRED_SPACING;
|
|
114
|
+
// Place buildings along the top edge (minZ), right-aligned
|
|
115
|
+
const totalBuildingWidth = buildingSpacing * (buildings.length - 1);
|
|
116
|
+
const startX = bounds.maxX - totalBuildingWidth;
|
|
117
|
+
const buildingLayout = buildings.map((b, i) => ({
|
|
118
|
+
buildingId: b.id,
|
|
119
|
+
position: {
|
|
120
|
+
x: clamp(startX + i * buildingSpacing, bounds.minX, bounds.maxX),
|
|
121
|
+
z: clamp(bounds.minZ, bounds.minZ, bounds.maxZ),
|
|
122
|
+
},
|
|
123
|
+
}));
|
|
124
|
+
// Shrink agent bounds: push minZ down to make room for buildings
|
|
125
|
+
const agentBounds = {
|
|
126
|
+
...bounds,
|
|
127
|
+
minZ: Math.min(bounds.minZ + buildingRowHeight, bounds.maxZ),
|
|
128
|
+
};
|
|
129
|
+
return { buildingLayout, agentBounds };
|
|
130
|
+
}
|
|
131
|
+
/**
|
|
132
|
+
* Organize agents within a single area using hierarchy-aware grid layout.
|
|
133
|
+
* Returns the new positions without applying them.
|
|
134
|
+
*/
|
|
135
|
+
export function calculateLayout(area, agents, buildings) {
|
|
136
|
+
const fullBounds = getAreaBounds(area);
|
|
137
|
+
const { buildingLayout, agentBounds } = placeBuildingsAndShrinkBounds(buildings, fullBounds);
|
|
138
|
+
if (agents.length === 0) {
|
|
139
|
+
return { organized: [], buildings: buildingLayout };
|
|
140
|
+
}
|
|
141
|
+
const usableHeight = agentBounds.maxZ - agentBounds.minZ;
|
|
142
|
+
const usableWidth = agentBounds.maxX - agentBounds.minX;
|
|
143
|
+
// Separate into hierarchy groups
|
|
144
|
+
const bosses = [];
|
|
145
|
+
const subordinatesByBoss = new Map();
|
|
146
|
+
const regulars = [];
|
|
147
|
+
const bossIdsInArea = new Set(agents.filter(a => a.isBoss).map(a => a.id));
|
|
148
|
+
for (const agent of agents) {
|
|
149
|
+
if (agent.isBoss) {
|
|
150
|
+
bosses.push(agent);
|
|
151
|
+
if (!subordinatesByBoss.has(agent.id))
|
|
152
|
+
subordinatesByBoss.set(agent.id, []);
|
|
153
|
+
}
|
|
154
|
+
else if (agent.bossId && bossIdsInArea.has(agent.bossId)) {
|
|
155
|
+
const subs = subordinatesByBoss.get(agent.bossId) || [];
|
|
156
|
+
subs.push(agent);
|
|
157
|
+
subordinatesByBoss.set(agent.bossId, subs);
|
|
158
|
+
}
|
|
159
|
+
else {
|
|
160
|
+
regulars.push(agent);
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
// Calculate max agents per row - adapt to fit
|
|
164
|
+
const maxPerRow = usableWidth <= 0
|
|
165
|
+
? agents.length
|
|
166
|
+
: Math.max(1, Math.floor(usableWidth / MIN_SPACING) + 1);
|
|
167
|
+
// Build rows: bosses first, then subs per boss, then regulars
|
|
168
|
+
const rows = [];
|
|
169
|
+
for (let i = 0; i < bosses.length; i += maxPerRow) {
|
|
170
|
+
rows.push(bosses.slice(i, i + maxPerRow));
|
|
171
|
+
}
|
|
172
|
+
for (const boss of bosses) {
|
|
173
|
+
const subs = subordinatesByBoss.get(boss.id) || [];
|
|
174
|
+
for (let i = 0; i < subs.length; i += maxPerRow) {
|
|
175
|
+
rows.push(subs.slice(i, i + maxPerRow));
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
for (let i = 0; i < regulars.length; i += maxPerRow) {
|
|
179
|
+
rows.push(regulars.slice(i, i + maxPerRow));
|
|
180
|
+
}
|
|
181
|
+
if (rows.length === 0) {
|
|
182
|
+
return { organized: [], buildings: buildingLayout };
|
|
183
|
+
}
|
|
184
|
+
// Calculate row spacing - shrink to fit within bounds
|
|
185
|
+
let rowSpacing;
|
|
186
|
+
if (rows.length === 1) {
|
|
187
|
+
rowSpacing = 0;
|
|
188
|
+
}
|
|
189
|
+
else {
|
|
190
|
+
rowSpacing = Math.max(MIN_SPACING, Math.min(PREFERRED_SPACING, usableHeight / (rows.length - 1)));
|
|
191
|
+
}
|
|
192
|
+
// Start from top of agent bounds
|
|
193
|
+
const startZ = rows.length === 1
|
|
194
|
+
? (agentBounds.minZ + agentBounds.maxZ) / 2
|
|
195
|
+
: agentBounds.minZ;
|
|
196
|
+
const organized = [];
|
|
197
|
+
for (let r = 0; r < rows.length; r++) {
|
|
198
|
+
const z = startZ + r * rowSpacing;
|
|
199
|
+
organized.push(...placeRow(rows[r], z, agentBounds));
|
|
200
|
+
}
|
|
201
|
+
return { organized, buildings: buildingLayout };
|
|
202
|
+
}
|
|
203
|
+
/**
|
|
204
|
+
* Organize agents and buildings within a single area and apply position updates.
|
|
205
|
+
*/
|
|
206
|
+
export function organizeArea(areaId) {
|
|
207
|
+
const areas = loadAreas();
|
|
208
|
+
const area = areas.find(a => a.id === areaId);
|
|
209
|
+
if (!area)
|
|
210
|
+
throw new Error(`Area ${areaId} not found`);
|
|
211
|
+
if (area.archived)
|
|
212
|
+
throw new Error(`Area ${areaId} is archived`);
|
|
213
|
+
const agents = area.assignedAgentIds
|
|
214
|
+
.map(id => agentService.getAgent(id))
|
|
215
|
+
.filter((a) => a !== undefined);
|
|
216
|
+
const buildings = getBuildingsInArea(area);
|
|
217
|
+
const result = calculateLayout(area, agents, buildings);
|
|
218
|
+
// Apply agent positions
|
|
219
|
+
for (const item of result.organized) {
|
|
220
|
+
agentService.updateAgent(item.agentId, { position: item.position }, false);
|
|
221
|
+
}
|
|
222
|
+
// Apply building positions
|
|
223
|
+
if (result.buildings.length > 0) {
|
|
224
|
+
const allBuildings = loadBuildings();
|
|
225
|
+
let changed = false;
|
|
226
|
+
for (const item of result.buildings) {
|
|
227
|
+
const idx = allBuildings.findIndex(b => b.id === item.buildingId);
|
|
228
|
+
if (idx !== -1) {
|
|
229
|
+
allBuildings[idx] = { ...allBuildings[idx], position: item.position };
|
|
230
|
+
changed = true;
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
if (changed)
|
|
234
|
+
saveBuildings(allBuildings);
|
|
235
|
+
}
|
|
236
|
+
log.log(`Organized ${result.organized.length} agents and ${result.buildings.length} buildings in area "${area.name}"`);
|
|
237
|
+
return result;
|
|
238
|
+
}
|
|
239
|
+
/**
|
|
240
|
+
* Organize all non-archived areas.
|
|
241
|
+
*/
|
|
242
|
+
export function organizeAllAreas() {
|
|
243
|
+
const areas = loadAreas();
|
|
244
|
+
const results = [];
|
|
245
|
+
for (const area of areas) {
|
|
246
|
+
if (area.archived)
|
|
247
|
+
continue;
|
|
248
|
+
// Organize even if no assigned agents — buildings may still need layout
|
|
249
|
+
try {
|
|
250
|
+
const result = organizeArea(area.id);
|
|
251
|
+
if (result.organized.length > 0 || result.buildings.length > 0) {
|
|
252
|
+
results.push({ areaId: area.id, areaName: area.name, ...result });
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
catch (err) {
|
|
256
|
+
log.error(`Failed to organize area "${area.name}": ${err.message}`);
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
return results;
|
|
260
|
+
}
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Workspace Service
|
|
3
|
+
* Manages workspaces - named groups of areas for filtering agent views
|
|
4
|
+
*/
|
|
5
|
+
import * as fs from 'fs';
|
|
6
|
+
import * as path from 'path';
|
|
7
|
+
import * as os from 'os';
|
|
8
|
+
import * as crypto from 'crypto';
|
|
9
|
+
import { createLogger } from '../utils/logger.js';
|
|
10
|
+
const log = createLogger('Workspaces');
|
|
11
|
+
const DATA_DIR = path.join(process.env.XDG_DATA_HOME || path.join(os.homedir(), '.local', 'share'), 'tide-commander');
|
|
12
|
+
const WORKSPACES_FILE = path.join(DATA_DIR, 'workspaces.json');
|
|
13
|
+
function ensureDataDir() {
|
|
14
|
+
if (!fs.existsSync(DATA_DIR)) {
|
|
15
|
+
fs.mkdirSync(DATA_DIR, { recursive: true });
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
function loadData() {
|
|
19
|
+
ensureDataDir();
|
|
20
|
+
try {
|
|
21
|
+
if (fs.existsSync(WORKSPACES_FILE)) {
|
|
22
|
+
return JSON.parse(fs.readFileSync(WORKSPACES_FILE, 'utf-8'));
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
catch (error) {
|
|
26
|
+
log.error(`Failed to load workspaces: ${error.message}`);
|
|
27
|
+
}
|
|
28
|
+
return { workspaces: [], activeWorkspaceId: null };
|
|
29
|
+
}
|
|
30
|
+
function saveData(data) {
|
|
31
|
+
ensureDataDir();
|
|
32
|
+
fs.writeFileSync(WORKSPACES_FILE, JSON.stringify(data, null, 2), 'utf-8');
|
|
33
|
+
}
|
|
34
|
+
export function getWorkspaces() {
|
|
35
|
+
return loadData().workspaces;
|
|
36
|
+
}
|
|
37
|
+
export function getWorkspace(id) {
|
|
38
|
+
const data = loadData();
|
|
39
|
+
return data.workspaces.find(w => w.id === id) || null;
|
|
40
|
+
}
|
|
41
|
+
export function createWorkspace(name, areaIds) {
|
|
42
|
+
const data = loadData();
|
|
43
|
+
const now = Date.now();
|
|
44
|
+
const workspace = {
|
|
45
|
+
id: crypto.randomUUID(),
|
|
46
|
+
name,
|
|
47
|
+
areaIds,
|
|
48
|
+
createdAt: now,
|
|
49
|
+
updatedAt: now,
|
|
50
|
+
};
|
|
51
|
+
data.workspaces.push(workspace);
|
|
52
|
+
saveData(data);
|
|
53
|
+
log.log(`Created workspace "${name}" with ${areaIds.length} areas`);
|
|
54
|
+
return workspace;
|
|
55
|
+
}
|
|
56
|
+
export function updateWorkspace(id, updates) {
|
|
57
|
+
const data = loadData();
|
|
58
|
+
const index = data.workspaces.findIndex(w => w.id === id);
|
|
59
|
+
if (index === -1)
|
|
60
|
+
return null;
|
|
61
|
+
const workspace = data.workspaces[index];
|
|
62
|
+
if (updates.name !== undefined)
|
|
63
|
+
workspace.name = updates.name;
|
|
64
|
+
if (updates.areaIds !== undefined)
|
|
65
|
+
workspace.areaIds = updates.areaIds;
|
|
66
|
+
if (updates.cameraState !== undefined)
|
|
67
|
+
workspace.cameraState = updates.cameraState;
|
|
68
|
+
if (updates.cameraState2d !== undefined)
|
|
69
|
+
workspace.cameraState2d = updates.cameraState2d;
|
|
70
|
+
workspace.updatedAt = Date.now();
|
|
71
|
+
data.workspaces[index] = workspace;
|
|
72
|
+
saveData(data);
|
|
73
|
+
log.log(`Updated workspace "${workspace.name}"`);
|
|
74
|
+
return workspace;
|
|
75
|
+
}
|
|
76
|
+
export function deleteWorkspace(id) {
|
|
77
|
+
const data = loadData();
|
|
78
|
+
const before = data.workspaces.length;
|
|
79
|
+
data.workspaces = data.workspaces.filter(w => w.id !== id);
|
|
80
|
+
if (data.workspaces.length === before)
|
|
81
|
+
return false;
|
|
82
|
+
// Clear active if deleted
|
|
83
|
+
if (data.activeWorkspaceId === id) {
|
|
84
|
+
data.activeWorkspaceId = null;
|
|
85
|
+
}
|
|
86
|
+
saveData(data);
|
|
87
|
+
log.log(`Deleted workspace ${id}`);
|
|
88
|
+
return true;
|
|
89
|
+
}
|
|
90
|
+
export function getActiveWorkspace() {
|
|
91
|
+
return loadData().activeWorkspaceId;
|
|
92
|
+
}
|
|
93
|
+
export function setActiveWorkspace(id) {
|
|
94
|
+
const data = loadData();
|
|
95
|
+
if (id !== null) {
|
|
96
|
+
const exists = data.workspaces.some(w => w.id === id);
|
|
97
|
+
if (!exists) {
|
|
98
|
+
throw new Error(`Workspace ${id} not found`);
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
data.activeWorkspaceId = id;
|
|
102
|
+
saveData(data);
|
|
103
|
+
log.log(`Active workspace set to: ${id ?? 'none (show all)'}`);
|
|
104
|
+
}
|
package/package.json
CHANGED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
import{u as $e,f as Ae,G as Le,E as Ee,r as t,J as De,S as se,p as _e,K as R,A as ae,X as O,Y as j,j as s,M as Be,O as Ie,P,U as A,V as L,W as Re,s as Oe}from"./main-Cjm0d8dZ.js";import"./modulepreload-polyfill-B5Qt9EMX.js";import"./vendor-react--Eh9ivFN.js";import"./vendor-three-Chj50gSY.js";function ne(p){const h=j.filter(S=>!p.has(`Boss ${S}`));return h.length===0?`Boss ${j[Math.floor(Math.random()*j.length)]}-${Date.now()%1e3}`:`Boss ${h[Math.floor(Math.random()*h.length)]}`}function Ue({isOpen:p,onClose:h,onSpawnStart:S,onSpawnEnd:E,spawnPosition:le}){const{t:n}=$e(["terminal","common"]),{agents:g}=Ae(),o=Le(),C=Ee(),[r,d]=t.useState(""),[k,te]=t.useState(()=>De(se.LAST_CWD)),[c,D]=t.useState("boss"),[T,q]=t.useState(!1),[ie,M]=t.useState(!1),[F,ce]=t.useState(!0),[U,oe]=t.useState("bypass"),[m,W]=t.useState("claude"),[f,N]=t.useState({fullAuto:!0,sandbox:"workspace-write",approvalMode:"on-request",search:!1}),[G,re]=t.useState("haiku"),[K,de]=t.useState("gpt-5.3-codex"),[v,y]=t.useState(new Set),[z,_]=t.useState(new Set),[u,ue]=t.useState(""),[b,pe]=t.useState(""),[J,me]=t.useState(""),$=t.useRef(null),B=t.useRef(!1),H=t.useRef(!1),x=t.useMemo(()=>C.filter(e=>e.enabled),[C]),X=t.useMemo(()=>{if(!b.trim())return x;const e=b.toLowerCase();return x.filter(a=>a.name.toLowerCase().includes(e)||a.description.toLowerCase().includes(e)||a.slug.toLowerCase().includes(e))},[x,b]),he=t.useMemo(()=>{var a;const e=o.find(l=>l.id===c);return(a=e==null?void 0:e.defaultSkillIds)!=null&&a.length?C.filter(l=>e.defaultSkillIds.includes(l.id)):[]},[o,c,C]),we=["full-notifications","streaming-exec","task-label","report-task-to-boss"];t.useEffect(()=>{if(p&&!H.current&&x.length>0){const a=x.filter(l=>we.includes(l.slug)).map(l=>l.id);a.length>0&&_(new Set(a))}H.current=p},[p,x]);const xe=t.useCallback(e=>{_(a=>{const l=new Set(a);return l.has(e)?l.delete(e):l.add(e),l})},[]),i=t.useMemo(()=>o.find(e=>e.id===c),[o,c]),fe=t.useMemo(()=>{if(i!=null&&i.model)return i.model},[i]),be=t.useMemo(()=>{if(i!=null&&i.customModelPath)return _e(`/api/custom-models/${i.id}`)},[i]),je=i==null?void 0:i.modelScale,Ne=t.useMemo(()=>i?"scout":c==="boss"?"architect":c,[c,i]),Y=Array.from(g.values()).filter(e=>!e.isBoss&&e.class!=="boss"&&!e.bossId),V=t.useMemo(()=>{if(!u.trim())return o;const e=u.toLowerCase();return o.filter(a=>a.name.toLowerCase().includes(e)||a.description.toLowerCase().includes(e)||a.id.toLowerCase().includes(e))},[o,u]),Q=t.useMemo(()=>{if(!u.trim())return R;const e=u.toLowerCase();return R.filter(a=>{const l=ae[a.id];return l?a.name.toLowerCase().includes(e)||a.id.toLowerCase().includes(e)||l.description.toLowerCase().includes(e):!1})},[u]),Z=t.useMemo(()=>{if(!u.trim())return!0;const e=u.toLowerCase(),a=O.boss;return"boss".includes(e)||a.description.toLowerCase().includes(e)},[u]);t.useEffect(()=>{if(p&&!B.current){B.current=!0;const e=new Set(Array.from(g.values()).map(l=>l.name)),a=o.find(l=>l.id===c);if(a){const l=j.filter(w=>!e.has(`${a.name} ${w}`));if(l.length===0){const w=j[Math.floor(Math.random()*j.length)];d(`${a.name} ${w}-${Date.now()%1e3}`)}else d(`${a.name} ${l[Math.floor(Math.random()*l.length)]}`)}else d(ne(e));y(new Set),$.current&&($.current.focus(),$.current.select())}else p||(B.current=!1)},[p,g]),t.useEffect(()=>{if(!p)return;const e=o.find(a=>a.id===c);if(e)if(r.startsWith("Boss ")){const a=r.substring(5);d(`${e.name} ${a}`)}else{const a=o.find(l=>r.startsWith(l.name+" "));if(a){const l=r.substring(a.name.length+1);d(`${e.name} ${l}`)}else d(`${e.name} ${r}`)}else{const a=o.find(l=>r.startsWith(l.name+" "));if(a){const l=r.substring(a.name.length+1);d(`Boss ${l}`)}else r.startsWith("Boss ")||d(`Boss ${r}`)}},[c]);const ve=()=>{if(M(!1),!k.trim()){M(!0);return}if(!r.trim()){const l=new Set(Array.from(g.values()).map(w=>w.name));d(ne(l));return}Re(se.LAST_CWD,k),S();const e=J.trim()||void 0,a=Array.from(z);Oe.spawnBossAgent(r.trim(),c,k.trim(),le||void 0,Array.from(v),m==="claude"?F:!1,U,m,m==="codex"?f:void 0,m==="codex"?K:void 0,m==="claude"?G:void 0,e,a),d(""),y(new Set),_(new Set),E(),h()},Se=()=>{q(!1),d(""),y(new Set),E(),h()},ge=()=>{q(!1),M(!0),E()};t.useEffect(()=>(window.__bossSpawnModalSuccess=Se,window.__bossSpawnModalError=ge,()=>{delete window.__bossSpawnModalSuccess,delete window.__bossSpawnModalError}),[r]);const Ce=e=>{e.target===e.currentTarget&&h()},ke=e=>{e.key==="Escape"&&h()},Me=e=>{const a=new Set(v);a.has(e)?a.delete(e):a.add(e),y(a)};if(!p)return null;const I=O.boss;return s.jsx("div",{className:`modal-overlay ${p?"visible":""}`,onClick:Ce,onKeyDown:ke,children:s.jsxs("div",{className:"modal boss-spawn-modal",children:[s.jsxs("div",{className:"modal-header",children:[s.jsx("span",{className:"boss-header-icon",children:I.icon}),n("terminal:spawn.deployBossTitle")]}),s.jsxs("div",{className:"modal-body spawn-modal-body",children:[s.jsxs("div",{className:"spawn-top-section",children:[s.jsx("div",{className:"spawn-preview-compact",children:s.jsx(Be,{agentClass:Ne,modelFile:fe,customModelUrl:be,modelScale:je,width:100,height:120})}),s.jsxs("div",{className:"spawn-class-section",children:[s.jsx("div",{className:"spawn-class-label",children:n("terminal:spawn.bossClass")}),o.length+R.length+1>6&&s.jsx("input",{type:"text",className:"spawn-input class-search-input",placeholder:n("terminal:spawn.filterClasses"),value:u,onChange:e=>ue(e.target.value)}),s.jsxs("div",{className:"class-selector-inline",children:[V.map(e=>s.jsxs("button",{className:`class-chip ${c===e.id?"selected":""}`,onClick:()=>D(e.id),title:e.description,children:[s.jsx("span",{className:"class-chip-icon",children:e.icon}),s.jsx("span",{className:"class-chip-name",children:e.name})]},e.id)),Z&&s.jsxs("button",{className:`class-chip ${c==="boss"?"selected":""}`,onClick:()=>D("boss"),title:I.description,children:[s.jsx("span",{className:"class-chip-icon",children:I.icon}),s.jsx("span",{className:"class-chip-name",children:n("terminal:spawn.bossClassName")})]}),Q.map(e=>{const a=ae[e.id];return a?s.jsxs("button",{className:`class-chip ${c===e.id?"selected":""}`,onClick:()=>D(e.id),title:a.description,children:[s.jsx("span",{className:"class-chip-icon",children:a.icon}),s.jsx("span",{className:"class-chip-name",children:e.name})]},e.id):null}),u&&V.length===0&&!Z&&Q.length===0&&s.jsx("div",{className:"class-search-empty",children:n("terminal:spawn.noClassesMatch",{query:u})})]})]})]}),s.jsxs("div",{className:"spawn-form-section",children:[s.jsxs("div",{className:"spawn-form-row",children:[s.jsxs("div",{className:"spawn-field",children:[s.jsx("label",{className:"spawn-label",children:n("common:labels.name")}),s.jsx("input",{ref:$,type:"text",className:"spawn-input",placeholder:n("terminal:spawn.bossNamePlaceholder"),value:r,onChange:e=>d(e.target.value)})]}),s.jsxs("div",{className:"spawn-field spawn-field-wide",children:[s.jsx("label",{className:"spawn-label",children:n("terminal:spawn.workingDir")}),s.jsx(Ie,{value:k,onChange:e=>{te(e),M(!1)},placeholder:n("terminal:spawn.workingDirPlaceholder"),className:"spawn-input",hasError:ie,directoriesOnly:!0})]})]}),s.jsxs("div",{className:"spawn-form-row",children:[s.jsxs("div",{className:"spawn-field",children:[s.jsx("label",{className:"spawn-label",children:n("common:labels.runtime")}),s.jsxs("div",{className:"spawn-select-row",children:[s.jsxs("button",{className:`spawn-select-btn ${m==="claude"?"selected":""}`,onClick:()=>W("claude"),title:n("terminal:spawn.useClaudeCli"),children:[s.jsx("img",{src:"/assets/claude.ico",alt:"Claude",className:"spawn-provider-icon"}),s.jsx("span",{children:"Claude"})]}),s.jsxs("button",{className:`spawn-select-btn ${m==="codex"?"selected":""}`,onClick:()=>W("codex"),title:n("terminal:spawn.useCodexCli"),children:[s.jsx("img",{src:"/assets/codex.ico",alt:"Codex",className:"spawn-provider-icon"}),s.jsx("span",{children:"Codex"})]})]})]}),s.jsxs("div",{className:"spawn-field",children:[s.jsx("label",{className:"spawn-label",children:n("common:labels.permissions")}),s.jsx("div",{className:"spawn-select-row",children:Object.keys(P).map(e=>s.jsxs("button",{className:`spawn-select-btn ${U===e?"selected":""}`,onClick:()=>oe(e),title:P[e].description,children:[s.jsx("span",{children:e==="bypass"?"⚡":"🔐"}),s.jsx("span",{children:P[e].label})]},e))})]})]}),s.jsxs("div",{className:"spawn-form-row",children:[s.jsxs("div",{className:"spawn-field",children:[s.jsx("label",{className:"spawn-label",children:n("common:labels.model")}),m==="claude"?s.jsx("div",{className:"spawn-select-row",children:Object.keys(A).map(e=>s.jsxs("button",{className:`spawn-select-btn ${G===e?"selected":""}`,onClick:()=>re(e),title:A[e].description,children:[s.jsx("span",{children:A[e].icon}),s.jsx("span",{children:A[e].label})]},e))}):m==="codex"?s.jsx("div",{className:"spawn-select-row spawn-select-row--codex-models",children:Object.keys(L).map(e=>s.jsxs("button",{className:`spawn-select-btn ${K===e?"selected":""}`,onClick:()=>de(e),title:L[e].description,children:[s.jsx("span",{children:L[e].icon}),s.jsx("span",{children:L[e].label})]},e))}):s.jsx("div",{className:"spawn-inline-hint",children:n("terminal:spawn.codex.configuration")})]}),s.jsxs("div",{className:"spawn-field",children:[s.jsx("label",{className:"spawn-label",children:n("common:labels.browser")}),s.jsx("div",{className:"spawn-form-row spawn-options-row",children:s.jsxs("label",{className:"spawn-checkbox",children:[s.jsx("input",{type:"checkbox",checked:F,onChange:e=>ce(e.target.checked),disabled:m!=="claude"}),s.jsx("span",{children:n("terminal:spawn.chromeBrowser")})]})})]})]}),m==="codex"&&s.jsx("div",{className:"spawn-form-row",children:s.jsxs("div",{className:"spawn-field",children:[s.jsx("label",{className:"spawn-label",children:n("terminal:spawn.codex.config")}),s.jsxs("div",{className:"spawn-options-row",style:{display:"flex",flexDirection:"column",gap:8},children:[s.jsxs("label",{className:"spawn-checkbox",children:[s.jsx("input",{type:"checkbox",checked:f.fullAuto!==!1,onChange:e=>N(a=>({...a,fullAuto:e.target.checked}))}),s.jsx("span",{children:n("terminal:spawn.codex.useFullAuto")})]}),s.jsxs("label",{className:"spawn-checkbox",children:[s.jsx("input",{type:"checkbox",checked:!!f.search,onChange:e=>N(a=>({...a,search:e.target.checked}))}),s.jsx("span",{children:n("terminal:spawn.codex.enableSearch")})]}),f.fullAuto===!1&&s.jsxs(s.Fragment,{children:[s.jsxs("select",{className:"spawn-input",value:f.sandbox||"workspace-write",onChange:e=>N(a=>({...a,sandbox:e.target.value})),children:[s.jsx("option",{value:"read-only",children:n("terminal:spawn.codex.sandboxReadOnly")}),s.jsx("option",{value:"workspace-write",children:n("terminal:spawn.codex.sandboxWorkspaceWrite")}),s.jsx("option",{value:"danger-full-access",children:n("terminal:spawn.codex.sandboxDangerFullAccess")})]}),s.jsxs("select",{className:"spawn-input",value:f.approvalMode||"on-request",onChange:e=>N(a=>({...a,approvalMode:e.target.value})),children:[s.jsx("option",{value:"untrusted",children:n("terminal:spawn.codex.approvalsUntrusted")}),s.jsx("option",{value:"on-failure",children:n("terminal:spawn.codex.approvalsOnFailure")}),s.jsx("option",{value:"on-request",children:n("terminal:spawn.codex.approvalsOnRequest")}),s.jsx("option",{value:"never",children:n("terminal:spawn.codex.approvalsNever")})]})]}),s.jsx("input",{type:"text",className:"spawn-input",placeholder:n("terminal:spawn.codex.profileOptional"),value:f.profile||"",onChange:e=>N(a=>({...a,profile:e.target.value||void 0}))})]})]})}),x.length>0&&s.jsxs("div",{className:"spawn-skills-section",children:[s.jsxs("label",{className:"spawn-label",children:[n("terminal:spawn.skills")," ",s.jsxs("span",{className:"spawn-label-hint",children:["(",n("common:labels.optional"),")"]})]}),x.length>6&&s.jsx("input",{type:"text",className:"spawn-input skill-search-input",placeholder:n("terminal:spawn.filterSkills"),value:b,onChange:e=>pe(e.target.value)}),s.jsxs("div",{className:"spawn-skills-inline",children:[X.map(e=>{const a=z.has(e.id);return he.some(w=>w.id===e.id)?null:s.jsxs("button",{className:`spawn-skill-chip ${a?"selected":""}`,onClick:()=>xe(e.id),title:e.description,children:[a&&s.jsx("span",{className:"spawn-skill-check",children:"✓"}),s.jsx("span",{children:e.name}),e.builtin&&s.jsx("span",{className:"spawn-skill-builtin",children:"TC"})]},e.id)}),b&&X.length===0&&s.jsx("div",{className:"skill-search-empty",children:n("terminal:spawn.noSkillsMatch",{query:b})})]})]}),s.jsxs("div",{className:"spawn-custom-instructions-section",children:[s.jsxs("label",{className:"spawn-label",children:[n("terminal:spawn.customInstructions")," ",s.jsxs("span",{className:"spawn-label-hint",children:["(",n("common:labels.optional"),")"]})]}),s.jsx("textarea",{className:"spawn-input spawn-textarea",placeholder:n("terminal:spawn.customInstructionsBossPlaceholder"),value:J,onChange:e=>me(e.target.value),rows:3})]}),s.jsxs("div",{className:"spawn-subordinates-section",children:[s.jsxs("label",{className:"spawn-label",children:[n("terminal:spawn.initialSubordinates")," ",s.jsxs("span",{className:"spawn-label-hint",children:["(",n("common:labels.optional"),")"]})]}),s.jsx("div",{className:"subordinates-selector-compact",children:Y.length===0?s.jsx("div",{className:"subordinates-empty",children:n("terminal:spawn.noAvailableAgents")}):Y.map(e=>{const a=v.has(e.id),l=O[e.class],w=o.find(ye=>ye.id===e.class),ee=l||w||{icon:"🤖",color:"#888888"};return s.jsxs("button",{className:`subordinate-chip ${a?"selected":""}`,onClick:()=>Me(e.id),children:[a&&s.jsx("span",{className:"subordinate-check",children:"✓"}),s.jsx("span",{className:"subordinate-chip-icon",style:{color:ee.color},children:ee.icon}),s.jsx("span",{className:"subordinate-chip-name",children:e.name})]},e.id)})}),v.size>0&&s.jsxs("div",{className:"subordinates-count",children:[v.size," ",n("common:labels.selected").toLowerCase()]})]})]})]}),s.jsxs("div",{className:"modal-footer",children:[s.jsx("button",{className:"btn btn-secondary",onClick:h,children:n("common:buttons.cancel")}),s.jsx("button",{className:"btn btn-boss",onClick:ve,disabled:T,children:n(T?"common:buttons.deploying":"common:buttons2.deployBoss")})]})]})})}export{Ue as BossSpawnModal};
|