@viceme-ai/cli 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +15 -0
- package/README.md +70 -0
- package/docs/releasing.md +63 -0
- package/npm/bin/viceme.mjs +45 -0
- package/npm/lib/installer.mjs +201 -0
- package/package.json +33 -0
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
## [0.1.0] - 2026-07-18
|
|
4
|
+
|
|
5
|
+
### Features
|
|
6
|
+
|
|
7
|
+
- automate CLI releases (`b1c27a5`)
|
|
8
|
+
- publish the Viceme CLI through npm (`80a45d3`)
|
|
9
|
+
- add skill agent publishing CLI (`00f173c`)
|
|
10
|
+
|
|
11
|
+
### Fixes
|
|
12
|
+
|
|
13
|
+
- harden release and API transport (`c45db91`)
|
|
14
|
+
- satisfy release workflow shellcheck (`96e8c4a`)
|
|
15
|
+
- record publication admission confirmation (`ed27923`)
|
package/README.md
ADDED
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
# Viceme CLI
|
|
2
|
+
|
|
3
|
+
`viceme` is the deterministic client used by the bundled Viceme Agent Skill. Source parsing, LLM compilation, BuildRun materialization, and share publication stay server-side.
|
|
4
|
+
|
|
5
|
+
## Install
|
|
6
|
+
|
|
7
|
+
Node.js 18.20 or newer is required for the npm launcher.
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
npm install --global @viceme-ai/cli
|
|
11
|
+
viceme install
|
|
12
|
+
```
|
|
13
|
+
|
|
14
|
+
For a one-shot bootstrap without a prior global install, use
|
|
15
|
+
`npx --yes @viceme-ai/cli@latest install`.
|
|
16
|
+
|
|
17
|
+
The root `install` command performs the complete bootstrap: it atomically installs the matching Viceme Skill into Codex and/or Claude Code, creates a non-sensitive local config, checks authentication without printing credentials, and returns the device-login command when needed.
|
|
18
|
+
|
|
19
|
+
The npm package is intentionally a small launcher, not six copies of the Go executable. It downloads the binary for the package's exact version from the corresponding GitHub Release, verifies the published SHA-256 checksum, writes a complete pair into a unique staging directory, and atomically publishes that directory as an immutable cache generation before executing it. Concurrent cold starts may repeat a download, but never share, overwrite, or delete a mutable binary/checksum pair; an interrupted process leaves only ignored staging state. Supported release targets are macOS and Linux on `amd64`/`arm64`, plus Windows on `amd64`/`arm64`.
|
|
20
|
+
|
|
21
|
+
## Authenticate and publish
|
|
22
|
+
|
|
23
|
+
The current Core records `--yes` as a versioned publication-admission confirmation and remains an internal, default-off allowlist probe. That admission permits the background compile, but it is not proof that the user reviewed the resulting immutable Release Candidate. Do not expose it as the public product flow: external rollout is blocked until T2 adds exact-candidate preview and confirmation before commit. The commands below exercise the transport and stable-link Core while that gate is implemented.
|
|
24
|
+
|
|
25
|
+
```bash
|
|
26
|
+
viceme auth login --no-wait --json
|
|
27
|
+
viceme skill inspect https://github.com/acme/poster-skill --json
|
|
28
|
+
viceme skill publish --resolution-id <resolution-id> --yes --json
|
|
29
|
+
viceme job wait <publication-id> --timeout 60s --json
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
Credentials are stored only in the operating-system keychain. There is no plaintext token fallback.
|
|
33
|
+
|
|
34
|
+
## Diagnose and update
|
|
35
|
+
|
|
36
|
+
```bash
|
|
37
|
+
viceme skills doctor --json
|
|
38
|
+
viceme update --check --json
|
|
39
|
+
viceme update --json
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
Every Skill installation records its CLI version, Skill version, minimum compatible CLI version, full bundle digest, and agent-readable embedded digest. `skills doctor` validates each item independently.
|
|
43
|
+
|
|
44
|
+
`viceme update` is supported for the npm launcher. It resolves the latest version, globally installs that exact package with lifecycle scripts disabled, and then invokes the same exact package to acquire its verified binary and reinstall its bundled Skill. A standalone development binary is not silently replaced; bootstrap it through npm first. Homebrew and signed standalone update manifests remain future distribution channels.
|
|
45
|
+
|
|
46
|
+
## Development and quality gates
|
|
47
|
+
|
|
48
|
+
```bash
|
|
49
|
+
make build
|
|
50
|
+
make test
|
|
51
|
+
make check
|
|
52
|
+
make npm-package-check
|
|
53
|
+
make quality-check
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
`make npm-package-check` does not require a published GitHub Release. It injects the locally built Go executable through `VICEME_BINARY_PATH`, packs the npm tarball, and runs the real npm launcher through `install` in an isolated temporary home. The override is for development and CI only.
|
|
57
|
+
|
|
58
|
+
The checked-in quality artifacts are:
|
|
59
|
+
|
|
60
|
+
- `skills/viceme/references/command-manifest.json`, generated from the Cobra command tree;
|
|
61
|
+
- `quality/example-dry-runs.json`, which executes the documented inspect/publish paths without network access;
|
|
62
|
+
- `quality/release-manifest.json`, which pins CLI/Skill compatibility and both Skill digests.
|
|
63
|
+
|
|
64
|
+
Set `VICEME_API_BASE_URL` for local integration. Release asset names are fixed as `viceme_<version>_<goos>_<goarch>[.exe]` with a sibling `.sha256`. The workflow checks that exactly all six binary/checksum pairs exist and blocks npm publication until they have been uploaded to a completed GitHub Release.
|
|
65
|
+
|
|
66
|
+
Releases do not require a maintainer to edit versions, write a changelog, create a tag, or run `npm publish`. A push to `dev` runs `.github/workflows/release-pr.yml`, which selects the next stable semantic version from Conventional Commits, synchronizes npm/Go/Skill metadata and generated digests, updates `CHANGELOG.md`, runs the release quality gates, and creates or updates the `dev` to `main` Release PR. Merge that PR to approve the release; `.github/workflows/release.yml` then tags the exact reviewed head, reconnects the merged `main` commit into `dev`, creates or verifies the immutable GitHub Release, and publishes or verifies the npm package. See [`docs/releasing.md`](docs/releasing.md) for setup, recovery, and versioning details.
|
|
67
|
+
|
|
68
|
+
npm publishing uses a pinned OIDC-capable npm CLI and provenance. The steady-state authentication path is GitHub Actions trusted publishing (`id-token: write`): configure `@viceme-ai/cli` to trust `ViceMe-AI/cli` and `.github/workflows/release.yml`. If npm requires a credential for the package's first-ever publication, temporarily configure a granular publish token as the `NPM_TOKEN` repository secret; the workflow exposes it only as `NODE_AUTH_TOKEN` to the final publish-or-verify step. Remove the secret after trusted publishing is configured.
|
|
69
|
+
|
|
70
|
+
Release reruns are fail-closed. If the GitHub Release already exists, the workflow byte-compares every existing asset with the binaries/checksums rebuilt from the tag, uploads only assets missing after an interrupted release, and then verifies the complete 12-asset set. It never overwrites an existing asset. npm publication accepts stable `x.y.z` versions only. If the exact version already exists, its registry integrity must equal the local `npm pack` integrity; an older `latest` tag is safely promoted to that exact version, while rerunning an older release never demotes a newer `latest`. This lets either publication stage be retried without deleting or replacing immutable content.
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
# Automated CLI releases
|
|
2
|
+
|
|
3
|
+
Viceme CLI uses an automated Release PR as the only normal production release
|
|
4
|
+
gate. Maintainers merge feature and fix PRs into `dev`; they do not edit version
|
|
5
|
+
files, create tags, write changelog entries, or run npm commands locally.
|
|
6
|
+
|
|
7
|
+
## Normal flow
|
|
8
|
+
|
|
9
|
+
1. A non-bot push to `dev` starts `Prepare Release PR`.
|
|
10
|
+
2. `npm/scripts/prepare-release.mjs` finds the newest reachable stable tag and
|
|
11
|
+
reads all unreleased non-merge commits.
|
|
12
|
+
3. Conventional Commits select the next version:
|
|
13
|
+
- a `BREAKING CHANGE` footer or `type!:` selects major;
|
|
14
|
+
- `feat:` selects minor;
|
|
15
|
+
- every other releasable change selects patch.
|
|
16
|
+
4. The workflow synchronizes `package.json`, `package-lock.json`, Go build
|
|
17
|
+
metadata, bundled Skill metadata, command manifest, release digests, and
|
|
18
|
+
`CHANGELOG.md`.
|
|
19
|
+
5. It runs `make check` and `make npm-package-check`, commits the generated
|
|
20
|
+
release files to `dev`, and creates or updates one `dev` to `main` PR titled
|
|
21
|
+
`chore(release): vX.Y.Z`.
|
|
22
|
+
6. A maintainer reviews and merges that PR using the repository's preferred
|
|
23
|
+
merge method.
|
|
24
|
+
7. `Release CLI and npm launcher` tags the exact reviewed PR head, reconnects
|
|
25
|
+
the merged `main` commit into `dev` when necessary, reruns the quality gates,
|
|
26
|
+
builds six platform binaries and six checksums, creates the GitHub Release,
|
|
27
|
+
and publishes the npm launcher.
|
|
28
|
+
|
|
29
|
+
## One-time repository setup
|
|
30
|
+
|
|
31
|
+
GitHub Actions needs `Read and write permissions` and permission to create pull
|
|
32
|
+
requests so the built-in `GITHUB_TOKEN` can update `dev` and maintain the
|
|
33
|
+
Release PR. No maintainer PAT is required by these workflows.
|
|
34
|
+
|
|
35
|
+
Configure npm trusted publishing for:
|
|
36
|
+
|
|
37
|
+
- npm package: `@viceme-ai/cli`;
|
|
38
|
+
- GitHub organization/repository: `ViceMe-AI/cli`;
|
|
39
|
+
- workflow file: `.github/workflows/release.yml`.
|
|
40
|
+
|
|
41
|
+
Trusted publishing is the steady-state path and uses GitHub OIDC plus npm
|
|
42
|
+
provenance. If npm does not allow trusted publishing to create the package on
|
|
43
|
+
its first release, add a repository secret named `NPM_TOKEN` containing a
|
|
44
|
+
granular automation token limited to `@viceme-ai/cli` publication. Remove that
|
|
45
|
+
secret after the package exists and trusted publishing is confirmed.
|
|
46
|
+
|
|
47
|
+
No release-specific environment variables are required. `GITHUB_TOKEN` is
|
|
48
|
+
provided by Actions. `NPM_TOKEN` is optional and should only be retained when
|
|
49
|
+
the npm account policy requires it.
|
|
50
|
+
|
|
51
|
+
## Recovery
|
|
52
|
+
|
|
53
|
+
The original merged Release PR workflow run is safe to rerun from GitHub
|
|
54
|
+
Actions. Existing tags must point to the same reviewed commit. Existing GitHub
|
|
55
|
+
Release assets are compared byte-for-byte and never overwritten. Existing npm
|
|
56
|
+
versions must have the same registry integrity as the locally packed artifact;
|
|
57
|
+
otherwise the workflow fails closed. A rerun of an older version cannot move
|
|
58
|
+
the npm `latest` tag behind a newer release.
|
|
59
|
+
|
|
60
|
+
There is deliberately no tag-push or manual-dispatch production trigger. A
|
|
61
|
+
production release can only originate from merging the repository-owned `dev`
|
|
62
|
+
Release PR into `main`; recovery reruns that same authorized event and exact
|
|
63
|
+
reviewed SHA.
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
import { spawnSync } from "node:child_process";
|
|
4
|
+
import { readFile } from "node:fs/promises";
|
|
5
|
+
import process from "node:process";
|
|
6
|
+
|
|
7
|
+
import { ensureBinary } from "../lib/installer.mjs";
|
|
8
|
+
|
|
9
|
+
export async function main(args = process.argv.slice(2), environment = process.env) {
|
|
10
|
+
const packageDocument = JSON.parse(
|
|
11
|
+
await readFile(new URL("../../package.json", import.meta.url), "utf8"),
|
|
12
|
+
);
|
|
13
|
+
const binary = await ensureBinary({
|
|
14
|
+
packageVersion: packageDocument.version,
|
|
15
|
+
environment,
|
|
16
|
+
});
|
|
17
|
+
const child = spawnSync(binary, args, {
|
|
18
|
+
stdio: "inherit",
|
|
19
|
+
env: {
|
|
20
|
+
...environment,
|
|
21
|
+
VICEME_INSTALL_METHOD:
|
|
22
|
+
environment.VICEME_INSTALL_METHOD === "npm"
|
|
23
|
+
? "npm"
|
|
24
|
+
: environment.VICEME_BINARY_PATH
|
|
25
|
+
? "development"
|
|
26
|
+
: "npm",
|
|
27
|
+
VICEME_NPM_PACKAGE_VERSION: packageDocument.version,
|
|
28
|
+
},
|
|
29
|
+
windowsHide: false,
|
|
30
|
+
});
|
|
31
|
+
if (child.error) {
|
|
32
|
+
throw child.error;
|
|
33
|
+
}
|
|
34
|
+
if (child.signal) {
|
|
35
|
+
return 128;
|
|
36
|
+
}
|
|
37
|
+
return child.status ?? 1;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
try {
|
|
41
|
+
process.exitCode = await main();
|
|
42
|
+
} catch (error) {
|
|
43
|
+
process.stderr.write(`viceme launcher: ${error.message}\n`);
|
|
44
|
+
process.exitCode = 1;
|
|
45
|
+
}
|
|
@@ -0,0 +1,201 @@
|
|
|
1
|
+
import { createHash, randomUUID } from "node:crypto";
|
|
2
|
+
import { access, chmod, mkdir, readFile, readdir, rename, rm, writeFile } from "node:fs/promises";
|
|
3
|
+
import { constants } from "node:fs";
|
|
4
|
+
import os from "node:os";
|
|
5
|
+
import path from "node:path";
|
|
6
|
+
|
|
7
|
+
const RELEASE_BASE_URL = "https://github.com/ViceMe-AI/cli/releases/download";
|
|
8
|
+
const VERSION_PATTERN = /^\d+\.\d+\.\d+(?:-[0-9A-Za-z.-]+)?$/;
|
|
9
|
+
const GENERATIONS_DIRECTORY = "generations";
|
|
10
|
+
const GENERATION_PREFIX = "generation-";
|
|
11
|
+
const STAGING_PREFIX = ".staging-";
|
|
12
|
+
|
|
13
|
+
export function releaseTarget(platform = process.platform, architecture = process.arch) {
|
|
14
|
+
const operatingSystems = {
|
|
15
|
+
darwin: "darwin",
|
|
16
|
+
linux: "linux",
|
|
17
|
+
win32: "windows",
|
|
18
|
+
};
|
|
19
|
+
const architectures = {
|
|
20
|
+
arm64: "arm64",
|
|
21
|
+
x64: "amd64",
|
|
22
|
+
};
|
|
23
|
+
const operatingSystem = operatingSystems[platform];
|
|
24
|
+
const goArchitecture = architectures[architecture];
|
|
25
|
+
if (!operatingSystem || !goArchitecture) {
|
|
26
|
+
throw new Error(`unsupported platform ${platform}/${architecture}`);
|
|
27
|
+
}
|
|
28
|
+
return {
|
|
29
|
+
operatingSystem,
|
|
30
|
+
architecture: goArchitecture,
|
|
31
|
+
extension: platform === "win32" ? ".exe" : "",
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export function releaseAssetName(version, platform, architecture) {
|
|
36
|
+
if (!VERSION_PATTERN.test(version)) {
|
|
37
|
+
throw new Error(`invalid package version ${version}`);
|
|
38
|
+
}
|
|
39
|
+
const target = releaseTarget(platform, architecture);
|
|
40
|
+
return `viceme_${version}_${target.operatingSystem}_${target.architecture}${target.extension}`;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
export async function ensureBinary({
|
|
44
|
+
packageVersion,
|
|
45
|
+
environment = process.env,
|
|
46
|
+
platform = process.platform,
|
|
47
|
+
architecture = process.arch,
|
|
48
|
+
fetchImplementation = globalThis.fetch,
|
|
49
|
+
releaseBaseURL = RELEASE_BASE_URL,
|
|
50
|
+
cacheDirectory,
|
|
51
|
+
allowInsecureURL = false,
|
|
52
|
+
}) {
|
|
53
|
+
if (environment.VICEME_BINARY_PATH) {
|
|
54
|
+
const overridden = path.resolve(environment.VICEME_BINARY_PATH);
|
|
55
|
+
await access(overridden, constants.X_OK);
|
|
56
|
+
return overridden;
|
|
57
|
+
}
|
|
58
|
+
if (typeof fetchImplementation !== "function") {
|
|
59
|
+
throw new Error("Node.js fetch support is required");
|
|
60
|
+
}
|
|
61
|
+
const asset = releaseAssetName(packageVersion, platform, architecture);
|
|
62
|
+
const root = cacheDirectory ?? defaultCacheDirectory(environment, platform);
|
|
63
|
+
const destinationDirectory = path.join(root, "cli", packageVersion);
|
|
64
|
+
const generationsDirectory = path.join(destinationDirectory, GENERATIONS_DIRECTORY);
|
|
65
|
+
const cached = await findValidGeneration(generationsDirectory, asset);
|
|
66
|
+
if (cached) {
|
|
67
|
+
return cached;
|
|
68
|
+
}
|
|
69
|
+
await mkdir(generationsDirectory, { recursive: true, mode: 0o700 });
|
|
70
|
+
const versionURL = `${releaseBaseURL}/v${packageVersion}`;
|
|
71
|
+
const checksumURL = `${versionURL}/${asset}.sha256`;
|
|
72
|
+
const binaryURL = `${versionURL}/${asset}`;
|
|
73
|
+
const [checksumDocument, binary] = await Promise.all([
|
|
74
|
+
download(checksumURL, fetchImplementation, allowInsecureURL),
|
|
75
|
+
download(binaryURL, fetchImplementation, allowInsecureURL),
|
|
76
|
+
]);
|
|
77
|
+
const expectedChecksum = parseChecksum(checksumDocument.toString("utf8"));
|
|
78
|
+
const actualChecksum = digest(binary);
|
|
79
|
+
if (actualChecksum !== expectedChecksum) {
|
|
80
|
+
throw new Error(`checksum mismatch for ${asset}`);
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
// If another cold start finished while this process downloaded, reuse its
|
|
84
|
+
// complete immutable generation. Duplicate downloads are harmless; shared
|
|
85
|
+
// partially-written binary/checksum pairs do not exist.
|
|
86
|
+
const publishedByContender = await findValidGeneration(generationsDirectory, asset);
|
|
87
|
+
if (publishedByContender) {
|
|
88
|
+
return publishedByContender;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
const generationID = `${expectedChecksum.slice(0, 16)}-${randomUUID()}`;
|
|
92
|
+
const stagingDirectory = path.join(
|
|
93
|
+
destinationDirectory,
|
|
94
|
+
`${STAGING_PREFIX}${process.pid}-${generationID}`,
|
|
95
|
+
);
|
|
96
|
+
const generationDirectory = path.join(
|
|
97
|
+
generationsDirectory,
|
|
98
|
+
`${GENERATION_PREFIX}${generationID}`,
|
|
99
|
+
);
|
|
100
|
+
const stagedBinary = path.join(stagingDirectory, asset);
|
|
101
|
+
const stagedChecksum = `${stagedBinary}.sha256`;
|
|
102
|
+
try {
|
|
103
|
+
await mkdir(stagingDirectory, { mode: 0o700 });
|
|
104
|
+
await writeFile(stagedBinary, binary, { mode: 0o700 });
|
|
105
|
+
await chmod(stagedBinary, 0o700);
|
|
106
|
+
await writeFile(stagedChecksum, `${expectedChecksum} ${asset}\n`, { mode: 0o600 });
|
|
107
|
+
if (!(await cachedBinaryIsValid(stagedBinary, stagedChecksum))) {
|
|
108
|
+
throw new Error(`staged binary verification failed for ${asset}`);
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
// Staging and generations share a parent filesystem. Renaming the complete
|
|
112
|
+
// unique directory is the only publication step, and its destination is
|
|
113
|
+
// never replaced or removed by another installer process.
|
|
114
|
+
await rename(stagingDirectory, generationDirectory);
|
|
115
|
+
} finally {
|
|
116
|
+
await rm(stagingDirectory, { recursive: true, force: true });
|
|
117
|
+
}
|
|
118
|
+
const installedBinary = path.join(generationDirectory, asset);
|
|
119
|
+
if (!(await cachedBinaryIsValid(installedBinary, `${installedBinary}.sha256`))) {
|
|
120
|
+
throw new Error(`published binary verification failed for ${asset}`);
|
|
121
|
+
}
|
|
122
|
+
return installedBinary;
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
async function findValidGeneration(generationsDirectory, asset) {
|
|
126
|
+
let entries;
|
|
127
|
+
try {
|
|
128
|
+
entries = await readdir(generationsDirectory, { withFileTypes: true });
|
|
129
|
+
} catch (error) {
|
|
130
|
+
if (error.code === "ENOENT") {
|
|
131
|
+
return undefined;
|
|
132
|
+
}
|
|
133
|
+
throw error;
|
|
134
|
+
}
|
|
135
|
+
for (const entry of entries
|
|
136
|
+
.filter((candidate) => candidate.isDirectory() && candidate.name.startsWith(GENERATION_PREFIX))
|
|
137
|
+
.sort((left, right) => left.name.localeCompare(right.name))) {
|
|
138
|
+
const binaryPath = path.join(generationsDirectory, entry.name, asset);
|
|
139
|
+
if (await cachedBinaryIsValid(binaryPath, `${binaryPath}.sha256`)) {
|
|
140
|
+
return binaryPath;
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
return undefined;
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
function defaultCacheDirectory(environment, platform) {
|
|
147
|
+
if (environment.VICEME_CACHE_DIR) {
|
|
148
|
+
return path.resolve(environment.VICEME_CACHE_DIR);
|
|
149
|
+
}
|
|
150
|
+
if (environment.XDG_CACHE_HOME) {
|
|
151
|
+
return path.join(environment.XDG_CACHE_HOME, "viceme");
|
|
152
|
+
}
|
|
153
|
+
if (platform === "win32" && environment.LOCALAPPDATA) {
|
|
154
|
+
return path.join(environment.LOCALAPPDATA, "Viceme", "Cache");
|
|
155
|
+
}
|
|
156
|
+
return path.join(os.homedir(), ".cache", "viceme");
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
async function cachedBinaryIsValid(binaryPath, checksumPath) {
|
|
160
|
+
try {
|
|
161
|
+
const [binary, checksumDocument] = await Promise.all([
|
|
162
|
+
readFile(binaryPath),
|
|
163
|
+
readFile(checksumPath, "utf8"),
|
|
164
|
+
access(binaryPath, constants.X_OK),
|
|
165
|
+
]);
|
|
166
|
+
const expected = parseChecksum(checksumDocument);
|
|
167
|
+
return digest(binary) === expected;
|
|
168
|
+
} catch {
|
|
169
|
+
return false;
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
async function download(url, fetchImplementation, allowInsecureURL) {
|
|
174
|
+
const parsed = new URL(url);
|
|
175
|
+
if (!allowInsecureURL && parsed.protocol !== "https:") {
|
|
176
|
+
throw new Error(`refusing non-HTTPS release URL ${url}`);
|
|
177
|
+
}
|
|
178
|
+
const response = await fetchImplementation(url, {
|
|
179
|
+
redirect: "follow",
|
|
180
|
+
headers: { "user-agent": "@viceme-ai/cli npm launcher" },
|
|
181
|
+
});
|
|
182
|
+
if (!response.ok) {
|
|
183
|
+
throw new Error(`download failed (${response.status}) for ${url}`);
|
|
184
|
+
}
|
|
185
|
+
if (!allowInsecureURL && response.url && new URL(response.url).protocol !== "https:") {
|
|
186
|
+
throw new Error(`release download redirected to a non-HTTPS URL`);
|
|
187
|
+
}
|
|
188
|
+
return Buffer.from(await response.arrayBuffer());
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
function parseChecksum(document) {
|
|
192
|
+
const match = document.trim().match(/^([a-fA-F0-9]{64})(?:\s|$)/);
|
|
193
|
+
if (!match) {
|
|
194
|
+
throw new Error("release checksum document is invalid");
|
|
195
|
+
}
|
|
196
|
+
return match[1].toLowerCase();
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
function digest(buffer) {
|
|
200
|
+
return createHash("sha256").update(buffer).digest("hex");
|
|
201
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@viceme-ai/cli",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Install and run the Viceme CLI and Viceme Agent Skill",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"bin": {
|
|
7
|
+
"viceme": "npm/bin/viceme.mjs"
|
|
8
|
+
},
|
|
9
|
+
"files": [
|
|
10
|
+
"npm/bin/",
|
|
11
|
+
"npm/lib/",
|
|
12
|
+
"README.md",
|
|
13
|
+
"CHANGELOG.md",
|
|
14
|
+
"docs/releasing.md"
|
|
15
|
+
],
|
|
16
|
+
"scripts": {
|
|
17
|
+
"test": "node --test npm/test/*.test.mjs",
|
|
18
|
+
"pack:check": "npm pack --dry-run",
|
|
19
|
+
"release:prepare": "node npm/scripts/prepare-release.mjs --fallback-ref origin/main"
|
|
20
|
+
},
|
|
21
|
+
"engines": {
|
|
22
|
+
"node": ">=18.20.0"
|
|
23
|
+
},
|
|
24
|
+
"repository": {
|
|
25
|
+
"type": "git",
|
|
26
|
+
"url": "git+https://github.com/ViceMe-AI/cli.git"
|
|
27
|
+
},
|
|
28
|
+
"publishConfig": {
|
|
29
|
+
"access": "public",
|
|
30
|
+
"provenance": true
|
|
31
|
+
},
|
|
32
|
+
"license": "Apache-2.0"
|
|
33
|
+
}
|