agent-hub-sdd 0.1.2 → 0.1.6
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 +20 -3
- package/electron/main.mjs +155 -37
- package/electron/stage-hub.mjs +56 -0
- package/electron/stage-hub.test.ts +33 -0
- package/next.config.ts +24 -1
- package/package.json +5 -5
- package/scripts/cli.cjs +9 -3
- package/scripts/kill-hub.ps1 +11 -0
- package/src/app/globals.css +2 -2
- package/src/app/layout.tsx +1 -15
package/README.md
CHANGED
|
@@ -12,13 +12,30 @@ Na pasta do repositório (depois de `npm install`):
|
|
|
12
12
|
npm run app
|
|
13
13
|
```
|
|
14
14
|
|
|
15
|
-
Pelo npm
|
|
15
|
+
Pelo npm (precisa do Node 20+ no PATH):
|
|
16
16
|
|
|
17
17
|
```powershell
|
|
18
|
-
npx agent-hub-sdd
|
|
18
|
+
npx --yes agent-hub-sdd@0.1.6
|
|
19
19
|
```
|
|
20
20
|
|
|
21
|
-
|
|
21
|
+
Na primeira abertura a janela diz que está compilando. O **N** no canto é o Next.js em modo dev, não o app. Sem `@0.1.6`, o Windows pode reusar cache antigo.
|
|
22
|
+
|
|
23
|
+
Se ainda abrir a 0.1.2, ou se o npm disser `EBUSY` / `resource busy or locked`, o Next da tentativa antiga ainda está com o cache aberto. O `Stop-Process electron` não mata esse `node`:
|
|
24
|
+
|
|
25
|
+
```powershell
|
|
26
|
+
Get-CimInstance Win32_Process | Where-Object {
|
|
27
|
+
$_.CommandLine -and ($_.CommandLine -match 'agent-hub|_npx|next dev|43147')
|
|
28
|
+
} | ForEach-Object { Stop-Process -Id $_.ProcessId -Force -ErrorAction SilentlyContinue }
|
|
29
|
+
Remove-Item -Recurse -Force "$env:LOCALAPPDATA\npm-cache\_npx" -ErrorAction SilentlyContinue
|
|
30
|
+
npx --yes agent-hub-sdd@0.1.6
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
Instalar global evita o rename do `npx` nessa pasta:
|
|
34
|
+
|
|
35
|
+
```powershell
|
|
36
|
+
npm install -g agent-hub-sdd@0.1.6
|
|
37
|
+
agent-hub-sdd
|
|
38
|
+
```
|
|
22
39
|
|
|
23
40
|
## App no Windows (a partir do código)
|
|
24
41
|
|
package/electron/main.mjs
CHANGED
|
@@ -1,66 +1,170 @@
|
|
|
1
1
|
import { spawn } from "node:child_process";
|
|
2
|
+
import fs from "node:fs";
|
|
2
3
|
import http from "node:http";
|
|
4
|
+
import { createRequire } from "node:module";
|
|
3
5
|
import path from "node:path";
|
|
4
6
|
import { fileURLToPath } from "node:url";
|
|
5
7
|
import { app, BrowserWindow, dialog, ipcMain, shell } from "electron";
|
|
8
|
+
import { stageHub } from "./stage-hub.mjs";
|
|
6
9
|
|
|
7
10
|
const PORT = Number(process.env.AGENT_HUB_PORT || 43147);
|
|
8
11
|
const HUB = `http://127.0.0.1:${PORT}`;
|
|
9
12
|
const here = path.dirname(fileURLToPath(import.meta.url));
|
|
10
13
|
const repoRoot = path.join(here, "..");
|
|
14
|
+
const VERSION = JSON.parse(fs.readFileSync(path.join(repoRoot, "package.json"), "utf8")).version;
|
|
11
15
|
|
|
12
16
|
let win;
|
|
13
17
|
let child;
|
|
14
18
|
let startedServer = false;
|
|
19
|
+
let logs = "";
|
|
15
20
|
|
|
16
21
|
function hubDir() {
|
|
17
|
-
|
|
22
|
+
if (app.isPackaged) return path.join(process.resourcesPath, "hub");
|
|
23
|
+
const dist = path.join(repoRoot, "dist-hub");
|
|
24
|
+
if (fs.existsSync(path.join(dist, "server.js"))) return dist;
|
|
25
|
+
return stageHub(repoRoot);
|
|
18
26
|
}
|
|
19
27
|
|
|
20
|
-
function
|
|
28
|
+
function nodeName() {
|
|
29
|
+
return process.platform === "win32" ? "node.exe" : "node";
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
function nodeBin() {
|
|
33
|
+
const guessed = [
|
|
34
|
+
process.env.AGENT_HUB_NODE,
|
|
35
|
+
process.env.npm_node_execpath,
|
|
36
|
+
];
|
|
37
|
+
const npmCli = process.env.npm_execpath;
|
|
38
|
+
if (npmCli) {
|
|
39
|
+
guessed.push(path.join(path.dirname(npmCli), nodeName()));
|
|
40
|
+
guessed.push(path.resolve(path.dirname(npmCli), "..", "..", "..", nodeName()));
|
|
41
|
+
}
|
|
42
|
+
if (process.platform === "win32") {
|
|
43
|
+
if (process.env.ProgramFiles) {
|
|
44
|
+
guessed.push(path.join(process.env.ProgramFiles, "nodejs", "node.exe"));
|
|
45
|
+
}
|
|
46
|
+
guessed.push("C:\\Program Files\\nodejs\\node.exe");
|
|
47
|
+
if (process.env.LOCALAPPDATA) {
|
|
48
|
+
guessed.push(path.join(process.env.LOCALAPPDATA, "Programs", "nodejs", "node.exe"));
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
for (const candidate of guessed) {
|
|
52
|
+
if (candidate && fs.existsSync(candidate)) return candidate;
|
|
53
|
+
}
|
|
54
|
+
return nodeName();
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
function nextBin(cwd) {
|
|
58
|
+
const starts = [path.join(cwd, "package.json"), fileURLToPath(import.meta.url)];
|
|
59
|
+
const errors = [];
|
|
60
|
+
for (const start of starts) {
|
|
61
|
+
try {
|
|
62
|
+
const req = createRequire(start);
|
|
63
|
+
const root = path.dirname(req.resolve("next/package.json"));
|
|
64
|
+
const bin = path.join(root, "dist", "bin", "next");
|
|
65
|
+
if (fs.existsSync(bin)) return bin;
|
|
66
|
+
errors.push(`Next em ${root}, mas falta dist/bin/next`);
|
|
67
|
+
} catch (error) {
|
|
68
|
+
errors.push(error instanceof Error ? error.message : String(error));
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
throw new Error(`Não achei o Next.js no pacote.\n${errors.join("\n")}`);
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
function withNodeOnPath(env, node) {
|
|
75
|
+
const next = { ...env };
|
|
76
|
+
if (!node || node === "node" || node === "node.exe") return next;
|
|
77
|
+
const dir = path.dirname(node);
|
|
78
|
+
const key = Object.keys(next).find((k) => k.toLowerCase() === "path") ?? "PATH";
|
|
79
|
+
next[key] = `${dir}${path.delimiter}${next[key] ?? ""}`;
|
|
80
|
+
return next;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
function hubHtml() {
|
|
21
84
|
return new Promise((resolve) => {
|
|
22
85
|
const req = http.get(HUB, (res) => {
|
|
23
|
-
|
|
24
|
-
|
|
86
|
+
const chunks = [];
|
|
87
|
+
res.on("data", (chunk) => {
|
|
88
|
+
chunks.push(chunk);
|
|
89
|
+
if (Buffer.concat(chunks).length > 40000) res.destroy();
|
|
90
|
+
});
|
|
91
|
+
res.on("end", () => {
|
|
92
|
+
resolve({ status: res.statusCode ?? 0, body: Buffer.concat(chunks).toString("utf8") });
|
|
93
|
+
});
|
|
25
94
|
});
|
|
26
|
-
req.on("error", () => resolve(
|
|
27
|
-
req.setTimeout(
|
|
95
|
+
req.on("error", () => resolve(null));
|
|
96
|
+
req.setTimeout(20000, () => {
|
|
28
97
|
req.destroy();
|
|
29
|
-
resolve(
|
|
98
|
+
resolve(null);
|
|
30
99
|
});
|
|
31
100
|
});
|
|
32
101
|
}
|
|
33
102
|
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
103
|
+
function pageReady(html) {
|
|
104
|
+
if (!html) return false;
|
|
105
|
+
if (/Can't resolve|Module not found|Module parse failed/.test(html.body)) return false;
|
|
106
|
+
return html.status === 200 && /Agent Hub|Carregando o hub/.test(html.body);
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
function note(chunk) {
|
|
110
|
+
logs = (logs + chunk.toString()).slice(-8000);
|
|
40
111
|
}
|
|
41
112
|
|
|
42
113
|
function startServer() {
|
|
43
114
|
const cwd = hubDir();
|
|
44
|
-
const
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
115
|
+
const node = nodeBin();
|
|
116
|
+
const serverJs = path.join(cwd, "server.js");
|
|
117
|
+
const args = fs.existsSync(serverJs)
|
|
118
|
+
? [serverJs]
|
|
119
|
+
: [nextBin(cwd), "dev", "--webpack", "-H", "127.0.0.1", "-p", String(PORT)];
|
|
120
|
+
const env = withNodeOnPath(
|
|
121
|
+
{ ...process.env, HOSTNAME: "127.0.0.1", PORT: String(PORT) },
|
|
122
|
+
node,
|
|
123
|
+
);
|
|
124
|
+
note(`\n${node} ${args.join(" ")}\n`);
|
|
125
|
+
const proc = spawn(node, args, {
|
|
126
|
+
cwd,
|
|
127
|
+
env,
|
|
128
|
+
stdio: ["ignore", "pipe", "pipe"],
|
|
129
|
+
windowsHide: true,
|
|
130
|
+
shell: false,
|
|
131
|
+
});
|
|
132
|
+
proc.stdout?.on("data", note);
|
|
133
|
+
proc.stderr?.on("data", note);
|
|
134
|
+
proc.on("error", (error) => note(`\n${error.message}\n`));
|
|
135
|
+
return proc;
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
function stopServer() {
|
|
139
|
+
if (!child || child.killed) return;
|
|
140
|
+
if (process.platform === "win32" && child.pid) {
|
|
141
|
+
spawn("taskkill", ["/pid", String(child.pid), "/t", "/f"], {
|
|
52
142
|
windowsHide: true,
|
|
53
143
|
shell: false,
|
|
144
|
+
stdio: "ignore",
|
|
54
145
|
});
|
|
146
|
+
return;
|
|
55
147
|
}
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
148
|
+
child.kill("SIGTERM");
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
async function waitForHub(proc) {
|
|
152
|
+
let exited;
|
|
153
|
+
proc?.once("exit", (code) => {
|
|
154
|
+
exited = code ?? 1;
|
|
63
155
|
});
|
|
156
|
+
for (let i = 0; i < 24; i++) {
|
|
157
|
+
const html = await hubHtml();
|
|
158
|
+
if (pageReady(html)) return;
|
|
159
|
+
if (html?.body && /Can't resolve|Module not found|Module parse failed/.test(html.body)) {
|
|
160
|
+
throw new Error(`Falha ao compilar o hub.\n${html.body.slice(0, 1500)}`);
|
|
161
|
+
}
|
|
162
|
+
if (exited !== undefined) {
|
|
163
|
+
throw new Error(`O servidor saiu com código ${exited}.\n${logs.trim() || "Sem log."}`);
|
|
164
|
+
}
|
|
165
|
+
await new Promise((r) => setTimeout(r, 500));
|
|
166
|
+
}
|
|
167
|
+
throw new Error(`O servidor não subiu em ${HUB}.\n${logs.trim() || "Sem log."}`);
|
|
64
168
|
}
|
|
65
169
|
|
|
66
170
|
function createWindow() {
|
|
@@ -88,7 +192,17 @@ function createWindow() {
|
|
|
88
192
|
},
|
|
89
193
|
});
|
|
90
194
|
win.once("ready-to-show", () => win.show());
|
|
91
|
-
win.loadURL(
|
|
195
|
+
void win.loadURL(
|
|
196
|
+
`data:text/html;charset=utf-8,${encodeURIComponent(`<!doctype html>
|
|
197
|
+
<html lang="pt-BR">
|
|
198
|
+
<body style="margin:0;background:#000;color:#e4e4e7;font-family:'Segoe UI',sans-serif;display:flex;min-height:100vh;align-items:center;justify-content:center">
|
|
199
|
+
<div style="text-align:center;max-width:28rem;padding:1.5rem">
|
|
200
|
+
<p style="font-size:1.15rem;margin:0 0 .75rem">Agent Hub ${VERSION}</p>
|
|
201
|
+
<p style="color:#a1a1aa;line-height:1.6;margin:0">Primeira abertura compila o hub. Pode levar alguns minutos. Não feche a janela.</p>
|
|
202
|
+
</div>
|
|
203
|
+
</body>
|
|
204
|
+
</html>`)}`,
|
|
205
|
+
);
|
|
92
206
|
win.webContents.setWindowOpenHandler(({ url }) => {
|
|
93
207
|
void shell.openExternal(url);
|
|
94
208
|
return { action: "deny" };
|
|
@@ -107,26 +221,30 @@ ipcMain.handle("pick-folder", async () => {
|
|
|
107
221
|
|
|
108
222
|
app.whenReady().then(async () => {
|
|
109
223
|
try {
|
|
110
|
-
|
|
224
|
+
createWindow();
|
|
225
|
+
const html = await hubHtml();
|
|
226
|
+
if (!pageReady(html)) {
|
|
111
227
|
child = startServer();
|
|
112
228
|
startedServer = true;
|
|
113
|
-
|
|
114
|
-
void dialog.showErrorBox("Agent Hub", error.message);
|
|
115
|
-
});
|
|
229
|
+
await waitForHub(child);
|
|
116
230
|
}
|
|
117
|
-
await
|
|
118
|
-
createWindow();
|
|
231
|
+
await win.loadURL(HUB);
|
|
119
232
|
} catch (error) {
|
|
233
|
+
stopServer();
|
|
120
234
|
const message = error instanceof Error ? error.message : String(error);
|
|
121
235
|
await dialog.showErrorBox(
|
|
122
236
|
"Agent Hub",
|
|
123
|
-
`${message}\n\
|
|
237
|
+
`${message}\n\nAgent Hub ${VERSION}. O app precisa do Node.js 20+ (o mesmo node do npx).\nRode: npx --yes agent-hub-sdd@${VERSION}`,
|
|
124
238
|
);
|
|
125
239
|
app.quit();
|
|
126
240
|
}
|
|
127
241
|
});
|
|
128
242
|
|
|
243
|
+
app.on("before-quit", () => {
|
|
244
|
+
if (startedServer) stopServer();
|
|
245
|
+
});
|
|
246
|
+
|
|
129
247
|
app.on("window-all-closed", () => {
|
|
130
|
-
if (startedServer
|
|
248
|
+
if (startedServer) stopServer();
|
|
131
249
|
app.quit();
|
|
132
250
|
});
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import fs from "node:fs";
|
|
2
|
+
import os from "node:os";
|
|
3
|
+
import path from "node:path";
|
|
4
|
+
|
|
5
|
+
const COPY = ["src", "public", "next.config.ts", "tsconfig.json", "components.json", "package.json"];
|
|
6
|
+
|
|
7
|
+
export function isNestedPackage(dir) {
|
|
8
|
+
return path.basename(path.dirname(path.resolve(dir))) === "node_modules";
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export function hubHome() {
|
|
12
|
+
return process.env.AGENT_HUB_HOME || path.join(os.homedir(), ".agent-hub");
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
function unlinkQuiet(target) {
|
|
16
|
+
try {
|
|
17
|
+
const st = fs.lstatSync(target);
|
|
18
|
+
if (st.isSymbolicLink()) {
|
|
19
|
+
fs.unlinkSync(target);
|
|
20
|
+
return;
|
|
21
|
+
}
|
|
22
|
+
} catch {
|
|
23
|
+
return;
|
|
24
|
+
}
|
|
25
|
+
fs.rmSync(target, { recursive: true, force: true });
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
function linkModules(from, to) {
|
|
29
|
+
try {
|
|
30
|
+
if (fs.existsSync(to) && fs.realpathSync(to) === fs.realpathSync(from)) return;
|
|
31
|
+
} catch {
|
|
32
|
+
// broken link
|
|
33
|
+
}
|
|
34
|
+
unlinkQuiet(to);
|
|
35
|
+
fs.symlinkSync(from, to, process.platform === "win32" ? "junction" : "dir");
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
export function stageHub(srcDir, destDir) {
|
|
39
|
+
const src = path.resolve(srcDir);
|
|
40
|
+
if (!isNestedPackage(src)) return src;
|
|
41
|
+
const dest = path.resolve(destDir ?? path.join(hubHome(), "runtime"));
|
|
42
|
+
fs.mkdirSync(dest, { recursive: true });
|
|
43
|
+
const version = JSON.parse(fs.readFileSync(path.join(src, "package.json"), "utf8")).version;
|
|
44
|
+
const stamp = path.join(dest, ".hub-version");
|
|
45
|
+
const same = fs.existsSync(stamp) && fs.readFileSync(stamp, "utf8").trim() === version;
|
|
46
|
+
if (!same) {
|
|
47
|
+
for (const name of COPY) {
|
|
48
|
+
const from = path.join(src, name);
|
|
49
|
+
if (!fs.existsSync(from)) continue;
|
|
50
|
+
fs.cpSync(from, path.join(dest, name), { recursive: true, force: true });
|
|
51
|
+
}
|
|
52
|
+
fs.writeFileSync(stamp, `${version}\n`);
|
|
53
|
+
}
|
|
54
|
+
linkModules(path.join(src, ".."), path.join(dest, "node_modules"));
|
|
55
|
+
return dest;
|
|
56
|
+
}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import fs from "node:fs";
|
|
2
|
+
import os from "node:os";
|
|
3
|
+
import path from "node:path";
|
|
4
|
+
import { afterEach, describe, expect, it } from "vitest";
|
|
5
|
+
import { isNestedPackage, stageHub } from "./stage-hub.mjs";
|
|
6
|
+
|
|
7
|
+
const temps: string[] = [];
|
|
8
|
+
|
|
9
|
+
afterEach(() => {
|
|
10
|
+
for (const dir of temps.splice(0)) fs.rmSync(dir, { recursive: true, force: true });
|
|
11
|
+
});
|
|
12
|
+
|
|
13
|
+
describe("stageHub", () => {
|
|
14
|
+
it("leaves a git clone in place", () => {
|
|
15
|
+
expect(isNestedPackage("/workspace")).toBe(false);
|
|
16
|
+
expect(stageHub("/workspace")).toBe(path.resolve("/workspace"));
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
it("copies an npx install out of node_modules", () => {
|
|
20
|
+
const root = fs.mkdtempSync(path.join(os.tmpdir(), "hub-stage-"));
|
|
21
|
+
temps.push(root);
|
|
22
|
+
const pkg = path.join(root, "node_modules", "agent-hub-sdd");
|
|
23
|
+
fs.mkdirSync(path.join(pkg, "src", "components"), { recursive: true });
|
|
24
|
+
fs.writeFileSync(path.join(pkg, "package.json"), JSON.stringify({ name: "agent-hub-sdd", version: "0.1.6" }));
|
|
25
|
+
fs.writeFileSync(path.join(pkg, "src", "components", "desktop-chrome.tsx"), "export const DesktopChrome = () => null;\n");
|
|
26
|
+
fs.writeFileSync(path.join(root, "node_modules", "sentinel"), "ok");
|
|
27
|
+
const dest = path.join(root, "runtime");
|
|
28
|
+
expect(stageHub(pkg, dest)).toBe(dest);
|
|
29
|
+
expect(fs.readFileSync(path.join(dest, "src", "components", "desktop-chrome.tsx"), "utf8")).toContain("DesktopChrome");
|
|
30
|
+
expect(fs.readFileSync(path.join(dest, "node_modules", "sentinel"), "utf8")).toBe("ok");
|
|
31
|
+
expect(fs.realpathSync(path.join(dest, "src")).includes(`${path.sep}node_modules${path.sep}`)).toBe(false);
|
|
32
|
+
});
|
|
33
|
+
});
|
package/next.config.ts
CHANGED
|
@@ -1,12 +1,35 @@
|
|
|
1
1
|
import type { NextConfig } from "next";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
|
|
4
|
+
const projectRoot = process.cwd();
|
|
5
|
+
const src = path.join(projectRoot, "src");
|
|
2
6
|
|
|
3
7
|
const nextConfig: NextConfig = {
|
|
4
|
-
/* config options here */
|
|
5
8
|
output: "standalone",
|
|
9
|
+
outputFileTracingRoot: projectRoot,
|
|
6
10
|
// Cache Components rejects `runtime` / `dynamic` on route handlers. The hub
|
|
7
11
|
// APIs stay Node and force-dynamic so the JSON store and SSE turns are not prerendered.
|
|
8
12
|
cacheComponents: false,
|
|
9
13
|
partialPrefetching: false,
|
|
14
|
+
agentRules: false,
|
|
15
|
+
devIndicators: false,
|
|
16
|
+
// npx installs this app under node_modules. Next's tsconfig-paths plugin
|
|
17
|
+
// skips any importer in node_modules, so @/ would 404 without a webpack alias.
|
|
18
|
+
webpack: (config) => {
|
|
19
|
+
config.resolve ??= {};
|
|
20
|
+
const alias = config.resolve.alias;
|
|
21
|
+
if (Array.isArray(alias)) {
|
|
22
|
+
alias.push({ name: "@", alias: src });
|
|
23
|
+
} else {
|
|
24
|
+
config.resolve.alias = { ...alias, "@": src };
|
|
25
|
+
}
|
|
26
|
+
return config;
|
|
27
|
+
},
|
|
28
|
+
turbopack: {
|
|
29
|
+
resolveAlias: {
|
|
30
|
+
"@": src,
|
|
31
|
+
},
|
|
32
|
+
},
|
|
10
33
|
};
|
|
11
34
|
|
|
12
35
|
export default nextConfig;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "agent-hub-sdd",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.6",
|
|
4
4
|
"private": false,
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": "Agent Hub",
|
|
@@ -59,12 +59,12 @@
|
|
|
59
59
|
"shadcn": "^4.22.1",
|
|
60
60
|
"tailwindcss": "^4",
|
|
61
61
|
"tw-animate-css": "^1.4.0",
|
|
62
|
-
"typescript": "^5"
|
|
63
|
-
},
|
|
64
|
-
"devDependencies": {
|
|
62
|
+
"typescript": "^5",
|
|
65
63
|
"@types/node": "^22.20.5",
|
|
66
64
|
"@types/react": "^19",
|
|
67
|
-
"@types/react-dom": "^19"
|
|
65
|
+
"@types/react-dom": "^19"
|
|
66
|
+
},
|
|
67
|
+
"devDependencies": {
|
|
68
68
|
"electron-builder": "^26.18.0",
|
|
69
69
|
"eslint": "^9",
|
|
70
70
|
"eslint-config-next": "16.4.3",
|
package/scripts/cli.cjs
CHANGED
|
@@ -1,8 +1,10 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
1
|
+
#!/usr/bin/env node
|
|
2
2
|
const { spawn } = require("node:child_process");
|
|
3
3
|
const path = require("node:path");
|
|
4
4
|
|
|
5
5
|
const root = path.join(__dirname, "..");
|
|
6
|
+
const pkg = require(path.join(root, "package.json"));
|
|
7
|
+
console.log(`Agent Hub ${pkg.version}`);
|
|
6
8
|
let electron;
|
|
7
9
|
try {
|
|
8
10
|
electron = require("electron");
|
|
@@ -11,12 +13,16 @@ try {
|
|
|
11
13
|
process.exit(1);
|
|
12
14
|
}
|
|
13
15
|
|
|
16
|
+
const env = { ...process.env, AGENT_HUB_NODE: process.execPath };
|
|
17
|
+
delete env.ELECTRON_RUN_AS_NODE;
|
|
18
|
+
if (!env.npm_node_execpath) env.npm_node_execpath = process.execPath;
|
|
19
|
+
|
|
14
20
|
const child = spawn(electron, [root], {
|
|
15
21
|
cwd: root,
|
|
16
22
|
stdio: "inherit",
|
|
17
|
-
env
|
|
23
|
+
env,
|
|
18
24
|
windowsHide: false,
|
|
19
|
-
shell:
|
|
25
|
+
shell: false,
|
|
20
26
|
});
|
|
21
27
|
child.on("error", (error) => {
|
|
22
28
|
console.error(error.message);
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
$ErrorActionPreference = "SilentlyContinue"
|
|
2
|
+
Get-CimInstance Win32_Process |
|
|
3
|
+
Where-Object {
|
|
4
|
+
$_.CommandLine -and
|
|
5
|
+
($_.CommandLine -match "agent-hub|_npx|next dev|43147")
|
|
6
|
+
} |
|
|
7
|
+
ForEach-Object { Stop-Process -Id $_.ProcessId -Force }
|
|
8
|
+
Stop-Process -Name electron -Force
|
|
9
|
+
$npx = Join-Path $env:LOCALAPPDATA "npm-cache\_npx"
|
|
10
|
+
if (Test-Path $npx) { Remove-Item -Recurse -Force $npx }
|
|
11
|
+
Write-Host "Cache do npx limpo. Rode: npx --yes agent-hub-sdd@0.1.6"
|
package/src/app/globals.css
CHANGED
|
@@ -7,8 +7,8 @@
|
|
|
7
7
|
@theme inline {
|
|
8
8
|
--color-background: var(--background);
|
|
9
9
|
--color-foreground: var(--foreground);
|
|
10
|
-
--font-sans:
|
|
11
|
-
--font-mono:
|
|
10
|
+
--font-sans: "Segoe UI", ui-sans-serif, system-ui, sans-serif;
|
|
11
|
+
--font-mono: "Cascadia Mono", Consolas, ui-monospace, monospace;
|
|
12
12
|
--font-heading: var(--font-sans);
|
|
13
13
|
--color-sidebar-ring: var(--sidebar-ring);
|
|
14
14
|
--color-sidebar-border: var(--sidebar-border);
|
package/src/app/layout.tsx
CHANGED
|
@@ -1,19 +1,8 @@
|
|
|
1
1
|
import type { Metadata } from "next";
|
|
2
|
-
import { Geist, Geist_Mono } from "next/font/google";
|
|
3
2
|
import { DesktopChrome } from "@/components/desktop-chrome";
|
|
4
3
|
import { TooltipProvider } from "@/components/ui/tooltip";
|
|
5
4
|
import "./globals.css";
|
|
6
5
|
|
|
7
|
-
const geistSans = Geist({
|
|
8
|
-
variable: "--font-geist-sans",
|
|
9
|
-
subsets: ["latin"],
|
|
10
|
-
});
|
|
11
|
-
|
|
12
|
-
const geistMono = Geist_Mono({
|
|
13
|
-
variable: "--font-geist-mono",
|
|
14
|
-
subsets: ["latin"],
|
|
15
|
-
});
|
|
16
|
-
|
|
17
6
|
export const metadata: Metadata = {
|
|
18
7
|
title: "Agent Hub",
|
|
19
8
|
description: "Harness visual local para agentes do SDLC",
|
|
@@ -21,10 +10,7 @@ export const metadata: Metadata = {
|
|
|
21
10
|
|
|
22
11
|
export default function RootLayout({ children }: LayoutProps<"/">) {
|
|
23
12
|
return (
|
|
24
|
-
<html
|
|
25
|
-
lang="pt-BR"
|
|
26
|
-
className={`${geistSans.variable} ${geistMono.variable} dark h-full antialiased`}
|
|
27
|
-
>
|
|
13
|
+
<html lang="pt-BR" className="dark h-full antialiased">
|
|
28
14
|
<body className="flex min-h-dvh flex-col bg-black text-zinc-100">
|
|
29
15
|
<TooltipProvider>
|
|
30
16
|
<DesktopChrome />
|