@saadnvd1/agent-os 0.3.654 → 0.3.657
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/app/api/health/env-failures/route.test.ts +60 -0
- package/app/api/health/env-failures/route.ts +51 -0
- package/app/api/shares/pdf/route.test.ts +122 -0
- package/app/api/shares/pdf/route.ts +31 -0
- package/app/api/shares/preview/route.ts +5 -0
- package/components/Health/EnvFailures/ClassRow.tsx +104 -0
- package/components/Health/EnvFailures/SessionList.tsx +49 -0
- package/components/Health/EnvFailures/index.tsx +46 -0
- package/components/Health/HealthPanel.tsx +11 -1
- package/components/Share/DownloadMenu.tsx +112 -0
- package/components/Share/ShareDialog.tsx +31 -47
- package/data/health/index.ts +31 -0
- package/data/shares/queries.ts +11 -0
- package/lib/agents/brief.ts +1 -1
- package/lib/agents/self-env.ts +1 -0
- package/lib/chat/runner.ts +3 -0
- package/lib/db/migrations/20261016T1800_env_failures.ts +33 -0
- package/lib/dev-instance/index.ts +6 -0
- package/lib/dev-instance/liveServer.ts +46 -0
- package/lib/dev-instance/pickPort.ts +66 -0
- package/lib/dev-instance/processInfo.ts +68 -0
- package/lib/dev-instance/spawnServer.ts +36 -0
- package/lib/dev-instance/startDevServer.ts +80 -0
- package/lib/dev-instance/stopDevServer.ts +98 -0
- package/lib/dev-instance/types.ts +13 -0
- package/lib/health/config.ts +8 -0
- package/lib/health/env/classes.ts +121 -0
- package/lib/health/env/classify.ts +67 -0
- package/lib/health/env/envTick.ts +46 -0
- package/lib/health/env/fixText.ts +24 -0
- package/lib/health/env/helpers.test-util.ts +21 -0
- package/lib/health/env/index.ts +6 -0
- package/lib/health/env/raiseAlerts.ts +84 -0
- package/lib/health/env/recordHit.ts +62 -0
- package/lib/health/env/sendFixNotes.ts +80 -0
- package/lib/health/env/summarize.ts +94 -0
- package/lib/health/env/types.ts +73 -0
- package/lib/health/tick.ts +6 -0
- package/lib/security/demo.ts +1 -0
- package/lib/share/pdf.ts +116 -0
- package/lib/share/view.ts +9 -1
- package/package.json +5 -3
- package/scripts/dev-stop.ts +7 -0
- package/scripts/dev-task.ts +16 -0
- package/scripts/screenshots/demo.ts +4 -8
- package/scripts/screenshots/run.ts +5 -8
- package/server.ts +25 -0
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
// What the environment-failure detector knows and shows. Plain data, no I/O.
|
|
2
|
+
|
|
3
|
+
export interface EnvClass {
|
|
4
|
+
id: string;
|
|
5
|
+
label: string;
|
|
6
|
+
// shell: only shell tools' output; guard: any tool's refused call.
|
|
7
|
+
scope: "shell" | "guard";
|
|
8
|
+
// Any of these, tested per line.
|
|
9
|
+
match: RegExp[];
|
|
10
|
+
// Also has to hold, tested on the whole output.
|
|
11
|
+
requires?: RegExp;
|
|
12
|
+
// Vetoes: a command or output that shows it isn't this machine's fault.
|
|
13
|
+
except?: { command?: RegExp; output?: RegExp };
|
|
14
|
+
// Sent once to a session that hit it; null means record and escalate only.
|
|
15
|
+
fix: string | null;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export interface EnvHit {
|
|
19
|
+
class: string;
|
|
20
|
+
line: string;
|
|
21
|
+
command: string;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export interface EnvFailureRow {
|
|
25
|
+
id: number;
|
|
26
|
+
session_id: string;
|
|
27
|
+
class: string;
|
|
28
|
+
project_id: string | null;
|
|
29
|
+
workspace_id: string | null;
|
|
30
|
+
command: string | null;
|
|
31
|
+
line: string | null;
|
|
32
|
+
last_item_id: string | null;
|
|
33
|
+
hits: number;
|
|
34
|
+
first_at: number;
|
|
35
|
+
last_at: number;
|
|
36
|
+
nudged_at: number | null;
|
|
37
|
+
nudge_skipped: string | null;
|
|
38
|
+
alerted: string | null;
|
|
39
|
+
alerted_at: number | null;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
export interface EnvClassView {
|
|
43
|
+
id: string;
|
|
44
|
+
label: string;
|
|
45
|
+
fix: string | null;
|
|
46
|
+
fixOverridden: boolean;
|
|
47
|
+
sessions7: number;
|
|
48
|
+
sessions30: number;
|
|
49
|
+
projects: {
|
|
50
|
+
id: string | null;
|
|
51
|
+
name: string;
|
|
52
|
+
sessions7: number;
|
|
53
|
+
sessions30: number;
|
|
54
|
+
}[];
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
export interface EnvSummary {
|
|
58
|
+
now: number;
|
|
59
|
+
classes: EnvClassView[];
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
export interface EnvSessionView {
|
|
63
|
+
sessionId: string;
|
|
64
|
+
name: string;
|
|
65
|
+
project: string | null;
|
|
66
|
+
command: string | null;
|
|
67
|
+
line: string | null;
|
|
68
|
+
hits: number;
|
|
69
|
+
firstAt: number;
|
|
70
|
+
lastAt: number;
|
|
71
|
+
// "sent", or why not.
|
|
72
|
+
fix: string;
|
|
73
|
+
}
|
package/lib/health/tick.ts
CHANGED
|
@@ -10,6 +10,7 @@ import { leaveHeartbeat } from "./boots";
|
|
|
10
10
|
import { healthConfig, type HealthConfig } from "./config";
|
|
11
11
|
import { runDetectors } from "./detectors";
|
|
12
12
|
import { gatherFacts, liveBackgroundJobs } from "./facts";
|
|
13
|
+
import { envTick, realEnv, type EnvDeps } from "./env/envTick";
|
|
13
14
|
import { addNote } from "./notes";
|
|
14
15
|
import { closeResolvedPieces } from "./pieces";
|
|
15
16
|
import { deliver, deliverClosed, type RouteDeps, realRoute } from "./route";
|
|
@@ -52,6 +53,7 @@ export interface TickDeps {
|
|
|
52
53
|
facts: (now: number, c: HealthConfig) => HealthFacts;
|
|
53
54
|
watch: WatchDeps;
|
|
54
55
|
route: RouteDeps;
|
|
56
|
+
env: EnvDeps;
|
|
55
57
|
wake: typeof wake;
|
|
56
58
|
}
|
|
57
59
|
|
|
@@ -60,6 +62,7 @@ export const realTick: TickDeps = {
|
|
|
60
62
|
facts: gatherFacts,
|
|
61
63
|
watch: { run: runClaude, usagePct: currentUsagePct },
|
|
62
64
|
route: realRoute,
|
|
65
|
+
env: realEnv,
|
|
63
66
|
wake,
|
|
64
67
|
};
|
|
65
68
|
|
|
@@ -137,6 +140,9 @@ export function healthTick(
|
|
|
137
140
|
closeResolvedPieces(now);
|
|
138
141
|
leaveHeartbeat(now, liveBackgroundJobs());
|
|
139
142
|
giveUpStale(now, deps);
|
|
143
|
+
void envTick(now, c, deps.env).catch((error) =>
|
|
144
|
+
console.error("[health] environment failures:", error)
|
|
145
|
+
);
|
|
140
146
|
|
|
141
147
|
const running: Promise<void>[] = [];
|
|
142
148
|
for (const row of openAnomalies()) {
|
package/lib/security/demo.ts
CHANGED
package/lib/share/pdf.ts
ADDED
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The share page as a PDF, printed by headless Chromium from the exact HTML
|
|
3
|
+
* the preview shows. One browser is launched lazily, reused, and closed after
|
|
4
|
+
* a quiet spell. Nothing on the page can reach the network: only the inlined
|
|
5
|
+
* markup is rendered.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import fs from "fs";
|
|
9
|
+
import type { Browser, LaunchOptions } from "playwright";
|
|
10
|
+
|
|
11
|
+
const IDLE_MS = 60_000;
|
|
12
|
+
const RENDER_MS = 30_000;
|
|
13
|
+
|
|
14
|
+
const CHROME_PATHS = [
|
|
15
|
+
"/Applications/Google Chrome.app/Contents/MacOS/Google Chrome",
|
|
16
|
+
"/usr/bin/google-chrome",
|
|
17
|
+
"/usr/bin/google-chrome-stable",
|
|
18
|
+
"/usr/bin/chromium",
|
|
19
|
+
"/usr/bin/chromium-browser",
|
|
20
|
+
];
|
|
21
|
+
|
|
22
|
+
/** Print-only rules, added here so the HTML download stays as it was. */
|
|
23
|
+
const PRINT_CSS = `
|
|
24
|
+
@page{margin:16mm 14mm}
|
|
25
|
+
html,body{background:#fff!important}
|
|
26
|
+
.wrap{max-width:none;padding:0}
|
|
27
|
+
*{-webkit-print-color-adjust:exact;print-color-adjust:exact}
|
|
28
|
+
pre code,pre,details.tool pre{white-space:pre-wrap!important;word-break:break-word!important;overflow-wrap:anywhere;overflow:visible;max-height:none}
|
|
29
|
+
table{display:table;width:100%;table-layout:auto;overflow-wrap:anywhere}
|
|
30
|
+
pre,table,tr,.plan,.error,details.tool{break-inside:avoid-page}
|
|
31
|
+
h1,h2,h3,h4{break-after:avoid}
|
|
32
|
+
img{max-width:100%;break-inside:avoid}
|
|
33
|
+
`;
|
|
34
|
+
|
|
35
|
+
let found: LaunchOptions | null | undefined;
|
|
36
|
+
|
|
37
|
+
/** How to launch a browser on this machine, or null when there is none. */
|
|
38
|
+
export async function findBrowser(): Promise<LaunchOptions | null> {
|
|
39
|
+
if (found !== undefined) return found;
|
|
40
|
+
const { chromium } = await import("playwright");
|
|
41
|
+
const candidates = [chromium.executablePath(), ...CHROME_PATHS];
|
|
42
|
+
const path = candidates.find((p) => p && fs.existsSync(p));
|
|
43
|
+
found = path ? { executablePath: path, headless: true } : null;
|
|
44
|
+
return found;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
export const pdfAvailable = async () => (await findBrowser()) !== null;
|
|
48
|
+
|
|
49
|
+
/** Test hook: forget the cached lookup and any open browser. */
|
|
50
|
+
export async function resetPdfBrowser() {
|
|
51
|
+
found = undefined;
|
|
52
|
+
await closeBrowser();
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
let browser: Promise<Browser> | null = null;
|
|
56
|
+
let idle: NodeJS.Timeout | null = null;
|
|
57
|
+
|
|
58
|
+
async function closeBrowser() {
|
|
59
|
+
if (idle) clearTimeout(idle);
|
|
60
|
+
idle = null;
|
|
61
|
+
const b = browser;
|
|
62
|
+
browser = null;
|
|
63
|
+
if (b) await (await b).close().catch(() => {});
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
async function getBrowser(): Promise<Browser> {
|
|
67
|
+
const opts = await findBrowser();
|
|
68
|
+
if (!opts) throw new Error("No browser to print with on this machine.");
|
|
69
|
+
if (!browser) {
|
|
70
|
+
const { chromium } = await import("playwright");
|
|
71
|
+
const launched = chromium.launch(opts);
|
|
72
|
+
browser = launched;
|
|
73
|
+
launched.then(
|
|
74
|
+
(b) =>
|
|
75
|
+
b.on("disconnected", () => browser === launched && (browser = null)),
|
|
76
|
+
() => {
|
|
77
|
+
if (browser === launched) browser = null;
|
|
78
|
+
}
|
|
79
|
+
);
|
|
80
|
+
}
|
|
81
|
+
return browser;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
const withPrint = (html: string) =>
|
|
85
|
+
html.includes("</head>")
|
|
86
|
+
? html.replace("</head>", () => `<style>${PRINT_CSS}</style></head>`)
|
|
87
|
+
: `<style>${PRINT_CSS}</style>${html}`;
|
|
88
|
+
|
|
89
|
+
/** The page printed to an A4 PDF. */
|
|
90
|
+
export async function renderPdf(html: string): Promise<Buffer> {
|
|
91
|
+
const b = await getBrowser();
|
|
92
|
+
if (idle) clearTimeout(idle);
|
|
93
|
+
const ctx = await b.newContext({
|
|
94
|
+
javaScriptEnabled: false,
|
|
95
|
+
colorScheme: "light",
|
|
96
|
+
});
|
|
97
|
+
try {
|
|
98
|
+
await ctx.setOffline(true);
|
|
99
|
+
const page = await ctx.newPage();
|
|
100
|
+
page.setDefaultTimeout(RENDER_MS);
|
|
101
|
+
await page.emulateMedia({ media: "print", colorScheme: "light" });
|
|
102
|
+
// "load" waits for images and fonts; scripts are off, so nothing runs after.
|
|
103
|
+
await page.setContent(withPrint(html), { waitUntil: "load" });
|
|
104
|
+
return Buffer.from(
|
|
105
|
+
await page.pdf({
|
|
106
|
+
format: "A4",
|
|
107
|
+
printBackground: true,
|
|
108
|
+
preferCSSPageSize: true,
|
|
109
|
+
})
|
|
110
|
+
);
|
|
111
|
+
} finally {
|
|
112
|
+
await ctx.close().catch(() => {});
|
|
113
|
+
idle = setTimeout(() => void closeBrowser(), IDLE_MS);
|
|
114
|
+
idle.unref();
|
|
115
|
+
}
|
|
116
|
+
}
|
package/lib/share/view.ts
CHANGED
|
@@ -22,6 +22,9 @@ export interface PreviewItem {
|
|
|
22
22
|
excerpt: string;
|
|
23
23
|
}
|
|
24
24
|
|
|
25
|
+
/** Download formats a server can make; the client lists what it is told. */
|
|
26
|
+
export type ShareFormat = "pdf" | "html" | "md";
|
|
27
|
+
|
|
25
28
|
export interface SharePreview {
|
|
26
29
|
html: string;
|
|
27
30
|
markdown: string;
|
|
@@ -33,6 +36,8 @@ export interface SharePreview {
|
|
|
33
36
|
hits: Record<"secret" | "path" | "ip" | "host" | "email", number>;
|
|
34
37
|
tooBig: boolean;
|
|
35
38
|
connectLinked: boolean;
|
|
39
|
+
/** Best first. pdf only when this machine has a browser to print with. */
|
|
40
|
+
formats: ShareFormat[];
|
|
36
41
|
}
|
|
37
42
|
|
|
38
43
|
export interface SharedLinkRow {
|
|
@@ -103,7 +108,10 @@ export function formatBytes(n: number): string {
|
|
|
103
108
|
}
|
|
104
109
|
|
|
105
110
|
/** A file name for a download: the title, plain, with .html (or `ext`). */
|
|
106
|
-
export function fileName(
|
|
111
|
+
export function fileName(
|
|
112
|
+
title: string,
|
|
113
|
+
ext: "html" | "md" | "pdf" = "html"
|
|
114
|
+
): string {
|
|
107
115
|
const slug = title
|
|
108
116
|
.toLowerCase()
|
|
109
117
|
.replace(/[^a-z0-9]+/g, "-")
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@saadnvd1/agent-os",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.657",
|
|
4
4
|
"description": "Self-hosted web UI for managing Claude Code sessions",
|
|
5
5
|
"bin": {
|
|
6
6
|
"agent-os": "scripts/agent-os"
|
|
@@ -31,10 +31,12 @@
|
|
|
31
31
|
"scripts": {
|
|
32
32
|
"predev": "node scripts/ensure-native.mjs && node scripts/gen-migrations-index.mjs",
|
|
33
33
|
"dev": "AGENTOS_DEV=1 tsx server.ts",
|
|
34
|
+
"dev:task": "tsx scripts/dev-task.ts",
|
|
35
|
+
"dev:stop": "tsx scripts/dev-stop.ts",
|
|
34
36
|
"prebuild": "node scripts/gen-migrations-index.mjs",
|
|
35
37
|
"build": "next build",
|
|
36
38
|
"prestart": "node scripts/ensure-native.mjs && node scripts/gen-migrations-index.mjs",
|
|
37
|
-
"start": "NODE_ENV=production tsx server.ts",
|
|
39
|
+
"start": "NODE_ENV=production AGENTOS_SERVER=live node --import tsx server.ts",
|
|
38
40
|
"pretypecheck": "node scripts/gen-migrations-index.mjs",
|
|
39
41
|
"typecheck": "tsc --noEmit",
|
|
40
42
|
"setup": "bash scripts/setup.sh",
|
|
@@ -141,6 +143,7 @@
|
|
|
141
143
|
"next": "^16.3.8",
|
|
142
144
|
"next-themes": "^0.4.6",
|
|
143
145
|
"node-pty": "^1.2.0-beta.6",
|
|
146
|
+
"playwright": "^1.63.0",
|
|
144
147
|
"qrcode": "^1.5.4",
|
|
145
148
|
"react": "^19.2.1",
|
|
146
149
|
"react-dom": "^19.2.1",
|
|
@@ -171,7 +174,6 @@
|
|
|
171
174
|
"eslint-config-next": "^16.3.8",
|
|
172
175
|
"husky": "^9.1.7",
|
|
173
176
|
"jsdom": "^30.1.2",
|
|
174
|
-
"playwright": "^1.63.0",
|
|
175
177
|
"prettier": "^3.7.4",
|
|
176
178
|
"prettier-plugin-tailwindcss": "^0.7.2",
|
|
177
179
|
"sharp": "^0.35.5",
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import fs from "fs";
|
|
2
|
+
import { startDevServer } from "@/lib/dev-instance";
|
|
3
|
+
|
|
4
|
+
const i = process.argv.indexOf("--port");
|
|
5
|
+
const requested = i === -1 ? undefined : Number(process.argv[i + 1]);
|
|
6
|
+
|
|
7
|
+
startDevServer({ worktree: fs.realpathSync(process.cwd()), requested })
|
|
8
|
+
.then((r) =>
|
|
9
|
+
console.log(
|
|
10
|
+
`Dev server ${r.reused ? "already running" : "started"}: pid ${r.pid} on http://localhost:${r.port} (log ${r.log}). Stop it with npm run dev:stop.`
|
|
11
|
+
)
|
|
12
|
+
)
|
|
13
|
+
.catch((e: Error) => {
|
|
14
|
+
console.error(e.message);
|
|
15
|
+
process.exit(1);
|
|
16
|
+
});
|
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
*/
|
|
12
12
|
import fs from "fs";
|
|
13
13
|
import path from "path";
|
|
14
|
-
import {
|
|
14
|
+
import { spawnServer } from "@/lib/dev-instance";
|
|
15
15
|
import { PORT, REPO, demoEnv } from "./config";
|
|
16
16
|
import { seed, teardownTmux } from "./seed";
|
|
17
17
|
import { startPeer } from "./peer";
|
|
@@ -35,13 +35,9 @@ async function main() {
|
|
|
35
35
|
process.env[k] ? [[k, process.env[k]!]] : []
|
|
36
36
|
)
|
|
37
37
|
);
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
});
|
|
42
|
-
const tsx = path.join(REPO, "node_modules", "tsx", "dist", "cli.mjs");
|
|
43
|
-
const server = spawn(process.execPath, [tsx, "server.ts"], {
|
|
44
|
-
cwd: REPO,
|
|
38
|
+
const server = spawnServer({
|
|
39
|
+
repo: REPO,
|
|
40
|
+
port: PORT,
|
|
45
41
|
env: demoEnv({
|
|
46
42
|
AGENTOS_BIND: "127.0.0.1",
|
|
47
43
|
...passed,
|
|
@@ -8,7 +8,8 @@
|
|
|
8
8
|
*/
|
|
9
9
|
import fs from "fs";
|
|
10
10
|
import path from "path";
|
|
11
|
-
import {
|
|
11
|
+
import { type ChildProcess } from "child_process";
|
|
12
|
+
import { spawnServer } from "@/lib/dev-instance";
|
|
12
13
|
import { BASE_URL, PORT, REPO, ROOT, demoEnv } from "./config";
|
|
13
14
|
import { seed, teardownTmux } from "./seed";
|
|
14
15
|
import { shoot } from "./shoot";
|
|
@@ -38,14 +39,10 @@ async function waitForServer(server: ChildProcess): Promise<void> {
|
|
|
38
39
|
|
|
39
40
|
function startServer(): ChildProcess {
|
|
40
41
|
const log = fs.openSync(path.join(ROOT, "server.log"), "w");
|
|
41
|
-
// server.ts imports the generated migrations index.
|
|
42
|
-
execFileSync(process.execPath, ["scripts/gen-migrations-index.mjs"], {
|
|
43
|
-
cwd: REPO,
|
|
44
|
-
});
|
|
45
|
-
const tsx = path.join(REPO, "node_modules", "tsx", "dist", "cli.mjs");
|
|
46
42
|
// Loopback only: the demo never listens on another network.
|
|
47
|
-
return
|
|
48
|
-
|
|
43
|
+
return spawnServer({
|
|
44
|
+
repo: REPO,
|
|
45
|
+
port: PORT,
|
|
49
46
|
// No watchers: the seeded orchestrator and tasks must sit still, never
|
|
50
47
|
// start an agent.
|
|
51
48
|
env: demoEnv({
|
package/server.ts
CHANGED
|
@@ -78,6 +78,11 @@ import { failInterruptedSetups } from "./lib/sessions/worktree-setup";
|
|
|
78
78
|
import { sweepOrphanTmux } from "./lib/tmux/orphans";
|
|
79
79
|
import { releaseInterruptedClaims } from "./lib/orchestrator/ask-approvals";
|
|
80
80
|
import { resumeReviews } from "./lib/orchestrator/review";
|
|
81
|
+
import {
|
|
82
|
+
isLiveServer,
|
|
83
|
+
readLiveRecord,
|
|
84
|
+
writeLiveRecord,
|
|
85
|
+
} from "./lib/dev-instance";
|
|
81
86
|
import { claimAgentosEnv } from "./lib/agents/self-env";
|
|
82
87
|
import { wslNotes, wslVersion } from "./lib/wsl";
|
|
83
88
|
import { getAllProjects } from "./lib/projects";
|
|
@@ -89,6 +94,19 @@ const hostname = "127.0.0.1";
|
|
|
89
94
|
|
|
90
95
|
// Support: npm run dev -- -p 3012
|
|
91
96
|
const port = serverPort();
|
|
97
|
+
const live = isLiveServer();
|
|
98
|
+
// A distinct title keeps `pkill -f server.ts` and `pkill tsx` off the
|
|
99
|
+
// production server; an AgentOS in a worktree can't take its port.
|
|
100
|
+
process.title = live ? "agentos-server" : "agentos-dev";
|
|
101
|
+
if (!live) {
|
|
102
|
+
const rec = readLiveRecord();
|
|
103
|
+
if (rec && rec.port === port && rec.pid !== process.pid) {
|
|
104
|
+
console.error(
|
|
105
|
+
`Refusing to start: port ${port} is the live AgentOS server (pid ${rec.pid}). Use npm run dev:task.`
|
|
106
|
+
);
|
|
107
|
+
process.exit(1);
|
|
108
|
+
}
|
|
109
|
+
}
|
|
92
110
|
claimAgentosEnv(port);
|
|
93
111
|
// Demo mode: seeded data, nothing that runs code (lib/security/demo).
|
|
94
112
|
const demo = demoMode();
|
|
@@ -302,6 +320,13 @@ app.prepare().then(async () => {
|
|
|
302
320
|
});
|
|
303
321
|
server.listen(port, address, () => {
|
|
304
322
|
console.log(`> Agent-OS ready on http://${address}:${port}`);
|
|
323
|
+
if (live)
|
|
324
|
+
writeLiveRecord({
|
|
325
|
+
pid: process.pid,
|
|
326
|
+
port,
|
|
327
|
+
cwd: process.cwd(),
|
|
328
|
+
startedAt: Date.now(),
|
|
329
|
+
});
|
|
305
330
|
});
|
|
306
331
|
};
|
|
307
332
|
const stopListening = (address: string) => {
|