infynon 0.2.6 → 0.2.7
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 +56 -8
- package/package.json +53 -34
- package/postinstall.js +302 -16
- package/run.js +245 -35
package/README.md
CHANGED
|
@@ -1,19 +1,26 @@
|
|
|
1
1
|
# infynon
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
[](https://www.npmjs.com/package/infynon)
|
|
4
|
+
[](https://www.npmjs.com/package/infynon)
|
|
5
|
+
[](https://github.com/d4rkNinja/infynon-cli/releases)
|
|
6
|
+
[](https://github.com/d4rkNinja/infynon-cli/blob/main/docs/agent-control-plane.md)
|
|
7
|
+
[](https://github.com/d4rkNinja/infynon-cli/blob/main/docs/commands.md)
|
|
4
8
|
|
|
5
|
-
|
|
9
|
+
INFYNON is a terminal control plane for agentic engineering: multi-agent workspace/task orchestration, package intelligence, API flow testing, and repository memory in one native CLI.
|
|
10
|
+
|
|
11
|
+
This npm package is the official wrapper for the INFYNON native binary. npm installs the matching optional platform package when available; otherwise the wrapper downloads and verifies the matching GitHub Release binary on first launch. The public distribution repo includes installers, npm/go wrappers, docs, and release assets; the core Rust implementation is not included.
|
|
6
12
|
|
|
7
13
|
## Why Install INFYNON
|
|
8
14
|
|
|
9
|
-
INFYNON is built for teams that need one terminal tool for
|
|
15
|
+
INFYNON is built for teams that need one terminal tool for five connected workflows:
|
|
10
16
|
|
|
11
17
|
| Workflow | Command Area | Purpose |
|
|
12
18
|
|---|---|---|
|
|
19
|
+
| Agent control plane | `infynon workspace`, `infynon task`, `infynon coding` | Coordinate Codex, Claude Code, Gemini CLI, and child agent sessions through durable workspace and task records. |
|
|
13
20
|
| Package intelligence | `infynon pkg` | Scan dependencies, inspect risk, audit package changes, and support safer install workflows. |
|
|
14
21
|
| API flow testing | `infynon weave` | Run multi-step API flows with context passed between requests. |
|
|
15
22
|
| Repository memory | `infynon trace` | Preserve structured handoff notes, branch context, package ownership, and repo memory. |
|
|
16
|
-
| Agent task contracts |
|
|
23
|
+
| Agent task contracts | GCCD briefs | Turn vague AI work requests into Goal, Context, Constraints, and Done When. |
|
|
17
24
|
|
|
18
25
|
## Install
|
|
19
26
|
|
|
@@ -21,7 +28,7 @@ INFYNON is built for teams that need one terminal tool for four connected workfl
|
|
|
21
28
|
npm install -g infynon
|
|
22
29
|
```
|
|
23
30
|
|
|
24
|
-
|
|
31
|
+
npm installs the wrapper and, when available, the matching optional native package for the current platform. If the optional package is unavailable, first launch downloads and verifies the matching GitHub Release binary.
|
|
25
32
|
|
|
26
33
|
## Supported Platforms
|
|
27
34
|
|
|
@@ -33,17 +40,55 @@ The installer downloads the binary for the current platform and makes `infynon`
|
|
|
33
40
|
|
|
34
41
|
Unsupported platforms can still install the npm wrapper, but the wrapper will not be able to download a native binary until a matching release asset exists.
|
|
35
42
|
|
|
43
|
+
## Provenance and Platform Packages
|
|
44
|
+
|
|
45
|
+
INFYNON npm packages are configured for npm provenance from the release pipeline. Provenance links the published package to the GitHub Actions workflow that produced it.
|
|
46
|
+
|
|
47
|
+
The package can use optional native binary packages:
|
|
48
|
+
|
|
49
|
+
- `@infynon/cli-win32-x64`
|
|
50
|
+
- `@infynon/cli-linux-x64`
|
|
51
|
+
- `@infynon/cli-linux-arm64`
|
|
52
|
+
- `@infynon/cli-darwin-x64`
|
|
53
|
+
- `@infynon/cli-darwin-arm64`
|
|
54
|
+
|
|
36
55
|
## Quick Start
|
|
37
56
|
|
|
38
57
|
```bash
|
|
39
58
|
infynon --help
|
|
59
|
+
infynon workspace agent-root-show
|
|
40
60
|
infynon pkg scan
|
|
41
61
|
infynon pkg audit
|
|
42
62
|
infynon weave flow run checkout
|
|
43
63
|
infynon trace tui
|
|
44
|
-
infynon task create task_001 --mutate --workspace
|
|
64
|
+
infynon task create task_001 --mutate --workspace app --agent codex --prompt "Ship the settings API patch"
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
## Agent Control Plane
|
|
68
|
+
|
|
69
|
+
Use INFYNON when one lead session needs to coordinate child coding agents.
|
|
70
|
+
|
|
71
|
+
```bash
|
|
72
|
+
infynon workspace agent-root-set --mutate --path D:/Codeverse/infynon-agent
|
|
73
|
+
infynon workspace create app --mutate --folder-name web --path D:/Codeverse/app --default
|
|
74
|
+
|
|
75
|
+
infynon task create task_ui_review \
|
|
76
|
+
--mutate \
|
|
77
|
+
--workspace app \
|
|
78
|
+
--folder-name web \
|
|
79
|
+
--agent claude \
|
|
80
|
+
--prompt "Review the settings UI change. Do not edit backend files. Done when findings are recorded."
|
|
81
|
+
|
|
82
|
+
infynon coding tui
|
|
45
83
|
```
|
|
46
84
|
|
|
85
|
+
Good fit:
|
|
86
|
+
|
|
87
|
+
- parent and child agent work
|
|
88
|
+
- Codex, Claude Code, and Gemini CLI sessions launched from the right workspace
|
|
89
|
+
- task retries where context and completion criteria must stay intact
|
|
90
|
+
- reviewable handoffs between agents and humans
|
|
91
|
+
|
|
47
92
|
## Package Intelligence
|
|
48
93
|
|
|
49
94
|
Use `infynon pkg` to inspect dependency risk and package state.
|
|
@@ -113,7 +158,6 @@ infynon task create task_001 \
|
|
|
113
158
|
|
|
114
159
|
Good fit:
|
|
115
160
|
|
|
116
|
-
- parent and child agent work
|
|
117
161
|
- scoped implementation tasks
|
|
118
162
|
- reviewable handoffs
|
|
119
163
|
- retries where completion criteria must stay intact
|
|
@@ -155,10 +199,14 @@ https://github.com/d4rkNinja/infynon-cli/releases
|
|
|
155
199
|
## Documentation
|
|
156
200
|
|
|
157
201
|
- Public docs: https://github.com/d4rkNinja/infynon-cli/tree/main/docs
|
|
202
|
+
- Agent control plane: https://github.com/d4rkNinja/infynon-cli/blob/main/docs/agent-control-plane.md
|
|
203
|
+
- AI agent workflow: https://github.com/d4rkNinja/infynon-cli/blob/main/docs/ai-agent-workflow.md
|
|
158
204
|
- GCCD task contracts: https://github.com/d4rkNinja/infynon-cli/blob/main/docs/gccd.md
|
|
205
|
+
- npm install: https://github.com/d4rkNinja/infynon-cli/blob/main/docs/npm-install.md
|
|
206
|
+
- Windows troubleshooting: https://github.com/d4rkNinja/infynon-cli/blob/main/docs/windows-troubleshooting.md
|
|
159
207
|
- Releases: https://github.com/d4rkNinja/infynon-cli/releases
|
|
160
208
|
- Issues: https://github.com/d4rkNinja/infynon-cli/issues
|
|
161
209
|
|
|
162
210
|
## Source Availability
|
|
163
211
|
|
|
164
|
-
This npm package distributes the INFYNON binary and installer wrapper only. The Rust
|
|
212
|
+
This npm package distributes the INFYNON binary and installer wrapper only. The core Rust implementation is not bundled in this package.
|
package/package.json
CHANGED
|
@@ -1,36 +1,55 @@
|
|
|
1
1
|
{
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
"
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
"
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
"
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
"
|
|
33
|
-
"
|
|
34
|
-
|
|
35
|
-
|
|
2
|
+
"name": "infynon",
|
|
3
|
+
"version": "0.2.7",
|
|
4
|
+
"description": "Security-first CLI for AI-assisted development: safe package installs, dependency scanning, API flow testing, and agent task orchestration.",
|
|
5
|
+
"bin": {
|
|
6
|
+
"infynon": "run.js",
|
|
7
|
+
"infynon-pkg": "run-pkg.js"
|
|
8
|
+
},
|
|
9
|
+
"files": [
|
|
10
|
+
"README.md",
|
|
11
|
+
"LICENSE",
|
|
12
|
+
"run.js",
|
|
13
|
+
"run-pkg.js",
|
|
14
|
+
"postinstall.js",
|
|
15
|
+
"preuninstall.js"
|
|
16
|
+
],
|
|
17
|
+
"optionalDependencies": {
|
|
18
|
+
"@infynon/cli-win32-x64": "0.2.7",
|
|
19
|
+
"@infynon/cli-linux-x64": "0.2.7",
|
|
20
|
+
"@infynon/cli-linux-arm64": "0.2.7",
|
|
21
|
+
"@infynon/cli-darwin-x64": "0.2.7",
|
|
22
|
+
"@infynon/cli-darwin-arm64": "0.2.7"
|
|
23
|
+
},
|
|
24
|
+
"engines": {
|
|
25
|
+
"node": ">=18"
|
|
26
|
+
},
|
|
27
|
+
"packageManager": "npm@11.8.0",
|
|
28
|
+
"author": "d4rkNinja",
|
|
29
|
+
"license": "SEE LICENSE IN LICENSE",
|
|
30
|
+
"repository": {
|
|
31
|
+
"type": "git",
|
|
32
|
+
"url": "git+https://github.com/d4rkNinja/infynon-cli.git",
|
|
33
|
+
"directory": "npm"
|
|
34
|
+
},
|
|
35
|
+
"bugs": {
|
|
36
|
+
"url": "https://github.com/d4rkNinja/infynon-cli/issues"
|
|
37
|
+
},
|
|
38
|
+
"homepage": "https://cli.infynon.com",
|
|
39
|
+
"publishConfig": {
|
|
40
|
+
"access": "public",
|
|
41
|
+
"provenance": true
|
|
42
|
+
},
|
|
43
|
+
"keywords": [
|
|
44
|
+
"cli",
|
|
45
|
+
"security",
|
|
46
|
+
"supply-chain",
|
|
47
|
+
"dependency-scanning",
|
|
48
|
+
"ai-agents",
|
|
49
|
+
"claude-code",
|
|
50
|
+
"codex",
|
|
51
|
+
"gemini-cli",
|
|
52
|
+
"api-testing",
|
|
53
|
+
"devtools"
|
|
54
|
+
]
|
|
36
55
|
}
|
package/postinstall.js
CHANGED
|
@@ -13,13 +13,49 @@ const BIN_DIR = path.join(__dirname, "bin");
|
|
|
13
13
|
const BIN_PATH = path.join(BIN_DIR, process.platform === "win32" ? "infynon.exe" : "infynon");
|
|
14
14
|
const TEMP_BIN_PATH = BIN_PATH + ".download-" + process.pid;
|
|
15
15
|
const TEMP_CHECKSUMS_PATH = BIN_PATH + ".checksums-" + process.pid + ".txt";
|
|
16
|
+
const TEMP_MANIFEST_PATH = BIN_PATH + ".manifest-" + process.pid + ".json";
|
|
16
17
|
|
|
17
18
|
function getTarget() {
|
|
18
|
-
if (process.platform === "win32" && process.arch === "x64")
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
19
|
+
if (process.platform === "win32" && process.arch === "x64") {
|
|
20
|
+
return {
|
|
21
|
+
target: "x86_64-pc-windows-msvc",
|
|
22
|
+
ext: ".exe",
|
|
23
|
+
packageName: "@infynon/cli-win32-x64",
|
|
24
|
+
binaryName: "infynon.exe",
|
|
25
|
+
};
|
|
26
|
+
}
|
|
27
|
+
if (process.platform === "linux" && process.arch === "x64") {
|
|
28
|
+
return {
|
|
29
|
+
target: "x86_64-unknown-linux-musl",
|
|
30
|
+
ext: "",
|
|
31
|
+
packageName: "@infynon/cli-linux-x64",
|
|
32
|
+
binaryName: "infynon",
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
if (process.platform === "linux" && process.arch === "arm64") {
|
|
36
|
+
return {
|
|
37
|
+
target: "aarch64-unknown-linux-musl",
|
|
38
|
+
ext: "",
|
|
39
|
+
packageName: "@infynon/cli-linux-arm64",
|
|
40
|
+
binaryName: "infynon",
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
if (process.platform === "darwin" && process.arch === "x64") {
|
|
44
|
+
return {
|
|
45
|
+
target: "x86_64-apple-darwin",
|
|
46
|
+
ext: "",
|
|
47
|
+
packageName: "@infynon/cli-darwin-x64",
|
|
48
|
+
binaryName: "infynon",
|
|
49
|
+
};
|
|
50
|
+
}
|
|
51
|
+
if (process.platform === "darwin" && process.arch === "arm64") {
|
|
52
|
+
return {
|
|
53
|
+
target: "aarch64-apple-darwin",
|
|
54
|
+
ext: "",
|
|
55
|
+
packageName: "@infynon/cli-darwin-arm64",
|
|
56
|
+
binaryName: "infynon",
|
|
57
|
+
};
|
|
58
|
+
}
|
|
23
59
|
return null;
|
|
24
60
|
}
|
|
25
61
|
|
|
@@ -82,6 +118,14 @@ function removeQuietly(filePath) {
|
|
|
82
118
|
} catch (_) {}
|
|
83
119
|
}
|
|
84
120
|
|
|
121
|
+
function isFile(filePath) {
|
|
122
|
+
try {
|
|
123
|
+
return fs.statSync(filePath).isFile();
|
|
124
|
+
} catch (_) {
|
|
125
|
+
return false;
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
|
|
85
129
|
function checksumForAsset(checksumsText, assetName) {
|
|
86
130
|
const lines = checksumsText.split(/\r?\n/);
|
|
87
131
|
for (const line of lines) {
|
|
@@ -106,19 +150,190 @@ function verifyChecksum(checksumsPath, filePath, assetName) {
|
|
|
106
150
|
}
|
|
107
151
|
}
|
|
108
152
|
|
|
109
|
-
function
|
|
110
|
-
const
|
|
153
|
+
function unavailable(message) {
|
|
154
|
+
const err = new Error(message);
|
|
155
|
+
err.fallbackAllowed = true;
|
|
156
|
+
return err;
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
function integrityFailure(message) {
|
|
160
|
+
const err = new Error(message);
|
|
161
|
+
err.integrityFailure = true;
|
|
162
|
+
return err;
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
function basenameMatches(value, assetName) {
|
|
166
|
+
return typeof value === "string" && path.basename(value) === assetName;
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
function entryName(entry) {
|
|
170
|
+
if (!entry || typeof entry !== "object") {
|
|
171
|
+
return null;
|
|
172
|
+
}
|
|
173
|
+
return entry.name || entry.filename || entry.file || entry.path || entry.asset || entry.asset_name || null;
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
function objectEntryForAsset(objectValue, assetName) {
|
|
177
|
+
if (!objectValue || typeof objectValue !== "object" || Array.isArray(objectValue)) {
|
|
178
|
+
return null;
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
if (Object.prototype.hasOwnProperty.call(objectValue, assetName)) {
|
|
182
|
+
const value = objectValue[assetName];
|
|
183
|
+
if (value && typeof value === "object") {
|
|
184
|
+
return Object.assign({ name: assetName }, value);
|
|
185
|
+
}
|
|
186
|
+
return { name: assetName, sha256: value };
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
for (const key of Object.keys(objectValue)) {
|
|
190
|
+
const value = objectValue[key];
|
|
191
|
+
if (path.basename(key) === assetName) {
|
|
192
|
+
if (value && typeof value === "object") {
|
|
193
|
+
return Object.assign({ name: key }, value);
|
|
194
|
+
}
|
|
195
|
+
return { name: key, sha256: value };
|
|
196
|
+
}
|
|
197
|
+
if (value && typeof value === "object" && basenameMatches(entryName(value), assetName)) {
|
|
198
|
+
return value;
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
return null;
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
function findManifestEntry(manifest, assetName) {
|
|
206
|
+
if (basenameMatches(entryName(manifest), assetName)) {
|
|
207
|
+
return manifest;
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
const collections = [
|
|
211
|
+
manifest && manifest.assets,
|
|
212
|
+
manifest && manifest.files,
|
|
213
|
+
manifest && manifest.binaries,
|
|
214
|
+
manifest && manifest.artifacts,
|
|
215
|
+
manifest && manifest.release_assets,
|
|
216
|
+
];
|
|
217
|
+
|
|
218
|
+
for (const collection of collections) {
|
|
219
|
+
if (Array.isArray(collection)) {
|
|
220
|
+
for (const entry of collection) {
|
|
221
|
+
if (basenameMatches(entryName(entry), assetName)) {
|
|
222
|
+
return entry;
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
} else {
|
|
226
|
+
const entry = objectEntryForAsset(collection, assetName);
|
|
227
|
+
if (entry) {
|
|
228
|
+
return entry;
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
return objectEntryForAsset(manifest, assetName);
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
function fieldValue(entry, names) {
|
|
237
|
+
for (const name of names) {
|
|
238
|
+
if (Object.prototype.hasOwnProperty.call(entry, name)) {
|
|
239
|
+
return entry[name];
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
return null;
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
function normalizeSha256(value) {
|
|
246
|
+
if (typeof value !== "string") {
|
|
247
|
+
return null;
|
|
248
|
+
}
|
|
249
|
+
const normalized = value.trim().toLowerCase().replace(/^sha256[:=\s]+/, "");
|
|
250
|
+
if (/^[a-f0-9]{64}$/.test(normalized)) {
|
|
251
|
+
return normalized;
|
|
252
|
+
}
|
|
253
|
+
return null;
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
function normalizeSize(value) {
|
|
257
|
+
if (typeof value === "number" && Number.isSafeInteger(value) && value >= 0) {
|
|
258
|
+
return value;
|
|
259
|
+
}
|
|
260
|
+
if (typeof value === "string" && /^\d+$/.test(value.trim())) {
|
|
261
|
+
const parsed = Number(value.trim());
|
|
262
|
+
if (Number.isSafeInteger(parsed)) {
|
|
263
|
+
return parsed;
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
return null;
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
function manifestVerificationForAsset(manifestPath, assetName) {
|
|
270
|
+
let manifest;
|
|
271
|
+
try {
|
|
272
|
+
manifest = JSON.parse(fs.readFileSync(manifestPath, "utf8"));
|
|
273
|
+
} catch (err) {
|
|
274
|
+
throw unavailable("release-manifest.json is not valid JSON: " + err.message);
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
const entry = findManifestEntry(manifest, assetName);
|
|
278
|
+
if (!entry) {
|
|
279
|
+
throw unavailable("release-manifest.json does not include " + assetName);
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
const sha256 = normalizeSha256(
|
|
283
|
+
fieldValue(entry, ["sha256", "sha_256", "sha256sum", "checksum", "digest"])
|
|
284
|
+
);
|
|
285
|
+
if (!sha256) {
|
|
286
|
+
throw unavailable("release-manifest.json does not include a SHA-256 for " + assetName);
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
const size = normalizeSize(fieldValue(entry, ["size", "size_bytes", "bytes", "length"]));
|
|
290
|
+
if (size === null) {
|
|
291
|
+
throw unavailable("release-manifest.json does not include a size for " + assetName);
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
return { sha256: sha256, size: size };
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
async function fetchManifestVerification(manifestUrl, assetName) {
|
|
298
|
+
try {
|
|
299
|
+
removeQuietly(TEMP_MANIFEST_PATH);
|
|
300
|
+
await downloadFile(manifestUrl, TEMP_MANIFEST_PATH);
|
|
301
|
+
return manifestVerificationForAsset(TEMP_MANIFEST_PATH, assetName);
|
|
302
|
+
} catch (err) {
|
|
303
|
+
if (err && err.integrityFailure) {
|
|
304
|
+
throw err;
|
|
305
|
+
}
|
|
306
|
+
console.warn("[infynon] release-manifest.json verification unavailable: " + err.message);
|
|
307
|
+
console.warn("[infynon] Falling back to checksums.txt.");
|
|
308
|
+
return null;
|
|
309
|
+
} finally {
|
|
310
|
+
removeQuietly(TEMP_MANIFEST_PATH);
|
|
311
|
+
}
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
function verifyManifestAsset(expected, filePath, assetName) {
|
|
315
|
+
const actualSize = fs.statSync(filePath).size;
|
|
316
|
+
if (actualSize !== expected.size) {
|
|
317
|
+
throw integrityFailure("Size mismatch for " + assetName);
|
|
318
|
+
}
|
|
319
|
+
const actualSha256 = sha256File(filePath);
|
|
320
|
+
if (actualSha256 !== expected.sha256) {
|
|
321
|
+
throw integrityFailure("SHA-256 mismatch for " + assetName);
|
|
322
|
+
}
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
function verifyBinary(binaryPath, label) {
|
|
326
|
+
const result = spawnSync(binaryPath, ["--version"], {
|
|
111
327
|
encoding: "utf8",
|
|
112
328
|
windowsHide: true,
|
|
113
329
|
});
|
|
114
330
|
if (result.error) {
|
|
115
|
-
throw new Error("
|
|
331
|
+
throw new Error(label + " is not executable: " + result.error.message);
|
|
116
332
|
}
|
|
117
333
|
if (result.status !== 0) {
|
|
118
334
|
const detail = (result.stderr || result.stdout || "").trim();
|
|
119
335
|
throw new Error(
|
|
120
|
-
"
|
|
121
|
-
(detail ? ": " + detail : " with exit code " + result.status)
|
|
336
|
+
label + " failed verification" + (detail ? ": " + detail : " with exit code " + result.status)
|
|
122
337
|
);
|
|
123
338
|
}
|
|
124
339
|
const versionFields = String((result.stdout || "") + " " + (result.stderr || ""))
|
|
@@ -128,24 +343,85 @@ function verifyBinary() {
|
|
|
128
343
|
return field.replace(/^v/, "");
|
|
129
344
|
});
|
|
130
345
|
if (versionFields.indexOf(VERSION) === -1) {
|
|
131
|
-
throw new Error("
|
|
346
|
+
throw new Error(label + " did not report version " + VERSION);
|
|
132
347
|
}
|
|
133
348
|
}
|
|
134
349
|
|
|
350
|
+
function resolvePlatformPackageBinary(info) {
|
|
351
|
+
let packageJsonPath;
|
|
352
|
+
try {
|
|
353
|
+
packageJsonPath = require.resolve(info.packageName + "/package.json", { paths: [__dirname] });
|
|
354
|
+
} catch (err) {
|
|
355
|
+
if (err && err.code !== "MODULE_NOT_FOUND") {
|
|
356
|
+
console.warn("[infynon] Could not inspect " + info.packageName + ": " + err.message);
|
|
357
|
+
}
|
|
358
|
+
return null;
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
let packageVersion = null;
|
|
362
|
+
try {
|
|
363
|
+
packageVersion = JSON.parse(fs.readFileSync(packageJsonPath, "utf8")).version;
|
|
364
|
+
} catch (err) {
|
|
365
|
+
console.warn("[infynon] Could not read " + info.packageName + " metadata: " + err.message);
|
|
366
|
+
return null;
|
|
367
|
+
}
|
|
368
|
+
|
|
369
|
+
if (packageVersion !== VERSION) {
|
|
370
|
+
console.warn(
|
|
371
|
+
"[infynon] Ignoring " +
|
|
372
|
+
info.packageName +
|
|
373
|
+
" " +
|
|
374
|
+
packageVersion +
|
|
375
|
+
"; expected wrapper version " +
|
|
376
|
+
VERSION +
|
|
377
|
+
"."
|
|
378
|
+
);
|
|
379
|
+
return null;
|
|
380
|
+
}
|
|
381
|
+
|
|
382
|
+
const binaryPath = path.join(path.dirname(packageJsonPath), "bin", info.binaryName);
|
|
383
|
+
if (!isFile(binaryPath)) {
|
|
384
|
+
console.warn("[infynon] Ignoring " + info.packageName + "; missing binary at " + binaryPath + ".");
|
|
385
|
+
return null;
|
|
386
|
+
}
|
|
387
|
+
|
|
388
|
+
return { path: binaryPath, packageName: info.packageName };
|
|
389
|
+
}
|
|
390
|
+
|
|
135
391
|
async function main() {
|
|
136
392
|
const info = getTarget();
|
|
137
393
|
|
|
138
394
|
if (!info) {
|
|
139
395
|
console.warn(
|
|
140
|
-
"[infynon] Unsupported platform: " +
|
|
141
|
-
|
|
396
|
+
"[infynon] Unsupported platform: " +
|
|
397
|
+
process.platform +
|
|
398
|
+
" " +
|
|
399
|
+
process.arch +
|
|
400
|
+
".\n" +
|
|
401
|
+
" Download a release manually: https://github.com/" +
|
|
402
|
+
REPO +
|
|
403
|
+
"/releases"
|
|
142
404
|
);
|
|
143
405
|
process.exit(0);
|
|
144
406
|
}
|
|
145
407
|
|
|
408
|
+
const platformBinary = resolvePlatformPackageBinary(info);
|
|
409
|
+
if (platformBinary) {
|
|
410
|
+
try {
|
|
411
|
+
verifyBinary(platformBinary.path, "Installed platform package " + platformBinary.packageName);
|
|
412
|
+
} catch (err) {
|
|
413
|
+
console.error("[infynon] Platform package verification failed: " + err.message);
|
|
414
|
+
console.error("[infynon] Reinstall after the package is corrected: npm install -g infynon");
|
|
415
|
+
process.exit(1);
|
|
416
|
+
}
|
|
417
|
+
console.log("[infynon] Using installed native package " + platformBinary.packageName + ".");
|
|
418
|
+
return;
|
|
419
|
+
}
|
|
420
|
+
|
|
146
421
|
const tag = "v" + VERSION;
|
|
147
422
|
const assetName = "infynon-" + info.target + info.ext;
|
|
148
423
|
const url = "https://github.com/" + REPO + "/releases/download/" + tag + "/" + assetName;
|
|
424
|
+
const manifestUrl = "https://github.com/" + REPO + "/releases/download/" + tag + "/release-manifest.json";
|
|
149
425
|
const checksumsUrl = "https://github.com/" + REPO + "/releases/download/" + tag + "/checksums.txt";
|
|
150
426
|
|
|
151
427
|
if (!fs.existsSync(BIN_DIR)) {
|
|
@@ -157,19 +433,29 @@ async function main() {
|
|
|
157
433
|
try {
|
|
158
434
|
removeQuietly(TEMP_BIN_PATH);
|
|
159
435
|
removeQuietly(TEMP_CHECKSUMS_PATH);
|
|
436
|
+
removeQuietly(TEMP_MANIFEST_PATH);
|
|
437
|
+
|
|
438
|
+
const manifestVerification = await fetchManifestVerification(manifestUrl, assetName);
|
|
160
439
|
await downloadFile(url, TEMP_BIN_PATH);
|
|
161
|
-
|
|
162
|
-
|
|
440
|
+
if (manifestVerification) {
|
|
441
|
+
verifyManifestAsset(manifestVerification, TEMP_BIN_PATH, assetName);
|
|
442
|
+
} else {
|
|
443
|
+
await downloadFile(checksumsUrl, TEMP_CHECKSUMS_PATH);
|
|
444
|
+
verifyChecksum(TEMP_CHECKSUMS_PATH, TEMP_BIN_PATH, assetName);
|
|
445
|
+
}
|
|
446
|
+
|
|
163
447
|
removeQuietly(BIN_PATH);
|
|
164
448
|
fs.renameSync(TEMP_BIN_PATH, BIN_PATH);
|
|
165
449
|
} catch (err) {
|
|
166
450
|
removeQuietly(TEMP_BIN_PATH);
|
|
167
451
|
removeQuietly(TEMP_CHECKSUMS_PATH);
|
|
452
|
+
removeQuietly(TEMP_MANIFEST_PATH);
|
|
168
453
|
console.error("[infynon] Download failed: " + err.message);
|
|
169
454
|
console.error("[infynon] Manual install: https://github.com/" + REPO + "/releases/tag/" + tag);
|
|
170
455
|
process.exit(1);
|
|
171
456
|
} finally {
|
|
172
457
|
removeQuietly(TEMP_CHECKSUMS_PATH);
|
|
458
|
+
removeQuietly(TEMP_MANIFEST_PATH);
|
|
173
459
|
}
|
|
174
460
|
|
|
175
461
|
if (process.platform !== "win32") {
|
|
@@ -177,7 +463,7 @@ async function main() {
|
|
|
177
463
|
}
|
|
178
464
|
|
|
179
465
|
try {
|
|
180
|
-
verifyBinary();
|
|
466
|
+
verifyBinary(BIN_PATH, "Downloaded binary");
|
|
181
467
|
} catch (err) {
|
|
182
468
|
removeQuietly(BIN_PATH);
|
|
183
469
|
console.error("[infynon] Binary verification failed: " + err.message);
|
package/run.js
CHANGED
|
@@ -1,55 +1,265 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
"use strict";
|
|
3
3
|
|
|
4
|
-
const path = require("path");
|
|
5
|
-
const { spawnSync } = require("child_process");
|
|
6
4
|
const fs = require("fs");
|
|
5
|
+
const path = require("path");
|
|
6
|
+
const { spawn } = require("child_process");
|
|
7
7
|
|
|
8
|
-
const
|
|
9
|
-
|
|
8
|
+
const PACKAGE_DIR = __dirname;
|
|
9
|
+
const VERSION = require("./package.json").version;
|
|
10
|
+
const RELEASES_URL = "https://github.com/d4rkNinja/infynon-cli/releases";
|
|
11
|
+
const LOCAL_BIN_PATH = path.join(
|
|
12
|
+
PACKAGE_DIR,
|
|
10
13
|
"bin",
|
|
11
14
|
process.platform === "win32" ? "infynon.exe" : "infynon"
|
|
12
15
|
);
|
|
16
|
+
const POSTINSTALL_PATH = path.join(PACKAGE_DIR, "postinstall.js");
|
|
13
17
|
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
if (process.platform === "
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
18
|
+
function getPlatformPackage() {
|
|
19
|
+
if (process.platform === "win32" && process.arch === "x64") {
|
|
20
|
+
return { packageName: "@infynon/cli-win32-x64", binaryName: "infynon.exe" };
|
|
21
|
+
}
|
|
22
|
+
if (process.platform === "linux" && process.arch === "x64") {
|
|
23
|
+
return { packageName: "@infynon/cli-linux-x64", binaryName: "infynon" };
|
|
24
|
+
}
|
|
25
|
+
if (process.platform === "linux" && process.arch === "arm64") {
|
|
26
|
+
return { packageName: "@infynon/cli-linux-arm64", binaryName: "infynon" };
|
|
27
|
+
}
|
|
28
|
+
if (process.platform === "darwin" && process.arch === "x64") {
|
|
29
|
+
return { packageName: "@infynon/cli-darwin-x64", binaryName: "infynon" };
|
|
30
|
+
}
|
|
31
|
+
if (process.platform === "darwin" && process.arch === "arm64") {
|
|
32
|
+
return { packageName: "@infynon/cli-darwin-arm64", binaryName: "infynon" };
|
|
33
|
+
}
|
|
34
|
+
return null;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
function isFile(filePath) {
|
|
38
|
+
try {
|
|
39
|
+
return fs.statSync(filePath).isFile();
|
|
40
|
+
} catch (_) {
|
|
41
|
+
return false;
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
function resolvePlatformBinary(notes) {
|
|
46
|
+
const info = getPlatformPackage();
|
|
47
|
+
if (!info) {
|
|
48
|
+
notes.push("No optional native package is published for " + process.platform + " " + process.arch + ".");
|
|
49
|
+
return null;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
let packageJsonPath;
|
|
53
|
+
try {
|
|
54
|
+
packageJsonPath = require.resolve(info.packageName + "/package.json", { paths: [PACKAGE_DIR] });
|
|
55
|
+
} catch (err) {
|
|
56
|
+
if (err && err.code !== "MODULE_NOT_FOUND") {
|
|
57
|
+
notes.push("Could not inspect " + info.packageName + ": " + err.message);
|
|
58
|
+
}
|
|
59
|
+
return null;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
const packageDir = path.dirname(packageJsonPath);
|
|
63
|
+
let packageVersion = null;
|
|
64
|
+
try {
|
|
65
|
+
packageVersion = JSON.parse(fs.readFileSync(packageJsonPath, "utf8")).version;
|
|
66
|
+
} catch (err) {
|
|
67
|
+
notes.push("Could not read " + info.packageName + " metadata: " + err.message);
|
|
68
|
+
return null;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
if (packageVersion !== VERSION) {
|
|
72
|
+
notes.push(
|
|
73
|
+
info.packageName + " is version " + packageVersion + ", but the wrapper is version " + VERSION + "."
|
|
74
|
+
);
|
|
75
|
+
return null;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
const binaryPath = path.join(packageDir, "bin", info.binaryName);
|
|
79
|
+
if (!isFile(binaryPath)) {
|
|
80
|
+
notes.push("Found " + info.packageName + ", but its binary is missing at " + binaryPath + ".");
|
|
81
|
+
return null;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
return { path: binaryPath, source: info.packageName };
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
function spawnInherited(command, args, options) {
|
|
88
|
+
return new Promise(function (resolve, reject) {
|
|
89
|
+
let child;
|
|
90
|
+
try {
|
|
91
|
+
child = spawn(
|
|
92
|
+
command,
|
|
93
|
+
args,
|
|
94
|
+
Object.assign(
|
|
95
|
+
{
|
|
96
|
+
stdio: "inherit",
|
|
97
|
+
windowsHide: false,
|
|
98
|
+
},
|
|
99
|
+
options || {}
|
|
100
|
+
)
|
|
101
|
+
);
|
|
102
|
+
} catch (err) {
|
|
103
|
+
reject(err);
|
|
104
|
+
return;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
child.on("error", reject);
|
|
108
|
+
child.on("exit", function (code, signal) {
|
|
109
|
+
resolve({ code: code, signal: signal });
|
|
110
|
+
});
|
|
111
|
+
});
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
async function runPostinstallFallback() {
|
|
115
|
+
if (!isFile(POSTINSTALL_PATH)) {
|
|
116
|
+
throw new Error("postinstall.js is missing at " + POSTINSTALL_PATH);
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
console.error("[infynon] Native binary is missing; running npm postinstall fallback once.");
|
|
120
|
+
const result = await spawnInherited(process.execPath, [POSTINSTALL_PATH], {
|
|
121
|
+
env: Object.assign({}, process.env, {
|
|
122
|
+
INFYNON_NPM_POSTINSTALL_FALLBACK: "1",
|
|
123
|
+
}),
|
|
124
|
+
});
|
|
125
|
+
|
|
126
|
+
if (result.signal) {
|
|
127
|
+
throw new Error("postinstall.js was terminated by signal " + result.signal);
|
|
128
|
+
}
|
|
129
|
+
if (result.code !== 0) {
|
|
130
|
+
throw new Error("postinstall.js failed with exit code " + result.code);
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
async function resolveNativeBinary() {
|
|
135
|
+
const notes = [];
|
|
136
|
+
const platformBinary = resolvePlatformBinary(notes);
|
|
137
|
+
if (platformBinary) {
|
|
138
|
+
return platformBinary;
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
if (isFile(LOCAL_BIN_PATH)) {
|
|
142
|
+
return { path: LOCAL_BIN_PATH, source: "npm/bin" };
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
await runPostinstallFallback();
|
|
146
|
+
if (isFile(LOCAL_BIN_PATH)) {
|
|
147
|
+
return { path: LOCAL_BIN_PATH, source: "npm/bin:postinstall" };
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
const message = [
|
|
151
|
+
"Binary not found for " + process.platform + " " + process.arch + ".",
|
|
152
|
+
"Expected local fallback at: " + LOCAL_BIN_PATH,
|
|
153
|
+
];
|
|
154
|
+
if (notes.length > 0) {
|
|
155
|
+
message.push("Resolution notes:");
|
|
156
|
+
for (const note of notes) {
|
|
157
|
+
message.push(" - " + note);
|
|
32
158
|
}
|
|
33
159
|
}
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
160
|
+
message.push("Try reinstalling: npm install -g infynon");
|
|
161
|
+
message.push("Or download a release manually: " + RELEASES_URL);
|
|
162
|
+
const err = new Error(message.join("\n"));
|
|
163
|
+
err.code = "BINARY_NOT_FOUND";
|
|
164
|
+
throw err;
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
function assertWindowsCommandLineFits(binaryPath, args) {
|
|
168
|
+
if (process.platform !== "win32") {
|
|
169
|
+
return;
|
|
170
|
+
}
|
|
171
|
+
const estimatedLength = binaryPath.length + args.reduce(function (total, arg) {
|
|
172
|
+
return total + String(arg).length + 3;
|
|
173
|
+
}, 0);
|
|
174
|
+
if (estimatedLength <= 30000) {
|
|
175
|
+
return;
|
|
176
|
+
}
|
|
177
|
+
const err = new Error(
|
|
178
|
+
"command line is too long for Windows process creation; reduce arguments or use file-based package-manager input"
|
|
179
|
+
);
|
|
180
|
+
err.code = "COMMAND_TOO_LONG";
|
|
181
|
+
throw err;
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
async function runBinary(resolution, args) {
|
|
185
|
+
assertWindowsCommandLineFits(resolution.path, args);
|
|
186
|
+
return spawnInherited(resolution.path, args, {
|
|
187
|
+
env: Object.assign({}, process.env, {
|
|
188
|
+
INFYNON_NPM_WRAPPER: __filename,
|
|
189
|
+
INFYNON_NPM_PACKAGE_DIR: PACKAGE_DIR,
|
|
190
|
+
INFYNON_NPM_BINARY: resolution.path,
|
|
191
|
+
INFYNON_NPM_BINARY_SOURCE: resolution.source,
|
|
192
|
+
}),
|
|
37
193
|
});
|
|
38
194
|
}
|
|
39
195
|
|
|
40
|
-
|
|
196
|
+
function exitFromChild(result) {
|
|
197
|
+
if (result.signal) {
|
|
198
|
+
try {
|
|
199
|
+
process.kill(process.pid, result.signal);
|
|
200
|
+
} catch (_) {
|
|
201
|
+
const signalExitCodes = { SIGHUP: 129, SIGINT: 130, SIGTERM: 143 };
|
|
202
|
+
process.exit(signalExitCodes[result.signal] || 1);
|
|
203
|
+
}
|
|
204
|
+
setTimeout(function () {
|
|
205
|
+
process.exit(1);
|
|
206
|
+
}, 1000);
|
|
207
|
+
return;
|
|
208
|
+
}
|
|
41
209
|
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
210
|
+
process.exit(result.code === null ? 1 : result.code);
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
function printLaunchError(err, resolution) {
|
|
214
|
+
const detected = [
|
|
215
|
+
"platform: " + process.platform + " " + process.arch,
|
|
216
|
+
"wrapper: " + __filename,
|
|
217
|
+
"package dir: " + PACKAGE_DIR,
|
|
218
|
+
"binary: " + (resolution ? resolution.path : "unresolved"),
|
|
219
|
+
"binary source: " + (resolution ? resolution.source : "unresolved"),
|
|
220
|
+
"node: " + process.version,
|
|
221
|
+
];
|
|
222
|
+
|
|
223
|
+
if (err && err.code === "BINARY_NOT_FOUND") {
|
|
224
|
+
console.error("[infynon] " + err.message);
|
|
225
|
+
console.error("[infynon] Detected:\n - " + detected.join("\n - "));
|
|
226
|
+
return;
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
if (resolution) {
|
|
230
|
+
console.error("[infynon] Failed to launch native binary: " + resolution.path);
|
|
231
|
+
} else {
|
|
232
|
+
console.error("[infynon] Failed to resolve native binary.");
|
|
47
233
|
}
|
|
48
|
-
|
|
234
|
+
console.error("[infynon] " + (err && err.message ? err.message : String(err)));
|
|
235
|
+
|
|
236
|
+
if (err && err.code === "COMMAND_TOO_LONG") {
|
|
237
|
+
console.error("[infynon] On Windows, pass large package-manager input through files instead of argv.");
|
|
238
|
+
} else if (err && err.code === "ENOENT") {
|
|
239
|
+
console.error("[infynon] The resolved binary path no longer exists. Try reinstalling: npm install -g infynon");
|
|
240
|
+
} else if (err && (err.code === "EACCES" || err.code === "EPERM")) {
|
|
241
|
+
console.error("[infynon] The resolved binary is not executable. Try reinstalling: npm install -g infynon");
|
|
242
|
+
} else if (process.platform === "win32") {
|
|
243
|
+
console.error("[infynon] If Windows blocked or quarantined the executable, reinstall to restore a verified copy.");
|
|
244
|
+
console.error("[infynon] Reinstall command: npm install -g infynon");
|
|
245
|
+
} else {
|
|
246
|
+
console.error("[infynon] Try reinstalling: npm install -g infynon");
|
|
247
|
+
}
|
|
248
|
+
console.error("[infynon] Detected:\n - " + detected.join("\n - "));
|
|
249
|
+
console.error("[infynon] Diagnostics: infynon doctor npm");
|
|
250
|
+
console.error("[infynon] Manual release downloads: " + RELEASES_URL);
|
|
49
251
|
}
|
|
50
252
|
|
|
51
|
-
|
|
52
|
-
|
|
253
|
+
async function main() {
|
|
254
|
+
let resolution = null;
|
|
255
|
+
try {
|
|
256
|
+
resolution = await resolveNativeBinary();
|
|
257
|
+
const result = await runBinary(resolution, process.argv.slice(2));
|
|
258
|
+
exitFromChild(result);
|
|
259
|
+
} catch (err) {
|
|
260
|
+
printLaunchError(err, resolution);
|
|
261
|
+
process.exit(1);
|
|
262
|
+
}
|
|
53
263
|
}
|
|
54
264
|
|
|
55
|
-
|
|
265
|
+
main();
|