@taskforcehq/taskforce 0.3.319 → 0.3.320
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/TaskforceCore.js +57 -16
- package/dist/server/routes/admin.js +40 -29
- package/dist/server/routes/auth.js +24 -0
- package/dist/server/routes.js +18 -27
- package/dist/ui/assets/{AgentsModule-DxsN8GQI.js → AgentsModule-BZG0WHDN.js} +1 -1
- package/dist/ui/assets/{AnnotatedAttachmentWorkspace-DUkRcfo7.js → AnnotatedAttachmentWorkspace-EQEzsETm.js} +1 -1
- package/dist/ui/assets/{ContextAttachmentManager-D_uqJ94r.js → ContextAttachmentManager-B3hESx6y.js} +1 -1
- package/dist/ui/assets/{DocumentWorkspace-DAsuvSWR.js → DocumentWorkspace-Z7bykYtA.js} +1 -1
- package/dist/ui/assets/{EntityActivityTimeline-QLUNczJS.js → EntityActivityTimeline-B2aXkxiE.js} +1 -1
- package/dist/ui/assets/{InitiativesModule-D23morXe.js → InitiativesModule-CSzXelzW.js} +1 -1
- package/dist/ui/assets/{PlansPage-X6c0yJ1a.js → PlansPage-2vebpTBs.js} +1 -1
- package/dist/ui/assets/{TaskContextUpload-BERMUT_S.js → TaskContextUpload-Bz06La5A.js} +1 -1
- package/dist/ui/assets/{TaskSettings-DlX_5CSg.js → TaskSettings-4PbrcOqD.js} +1 -1
- package/dist/ui/assets/{WorkflowsModule-CVu4lhC1.js → WorkflowsModule-AyOzrP3y.js} +1 -1
- package/dist/ui/assets/documentReferences-CKk8ylqA.js +1 -0
- package/dist/ui/assets/index-rB9GGUCz.js +5 -0
- package/dist/ui/index.html +1 -1
- package/package.json +1 -1
- package/dist/ui/assets/documentReferences-BCXQQ60P.js +0 -1
- package/dist/ui/assets/index-CdlMnBxP.js +0 -5
package/dist/TaskforceCore.js
CHANGED
|
@@ -579,14 +579,44 @@ function TaskforceCoreWithRouter({ config = {}, initialTaskId, onTaskCountChange
|
|
|
579
579
|
persistWorkspaceSyncStatePatch,
|
|
580
580
|
refreshSetupContext
|
|
581
581
|
]);
|
|
582
|
-
const
|
|
582
|
+
const nextTarget = useMemo(() => {
|
|
583
583
|
const raw = new URLSearchParams(location.search).get('next') || '/';
|
|
584
|
-
if (
|
|
585
|
-
|
|
586
|
-
|
|
584
|
+
if (raw.startsWith('/')) {
|
|
585
|
+
if (raw === '/login')
|
|
586
|
+
return '/';
|
|
587
|
+
return raw;
|
|
588
|
+
}
|
|
589
|
+
try {
|
|
590
|
+
const parsed = new URL(raw);
|
|
591
|
+
const host = parsed.hostname.toLowerCase();
|
|
592
|
+
const isLocal = host === 'localhost' || host === '127.0.0.1' || host === '::1';
|
|
593
|
+
const isTaskforceHost = host === 'taskforcehq.ai' || host.endsWith('.taskforcehq.ai');
|
|
594
|
+
if ((parsed.protocol === 'http:' || parsed.protocol === 'https:') && (isLocal || isTaskforceHost)) {
|
|
595
|
+
return parsed.toString();
|
|
596
|
+
}
|
|
597
|
+
}
|
|
598
|
+
catch {
|
|
587
599
|
return '/';
|
|
588
|
-
|
|
600
|
+
}
|
|
601
|
+
return '/';
|
|
589
602
|
}, [location.search]);
|
|
603
|
+
const nextPath = useMemo(() => nextTarget.startsWith('/') ? nextTarget : '/', [nextTarget]);
|
|
604
|
+
const navigateToPostAuthTarget = useCallback((target, options) => {
|
|
605
|
+
if (!target)
|
|
606
|
+
return;
|
|
607
|
+
if (target.startsWith('/')) {
|
|
608
|
+
navigate(target, { replace: options?.replace === true });
|
|
609
|
+
return;
|
|
610
|
+
}
|
|
611
|
+
if (typeof window !== 'undefined') {
|
|
612
|
+
if (options?.replace === true) {
|
|
613
|
+
window.location.replace(target);
|
|
614
|
+
}
|
|
615
|
+
else {
|
|
616
|
+
window.location.assign(target);
|
|
617
|
+
}
|
|
618
|
+
}
|
|
619
|
+
}, [navigate]);
|
|
590
620
|
const shouldReturnToCloudWorkspaceSelection = useMemo(() => {
|
|
591
621
|
try {
|
|
592
622
|
const parsed = new URL(nextPath, 'https://taskforce.local');
|
|
@@ -622,9 +652,9 @@ function TaskforceCoreWithRouter({ config = {}, initialTaskId, onTaskCountChange
|
|
|
622
652
|
interval: selectedSignupPlanVersionId ? resolvedSignupInterval : null
|
|
623
653
|
});
|
|
624
654
|
}
|
|
625
|
-
return
|
|
655
|
+
return nextTarget;
|
|
626
656
|
}, [
|
|
627
|
-
|
|
657
|
+
nextTarget,
|
|
628
658
|
planSelectionPath,
|
|
629
659
|
resolvedSignupInterval,
|
|
630
660
|
selectedSignupPlanId,
|
|
@@ -688,9 +718,13 @@ function TaskforceCoreWithRouter({ config = {}, initialTaskId, onTaskCountChange
|
|
|
688
718
|
? resolveWorkspaceSetupPath()
|
|
689
719
|
: pendingPostAuthRedirect.path;
|
|
690
720
|
const currentRoute = `${location.pathname}${location.search}${location.hash || ''}`;
|
|
691
|
-
if (
|
|
692
|
-
|
|
721
|
+
if (destination.startsWith('/')) {
|
|
722
|
+
if (currentRoute !== destination) {
|
|
723
|
+
navigate(destination, { replace: true });
|
|
724
|
+
}
|
|
725
|
+
return;
|
|
693
726
|
}
|
|
727
|
+
navigateToPostAuthTarget(destination, { replace: true });
|
|
694
728
|
}, [
|
|
695
729
|
authSessionResolved,
|
|
696
730
|
cloudAccountAccessPending,
|
|
@@ -699,6 +733,7 @@ function TaskforceCoreWithRouter({ config = {}, initialTaskId, onTaskCountChange
|
|
|
699
733
|
location.pathname,
|
|
700
734
|
location.search,
|
|
701
735
|
navigate,
|
|
736
|
+
navigateToPostAuthTarget,
|
|
702
737
|
pendingPostAuthRedirect,
|
|
703
738
|
resolveWorkspaceSetupPath
|
|
704
739
|
]);
|
|
@@ -726,9 +761,13 @@ function TaskforceCoreWithRouter({ config = {}, initialTaskId, onTaskCountChange
|
|
|
726
761
|
? pendingPostAuthRedirect.path
|
|
727
762
|
: planSelectionPath;
|
|
728
763
|
const currentRoute = `${location.pathname}${location.search}${location.hash || ''}`;
|
|
729
|
-
if (
|
|
730
|
-
|
|
764
|
+
if (destination.startsWith('/')) {
|
|
765
|
+
if (currentRoute !== destination) {
|
|
766
|
+
navigate(destination, { replace: true });
|
|
767
|
+
}
|
|
768
|
+
return;
|
|
731
769
|
}
|
|
770
|
+
navigateToPostAuthTarget(destination, { replace: true });
|
|
732
771
|
}, [
|
|
733
772
|
accountAccessGate?.canAccessApp,
|
|
734
773
|
accountAccessGateResolved,
|
|
@@ -739,6 +778,7 @@ function TaskforceCoreWithRouter({ config = {}, initialTaskId, onTaskCountChange
|
|
|
739
778
|
location.pathname,
|
|
740
779
|
location.search,
|
|
741
780
|
navigate,
|
|
781
|
+
navigateToPostAuthTarget,
|
|
742
782
|
pendingPostAuthRedirect,
|
|
743
783
|
planSelectionPath
|
|
744
784
|
]);
|
|
@@ -901,8 +941,9 @@ function TaskforceCoreWithRouter({ config = {}, initialTaskId, onTaskCountChange
|
|
|
901
941
|
if (shouldDelayAuthenticatedLoginRedirect) {
|
|
902
942
|
return;
|
|
903
943
|
}
|
|
904
|
-
if (isLoginRoute && isAuthenticated && !shouldStayOnInviteRoute)
|
|
905
|
-
|
|
944
|
+
if (isLoginRoute && isAuthenticated && !shouldStayOnInviteRoute) {
|
|
945
|
+
navigateToPostAuthTarget(nextTarget, { replace: true });
|
|
946
|
+
}
|
|
906
947
|
}
|
|
907
948
|
else {
|
|
908
949
|
if (!isLoginRoute) {
|
|
@@ -1299,7 +1340,7 @@ function TaskforceCoreWithRouter({ config = {}, initialTaskId, onTaskCountChange
|
|
|
1299
1340
|
setLoginBusy(false);
|
|
1300
1341
|
return;
|
|
1301
1342
|
}
|
|
1302
|
-
|
|
1343
|
+
navigateToPostAuthTarget(nextTarget, { replace: true });
|
|
1303
1344
|
}
|
|
1304
1345
|
else if (authMode === 'login') {
|
|
1305
1346
|
if (inviteToken.trim()) {
|
|
@@ -1319,14 +1360,14 @@ function TaskforceCoreWithRouter({ config = {}, initialTaskId, onTaskCountChange
|
|
|
1319
1360
|
setLoginDisplayName('');
|
|
1320
1361
|
setLoginPassword('');
|
|
1321
1362
|
setLoginPasswordConfirm('');
|
|
1322
|
-
|
|
1363
|
+
navigateToPostAuthTarget(nextTarget, { replace: true });
|
|
1323
1364
|
}
|
|
1324
1365
|
else {
|
|
1325
1366
|
setLoginEmail('');
|
|
1326
1367
|
setLoginDisplayName('');
|
|
1327
1368
|
setLoginPassword('');
|
|
1328
1369
|
setLoginPasswordConfirm('');
|
|
1329
|
-
|
|
1370
|
+
navigateToPostAuthTarget(nextTarget, { replace: true });
|
|
1330
1371
|
}
|
|
1331
1372
|
setLoginBusy(false);
|
|
1332
1373
|
}, children: loginBusy ? 'Working...' : (authMode === 'login' ? 'Sign In'
|
|
@@ -262,7 +262,7 @@ export function registerAdminRoutes(deps) {
|
|
|
262
262
|
const scopeLabels = {
|
|
263
263
|
'tasks:read': 'Read tasks',
|
|
264
264
|
'tasks:write': 'Update tasks',
|
|
265
|
-
'workspace:read': 'Read workspace
|
|
265
|
+
'workspace:read': 'Read workspace'
|
|
266
266
|
};
|
|
267
267
|
const options = input.workspaces.map((workspace) => {
|
|
268
268
|
const selected = workspace.id === input.preselectedWorkspaceId ? ' selected' : '';
|
|
@@ -285,25 +285,19 @@ export function registerAdminRoutes(deps) {
|
|
|
285
285
|
:root { color-scheme: dark; }
|
|
286
286
|
body { margin: 0; font-family: "Outfit", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif; background: radial-gradient(circle at top, rgba(255,138,0,0.08), transparent 30%), #111126; color: #f8f7ff; }
|
|
287
287
|
.wrap { min-height: 100vh; display: flex; align-items: center; justify-content: center; padding: 32px 24px; }
|
|
288
|
-
.card { width: min(
|
|
289
|
-
|
|
290
|
-
.
|
|
291
|
-
h1 { margin: 0; font-size: clamp(2.25rem, 5vw, 3rem); line-height: 0.96; letter-spacing: -0.04em; color: #f8f7ff; }
|
|
292
|
-
.brand-word { font-family: "Russo One", sans-serif; font-size: 0.94em; letter-spacing: -0.03em; }
|
|
293
|
-
.lede { margin: 18px 0 8px; font-size: 1.15rem; line-height: 1.5; color: rgba(255,255,255,0.78); max-width: 34ch; }
|
|
288
|
+
.card { width: min(720px, 100%); background: rgba(22,22,44,0.95); border: 1px solid rgba(255,255,255,0.08); border-radius: 32px; box-shadow: 0 24px 80px rgba(0,0,0,0.35); padding: 40px 48px; }
|
|
289
|
+
h1 { margin: 0; font-size: clamp(1.95rem, 4.2vw, 2.6rem); line-height: 1.02; letter-spacing: -0.035em; color: #f8f7ff; }
|
|
290
|
+
.lede { margin: 16px 0 8px; font-size: 1.1rem; line-height: 1.45; color: rgba(255,255,255,0.78); max-width: 48ch; text-wrap: pretty; }
|
|
294
291
|
.lede strong { color: #fff; }
|
|
295
|
-
.
|
|
292
|
+
.session-row { display: flex; align-items: center; gap: 14px; flex-wrap: wrap; margin: 0 0 28px; }
|
|
293
|
+
.user-meta { margin: 0; color: rgba(255,255,255,0.5); font-size: 0.9rem; }
|
|
294
|
+
.account-switch { padding: 0; border: 0; min-width: 0; background: transparent; color: #cbd8ff; font-size: 0.9rem; font-weight: 600; box-shadow: none; }
|
|
295
|
+
.account-switch:hover { transform: none; color: #ffffff; text-decoration: underline; }
|
|
296
296
|
label { display: block; font-size: 13px; font-weight: 700; margin: 24px 0 10px; color: rgba(255,255,255,0.92); letter-spacing: 0.01em; }
|
|
297
297
|
select { width: 100%; border-radius: 18px; border: 1px solid rgba(255,255,255,0.1); background: #1f2039; color: #fff; padding: 16px 18px; font-size: 15px; font-weight: 600; box-shadow: inset 0 1px 0 rgba(255,255,255,0.02); }
|
|
298
298
|
.scopes { display:flex; flex-wrap:wrap; gap:10px; margin-top:8px; }
|
|
299
299
|
.scope-chip { display:inline-flex; padding:8px 14px; border-radius:999px; background:#2a2947; border:1px solid rgba(255,255,255,0.09); font-size:13px; font-weight:600; color:rgba(255,255,255,0.92); }
|
|
300
|
-
.
|
|
301
|
-
.details summary { list-style: none; cursor: pointer; padding: 16px 18px; font-size: 0.92rem; font-weight: 700; color: rgba(255,255,255,0.86); }
|
|
302
|
-
.details summary::-webkit-details-marker { display: none; }
|
|
303
|
-
.details-body { padding: 0 18px 18px; color: rgba(255,255,255,0.68); font-size: 0.9rem; line-height: 1.5; }
|
|
304
|
-
.details-row { margin: 0 0 8px; }
|
|
305
|
-
.details-row strong { color: rgba(255,255,255,0.92); }
|
|
306
|
-
.legal { margin: 24px 2px 0; color: rgba(255,255,255,0.58); font-size: 0.9rem; line-height: 1.55; max-width: 56ch; }
|
|
300
|
+
.legal { margin: 28px 2px 0; color: rgba(255,255,255,0.58); font-size: 0.9rem; line-height: 1.55; max-width: 58ch; }
|
|
307
301
|
.error { margin-bottom: 18px; padding: 12px 14px; border-radius: 14px; background: rgba(255,99,132,0.12); color: #ffb6c6; border: 1px solid rgba(255,99,132,0.25); }
|
|
308
302
|
.actions { display:flex; justify-content:flex-end; gap:14px; margin-top: 36px; }
|
|
309
303
|
button { border-radius: 18px; padding: 15px 22px; font-size: 15px; font-weight: 700; cursor: pointer; border: 1px solid rgba(255,255,255,0.08); min-width: 156px; transition: transform 160ms ease, box-shadow 160ms ease, border-color 160ms ease, background-color 160ms ease; }
|
|
@@ -313,7 +307,8 @@ export function registerAdminRoutes(deps) {
|
|
|
313
307
|
a { color: #cbd8ff; }
|
|
314
308
|
@media (max-width: 720px) {
|
|
315
309
|
.card { padding: 30px 22px; border-radius: 28px; }
|
|
316
|
-
h1 { font-size:
|
|
310
|
+
h1 { font-size: 1.85rem; }
|
|
311
|
+
.lede { max-width: none; }
|
|
317
312
|
.actions { flex-direction: column-reverse; }
|
|
318
313
|
button { width: 100%; }
|
|
319
314
|
}
|
|
@@ -322,22 +317,17 @@ export function registerAdminRoutes(deps) {
|
|
|
322
317
|
<body>
|
|
323
318
|
<div class="wrap">
|
|
324
319
|
<form class="card" method="post" action="/oauth/mcp/authorize">
|
|
325
|
-
<
|
|
326
|
-
<h1>Authorize <span class="brand-word">Taskforce</span> Connector</h1>
|
|
320
|
+
<h1>Authorize Taskforce Connector</h1>
|
|
327
321
|
<p class="lede"><strong>${escapeHtml(input.clientName)}</strong> wants to connect to Taskforce for <strong>${escapeHtml(input.userName)}</strong>.</p>
|
|
328
|
-
|
|
322
|
+
<div class="session-row">
|
|
323
|
+
${input.userMeta ? `<p class="user-meta">Signed in as ${escapeHtml(input.userMeta)}</p>` : ''}
|
|
324
|
+
<button class="account-switch" type="submit" formaction="/api/taskforce/auth/logout" formmethod="post" formnovalidate>Use different account</button>
|
|
325
|
+
</div>
|
|
329
326
|
${input.error ? `<div class="error">${escapeHtml(input.error)}</div>` : ''}
|
|
330
|
-
<label for="workspaceId">Workspace</label>
|
|
327
|
+
<label for="workspaceId">Select Workspace</label>
|
|
331
328
|
<select id="workspaceId" name="workspaceId" required>${options}</select>
|
|
332
329
|
<label>Requested Permissions</label>
|
|
333
330
|
<div class="scopes">${scopeBadges}</div>
|
|
334
|
-
<details class="details">
|
|
335
|
-
<summary>Technical details</summary>
|
|
336
|
-
<div class="details-body">
|
|
337
|
-
<div class="details-row"><strong>Client:</strong> ${escapeHtml(input.clientName)}</div>
|
|
338
|
-
<div class="details-row"><strong>Redirect URI:</strong> ${escapeHtml(input.redirectUri)}</div>
|
|
339
|
-
</div>
|
|
340
|
-
</details>
|
|
341
331
|
<p class="legal">By authorizing this connector, you agree to Taskforce's <a href="${termsUrl}" target="_blank" rel="noopener noreferrer">Terms of Service</a> and acknowledge the <a href="${privacyUrl}" target="_blank" rel="noopener noreferrer">Privacy Policy</a>.</p>
|
|
342
332
|
<input type="hidden" name="client_id" value="${escapeHtml(input.clientId)}" />
|
|
343
333
|
<input type="hidden" name="redirect_uri" value="${escapeHtml(input.redirectUri)}" />
|
|
@@ -345,6 +335,7 @@ export function registerAdminRoutes(deps) {
|
|
|
345
335
|
<input type="hidden" name="state" value="${escapeHtml(input.state)}" />
|
|
346
336
|
<input type="hidden" name="code_challenge" value="${escapeHtml(input.codeChallenge)}" />
|
|
347
337
|
<input type="hidden" name="code_challenge_method" value="${escapeHtml(input.codeChallengeMethod)}" />
|
|
338
|
+
<input type="hidden" name="next" value="${escapeHtml(input.authorizeUrl)}" />
|
|
348
339
|
<div class="actions">
|
|
349
340
|
<button type="submit" name="decision" value="deny">Cancel</button>
|
|
350
341
|
<button type="submit" name="decision" value="approve">Authorize</button>
|
|
@@ -2667,8 +2658,18 @@ export function registerAdminRoutes(deps) {
|
|
|
2667
2658
|
const userIdentity = core.resolveUserIdentity(access.userId);
|
|
2668
2659
|
const userName = String(userIdentity?.displayName || userIdentity?.email || access.userId).trim() || String(access.userId);
|
|
2669
2660
|
const userMeta = userIdentity?.email && userIdentity.email !== userName ? String(userIdentity.email).trim() : null;
|
|
2661
|
+
const authorizeUrl = new URL('/oauth/mcp/authorize', resolveMcpOauthIssuer(req));
|
|
2662
|
+
authorizeUrl.searchParams.set('client_id', clientId);
|
|
2663
|
+
authorizeUrl.searchParams.set('redirect_uri', redirectUri);
|
|
2664
|
+
authorizeUrl.searchParams.set('response_type', responseType);
|
|
2665
|
+
authorizeUrl.searchParams.set('code_challenge', codeChallenge);
|
|
2666
|
+
authorizeUrl.searchParams.set('code_challenge_method', codeChallengeMethod);
|
|
2667
|
+
if (state)
|
|
2668
|
+
authorizeUrl.searchParams.set('state', state);
|
|
2669
|
+
if (scopes.length > 0)
|
|
2670
|
+
authorizeUrl.searchParams.set('scope', scopes.join(' '));
|
|
2670
2671
|
res.writeHead(200, { 'Content-Type': 'text/html; charset=utf-8' });
|
|
2671
|
-
res.end(renderMcpOauthAuthorizePage({ userName, userMeta, workspaces, preselectedWorkspaceId: preferredWorkspaceId, clientName: clientRecord.clientName, clientId: clientRecord.id, redirectUri, scopes, state, codeChallenge, codeChallengeMethod }));
|
|
2672
|
+
res.end(renderMcpOauthAuthorizePage({ userName, userMeta, authorizeUrl: authorizeUrl.toString(), workspaces, preselectedWorkspaceId: preferredWorkspaceId, clientName: clientRecord.clientName, clientId: clientRecord.id, redirectUri, scopes, state, codeChallenge, codeChallengeMethod }));
|
|
2672
2673
|
}
|
|
2673
2674
|
catch (error) {
|
|
2674
2675
|
const statusCode = resolveErrorStatusCode(error);
|
|
@@ -2713,8 +2714,18 @@ export function registerAdminRoutes(deps) {
|
|
|
2713
2714
|
const userIdentity = core.resolveUserIdentity(access.userId);
|
|
2714
2715
|
const userName = String(userIdentity?.displayName || userIdentity?.email || access.userId).trim() || String(access.userId);
|
|
2715
2716
|
const userMeta = userIdentity?.email && userIdentity.email !== userName ? String(userIdentity.email).trim() : null;
|
|
2717
|
+
const authorizeUrl = new URL('/oauth/mcp/authorize', resolveMcpOauthIssuer(req));
|
|
2718
|
+
authorizeUrl.searchParams.set('client_id', clientId);
|
|
2719
|
+
authorizeUrl.searchParams.set('redirect_uri', redirectUri);
|
|
2720
|
+
authorizeUrl.searchParams.set('response_type', responseType);
|
|
2721
|
+
authorizeUrl.searchParams.set('code_challenge', codeChallenge);
|
|
2722
|
+
authorizeUrl.searchParams.set('code_challenge_method', codeChallengeMethod);
|
|
2723
|
+
if (state)
|
|
2724
|
+
authorizeUrl.searchParams.set('state', state);
|
|
2725
|
+
if (scopes.length > 0)
|
|
2726
|
+
authorizeUrl.searchParams.set('scope', scopes.join(' '));
|
|
2716
2727
|
res.writeHead(200, { 'Content-Type': 'text/html; charset=utf-8' });
|
|
2717
|
-
res.end(renderMcpOauthAuthorizePage({ userName, userMeta, workspaces, preselectedWorkspaceId: preferredWorkspaceId, clientName: clientRecord.clientName, clientId: clientRecord.id, redirectUri, scopes, state, codeChallenge, codeChallengeMethod }));
|
|
2728
|
+
res.end(renderMcpOauthAuthorizePage({ userName, userMeta, authorizeUrl: authorizeUrl.toString(), workspaces, preselectedWorkspaceId: preferredWorkspaceId, clientName: clientRecord.clientName, clientId: clientRecord.id, redirectUri, scopes, state, codeChallenge, codeChallengeMethod }));
|
|
2718
2729
|
}
|
|
2719
2730
|
catch (error) {
|
|
2720
2731
|
const statusCode = resolveErrorStatusCode(error);
|
|
@@ -2068,7 +2068,31 @@ export function registerAuthRoutes(deps) {
|
|
|
2068
2068
|
for (const domain of domains) {
|
|
2069
2069
|
clearCookies.push(`${clearBase}; Domain=${domain}`);
|
|
2070
2070
|
}
|
|
2071
|
+
const body = await parseFormBody(req);
|
|
2072
|
+
const requestedNext = String(body.next || '').trim();
|
|
2073
|
+
const next = (() => {
|
|
2074
|
+
if (!requestedNext)
|
|
2075
|
+
return '';
|
|
2076
|
+
try {
|
|
2077
|
+
const parsed = new URL(requestedNext);
|
|
2078
|
+
const host = parsed.hostname.toLowerCase();
|
|
2079
|
+
const isLocal = host === 'localhost' || host === '127.0.0.1' || host === '::1';
|
|
2080
|
+
const isTaskforceHost = host === 'taskforcehq.ai' || host.endsWith('.taskforcehq.ai');
|
|
2081
|
+
if ((parsed.protocol === 'http:' || parsed.protocol === 'https:') && (isLocal || isTaskforceHost)) {
|
|
2082
|
+
return parsed.toString();
|
|
2083
|
+
}
|
|
2084
|
+
}
|
|
2085
|
+
catch {
|
|
2086
|
+
return '';
|
|
2087
|
+
}
|
|
2088
|
+
return '';
|
|
2089
|
+
})();
|
|
2071
2090
|
res.setHeader('Set-Cookie', [...new Set(clearCookies)]);
|
|
2091
|
+
if (next) {
|
|
2092
|
+
res.writeHead(303, { Location: next });
|
|
2093
|
+
res.end();
|
|
2094
|
+
return;
|
|
2095
|
+
}
|
|
2072
2096
|
res.writeHead(200, { 'Content-Type': 'application/json' });
|
|
2073
2097
|
res.end(JSON.stringify({ success: true }));
|
|
2074
2098
|
});
|
package/dist/server/routes.js
CHANGED
|
@@ -1265,7 +1265,7 @@ export function createRoutes(core, context = {}) {
|
|
|
1265
1265
|
const scopeLabels = {
|
|
1266
1266
|
'tasks:read': 'Read tasks',
|
|
1267
1267
|
'tasks:write': 'Update tasks',
|
|
1268
|
-
'workspace:read': 'Read workspace
|
|
1268
|
+
'workspace:read': 'Read workspace'
|
|
1269
1269
|
};
|
|
1270
1270
|
const options = input.workspaces.map((workspace) => {
|
|
1271
1271
|
const selected = workspace.id === input.preselectedWorkspaceId ? ' selected' : '';
|
|
@@ -1288,25 +1288,19 @@ export function createRoutes(core, context = {}) {
|
|
|
1288
1288
|
:root { color-scheme: dark; }
|
|
1289
1289
|
body { margin: 0; font-family: "Outfit", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif; background: radial-gradient(circle at top, rgba(255,138,0,0.08), transparent 30%), #111126; color: #f8f7ff; }
|
|
1290
1290
|
.wrap { min-height: 100vh; display: flex; align-items: center; justify-content: center; padding: 32px 24px; }
|
|
1291
|
-
.card { width: min(
|
|
1292
|
-
|
|
1293
|
-
.
|
|
1294
|
-
h1 { margin: 0; font-size: clamp(2.25rem, 5vw, 3rem); line-height: 0.96; letter-spacing: -0.04em; color: #f8f7ff; }
|
|
1295
|
-
.brand-word { font-family: "Russo One", sans-serif; font-size: 0.94em; letter-spacing: -0.03em; }
|
|
1296
|
-
.lede { margin: 18px 0 8px; font-size: 1.15rem; line-height: 1.5; color: rgba(255,255,255,0.78); max-width: 34ch; }
|
|
1291
|
+
.card { width: min(720px, 100%); background: rgba(22,22,44,0.95); border: 1px solid rgba(255,255,255,0.08); border-radius: 32px; box-shadow: 0 24px 80px rgba(0,0,0,0.35); padding: 40px 48px; }
|
|
1292
|
+
h1 { margin: 0; font-size: clamp(1.95rem, 4.2vw, 2.6rem); line-height: 1.02; letter-spacing: -0.035em; color: #f8f7ff; }
|
|
1293
|
+
.lede { margin: 16px 0 8px; font-size: 1.1rem; line-height: 1.45; color: rgba(255,255,255,0.78); max-width: 48ch; text-wrap: pretty; }
|
|
1297
1294
|
.lede strong { color: #fff; }
|
|
1298
|
-
.
|
|
1295
|
+
.session-row { display: flex; align-items: center; gap: 14px; flex-wrap: wrap; margin: 0 0 28px; }
|
|
1296
|
+
.user-meta { margin: 0; color: rgba(255,255,255,0.5); font-size: 0.9rem; }
|
|
1297
|
+
.account-switch { padding: 0; border: 0; min-width: 0; background: transparent; color: #cbd8ff; font-size: 0.9rem; font-weight: 600; box-shadow: none; }
|
|
1298
|
+
.account-switch:hover { transform: none; color: #ffffff; text-decoration: underline; }
|
|
1299
1299
|
label { display: block; font-size: 13px; font-weight: 700; margin: 24px 0 10px; color: rgba(255,255,255,0.92); letter-spacing: 0.01em; }
|
|
1300
1300
|
select { width: 100%; border-radius: 18px; border: 1px solid rgba(255,255,255,0.1); background: #1f2039; color: #fff; padding: 16px 18px; font-size: 15px; font-weight: 600; box-shadow: inset 0 1px 0 rgba(255,255,255,0.02); }
|
|
1301
1301
|
.scopes { display:flex; flex-wrap:wrap; gap:10px; margin-top:8px; }
|
|
1302
1302
|
.scope-chip { display:inline-flex; padding:8px 14px; border-radius:999px; background:#2a2947; border:1px solid rgba(255,255,255,0.09); font-size:13px; font-weight:600; color:rgba(255,255,255,0.92); }
|
|
1303
|
-
.
|
|
1304
|
-
.details summary { list-style: none; cursor: pointer; padding: 16px 18px; font-size: 0.92rem; font-weight: 700; color: rgba(255,255,255,0.86); }
|
|
1305
|
-
.details summary::-webkit-details-marker { display: none; }
|
|
1306
|
-
.details-body { padding: 0 18px 18px; color: rgba(255,255,255,0.68); font-size: 0.9rem; line-height: 1.5; }
|
|
1307
|
-
.details-row { margin: 0 0 8px; }
|
|
1308
|
-
.details-row strong { color: rgba(255,255,255,0.92); }
|
|
1309
|
-
.legal { margin: 24px 2px 0; color: rgba(255,255,255,0.58); font-size: 0.9rem; line-height: 1.55; max-width: 56ch; }
|
|
1303
|
+
.legal { margin: 28px 2px 0; color: rgba(255,255,255,0.58); font-size: 0.9rem; line-height: 1.55; max-width: 58ch; }
|
|
1310
1304
|
.error { margin-bottom: 18px; padding: 12px 14px; border-radius: 14px; background: rgba(255,99,132,0.12); color: #ffb6c6; border: 1px solid rgba(255,99,132,0.25); }
|
|
1311
1305
|
.actions { display:flex; justify-content:flex-end; gap:14px; margin-top: 36px; }
|
|
1312
1306
|
button { border-radius: 18px; padding: 15px 22px; font-size: 15px; font-weight: 700; cursor: pointer; border: 1px solid rgba(255,255,255,0.08); min-width: 156px; transition: transform 160ms ease, box-shadow 160ms ease, border-color 160ms ease, background-color 160ms ease; }
|
|
@@ -1316,7 +1310,8 @@ export function createRoutes(core, context = {}) {
|
|
|
1316
1310
|
a { color: #cbd8ff; }
|
|
1317
1311
|
@media (max-width: 720px) {
|
|
1318
1312
|
.card { padding: 30px 22px; border-radius: 28px; }
|
|
1319
|
-
h1 { font-size:
|
|
1313
|
+
h1 { font-size: 1.85rem; }
|
|
1314
|
+
.lede { max-width: none; }
|
|
1320
1315
|
.actions { flex-direction: column-reverse; }
|
|
1321
1316
|
button { width: 100%; }
|
|
1322
1317
|
}
|
|
@@ -1325,22 +1320,17 @@ export function createRoutes(core, context = {}) {
|
|
|
1325
1320
|
<body>
|
|
1326
1321
|
<div class="wrap">
|
|
1327
1322
|
<form class="card" method="post" action="/oauth/mcp/authorize">
|
|
1328
|
-
<
|
|
1329
|
-
<h1>Authorize <span class="brand-word">Taskforce</span> Connector</h1>
|
|
1323
|
+
<h1>Authorize Taskforce Connector</h1>
|
|
1330
1324
|
<p class="lede"><strong>${escapeHtml(input.clientName)}</strong> wants to connect to Taskforce for <strong>${escapeHtml(input.userName)}</strong>.</p>
|
|
1331
|
-
|
|
1325
|
+
<div class="session-row">
|
|
1326
|
+
${input.userMeta ? `<p class="user-meta">Signed in as ${escapeHtml(input.userMeta)}</p>` : ''}
|
|
1327
|
+
<button class="account-switch" type="submit" formaction="/api/taskforce/auth/logout" formmethod="post" formnovalidate>Use different account</button>
|
|
1328
|
+
</div>
|
|
1332
1329
|
${input.error ? `<div class="error">${escapeHtml(input.error)}</div>` : ''}
|
|
1333
|
-
<label for="workspaceId">Workspace</label>
|
|
1330
|
+
<label for="workspaceId">Select Workspace</label>
|
|
1334
1331
|
<select id="workspaceId" name="workspaceId" required>${options}</select>
|
|
1335
1332
|
<label>Requested Permissions</label>
|
|
1336
1333
|
<div class="scopes">${scopeBadges}</div>
|
|
1337
|
-
<details class="details">
|
|
1338
|
-
<summary>Technical details</summary>
|
|
1339
|
-
<div class="details-body">
|
|
1340
|
-
<div class="details-row"><strong>Client:</strong> ${escapeHtml(input.clientName)}</div>
|
|
1341
|
-
<div class="details-row"><strong>Redirect URI:</strong> ${escapeHtml(input.redirectUri)}</div>
|
|
1342
|
-
</div>
|
|
1343
|
-
</details>
|
|
1344
1334
|
<p class="legal">By authorizing this connector, you agree to Taskforce's <a href="${termsUrl}" target="_blank" rel="noopener noreferrer">Terms of Service</a> and acknowledge the <a href="${privacyUrl}" target="_blank" rel="noopener noreferrer">Privacy Policy</a>.</p>
|
|
1345
1335
|
<input type="hidden" name="client_id" value="${escapeHtml(input.clientId)}" />
|
|
1346
1336
|
<input type="hidden" name="redirect_uri" value="${escapeHtml(input.redirectUri)}" />
|
|
@@ -1348,6 +1338,7 @@ export function createRoutes(core, context = {}) {
|
|
|
1348
1338
|
<input type="hidden" name="state" value="${escapeHtml(input.state)}" />
|
|
1349
1339
|
<input type="hidden" name="code_challenge" value="${escapeHtml(input.codeChallenge)}" />
|
|
1350
1340
|
<input type="hidden" name="code_challenge_method" value="${escapeHtml(input.codeChallengeMethod)}" />
|
|
1341
|
+
<input type="hidden" name="next" value="${escapeHtml(input.authorizeUrl)}" />
|
|
1351
1342
|
<div class="actions">
|
|
1352
1343
|
<button type="submit" name="decision" value="deny">Cancel</button>
|
|
1353
1344
|
<button type="submit" name="decision" value="approve">Authorize</button>
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{r as c,j as e}from"./vendor-react-CKJs5o3c.js";import{s as Ue,p as _,u as ie,v as De,w as se,x as le,y as we,t as r,E as Te,z as Me,B as ke,C as fe,F as b,G as oe}from"./index-CdlMnBxP.js";import{a0 as Re,x as $e,h as Ee,B as ne,m as C,b as Le,$ as Be,a5 as Ge}from"./vendor-icons-CLnehDTw.js";import"./vendor-markdown-BUxTU7dS.js";import"./vendor-dnd-DRzYolkg.js";import"./vendor-router-BbWMxlnO.js";function Fe(l){return typeof l.avatarUrl=="string"&&l.avatarUrl.trim().length>0}function K(l){return l?fe(l.avatarUrl,l.avatarRevision,l.avatarUpdatedAt):""}function ze(l){return l?fe(l.avatarSourceUrl,l.avatarRevision,l.avatarUpdatedAt):""}function He(l){return l.find(Fe)||l[0]}function ce(l){return`${l.seatScope||"unknown"}:${l.name.trim().toLowerCase()}`}function Oe(l){const m=Math.max(0,l-1);return`${m} duplicate${m===1?"":"s"}`}function de(l){return!l||typeof l!="object"?null:{used:Math.max(0,Number(l.used||0)),limit:l.limit===null||l.limit===void 0?null:Math.max(0,Number(l.limit||0)),remaining:l.remaining===null||l.remaining===void 0?null:Math.max(0,Number(l.remaining||0))}}function pe(l,m){const U=m?.variant==="inline"?`${r.aiProfileSeatScopeIcon} ${r.aiProfileSeatScopeIconInline}`:m?.variant==="detail"?`${r.aiProfileSeatScopeIcon} ${r.aiProfileSeatScopeIconDetail}`:r.aiProfileSeatScopeIcon;return l==="cloud_metered"?e.jsx("span",{className:`${U} ${r.aiProfileSeatScopeIconCloud}`,title:"Cloud MCP","aria-label":"Cloud MCP",children:e.jsx(Be,{size:20})}):l==="local_unmetered"?e.jsx("span",{className:`${U} ${r.aiProfileSeatScopeIconLocal}`,title:"Local MCP","aria-label":"Local MCP",children:e.jsx(Ge,{size:20})}):null}function qe({workspaceId:l,cloudAuthConfigured:m=!1,authSessionResolved:U=!1,isAuthenticated:ue=!1,cloudAiProfileSeatUsage:me=null,agentTrayOpen:E=!1,onCloseAgentTray:he,mcpSettingsNode:V=null}){const[S,L]=c.useState([]),[ve,ge]=c.useState(null),[B,J]=c.useState(!1),[h,D]=c.useState(null),[w,v]=c.useState(null),[T,x]=c.useState(null),[W,G]=c.useState(null),[ye,Y]=c.useState(!1),[P,j]=c.useState(null),[q,Q]=c.useState(null),[X,M]=c.useState(!1),[Pe,g]=c.useState(null),k=c.useCallback(async()=>{if(l){J(!0);try{const a=`/api/taskforce/workspace/assignee-options?workspaceId=${encodeURIComponent(l)}&kind=agent`,t=await fetch(a,{credentials:"include"}),i=t.ok?await t.json().catch(()=>({})):{},o=de(i?.aiProfileSeatUsage),d=Array.isArray(i?.assignees)?i.assignees.filter(s=>s.kind==="agent").map(s=>({id:String(s.value||""),name:String(s.label||s.value||"Unknown Agent"),username:String(s.username||s.value||""),icon:String(s.icon||"Bot"),color:String(s.color||"#6B7280"),avatarUrl:typeof s.avatarUrl=="string"?s.avatarUrl:null,avatarSourceUrl:typeof s.avatarSourceUrl=="string"?s.avatarSourceUrl:null,avatarRevision:Number.isFinite(Number(s.avatarRevision))?Math.max(0,Math.floor(Number(s.avatarRevision))):0,avatarUpdatedAt:typeof s.avatarUpdatedAt=="string"?s.avatarUpdatedAt:null,kind:String(s.kind||"agent"),description:typeof s.description=="string"?s.description:null,role:typeof s.role=="string"?s.role:null,provider:typeof s.provider=="string"?s.provider:null,model:typeof s.model=="string"?s.model:null,surfaceType:_(s.surfaceType),seatScope:Ue(s.seatScope),archivedAt:typeof s.archivedAt=="string"?s.archivedAt:null,createdAt:String(s.createdAt||""),updatedAt:String(s.updatedAt||s.createdAt||""),lastActiveAt:typeof s.lastActiveAt=="string"?s.lastActiveAt:null})):[];L(d),ge(o),x(s=>s&&!d.some(p=>p.id===s.profileId)?null:s),G(s=>s&&!d.some(p=>p.id===s.profileId)?null:s)}finally{J(!1)}}},[l]);c.useEffect(()=>{k()},[k]);const R=c.useMemo(()=>{const a=new Map;for(const t of S){const i=ie(t.surfaceType);a.has(i)||a.set(i,new Map);const o=a.get(i),d=ce(t);o.has(d)||o.set(d,[]),o.get(d).push(t)}return De.map(t=>({section:t,label:se(t),groups:Array.from(a.get(t)?.entries()||[]).map(([i,o])=>({groupId:`${t}:${i}`,section:t,sectionLabel:se(t),profiles:o,primaryProfile:He(o)}))})).filter(t=>t.groups.length>0)},[S]),y=c.useMemo(()=>R.flatMap(a=>a.groups),[R]),n=c.useMemo(()=>y.find(a=>a.groupId===P)||y[0]||null,[y,P]),u=c.useMemo(()=>S.find(a=>a.id===q)||null,[S,q]);c.useEffect(()=>{if(!y.length){P!==null&&j(null);return}(!P||!y.some(a=>a.groupId===P))&&j(y[0].groupId)},[y,P]);const Ae=async(a,t)=>{v(null),x(null);try{const i=await fetch("/api/taskforce/workspace/ai-profiles/merge",{method:"POST",credentials:"include",headers:{"Content-Type":"application/json"},body:JSON.stringify({keepId:a,mergeId:t})}),o=await i.json().catch(()=>({}));if(!i.ok){v({type:"error",message:String(o?.error||"Failed to merge AI profiles.")});return}D(null),v({type:"success",message:"AI profiles merged."}),await k(),b({workspaceId:l,profileId:a,reason:"merge"})}catch{v({type:"error",message:"Failed to merge AI profiles."})}},Z=async a=>{if(window.confirm(`Remove ${a.name} from the active roster? This frees an AI profile seat and preserves task and comment history.`)){x(null),v(null);try{const i=await fetch("/api/taskforce/workspace/ai-profiles/archive",{method:"POST",credentials:"include",headers:{"Content-Type":"application/json"},body:JSON.stringify({profileId:a.id,reason:"manual_archive"})}),o=await i.json().catch(()=>({}));if(!i.ok){x({profileId:a.id,message:String(o?.error||"Failed to remove AI profile from roster.")});return}(h?.keepId===a.id||h?.mergeId===a.id)&&D(null),v({type:"success",message:"AI profile removed from active roster."}),await k(),b({workspaceId:l,profileId:a.id,reason:"archive"})}catch{x({profileId:a.id,message:"Failed to remove AI profile from roster."})}}},Se=async(a,t)=>{const i=_(t),o=new Set(a.profiles.map(p=>p.surfaceType??"")),d=i??"";if(o.size===1&&o.has(d))return;Y(!0),G(null),v(null);const s=[];try{for(const f of a.profiles){const I=await fetch("/api/taskforce/workspace/ai-profiles/surface-type",{method:"POST",credentials:"include",headers:{"Content-Type":"application/json"},body:JSON.stringify({profileId:f.id,surfaceType:i})}),H=await I.json().catch(()=>({}));if(!I.ok||!H?.profile)throw new Error(String(H?.error||"Failed to update AI profile category."));const O=H.profile;s.push({...f,surfaceType:_(O.surfaceType),updatedAt:typeof O.updatedAt=="string"?O.updatedAt:f.updatedAt})}const p=new Map(s.map(f=>[f.id,f]));L(f=>f.map(I=>p.get(I.id)||I));const A=p.get(a.primaryProfile.id)||{...a.primaryProfile,surfaceType:i},Ce=ie(A.surfaceType);j(`${Ce}:${ce(A)}`),v({type:"success",message:"AI profile category updated."});for(const f of s)b({workspaceId:l,profileId:f.id,reason:"update"})}catch(p){G({profileId:a.primaryProfile.id,message:String(p?.message||"Failed to update AI profile category.")})}finally{Y(!1)}},ee=a=>new Promise((t,i)=>{const o=new FileReader;o.onload=()=>t(String(o.result||"")),o.onerror=()=>i(new Error("Failed to read image file.")),o.readAsDataURL(a)}),ae=a=>{const t=String(a?.id||"").trim();t&&L(i=>i.map(o=>o.id===t?{...o,avatarUrl:typeof a.avatarUrl=="string"?a.avatarUrl:null,avatarSourceUrl:typeof a.avatarSourceUrl=="string"?a.avatarSourceUrl:null,avatarRevision:Number.isFinite(Number(a.avatarRevision))?Math.max(0,Math.floor(Number(a.avatarRevision))):o.avatarRevision,avatarUpdatedAt:typeof a.avatarUpdatedAt=="string"?a.avatarUpdatedAt:o.avatarUpdatedAt,updatedAt:typeof a.updatedAt=="string"?a.updatedAt:o.updatedAt}:o))},xe=async(a,t,i)=>{const o=await ee(t),d=i?await ee(i):null,s={profileId:a,displayImage:{dataUrl:o,mimeType:t.type||"application/octet-stream",originalName:t.name||"display-avatar"}};i&&d&&(s.sourceImage={dataUrl:d,mimeType:i.type||"application/octet-stream",originalName:i.name||"source-avatar"});const p=await fetch("/api/taskforce/workspace/ai-profiles/avatar/upload",{method:"POST",credentials:"include",headers:{"Content-Type":"application/json"},body:JSON.stringify(s)}),A=await p.json().catch(()=>({}));if(!p.ok||!A?.profile)throw new Error(String(A?.error||"Failed to update AI profile avatar."));ae(A.profile),b({workspaceId:l,profileId:a,reason:"avatar"})},je=async a=>{const t=await fetch("/api/taskforce/workspace/ai-profiles/avatar",{method:"POST",credentials:"include",headers:{"Content-Type":"application/json"},body:JSON.stringify({profileId:a,avatarUrl:null,avatarSourceUrl:null})}),i=await t.json().catch(()=>({}));if(!t.ok||!i?.profile)throw new Error(String(i?.error||"Failed to update AI profile avatar."));ae(i.profile),b({workspaceId:l,profileId:a,reason:"avatar"})},Ne=async(a,t)=>{if(!u)return!1;if(!a.type.startsWith("image/"))return g("AI profile photo must be an image file."),!1;M(!0),g(null);try{const i=await oe(a,{maxBytes:5242880});if(i.exceededLimit)throw new Error(a.type==="image/gif"?"Animated GIF AI profile photos must be 5 MB or smaller.":"AI profile photo must be 5 MB or smaller.");const o=i.file;let d=null;if(t){const s=await oe(t,{maxBytes:5242880});if(s.exceededLimit)throw new Error(t.type==="image/gif"?"Animated GIF AI profile photos must be 5 MB or smaller.":"AI profile source photo must be 5 MB or smaller.");d=s.file}return await xe(u.id,o,d),!0}catch(i){return g(String(i?.message||"Failed to update AI profile photo.")),!1}finally{M(!1)}},Ie=async()=>{if(u){M(!0),g(null);try{await je(u.id)}catch(a){g(String(a?.message||"Failed to remove AI profile photo."))}finally{M(!1)}}},N=a=>{const t=String(a||"").trim();if(!t)return"Unknown";const i=Date.parse(t);return Number.isFinite(i)?new Date(i).toLocaleString(void 0,{dateStyle:"medium",timeStyle:"short"}):t},re=c.useMemo(()=>{if(!n)return{attributes:[],dates:[]};const a=n.primaryProfile,t=i=>i||"Not set";return{attributes:[{label:"Category",value:a.surfaceType?le(a.surfaceType):""},{label:"Connection",value:a.seatScope?we(a.seatScope):""},{label:"Provider",value:a.provider||""},{label:"Model",value:a.model||""}].map(i=>({...i,value:t(i.value)})),dates:[{label:"Status",value:a.archivedAt?"Retired":"Active"},{label:"Recruited",value:N(a.createdAt)},{label:"Last updated",value:N(a.updatedAt)},{label:"Last active",value:N(a.lastActiveAt)}].map(i=>({...i,value:t(i.value)}))}},[n]),be=!!n&&n.profiles.length>1,te=m&&U&&!ue,$=m?de(me):ve,F=te?"Log into account for Cloud agents":$?`${$.used}/${$.limit===null?"Unlimited":$.limit}`:null,z=!!V;return e.jsxs("section",{className:`${r.agentsModuleRoot} ${z?r.agentsModuleWithTray:""} ${z&&E?r.agentsModuleTrayOpen:""}`.trim(),children:[z&&e.jsxs("aside",{className:`${r.agentTrayPanel} ${E?r.agentTrayPanelOpen:""}`.trim(),"aria-label":"Agent MCP settings tray","aria-hidden":!E,children:[e.jsxs("div",{className:r.agentTrayHeader,children:[e.jsxs("span",{className:r.agentTrayTitle,children:[e.jsx(Re,{size:14}),"MCP Settings"]}),e.jsx("button",{type:"button",className:"tf-control-icon",onClick:he,title:"Collapse agent tray","aria-label":"Collapse agent tray",children:e.jsx($e,{size:16})})]}),e.jsx("div",{className:`${r.agentTrayContent} tf-scrollbar tf-scrollbar--track-transparent`,children:e.jsx("div",{className:r.agentTrayContentInner,children:V})})]}),e.jsx("div",{className:r.agentsModuleContent,children:e.jsx("div",{className:`${r.settingGroup} ${r.agentsModuleGroup}`,children:e.jsxs("div",{children:[e.jsx("h4",{className:r.settingSubTitle,children:"Registered AI Profiles"}),e.jsx("p",{className:`${r.settingsHint} ${r.marginBottom12}`,children:"AI agents register profiles when connecting via MCP. Merge duplicates created when a token was lost, or remove inactive profiles from the active roster to free seats while preserving history."}),F&&e.jsx("div",{className:r.aiProfileSeatSummary,children:te?F:`Registered Cloud Agents: ${F}`}),B&&e.jsxs("div",{className:r.settingsHint,children:[e.jsx(Ee,{size:13,className:r.spinner})," Loading profiles…"]}),!B&&S.length===0&&e.jsx("div",{className:r.settingsHint,children:"No AI profiles registered yet."}),!B&&R.length>0&&e.jsxs("div",{className:r.aiProfilesExplorer,children:[e.jsx("div",{className:`${r.aiProfilesListPane} tf-scrollbar tf-scrollbar--track-transparent`,children:e.jsx("div",{className:r.aiProfilesList,children:R.map(a=>e.jsxs("div",{className:r.aiProfilesSection,children:[e.jsx("div",{className:r.aiProfilesSectionHeader,children:a.label}),a.groups.map(t=>{const i=n?.groupId===t.groupId,o=t.primaryProfile;return e.jsxs("div",{role:"button",tabIndex:0,className:`${r.aiProfileGroup} ${t.profiles.length>1?r.aiProfileGroupDuplicate:""} ${i?r.aiProfileGroupSelected:""}`,onClick:()=>j(t.groupId),onKeyDown:d=>{d.key!=="Enter"&&d.key!==" "||(d.preventDefault(),j(t.groupId))},"aria-pressed":i,children:[pe(o.seatScope),e.jsxs("div",{className:r.aiProfileGroupHeader,children:[e.jsx("span",{className:r.aiProfileGroupAvatar,style:{color:o.color},children:o.avatarUrl?e.jsx("img",{src:K(o),alt:""}):e.jsx(ne,{size:22})}),e.jsxs("span",{className:r.aiProfileGroupIdentity,children:[e.jsx("span",{className:r.aiProfileName,children:o.name}),e.jsxs("span",{className:r.aiProfileHandle,children:["@",o.username]}),e.jsx("span",{className:r.aiProfileRole,children:o.role||"Role not set"}),t.profiles.length>1&&e.jsxs("span",{className:r.aiProfileDuplicateBadge,children:[e.jsx(C,{size:11})," ",Oe(t.profiles.length)]})]})]})]},t.groupId)})]},a.section))})}),n&&e.jsx("div",{className:r.aiProfileDetailPane,children:e.jsxs("div",{className:r.aiProfileDetailCard,children:[pe(n.primaryProfile.seatScope,{variant:"detail"}),e.jsxs("div",{className:r.aiProfileDetailHero,children:[e.jsx(Te,{label:"Edit AI profile photo",imageUrl:K(n.primaryProfile),fallback:e.jsx(ne,{size:38}),accentColor:n.primaryProfile.color,size:176,width:153,height:207,radius:6,editBadgeSize:28,editIconSize:14,className:r.aiProfileDetailAvatar,onClick:()=>{g(null),Q(n.primaryProfile.id)}}),e.jsxs("div",{className:r.aiProfileDetailHeading,children:[e.jsx("div",{className:r.aiProfileDetailTitleRow,children:e.jsx("h5",{className:r.aiProfileDetailTitle,children:n.primaryProfile.name})}),e.jsxs("div",{className:r.aiProfileDetailMetaRow,children:[e.jsxs("span",{className:r.aiProfileDetailHandle,children:["@",n.primaryProfile.username]}),n.profiles.length>1&&e.jsxs("span",{className:r.aiProfileDetailLinkedCount,children:[n.profiles.length," linked"]})]}),e.jsxs("div",{className:r.aiProfileDetailRole,children:["Role: ",n.primaryProfile.role||"Not set"]}),n.primaryProfile.description&&e.jsx("div",{className:r.aiProfileDetailDescription,children:n.primaryProfile.description}),e.jsxs("div",{className:r.aiProfileSignatureColor,children:[e.jsx("span",{children:"Signature color"}),e.jsx("span",{className:r.aiProfileSignatureSwatch,style:{backgroundColor:n.primaryProfile.color},"aria-hidden":"true"}),e.jsx("span",{children:n.primaryProfile.color})]})]})]}),e.jsxs("div",{className:r.aiProfileDetailDataList,children:[e.jsx("div",{className:r.aiProfileDetailDataGroup,children:re.attributes.map(a=>e.jsxs("div",{className:r.aiProfileDetailDataRow,children:[e.jsx("span",{className:r.aiProfileDetailDataLabel,children:a.label}),a.label==="Category"?e.jsx("span",{className:r.aiProfileDetailDataValue,children:e.jsxs("select",{className:r.aiProfileStatusSelect,"aria-label":"Agent category",value:n.primaryProfile.surfaceType??"",disabled:ye,onChange:t=>{Se(n,t.target.value)},children:[e.jsx("option",{value:"",children:"Unclassified"}),Me.map(t=>e.jsx("option",{value:t,children:le(t)},t))]})}):e.jsx("span",{className:r.aiProfileDetailDataValue,children:a.value})]},a.label))}),e.jsx("div",{className:`${r.aiProfileDetailDataGroup} ${r.aiProfileDetailDateGroup}`,children:re.dates.map(a=>e.jsxs("div",{className:r.aiProfileDetailDataRow,children:[e.jsx("span",{className:r.aiProfileDetailDataLabel,children:a.label}),a.label==="Status"?e.jsx("span",{className:r.aiProfileDetailDataValue,children:e.jsxs("select",{className:r.aiProfileStatusSelect,"aria-label":"Agent roster status",value:n.primaryProfile.archivedAt?"retired":"active",onChange:t=>{t.target.value==="retire"&&Z(n.primaryProfile)},children:[e.jsx("option",{value:"active",children:"Active"}),n.primaryProfile.archivedAt?e.jsx("option",{value:"retired",children:"Retired"}):e.jsx("option",{value:"retire",children:"Retire from roster"})]})}):e.jsx("span",{className:r.aiProfileDetailDataValue,children:a.value})]},a.label))})]}),W?.profileId===n.primaryProfile.id&&e.jsxs("div",{className:r.aiProfileInlineError,role:"alert",children:[e.jsx(C,{size:12}),e.jsx("span",{children:W.message})]}),be?e.jsxs("div",{className:r.aiProfileInstanceSection,children:[e.jsxs("div",{className:r.aiProfileInstanceSectionHeader,children:[e.jsx("span",{children:"Profile instances"}),e.jsx("span",{className:r.settingsHint,children:"Choose a keeper here if duplicates need to be merged."})]}),e.jsx("div",{className:r.aiProfileInstanceList,children:n.profiles.map(a=>e.jsxs("div",{className:r.aiProfileInstanceCard,children:[e.jsxs("div",{className:r.aiProfileInstanceTopRow,children:[e.jsxs("div",{children:[e.jsxs("div",{className:r.aiProfileInstanceName,children:["@",a.username]}),e.jsx("div",{className:r.aiProfileIdChip,children:a.id})]}),h?.keepId===a.id&&e.jsx("span",{className:r.aiProfileKeepBadge,children:"Keeping"})]}),e.jsxs("div",{className:r.aiProfileInstanceMeta,children:[e.jsxs("span",{children:["Created ",N(a.createdAt)]}),e.jsxs("span",{children:["Updated ",N(a.updatedAt)]})]}),h?.keepId!==a.id&&e.jsx("button",{className:r.secondaryHeaderBtn,title:"Keep this profile, merge others into it",onClick:()=>{const t=n.profiles.find(i=>i.id!==a.id)?.id;t&&D({keepId:a.id,mergeId:t})},children:"Keep this"}),T?.profileId===a.id&&e.jsxs("div",{className:r.aiProfileInlineError,role:"alert",children:[e.jsx(C,{size:12}),e.jsx("span",{children:T.message})]}),e.jsx("button",{className:r.aiProfileDangerTextButton,title:"Remove this profile from the active roster",onClick:()=>{Z(a)},children:"Remove from roster"})]},a.id))}),h&&n.profiles.some(a=>a.id===h.keepId)&&e.jsxs("div",{className:r.aiProfileMergeActions,children:[e.jsx("button",{className:r.dangerBtn,onClick:()=>{Ae(h.keepId,h.mergeId)},children:"Merge duplicates"}),e.jsx("button",{className:r.secondaryHeaderBtn,onClick:()=>D(null),children:"Cancel"})]})]}):e.jsxs("div",{className:r.aiProfileIdFooter,children:[e.jsx("div",{className:r.aiProfileIdFooterValue,children:n.primaryProfile.id}),T?.profileId===n.primaryProfile.id&&e.jsxs("div",{className:r.aiProfileInlineError,role:"alert",children:[e.jsx(C,{size:12}),e.jsx("span",{children:T.message})]})]})]})})]}),w&&e.jsxs("div",{className:`${w.type==="success"?r.successMessage:r.errorMessage} ${r.marginTop12}`,children:[w.type==="success"?e.jsx(Le,{size:14}):e.jsx(C,{size:14}),w.message]})]})})}),e.jsx(ke,{isOpen:!!u,theme:"dark",title:"Edit AI Profile Photo",currentImageUrl:K(u),editorImageUrl:ze(u),fallbackInitial:(u?.name||"AI").charAt(0).toUpperCase(),accept:"image/png,image/jpeg,image/webp,image/gif",busy:X,hasPendingImage:!1,canRemove:!!u?.avatarUrl,error:Pe,notice:null,onClose:()=>{X||(Q(null),g(null))},onApplyImage:Ne,onRemoveImage:Ie})]})}export{qe as AgentsModule};
|
|
1
|
+
import{r as c,j as e}from"./vendor-react-CKJs5o3c.js";import{s as Ue,p as _,u as ie,v as De,w as se,x as le,y as we,t as r,E as Te,z as Me,B as ke,C as fe,F as b,G as oe}from"./index-rB9GGUCz.js";import{a0 as Re,x as $e,h as Ee,B as ne,m as C,b as Le,$ as Be,a5 as Ge}from"./vendor-icons-CLnehDTw.js";import"./vendor-markdown-BUxTU7dS.js";import"./vendor-dnd-DRzYolkg.js";import"./vendor-router-BbWMxlnO.js";function Fe(l){return typeof l.avatarUrl=="string"&&l.avatarUrl.trim().length>0}function K(l){return l?fe(l.avatarUrl,l.avatarRevision,l.avatarUpdatedAt):""}function ze(l){return l?fe(l.avatarSourceUrl,l.avatarRevision,l.avatarUpdatedAt):""}function He(l){return l.find(Fe)||l[0]}function ce(l){return`${l.seatScope||"unknown"}:${l.name.trim().toLowerCase()}`}function Oe(l){const m=Math.max(0,l-1);return`${m} duplicate${m===1?"":"s"}`}function de(l){return!l||typeof l!="object"?null:{used:Math.max(0,Number(l.used||0)),limit:l.limit===null||l.limit===void 0?null:Math.max(0,Number(l.limit||0)),remaining:l.remaining===null||l.remaining===void 0?null:Math.max(0,Number(l.remaining||0))}}function pe(l,m){const U=m?.variant==="inline"?`${r.aiProfileSeatScopeIcon} ${r.aiProfileSeatScopeIconInline}`:m?.variant==="detail"?`${r.aiProfileSeatScopeIcon} ${r.aiProfileSeatScopeIconDetail}`:r.aiProfileSeatScopeIcon;return l==="cloud_metered"?e.jsx("span",{className:`${U} ${r.aiProfileSeatScopeIconCloud}`,title:"Cloud MCP","aria-label":"Cloud MCP",children:e.jsx(Be,{size:20})}):l==="local_unmetered"?e.jsx("span",{className:`${U} ${r.aiProfileSeatScopeIconLocal}`,title:"Local MCP","aria-label":"Local MCP",children:e.jsx(Ge,{size:20})}):null}function qe({workspaceId:l,cloudAuthConfigured:m=!1,authSessionResolved:U=!1,isAuthenticated:ue=!1,cloudAiProfileSeatUsage:me=null,agentTrayOpen:E=!1,onCloseAgentTray:he,mcpSettingsNode:V=null}){const[S,L]=c.useState([]),[ve,ge]=c.useState(null),[B,J]=c.useState(!1),[h,D]=c.useState(null),[w,v]=c.useState(null),[T,x]=c.useState(null),[W,G]=c.useState(null),[ye,Y]=c.useState(!1),[P,j]=c.useState(null),[q,Q]=c.useState(null),[X,M]=c.useState(!1),[Pe,g]=c.useState(null),k=c.useCallback(async()=>{if(l){J(!0);try{const a=`/api/taskforce/workspace/assignee-options?workspaceId=${encodeURIComponent(l)}&kind=agent`,t=await fetch(a,{credentials:"include"}),i=t.ok?await t.json().catch(()=>({})):{},o=de(i?.aiProfileSeatUsage),d=Array.isArray(i?.assignees)?i.assignees.filter(s=>s.kind==="agent").map(s=>({id:String(s.value||""),name:String(s.label||s.value||"Unknown Agent"),username:String(s.username||s.value||""),icon:String(s.icon||"Bot"),color:String(s.color||"#6B7280"),avatarUrl:typeof s.avatarUrl=="string"?s.avatarUrl:null,avatarSourceUrl:typeof s.avatarSourceUrl=="string"?s.avatarSourceUrl:null,avatarRevision:Number.isFinite(Number(s.avatarRevision))?Math.max(0,Math.floor(Number(s.avatarRevision))):0,avatarUpdatedAt:typeof s.avatarUpdatedAt=="string"?s.avatarUpdatedAt:null,kind:String(s.kind||"agent"),description:typeof s.description=="string"?s.description:null,role:typeof s.role=="string"?s.role:null,provider:typeof s.provider=="string"?s.provider:null,model:typeof s.model=="string"?s.model:null,surfaceType:_(s.surfaceType),seatScope:Ue(s.seatScope),archivedAt:typeof s.archivedAt=="string"?s.archivedAt:null,createdAt:String(s.createdAt||""),updatedAt:String(s.updatedAt||s.createdAt||""),lastActiveAt:typeof s.lastActiveAt=="string"?s.lastActiveAt:null})):[];L(d),ge(o),x(s=>s&&!d.some(p=>p.id===s.profileId)?null:s),G(s=>s&&!d.some(p=>p.id===s.profileId)?null:s)}finally{J(!1)}}},[l]);c.useEffect(()=>{k()},[k]);const R=c.useMemo(()=>{const a=new Map;for(const t of S){const i=ie(t.surfaceType);a.has(i)||a.set(i,new Map);const o=a.get(i),d=ce(t);o.has(d)||o.set(d,[]),o.get(d).push(t)}return De.map(t=>({section:t,label:se(t),groups:Array.from(a.get(t)?.entries()||[]).map(([i,o])=>({groupId:`${t}:${i}`,section:t,sectionLabel:se(t),profiles:o,primaryProfile:He(o)}))})).filter(t=>t.groups.length>0)},[S]),y=c.useMemo(()=>R.flatMap(a=>a.groups),[R]),n=c.useMemo(()=>y.find(a=>a.groupId===P)||y[0]||null,[y,P]),u=c.useMemo(()=>S.find(a=>a.id===q)||null,[S,q]);c.useEffect(()=>{if(!y.length){P!==null&&j(null);return}(!P||!y.some(a=>a.groupId===P))&&j(y[0].groupId)},[y,P]);const Ae=async(a,t)=>{v(null),x(null);try{const i=await fetch("/api/taskforce/workspace/ai-profiles/merge",{method:"POST",credentials:"include",headers:{"Content-Type":"application/json"},body:JSON.stringify({keepId:a,mergeId:t})}),o=await i.json().catch(()=>({}));if(!i.ok){v({type:"error",message:String(o?.error||"Failed to merge AI profiles.")});return}D(null),v({type:"success",message:"AI profiles merged."}),await k(),b({workspaceId:l,profileId:a,reason:"merge"})}catch{v({type:"error",message:"Failed to merge AI profiles."})}},Z=async a=>{if(window.confirm(`Remove ${a.name} from the active roster? This frees an AI profile seat and preserves task and comment history.`)){x(null),v(null);try{const i=await fetch("/api/taskforce/workspace/ai-profiles/archive",{method:"POST",credentials:"include",headers:{"Content-Type":"application/json"},body:JSON.stringify({profileId:a.id,reason:"manual_archive"})}),o=await i.json().catch(()=>({}));if(!i.ok){x({profileId:a.id,message:String(o?.error||"Failed to remove AI profile from roster.")});return}(h?.keepId===a.id||h?.mergeId===a.id)&&D(null),v({type:"success",message:"AI profile removed from active roster."}),await k(),b({workspaceId:l,profileId:a.id,reason:"archive"})}catch{x({profileId:a.id,message:"Failed to remove AI profile from roster."})}}},Se=async(a,t)=>{const i=_(t),o=new Set(a.profiles.map(p=>p.surfaceType??"")),d=i??"";if(o.size===1&&o.has(d))return;Y(!0),G(null),v(null);const s=[];try{for(const f of a.profiles){const I=await fetch("/api/taskforce/workspace/ai-profiles/surface-type",{method:"POST",credentials:"include",headers:{"Content-Type":"application/json"},body:JSON.stringify({profileId:f.id,surfaceType:i})}),H=await I.json().catch(()=>({}));if(!I.ok||!H?.profile)throw new Error(String(H?.error||"Failed to update AI profile category."));const O=H.profile;s.push({...f,surfaceType:_(O.surfaceType),updatedAt:typeof O.updatedAt=="string"?O.updatedAt:f.updatedAt})}const p=new Map(s.map(f=>[f.id,f]));L(f=>f.map(I=>p.get(I.id)||I));const A=p.get(a.primaryProfile.id)||{...a.primaryProfile,surfaceType:i},Ce=ie(A.surfaceType);j(`${Ce}:${ce(A)}`),v({type:"success",message:"AI profile category updated."});for(const f of s)b({workspaceId:l,profileId:f.id,reason:"update"})}catch(p){G({profileId:a.primaryProfile.id,message:String(p?.message||"Failed to update AI profile category.")})}finally{Y(!1)}},ee=a=>new Promise((t,i)=>{const o=new FileReader;o.onload=()=>t(String(o.result||"")),o.onerror=()=>i(new Error("Failed to read image file.")),o.readAsDataURL(a)}),ae=a=>{const t=String(a?.id||"").trim();t&&L(i=>i.map(o=>o.id===t?{...o,avatarUrl:typeof a.avatarUrl=="string"?a.avatarUrl:null,avatarSourceUrl:typeof a.avatarSourceUrl=="string"?a.avatarSourceUrl:null,avatarRevision:Number.isFinite(Number(a.avatarRevision))?Math.max(0,Math.floor(Number(a.avatarRevision))):o.avatarRevision,avatarUpdatedAt:typeof a.avatarUpdatedAt=="string"?a.avatarUpdatedAt:o.avatarUpdatedAt,updatedAt:typeof a.updatedAt=="string"?a.updatedAt:o.updatedAt}:o))},xe=async(a,t,i)=>{const o=await ee(t),d=i?await ee(i):null,s={profileId:a,displayImage:{dataUrl:o,mimeType:t.type||"application/octet-stream",originalName:t.name||"display-avatar"}};i&&d&&(s.sourceImage={dataUrl:d,mimeType:i.type||"application/octet-stream",originalName:i.name||"source-avatar"});const p=await fetch("/api/taskforce/workspace/ai-profiles/avatar/upload",{method:"POST",credentials:"include",headers:{"Content-Type":"application/json"},body:JSON.stringify(s)}),A=await p.json().catch(()=>({}));if(!p.ok||!A?.profile)throw new Error(String(A?.error||"Failed to update AI profile avatar."));ae(A.profile),b({workspaceId:l,profileId:a,reason:"avatar"})},je=async a=>{const t=await fetch("/api/taskforce/workspace/ai-profiles/avatar",{method:"POST",credentials:"include",headers:{"Content-Type":"application/json"},body:JSON.stringify({profileId:a,avatarUrl:null,avatarSourceUrl:null})}),i=await t.json().catch(()=>({}));if(!t.ok||!i?.profile)throw new Error(String(i?.error||"Failed to update AI profile avatar."));ae(i.profile),b({workspaceId:l,profileId:a,reason:"avatar"})},Ne=async(a,t)=>{if(!u)return!1;if(!a.type.startsWith("image/"))return g("AI profile photo must be an image file."),!1;M(!0),g(null);try{const i=await oe(a,{maxBytes:5242880});if(i.exceededLimit)throw new Error(a.type==="image/gif"?"Animated GIF AI profile photos must be 5 MB or smaller.":"AI profile photo must be 5 MB or smaller.");const o=i.file;let d=null;if(t){const s=await oe(t,{maxBytes:5242880});if(s.exceededLimit)throw new Error(t.type==="image/gif"?"Animated GIF AI profile photos must be 5 MB or smaller.":"AI profile source photo must be 5 MB or smaller.");d=s.file}return await xe(u.id,o,d),!0}catch(i){return g(String(i?.message||"Failed to update AI profile photo.")),!1}finally{M(!1)}},Ie=async()=>{if(u){M(!0),g(null);try{await je(u.id)}catch(a){g(String(a?.message||"Failed to remove AI profile photo."))}finally{M(!1)}}},N=a=>{const t=String(a||"").trim();if(!t)return"Unknown";const i=Date.parse(t);return Number.isFinite(i)?new Date(i).toLocaleString(void 0,{dateStyle:"medium",timeStyle:"short"}):t},re=c.useMemo(()=>{if(!n)return{attributes:[],dates:[]};const a=n.primaryProfile,t=i=>i||"Not set";return{attributes:[{label:"Category",value:a.surfaceType?le(a.surfaceType):""},{label:"Connection",value:a.seatScope?we(a.seatScope):""},{label:"Provider",value:a.provider||""},{label:"Model",value:a.model||""}].map(i=>({...i,value:t(i.value)})),dates:[{label:"Status",value:a.archivedAt?"Retired":"Active"},{label:"Recruited",value:N(a.createdAt)},{label:"Last updated",value:N(a.updatedAt)},{label:"Last active",value:N(a.lastActiveAt)}].map(i=>({...i,value:t(i.value)}))}},[n]),be=!!n&&n.profiles.length>1,te=m&&U&&!ue,$=m?de(me):ve,F=te?"Log into account for Cloud agents":$?`${$.used}/${$.limit===null?"Unlimited":$.limit}`:null,z=!!V;return e.jsxs("section",{className:`${r.agentsModuleRoot} ${z?r.agentsModuleWithTray:""} ${z&&E?r.agentsModuleTrayOpen:""}`.trim(),children:[z&&e.jsxs("aside",{className:`${r.agentTrayPanel} ${E?r.agentTrayPanelOpen:""}`.trim(),"aria-label":"Agent MCP settings tray","aria-hidden":!E,children:[e.jsxs("div",{className:r.agentTrayHeader,children:[e.jsxs("span",{className:r.agentTrayTitle,children:[e.jsx(Re,{size:14}),"MCP Settings"]}),e.jsx("button",{type:"button",className:"tf-control-icon",onClick:he,title:"Collapse agent tray","aria-label":"Collapse agent tray",children:e.jsx($e,{size:16})})]}),e.jsx("div",{className:`${r.agentTrayContent} tf-scrollbar tf-scrollbar--track-transparent`,children:e.jsx("div",{className:r.agentTrayContentInner,children:V})})]}),e.jsx("div",{className:r.agentsModuleContent,children:e.jsx("div",{className:`${r.settingGroup} ${r.agentsModuleGroup}`,children:e.jsxs("div",{children:[e.jsx("h4",{className:r.settingSubTitle,children:"Registered AI Profiles"}),e.jsx("p",{className:`${r.settingsHint} ${r.marginBottom12}`,children:"AI agents register profiles when connecting via MCP. Merge duplicates created when a token was lost, or remove inactive profiles from the active roster to free seats while preserving history."}),F&&e.jsx("div",{className:r.aiProfileSeatSummary,children:te?F:`Registered Cloud Agents: ${F}`}),B&&e.jsxs("div",{className:r.settingsHint,children:[e.jsx(Ee,{size:13,className:r.spinner})," Loading profiles…"]}),!B&&S.length===0&&e.jsx("div",{className:r.settingsHint,children:"No AI profiles registered yet."}),!B&&R.length>0&&e.jsxs("div",{className:r.aiProfilesExplorer,children:[e.jsx("div",{className:`${r.aiProfilesListPane} tf-scrollbar tf-scrollbar--track-transparent`,children:e.jsx("div",{className:r.aiProfilesList,children:R.map(a=>e.jsxs("div",{className:r.aiProfilesSection,children:[e.jsx("div",{className:r.aiProfilesSectionHeader,children:a.label}),a.groups.map(t=>{const i=n?.groupId===t.groupId,o=t.primaryProfile;return e.jsxs("div",{role:"button",tabIndex:0,className:`${r.aiProfileGroup} ${t.profiles.length>1?r.aiProfileGroupDuplicate:""} ${i?r.aiProfileGroupSelected:""}`,onClick:()=>j(t.groupId),onKeyDown:d=>{d.key!=="Enter"&&d.key!==" "||(d.preventDefault(),j(t.groupId))},"aria-pressed":i,children:[pe(o.seatScope),e.jsxs("div",{className:r.aiProfileGroupHeader,children:[e.jsx("span",{className:r.aiProfileGroupAvatar,style:{color:o.color},children:o.avatarUrl?e.jsx("img",{src:K(o),alt:""}):e.jsx(ne,{size:22})}),e.jsxs("span",{className:r.aiProfileGroupIdentity,children:[e.jsx("span",{className:r.aiProfileName,children:o.name}),e.jsxs("span",{className:r.aiProfileHandle,children:["@",o.username]}),e.jsx("span",{className:r.aiProfileRole,children:o.role||"Role not set"}),t.profiles.length>1&&e.jsxs("span",{className:r.aiProfileDuplicateBadge,children:[e.jsx(C,{size:11})," ",Oe(t.profiles.length)]})]})]})]},t.groupId)})]},a.section))})}),n&&e.jsx("div",{className:r.aiProfileDetailPane,children:e.jsxs("div",{className:r.aiProfileDetailCard,children:[pe(n.primaryProfile.seatScope,{variant:"detail"}),e.jsxs("div",{className:r.aiProfileDetailHero,children:[e.jsx(Te,{label:"Edit AI profile photo",imageUrl:K(n.primaryProfile),fallback:e.jsx(ne,{size:38}),accentColor:n.primaryProfile.color,size:176,width:153,height:207,radius:6,editBadgeSize:28,editIconSize:14,className:r.aiProfileDetailAvatar,onClick:()=>{g(null),Q(n.primaryProfile.id)}}),e.jsxs("div",{className:r.aiProfileDetailHeading,children:[e.jsx("div",{className:r.aiProfileDetailTitleRow,children:e.jsx("h5",{className:r.aiProfileDetailTitle,children:n.primaryProfile.name})}),e.jsxs("div",{className:r.aiProfileDetailMetaRow,children:[e.jsxs("span",{className:r.aiProfileDetailHandle,children:["@",n.primaryProfile.username]}),n.profiles.length>1&&e.jsxs("span",{className:r.aiProfileDetailLinkedCount,children:[n.profiles.length," linked"]})]}),e.jsxs("div",{className:r.aiProfileDetailRole,children:["Role: ",n.primaryProfile.role||"Not set"]}),n.primaryProfile.description&&e.jsx("div",{className:r.aiProfileDetailDescription,children:n.primaryProfile.description}),e.jsxs("div",{className:r.aiProfileSignatureColor,children:[e.jsx("span",{children:"Signature color"}),e.jsx("span",{className:r.aiProfileSignatureSwatch,style:{backgroundColor:n.primaryProfile.color},"aria-hidden":"true"}),e.jsx("span",{children:n.primaryProfile.color})]})]})]}),e.jsxs("div",{className:r.aiProfileDetailDataList,children:[e.jsx("div",{className:r.aiProfileDetailDataGroup,children:re.attributes.map(a=>e.jsxs("div",{className:r.aiProfileDetailDataRow,children:[e.jsx("span",{className:r.aiProfileDetailDataLabel,children:a.label}),a.label==="Category"?e.jsx("span",{className:r.aiProfileDetailDataValue,children:e.jsxs("select",{className:r.aiProfileStatusSelect,"aria-label":"Agent category",value:n.primaryProfile.surfaceType??"",disabled:ye,onChange:t=>{Se(n,t.target.value)},children:[e.jsx("option",{value:"",children:"Unclassified"}),Me.map(t=>e.jsx("option",{value:t,children:le(t)},t))]})}):e.jsx("span",{className:r.aiProfileDetailDataValue,children:a.value})]},a.label))}),e.jsx("div",{className:`${r.aiProfileDetailDataGroup} ${r.aiProfileDetailDateGroup}`,children:re.dates.map(a=>e.jsxs("div",{className:r.aiProfileDetailDataRow,children:[e.jsx("span",{className:r.aiProfileDetailDataLabel,children:a.label}),a.label==="Status"?e.jsx("span",{className:r.aiProfileDetailDataValue,children:e.jsxs("select",{className:r.aiProfileStatusSelect,"aria-label":"Agent roster status",value:n.primaryProfile.archivedAt?"retired":"active",onChange:t=>{t.target.value==="retire"&&Z(n.primaryProfile)},children:[e.jsx("option",{value:"active",children:"Active"}),n.primaryProfile.archivedAt?e.jsx("option",{value:"retired",children:"Retired"}):e.jsx("option",{value:"retire",children:"Retire from roster"})]})}):e.jsx("span",{className:r.aiProfileDetailDataValue,children:a.value})]},a.label))})]}),W?.profileId===n.primaryProfile.id&&e.jsxs("div",{className:r.aiProfileInlineError,role:"alert",children:[e.jsx(C,{size:12}),e.jsx("span",{children:W.message})]}),be?e.jsxs("div",{className:r.aiProfileInstanceSection,children:[e.jsxs("div",{className:r.aiProfileInstanceSectionHeader,children:[e.jsx("span",{children:"Profile instances"}),e.jsx("span",{className:r.settingsHint,children:"Choose a keeper here if duplicates need to be merged."})]}),e.jsx("div",{className:r.aiProfileInstanceList,children:n.profiles.map(a=>e.jsxs("div",{className:r.aiProfileInstanceCard,children:[e.jsxs("div",{className:r.aiProfileInstanceTopRow,children:[e.jsxs("div",{children:[e.jsxs("div",{className:r.aiProfileInstanceName,children:["@",a.username]}),e.jsx("div",{className:r.aiProfileIdChip,children:a.id})]}),h?.keepId===a.id&&e.jsx("span",{className:r.aiProfileKeepBadge,children:"Keeping"})]}),e.jsxs("div",{className:r.aiProfileInstanceMeta,children:[e.jsxs("span",{children:["Created ",N(a.createdAt)]}),e.jsxs("span",{children:["Updated ",N(a.updatedAt)]})]}),h?.keepId!==a.id&&e.jsx("button",{className:r.secondaryHeaderBtn,title:"Keep this profile, merge others into it",onClick:()=>{const t=n.profiles.find(i=>i.id!==a.id)?.id;t&&D({keepId:a.id,mergeId:t})},children:"Keep this"}),T?.profileId===a.id&&e.jsxs("div",{className:r.aiProfileInlineError,role:"alert",children:[e.jsx(C,{size:12}),e.jsx("span",{children:T.message})]}),e.jsx("button",{className:r.aiProfileDangerTextButton,title:"Remove this profile from the active roster",onClick:()=>{Z(a)},children:"Remove from roster"})]},a.id))}),h&&n.profiles.some(a=>a.id===h.keepId)&&e.jsxs("div",{className:r.aiProfileMergeActions,children:[e.jsx("button",{className:r.dangerBtn,onClick:()=>{Ae(h.keepId,h.mergeId)},children:"Merge duplicates"}),e.jsx("button",{className:r.secondaryHeaderBtn,onClick:()=>D(null),children:"Cancel"})]})]}):e.jsxs("div",{className:r.aiProfileIdFooter,children:[e.jsx("div",{className:r.aiProfileIdFooterValue,children:n.primaryProfile.id}),T?.profileId===n.primaryProfile.id&&e.jsxs("div",{className:r.aiProfileInlineError,role:"alert",children:[e.jsx(C,{size:12}),e.jsx("span",{children:T.message})]})]})]})})]}),w&&e.jsxs("div",{className:`${w.type==="success"?r.successMessage:r.errorMessage} ${r.marginTop12}`,children:[w.type==="success"?e.jsx(Le,{size:14}):e.jsx(C,{size:14}),w.message]})]})})}),e.jsx(ke,{isOpen:!!u,theme:"dark",title:"Edit AI Profile Photo",currentImageUrl:K(u),editorImageUrl:ze(u),fallbackInitial:(u?.name||"AI").charAt(0).toUpperCase(),accept:"image/png,image/jpeg,image/webp,image/gif",busy:X,hasPendingImage:!1,canRemove:!!u?.avatarUrl,error:Pe,notice:null,onClose:()=>{X||(Q(null),g(null))},onApplyImage:Ne,onRemoveImage:Ie})]})}export{qe as AgentsModule};
|