@goondocks/myco 1.1.1 → 1.2.0-beta.1
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/ui/index.html
CHANGED
|
@@ -5,8 +5,8 @@
|
|
|
5
5
|
<link rel="icon" type="image/svg+xml" href="/favicon.svg" />
|
|
6
6
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
7
7
|
<title>Myco</title>
|
|
8
|
-
<script type="module" crossorigin src="/assets/index-
|
|
9
|
-
<link rel="stylesheet" crossorigin href="/assets/index-
|
|
8
|
+
<script type="module" crossorigin src="/assets/index-BnYhMW3F.js"></script>
|
|
9
|
+
<link rel="stylesheet" crossorigin href="/assets/index-B7gU3-ZJ.css">
|
|
10
10
|
</head>
|
|
11
11
|
<body>
|
|
12
12
|
<div id="root"></div>
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@goondocks/myco",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.2.0-beta.1",
|
|
4
4
|
"description": "Collective agent intelligence — Claude Code plugin",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -73,11 +73,11 @@
|
|
|
73
73
|
"zod": "^4.4.3"
|
|
74
74
|
},
|
|
75
75
|
"optionalDependencies": {
|
|
76
|
-
"@goondocks/myco-darwin-arm64": "1.
|
|
77
|
-
"@goondocks/myco-darwin-x64": "1.
|
|
78
|
-
"@goondocks/myco-linux-arm64": "1.
|
|
79
|
-
"@goondocks/myco-linux-x64": "1.
|
|
80
|
-
"@goondocks/myco-windows-x64": "1.
|
|
76
|
+
"@goondocks/myco-darwin-arm64": "1.2.0-beta.1",
|
|
77
|
+
"@goondocks/myco-darwin-x64": "1.2.0-beta.1",
|
|
78
|
+
"@goondocks/myco-linux-arm64": "1.2.0-beta.1",
|
|
79
|
+
"@goondocks/myco-linux-x64": "1.2.0-beta.1",
|
|
80
|
+
"@goondocks/myco-windows-x64": "1.2.0-beta.1"
|
|
81
81
|
},
|
|
82
82
|
"devDependencies": {
|
|
83
83
|
"@goondocks/myco-shared": "*",
|
|
@@ -1,23 +1,27 @@
|
|
|
1
|
-
// Postinstall:
|
|
2
|
-
// installed as an optionalDependency, mark it executable, and write
|
|
3
|
-
// `vendor/resolved.json` so `bin/myco` can dispatch without re-resolving on
|
|
4
|
-
// every invocation.
|
|
1
|
+
// Postinstall: bootstrap npm into the single self-updating managed binary.
|
|
5
2
|
//
|
|
6
3
|
// Each supported platform has its own published package
|
|
7
4
|
// (`@goondocks/myco-<target>`) whose `package.json` carries the matching
|
|
8
5
|
// `os` and `cpu` filters. npm installs only the matching one; the rest are
|
|
9
6
|
// skipped. This script uses `require.resolve` to find the binary inside the
|
|
10
|
-
// installed platform package,
|
|
11
|
-
//
|
|
12
|
-
//
|
|
7
|
+
// installed platform package, writes `vendor/resolved.json` (so a fallback
|
|
8
|
+
// `bin/myco` dispatch can find it), and then CONVERGES: it copies the
|
|
9
|
+
// selected binary into the canonical managed location (`~/.myco/bin/myco`),
|
|
10
|
+
// reconciles the `runtime.command` pin so every CLI invocation re-execs the
|
|
11
|
+
// managed binary, writes the install marker, and re-points the OS service at
|
|
12
|
+
// the managed binary. npm thus becomes a one-time bootstrap that hands off to
|
|
13
|
+
// the same self-updating binary the curl installer produces.
|
|
14
|
+
//
|
|
15
|
+
// This module exports `convergeNpmInstall` so the convergence mechanics are
|
|
16
|
+
// unit-testable in isolation. The postinstall side effects run only when the
|
|
17
|
+
// file is executed as the main module (the is-main guard at the bottom), so
|
|
18
|
+
// importing it for a test is side-effect free.
|
|
13
19
|
|
|
14
20
|
import fs from 'node:fs';
|
|
15
21
|
import path from 'node:path';
|
|
22
|
+
import os from 'node:os';
|
|
16
23
|
import { createRequire } from 'node:module';
|
|
17
|
-
import { fileURLToPath } from 'node:url';
|
|
18
|
-
|
|
19
|
-
const pkgRoot = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '..');
|
|
20
|
-
const require = createRequire(import.meta.url);
|
|
24
|
+
import { fileURLToPath, pathToFileURL } from 'node:url';
|
|
21
25
|
|
|
22
26
|
function detectTarget() {
|
|
23
27
|
const { platform, arch } = process;
|
|
@@ -27,79 +31,291 @@ function detectTarget() {
|
|
|
27
31
|
return null;
|
|
28
32
|
}
|
|
29
33
|
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
34
|
+
/**
|
|
35
|
+
* Converge the npm install onto the single managed binary. Pure-ish: all fs
|
|
36
|
+
* I/O is confined to `home` / `dest`. Every step is wrapped so a failure logs
|
|
37
|
+
* to stderr and is NON-FATAL — npm postinstall must never fail because the
|
|
38
|
+
* daemon's lazy-spawn path and `myco doctor` still recover the gap.
|
|
39
|
+
*
|
|
40
|
+
* `dest` and `versionedDest` are INJECTED (not computed here) so the paths
|
|
41
|
+
* live in exactly one place — `@myco/install/managed-binary` — which prod
|
|
42
|
+
* computes from the compiled `dist` module and tests compute from the `.ts`.
|
|
43
|
+
* Same source, zero `.ts`/`.mjs` drift.
|
|
44
|
+
*
|
|
45
|
+
* Layout produced (mirrors install.sh / the daemon helpers):
|
|
46
|
+
* `versionedDest` → <bindir>/versions/<bare-semver>/myco[.exe]
|
|
47
|
+
* `dest` → <bindir>/myco[.exe] (stable, current slot)
|
|
48
|
+
*
|
|
49
|
+
* Sequence: place at versioned slot via atomic temp+rename, then copy from
|
|
50
|
+
* the versioned slot to the stable path via a second atomic temp+rename. A
|
|
51
|
+
* partial copy can never leave a broken stable binary.
|
|
52
|
+
*
|
|
53
|
+
* Returns `{ dest, copied, pinAction }` for callers/tests to assert.
|
|
54
|
+
*
|
|
55
|
+
* @param {{ home: string, platform: string, resolvedBinary: string, dest: string, channel: string, version?: string, versionedDest?: string, writeMarker?: Function }} args
|
|
56
|
+
*/
|
|
57
|
+
export function convergeNpmInstall({ home, platform, resolvedBinary, dest, channel, version, versionedDest, writeMarker }) {
|
|
58
|
+
const log = (msg) => process.stderr.write(`[myco] ${msg}\n`);
|
|
59
|
+
const mycoHome = path.join(home, '.myco');
|
|
60
|
+
let copied = false;
|
|
61
|
+
let pinAction = 'skipped';
|
|
62
|
+
|
|
63
|
+
// --- Step 1: Atomic placement into the versioned slot -------------------
|
|
64
|
+
// When `versionedDest` is provided, place the binary at the versioned path
|
|
65
|
+
// first. This is the canonical layout: <bindir>/versions/<semver>/myco[.exe].
|
|
66
|
+
// Uses the same temp+rename pattern as the stable copy below.
|
|
67
|
+
let sourceForStable = resolvedBinary;
|
|
68
|
+
if (versionedDest) {
|
|
69
|
+
try {
|
|
70
|
+
fs.mkdirSync(path.dirname(versionedDest), { recursive: true });
|
|
71
|
+
const tmpV = `${versionedDest}.tmp-${process.pid}`;
|
|
72
|
+
try {
|
|
73
|
+
fs.copyFileSync(resolvedBinary, tmpV);
|
|
74
|
+
if (platform !== 'win32') {
|
|
75
|
+
try { fs.chmodSync(tmpV, 0o755); } catch { /* best effort */ }
|
|
76
|
+
}
|
|
77
|
+
fs.renameSync(tmpV, versionedDest);
|
|
78
|
+
// Stable copy reads from the versioned slot — ensures both paths
|
|
79
|
+
// hold identical bytes from the same verified source.
|
|
80
|
+
sourceForStable = versionedDest;
|
|
81
|
+
} catch (err) {
|
|
82
|
+
try { fs.rmSync(tmpV, { force: true }); } catch { /* best effort */ }
|
|
83
|
+
throw err;
|
|
84
|
+
}
|
|
85
|
+
} catch (err) {
|
|
86
|
+
log(`versioned binary placement skipped: ${err?.message ?? err}`);
|
|
87
|
+
// Fall back to copying directly from the resolved source binary.
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
// --- Step 2: Atomic copy to the stable dest ----------------------------
|
|
92
|
+
// Write to a pid-suffixed temp file in the same directory, then rename onto
|
|
93
|
+
// `dest`. A reader either sees the old binary or the new one, never a
|
|
94
|
+
// half-written file.
|
|
95
|
+
try {
|
|
96
|
+
fs.mkdirSync(path.dirname(dest), { recursive: true });
|
|
97
|
+
const tmp = `${dest}.tmp-${process.pid}`;
|
|
98
|
+
try {
|
|
99
|
+
fs.copyFileSync(sourceForStable, tmp);
|
|
100
|
+
if (platform !== 'win32') {
|
|
101
|
+
try { fs.chmodSync(tmp, 0o755); } catch { /* best effort */ }
|
|
102
|
+
}
|
|
103
|
+
fs.renameSync(tmp, dest);
|
|
104
|
+
copied = true;
|
|
105
|
+
} catch (err) {
|
|
106
|
+
// Clean up the temp file on any failure.
|
|
107
|
+
try { fs.rmSync(tmp, { force: true }); } catch { /* best effort */ }
|
|
108
|
+
// On win32 the existing managed binary may be running (the daemon /
|
|
109
|
+
// the launcher), so the rename fails EBUSY/EPERM. Task 9 handles the
|
|
110
|
+
// win32 in-place swap; here we skip and continue, non-fatal.
|
|
111
|
+
if (platform === 'win32' && (err?.code === 'EBUSY' || err?.code === 'EPERM')) {
|
|
112
|
+
log('managed binary in use; skipped (win32 swap deferred to update path)');
|
|
113
|
+
} else {
|
|
114
|
+
throw err;
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
} catch (err) {
|
|
118
|
+
log(`managed binary copy skipped: ${err?.message ?? err}`);
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
// --- Pin reconciliation -------------------------------------------------
|
|
122
|
+
// `runtime.command` is the machine pin every CLI shim re-execs. We may
|
|
123
|
+
// safely point it at the managed binary, but must NOT clobber an active
|
|
124
|
+
// beta managed-runtime pin or a deliberate external/dev pin.
|
|
125
|
+
try {
|
|
126
|
+
const pinPath = path.join(mycoHome, 'runtime.command');
|
|
127
|
+
let pin = '';
|
|
128
|
+
try { pin = fs.readFileSync(pinPath, 'utf8').trim(); } catch { /* absent */ }
|
|
129
|
+
|
|
130
|
+
// Preserve a legacy managed-runtime pin (a path under ~/.myco/runtime/node_modules/)
|
|
131
|
+
// so a pre-native-installer setup isn't stranded.
|
|
132
|
+
const normalize = (p) => p.split(path.sep).join('/');
|
|
133
|
+
const managedPrefix = `${normalize(path.join(mycoHome, 'runtime'))}/node_modules/`;
|
|
134
|
+
const isManagedRuntimePin = pin !== '' && normalize(pin).startsWith(managedPrefix);
|
|
135
|
+
|
|
136
|
+
const shouldWrite =
|
|
137
|
+
pin === '' ||
|
|
138
|
+
pin === dest ||
|
|
139
|
+
(pin.includes('/node_modules/') && !isManagedRuntimePin);
|
|
140
|
+
|
|
141
|
+
if (shouldWrite) {
|
|
142
|
+
fs.mkdirSync(mycoHome, { recursive: true });
|
|
143
|
+
fs.writeFileSync(pinPath, `${dest}\n`, { mode: 0o644 });
|
|
144
|
+
// `mode` in writeFileSync is masked by umask; chmod to be certain the
|
|
145
|
+
// pin is never group/other-writable (runtime-redirect.cjs refuses it).
|
|
146
|
+
try { fs.chmodSync(pinPath, 0o644); } catch { /* best effort */ }
|
|
147
|
+
pinAction = 'wrote';
|
|
148
|
+
} else if (isManagedRuntimePin) {
|
|
149
|
+
log('legacy managed-runtime pin detected; not re-pointing runtime.command');
|
|
150
|
+
pinAction = 'preserved-managed';
|
|
151
|
+
} else {
|
|
152
|
+
log('external runtime.command pin preserved; not re-pointing');
|
|
153
|
+
pinAction = 'preserved-external';
|
|
154
|
+
}
|
|
155
|
+
} catch (err) {
|
|
156
|
+
log(`runtime.command reconcile skipped: ${err?.message ?? err}`);
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
// --- Install marker -----------------------------------------------------
|
|
160
|
+
// When `writeMarker` is injected (production: `managed.writeInstallMarker`
|
|
161
|
+
// from dist/src/install/managed-binary.js), delegate to the canonical helper
|
|
162
|
+
// so the format is defined in exactly one place. Tests that call
|
|
163
|
+
// convergeNpmInstall directly do not inject `writeMarker`, so the inline
|
|
164
|
+
// fallback keeps convergence unit-testable without a compiled dist/.
|
|
165
|
+
try {
|
|
166
|
+
if (writeMarker) {
|
|
167
|
+
writeMarker(mycoHome, { channel, source: 'npm', bin: dest });
|
|
168
|
+
} else {
|
|
169
|
+
fs.mkdirSync(mycoHome, { recursive: true });
|
|
170
|
+
fs.writeFileSync(
|
|
171
|
+
path.join(mycoHome, 'install.json'),
|
|
172
|
+
JSON.stringify({ channel, source: 'npm', bin: dest }, null, 2),
|
|
173
|
+
);
|
|
174
|
+
}
|
|
175
|
+
} catch (err) {
|
|
176
|
+
log(`install marker skipped: ${err?.message ?? err}`);
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
return { dest, copied, pinAction };
|
|
37
180
|
}
|
|
38
181
|
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
182
|
+
/**
|
|
183
|
+
* Derive the release channel from this package's own version: a semver
|
|
184
|
+
* prerelease component (`-beta`, `-alpha`, `-rc`, …) => 'beta', else 'stable'.
|
|
185
|
+
* Any error defaults to 'stable'.
|
|
186
|
+
*/
|
|
187
|
+
function deriveChannel(pkgRoot) {
|
|
188
|
+
try {
|
|
189
|
+
const pkg = JSON.parse(fs.readFileSync(path.join(pkgRoot, 'package.json'), 'utf8'));
|
|
190
|
+
return /-(?:beta|alpha|rc|next|canary|dev)\b/.test(String(pkg.version)) ? 'beta' : 'stable';
|
|
191
|
+
} catch {
|
|
192
|
+
return 'stable';
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
async function main() {
|
|
197
|
+
const pkgRoot = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '..');
|
|
198
|
+
const require = createRequire(import.meta.url);
|
|
199
|
+
|
|
200
|
+
const target = detectTarget();
|
|
201
|
+
if (!target) {
|
|
202
|
+
process.stderr.write(
|
|
203
|
+
`[myco] Unsupported platform: ${process.platform}-${process.arch}. ` +
|
|
204
|
+
`Supported: darwin-{arm64,x64}, linux-{x64,arm64}, windows-x64.\n`,
|
|
205
|
+
);
|
|
206
|
+
process.exit(1);
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
const platformPkg = `@goondocks/myco-${target}`;
|
|
210
|
+
const binaryName = process.platform === 'win32' ? 'myco.exe' : 'myco';
|
|
211
|
+
|
|
212
|
+
// Source-checkout escape hatch: during local dev (`npm ci` in the monorepo)
|
|
213
|
+
// the platform package's `bin/` directory is empty until `make dev-link` (or
|
|
214
|
+
// an explicit `npm run build:binary`) compiles the host-target binary into
|
|
215
|
+
// it. Detect that state via the presence of `src/`, and exit 0 with a hint
|
|
216
|
+
// so monorepo installs don't trip postinstall.
|
|
217
|
+
const isSourceCheckout = fs.existsSync(path.join(pkgRoot, 'src'));
|
|
218
|
+
|
|
219
|
+
let binaryPath;
|
|
220
|
+
try {
|
|
221
|
+
binaryPath = require.resolve(`${platformPkg}/bin/${binaryName}`);
|
|
222
|
+
} catch (err) {
|
|
223
|
+
if (isSourceCheckout) {
|
|
224
|
+
process.stderr.write(
|
|
225
|
+
`[myco] No platform binary found in ${platformPkg}/bin/${binaryName}. ` +
|
|
226
|
+
`Skipping postinstall in source checkout (expected before \`make dev-link\`).\n`,
|
|
227
|
+
);
|
|
228
|
+
process.exit(0);
|
|
229
|
+
}
|
|
54
230
|
process.stderr.write(
|
|
55
|
-
`[myco]
|
|
56
|
-
`
|
|
231
|
+
`[myco] Platform binary package ${platformPkg} is not installed. ` +
|
|
232
|
+
`npm should have installed it as an optionalDependency of @goondocks/myco. ` +
|
|
233
|
+
`Try: npm install --include=optional -g @goondocks/myco\n` +
|
|
234
|
+
`(reason: ${err.message})\n`,
|
|
57
235
|
);
|
|
58
|
-
process.exit(
|
|
236
|
+
process.exit(1);
|
|
59
237
|
}
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
238
|
+
|
|
239
|
+
if (process.platform !== 'win32') {
|
|
240
|
+
try { fs.chmodSync(binaryPath, 0o755); } catch { /* best effort */ }
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
const vendorDir = path.join(pkgRoot, 'vendor');
|
|
244
|
+
fs.mkdirSync(vendorDir, { recursive: true });
|
|
245
|
+
const resolvedPath = path.join(vendorDir, 'resolved.json');
|
|
246
|
+
fs.writeFileSync(
|
|
247
|
+
resolvedPath,
|
|
248
|
+
JSON.stringify({ target, binaryPath }, null, 2) + '\n',
|
|
249
|
+
'utf-8',
|
|
65
250
|
);
|
|
66
|
-
process.exit(1);
|
|
67
|
-
}
|
|
68
251
|
|
|
69
|
-
|
|
70
|
-
try { fs.chmodSync(binaryPath, 0o755); } catch { /* best effort */ }
|
|
71
|
-
}
|
|
252
|
+
process.stdout.write(`[myco] Selected platform binary: ${binaryPath}\n`);
|
|
72
253
|
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
//
|
|
87
|
-
//
|
|
88
|
-
//
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
254
|
+
// Converge npm into the single managed binary, then self-install/repoint the
|
|
255
|
+
// OS service at it. Skipped in source checkouts (no published dist/). Both
|
|
256
|
+
// steps are wrapped so neither can fail the postinstall — the daemon's
|
|
257
|
+
// lazy-spawn path still works and `myco doctor` surfaces any gap.
|
|
258
|
+
// Plan reference: Decision 13 / Step 12.
|
|
259
|
+
if (!isSourceCheckout) {
|
|
260
|
+
const distManagedBinary = path.join(pkgRoot, 'dist/src/install/managed-binary.js');
|
|
261
|
+
let dest = null;
|
|
262
|
+
if (fs.existsSync(distManagedBinary)) {
|
|
263
|
+
try {
|
|
264
|
+
const managed = await import(distManagedBinary);
|
|
265
|
+
const home = os.homedir();
|
|
266
|
+
const platform = process.platform;
|
|
267
|
+
// `pkg.version` is the bare semver (e.g. "1.2.3") — npm packages never
|
|
268
|
+
// carry the "myco/v" tag prefix that curl installers use. This is the
|
|
269
|
+
// exact version string the daemon's versionBinaryPath() expects.
|
|
270
|
+
let pkg = { version: null };
|
|
271
|
+
try {
|
|
272
|
+
pkg = JSON.parse(fs.readFileSync(path.join(pkgRoot, 'package.json'), 'utf8'));
|
|
273
|
+
} catch { /* version stays null; versionedDest skipped */ }
|
|
274
|
+
const version = pkg.version ?? null;
|
|
275
|
+
dest = managed.managedBinaryPath(home, platform);
|
|
276
|
+
const versionedDest = version
|
|
277
|
+
? managed.versionBinaryPath(home, platform, version)
|
|
278
|
+
: null;
|
|
279
|
+
const channel = deriveChannel(pkgRoot);
|
|
280
|
+
convergeNpmInstall({
|
|
281
|
+
home,
|
|
282
|
+
platform,
|
|
283
|
+
resolvedBinary: binaryPath,
|
|
284
|
+
dest,
|
|
285
|
+
channel,
|
|
286
|
+
version,
|
|
287
|
+
versionedDest,
|
|
288
|
+
writeMarker: managed.writeInstallMarker,
|
|
289
|
+
});
|
|
290
|
+
} catch (err) {
|
|
291
|
+
process.stderr.write(`[myco] Convergence skipped: ${err?.message ?? err}\n`);
|
|
292
|
+
}
|
|
293
|
+
} else {
|
|
294
|
+
process.stderr.write('[myco] Convergence skipped: managed-binary module not found in dist/\n');
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
const distSelfInstall = path.join(pkgRoot, 'dist/src/service/self-install.js');
|
|
298
|
+
if (fs.existsSync(distSelfInstall)) {
|
|
299
|
+
try {
|
|
300
|
+
const mod = await import(distSelfInstall);
|
|
301
|
+
const stderrLogger = {
|
|
302
|
+
info: (kind, message) => process.stderr.write(`[myco] ${kind}: ${message}\n`),
|
|
303
|
+
debug: () => undefined,
|
|
304
|
+
warn: (kind, message) => process.stderr.write(`[myco] ${kind}: ${message}\n`),
|
|
305
|
+
error: (kind, message) => process.stderr.write(`[myco] ${kind}: ${message}\n`),
|
|
306
|
+
};
|
|
307
|
+
// Re-point the service unit at the managed binary so a self-update's
|
|
308
|
+
// in-place swap takes effect on the next supervisor restart. Falls
|
|
309
|
+
// back to the default executable inside self-install if `dest` is null.
|
|
310
|
+
await mod.ensureSelfInstalledAsService(stderrLogger, dest ? { executable: dest } : {});
|
|
311
|
+
} catch (err) {
|
|
312
|
+
process.stderr.write(`[myco] Service install skipped: ${err?.message ?? err}\n`);
|
|
313
|
+
}
|
|
103
314
|
}
|
|
104
315
|
}
|
|
105
316
|
}
|
|
317
|
+
|
|
318
|
+
const isMain = process.argv[1] && import.meta.url === pathToFileURL(process.argv[1]).href;
|
|
319
|
+
if (isMain) {
|
|
320
|
+
await main();
|
|
321
|
+
}
|