feat: publish Sandwich Hime tooling preview
Publish the exact sanitized Agent Skill and VS Code preview source tree with independent license boundaries, deterministic provenance manifests, and no private development history. Material design and implementation assistance was provided by OpenAI Codex. Signed-off-by: Cole Speelman <crspeelman@gmail.com>
This commit is contained in:
@@ -0,0 +1,31 @@
|
||||
// SPDX-License-Identifier: AGPL-3.0-only
|
||||
'use strict';
|
||||
|
||||
const fs = require('node:fs');
|
||||
const path = require('node:path');
|
||||
|
||||
const root = path.join(__dirname, '..');
|
||||
const manifest = JSON.parse(fs.readFileSync(path.join(root, 'package.json'), 'utf8'));
|
||||
const lock = JSON.parse(fs.readFileSync(path.join(root, 'package-lock.json'), 'utf8'));
|
||||
if (lock.lockfileVersion !== 3) throw new Error('package-lock must use lockfileVersion 3');
|
||||
|
||||
for (const group of ['dependencies', 'devDependencies']) {
|
||||
for (const [name, version] of Object.entries(manifest[group] || {})) {
|
||||
if (!/^\d+\.\d+\.\d+(?:-[0-9A-Za-z.-]+)?$/.test(version)) {
|
||||
throw new Error(`${group} must pin ${name} to an exact version`);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const productionLicenses = new Set(['MIT', 'ISC']);
|
||||
for (const [name, pkg] of Object.entries(lock.packages || {})) {
|
||||
if (!name.startsWith('node_modules/')) continue;
|
||||
if (!pkg.version || !pkg.integrity || !pkg.resolved?.startsWith('https://registry.npmjs.org/')) {
|
||||
throw new Error(`unverifiable registry dependency: ${name}`);
|
||||
}
|
||||
if (!pkg.dev && !productionLicenses.has(pkg.license)) {
|
||||
throw new Error(`unreviewed production dependency license: ${name} (${pkg.license || 'missing'})`);
|
||||
}
|
||||
}
|
||||
|
||||
console.log('lockfile integrity, exact direct pins, registry origins, and production licenses verified');
|
||||
@@ -0,0 +1,28 @@
|
||||
// SPDX-License-Identifier: AGPL-3.0-only
|
||||
'use strict';
|
||||
const fs = require('node:fs');
|
||||
const path = require('node:path');
|
||||
const childProcess = require('node:child_process');
|
||||
|
||||
const root = path.join(__dirname, '..');
|
||||
const manifest = JSON.parse(fs.readFileSync(path.join(root, 'package.json'), 'utf8'));
|
||||
if (manifest.publisher !== 'gamertan' || manifest.name !== 'sandwich-hime' || manifest.version !== '0.1.0-preview.1') throw new Error('extension identity drift');
|
||||
if (JSON.stringify(manifest.dependencies) !== JSON.stringify({'vscode-languageclient': '9.0.1'})) throw new Error('runtime dependency allowlist drift');
|
||||
const bundle = fs.readFileSync(path.join(root, 'dist', 'extension.js'), 'utf8');
|
||||
if (!bundle.startsWith('// SPDX-License-Identifier: AGPL-3.0-only')) throw new Error('bundle license marker missing');
|
||||
for (const forbidden of ['@opentelemetry', 'applicationinsights', 'segment.io', 'mixpanel']) {
|
||||
if (bundle.toLowerCase().includes(forbidden)) throw new Error(`forbidden telemetry marker in bundle: ${forbidden}`);
|
||||
}
|
||||
if (Buffer.byteLength(bundle) > 4 * 1024 * 1024) throw new Error('bundled extension exceeds 4 MiB');
|
||||
const vsce = path.join(root, 'node_modules', '@vscode', 'vsce', 'vsce');
|
||||
const listed = childProcess.spawnSync(process.execPath, [vsce, 'ls', '--no-dependencies'], {cwd: root, encoding: 'utf8'});
|
||||
if (listed.status !== 0) throw new Error(`vsce file audit failed: ${listed.stderr}`);
|
||||
const allowed = [
|
||||
'.vscodeignore', 'README.md', 'LICENSE', 'SECURITY.md', 'THIRD_PARTY_NOTICES.md', 'package.json',
|
||||
'dist/extension.js', 'language-configuration.json', 'snippets/sando.json',
|
||||
'syntaxes/sando.tmLanguage.json',
|
||||
];
|
||||
const actual = listed.stdout.trim().split(/\r?\n/).map((line) => line.trim()).filter(Boolean).sort();
|
||||
for (const file of actual) if (!allowed.includes(file)) throw new Error(`unexpected packaged file: ${file}`);
|
||||
for (const file of allowed.filter((name) => name !== '.vscodeignore')) if (!actual.includes(file)) throw new Error(`missing packaged file: ${file}`);
|
||||
console.log(`package allowlist verified: ${actual.length} files, bundle ${Buffer.byteLength(bundle)} bytes`);
|
||||
@@ -0,0 +1,17 @@
|
||||
// SPDX-License-Identifier: AGPL-3.0-only
|
||||
'use strict';
|
||||
const path = require('node:path');
|
||||
const esbuild = require('esbuild');
|
||||
esbuild.buildSync({
|
||||
entryPoints: [path.join(__dirname, '..', 'src', 'extension.js')],
|
||||
outfile: path.join(__dirname, '..', 'dist', 'extension.js'),
|
||||
bundle: true,
|
||||
platform: 'node',
|
||||
format: 'cjs',
|
||||
target: 'node20',
|
||||
external: ['vscode'],
|
||||
legalComments: 'eof',
|
||||
banner: {js: '// SPDX-License-Identifier: AGPL-3.0-only'},
|
||||
sourcemap: false,
|
||||
minify: false,
|
||||
});
|
||||
@@ -0,0 +1,5 @@
|
||||
// SPDX-License-Identifier: AGPL-3.0-only
|
||||
'use strict';
|
||||
const fs = require('node:fs');
|
||||
const path = require('node:path');
|
||||
for (const name of ['dist']) fs.rmSync(path.join(__dirname, '..', name), {recursive: true, force: true});
|
||||
@@ -0,0 +1,16 @@
|
||||
// SPDX-License-Identifier: AGPL-3.0-only
|
||||
'use strict';
|
||||
const fs = require('node:fs');
|
||||
const path = require('node:path');
|
||||
const crypto = require('node:crypto');
|
||||
const root = path.join(__dirname, '..');
|
||||
const lock = JSON.parse(fs.readFileSync(path.join(root, 'package-lock.json'), 'utf8'));
|
||||
const components = Object.entries(lock.packages || {}).filter(([name]) => name.startsWith('node_modules/')).map(([name, value]) => ({
|
||||
type: 'library', name: name.slice('node_modules/'.length), version: value.version,
|
||||
licenses: value.license ? [{license: {id: value.license}}] : undefined,
|
||||
hashes: value.integrity ? [{alg: 'SHA-512', content: value.integrity.replace(/^sha512-/, '')}] : undefined,
|
||||
})).sort((a, b) => a.name.localeCompare(b.name));
|
||||
const lockDigest = crypto.createHash('sha256').update(JSON.stringify(lock)).digest('hex');
|
||||
const uuid = `${lockDigest.slice(0, 8)}-${lockDigest.slice(8, 12)}-5${lockDigest.slice(13, 16)}-a${lockDigest.slice(17, 20)}-${lockDigest.slice(20, 32)}`;
|
||||
const document = {bomFormat: 'CycloneDX', specVersion: '1.5', serialNumber: `urn:uuid:${uuid}`, version: 1, metadata: {component: {type: 'application', name: 'gamertan.sandwich-hime', version: '0.1.0-preview.1'}}, components};
|
||||
process.stdout.write(`${JSON.stringify(document, null, 2)}\n`);
|
||||
Reference in New Issue
Block a user