@sodapop-sh/cli 0.1.0-rc.9
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/LICENSE +21 -0
- package/README.md +35 -0
- package/THIRD_PARTY_NOTICES.md +17 -0
- package/bin/sodapop.js +10 -0
- package/lib/launcher.js +170 -0
- package/package.json +83 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Sodapop contributors
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
# `@sodapop-sh/cli`
|
|
2
|
+
|
|
3
|
+
The npm launcher for [Sodapop](https://sodapop.sh), a terminal
|
|
4
|
+
application backed by the official GitHub Copilot SDK.
|
|
5
|
+
|
|
6
|
+
After the corresponding packages are published, install with
|
|
7
|
+
`npm install --global @sodapop-sh/cli` and run `sodapop`.
|
|
8
|
+
|
|
9
|
+
The package installs an exact-version native payload through an
|
|
10
|
+
OS/architecture-specific optional dependency. It does not download executables
|
|
11
|
+
from a release URL at installation or first run. Available targets are declared
|
|
12
|
+
by this package's release metadata:
|
|
13
|
+
|
|
14
|
+
- macOS on Apple silicon or Intel
|
|
15
|
+
- glibc-based Linux on arm64 or amd64
|
|
16
|
+
- Windows x64, only in releases with a qualified Windows payload
|
|
17
|
+
|
|
18
|
+
Other operating system or CPU combinations, musl Linux, and ambiguous Linux
|
|
19
|
+
libc detection are not supported.
|
|
20
|
+
Installing with optional dependencies disabled leaves no native payload and
|
|
21
|
+
causes the launcher to report a corrective error. There is no installed Copilot
|
|
22
|
+
fallback. The launcher checks exact versions, platform metadata, and the native
|
|
23
|
+
binary SHA-256 before launch. Node 18 or later is needed; Go is not required.
|
|
24
|
+
|
|
25
|
+
The launcher preserves terminal input/output, working directory, arguments, and
|
|
26
|
+
native exit status. Uninstalling the npm packages does not remove Sodapop state,
|
|
27
|
+
runtime caches, credentials, or shell profiles.
|
|
28
|
+
|
|
29
|
+
Sodapop's original code is MIT-licensed. Native optional dependencies are
|
|
30
|
+
composite bundles whose upstream licenses and runtime terms are included in
|
|
31
|
+
their `LICENSES/` and `THIRD_PARTY_NOTICES.md`; they are not licensed only as MIT.
|
|
32
|
+
Sodapop is not an official GitHub product.
|
|
33
|
+
|
|
34
|
+
Source, documentation, and license information are available in the
|
|
35
|
+
[Sodapop repository](https://github.com/VeVarunSharma/sodapop).
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
# Third-party notices
|
|
2
|
+
|
|
3
|
+
Sodapop uses the GitHub Copilot Go SDK and Charm's Bubble Tea, Bubbles, Lip Gloss, Glamour, and Harmonica libraries, along with their Go dependencies. These components retain their respective copyrights and licenses.
|
|
4
|
+
|
|
5
|
+
- [GitHub Copilot SDK](https://github.com/github/copilot-sdk): MIT.
|
|
6
|
+
- [Bubble Tea](https://github.com/charmbracelet/bubbletea): MIT.
|
|
7
|
+
- [Bubbles](https://github.com/charmbracelet/bubbles): MIT.
|
|
8
|
+
- [Lip Gloss](https://github.com/charmbracelet/lipgloss): MIT.
|
|
9
|
+
- [Glamour](https://github.com/charmbracelet/glamour): MIT.
|
|
10
|
+
- [Harmonica](https://github.com/charmbracelet/harmonica): MIT; its spring implementation also retains Ryan Juckett's original license notice.
|
|
11
|
+
- [Copilot runtime releases](https://github.com/github/copilot-cli/releases): distributed under the accompanying runtime terms.
|
|
12
|
+
|
|
13
|
+
The pinned Copilot runtime is a separate GitHub distribution. Its applicable license and notices are included by the official SDK bundler. A candidate archive includes that runtime license and dependency license/notice files under `LICENSES/`.
|
|
14
|
+
|
|
15
|
+
Sodapop does not incorporate Crush application code or artwork. Crush's application license is separate from the licenses of the Charm UI libraries.
|
|
16
|
+
|
|
17
|
+
Sodapop's original code is licensed under the MIT license in `LICENSE`. That license does not replace the separate terms of the bundled runtime or dependencies. Sodapop is not an official GitHub product.
|
package/bin/sodapop.js
ADDED
package/lib/launcher.js
ADDED
|
@@ -0,0 +1,170 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
const fs = require("node:fs");
|
|
4
|
+
const path = require("node:path");
|
|
5
|
+
const { createHash } = require("node:crypto");
|
|
6
|
+
const { spawn } = require("node:child_process");
|
|
7
|
+
|
|
8
|
+
const TARGETS = Object.freeze({
|
|
9
|
+
"darwin-arm64": { package: "@sodapop-sh/darwin-arm64", platform: "darwin/arm64", os: "darwin", cpu: "arm64", binary: "sodapop" },
|
|
10
|
+
"darwin-x64": { package: "@sodapop-sh/darwin-amd64", platform: "darwin/amd64", os: "darwin", cpu: "x64", binary: "sodapop" },
|
|
11
|
+
"linux-arm64": { package: "@sodapop-sh/linux-arm64", platform: "linux/arm64", os: "linux", cpu: "arm64", binary: "sodapop" },
|
|
12
|
+
"linux-x64": { package: "@sodapop-sh/linux-amd64", platform: "linux/amd64", os: "linux", cpu: "x64", binary: "sodapop" },
|
|
13
|
+
"win32-x64": { package: "@sodapop-sh/windows-amd64", platform: "windows/amd64", os: "win32", cpu: "x64", binary: "sodapop.exe" }
|
|
14
|
+
});
|
|
15
|
+
const PLATFORM_PACKAGES = Object.freeze(Object.fromEntries(Object.entries(TARGETS).map(([key, target]) => [key, target.package])));
|
|
16
|
+
|
|
17
|
+
function failure(code, message) {
|
|
18
|
+
return Object.assign(new Error(`${code}: ${message}`), { code });
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
function selectTarget(platform = process.platform, arch = process.arch) {
|
|
22
|
+
const target = TARGETS[`${platform}-${arch}`];
|
|
23
|
+
if (!target) throw failure("SODAPOP_UNSUPPORTED_PLATFORM", `unsupported platform ${platform}/${arch}`);
|
|
24
|
+
return target;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
function platformPackage(platform, arch) {
|
|
28
|
+
return selectTarget(platform, arch).package;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
function requireGlibc(getReport = () => process.report.getReport()) {
|
|
32
|
+
let report;
|
|
33
|
+
try {
|
|
34
|
+
report = getReport();
|
|
35
|
+
} catch {
|
|
36
|
+
throw failure("SODAPOP_AMBIGUOUS_LIBC", "cannot identify the Linux C library; a glibc-based Node.js runtime is required");
|
|
37
|
+
}
|
|
38
|
+
const glibc = typeof report?.header?.glibcVersionRuntime === "string" && /^\d+\.\d+/.test(report.header.glibcVersionRuntime);
|
|
39
|
+
const musl = Array.isArray(report?.sharedObjects) && report.sharedObjects.some((name) => /(?:^|[/\\])(?:ld-musl-|libc\.musl-)/.test(name));
|
|
40
|
+
if (musl && !glibc) throw failure("SODAPOP_UNSUPPORTED_LIBC", "Linux musl is unsupported; use glibc Linux");
|
|
41
|
+
if (!glibc || musl) throw failure("SODAPOP_AMBIGUOUS_LIBC", "cannot unambiguously identify glibc Linux");
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
function hashFile(filename) {
|
|
45
|
+
const hash = createHash("sha256");
|
|
46
|
+
const fd = fs.openSync(filename, "r");
|
|
47
|
+
try {
|
|
48
|
+
const buffer = Buffer.alloc(1024 * 1024);
|
|
49
|
+
let count;
|
|
50
|
+
while ((count = fs.readSync(fd, buffer, 0, buffer.length, null)) !== 0) hash.update(buffer.subarray(0, count));
|
|
51
|
+
} finally {
|
|
52
|
+
fs.closeSync(fd);
|
|
53
|
+
}
|
|
54
|
+
return hash.digest("hex");
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
function resolvePayload(options = {}) {
|
|
58
|
+
const packageRoot = options.packageRoot || path.resolve(__dirname, "..");
|
|
59
|
+
const target = selectTarget(options.platform, options.arch);
|
|
60
|
+
if (target.os === "linux") requireGlibc(options.getReport);
|
|
61
|
+
const cli = JSON.parse(fs.readFileSync(path.join(packageRoot, "package.json"), "utf8"));
|
|
62
|
+
const entries = cli.sodapop?.artifacts?.filter((artifact) => artifact.platform === target.platform);
|
|
63
|
+
if (!entries || entries.length === 0 || !Object.hasOwn(cli.optionalDependencies || {}, target.package)) {
|
|
64
|
+
throw failure("SODAPOP_UNAVAILABLE_PLATFORM", `${target.platform} is not declared by @sodapop-sh/cli@${cli.version}`);
|
|
65
|
+
}
|
|
66
|
+
if (entries.length !== 1) throw failure("SODAPOP_AMBIGUOUS_PLATFORM", `multiple payloads declared for ${target.platform}`);
|
|
67
|
+
const artifact = entries[0];
|
|
68
|
+
if (cli.sodapop.schema_version !== 1 || cli.sodapop.version !== cli.version ||
|
|
69
|
+
cli.optionalDependencies[target.package] !== cli.version) {
|
|
70
|
+
throw failure("SODAPOP_PAYLOAD_MISMATCH", "CLI release metadata or exact-version dependency is inconsistent");
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
let metadataPath;
|
|
74
|
+
try {
|
|
75
|
+
metadataPath = require.resolve(`${target.package}/package.json`, { paths: [packageRoot] });
|
|
76
|
+
} catch (error) {
|
|
77
|
+
if (error.code !== "MODULE_NOT_FOUND") throw error;
|
|
78
|
+
throw failure("SODAPOP_MISSING_PAYLOAD", `native payload ${target.package}@${cli.version} is missing; reinstall @sodapop-sh/cli with optional dependencies enabled`);
|
|
79
|
+
}
|
|
80
|
+
const metadata = JSON.parse(fs.readFileSync(metadataPath, "utf8"));
|
|
81
|
+
if (metadata.version !== cli.version) {
|
|
82
|
+
throw failure("SODAPOP_VERSION_MISMATCH", `native payload version mismatch: @sodapop-sh/cli is ${cli.version}, ${target.package} is ${metadata.version}; reinstall @sodapop-sh/cli`);
|
|
83
|
+
}
|
|
84
|
+
const single = (value, expected) => Array.isArray(value) && value.length === 1 && value[0] === expected;
|
|
85
|
+
const binding = metadata.sodapop;
|
|
86
|
+
if (metadata.name !== target.package || !single(metadata.os, target.os) || !single(metadata.cpu, target.cpu) ||
|
|
87
|
+
(target.os === "linux" && !single(metadata.libc, "glibc")) ||
|
|
88
|
+
["schema_version", "version", "commit", "copilot_sdk_version", "copilot_runtime_version"].some((key) => binding?.[key] !== cli.sodapop[key]) ||
|
|
89
|
+
["platform", "archive", "archive_sha256", "binary_sha256"].some((key) => binding?.artifact?.[key] !== artifact[key]) ||
|
|
90
|
+
!/^[a-f0-9]{64}$/.test(artifact.binary_sha256)) {
|
|
91
|
+
throw failure("SODAPOP_PAYLOAD_MISMATCH", `native payload metadata mismatch for ${target.package}`);
|
|
92
|
+
}
|
|
93
|
+
const binary = path.join(path.dirname(metadataPath), "bin", target.binary);
|
|
94
|
+
try {
|
|
95
|
+
if (!fs.lstatSync(binary).isFile()) throw failure("SODAPOP_PAYLOAD_MISMATCH", "native payload must be a regular file");
|
|
96
|
+
fs.accessSync(binary, target.os === "win32" ? fs.constants.R_OK : fs.constants.X_OK);
|
|
97
|
+
} catch (error) {
|
|
98
|
+
if (error.code === "SODAPOP_PAYLOAD_MISMATCH") throw error;
|
|
99
|
+
if (!["ENOENT", "EACCES", "EPERM"].includes(error.code)) throw error;
|
|
100
|
+
throw failure("SODAPOP_MISSING_PAYLOAD", `${target.package}@${cli.version} does not contain executable bin/${target.binary}`);
|
|
101
|
+
}
|
|
102
|
+
if (hashFile(binary) !== artifact.binary_sha256) {
|
|
103
|
+
throw failure("SODAPOP_PAYLOAD_MISMATCH", `native payload binary hash mismatch for ${target.package}; reinstall @sodapop-sh/cli`);
|
|
104
|
+
}
|
|
105
|
+
return binary;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
async function main(args, options = {}) {
|
|
109
|
+
const runtime = options.process || process;
|
|
110
|
+
const errorOutput = options.errorOutput || ((message) => console.error(message));
|
|
111
|
+
let binary;
|
|
112
|
+
try {
|
|
113
|
+
binary = resolvePayload(options);
|
|
114
|
+
} catch (error) {
|
|
115
|
+
errorOutput(`sodapop: ${error.message}`);
|
|
116
|
+
return 1;
|
|
117
|
+
}
|
|
118
|
+
// Replacing Node preserves terminal job control and delivers each POSIX signal
|
|
119
|
+
// once. Older Node and Windows use the inherited-console subprocess path.
|
|
120
|
+
if (!options.spawn && runtime.platform !== "win32" && typeof runtime.execve === "function") {
|
|
121
|
+
try {
|
|
122
|
+
runtime.execve(binary, [binary, ...args], runtime.env);
|
|
123
|
+
} catch (error) {
|
|
124
|
+
errorOutput(`sodapop: SODAPOP_START_FAILED: ${error.message}`);
|
|
125
|
+
return 1;
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
return new Promise((resolve) => {
|
|
129
|
+
let child;
|
|
130
|
+
try {
|
|
131
|
+
child = (options.spawn || spawn)(binary, args, { stdio: "inherit", env: runtime.env, cwd: runtime.cwd(), shell: false });
|
|
132
|
+
} catch (error) {
|
|
133
|
+
errorOutput(`sodapop: SODAPOP_START_FAILED: ${error.message}`);
|
|
134
|
+
resolve(1);
|
|
135
|
+
return;
|
|
136
|
+
}
|
|
137
|
+
const handlers = new Map();
|
|
138
|
+
for (const signal of ["SIGINT", "SIGTERM", "SIGHUP"]) {
|
|
139
|
+
const handler = () => {
|
|
140
|
+
// Windows broadcasts Ctrl+C to the inherited console. kill(SIGINT)
|
|
141
|
+
// would forcibly terminate the child instead of letting it handle it.
|
|
142
|
+
if (!(runtime.platform === "win32" && signal === "SIGINT")) child.kill(signal);
|
|
143
|
+
};
|
|
144
|
+
handlers.set(signal, handler);
|
|
145
|
+
runtime.on(signal, handler);
|
|
146
|
+
}
|
|
147
|
+
const cleanup = () => {
|
|
148
|
+
for (const [signal, handler] of handlers) runtime.removeListener(signal, handler);
|
|
149
|
+
};
|
|
150
|
+
let settled = false;
|
|
151
|
+
child.once("error", (error) => {
|
|
152
|
+
if (settled) return;
|
|
153
|
+
settled = true;
|
|
154
|
+
cleanup();
|
|
155
|
+
errorOutput(`sodapop: SODAPOP_START_FAILED: ${error.message}`);
|
|
156
|
+
resolve(1);
|
|
157
|
+
});
|
|
158
|
+
child.once("exit", (status, signal) => {
|
|
159
|
+
if (settled) return;
|
|
160
|
+
settled = true;
|
|
161
|
+
cleanup();
|
|
162
|
+
if (signal) {
|
|
163
|
+
runtime.kill(runtime.pid, signal);
|
|
164
|
+
resolve(1);
|
|
165
|
+
} else resolve(status === null ? 1 : status);
|
|
166
|
+
});
|
|
167
|
+
});
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
module.exports = { TARGETS, PLATFORM_PACKAGES, hashFile, main, platformPackage, requireGlibc, resolvePayload };
|
package/package.json
ADDED
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@sodapop-sh/cli",
|
|
3
|
+
"version": "0.1.0-rc.9",
|
|
4
|
+
"description": "Sodapop terminal application backed by the official GitHub Copilot SDK",
|
|
5
|
+
"license": "MIT",
|
|
6
|
+
"homepage": "https://sodapop.sh",
|
|
7
|
+
"repository": {
|
|
8
|
+
"type": "git",
|
|
9
|
+
"url": "git+https://github.com/VeVarunSharma/sodapop.git"
|
|
10
|
+
},
|
|
11
|
+
"bugs": {
|
|
12
|
+
"url": "https://github.com/VeVarunSharma/sodapop/issues"
|
|
13
|
+
},
|
|
14
|
+
"keywords": [
|
|
15
|
+
"copilot",
|
|
16
|
+
"github-copilot",
|
|
17
|
+
"terminal",
|
|
18
|
+
"cli",
|
|
19
|
+
"tui"
|
|
20
|
+
],
|
|
21
|
+
"bin": {
|
|
22
|
+
"sodapop": "bin/sodapop.js"
|
|
23
|
+
},
|
|
24
|
+
"files": [
|
|
25
|
+
"bin/sodapop.js",
|
|
26
|
+
"lib/launcher.js",
|
|
27
|
+
"LICENSE",
|
|
28
|
+
"README.md",
|
|
29
|
+
"THIRD_PARTY_NOTICES.md"
|
|
30
|
+
],
|
|
31
|
+
"engines": {
|
|
32
|
+
"node": ">=18"
|
|
33
|
+
},
|
|
34
|
+
"optionalDependencies": {
|
|
35
|
+
"@sodapop-sh/darwin-amd64": "0.1.0-rc.9",
|
|
36
|
+
"@sodapop-sh/darwin-arm64": "0.1.0-rc.9",
|
|
37
|
+
"@sodapop-sh/linux-amd64": "0.1.0-rc.9",
|
|
38
|
+
"@sodapop-sh/linux-arm64": "0.1.0-rc.9",
|
|
39
|
+
"@sodapop-sh/windows-amd64": "0.1.0-rc.9"
|
|
40
|
+
},
|
|
41
|
+
"publishConfig": {
|
|
42
|
+
"access": "public"
|
|
43
|
+
},
|
|
44
|
+
"sodapop": {
|
|
45
|
+
"schema_version": 1,
|
|
46
|
+
"version": "0.1.0-rc.9",
|
|
47
|
+
"commit": "9670eb81481c6eebd751b54e3546939e4db4659d",
|
|
48
|
+
"copilot_runtime_version": "1.0.83",
|
|
49
|
+
"copilot_sdk_version": "1.0.13",
|
|
50
|
+
"artifacts": [
|
|
51
|
+
{
|
|
52
|
+
"platform": "darwin/amd64",
|
|
53
|
+
"archive": "sodapop-0.1.0-rc.9-darwin-amd64.tar.gz",
|
|
54
|
+
"archive_sha256": "19f171e8f9afe8548b5720f11e6b0aa657092ceb3485bd062b5a3a7ac96aac11",
|
|
55
|
+
"binary_sha256": "37e0c546ca7c4085d43c85c37d1a18ed68ee3e622664b4a7dfe9320dd470b334"
|
|
56
|
+
},
|
|
57
|
+
{
|
|
58
|
+
"platform": "darwin/arm64",
|
|
59
|
+
"archive": "sodapop-0.1.0-rc.9-darwin-arm64.tar.gz",
|
|
60
|
+
"archive_sha256": "eab963e3f4051a117dfa3b9ba7b8dd6e7f78f4ba9df60d18cfe32d24ab58ea62",
|
|
61
|
+
"binary_sha256": "89b7e059ab11ad29f375893bd3e16dc5e3acfe9a1ed674bfedddc26d0a7fdbe6"
|
|
62
|
+
},
|
|
63
|
+
{
|
|
64
|
+
"platform": "linux/amd64",
|
|
65
|
+
"archive": "sodapop-0.1.0-rc.9-linux-amd64.tar.gz",
|
|
66
|
+
"archive_sha256": "3d0713dca2e766c2f1aa73cae8dfb1ea1f21e7f28a01ad14e52d7bbb6a2b653e",
|
|
67
|
+
"binary_sha256": "3aac57c12a8eb9ac2e166df37d17d62ec9d7fa5bb81fe0437c4924e707a049ce"
|
|
68
|
+
},
|
|
69
|
+
{
|
|
70
|
+
"platform": "linux/arm64",
|
|
71
|
+
"archive": "sodapop-0.1.0-rc.9-linux-arm64.tar.gz",
|
|
72
|
+
"archive_sha256": "ddaa4bb8725cd394cafbac3087741303219e3764d2b9e703f4ab6bc77fe95d97",
|
|
73
|
+
"binary_sha256": "20bf017d1e4bb0f0bc3593ee62110cbef3dce264d76e3cbd2b344ceb1f00b8c5"
|
|
74
|
+
},
|
|
75
|
+
{
|
|
76
|
+
"platform": "windows/amd64",
|
|
77
|
+
"archive": "sodapop-0.1.0-rc.9-windows-amd64.zip",
|
|
78
|
+
"archive_sha256": "898c955fbfa01a719ba9d1b3b1542cdb92582a2447d218345cf7ff321697a081",
|
|
79
|
+
"binary_sha256": "d84c057284ec9eb823c29b9ec0e6b9291ac11d20d7746b02dac96495f8551474"
|
|
80
|
+
}
|
|
81
|
+
]
|
|
82
|
+
}
|
|
83
|
+
}
|