@t275005746/gse 0.1.0 → 0.1.2
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/.github/ISSUE_TEMPLATE/bug_report.yml +42 -42
- package/.github/ISSUE_TEMPLATE/change_request.yml +50 -50
- package/.github/ISSUE_TEMPLATE/config.yml +5 -5
- package/.github/PULL_REQUEST_TEMPLATE.md +38 -38
- package/.github/workflows/validate-gse.yml +33 -33
- package/.gse/README.md +18 -18
- package/.gse/gse-development-protocol.md +50 -50
- package/.gse/project-profile.md +29 -29
- package/.gse/quality-gates.md +25 -25
- package/.gse/releases/public-registry-publication-npm.md +49 -0
- package/.gse/releases/public-release-owner-required.md +65 -65
- package/.gse/releases/public-security-contact-owner-required.md +45 -45
- package/.gse/state.json +147 -27
- package/CHANGELOG.md +31 -15
- package/CONTRIBUTING.md +64 -64
- package/LICENSE +21 -21
- package/README.md +14 -7
- package/README.zh-CN.md +14 -7
- package/SECURITY.md +41 -41
- package/SKILL.md +32 -12
- package/SUPPORT.md +38 -38
- package/assets/marketplace/README.md +7 -7
- package/assets/marketplace/gse-listing.json +75 -75
- package/assets/templates/acceptance-execution-packet.md +73 -73
- package/assets/templates/adr.md +14 -14
- package/assets/templates/change-brief.md +16 -16
- package/assets/templates/design.md +18 -18
- package/assets/templates/dispatch-packet.md +100 -92
- package/assets/templates/evidence.md +15 -9
- package/assets/templates/execution-quality-pack.md +65 -65
- package/assets/templates/goal-map.md +21 -21
- package/assets/templates/host-adapter.md +48 -48
- package/assets/templates/host-ui-invocation-record.md +42 -42
- package/assets/templates/incident-review.md +60 -60
- package/assets/templates/public-channel-publication-record.md +49 -49
- package/assets/templates/public-ci-run-record.md +53 -53
- package/assets/templates/public-release-record.md +65 -65
- package/assets/templates/public-repository-settings-record.md +59 -59
- package/assets/templates/public-security-contact-record.md +45 -45
- package/assets/templates/release-trust-record.md +32 -32
- package/assets/templates/review.md +18 -18
- package/assets/templates/role-fallback-packet.md +49 -0
- package/assets/templates/spec.md +16 -16
- package/assets/templates/target-adoption-evidence.md +29 -29
- package/assets/templates/tasks.md +17 -17
- package/assets/templates/update-release-acceptance-record.md +58 -58
- package/examples/README.md +22 -22
- package/examples/agent-runtime-host/.claude/gse-adapter.md +6 -6
- package/examples/agent-runtime-host/.codex/gse-adapter.md +7 -7
- package/examples/agent-runtime-host/.gse/goal-map.md +7 -7
- package/examples/agent-runtime-host/.gse/project-profile.md +27 -27
- package/examples/agent-runtime-host/.gse/tooling.md +5 -5
- package/examples/agent-runtime-host/.mcp.json +9 -9
- package/examples/agent-runtime-host/AGENTS.md +5 -5
- package/examples/agent-runtime-host/README.md +10 -10
- package/examples/agent-runtime-host/docs/model-routing.md +5 -5
- package/examples/cli-tool/.gse/project-profile.md +21 -21
- package/examples/cli-tool/AGENTS.md +5 -5
- package/examples/cli-tool/README.md +12 -12
- package/examples/cli-tool/package.json +21 -21
- package/examples/small-app/.env.example +2 -2
- package/examples/small-app/.github/workflows/ci.yml +8 -8
- package/examples/small-app/AGENTS.md +5 -5
- package/examples/small-app/README.md +12 -12
- package/examples/small-app/package.json +26 -26
- package/examples/small-app/playwright.config.ts +4 -4
- package/package.json +52 -44
- package/references/adoption-recipes.md +171 -171
- package/references/agent-roles.md +42 -21
- package/references/architecture-health.md +101 -101
- package/references/benchmark-audit.md +73 -73
- package/references/commands.md +74 -45
- package/references/community-channels.md +46 -46
- package/references/compatibility.md +70 -70
- package/references/design-basis.md +38 -38
- package/references/domain-model.md +129 -129
- package/references/domain-quality-gates.md +75 -75
- package/references/drift-audit.md +81 -80
- package/references/evidence-taxonomy.md +145 -108
- package/references/file-ownership.md +97 -93
- package/references/final-form-roadmap.md +201 -0
- package/references/final-readiness.md +84 -84
- package/references/forward-test.md +133 -133
- package/references/goal-map.md +36 -36
- package/references/host-adapters.md +129 -101
- package/references/learning-system.md +42 -26
- package/references/maintenance-cadence.md +51 -0
- package/references/marketplace-discovery.md +46 -46
- package/references/model-routing.md +103 -103
- package/references/open-source-defaults.md +39 -39
- package/references/operating-model.md +52 -52
- package/references/packaging.md +277 -277
- package/references/project-agent-workspace.md +89 -89
- package/references/project-bootstrap.md +122 -122
- package/references/project-guards.md +52 -0
- package/references/project-profile.md +57 -57
- package/references/public-release.md +174 -174
- package/references/quality-gates.md +96 -89
- package/references/recovery.md +176 -176
- package/references/release-trust.md +43 -43
- package/references/release.md +126 -126
- package/references/review.md +141 -141
- package/references/role-dispatch-fallback.md +54 -0
- package/references/router.md +90 -90
- package/references/spec-workflow.md +66 -66
- package/references/task-levels.md +81 -81
- package/references/tool-adapters.md +80 -70
- package/scripts/audit-acceptance-execution-packet.mjs +133 -133
- package/scripts/audit-adoption-recipes.mjs +99 -99
- package/scripts/audit-change-lifecycle.mjs +77 -77
- package/scripts/audit-change-system.mjs +134 -134
- package/scripts/audit-ci-readiness.mjs +107 -107
- package/scripts/audit-close-gate-hardening.mjs +188 -0
- package/scripts/audit-close-gate.mjs +448 -262
- package/scripts/audit-command-adapters.mjs +91 -91
- package/scripts/audit-command-execution.mjs +212 -199
- package/scripts/audit-commands.mjs +7 -5
- package/scripts/audit-compatibility.mjs +149 -149
- package/scripts/audit-completion-plan-drill.mjs +230 -0
- package/scripts/audit-completion-readiness.mjs +290 -287
- package/scripts/audit-continue-preflight.mjs +234 -0
- package/scripts/audit-distribution.mjs +251 -251
- package/scripts/audit-domain-quality-gates.mjs +108 -108
- package/scripts/audit-evidence-levels.mjs +217 -0
- package/scripts/audit-evidence-placeholders.mjs +126 -126
- package/scripts/audit-evidence-review-queue.mjs +206 -0
- package/scripts/audit-final-acceptance-packet.mjs +9 -9
- package/scripts/audit-final-form-progress-report.mjs +19 -18
- package/scripts/audit-final-form-roadmap.mjs +157 -0
- package/scripts/audit-final-form-stale-copy.mjs +2 -2
- package/scripts/audit-final-readiness-promotion.mjs +255 -255
- package/scripts/audit-final-readiness.mjs +226 -226
- package/scripts/audit-fixtures.mjs +154 -154
- package/scripts/audit-fresh-session-readiness.mjs +177 -177
- package/scripts/audit-host-capabilities.mjs +237 -0
- package/scripts/audit-host-runtime-evidence-handoff.mjs +159 -159
- package/scripts/audit-host-runtime-invocation-drill.mjs +240 -240
- package/scripts/audit-host-runtime-invocations.mjs +254 -254
- package/scripts/audit-host-ui-invocation.mjs +132 -132
- package/scripts/audit-installed-sync.mjs +203 -0
- package/scripts/audit-learning-drift.mjs +292 -0
- package/scripts/audit-learning-promotion.mjs +351 -0
- package/scripts/audit-learning-system.mjs +24 -14
- package/scripts/audit-local-final-form-completion.mjs +11 -10
- package/scripts/audit-maintenance-cadence.mjs +139 -0
- package/scripts/audit-maintenance-snapshot.mjs +181 -0
- package/scripts/audit-marketplace-discovery.mjs +122 -122
- package/scripts/audit-npm-package-metadata.mjs +160 -154
- package/scripts/audit-npm-publish-dry-run.mjs +194 -166
- package/scripts/audit-npm-tarball-install.mjs +195 -189
- package/scripts/audit-open-source-defaults.mjs +92 -92
- package/scripts/audit-open-source-readiness.mjs +97 -97
- package/scripts/audit-owner-external-gate-kit.mjs +12 -11
- package/scripts/audit-project-guards.mjs +189 -0
- package/scripts/audit-project.mjs +144 -141
- package/scripts/audit-public-acceptance-command-dry-run-drill.mjs +203 -203
- package/scripts/audit-public-acceptance-handoff.mjs +10 -10
- package/scripts/audit-public-acceptance-readiness.mjs +222 -222
- package/scripts/audit-public-channel-publication.mjs +248 -248
- package/scripts/audit-public-ci-run.mjs +184 -184
- package/scripts/audit-public-collaboration-templates.mjs +98 -98
- package/scripts/audit-public-external-gate-probe.mjs +206 -206
- package/scripts/audit-public-release-checklist.mjs +17 -15
- package/scripts/audit-public-release-decision.mjs +201 -201
- package/scripts/audit-public-release-metadata.mjs +176 -176
- package/scripts/audit-public-repository-settings.mjs +237 -237
- package/scripts/audit-public-security-contact.mjs +171 -171
- package/scripts/audit-readme-docs.mjs +6 -4
- package/scripts/audit-recovery-readiness.mjs +98 -98
- package/scripts/audit-release-bundle.mjs +13 -11
- package/scripts/audit-release-owner-action-plan-drill.mjs +5 -4
- package/scripts/audit-release-owner-action-plan.mjs +10 -10
- package/scripts/audit-release-readiness.mjs +106 -106
- package/scripts/audit-release-status-manifest.mjs +4 -4
- package/scripts/audit-release-trust.mjs +62 -62
- package/scripts/audit-remote-distribution.mjs +266 -266
- package/scripts/audit-roadmap-consistency.mjs +234 -230
- package/scripts/audit-role-dispatch-fallback.mjs +212 -0
- package/scripts/audit-session-sync.mjs +127 -0
- package/scripts/audit-signing.mjs +147 -147
- package/scripts/audit-state-freshness.mjs +20 -14
- package/scripts/audit-state-repair.mjs +360 -0
- package/scripts/audit-target-adoption-evidence.mjs +117 -117
- package/scripts/audit-target-hardening-drills.mjs +267 -0
- package/scripts/audit-target-project.mjs +507 -507
- package/scripts/audit-tool-fallback-policy.mjs +180 -0
- package/scripts/audit-ui-browser-evidence-policy.mjs +191 -0
- package/scripts/audit-update-release-acceptance.mjs +136 -136
- package/scripts/audit-v1-target-validation.mjs +269 -269
- package/scripts/audit-validation-profiles.mjs +125 -123
- package/scripts/backfill-evidence-levels.mjs +98 -0
- package/scripts/check-encoding.mjs +72 -0
- package/scripts/close-change.mjs +116 -116
- package/scripts/discover-project-profile.mjs +307 -307
- package/scripts/generate-command-adapter.mjs +231 -231
- package/scripts/generate-continue-packet.mjs +1214 -0
- package/scripts/generate-final-acceptance-packet.mjs +188 -173
- package/scripts/generate-final-form-progress-report.mjs +191 -189
- package/scripts/generate-host-runtime-evidence-handoff.mjs +198 -191
- package/scripts/generate-maintenance-snapshot.mjs +216 -0
- package/scripts/generate-owner-external-gate-kit.mjs +295 -295
- package/scripts/generate-public-acceptance-handoff.mjs +168 -156
- package/scripts/generate-public-release-checklist.mjs +207 -207
- package/scripts/generate-release-bundle.mjs +505 -505
- package/scripts/generate-release-owner-action-plan.mjs +172 -171
- package/scripts/generate-release-status-manifest.mjs +199 -198
- package/scripts/generate-session-prompt.mjs +188 -188
- package/scripts/gse.mjs +67 -67
- package/scripts/init-change.mjs +265 -265
- package/scripts/init-project.mjs +793 -712
- package/scripts/install-gse.mjs +234 -234
- package/scripts/lib/evidence-placeholders.mjs +28 -28
- package/scripts/package-gse.mjs +174 -174
- package/scripts/probe-public-external-gates.mjs +167 -167
- package/scripts/record-host-invocation.mjs +151 -151
- package/scripts/record-learning.mjs +37 -8
- package/scripts/record-public-channel-publication.mjs +178 -178
- package/scripts/record-public-ci-run.mjs +180 -180
- package/scripts/record-public-release.mjs +175 -175
- package/scripts/record-public-repository-settings.mjs +209 -209
- package/scripts/record-public-security-contact.mjs +157 -157
- package/scripts/record-session-sync.mjs +87 -0
- package/scripts/run-gse-command.mjs +79 -47
- package/scripts/run-validation-profile.mjs +24 -5
- package/scripts/sign-gse-package.mjs +83 -83
- package/scripts/update-project-state.mjs +223 -223
- package/scripts/validate-gse.mjs +216 -0
- package/scripts/verify-gse-package.mjs +85 -85
|
@@ -1,266 +1,266 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
import fs from 'node:fs'
|
|
3
|
-
import http from 'node:http'
|
|
4
|
-
import os from 'node:os'
|
|
5
|
-
import path from 'node:path'
|
|
6
|
-
import { spawn, spawnSync } from 'node:child_process'
|
|
7
|
-
|
|
8
|
-
const args = process.argv.slice(2)
|
|
9
|
-
|
|
10
|
-
function readArg(name, fallback = null) {
|
|
11
|
-
const index = args.indexOf(name)
|
|
12
|
-
if (index === -1) return fallback
|
|
13
|
-
return args[index + 1] ?? fallback
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
const root = path.resolve(readArg('--root', path.join(import.meta.dirname, '..')))
|
|
17
|
-
const jsonOnly = args.includes('--json')
|
|
18
|
-
const profile = readArg('--profile', 'full')
|
|
19
|
-
if (!['smoke', 'full'].includes(profile)) {
|
|
20
|
-
console.error('Unsupported --profile. Expected smoke or full.')
|
|
21
|
-
process.exit(1)
|
|
22
|
-
}
|
|
23
|
-
const smokeProfile = profile === 'smoke'
|
|
24
|
-
|
|
25
|
-
function run(command, commandArgs, cwd = root) {
|
|
26
|
-
const result = spawnSync(command, commandArgs, {
|
|
27
|
-
cwd,
|
|
28
|
-
encoding: 'utf8',
|
|
29
|
-
windowsHide: true,
|
|
30
|
-
})
|
|
31
|
-
return {
|
|
32
|
-
command: [command, ...commandArgs].join(' '),
|
|
33
|
-
status: result.status ?? 1,
|
|
34
|
-
stdout: (result.stdout ?? '').trim(),
|
|
35
|
-
stderr: (result.stderr ?? '').trim(),
|
|
36
|
-
}
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
function runAsync(command, commandArgs, cwd = root) {
|
|
40
|
-
return new Promise((resolve) => {
|
|
41
|
-
const child = spawn(command, commandArgs, { cwd, windowsHide: true })
|
|
42
|
-
let stdout = ''
|
|
43
|
-
let stderr = ''
|
|
44
|
-
child.stdout.on('data', (chunk) => { stdout += chunk.toString('utf8') })
|
|
45
|
-
child.stderr.on('data', (chunk) => { stderr += chunk.toString('utf8') })
|
|
46
|
-
child.on('close', (status) => {
|
|
47
|
-
resolve({
|
|
48
|
-
command: [command, ...commandArgs].join(' '),
|
|
49
|
-
status: status ?? 1,
|
|
50
|
-
stdout: stdout.trim(),
|
|
51
|
-
stderr: stderr.trim(),
|
|
52
|
-
})
|
|
53
|
-
})
|
|
54
|
-
})
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
function parseJson(text) {
|
|
58
|
-
try { return JSON.parse(text) } catch { return null }
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
function check(id, label, ok, evidence, risk = '') {
|
|
62
|
-
return { id, label, status: ok ? 'passed' : 'failed', evidence, risk }
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
function skipped(id, label, evidence, risk = '') {
|
|
66
|
-
return { id, label, status: 'skipped', evidence, risk }
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
function runInstalledPackageValidation(target) {
|
|
70
|
-
const validationCommands = [
|
|
71
|
-
['audit-gse.mjs', ['--root', target, '--json']],
|
|
72
|
-
['audit-project.mjs', ['--root', target, '--json']],
|
|
73
|
-
['audit-fixtures.mjs', ['--root', target, '--json']],
|
|
74
|
-
['audit-commands.mjs', ['--root', target, '--json']],
|
|
75
|
-
['audit-command-execution.mjs', ['--root', target, '--profile', 'lite', '--json']],
|
|
76
|
-
['audit-readme-docs.mjs', ['--root', target, '--json']],
|
|
77
|
-
['audit-open-source-readiness.mjs', ['--root', target, '--json']],
|
|
78
|
-
['audit-marketplace-discovery.mjs', ['--root', target, '--json']],
|
|
79
|
-
['generate-session-prompt.mjs', ['--root', target, '--json']],
|
|
80
|
-
]
|
|
81
|
-
const results = validationCommands.map(([script, commandArgs]) => {
|
|
82
|
-
const result = run(process.execPath, [path.join(target, 'scripts', script), ...commandArgs], target)
|
|
83
|
-
const parsed = parseJson(result.stdout)
|
|
84
|
-
const failed = parsed?.summary?.failed
|
|
85
|
-
const ok = result.status === 0 || failed === 0
|
|
86
|
-
return {
|
|
87
|
-
script,
|
|
88
|
-
command: result.command,
|
|
89
|
-
status: result.status,
|
|
90
|
-
ok,
|
|
91
|
-
summary: parsed?.summary ?? null,
|
|
92
|
-
stderr: result.stderr,
|
|
93
|
-
}
|
|
94
|
-
})
|
|
95
|
-
const passed = results.filter((item) => item.ok).length
|
|
96
|
-
const failed = results.length - passed
|
|
97
|
-
return {
|
|
98
|
-
command: 'remote installed package validation: ' + validationCommands.map(([script]) => script).join(', '),
|
|
99
|
-
status: failed === 0 ? 0 : 1,
|
|
100
|
-
stdout: JSON.stringify({
|
|
101
|
-
summary: {
|
|
102
|
-
status: failed === 0 ? 'passed' : 'failed',
|
|
103
|
-
passed,
|
|
104
|
-
failed,
|
|
105
|
-
total: results.length,
|
|
106
|
-
},
|
|
107
|
-
results,
|
|
108
|
-
limits: [
|
|
109
|
-
'Installed-package validation checks portable skill structure, bootstrap fixtures, command semantics, lite command execution, README docs, open-source readiness, marketplace metadata, and session prompt generation.',
|
|
110
|
-
'It intentionally excludes source-workspace-only roadmap, long evidence logs, release-bundle cache, and owner/external acceptance artifacts.',
|
|
111
|
-
],
|
|
112
|
-
}),
|
|
113
|
-
stderr: results.filter((item) => !item.ok).map((item) => item.script + ': ' + item.stderr).filter(Boolean).join('\n'),
|
|
114
|
-
}
|
|
115
|
-
}
|
|
116
|
-
|
|
117
|
-
function contentType(filePath) {
|
|
118
|
-
if (filePath.endsWith('.json')) return 'application/json'
|
|
119
|
-
if (filePath.endsWith('.md')) return 'text/markdown; charset=utf-8'
|
|
120
|
-
if (filePath.endsWith('.mjs')) return 'text/javascript; charset=utf-8'
|
|
121
|
-
return 'application/octet-stream'
|
|
122
|
-
}
|
|
123
|
-
|
|
124
|
-
function createStaticServer(baseDir) {
|
|
125
|
-
const server = http.createServer((request, response) => {
|
|
126
|
-
try {
|
|
127
|
-
const url = new URL(request.url ?? '/', 'http://127.0.0.1')
|
|
128
|
-
const relativePath = decodeURIComponent(url.pathname.replace(/^\/+/, '')) || 'gse-package-manifest.json'
|
|
129
|
-
const fullPath = path.resolve(baseDir, relativePath)
|
|
130
|
-
if (!fullPath.startsWith(path.resolve(baseDir))) {
|
|
131
|
-
response.writeHead(403)
|
|
132
|
-
response.end('Forbidden')
|
|
133
|
-
return
|
|
134
|
-
}
|
|
135
|
-
if (!fs.existsSync(fullPath) || !fs.statSync(fullPath).isFile()) {
|
|
136
|
-
response.writeHead(404)
|
|
137
|
-
response.end('Not found')
|
|
138
|
-
return
|
|
139
|
-
}
|
|
140
|
-
response.writeHead(200, { 'content-type': contentType(fullPath) })
|
|
141
|
-
fs.createReadStream(fullPath).pipe(response)
|
|
142
|
-
} catch (error) {
|
|
143
|
-
response.writeHead(500)
|
|
144
|
-
response.end(error instanceof Error ? error.message : String(error))
|
|
145
|
-
}
|
|
146
|
-
})
|
|
147
|
-
return new Promise((resolve) => {
|
|
148
|
-
server.listen(0, '127.0.0.1', () => {
|
|
149
|
-
const address = server.address()
|
|
150
|
-
resolve({ server, baseUrl: `http://127.0.0.1:${address.port}/` })
|
|
151
|
-
})
|
|
152
|
-
})
|
|
153
|
-
}
|
|
154
|
-
|
|
155
|
-
const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), 'gse-remote-distribution-'))
|
|
156
|
-
const packageOut = path.join(tempRoot, 'package')
|
|
157
|
-
const installTarget = path.join(tempRoot, 'installed-gse')
|
|
158
|
-
const tamperedTarget = path.join(tempRoot, 'tampered-install')
|
|
159
|
-
|
|
160
|
-
const packageRun = run(process.execPath, [
|
|
161
|
-
path.join(root, 'scripts', 'package-gse.mjs'),
|
|
162
|
-
'--root',
|
|
163
|
-
root,
|
|
164
|
-
'--out',
|
|
165
|
-
packageOut,
|
|
166
|
-
'--label',
|
|
167
|
-
'gse-remote-audit',
|
|
168
|
-
'--json',
|
|
169
|
-
])
|
|
170
|
-
const packageData = parseJson(packageRun.stdout)
|
|
171
|
-
|
|
172
|
-
const manifestPath = path.join(packageOut, 'gse-package-manifest.json')
|
|
173
|
-
const manifest = fs.existsSync(manifestPath) ? JSON.parse(fs.readFileSync(manifestPath, 'utf8')) : null
|
|
174
|
-
|
|
175
|
-
let server
|
|
176
|
-
let baseUrl = ''
|
|
177
|
-
let remoteInstall = { status: 1, command: 'not-run', stdout: '', stderr: '' }
|
|
178
|
-
let installedValidate = { status: 1, command: 'not-run', stdout: '', stderr: '' }
|
|
179
|
-
let installedCli = { status: 1, command: 'not-run', stdout: '', stderr: '' }
|
|
180
|
-
let tamperedInstall = { status: 1, command: 'not-run', stdout: '', stderr: '' }
|
|
181
|
-
|
|
182
|
-
try {
|
|
183
|
-
const started = await createStaticServer(packageOut)
|
|
184
|
-
server = started.server
|
|
185
|
-
baseUrl = started.baseUrl
|
|
186
|
-
remoteInstall = await runAsync(process.execPath, [
|
|
187
|
-
path.join(root, 'scripts', 'install-gse.mjs'),
|
|
188
|
-
'--source-url',
|
|
189
|
-
baseUrl,
|
|
190
|
-
'--target',
|
|
191
|
-
installTarget,
|
|
192
|
-
'--json',
|
|
193
|
-
])
|
|
194
|
-
installedValidate = smokeProfile
|
|
195
|
-
? { status: 0, command: 'skipped by --profile smoke', stdout: '', stderr: '' }
|
|
196
|
-
: runInstalledPackageValidation(installTarget)
|
|
197
|
-
installedCli = await runAsync(process.execPath, [
|
|
198
|
-
path.join(installTarget, 'scripts', 'gse.mjs'),
|
|
199
|
-
'status',
|
|
200
|
-
'--target',
|
|
201
|
-
installTarget,
|
|
202
|
-
'--json',
|
|
203
|
-
], installTarget)
|
|
204
|
-
|
|
205
|
-
fs.appendFileSync(path.join(packageOut, 'README.md'), '\nTampered for integrity audit.\n', 'utf8')
|
|
206
|
-
tamperedInstall = await runAsync(process.execPath, [
|
|
207
|
-
path.join(root, 'scripts', 'install-gse.mjs'),
|
|
208
|
-
'--source-url',
|
|
209
|
-
baseUrl,
|
|
210
|
-
'--target',
|
|
211
|
-
tamperedTarget,
|
|
212
|
-
'--json',
|
|
213
|
-
])
|
|
214
|
-
} finally {
|
|
215
|
-
if (server) await new Promise((resolve) => server.close(resolve))
|
|
216
|
-
}
|
|
217
|
-
|
|
218
|
-
const remoteInstallData = parseJson(remoteInstall.stdout)
|
|
219
|
-
const installedValidateData = parseJson(installedValidate.stdout)
|
|
220
|
-
const installedCliData = parseJson(installedCli.stdout)
|
|
221
|
-
const tamperedInstallData = parseJson(tamperedInstall.stdout)
|
|
222
|
-
|
|
223
|
-
const checks = [
|
|
224
|
-
check('RD01', 'package manifest includes sha256 file hashes', packageRun.status === 0 && manifest?.integrity?.algorithm === 'sha256' && manifest?.fileHashes?.['SKILL.md'], 'gse-package-manifest.json'),
|
|
225
|
-
check('RD02', 'HTTP package source served manifest and files', Boolean(baseUrl) && remoteInstall.status === 0, baseUrl),
|
|
226
|
-
check('RD03', 'install-gse supports --source-url', remoteInstall.status === 0 && remoteInstallData?.sourceMode === 'http-url', remoteInstall.command),
|
|
227
|
-
check('RD04', 'remote install validates file integrity', remoteInstall.status === 0 && remoteInstallData?.summary?.integrityFailed === 0 && remoteInstallData?.integrity?.algorithm === 'sha256', 'install summary integrity'),
|
|
228
|
-
smokeProfile
|
|
229
|
-
? skipped('RD05', 'remote installed copy validates', installedValidate.command, 'Run --profile full for remote installed-copy validation.')
|
|
230
|
-
: check('RD05', 'remote installed copy validates', installedValidate.status === 0 && installedValidateData?.summary?.failed === 0, installedValidate.command),
|
|
231
|
-
check('RD06', 'remote installed short CLI wrapper runs status command', installedCli.status === 0 && installedCliData?.command === '/gse status' && installedCliData?.project?.stateValid === true, installedCli.command),
|
|
232
|
-
check('RD07', 'tampered remote package fails integrity gate', tamperedInstall.status === 1 && tamperedInstallData?.summary?.integrityFailed > 0, 'tampered README.md install'),
|
|
233
|
-
]
|
|
234
|
-
|
|
235
|
-
const passed = checks.filter((item) => item.status === 'passed').length
|
|
236
|
-
const skippedCount = checks.filter((item) => item.status === 'skipped').length
|
|
237
|
-
const failed = checks.filter((item) => item.status === 'failed').length
|
|
238
|
-
const report = {
|
|
239
|
-
root,
|
|
240
|
-
generatedAt: new Date().toISOString(),
|
|
241
|
-
tempRoot,
|
|
242
|
-
baseUrl,
|
|
243
|
-
profile,
|
|
244
|
-
summary: { status: failed === 0 ? 'passed' : 'failed', passed, failed, skipped: skippedCount, total: checks.length },
|
|
245
|
-
workflows: {
|
|
246
|
-
remoteInstall: remoteInstall.status === 0 ? 'verified' : 'failed',
|
|
247
|
-
integrityGate: tamperedInstall.status === 1 ? 'verified' : 'failed',
|
|
248
|
-
installedValidation: smokeProfile ? 'skipped' : installedValidate.status === 0 ? 'verified' : 'failed',
|
|
249
|
-
installedCli: installedCli.status === 0 ? 'verified' : 'failed',
|
|
250
|
-
},
|
|
251
|
-
commands: [packageRun.command, remoteInstall.command, installedValidate.command, installedCli.command, tamperedInstall.command],
|
|
252
|
-
limits: [
|
|
253
|
-
'This audit verifies HTTP URL install through a local ephemeral server and manifest integrity checks.',
|
|
254
|
-
'Use --profile smoke for routine remote install, CLI, and integrity checks; use --profile full before release or when installed-copy validation matters.',
|
|
255
|
-
'Installed-package validation intentionally excludes source-workspace-only roadmap, long evidence logs, release-bundle cache, and owner/external acceptance artifacts.',
|
|
256
|
-
'It does not publish to a public registry, verify marketplace discovery, or sign artifacts with a trusted key.',
|
|
257
|
-
],
|
|
258
|
-
checks,
|
|
259
|
-
}
|
|
260
|
-
|
|
261
|
-
fs.rmSync(tempRoot, { recursive: true, force: true })
|
|
262
|
-
|
|
263
|
-
if (jsonOnly) console.log(JSON.stringify(report, null, 2))
|
|
264
|
-
else console.log(JSON.stringify(report, null, 2))
|
|
265
|
-
|
|
266
|
-
if (failed > 0) process.exit(1)
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import fs from 'node:fs'
|
|
3
|
+
import http from 'node:http'
|
|
4
|
+
import os from 'node:os'
|
|
5
|
+
import path from 'node:path'
|
|
6
|
+
import { spawn, spawnSync } from 'node:child_process'
|
|
7
|
+
|
|
8
|
+
const args = process.argv.slice(2)
|
|
9
|
+
|
|
10
|
+
function readArg(name, fallback = null) {
|
|
11
|
+
const index = args.indexOf(name)
|
|
12
|
+
if (index === -1) return fallback
|
|
13
|
+
return args[index + 1] ?? fallback
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
const root = path.resolve(readArg('--root', path.join(import.meta.dirname, '..')))
|
|
17
|
+
const jsonOnly = args.includes('--json')
|
|
18
|
+
const profile = readArg('--profile', 'full')
|
|
19
|
+
if (!['smoke', 'full'].includes(profile)) {
|
|
20
|
+
console.error('Unsupported --profile. Expected smoke or full.')
|
|
21
|
+
process.exit(1)
|
|
22
|
+
}
|
|
23
|
+
const smokeProfile = profile === 'smoke'
|
|
24
|
+
|
|
25
|
+
function run(command, commandArgs, cwd = root) {
|
|
26
|
+
const result = spawnSync(command, commandArgs, {
|
|
27
|
+
cwd,
|
|
28
|
+
encoding: 'utf8',
|
|
29
|
+
windowsHide: true,
|
|
30
|
+
})
|
|
31
|
+
return {
|
|
32
|
+
command: [command, ...commandArgs].join(' '),
|
|
33
|
+
status: result.status ?? 1,
|
|
34
|
+
stdout: (result.stdout ?? '').trim(),
|
|
35
|
+
stderr: (result.stderr ?? '').trim(),
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
function runAsync(command, commandArgs, cwd = root) {
|
|
40
|
+
return new Promise((resolve) => {
|
|
41
|
+
const child = spawn(command, commandArgs, { cwd, windowsHide: true })
|
|
42
|
+
let stdout = ''
|
|
43
|
+
let stderr = ''
|
|
44
|
+
child.stdout.on('data', (chunk) => { stdout += chunk.toString('utf8') })
|
|
45
|
+
child.stderr.on('data', (chunk) => { stderr += chunk.toString('utf8') })
|
|
46
|
+
child.on('close', (status) => {
|
|
47
|
+
resolve({
|
|
48
|
+
command: [command, ...commandArgs].join(' '),
|
|
49
|
+
status: status ?? 1,
|
|
50
|
+
stdout: stdout.trim(),
|
|
51
|
+
stderr: stderr.trim(),
|
|
52
|
+
})
|
|
53
|
+
})
|
|
54
|
+
})
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
function parseJson(text) {
|
|
58
|
+
try { return JSON.parse(text) } catch { return null }
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
function check(id, label, ok, evidence, risk = '') {
|
|
62
|
+
return { id, label, status: ok ? 'passed' : 'failed', evidence, risk }
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
function skipped(id, label, evidence, risk = '') {
|
|
66
|
+
return { id, label, status: 'skipped', evidence, risk }
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
function runInstalledPackageValidation(target) {
|
|
70
|
+
const validationCommands = [
|
|
71
|
+
['audit-gse.mjs', ['--root', target, '--json']],
|
|
72
|
+
['audit-project.mjs', ['--root', target, '--json']],
|
|
73
|
+
['audit-fixtures.mjs', ['--root', target, '--json']],
|
|
74
|
+
['audit-commands.mjs', ['--root', target, '--json']],
|
|
75
|
+
['audit-command-execution.mjs', ['--root', target, '--profile', 'lite', '--json']],
|
|
76
|
+
['audit-readme-docs.mjs', ['--root', target, '--json']],
|
|
77
|
+
['audit-open-source-readiness.mjs', ['--root', target, '--json']],
|
|
78
|
+
['audit-marketplace-discovery.mjs', ['--root', target, '--json']],
|
|
79
|
+
['generate-session-prompt.mjs', ['--root', target, '--json']],
|
|
80
|
+
]
|
|
81
|
+
const results = validationCommands.map(([script, commandArgs]) => {
|
|
82
|
+
const result = run(process.execPath, [path.join(target, 'scripts', script), ...commandArgs], target)
|
|
83
|
+
const parsed = parseJson(result.stdout)
|
|
84
|
+
const failed = parsed?.summary?.failed
|
|
85
|
+
const ok = result.status === 0 || failed === 0
|
|
86
|
+
return {
|
|
87
|
+
script,
|
|
88
|
+
command: result.command,
|
|
89
|
+
status: result.status,
|
|
90
|
+
ok,
|
|
91
|
+
summary: parsed?.summary ?? null,
|
|
92
|
+
stderr: result.stderr,
|
|
93
|
+
}
|
|
94
|
+
})
|
|
95
|
+
const passed = results.filter((item) => item.ok).length
|
|
96
|
+
const failed = results.length - passed
|
|
97
|
+
return {
|
|
98
|
+
command: 'remote installed package validation: ' + validationCommands.map(([script]) => script).join(', '),
|
|
99
|
+
status: failed === 0 ? 0 : 1,
|
|
100
|
+
stdout: JSON.stringify({
|
|
101
|
+
summary: {
|
|
102
|
+
status: failed === 0 ? 'passed' : 'failed',
|
|
103
|
+
passed,
|
|
104
|
+
failed,
|
|
105
|
+
total: results.length,
|
|
106
|
+
},
|
|
107
|
+
results,
|
|
108
|
+
limits: [
|
|
109
|
+
'Installed-package validation checks portable skill structure, bootstrap fixtures, command semantics, lite command execution, README docs, open-source readiness, marketplace metadata, and session prompt generation.',
|
|
110
|
+
'It intentionally excludes source-workspace-only roadmap, long evidence logs, release-bundle cache, and owner/external acceptance artifacts.',
|
|
111
|
+
],
|
|
112
|
+
}),
|
|
113
|
+
stderr: results.filter((item) => !item.ok).map((item) => item.script + ': ' + item.stderr).filter(Boolean).join('\n'),
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
function contentType(filePath) {
|
|
118
|
+
if (filePath.endsWith('.json')) return 'application/json'
|
|
119
|
+
if (filePath.endsWith('.md')) return 'text/markdown; charset=utf-8'
|
|
120
|
+
if (filePath.endsWith('.mjs')) return 'text/javascript; charset=utf-8'
|
|
121
|
+
return 'application/octet-stream'
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
function createStaticServer(baseDir) {
|
|
125
|
+
const server = http.createServer((request, response) => {
|
|
126
|
+
try {
|
|
127
|
+
const url = new URL(request.url ?? '/', 'http://127.0.0.1')
|
|
128
|
+
const relativePath = decodeURIComponent(url.pathname.replace(/^\/+/, '')) || 'gse-package-manifest.json'
|
|
129
|
+
const fullPath = path.resolve(baseDir, relativePath)
|
|
130
|
+
if (!fullPath.startsWith(path.resolve(baseDir))) {
|
|
131
|
+
response.writeHead(403)
|
|
132
|
+
response.end('Forbidden')
|
|
133
|
+
return
|
|
134
|
+
}
|
|
135
|
+
if (!fs.existsSync(fullPath) || !fs.statSync(fullPath).isFile()) {
|
|
136
|
+
response.writeHead(404)
|
|
137
|
+
response.end('Not found')
|
|
138
|
+
return
|
|
139
|
+
}
|
|
140
|
+
response.writeHead(200, { 'content-type': contentType(fullPath) })
|
|
141
|
+
fs.createReadStream(fullPath).pipe(response)
|
|
142
|
+
} catch (error) {
|
|
143
|
+
response.writeHead(500)
|
|
144
|
+
response.end(error instanceof Error ? error.message : String(error))
|
|
145
|
+
}
|
|
146
|
+
})
|
|
147
|
+
return new Promise((resolve) => {
|
|
148
|
+
server.listen(0, '127.0.0.1', () => {
|
|
149
|
+
const address = server.address()
|
|
150
|
+
resolve({ server, baseUrl: `http://127.0.0.1:${address.port}/` })
|
|
151
|
+
})
|
|
152
|
+
})
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), 'gse-remote-distribution-'))
|
|
156
|
+
const packageOut = path.join(tempRoot, 'package')
|
|
157
|
+
const installTarget = path.join(tempRoot, 'installed-gse')
|
|
158
|
+
const tamperedTarget = path.join(tempRoot, 'tampered-install')
|
|
159
|
+
|
|
160
|
+
const packageRun = run(process.execPath, [
|
|
161
|
+
path.join(root, 'scripts', 'package-gse.mjs'),
|
|
162
|
+
'--root',
|
|
163
|
+
root,
|
|
164
|
+
'--out',
|
|
165
|
+
packageOut,
|
|
166
|
+
'--label',
|
|
167
|
+
'gse-remote-audit',
|
|
168
|
+
'--json',
|
|
169
|
+
])
|
|
170
|
+
const packageData = parseJson(packageRun.stdout)
|
|
171
|
+
|
|
172
|
+
const manifestPath = path.join(packageOut, 'gse-package-manifest.json')
|
|
173
|
+
const manifest = fs.existsSync(manifestPath) ? JSON.parse(fs.readFileSync(manifestPath, 'utf8')) : null
|
|
174
|
+
|
|
175
|
+
let server
|
|
176
|
+
let baseUrl = ''
|
|
177
|
+
let remoteInstall = { status: 1, command: 'not-run', stdout: '', stderr: '' }
|
|
178
|
+
let installedValidate = { status: 1, command: 'not-run', stdout: '', stderr: '' }
|
|
179
|
+
let installedCli = { status: 1, command: 'not-run', stdout: '', stderr: '' }
|
|
180
|
+
let tamperedInstall = { status: 1, command: 'not-run', stdout: '', stderr: '' }
|
|
181
|
+
|
|
182
|
+
try {
|
|
183
|
+
const started = await createStaticServer(packageOut)
|
|
184
|
+
server = started.server
|
|
185
|
+
baseUrl = started.baseUrl
|
|
186
|
+
remoteInstall = await runAsync(process.execPath, [
|
|
187
|
+
path.join(root, 'scripts', 'install-gse.mjs'),
|
|
188
|
+
'--source-url',
|
|
189
|
+
baseUrl,
|
|
190
|
+
'--target',
|
|
191
|
+
installTarget,
|
|
192
|
+
'--json',
|
|
193
|
+
])
|
|
194
|
+
installedValidate = smokeProfile
|
|
195
|
+
? { status: 0, command: 'skipped by --profile smoke', stdout: '', stderr: '' }
|
|
196
|
+
: runInstalledPackageValidation(installTarget)
|
|
197
|
+
installedCli = await runAsync(process.execPath, [
|
|
198
|
+
path.join(installTarget, 'scripts', 'gse.mjs'),
|
|
199
|
+
'status',
|
|
200
|
+
'--target',
|
|
201
|
+
installTarget,
|
|
202
|
+
'--json',
|
|
203
|
+
], installTarget)
|
|
204
|
+
|
|
205
|
+
fs.appendFileSync(path.join(packageOut, 'README.md'), '\nTampered for integrity audit.\n', 'utf8')
|
|
206
|
+
tamperedInstall = await runAsync(process.execPath, [
|
|
207
|
+
path.join(root, 'scripts', 'install-gse.mjs'),
|
|
208
|
+
'--source-url',
|
|
209
|
+
baseUrl,
|
|
210
|
+
'--target',
|
|
211
|
+
tamperedTarget,
|
|
212
|
+
'--json',
|
|
213
|
+
])
|
|
214
|
+
} finally {
|
|
215
|
+
if (server) await new Promise((resolve) => server.close(resolve))
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
const remoteInstallData = parseJson(remoteInstall.stdout)
|
|
219
|
+
const installedValidateData = parseJson(installedValidate.stdout)
|
|
220
|
+
const installedCliData = parseJson(installedCli.stdout)
|
|
221
|
+
const tamperedInstallData = parseJson(tamperedInstall.stdout)
|
|
222
|
+
|
|
223
|
+
const checks = [
|
|
224
|
+
check('RD01', 'package manifest includes sha256 file hashes', packageRun.status === 0 && manifest?.integrity?.algorithm === 'sha256' && manifest?.fileHashes?.['SKILL.md'], 'gse-package-manifest.json'),
|
|
225
|
+
check('RD02', 'HTTP package source served manifest and files', Boolean(baseUrl) && remoteInstall.status === 0, baseUrl),
|
|
226
|
+
check('RD03', 'install-gse supports --source-url', remoteInstall.status === 0 && remoteInstallData?.sourceMode === 'http-url', remoteInstall.command),
|
|
227
|
+
check('RD04', 'remote install validates file integrity', remoteInstall.status === 0 && remoteInstallData?.summary?.integrityFailed === 0 && remoteInstallData?.integrity?.algorithm === 'sha256', 'install summary integrity'),
|
|
228
|
+
smokeProfile
|
|
229
|
+
? skipped('RD05', 'remote installed copy validates', installedValidate.command, 'Run --profile full for remote installed-copy validation.')
|
|
230
|
+
: check('RD05', 'remote installed copy validates', installedValidate.status === 0 && installedValidateData?.summary?.failed === 0, installedValidate.command),
|
|
231
|
+
check('RD06', 'remote installed short CLI wrapper runs status command', installedCli.status === 0 && installedCliData?.command === '/gse status' && installedCliData?.project?.stateValid === true, installedCli.command),
|
|
232
|
+
check('RD07', 'tampered remote package fails integrity gate', tamperedInstall.status === 1 && tamperedInstallData?.summary?.integrityFailed > 0, 'tampered README.md install'),
|
|
233
|
+
]
|
|
234
|
+
|
|
235
|
+
const passed = checks.filter((item) => item.status === 'passed').length
|
|
236
|
+
const skippedCount = checks.filter((item) => item.status === 'skipped').length
|
|
237
|
+
const failed = checks.filter((item) => item.status === 'failed').length
|
|
238
|
+
const report = {
|
|
239
|
+
root,
|
|
240
|
+
generatedAt: new Date().toISOString(),
|
|
241
|
+
tempRoot,
|
|
242
|
+
baseUrl,
|
|
243
|
+
profile,
|
|
244
|
+
summary: { status: failed === 0 ? 'passed' : 'failed', passed, failed, skipped: skippedCount, total: checks.length },
|
|
245
|
+
workflows: {
|
|
246
|
+
remoteInstall: remoteInstall.status === 0 ? 'verified' : 'failed',
|
|
247
|
+
integrityGate: tamperedInstall.status === 1 ? 'verified' : 'failed',
|
|
248
|
+
installedValidation: smokeProfile ? 'skipped' : installedValidate.status === 0 ? 'verified' : 'failed',
|
|
249
|
+
installedCli: installedCli.status === 0 ? 'verified' : 'failed',
|
|
250
|
+
},
|
|
251
|
+
commands: [packageRun.command, remoteInstall.command, installedValidate.command, installedCli.command, tamperedInstall.command],
|
|
252
|
+
limits: [
|
|
253
|
+
'This audit verifies HTTP URL install through a local ephemeral server and manifest integrity checks.',
|
|
254
|
+
'Use --profile smoke for routine remote install, CLI, and integrity checks; use --profile full before release or when installed-copy validation matters.',
|
|
255
|
+
'Installed-package validation intentionally excludes source-workspace-only roadmap, long evidence logs, release-bundle cache, and owner/external acceptance artifacts.',
|
|
256
|
+
'It does not publish to a public registry, verify marketplace discovery, or sign artifacts with a trusted key.',
|
|
257
|
+
],
|
|
258
|
+
checks,
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
fs.rmSync(tempRoot, { recursive: true, force: true })
|
|
262
|
+
|
|
263
|
+
if (jsonOnly) console.log(JSON.stringify(report, null, 2))
|
|
264
|
+
else console.log(JSON.stringify(report, null, 2))
|
|
265
|
+
|
|
266
|
+
if (failed > 0) process.exit(1)
|