@masstensor/anotify 0.0.0-stage → 0.5.8
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/LICENSE +21 -0
- package/README.md +14 -2
- package/dist/src/adapters.js +97 -0
- package/dist/src/adapters.js.map +1 -0
- package/dist/src/claude-question-wait.js +67 -0
- package/dist/src/claude-question-wait.js.map +1 -0
- package/dist/src/claude-question.js +22 -0
- package/dist/src/claude-question.js.map +1 -0
- package/dist/src/claude.js +23 -0
- package/dist/src/claude.js.map +1 -0
- package/dist/src/cli.js +294 -0
- package/dist/src/cli.js.map +1 -0
- package/dist/src/codex-readiness.js +37 -0
- package/dist/src/codex-readiness.js.map +1 -0
- package/dist/src/config.js +21 -0
- package/dist/src/config.js.map +1 -0
- package/dist/src/crypto.js +26 -0
- package/dist/src/crypto.js.map +1 -0
- package/dist/src/daemon.js +356 -0
- package/dist/src/daemon.js.map +1 -0
- package/dist/src/demand.js +89 -0
- package/dist/src/demand.js.map +1 -0
- package/dist/src/device-name.js +12 -0
- package/dist/src/device-name.js.map +1 -0
- package/dist/src/http.js +38 -0
- package/dist/src/http.js.map +1 -0
- package/dist/src/install.js +254 -0
- package/dist/src/install.js.map +1 -0
- package/dist/src/integration.js +184 -0
- package/dist/src/integration.js.map +1 -0
- package/dist/src/mcp.js +48 -0
- package/dist/src/mcp.js.map +1 -0
- package/dist/src/native-owner.js +44 -0
- package/dist/src/native-owner.js.map +1 -0
- package/dist/src/npm-cli.js +24 -0
- package/dist/src/npm-cli.js.map +1 -0
- package/dist/src/owners.js +57 -0
- package/dist/src/owners.js.map +1 -0
- package/dist/src/pairing-cli.js +125 -0
- package/dist/src/pairing-cli.js.map +1 -0
- package/dist/src/pairing.js +57 -0
- package/dist/src/pairing.js.map +1 -0
- package/dist/src/preferences.js +18 -0
- package/dist/src/preferences.js.map +1 -0
- package/dist/src/protocol.js +40 -0
- package/dist/src/protocol.js.map +1 -0
- package/dist/src/question-crypto.js +29 -0
- package/dist/src/question-crypto.js.map +1 -0
- package/dist/src/readiness.js +58 -0
- package/dist/src/readiness.js.map +1 -0
- package/dist/src/service.js +54 -0
- package/dist/src/service.js.map +1 -0
- package/dist/src/sessions.js +217 -0
- package/dist/src/sessions.js.map +1 -0
- package/dist/src/setup-cli.js +59 -0
- package/dist/src/setup-cli.js.map +1 -0
- package/dist/src/terminal.js +68 -0
- package/dist/src/terminal.js.map +1 -0
- package/dist/src/vault.js +49 -0
- package/dist/src/vault.js.map +1 -0
- package/dist/src/wizard.js +43 -0
- package/dist/src/wizard.js.map +1 -0
- package/npm-shrinkwrap.json +1919 -0
- package/package.json +26 -4
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
import { spawn } from 'node:child_process';
|
|
2
|
+
import { openSync, closeSync, mkdirSync } from 'node:fs';
|
|
3
|
+
import { join } from 'node:path';
|
|
4
|
+
import { request, HttpError } from './http.js';
|
|
5
|
+
const starting = new Map();
|
|
6
|
+
export async function probe(vault) {
|
|
7
|
+
const c = vault.load('connection');
|
|
8
|
+
if (!c)
|
|
9
|
+
throw Error('No running service');
|
|
10
|
+
return request(c.url + '/status', c.auth, 'POST', {});
|
|
11
|
+
}
|
|
12
|
+
export async function ensureService(vault, cli, idleSeconds = 60) {
|
|
13
|
+
try {
|
|
14
|
+
await probe(vault);
|
|
15
|
+
return;
|
|
16
|
+
}
|
|
17
|
+
catch { /* Idle/stopped is normal. */ }
|
|
18
|
+
let pending = starting.get(vault.dir);
|
|
19
|
+
if (!pending) {
|
|
20
|
+
pending = (async () => {
|
|
21
|
+
const logs = join(vault.dir, 'logs');
|
|
22
|
+
mkdirSync(logs, { recursive: true, mode: 0o700 });
|
|
23
|
+
const output = openSync(join(logs, 'on-demand.log'), 'a', 0o600);
|
|
24
|
+
try {
|
|
25
|
+
const child = spawn(process.execPath, [cli, 'daemon', '--idle-seconds', String(idleSeconds)], { detached: true, stdio: ['ignore', output, output], env: { ...process.env, AGENT_NOTIFY_HOME: vault.dir } });
|
|
26
|
+
await new Promise((done, reject) => { child.once('spawn', done); child.once('error', reject); });
|
|
27
|
+
child.unref();
|
|
28
|
+
}
|
|
29
|
+
finally {
|
|
30
|
+
closeSync(output);
|
|
31
|
+
}
|
|
32
|
+
const deadline = Date.now() + 12000;
|
|
33
|
+
while (Date.now() < deadline) {
|
|
34
|
+
try {
|
|
35
|
+
await probe(vault);
|
|
36
|
+
return;
|
|
37
|
+
}
|
|
38
|
+
catch {
|
|
39
|
+
await new Promise(done => setTimeout(done, 100));
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
throw Error('On-demand service did not start. Check state/logs/on-demand.log');
|
|
43
|
+
})();
|
|
44
|
+
starting.set(vault.dir, pending);
|
|
45
|
+
}
|
|
46
|
+
try {
|
|
47
|
+
await pending;
|
|
48
|
+
}
|
|
49
|
+
finally {
|
|
50
|
+
if (starting.get(vault.dir) === pending)
|
|
51
|
+
starting.delete(vault.dir);
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
export async function demandPost(vault, cli, path, body) {
|
|
55
|
+
for (let attempt = 0; attempt < 2; attempt++) {
|
|
56
|
+
await ensureService(vault, cli);
|
|
57
|
+
const c = vault.load('connection');
|
|
58
|
+
try {
|
|
59
|
+
return await request(c.url + path, c.auth, 'POST', body);
|
|
60
|
+
}
|
|
61
|
+
catch (error) {
|
|
62
|
+
if (error instanceof HttpError || attempt)
|
|
63
|
+
throw error;
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
throw Error('Local service unavailable');
|
|
67
|
+
}
|
|
68
|
+
export async function stopService(vault) {
|
|
69
|
+
try {
|
|
70
|
+
await probe(vault);
|
|
71
|
+
}
|
|
72
|
+
catch {
|
|
73
|
+
return;
|
|
74
|
+
}
|
|
75
|
+
const c = vault.load('connection');
|
|
76
|
+
await request(c.url + '/shutdown', c.auth, 'POST', {});
|
|
77
|
+
const deadline = Date.now() + 12000;
|
|
78
|
+
while (Date.now() < deadline) {
|
|
79
|
+
try {
|
|
80
|
+
await probe(vault);
|
|
81
|
+
}
|
|
82
|
+
catch {
|
|
83
|
+
return;
|
|
84
|
+
}
|
|
85
|
+
await new Promise(done => setTimeout(done, 100));
|
|
86
|
+
}
|
|
87
|
+
throw Error('Local service did not stop');
|
|
88
|
+
}
|
|
89
|
+
//# sourceMappingURL=demand.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"demand.js","sourceRoot":"","sources":["../../src/demand.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { execFileSync } from 'node:child_process';
|
|
2
|
+
import { hostname } from 'node:os';
|
|
3
|
+
export function computerName() {
|
|
4
|
+
let name = '';
|
|
5
|
+
if (process.platform === 'darwin')
|
|
6
|
+
try {
|
|
7
|
+
name = execFileSync('/usr/sbin/scutil', ['--get', 'ComputerName'], { encoding: 'utf8', stdio: ['ignore', 'pipe', 'ignore'], timeout: 3000 }).trim();
|
|
8
|
+
}
|
|
9
|
+
catch { }
|
|
10
|
+
return Array.from((name || hostname().replace(/\.local$/i, '')).replace(/[\u0000-\u001f\u007f]/g, '').trim()).slice(0, 60).join('');
|
|
11
|
+
}
|
|
12
|
+
//# sourceMappingURL=device-name.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"device-name.js","sourceRoot":"","sources":["../../src/device-name.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAClD,OAAO,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AAEnC,MAAM,UAAU,YAAY;IAC3B,IAAI,IAAI,GAAC,EAAE,CAAC;IACZ,IAAG,OAAO,CAAC,QAAQ,KAAG,QAAQ;QAAC,IAAG,CAAC;YAAA,IAAI,GAAC,YAAY,CAAC,kBAAkB,EAAC,CAAC,OAAO,EAAC,cAAc,CAAC,EAAC,EAAC,QAAQ,EAAC,MAAM,EAAC,KAAK,EAAC,CAAC,QAAQ,EAAC,MAAM,EAAC,QAAQ,CAAC,EAAC,OAAO,EAAC,IAAI,EAAC,CAAC,CAAC,IAAI,EAAE,CAAC;QAAA,CAAC;QAAA,MAAK,CAAC,CAAA,CAAC;IACjL,OAAO,KAAK,CAAC,IAAI,CAAC,CAAC,IAAI,IAAE,QAAQ,EAAE,CAAC,OAAO,CAAC,WAAW,EAAC,EAAE,CAAC,CAAC,CAAC,OAAO,CAAC,wBAAwB,EAAC,EAAE,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,EAAC,EAAE,CAAC,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;AAChI,CAAC"}
|
package/dist/src/http.js
ADDED
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { createServer } from 'node:http';
|
|
2
|
+
export class HttpError extends Error {
|
|
3
|
+
status;
|
|
4
|
+
constructor(status, message) {
|
|
5
|
+
super(message);
|
|
6
|
+
this.status = status;
|
|
7
|
+
}
|
|
8
|
+
}
|
|
9
|
+
export async function jsonBody(req, max = 1_600_000) { let size = 0; const chunks = []; for await (const chunk of req) {
|
|
10
|
+
const b = Buffer.from(chunk);
|
|
11
|
+
size += b.length;
|
|
12
|
+
if (size > max)
|
|
13
|
+
throw new HttpError(413, 'Request too large');
|
|
14
|
+
chunks.push(b);
|
|
15
|
+
} try {
|
|
16
|
+
return JSON.parse(Buffer.concat(chunks).toString());
|
|
17
|
+
}
|
|
18
|
+
catch {
|
|
19
|
+
throw new HttpError(400, 'Invalid JSON');
|
|
20
|
+
} }
|
|
21
|
+
export function bearer(req) { const header = req.headers.authorization; if (!header?.startsWith('Bearer '))
|
|
22
|
+
throw new HttpError(401, 'Authorization required'); return header.slice(7); }
|
|
23
|
+
export function send(res, status, value) { res.writeHead(status, { 'content-type': 'application/json', 'cache-control': 'no-store', 'x-content-type-options': 'nosniff' }); res.end(JSON.stringify(value)); }
|
|
24
|
+
export async function listen(handler, port = 0, host = '127.0.0.1') {
|
|
25
|
+
const server = createServer((req, res) => { void handler(req, res).catch(error => { if (res.headersSent) {
|
|
26
|
+
res.destroy();
|
|
27
|
+
return;
|
|
28
|
+
} send(res, error instanceof HttpError ? error.status : 400, { error: error instanceof HttpError ? error.message : 'Invalid request' }); }); });
|
|
29
|
+
await new Promise((done, reject) => { server.once('error', reject); server.listen(port, host, done); });
|
|
30
|
+
return { server, url: `http://127.0.0.1:${server.address().port}` };
|
|
31
|
+
}
|
|
32
|
+
export async function request(url, auth, method = 'GET', body) { const res = await fetch(url, { method, headers: { ...(auth ? { authorization: 'Bearer ' + auth } : {}), ...(body ? { 'content-type': 'application/json' } : {}) }, body: body ? JSON.stringify(body) : undefined, signal: AbortSignal.timeout(10000) }); if (!res.ok) {
|
|
33
|
+
const data = await res.json().catch(() => null);
|
|
34
|
+
const detail = typeof data?.error === 'string' ? ': ' + data.error.slice(0, 300) : '';
|
|
35
|
+
throw new HttpError(res.status, `Request failed (${res.status})${detail}`);
|
|
36
|
+
} return await res.json(); }
|
|
37
|
+
export async function closeServer(server) { server.closeAllConnections(); await new Promise((done, reject) => server.close(e => e ? reject(e) : done())); }
|
|
38
|
+
//# sourceMappingURL=http.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"http.js","sourceRoot":"","sources":["../../src/http.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,254 @@
|
|
|
1
|
+
import { cpSync, existsSync, mkdirSync, readFileSync, readdirSync, readlinkSync, symlinkSync, renameSync, rmSync, lstatSync } from 'node:fs';
|
|
2
|
+
import { join, dirname, resolve } from 'node:path';
|
|
3
|
+
import { homedir } from 'node:os';
|
|
4
|
+
import { execFileSync } from 'node:child_process';
|
|
5
|
+
import { createHash, randomUUID } from 'node:crypto';
|
|
6
|
+
import { fileURLToPath, pathToFileURL } from 'node:url';
|
|
7
|
+
import { Vault } from './vault.js';
|
|
8
|
+
import { migrateState } from './service.js';
|
|
9
|
+
import { planIntegrations, planRemoval, applyChanges, atomicFile } from './integration.js';
|
|
10
|
+
import { probe, stopService } from './demand.js';
|
|
11
|
+
import { DatabaseSync } from 'node:sqlite';
|
|
12
|
+
import { reconcileClients } from './owners.js';
|
|
13
|
+
import { readPreferences, writePreferences, loadPreferences, preferencesSchema } from './preferences.js';
|
|
14
|
+
import { Daemon } from './daemon.js';
|
|
15
|
+
export const label = 'app.agentnotify.local';
|
|
16
|
+
const quote = (s) => "'" + s.replaceAll("'", "'\\''") + "'";
|
|
17
|
+
export function layout(home = homedir()) {
|
|
18
|
+
const root = join(home, 'Library', 'Application Support', 'AgentNotify');
|
|
19
|
+
return { home, root, state: join(root, 'state'), current: join(root, 'current'), manifest: join(root, 'installation.json'), launcher: join(home, '.local', 'bin', 'anotify'), plist: join(home, 'Library', 'LaunchAgents', label + '.plist') };
|
|
20
|
+
}
|
|
21
|
+
export function launchd() {
|
|
22
|
+
const target = `gui/${process.getuid()}/${label}`;
|
|
23
|
+
const run = (args) => execFileSync('/bin/launchctl', args, { encoding: 'utf8', stdio: ['ignore', 'pipe', 'pipe'] });
|
|
24
|
+
return { loaded: () => { try {
|
|
25
|
+
run(['print', target]);
|
|
26
|
+
return true;
|
|
27
|
+
}
|
|
28
|
+
catch {
|
|
29
|
+
return false;
|
|
30
|
+
} }, stop: () => { run(['bootout', target]); }, start: plist => { run(['enable', target]); run(['bootstrap', `gui/${process.getuid()}`, plist]); } };
|
|
31
|
+
}
|
|
32
|
+
function manifest(paths) {
|
|
33
|
+
if (!existsSync(paths.manifest))
|
|
34
|
+
return;
|
|
35
|
+
const value = JSON.parse(readFileSync(paths.manifest, 'utf8'));
|
|
36
|
+
if (value.schema !== 1 || value.owner !== 'agent-notify')
|
|
37
|
+
throw Error('Unrecognized installation manifest');
|
|
38
|
+
return value;
|
|
39
|
+
}
|
|
40
|
+
function packageRoot(from) {
|
|
41
|
+
let path = from;
|
|
42
|
+
while (dirname(path) !== path) {
|
|
43
|
+
if (existsSync(join(path, 'package.json'))) {
|
|
44
|
+
const candidate = JSON.parse(readFileSync(join(path, 'package.json'), 'utf8'));
|
|
45
|
+
if (candidate.name === 'agent-notify' || candidate.agentNotifyPackage === 1)
|
|
46
|
+
return path;
|
|
47
|
+
}
|
|
48
|
+
path = dirname(path);
|
|
49
|
+
}
|
|
50
|
+
throw Error('Cannot locate Agent Notify package');
|
|
51
|
+
}
|
|
52
|
+
function prepareRelease(paths) {
|
|
53
|
+
const moduleDir = dirname(fileURLToPath(import.meta.url));
|
|
54
|
+
const pkg = packageRoot(moduleDir);
|
|
55
|
+
const sources = existsSync(join(moduleDir, 'cli.js')) ? moduleDir : join(pkg, 'dist', 'src');
|
|
56
|
+
const digest = createHash('sha256');
|
|
57
|
+
for (const name of readdirSync(sources).sort())
|
|
58
|
+
if (name.endsWith('.js'))
|
|
59
|
+
digest.update(name).update(readFileSync(join(sources, name)));
|
|
60
|
+
const lock = existsSync(join(pkg, 'npm-shrinkwrap.json')) ? join(pkg, 'npm-shrinkwrap.json') : join(pkg, 'package-lock.json');
|
|
61
|
+
digest.update(readFileSync(lock)).update(process.execPath);
|
|
62
|
+
const version = JSON.parse(readFileSync(join(pkg, 'package.json'), 'utf8')).version;
|
|
63
|
+
const release = join(paths.root, 'releases', version + '-' + digest.digest('hex').slice(0, 16));
|
|
64
|
+
if (!existsSync(join(release, 'ready.json'))) {
|
|
65
|
+
const temporary = release + '.' + randomUUID();
|
|
66
|
+
mkdirSync(temporary, { recursive: true, mode: 0o700 });
|
|
67
|
+
try {
|
|
68
|
+
cpSync(sources, join(temporary, 'src'), { recursive: true });
|
|
69
|
+
cpSync(join(pkg, 'package.json'), join(temporary, 'package.json'));
|
|
70
|
+
cpSync(lock, join(temporary, 'package-lock.json'));
|
|
71
|
+
if (lock.endsWith('package-lock.json') && existsSync(join(pkg, 'node_modules')))
|
|
72
|
+
cpSync(join(pkg, 'node_modules'), join(temporary, 'node_modules'), { recursive: true, verbatimSymlinks: true });
|
|
73
|
+
else
|
|
74
|
+
execFileSync('npm', ['ci', '--omit=dev', '--ignore-scripts', '--no-audit', '--no-fund'], { cwd: temporary, stdio: 'pipe' });
|
|
75
|
+
execFileSync(process.execPath, ['--input-type=module', '-e', `await import(${JSON.stringify(pathToFileURL(join(temporary, 'src', 'daemon.js')).href)})`], { stdio: 'pipe' });
|
|
76
|
+
atomicFile(join(temporary, 'ready.json'), JSON.stringify({ version }));
|
|
77
|
+
renameSync(temporary, release);
|
|
78
|
+
}
|
|
79
|
+
finally {
|
|
80
|
+
rmSync(temporary, { recursive: true, force: true });
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
return { version, release };
|
|
84
|
+
}
|
|
85
|
+
function pointTo(paths, release) {
|
|
86
|
+
if (!release) {
|
|
87
|
+
rmSync(paths.current, { force: true });
|
|
88
|
+
return;
|
|
89
|
+
}
|
|
90
|
+
const temporary = paths.current + '.' + randomUUID();
|
|
91
|
+
symlinkSync(release, temporary, 'dir');
|
|
92
|
+
renameSync(temporary, paths.current);
|
|
93
|
+
}
|
|
94
|
+
export const serviceStatus = probe;
|
|
95
|
+
function savedCounts(state) {
|
|
96
|
+
if (!existsSync(join(state, 'local.sqlite')))
|
|
97
|
+
return { queued: 0, activeClients: 0, managedActiveClients: 0 };
|
|
98
|
+
const db = new DatabaseSync(join(state, 'local.sqlite'));
|
|
99
|
+
try {
|
|
100
|
+
reconcileClients(db);
|
|
101
|
+
const activeClients = Number(db.prepare('SELECT COUNT(*) AS n FROM clients WHERE closed=0').get().n);
|
|
102
|
+
const hasDirect = !!db.prepare("SELECT 1 FROM sqlite_master WHERE type='table' AND name='direct_clients'").get();
|
|
103
|
+
const managedActiveClients = hasDirect ? Number(db.prepare('SELECT COUNT(*) AS n FROM clients WHERE closed=0 AND id NOT IN (SELECT client FROM direct_clients)').get().n) : activeClients;
|
|
104
|
+
return { queued: Number(db.prepare("SELECT COUNT(*) AS n FROM events WHERE status='queued'").get().n), activeClients, managedActiveClients, preferences: readPreferences(db) };
|
|
105
|
+
}
|
|
106
|
+
finally {
|
|
107
|
+
db.close();
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
export async function install(options = {}) {
|
|
111
|
+
if (!options.manager && process.platform !== 'darwin')
|
|
112
|
+
throw Error('Installation requires macOS');
|
|
113
|
+
const paths = layout(options.home);
|
|
114
|
+
const old = manifest(paths);
|
|
115
|
+
const manager = options.manager ?? launchd();
|
|
116
|
+
if (!old && (existsSync(paths.current) || existsSync(paths.launcher) || existsSync(paths.plist) || manager.loaded()))
|
|
117
|
+
throw Error('An installation path or service is already owned by another application');
|
|
118
|
+
if (old)
|
|
119
|
+
for (const [path, expected] of [[paths.launcher, old.launcherText], [paths.plist, old.plistText]])
|
|
120
|
+
if (existsSync(path) && (lstatSync(path).isSymbolicLink() || readFileSync(path, 'utf8') !== expected))
|
|
121
|
+
throw Error(`Managed file was edited: ${path}`);
|
|
122
|
+
mkdirSync(paths.root, { recursive: true, mode: 0o700 });
|
|
123
|
+
const vault = options.vault ?? new Vault(paths.state);
|
|
124
|
+
const previous = vault.load('integrations') ?? { files: [] };
|
|
125
|
+
const oldPreferences = loadPreferences(paths.state);
|
|
126
|
+
const preferences = preferencesSchema.parse(options.preferences ?? oldPreferences);
|
|
127
|
+
if (savedCounts(paths.state).managedActiveClients)
|
|
128
|
+
throw Error('Close registered Agent sessions before installing or changing integrations. They have not been interrupted.');
|
|
129
|
+
// Conflicts are discovered before code staging, migration, or changes to any host settings.
|
|
130
|
+
const plan = planIntegrations(paths.home, join(paths.current, 'src', 'cli.js'), paths.state, previous, preferences.hosts);
|
|
131
|
+
const release = prepareRelease(paths);
|
|
132
|
+
if (options.migrateFrom)
|
|
133
|
+
await migrateState(new Vault(resolve(options.migrateFrom)), vault);
|
|
134
|
+
const launcherText = `#!/bin/sh\n# Agent Notify managed launcher v1\nexport AGENT_NOTIFY_HOME=${quote(paths.state)}\nexport AGENT_NOTIFY_MANAGED=1\nexec ${quote(process.execPath)} --disable-warning=ExperimentalWarning ${quote(join(paths.current, 'src', 'cli.js'))} "$@"\n`;
|
|
135
|
+
const plistText = '';
|
|
136
|
+
const oldLink = existsSync(paths.current) ? readlinkSync(paths.current) : undefined;
|
|
137
|
+
const wasRunning = manager.loaded();
|
|
138
|
+
const fileChanges = [...plan.changes,
|
|
139
|
+
{ path: paths.launcher, before: existsSync(paths.launcher) ? readFileSync(paths.launcher, 'utf8') : null, after: launcherText },
|
|
140
|
+
{ path: paths.plist, before: existsSync(paths.plist) ? readFileSync(paths.plist, 'utf8') : null, after: null }];
|
|
141
|
+
let applied = false, preferencesSaved = false;
|
|
142
|
+
try {
|
|
143
|
+
if (wasRunning)
|
|
144
|
+
manager.stop();
|
|
145
|
+
let running;
|
|
146
|
+
try {
|
|
147
|
+
running = await serviceStatus(vault);
|
|
148
|
+
}
|
|
149
|
+
catch { }
|
|
150
|
+
if ((running?.managedActiveClients ?? running?.activeClients ?? 0) > 0)
|
|
151
|
+
throw Error('Close registered Agent sessions before updating the local helper. They have not been interrupted.');
|
|
152
|
+
if (running)
|
|
153
|
+
await stopService(vault);
|
|
154
|
+
pointTo(paths, release.release);
|
|
155
|
+
applyChanges(fileChanges);
|
|
156
|
+
applied = true;
|
|
157
|
+
const { chmodSync } = await import('node:fs');
|
|
158
|
+
chmodSync(paths.launcher, 0o700);
|
|
159
|
+
if (options.verify)
|
|
160
|
+
options.verify();
|
|
161
|
+
else
|
|
162
|
+
execFileSync(process.execPath, [join(paths.current, 'src', 'cli.js'), 'config', 'claude'], { stdio: 'pipe' });
|
|
163
|
+
vault.save('integrations', plan.receipt);
|
|
164
|
+
const state = new Daemon(vault);
|
|
165
|
+
try {
|
|
166
|
+
writePreferences(state.db, preferences);
|
|
167
|
+
preferencesSaved = true;
|
|
168
|
+
}
|
|
169
|
+
finally {
|
|
170
|
+
state.db.close();
|
|
171
|
+
}
|
|
172
|
+
atomicFile(paths.manifest, JSON.stringify({ schema: 1, owner: 'agent-notify', installed: true, mode: 'on-demand', ...release, launcherText, plistText }, null, 2));
|
|
173
|
+
return { installed: true, mode: 'on-demand', loginBackgroundService: false, command: paths.launcher, state: paths.state, paired: !!vault.load('sender'), preferences, trustReview: 'Review new hooks/MCP definitions in each host. Existing sessions must load the updated hooks.' };
|
|
174
|
+
}
|
|
175
|
+
catch (error) {
|
|
176
|
+
if (manager.loaded())
|
|
177
|
+
manager.stop();
|
|
178
|
+
if (applied)
|
|
179
|
+
applyChanges(fileChanges.map(c => ({ ...c, before: c.after, after: c.path === paths.plist ? null : c.before })));
|
|
180
|
+
pointTo(paths, oldLink);
|
|
181
|
+
if (applied)
|
|
182
|
+
vault.save('integrations', previous);
|
|
183
|
+
if (preferencesSaved) {
|
|
184
|
+
const db = new DatabaseSync(join(paths.state, 'local.sqlite'));
|
|
185
|
+
try {
|
|
186
|
+
writePreferences(db, oldPreferences);
|
|
187
|
+
}
|
|
188
|
+
finally {
|
|
189
|
+
db.close();
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
// A failed transition must not reinstate the login job the user asked to remove.
|
|
193
|
+
throw error;
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
export async function uninstall(options = {}) {
|
|
197
|
+
const paths = layout(options.home);
|
|
198
|
+
const old = manifest(paths);
|
|
199
|
+
if (!old?.installed)
|
|
200
|
+
return { installed: false, statePreserved: paths.state };
|
|
201
|
+
const manager = options.manager ?? launchd();
|
|
202
|
+
const vault = options.vault ?? new Vault(paths.state);
|
|
203
|
+
if (savedCounts(paths.state).managedActiveClients)
|
|
204
|
+
throw Error('Close registered Agent sessions before uninstalling');
|
|
205
|
+
let running = false;
|
|
206
|
+
try {
|
|
207
|
+
const status = await serviceStatus(vault);
|
|
208
|
+
running = true;
|
|
209
|
+
if ((status.managedActiveClients ?? status.activeClients) > 0)
|
|
210
|
+
throw Error('Close registered Agent sessions before uninstalling');
|
|
211
|
+
}
|
|
212
|
+
catch (e) {
|
|
213
|
+
if (e instanceof Error && e.message.includes('Close registered'))
|
|
214
|
+
throw e;
|
|
215
|
+
}
|
|
216
|
+
const removal = planRemoval(vault.load('integrations') ?? { files: [] });
|
|
217
|
+
for (const [path, expected] of [[paths.launcher, old.launcherText], [paths.plist, old.plistText]])
|
|
218
|
+
if (existsSync(path)) {
|
|
219
|
+
if (lstatSync(path).isSymbolicLink() || readFileSync(path, 'utf8') !== expected)
|
|
220
|
+
throw Error(`Managed file was edited: ${path}`);
|
|
221
|
+
removal.push({ path: path, before: expected, after: null });
|
|
222
|
+
}
|
|
223
|
+
const wasRunning = manager.loaded();
|
|
224
|
+
try {
|
|
225
|
+
if (wasRunning)
|
|
226
|
+
manager.stop();
|
|
227
|
+
if (running && !wasRunning)
|
|
228
|
+
await stopService(vault);
|
|
229
|
+
applyChanges(removal);
|
|
230
|
+
}
|
|
231
|
+
catch (error) {
|
|
232
|
+
if (wasRunning)
|
|
233
|
+
manager.start(paths.plist);
|
|
234
|
+
throw error;
|
|
235
|
+
}
|
|
236
|
+
atomicFile(paths.manifest, JSON.stringify({ ...old, installed: false }, null, 2));
|
|
237
|
+
// Encrypted state, credentials and runtime cache are deliberately retained for recovery/reinstall.
|
|
238
|
+
return { installed: false, statePreserved: paths.state };
|
|
239
|
+
}
|
|
240
|
+
export async function installationStatus() {
|
|
241
|
+
const paths = layout();
|
|
242
|
+
const installed = manifest(paths);
|
|
243
|
+
const loaded = launchd().loaded();
|
|
244
|
+
if (!existsSync(paths.state))
|
|
245
|
+
return { installed: !!installed?.installed, loaded, running: false };
|
|
246
|
+
const vault = new Vault(paths.state);
|
|
247
|
+
try {
|
|
248
|
+
return { installed: !!installed?.installed, mode: 'on-demand', loginBackgroundService: loaded, running: true, ...await serviceStatus(vault) };
|
|
249
|
+
}
|
|
250
|
+
catch {
|
|
251
|
+
return { installed: !!installed?.installed, mode: 'on-demand', loginBackgroundService: loaded, running: false, state: 'idle', paired: !!vault.load('sender'), ...savedCounts(paths.state) };
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
//# sourceMappingURL=install.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"install.js","sourceRoot":"","sources":["../../src/install.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,184 @@
|
|
|
1
|
+
import { readFileSync, existsSync, mkdirSync, writeFileSync, renameSync, rmSync, lstatSync } from 'node:fs';
|
|
2
|
+
import { dirname, join } from 'node:path';
|
|
3
|
+
import { randomUUID } from 'node:crypto';
|
|
4
|
+
import { parse as parseToml } from 'smol-toml';
|
|
5
|
+
import { hostConfig } from './config.js';
|
|
6
|
+
const quote = (s) => "'" + s.replaceAll("'", "'\\''") + "'";
|
|
7
|
+
export const managedHookCommand = (state, cli, host) => `AGENT_NOTIFY_HOME=${quote(state)} ${[process.execPath, cli, 'installed-hook', host].map(quote).join(' ')}`;
|
|
8
|
+
const same = (a, b) => JSON.stringify(a) === JSON.stringify(b);
|
|
9
|
+
function object(value) { if (!value || typeof value !== 'object' || Array.isArray(value))
|
|
10
|
+
throw Error('Expected a settings object'); return value; }
|
|
11
|
+
const read = (path) => { if (!existsSync(path))
|
|
12
|
+
return null; if (lstatSync(path).isSymbolicLink())
|
|
13
|
+
throw Error(`Settings symlink requires manual integration: ${path}`); return readFileSync(path, 'utf8'); };
|
|
14
|
+
const json = (value) => JSON.stringify(value, null, 2) + '\n';
|
|
15
|
+
function removeOwned(current, file) {
|
|
16
|
+
if (current === null)
|
|
17
|
+
return null;
|
|
18
|
+
if (current === file.installed)
|
|
19
|
+
return file.original;
|
|
20
|
+
if (file.block) {
|
|
21
|
+
if (!current.includes(file.block))
|
|
22
|
+
throw Error(`Managed Codex block was edited: ${file.path}`);
|
|
23
|
+
const clean = current.replace(file.block, '');
|
|
24
|
+
parseToml(clean);
|
|
25
|
+
return clean;
|
|
26
|
+
}
|
|
27
|
+
const root = object(JSON.parse(current));
|
|
28
|
+
if (file.server) {
|
|
29
|
+
const servers = object(root.mcpServers ?? {});
|
|
30
|
+
if ('agent_notify' in servers && !same(servers.agent_notify, file.server))
|
|
31
|
+
throw Error(`Managed MCP entry was edited: ${file.path}`);
|
|
32
|
+
delete servers.agent_notify;
|
|
33
|
+
if (Object.keys(servers).length)
|
|
34
|
+
root.mcpServers = servers;
|
|
35
|
+
else
|
|
36
|
+
delete root.mcpServers;
|
|
37
|
+
}
|
|
38
|
+
if (file.handlers.length && root.hooks) {
|
|
39
|
+
const hooks = object(root.hooks);
|
|
40
|
+
for (const [event, groups] of Object.entries(hooks)) {
|
|
41
|
+
if (!Array.isArray(groups))
|
|
42
|
+
throw Error(`Invalid hook groups: ${file.path}`);
|
|
43
|
+
hooks[event] = groups.flatMap(group => {
|
|
44
|
+
const g = object(group);
|
|
45
|
+
if (!Array.isArray(g.hooks))
|
|
46
|
+
return [group];
|
|
47
|
+
const remaining = g.hooks.filter(h => {
|
|
48
|
+
if (file.handlers.some(owned => same(owned, h)))
|
|
49
|
+
return false;
|
|
50
|
+
// Claude settings may normalize away a descriptive name. Accept only that
|
|
51
|
+
// exact change; commands, timeouts and all other fields must still match.
|
|
52
|
+
if (file.path.endsWith('/.claude/settings.json') && object(h).name === undefined && file.handlers.some(owned => {
|
|
53
|
+
const { name, ...rest } = object(owned);
|
|
54
|
+
return typeof name === 'string' && name.startsWith('anotify-managed-') && same(rest, h);
|
|
55
|
+
}))
|
|
56
|
+
return false;
|
|
57
|
+
if (file.handlers.some(owned => (typeof object(owned).name === 'string' && object(owned).name === object(h).name) || object(owned).command === object(h).command))
|
|
58
|
+
throw Error(`Managed hook was edited: ${file.path}`);
|
|
59
|
+
return true;
|
|
60
|
+
});
|
|
61
|
+
if (remaining.length === g.hooks.length)
|
|
62
|
+
return [group];
|
|
63
|
+
return remaining.length ? [{ ...g, hooks: remaining }] : [];
|
|
64
|
+
});
|
|
65
|
+
if (!hooks[event].length)
|
|
66
|
+
delete hooks[event];
|
|
67
|
+
}
|
|
68
|
+
if (!Object.keys(hooks).length)
|
|
69
|
+
delete root.hooks;
|
|
70
|
+
}
|
|
71
|
+
return !Object.keys(root).length && file.original === null ? null : json(root);
|
|
72
|
+
}
|
|
73
|
+
/** Plan all changes before writing any user configuration. Receipt is encrypted by the caller. */
|
|
74
|
+
export function planIntegrations(home, cli, state, previous = { files: [] }, hosts = ['claude-code', 'codex', 'gemini-cli']) {
|
|
75
|
+
const actual = new Map();
|
|
76
|
+
const baseline = new Map();
|
|
77
|
+
const files = new Map();
|
|
78
|
+
function prepare(path) {
|
|
79
|
+
if (!actual.has(path)) {
|
|
80
|
+
const current = read(path);
|
|
81
|
+
actual.set(path, current);
|
|
82
|
+
const old = previous.files.find(f => f.path === path);
|
|
83
|
+
baseline.set(path, old ? removeOwned(current, old) : current);
|
|
84
|
+
}
|
|
85
|
+
if (!files.has(path))
|
|
86
|
+
files.set(path, { path, original: baseline.get(path), installed: baseline.get(path) ?? '', handlers: [] });
|
|
87
|
+
return files.get(path);
|
|
88
|
+
}
|
|
89
|
+
for (const host of hosts) {
|
|
90
|
+
const generated = hostConfig(host, cli);
|
|
91
|
+
const hooks = ('settings' in generated ? generated.settings?.hooks : generated.hooks?.hooks);
|
|
92
|
+
const command = managedHookCommand(state, cli, host);
|
|
93
|
+
for (const groups of Object.values(hooks))
|
|
94
|
+
for (const group of groups)
|
|
95
|
+
for (const handler of group.hooks) {
|
|
96
|
+
handler.command = command;
|
|
97
|
+
handler.name = 'anotify-managed-' + handler.name;
|
|
98
|
+
}
|
|
99
|
+
const path = host === 'claude-code' ? join(home, '.claude', 'settings.json') : host === 'codex' ? join(home, '.codex', 'hooks.json') : join(home, '.gemini', 'settings.json');
|
|
100
|
+
const hookFile = prepare(path);
|
|
101
|
+
const root = object(JSON.parse(hookFile.installed || '{}'));
|
|
102
|
+
const existing = object(root.hooks ?? {});
|
|
103
|
+
for (const [event, groups] of Object.entries(hooks)) {
|
|
104
|
+
if (existing[event] !== undefined && !Array.isArray(existing[event]))
|
|
105
|
+
throw Error(`Invalid hook configuration: ${path}`);
|
|
106
|
+
existing[event] = [...(existing[event] ?? []), ...groups];
|
|
107
|
+
hookFile.handlers.push(...groups.flatMap(g => g.hooks));
|
|
108
|
+
}
|
|
109
|
+
root.hooks = existing;
|
|
110
|
+
hookFile.installed = json(root);
|
|
111
|
+
const server = { command: process.execPath, args: [cli, 'installed-mcp', host], env: { AGENT_NOTIFY_HOME: state, ...(host === 'gemini-cli' ? { AGENT_NOTIFY_INSTANCE: '${AGENT_NOTIFY_INSTANCE}' } : {}) } };
|
|
112
|
+
if (host === 'codex') {
|
|
113
|
+
const config = prepare(join(home, '.codex', 'config.toml'));
|
|
114
|
+
const parsed = parseToml(config.installed);
|
|
115
|
+
if (parsed.mcp_servers && 'agent_notify' in object(parsed.mcp_servers))
|
|
116
|
+
throw Error('Codex already has an unmanaged agent_notify server');
|
|
117
|
+
config.block = '\n# BEGIN anotify managed MCP\n[mcp_servers.agent_notify]\n' + `command = ${JSON.stringify(server.command)}\nargs = ${JSON.stringify(server.args)}\nenv_vars = ["AGENT_NOTIFY_INSTANCE"]\n[mcp_servers.agent_notify.env]\nAGENT_NOTIFY_HOME = ${JSON.stringify(state)}\n# END anotify managed MCP\n`;
|
|
118
|
+
config.installed += config.block;
|
|
119
|
+
parseToml(config.installed);
|
|
120
|
+
}
|
|
121
|
+
else {
|
|
122
|
+
const file = host === 'claude-code' ? prepare(join(home, '.claude.json')) : hookFile;
|
|
123
|
+
const value = object(JSON.parse(file.installed || '{}'));
|
|
124
|
+
const servers = object(value.mcpServers ?? {});
|
|
125
|
+
if ('agent_notify' in servers)
|
|
126
|
+
throw Error(`${host} already has an unmanaged agent_notify server`);
|
|
127
|
+
servers.agent_notify = server;
|
|
128
|
+
value.mcpServers = servers;
|
|
129
|
+
file.server = server;
|
|
130
|
+
file.installed = json(value);
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
// Explicit host selection can remove only our old entries, preserving other
|
|
134
|
+
// settings and refusing edited owned definitions before any file is written.
|
|
135
|
+
const removed = [];
|
|
136
|
+
for (const old of previous.files)
|
|
137
|
+
if (!files.has(old.path)) {
|
|
138
|
+
const before = read(old.path);
|
|
139
|
+
removed.push({ path: old.path, before, after: removeOwned(before, old) });
|
|
140
|
+
}
|
|
141
|
+
return { receipt: { files: [...files.values()] }, changes: [...[...files.values()].map(file => ({ path: file.path, before: actual.get(file.path), after: file.installed })), ...removed] };
|
|
142
|
+
}
|
|
143
|
+
export function planRemoval(receipt) { return receipt.files.map(file => { const before = read(file.path); return { path: file.path, before, after: removeOwned(before, file) }; }); }
|
|
144
|
+
export function atomicFile(path, text) {
|
|
145
|
+
mkdirSync(dirname(path), { recursive: true, mode: 0o700 });
|
|
146
|
+
const temporary = path + '.' + randomUUID() + '.tmp';
|
|
147
|
+
try {
|
|
148
|
+
writeFileSync(temporary, text, { mode: 0o600, flag: 'wx' });
|
|
149
|
+
renameSync(temporary, path);
|
|
150
|
+
}
|
|
151
|
+
finally {
|
|
152
|
+
rmSync(temporary, { force: true });
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
export function applyChanges(changes) {
|
|
156
|
+
for (const change of changes)
|
|
157
|
+
if (read(change.path) !== change.before)
|
|
158
|
+
throw Error(`Settings changed during installation: ${change.path}`);
|
|
159
|
+
const applied = [];
|
|
160
|
+
try {
|
|
161
|
+
for (const change of changes) {
|
|
162
|
+
if (change.before === change.after)
|
|
163
|
+
continue;
|
|
164
|
+
if (read(change.path) !== change.before)
|
|
165
|
+
throw Error(`Settings changed during installation: ${change.path}`);
|
|
166
|
+
if (change.after === null)
|
|
167
|
+
rmSync(change.path, { force: true });
|
|
168
|
+
else
|
|
169
|
+
atomicFile(change.path, change.after);
|
|
170
|
+
applied.push(change);
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
catch (error) {
|
|
174
|
+
for (const change of applied.reverse())
|
|
175
|
+
if (read(change.path) === change.after) {
|
|
176
|
+
if (change.before === null)
|
|
177
|
+
rmSync(change.path, { force: true });
|
|
178
|
+
else
|
|
179
|
+
atomicFile(change.path, change.before);
|
|
180
|
+
}
|
|
181
|
+
throw error;
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
//# sourceMappingURL=integration.js.map
|