tide-commander 1.160.0 → 1.162.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/CHANGELOG.md +26 -0
- package/dist/assets/{BossLogsModal-COg0RBw_.js → BossLogsModal-DrYF13xp.js} +1 -1
- package/dist/assets/{BossSpawnModal-Bvkk2uFi.js → BossSpawnModal-2ZgbW8Iy.js} +1 -1
- package/dist/assets/{ControlsModal-BWcWGs2x.js → ControlsModal-CRdNNTEo.js} +1 -1
- package/dist/assets/{DockerLogsModal-C4tJ59Pf.js → DockerLogsModal-BGPZP9_V.js} +1 -1
- package/dist/assets/{EmbeddedEditor-DiDm5EvF.js → EmbeddedEditor-B99bT-bl.js} +1 -1
- package/dist/assets/{GmailOAuthSetup-GkYs6wUs.js → GmailOAuthSetup-DmMCxdUW.js} +1 -1
- package/dist/assets/{GoogleOAuthSetup-KKwp2gEQ.js → GoogleOAuthSetup-BZqit-mW.js} +1 -1
- package/dist/assets/{IframeModal-D0iTkxZB.js → IframeModal-vMY7XxHP.js} +1 -1
- package/dist/assets/{IntegrationsPanel-CCKoizJa.js → IntegrationsPanel-DVmqPRTs.js} +2 -2
- package/dist/assets/{LogViewerModal-CSGEkyhZ.js → LogViewerModal-rYC6IVPV.js} +1 -1
- package/dist/assets/{MonitoringModal-Be6uEd7K.js → MonitoringModal-C-QXpZkE.js} +1 -1
- package/dist/assets/{PM2LogsModal-DZG7Tcw7.js → PM2LogsModal-C3A_ohFv.js} +1 -1
- package/dist/assets/{RestoreArchivedAreaModal-CigBbBB_.js → RestoreArchivedAreaModal-Dx94NzSz.js} +1 -1
- package/dist/assets/{Scene2DCanvas-sJig3vJ4.js → Scene2DCanvas-BxOqJ0IB.js} +1 -1
- package/dist/assets/{SceneManager-BG_0UVJc.js → SceneManager-CxcXfq_u.js} +1 -1
- package/dist/assets/{SkillsPanel-BnAf8uyn.js → SkillsPanel-Fu5sfG7Q.js} +1 -1
- package/dist/assets/{SlackMultiInstanceSetup-DGuexHRz.js → SlackMultiInstanceSetup-jnGMesoq.js} +1 -1
- package/dist/assets/{SpawnModal-FjDJZlIm.js → SpawnModal-CGYg4uTC.js} +1 -1
- package/dist/assets/{StatisticsModal-CbbdwSF0.js → StatisticsModal-BOV6Juts.js} +1 -1
- package/dist/assets/{SubordinateAssignmentModal-Dn2uuOcT.js → SubordinateAssignmentModal-Bhgj9FvT.js} +1 -1
- package/dist/assets/{TriggerManagerPanel-Cj9pqf-r.js → TriggerManagerPanel-Qkw4ajmL.js} +1 -1
- package/dist/assets/{WorkflowEditorPanel-nucrx_0v.js → WorkflowEditorPanel-DTIBRGz4.js} +1 -1
- package/dist/assets/{area-logos-C93KZk3h.js → area-logos-uM2FM0I2.js} +1 -1
- package/dist/assets/{index-Gjc7NXlZ.js → index-3y5Ajiyr.js} +1 -1
- package/dist/assets/{index-iX9jPnEp.js → index-5tz2e3op.js} +1 -1
- package/dist/assets/{index-ojmo-Cva.js → index-B03BKiDN.js} +1 -1
- package/dist/assets/index-BFJWNGpT.js +19 -0
- package/dist/assets/{index-BkbKu66S.js → index-BvD1v4JH.js} +1 -1
- package/dist/assets/{index-D7YoMagO.js → index-BvOgm4yp.js} +1 -1
- package/dist/assets/{index-CmmI2kZf.js → index-CpQeaxZ7.js} +2 -2
- package/dist/assets/index-CrBXvs1w.css +1 -0
- package/dist/assets/{index-fh53j_4E.js → index-DzQxMm67.js} +1 -1
- package/dist/assets/index-cA0kPNUq.js +2 -0
- package/dist/assets/{main-BZcOdtO_.css → main-B-jhtDyZ.css} +1 -1
- package/dist/assets/main-rX2ghMIV.js +297 -0
- package/dist/assets/{web-8gl2noUE.js → web-ClQyiX7L.js} +1 -1
- package/dist/assets/{web-M9cLwTrs.js → web-NCIGsXXA.js} +1 -1
- package/dist/assets/{web-D0uiHhnA.js → web-ml-7D2g7.js} +1 -1
- package/dist/build-info.json +1 -1
- package/dist/index.html +2 -2
- package/dist/locales/en/terminal.json +4 -0
- package/dist/locales/es/terminal.json +1 -0
- package/dist/src/packages/server/claude/backend.js +4 -0
- package/dist/src/packages/server/claude/session-loader.js +116 -0
- package/dist/src/packages/server/codex/generated-image.js +35 -0
- package/dist/src/packages/server/codex/json-event-parser.js +122 -0
- package/dist/src/packages/server/index.js +4 -0
- package/dist/src/packages/server/routes/agents.js +9 -6
- package/dist/src/packages/server/services/claude-credentials-service.js +172 -13
- package/dist/src/packages/server/services/claude-usage-service.js +5 -1
- package/dist/src/packages/server/services/codex-usage-service.js +93 -0
- package/package.json +1 -1
- package/dist/assets/index-BIxFr4P2.css +0 -1
- package/dist/assets/index-DIOhr3im.js +0 -19
- package/dist/assets/index-DqM0QltS.js +0 -2
- package/dist/assets/main-BFW0rGXU.js +0 -297
|
@@ -162,6 +162,13 @@ export function listClaudeCredentialProfiles() {
|
|
|
162
162
|
const OAUTH_TOKEN_URL = 'https://console.anthropic.com/v1/oauth/token';
|
|
163
163
|
const OAUTH_CLIENT_ID = '9d1c250a-e61b-44d9-88ed-5944d1962f5e';
|
|
164
164
|
const OAUTH_REFRESH_TIMEOUT_MS = 10_000;
|
|
165
|
+
// Claude Code refreshes an access token when it is within five minutes of
|
|
166
|
+
// expiry. Tide goes a little earlier so it normally owns token rotation for
|
|
167
|
+
// every saved account (including the active one) and can persist the rotated
|
|
168
|
+
// grant to all duplicate profile files before a CLI process needs it.
|
|
169
|
+
const KEEP_ALIVE_INTERVAL_MS = 5 * 60 * 1000;
|
|
170
|
+
const KEEP_ALIVE_REFRESH_WINDOW_MS = 15 * 60 * 1000;
|
|
171
|
+
const KEEP_ALIVE_REFRESH_TOKEN_WINDOW_MS = 24 * 60 * 60 * 1000;
|
|
165
172
|
const GRANT_USAGE_TTL_OK_MS = 2 * 60 * 1000;
|
|
166
173
|
const GRANT_USAGE_TTL_ERR_MS = 45 * 1000;
|
|
167
174
|
// After a failed refresh, leave the grant alone for a while — retrying a
|
|
@@ -183,22 +190,36 @@ function readProfileTokens(filePath) {
|
|
|
183
190
|
expiresAt: typeof oauth?.expiresAt === 'number' ? oauth.expiresAt : null,
|
|
184
191
|
refreshToken: typeof oauth?.refreshToken === 'string' && oauth.refreshToken ? oauth.refreshToken : null,
|
|
185
192
|
refreshTokenExpiresAt: typeof oauth?.refreshTokenExpiresAt === 'number' ? oauth.refreshTokenExpiresAt : null,
|
|
193
|
+
scopes: Array.isArray(oauth?.scopes)
|
|
194
|
+
? oauth.scopes.filter((scope) => typeof scope === 'string')
|
|
195
|
+
: [],
|
|
196
|
+
clientId: typeof oauth?.clientId === 'string' && oauth.clientId ? oauth.clientId : null,
|
|
186
197
|
};
|
|
187
198
|
}
|
|
188
199
|
catch {
|
|
189
|
-
return {
|
|
200
|
+
return {
|
|
201
|
+
accessToken: null,
|
|
202
|
+
expiresAt: null,
|
|
203
|
+
refreshToken: null,
|
|
204
|
+
refreshTokenExpiresAt: null,
|
|
205
|
+
scopes: [],
|
|
206
|
+
clientId: null,
|
|
207
|
+
};
|
|
190
208
|
}
|
|
191
209
|
}
|
|
192
210
|
/** Refresh an OAuth grant the way the CLI does. Returns tokens or an error string. */
|
|
193
|
-
async function refreshOAuthGrant(
|
|
211
|
+
async function refreshOAuthGrant(tokens) {
|
|
212
|
+
if (!tokens.refreshToken)
|
|
213
|
+
return { error: 'No refresh token stored' };
|
|
194
214
|
try {
|
|
195
215
|
const response = await fetch(OAUTH_TOKEN_URL, {
|
|
196
216
|
method: 'POST',
|
|
197
217
|
headers: { 'Content-Type': 'application/json' },
|
|
198
218
|
body: JSON.stringify({
|
|
199
219
|
grant_type: 'refresh_token',
|
|
200
|
-
refresh_token: refreshToken,
|
|
201
|
-
client_id: OAUTH_CLIENT_ID,
|
|
220
|
+
refresh_token: tokens.refreshToken,
|
|
221
|
+
client_id: tokens.clientId ?? OAUTH_CLIENT_ID,
|
|
222
|
+
...(tokens.scopes.length > 0 ? { scope: tokens.scopes.join(' ') } : {}),
|
|
202
223
|
}),
|
|
203
224
|
signal: AbortSignal.timeout(OAUTH_REFRESH_TIMEOUT_MS),
|
|
204
225
|
});
|
|
@@ -210,10 +231,19 @@ async function refreshOAuthGrant(refreshToken) {
|
|
|
210
231
|
if (!accessToken)
|
|
211
232
|
return { error: 'Token refresh returned no access token' };
|
|
212
233
|
const expiresIn = typeof body.expires_in === 'number' ? body.expires_in : 3600;
|
|
234
|
+
const refreshTokenExpiresIn = typeof body.refresh_token_expires_in === 'number'
|
|
235
|
+
? body.refresh_token_expires_in
|
|
236
|
+
: null;
|
|
213
237
|
return {
|
|
214
238
|
accessToken,
|
|
215
239
|
refreshToken: typeof body.refresh_token === 'string' && body.refresh_token ? body.refresh_token : null,
|
|
216
240
|
expiresAt: Date.now() + expiresIn * 1000,
|
|
241
|
+
refreshTokenExpiresAt: refreshTokenExpiresIn == null
|
|
242
|
+
? null
|
|
243
|
+
: Date.now() + refreshTokenExpiresIn * 1000,
|
|
244
|
+
scopes: typeof body.scope === 'string'
|
|
245
|
+
? body.scope.split(' ').filter(Boolean)
|
|
246
|
+
: null,
|
|
217
247
|
};
|
|
218
248
|
}
|
|
219
249
|
catch (err) {
|
|
@@ -229,9 +259,36 @@ function writeRefreshedTokens(filePath, refreshed) {
|
|
|
229
259
|
oauth.expiresAt = refreshed.expiresAt;
|
|
230
260
|
if (refreshed.refreshToken)
|
|
231
261
|
oauth.refreshToken = refreshed.refreshToken;
|
|
262
|
+
if (refreshed.refreshTokenExpiresAt != null) {
|
|
263
|
+
oauth.refreshTokenExpiresAt = refreshed.refreshTokenExpiresAt;
|
|
264
|
+
}
|
|
265
|
+
if (refreshed.scopes)
|
|
266
|
+
oauth.scopes = refreshed.scopes;
|
|
232
267
|
data.claudeAiOauth = oauth;
|
|
233
268
|
writeCredentialsAtomic(filePath, JSON.stringify(data));
|
|
234
269
|
}
|
|
270
|
+
/**
|
|
271
|
+
* Persist one rotated grant to all of its copies. If another process (usually
|
|
272
|
+
* Claude Code) changed any refresh token while our request was in flight, do
|
|
273
|
+
* not overwrite its newer credentials with our response.
|
|
274
|
+
*/
|
|
275
|
+
function persistRefreshedGrant(group, refreshed) {
|
|
276
|
+
const expectedRefreshToken = group.tokens.refreshToken;
|
|
277
|
+
const changedPath = group.paths.find((filePath) => readProfileTokens(filePath).refreshToken !== expectedRefreshToken);
|
|
278
|
+
if (changedPath) {
|
|
279
|
+
log.warn(`Claude credentials changed during refresh; leaving newer on-disk grant untouched (${group.ids.join(', ')})`);
|
|
280
|
+
return false;
|
|
281
|
+
}
|
|
282
|
+
for (const filePath of group.paths) {
|
|
283
|
+
try {
|
|
284
|
+
writeRefreshedTokens(filePath, refreshed);
|
|
285
|
+
}
|
|
286
|
+
catch (err) {
|
|
287
|
+
log.warn(`Refreshed grant but failed to persist to ${filePath}: ${err}`);
|
|
288
|
+
}
|
|
289
|
+
}
|
|
290
|
+
return true;
|
|
291
|
+
}
|
|
235
292
|
function grantCacheKey(token) {
|
|
236
293
|
return crypto.createHash('sha256').update(token).digest('hex').slice(0, 12);
|
|
237
294
|
}
|
|
@@ -267,7 +324,7 @@ function fetchUsageForGrant(cacheKey, group) {
|
|
|
267
324
|
error = 'Session expired — sign in to this account again';
|
|
268
325
|
}
|
|
269
326
|
else {
|
|
270
|
-
const refreshed = await refreshOAuthGrant(tokens
|
|
327
|
+
const refreshed = await refreshOAuthGrant(tokens);
|
|
271
328
|
if ('error' in refreshed) {
|
|
272
329
|
accessToken = null;
|
|
273
330
|
error = refreshed.error;
|
|
@@ -280,14 +337,7 @@ function fetchUsageForGrant(cacheKey, group) {
|
|
|
280
337
|
grantUsageCache.set(cacheKey, entry);
|
|
281
338
|
return entry;
|
|
282
339
|
}
|
|
283
|
-
|
|
284
|
-
try {
|
|
285
|
-
writeRefreshedTokens(filePath, refreshed);
|
|
286
|
-
}
|
|
287
|
-
catch (err) {
|
|
288
|
-
log.warn(`Refreshed grant but failed to persist to ${filePath}: ${err}`);
|
|
289
|
-
}
|
|
290
|
-
}
|
|
340
|
+
persistRefreshedGrant(group, refreshed);
|
|
291
341
|
log.log(`Refreshed dormant Claude credentials for profile(s): ${group.ids.join(', ')}`);
|
|
292
342
|
accessToken = refreshed.accessToken;
|
|
293
343
|
}
|
|
@@ -364,6 +414,115 @@ export async function getClaudeCredentialProfilesUsage() {
|
|
|
364
414
|
}));
|
|
365
415
|
return { usage };
|
|
366
416
|
}
|
|
417
|
+
let keepAliveTimer = null;
|
|
418
|
+
let keepAliveInFlight = null;
|
|
419
|
+
const keepAliveFailureCooldown = new Map();
|
|
420
|
+
/** Run one keepalive pass. Exported to make the credential lifecycle testable. */
|
|
421
|
+
export function runClaudeCredentialKeepAliveNow() {
|
|
422
|
+
if (keepAliveInFlight)
|
|
423
|
+
return keepAliveInFlight;
|
|
424
|
+
keepAliveInFlight = (async () => {
|
|
425
|
+
const now = Date.now();
|
|
426
|
+
const list = listClaudeCredentialProfiles();
|
|
427
|
+
const candidates = [...(list.active ? [list.active] : []), ...list.profiles];
|
|
428
|
+
const groups = new Map();
|
|
429
|
+
for (const meta of candidates) {
|
|
430
|
+
if (!meta.valid)
|
|
431
|
+
continue;
|
|
432
|
+
const tokens = readProfileTokens(meta.path);
|
|
433
|
+
if (!tokens.refreshToken)
|
|
434
|
+
continue;
|
|
435
|
+
const key = grantCacheKey(tokens.refreshToken);
|
|
436
|
+
const existing = groups.get(key);
|
|
437
|
+
if (existing) {
|
|
438
|
+
existing.ids.push(meta.id);
|
|
439
|
+
if (!existing.paths.includes(meta.path))
|
|
440
|
+
existing.paths.push(meta.path);
|
|
441
|
+
existing.includesActive = existing.includesActive || meta.id === 'active';
|
|
442
|
+
// Copies can have different access-token expiries even while they still
|
|
443
|
+
// share a refresh grant. Refresh according to the freshest copy.
|
|
444
|
+
if ((tokens.expiresAt ?? 0) > (existing.tokens.expiresAt ?? 0))
|
|
445
|
+
existing.tokens = tokens;
|
|
446
|
+
}
|
|
447
|
+
else {
|
|
448
|
+
groups.set(key, {
|
|
449
|
+
ids: [meta.id],
|
|
450
|
+
paths: [meta.path],
|
|
451
|
+
includesActive: meta.id === 'active',
|
|
452
|
+
tokens,
|
|
453
|
+
});
|
|
454
|
+
}
|
|
455
|
+
}
|
|
456
|
+
const result = {
|
|
457
|
+
checkedGrants: groups.size,
|
|
458
|
+
refreshedGrants: 0,
|
|
459
|
+
skippedGrants: 0,
|
|
460
|
+
failedGrants: 0,
|
|
461
|
+
};
|
|
462
|
+
await Promise.all(Array.from(groups.entries()).map(async ([key, group]) => {
|
|
463
|
+
const { tokens } = group;
|
|
464
|
+
const refreshExpired = tokens.refreshTokenExpiresAt != null
|
|
465
|
+
&& tokens.refreshTokenExpiresAt <= now;
|
|
466
|
+
const refreshDue = tokens.expiresAt == null
|
|
467
|
+
|| tokens.expiresAt <= now + KEEP_ALIVE_REFRESH_WINDOW_MS
|
|
468
|
+
|| (tokens.refreshTokenExpiresAt != null
|
|
469
|
+
&& tokens.refreshTokenExpiresAt <= now + KEEP_ALIVE_REFRESH_TOKEN_WINDOW_MS);
|
|
470
|
+
const retryAfter = keepAliveFailureCooldown.get(key) ?? 0;
|
|
471
|
+
if (refreshExpired || !refreshDue || retryAfter > now) {
|
|
472
|
+
result.skippedGrants += 1;
|
|
473
|
+
return;
|
|
474
|
+
}
|
|
475
|
+
const refreshed = await refreshOAuthGrant(tokens);
|
|
476
|
+
if ('error' in refreshed) {
|
|
477
|
+
keepAliveFailureCooldown.set(key, Date.now() + GRANT_REFRESH_FAIL_COOLDOWN_MS);
|
|
478
|
+
result.failedGrants += 1;
|
|
479
|
+
log.warn(`Claude credential keepalive failed for ${group.ids.join(', ')}: ${refreshed.error}`);
|
|
480
|
+
return;
|
|
481
|
+
}
|
|
482
|
+
if (persistRefreshedGrant(group, refreshed)) {
|
|
483
|
+
result.refreshedGrants += 1;
|
|
484
|
+
keepAliveFailureCooldown.delete(key);
|
|
485
|
+
// Cached gauges contain the previous access token/fingerprint.
|
|
486
|
+
grantUsageCache.delete(key);
|
|
487
|
+
resetClaudeRateLimitCache();
|
|
488
|
+
log.log(`Kept Claude account session alive: ${group.ids.join(', ')}`);
|
|
489
|
+
}
|
|
490
|
+
else {
|
|
491
|
+
result.skippedGrants += 1;
|
|
492
|
+
}
|
|
493
|
+
}));
|
|
494
|
+
return result;
|
|
495
|
+
})().finally(() => {
|
|
496
|
+
keepAliveInFlight = null;
|
|
497
|
+
});
|
|
498
|
+
return keepAliveInFlight;
|
|
499
|
+
}
|
|
500
|
+
/** Start refreshing saved Claude account sessions for the server lifetime. */
|
|
501
|
+
export function initClaudeCredentialKeepAlive() {
|
|
502
|
+
if (keepAliveTimer || process.env.CLAUDE_CREDENTIAL_KEEPALIVE === '0')
|
|
503
|
+
return;
|
|
504
|
+
void runClaudeCredentialKeepAliveNow().catch((err) => {
|
|
505
|
+
log.warn(`Initial Claude credential keepalive failed: ${err}`);
|
|
506
|
+
});
|
|
507
|
+
keepAliveTimer = setInterval(() => {
|
|
508
|
+
void runClaudeCredentialKeepAliveNow().catch((err) => {
|
|
509
|
+
log.warn(`Claude credential keepalive pass failed: ${err}`);
|
|
510
|
+
});
|
|
511
|
+
}, KEEP_ALIVE_INTERVAL_MS);
|
|
512
|
+
keepAliveTimer.unref?.();
|
|
513
|
+
log.log('Claude credential keepalive started');
|
|
514
|
+
}
|
|
515
|
+
/** Stop the keepalive scheduler during graceful shutdown. */
|
|
516
|
+
export function shutdownClaudeCredentialKeepAlive() {
|
|
517
|
+
if (keepAliveTimer)
|
|
518
|
+
clearInterval(keepAliveTimer);
|
|
519
|
+
keepAliveTimer = null;
|
|
520
|
+
}
|
|
521
|
+
export function resetClaudeCredentialKeepAliveForTests() {
|
|
522
|
+
shutdownClaudeCredentialKeepAlive();
|
|
523
|
+
keepAliveFailureCooldown.clear();
|
|
524
|
+
keepAliveInFlight = null;
|
|
525
|
+
}
|
|
367
526
|
function assertValidName(name) {
|
|
368
527
|
if (!isValidProfileName(name)) {
|
|
369
528
|
throw new Error(`Invalid profile name "${name}". Use 1–64 chars: letters, digits, dot, underscore, hyphen; must start with alphanumeric.`);
|
|
@@ -328,12 +328,16 @@ export async function fetchClaudeRateLimitsForToken(accessToken) {
|
|
|
328
328
|
};
|
|
329
329
|
}
|
|
330
330
|
const body = await response.json();
|
|
331
|
+
// Claude Code 2.1.207 labels this allowance "Fable", although the live
|
|
332
|
+
// endpoint still exposes it under the legacy seven_day_sonnet key.
|
|
333
|
+
const sevenDayFable = parseRateLimitWindow(body.seven_day_fable ?? body.seven_day_sonnet);
|
|
331
334
|
return {
|
|
332
335
|
rateLimits: {
|
|
333
336
|
fiveHour: parseRateLimitWindow(body.five_hour),
|
|
334
337
|
sevenDay: parseRateLimitWindow(body.seven_day),
|
|
335
338
|
sevenDayOpus: parseRateLimitWindow(body.seven_day_opus),
|
|
336
|
-
|
|
339
|
+
sevenDayFable,
|
|
340
|
+
sevenDaySonnet: sevenDayFable,
|
|
337
341
|
},
|
|
338
342
|
error: null,
|
|
339
343
|
status: response.status,
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
import { spawn } from 'child_process';
|
|
2
|
+
import { getCodexBinaryPath } from './system-prompt-service.js';
|
|
3
|
+
export function classifyCodexRateLimits(raw) {
|
|
4
|
+
const result = { daily: null, weekly: null };
|
|
5
|
+
for (const value of [raw?.primary, raw?.secondary]) {
|
|
6
|
+
if (!value || typeof value.usedPercent !== 'number')
|
|
7
|
+
continue;
|
|
8
|
+
const mins = typeof value.windowDurationMins === 'number' ? value.windowDurationMins : null;
|
|
9
|
+
const resetsAtSeconds = typeof value.resetsAt === 'number' ? value.resetsAt : 0;
|
|
10
|
+
const window = {
|
|
11
|
+
utilization: Math.max(0, Math.min(100, value.usedPercent)),
|
|
12
|
+
resetsAt: resetsAtSeconds > 0 ? new Date(resetsAtSeconds * 1000).toISOString() : '',
|
|
13
|
+
windowDurationMins: mins,
|
|
14
|
+
};
|
|
15
|
+
// App-server identifies windows by duration rather than by name. Anything
|
|
16
|
+
// up to two days is the daily allowance; longer periods are weekly.
|
|
17
|
+
if (mins !== null && mins <= 2 * 24 * 60)
|
|
18
|
+
result.daily = window;
|
|
19
|
+
else
|
|
20
|
+
result.weekly = window;
|
|
21
|
+
}
|
|
22
|
+
return result;
|
|
23
|
+
}
|
|
24
|
+
function readNativeRateLimits() {
|
|
25
|
+
return new Promise((resolve, reject) => {
|
|
26
|
+
const child = spawn(getCodexBinaryPath(), ['app-server'], { stdio: ['pipe', 'pipe', 'pipe'], env: process.env });
|
|
27
|
+
let stdout = '';
|
|
28
|
+
let stderr = '';
|
|
29
|
+
let settled = false;
|
|
30
|
+
const finish = (err, value) => {
|
|
31
|
+
if (settled)
|
|
32
|
+
return;
|
|
33
|
+
settled = true;
|
|
34
|
+
clearTimeout(timer);
|
|
35
|
+
child.kill('SIGTERM');
|
|
36
|
+
if (err)
|
|
37
|
+
reject(err);
|
|
38
|
+
else
|
|
39
|
+
resolve(value ?? {});
|
|
40
|
+
};
|
|
41
|
+
const timer = setTimeout(() => finish(new Error('Codex usage request timed out')), 10_000);
|
|
42
|
+
child.on('error', (err) => finish(err));
|
|
43
|
+
child.stderr?.on('data', (chunk) => { stderr += chunk.toString(); });
|
|
44
|
+
child.stdout?.on('data', (chunk) => {
|
|
45
|
+
stdout += chunk.toString();
|
|
46
|
+
const lines = stdout.split('\n');
|
|
47
|
+
stdout = lines.pop() ?? '';
|
|
48
|
+
for (const line of lines) {
|
|
49
|
+
if (!line.trim())
|
|
50
|
+
continue;
|
|
51
|
+
try {
|
|
52
|
+
const message = JSON.parse(line);
|
|
53
|
+
if (message.id === 1 && message.result) {
|
|
54
|
+
child.stdin?.write(`${JSON.stringify({ method: 'initialized', params: {} })}\n`);
|
|
55
|
+
child.stdin?.write(`${JSON.stringify({ id: 2, method: 'account/rateLimits/read', params: {} })}\n`);
|
|
56
|
+
}
|
|
57
|
+
else if (message.id === 2) {
|
|
58
|
+
if (message.error)
|
|
59
|
+
finish(new Error(message.error.message || 'Codex rejected the usage request'));
|
|
60
|
+
else
|
|
61
|
+
finish(undefined, message.result?.rateLimits ?? {});
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
catch { /* wait for the next complete JSON line */ }
|
|
65
|
+
}
|
|
66
|
+
});
|
|
67
|
+
child.on('close', (code) => {
|
|
68
|
+
if (!settled)
|
|
69
|
+
finish(new Error(stderr.trim() || `Codex app-server exited with code ${code}`));
|
|
70
|
+
});
|
|
71
|
+
child.stdin?.write(`${JSON.stringify({
|
|
72
|
+
id: 1,
|
|
73
|
+
method: 'initialize',
|
|
74
|
+
params: { clientInfo: { name: 'tide-commander', title: 'Tide Commander', version: '1.0.0' }, capabilities: { experimentalApi: true } },
|
|
75
|
+
})}\n`);
|
|
76
|
+
});
|
|
77
|
+
}
|
|
78
|
+
let cache = null;
|
|
79
|
+
export async function buildCodexUsageSnapshot(_agent) {
|
|
80
|
+
if (cache && cache.expiresAt > Date.now())
|
|
81
|
+
return cache.snapshot;
|
|
82
|
+
let snapshot;
|
|
83
|
+
try {
|
|
84
|
+
const rateLimits = classifyCodexRateLimits(await readNativeRateLimits());
|
|
85
|
+
snapshot = { provider: 'codex', fetchedAt: Date.now(), rateLimits, rateLimitsError: null, cliHint: 'Run /status in Codex to see current usage limits.' };
|
|
86
|
+
}
|
|
87
|
+
catch (err) {
|
|
88
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
89
|
+
snapshot = { provider: 'codex', fetchedAt: Date.now(), rateLimits: null, rateLimitsError: message, cliHint: 'Run /status in Codex to see current usage limits.' };
|
|
90
|
+
}
|
|
91
|
+
cache = { expiresAt: Date.now() + 60_000, snapshot };
|
|
92
|
+
return snapshot;
|
|
93
|
+
}
|
package/package.json
CHANGED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
:root{--bg-primary: #0d0d14;--bg-secondary: #14141e;--bg-tertiary: #1c1c28;--border-color: #2a2a3a;--text-primary: #d0d0d8;--text-secondary: #8a8a98;--text-muted: #606070;--accent-blue: #5a8fd4;--accent-green: #5cb88a;--accent-orange: #d4a05a;--accent-red: #d45a5a;--accent-purple: #8a6fbf;--accent-cyan: #5ab8c8;--accent-claude: #a06848;--accent-claude-light: #c8896a;--accent-pink: #5a8fd4;--accent-yellow: #d0d0d8;--font-size-base: 14px;--line-height-base: 1.5;--msg-user-bg: color-mix(in srgb, #5ab8c8 8%, transparent);--msg-user-highlight-bg: var(--msg-user-bg);--msg-user-border: #5ab8c8;--msg-user-text: #d0d0d8;--msg-assistant-bg: color-mix(in srgb, #5cb88a 8%, transparent);--msg-assistant-border: #5cb88a;--msg-assistant-text: #d0d0d8;--tool-use-bg: color-mix(in srgb, #d4a05a 8%, transparent);--tool-use-border: #d4a05a;--tool-use-text: #d4a05a;--tool-use-name: #d4a05a;--tool-result-bg: color-mix(in srgb, #8a6fbf 6%, transparent);--tool-result-border: #8a6fbf;--tool-result-text: #5cb88a;--context-bar-bg: #1c1c28;--context-bar-fill: #8a6fbf;--output-line-bg: transparent;--task-label-color: #5ab8c8}.view-mode-toggle{display:flex;gap:2px;position:relative;background:#0009;border-radius:6px;padding:3px;border:1px solid #2a2a3a}.view-mode-toggle__indicator{position:absolute;top:3px;bottom:3px;background:#d4a05a2e;border-radius:4px;transition:left .2s cubic-bezier(.4,0,.2,1),width .2s cubic-bezier(.4,0,.2,1);pointer-events:none;z-index:0}.view-mode-toggle__btn{display:flex;align-items:center;gap:5px;padding:5px 10px;border:none;border-radius:4px;background:transparent;color:#8a8a98;font-size:11px;font-weight:600;font-family:JetBrains Mono,SF Mono,Fira Code,Consolas,monospace;cursor:pointer;transition:color .15s ease,background .15s ease;white-space:nowrap;position:relative;z-index:1}.view-mode-toggle__btn:hover:not(.view-mode-toggle__btn--active){background:#ffffff14;color:#d0d0d8}.view-mode-toggle__btn--active{color:#d4a05a}.view-mode-toggle__btn--active svg{stroke:#d4a05a}.view-mode-toggle__icon{display:flex;align-items:center;justify-content:center;flex-shrink:0}.view-mode-toggle__label{line-height:1}.view-mode-toggle--floating{position:fixed;bottom:70px;left:60px;z-index:150;box-shadow:0 2px 8px #0000004d}@media(max-width:768px){.view-mode-toggle__btn{padding:10px 14px;min-height:44px;min-width:44px;font-size:12px;touch-action:manipulation;-webkit-tap-highlight-color:transparent}.view-mode-toggle__btn:active{opacity:.7;transform:scale(.95)}.view-mode-toggle__icon svg{width:16px;height:16px}.view-mode-toggle__label{font-size:11px}}@media(max-width:480px){.view-mode-toggle--floating{padding:3px}.view-mode-toggle__btn{padding:8px 10px;min-height:44px}.view-mode-toggle__label{display:none}}.app.view-mode-flat .guake-terminal{display:none}.flat-view{--flat-middle-width: 1fr;--flat-inspector-width: 320px;display:grid;grid-template-columns:var(--flat-middle-width) 3px 1fr;height:100%;width:100%;background:#282a36;overflow:hidden;padding-left:64px;box-sizing:border-box;transition:grid-template-columns .25s ease}.flat-view--with-inspector{grid-template-columns:var(--flat-middle-width) 3px 1fr 3px var(--flat-inspector-width)}.flat-splitter{position:relative;cursor:col-resize;background:#ffffff0a;touch-action:none;-webkit-user-select:none;user-select:none;transition:background .15s ease;z-index:2}.flat-splitter:before{content:"";position:absolute;top:0;bottom:0;left:-5px;right:-5px}.flat-splitter:hover{background:#8be9fd73}body.flat-splitter-dragging{cursor:col-resize!important;-webkit-user-select:none!important;user-select:none!important}body.flat-splitter-dragging .flat-splitter{background:#8be9fd99}body.flat-splitter-dragging .flat-view{transition:none!important}.flat-middle{display:flex;flex-direction:column;background:#282a36;border-right:1px solid rgba(255,255,255,.06);overflow:hidden;min-width:280px}.flat-middle__header{display:flex;align-items:center;justify-content:space-between;padding:12px 16px;border-bottom:1px solid rgba(255,255,255,.06);flex-shrink:0;gap:12px}.flat-middle__title{font-size:14px;font-weight:600;color:#f8f8f2;margin:0}.flat-middle__actions{display:flex;gap:6px;flex-shrink:0}.flat-middle__content{flex:1;overflow-y:auto;padding:12px;display:flex;flex-direction:column;gap:8px}.flat-middle__content::-webkit-scrollbar{width:6px}.flat-middle__content::-webkit-scrollbar-track{background:transparent}.flat-middle__content::-webkit-scrollbar-thumb{background:#ffffff1a;border-radius:3px}.flat-middle__content::-webkit-scrollbar-thumb:hover{background:#fff3}.flat-cta-btn{padding:4px 10px;font-size:11px;font-weight:500;border:none;border-radius:4px;cursor:pointer;transition:all .15s ease;white-space:nowrap}.flat-cta-btn--agent{background:#50fa7b26;color:#50fa7b}.flat-cta-btn--agent:hover{background:#50fa7b40}.flat-cta-btn--boss{background:#ffd70026;color:gold}.flat-cta-btn--boss:hover{background:#ffd70040}.flat-cta-btn--building{background:#8be9fd26;color:#8be9fd}.flat-cta-btn--building:hover{background:#8be9fd40}.flat-cta-btn--area{background:#bd93f926;color:#bd93f9}.flat-cta-btn--area:hover{background:#bd93f940}.flat-middle .agent-overview-panel{position:relative;inset:auto;width:100%;height:100%;border-right:none;flex:1;min-height:0;z-index:auto}.flat-middle .agent-overview-panel .aop-row-controls .close-btn{display:none}.flat-middle__content:has(>.agent-overview-panel){padding:0;overflow:hidden}.flat-right{display:flex;flex-direction:column;background:#21222c;overflow:hidden}.flat-chat{display:flex;flex-direction:column;height:100%}.flat-chat--empty{align-items:center;justify-content:center}.flat-chat__placeholder{display:flex;flex-direction:column;align-items:center;gap:12px;color:#6272a4}.flat-chat__placeholder-icon{font-size:48px;opacity:.5}.flat-chat__placeholder-text{font-size:14px}.flat-map{display:flex;flex-direction:column;height:100%;padding:12px 16px;gap:10px;overflow-y:auto;position:relative}.flat-map::-webkit-scrollbar{width:6px}.flat-map::-webkit-scrollbar-track{background:transparent}.flat-map::-webkit-scrollbar-thumb{background:#ffffff1a;border-radius:3px}.flat-map::-webkit-scrollbar-thumb:hover{background:#fff3}.flat-map__header{display:flex;align-items:baseline;justify-content:space-between;gap:12px;flex-shrink:0;padding-right:130px}.flat-map__title{font-size:13px;font-weight:600;color:#f8f8f2}.flat-map__hint{font-size:11px;color:#6272a4}.flat-map__stats{display:none}.flat-map__view-mode{position:absolute;top:10px;right:12px;z-index:5;background:color-mix(in srgb,#1e1f29 92%,transparent);backdrop-filter:blur(6px);-webkit-backdrop-filter:blur(6px);border-radius:999px;box-shadow:0 2px 8px #0000004d}.flat-map__grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(220px,1fr));gap:10px;align-content:start}.flat-map__empty{grid-column:1/-1;display:flex;align-items:center;justify-content:center;padding:40px;color:#6272a4;font-size:13px}.flat-map-area-card{display:flex;flex-direction:column;position:relative;isolation:isolate;background:color-mix(in srgb,var(--area-color, #6272a4) 12%,#363848);border:1px solid color-mix(in srgb,var(--area-color, #6272a4) 38%,transparent);border-radius:8px;overflow:hidden;transition:border-color .15s ease,box-shadow .15s ease,background .15s ease}.flat-map-area-card:hover{border-color:color-mix(in srgb,var(--area-color, #6272a4) 58%,transparent);box-shadow:0 2px 8px #00000040}.flat-map-area-card__header{display:flex;align-items:center;gap:6px;padding:6px 8px;background:transparent;border:none;cursor:pointer;width:100%;text-align:left;color:#f8f8f2;font-size:12px;font-weight:600;transition:background .15s ease}.flat-map-area-card__header:hover{background:#ffffff0a}.flat-map-area-card__color{width:8px;height:8px;border-radius:50%;flex-shrink:0}.flat-map-area-card__logo{position:absolute;right:8px;bottom:8px;width:88px;height:88px;max-width:55%;max-height:72%;object-fit:contain;opacity:.3;pointer-events:none;-webkit-user-select:none;user-select:none;z-index:-1;border-radius:4px}.flat-map-area-card__name{flex:1 1 auto;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.flat-map-area-card__count{display:inline-flex;align-items:center;gap:3px;font-size:10px;font-weight:500;color:#6272a4;background:#ffffff0f;padding:1px 5px;border-radius:8px;flex-shrink:0}.flat-map-area-card__caret{font-size:10px;color:#6272a4;flex-shrink:0;width:12px;text-align:center}.flat-map-area-card__agents{display:flex;flex-wrap:wrap;gap:4px;padding:0 8px 8px}.flat-map-agent-chip{position:relative;display:inline-flex;align-items:center;gap:5px;padding:3px 7px 6px;background:#ffffff0a;border:1px solid rgba(255,255,255,.06);border-radius:10px;cursor:pointer;color:#9a9caa;font-size:11px;text-align:left;transition:background .15s ease,color .15s ease,border-color .15s ease;white-space:nowrap;min-width:0;overflow:hidden}.flat-map-agent-chip:hover{background:#ffffff14;border-color:#ffffff1f;color:#f8f8f2}.flat-map-agent-chip.working{overflow:visible;isolation:isolate;background-color:#4a9eff1f;background-image:linear-gradient(100deg,transparent 30%,rgba(180,230,255,.45) 49%,rgba(139,220,240,.3) 51%,transparent 70%),linear-gradient(100deg,transparent 32%,rgba(139,233,253,.3) 50%,transparent 68%);background-size:220% 100%,260% 100%;background-repeat:no-repeat,no-repeat;background-position:150% 0,150% 0;border-color:#8be9fdb3;color:#f8f8f2;box-shadow:0 0 8px #4a9eff59,inset 0 0 0 1px #8be9fd00;transform-origin:center;will-change:transform,background-position,box-shadow,border-color;animation:flat-map-agent-working-shimmer .75s linear infinite,flat-map-agent-working-shimmer-2 1.15s linear infinite,flat-map-agent-working-pulse 1.1s ease-in-out infinite,flat-map-agent-working-beat .85s ease-in-out infinite,flat-map-agent-working-iris 3.2s linear infinite}.flat-map-agent-chip.working:before{content:"";position:absolute;top:-2px;right:-2px;bottom:-2px;left:-2px;border-radius:inherit;pointer-events:none;z-index:-1;background:conic-gradient(from 0deg,#8be9fd00,#8bc8fd8c,#a082e680,#dc82c873,#dcb48266,#8bc8fd8c,#8be9fd00);filter:blur(8px);transform-origin:center;will-change:transform,opacity;animation:flat-map-agent-working-spin 3.8s linear infinite,flat-map-agent-working-breath 2.6s ease-in-out infinite}.flat-map-agent-chip.working:after{content:"";position:absolute;top:0;right:0;bottom:0;left:0;border-radius:inherit;pointer-events:none;z-index:1;background-image:radial-gradient(1px 1px at 18% 32%,rgba(255,255,255,.7) 0%,transparent 60%),radial-gradient(1px 1px at 72% 58%,rgba(200,240,255,.65) 0%,transparent 60%),radial-gradient(1.5px 1.5px at 42% 80%,rgba(220,200,255,.55) 0%,transparent 60%),radial-gradient(1px 1px at 88% 22%,rgba(255,255,255,.6) 0%,transparent 60%),radial-gradient(1px 1px at 12% 70%,rgba(210,190,255,.55) 0%,transparent 60%),radial-gradient(1px 1px at 58% 18%,rgba(255,255,255,.55) 0%,transparent 60%);mix-blend-mode:screen;opacity:.5;animation:flat-map-agent-working-twinkle 1.9s ease-in-out infinite}.flat-map-agent-chip.working .flat-map-agent-chip__dot{position:relative;z-index:2;box-shadow:0 0 6px #4a9effb3;animation:flat-map-agent-working-dot .65s ease-in-out infinite}.flat-map-agent-chip.working .flat-map-agent-chip__name,.flat-map-agent-chip.working .flat-map-agent-chip__provider-icon,.flat-map-agent-chip.working .flat-map-agent-chip__crown{position:relative;z-index:2}@keyframes flat-map-agent-working-shimmer{0%{background-position:150% 0,150% 0}to{background-position:-70% 0,150% 0}}@keyframes flat-map-agent-working-shimmer-2{0%{background-position:150% 0,150% 0}to{background-position:150% 0,-70% 0}}@keyframes flat-map-agent-working-pulse{0%,to{border-color:#4a9eff8c;box-shadow:0 0 6px #4a9eff59,inset 0 0 0 1px #8be9fd00}50%{border-color:#b4faff;box-shadow:0 0 18px #4a9effcc,0 0 30px #8be9fd59,inset 0 0 0 1px #b4faff8c}}@keyframes flat-map-agent-working-beat{0%,to{transform:scale(1)}35%{transform:scale(1.035)}55%{transform:scale(.99)}75%{transform:scale(1.015)}}@keyframes flat-map-agent-working-dot{0%,to{transform:scale(1);box-shadow:0 0 6px #4a9effb3}50%{transform:scale(1.7);box-shadow:0 0 12px #b4faff,0 0 22px #4a9effb3}}@keyframes flat-map-agent-working-iris{0%{border-color:#8be9fdb3}25%{border-color:#b482ffd9}50%{border-color:#ff78dccc}75%{border-color:#ffc878bf}to{border-color:#8be9fdb3}}@keyframes flat-map-agent-working-spin{0%{transform:rotate(0)}to{transform:rotate(360deg)}}@keyframes flat-map-agent-working-breath{0%,to{opacity:.3;filter:blur(7px)}40%{opacity:.7;filter:blur(11px)}70%{opacity:.45;filter:blur(8px)}}@keyframes flat-map-agent-working-twinkle{0%,to{opacity:.25;transform:translate(0) scale(.9)}25%{opacity:1;transform:translate(1px,-1px) scale(1.1)}50%{opacity:.55;transform:translate(-1px,1px) scale(.95)}75%{opacity:1;transform:translate(1px,1px) scale(1.15)}}@media(prefers-reduced-motion:reduce){.flat-map-agent-chip.working{animation:flat-map-agent-working-shimmer 5s linear infinite;background-image:linear-gradient(110deg,transparent 40%,rgba(139,233,253,.3) 50%,transparent 60%),linear-gradient(110deg,transparent 40%,rgba(139,233,253,0) 50%,transparent 60%);transform:none}.flat-map-agent-chip.working:before,.flat-map-agent-chip.working:after{animation:none;opacity:.3}.flat-map-agent-chip.working .flat-map-agent-chip__dot{animation:none}}.flat-map-agent-chip__name{min-width:0;overflow:hidden;text-overflow:ellipsis}.flat-map-agent-chip__dot{width:6px;height:6px;border-radius:50%;flex-shrink:0}.flat-map-agent-chip__provider-icon{width:12px;height:12px;object-fit:contain;flex-shrink:0;filter:drop-shadow(0 1px 2px rgba(0,0,0,.25))}.flat-map-agent-chip__crown{display:inline-flex;align-items:center;justify-content:center;flex-shrink:0}.flat-map-agent-chip__context-bar{position:absolute;left:0;right:0;bottom:0;height:2px;background:#ffffff0f;pointer-events:none}.flat-map-agent-chip__context-bar-fill{display:block;height:100%;transition:width .2s ease,background-color .2s ease}.flat-map-area-card__buildings,.flat-map-area-card__folders{display:flex;flex-wrap:wrap;gap:4px;padding:6px 8px 8px;border-top:1px dashed rgba(255,255,255,.05);margin-top:2px}.flat-map-folder-chip{display:inline-flex;align-items:center;gap:5px;padding:3px 7px;background:#ffffff06;border:1px solid rgba(255,255,255,.06);border-radius:8px;cursor:pointer;color:#9a9caa;font-size:11px;font-family:JetBrains Mono,Fira Code,monospace;text-align:left;transition:background .15s ease,color .15s ease,border-color .15s ease;white-space:nowrap;min-width:0;max-width:100%;overflow:hidden}.flat-map-folder-chip:hover{background:#ffffff12;border-color:#ffffff2e;color:#f8f8f2}.flat-map-folder-chip__name{min-width:0;overflow:hidden;text-overflow:ellipsis}.flat-map-building-chip{display:inline-flex;align-items:center;gap:5px;padding:3px 7px;background:#ffffff06;border:1px dashed rgba(255,255,255,.08);border-left:2px solid var(--building-type-color, rgba(255, 255, 255, .08));border-radius:8px;cursor:pointer;color:#9a9caa;font-size:11px;text-align:left;transition:background .15s ease,color .15s ease,border-color .15s ease;white-space:nowrap;min-width:0;overflow:hidden}.flat-map-building-chip:hover{background:#ffffff12;border-color:#ffffff2e;border-left-color:var(--building-type-color, rgba(255, 255, 255, .18));color:#f8f8f2}.flat-map-building-chip__name{min-width:0;overflow:hidden;text-overflow:ellipsis}.flat-map-building-chip__dot{width:6px;height:6px;border-radius:50%;flex-shrink:0}.flat-map-building-chip--error{border-color:#ff4a4a66}.flat-map-building-chip--running{border-style:solid;border-color:#4aff9e40}.flat-terminal-wrapper{display:flex;flex-direction:column;height:100%;min-height:0;position:relative;background:#21222c;overflow:hidden;--guake-side-panel-width: 380px}.flat-terminal-wrapper--with-side-panel>*:not(.guake-git-panel):not(.guake-buildings-panel):not(.guake-workflow-panel):not(.agent-debug-panel){margin-right:var(--guake-side-panel-width);transition:margin-right .2s ease}.flat-terminal-wrapper__header{display:flex;flex-direction:row;align-items:center;gap:12px;padding:10px 12px 6px;border-bottom:1px solid rgba(255,255,255,.06);background:#1e1f29;flex-shrink:0;min-height:40px;overflow:visible}.flat-terminal-wrapper__header-main{display:flex;align-items:center;gap:10px;min-width:0;flex:1 1 auto;overflow:visible;padding:4px 6px;margin:0;background:transparent;border:1px solid transparent;border-radius:8px;color:inherit;font:inherit;text-align:left;cursor:pointer;transition:background .15s ease,border-color .15s ease}.flat-terminal-wrapper__header-main:hover{background:#ffffff0a;border-color:#ffffff0f}.flat-terminal-wrapper__header-main--active{background:#bd93f92e;border-color:#bd93f973}.flat-terminal-wrapper__header-main--active:hover{background:#bd93f942}.flat-terminal-wrapper__header-avatar{display:inline-flex;align-items:center;justify-content:center;flex-shrink:0;position:relative;width:28px;height:28px;border-radius:8px;overflow:visible;transform-origin:center center;border:1.5px solid transparent;box-sizing:border-box}.flat-terminal-wrapper__header-avatar>*{display:block;width:100%;height:100%;object-fit:cover;border-radius:7px}.flat-terminal-wrapper__header-avatar.is-working{border-color:#8be9fd8c;animation:flat-header-avatar-sway 1.2s ease-in-out infinite!important}html.low-power .flat-terminal-wrapper__header-avatar.is-working{animation:none!important}.flat-terminal-wrapper__header-avatar.is-working{z-index:1;will-change:transform;transform-origin:center center}.flat-terminal-wrapper__header-meta{display:flex;align-items:center;justify-content:flex-end;gap:6px;flex:0 0 auto;flex-wrap:nowrap}.flat-terminal-wrapper__statusbar{display:flex;align-items:center;gap:10px;padding:6px 12px;flex-shrink:0;background:#1e1f29;border-top:1px solid rgba(255,255,255,.06);min-height:32px;box-sizing:border-box}.flat-terminal-wrapper__statusbar .flat-terminal-wrapper__cwd,.flat-terminal-wrapper__statusbar .flat-terminal-wrapper__context{background:transparent;border-color:#ffffff0a}.flat-terminal-wrapper__statusbar-spacer{flex:1 1 auto}.flat-terminal-wrapper__buildings{display:inline-flex;align-items:center;gap:4px;padding:2px;background:#ffffff0a;border:1px solid rgba(255,255,255,.06);border-radius:10px}.flat-terminal-wrapper__building-btn{display:inline-flex;align-items:center;justify-content:center;width:26px;height:22px;padding:0;color:#6272a4;background:transparent;border:none;border-radius:7px;cursor:pointer;transition:background .15s ease,color .15s ease}.flat-terminal-wrapper__building-btn:hover{color:#f8f8f2;background:#ffffff14}.flat-terminal-wrapper__building-btn--offline{color:#ffb86cbf}.flat-terminal-wrapper__building-btn--offline:hover{color:#ffb86c}.flat-terminal-wrapper__building-btn--active{color:#f8f8f2;background:#bd93f947;box-shadow:0 0 0 1px #bd93f973}.flat-terminal-wrapper__building-btn--active:hover{background:#bd93f95c}.flat-terminal-wrapper__building-btn--tests-working{color:#8be9fd;background:#8be9fd24;box-shadow:0 0 0 1px #8be9fd66;animation:flat-tests-glow 1.4s ease-in-out infinite}.flat-terminal-wrapper__building-btn--tests-working svg{animation:flat-tests-shake 1.1s ease-in-out infinite}.flat-terminal-wrapper__building-btn--tests-working:hover{color:#8be9fd;background:#8be9fd38}.flat-terminal-wrapper__detached{display:inline-flex;align-items:center;gap:6px;padding:3px 9px;font-size:11px;font-weight:500;color:#ffb86c;background:#ffb86c1f;border:1px solid rgba(255,184,108,.35);border-radius:10px;animation:blink 1.6s ease-in-out infinite}.flat-terminal-wrapper__area-dir{display:inline-flex;align-items:center;gap:5px;padding:3px 8px;font-size:11px;font-family:SF Mono,Menlo,Consolas,monospace;color:#9a9caa;background:#ffffff08;border:1px solid rgba(255,255,255,.05);border-radius:10px;cursor:pointer;max-width:240px;min-width:0;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;transition:background .15s ease,color .15s ease,border-color .15s ease}.flat-terminal-wrapper__area-dir:hover{background:#ffffff14;color:#f8f8f2;border-color:#ffffff24}.flat-terminal-wrapper__area-dir-name{overflow:hidden;text-overflow:ellipsis}.flat-terminal-wrapper__area-dir-branch{display:inline-flex;align-items:center;gap:3px;color:#8be9fd;opacity:.85}.flat-terminal-wrapper__branch-ahead{display:inline-flex;align-items:center;color:#50fa7b}.flat-terminal-wrapper__branch-behind{display:inline-flex;align-items:center;color:#ffb86c}.flat-terminal-wrapper__area-fetch{display:inline-flex;align-items:center;padding:1px 3px;border-radius:4px;color:#6272a4;cursor:pointer;transition:background .15s ease,color .15s ease}.flat-terminal-wrapper__area-fetch:hover{background:#ffffff14;color:#f8f8f2}.flat-terminal-wrapper__area-fetch--fetching{animation:blink 1s ease-in-out infinite}.flat-terminal-wrapper__context-label{color:#6272a4;margin-right:2px}.flat-terminal-wrapper__context-free{color:#6272a4;font-size:10px}.flat-terminal-wrapper__actions{display:inline-flex;align-items:center;gap:4px;padding:2px;background:#ffffff0a;border:1px solid rgba(255,255,255,.06);border-radius:10px}.flat-terminal-wrapper__action-btn{display:inline-flex;align-items:center;justify-content:center;width:26px;height:24px;padding:0;font-size:12px;line-height:1;color:#6272a4;background:transparent;border:none;border-radius:7px;cursor:pointer;transition:background .15s ease,color .15s ease,box-shadow .15s ease}.flat-terminal-wrapper__action-btn:hover:not(:disabled){color:#f8f8f2;background:#ffffff0f}.flat-terminal-wrapper__action-btn:disabled{opacity:.35;cursor:not-allowed}.flat-terminal-wrapper__action-btn--active{color:#f8f8f2;background:#bd93f947;box-shadow:0 0 0 1px #bd93f973}.flat-terminal-wrapper__action-btn--active:hover{background:#bd93f95c}.flat-terminal-wrapper__action-btn--danger{color:#ff5555d9}.flat-terminal-wrapper__action-btn--danger:hover{color:#f55;background:#ff55551f}.flat-terminal-wrapper__action-btn--confirm{color:#f55;background:#ff55552e;box-shadow:0 0 0 1px #ff555580;animation:blink 1s ease-in-out infinite}.flat-terminal-wrapper__more{position:relative;display:inline-flex}.flat-terminal-wrapper__more-menu{position:absolute;top:calc(100% + 4px);right:0;min-width:220px;padding:4px;background:#363848;border:1px solid rgba(255,255,255,.08);border-radius:8px;box-shadow:0 8px 28px #00000073;z-index:100}.flat-terminal-wrapper__more-item{display:flex;align-items:center;gap:8px;width:100%;padding:6px 10px;font-size:12px;color:#f8f8f2;background:transparent;border:none;border-radius:6px;cursor:pointer;text-align:left;transition:background .15s ease,color .15s ease}.flat-terminal-wrapper__more-item:hover:not(:disabled){background:#ffffff0f}.flat-terminal-wrapper__more-item:disabled{opacity:.45;cursor:not-allowed}.flat-terminal-wrapper__more-item--danger{color:#ff5555e6}.flat-terminal-wrapper__more-item--danger:hover:not(:disabled){color:#f55;background:#ff55551f}.flat-terminal-wrapper__more-divider{height:1px;margin:4px 2px;background:#ffffff0f}.flat-terminal-wrapper__header-info{display:flex;flex-direction:row;align-items:center;gap:6px;min-width:0;flex:1 1 auto;overflow:hidden}.flat-terminal-wrapper__header-name{font-size:13px;font-weight:600;color:#f8f8f2;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;min-width:0;flex:1 1 auto}.flat-terminal-wrapper__header-status{font-size:10px;text-transform:capitalize;flex-shrink:0}.flat-terminal-wrapper__header-task{font-size:11px;color:#6272a4;max-width:220px;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;padding:2px 8px;border-radius:10px;background:#ffffff0a;border:1px solid rgba(255,255,255,.06);flex-shrink:1}.flat-terminal-wrapper__header-model{display:inline-flex;align-items:center;gap:6px;flex-shrink:0;min-width:0}.flat-terminal-wrapper__header-provider-icon{width:16px;height:16px;object-fit:contain;flex-shrink:0;filter:drop-shadow(0 1px 2px rgba(0,0,0,.25))}.flat-terminal-wrapper__header-model-chip{display:inline-flex;align-items:center;gap:4px;padding:2px 8px;font-size:10px;font-family:SF Mono,Menlo,Consolas,monospace;font-weight:600;color:#9a9caa;background:#ffffff0a;border:1px solid rgba(255,255,255,.06);border-radius:999px;white-space:nowrap;max-width:180px;overflow:hidden;text-overflow:ellipsis;line-height:1.4}.flat-terminal-wrapper__header-model-name{color:#f8f8f2;text-transform:lowercase;overflow:hidden;text-overflow:ellipsis}.flat-terminal-wrapper__header-model-sep{opacity:.6}.flat-terminal-wrapper__header-model-effort{color:#bd93f9;text-transform:lowercase}.flat-terminal-wrapper__cwd{display:inline-flex;align-items:center;gap:6px;padding:4px 9px;font-size:11px;color:#9a9caa;background:#ffffff0a;border:1px solid rgba(255,255,255,.06);border-radius:10px;cursor:pointer;max-width:240px;transition:background .15s ease,border-color .15s ease,color .15s ease}.flat-terminal-wrapper__cwd:hover{background:#ffffff14;border-color:#ffffff24;color:#f8f8f2}.flat-terminal-wrapper__cwd-icon{font-size:12px;line-height:1}.flat-terminal-wrapper__cwd-text{white-space:nowrap;overflow:hidden;text-overflow:ellipsis;font-family:SF Mono,Menlo,Consolas,monospace}.flat-terminal-wrapper__context{display:inline-flex;align-items:center;gap:6px;padding:4px 9px;font-size:11px;color:#9a9caa;background:#ffffff0a;border:1px solid rgba(255,255,255,.06);border-radius:10px;cursor:pointer;transition:background .15s ease,border-color .15s ease}.flat-terminal-wrapper__context:hover{background:#ffffff14;border-color:#ffffff24}.flat-terminal-wrapper__context-icon{font-size:12px;line-height:1}.flat-terminal-wrapper__context-bar{position:relative;width:56px;height:5px;border-radius:3px;background:#ffffff14;overflow:hidden}.flat-terminal-wrapper__context-bar-fill{position:absolute;left:0;top:0;height:100%;border-radius:3px;transition:width .2s ease,background-color .2s ease}.flat-terminal-wrapper__context-tokens{font-family:SF Mono,Menlo,Consolas,monospace;font-size:11px;font-weight:600;font-variant-numeric:tabular-nums}.flat-terminal-wrapper__context-warning{font-size:11px;opacity:.8}.flat-terminal-wrapper__view-mode{display:inline-flex;align-items:center;padding:2px;gap:2px;background:#ffffff0a;border:1px solid rgba(255,255,255,.06);border-radius:10px}.flat-terminal-wrapper__view-mode-btn{display:inline-flex;align-items:center;gap:5px;padding:4px 9px;font-size:11px;font-weight:500;color:#6272a4;background:transparent;border:none;border-radius:8px;cursor:pointer;transition:background .15s ease,color .15s ease,box-shadow .15s ease}.flat-terminal-wrapper__view-mode-btn:hover{color:#f8f8f2;background:#ffffff0a}.flat-terminal-wrapper__view-mode-btn--active{color:#f8f8f2;background:#bd93f947;box-shadow:0 0 0 1px #bd93f980,0 1px 3px #00000040}.flat-terminal-wrapper__view-mode-btn--active:hover{background:#bd93f95c}.flat-terminal-wrapper__view-mode-icon{font-size:13px;line-height:1}.flat-terminal-wrapper__view-mode-label{line-height:1}.flat-terminal-wrapper__theme{display:inline-flex;align-items:center}.flat-terminal-wrapper__theme .theme-selector-trigger{padding:4px 9px;font-size:11px;border-radius:10px;background:#ffffff0a;border:1px solid rgba(255,255,255,.06)}.flat-terminal-wrapper__theme .theme-selector-trigger:hover{background:#ffffff14;border-color:#ffffff24}.flat-terminal-wrapper__inspector-toggle{display:inline-flex;align-items:center;gap:5px;padding:4px 9px;font-size:11px;font-weight:500;color:#9a9caa;background:#ffffff0a;border:1px solid rgba(255,255,255,.06);border-radius:10px;cursor:pointer;transition:background .15s ease,border-color .15s ease,color .15s ease,box-shadow .15s ease}.flat-terminal-wrapper__inspector-toggle:hover{color:#f8f8f2;background:#ffffff14;border-color:#ffffff24}.flat-terminal-wrapper__inspector-toggle--active{color:#f8f8f2;background:#bd93f938;border-color:#bd93f98c;box-shadow:0 0 0 1px #bd93f959,0 1px 3px #00000040}.flat-terminal-wrapper__inspector-toggle--active:hover{background:#bd93f952}.flat-terminal-wrapper__inspector-icon{display:inline-flex;align-items:center;justify-content:center;line-height:1}.flat-terminal-wrapper__inspector-label{line-height:1}.flat-terminal-wrapper__close{display:inline-flex;align-items:center;justify-content:center;width:26px;height:26px;padding:0;color:#6272a4;background:#ffffff0a;border:1px solid rgba(255,255,255,.06);border-radius:10px;cursor:pointer;transition:background .15s ease,color .15s ease,border-color .15s ease}.flat-terminal-wrapper__close:hover{color:#f55;background:#ff55551f;border-color:#f556}@media(max-width:1100px){.flat-terminal-wrapper__view-mode-label,.flat-terminal-wrapper__inspector-label{display:none}.flat-terminal-wrapper__cwd{max-width:160px}}.flat-terminal-wrapper .guake-output{flex:1 1 auto;min-height:0;overflow-y:auto;padding:14px 18px}.flat-terminal-wrapper .guake-output::-webkit-scrollbar{width:6px}.flat-terminal-wrapper .guake-output::-webkit-scrollbar-track{background:transparent}.flat-terminal-wrapper .guake-output::-webkit-scrollbar-thumb{background:#ffffff1a;border-radius:3px}.flat-terminal-wrapper .guake-input-container{position:relative;flex-shrink:0;border-top:1px solid rgba(255,255,255,.06);background:#1e1f29}.flat-terminal-wrapper .guake-search-bar,.flat-terminal-wrapper .atp-search-bar{position:sticky;top:0;z-index:10;background:#1e1f29}.flat-terminal-wrapper .guake-handle,.flat-terminal-wrapper .atp-resize-handle{display:none}.flat-terminal-wrapper .compacting-indicator{padding:12px;margin:8px}@keyframes blink{0%,to{opacity:1}50%{opacity:.3}}.flat-inspector{display:flex;flex-direction:column;min-width:0;min-height:0;height:100%;background:#1e1f29;border-left:1px solid rgba(255,255,255,.06);overflow:hidden;animation:flat-inspector-slide-in .22s ease}.flat-inspector__header{display:flex;align-items:center;justify-content:space-between;padding:10px 14px;border-bottom:1px solid rgba(255,255,255,.06);background:#21222c;flex-shrink:0}.flat-inspector__tabs{display:inline-flex;align-items:center;gap:4px;padding:2px;background:#00000040;border:1px solid rgba(255,255,255,.06);border-radius:6px}.flat-inspector__tab{padding:4px 10px;font-size:12px;font-weight:500;color:#6272a4;background:transparent;border:none;border-radius:4px;cursor:pointer;transition:background .15s ease,color .15s ease}.flat-inspector__tab:hover{color:#f8f8f2}.flat-inspector__tab--active{color:#f8f8f2;background:#ffffff14}.flat-inspector__close{display:inline-flex;align-items:center;justify-content:center;width:24px;height:24px;padding:0;font-size:13px;color:#6272a4;background:transparent;border:1px solid transparent;border-radius:6px;cursor:pointer;transition:background .15s ease,color .15s ease,border-color .15s ease}.flat-inspector__close:hover{color:#f8f8f2;background:#ffffff0f;border-color:#ffffff1a}.flat-inspector__body{flex:1 1 auto;min-height:0;min-width:0;overflow-y:auto;overflow-x:hidden;display:flex;flex-direction:column}.flat-inspector__body .unit-panel{flex:1 1 auto;min-height:0;min-width:0;padding:10px}.flat-inspector__empty{display:flex;align-items:center;justify-content:center;flex:1;color:#6272a4;font-size:13px}@keyframes flat-inspector-slide-in{0%{opacity:0;transform:translate(12px)}to{opacity:1;transform:translate(0)}}.flat-mobile-sidebar-toggle,.flat-mobile-sidebar-backdrop{display:none}@media(max-width:1024px){.flat-view,.flat-view--with-inspector{grid-template-columns:1fr}.flat-splitter{display:none}.flat-right{grid-column:1/-1}.flat-right>.flat-terminal-wrapper,.flat-right>.flat-chat{flex:1 1 auto;min-height:0;height:auto}.flat-middle{position:fixed;top:0;left:0;bottom:0;width:min(380px,92vw);z-index:185;transform:translate(-110%);transition:transform .22s ease;border-right:1px solid rgba(255,255,255,.08);box-shadow:6px 0 32px #0009}.flat-view--mobile-sidebar-open .flat-middle{transform:translate(0)}.flat-mobile-sidebar-toggle{display:inline-flex;align-items:center;gap:8px;align-self:flex-start;margin:10px 12px 0;padding:8px 12px;font-size:12px;font-weight:500;color:#f8f8f2;background:#363848f2;border:1px solid rgba(255,255,255,.08);border-radius:10px;cursor:pointer;min-height:36px;flex-shrink:0;transition:background .15s ease,border-color .15s ease}.flat-mobile-sidebar-toggle:hover,.flat-mobile-sidebar-toggle:active{background:#bd93f947;border-color:#bd93f980}.flat-mobile-sidebar-toggle__label{line-height:1}.flat-mobile-sidebar-backdrop{display:block;position:fixed;top:0;right:0;bottom:0;left:0;z-index:184;background:#000000ad;-webkit-backdrop-filter:blur(2px);backdrop-filter:blur(2px);animation:flat-inspector-fade-in .18s ease}.flat-inspector{position:fixed;top:0;right:0;bottom:0;left:0;z-index:186;border-left:none;animation:none;transform:translate(100%);transition:transform .22s ease}.flat-inspector--open{transform:translate(0)}.flat-inspector--closing{transform:translate(100%)}.flat-inspector__tab{min-height:36px;padding:6px 14px}.flat-inspector__close{min-width:40px;min-height:40px;display:inline-flex;align-items:center;justify-content:center}}.flat-chat-actions-sheet-root{position:fixed;top:0;right:0;bottom:0;left:0;z-index:2050}.flat-chat-actions-sheet__backdrop{position:absolute;top:0;right:0;bottom:0;left:0;background:#00000073;animation:flat-sheet-fade .15s ease}.flat-chat-actions-sheet{position:absolute;left:0;right:0;bottom:var(--mobile-bottom-stack-height, 72px);display:flex;flex-direction:column;gap:10px;padding:8px 14px calc(14px + env(safe-area-inset-bottom,0px));background:#363848;border:1px solid rgba(255,255,255,.08);border-bottom:none;border-top-left-radius:18px;border-top-right-radius:18px;box-shadow:0 -12px 32px #00000080;animation:flat-sheet-slide-up .2s cubic-bezier(.22,1,.36,1)}.flat-chat-actions-sheet__grabber{width:40px;height:4px;margin:2px auto;border-radius:999px;background:#ffffff38}.flat-chat-actions-sheet__title-row{display:flex;align-items:center;justify-content:space-between;gap:8px}.flat-chat-actions-sheet__title{font-size:13px;font-weight:600;color:#f8f8f2;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.flat-chat-actions-sheet__done{flex-shrink:0;font-size:13px;font-weight:500;color:#bd93f9;background:none;border:none;padding:6px 4px;cursor:pointer}.flat-chat-actions-sheet__grid{display:grid;grid-template-columns:repeat(4,1fr);gap:8px}.flat-chat-actions-sheet__item{display:flex;flex-direction:column;align-items:center;justify-content:center;gap:6px;min-height:64px;padding:8px 4px;font-size:11px;color:#f8f8f2;background:#ffffff0d;border:1px solid rgba(255,255,255,.07);border-radius:12px;cursor:pointer}.flat-chat-actions-sheet__item span{text-align:center;line-height:1.2}.flat-chat-actions-sheet__item.is-active{background:#bd93f93d;border-color:#bd93f980}.flat-chat-actions-sheet__list{display:flex;flex-direction:column;gap:2px}.flat-chat-actions-sheet__row{display:flex;align-items:center;gap:12px;width:100%;min-height:48px;padding:10px 12px;font-size:14px;color:#f8f8f2;background:transparent;border:none;border-radius:10px;cursor:pointer;text-align:left}.flat-chat-actions-sheet__row:active{background:#ffffff0f}.flat-chat-actions-sheet__row:disabled{opacity:.4;cursor:not-allowed}.flat-chat-actions-sheet__row--danger{color:#ff5555e6}.flat-chat-actions-sheet__row.is-armed{color:#f55;background:#ff555529;box-shadow:inset 0 0 0 1px #ff555580}@keyframes flat-sheet-slide-up{0%{transform:translateY(100%)}to{transform:translateY(0)}}@keyframes flat-sheet-fade{0%{opacity:0}to{opacity:1}}@media(max-width:768px){.app.view-mode-flat .mobile-fab-toggle,.app.view-mode-flat .mobile-fab-menu,.app.view-mode-flat .voice-assistant-btn.mobile-voice-assistant{display:none!important}.app.view-mode-flat .flat-view{padding-left:0}.app.view-mode-flat .main-content{height:calc(100% - var(--mobile-bottom-stack-height, 72px))!important;max-height:calc(100% - var(--mobile-bottom-stack-height, 72px))!important;overflow:hidden}.app.view-mode-flat .flat-terminal-wrapper__header,.app.view-mode-flat .flat-inspector__header,.app.view-mode-flat .flat-middle__header,.app.view-mode-flat .flat-map__header{padding-top:calc(6px + env(safe-area-inset-top,0px))}.app.view-mode-flat .flat-terminal-wrapper__header{padding-top:calc(2px + env(safe-area-inset-top,0px))}.app.view-mode-flat .flat-inspector__body,.app.view-mode-flat .flat-middle__content{padding-bottom:var(--mobile-bottom-stack-height, 72px)}.flat-view{padding-left:56px}.flat-middle__header{flex-wrap:wrap;padding:6px 10px;gap:6px}.flat-middle__content{padding:8px}.flat-cta-btn{padding:8px 10px;font-size:11px;min-height:34px}.flat-chat--empty{align-items:stretch;justify-content:flex-start}.flat-map{width:100%;padding:0 10px 12px;gap:8px;overflow-x:hidden;overflow-y:auto;-webkit-overflow-scrolling:touch;touch-action:pan-y}.app.view-mode-flat .flat-map__header{display:grid;grid-template-columns:minmax(0,1fr) auto;grid-template-areas:"title toggle" "stats stats";align-items:center;gap:3px 10px;position:sticky;top:0;z-index:8;margin:0 -10px;padding:calc(6px + env(safe-area-inset-top,0px)) 10px 8px;background:color-mix(in srgb,#282a36 94%,transparent);border-bottom:1px solid rgba(255,255,255,.05);backdrop-filter:blur(10px);-webkit-backdrop-filter:blur(10px)}.flat-map__title{grid-area:title;font-size:15px}.flat-map__hint{display:none}.flat-map__stats{grid-area:stats;display:flex;align-items:center;gap:8px;min-width:0;color:#6272a4;font-size:10px;font-family:SF Mono,Menlo,Consolas,monospace}.flat-map__stats-working{display:inline-flex;align-items:center;gap:4px;color:color-mix(in srgb,#4a9eff 78%,#9a9caa)}.flat-map__stats-dot{width:6px;height:6px;flex-shrink:0;border-radius:50%;background:#4a9eff;box-shadow:0 0 6px #4a9eff8c}.flat-map__view-mode{grid-area:toggle;position:relative;top:auto;right:auto;justify-self:end}.flat-map__view-mode .view-mode-toggle__btn{min-width:40px;min-height:40px;padding:6px 9px}.flat-map__grid{width:100%;grid-template-columns:repeat(2,minmax(0,1fr))!important;gap:8px}.flat-map-area-card{min-width:0;grid-row:auto!important;grid-column:auto!important;border-radius:12px}.flat-map-area-card__header{padding:7px 8px;gap:6px;min-height:48px}.flat-map-area-card__name{font-size:12px}.flat-map-area-card__count{font-size:9px;padding:2px 5px}.flat-map-area-card__logo{position:static;width:22px;height:22px;max-width:22px;max-height:22px;flex-shrink:0;opacity:.82;z-index:0;border-radius:5px}.flat-map-area-card__caret{flex-shrink:0;margin-left:auto;opacity:.7}.flat-map-area-card--collapsed .flat-map-area-card__agents,.flat-map-area-card--collapsed .flat-map-area-card__buildings,.flat-map-area-card--collapsed .flat-map-area-card__folders{display:none}.flat-map-area-card--expanded{grid-column:1/-1!important;border-color:color-mix(in srgb,var(--area-color, #6272a4) 72%,transparent);box-shadow:0 6px 18px #0000004d}.flat-map-area-card__agents{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:6px;padding:2px 8px 8px}.flat-map-agent-chip{width:100%;padding:7px 10px 9px;font-size:12px;min-height:40px}.flat-map-area-card__folders,.flat-map-area-card__buildings{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:6px}.flat-map-folder-chip,.flat-map-building-chip{width:100%;min-height:36px}.flat-terminal-wrapper__header{flex-wrap:nowrap;padding:6px 10px 4px;gap:8px;min-height:40px;overflow:visible}.flat-terminal-wrapper__header-main{flex:1 1 auto;min-width:0;overflow:hidden;display:grid;grid-template-columns:auto minmax(0,1fr);grid-template-areas:"avatar info" "avatar model";align-items:center;column-gap:8px;row-gap:0;padding:2px 4px}.flat-terminal-wrapper__header-avatar{grid-area:avatar}.flat-terminal-wrapper__header-info{grid-area:info;min-width:0;align-self:end}.flat-terminal-wrapper__header-name{flex:1 1 auto;min-width:0}.flat-terminal-wrapper__header-status{font-size:9px;padding:1px 6px;border-radius:999px;background:#ffffff0f;max-width:72px;overflow:hidden;text-overflow:ellipsis}.flat-terminal-wrapper__header-model{grid-area:model;align-self:start;justify-self:start;min-width:0;max-width:100%;gap:4px}.flat-terminal-wrapper__header-provider-icon{width:11px;height:11px}.flat-terminal-wrapper__header-model-chip{padding:0;background:none;border:none;border-radius:0;font-size:9px;max-width:100%}.flat-terminal-wrapper__header-meta{flex:0 0 auto;justify-content:flex-end;flex-wrap:nowrap;gap:4px}.flat-terminal-wrapper__header-task{display:none}.flat-terminal-wrapper__header-avatar,.flat-terminal-wrapper__header-avatar>*{width:28px;height:28px}.flat-terminal-wrapper__action-btn,.flat-terminal-wrapper__building-btn{min-width:36px;min-height:36px}.flat-terminal-wrapper__statusbar{flex-wrap:wrap;padding:6px 10px;gap:6px;row-gap:6px}.flat-terminal-wrapper__action-btn,.flat-terminal-wrapper__building-btn,.flat-terminal-wrapper__close{width:28px;height:28px}.flat-terminal-wrapper__inspector-toggle{padding:4px 6px}.flat-terminal-wrapper__inspector-label,.flat-terminal-wrapper__actions{display:none}.flat-terminal-wrapper{--guake-side-panel-width: 100%;background:var(--bg-primary)}.flat-right{background:var(--bg-primary)}.flat-terminal-wrapper--with-side-panel>*:not(.guake-git-panel):not(.guake-buildings-panel):not(.guake-workflow-panel):not(.agent-debug-panel){margin-right:0}.flat-terminal-wrapper .guake-git-panel,.flat-terminal-wrapper .guake-buildings-panel,.flat-terminal-wrapper .guake-workflow-panel,.flat-terminal-wrapper .agent-debug-panel{width:100%}.flat-bottom-panel{height:180px}.flat-terminal-wrapper__view-mode,.flat-terminal-wrapper__close,.flat-terminal-wrapper__statusbar,.app.view-mode-flat .flat-mobile-sidebar-toggle,.app.view-mode-flat .flat-terminal-wrapper__inspector-toggle{display:none}}@media(max-width:480px){.flat-view{padding-left:52px}.flat-middle{width:100vw;max-width:100vw;box-shadow:none}.flat-middle__header{padding:8px 10px}.flat-middle__actions{gap:4px}.flat-cta-btn{padding:6px 8px;font-size:10px;min-height:32px}.flat-map-area-card__header{padding:8px 10px;font-size:13px}.flat-map-agent-chip{font-size:12px;padding:6px 8px 8px}.flat-terminal-wrapper__header-name{font-size:12px}.flat-terminal-wrapper__header-model-chip{max-width:100%}.flat-inspector__body{padding-bottom:env(safe-area-inset-bottom,0)}}@media(min-width:1400px){.flat-view{--flat-middle-width: minmax(280px, 400px);--flat-inspector-width: 360px}}@keyframes flat-inspector-fade-in{0%{opacity:0}to{opacity:1}}.flat-classic-terminal{flex:1 1 auto;min-height:0;display:flex;position:relative;overflow:hidden;background:#1a1a2e}.flat-classic-terminal .guake-bottom-terminal-embed{flex:1 1 auto;width:100%;height:100%;min-height:0}.flat-classic-terminal--message{align-items:center;justify-content:center;color:#6272a4;font-size:13px}.flat-bottom-panel{display:flex;flex-direction:column;flex-shrink:0;height:250px;min-height:0;background:#1e1f29;border-top:1px solid rgba(255,255,255,.08);overflow:hidden}.flat-bottom-panel__header{display:flex;align-items:center;justify-content:space-between;gap:10px;padding:4px 10px;background:#21222c;border-bottom:1px solid rgba(255,255,255,.06);flex-shrink:0;min-height:28px}.flat-bottom-panel__title{display:inline-flex;align-items:center;gap:6px;font-size:11px;font-weight:500;color:#f8f8f2;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.flat-bottom-panel__muted{color:#6272a4;font-weight:400;font-size:10px}.flat-bottom-panel__header-actions{display:inline-flex;align-items:center;gap:2px;flex-shrink:0}.flat-bottom-panel__close{display:inline-flex;align-items:center;justify-content:center;width:22px;height:22px;padding:0;color:#6272a4;background:transparent;border:none;border-radius:6px;cursor:pointer;transition:background .15s ease,color .15s ease}.flat-bottom-panel__close:hover{color:#f8f8f2;background:#ffffff14}.flat-bottom-panel__body{flex:1 1 auto;min-height:0;display:flex;background:#282a36}.flat-bottom-panel__body>*{flex:1 1 auto;min-height:0}.flat-bottom-panel__placeholder{flex:1 1 auto;display:flex;align-items:center;justify-content:center;color:#6272a4;font-size:12px}@keyframes flat-tests-shake{0%,to{transform:rotate(0)}20%{transform:rotate(-14deg)}40%{transform:rotate(10deg)}60%{transform:rotate(-7deg)}80%{transform:rotate(4deg)}}@keyframes flat-tests-glow{0%,to{box-shadow:0 0 0 1px #8be9fd4d}50%{box-shadow:0 0 6px 1px #8be9fdb3}}@keyframes flat-header-avatar-sway{0%,to{transform:rotate(0)}25%{transform:rotate(-8deg)}75%{transform:rotate(8deg)}}
|