terminay 4.2.0
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/dist/account.d.ts +31 -0
- package/dist/account.js +150 -0
- package/dist/account.js.map +1 -0
- package/dist/address.d.ts +14 -0
- package/dist/address.js +58 -0
- package/dist/address.js.map +1 -0
- package/dist/args.d.ts +37 -0
- package/dist/args.js +252 -0
- package/dist/args.js.map +1 -0
- package/dist/cli.d.ts +2 -0
- package/dist/cli.js +92 -0
- package/dist/cli.js.map +1 -0
- package/dist/commands/install.d.ts +48 -0
- package/dist/commands/install.js +213 -0
- package/dist/commands/install.js.map +1 -0
- package/dist/commands/lifecycle.d.ts +23 -0
- package/dist/commands/lifecycle.js +66 -0
- package/dist/commands/lifecycle.js.map +1 -0
- package/dist/commands/pairing.d.ts +23 -0
- package/dist/commands/pairing.js +143 -0
- package/dist/commands/pairing.js.map +1 -0
- package/dist/commands/uninstall.d.ts +26 -0
- package/dist/commands/uninstall.js +70 -0
- package/dist/commands/uninstall.js.map +1 -0
- package/dist/commands/upgrade.d.ts +30 -0
- package/dist/commands/upgrade.js +175 -0
- package/dist/commands/upgrade.js.map +1 -0
- package/dist/context.d.ts +26 -0
- package/dist/context.js +37 -0
- package/dist/context.js.map +1 -0
- package/dist/download.d.ts +11 -0
- package/dist/download.js +95 -0
- package/dist/download.js.map +1 -0
- package/dist/health.d.ts +18 -0
- package/dist/health.js +61 -0
- package/dist/health.js.map +1 -0
- package/dist/http.d.ts +11 -0
- package/dist/http.js +88 -0
- package/dist/http.js.map +1 -0
- package/dist/index.d.ts +12 -0
- package/dist/index.js +11 -0
- package/dist/index.js.map +1 -0
- package/dist/install.d.ts +30 -0
- package/dist/install.js +100 -0
- package/dist/install.js.map +1 -0
- package/dist/layout.d.ts +52 -0
- package/dist/layout.js +96 -0
- package/dist/layout.js.map +1 -0
- package/dist/manifest.d.ts +46 -0
- package/dist/manifest.js +115 -0
- package/dist/manifest.js.map +1 -0
- package/dist/platform.d.ts +17 -0
- package/dist/platform.js +34 -0
- package/dist/platform.js.map +1 -0
- package/dist/prompt.d.ts +20 -0
- package/dist/prompt.js +47 -0
- package/dist/prompt.js.map +1 -0
- package/dist/resolve.d.ts +35 -0
- package/dist/resolve.js +194 -0
- package/dist/resolve.js.map +1 -0
- package/dist/socket.d.ts +55 -0
- package/dist/socket.js +98 -0
- package/dist/socket.js.map +1 -0
- package/dist/socketClient.d.ts +1 -0
- package/dist/socketClient.js +22 -0
- package/dist/socketClient.js.map +1 -0
- package/dist/source.d.ts +24 -0
- package/dist/source.js +152 -0
- package/dist/source.js.map +1 -0
- package/dist/systemd.d.ts +30 -0
- package/dist/systemd.js +86 -0
- package/dist/systemd.js.map +1 -0
- package/dist/unit.d.ts +37 -0
- package/dist/unit.js +74 -0
- package/dist/unit.js.map +1 -0
- package/dist/verify.d.ts +24 -0
- package/dist/verify.js +46 -0
- package/dist/verify.js.map +1 -0
- package/package.json +49 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"pairing.js","sourceRoot":"","sources":["../../src/commands/pairing.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import type { DaemonOptions } from '../args.js';
|
|
2
|
+
import type { CommandContext } from '../context.js';
|
|
3
|
+
import { type PromptStreams } from '../prompt.js';
|
|
4
|
+
/**
|
|
5
|
+
* `daemon uninstall` — remove the service, keep the data.
|
|
6
|
+
*
|
|
7
|
+
* The data root holds the host key, the paired device records, and whatever
|
|
8
|
+
* the operator's projects left behind. Removing it means every paired device
|
|
9
|
+
* has to pair again and anything not backed up is gone, so it survives an
|
|
10
|
+
* uninstall unless `--purge` asks for it and the operator confirms.
|
|
11
|
+
*/
|
|
12
|
+
export interface UninstallResult {
|
|
13
|
+
readonly removedVersions: boolean;
|
|
14
|
+
readonly removedDataRoot: boolean;
|
|
15
|
+
}
|
|
16
|
+
export declare class UninstallError extends Error {
|
|
17
|
+
}
|
|
18
|
+
export declare function runUninstall(options: DaemonOptions, context: CommandContext, streams?: PromptStreams): Promise<UninstallResult>;
|
|
19
|
+
/**
|
|
20
|
+
* `daemon reset-identity` — rotate the host key and revoke every device.
|
|
21
|
+
*
|
|
22
|
+
* The server owns the rotation; the CLI only wraps it in the stop and start it
|
|
23
|
+
* requires. Confirmation is not politeness here: every paired device loses
|
|
24
|
+
* access the moment this completes.
|
|
25
|
+
*/
|
|
26
|
+
export declare function runResetIdentity(options: DaemonOptions, context: CommandContext, streams?: PromptStreams): Promise<boolean>;
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import { rm } from 'node:fs/promises';
|
|
2
|
+
import { confirm, defaultStreams, isInteractive, } from '../prompt.js';
|
|
3
|
+
export class UninstallError extends Error {
|
|
4
|
+
}
|
|
5
|
+
export async function runUninstall(options, context, streams = defaultStreams()) {
|
|
6
|
+
const { layout, record, systemd, write } = context;
|
|
7
|
+
let purge = false;
|
|
8
|
+
if (options.purge) {
|
|
9
|
+
if (options.yes) {
|
|
10
|
+
purge = true;
|
|
11
|
+
}
|
|
12
|
+
else if (isInteractive(streams)) {
|
|
13
|
+
purge = await confirm(`Remove ${record.dataRoot} as well? Every paired device will have to pair again, and anything stored there is gone.`, streams);
|
|
14
|
+
if (!purge)
|
|
15
|
+
write('Keeping the data root.');
|
|
16
|
+
}
|
|
17
|
+
else {
|
|
18
|
+
throw new UninstallError('--purge removes the data root, so it needs --yes when there is no terminal to confirm on.');
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
// Stopped before the unit is removed, or systemd keeps supervising a
|
|
22
|
+
// service whose definition no longer exists.
|
|
23
|
+
await systemd.stop().catch(() => undefined);
|
|
24
|
+
await systemd.disable();
|
|
25
|
+
await rm(layout.unitPath, { force: true });
|
|
26
|
+
await systemd.daemonReload();
|
|
27
|
+
await systemd.resetFailed();
|
|
28
|
+
await rm(layout.versionsDirectory, { recursive: true, force: true });
|
|
29
|
+
await rm(layout.currentLink, { force: true });
|
|
30
|
+
await rm(layout.recordPath, { force: true });
|
|
31
|
+
await rm(layout.environmentFile, { force: true });
|
|
32
|
+
if (purge)
|
|
33
|
+
await rm(record.dataRoot, { recursive: true, force: true });
|
|
34
|
+
write('Terminay Server is uninstalled.');
|
|
35
|
+
write(purge
|
|
36
|
+
? `Removed the data root at ${record.dataRoot}.`
|
|
37
|
+
: `Kept the data root at ${record.dataRoot}.`);
|
|
38
|
+
return Object.freeze({ removedVersions: true, removedDataRoot: purge });
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* `daemon reset-identity` — rotate the host key and revoke every device.
|
|
42
|
+
*
|
|
43
|
+
* The server owns the rotation; the CLI only wraps it in the stop and start it
|
|
44
|
+
* requires. Confirmation is not politeness here: every paired device loses
|
|
45
|
+
* access the moment this completes.
|
|
46
|
+
*/
|
|
47
|
+
export async function runResetIdentity(options, context, streams = defaultStreams()) {
|
|
48
|
+
const { record, systemd, write } = context;
|
|
49
|
+
if (!options.yes) {
|
|
50
|
+
if (!isInteractive(streams)) {
|
|
51
|
+
throw new UninstallError('resetting the identity unpairs every device, so it needs --yes when there is no terminal to confirm on.');
|
|
52
|
+
}
|
|
53
|
+
const confirmed = await confirm("Rotate this server's host key and revoke every paired device? They will all have to pair again.", streams);
|
|
54
|
+
if (!confirmed) {
|
|
55
|
+
write('Left the identity unchanged.');
|
|
56
|
+
return false;
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
await systemd.stop();
|
|
60
|
+
const { execFile } = await import('node:child_process');
|
|
61
|
+
const { promisify } = await import('node:util');
|
|
62
|
+
await promisify(execFile)(`${context.layout.currentLink}/bin/terminay-server`, ['reset-identity'], {
|
|
63
|
+
timeout: 120_000,
|
|
64
|
+
env: { ...process.env, TERMINAY_DATA_ROOT: record.dataRoot },
|
|
65
|
+
});
|
|
66
|
+
await systemd.start();
|
|
67
|
+
write('The host key is rotated and every device is revoked. Pair again with `terminay daemon qr-code`.');
|
|
68
|
+
return true;
|
|
69
|
+
}
|
|
70
|
+
//# sourceMappingURL=uninstall.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"uninstall.js","sourceRoot":"","sources":["../../src/commands/uninstall.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import type { DaemonOptions } from '../args.js';
|
|
2
|
+
import type { CommandContext } from '../context.js';
|
|
3
|
+
/**
|
|
4
|
+
* `daemon upgrade` — follow the installed channel, and never leave the machine
|
|
5
|
+
* without a working server.
|
|
6
|
+
*
|
|
7
|
+
* The sequence is the one the release and update policy names: verify, stage
|
|
8
|
+
* beside the running version, stop, switch, start, wait for readiness, and on
|
|
9
|
+
* failure switch back and start what was working. The data root is never
|
|
10
|
+
* touched, so a rollback restores the previous version against the same state
|
|
11
|
+
* it was already running with.
|
|
12
|
+
*/
|
|
13
|
+
export interface UpgradeDependencies {
|
|
14
|
+
readonly repository?: string;
|
|
15
|
+
readonly apiBase?: string;
|
|
16
|
+
readonly webBase?: string;
|
|
17
|
+
readonly architecture?: 'x64' | 'arm64';
|
|
18
|
+
readonly readinessTimeoutMs?: number;
|
|
19
|
+
/** See `InstallDependencies.releasePublicKeyPem`: a test seam, not a bypass. */
|
|
20
|
+
readonly releasePublicKeyPem?: string;
|
|
21
|
+
}
|
|
22
|
+
export interface UpgradeResult {
|
|
23
|
+
readonly from: string;
|
|
24
|
+
readonly to: string;
|
|
25
|
+
readonly rolledBack: boolean;
|
|
26
|
+
readonly upToDate: boolean;
|
|
27
|
+
}
|
|
28
|
+
export declare class UpgradeError extends Error {
|
|
29
|
+
}
|
|
30
|
+
export declare function runUpgrade(ref: string | undefined, options: DaemonOptions, context: CommandContext, dependencies?: UpgradeDependencies): Promise<UpgradeResult>;
|
|
@@ -0,0 +1,175 @@
|
|
|
1
|
+
import { discardDownloads, downloadAsset, downloadBytes, downloadText, } from '../download.js';
|
|
2
|
+
import { waitForReady } from '../health.js';
|
|
3
|
+
import { activate, activeVersion, installArchive, retain } from '../install.js';
|
|
4
|
+
import { stagedName, writeInstallRecord } from '../layout.js';
|
|
5
|
+
import { hostArchitecture } from '../platform.js';
|
|
6
|
+
import { resolveRef } from '../resolve.js';
|
|
7
|
+
import { buildFromSource } from '../source.js';
|
|
8
|
+
import { verifyArchive } from '../verify.js';
|
|
9
|
+
export class UpgradeError extends Error {
|
|
10
|
+
}
|
|
11
|
+
/** Compare two release versions by semver precedence, ignoring prerelease tags. */
|
|
12
|
+
function compareVersions(left, right) {
|
|
13
|
+
const parse = (value) => value.split(/[-+]/u, 1)[0]?.split('.').map(Number) ?? [];
|
|
14
|
+
const a = parse(left);
|
|
15
|
+
const b = parse(right);
|
|
16
|
+
for (let index = 0; index < 3; index += 1) {
|
|
17
|
+
const difference = (a[index] ?? 0) - (b[index] ?? 0);
|
|
18
|
+
if (difference !== 0)
|
|
19
|
+
return difference < 0 ? -1 : 1;
|
|
20
|
+
}
|
|
21
|
+
return 0;
|
|
22
|
+
}
|
|
23
|
+
export async function runUpgrade(ref, options, context, dependencies = {}) {
|
|
24
|
+
const { layout, record, systemd, write } = context;
|
|
25
|
+
// A source install has no ordering the CLI can trust — a branch tip moves
|
|
26
|
+
// without any version changing — so it must be told what to move to.
|
|
27
|
+
if (ref === undefined && record.channel === 'source') {
|
|
28
|
+
throw new UpgradeError('this server was built from source, which has no channel to follow. Name the branch, commit, or release to upgrade to.');
|
|
29
|
+
}
|
|
30
|
+
const target = ref ?? (record.channel === 'main' ? 'main' : undefined);
|
|
31
|
+
const architecture = dependencies.architecture ?? hostArchitecture();
|
|
32
|
+
const resolved = await resolveRef(target, {
|
|
33
|
+
architecture,
|
|
34
|
+
...(dependencies.repository === undefined
|
|
35
|
+
? {}
|
|
36
|
+
: { repository: dependencies.repository }),
|
|
37
|
+
...(dependencies.apiBase === undefined
|
|
38
|
+
? {}
|
|
39
|
+
: { apiBase: dependencies.apiBase }),
|
|
40
|
+
...(dependencies.webBase === undefined
|
|
41
|
+
? {}
|
|
42
|
+
: { webBase: dependencies.webBase }),
|
|
43
|
+
});
|
|
44
|
+
if (resolved.channel === 'tag' && record.channel === 'tag') {
|
|
45
|
+
const order = compareVersions(resolved.version, record.version);
|
|
46
|
+
if (order === 0) {
|
|
47
|
+
write(`Already on ${record.version}. Nothing to do.`);
|
|
48
|
+
return Object.freeze({
|
|
49
|
+
from: record.version,
|
|
50
|
+
to: record.version,
|
|
51
|
+
rolledBack: false,
|
|
52
|
+
upToDate: true,
|
|
53
|
+
});
|
|
54
|
+
}
|
|
55
|
+
if (order < 0 && !options.allowDowngrade) {
|
|
56
|
+
throw new UpgradeError(`refusing to move from ${record.version} down to ${resolved.version}. Pass --allow-downgrade if that is what you want.`);
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
if (resolved.channel === 'main' && record.channel === 'main') {
|
|
60
|
+
// The rolling channel reuses one version string, so the published time
|
|
61
|
+
// is the only ordering it has.
|
|
62
|
+
const published = resolved.publishedAt;
|
|
63
|
+
if (published !== undefined && record.publishedAt !== undefined) {
|
|
64
|
+
if (Date.parse(published) < Date.parse(record.publishedAt) &&
|
|
65
|
+
!options.allowDowngrade) {
|
|
66
|
+
throw new UpgradeError(`the ${resolved.channel} release published at ${published} is older than the installed one from ${record.publishedAt}. Pass --allow-downgrade to install it anyway.`);
|
|
67
|
+
}
|
|
68
|
+
if (Date.parse(published) === Date.parse(record.publishedAt)) {
|
|
69
|
+
write(`Already on the newest ${record.channel} build. Nothing to do.`);
|
|
70
|
+
return Object.freeze({
|
|
71
|
+
from: record.version,
|
|
72
|
+
to: record.version,
|
|
73
|
+
rolledBack: false,
|
|
74
|
+
upToDate: true,
|
|
75
|
+
});
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
const previous = (await activeVersion(layout)) ??
|
|
80
|
+
stagedName(record.channel, record.version, record.revision);
|
|
81
|
+
// Staged beside the running version: nothing below this point has stopped
|
|
82
|
+
// the service yet.
|
|
83
|
+
let archivePath;
|
|
84
|
+
if (resolved.channel === 'source') {
|
|
85
|
+
write(`Building ${resolved.sourceRef ?? resolved.version} from source. This takes a while.`);
|
|
86
|
+
const built = await buildFromSource({
|
|
87
|
+
ref: resolved.sourceRef ?? resolved.version,
|
|
88
|
+
...(resolved.revision === undefined
|
|
89
|
+
? {}
|
|
90
|
+
: { revision: resolved.revision }),
|
|
91
|
+
destination: layout.prefix,
|
|
92
|
+
write,
|
|
93
|
+
});
|
|
94
|
+
archivePath = built.archivePath;
|
|
95
|
+
}
|
|
96
|
+
else {
|
|
97
|
+
const assets = resolved.assets;
|
|
98
|
+
if (assets === undefined)
|
|
99
|
+
throw new UpgradeError('the resolved release names no archive to download');
|
|
100
|
+
write('Downloading and verifying …');
|
|
101
|
+
const asset = await downloadAsset(assets.archive, layout.prefix);
|
|
102
|
+
const [sidecar, signature] = await Promise.all([
|
|
103
|
+
downloadText(assets.sha256),
|
|
104
|
+
downloadBytes(assets.signature),
|
|
105
|
+
]);
|
|
106
|
+
await verifyArchive({
|
|
107
|
+
archivePath: asset.path,
|
|
108
|
+
sidecar,
|
|
109
|
+
signature,
|
|
110
|
+
digest: asset.sha256,
|
|
111
|
+
...(dependencies.releasePublicKeyPem === undefined
|
|
112
|
+
? {}
|
|
113
|
+
: { publicKeyPem: dependencies.releasePublicKeyPem }),
|
|
114
|
+
});
|
|
115
|
+
archivePath = asset.path;
|
|
116
|
+
}
|
|
117
|
+
const installed = await installArchive({
|
|
118
|
+
layout,
|
|
119
|
+
archivePath,
|
|
120
|
+
channel: resolved.channel,
|
|
121
|
+
expected: {
|
|
122
|
+
channel: resolved.channel,
|
|
123
|
+
architecture,
|
|
124
|
+
...(resolved.channel === 'tag' ? { version: resolved.version } : {}),
|
|
125
|
+
...(resolved.revision === undefined
|
|
126
|
+
? {}
|
|
127
|
+
: { revision: resolved.revision }),
|
|
128
|
+
},
|
|
129
|
+
});
|
|
130
|
+
await discardDownloads(layout.prefix);
|
|
131
|
+
if (installed.name === previous) {
|
|
132
|
+
write(`Already running ${installed.manifest.version}. Nothing to do.`);
|
|
133
|
+
return Object.freeze({
|
|
134
|
+
from: record.version,
|
|
135
|
+
to: installed.manifest.version,
|
|
136
|
+
rolledBack: false,
|
|
137
|
+
upToDate: true,
|
|
138
|
+
});
|
|
139
|
+
}
|
|
140
|
+
write(`Upgrading ${record.version} → ${installed.manifest.version} …`);
|
|
141
|
+
await systemd.stop();
|
|
142
|
+
await activate(layout, installed.name);
|
|
143
|
+
await systemd.start();
|
|
144
|
+
if ((await waitForReady(record.healthPort, dependencies.readinessTimeoutMs)) ===
|
|
145
|
+
undefined) {
|
|
146
|
+
write('The upgraded server did not report ready. Rolling back.');
|
|
147
|
+
await systemd.stop();
|
|
148
|
+
await activate(layout, previous);
|
|
149
|
+
await systemd.start();
|
|
150
|
+
const recovered = (await waitForReady(record.healthPort, dependencies.readinessTimeoutMs)) !== undefined;
|
|
151
|
+
write(await systemd.journal(20));
|
|
152
|
+
throw new UpgradeError(`the upgrade to ${installed.manifest.version} did not become ready, so ${record.version} was restored and ${recovered ? 'is running again' : 'was started, but has not reported ready either'}.`);
|
|
153
|
+
}
|
|
154
|
+
await writeInstallRecord(layout, {
|
|
155
|
+
...record,
|
|
156
|
+
channel: resolved.channel,
|
|
157
|
+
version: installed.manifest.version,
|
|
158
|
+
revision: installed.manifest.revision,
|
|
159
|
+
...(resolved.publishedAt === undefined
|
|
160
|
+
? {}
|
|
161
|
+
: { publishedAt: resolved.publishedAt }),
|
|
162
|
+
installedAt: new Date().toISOString(),
|
|
163
|
+
});
|
|
164
|
+
const removed = await retain(layout, [installed.name, previous]);
|
|
165
|
+
write(`Upgraded to ${installed.manifest.version}. Kept ${previous} to roll back to.`);
|
|
166
|
+
if (removed.length > 0)
|
|
167
|
+
write(`Removed older versions: ${removed.join(', ')}.`);
|
|
168
|
+
return Object.freeze({
|
|
169
|
+
from: record.version,
|
|
170
|
+
to: installed.manifest.version,
|
|
171
|
+
rolledBack: false,
|
|
172
|
+
upToDate: false,
|
|
173
|
+
});
|
|
174
|
+
}
|
|
175
|
+
//# sourceMappingURL=upgrade.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"upgrade.js","sourceRoot":"","sources":["../../src/commands/upgrade.ts"],"names":[],"mappings":"AAEA,OAAO,EACN,gBAAgB,EAChB,aAAa,EACb,aAAa,EACb,YAAY,GACZ,MAAM,gBAAgB,CAAC;AACxB,OAAO,EAAE,YAAY,EAAE,MAAM,cAAc,CAAC;AAC5C,OAAO,EAAE,QAAQ,EAAE,aAAa,EAAE,cAAc,EAAE,MAAM,EAAE,MAAM,eAAe,CAAC;AAChF,OAAO,EAAE,UAAU,EAAE,kBAAkB,EAAE,MAAM,cAAc,CAAC;AAC9D,OAAO,EAAE,gBAAgB,EAAE,MAAM,gBAAgB,CAAC;AAClD,OAAO,EAAE,UAAU,EAAE,MAAM,eAAe,CAAC;AAC3C,OAAO,EAAE,eAAe,EAAE,MAAM,cAAc,CAAC;AAC/C,OAAO,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AA8B7C,MAAM,OAAO,YAAa,SAAQ,KAAK;CAAG;AAE1C,mFAAmF;AACnF,SAAS,eAAe,CAAC,IAAY,EAAE,KAAa;IACnD,MAAM,KAAK,GAAG,CAAC,KAAa,EAAE,EAAE,CAC/B,KAAK,CAAC,KAAK,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,KAAK,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC,MAAM,CAAC,IAAI,EAAE,CAAC;IAC1D,MAAM,CAAC,GAAG,KAAK,CAAC,IAAI,CAAC,CAAC;IACtB,MAAM,CAAC,GAAG,KAAK,CAAC,KAAK,CAAC,CAAC;IACvB,KAAK,IAAI,KAAK,GAAG,CAAC,EAAE,KAAK,GAAG,CAAC,EAAE,KAAK,IAAI,CAAC,EAAE,CAAC;QAC3C,MAAM,UAAU,GAAG,CAAC,CAAC,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC;QACrD,IAAI,UAAU,KAAK,CAAC;YAAE,OAAO,UAAU,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;IACtD,CAAC;IACD,OAAO,CAAC,CAAC;AACV,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,UAAU,CAC/B,GAAuB,EACvB,OAAsB,EACtB,OAAuB,EACvB,eAAoC,EAAE;IAEtC,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,KAAK,EAAE,GAAG,OAAO,CAAC;IAEnD,0EAA0E;IAC1E,qEAAqE;IACrE,IAAI,GAAG,KAAK,SAAS,IAAI,MAAM,CAAC,OAAO,KAAK,QAAQ,EAAE,CAAC;QACtD,MAAM,IAAI,YAAY,CACrB,uHAAuH,CACvH,CAAC;IACH,CAAC;IACD,MAAM,MAAM,GAAG,GAAG,IAAI,CAAC,MAAM,CAAC,OAAO,KAAK,MAAM,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC;IAEvE,MAAM,YAAY,GAAG,YAAY,CAAC,YAAY,IAAI,gBAAgB,EAAE,CAAC;IACrE,MAAM,QAAQ,GAAG,MAAM,UAAU,CAAC,MAAM,EAAE;QACzC,YAAY;QACZ,GAAG,CAAC,YAAY,CAAC,UAAU,KAAK,SAAS;YACxC,CAAC,CAAC,EAAE;YACJ,CAAC,CAAC,EAAE,UAAU,EAAE,YAAY,CAAC,UAAU,EAAE,CAAC;QAC3C,GAAG,CAAC,YAAY,CAAC,OAAO,KAAK,SAAS;YACrC,CAAC,CAAC,EAAE;YACJ,CAAC,CAAC,EAAE,OAAO,EAAE,YAAY,CAAC,OAAO,EAAE,CAAC;QACrC,GAAG,CAAC,YAAY,CAAC,OAAO,KAAK,SAAS;YACrC,CAAC,CAAC,EAAE;YACJ,CAAC,CAAC,EAAE,OAAO,EAAE,YAAY,CAAC,OAAO,EAAE,CAAC;KACrC,CAAC,CAAC;IAEH,IAAI,QAAQ,CAAC,OAAO,KAAK,KAAK,IAAI,MAAM,CAAC,OAAO,KAAK,KAAK,EAAE,CAAC;QAC5D,MAAM,KAAK,GAAG,eAAe,CAAC,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC,OAAO,CAAC,CAAC;QAChE,IAAI,KAAK,KAAK,CAAC,EAAE,CAAC;YACjB,KAAK,CAAC,cAAc,MAAM,CAAC,OAAO,kBAAkB,CAAC,CAAC;YACtD,OAAO,MAAM,CAAC,MAAM,CAAC;gBACpB,IAAI,EAAE,MAAM,CAAC,OAAO;gBACpB,EAAE,EAAE,MAAM,CAAC,OAAO;gBAClB,UAAU,EAAE,KAAK;gBACjB,QAAQ,EAAE,IAAI;aACd,CAAC,CAAC;QACJ,CAAC;QACD,IAAI,KAAK,GAAG,CAAC,IAAI,CAAC,OAAO,CAAC,cAAc,EAAE,CAAC;YAC1C,MAAM,IAAI,YAAY,CACrB,yBAAyB,MAAM,CAAC,OAAO,YAAY,QAAQ,CAAC,OAAO,oDAAoD,CACvH,CAAC;QACH,CAAC;IACF,CAAC;IACD,IAAI,QAAQ,CAAC,OAAO,KAAK,MAAM,IAAI,MAAM,CAAC,OAAO,KAAK,MAAM,EAAE,CAAC;QAC9D,uEAAuE;QACvE,+BAA+B;QAC/B,MAAM,SAAS,GAAG,QAAQ,CAAC,WAAW,CAAC;QACvC,IAAI,SAAS,KAAK,SAAS,IAAI,MAAM,CAAC,WAAW,KAAK,SAAS,EAAE,CAAC;YACjE,IACC,IAAI,CAAC,KAAK,CAAC,SAAS,CAAC,GAAG,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,WAAW,CAAC;gBACtD,CAAC,OAAO,CAAC,cAAc,EACtB,CAAC;gBACF,MAAM,IAAI,YAAY,CACrB,OAAO,QAAQ,CAAC,OAAO,yBAAyB,SAAS,yCAAyC,MAAM,CAAC,WAAW,gDAAgD,CACpK,CAAC;YACH,CAAC;YACD,IAAI,IAAI,CAAC,KAAK,CAAC,SAAS,CAAC,KAAK,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,WAAW,CAAC,EAAE,CAAC;gBAC9D,KAAK,CAAC,yBAAyB,MAAM,CAAC,OAAO,wBAAwB,CAAC,CAAC;gBACvE,OAAO,MAAM,CAAC,MAAM,CAAC;oBACpB,IAAI,EAAE,MAAM,CAAC,OAAO;oBACpB,EAAE,EAAE,MAAM,CAAC,OAAO;oBAClB,UAAU,EAAE,KAAK;oBACjB,QAAQ,EAAE,IAAI;iBACd,CAAC,CAAC;YACJ,CAAC;QACF,CAAC;IACF,CAAC;IAED,MAAM,QAAQ,GACb,CAAC,MAAM,aAAa,CAAC,MAAM,CAAC,CAAC;QAC7B,UAAU,CAAC,MAAM,CAAC,OAAO,EAAE,MAAM,CAAC,OAAO,EAAE,MAAM,CAAC,QAAQ,CAAC,CAAC;IAE7D,0EAA0E;IAC1E,mBAAmB;IACnB,IAAI,WAAmB,CAAC;IACxB,IAAI,QAAQ,CAAC,OAAO,KAAK,QAAQ,EAAE,CAAC;QACnC,KAAK,CACJ,YAAY,QAAQ,CAAC,SAAS,IAAI,QAAQ,CAAC,OAAO,mCAAmC,CACrF,CAAC;QACF,MAAM,KAAK,GAAG,MAAM,eAAe,CAAC;YACnC,GAAG,EAAE,QAAQ,CAAC,SAAS,IAAI,QAAQ,CAAC,OAAO;YAC3C,GAAG,CAAC,QAAQ,CAAC,QAAQ,KAAK,SAAS;gBAClC,CAAC,CAAC,EAAE;gBACJ,CAAC,CAAC,EAAE,QAAQ,EAAE,QAAQ,CAAC,QAAQ,EAAE,CAAC;YACnC,WAAW,EAAE,MAAM,CAAC,MAAM;YAC1B,KAAK;SACL,CAAC,CAAC;QACH,WAAW,GAAG,KAAK,CAAC,WAAW,CAAC;IACjC,CAAC;SAAM,CAAC;QACP,MAAM,MAAM,GAAG,QAAQ,CAAC,MAAM,CAAC;QAC/B,IAAI,MAAM,KAAK,SAAS;YACvB,MAAM,IAAI,YAAY,CACrB,mDAAmD,CACnD,CAAC;QACH,KAAK,CAAC,6BAA6B,CAAC,CAAC;QACrC,MAAM,KAAK,GAAG,MAAM,aAAa,CAAC,MAAM,CAAC,OAAO,EAAE,MAAM,CAAC,MAAM,CAAC,CAAC;QACjE,MAAM,CAAC,OAAO,EAAE,SAAS,CAAC,GAAG,MAAM,OAAO,CAAC,GAAG,CAAC;YAC9C,YAAY,CAAC,MAAM,CAAC,MAAM,CAAC;YAC3B,aAAa,CAAC,MAAM,CAAC,SAAS,CAAC;SAC/B,CAAC,CAAC;QACH,MAAM,aAAa,CAAC;YACnB,WAAW,EAAE,KAAK,CAAC,IAAI;YACvB,OAAO;YACP,SAAS;YACT,MAAM,EAAE,KAAK,CAAC,MAAM;YACpB,GAAG,CAAC,YAAY,CAAC,mBAAmB,KAAK,SAAS;gBACjD,CAAC,CAAC,EAAE;gBACJ,CAAC,CAAC,EAAE,YAAY,EAAE,YAAY,CAAC,mBAAmB,EAAE,CAAC;SACtD,CAAC,CAAC;QACH,WAAW,GAAG,KAAK,CAAC,IAAI,CAAC;IAC1B,CAAC;IAED,MAAM,SAAS,GAAG,MAAM,cAAc,CAAC;QACtC,MAAM;QACN,WAAW;QACX,OAAO,EAAE,QAAQ,CAAC,OAAO;QACzB,QAAQ,EAAE;YACT,OAAO,EAAE,QAAQ,CAAC,OAAO;YACzB,YAAY;YACZ,GAAG,CAAC,QAAQ,CAAC,OAAO,KAAK,KAAK,CAAC,CAAC,CAAC,EAAE,OAAO,EAAE,QAAQ,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;YACpE,GAAG,CAAC,QAAQ,CAAC,QAAQ,KAAK,SAAS;gBAClC,CAAC,CAAC,EAAE;gBACJ,CAAC,CAAC,EAAE,QAAQ,EAAE,QAAQ,CAAC,QAAQ,EAAE,CAAC;SACnC;KACD,CAAC,CAAC;IACH,MAAM,gBAAgB,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC;IAEtC,IAAI,SAAS,CAAC,IAAI,KAAK,QAAQ,EAAE,CAAC;QACjC,KAAK,CAAC,mBAAmB,SAAS,CAAC,QAAQ,CAAC,OAAO,kBAAkB,CAAC,CAAC;QACvE,OAAO,MAAM,CAAC,MAAM,CAAC;YACpB,IAAI,EAAE,MAAM,CAAC,OAAO;YACpB,EAAE,EAAE,SAAS,CAAC,QAAQ,CAAC,OAAO;YAC9B,UAAU,EAAE,KAAK;YACjB,QAAQ,EAAE,IAAI;SACd,CAAC,CAAC;IACJ,CAAC;IAED,KAAK,CAAC,aAAa,MAAM,CAAC,OAAO,MAAM,SAAS,CAAC,QAAQ,CAAC,OAAO,IAAI,CAAC,CAAC;IACvE,MAAM,OAAO,CAAC,IAAI,EAAE,CAAC;IACrB,MAAM,QAAQ,CAAC,MAAM,EAAE,SAAS,CAAC,IAAI,CAAC,CAAC;IACvC,MAAM,OAAO,CAAC,KAAK,EAAE,CAAC;IAEtB,IACC,CAAC,MAAM,YAAY,CAAC,MAAM,CAAC,UAAU,EAAE,YAAY,CAAC,kBAAkB,CAAC,CAAC;QACxE,SAAS,EACR,CAAC;QACF,KAAK,CAAC,yDAAyD,CAAC,CAAC;QACjE,MAAM,OAAO,CAAC,IAAI,EAAE,CAAC;QACrB,MAAM,QAAQ,CAAC,MAAM,EAAE,QAAQ,CAAC,CAAC;QACjC,MAAM,OAAO,CAAC,KAAK,EAAE,CAAC;QACtB,MAAM,SAAS,GACd,CAAC,MAAM,YAAY,CAClB,MAAM,CAAC,UAAU,EACjB,YAAY,CAAC,kBAAkB,CAC/B,CAAC,KAAK,SAAS,CAAC;QAClB,KAAK,CAAC,MAAM,OAAO,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC,CAAC;QACjC,MAAM,IAAI,YAAY,CACrB,kBAAkB,SAAS,CAAC,QAAQ,CAAC,OAAO,6BAA6B,MAAM,CAAC,OAAO,qBAAqB,SAAS,CAAC,CAAC,CAAC,kBAAkB,CAAC,CAAC,CAAC,gDAAgD,GAAG,CAChM,CAAC;IACH,CAAC;IAED,MAAM,kBAAkB,CAAC,MAAM,EAAE;QAChC,GAAG,MAAM;QACT,OAAO,EAAE,QAAQ,CAAC,OAAO;QACzB,OAAO,EAAE,SAAS,CAAC,QAAQ,CAAC,OAAO;QACnC,QAAQ,EAAE,SAAS,CAAC,QAAQ,CAAC,QAAQ;QACrC,GAAG,CAAC,QAAQ,CAAC,WAAW,KAAK,SAAS;YACrC,CAAC,CAAC,EAAE;YACJ,CAAC,CAAC,EAAE,WAAW,EAAE,QAAQ,CAAC,WAAW,EAAE,CAAC;QACzC,WAAW,EAAE,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE;KACrC,CAAC,CAAC;IACH,MAAM,OAAO,GAAG,MAAM,MAAM,CAAC,MAAM,EAAE,CAAC,SAAS,CAAC,IAAI,EAAE,QAAQ,CAAC,CAAC,CAAC;IACjE,KAAK,CACJ,eAAe,SAAS,CAAC,QAAQ,CAAC,OAAO,UAAU,QAAQ,mBAAmB,CAC9E,CAAC;IACF,IAAI,OAAO,CAAC,MAAM,GAAG,CAAC;QACrB,KAAK,CAAC,2BAA2B,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;IAEzD,OAAO,MAAM,CAAC,MAAM,CAAC;QACpB,IAAI,EAAE,MAAM,CAAC,OAAO;QACpB,EAAE,EAAE,SAAS,CAAC,QAAQ,CAAC,OAAO;QAC9B,UAAU,EAAE,KAAK;QACjB,QAAQ,EAAE,KAAK;KACf,CAAC,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import type { DaemonOptions } from './args.js';
|
|
2
|
+
import { type InstallLayout, type InstallRecord } from './layout.js';
|
|
3
|
+
import { type Systemd } from './systemd.js';
|
|
4
|
+
/**
|
|
5
|
+
* What every command after `install` needs: which installation it is acting
|
|
6
|
+
* on, and the record describing it.
|
|
7
|
+
*
|
|
8
|
+
* A machine can hold a system install and a user install at once, so a command
|
|
9
|
+
* with no scope flag looks for whichever one actually exists rather than
|
|
10
|
+
* guessing. Finding both without a flag is ambiguous and says so.
|
|
11
|
+
*/
|
|
12
|
+
export interface CommandContext {
|
|
13
|
+
readonly layout: InstallLayout;
|
|
14
|
+
readonly record: InstallRecord;
|
|
15
|
+
readonly systemd: Systemd;
|
|
16
|
+
readonly write: (line: string) => void;
|
|
17
|
+
}
|
|
18
|
+
export declare class NotInstalledError extends Error {
|
|
19
|
+
}
|
|
20
|
+
export interface ResolveContextOptions {
|
|
21
|
+
readonly options: DaemonOptions;
|
|
22
|
+
readonly home?: string;
|
|
23
|
+
readonly env?: Readonly<Record<string, string | undefined>>;
|
|
24
|
+
readonly write?: (line: string) => void;
|
|
25
|
+
}
|
|
26
|
+
export declare function resolveContext(options: ResolveContextOptions): Promise<CommandContext>;
|
package/dist/context.js
ADDED
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { homedir } from 'node:os';
|
|
2
|
+
import { installLayout, readInstallRecord, } from './layout.js';
|
|
3
|
+
import { createSystemd } from './systemd.js';
|
|
4
|
+
export class NotInstalledError extends Error {
|
|
5
|
+
}
|
|
6
|
+
export async function resolveContext(options) {
|
|
7
|
+
const home = options.home ?? homedir();
|
|
8
|
+
const write = options.write ?? ((line) => process.stdout.write(`${line}\n`));
|
|
9
|
+
const requested = options.options.scope;
|
|
10
|
+
const candidates = requested === undefined ? ['system', 'user'] : [requested];
|
|
11
|
+
const found = [];
|
|
12
|
+
for (const scope of candidates) {
|
|
13
|
+
const layout = installLayout(scope, home);
|
|
14
|
+
const record = await readInstallRecord(layout).catch(() => undefined);
|
|
15
|
+
if (record !== undefined)
|
|
16
|
+
found.push({ layout, record });
|
|
17
|
+
}
|
|
18
|
+
if (found.length === 0) {
|
|
19
|
+
throw new NotInstalledError(requested === undefined
|
|
20
|
+
? 'no Terminay Server is installed on this machine. Install one with `npx terminay daemon install`.'
|
|
21
|
+
: `no ${requested}-scope Terminay Server is installed on this machine.`);
|
|
22
|
+
}
|
|
23
|
+
if (found.length > 1) {
|
|
24
|
+
throw new NotInstalledError('both a system-wide and a user install exist here. Choose one with --system or --user.');
|
|
25
|
+
}
|
|
26
|
+
const [only] = found;
|
|
27
|
+
return Object.freeze({
|
|
28
|
+
layout: only.layout,
|
|
29
|
+
record: only.record,
|
|
30
|
+
systemd: createSystemd({
|
|
31
|
+
scope: only.layout.scope,
|
|
32
|
+
...(options.env === undefined ? {} : { env: options.env }),
|
|
33
|
+
}),
|
|
34
|
+
write,
|
|
35
|
+
});
|
|
36
|
+
}
|
|
37
|
+
//# sourceMappingURL=context.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"context.js","sourceRoot":"","sources":["../src/context.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,SAAS,CAAC;AAGlC,OAAO,EAGN,aAAa,EACb,iBAAiB,GACjB,MAAM,aAAa,CAAC;AACrB,OAAO,EAAE,aAAa,EAAgB,MAAM,cAAc,CAAC;AAkB3D,MAAM,OAAO,iBAAkB,SAAQ,KAAK;CAAG;AAS/C,MAAM,CAAC,KAAK,UAAU,cAAc,CACnC,OAA8B;IAE9B,MAAM,IAAI,GAAG,OAAO,CAAC,IAAI,IAAI,OAAO,EAAE,CAAC;IACvC,MAAM,KAAK,GACV,OAAO,CAAC,KAAK,IAAI,CAAC,CAAC,IAAY,EAAE,EAAE,CAAC,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,GAAG,IAAI,IAAI,CAAC,CAAC,CAAC;IACxE,MAAM,SAAS,GAAG,OAAO,CAAC,OAAO,CAAC,KAAK,CAAC;IAExC,MAAM,UAAU,GACf,SAAS,KAAK,SAAS,CAAC,CAAC,CAAC,CAAC,QAAQ,EAAE,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC;IAC5D,MAAM,KAAK,GAAuD,EAAE,CAAC;IACrE,KAAK,MAAM,KAAK,IAAI,UAAU,EAAE,CAAC;QAChC,MAAM,MAAM,GAAG,aAAa,CAAC,KAAK,EAAE,IAAI,CAAC,CAAC;QAC1C,MAAM,MAAM,GAAG,MAAM,iBAAiB,CAAC,MAAM,CAAC,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,SAAS,CAAC,CAAC;QACtE,IAAI,MAAM,KAAK,SAAS;YAAE,KAAK,CAAC,IAAI,CAAC,EAAE,MAAM,EAAE,MAAM,EAAE,CAAC,CAAC;IAC1D,CAAC;IAED,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACxB,MAAM,IAAI,iBAAiB,CAC1B,SAAS,KAAK,SAAS;YACtB,CAAC,CAAC,kGAAkG;YACpG,CAAC,CAAC,MAAM,SAAS,sDAAsD,CACxE,CAAC;IACH,CAAC;IACD,IAAI,KAAK,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QACtB,MAAM,IAAI,iBAAiB,CAC1B,uFAAuF,CACvF,CAAC;IACH,CAAC;IAED,MAAM,CAAC,IAAI,CAAC,GAAG,KAA2D,CAAC;IAC3E,OAAO,MAAM,CAAC,MAAM,CAAC;QACpB,MAAM,EAAE,IAAI,CAAC,MAAM;QACnB,MAAM,EAAE,IAAI,CAAC,MAAM;QACnB,OAAO,EAAE,aAAa,CAAC;YACtB,KAAK,EAAE,IAAI,CAAC,MAAM,CAAC,KAAK;YACxB,GAAG,CAAC,OAAO,CAAC,GAAG,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,GAAG,EAAE,OAAO,CAAC,GAAG,EAAE,CAAC;SAC1D,CAAC;QACF,KAAK;KACL,CAAC,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
export interface DownloadedAsset {
|
|
2
|
+
readonly path: string;
|
|
3
|
+
readonly sha256: string;
|
|
4
|
+
readonly bytes: number;
|
|
5
|
+
}
|
|
6
|
+
export declare function hashFile(path: string): Promise<string>;
|
|
7
|
+
export declare function downloadAsset(url: string, prefix: string, name?: string): Promise<DownloadedAsset>;
|
|
8
|
+
/** Sidecars are tiny, so they are read into memory rather than staged. */
|
|
9
|
+
export declare function downloadText(url: string): Promise<string>;
|
|
10
|
+
export declare function downloadBytes(url: string): Promise<Buffer>;
|
|
11
|
+
export declare function discardDownloads(prefix: string): Promise<void>;
|
package/dist/download.js
ADDED
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
import { createHash } from 'node:crypto';
|
|
2
|
+
import { createReadStream, createWriteStream } from 'node:fs';
|
|
3
|
+
import { mkdir, rm, stat } from 'node:fs/promises';
|
|
4
|
+
import { basename, join } from 'node:path';
|
|
5
|
+
import { pipeline } from 'node:stream/promises';
|
|
6
|
+
import { openStream, request } from './http.js';
|
|
7
|
+
/**
|
|
8
|
+
* Fetching a release asset onto the operator's disk.
|
|
9
|
+
*
|
|
10
|
+
* The archive is around 90 MB, so it is streamed to a partial file and hashed
|
|
11
|
+
* by reading that file back rather than buffered in memory. A partial file
|
|
12
|
+
* that survives an interrupted install is resumed with a range request, and a
|
|
13
|
+
* download that fails for any other reason is deleted: a truncated archive
|
|
14
|
+
* that lingers would fail verification on the next attempt with a message
|
|
15
|
+
* about a bad signature rather than a bad download.
|
|
16
|
+
*/
|
|
17
|
+
const DOWNLOAD_DIRECTORY = '.downloads';
|
|
18
|
+
const MAX_ASSET_BYTES = 512 * 1024 * 1024;
|
|
19
|
+
export async function hashFile(path) {
|
|
20
|
+
const hash = createHash('sha256');
|
|
21
|
+
await pipeline(createReadStream(path), hash);
|
|
22
|
+
return hash.digest('hex');
|
|
23
|
+
}
|
|
24
|
+
async function sizeOf(path) {
|
|
25
|
+
const info = await stat(path).catch(() => undefined);
|
|
26
|
+
return info?.isFile() === true ? info.size : 0;
|
|
27
|
+
}
|
|
28
|
+
export async function downloadAsset(url, prefix, name = basename(new URL(url).pathname)) {
|
|
29
|
+
const directory = join(prefix, DOWNLOAD_DIRECTORY);
|
|
30
|
+
await mkdir(directory, { recursive: true, mode: 0o700 });
|
|
31
|
+
const target = join(directory, `${name}.part`);
|
|
32
|
+
const existing = await sizeOf(target);
|
|
33
|
+
try {
|
|
34
|
+
const response = await openStream(url, '*/*', undefined, existing > 0 ? { range: `bytes=${existing}-` } : undefined);
|
|
35
|
+
const status = response.statusCode ?? 0;
|
|
36
|
+
// 206 continues the partial file; 200 means the server ignored the range
|
|
37
|
+
// and is sending the whole asset, so the partial file is replaced.
|
|
38
|
+
const resuming = status === 206 && existing > 0;
|
|
39
|
+
if (status !== 200 && status !== 206) {
|
|
40
|
+
response.resume();
|
|
41
|
+
throw new Error(`downloading ${url} failed with status ${status}`);
|
|
42
|
+
}
|
|
43
|
+
const declared = Number(response.headers['content-length'] ?? '0');
|
|
44
|
+
if (Number.isFinite(declared) &&
|
|
45
|
+
declared + (resuming ? existing : 0) > MAX_ASSET_BYTES) {
|
|
46
|
+
response.resume();
|
|
47
|
+
throw new Error(`refusing to download ${url}: it is larger than the ${MAX_ASSET_BYTES}-byte limit`);
|
|
48
|
+
}
|
|
49
|
+
await pipeline(response, createWriteStream(target, resuming ? { flags: 'a' } : { flags: 'w', mode: 0o600 }));
|
|
50
|
+
const bytes = await sizeOf(target);
|
|
51
|
+
if (bytes === 0)
|
|
52
|
+
throw new Error(`downloading ${url} produced an empty file`);
|
|
53
|
+
if (bytes > MAX_ASSET_BYTES)
|
|
54
|
+
throw new Error(`${url} is larger than the ${MAX_ASSET_BYTES}-byte limit`);
|
|
55
|
+
return Object.freeze({
|
|
56
|
+
path: target,
|
|
57
|
+
sha256: await hashFile(target),
|
|
58
|
+
bytes,
|
|
59
|
+
});
|
|
60
|
+
}
|
|
61
|
+
catch (error) {
|
|
62
|
+
await rm(target, { force: true });
|
|
63
|
+
throw error;
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
/** Sidecars are tiny, so they are read into memory rather than staged. */
|
|
67
|
+
export async function downloadText(url) {
|
|
68
|
+
const response = await request(url, '*/*');
|
|
69
|
+
if (response.status !== 200)
|
|
70
|
+
throw new Error(`downloading ${url} failed with status ${response.status}`);
|
|
71
|
+
return response.body;
|
|
72
|
+
}
|
|
73
|
+
export async function downloadBytes(url) {
|
|
74
|
+
const response = await openStream(url, '*/*');
|
|
75
|
+
const status = response.statusCode ?? 0;
|
|
76
|
+
if (status !== 200) {
|
|
77
|
+
response.resume();
|
|
78
|
+
throw new Error(`downloading ${url} failed with status ${status}`);
|
|
79
|
+
}
|
|
80
|
+
const chunks = [];
|
|
81
|
+
let total = 0;
|
|
82
|
+
for await (const chunk of response) {
|
|
83
|
+
total += chunk.length;
|
|
84
|
+
if (total > 64 * 1024) {
|
|
85
|
+
response.destroy();
|
|
86
|
+
throw new Error(`${url} is unexpectedly large for a signature`);
|
|
87
|
+
}
|
|
88
|
+
chunks.push(chunk);
|
|
89
|
+
}
|
|
90
|
+
return Buffer.concat(chunks);
|
|
91
|
+
}
|
|
92
|
+
export async function discardDownloads(prefix) {
|
|
93
|
+
await rm(join(prefix, DOWNLOAD_DIRECTORY), { recursive: true, force: true });
|
|
94
|
+
}
|
|
95
|
+
//# sourceMappingURL=download.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"download.js","sourceRoot":"","sources":["../src/download.ts"],"names":[],"mappings":"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"}
|
package/dist/health.d.ts
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Waiting for the server to say it is ready.
|
|
3
|
+
*
|
|
4
|
+
* The health endpoint is loopback-only by design, so this is an operator-side
|
|
5
|
+
* check running on the same machine, never something a device reaches. An
|
|
6
|
+
* upgrade that cannot get a ready answer within the deadline is the signal to
|
|
7
|
+
* roll back, so the deadline is the whole point of this module.
|
|
8
|
+
*/
|
|
9
|
+
export declare const READINESS_TIMEOUT_MS = 60000;
|
|
10
|
+
export interface HealthSnapshot {
|
|
11
|
+
readonly status: string;
|
|
12
|
+
readonly ready: boolean;
|
|
13
|
+
readonly phase?: string;
|
|
14
|
+
readonly serverId?: string;
|
|
15
|
+
readonly version?: string;
|
|
16
|
+
}
|
|
17
|
+
export declare function probeHealth(port: number, path?: '/readyz' | '/healthz'): Promise<HealthSnapshot | undefined>;
|
|
18
|
+
export declare function waitForReady(port: number, timeoutMs?: number, now?: () => number): Promise<HealthSnapshot | undefined>;
|
package/dist/health.js
ADDED
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import { get } from 'node:http';
|
|
2
|
+
/**
|
|
3
|
+
* Waiting for the server to say it is ready.
|
|
4
|
+
*
|
|
5
|
+
* The health endpoint is loopback-only by design, so this is an operator-side
|
|
6
|
+
* check running on the same machine, never something a device reaches. An
|
|
7
|
+
* upgrade that cannot get a ready answer within the deadline is the signal to
|
|
8
|
+
* roll back, so the deadline is the whole point of this module.
|
|
9
|
+
*/
|
|
10
|
+
export const READINESS_TIMEOUT_MS = 60_000;
|
|
11
|
+
const POLL_INTERVAL_MS = 500;
|
|
12
|
+
const REQUEST_TIMEOUT_MS = 5_000;
|
|
13
|
+
export function probeHealth(port, path = '/readyz') {
|
|
14
|
+
return new Promise((resolve) => {
|
|
15
|
+
const request = get({ host: '127.0.0.1', port, path, timeout: REQUEST_TIMEOUT_MS }, (response) => {
|
|
16
|
+
let body = '';
|
|
17
|
+
response.setEncoding('utf8');
|
|
18
|
+
response.on('data', (chunk) => {
|
|
19
|
+
body += chunk;
|
|
20
|
+
if (body.length > 64 * 1024)
|
|
21
|
+
response.destroy();
|
|
22
|
+
});
|
|
23
|
+
response.on('end', () => {
|
|
24
|
+
try {
|
|
25
|
+
const parsed = JSON.parse(body);
|
|
26
|
+
resolve({
|
|
27
|
+
status: String(parsed.status ?? 'unknown'),
|
|
28
|
+
ready: parsed.ready === true,
|
|
29
|
+
...(typeof parsed.phase === 'string'
|
|
30
|
+
? { phase: parsed.phase }
|
|
31
|
+
: {}),
|
|
32
|
+
...(typeof parsed.serverId === 'string'
|
|
33
|
+
? { serverId: parsed.serverId }
|
|
34
|
+
: {}),
|
|
35
|
+
...(typeof parsed.version === 'string'
|
|
36
|
+
? { version: parsed.version }
|
|
37
|
+
: {}),
|
|
38
|
+
});
|
|
39
|
+
}
|
|
40
|
+
catch {
|
|
41
|
+
resolve(undefined);
|
|
42
|
+
}
|
|
43
|
+
});
|
|
44
|
+
});
|
|
45
|
+
request.on('timeout', () => request.destroy());
|
|
46
|
+
request.on('error', () => resolve(undefined));
|
|
47
|
+
});
|
|
48
|
+
}
|
|
49
|
+
const sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
|
|
50
|
+
export async function waitForReady(port, timeoutMs = READINESS_TIMEOUT_MS, now = Date.now) {
|
|
51
|
+
const deadline = now() + timeoutMs;
|
|
52
|
+
for (;;) {
|
|
53
|
+
const snapshot = await probeHealth(port);
|
|
54
|
+
if (snapshot?.ready === true)
|
|
55
|
+
return snapshot;
|
|
56
|
+
if (now() >= deadline)
|
|
57
|
+
return undefined;
|
|
58
|
+
await sleep(POLL_INTERVAL_MS);
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
//# sourceMappingURL=health.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"health.js","sourceRoot":"","sources":["../src/health.ts"],"names":[],"mappings":"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"}
|
package/dist/http.d.ts
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { IncomingMessage } from 'node:http';
|
|
2
|
+
/** Sentinel for `openStream`: return the redirect itself rather than follow it. */
|
|
3
|
+
export declare const NO_REDIRECT = -1;
|
|
4
|
+
export interface HttpResponse {
|
|
5
|
+
readonly status: number;
|
|
6
|
+
readonly headers: Readonly<Record<string, string | string[] | undefined>>;
|
|
7
|
+
readonly body: string;
|
|
8
|
+
}
|
|
9
|
+
export declare function openStream(url: string, accept?: string, redirectsLeft?: number, extraHeaders?: Readonly<Record<string, string>>): Promise<IncomingMessage>;
|
|
10
|
+
export declare function request(url: string, accept?: string, followRedirects?: boolean): Promise<HttpResponse>;
|
|
11
|
+
export declare function requestJson(url: string): Promise<unknown>;
|