@crocoder-dev/sce 0.2.0-pre-alpha-v2

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 ADDED
@@ -0,0 +1,31 @@
1
+ # sce
2
+
3
+ Thin npm launcher package for the `sce` CLI.
4
+
5
+ Published from the `crocoder-dev/shared-context-engineering` repository.
6
+
7
+ ## Install
8
+
9
+ ```bash
10
+ npm install -g sce
11
+ ```
12
+
13
+ Supported npm install targets:
14
+
15
+ - `darwin/arm64` → `aarch64-apple-darwin`
16
+ - `linux/arm64` → `aarch64-unknown-linux-gnu`
17
+ - `linux/x64` → `x86_64-unknown-linux-gnu`
18
+
19
+ ## Release flow
20
+
21
+ On install, this package downloads the matching platform release artifact for the
22
+ current `sce` version from GitHub Releases, verifies the published SHA-256
23
+ checksum, and installs the native `sce` binary for local execution. Linux ARM
24
+ is an officially supported npm install target via `linux/arm64` mapping to the
25
+ GitHub release artifact for `aarch64-unknown-linux-gnu`.
26
+
27
+ Repo-root `.version` is the canonical checked-in release version source. GitHub
28
+ Releases publish the canonical signed release archives and manifest/checksum
29
+ assets first; npm registry publication is a separate downstream publish stage
30
+ for the already-versioned checked-in package and does not auto-bump the package
31
+ version during publish.
package/bin/sce.js ADDED
@@ -0,0 +1,63 @@
1
+ #!/usr/bin/env node
2
+
3
+ import { spawn } from "node:child_process";
4
+ import { accessSync, constants as fsConstants } from "node:fs";
5
+ import path from "node:path";
6
+ import process from "node:process";
7
+ import { fileURLToPath } from "node:url";
8
+
9
+ import {
10
+ formatUnsupportedPlatformMessage,
11
+ getInstalledBinaryPath,
12
+ } from "../lib/platform.js";
13
+
14
+ const __dirname = path.dirname(fileURLToPath(import.meta.url));
15
+
16
+ function ensureInstalledBinaryPath() {
17
+ const binaryPath = getInstalledBinaryPath(__dirname);
18
+
19
+ try {
20
+ accessSync(binaryPath, fsConstants.X_OK);
21
+ return binaryPath;
22
+ } catch {
23
+ return null;
24
+ }
25
+ }
26
+
27
+ const binaryPath = ensureInstalledBinaryPath();
28
+
29
+ if (!binaryPath) {
30
+ const unsupportedMessage = formatUnsupportedPlatformMessage(
31
+ process.platform,
32
+ process.arch,
33
+ );
34
+
35
+ if (unsupportedMessage) {
36
+ console.error(unsupportedMessage);
37
+ } else {
38
+ console.error(
39
+ "The native sce binary is not installed. Try reinstalling the package or run `npm rebuild sce` to retry the download.",
40
+ );
41
+ }
42
+
43
+ process.exit(1);
44
+ }
45
+
46
+ const child = spawn(binaryPath, process.argv.slice(2), {
47
+ stdio: "inherit",
48
+ env: process.env,
49
+ });
50
+
51
+ child.on("exit", (code, signal) => {
52
+ if (signal) {
53
+ process.kill(process.pid, signal);
54
+ return;
55
+ }
56
+
57
+ process.exit(code ?? 1);
58
+ });
59
+
60
+ child.on("error", (error) => {
61
+ console.error(`Failed to launch sce: ${error.message}`);
62
+ process.exit(1);
63
+ });
package/lib/install.js ADDED
@@ -0,0 +1,411 @@
1
+ import { spawnSync } from "node:child_process";
2
+ import { createHash, createVerify } from "node:crypto";
3
+ import {
4
+ chmodSync,
5
+ copyFileSync,
6
+ createReadStream,
7
+ createWriteStream,
8
+ existsSync,
9
+ mkdirSync,
10
+ mkdtempSync,
11
+ readFileSync,
12
+ realpathSync,
13
+ rmSync,
14
+ } from "node:fs";
15
+ import { get } from "node:https";
16
+ import { tmpdir } from "node:os";
17
+ import path from "node:path";
18
+ import process from "node:process";
19
+ import { pipeline } from "node:stream/promises";
20
+ import { fileURLToPath } from "node:url";
21
+
22
+ import {
23
+ formatUnsupportedPlatformMessage,
24
+ getArchiveName,
25
+ getArchiveRoot,
26
+ getInstalledBinaryPath,
27
+ getReleaseManifestName,
28
+ resolveSupportedPlatform,
29
+ selectReleaseArtifact,
30
+ } from "./platform.js";
31
+
32
+ const __dirname = path.dirname(fileURLToPath(import.meta.url));
33
+ const PACKAGE_ROOT = path.resolve(__dirname, "..");
34
+ const PACKAGE_JSON_PATH = path.join(PACKAGE_ROOT, "package.json");
35
+ const MANIFEST_PUBLIC_KEY_PATH = path.join(
36
+ __dirname,
37
+ "release-manifest-public-key.pem",
38
+ );
39
+ const RUNTIME_DIR = path.join(PACKAGE_ROOT, "runtime");
40
+ const DOWNLOAD_TIMEOUT_MS = 30_000;
41
+ const DEFAULT_REPOSITORY_SLUG = "crocoder-dev/shared-context-engineering";
42
+
43
+ function coerceManifestPayload(manifestPayload) {
44
+ if (typeof manifestPayload === "string" || Buffer.isBuffer(manifestPayload)) {
45
+ return manifestPayload;
46
+ }
47
+
48
+ return JSON.stringify(manifestPayload);
49
+ }
50
+
51
+ function decodeManifestSignature(signaturePayload) {
52
+ if (Buffer.isBuffer(signaturePayload)) {
53
+ return signaturePayload;
54
+ }
55
+
56
+ if (
57
+ typeof signaturePayload !== "string" ||
58
+ signaturePayload.trim().length === 0
59
+ ) {
60
+ throw new Error(
61
+ "Release manifest signature payload must be a non-empty base64 string or Buffer.",
62
+ );
63
+ }
64
+
65
+ return Buffer.from(signaturePayload.trim(), "base64");
66
+ }
67
+
68
+ export function readBundledReleaseManifestPublicKey(
69
+ publicKeyPath = MANIFEST_PUBLIC_KEY_PATH,
70
+ ) {
71
+ return readFileSync(publicKeyPath, "utf8");
72
+ }
73
+
74
+ export function verifyReleaseManifestSignature(
75
+ manifestPayload,
76
+ signaturePayload,
77
+ publicKeyPem = readBundledReleaseManifestPublicKey(),
78
+ ) {
79
+ const verifier = createVerify("sha256");
80
+ verifier.update(coerceManifestPayload(manifestPayload));
81
+ verifier.end();
82
+
83
+ return verifier.verify(
84
+ publicKeyPem,
85
+ decodeManifestSignature(signaturePayload),
86
+ );
87
+ }
88
+
89
+ function readPackageVersion() {
90
+ const packageJson = JSON.parse(readFileSync(PACKAGE_JSON_PATH, "utf8"));
91
+
92
+ if (!packageJson.version || typeof packageJson.version !== "string") {
93
+ throw new Error("Invalid npm package metadata: missing version.");
94
+ }
95
+
96
+ return packageJson.version;
97
+ }
98
+
99
+ function getRepositorySlug() {
100
+ return DEFAULT_REPOSITORY_SLUG;
101
+ }
102
+
103
+ export function getReleaseBaseUrl(version) {
104
+ return (
105
+ process.env.SCE_NPM_RELEASE_BASE_URL ??
106
+ `https://github.com/${DEFAULT_REPOSITORY_SLUG}/releases/download/v${version}`
107
+ );
108
+ }
109
+
110
+ function getManifestUrl(version) {
111
+ return `${getReleaseBaseUrl(version)}/${getReleaseManifestName(version)}`;
112
+ }
113
+
114
+ function getManifestSignatureUrl(version) {
115
+ return `${getManifestUrl(version)}.sig`;
116
+ }
117
+
118
+ async function downloadToFile(url, destinationPath, redirectsRemaining = 5) {
119
+ await new Promise((resolve, reject) => {
120
+ let settled = false;
121
+ let timeoutId;
122
+
123
+ const finish = (handler, value) => {
124
+ if (settled) {
125
+ return;
126
+ }
127
+
128
+ settled = true;
129
+ clearTimeout(timeoutId);
130
+ request.removeListener("error", handleError);
131
+ handler(value);
132
+ };
133
+
134
+ const handleError = (error) => {
135
+ finish(reject, error);
136
+ };
137
+
138
+ const request = get(url, (response) => {
139
+ const { statusCode = 0, headers } = response;
140
+
141
+ if (statusCode >= 300 && statusCode < 400 && headers.location) {
142
+ clearTimeout(timeoutId);
143
+ response.resume();
144
+
145
+ if (redirectsRemaining <= 0) {
146
+ finish(
147
+ reject,
148
+ new Error(`Too many redirects while downloading ${url}.`),
149
+ );
150
+ return;
151
+ }
152
+
153
+ const redirectedUrl = new URL(headers.location, url).toString();
154
+ downloadToFile(redirectedUrl, destinationPath, redirectsRemaining - 1)
155
+ .then((value) => finish(resolve, value))
156
+ .catch((error) => finish(reject, error));
157
+ return;
158
+ }
159
+
160
+ if (statusCode !== 200) {
161
+ clearTimeout(timeoutId);
162
+ response.resume();
163
+ finish(
164
+ reject,
165
+ new Error(
166
+ `Unexpected response ${statusCode} while downloading ${url}.`,
167
+ ),
168
+ );
169
+ return;
170
+ }
171
+
172
+ const output = createWriteStream(destinationPath);
173
+ pipeline(response, output)
174
+ .then((value) => finish(resolve, value))
175
+ .catch((error) => finish(reject, error));
176
+ });
177
+
178
+ timeoutId = setTimeout(() => {
179
+ const error = new Error(
180
+ `Request timed out after ${DOWNLOAD_TIMEOUT_MS}ms while downloading ${url}.`,
181
+ );
182
+ request.destroy(error);
183
+ }, DOWNLOAD_TIMEOUT_MS);
184
+
185
+ request.on("error", handleError);
186
+ });
187
+ }
188
+
189
+ async function downloadText(url) {
190
+ const tempDir = mkdtempSync(path.join(tmpdir(), "sce-npm-text-"));
191
+ const textPath = path.join(tempDir, "payload.txt");
192
+
193
+ try {
194
+ await downloadToFile(url, textPath);
195
+ return readFileSync(textPath, "utf8");
196
+ } finally {
197
+ rmSync(tempDir, { recursive: true, force: true });
198
+ }
199
+ }
200
+
201
+ export function parseVerifiedReleaseManifest(
202
+ manifestPayload,
203
+ signaturePayload,
204
+ publicKeyPem = readBundledReleaseManifestPublicKey(),
205
+ ) {
206
+ let manifest;
207
+
208
+ try {
209
+ manifest = JSON.parse(coerceManifestPayload(manifestPayload).toString());
210
+ } catch {
211
+ throw new Error(
212
+ "Invalid sce release manifest: failed to parse JSON payload.",
213
+ );
214
+ }
215
+
216
+ try {
217
+ if (
218
+ !verifyReleaseManifestSignature(
219
+ manifestPayload,
220
+ signaturePayload,
221
+ publicKeyPem,
222
+ )
223
+ ) {
224
+ throw new Error();
225
+ }
226
+ } catch {
227
+ throw new Error(
228
+ "Release manifest authenticity check failed: signature verification did not succeed.",
229
+ );
230
+ }
231
+
232
+ return manifest;
233
+ }
234
+
235
+ export async function loadVerifiedReleaseManifest(
236
+ version,
237
+ {
238
+ downloadManifest = downloadText,
239
+ downloadManifestSignature = downloadText,
240
+ publicKeyPem = readBundledReleaseManifestPublicKey(),
241
+ } = {},
242
+ ) {
243
+ let manifestPayload;
244
+
245
+ try {
246
+ manifestPayload = await downloadManifest(getManifestUrl(version));
247
+ } catch (error) {
248
+ throw new Error(
249
+ `Failed to download sce release manifest: ${error.message}`,
250
+ );
251
+ }
252
+
253
+ let signaturePayload;
254
+
255
+ try {
256
+ signaturePayload = await downloadManifestSignature(
257
+ getManifestSignatureUrl(version),
258
+ );
259
+ } catch (error) {
260
+ throw new Error(
261
+ `Failed to download sce release manifest signature: ${error.message}`,
262
+ );
263
+ }
264
+
265
+ return parseVerifiedReleaseManifest(
266
+ manifestPayload,
267
+ signaturePayload,
268
+ publicKeyPem,
269
+ );
270
+ }
271
+
272
+ function sha256File(filePath) {
273
+ return new Promise((resolve, reject) => {
274
+ const hash = createHash("sha256");
275
+ const input = createReadStream(filePath);
276
+
277
+ input.on("data", (chunk) => {
278
+ hash.update(chunk);
279
+ });
280
+
281
+ input.on("end", () => {
282
+ resolve(hash.digest("hex"));
283
+ });
284
+
285
+ input.on("error", (error) => {
286
+ reject(error);
287
+ });
288
+ });
289
+ }
290
+
291
+ function extractArchive(archivePath, destinationDir) {
292
+ const tarResult = spawnSync(
293
+ "tar",
294
+ ["-xzf", archivePath, "-C", destinationDir],
295
+ {
296
+ stdio: "pipe",
297
+ encoding: "utf8",
298
+ },
299
+ );
300
+
301
+ if (tarResult.error) {
302
+ throw new Error(
303
+ `Failed to extract sce release archive: ${tarResult.error.message}`,
304
+ );
305
+ }
306
+
307
+ if (tarResult.status !== 0) {
308
+ throw new Error(
309
+ tarResult.stderr?.trim() ||
310
+ tarResult.error?.message ||
311
+ "Failed to extract sce release archive.",
312
+ );
313
+ }
314
+ }
315
+
316
+ export async function installBinaryWithDependencies({
317
+ supportedPlatform = resolveSupportedPlatform(),
318
+ unsupportedMessage = formatUnsupportedPlatformMessage(),
319
+ version = readPackageVersion(),
320
+ loadReleaseManifest = loadVerifiedReleaseManifest,
321
+ downloadArchive = downloadToFile,
322
+ checksumFile = sha256File,
323
+ extractArchiveFn = extractArchive,
324
+ fileExists = existsSync,
325
+ createRuntimeDir = mkdirSync,
326
+ copyBinary = copyFileSync,
327
+ chmodBinary = chmodSync,
328
+ createTempDir = () => mkdtempSync(path.join(tmpdir(), "sce-npm-install-")),
329
+ removeTempDir = (tempDir) =>
330
+ rmSync(tempDir, { recursive: true, force: true }),
331
+ } = {}) {
332
+ if (process.env.SCE_NPM_SKIP_DOWNLOAD === "1") {
333
+ console.log(
334
+ "Skipping sce binary download because SCE_NPM_SKIP_DOWNLOAD=1.",
335
+ );
336
+ return;
337
+ }
338
+
339
+ if (!supportedPlatform) {
340
+ throw new Error(
341
+ unsupportedMessage ?? "Unsupported platform for npm sce package.",
342
+ );
343
+ }
344
+
345
+ const releaseManifest = await loadReleaseManifest(version);
346
+ const artifact = selectReleaseArtifact(
347
+ releaseManifest,
348
+ supportedPlatform.targetTriple,
349
+ );
350
+ const archiveName =
351
+ artifact.archive ?? getArchiveName(version, supportedPlatform.targetTriple);
352
+ const expectedChecksum = artifact.checksum_sha256;
353
+
354
+ if (!expectedChecksum) {
355
+ throw new Error(
356
+ `Release artifact ${archiveName} is missing checksum_sha256 metadata.`,
357
+ );
358
+ }
359
+
360
+ const tempDir = createTempDir();
361
+
362
+ try {
363
+ const archivePath = path.join(tempDir, archiveName);
364
+ const archiveUrl = `${getReleaseBaseUrl(version)}/${archiveName}`;
365
+
366
+ await downloadArchive(archiveUrl, archivePath);
367
+
368
+ const actualChecksum = await checksumFile(archivePath);
369
+ if (actualChecksum !== expectedChecksum) {
370
+ throw new Error(
371
+ `Downloaded sce archive checksum mismatch for ${archiveName}: expected ${expectedChecksum}, received ${actualChecksum}.`,
372
+ );
373
+ }
374
+
375
+ extractArchiveFn(archivePath, tempDir);
376
+
377
+ const extractedBinaryPath = path.join(
378
+ tempDir,
379
+ getArchiveRoot(version, supportedPlatform.targetTriple),
380
+ "bin",
381
+ "sce",
382
+ );
383
+
384
+ if (!fileExists(extractedBinaryPath)) {
385
+ throw new Error(
386
+ `Extracted sce archive did not contain ${archiveName} -> bin/sce.`,
387
+ );
388
+ }
389
+
390
+ createRuntimeDir(RUNTIME_DIR, { recursive: true });
391
+ const installedBinaryPath = getInstalledBinaryPath(__dirname);
392
+ copyBinary(extractedBinaryPath, installedBinaryPath);
393
+ chmodBinary(installedBinaryPath, 0o755);
394
+ } finally {
395
+ removeTempDir(tempDir);
396
+ }
397
+ }
398
+
399
+ export async function installBinary() {
400
+ await installBinaryWithDependencies();
401
+ }
402
+
403
+ if (
404
+ process.argv[1] &&
405
+ realpathSync(process.argv[1]) === realpathSync(fileURLToPath(import.meta.url))
406
+ ) {
407
+ installBinary().catch((error) => {
408
+ console.error(`Failed to install sce via npm: ${error.message}`);
409
+ process.exit(1);
410
+ });
411
+ }
@@ -0,0 +1,68 @@
1
+ import path from "node:path";
2
+
3
+ const SUPPORTED_TARGETS = new Map([
4
+ [
5
+ "darwin:arm64",
6
+ { targetTriple: "aarch64-apple-darwin", os: "darwin", arch: "arm64" },
7
+ ],
8
+ [
9
+ "linux:arm64",
10
+ { targetTriple: "aarch64-unknown-linux-gnu", os: "linux", arch: "arm64" },
11
+ ],
12
+ [
13
+ "linux:x64",
14
+ { targetTriple: "x86_64-unknown-linux-gnu", os: "linux", arch: "x64" },
15
+ ],
16
+ ]);
17
+
18
+ export function resolveSupportedPlatform(
19
+ platform = process.platform,
20
+ arch = process.arch,
21
+ ) {
22
+ return SUPPORTED_TARGETS.get(`${platform}:${arch}`) ?? null;
23
+ }
24
+
25
+ export function getArchiveRoot(version, targetTriple) {
26
+ return `sce-v${version}-${targetTriple}`;
27
+ }
28
+
29
+ export function getArchiveName(version, targetTriple) {
30
+ return `${getArchiveRoot(version, targetTriple)}.tar.gz`;
31
+ }
32
+
33
+ export function getReleaseManifestName(version) {
34
+ return `sce-v${version}-release-manifest.json`;
35
+ }
36
+
37
+ export function getInstalledBinaryPath(baseDir) {
38
+ return path.resolve(baseDir, "..", "runtime", "sce");
39
+ }
40
+
41
+ export function formatUnsupportedPlatformMessage(
42
+ platform = process.platform,
43
+ arch = process.arch,
44
+ ) {
45
+ if (resolveSupportedPlatform(platform, arch)) {
46
+ return null;
47
+ }
48
+
49
+ return `The npm sce package currently supports darwin/arm64, linux/arm64, and linux/x64. Received ${platform}/${arch}.`;
50
+ }
51
+
52
+ export function selectReleaseArtifact(releaseManifest, targetTriple) {
53
+ if (!releaseManifest || !Array.isArray(releaseManifest.artifacts)) {
54
+ throw new Error("Invalid sce release manifest: missing artifacts array.");
55
+ }
56
+
57
+ const artifact = releaseManifest.artifacts.find(
58
+ (candidate) => candidate.target_triple === targetTriple,
59
+ );
60
+
61
+ if (!artifact) {
62
+ throw new Error(
63
+ `No sce release artifact found for target ${targetTriple}.`,
64
+ );
65
+ }
66
+
67
+ return artifact;
68
+ }
@@ -0,0 +1,9 @@
1
+ -----BEGIN PUBLIC KEY-----
2
+ MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA1a1Xxm6XPieD8JERTBgp
3
+ j+0iRxTJwZVDgxl6763SM6gB+y3FFyBCrul1K7YoubmiQ7v+P8c/joE+bxBpcXLi
4
+ ss+vdt+//dHlDy9N2643vgIqiG9ILzDTZAMfQyZrB76Yw9lztrI9zXNntWPKmmuj
5
+ k4io5HoJ6vOf8RttC1MZwW3kohv4+x+w38KJGMuPqJ3ltMvnAiWucPDCPvktMKra
6
+ lhFuYF6pyDJfiE554R6G3usqtDBUmkzwUDZH168vgzgyNqwzP0Rzuex86Wgy+ZkF
7
+ PEGo5WI2bGQRZhgzqb9S1H54JycH/XG7pBNdhO6MTViwTbpAb5a0UsNvdqesXHPq
8
+ DwIDAQAB
9
+ -----END PUBLIC KEY-----
package/package.json ADDED
@@ -0,0 +1,39 @@
1
+ {
2
+ "name": "@crocoder-dev/sce",
3
+ "version": "0.2.0-pre-alpha-v2",
4
+ "description": "npm launcher package for the Shared Context Engineering CLI",
5
+ "type": "module",
6
+ "private": false,
7
+ "bin": {
8
+ "sce": "./bin/sce.js"
9
+ },
10
+ "files": [
11
+ "bin",
12
+ "lib",
13
+ "README.md"
14
+ ],
15
+ "scripts": {
16
+ "postinstall": "node ./lib/install.js",
17
+ "test": "bun test ./test/*.test.js"
18
+ },
19
+ "publishConfig": {
20
+ "access": "public"
21
+ },
22
+ "repository": {
23
+ "type": "git",
24
+ "url": "git+https://github.com/crocoder-dev/shared-context-engineering.git"
25
+ },
26
+ "homepage": "https://github.com/crocoder-dev/shared-context-engineering",
27
+ "bugs": {
28
+ "url": "https://github.com/crocoder-dev/shared-context-engineering/issues"
29
+ },
30
+ "keywords": [
31
+ "sce",
32
+ "cli",
33
+ "shared-context-engineering"
34
+ ],
35
+ "license": "MIT",
36
+ "engines": {
37
+ "node": ">=18"
38
+ }
39
+ }