@vifu/vf 0.1.0-alpha.8
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 +180 -0
- package/bin/vf.js +95 -0
- package/install.sh +333 -0
- package/package.json +29 -0
- package/version.json +35 -0
package/README.md
ADDED
|
@@ -0,0 +1,180 @@
|
|
|
1
|
+
# VifuHub CLI
|
|
2
|
+
|
|
3
|
+
`vf` is the command-line tool for building, checking, and publishing playable
|
|
4
|
+
AI-native game releases to VifuHub.
|
|
5
|
+
|
|
6
|
+
```bash
|
|
7
|
+
vf manifest check
|
|
8
|
+
vf build
|
|
9
|
+
vf deploy
|
|
10
|
+
```
|
|
11
|
+
|
|
12
|
+
When run from a game directory, `vf deploy` resolves the local project,
|
|
13
|
+
validates `manifest.json`, builds the game, checks the Agent Runtime artifact,
|
|
14
|
+
uploads only when the artifact is valid, and prints the playable URL.
|
|
15
|
+
|
|
16
|
+
## Install
|
|
17
|
+
|
|
18
|
+
```bash
|
|
19
|
+
npm install -g @vifu/vf
|
|
20
|
+
vf --help
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
The native installer is also available for macOS, Linux, and Windows shells:
|
|
24
|
+
|
|
25
|
+
```bash
|
|
26
|
+
curl -fsSL https://vifu.dev/cli | bash
|
|
27
|
+
vf --help
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
For repeated use inside one project:
|
|
31
|
+
|
|
32
|
+
```bash
|
|
33
|
+
npm install --save-dev @vifu/vf
|
|
34
|
+
npx vf --help
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
## Agent Workflows
|
|
38
|
+
|
|
39
|
+
Install the VifuHub CLI skill separately from
|
|
40
|
+
[github.com/vifudotdev/skills](https://github.com/vifudotdev/skills):
|
|
41
|
+
|
|
42
|
+
```bash
|
|
43
|
+
npx skills add vifudotdev/skills --skill "vifu-cli"
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
Then a coding agent can start from the current directory:
|
|
47
|
+
|
|
48
|
+
```text
|
|
49
|
+
Use the VifuHub CLI skill. Deploy this game directory and report the Playable URL.
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
The skill tells agents to check `vf --help`, then use the one-step deploy path:
|
|
53
|
+
|
|
54
|
+
```bash
|
|
55
|
+
vf deploy --yes
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
`vf deploy --yes` creates missing setup from detected defaults, builds, checks
|
|
59
|
+
the output, uploads the valid artifact, and prints `Playable URL:` when sharing
|
|
60
|
+
succeeds. If authentication is missing, run:
|
|
61
|
+
|
|
62
|
+
```bash
|
|
63
|
+
vf login
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
Create a new project:
|
|
67
|
+
|
|
68
|
+
```bash
|
|
69
|
+
npm create @vifu/vifu my-game -- --template vanilla --no-interactive
|
|
70
|
+
cd my-game
|
|
71
|
+
vf deploy --yes
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
Deploy an existing project:
|
|
75
|
+
|
|
76
|
+
```bash
|
|
77
|
+
cd path/to/game
|
|
78
|
+
vf deploy --yes
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
You can also pass the project path explicitly:
|
|
82
|
+
|
|
83
|
+
```bash
|
|
84
|
+
vf deploy ./path/to/game
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
## Manifest
|
|
88
|
+
|
|
89
|
+
Every deployable game has a public V1 `manifest.json`:
|
|
90
|
+
|
|
91
|
+
```json
|
|
92
|
+
{
|
|
93
|
+
"name": "my-ai-game"
|
|
94
|
+
}
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
VifuHub fills deployable defaults and infers common build settings from the
|
|
98
|
+
project. Add fields only when you need to override detection or disable an
|
|
99
|
+
Agent Runtime feature. Do not put internal runtime keys such as `runtime`,
|
|
100
|
+
`services`, `devices`, `resources`, `permissions`, `capabilities`, `browser`,
|
|
101
|
+
or `security` in public game manifests.
|
|
102
|
+
|
|
103
|
+
Useful commands:
|
|
104
|
+
|
|
105
|
+
```bash
|
|
106
|
+
vf manifest check
|
|
107
|
+
vf manifest check --dir ./path/to/game
|
|
108
|
+
vf manifest explain
|
|
109
|
+
vf manifest explain --dir ./path/to/game --debug
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
`manifest explain --debug` is for Agent Runtime development. It shows the
|
|
113
|
+
compiled internal policy that the runtime will enforce.
|
|
114
|
+
|
|
115
|
+
## Release Policy
|
|
116
|
+
|
|
117
|
+
`vf deploy` and `vf publish` treat the built output as the reviewed Agent
|
|
118
|
+
Runtime artifact. The CLI does not rewrite the game, download CDN scripts, or
|
|
119
|
+
create a deploy lockfile.
|
|
120
|
+
|
|
121
|
+
Allowed:
|
|
122
|
+
|
|
123
|
+
- bundled game JavaScript
|
|
124
|
+
- local build output from Vite, Angular, Phaser, Three.js, Pixi, plain HTML, or
|
|
125
|
+
another web build tool
|
|
126
|
+
- approved external static CSS, fonts, images, and media
|
|
127
|
+
- AI/backend access through `@vifu/hub` or `window.Vifu`
|
|
128
|
+
|
|
129
|
+
Blocked:
|
|
130
|
+
|
|
131
|
+
- remote `<script src="https://...">`
|
|
132
|
+
- remote JavaScript `import("https://...")`
|
|
133
|
+
- remote workers or `importScripts(...)`
|
|
134
|
+
- remote `.js`, `.mjs`, or `.wasm` URLs inside built files
|
|
135
|
+
- direct calls to external AI/backend APIs from game JavaScript
|
|
136
|
+
|
|
137
|
+
If deploy fails, read the file, line, rule, URL, and fix shown by the CLI. The
|
|
138
|
+
most common cause is an optional local or third-party provider being statically
|
|
139
|
+
imported into a production browser bundle.
|
|
140
|
+
|
|
141
|
+
The canonical policy is [.spec/runtime-artifact-policy.md](../../.spec/runtime-artifact-policy.md).
|
|
142
|
+
|
|
143
|
+
## SDK Integration
|
|
144
|
+
|
|
145
|
+
Games should call Agent Runtime capabilities through the VifuHub SDK:
|
|
146
|
+
|
|
147
|
+
```js
|
|
148
|
+
const result = await Vifu.ai.generateText({
|
|
149
|
+
model: "quality",
|
|
150
|
+
messages: [{ role: "user", content: "Describe the next room." }]
|
|
151
|
+
});
|
|
152
|
+
|
|
153
|
+
await Vifu.gameState.save({ reason: "checkpoint" });
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
The SDK keeps runtime transport, host authentication, model routing, quota,
|
|
157
|
+
and backend URLs out of game code. See
|
|
158
|
+
[packages/sdk/README.md](../../packages/sdk/README.md).
|
|
159
|
+
|
|
160
|
+
## Existing Games
|
|
161
|
+
|
|
162
|
+
To adapt an existing browser game:
|
|
163
|
+
|
|
164
|
+
1. Add `manifest.json`.
|
|
165
|
+
2. Start with only `name`; let VifuHub infer common entry and build settings.
|
|
166
|
+
3. Build a static directory with `index.html`.
|
|
167
|
+
4. Replace direct AI/backend calls with the VifuHub SDK.
|
|
168
|
+
5. Remove unused remote/local AI providers from the published build.
|
|
169
|
+
6. Run `vf deploy`.
|
|
170
|
+
|
|
171
|
+
Extra source workflows live under explicit commands:
|
|
172
|
+
|
|
173
|
+
```bash
|
|
174
|
+
vf remix my-remix --from ./existing-game
|
|
175
|
+
vf import anki jlpt-review --apkg ./deck.apkg
|
|
176
|
+
vf import podcast listening-game --feed-url https://example.com/feed.xml
|
|
177
|
+
```
|
|
178
|
+
|
|
179
|
+
`vf create` and `vf new` are intentionally not exposed. Use
|
|
180
|
+
`npm create @vifu/vifu` for new template projects.
|
package/bin/vf.js
ADDED
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
const childProcess = require("node:child_process");
|
|
3
|
+
const fs = require("node:fs");
|
|
4
|
+
const os = require("node:os");
|
|
5
|
+
const path = require("node:path");
|
|
6
|
+
|
|
7
|
+
const platformMap = {
|
|
8
|
+
darwin: "darwin",
|
|
9
|
+
linux: "linux",
|
|
10
|
+
win32: "windows",
|
|
11
|
+
};
|
|
12
|
+
|
|
13
|
+
const archMap = {
|
|
14
|
+
x64: "x64",
|
|
15
|
+
arm64: "arm64",
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
function isMusl() {
|
|
19
|
+
if (os.platform() !== "linux") return false;
|
|
20
|
+
try {
|
|
21
|
+
if (fs.existsSync("/etc/alpine-release")) return true;
|
|
22
|
+
} catch {
|
|
23
|
+
// Ignore blocked filesystem probes.
|
|
24
|
+
}
|
|
25
|
+
try {
|
|
26
|
+
const result = childProcess.spawnSync("ldd", ["--version"], {
|
|
27
|
+
encoding: "utf8",
|
|
28
|
+
windowsHide: true,
|
|
29
|
+
});
|
|
30
|
+
return `${result.stdout || ""}${result.stderr || ""}`.toLowerCase().includes("musl");
|
|
31
|
+
} catch {
|
|
32
|
+
return false;
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
function targetId() {
|
|
37
|
+
const platform = platformMap[os.platform()] || os.platform();
|
|
38
|
+
const arch = archMap[os.arch()] || os.arch();
|
|
39
|
+
if (arch !== "x64" && arch !== "arm64") {
|
|
40
|
+
throw new Error(`Unsupported VifuHub CLI architecture: ${os.arch()}`);
|
|
41
|
+
}
|
|
42
|
+
if (platform === "linux") return `linux-${arch}${isMusl() ? "-musl" : ""}`;
|
|
43
|
+
if (platform === "darwin" && (arch === "x64" || arch === "arm64")) return `darwin-${arch}`;
|
|
44
|
+
if (platform === "windows" && arch === "x64") return "windows-x64";
|
|
45
|
+
throw new Error(`Unsupported VifuHub CLI system: ${os.platform()}/${os.arch()}`);
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
function packageNames() {
|
|
49
|
+
const id = targetId();
|
|
50
|
+
const primary = `@vifu/vf-${id}`;
|
|
51
|
+
if (!id.startsWith("linux-")) return [primary];
|
|
52
|
+
if (id === "linux-x64") return [primary, "@vifu/vf-linux-x64-musl"];
|
|
53
|
+
if (id === "linux-x64-musl") return [primary, "@vifu/vf-linux-x64"];
|
|
54
|
+
if (id === "linux-arm64") return [primary, "@vifu/vf-linux-arm64-musl"];
|
|
55
|
+
if (id === "linux-arm64-musl") return [primary, "@vifu/vf-linux-arm64"];
|
|
56
|
+
return [primary];
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
function resolveBinary(packageName) {
|
|
60
|
+
const packageJsonPath = require.resolve(`${packageName}/package.json`);
|
|
61
|
+
const binaryName = os.platform() === "win32" ? "vf.exe" : "vf";
|
|
62
|
+
const binaryPath = path.join(path.dirname(packageJsonPath), "bin", binaryName);
|
|
63
|
+
if (!fs.existsSync(binaryPath)) {
|
|
64
|
+
throw new Error(`Binary not found at ${binaryPath}`);
|
|
65
|
+
}
|
|
66
|
+
return binaryPath;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
function main() {
|
|
70
|
+
const errors = [];
|
|
71
|
+
for (const packageName of packageNames()) {
|
|
72
|
+
try {
|
|
73
|
+
const binaryPath = resolveBinary(packageName);
|
|
74
|
+
const result = childProcess.spawnSync(binaryPath, process.argv.slice(2), {
|
|
75
|
+
stdio: "inherit",
|
|
76
|
+
windowsHide: false,
|
|
77
|
+
});
|
|
78
|
+
if (result.error) throw result.error;
|
|
79
|
+
if (result.signal) {
|
|
80
|
+
process.kill(process.pid, result.signal);
|
|
81
|
+
return;
|
|
82
|
+
}
|
|
83
|
+
process.exit(typeof result.status === "number" ? result.status : 1);
|
|
84
|
+
} catch (error) {
|
|
85
|
+
errors.push(`${packageName}: ${error instanceof Error ? error.message : String(error)}`);
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
console.error("Unable to find the VifuHub CLI binary for this system.");
|
|
89
|
+
console.error("Tried:");
|
|
90
|
+
for (const error of errors) console.error(` - ${error}`);
|
|
91
|
+
console.error("Reinstall @vifu/vf and make sure optional dependencies are enabled.");
|
|
92
|
+
process.exit(1);
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
main();
|
package/install.sh
ADDED
|
@@ -0,0 +1,333 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
|
|
3
|
+
set -euo pipefail
|
|
4
|
+
|
|
5
|
+
PROGRAM_NAME="vf"
|
|
6
|
+
NPM_PACKAGE_NAME="${VIFU_NPM_PACKAGE:-@vifu/vf}"
|
|
7
|
+
MANIFEST_URL="${VIFU_VERSION_MANIFEST_URL:-https://vifu.dev/cli/version.json}"
|
|
8
|
+
STATE_DIR="${VIFU_STATE_DIR:-$HOME/.vifu}"
|
|
9
|
+
VERSION_INSTALL_PATH="${STATE_DIR}/version.json"
|
|
10
|
+
|
|
11
|
+
tmp_dir=""
|
|
12
|
+
npm_package_name="$NPM_PACKAGE_NAME"
|
|
13
|
+
|
|
14
|
+
say() {
|
|
15
|
+
printf '%s\n' "$*"
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
fail() {
|
|
19
|
+
printf 'VifuHub CLI install: %s\n' "$*" >&2
|
|
20
|
+
exit 1
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
cleanup() {
|
|
24
|
+
if [ -n "$tmp_dir" ]; then
|
|
25
|
+
rm -rf "$tmp_dir"
|
|
26
|
+
fi
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
has_command() {
|
|
30
|
+
command -v "$1" >/dev/null 2>&1
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
download_file() {
|
|
34
|
+
local url="$1"
|
|
35
|
+
local output="$2"
|
|
36
|
+
|
|
37
|
+
if has_command curl; then
|
|
38
|
+
curl -fsSL "$url" -o "$output"
|
|
39
|
+
return
|
|
40
|
+
fi
|
|
41
|
+
if has_command wget; then
|
|
42
|
+
wget -qO "$output" "$url"
|
|
43
|
+
return
|
|
44
|
+
fi
|
|
45
|
+
|
|
46
|
+
fail "curl or wget is required"
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
append_line_if_missing() {
|
|
50
|
+
local file="$1"
|
|
51
|
+
local line="$2"
|
|
52
|
+
|
|
53
|
+
mkdir -p "$(dirname "$file")"
|
|
54
|
+
touch "$file"
|
|
55
|
+
if ! grep -Fqx "$line" "$file"; then
|
|
56
|
+
printf '\n%s\n' "$line" >>"$file"
|
|
57
|
+
fi
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
pick_unix_rc_file() {
|
|
61
|
+
local shell_name
|
|
62
|
+
shell_name="$(basename "${SHELL:-}")"
|
|
63
|
+
|
|
64
|
+
case "$shell_name" in
|
|
65
|
+
zsh)
|
|
66
|
+
printf '%s\n' "${ZDOTDIR:-$HOME}/.zshrc"
|
|
67
|
+
;;
|
|
68
|
+
bash)
|
|
69
|
+
if [ -f "$HOME/.bashrc" ] || [ ! -f "$HOME/.bash_profile" ]; then
|
|
70
|
+
printf '%s\n' "$HOME/.bashrc"
|
|
71
|
+
else
|
|
72
|
+
printf '%s\n' "$HOME/.bash_profile"
|
|
73
|
+
fi
|
|
74
|
+
;;
|
|
75
|
+
*)
|
|
76
|
+
printf '%s\n' "$HOME/.profile"
|
|
77
|
+
;;
|
|
78
|
+
esac
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
ensure_unix_path() {
|
|
82
|
+
local install_dir="$1"
|
|
83
|
+
local rc_file
|
|
84
|
+
|
|
85
|
+
case ":$PATH:" in
|
|
86
|
+
*":$install_dir:"*)
|
|
87
|
+
say "PATH already contains $install_dir"
|
|
88
|
+
return
|
|
89
|
+
;;
|
|
90
|
+
esac
|
|
91
|
+
|
|
92
|
+
rc_file="$(pick_unix_rc_file)"
|
|
93
|
+
append_line_if_missing "$rc_file" "export PATH=\"$install_dir:\$PATH\""
|
|
94
|
+
say "Added $install_dir to PATH startup file: $rc_file"
|
|
95
|
+
say "For this terminal, run: export PATH=\"$install_dir:\$PATH\""
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
windows_path_contains() {
|
|
99
|
+
local install_dir="$1"
|
|
100
|
+
case ";${PATH};" in
|
|
101
|
+
*";${install_dir};"*) return 0 ;;
|
|
102
|
+
*) return 1 ;;
|
|
103
|
+
esac
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
ensure_windows_path() {
|
|
107
|
+
local install_dir="$1"
|
|
108
|
+
local install_dir_windows
|
|
109
|
+
|
|
110
|
+
if windows_path_contains "$install_dir"; then
|
|
111
|
+
say "PATH already contains $install_dir"
|
|
112
|
+
return
|
|
113
|
+
fi
|
|
114
|
+
|
|
115
|
+
if ! has_command powershell.exe; then
|
|
116
|
+
say "powershell.exe not found; add $install_dir to your user PATH manually"
|
|
117
|
+
return
|
|
118
|
+
fi
|
|
119
|
+
|
|
120
|
+
if has_command cygpath; then
|
|
121
|
+
install_dir_windows="$(cygpath -w "$install_dir")"
|
|
122
|
+
else
|
|
123
|
+
install_dir_windows="$install_dir"
|
|
124
|
+
fi
|
|
125
|
+
|
|
126
|
+
powershell.exe -NoProfile -Command \
|
|
127
|
+
"\$target='${install_dir_windows}'; \$current=[Environment]::GetEnvironmentVariable('Path','User'); if ([string]::IsNullOrWhiteSpace(\$current)) { [Environment]::SetEnvironmentVariable('Path', \$target, 'User') } elseif (-not (\$current.Split(';') -contains \$target)) { [Environment]::SetEnvironmentVariable('Path', \$current + ';' + \$target, 'User') }"
|
|
128
|
+
|
|
129
|
+
say "Added $install_dir to Windows user PATH"
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
clear_macos_quarantine() {
|
|
133
|
+
local target="$1"
|
|
134
|
+
|
|
135
|
+
if ! has_command xattr; then
|
|
136
|
+
return
|
|
137
|
+
fi
|
|
138
|
+
|
|
139
|
+
xattr -d com.apple.quarantine "$target" >/dev/null 2>&1 || true
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
detect_linux_libc_suffix() {
|
|
143
|
+
if has_command ldd && ldd --version 2>&1 | grep -qi musl; then
|
|
144
|
+
printf '%s\n' "-musl"
|
|
145
|
+
return
|
|
146
|
+
fi
|
|
147
|
+
if ls /lib/ld-musl-* >/dev/null 2>&1 || ls /usr/lib/ld-musl-* >/dev/null 2>&1; then
|
|
148
|
+
printf '%s\n' "-musl"
|
|
149
|
+
return
|
|
150
|
+
fi
|
|
151
|
+
printf '%s\n' ""
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
detect_platform() {
|
|
155
|
+
local os
|
|
156
|
+
local arch
|
|
157
|
+
local os_id
|
|
158
|
+
local arch_id
|
|
159
|
+
local libc_suffix
|
|
160
|
+
|
|
161
|
+
os="$(uname -s 2>/dev/null || true)"
|
|
162
|
+
arch="$(uname -m 2>/dev/null || true)"
|
|
163
|
+
|
|
164
|
+
case "$os" in
|
|
165
|
+
Darwin)
|
|
166
|
+
os_id="darwin"
|
|
167
|
+
PLATFORM_KIND="unix"
|
|
168
|
+
DEFAULT_INSTALL_DIR="${VIFU_INSTALL_DIR:-${VIFU_CLI_INSTALL_DIR:-$HOME/.local/bin}}"
|
|
169
|
+
;;
|
|
170
|
+
Linux)
|
|
171
|
+
os_id="linux"
|
|
172
|
+
PLATFORM_KIND="unix"
|
|
173
|
+
DEFAULT_INSTALL_DIR="${VIFU_INSTALL_DIR:-${VIFU_CLI_INSTALL_DIR:-$HOME/.local/bin}}"
|
|
174
|
+
;;
|
|
175
|
+
MINGW*|MSYS*|CYGWIN*)
|
|
176
|
+
os_id="windows"
|
|
177
|
+
PLATFORM_KIND="windows"
|
|
178
|
+
DEFAULT_INSTALL_DIR="${VIFU_INSTALL_DIR:-${VIFU_CLI_INSTALL_DIR:-$HOME/bin}}"
|
|
179
|
+
;;
|
|
180
|
+
*)
|
|
181
|
+
fail "unsupported operating system: ${os:-unknown}"
|
|
182
|
+
;;
|
|
183
|
+
esac
|
|
184
|
+
|
|
185
|
+
case "$arch" in
|
|
186
|
+
arm64|aarch64)
|
|
187
|
+
arch_id="arm64"
|
|
188
|
+
;;
|
|
189
|
+
x86_64|amd64)
|
|
190
|
+
arch_id="x64"
|
|
191
|
+
;;
|
|
192
|
+
*)
|
|
193
|
+
fail "unsupported CPU architecture: ${arch:-unknown}"
|
|
194
|
+
;;
|
|
195
|
+
esac
|
|
196
|
+
|
|
197
|
+
libc_suffix=""
|
|
198
|
+
if [ "$os_id" = "linux" ]; then
|
|
199
|
+
libc_suffix="$(detect_linux_libc_suffix)"
|
|
200
|
+
fi
|
|
201
|
+
|
|
202
|
+
TARGET_ID="${VIFU_INSTALL_TARGET:-${os_id}-${arch_id}${libc_suffix}}"
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
json_value() {
|
|
206
|
+
local key="$1"
|
|
207
|
+
local file="$2"
|
|
208
|
+
sed -n "s/.*\"$key\"[[:space:]]*:[[:space:]]*\"\\([^\"]*\\)\".*/\\1/p" "$file" | head -n 1
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
target_json_value() {
|
|
212
|
+
local target="$1"
|
|
213
|
+
local key="$2"
|
|
214
|
+
local file="$3"
|
|
215
|
+
|
|
216
|
+
sed -n "/\"$target\"[[:space:]]*:/,/^[[:space:]]*}/p" "$file" \
|
|
217
|
+
| sed -n "s/.*\"$key\"[[:space:]]*:[[:space:]]*\"\\([^\"]*\\)\".*/\\1/p" \
|
|
218
|
+
| head -n 1
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
try_npm_fallback() {
|
|
222
|
+
if [ "${VIFU_NO_NPM_FALLBACK:-}" = "1" ]; then
|
|
223
|
+
return 1
|
|
224
|
+
fi
|
|
225
|
+
if ! has_command npm; then
|
|
226
|
+
return 1
|
|
227
|
+
fi
|
|
228
|
+
|
|
229
|
+
say "Native package unavailable; falling back to npm install -g ${npm_package_name}"
|
|
230
|
+
npm install -g "${npm_package_name}"
|
|
231
|
+
return 0
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
main() {
|
|
235
|
+
local manifest_file
|
|
236
|
+
local archive_file
|
|
237
|
+
local package_dir
|
|
238
|
+
local package_root
|
|
239
|
+
local package_url
|
|
240
|
+
local binary_rel
|
|
241
|
+
local binary_path
|
|
242
|
+
local install_path
|
|
243
|
+
local version
|
|
244
|
+
local manifest_package
|
|
245
|
+
local target_name
|
|
246
|
+
|
|
247
|
+
detect_platform
|
|
248
|
+
|
|
249
|
+
if ! has_command tar; then
|
|
250
|
+
fail "tar is required"
|
|
251
|
+
fi
|
|
252
|
+
|
|
253
|
+
tmp_dir="$(mktemp -d 2>/dev/null || mktemp -d -t vifuhub-cli)"
|
|
254
|
+
trap cleanup EXIT INT TERM
|
|
255
|
+
|
|
256
|
+
manifest_file="$tmp_dir/version.json"
|
|
257
|
+
archive_file="$tmp_dir/vifuhub-cli.tgz"
|
|
258
|
+
package_dir="$tmp_dir/package"
|
|
259
|
+
|
|
260
|
+
say "Downloading $MANIFEST_URL"
|
|
261
|
+
download_file "$MANIFEST_URL" "$manifest_file"
|
|
262
|
+
|
|
263
|
+
version="$(json_value version "$manifest_file")"
|
|
264
|
+
manifest_package="$(json_value package "$manifest_file")"
|
|
265
|
+
if [ -n "$manifest_package" ]; then
|
|
266
|
+
npm_package_name="$manifest_package"
|
|
267
|
+
fi
|
|
268
|
+
|
|
269
|
+
package_url="$(target_json_value "$TARGET_ID" url "$manifest_file")"
|
|
270
|
+
binary_rel="$(target_json_value "$TARGET_ID" binary "$manifest_file")"
|
|
271
|
+
|
|
272
|
+
if [ -z "$package_url" ] || [ -z "$binary_rel" ]; then
|
|
273
|
+
say "No native VifuHub CLI package is listed for target: $TARGET_ID" >&2
|
|
274
|
+
try_npm_fallback && exit 0
|
|
275
|
+
fail "use npm install -g ${npm_package_name}"
|
|
276
|
+
fi
|
|
277
|
+
|
|
278
|
+
say "Installing VifuHub CLI ${version:-latest} for $TARGET_ID"
|
|
279
|
+
say "Downloading $package_url"
|
|
280
|
+
if ! download_file "$package_url" "$archive_file"; then
|
|
281
|
+
say "Failed to download native package: $package_url" >&2
|
|
282
|
+
try_npm_fallback && exit 0
|
|
283
|
+
fail "download failed"
|
|
284
|
+
fi
|
|
285
|
+
|
|
286
|
+
mkdir -p "$package_dir"
|
|
287
|
+
tar -xzf "$archive_file" -C "$package_dir"
|
|
288
|
+
package_root="$package_dir/package"
|
|
289
|
+
binary_path="$package_root/$binary_rel"
|
|
290
|
+
if [ ! -f "$binary_path" ]; then
|
|
291
|
+
fail "package did not contain $binary_rel"
|
|
292
|
+
fi
|
|
293
|
+
|
|
294
|
+
case "$binary_rel" in
|
|
295
|
+
*.exe) target_name="${PROGRAM_NAME}.exe" ;;
|
|
296
|
+
*) target_name="$PROGRAM_NAME" ;;
|
|
297
|
+
esac
|
|
298
|
+
|
|
299
|
+
mkdir -p "$DEFAULT_INSTALL_DIR"
|
|
300
|
+
install_path="$DEFAULT_INSTALL_DIR/$target_name"
|
|
301
|
+
cp "$binary_path" "$install_path"
|
|
302
|
+
chmod 755 "$install_path"
|
|
303
|
+
|
|
304
|
+
mkdir -p "$STATE_DIR"
|
|
305
|
+
cp "$manifest_file" "$VERSION_INSTALL_PATH"
|
|
306
|
+
say "Version manifest: $VERSION_INSTALL_PATH"
|
|
307
|
+
|
|
308
|
+
case "$PLATFORM_KIND" in
|
|
309
|
+
unix)
|
|
310
|
+
ensure_unix_path "$DEFAULT_INSTALL_DIR"
|
|
311
|
+
;;
|
|
312
|
+
windows)
|
|
313
|
+
ensure_windows_path "$DEFAULT_INSTALL_DIR"
|
|
314
|
+
;;
|
|
315
|
+
esac
|
|
316
|
+
|
|
317
|
+
if [ "$(uname -s 2>/dev/null || true)" = "Darwin" ]; then
|
|
318
|
+
clear_macos_quarantine "$install_path"
|
|
319
|
+
fi
|
|
320
|
+
|
|
321
|
+
say "VifuHub CLI installed: $install_path"
|
|
322
|
+
if "$install_path" --help >/dev/null 2>&1; then
|
|
323
|
+
say "Command check: ok"
|
|
324
|
+
else
|
|
325
|
+
say "Command check: $install_path --help failed; inspect the binary before using it" >&2
|
|
326
|
+
fi
|
|
327
|
+
|
|
328
|
+
say "Try:"
|
|
329
|
+
say " vf --help"
|
|
330
|
+
say " vf deploy --yes"
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
main "$@"
|
package/package.json
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@vifu/vf",
|
|
3
|
+
"version": "0.1.0-alpha.8",
|
|
4
|
+
"description": "VifuHub CLI for building, checking, and publishing AI-native games.",
|
|
5
|
+
"bin": {
|
|
6
|
+
"vf": "bin/vf.js"
|
|
7
|
+
},
|
|
8
|
+
"files": [
|
|
9
|
+
"bin/vf.js",
|
|
10
|
+
"README.md",
|
|
11
|
+
"install.sh",
|
|
12
|
+
"version.json"
|
|
13
|
+
],
|
|
14
|
+
"publishConfig": {
|
|
15
|
+
"access": "public"
|
|
16
|
+
},
|
|
17
|
+
"engines": {
|
|
18
|
+
"node": ">=18.17.0"
|
|
19
|
+
},
|
|
20
|
+
"optionalDependencies": {
|
|
21
|
+
"@vifu/vf-darwin-arm64": "0.1.0-alpha.8",
|
|
22
|
+
"@vifu/vf-darwin-x64": "0.1.0-alpha.8",
|
|
23
|
+
"@vifu/vf-linux-arm64": "0.1.0-alpha.8",
|
|
24
|
+
"@vifu/vf-linux-arm64-musl": "0.1.0-alpha.8",
|
|
25
|
+
"@vifu/vf-linux-x64": "0.1.0-alpha.8",
|
|
26
|
+
"@vifu/vf-linux-x64-musl": "0.1.0-alpha.8",
|
|
27
|
+
"@vifu/vf-windows-x64": "0.1.0-alpha.8"
|
|
28
|
+
}
|
|
29
|
+
}
|
package/version.json
ADDED
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": "0.1.0-alpha.8",
|
|
3
|
+
"package": "@vifu/vf",
|
|
4
|
+
"installer": "https://vifu.dev/cli",
|
|
5
|
+
"targets": {
|
|
6
|
+
"darwin-arm64": {
|
|
7
|
+
"url": "https://registry.npmjs.org/@vifu/vf-darwin-arm64/-/vf-darwin-arm64-0.1.0-alpha.8.tgz",
|
|
8
|
+
"binary": "bin/vf"
|
|
9
|
+
},
|
|
10
|
+
"darwin-x64": {
|
|
11
|
+
"url": "https://registry.npmjs.org/@vifu/vf-darwin-x64/-/vf-darwin-x64-0.1.0-alpha.8.tgz",
|
|
12
|
+
"binary": "bin/vf"
|
|
13
|
+
},
|
|
14
|
+
"linux-arm64": {
|
|
15
|
+
"url": "https://registry.npmjs.org/@vifu/vf-linux-arm64/-/vf-linux-arm64-0.1.0-alpha.8.tgz",
|
|
16
|
+
"binary": "bin/vf"
|
|
17
|
+
},
|
|
18
|
+
"linux-arm64-musl": {
|
|
19
|
+
"url": "https://registry.npmjs.org/@vifu/vf-linux-arm64-musl/-/vf-linux-arm64-musl-0.1.0-alpha.8.tgz",
|
|
20
|
+
"binary": "bin/vf"
|
|
21
|
+
},
|
|
22
|
+
"linux-x64": {
|
|
23
|
+
"url": "https://registry.npmjs.org/@vifu/vf-linux-x64/-/vf-linux-x64-0.1.0-alpha.8.tgz",
|
|
24
|
+
"binary": "bin/vf"
|
|
25
|
+
},
|
|
26
|
+
"linux-x64-musl": {
|
|
27
|
+
"url": "https://registry.npmjs.org/@vifu/vf-linux-x64-musl/-/vf-linux-x64-musl-0.1.0-alpha.8.tgz",
|
|
28
|
+
"binary": "bin/vf"
|
|
29
|
+
},
|
|
30
|
+
"windows-x64": {
|
|
31
|
+
"url": "https://registry.npmjs.org/@vifu/vf-windows-x64/-/vf-windows-x64-0.1.0-alpha.8.tgz",
|
|
32
|
+
"binary": "bin/vf.exe"
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
}
|