@nessielabs/daemon 0.3.2 → 0.5.26
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 +8 -19
- package/dist/cli.js +46 -18
- package/dist/commands/auth.js +116 -0
- package/dist/commands/integrations.js +84 -0
- package/dist/commands/login.js +145 -0
- package/dist/commands/run.js +21 -6
- package/dist/commands/service.js +18 -36
- package/dist/commands/sharing.js +399 -0
- package/dist/commands/status.js +67 -0
- package/dist/commands/teams.js +64 -0
- package/dist/nera/client.js +156 -0
- package/dist/nera/process.js +78 -0
- package/dist/nera/runtimeFile.js +25 -0
- package/dist/platform/binary.js +21 -0
- package/dist/platform/openBrowser.js +14 -0
- package/dist/platform/paths.js +13 -0
- package/dist/platform/table.js +85 -0
- package/package.json +9 -3
- package/vendor/nera/darwin-arm64/nera +0 -0
- package/vendor/nera/darwin-x64/nera +0 -0
- package/vendor/nera/linux-arm64/nera +0 -0
- package/vendor/nera/linux-x64/nera +0 -0
- package/vendor/nera/win32-arm64/nera.exe +0 -0
- package/vendor/nera/win32-x64/nera.exe +0 -0
- package/vendor/proto/nessie.edge.v1.proto +475 -0
- package/dist/api/http.js +0 -25
- package/dist/auth/deviceFlow.js +0 -36
- package/dist/auth/tokens.js +0 -30
- package/dist/commands/setup.js +0 -78
- package/dist/config/endpoints.js +0 -2
- package/dist/config/paths.js +0 -19
- package/dist/config/store.js +0 -63
- package/dist/git/repoResolver.js +0 -215
- package/dist/parser/claudeCode.js +0 -84
- package/dist/parser/codex.js +0 -86
- package/dist/parser/jsonl.js +0 -24
- package/dist/parser/types.js +0 -9
- package/dist/redact/secrets.js +0 -60
- package/dist/service/process.js +0 -134
- package/dist/service/systemd.js +0 -38
- package/dist/sources/detect.js +0 -43
- package/dist/sync/batch.js +0 -50
- package/dist/sync/client.js +0 -47
- package/dist/sync/ids.js +0 -11
- package/dist/sync/loop.js +0 -76
- package/dist/sync/map.js +0 -111
- package/dist/sync/scan.js +0 -60
- package/dist/sync/slices.js +0 -74
- package/dist/sync/types.js +0 -1
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
import { spawn } from "node:child_process";
|
|
2
|
+
import { mkdir, open } from "node:fs/promises";
|
|
3
|
+
import { dirname } from "node:path";
|
|
4
|
+
import { getHealth } from "./client.js";
|
|
5
|
+
import { readRuntimeFile } from "./runtimeFile.js";
|
|
6
|
+
import { neraBinaryPath } from "../platform/binary.js";
|
|
7
|
+
import { daemonPaths } from "../platform/paths.js";
|
|
8
|
+
export async function probeRuntime() {
|
|
9
|
+
const endpoint = await readRuntimeFile();
|
|
10
|
+
if (!endpoint)
|
|
11
|
+
return null;
|
|
12
|
+
try {
|
|
13
|
+
await getHealth(endpoint);
|
|
14
|
+
return endpoint;
|
|
15
|
+
}
|
|
16
|
+
catch {
|
|
17
|
+
return null;
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
export async function ensureRuntime(options) {
|
|
21
|
+
const existing = await probeRuntime();
|
|
22
|
+
if (existing)
|
|
23
|
+
return { ...existing, started: false };
|
|
24
|
+
const paths = daemonPaths();
|
|
25
|
+
await mkdir(dirname(paths.logFile), { recursive: true });
|
|
26
|
+
const log = await open(paths.logFile, "a");
|
|
27
|
+
let child;
|
|
28
|
+
try {
|
|
29
|
+
child = spawn(neraBinaryPath(), [], {
|
|
30
|
+
detached: options.detached,
|
|
31
|
+
stdio: ["ignore", log.fd, log.fd],
|
|
32
|
+
});
|
|
33
|
+
if (options.detached)
|
|
34
|
+
child.unref();
|
|
35
|
+
}
|
|
36
|
+
finally {
|
|
37
|
+
await log.close();
|
|
38
|
+
}
|
|
39
|
+
try {
|
|
40
|
+
const endpoint = await waitForRuntime(child, paths.logFile);
|
|
41
|
+
return { ...endpoint, started: true, child };
|
|
42
|
+
}
|
|
43
|
+
catch (error) {
|
|
44
|
+
try {
|
|
45
|
+
child.kill("SIGTERM");
|
|
46
|
+
}
|
|
47
|
+
catch {
|
|
48
|
+
// The runtime may have exited before cleanup.
|
|
49
|
+
}
|
|
50
|
+
throw error;
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
export async function stopRuntime() {
|
|
54
|
+
const endpoint = await probeRuntime();
|
|
55
|
+
if (!endpoint)
|
|
56
|
+
return null;
|
|
57
|
+
process.kill(endpoint.pid, "SIGTERM");
|
|
58
|
+
return endpoint.pid;
|
|
59
|
+
}
|
|
60
|
+
async function waitForRuntime(child, logFile) {
|
|
61
|
+
const deadline = Date.now() + 30_000;
|
|
62
|
+
while (Date.now() < deadline) {
|
|
63
|
+
if (child.exitCode !== null)
|
|
64
|
+
throw new Error(`Nessie exited before it was ready with code ${child.exitCode}. Check ${logFile}.`);
|
|
65
|
+
const endpoint = await readRuntimeFile();
|
|
66
|
+
if (endpoint) {
|
|
67
|
+
try {
|
|
68
|
+
await getHealth(endpoint);
|
|
69
|
+
return endpoint;
|
|
70
|
+
}
|
|
71
|
+
catch {
|
|
72
|
+
// Keep waiting until the server accepts connections.
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
await new Promise((resolve) => setTimeout(resolve, 200));
|
|
76
|
+
}
|
|
77
|
+
throw new Error(`Timed out starting Nessie. Check ${logFile}.`);
|
|
78
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { readFile } from "node:fs/promises";
|
|
2
|
+
import { homedir } from "node:os";
|
|
3
|
+
import { join } from "node:path";
|
|
4
|
+
export async function readRuntimeFile() {
|
|
5
|
+
try {
|
|
6
|
+
const parsed = JSON.parse(await readFile(runtimeFilePath(), "utf8"));
|
|
7
|
+
if (!parsed.host || !parsed.port || !parsed.token || !parsed.pid)
|
|
8
|
+
return null;
|
|
9
|
+
return parsed;
|
|
10
|
+
}
|
|
11
|
+
catch {
|
|
12
|
+
return null;
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
export function runtimeFilePath() {
|
|
16
|
+
if (process.env.NERA_CONNECTION_CONFIG)
|
|
17
|
+
return process.env.NERA_CONNECTION_CONFIG;
|
|
18
|
+
if (process.platform === "darwin") {
|
|
19
|
+
return join(homedir(), "Library", "Application Support", "com.nessielabs.Nessie", "connection-config.json");
|
|
20
|
+
}
|
|
21
|
+
if (process.platform === "win32") {
|
|
22
|
+
return join(process.env.LOCALAPPDATA ?? join(homedir(), "AppData", "Local"), "Nessie Labs", "Nessie", "data", "connection-config.json");
|
|
23
|
+
}
|
|
24
|
+
return join(process.env.XDG_DATA_HOME ?? join(homedir(), ".local", "share"), "nessie", "connection-config.json");
|
|
25
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { accessSync, constants } from "node:fs";
|
|
2
|
+
import { dirname, resolve } from "node:path";
|
|
3
|
+
import { fileURLToPath } from "node:url";
|
|
4
|
+
export function neraBinaryPath() {
|
|
5
|
+
const explicit = process.env.NERA_BIN;
|
|
6
|
+
if (explicit)
|
|
7
|
+
return explicit;
|
|
8
|
+
const fileName = process.platform === "win32" ? "nera.exe" : "nera";
|
|
9
|
+
const packaged = resolve(dirname(fileURLToPath(import.meta.url)), "../../vendor/nera", platformKey(), fileName);
|
|
10
|
+
try {
|
|
11
|
+
accessSync(packaged, constants.X_OK);
|
|
12
|
+
return packaged;
|
|
13
|
+
}
|
|
14
|
+
catch {
|
|
15
|
+
throw new Error(`Embedded Nessie runtime binary not found for ${platformKey()}. Run npm run build from apps/nessie-daemon.`);
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
export function platformKey() {
|
|
19
|
+
const arch = process.arch;
|
|
20
|
+
return `${process.platform}-${arch}`;
|
|
21
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { spawn } from "node:child_process";
|
|
2
|
+
export async function openBrowser(url) {
|
|
3
|
+
const command = process.platform === "darwin" ? "open" : process.platform === "win32" ? "cmd" : "xdg-open";
|
|
4
|
+
const args = process.platform === "win32" ? ["/c", "start", "", url] : [url];
|
|
5
|
+
await new Promise((resolve) => {
|
|
6
|
+
const child = spawn(command, args, { stdio: "ignore", detached: true });
|
|
7
|
+
child.once("error", () => {
|
|
8
|
+
console.log(`Open this URL to sign in: ${url}`);
|
|
9
|
+
resolve();
|
|
10
|
+
});
|
|
11
|
+
child.once("exit", () => resolve());
|
|
12
|
+
child.unref();
|
|
13
|
+
});
|
|
14
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { homedir } from "node:os";
|
|
2
|
+
import { join } from "node:path";
|
|
3
|
+
export function daemonPaths() {
|
|
4
|
+
const stateDir = process.env.XDG_STATE_HOME
|
|
5
|
+
? join(process.env.XDG_STATE_HOME, "nessie-daemon")
|
|
6
|
+
: process.platform === "darwin"
|
|
7
|
+
? join(homedir(), "Library", "Logs", "NessieDaemon")
|
|
8
|
+
: join(homedir(), ".local", "state", "nessie-daemon");
|
|
9
|
+
return {
|
|
10
|
+
stateDir,
|
|
11
|
+
logFile: join(stateDir, "nera.log"),
|
|
12
|
+
};
|
|
13
|
+
}
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
const TAB_WIDTH = 8;
|
|
2
|
+
const ANSI = {
|
|
3
|
+
bold: "1",
|
|
4
|
+
dim: "2",
|
|
5
|
+
green: "32",
|
|
6
|
+
yellow: "33",
|
|
7
|
+
red: "31",
|
|
8
|
+
cyan: "36",
|
|
9
|
+
};
|
|
10
|
+
export function renderTabTable(rows, colors) {
|
|
11
|
+
const normalized = rows.map((row) => row.map(normalizeCell));
|
|
12
|
+
const first = normalized[0];
|
|
13
|
+
if (!first || first.length === 0)
|
|
14
|
+
return "";
|
|
15
|
+
const columnCount = first.length;
|
|
16
|
+
const columnStops = Array(columnCount).fill(0);
|
|
17
|
+
let start = 0;
|
|
18
|
+
for (let column = 0; column < columnCount - 1; column += 1) {
|
|
19
|
+
let widest = 0;
|
|
20
|
+
for (const row of normalized) {
|
|
21
|
+
if (column < row.length)
|
|
22
|
+
widest = Math.max(widest, visibleWidth(row[column] ?? ""));
|
|
23
|
+
}
|
|
24
|
+
const stop = (Math.floor((start + widest) / TAB_WIDTH) + 1) * TAB_WIDTH;
|
|
25
|
+
columnStops[column] = stop;
|
|
26
|
+
start = stop;
|
|
27
|
+
}
|
|
28
|
+
const lines = normalized.map((row, rowIndex) => {
|
|
29
|
+
let position = 0;
|
|
30
|
+
let line = "";
|
|
31
|
+
for (let column = 0; column < row.length; column += 1) {
|
|
32
|
+
const cell = row[column] ?? "";
|
|
33
|
+
line += colorize(cell, colors?.[rowIndex]?.[column]);
|
|
34
|
+
position += visibleWidth(cell);
|
|
35
|
+
if (column < columnCount - 1) {
|
|
36
|
+
while (position < columnStops[column]) {
|
|
37
|
+
line += "\t";
|
|
38
|
+
position = (Math.floor(position / TAB_WIDTH) + 1) * TAB_WIDTH;
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
return line;
|
|
43
|
+
});
|
|
44
|
+
return `${lines.join("\n")}\n`;
|
|
45
|
+
}
|
|
46
|
+
function normalizeCell(cell) {
|
|
47
|
+
return cell.replace(/[\n\r\t]/g, " ").trim();
|
|
48
|
+
}
|
|
49
|
+
function visibleWidth(cell) {
|
|
50
|
+
let width = 0;
|
|
51
|
+
for (const segment of graphemes(stripAnsi(cell))) {
|
|
52
|
+
width += isWide(segment) ? 2 : 1;
|
|
53
|
+
}
|
|
54
|
+
return width;
|
|
55
|
+
}
|
|
56
|
+
function colorize(cell, color) {
|
|
57
|
+
if (!color || !process.stdout.isTTY)
|
|
58
|
+
return cell;
|
|
59
|
+
return `\u001B[${ANSI[color]}m${cell}\u001B[0m`;
|
|
60
|
+
}
|
|
61
|
+
function stripAnsi(cell) {
|
|
62
|
+
return cell.replace(/\u001B\[[0-?]*[ -/]*[@-~]/g, "");
|
|
63
|
+
}
|
|
64
|
+
function graphemes(cell) {
|
|
65
|
+
if (typeof Intl.Segmenter === "function") {
|
|
66
|
+
const segmenter = new Intl.Segmenter(undefined, { granularity: "grapheme" });
|
|
67
|
+
return Array.from(segmenter.segment(cell), (segment) => segment.segment);
|
|
68
|
+
}
|
|
69
|
+
return Array.from(cell);
|
|
70
|
+
}
|
|
71
|
+
function isWide(segment) {
|
|
72
|
+
const codePoint = segment.codePointAt(0);
|
|
73
|
+
if (codePoint === undefined)
|
|
74
|
+
return false;
|
|
75
|
+
return ((codePoint >= 0x1100 && codePoint <= 0x115F)
|
|
76
|
+
|| (codePoint >= 0x2329 && codePoint <= 0x232A)
|
|
77
|
+
|| (codePoint >= 0x2E80 && codePoint <= 0xA4CF)
|
|
78
|
+
|| (codePoint >= 0xAC00 && codePoint <= 0xD7A3)
|
|
79
|
+
|| (codePoint >= 0xF900 && codePoint <= 0xFAFF)
|
|
80
|
+
|| (codePoint >= 0xFE10 && codePoint <= 0xFE19)
|
|
81
|
+
|| (codePoint >= 0xFE30 && codePoint <= 0xFE6F)
|
|
82
|
+
|| (codePoint >= 0xFF00 && codePoint <= 0xFF60)
|
|
83
|
+
|| (codePoint >= 0xFFE0 && codePoint <= 0xFFE6)
|
|
84
|
+
|| (codePoint >= 0x1F300 && codePoint <= 0x1FAFF));
|
|
85
|
+
}
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@nessielabs/daemon",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"description": "Headless
|
|
3
|
+
"version": "0.5.26",
|
|
4
|
+
"description": "Headless Nessie daemon for macOS and Linux agent trace ingestion.",
|
|
5
5
|
"license": "UNLICENSED",
|
|
6
6
|
"type": "module",
|
|
7
7
|
"bin": {
|
|
@@ -9,6 +9,7 @@
|
|
|
9
9
|
},
|
|
10
10
|
"files": [
|
|
11
11
|
"dist/**/*.js",
|
|
12
|
+
"vendor/**/*",
|
|
12
13
|
"README.md"
|
|
13
14
|
],
|
|
14
15
|
"publishConfig": {
|
|
@@ -18,12 +19,17 @@
|
|
|
18
19
|
"node": ">=20"
|
|
19
20
|
},
|
|
20
21
|
"scripts": {
|
|
21
|
-
"build": "rm -rf dist && tsc -p tsconfig.json && chmod +x dist/cli.js",
|
|
22
|
+
"build": "rm -rf dist && node scripts/build-nera.mjs && tsc -p tsconfig.json && chmod +x dist/cli.js",
|
|
23
|
+
"build:ts": "rm -rf dist && tsc -p tsconfig.json && chmod +x dist/cli.js",
|
|
24
|
+
"build:nera": "node scripts/build-nera.mjs",
|
|
22
25
|
"check": "tsc -p tsconfig.json --noEmit",
|
|
23
26
|
"dev": "tsx src/cli.ts",
|
|
27
|
+
"prepack": "npm run build",
|
|
24
28
|
"test": "vitest run"
|
|
25
29
|
},
|
|
26
30
|
"dependencies": {
|
|
31
|
+
"@grpc/grpc-js": "^1.14.1",
|
|
32
|
+
"@grpc/proto-loader": "^0.8.0",
|
|
27
33
|
"@inquirer/prompts": "^7.8.0",
|
|
28
34
|
"commander": "^14.0.2"
|
|
29
35
|
},
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|