@tomasko/podcomp 0.0.0-stage → 0.1.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/README.md +94 -2
- package/bin/podcomp.js +58 -0
- package/lib.js +270 -0
- package/package.json +42 -4
package/README.md
CHANGED
|
@@ -1,3 +1,95 @@
|
|
|
1
|
-
#
|
|
1
|
+
# `@tomasko/podcomp`
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
The npm launcher for [`podcomp`](https://github.com/tomasky/podcomp) — a
|
|
4
|
+
Compose Spec implementation that runs on Podman, written in MoonBit.
|
|
5
|
+
|
|
6
|
+
```sh
|
|
7
|
+
npm install -g @tomasko/podcomp
|
|
8
|
+
podcomp up
|
|
9
|
+
podcomp ps
|
|
10
|
+
podcomp logs -f web
|
|
11
|
+
podcomp down
|
|
12
|
+
```
|
|
13
|
+
|
|
14
|
+
`podcomp` reads the same `compose.yaml` / `compose.yml` / `docker-compose.yml`
|
|
15
|
+
files Docker Compose reads, merges and interpolates them, and drives `podman`
|
|
16
|
+
directly. There is no daemon, no Python runtime and no background state: one
|
|
17
|
+
native binary.
|
|
18
|
+
|
|
19
|
+
## What this package contains
|
|
20
|
+
|
|
21
|
+
A small launcher, not the binary itself. On the first `podcomp` run it
|
|
22
|
+
downloads the prebuilt native binary for your platform from the GitHub release
|
|
23
|
+
and caches it. Nothing is downloaded at install time.
|
|
24
|
+
|
|
25
|
+
| Platform | Target |
|
|
26
|
+
| --- | --- |
|
|
27
|
+
| Linux x86-64 | `podcomp-linux-x86_64` (static, musl) |
|
|
28
|
+
| Linux arm64 | `podcomp-linux-aarch64` (static, musl) |
|
|
29
|
+
| macOS Apple silicon | `podcomp-darwin-aarch64` |
|
|
30
|
+
|
|
31
|
+
The Linux builds are linked statically against musl, so they run on any
|
|
32
|
+
distribution regardless of its glibc version.
|
|
33
|
+
|
|
34
|
+
## Why there is no `postinstall`
|
|
35
|
+
|
|
36
|
+
This package deliberately declares **no install script**. Since npm 12,
|
|
37
|
+
dependency lifecycle scripts are blocked unless the user opts in; and pnpm 12
|
|
38
|
+
fails a *project* install outright with `ERR_PNPM_IGNORED_BUILDS` when a
|
|
39
|
+
dependency declares one:
|
|
40
|
+
|
|
41
|
+
```console
|
|
42
|
+
$ pnpm add @tomasko/podcomp
|
|
43
|
+
Error: ERR_PNPM_IGNORED_BUILDS
|
|
44
|
+
× adding a new package
|
|
45
|
+
╰─▶ Ignored build scripts: @tomasko/podcomp@0.1.1
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
Downloading lazily from `bin/podcomp.js` sidesteps all of it: it works under
|
|
49
|
+
npm, pnpm, yarn, bun, `npx`, and `--ignore-scripts`, because it is an ordinary
|
|
50
|
+
command rather than an install hook.
|
|
51
|
+
|
|
52
|
+
## Cache location
|
|
53
|
+
|
|
54
|
+
The downloaded binary is kept outside the package, so reinstalling or clearing
|
|
55
|
+
`node_modules` never triggers a second download:
|
|
56
|
+
|
|
57
|
+
| Platform | Path |
|
|
58
|
+
| --- | --- |
|
|
59
|
+
| Linux | `$XDG_CACHE_HOME/podcomp` or `~/.cache/podcomp` |
|
|
60
|
+
| macOS | `~/Library/Caches/podcomp` |
|
|
61
|
+
|
|
62
|
+
## Environment variables
|
|
63
|
+
|
|
64
|
+
| Variable | Effect |
|
|
65
|
+
| --- | --- |
|
|
66
|
+
| `PODCOMP_BINARY` | Run this binary and never download anything |
|
|
67
|
+
| `PODCOMP_CACHE_DIR` | Where to keep the downloaded binary |
|
|
68
|
+
| `PODCOMP_VERSION` | Release version to fetch (default: this package's version) |
|
|
69
|
+
| `PODCOMP_REPO` | `owner/repo` to download from (default: `tomasky/podcomp`) |
|
|
70
|
+
| `PODCOMP_DOWNLOAD_BASE` | Full base URL, overriding `PODCOMP_REPO`/`PODCOMP_VERSION` |
|
|
71
|
+
| `PODCOMP_FORCE_DOWNLOAD` | Re-download even if the cached binary exists |
|
|
72
|
+
| `PODCOMP_SKIP_DOWNLOAD` | Never download; fail if the binary is absent |
|
|
73
|
+
| `PODCOMP_NO_VERIFY` | Skip SHA-256 verification of the download |
|
|
74
|
+
|
|
75
|
+
`PODCOMP_DOWNLOAD_BASE` and `PODCOMP_REPO` are there for forks, private mirrors
|
|
76
|
+
and air-gapped installs:
|
|
77
|
+
|
|
78
|
+
```sh
|
|
79
|
+
PODCOMP_DOWNLOAD_BASE=https://internal.example.com/podcomp npm install -g @tomasko/podcomp
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
## Other ways to install
|
|
83
|
+
|
|
84
|
+
```sh
|
|
85
|
+
# Installer script (no Node.js required)
|
|
86
|
+
curl -LsSf https://raw.githubusercontent.com/tomasky/podcomp/main/install.sh | bash
|
|
87
|
+
|
|
88
|
+
# Build from source
|
|
89
|
+
git clone https://github.com/tomasky/podcomp && cd podcomp
|
|
90
|
+
moon build --target native --release
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
## License
|
|
94
|
+
|
|
95
|
+
Apache-2.0
|
package/bin/podcomp.js
ADDED
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
// Launcher for the native `podcomp` binary.
|
|
3
|
+
//
|
|
4
|
+
// The package ships no install script, so this is where the binary is fetched:
|
|
5
|
+
// on the first run, straight into the user cache. Arguments, stdio, signals and
|
|
6
|
+
// the exit code are all forwarded to the real binary.
|
|
7
|
+
"use strict";
|
|
8
|
+
|
|
9
|
+
const { spawn } = require("child_process");
|
|
10
|
+
const { ensureBinary } = require("../lib.js");
|
|
11
|
+
|
|
12
|
+
const SIGNALS = ["SIGINT", "SIGTERM", "SIGHUP", "SIGQUIT"];
|
|
13
|
+
|
|
14
|
+
async function main() {
|
|
15
|
+
let binary;
|
|
16
|
+
try {
|
|
17
|
+
binary = await ensureBinary({
|
|
18
|
+
log: (line) => process.stderr.write(`${line}\n`),
|
|
19
|
+
});
|
|
20
|
+
} catch (err) {
|
|
21
|
+
process.stderr.write(`podcomp: ${err.message}\n`);
|
|
22
|
+
process.exit(1);
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
const child = spawn(binary, process.argv.slice(2), { stdio: "inherit" });
|
|
26
|
+
|
|
27
|
+
// Forward signals so Ctrl-C reaches the CLI rather than killing only the
|
|
28
|
+
// wrapper and orphaning the child.
|
|
29
|
+
for (const signal of SIGNALS) {
|
|
30
|
+
process.on(signal, () => {
|
|
31
|
+
try {
|
|
32
|
+
child.kill(signal);
|
|
33
|
+
} catch {
|
|
34
|
+
/* already gone */
|
|
35
|
+
}
|
|
36
|
+
});
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
child.on("error", (err) => {
|
|
40
|
+
process.stderr.write(`podcomp: failed to launch ${binary}: ${err.message}\n`);
|
|
41
|
+
process.exit(1);
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
child.on("exit", (code, signal) => {
|
|
45
|
+
if (signal) {
|
|
46
|
+
// Mirror the child's death by signal instead of inventing an exit code.
|
|
47
|
+
process.removeAllListeners(signal);
|
|
48
|
+
process.kill(process.pid, signal);
|
|
49
|
+
return;
|
|
50
|
+
}
|
|
51
|
+
process.exit(code === null ? 1 : code);
|
|
52
|
+
});
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
main().catch((err) => {
|
|
56
|
+
process.stderr.write(`podcomp: ${err && err.stack ? err.stack : err}\n`);
|
|
57
|
+
process.exit(1);
|
|
58
|
+
});
|
package/lib.js
ADDED
|
@@ -0,0 +1,270 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
// Shared logic for the npm launcher: work out which prebuilt `podcomp` binary
|
|
4
|
+
// belongs on this machine, and fetch it from GitHub Releases on demand.
|
|
5
|
+
//
|
|
6
|
+
// podcomp deliberately ships **no install script**. As of npm 12 dependency
|
|
7
|
+
// lifecycle scripts are blocked unless the user opts in, and pnpm 12 fails a
|
|
8
|
+
// *project* install outright with ERR_PNPM_IGNORED_BUILDS when a dependency
|
|
9
|
+
// declares one. Downloading on first run works under every package manager,
|
|
10
|
+
// under `--ignore-scripts`, and under `npx`, so that is what this does.
|
|
11
|
+
//
|
|
12
|
+
// Overrides (all optional):
|
|
13
|
+
// PODCOMP_REPO owner/repo (default: tomasky/podcomp)
|
|
14
|
+
// PODCOMP_VERSION release version (default: this package's version)
|
|
15
|
+
// PODCOMP_DOWNLOAD_BASE full base URL (overrides PODCOMP_REPO/PODCOMP_VERSION)
|
|
16
|
+
// PODCOMP_CACHE_DIR where the binary is kept
|
|
17
|
+
// PODCOMP_BINARY run this binary and never download
|
|
18
|
+
// PODCOMP_FORCE_DOWNLOAD re-download even if the binary is already present
|
|
19
|
+
// PODCOMP_SKIP_DOWNLOAD never download; fail if the binary is absent
|
|
20
|
+
// PODCOMP_NO_VERIFY skip SHA-256 verification
|
|
21
|
+
|
|
22
|
+
const crypto = require("crypto");
|
|
23
|
+
const fs = require("fs");
|
|
24
|
+
const os = require("os");
|
|
25
|
+
const path = require("path");
|
|
26
|
+
const https = require("https");
|
|
27
|
+
const http = require("http");
|
|
28
|
+
|
|
29
|
+
const pkg = require("./package.json");
|
|
30
|
+
|
|
31
|
+
// Node's `platform-arch` -> the release target name. Intel macOS is absent on
|
|
32
|
+
// purpose: the MoonBit toolchain no longer ships a darwin-x86_64 target.
|
|
33
|
+
const TARGETS = {
|
|
34
|
+
"linux-x64": "linux-x86_64",
|
|
35
|
+
"linux-arm64": "linux-aarch64",
|
|
36
|
+
"darwin-arm64": "darwin-aarch64",
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
const SUPPORTED = "linux-x86_64, linux-aarch64 and darwin-aarch64";
|
|
40
|
+
|
|
41
|
+
function targetName() {
|
|
42
|
+
return TARGETS[`${process.platform}-${process.arch}`];
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
function repoSlug() {
|
|
46
|
+
if (process.env.PODCOMP_REPO) return process.env.PODCOMP_REPO;
|
|
47
|
+
const url =
|
|
48
|
+
typeof pkg.repository === "string"
|
|
49
|
+
? pkg.repository
|
|
50
|
+
: (pkg.repository && pkg.repository.url) || "";
|
|
51
|
+
const m = url.match(/github\.com[/:]([^/]+)\/([^/.]+)/);
|
|
52
|
+
return m ? `${m[1]}/${m[2]}` : "tomasky/podcomp";
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
function version() {
|
|
56
|
+
return (process.env.PODCOMP_VERSION || pkg.version).replace(/^v/, "");
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
function assetName(target) {
|
|
60
|
+
return `podcomp-${target}`;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
// Both URLs are tried in order: the pinned version first, then whatever is
|
|
64
|
+
// latest. The fallback keeps the launcher useful when a release was re-cut
|
|
65
|
+
// under a different tag, or when package.json was bumped ahead of the tag.
|
|
66
|
+
function downloadUrls(target) {
|
|
67
|
+
const asset = assetName(target);
|
|
68
|
+
if (process.env.PODCOMP_DOWNLOAD_BASE) {
|
|
69
|
+
const base = process.env.PODCOMP_DOWNLOAD_BASE.replace(/\/+$/, "");
|
|
70
|
+
return [`${base}/${asset}`];
|
|
71
|
+
}
|
|
72
|
+
const repo = repoSlug();
|
|
73
|
+
return [
|
|
74
|
+
`https://github.com/${repo}/releases/download/v${version()}/${asset}`,
|
|
75
|
+
`https://github.com/${repo}/releases/latest/download/${asset}`,
|
|
76
|
+
];
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
function cacheDir() {
|
|
80
|
+
if (process.env.PODCOMP_CACHE_DIR) return process.env.PODCOMP_CACHE_DIR;
|
|
81
|
+
const home = os.homedir();
|
|
82
|
+
if (process.platform === "darwin") {
|
|
83
|
+
return path.join(home, "Library", "Caches", "podcomp");
|
|
84
|
+
}
|
|
85
|
+
const base = process.env.XDG_CACHE_HOME || path.join(home, ".cache");
|
|
86
|
+
return path.join(base, "podcomp");
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
function binaryPath(target) {
|
|
90
|
+
return path.join(cacheDir(), `podcomp-${version()}-${target}`);
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
function isRunnable(file) {
|
|
94
|
+
try {
|
|
95
|
+
fs.accessSync(file, fs.constants.X_OK);
|
|
96
|
+
return true;
|
|
97
|
+
} catch {
|
|
98
|
+
return false;
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
// --- HTTP ------------------------------------------------------------------
|
|
103
|
+
|
|
104
|
+
function fetch(url, redirects) {
|
|
105
|
+
return new Promise((resolve, reject) => {
|
|
106
|
+
if (redirects > 10) {
|
|
107
|
+
reject(new Error("too many redirects"));
|
|
108
|
+
return;
|
|
109
|
+
}
|
|
110
|
+
const client = url.startsWith("http:") ? http : https;
|
|
111
|
+
const req = client.get(
|
|
112
|
+
url,
|
|
113
|
+
{ headers: { "user-agent": `podcomp-npm/${pkg.version}` } },
|
|
114
|
+
(res) => {
|
|
115
|
+
const { statusCode, headers } = res;
|
|
116
|
+
if (statusCode >= 300 && statusCode < 400 && headers.location) {
|
|
117
|
+
res.resume();
|
|
118
|
+
resolve(fetch(new URL(headers.location, url).toString(), redirects + 1));
|
|
119
|
+
return;
|
|
120
|
+
}
|
|
121
|
+
if (statusCode !== 200) {
|
|
122
|
+
res.resume();
|
|
123
|
+
const err = new Error(`GET ${url} -> HTTP ${statusCode}`);
|
|
124
|
+
err.statusCode = statusCode;
|
|
125
|
+
reject(err);
|
|
126
|
+
return;
|
|
127
|
+
}
|
|
128
|
+
resolve(res);
|
|
129
|
+
}
|
|
130
|
+
);
|
|
131
|
+
req.on("error", reject);
|
|
132
|
+
req.setTimeout(120000, () => req.destroy(new Error(`timed out fetching ${url}`)));
|
|
133
|
+
});
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
async function fetchBuffer(url) {
|
|
137
|
+
const res = await fetch(url, 0);
|
|
138
|
+
const chunks = [];
|
|
139
|
+
for await (const chunk of res) chunks.push(chunk);
|
|
140
|
+
return Buffer.concat(chunks);
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
async function fetchText(url) {
|
|
144
|
+
return (await fetchBuffer(url)).toString("utf8");
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
// --- install ---------------------------------------------------------------
|
|
148
|
+
|
|
149
|
+
// Download to a sibling temp file and rename into place, so a crashed or
|
|
150
|
+
// concurrent run can never leave a half-written binary at `dest`.
|
|
151
|
+
async function downloadTo(url, dest) {
|
|
152
|
+
const res = await fetch(url, 0);
|
|
153
|
+
fs.mkdirSync(path.dirname(dest), { recursive: true });
|
|
154
|
+
const tmp = `${dest}.${process.pid}.${Date.now()}.part`;
|
|
155
|
+
try {
|
|
156
|
+
await new Promise((resolve, reject) => {
|
|
157
|
+
const out = fs.createWriteStream(tmp, { mode: 0o755 });
|
|
158
|
+
res.pipe(out);
|
|
159
|
+
out.on("finish", () => out.close(resolve));
|
|
160
|
+
out.on("error", reject);
|
|
161
|
+
res.on("error", reject);
|
|
162
|
+
});
|
|
163
|
+
const size = fs.statSync(tmp).size;
|
|
164
|
+
if (size === 0) throw new Error(`${url} returned an empty file`);
|
|
165
|
+
fs.chmodSync(tmp, 0o755);
|
|
166
|
+
fs.renameSync(tmp, dest);
|
|
167
|
+
} catch (err) {
|
|
168
|
+
fs.rmSync(tmp, { force: true });
|
|
169
|
+
throw err;
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
// Verify against the `<asset>.sha256` published next to the binary. A missing
|
|
174
|
+
// checksum is a warning, not a failure, so older releases keep working.
|
|
175
|
+
async function verify(dest, url, log) {
|
|
176
|
+
if (process.env.PODCOMP_NO_VERIFY) return;
|
|
177
|
+
let published;
|
|
178
|
+
try {
|
|
179
|
+
published = (await fetchText(`${url}.sha256`)).trim();
|
|
180
|
+
} catch {
|
|
181
|
+
log(`podcomp: warning: no checksum published for ${path.basename(url)}`);
|
|
182
|
+
return;
|
|
183
|
+
}
|
|
184
|
+
const expected = (published.split(/\s+/)[0] || "").toLowerCase();
|
|
185
|
+
if (!/^[0-9a-f]{64}$/.test(expected)) {
|
|
186
|
+
log("podcomp: warning: published checksum is not a SHA-256 digest");
|
|
187
|
+
return;
|
|
188
|
+
}
|
|
189
|
+
const actual = crypto.createHash("sha256").update(fs.readFileSync(dest)).digest("hex");
|
|
190
|
+
if (actual !== expected) {
|
|
191
|
+
fs.rmSync(dest, { force: true });
|
|
192
|
+
throw new Error(
|
|
193
|
+
`checksum mismatch for ${path.basename(url)}\n` +
|
|
194
|
+
` expected ${expected}\n actual ${actual}`
|
|
195
|
+
);
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
/**
|
|
200
|
+
* Resolve to a runnable `podcomp` binary, downloading it when it is missing.
|
|
201
|
+
*/
|
|
202
|
+
async function ensureBinary({ force = false, log = () => {} } = {}) {
|
|
203
|
+
if (process.env.PODCOMP_BINARY) {
|
|
204
|
+
if (!fs.existsSync(process.env.PODCOMP_BINARY)) {
|
|
205
|
+
throw new Error(
|
|
206
|
+
`PODCOMP_BINARY points at ${process.env.PODCOMP_BINARY}, which does not exist`
|
|
207
|
+
);
|
|
208
|
+
}
|
|
209
|
+
return process.env.PODCOMP_BINARY;
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
const target = targetName();
|
|
213
|
+
if (!target) {
|
|
214
|
+
throw new Error(
|
|
215
|
+
`podcomp does not ship a binary for ${process.platform}/${process.arch}; ` +
|
|
216
|
+
`supported: ${SUPPORTED}. Build from source instead: ` +
|
|
217
|
+
`https://github.com/${repoSlug()}#build-from-source`
|
|
218
|
+
);
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
const dest = binaryPath(target);
|
|
222
|
+
const wanted = force || process.env.PODCOMP_FORCE_DOWNLOAD;
|
|
223
|
+
if (!wanted && isRunnable(dest)) return dest;
|
|
224
|
+
|
|
225
|
+
if (process.env.PODCOMP_SKIP_DOWNLOAD) {
|
|
226
|
+
if (isRunnable(dest)) return dest;
|
|
227
|
+
throw new Error(
|
|
228
|
+
`PODCOMP_SKIP_DOWNLOAD is set but no podcomp binary is installed at ${dest}`
|
|
229
|
+
);
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
const urls = downloadUrls(target);
|
|
233
|
+
let lastError;
|
|
234
|
+
for (const url of urls) {
|
|
235
|
+
try {
|
|
236
|
+
log(`podcomp: downloading ${url}`);
|
|
237
|
+
await downloadTo(url, dest);
|
|
238
|
+
await verify(dest, url, log);
|
|
239
|
+
log(`podcomp: installed ${dest}`);
|
|
240
|
+
return dest;
|
|
241
|
+
} catch (err) {
|
|
242
|
+
lastError = err;
|
|
243
|
+
log(`podcomp: ${err.message}`);
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
throw new Error(
|
|
248
|
+
`could not download the podcomp binary for ${target}.\n` +
|
|
249
|
+
`Tried:\n${urls.map((u) => ` ${u}`).join("\n")}\n` +
|
|
250
|
+
`Last error: ${lastError && lastError.message}\n\n` +
|
|
251
|
+
`Install it another way instead:\n` +
|
|
252
|
+
` curl -LsSf https://raw.githubusercontent.com/${repoSlug()}/main/install.sh | bash\n` +
|
|
253
|
+
`or set PODCOMP_DOWNLOAD_BASE to a mirror.`
|
|
254
|
+
);
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
module.exports = {
|
|
258
|
+
pkg,
|
|
259
|
+
TARGETS,
|
|
260
|
+
SUPPORTED,
|
|
261
|
+
targetName,
|
|
262
|
+
repoSlug,
|
|
263
|
+
version,
|
|
264
|
+
assetName,
|
|
265
|
+
downloadUrls,
|
|
266
|
+
cacheDir,
|
|
267
|
+
binaryPath,
|
|
268
|
+
isRunnable,
|
|
269
|
+
ensureBinary,
|
|
270
|
+
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,44 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@tomasko/podcomp",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"
|
|
5
|
-
"
|
|
6
|
-
|
|
3
|
+
"version": "0.1.1",
|
|
4
|
+
"description": "Run Compose files with Podman , a single native binary, no daemon, no Python.",
|
|
5
|
+
"license": "Apache-2.0",
|
|
6
|
+
"bin": {
|
|
7
|
+
"podcomp": "bin/podcomp.js"
|
|
8
|
+
},
|
|
9
|
+
"files": [
|
|
10
|
+
"bin/podcomp.js",
|
|
11
|
+
"lib.js",
|
|
12
|
+
"README.md"
|
|
13
|
+
],
|
|
14
|
+
"engines": {
|
|
15
|
+
"node": ">=18"
|
|
16
|
+
},
|
|
17
|
+
"os": [
|
|
18
|
+
"darwin",
|
|
19
|
+
"linux"
|
|
20
|
+
],
|
|
21
|
+
"cpu": [
|
|
22
|
+
"x64",
|
|
23
|
+
"arm64"
|
|
24
|
+
],
|
|
25
|
+
"scripts": {
|
|
26
|
+
"test": "node --test"
|
|
27
|
+
},
|
|
28
|
+
"repository": {
|
|
29
|
+
"type": "git",
|
|
30
|
+
"url": "https://github.com/tomasky/podcomp.git"
|
|
31
|
+
},
|
|
32
|
+
"homepage": "https://github.com/tomasky/podcomp",
|
|
33
|
+
"bugs": "https://github.com/tomasky/podcomp/issues",
|
|
34
|
+
"keywords": [
|
|
35
|
+
"podman",
|
|
36
|
+
"docker-compose",
|
|
37
|
+
"compose",
|
|
38
|
+
"containers",
|
|
39
|
+
"orchestration",
|
|
40
|
+
"cli",
|
|
41
|
+
"moonbit"
|
|
42
|
+
],
|
|
43
|
+
"preferGlobal": true
|
|
44
|
+
}
|