conectese 0.1.15 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +2 -2
- package/agents/conectese_admin/AGENT.md +10 -5
- package/agents/data-extractor/AGENT.md +35 -3
- package/agents/direito-administrativo/AGENT.md +68 -22
- package/agents/direito-aduaneiro/AGENT.md +55 -0
- package/agents/direito-aeroportuario/AGENT.md +53 -0
- package/agents/direito-agrario/AGENT.md +73 -0
- package/agents/direito-ambiental/AGENT.md +61 -24
- package/agents/direito-bancario/AGENT.md +79 -0
- package/agents/direito-civil/AGENT.md +71 -20
- package/agents/direito-constitucional/AGENT.md +79 -0
- package/agents/direito-da-crianca-e-do-adolescente-eca/AGENT.md +69 -0
- package/agents/direito-da-propriedade-intelectual/AGENT.md +65 -0
- package/agents/direito-de-familia/AGENT.md +92 -0
- package/agents/direito-de-transito/AGENT.md +59 -0
- package/agents/direito-desportivo/AGENT.md +40 -27
- package/agents/direito-digital/AGENT.md +61 -24
- package/agents/direito-do-consumidor/AGENT.md +77 -0
- package/agents/direito-do-trabalho/AGENT.md +73 -20
- package/agents/direito-economico/AGENT.md +58 -0
- package/agents/direito-eleitoral/AGENT.md +56 -24
- package/agents/direito-empresarial/AGENT.md +62 -24
- package/agents/direito-financeiro/AGENT.md +63 -0
- package/agents/direito-imobiliario/AGENT.md +79 -0
- package/agents/direito-indigena/AGENT.md +72 -0
- package/agents/direito-internacional/AGENT.md +55 -24
- package/agents/direito-maritimo/AGENT.md +55 -0
- package/agents/direito-medico-e-da-saude/AGENT.md +69 -0
- package/agents/direito-militar/AGENT.md +54 -24
- package/agents/direito-notarial-e-registral/AGENT.md +47 -24
- package/agents/direito-penal/AGENT.md +74 -19
- package/agents/direito-previdenciario/AGENT.md +84 -0
- package/agents/direito-processal-civil/AGENT.md +72 -20
- package/agents/direito-processal-do-trabalho/AGENT.md +58 -24
- package/agents/direito-processal-militar/AGENT.md +38 -27
- package/agents/direito-processal-penal/AGENT.md +62 -24
- package/agents/direito-processual-civil/AGENT.md +94 -0
- package/agents/direito-processual-do-trabalho/AGENT.md +76 -0
- package/agents/direito-processual-militar/AGENT.md +53 -0
- package/agents/direito-processual-penal/AGENT.md +80 -0
- package/agents/direito-securitario/AGENT.md +75 -0
- package/agents/direito-sindical/AGENT.md +46 -24
- package/agents/direito-societario/AGENT.md +65 -0
- package/agents/direito-tributario/AGENT.md +98 -0
- package/agents/direito-urbanistico/AGENT.md +58 -0
- package/agents/direitos-humanos/AGENT.md +76 -0
- package/agents/legal-analyst/AGENT.md +1 -1
- package/agents/legal-designer/AGENT.md +8 -3
- package/agents/lgpd-anonymizer/AGENT.md +66 -4
- package/agents/lgpd-restorer/AGENT.md +38 -4
- package/agents/task-router/AGENT.md +47 -3
- package/bin/conectese.js +4 -4
- package/dashboard/src/App.tsx +6 -4
- package/dashboard/src/components/CheckpointModal.tsx +150 -0
- package/dashboard/src/components/StatusBadge.tsx +3 -3
- package/dashboard/src/components/StatusBar.tsx +7 -7
- package/dashboard/src/components/{SquadCard.tsx → TeamCard.tsx} +7 -7
- package/dashboard/src/components/{SquadSelector.tsx → TeamSelector.tsx} +18 -18
- package/dashboard/src/hooks/{useSquadSocket.ts → useTeamSocket.ts} +13 -13
- package/dashboard/src/lib/normalizeState.ts +3 -3
- package/dashboard/src/office/OfficeScene.ts +3 -3
- package/dashboard/src/office/PhaserGame.tsx +6 -6
- package/dashboard/src/plugin/{squadWatcher.ts → teamWatcher.ts} +83 -44
- package/dashboard/src/store/useTeamStore.ts +56 -0
- package/dashboard/src/types/state.ts +10 -9
- package/dashboard/tsconfig.tsbuildinfo +1 -1
- package/dashboard/vite.config.ts +2 -2
- package/package.json +12 -9
- package/skills/README.md +1 -1
- package/skills/conectese-agent-creator/SKILL.md +1 -1
- package/skills/conectese-design/SKILL.md +50 -0
- package/skills/conectese-skill-creator/SKILL.md +3 -3
- package/skills/conectese-skill-creator/references/skill-format.md +4 -4
- package/skills/document-analyzer/SKILL.md +40 -0
- package/skills/document-analyzer/scripts/parse-doc.js +113 -0
- package/skills/image-ai-generator/SKILL.md +5 -5
- package/skills/image-creator/SKILL.md +3 -3
- package/skills/image-fetcher/SKILL.md +5 -5
- package/skills/instagram-publisher/SKILL.md +2 -2
- package/skills/jurisprudencia-validator/SKILL.md +45 -0
- package/skills/jurisprudencia-validator/scripts/validate-urls.js +103 -0
- package/skills/legal-pricing/SKILL.md +33 -0
- package/skills/legal-pricing/scripts/pricing-engine.js +72 -0
- package/skills/resend/SKILL.md +2 -2
- package/skills/template-designer/SKILL.md +29 -29
- package/src/agents-cli.js +1 -1
- package/src/agents.js +1 -1
- package/src/init.js +2 -1
- package/src/locales/en.json +2 -2
- package/src/locales/es.json +2 -2
- package/src/locales/pt-BR.json +2 -2
- package/src/readme/README.md +26 -26
- package/src/runs.js +14 -14
- package/src/skills-cli.js +1 -1
- package/src/update.js +1 -1
- package/templates/_conectese/.conectese-version +1 -1
- package/templates/ide-templates/antigravity/.agent/rules/conectese.md +10 -10
- package/templates/ide-templates/antigravity/.agent/workflows/conectese.md +18 -18
- package/templates/ide-templates/claude-code/.claude/skills/conectese/SKILL.md +31 -31
- package/templates/ide-templates/claude-code/CLAUDE.md +10 -10
- package/templates/ide-templates/codex/.agents/skills/conectese/SKILL.md +1 -1
- package/templates/ide-templates/codex/AGENTS.md +18 -18
- package/templates/ide-templates/cursor/.cursor/commands/conectese.md +1 -1
- package/templates/ide-templates/opencode/.opencode/commands/conectese.md +1 -1
- package/templates/ide-templates/opencode/AGENTS.md +18 -18
- package/templates/ide-templates/vscode-copilot/.github/prompts/conectese.prompt.md +33 -33
- package/_conectese/.conectese-version +0 -1
- package/_conectese/_memory/company/modelo_contrato.md +0 -18
- package/_conectese/_memory/company/modelo_procuracao.md +0 -15
- package/_conectese/_memory/company/modelo_recibo.md +0 -13
- package/_conectese/config/playwright.config.json +0 -11
- package/_conectese/core/architect.agent.yaml +0 -110
- package/_conectese/core/best-practices/_catalog.yaml +0 -116
- package/_conectese/core/best-practices/blog-post.md +0 -132
- package/_conectese/core/best-practices/blog-seo.md +0 -127
- package/_conectese/core/best-practices/copywriting.md +0 -426
- package/_conectese/core/best-practices/data-analysis.md +0 -401
- package/_conectese/core/best-practices/email-newsletter.md +0 -118
- package/_conectese/core/best-practices/email-sales.md +0 -110
- package/_conectese/core/best-practices/image-design.md +0 -348
- package/_conectese/core/best-practices/instagram-feed.md +0 -235
- package/_conectese/core/best-practices/instagram-reels.md +0 -112
- package/_conectese/core/best-practices/instagram-stories.md +0 -107
- package/_conectese/core/best-practices/linkedin-article.md +0 -116
- package/_conectese/core/best-practices/linkedin-post.md +0 -121
- package/_conectese/core/best-practices/researching.md +0 -349
- package/_conectese/core/best-practices/review.md +0 -269
- package/_conectese/core/best-practices/social-networks-publishing.md +0 -294
- package/_conectese/core/best-practices/strategist.md +0 -344
- package/_conectese/core/best-practices/technical-writing.md +0 -365
- package/_conectese/core/best-practices/twitter-post.md +0 -105
- package/_conectese/core/best-practices/twitter-thread.md +0 -122
- package/_conectese/core/best-practices/whatsapp-broadcast.md +0 -107
- package/_conectese/core/best-practices/youtube-script.md +0 -122
- package/_conectese/core/best-practices/youtube-shorts.md +0 -112
- package/_conectese/core/prompts/build.prompt.md +0 -547
- package/_conectese/core/prompts/design.prompt.md +0 -469
- package/_conectese/core/prompts/discovery.prompt.md +0 -269
- package/_conectese/core/prompts/sherlock-instagram.md +0 -123
- package/_conectese/core/prompts/sherlock-linkedin.md +0 -73
- package/_conectese/core/prompts/sherlock-shared.md +0 -684
- package/_conectese/core/prompts/sherlock-twitter.md +0 -78
- package/_conectese/core/prompts/sherlock-youtube.md +0 -85
- package/_conectese/core/runner.pipeline.md +0 -535
- package/_conectese/core/skills.engine.md +0 -381
- package/agents/direito-adaneiro/AGENT.md +0 -42
- package/agents/direito-aeroporta-rio/AGENT.md +0 -42
- package/agents/direito-agra-rio/AGENT.md +0 -42
- package/agents/direito-banca-rio/AGENT.md +0 -42
- package/agents/direito-constitcional/AGENT.md +0 -42
- package/agents/direito-da-crianc-a-e-do-adolescente-eca/AGENT.md +0 -42
- package/agents/direito-da-propriedade-intelectal/AGENT.md +0 -42
- package/agents/direito-de-ami-lia/AGENT.md +0 -42
- package/agents/direito-de-tra-nsito/AGENT.md +0 -42
- package/agents/direito-do-consmidor/AGENT.md +0 -42
- package/agents/direito-econo-mico/AGENT.md +0 -42
- package/agents/direito-imobilia-rio/AGENT.md +0 -42
- package/agents/direito-inanceiro/AGENT.md +0 -42
- package/agents/direito-mari-timo/AGENT.md +0 -42
- package/agents/direito-me-dico-e-da-sa-de/AGENT.md +0 -42
- package/agents/direito-ndia-rio/AGENT.md +0 -42
- package/agents/direito-previdencia-rio/AGENT.md +0 -42
- package/agents/direito-rbani-stico/AGENT.md +0 -42
- package/agents/direito-secrita-rio/AGENT.md +0 -42
- package/agents/direito-societa-rio/AGENT.md +0 -42
- package/agents/direito-tribta-rio/AGENT.md +0 -42
- package/agents/direitos-hmanos/AGENT.md +0 -42
- package/dashboard/src/store/useSquadStore.ts +0 -56
package/bin/conectese.js
CHANGED
|
@@ -47,8 +47,8 @@ if (command === 'init') {
|
|
|
47
47
|
const result = await agentsCli(subcommand, args, process.cwd());
|
|
48
48
|
if (!result.success) process.exitCode = 1;
|
|
49
49
|
} else if (command === 'runs') {
|
|
50
|
-
const
|
|
51
|
-
const runs = await listRuns(
|
|
50
|
+
const teamName = positionals[1] || null;
|
|
51
|
+
const runs = await listRuns(teamName, process.cwd());
|
|
52
52
|
printRuns(runs);
|
|
53
53
|
} else {
|
|
54
54
|
console.log(`
|
|
@@ -65,9 +65,9 @@ if (command === 'init') {
|
|
|
65
65
|
npx conectese agents install <name> Install a predefined agent
|
|
66
66
|
npx conectese agents remove <name> Remove an agent
|
|
67
67
|
npx conectese agents update Update all agents
|
|
68
|
-
npx conectese runs [
|
|
68
|
+
npx conectese runs [team-name] View execution history
|
|
69
69
|
|
|
70
|
-
Learn more: https://github.com/
|
|
70
|
+
Learn more: https://github.com/henriqueadm-ai/conectese
|
|
71
71
|
`);
|
|
72
72
|
if (command) process.exitCode = 1;
|
|
73
73
|
}
|
package/dashboard/src/App.tsx
CHANGED
|
@@ -1,12 +1,13 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
1
|
+
import { useTeamSocket } from "@/hooks/useTeamSocket";
|
|
2
|
+
import { TeamSelector } from "@/components/TeamSelector";
|
|
3
3
|
import { PhaserGame } from "@/office/PhaserGame";
|
|
4
4
|
import { StatusBar } from "@/components/StatusBar";
|
|
5
5
|
import { IngestionModal } from "@/components/IngestionModal";
|
|
6
|
+
import { CheckpointModal } from "@/components/CheckpointModal";
|
|
6
7
|
import { useState } from "react";
|
|
7
8
|
|
|
8
9
|
export function App() {
|
|
9
|
-
|
|
10
|
+
useTeamSocket();
|
|
10
11
|
const [showIngestion, setShowIngestion] = useState(false);
|
|
11
12
|
|
|
12
13
|
return (
|
|
@@ -74,7 +75,7 @@ export function App() {
|
|
|
74
75
|
|
|
75
76
|
{/* Main content */}
|
|
76
77
|
<div style={{ display: "flex", flex: 1, overflow: "hidden" }}>
|
|
77
|
-
<
|
|
78
|
+
<TeamSelector />
|
|
78
79
|
<PhaserGame />
|
|
79
80
|
</div>
|
|
80
81
|
|
|
@@ -83,6 +84,7 @@ export function App() {
|
|
|
83
84
|
|
|
84
85
|
{/* Overlays */}
|
|
85
86
|
{showIngestion && <IngestionModal onClose={() => setShowIngestion(false)} />}
|
|
87
|
+
<CheckpointModal />
|
|
86
88
|
</div>
|
|
87
89
|
);
|
|
88
90
|
}
|
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
import { useState, useEffect } from "react";
|
|
2
|
+
import { useTeamStore } from "@/store/useTeamStore";
|
|
3
|
+
|
|
4
|
+
export function CheckpointModal() {
|
|
5
|
+
const selectedTeam = useTeamStore((s) => s.selectedTeam);
|
|
6
|
+
const state = useTeamStore((s) =>
|
|
7
|
+
s.selectedTeam ? s.activeStates.get(s.selectedTeam) : undefined
|
|
8
|
+
);
|
|
9
|
+
|
|
10
|
+
const [formData, setFormData] = useState<Record<string, string>>({});
|
|
11
|
+
const [submitting, setSubmitting] = useState(false);
|
|
12
|
+
const [error, setError] = useState("");
|
|
13
|
+
|
|
14
|
+
const isActive = state?.status === "checkpoint" && state.handoff?.missingData && state.handoff.missingData.length > 0;
|
|
15
|
+
const missingFields = state?.handoff?.missingData || [];
|
|
16
|
+
|
|
17
|
+
// Reset form when checkpoint fields change
|
|
18
|
+
useEffect(() => {
|
|
19
|
+
if (isActive) {
|
|
20
|
+
const initial: Record<string, string> = {};
|
|
21
|
+
missingFields.forEach(f => initial[f] = "");
|
|
22
|
+
setFormData(initial);
|
|
23
|
+
setError("");
|
|
24
|
+
}
|
|
25
|
+
}, [isActive, missingFields.join(",")]);
|
|
26
|
+
|
|
27
|
+
if (!isActive) return null;
|
|
28
|
+
|
|
29
|
+
const handleSubmit = async (e: React.FormEvent) => {
|
|
30
|
+
e.preventDefault();
|
|
31
|
+
if (!selectedTeam) return;
|
|
32
|
+
|
|
33
|
+
// Build payload ensuring numbers where appropriate
|
|
34
|
+
const payload: Record<string, string | number> = {};
|
|
35
|
+
for (const [key, val] of Object.entries(formData)) {
|
|
36
|
+
if (val.trim() === "") continue; // or might throw an error
|
|
37
|
+
const num = Number(val);
|
|
38
|
+
payload[key] = isNaN(num) ? val : num;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
setSubmitting(true);
|
|
42
|
+
setError("");
|
|
43
|
+
|
|
44
|
+
try {
|
|
45
|
+
const res = await fetch(`/api/team/${selectedTeam}/checkpoint`, {
|
|
46
|
+
method: "POST",
|
|
47
|
+
headers: { "Content-Type": "application/json" },
|
|
48
|
+
body: JSON.stringify(payload),
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
if (!res.ok) {
|
|
52
|
+
const data = await res.json();
|
|
53
|
+
throw new Error(data.error || "Failed to submit checkpoint data");
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
// Successfully submitted.
|
|
57
|
+
// The watcher plugin will reset the team status to "running"
|
|
58
|
+
// avoiding the need to manually close the modal.
|
|
59
|
+
} catch (err: any) {
|
|
60
|
+
setError(err.message || "Erro desconhecido ao submeter dados financeiros.");
|
|
61
|
+
} finally {
|
|
62
|
+
setSubmitting(false);
|
|
63
|
+
}
|
|
64
|
+
};
|
|
65
|
+
|
|
66
|
+
return (
|
|
67
|
+
<div style={{
|
|
68
|
+
position: "fixed",
|
|
69
|
+
top: 0, left: 0, right: 0, bottom: 0,
|
|
70
|
+
backgroundColor: "rgba(0, 0, 0, 0.7)",
|
|
71
|
+
display: "flex",
|
|
72
|
+
alignItems: "center",
|
|
73
|
+
justifyContent: "center",
|
|
74
|
+
zIndex: 9999,
|
|
75
|
+
backdropFilter: "blur(2px)"
|
|
76
|
+
}}>
|
|
77
|
+
<div style={{
|
|
78
|
+
backgroundColor: "var(--bg-main, #1e1e1e)",
|
|
79
|
+
borderRadius: 8,
|
|
80
|
+
width: 400,
|
|
81
|
+
maxWidth: "90%",
|
|
82
|
+
padding: 24,
|
|
83
|
+
boxShadow: "0 10px 25px rgba(0,0,0,0.5)",
|
|
84
|
+
border: "1px solid var(--border, #333)",
|
|
85
|
+
display: "flex",
|
|
86
|
+
flexDirection: "column",
|
|
87
|
+
gap: 16
|
|
88
|
+
}}>
|
|
89
|
+
<div style={{ display: "flex", justifyContent: "space-between", alignItems: "center" }}>
|
|
90
|
+
<h2 style={{ margin: 0, fontSize: 18, color: "var(--accent, #3b82f6)" }}>Atenção Necessária</h2>
|
|
91
|
+
</div>
|
|
92
|
+
|
|
93
|
+
<p style={{ margin: 0, fontSize: 13, color: "var(--text-muted, #ccc)" }}>
|
|
94
|
+
O agente encontrou dados faltantes (Halt & Catch protocol).
|
|
95
|
+
Por favor, preencha os valores abaixo para que o cálculo possa continuar com segurança.
|
|
96
|
+
</p>
|
|
97
|
+
|
|
98
|
+
<form onSubmit={handleSubmit} style={{ display: "flex", flexDirection: "column", gap: 12 }}>
|
|
99
|
+
{missingFields.map((field) => (
|
|
100
|
+
<div key={field}>
|
|
101
|
+
<label style={{ display: "block", fontSize: 12, marginBottom: 4, fontWeight: "bold" }}>
|
|
102
|
+
{field}
|
|
103
|
+
</label>
|
|
104
|
+
<input
|
|
105
|
+
type="text"
|
|
106
|
+
required
|
|
107
|
+
value={formData[field] || ""}
|
|
108
|
+
onChange={(e) => setFormData({ ...formData, [field]: e.target.value })}
|
|
109
|
+
style={{
|
|
110
|
+
width: "100%",
|
|
111
|
+
padding: "8px 12px",
|
|
112
|
+
borderRadius: 4,
|
|
113
|
+
border: "1px solid var(--border, #444)",
|
|
114
|
+
background: "var(--bg-sidebar, #121212)",
|
|
115
|
+
color: "inherit",
|
|
116
|
+
boxSizing: "border-box"
|
|
117
|
+
}}
|
|
118
|
+
/>
|
|
119
|
+
</div>
|
|
120
|
+
))}
|
|
121
|
+
|
|
122
|
+
{error && (
|
|
123
|
+
<div style={{ color: "#ef4444", fontSize: 13, background: "rgba(239,68,68,0.1)", padding: 8, borderRadius: 4, textAlign: "center" }}>
|
|
124
|
+
{error}
|
|
125
|
+
</div>
|
|
126
|
+
)}
|
|
127
|
+
|
|
128
|
+
<div style={{ display: "flex", justifyContent: "flex-end", gap: 8, marginTop: 8 }}>
|
|
129
|
+
<button
|
|
130
|
+
type="submit"
|
|
131
|
+
disabled={submitting}
|
|
132
|
+
style={{
|
|
133
|
+
padding: "8px 16px",
|
|
134
|
+
background: "var(--accent, #3b82f6)",
|
|
135
|
+
border: "none",
|
|
136
|
+
color: "white",
|
|
137
|
+
borderRadius: 4,
|
|
138
|
+
cursor: submitting ? "not-allowed" : "pointer",
|
|
139
|
+
opacity: submitting ? 0.6 : 1,
|
|
140
|
+
fontWeight: 500
|
|
141
|
+
}}
|
|
142
|
+
>
|
|
143
|
+
{submitting ? "Enviando..." : "Enviar e Prosseguir"}
|
|
144
|
+
</button>
|
|
145
|
+
</div>
|
|
146
|
+
</form>
|
|
147
|
+
</div>
|
|
148
|
+
</div>
|
|
149
|
+
);
|
|
150
|
+
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { TeamStatus } from "@/types/state";
|
|
2
2
|
|
|
3
|
-
const STATUS_COLORS: Record<
|
|
3
|
+
const STATUS_COLORS: Record<TeamStatus | "inactive", string> = {
|
|
4
4
|
idle: "#888",
|
|
5
5
|
running: "var(--accent-cyan)",
|
|
6
6
|
completed: "var(--accent-green)",
|
|
@@ -9,7 +9,7 @@ const STATUS_COLORS: Record<SquadStatus | "inactive", string> = {
|
|
|
9
9
|
};
|
|
10
10
|
|
|
11
11
|
interface StatusBadgeProps {
|
|
12
|
-
status:
|
|
12
|
+
status: TeamStatus | "inactive";
|
|
13
13
|
}
|
|
14
14
|
|
|
15
15
|
export function StatusBadge({ status }: StatusBadgeProps) {
|
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
import { useEffect, useState } from "react";
|
|
2
|
-
import {
|
|
2
|
+
import { useTeamStore } from "@/store/useTeamStore";
|
|
3
3
|
import { formatElapsed } from "@/lib/formatTime";
|
|
4
4
|
|
|
5
5
|
export function StatusBar() {
|
|
6
|
-
const
|
|
7
|
-
const state =
|
|
8
|
-
s.
|
|
6
|
+
const selectedTeam = useTeamStore((s) => s.selectedTeam);
|
|
7
|
+
const state = useTeamStore((s) =>
|
|
8
|
+
s.selectedTeam ? s.activeStates.get(s.selectedTeam) : undefined
|
|
9
9
|
);
|
|
10
|
-
const isConnected =
|
|
10
|
+
const isConnected = useTeamStore((s) => s.isConnected);
|
|
11
11
|
|
|
12
12
|
// Elapsed timer
|
|
13
13
|
const [elapsed, setElapsed] = useState(0);
|
|
@@ -25,11 +25,11 @@ export function StatusBar() {
|
|
|
25
25
|
return () => clearInterval(interval);
|
|
26
26
|
}, [state?.startedAt]);
|
|
27
27
|
|
|
28
|
-
if (!
|
|
28
|
+
if (!selectedTeam || !state) {
|
|
29
29
|
return (
|
|
30
30
|
<footer style={footerStyle}>
|
|
31
31
|
<span style={{ color: "var(--text-secondary)" }}>
|
|
32
|
-
Select an active
|
|
32
|
+
Select an active team to monitor
|
|
33
33
|
</span>
|
|
34
34
|
<ConnectionDot connected={isConnected} />
|
|
35
35
|
</footer>
|
|
@@ -1,14 +1,14 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { TeamInfo, TeamState } from "@/types/state";
|
|
2
2
|
import { StatusBadge } from "./StatusBadge";
|
|
3
3
|
|
|
4
|
-
interface
|
|
5
|
-
|
|
6
|
-
state:
|
|
4
|
+
interface TeamCardProps {
|
|
5
|
+
team: TeamInfo;
|
|
6
|
+
state: TeamState | undefined;
|
|
7
7
|
isSelected: boolean;
|
|
8
8
|
onSelect: () => void;
|
|
9
9
|
}
|
|
10
10
|
|
|
11
|
-
export function
|
|
11
|
+
export function TeamCard({ team, state, isSelected, onSelect }: TeamCardProps) {
|
|
12
12
|
const isActive = !!state;
|
|
13
13
|
const status = state?.status ?? "inactive";
|
|
14
14
|
|
|
@@ -33,9 +33,9 @@ export function SquadCard({ squad, state, isSelected, onSelect }: SquadCardProps
|
|
|
33
33
|
}}
|
|
34
34
|
>
|
|
35
35
|
<StatusBadge status={status} />
|
|
36
|
-
<span style={{ marginRight: 4 }}>{
|
|
36
|
+
<span style={{ marginRight: 4 }}>{team.icon}</span>
|
|
37
37
|
<span style={{ flex: 1, overflow: "hidden", textOverflow: "ellipsis", whiteSpace: "nowrap" }}>
|
|
38
|
-
{
|
|
38
|
+
{team.name}
|
|
39
39
|
</span>
|
|
40
40
|
{state?.step && (
|
|
41
41
|
<span style={{ fontSize: 11, color: "var(--text-secondary)" }}>
|
|
@@ -1,14 +1,14 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
1
|
+
import { useTeamStore } from "@/store/useTeamStore";
|
|
2
|
+
import { TeamCard } from "./TeamCard";
|
|
3
3
|
|
|
4
|
-
export function
|
|
5
|
-
const
|
|
6
|
-
const activeStates =
|
|
7
|
-
const
|
|
8
|
-
const
|
|
4
|
+
export function TeamSelector() {
|
|
5
|
+
const teams = useTeamStore((s) => s.teams);
|
|
6
|
+
const activeStates = useTeamStore((s) => s.activeStates);
|
|
7
|
+
const selectedTeam = useTeamStore((s) => s.selectedTeam);
|
|
8
|
+
const selectTeam = useTeamStore((s) => s.selectTeam);
|
|
9
9
|
|
|
10
|
-
// Sort: active
|
|
11
|
-
const
|
|
10
|
+
// Sort: active teams first, then alphabetical
|
|
11
|
+
const teamList = Array.from(teams.values()).sort((a, b) => {
|
|
12
12
|
const aActive = activeStates.has(a.code) ? 0 : 1;
|
|
13
13
|
const bActive = activeStates.has(b.code) ? 0 : 1;
|
|
14
14
|
if (aActive !== bActive) return aActive - bActive;
|
|
@@ -41,18 +41,18 @@ export function SquadSelector() {
|
|
|
41
41
|
Squads
|
|
42
42
|
</div>
|
|
43
43
|
<div style={{ flex: 1, overflowY: "auto" }}>
|
|
44
|
-
{
|
|
44
|
+
{teamList.length === 0 && (
|
|
45
45
|
<div style={{ padding: "16px 12px", color: "var(--text-secondary)", fontSize: 12 }}>
|
|
46
|
-
No
|
|
46
|
+
No teams found
|
|
47
47
|
</div>
|
|
48
48
|
)}
|
|
49
|
-
{
|
|
50
|
-
<
|
|
51
|
-
key={
|
|
52
|
-
|
|
53
|
-
state={activeStates.get(
|
|
54
|
-
isSelected={
|
|
55
|
-
onSelect={() =>
|
|
49
|
+
{teamList.map((team) => (
|
|
50
|
+
<TeamCard
|
|
51
|
+
key={team.code}
|
|
52
|
+
team={team}
|
|
53
|
+
state={activeStates.get(team.code)}
|
|
54
|
+
isSelected={selectedTeam === team.code}
|
|
55
|
+
onSelect={() => selectTeam(team.code)}
|
|
56
56
|
/>
|
|
57
57
|
))}
|
|
58
58
|
</div>
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { useEffect, useRef } from "react";
|
|
2
|
-
import {
|
|
2
|
+
import { useTeamStore } from "@/store/useTeamStore";
|
|
3
3
|
import type { WsMessage } from "@/types/state";
|
|
4
4
|
|
|
5
5
|
const RECONNECT_BASE_MS = 1000;
|
|
@@ -7,14 +7,14 @@ const RECONNECT_MAX_MS = 30000;
|
|
|
7
7
|
const WS_FAIL_THRESHOLD = 3;
|
|
8
8
|
const POLL_INTERVAL_MS = 3000;
|
|
9
9
|
|
|
10
|
-
export function
|
|
10
|
+
export function useTeamSocket() {
|
|
11
11
|
const wsRef = useRef<WebSocket | null>(null);
|
|
12
12
|
const pollTimerRef = useRef<ReturnType<typeof setInterval> | null>(null);
|
|
13
13
|
|
|
14
|
-
const setConnected =
|
|
15
|
-
const setSnapshot =
|
|
16
|
-
const
|
|
17
|
-
const
|
|
14
|
+
const setConnected = useTeamStore((s) => s.setConnected);
|
|
15
|
+
const setSnapshot = useTeamStore((s) => s.setSnapshot);
|
|
16
|
+
const updateTeamState = useTeamStore((s) => s.updateTeamState);
|
|
17
|
+
const setTeamInactive = useTeamStore((s) => s.setTeamInactive);
|
|
18
18
|
|
|
19
19
|
useEffect(() => {
|
|
20
20
|
let disposed = false;
|
|
@@ -26,13 +26,13 @@ export function useSquadSocket() {
|
|
|
26
26
|
if (disposed) return;
|
|
27
27
|
switch (msg.type) {
|
|
28
28
|
case "SNAPSHOT":
|
|
29
|
-
setSnapshot(msg.
|
|
29
|
+
setSnapshot(msg.teams, msg.activeStates);
|
|
30
30
|
break;
|
|
31
|
-
case "
|
|
32
|
-
|
|
31
|
+
case "TEAM_UPDATE":
|
|
32
|
+
updateTeamState(msg.team, msg.state);
|
|
33
33
|
break;
|
|
34
|
-
case "
|
|
35
|
-
|
|
34
|
+
case "TEAM_INACTIVE":
|
|
35
|
+
setTeamInactive(msg.team);
|
|
36
36
|
break;
|
|
37
37
|
}
|
|
38
38
|
}
|
|
@@ -78,7 +78,7 @@ export function useSquadSocket() {
|
|
|
78
78
|
|
|
79
79
|
const protocol = window.location.protocol === "https:" ? "wss:" : "ws:";
|
|
80
80
|
const ws = new WebSocket(
|
|
81
|
-
`${protocol}//${window.location.host}/
|
|
81
|
+
`${protocol}//${window.location.host}/__teams_ws`,
|
|
82
82
|
);
|
|
83
83
|
wsRef.current = ws;
|
|
84
84
|
|
|
@@ -131,5 +131,5 @@ export function useSquadSocket() {
|
|
|
131
131
|
wsRef.current?.close();
|
|
132
132
|
wsRef.current = null;
|
|
133
133
|
};
|
|
134
|
-
}, [setConnected, setSnapshot,
|
|
134
|
+
}, [setConnected, setSnapshot, updateTeamState, setTeamInactive]);
|
|
135
135
|
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { TeamState, Agent } from "@/types/state";
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
4
|
* Returns agents sorted by desk position (row first, then col).
|
|
@@ -13,13 +13,13 @@ export function sortAgentsByDesk(agents: Agent[]): Agent[] {
|
|
|
13
13
|
/**
|
|
14
14
|
* Find agent by id.
|
|
15
15
|
*/
|
|
16
|
-
export function findAgent(state:
|
|
16
|
+
export function findAgent(state: TeamState, agentId: string): Agent | undefined {
|
|
17
17
|
return state.agents.find((a) => a.id === agentId);
|
|
18
18
|
}
|
|
19
19
|
|
|
20
20
|
/**
|
|
21
21
|
* Returns the currently working agent, if any.
|
|
22
22
|
*/
|
|
23
|
-
export function getWorkingAgent(state:
|
|
23
|
+
export function getWorkingAgent(state: TeamState): Agent | undefined {
|
|
24
24
|
return state.agents.find((a) => a.status === "working");
|
|
25
25
|
}
|
|
@@ -8,7 +8,7 @@ import {
|
|
|
8
8
|
import { CELL_W, CELL_H, MARGIN, WALL_H } from './palette';
|
|
9
9
|
import { RoomBuilder } from './RoomBuilder';
|
|
10
10
|
import { AgentSprite } from './AgentSprite';
|
|
11
|
-
import type {
|
|
11
|
+
import type { TeamState, Agent } from '@/types/state';
|
|
12
12
|
|
|
13
13
|
function assignCharacters(agents: Agent[]): Map<string, CharacterName> {
|
|
14
14
|
const assignments = new Map<string, CharacterName>();
|
|
@@ -80,14 +80,14 @@ export class OfficeScene extends Phaser.Scene {
|
|
|
80
80
|
|
|
81
81
|
this.roomBuilder = new RoomBuilder(this);
|
|
82
82
|
|
|
83
|
-
this.events.on('stateUpdate', (state:
|
|
83
|
+
this.events.on('stateUpdate', (state: TeamState | null) => {
|
|
84
84
|
this.onStateUpdate(state);
|
|
85
85
|
});
|
|
86
86
|
|
|
87
87
|
this.renderScene(DEMO_AGENTS);
|
|
88
88
|
}
|
|
89
89
|
|
|
90
|
-
private onStateUpdate(state:
|
|
90
|
+
private onStateUpdate(state: TeamState | null): void {
|
|
91
91
|
const agents = state?.agents ?? DEMO_AGENTS;
|
|
92
92
|
this.renderScene(agents);
|
|
93
93
|
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { useEffect, useRef } from 'react';
|
|
2
2
|
import Phaser from 'phaser';
|
|
3
3
|
import { OfficeScene } from './OfficeScene';
|
|
4
|
-
import {
|
|
4
|
+
import { useTeamStore } from '@/store/useTeamStore';
|
|
5
5
|
|
|
6
6
|
export function PhaserGame() {
|
|
7
7
|
const containerRef = useRef<HTMLDivElement>(null);
|
|
@@ -52,18 +52,18 @@ export function PhaserGame() {
|
|
|
52
52
|
|
|
53
53
|
// Bridge React state → Phaser scene
|
|
54
54
|
useEffect(() => {
|
|
55
|
-
return
|
|
55
|
+
return useTeamStore.subscribe((state) => {
|
|
56
56
|
const game = gameRef.current;
|
|
57
57
|
if (!game) return;
|
|
58
58
|
const scene = game.scene.getScene('OfficeScene') as OfficeScene | null;
|
|
59
59
|
if (!scene || !scene.scene.isActive()) return;
|
|
60
60
|
|
|
61
|
-
const
|
|
62
|
-
const
|
|
63
|
-
? state.activeStates.get(
|
|
61
|
+
const selectedTeam = state.selectedTeam;
|
|
62
|
+
const teamState = selectedTeam
|
|
63
|
+
? state.activeStates.get(selectedTeam) ?? null
|
|
64
64
|
: null;
|
|
65
65
|
|
|
66
|
-
scene.events.emit('stateUpdate',
|
|
66
|
+
scene.events.emit('stateUpdate', teamState);
|
|
67
67
|
});
|
|
68
68
|
}, []);
|
|
69
69
|
|