@lightupai/polaris 0.0.32 → 0.0.34
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/README.md +1 -0
- package/docs/design-mentions.md +109 -0
- package/package.json +1 -1
- package/src/client/client.ts +19 -3
- package/src/daemon/daemon.ts +18 -0
- package/src/web/pages.ts +122 -55
- package/tests/web.test.ts +1 -1
package/README.md
CHANGED
|
@@ -119,6 +119,7 @@ tests/ Test suite (bun test)
|
|
|
119
119
|
- [ ] Reconciliation and recovery — `polaris recover` command that diffs the daemon JSONL log against the DB, backfills missing events, and posts an abridged recovery summary to Slack as a thread reply at the correct timeline position
|
|
120
120
|
- [ ] CD pipeline for Hetzner — auto-deploy to production on merge to master (SSH + docker compose up), similar to the npm publish job
|
|
121
121
|
- [ ] Auto-update local skill/hooks — locally installed skill and hook files go stale when the repo changes. `polaris install` fixes it but there's no staleness detection or auto-update mechanism
|
|
122
|
+
- [ ] Update available indicator — daemon periodically checks npm for newer version, caches the result. Status line shows "update available" when stale. `polaris update` command installs the latest version and rewrites skill/hooks.
|
|
122
123
|
- [ ] Slack channel name collision — if a channel name was previously deleted, Slack reserves it. Bridge should handle `name_taken` by trying a prefix/suffix (e.g., `p-project-name`)
|
|
123
124
|
|
|
124
125
|
## Development
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
# Design: @Mentions
|
|
2
|
+
|
|
3
|
+
## Context
|
|
4
|
+
|
|
5
|
+
Users want to tag teammates while working in a coding session — "get Krishna's opinion on this approach." The tag should be a real Slack mention that triggers a notification, not plain text.
|
|
6
|
+
|
|
7
|
+
## UX Goal
|
|
8
|
+
|
|
9
|
+
As close to native Slack `@mention` as possible. The agent knows the team, resolves names accurately, and the tagged person gets a real notification.
|
|
10
|
+
|
|
11
|
+
## Design Decisions
|
|
12
|
+
|
|
13
|
+
1. **Discovery**: Agent has the full team member list available (via `polaris_team` tool). No guessing or fuzzy matching.
|
|
14
|
+
2. **Resolution**: Agent resolves `@krishna` → Slack user ID inline when constructing the message. The team list is fetched at session start or on demand.
|
|
15
|
+
3. **Semantics**: Start as a Slack mention (notification only). Extend later to invitations, review requests, tracked consultations.
|
|
16
|
+
4. **Inbound mentions**: When Slack users @mention other users in messages to sessions, the floor captures the mention semantically (who was tagged, in what context).
|
|
17
|
+
|
|
18
|
+
## Implementation
|
|
19
|
+
|
|
20
|
+
### polaris_team tool
|
|
21
|
+
|
|
22
|
+
New MCP tool that returns the org's team members with their Slack user IDs.
|
|
23
|
+
|
|
24
|
+
```typescript
|
|
25
|
+
{
|
|
26
|
+
name: "polaris_team",
|
|
27
|
+
description: "List team members with their Slack identities. Use this to resolve @mentions.",
|
|
28
|
+
inputSchema: { type: "object", properties: {} },
|
|
29
|
+
}
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
Response:
|
|
33
|
+
```json
|
|
34
|
+
{
|
|
35
|
+
"members": [
|
|
36
|
+
{ "name": "Krishna Patel", "participant_id": "user:krishna.patel", "slack_id": "U0XXXXXXX", "slack_display": "krishna" },
|
|
37
|
+
{ "name": "Tuhin Roy", "participant_id": "user:tuhin.roy", "slack_id": "U0YYYYYYY", "slack_display": "tuhin" },
|
|
38
|
+
{ "name": "Manu Bansal", "participant_id": "user:manu.bansal", "slack_id": "UCUHHNJDT", "slack_display": "manu" }
|
|
39
|
+
]
|
|
40
|
+
}
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
### Data source
|
|
44
|
+
|
|
45
|
+
The team list comes from:
|
|
46
|
+
1. **Slack workspace members** — `users.list` API call, cached by the bridge/API
|
|
47
|
+
2. **Polaris users table** — users who have signed up, with their participant IDs
|
|
48
|
+
|
|
49
|
+
The API needs a new endpoint: `GET /team` that joins Slack workspace members with Polaris users. The daemon proxies this like other API calls.
|
|
50
|
+
|
|
51
|
+
### Mention in polaris_reply
|
|
52
|
+
|
|
53
|
+
When the agent calls `polaris_reply` with text containing `<@UXXXXXXX>`, the bridge posts it as-is — Slack renders the mention natively.
|
|
54
|
+
|
|
55
|
+
The agent's flow:
|
|
56
|
+
1. User says "tag krishna about this auth approach"
|
|
57
|
+
2. Agent calls `polaris_team` (or uses cached result)
|
|
58
|
+
3. Finds Krishna → `slack_id: U0XXXXXXX`
|
|
59
|
+
4. Calls `polaris_reply` with `"<@U0XXXXXXX> what do you think about this auth approach?"`
|
|
60
|
+
5. Bridge posts to Slack, Krishna gets a notification
|
|
61
|
+
|
|
62
|
+
### Mention resolution in the bridge (outbound)
|
|
63
|
+
|
|
64
|
+
As a fallback, the bridge can also resolve `@krishna` → `<@U0XXXXXXX>` in message text before posting. This handles cases where the agent or hooks include plain `@name` without resolution.
|
|
65
|
+
|
|
66
|
+
Resolution logic:
|
|
67
|
+
1. Scan message text for `@word` patterns
|
|
68
|
+
2. Look up each word against Slack display names (cached)
|
|
69
|
+
3. Replace with `<@slack_id>` if unique match
|
|
70
|
+
4. Leave as plain text if no match or ambiguous
|
|
71
|
+
|
|
72
|
+
### Inbound mention tracking
|
|
73
|
+
|
|
74
|
+
When a Slack message is injected into a session and contains `<@UXXXXXXX>` patterns:
|
|
75
|
+
1. The bridge resolves the Slack ID to a display name
|
|
76
|
+
2. The inject event payload includes a `mentions` array: `["user:krishna.patel"]`
|
|
77
|
+
3. The floor records who was consulted on what
|
|
78
|
+
|
|
79
|
+
This is metadata on the event — no schema change needed (it goes in the JSONB payload).
|
|
80
|
+
|
|
81
|
+
## Caching
|
|
82
|
+
|
|
83
|
+
The Slack user list is expensive to fetch (paginated API call). Cache it:
|
|
84
|
+
- **Bridge**: on startup and every 30 minutes
|
|
85
|
+
- **API /team endpoint**: cache for 5 minutes
|
|
86
|
+
- **Agent**: fetches once per session via `polaris_team`, uses for all mentions in that session
|
|
87
|
+
|
|
88
|
+
## Skill update
|
|
89
|
+
|
|
90
|
+
The skill instructions should tell the agent:
|
|
91
|
+
- When the user mentions someone by name, call `polaris_team` to resolve their Slack ID
|
|
92
|
+
- Use `<@slack_id>` format in `polaris_reply` messages
|
|
93
|
+
- If unsure which person, present the matches and ask
|
|
94
|
+
|
|
95
|
+
## Future extensions
|
|
96
|
+
|
|
97
|
+
- **Mention as invitation**: tagging someone could auto-invite them as an advisor to the session
|
|
98
|
+
- **Review request**: `@krishna review this PR` creates a tracked review request
|
|
99
|
+
- **Mention analytics**: dashboard shows who was consulted most, on which projects
|
|
100
|
+
- **Agent-to-person tagging**: named agents like Dean could tag humans when they need input
|
|
101
|
+
|
|
102
|
+
## Implementation Order
|
|
103
|
+
|
|
104
|
+
1. `GET /team` API endpoint (join Slack users with Polaris users)
|
|
105
|
+
2. Daemon `/team` proxy endpoint
|
|
106
|
+
3. `polaris_team` MCP tool
|
|
107
|
+
4. Bridge fallback mention resolution (outbound)
|
|
108
|
+
5. Inbound mention tracking in inject events
|
|
109
|
+
6. Skill update with mention instructions
|
package/package.json
CHANGED
package/src/client/client.ts
CHANGED
|
@@ -53,12 +53,12 @@ mcp.setRequestHandler(ListToolsRequestSchema, async () => ({
|
|
|
53
53
|
inputSchema: {
|
|
54
54
|
type: "object" as const,
|
|
55
55
|
properties: {
|
|
56
|
-
channel: { type: "string", description: "Channel name (e.g., #polaris-dev
|
|
56
|
+
channel: { type: "string", description: "Channel name (e.g., #polaris-dev). Omit to list available channels." },
|
|
57
57
|
user: { type: "string", description: "Your participant ID (e.g., user:manu)" },
|
|
58
58
|
session: { type: "string", description: "Session name (optional — auto-generated if omitted)" },
|
|
59
59
|
agent: { type: "string", description: "Agent identity (optional — defaults to agent:claude)" },
|
|
60
60
|
},
|
|
61
|
-
required: ["
|
|
61
|
+
required: ["user"],
|
|
62
62
|
},
|
|
63
63
|
},
|
|
64
64
|
{
|
|
@@ -128,7 +128,23 @@ mcp.setRequestHandler(CallToolRequestSchema, async (req) => {
|
|
|
128
128
|
const { name, arguments: args } = req.params;
|
|
129
129
|
|
|
130
130
|
if (name === "polaris_connect") {
|
|
131
|
-
const { channel, user, session, agent } = args as { channel
|
|
131
|
+
const { channel, user, session, agent } = args as { channel?: string; user: string; session?: string; agent?: string };
|
|
132
|
+
|
|
133
|
+
// If no channel specified, list available channels
|
|
134
|
+
if (!channel) {
|
|
135
|
+
try {
|
|
136
|
+
const res = await daemonGet("/channels");
|
|
137
|
+
if (res.ok) {
|
|
138
|
+
const body = await res.json() as { channels: string[] };
|
|
139
|
+
if (body.channels.length === 0) {
|
|
140
|
+
return { content: [{ type: "text", text: "No channels found. Create one by joining: `/polaris join #channel-name`" }] };
|
|
141
|
+
}
|
|
142
|
+
return { content: [{ type: "text", text: `Available channels:\n${body.channels.map(c => ` ${c}`).join("\n")}\n\nJoin one with: /polaris join #channel-name` }] };
|
|
143
|
+
}
|
|
144
|
+
} catch { /* fall through */ }
|
|
145
|
+
return { content: [{ type: "text", text: "Specify a channel: `/polaris join #channel-name`" }] };
|
|
146
|
+
}
|
|
147
|
+
|
|
132
148
|
const project = channel.replace(/^#/, ""); // strip leading # if present
|
|
133
149
|
try {
|
|
134
150
|
const res = await daemonPost("/connect", {
|
package/src/daemon/daemon.ts
CHANGED
|
@@ -651,6 +651,24 @@ export function startDaemon(port = Number(process.env.POLARIS_DAEMON_PORT ?? 432
|
|
|
651
651
|
return json(data);
|
|
652
652
|
}
|
|
653
653
|
|
|
654
|
+
// GET /channels — list available channels/projects
|
|
655
|
+
if (method === "GET" && pathname === "/channels") {
|
|
656
|
+
try {
|
|
657
|
+
const serviceUrl = getServiceUrl();
|
|
658
|
+
const res = await fetch(`${serviceUrl}/projects`, {
|
|
659
|
+
headers: await authHeaders(),
|
|
660
|
+
});
|
|
661
|
+
if (!res.ok) return error("Failed to fetch channels", res.status);
|
|
662
|
+
const projects = (await res.json()) as Array<{ name: string; slack_channel_name?: string }>;
|
|
663
|
+
const channels = projects
|
|
664
|
+
.filter((p) => p.name !== "_system")
|
|
665
|
+
.map((p) => `#${p.slack_channel_name || p.name}`);
|
|
666
|
+
return json({ channels });
|
|
667
|
+
} catch {
|
|
668
|
+
return error("API unreachable", 503);
|
|
669
|
+
}
|
|
670
|
+
}
|
|
671
|
+
|
|
654
672
|
// POST /backfill — recover lost events from daemon log
|
|
655
673
|
if (method === "POST" && pathname === "/backfill") {
|
|
656
674
|
try {
|
package/src/web/pages.ts
CHANGED
|
@@ -6,51 +6,108 @@ import type { Org } from "../service/db";
|
|
|
6
6
|
export function renderLandingPage(): string {
|
|
7
7
|
return `
|
|
8
8
|
${nav()}
|
|
9
|
-
<div class="max-w-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
9
|
+
<div class="max-w-3xl mx-auto px-6">
|
|
10
|
+
|
|
11
|
+
<!-- Hero -->
|
|
12
|
+
<div class="pt-24 pb-16">
|
|
13
|
+
<h1 class="text-4xl font-bold tracking-tight text-gray-900 sm:text-5xl">
|
|
14
|
+
Multiplayer collaboration<br>for Claude Code
|
|
13
15
|
</h1>
|
|
14
|
-
<p class="mt-
|
|
15
|
-
|
|
16
|
+
<p class="mt-4 text-lg text-gray-500 max-w-xl">
|
|
17
|
+
Your teammates see what your agent is doing. They can jump in from Slack and steer it in real time.
|
|
16
18
|
</p>
|
|
17
|
-
<div class="mt-
|
|
18
|
-
<a href="
|
|
19
|
-
|
|
20
|
-
Sign up with Google
|
|
21
|
-
</a>
|
|
22
|
-
<a href="/login" class="px-6 py-3 text-sm font-semibold text-gray-500 hover:text-gray-700 transition">Already have an account? Sign in</a>
|
|
19
|
+
<div class="mt-8 flex items-center gap-4">
|
|
20
|
+
<a href="#get-started" class="px-5 py-2.5 bg-gray-900 text-white text-sm font-medium rounded-lg hover:bg-gray-800 transition">Get started</a>
|
|
21
|
+
<a href="https://github.com/anthropics/polaris" class="text-sm font-medium text-gray-500 hover:text-gray-700 transition">GitHub</a>
|
|
23
22
|
</div>
|
|
24
|
-
</div>
|
|
25
23
|
|
|
26
|
-
|
|
27
|
-
<div>
|
|
28
|
-
<div class="
|
|
29
|
-
<
|
|
24
|
+
<!-- Terminal demo -->
|
|
25
|
+
<div class="mt-12 bg-gray-900 rounded-xl overflow-hidden shadow-2xl">
|
|
26
|
+
<div class="flex items-center gap-1.5 px-4 py-3 bg-gray-800">
|
|
27
|
+
<div class="w-3 h-3 rounded-full bg-red-500/80"></div>
|
|
28
|
+
<div class="w-3 h-3 rounded-full bg-yellow-500/80"></div>
|
|
29
|
+
<div class="w-3 h-3 rounded-full bg-green-500/80"></div>
|
|
30
|
+
<span class="ml-3 text-xs text-gray-500 font-mono">claude code</span>
|
|
30
31
|
</div>
|
|
31
|
-
<
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
32
|
+
<div class="px-5 py-4 font-mono text-sm leading-relaxed space-y-3">
|
|
33
|
+
<div>
|
|
34
|
+
<span class="text-green-400">$</span> <span class="text-gray-300">npm install -g @lightupai/polaris</span>
|
|
35
|
+
</div>
|
|
36
|
+
<div>
|
|
37
|
+
<span class="text-green-400">$</span> <span class="text-gray-300">polaris</span>
|
|
38
|
+
</div>
|
|
39
|
+
<div class="text-gray-500"> Hooks installed. MCP server registered. Logged in as manu@acme.dev</div>
|
|
40
|
+
<div class="border-t border-gray-700 pt-3">
|
|
41
|
+
<span class="text-polaris-400">></span> <span class="text-gray-300">/polaris join #webapp</span>
|
|
42
|
+
</div>
|
|
43
|
+
<div class="text-gray-500"> Connected to webapp/s-4f2a as user:manu</div>
|
|
44
|
+
<div class="border-t border-gray-700 pt-3">
|
|
45
|
+
<span class="text-polaris-400">></span> <span class="text-gray-300">implement the auth middleware using RS256</span>
|
|
46
|
+
</div>
|
|
47
|
+
<div class="text-gray-400"> I'll create src/middleware/auth.ts with RS256 JWT verification...</div>
|
|
48
|
+
<div class="mt-1 pl-4 border-l-2 border-yellow-500/50">
|
|
49
|
+
<span class="text-yellow-400 text-xs">priya via slack</span>
|
|
50
|
+
<span class="text-gray-400"> — make sure to add rate limiting on that endpoint</span>
|
|
51
|
+
</div>
|
|
52
|
+
<div class="text-gray-400"> Good call. Adding rate limiting middleware before deploying...</div>
|
|
37
53
|
</div>
|
|
38
|
-
<h3 class="text-sm font-semibold text-gray-900">Context injection</h3>
|
|
39
|
-
<p class="mt-2 text-sm text-gray-600">Teammates inject expertise directly into your agent session from Slack, WhatsApp, or any floor.</p>
|
|
40
54
|
</div>
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
55
|
+
</div>
|
|
56
|
+
|
|
57
|
+
<!-- How it works -->
|
|
58
|
+
<div class="py-16 border-t border-gray-200">
|
|
59
|
+
<h2 class="text-sm font-semibold text-gray-400 uppercase tracking-wider">How it works</h2>
|
|
60
|
+
<div class="mt-8 space-y-8">
|
|
61
|
+
<div class="flex gap-4">
|
|
62
|
+
<div class="w-8 h-8 rounded-lg bg-gray-900 flex items-center justify-center text-white text-sm font-bold shrink-0">1</div>
|
|
63
|
+
<div>
|
|
64
|
+
<h3 class="font-semibold text-gray-900">Install</h3>
|
|
65
|
+
<p class="mt-1 text-sm text-gray-500"><code class="bg-gray-100 px-1.5 py-0.5 rounded text-gray-700 text-xs">npm install -g @lightupai/polaris && polaris</code> sets up hooks, MCP server, and authenticates your team.</p>
|
|
66
|
+
</div>
|
|
67
|
+
</div>
|
|
68
|
+
<div class="flex gap-4">
|
|
69
|
+
<div class="w-8 h-8 rounded-lg bg-gray-900 flex items-center justify-center text-white text-sm font-bold shrink-0">2</div>
|
|
70
|
+
<div>
|
|
71
|
+
<h3 class="font-semibold text-gray-900">Connect</h3>
|
|
72
|
+
<p class="mt-1 text-sm text-gray-500"><code class="bg-gray-100 px-1.5 py-0.5 rounded text-gray-700 text-xs">/polaris join #your-channel</code> links your Claude Code session to your team's Slack channel.</p>
|
|
73
|
+
</div>
|
|
74
|
+
</div>
|
|
75
|
+
<div class="flex gap-4">
|
|
76
|
+
<div class="w-8 h-8 rounded-lg bg-gray-900 flex items-center justify-center text-white text-sm font-bold shrink-0">3</div>
|
|
77
|
+
<div>
|
|
78
|
+
<h3 class="font-semibold text-gray-900">Collaborate</h3>
|
|
79
|
+
<p class="mt-1 text-sm text-gray-500">Every prompt and response streams to Slack. Teammates reply there and their messages appear inline in your agent session.</p>
|
|
80
|
+
</div>
|
|
44
81
|
</div>
|
|
45
|
-
<h3 class="text-sm font-semibold text-gray-900">Multiplayer</h3>
|
|
46
|
-
<p class="mt-2 text-sm text-gray-600">Multiple drivers, concurrent sessions, seamless handoffs. Humans and AI agents as first-class participants.</p>
|
|
47
82
|
</div>
|
|
48
83
|
</div>
|
|
49
84
|
|
|
85
|
+
<!-- Why Polaris -->
|
|
50
86
|
<div class="py-16 border-t border-gray-200">
|
|
51
|
-
<h2 class="text-
|
|
52
|
-
<
|
|
87
|
+
<h2 class="text-sm font-semibold text-gray-400 uppercase tracking-wider">Why Polaris</h2>
|
|
88
|
+
<div class="mt-8 grid grid-cols-1 md:grid-cols-2 gap-6">
|
|
89
|
+
<div class="border border-gray-200 rounded-lg p-5">
|
|
90
|
+
<h3 class="font-semibold text-gray-900">No context switching</h3>
|
|
91
|
+
<p class="mt-2 text-sm text-gray-500">Teammate messages arrive directly in your coding session. No tab switching, no copy-pasting, no "hey can you check Slack."</p>
|
|
92
|
+
</div>
|
|
93
|
+
<div class="border border-gray-200 rounded-lg p-5">
|
|
94
|
+
<h3 class="font-semibold text-gray-900">Full session visibility</h3>
|
|
95
|
+
<p class="mt-2 text-sm text-gray-500">Every prompt, tool call, and response is captured and streamed to your team. Anyone can see what's happening and jump in.</p>
|
|
96
|
+
</div>
|
|
97
|
+
<div class="border border-gray-200 rounded-lg p-5">
|
|
98
|
+
<h3 class="font-semibold text-gray-900">Human + AI multiplayer</h3>
|
|
99
|
+
<p class="mt-2 text-sm text-gray-500">Multiple developers, multiple agents, concurrent sessions. Humans and AI are first-class participants on the same floor.</p>
|
|
100
|
+
</div>
|
|
101
|
+
<div class="border border-gray-200 rounded-lg p-5">
|
|
102
|
+
<h3 class="font-semibold text-gray-900">Two-minute setup</h3>
|
|
103
|
+
<p class="mt-2 text-sm text-gray-500">One npm install, one command. No config files, no Docker, no infrastructure. Works with your existing Slack workspace.</p>
|
|
104
|
+
</div>
|
|
105
|
+
</div>
|
|
106
|
+
</div>
|
|
53
107
|
|
|
108
|
+
<!-- Slack demo -->
|
|
109
|
+
<div class="py-16 border-t border-gray-200">
|
|
110
|
+
<h2 class="text-sm font-semibold text-gray-400 uppercase tracking-wider">What your team sees in Slack</h2>
|
|
54
111
|
<div class="mt-8 bg-white border border-gray-200 rounded-xl shadow-lg overflow-hidden">
|
|
55
112
|
<div class="flex">
|
|
56
113
|
<div class="w-14 bg-[#4A154B] shrink-0 flex flex-col items-center py-3 gap-3">
|
|
@@ -58,9 +115,6 @@ export function renderLandingPage(): string {
|
|
|
58
115
|
<div class="w-8 h-8 rounded-lg bg-white/10 flex items-center justify-center">
|
|
59
116
|
<svg class="w-4 h-4 text-white/60" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M8 10h.01M12 10h.01M16 10h.01M21 12c0 4.418-4.03 8-9 8a9.863 9.863 0 01-4.255-.949L3 20l1.395-3.72C3.512 15.042 3 13.574 3 12c0-4.418 4.03-8 9-8s9 3.582 9 8z"/></svg>
|
|
60
117
|
</div>
|
|
61
|
-
<div class="w-8 h-8 rounded-lg bg-white/10 flex items-center justify-center">
|
|
62
|
-
<svg class="w-4 h-4 text-white/60" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M15 17h5l-1.405-1.405A2.032 2.032 0 0118 14.158V11a6.002 6.002 0 00-4-5.659V5a2 2 0 10-4 0v.341C7.67 6.165 6 8.388 6 11v3.159c0 .538-.214 1.055-.595 1.436L4 17h5m6 0v1a3 3 0 11-6 0v-1m6 0H9"/></svg>
|
|
63
|
-
</div>
|
|
64
118
|
</div>
|
|
65
119
|
<div class="flex-1">
|
|
66
120
|
<div class="border-b border-gray-200 px-4 py-2.5 flex items-center justify-between">
|
|
@@ -107,33 +161,46 @@ export function renderLandingPage(): string {
|
|
|
107
161
|
<p class="text-gray-700">Good point from Priya. Switching to RS256 and updating the key config...</p>
|
|
108
162
|
</div>
|
|
109
163
|
</div>
|
|
110
|
-
<div class="flex gap-3">
|
|
111
|
-
<div class="w-8 h-8 rounded-md bg-purple-600 flex items-center justify-center text-white text-xs font-bold shrink-0 mt-0.5">
|
|
112
|
-
<svg class="w-4 h-4 text-white" fill="currentColor" viewBox="0 0 24 24"><path d="M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm-2 15l-5-5 1.41-1.41L10 14.17l7.59-7.59L19 8l-9 9z"/></svg>
|
|
113
|
-
</div>
|
|
114
|
-
<div>
|
|
115
|
-
<div class="flex items-baseline gap-2"><span class="text-gray-900 font-bold text-sm">security-bot</span><span class="bg-gray-100 text-gray-600 text-xs px-1.5 py-0.5 rounded">agent</span><span class="text-gray-400 text-xs">→ auth</span><span class="text-gray-400 text-xs">10:34 AM</span></div>
|
|
116
|
-
<p class="text-gray-700">This auth endpoint needs rate limiting before going to production</p>
|
|
117
|
-
</div>
|
|
118
|
-
</div>
|
|
119
|
-
<div class="flex gap-3">
|
|
120
|
-
<div class="w-8 h-8 rounded-md bg-green-600 flex items-center justify-center text-white text-xs font-bold shrink-0 mt-0.5">AI</div>
|
|
121
|
-
<div>
|
|
122
|
-
<div class="flex items-baseline gap-2"><span class="text-gray-900 font-bold text-sm">Agent</span><span class="text-gray-400 text-xs">→ manu/auth</span><span class="text-gray-400 text-xs">10:34 AM</span></div>
|
|
123
|
-
<p class="text-gray-700">Adding rate limiting middleware to the auth endpoints...</p>
|
|
124
|
-
</div>
|
|
125
|
-
</div>
|
|
126
164
|
</div>
|
|
127
165
|
</div>
|
|
128
166
|
</div>
|
|
129
167
|
</div>
|
|
130
|
-
|
|
168
|
+
</div>
|
|
169
|
+
|
|
170
|
+
<!-- Get Started -->
|
|
171
|
+
<div id="get-started" class="py-16 border-t border-gray-200">
|
|
172
|
+
<h2 class="text-2xl font-bold text-gray-900">Get started</h2>
|
|
173
|
+
<div class="mt-6 bg-gray-900 rounded-xl overflow-hidden">
|
|
174
|
+
<div class="flex items-center justify-between px-4 py-2.5 bg-gray-800">
|
|
175
|
+
<span class="text-xs text-gray-500 font-mono">terminal</span>
|
|
176
|
+
<button class="polaris-copy text-gray-500 hover:text-gray-300 transition" data-copy="install-cmd">
|
|
177
|
+
<svg class="w-3.5 h-3.5" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M8 16H6a2 2 0 01-2-2V6a2 2 0 012-2h8a2 2 0 012 2v2m-6 12h8a2 2 0 002-2v-8a2 2 0 00-2-2h-8a2 2 0 00-2 2v8a2 2 0 002 2z"/></svg>
|
|
178
|
+
</button>
|
|
179
|
+
</div>
|
|
180
|
+
<pre class="px-5 py-4 font-mono text-sm text-gray-300 leading-relaxed" id="install-cmd"><span class="text-green-400">$</span> npm install -g @lightupai/polaris
|
|
181
|
+
<span class="text-green-400">$</span> polaris</pre>
|
|
182
|
+
</div>
|
|
183
|
+
<p class="mt-4 text-sm text-gray-500">Then in Claude Code:</p>
|
|
184
|
+
<div class="mt-2 bg-gray-900 rounded-xl overflow-hidden">
|
|
185
|
+
<pre class="px-5 py-4 font-mono text-sm text-gray-300 leading-relaxed"><span class="text-polaris-400">></span> /polaris join #your-channel</pre>
|
|
186
|
+
</div>
|
|
187
|
+
<p class="mt-6 text-sm text-gray-500">That's it. Your session is now live to your team.</p>
|
|
188
|
+
<div class="mt-8">
|
|
189
|
+
<a href="/signup" class="inline-flex items-center gap-3 px-5 py-2.5 bg-white border border-gray-300 rounded-lg shadow-sm hover:bg-gray-50 transition text-sm font-medium text-gray-700">
|
|
190
|
+
<svg width="16" height="16" viewBox="0 0 18 18" xmlns="http://www.w3.org/2000/svg"><path d="M17.64 9.2c0-.637-.057-1.251-.164-1.84H9v3.481h4.844a4.14 4.14 0 01-1.796 2.716v2.259h2.908c1.702-1.567 2.684-3.875 2.684-6.615z" fill="#4285F4"/><path d="M9 18c2.43 0 4.467-.806 5.956-2.18l-2.908-2.259c-.806.54-1.837.86-3.048.86-2.344 0-4.328-1.584-5.036-3.711H.957v2.332A8.997 8.997 0 009 18z" fill="#34A853"/><path d="M3.964 10.71A5.41 5.41 0 013.682 9c0-.593.102-1.17.282-1.71V4.958H.957A8.996 8.996 0 000 9c0 1.452.348 2.827.957 4.042l3.007-2.332z" fill="#FBBC05"/><path d="M9 3.58c1.321 0 2.508.454 3.44 1.345l2.582-2.58C13.463.891 11.426 0 9 0A8.997 8.997 0 00.957 4.958L3.964 6.29C4.672 4.163 6.656 2.58 9 3.58z" fill="#EA4335"/></svg>
|
|
191
|
+
Sign up with Google
|
|
192
|
+
</a>
|
|
193
|
+
</div>
|
|
131
194
|
</div>
|
|
132
195
|
</div>
|
|
133
196
|
|
|
134
|
-
<footer class="border-t border-gray-200 mt-
|
|
135
|
-
<div class="max-w-
|
|
136
|
-
Polaris
|
|
197
|
+
<footer class="border-t border-gray-200 mt-8">
|
|
198
|
+
<div class="max-w-3xl mx-auto px-6 py-8 flex items-center justify-between text-sm text-gray-400">
|
|
199
|
+
<span>Polaris</span>
|
|
200
|
+
<div class="flex items-center gap-6">
|
|
201
|
+
<a href="https://github.com/anthropics/polaris" class="hover:text-gray-600 transition">GitHub</a>
|
|
202
|
+
<a href="/login" class="hover:text-gray-600 transition">Sign in</a>
|
|
203
|
+
</div>
|
|
137
204
|
</div>
|
|
138
205
|
</footer>`;
|
|
139
206
|
}
|
package/tests/web.test.ts
CHANGED
|
@@ -275,7 +275,7 @@ describe("routes", () => {
|
|
|
275
275
|
const res = await app.request("/");
|
|
276
276
|
expect(res.status).toBe(200);
|
|
277
277
|
const body = await res.text();
|
|
278
|
-
expect(body).toContain("Multiplayer
|
|
278
|
+
expect(body).toContain("Multiplayer collaboration");
|
|
279
279
|
});
|
|
280
280
|
|
|
281
281
|
test("GET /preview returns 200", async () => {
|