- instance.brand (env AGNES_INSTANCE_BRAND, default "Agnes") + instance.workspace_dir replace hard-coded "Agnes" / "~/Agnes" across /home, /setup, /setup-advanced, /login, /install, /me/debug, and the Claude Code clipboard setup script. Terraform-friendly env override; defaults preserve existing Agnes branding. - Explicit "create workspace folder" step on /home (OS-tabbed mkdir+cd) + same step baked into the clipboard script as step 2. Drops the implicit assumption that `agnes init --workspace .` lands in a sensibly-cd'd shell. - Final "Restart Claude Code" step in the setup script (unconditional, between connectors and Confirm) so freshly-installed plugins, MCP servers, and SessionStart hooks load on the next Claude Code session. - Asana reverted from hosted Remote MCP back to PAT + raw REST against app.asana.com/api/1.0. MCP envelope shape consumed ~5x tokens per call; the PAT path lets the agent read flat REST fields. Existing MCP registration is detected and the user is asked whether to remove it (default Y, with benefits listed: token cost, no third-party hop, no OAuth refresh dance, deterministic envelope shape). - Atlassian connector instructs picking the longest API-token expiry (today "1 year") to cut re-mint friction. No public query-parameter hook exists on id.atlassian.com to pre-select expiry, so the prompt documents the manual click and acknowledges that limitation. - Uniform ✅ / ❌ per-connector marker contract (Asana, GWS, Atlassian) for the Confirm summary to grep. Each connector now ends with a Claude-driven end-to-end test that uses Claude Code's own bash to exercise the stored credential and prints "✅ <Connector> integration verified — ..." (or the failure variant).
142 lines
7.4 KiB
HTML
142 lines
7.4 KiB
HTML
{% extends "base_login.html" %}
|
|
|
|
{% block title %}Login - Data Analyst Portal{% endblock %}
|
|
|
|
{% block content %}
|
|
<div class="login-page">
|
|
<div class="login-split">
|
|
<!-- Left: Features & Value Proposition -->
|
|
<div class="login-features">
|
|
<div class="features-content">
|
|
<h1 class="features-title">{{ config.INSTANCE_NAME }}</h1>
|
|
<p class="features-subtitle">
|
|
Your local AI agent works with centralized data, shared context, and corporate memory that learns from everyone.
|
|
</p>
|
|
|
|
<div class="feature-cards">
|
|
<div class="feature-card">
|
|
<div class="feature-icon feature-icon-data">
|
|
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
|
<ellipse cx="12" cy="5" rx="9" ry="3"/>
|
|
<path d="M21 12c0 1.66-4 3-9 3s-9-1.34-9-3"/>
|
|
<path d="M3 5v14c0 1.66 4 3 9 3s9-1.34 9-3V5"/>
|
|
</svg>
|
|
</div>
|
|
<div class="feature-text">
|
|
<h3>Unified Data Access</h3>
|
|
<p>Synced data from central server with semantic layer, metrics definitions, and full documentation.</p>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="feature-card">
|
|
<div class="feature-icon feature-icon-memory">
|
|
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
|
<path d="M12 2a4 4 0 0 1 4 4c0 1.1-.9 2-2 2h-4c-1.1 0-2-.9-2-2a4 4 0 0 1 4-4z"/>
|
|
<path d="M12 8v8"/>
|
|
<path d="M8 12h8"/>
|
|
<circle cx="12" cy="19" r="3"/>
|
|
</svg>
|
|
</div>
|
|
<div class="feature-text">
|
|
<h3>Corporate Memory</h3>
|
|
<p>Shared context across all users. When someone discovers something, everyone's AI knows it.</p>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="feature-card">
|
|
<div class="feature-icon feature-icon-auto">
|
|
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
|
<path d="M13 2L3 14h9l-1 8 10-12h-9l1-8z"/>
|
|
</svg>
|
|
</div>
|
|
<div class="feature-text">
|
|
<h3>Instant Automation</h3>
|
|
<p>Turn any analysis into automated scripts. Runs without AI - fast, cheap, and repeatable.</p>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="feature-card">
|
|
<div class="feature-icon feature-icon-notif">
|
|
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
|
<path d="M18 8A6 6 0 0 0 6 8c0 7-3 9-3 9h18s-3-2-3-9"/>
|
|
<path d="M13.73 21a2 2 0 0 1-3.46 0"/>
|
|
</svg>
|
|
</div>
|
|
<div class="feature-text">
|
|
<h3>Smart Notifications</h3>
|
|
<p>Your AI agent can set up alerts and deploy them to the cloud. Server monitors 24/7.</p>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="feature-card">
|
|
<div class="feature-icon feature-icon-performance">
|
|
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
|
<path d="M3 3v18h18"/>
|
|
<path d="M18 17V9"/>
|
|
<path d="M13 17v-6"/>
|
|
<path d="M8 17v-3"/>
|
|
<polyline points="7 7 12 3 17 6 22 2"/>
|
|
</svg>
|
|
</div>
|
|
<div class="feature-text">
|
|
<h3>Performance Intelligence</h3>
|
|
<p>Track how data and AI drive measurable business outcomes. Real-time visibility into team maturity, process improvements, and ROI across departments.</p>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Right: Login Card -->
|
|
<div class="login-card-wrapper">
|
|
<div class="login-card">
|
|
<h2>Sign In</h2>
|
|
<p class="login-description">
|
|
Access your AI data analysis workspace
|
|
</p>
|
|
|
|
{% set _err = request.query_params.get('error') %}
|
|
{% set _err_messages = {
|
|
'not_in_allowed_group': "Your Google account isn't a member of any group permitted to use this " ~ (instance_brand or "Agnes") ~ " instance. Ask your " ~ (instance_brand or "Agnes") ~ " administrator to grant you access.",
|
|
'group_check_unavailable': "We couldn't verify your group membership with Google right now. Please try signing in again in a moment.",
|
|
'deactivated': "This account has been deactivated. Contact your " ~ (instance_brand or "Agnes") ~ " administrator if you believe this is in error.",
|
|
'oauth_failed': "Google sign-in failed. Please try again.",
|
|
'no_email': "Google didn't return an email for this account.",
|
|
'domain_not_allowed': "This email's domain is not permitted to sign in to this " ~ (instance_brand or "Agnes") ~ " instance.",
|
|
'google_not_configured': "Google sign-in is not configured on this server.",
|
|
} %}
|
|
{% if _err and _err_messages.get(_err) %}
|
|
<div class="login-error" role="alert" style="background:#fef2f2;border:1px solid #fecaca;color:#b91c1c;padding:12px 14px;border-radius:8px;font-size:13px;line-height:1.5;margin:0 auto 16px;max-width:280px;text-align:left;">
|
|
{{ _err_messages.get(_err) }}
|
|
</div>
|
|
{% endif %}
|
|
|
|
{% for btn in login_buttons %}
|
|
<a href="{{ btn.url }}" class="btn {{ btn.css_class|default('btn-secondary') }}" style="width: 100%; max-width: 280px;">
|
|
{% if btn.icon_html %}{{ btn.icon_html|safe }}{% endif %}
|
|
{{ btn.text }}
|
|
</a>
|
|
|
|
{% if btn.subtitle %}
|
|
<p class="login-note">
|
|
{{ btn.subtitle|safe }}
|
|
</p>
|
|
{% endif %}
|
|
|
|
{% if not loop.last %}
|
|
<div class="divider">
|
|
<span>or</span>
|
|
</div>
|
|
{% endif %}
|
|
{% endfor %}
|
|
|
|
{% if not login_buttons %}
|
|
<p class="login-note">
|
|
No authentication providers are configured. Please set up at least one provider.
|
|
</p>
|
|
{% endif %}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
{% endblock %}
|