@amalgm/shell 0.1.161 → 0.1.163
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/PURPOSE.md +98 -14
- package/README.md +16 -0
- package/dist/authorization/recovery-host.d.ts +2 -1
- package/dist/authorization/recovery-host.js +3 -15
- package/dist/authorization/recovery-host.js.map +1 -1
- package/dist/cli-arguments.d.ts +8 -0
- package/dist/cli-arguments.js +47 -0
- package/dist/cli-arguments.js.map +1 -0
- package/dist/cli.d.ts +6 -4
- package/dist/cli.js +85 -73
- package/dist/cli.js.map +1 -1
- package/dist/compose.js +4 -3
- package/dist/compose.js.map +1 -1
- package/dist/development/profile.d.ts +27 -0
- package/dist/development/profile.js +142 -0
- package/dist/development/profile.js.map +1 -0
- package/dist/files-register-host.d.ts +1 -0
- package/dist/files-register-host.js +1 -0
- package/dist/files-register-host.js.map +1 -1
- package/dist/installation/environment.d.ts +30 -0
- package/dist/installation/environment.js +28 -0
- package/dist/installation/environment.js.map +1 -0
- package/dist/installation/lease.d.ts +6 -0
- package/dist/installation/lease.js +28 -0
- package/dist/installation/lease.js.map +1 -0
- package/dist/installation/ownership.d.ts +30 -0
- package/dist/installation/ownership.js +156 -0
- package/dist/installation/ownership.js.map +1 -0
- package/dist/installation/selection.d.ts +8 -0
- package/dist/installation/selection.js +29 -0
- package/dist/installation/selection.js.map +1 -0
- package/dist/machine-host.d.ts +26 -5
- package/dist/machine-host.js +192 -69
- package/dist/machine-host.js.map +1 -1
- package/dist/main.js +56 -2
- package/dist/main.js.map +1 -1
- package/dist/platform-product-host.d.ts +9 -9
- package/dist/platform-product-host.js +17 -17
- package/dist/platform-product-host.js.map +1 -1
- package/dist/platform-tools.d.ts +4 -3
- package/dist/platform-tools.js +34 -37
- package/dist/platform-tools.js.map +1 -1
- package/dist/process-lifecycle.d.ts +8 -0
- package/dist/process-lifecycle.js +32 -0
- package/dist/process-lifecycle.js.map +1 -0
- package/dist/runtime-command-host.d.ts +7 -0
- package/dist/runtime-command-host.js +49 -13
- package/dist/runtime-command-host.js.map +1 -1
- package/dist/service/adapter.d.ts +36 -0
- package/dist/service/adapter.js +24 -0
- package/dist/service/adapter.js.map +1 -0
- package/dist/service/cli.d.ts +8 -0
- package/dist/service/cli.js +196 -0
- package/dist/service/cli.js.map +1 -0
- package/dist/service/control.d.ts +29 -0
- package/dist/service/control.js +68 -0
- package/dist/service/control.js.map +1 -0
- package/dist/service/index.d.ts +9 -0
- package/dist/service/index.js +10 -0
- package/dist/service/index.js.map +1 -0
- package/dist/service/keepalive.d.ts +11 -0
- package/dist/service/keepalive.js +180 -0
- package/dist/service/keepalive.js.map +1 -0
- package/dist/service/launchd.d.ts +9 -0
- package/dist/service/launchd.js +105 -0
- package/dist/service/launchd.js.map +1 -0
- package/dist/service/owner.d.ts +4 -0
- package/dist/service/owner.js +14 -0
- package/dist/service/owner.js.map +1 -0
- package/dist/service/portable.d.ts +10 -0
- package/dist/service/portable.js +32 -0
- package/dist/service/portable.js.map +1 -0
- package/dist/service/registration.d.ts +82 -0
- package/dist/service/registration.js +114 -0
- package/dist/service/registration.js.map +1 -0
- package/dist/service/systemd.d.ts +9 -0
- package/dist/service/systemd.js +94 -0
- package/dist/service/systemd.js.map +1 -0
- package/dist/service/windows-task.d.ts +16 -0
- package/dist/service/windows-task.js +82 -0
- package/dist/service/windows-task.js.map +1 -0
- package/dist/service/windows.d.ts +2 -0
- package/dist/service/windows.js +65 -0
- package/dist/service/windows.js.map +1 -0
- package/dist/user-ground-host.d.ts +3 -0
- package/dist/user-ground-host.js +13 -0
- package/dist/user-ground-host.js.map +1 -1
- package/npm-shrinkwrap.json +49 -83
- package/package.json +9 -9
|
@@ -0,0 +1,180 @@
|
|
|
1
|
+
import { spawn } from 'node:child_process';
|
|
2
|
+
import { randomBytes, randomUUID, timingSafeEqual } from 'node:crypto';
|
|
3
|
+
import { closeSync, fstatSync, openSync, renameSync, rmSync, writeSync } from 'node:fs';
|
|
4
|
+
import { createServer } from 'node:http';
|
|
5
|
+
import { join } from 'node:path';
|
|
6
|
+
import { recentCrashes, restartDelayMs } from '@amalgm/core/supervision';
|
|
7
|
+
import { runtimeBindingsMatch } from '@amalgm/core/binding';
|
|
8
|
+
import { atomicJson, ensurePrivateDir } from '../filesystem.js';
|
|
9
|
+
import { acquireProcessLease } from '../installation/lease.js';
|
|
10
|
+
import { readRegistration } from './registration.js';
|
|
11
|
+
import { controllerLeaseFile, controllerStateFile } from './control.js';
|
|
12
|
+
export const STOP_GRACE_MS = 25_000;
|
|
13
|
+
export const LOG_LIMIT_BYTES = 8 * 1024 * 1024;
|
|
14
|
+
/** One leased controller supervises one runtime over IPC. OS adapters restart
|
|
15
|
+
* the controller; Core supplies bounded restart timing for its runtime child. */
|
|
16
|
+
export async function runKeepalive(identity) {
|
|
17
|
+
const registration = readRegistration(identity);
|
|
18
|
+
if (!registration)
|
|
19
|
+
throw new Error('This environment has no runtime service registration.');
|
|
20
|
+
ensurePrivateDir(identity.serviceDir);
|
|
21
|
+
const lease = acquireProcessLease(controllerLeaseFile(identity));
|
|
22
|
+
if (!lease)
|
|
23
|
+
return 0; // A concurrent start binds to the existing owner.
|
|
24
|
+
const log = openRotatingLog(join(identity.serviceDir, 'service.log'), LOG_LIMIT_BYTES);
|
|
25
|
+
let child = null;
|
|
26
|
+
let stopping = false;
|
|
27
|
+
let wake = null;
|
|
28
|
+
let killTimer;
|
|
29
|
+
let state = {
|
|
30
|
+
binding: identity.binding, user: identity.user, instanceId: randomUUID(), pid: process.pid,
|
|
31
|
+
port: 0, token: randomBytes(32).toString('base64url'), childPid: null, restarts: 0,
|
|
32
|
+
};
|
|
33
|
+
const publish = (patch) => {
|
|
34
|
+
state = { ...state, ...patch };
|
|
35
|
+
atomicJson(controllerStateFile(identity), state);
|
|
36
|
+
};
|
|
37
|
+
const stop = () => {
|
|
38
|
+
if (stopping)
|
|
39
|
+
return;
|
|
40
|
+
stopping = true;
|
|
41
|
+
if (child && child.exitCode === null && child.signalCode === null) {
|
|
42
|
+
const owned = child;
|
|
43
|
+
if (owned.connected)
|
|
44
|
+
owned.send({ type: 'amalgm-shutdown' }, () => { });
|
|
45
|
+
else
|
|
46
|
+
owned.kill('SIGTERM');
|
|
47
|
+
killTimer = setTimeout(() => {
|
|
48
|
+
if (owned.exitCode === null && owned.signalCode === null)
|
|
49
|
+
owned.kill('SIGKILL');
|
|
50
|
+
}, STOP_GRACE_MS);
|
|
51
|
+
killTimer.unref();
|
|
52
|
+
}
|
|
53
|
+
wake?.();
|
|
54
|
+
};
|
|
55
|
+
const server = createServer((request, response) => {
|
|
56
|
+
const actual = Buffer.from(request.headers.authorization || '');
|
|
57
|
+
const expected = Buffer.from(`Bearer ${state.token}`);
|
|
58
|
+
if (actual.length !== expected.length || !timingSafeEqual(actual, expected)) {
|
|
59
|
+
response.writeHead(401).end();
|
|
60
|
+
return;
|
|
61
|
+
}
|
|
62
|
+
if (request.method === 'GET' && request.url === '/status'
|
|
63
|
+
|| request.method === 'POST' && request.url === '/stop') {
|
|
64
|
+
// A valid token proves access to this endpoint, not that the caller
|
|
65
|
+
// selected this owner. Mutation admission must prove both before effects.
|
|
66
|
+
if (request.method === 'POST') {
|
|
67
|
+
let expectedIdentity;
|
|
68
|
+
try {
|
|
69
|
+
expectedIdentity = JSON.parse(String(request.headers['x-amalgm-controller-identity'] || ''));
|
|
70
|
+
}
|
|
71
|
+
catch {
|
|
72
|
+
response.writeHead(409).end();
|
|
73
|
+
return;
|
|
74
|
+
}
|
|
75
|
+
if (expectedIdentity?.instanceId !== state.instanceId || expectedIdentity?.user !== state.user
|
|
76
|
+
|| !runtimeBindingsMatch(expectedIdentity?.binding, state.binding)) {
|
|
77
|
+
response.writeHead(409).end();
|
|
78
|
+
return;
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
response.writeHead(200, { 'content-type': 'application/json', 'cache-control': 'no-store' });
|
|
82
|
+
response.end(JSON.stringify({ ...state, token: '' }));
|
|
83
|
+
if (request.url === '/stop')
|
|
84
|
+
stop();
|
|
85
|
+
return;
|
|
86
|
+
}
|
|
87
|
+
response.writeHead(404).end();
|
|
88
|
+
});
|
|
89
|
+
process.once('SIGTERM', stop);
|
|
90
|
+
process.once('SIGINT', stop);
|
|
91
|
+
let crashes = [];
|
|
92
|
+
try {
|
|
93
|
+
await new Promise((resolve, reject) => {
|
|
94
|
+
server.once('error', reject);
|
|
95
|
+
server.listen(0, '127.0.0.1', resolve);
|
|
96
|
+
});
|
|
97
|
+
const address = server.address();
|
|
98
|
+
if (!address || typeof address === 'string')
|
|
99
|
+
throw new Error('The controller did not receive a local address.');
|
|
100
|
+
publish({ port: address.port });
|
|
101
|
+
while (!stopping) {
|
|
102
|
+
child = spawn(registration.node, [registration.bin, ...registration.run_args], {
|
|
103
|
+
cwd: identity.home, env: process.env,
|
|
104
|
+
stdio: ['ignore', 'pipe', 'pipe', 'ipc'], windowsHide: true,
|
|
105
|
+
});
|
|
106
|
+
const exiting = childExit(child);
|
|
107
|
+
child.stdout?.on('data', (chunk) => log.write(chunk));
|
|
108
|
+
child.stderr?.on('data', (chunk) => log.write(chunk));
|
|
109
|
+
publish({ childPid: child.pid ?? null });
|
|
110
|
+
const outcome = await exiting;
|
|
111
|
+
child = null;
|
|
112
|
+
if (killTimer)
|
|
113
|
+
clearTimeout(killTimer);
|
|
114
|
+
publish({ childPid: null });
|
|
115
|
+
if (stopping)
|
|
116
|
+
break;
|
|
117
|
+
const now = Date.now();
|
|
118
|
+
crashes = [...recentCrashes(crashes, now), now];
|
|
119
|
+
const delayMs = restartDelayMs(crashes.length);
|
|
120
|
+
publish({ restarts: state.restarts + 1 });
|
|
121
|
+
log.write(`${JSON.stringify({ event: 'runtime-exited', ...outcome, restartInMs: delayMs })}\n`);
|
|
122
|
+
await new Promise((resolve) => {
|
|
123
|
+
const timer = setTimeout(resolve, delayMs);
|
|
124
|
+
wake = () => { clearTimeout(timer); resolve(); };
|
|
125
|
+
});
|
|
126
|
+
wake = null;
|
|
127
|
+
}
|
|
128
|
+
return 0;
|
|
129
|
+
}
|
|
130
|
+
finally {
|
|
131
|
+
stop();
|
|
132
|
+
if (child && child.exitCode === null && child.signalCode === null)
|
|
133
|
+
await childExit(child);
|
|
134
|
+
if (killTimer)
|
|
135
|
+
clearTimeout(killTimer);
|
|
136
|
+
process.off('SIGTERM', stop);
|
|
137
|
+
process.off('SIGINT', stop);
|
|
138
|
+
server.closeAllConnections();
|
|
139
|
+
await new Promise((resolve) => { if (server.listening)
|
|
140
|
+
server.close(() => resolve());
|
|
141
|
+
else
|
|
142
|
+
resolve(); });
|
|
143
|
+
rmSync(controllerStateFile(identity), { force: true });
|
|
144
|
+
log.close();
|
|
145
|
+
lease.close();
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
function childExit(child) {
|
|
149
|
+
if (child.exitCode !== null || child.signalCode !== null)
|
|
150
|
+
return Promise.resolve({ code: child.exitCode, signal: child.signalCode });
|
|
151
|
+
return new Promise((resolve) => {
|
|
152
|
+
child.once('error', () => resolve({ code: 1, signal: null }));
|
|
153
|
+
child.once('exit', (code, signal) => resolve({ code, signal }));
|
|
154
|
+
});
|
|
155
|
+
}
|
|
156
|
+
/** Two bounded log files; even a single oversized write obeys the limit. */
|
|
157
|
+
export function openRotatingLog(file, limitBytes) {
|
|
158
|
+
let fd = openSync(file, 'a');
|
|
159
|
+
let size = fstatSync(fd).size;
|
|
160
|
+
return {
|
|
161
|
+
write(chunk) {
|
|
162
|
+
const bytes = Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk);
|
|
163
|
+
for (let offset = 0; offset < bytes.length;) {
|
|
164
|
+
if (size >= limitBytes) {
|
|
165
|
+
closeSync(fd);
|
|
166
|
+
rmSync(`${file}.1`, { force: true });
|
|
167
|
+
renameSync(file, `${file}.1`);
|
|
168
|
+
fd = openSync(file, 'a');
|
|
169
|
+
size = 0;
|
|
170
|
+
}
|
|
171
|
+
const count = Math.min(bytes.length - offset, limitBytes - size);
|
|
172
|
+
writeSync(fd, bytes, offset, count);
|
|
173
|
+
offset += count;
|
|
174
|
+
size += count;
|
|
175
|
+
}
|
|
176
|
+
},
|
|
177
|
+
close() { closeSync(fd); },
|
|
178
|
+
};
|
|
179
|
+
}
|
|
180
|
+
//# sourceMappingURL=keepalive.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"keepalive.js","sourceRoot":"","sources":["../../src/service/keepalive.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { type ServiceOwner } from "./adapter.js";
|
|
2
|
+
export declare function renderLaunchdPlist(input: {
|
|
3
|
+
readonly label: string;
|
|
4
|
+
readonly command: readonly string[];
|
|
5
|
+
readonly environment: Readonly<Record<string, string>>;
|
|
6
|
+
readonly workingDirectory: string;
|
|
7
|
+
readonly logPath: string;
|
|
8
|
+
}): string;
|
|
9
|
+
export declare const launchdOwner: ServiceOwner;
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
import { stopKeepalive } from "./control.js";
|
|
2
|
+
/**
|
|
3
|
+
* macOS: a LaunchAgent in the user's `gui/<uid>` domain. launchd starts it
|
|
4
|
+
* at login, restarts it whenever it exits non-zero, and stops it at logout.
|
|
5
|
+
* Every knob is written out, so the policy is the file, not a default.
|
|
6
|
+
*/
|
|
7
|
+
import { existsSync, rmSync } from "node:fs";
|
|
8
|
+
import { dirname } from "node:path";
|
|
9
|
+
import { launchdPlistPath, plistEscape } from "@amalgm/core/supervision";
|
|
10
|
+
import { atomicWrite, ensureUserDir } from "../filesystem.js";
|
|
11
|
+
import { keepaliveCommand, osCommand, serviceEnvironment } from "./adapter.js";
|
|
12
|
+
/** launchd's own default, stated: a non-zero exit is retried after 10s. */
|
|
13
|
+
const THROTTLE_INTERVAL_S = 10;
|
|
14
|
+
/** SIGTERM, then SIGKILL after this; the keepalive stops its child within 25s. */
|
|
15
|
+
const EXIT_TIMEOUT_S = 30;
|
|
16
|
+
export function renderLaunchdPlist(input) {
|
|
17
|
+
const args = input.command.map((part) => ` <string>${plistEscape(part)}</string>`).join("\n");
|
|
18
|
+
const env = Object.entries(input.environment)
|
|
19
|
+
.map(([key, value]) => ` <key>${plistEscape(key)}</key>\n <string>${plistEscape(value)}</string>`)
|
|
20
|
+
.join("\n");
|
|
21
|
+
return `<?xml version="1.0" encoding="UTF-8"?>
|
|
22
|
+
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
|
23
|
+
<plist version="1.0">
|
|
24
|
+
<dict>
|
|
25
|
+
<key>Label</key>
|
|
26
|
+
<string>${plistEscape(input.label)}</string>
|
|
27
|
+
<key>ProgramArguments</key>
|
|
28
|
+
<array>
|
|
29
|
+
${args}
|
|
30
|
+
</array>
|
|
31
|
+
<key>EnvironmentVariables</key>
|
|
32
|
+
<dict>
|
|
33
|
+
${env}
|
|
34
|
+
</dict>
|
|
35
|
+
<key>WorkingDirectory</key>
|
|
36
|
+
<string>${plistEscape(input.workingDirectory)}</string>
|
|
37
|
+
<key>RunAtLoad</key>
|
|
38
|
+
<true/>
|
|
39
|
+
<key>KeepAlive</key>
|
|
40
|
+
<dict>
|
|
41
|
+
<key>SuccessfulExit</key>
|
|
42
|
+
<false/>
|
|
43
|
+
</dict>
|
|
44
|
+
<key>ThrottleInterval</key>
|
|
45
|
+
<integer>${THROTTLE_INTERVAL_S}</integer>
|
|
46
|
+
<key>ExitTimeOut</key>
|
|
47
|
+
<integer>${EXIT_TIMEOUT_S}</integer>
|
|
48
|
+
<key>StandardOutPath</key>
|
|
49
|
+
<string>${plistEscape(input.logPath)}</string>
|
|
50
|
+
<key>StandardErrorPath</key>
|
|
51
|
+
<string>${plistEscape(input.logPath)}</string>
|
|
52
|
+
</dict>
|
|
53
|
+
</plist>
|
|
54
|
+
`;
|
|
55
|
+
}
|
|
56
|
+
function domain() {
|
|
57
|
+
return `gui/${process.getuid?.() ?? 0}`;
|
|
58
|
+
}
|
|
59
|
+
function plist(identity) {
|
|
60
|
+
return launchdPlistPath(identity.home, identity.label);
|
|
61
|
+
}
|
|
62
|
+
function launchctl(args, tolerate = false) {
|
|
63
|
+
return osCommand("launchctl", args, { tolerate });
|
|
64
|
+
}
|
|
65
|
+
export const launchdOwner = {
|
|
66
|
+
backend: "launchd",
|
|
67
|
+
files: (identity) => ({ plist: plist(identity) }),
|
|
68
|
+
async install(identity, launcher, keepaliveArgs) {
|
|
69
|
+
await stopKeepalive(identity);
|
|
70
|
+
const file = plist(identity);
|
|
71
|
+
ensureUserDir(dirname(file));
|
|
72
|
+
atomicWrite(file, renderLaunchdPlist({
|
|
73
|
+
label: identity.label,
|
|
74
|
+
command: keepaliveCommand(launcher, keepaliveArgs),
|
|
75
|
+
environment: serviceEnvironment(identity),
|
|
76
|
+
workingDirectory: identity.home,
|
|
77
|
+
logPath: `${identity.serviceDir}/launchd.log`,
|
|
78
|
+
}), 0o644);
|
|
79
|
+
launchctl(["bootout", `${domain()}/${identity.label}`], true);
|
|
80
|
+
launchctl(["bootstrap", domain(), file]);
|
|
81
|
+
launchctl(["enable", `${domain()}/${identity.label}`]);
|
|
82
|
+
},
|
|
83
|
+
start(identity) {
|
|
84
|
+
launchctl(["bootstrap", domain(), plist(identity)], true);
|
|
85
|
+
launchctl(["enable", `${domain()}/${identity.label}`], true);
|
|
86
|
+
launchctl(["kickstart", `${domain()}/${identity.label}`]);
|
|
87
|
+
},
|
|
88
|
+
async stop(identity) {
|
|
89
|
+
await stopKeepalive(identity);
|
|
90
|
+
launchctl(["bootout", `${domain()}/${identity.label}`], true);
|
|
91
|
+
},
|
|
92
|
+
status(identity) {
|
|
93
|
+
const registered = existsSync(plist(identity));
|
|
94
|
+
const print = launchctl(["print", `${domain()}/${identity.label}`], true);
|
|
95
|
+
const state = /^\s*state = (\S+)/m.exec(print.stdout)?.[1] ?? null;
|
|
96
|
+
const pid = /^\s*pid = (\d+)/m.exec(print.stdout)?.[1];
|
|
97
|
+
return { registered, loaded: print.ok, state, pid: pid ? Number(pid) : null };
|
|
98
|
+
},
|
|
99
|
+
async uninstall(identity) {
|
|
100
|
+
await stopKeepalive(identity);
|
|
101
|
+
launchctl(["bootout", `${domain()}/${identity.label}`], true);
|
|
102
|
+
rmSync(plist(identity), { force: true });
|
|
103
|
+
},
|
|
104
|
+
};
|
|
105
|
+
//# sourceMappingURL=launchd.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"launchd.js","sourceRoot":"","sources":["../../src/service/launchd.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { launchdOwner } from './launchd.js';
|
|
2
|
+
import { systemdOwner } from './systemd.js';
|
|
3
|
+
import { windowsOwner } from './windows.js';
|
|
4
|
+
import { portableOwner } from './portable.js';
|
|
5
|
+
const OWNERS = {
|
|
6
|
+
launchd: launchdOwner,
|
|
7
|
+
systemd: systemdOwner,
|
|
8
|
+
windows: windowsOwner,
|
|
9
|
+
portable: portableOwner,
|
|
10
|
+
};
|
|
11
|
+
export function ownerFor(backend) {
|
|
12
|
+
return OWNERS[backend];
|
|
13
|
+
}
|
|
14
|
+
//# sourceMappingURL=owner.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"owner.js","sourceRoot":"","sources":["../../src/service/owner.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,YAAY,EAAE,MAAM,cAAc,CAAC;AAC5C,OAAO,EAAE,YAAY,EAAE,MAAM,cAAc,CAAC;AAC5C,OAAO,EAAE,YAAY,EAAE,MAAM,cAAc,CAAC;AAC5C,OAAO,EAAE,aAAa,EAAE,MAAM,eAAe,CAAC;AAE9C,MAAM,MAAM,GAA4C;IACtD,OAAO,EAAE,YAAY;IACrB,OAAO,EAAE,YAAY;IACrB,OAAO,EAAE,YAAY;IACrB,QAAQ,EAAE,aAAa;CACxB,CAAC;AAEF,MAAM,UAAU,QAAQ,CAAC,OAAgB;IACvC,OAAO,MAAM,CAAC,OAAO,CAAC,CAAC;AACzB,CAAC"}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Portable: no OS owner. Containers and hosts without a user service
|
|
3
|
+
* manager run the keepalive themselves as the foreground workload — the
|
|
4
|
+
* Daytona lesson: be the declared process, never a detached one. Install
|
|
5
|
+
* only records the service; start prints the command to run.
|
|
6
|
+
*/
|
|
7
|
+
import type { ServiceIdentity } from "./registration.js";
|
|
8
|
+
import type { ServiceOwner } from "./adapter.js";
|
|
9
|
+
export declare function portableEntrypoint(identity: ServiceIdentity, keepaliveArgs: readonly string[]): string;
|
|
10
|
+
export declare const portableOwner: ServiceOwner;
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Portable: no OS owner. Containers and hosts without a user service
|
|
3
|
+
* manager run the keepalive themselves as the foreground workload — the
|
|
4
|
+
* Daytona lesson: be the declared process, never a detached one. Install
|
|
5
|
+
* only records the service; start prints the command to run.
|
|
6
|
+
*/
|
|
7
|
+
import { keepaliveStatus, stopKeepalive } from "./control.js";
|
|
8
|
+
import { shellQuote } from "@amalgm/core/supervision";
|
|
9
|
+
export function portableEntrypoint(identity, keepaliveArgs) {
|
|
10
|
+
return ["amalgm", ...keepaliveArgs].map(shellQuote).join(" ");
|
|
11
|
+
}
|
|
12
|
+
export const portableOwner = {
|
|
13
|
+
backend: "portable",
|
|
14
|
+
files: () => ({}),
|
|
15
|
+
install() {
|
|
16
|
+
// Nothing to write: the registration record is the whole install.
|
|
17
|
+
},
|
|
18
|
+
start(identity, registration) {
|
|
19
|
+
throw new Error(`Portable mode has no OS owner. Run this in the foreground as your entrypoint:\n ${portableEntrypoint(identity, registration.keepalive_args)}`);
|
|
20
|
+
},
|
|
21
|
+
async stop(identity) {
|
|
22
|
+
await stopKeepalive(identity);
|
|
23
|
+
},
|
|
24
|
+
async status(identity) {
|
|
25
|
+
const keepalive = await keepaliveStatus(identity);
|
|
26
|
+
return { registered: true, loaded: null, state: keepalive?.alive ? "running" : "stopped", pid: keepalive?.alive ? keepalive.pid : null };
|
|
27
|
+
},
|
|
28
|
+
async uninstall(identity) {
|
|
29
|
+
await stopKeepalive(identity);
|
|
30
|
+
},
|
|
31
|
+
};
|
|
32
|
+
//# sourceMappingURL=portable.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"portable.js","sourceRoot":"","sources":["../../src/service/portable.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EAAE,eAAe,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AAC9D,OAAO,EAAE,UAAU,EAAE,MAAM,0BAA0B,CAAC;AAItD,MAAM,UAAU,kBAAkB,CAAC,QAAyB,EAAE,aAAgC;IAC5F,OAAO,CAAC,QAAQ,EAAE,GAAG,aAAa,CAAC,CAAC,GAAG,CAAC,UAAU,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;AAChE,CAAC;AAED,MAAM,CAAC,MAAM,aAAa,GAAiB;IACzC,OAAO,EAAE,UAAU;IACnB,KAAK,EAAE,GAAG,EAAE,CAAC,CAAC,EAAE,CAAC;IACjB,OAAO;QACL,kEAAkE;IACpE,CAAC;IACD,KAAK,CAAC,QAAQ,EAAE,YAAY;QAC1B,MAAM,IAAI,KAAK,CACb,oFAAoF,kBAAkB,CAAC,QAAQ,EAAE,YAAY,CAAC,cAAc,CAAC,EAAE,CAChJ,CAAC;IACJ,CAAC;IACD,KAAK,CAAC,IAAI,CAAC,QAAQ;QACjB,MAAM,aAAa,CAAC,QAAQ,CAAC,CAAC;IAChC,CAAC;IACD,KAAK,CAAC,MAAM,CAAC,QAAQ;QACnB,MAAM,SAAS,GAAG,MAAM,eAAe,CAAC,QAAQ,CAAC,CAAC;QAClD,OAAO,EAAE,UAAU,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,KAAK,EAAE,SAAS,EAAE,KAAK,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,SAAS,EAAE,GAAG,EAAE,SAAS,EAAE,KAAK,CAAC,CAAC,CAAC,SAAS,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC;IAC3I,CAAC;IACD,KAAK,CAAC,SAAS,CAAC,QAAQ;QACtB,MAAM,aAAa,CAAC,QAAQ,CAAC,CAAC;IAChC,CAAC;CACF,CAAC"}
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
import { type RuntimeBinding } from "@amalgm/core/identity";
|
|
2
|
+
import { type ServiceState } from "@amalgm/core/supervision";
|
|
3
|
+
/** The OS owners. `portable` is "no OS owner: you run the keepalive in the foreground". */
|
|
4
|
+
export type Backend = "launchd" | "systemd" | "windows" | "portable";
|
|
5
|
+
/** What selects one service: one user, one runtime label, one Amalgm root. */
|
|
6
|
+
export interface ServiceSelection {
|
|
7
|
+
readonly user: string;
|
|
8
|
+
readonly runtimeLabel?: string | undefined;
|
|
9
|
+
readonly branch?: string | undefined;
|
|
10
|
+
readonly localSession?: string | undefined;
|
|
11
|
+
readonly home?: string | undefined;
|
|
12
|
+
readonly amalgmRoot?: string | undefined;
|
|
13
|
+
}
|
|
14
|
+
export interface ServiceIdentity extends ServiceSelection {
|
|
15
|
+
readonly binding: RuntimeBinding;
|
|
16
|
+
readonly label: string;
|
|
17
|
+
readonly labelScope: string;
|
|
18
|
+
readonly home: string;
|
|
19
|
+
readonly amalgmRoot: string;
|
|
20
|
+
/** `<amalgmRoot>/.amalgm/service/<label>`: registration, keepalive state, logs. */
|
|
21
|
+
readonly serviceDir: string;
|
|
22
|
+
}
|
|
23
|
+
/** The Node and Shell entry the service runs; both are absolute and version-stable. */
|
|
24
|
+
export interface Launcher {
|
|
25
|
+
readonly node: string;
|
|
26
|
+
readonly bin: string;
|
|
27
|
+
readonly packageRoot: string;
|
|
28
|
+
readonly packageVersion: string;
|
|
29
|
+
}
|
|
30
|
+
/** The registration record, shaped as Core's `ServiceState` plus what to run. */
|
|
31
|
+
export interface Registration extends ServiceState {
|
|
32
|
+
readonly binding: RuntimeBinding;
|
|
33
|
+
readonly user: string;
|
|
34
|
+
readonly amalgm_root: string;
|
|
35
|
+
readonly backend: Backend;
|
|
36
|
+
readonly label: string;
|
|
37
|
+
readonly package_root: string;
|
|
38
|
+
readonly package_version: string;
|
|
39
|
+
readonly node: string;
|
|
40
|
+
readonly bin: string;
|
|
41
|
+
readonly runtime_host: "node";
|
|
42
|
+
readonly local_only: false;
|
|
43
|
+
readonly files: Readonly<Record<string, string>>;
|
|
44
|
+
/** Selects the service dir: passed to `amalgm service keepalive`. */
|
|
45
|
+
readonly keepalive_args: readonly string[];
|
|
46
|
+
/** Selects the runtime: passed to `amalgm run` by the keepalive. */
|
|
47
|
+
readonly run_args: readonly string[];
|
|
48
|
+
readonly installed_at: string;
|
|
49
|
+
}
|
|
50
|
+
export declare function serviceIdentity(selection: ServiceSelection): ServiceIdentity;
|
|
51
|
+
/**
|
|
52
|
+
* The launcher that is running right now. A desktop bundle may not own the
|
|
53
|
+
* service: registering the bundled copy is the auto-update loop (npm
|
|
54
|
+
* updates one tree while the OS reboots the frozen bundle forever), and an
|
|
55
|
+
* Electron binary posing as Node is not the Node the unit must name.
|
|
56
|
+
*/
|
|
57
|
+
export declare function currentLauncher(facts?: {
|
|
58
|
+
readonly execPath?: string;
|
|
59
|
+
readonly electron?: boolean;
|
|
60
|
+
}): Launcher;
|
|
61
|
+
/**
|
|
62
|
+
* Which owner keeps the service alive. `auto` asks the platform: launchd on
|
|
63
|
+
* macOS, Task Scheduler on Windows, the user's systemd where it answers,
|
|
64
|
+
* and otherwise portable. An explicit mode must fit the platform.
|
|
65
|
+
*/
|
|
66
|
+
export declare function detectBackend(mode: unknown, facts: {
|
|
67
|
+
readonly platform: string;
|
|
68
|
+
readonly userSystemd: () => boolean;
|
|
69
|
+
}): Backend;
|
|
70
|
+
export declare function registrationFile(identity: ServiceIdentity): string;
|
|
71
|
+
export declare function readRegistration(identity: ServiceIdentity): Registration | null;
|
|
72
|
+
export declare function writeRegistration(identity: ServiceIdentity, registration: Registration): void;
|
|
73
|
+
/**
|
|
74
|
+
* An existing registration already says everything this install would say:
|
|
75
|
+
* same package tree and version, same owner, same arguments, files present.
|
|
76
|
+
* Re-running install then leaves the running service alone.
|
|
77
|
+
*/
|
|
78
|
+
export declare function registrationSatisfies(existing: Registration | null, wanted: {
|
|
79
|
+
readonly launcher: Launcher;
|
|
80
|
+
readonly backend: Backend;
|
|
81
|
+
readonly run_args: readonly string[];
|
|
82
|
+
}): boolean;
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
import { createHash } from 'node:crypto';
|
|
2
|
+
/**
|
|
3
|
+
* The service drawer: who this service is (its label), where its files live,
|
|
4
|
+
* which launcher installed it, and which OS owner keeps it alive.
|
|
5
|
+
*
|
|
6
|
+
* Core states the law — the label (`serviceLabel`, `serviceLabelScope`), the
|
|
7
|
+
* launcher-artifact paths, the registration shape (`ServiceState`), and the
|
|
8
|
+
* decisions over it (`stateMatchesRequest`, `normalizeMode`,
|
|
9
|
+
* `pathLooksLikeDesktopBundle`). This file only reads the machine and writes
|
|
10
|
+
* one JSON record; it never loads the runtime.
|
|
11
|
+
*/
|
|
12
|
+
import { existsSync, readFileSync } from "node:fs";
|
|
13
|
+
import { homedir } from "node:os";
|
|
14
|
+
import { join, resolve } from "node:path";
|
|
15
|
+
import { fileURLToPath } from "node:url";
|
|
16
|
+
import { USER_CONTROL_DIR_NAME, resolveRuntimeBinding, runtimeBindingsMatch, runtimeRootSegments } from "@amalgm/core/identity";
|
|
17
|
+
import { normalizeMode, pathLooksLikeDesktopBundle, serviceLabel, stateMatchesRequest, } from "@amalgm/core/supervision";
|
|
18
|
+
import { atomicJson, readJson } from "../filesystem.js";
|
|
19
|
+
export function serviceIdentity(selection) {
|
|
20
|
+
const user = selection.user.trim().toLowerCase();
|
|
21
|
+
const binding = resolveRuntimeBinding(selection);
|
|
22
|
+
if (!user)
|
|
23
|
+
throw new Error("A service is registered for one user: pass --user EMAIL");
|
|
24
|
+
const home = selection.home ? resolve(selection.home) : homedir();
|
|
25
|
+
const amalgmRoot = selection.amalgmRoot ? resolve(selection.amalgmRoot) : join(home, ...runtimeRootSegments(binding));
|
|
26
|
+
const labelScope = `${binding.runtimeLabel}.${createHash("sha256").update(JSON.stringify([user, binding])).digest("hex").slice(0, 32)}`;
|
|
27
|
+
const label = serviceLabel(labelScope);
|
|
28
|
+
return {
|
|
29
|
+
...selection,
|
|
30
|
+
...binding,
|
|
31
|
+
binding,
|
|
32
|
+
user,
|
|
33
|
+
home,
|
|
34
|
+
amalgmRoot,
|
|
35
|
+
labelScope,
|
|
36
|
+
label,
|
|
37
|
+
serviceDir: join(amalgmRoot, USER_CONTROL_DIR_NAME, "service", label),
|
|
38
|
+
};
|
|
39
|
+
}
|
|
40
|
+
const PACKAGE_ROOT = fileURLToPath(new URL("../../", import.meta.url)).replace(/[\\/]+$/, "");
|
|
41
|
+
const MAIN_ENTRY = fileURLToPath(new URL("../main.js", import.meta.url));
|
|
42
|
+
/**
|
|
43
|
+
* The launcher that is running right now. A desktop bundle may not own the
|
|
44
|
+
* service: registering the bundled copy is the auto-update loop (npm
|
|
45
|
+
* updates one tree while the OS reboots the frozen bundle forever), and an
|
|
46
|
+
* Electron binary posing as Node is not the Node the unit must name.
|
|
47
|
+
*/
|
|
48
|
+
export function currentLauncher(facts = {}) {
|
|
49
|
+
const node = facts.execPath ?? process.execPath;
|
|
50
|
+
const electron = facts.electron ?? Boolean(process.versions.electron);
|
|
51
|
+
if (electron || pathLooksLikeDesktopBundle(node) || pathLooksLikeDesktopBundle(MAIN_ENTRY)) {
|
|
52
|
+
throw new Error("The always-on service must be installed from the npm package (npm install -g @amalgm/shell); "
|
|
53
|
+
+ "a desktop bundle cannot own it");
|
|
54
|
+
}
|
|
55
|
+
const packageJson = JSON.parse(readFileSync(join(PACKAGE_ROOT, "package.json"), "utf8"));
|
|
56
|
+
return { node, bin: MAIN_ENTRY, packageRoot: PACKAGE_ROOT, packageVersion: String(packageJson.version) };
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* Which owner keeps the service alive. `auto` asks the platform: launchd on
|
|
60
|
+
* macOS, Task Scheduler on Windows, the user's systemd where it answers,
|
|
61
|
+
* and otherwise portable. An explicit mode must fit the platform.
|
|
62
|
+
*/
|
|
63
|
+
export function detectBackend(mode, facts) {
|
|
64
|
+
const requested = normalizeMode(mode);
|
|
65
|
+
const native = facts.platform === "darwin" ? "launchd"
|
|
66
|
+
: facts.platform === "win32" ? "windows"
|
|
67
|
+
: facts.userSystemd() ? "systemd"
|
|
68
|
+
: "portable";
|
|
69
|
+
if (requested === "auto")
|
|
70
|
+
return native;
|
|
71
|
+
if (requested === "portable")
|
|
72
|
+
return "portable";
|
|
73
|
+
if ((requested === "launchd") !== (facts.platform === "darwin")
|
|
74
|
+
|| (requested === "windows") !== (facts.platform === "win32")
|
|
75
|
+
|| (requested === "systemd" && native !== "systemd")) {
|
|
76
|
+
throw new Error(`Service mode "${requested}" is not available on this machine (auto would choose ${native})`);
|
|
77
|
+
}
|
|
78
|
+
return requested;
|
|
79
|
+
}
|
|
80
|
+
export function registrationFile(identity) {
|
|
81
|
+
return join(identity.serviceDir, "registration.json");
|
|
82
|
+
}
|
|
83
|
+
export function readRegistration(identity) {
|
|
84
|
+
const record = readJson(registrationFile(identity));
|
|
85
|
+
if (!record)
|
|
86
|
+
return null;
|
|
87
|
+
if (record.label !== identity.label || typeof record.bin !== "string"
|
|
88
|
+
|| record.user !== identity.user || record.amalgm_root !== identity.amalgmRoot
|
|
89
|
+
|| !runtimeBindingsMatch(record.binding, identity.binding)) {
|
|
90
|
+
throw new Error('The service registration belongs to a different installation.');
|
|
91
|
+
}
|
|
92
|
+
return record;
|
|
93
|
+
}
|
|
94
|
+
export function writeRegistration(identity, registration) {
|
|
95
|
+
atomicJson(registrationFile(identity), registration);
|
|
96
|
+
}
|
|
97
|
+
/**
|
|
98
|
+
* An existing registration already says everything this install would say:
|
|
99
|
+
* same package tree and version, same owner, same arguments, files present.
|
|
100
|
+
* Re-running install then leaves the running service alone.
|
|
101
|
+
*/
|
|
102
|
+
export function registrationSatisfies(existing, wanted) {
|
|
103
|
+
return existing !== null
|
|
104
|
+
&& stateMatchesRequest(existing, { mode: wanted.backend }, {
|
|
105
|
+
packageRoot: wanted.launcher.packageRoot,
|
|
106
|
+
packageVersion: wanted.launcher.packageVersion,
|
|
107
|
+
autoBackend: wanted.backend,
|
|
108
|
+
})
|
|
109
|
+
&& existing.node === wanted.launcher.node
|
|
110
|
+
&& existing.bin === wanted.launcher.bin
|
|
111
|
+
&& JSON.stringify(existing.run_args) === JSON.stringify(wanted.run_args)
|
|
112
|
+
&& Object.values(existing.files).every((file) => existsSync(file));
|
|
113
|
+
}
|
|
114
|
+
//# sourceMappingURL=registration.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"registration.js","sourceRoot":"","sources":["../../src/service/registration.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AACzC;;;;;;;;;GASG;AAEH,OAAO,EAAE,UAAU,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AACnD,OAAO,EAAE,OAAO,EAAE,MAAM,SAAS,CAAC;AAClC,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAC1C,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AAEzC,OAAO,EAAE,qBAAqB,EAAE,qBAAqB,EAAE,oBAAoB,EAAE,mBAAmB,EAAuB,MAAM,uBAAuB,CAAC;AACrJ,OAAO,EACL,aAAa,EACb,0BAA0B,EAC1B,YAAY,EACZ,mBAAmB,GAEpB,MAAM,0BAA0B,CAAC;AAElC,OAAO,EAAE,UAAU,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AAsDxD,MAAM,UAAU,eAAe,CAAC,SAA2B;IACzD,MAAM,IAAI,GAAG,SAAS,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC;IACjD,MAAM,OAAO,GAAG,qBAAqB,CAAC,SAAS,CAAC,CAAC;IACjD,IAAI,CAAC,IAAI;QAAE,MAAM,IAAI,KAAK,CAAC,yDAAyD,CAAC,CAAC;IACtF,MAAM,IAAI,GAAG,SAAS,CAAC,IAAI,CAAC,CAAC,CAAC,OAAO,CAAC,SAAS,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,OAAO,EAAE,CAAC;IAClE,MAAM,UAAU,GAAG,SAAS,CAAC,UAAU,CAAC,CAAC,CAAC,OAAO,CAAC,SAAS,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,EAAE,GAAG,mBAAmB,CAAC,OAAO,CAAC,CAAC,CAAC;IACtH,MAAM,UAAU,GAAG,GAAG,OAAO,CAAC,YAAY,IAAI,UAAU,CAAC,QAAQ,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,EAAE,CAAC;IACxI,MAAM,KAAK,GAAG,YAAY,CAAC,UAAU,CAAC,CAAC;IACvC,OAAO;QACL,GAAG,SAAS;QACZ,GAAG,OAAO;QACV,OAAO;QACP,IAAI;QACJ,IAAI;QACJ,UAAU;QACV,UAAU;QACV,KAAK;QACL,UAAU,EAAE,IAAI,CAAC,UAAU,EAAE,qBAAqB,EAAE,SAAS,EAAE,KAAK,CAAC;KACtE,CAAC;AACJ,CAAC;AAED,MAAM,YAAY,GAAG,aAAa,CAAC,IAAI,GAAG,CAAC,QAAQ,EAAE,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,OAAO,CAAC,SAAS,EAAE,EAAE,CAAC,CAAC;AAC9F,MAAM,UAAU,GAAG,aAAa,CAAC,IAAI,GAAG,CAAC,YAAY,EAAE,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC;AAEzE;;;;;GAKG;AACH,MAAM,UAAU,eAAe,CAAC,QAG5B,EAAE;IACJ,MAAM,IAAI,GAAG,KAAK,CAAC,QAAQ,IAAI,OAAO,CAAC,QAAQ,CAAC;IAChD,MAAM,QAAQ,GAAG,KAAK,CAAC,QAAQ,IAAI,OAAO,CAAC,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAC;IACtE,IAAI,QAAQ,IAAI,0BAA0B,CAAC,IAAI,CAAC,IAAI,0BAA0B,CAAC,UAAU,CAAC,EAAE,CAAC;QAC3F,MAAM,IAAI,KAAK,CACb,+FAA+F;cAC7F,gCAAgC,CACnC,CAAC;IACJ,CAAC;IACD,MAAM,WAAW,GAAG,IAAI,CAAC,KAAK,CAAC,YAAY,CAAC,IAAI,CAAC,YAAY,EAAE,cAAc,CAAC,EAAE,MAAM,CAAC,CAAwB,CAAC;IAChH,OAAO,EAAE,IAAI,EAAE,GAAG,EAAE,UAAU,EAAE,WAAW,EAAE,YAAY,EAAE,cAAc,EAAE,MAAM,CAAC,WAAW,CAAC,OAAO,CAAC,EAAE,CAAC;AAC3G,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,aAAa,CAC3B,IAAa,EACb,KAAyE;IAEzE,MAAM,SAAS,GAAG,aAAa,CAAC,IAAI,CAAC,CAAC;IACtC,MAAM,MAAM,GAAY,KAAK,CAAC,QAAQ,KAAK,QAAQ,CAAC,CAAC,CAAC,SAAS;QAC7D,CAAC,CAAC,KAAK,CAAC,QAAQ,KAAK,OAAO,CAAC,CAAC,CAAC,SAAS;YACxC,CAAC,CAAC,KAAK,CAAC,WAAW,EAAE,CAAC,CAAC,CAAC,SAAS;gBACjC,CAAC,CAAC,UAAU,CAAC;IACf,IAAI,SAAS,KAAK,MAAM;QAAE,OAAO,MAAM,CAAC;IACxC,IAAI,SAAS,KAAK,UAAU;QAAE,OAAO,UAAU,CAAC;IAChD,IAAI,CAAC,SAAS,KAAK,SAAS,CAAC,KAAK,CAAC,KAAK,CAAC,QAAQ,KAAK,QAAQ,CAAC;WAC1D,CAAC,SAAS,KAAK,SAAS,CAAC,KAAK,CAAC,KAAK,CAAC,QAAQ,KAAK,OAAO,CAAC;WAC1D,CAAC,SAAS,KAAK,SAAS,IAAI,MAAM,KAAK,SAAS,CAAC,EAAE,CAAC;QACvD,MAAM,IAAI,KAAK,CAAC,iBAAiB,SAAS,yDAAyD,MAAM,GAAG,CAAC,CAAC;IAChH,CAAC;IACD,OAAO,SAAS,CAAC;AACnB,CAAC;AAED,MAAM,UAAU,gBAAgB,CAAC,QAAyB;IACxD,OAAO,IAAI,CAAC,QAAQ,CAAC,UAAU,EAAE,mBAAmB,CAAC,CAAC;AACxD,CAAC;AAED,MAAM,UAAU,gBAAgB,CAAC,QAAyB;IACxD,MAAM,MAAM,GAAG,QAAQ,CAAe,gBAAgB,CAAC,QAAQ,CAAC,CAAC,CAAC;IAClE,IAAI,CAAC,MAAM;QAAE,OAAO,IAAI,CAAC;IACzB,IAAI,MAAM,CAAC,KAAK,KAAK,QAAQ,CAAC,KAAK,IAAI,OAAO,MAAM,CAAC,GAAG,KAAK,QAAQ;WAChE,MAAM,CAAC,IAAI,KAAK,QAAQ,CAAC,IAAI,IAAI,MAAM,CAAC,WAAW,KAAK,QAAQ,CAAC,UAAU;WAC3E,CAAC,oBAAoB,CAAC,MAAM,CAAC,OAAO,EAAE,QAAQ,CAAC,OAAO,CAAC,EAAE,CAAC;QAC7D,MAAM,IAAI,KAAK,CAAC,+DAA+D,CAAC,CAAC;IACnF,CAAC;IACD,OAAO,MAAM,CAAC;AAChB,CAAC;AAED,MAAM,UAAU,iBAAiB,CAAC,QAAyB,EAAE,YAA0B;IACrF,UAAU,CAAC,gBAAgB,CAAC,QAAQ,CAAC,EAAE,YAAY,CAAC,CAAC;AACvD,CAAC;AAGD;;;;GAIG;AACH,MAAM,UAAU,qBAAqB,CACnC,QAA6B,EAC7B,MAAwG;IAExG,OAAO,QAAQ,KAAK,IAAI;WACnB,mBAAmB,CAAC,QAAQ,EAAE,EAAE,IAAI,EAAE,MAAM,CAAC,OAAO,EAAE,EAAE;YACzD,WAAW,EAAE,MAAM,CAAC,QAAQ,CAAC,WAAW;YACxC,cAAc,EAAE,MAAM,CAAC,QAAQ,CAAC,cAAc;YAC9C,WAAW,EAAE,MAAM,CAAC,OAAO;SAC5B,CAAC;WACC,QAAQ,CAAC,IAAI,KAAK,MAAM,CAAC,QAAQ,CAAC,IAAI;WACtC,QAAQ,CAAC,GAAG,KAAK,MAAM,CAAC,QAAQ,CAAC,GAAG;WACpC,IAAI,CAAC,SAAS,CAAC,QAAQ,CAAC,QAAQ,CAAC,KAAK,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC,QAAQ,CAAC;WACrE,MAAM,CAAC,MAAM,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC,KAAK,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,UAAU,CAAC,IAAI,CAAC,CAAC,CAAC;AACvE,CAAC"}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { type ServiceOwner } from "./adapter.js";
|
|
2
|
+
export declare function renderSystemdUnit(input: {
|
|
3
|
+
readonly command: readonly string[];
|
|
4
|
+
readonly environment: Readonly<Record<string, string>>;
|
|
5
|
+
readonly workingDirectory: string;
|
|
6
|
+
}): string;
|
|
7
|
+
/** Does this login have a user manager to talk to? Containers and bare SSH often do not. */
|
|
8
|
+
export declare function userSystemdAvailable(): boolean;
|
|
9
|
+
export declare const systemdOwner: ServiceOwner;
|