@mnstry/atelier 0.2.0-alpha.1
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 +383 -0
- package/LICENSE +202 -0
- package/NOTICE +5 -0
- package/README.md +262 -0
- package/SECURITY.md +93 -0
- package/TRADEMARKS.md +88 -0
- package/announcements/2026-08-06-announcements-channel.v1.json +12 -0
- package/announcements/keys/mnstry-announcements.public.v1.json +9 -0
- package/bin/atelier.mjs +2 -0
- package/bin/mnstry-atelier.mjs +3 -0
- package/contracts/analysis-adapter.v1.schema.json +248 -0
- package/contracts/atelier-action-intent.v1.schema.json +143 -0
- package/contracts/atelier-analysis-adapter.v1.schema.json +360 -0
- package/contracts/atelier-attestation.v1.schema.json +239 -0
- package/contracts/atelier-boundary-policy.v1.schema.json +339 -0
- package/contracts/atelier-claim.v1.schema.json +97 -0
- package/contracts/atelier-export.v1.schema.json +1015 -0
- package/contracts/atelier-extension-pack.v1.schema.json +290 -0
- package/contracts/atelier-kit-manifest.v1.schema.json +273 -0
- package/contracts/atelier-lock.v1.schema.json +364 -0
- package/contracts/atelier-migration.v1.schema.json +162 -0
- package/contracts/atelier-project-config.v1.schema.json +352 -0
- package/contracts/atelier-readiness-protocol.v1.schema.json +517 -0
- package/contracts/atelier-readiness-run.v1.schema.json +229 -0
- package/contracts/atelier-readiness.v1.schema.json +457 -0
- package/contracts/compat-baseline.json +10 -0
- package/contracts/git-promote-event.v1.schema.json +145 -0
- package/contracts/knowledge-source-sidecar.v1.schema.json +157 -0
- package/contracts/mnstry-atelier-semantic-profile.v1.json +254 -0
- package/docs/announcements.md +101 -0
- package/docs/atelier-runtime.md +34 -0
- package/docs/attestation.md +201 -0
- package/docs/continuity.md +55 -0
- package/docs/contract-stability.md +111 -0
- package/docs/distributions.md +220 -0
- package/docs/install.md +140 -0
- package/docs/knowledge-graph.md +140 -0
- package/docs/ontology.md +206 -0
- package/docs/release-engineering.md +151 -0
- package/docs/repo-boundary-guard.md +199 -0
- package/docs/tenant-readiness.md +64 -0
- package/docs/upgrade.md +124 -0
- package/fixtures/analysis/invalid/native-graph.v1.json +4 -0
- package/fixtures/analysis/invalid/promoted-claim.v1.json +17 -0
- package/fixtures/analysis/invalid/runtime-import.v1.json +20 -0
- package/fixtures/analysis/valid/output.claims.v1.json +17 -0
- package/fixtures/analysis-adapter/invalid/manifest-hidden-provider.v1.json +24 -0
- package/fixtures/analysis-adapter/invalid/output-frontmatter-mutation.v1.json +21 -0
- package/fixtures/analysis-adapter/invalid/output-native-graph.v1.json +14 -0
- package/fixtures/analysis-adapter/manifest.disabled.v1.json +22 -0
- package/fixtures/analysis-adapter/manifest.enabled-local.v1.json +23 -0
- package/fixtures/analysis-adapter/output.claims.v1.json +21 -0
- package/fixtures/atelier-action-intent/invalid/runtime-mutation-authority.v1.json +16 -0
- package/fixtures/atelier-action-intent/invalid/unknown-top-level-field.v1.json +17 -0
- package/fixtures/atelier-action-intent/invalid/writes-beyond-clipboard.v1.json +16 -0
- package/fixtures/atelier-action-intent/valid/copy-agent-prompt.v1.json +16 -0
- package/fixtures/atelier-action-intent/valid/copy-repo-path.v1.json +22 -0
- package/fixtures/atelier-analysis-adapter/invalid/hidden-model-provider.v1.json +24 -0
- package/fixtures/atelier-analysis-adapter/invalid/inert-with-execute-command.v1.json +24 -0
- package/fixtures/atelier-analysis-adapter/invalid/proposals-with-runtime-import.v1.json +30 -0
- package/fixtures/atelier-analysis-adapter/valid/manifest-inert.v1.json +24 -0
- package/fixtures/atelier-analysis-adapter/valid/proposals-claim-only.v1.json +27 -0
- package/fixtures/atelier-attestation/invalid/bad-digest.v1.json +23 -0
- package/fixtures/atelier-attestation/invalid/conformance-scope.v1.json +23 -0
- package/fixtures/atelier-attestation/invalid/missing-signature.v1.json +22 -0
- package/fixtures/atelier-attestation/invalid/unknown-root-field.v1.json +24 -0
- package/fixtures/atelier-attestation/keys/roundtrip-issuer.public.v1.json +9 -0
- package/fixtures/atelier-attestation/valid/rejected-with-reasons.v1.json +49 -0
- package/fixtures/atelier-attestation/valid/signed-admitted.v1.json +35 -0
- package/fixtures/atelier-attestation/valid/signed-roundtrip.v1.json +29 -0
- package/fixtures/atelier-attestation/valid/unsigned-advisory.v1.json +23 -0
- package/fixtures/atelier-claim/invalid/unknown-provider.v1.json +15 -0
- package/fixtures/atelier-claim/invalid/unknown-top-level-field.v1.json +16 -0
- package/fixtures/atelier-claim/invalid/wrong-schema-const.v1.json +15 -0
- package/fixtures/atelier-claim/valid/adapter-style-proposed-relation.v1.json +19 -0
- package/fixtures/atelier-claim/valid/codex-proposed-relation.v1.json +16 -0
- package/fixtures/atelier-claim/valid/manual-proposed-relation.v1.json +16 -0
- package/fixtures/atelier-claim/valid/readiness-proposed-relation.v1.json +18 -0
- package/fixtures/atelier-export/invalid/dirty-unforced-empty-taint.v1.json +19 -0
- package/fixtures/atelier-export/invalid/object-local-audience-visibility.v1.json +11 -0
- package/fixtures/atelier-export/invalid/open-object-class-owner.v1.json +23 -0
- package/fixtures/atelier-export/invalid/pricing-legacy-amount.v1.json +23 -0
- package/fixtures/atelier-export/invalid/public-disguised-internal-source-ref.v1.json +11 -0
- package/fixtures/atelier-export/invalid/public-nested-private-source-ref.v1.json +17 -0
- package/fixtures/atelier-export/invalid/public-resolved-private-source-ref.v1.json +13 -0
- package/fixtures/atelier-export/invalid/public-resolved-sensitive-source-ref.v1.json +13 -0
- package/fixtures/atelier-export/invalid/public-source-ref-missing-provenance.v1.json +20 -0
- package/fixtures/atelier-export/invalid/public-source-team-kg.json +12 -0
- package/fixtures/atelier-export/invalid/source-node-legacy-visibility.v1.json +21 -0
- package/fixtures/atelier-export/invalid/source-node-unknown-field.v1.json +18 -0
- package/fixtures/atelier-export/invalid/unresolved-public-source-ref.v1.json +11 -0
- package/fixtures/atelier-export/sample-studio-offer.v1.json +397 -0
- package/fixtures/atelier-extension-pack/invalid/bundled-slug-collision.v1.json +22 -0
- package/fixtures/atelier-extension-pack/invalid/escapes-root.v1.json +22 -0
- package/fixtures/atelier-extension-pack/invalid/posture-tampered.v1.json +22 -0
- package/fixtures/atelier-extension-pack/invalid/protocols/autonomous-authority.v1.json +118 -0
- package/fixtures/atelier-extension-pack/invalid/protocols/offer-map.v1.json +109 -0
- package/fixtures/atelier-extension-pack/invalid/redefines-runtime-term.v1.json +19 -0
- package/fixtures/atelier-extension-pack/invalid/wrong-namespace.v1.json +19 -0
- package/fixtures/atelier-extension-pack/valid/protocols/contract-gate.v1.json +109 -0
- package/fixtures/atelier-extension-pack/valid/sample-pack.v1.json +48 -0
- package/fixtures/atelier-kit-manifest/invalid/runtime-copy-without-canonical-role.v1.json +53 -0
- package/fixtures/atelier-kit-manifest/invalid/telemetry-send-path.v1.json +21 -0
- package/fixtures/atelier-kit-manifest/valid/mnstry-atelier.valid.v1.json +76 -0
- package/fixtures/atelier-lock/invalid/absolute-contract-path.v1.json +40 -0
- package/fixtures/atelier-lock/invalid/runtime-mutation-enabled.v1.json +41 -0
- package/fixtures/atelier-lock/invalid/unknown-top-level-field.v1.json +42 -0
- package/fixtures/atelier-lock/valid/extension-pack.v1.json +55 -0
- package/fixtures/atelier-lock/valid/minimal.v1.json +44 -0
- package/fixtures/atelier-migration/invalid/live-apply-mode.v1.json +21 -0
- package/fixtures/atelier-migration/invalid/missing-review-evidence.v1.json +20 -0
- package/fixtures/atelier-migration/invalid/runtime-mutation-enabled.v1.json +21 -0
- package/fixtures/atelier-migration/valid/minimal.v1.json +32 -0
- package/fixtures/atelier-project-config/invalid/absolute-repo-path.v1.json +11 -0
- package/fixtures/atelier-project-config/invalid/ext-not-object.v1.json +12 -0
- package/fixtures/atelier-project-config/invalid/external-kind-read-boundary.v1.json +12 -0
- package/fixtures/atelier-project-config/invalid/missing-repos-and-alignment.v1.json +7 -0
- package/fixtures/atelier-project-config/invalid/unknown-top-level-field.v1.json +14 -0
- package/fixtures/atelier-project-config/valid/alignment-scaffold.v1.json +15 -0
- package/fixtures/atelier-project-config/valid/ext-extension-packs.v1.json +36 -0
- package/fixtures/atelier-project-config/valid/repo-workspace.v1.json +23 -0
- package/fixtures/atelier-project-config/validator-only/ext-member-not-object.v1.json +14 -0
- package/fixtures/atelier-readiness/invalid/dry-run-only-false.v1.json +87 -0
- package/fixtures/atelier-readiness/invalid/support-bundle-send-path.v1.json +87 -0
- package/fixtures/atelier-readiness/invalid/unknown-top-level-field.v1.json +88 -0
- package/fixtures/atelier-readiness/valid/ready-workspace.v1.json +84 -0
- package/fixtures/boundary-policy/invalid/content-rule-exception-blanket-path.v1.json +67 -0
- package/fixtures/boundary-policy/invalid/invalid-actor-private-repo-mapping.v1.json +20 -0
- package/fixtures/boundary-policy/invalid/legacy-warning-missing-warning.v1.json +20 -0
- package/fixtures/boundary-policy/invalid/missing-repo-coverage.v1.json +19 -0
- package/fixtures/boundary-policy/invalid/unknown-key.v1.json +12 -0
- package/fixtures/boundary-policy/valid/content-rule-exception.v1.json +67 -0
- package/fixtures/boundary-policy/valid/legacy-warning.v1.json +33 -0
- package/fixtures/boundary-policy/valid/strict-private-shared.v1.json +40 -0
- package/fixtures/git-promote/invalid/provision-named-revocable.v1.json +29 -0
- package/fixtures/git-promote/private-to-team.v1.json +29 -0
- package/fixtures/knowledge-source-sidecar/invalid/legacy-visibility-field.v1.json +17 -0
- package/fixtures/knowledge-source-sidecar/invalid/missing-audience.v1.json +15 -0
- package/fixtures/knowledge-source-sidecar/invalid/unknown-relation-kind.v1.json +18 -0
- package/fixtures/knowledge-source-sidecar/valid/html-source.v1.json +18 -0
- package/fixtures/knowledge-source-sidecar/valid/pdf-evidence.v1.json +18 -0
- package/fixtures/projects/neutral-project/atelier.project.json +15 -0
- package/fixtures/projects/neutral-project/ops/config/repo-access.v1.json +12 -0
- package/fixtures/projects/neutral-project/project-knowledge.graph.json +69 -0
- package/fixtures/projects/sample-workspace/atelier.project.json +23 -0
- package/fixtures/projects/sample-workspace/boundary-policy.v1.json +59 -0
- package/fixtures/projects/sample-workspace/content/README.md +16 -0
- package/fixtures/projects/sample-workspace/content/source.html +5 -0
- package/fixtures/projects/sample-workspace/content/source.html.kg.json +15 -0
- package/fixtures/projects/sample-workspace/repo-access.v1.json +9 -0
- package/fixtures/projects/source-formats-workspace/atelier.project.json +23 -0
- package/fixtures/projects/source-formats-workspace/content/README.md +17 -0
- package/fixtures/projects/source-formats-workspace/content/data.json +7 -0
- package/fixtures/projects/source-formats-workspace/content/data.json.kg.json +15 -0
- package/fixtures/projects/source-formats-workspace/content/logo.png.kg.json +15 -0
- package/fixtures/projects/source-formats-workspace/content/metrics.csv +3 -0
- package/fixtures/projects/source-formats-workspace/content/metrics.csv.kg.json +15 -0
- package/fixtures/projects/source-formats-workspace/content/pipeline.yaml +4 -0
- package/fixtures/projects/source-formats-workspace/content/pipeline.yaml.kg.json +15 -0
- package/fixtures/projects/source-formats-workspace/repo-access.v1.json +9 -0
- package/fixtures/readiness-protocols/extension-pack/invalid/protocol-missing-path.v1.json +20 -0
- package/fixtures/readiness-protocols/extension-pack/valid/with-protocol.v1.json +24 -0
- package/fixtures/readiness-protocols/protocol/invalid/missing-check-target.v1.json +103 -0
- package/fixtures/readiness-protocols/protocol/invalid/runtime-mutation-enabled.v1.json +107 -0
- package/fixtures/readiness-protocols/protocol/valid/minimal.v1.json +100 -0
- package/fixtures/readiness-protocols/run/invalid/missing-evidence.v1.json +37 -0
- package/fixtures/readiness-protocols/run/invalid/pass-with-blocker.v1.json +22 -0
- package/fixtures/readiness-protocols/run/invalid/runtime-mutation-enabled.v1.json +22 -0
- package/fixtures/readiness-protocols/run/valid/pass.v1.json +44 -0
- package/fixtures/support/invalid/leaky-support.v1.json +13 -0
- package/fixtures/support/valid/minimal-safe.v1.json +20 -0
- package/package.json +98 -0
- package/skills/claude/atelier-open/SKILL.md +68 -0
- package/skills/claude/mnstry-readiness/SKILL.md +41 -0
- package/skills/codex/atelier-open/SKILL.md +68 -0
- package/skills/codex/mnstry-readiness/SKILL.md +41 -0
- package/src/analysis/adapter.mjs +20 -0
- package/src/analysis/analysis-claim-contract.mjs +335 -0
- package/src/attestation/jcs.mjs +64 -0
- package/src/attestation/sign.mjs +305 -0
- package/src/boundary/content-rules.mjs +262 -0
- package/src/boundary/policy.mjs +806 -0
- package/src/check-atelier-export-contract.mjs +116 -0
- package/src/cli/run.mjs +283 -0
- package/src/collaboration/proposals.mjs +262 -0
- package/src/commands/analysis.mjs +8 -0
- package/src/commands/announcements.mjs +308 -0
- package/src/commands/attestation.mjs +198 -0
- package/src/commands/boundary.mjs +25 -0
- package/src/commands/config.mjs +4 -0
- package/src/commands/context.mjs +3 -0
- package/src/commands/distribution.mjs +224 -0
- package/src/commands/egress.mjs +11 -0
- package/src/commands/extension-pack.mjs +209 -0
- package/src/commands/feedback.mjs +3 -0
- package/src/commands/graph.mjs +3 -0
- package/src/commands/init.mjs +130 -0
- package/src/commands/lock.mjs +3 -0
- package/src/commands/project.mjs +3 -0
- package/src/commands/promote.mjs +4 -0
- package/src/commands/readiness.mjs +3 -0
- package/src/commands/server.mjs +6 -0
- package/src/commands/setup.mjs +250 -0
- package/src/commands/support.mjs +3 -0
- package/src/commands/upgrade.mjs +3 -0
- package/src/contracts/corpus.mjs +171 -0
- package/src/egress/check.mjs +45 -0
- package/src/egress/forbidden-egress.mjs +323 -0
- package/src/export/atelier-export-contract.mjs +722 -0
- package/src/extension-packs/loader.mjs +491 -0
- package/src/graph/graph.mjs +370 -0
- package/src/graph/knowledge-graph.mjs +887 -0
- package/src/harness/context-client.mjs +144 -0
- package/src/harness/context.mjs +27 -0
- package/src/index.mjs +88 -0
- package/src/project/config.mjs +557 -0
- package/src/project/file-class.mjs +113 -0
- package/src/project/git-ignore.mjs +31 -0
- package/src/project/path-match.mjs +30 -0
- package/src/project/repo-identity.mjs +176 -0
- package/src/projection/alignment-projection.mjs +169 -0
- package/src/projection/policy.mjs +90 -0
- package/src/projection/project.mjs +152 -0
- package/src/readiness/readiness.mjs +312 -0
- package/src/readiness-protocols/bundled-pack.mjs +766 -0
- package/src/readiness-protocols/runtime.mjs +419 -0
- package/src/server/local-sidecar.mjs +640 -0
- package/src/server/security.mjs +229 -0
- package/src/server/server.mjs +42 -0
- package/src/support/feedback-report.mjs +414 -0
- package/src/support/support-bundle.mjs +226 -0
- package/src/ui/html-primitives.mjs +104 -0
- package/src/upgrade/upgrade.mjs +701 -0
- package/src/validate-atelier-export-dry-run.mjs +75 -0
- package/templates/atelier.project.example.json +23 -0
- package/templates/distribution-workspace/.claude/launch.json +12 -0
- package/templates/distribution-workspace/README.md +86 -0
- package/templates/distribution-workspace/atelier.project.json +36 -0
- package/templates/distribution-workspace/boundary-policy.v1.json +61 -0
- package/templates/distribution-workspace/gitignore +29 -0
- package/templates/distribution-workspace/governance/repo-boundary-ledger.md +5 -0
- package/templates/distribution-workspace/project/README.md +19 -0
- package/templates/distribution-workspace/repo-access.v1.json +9 -0
- package/templates/launch.json +12 -0
- package/templates/private-domain-workspace/.claude/launch.json +12 -0
- package/templates/private-domain-workspace/README.md +50 -0
- package/templates/private-domain-workspace/atelier.project.json +28 -0
- package/templates/private-domain-workspace/boundary-policy.v1.json +54 -0
- package/templates/private-domain-workspace/domain/README.md +19 -0
- package/templates/private-domain-workspace/gitignore +29 -0
- package/templates/private-domain-workspace/governance/repo-boundary-ledger.md +4 -0
- package/templates/private-domain-workspace/repo-access.v1.json +9 -0
- package/templates/shared-project-workspace/.claude/launch.json +12 -0
- package/templates/shared-project-workspace/README.md +51 -0
- package/templates/shared-project-workspace/atelier.project.json +28 -0
- package/templates/shared-project-workspace/boundary-policy.v1.json +61 -0
- package/templates/shared-project-workspace/gitignore +29 -0
- package/templates/shared-project-workspace/governance/repo-boundary-ledger.md +5 -0
- package/templates/shared-project-workspace/project/README.md +19 -0
- package/templates/shared-project-workspace/repo-access.v1.json +9 -0
|
@@ -0,0 +1,226 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
import crypto from 'node:crypto'
|
|
4
|
+
import fs from 'node:fs'
|
|
5
|
+
import path from 'node:path'
|
|
6
|
+
import { fileURLToPath } from 'node:url'
|
|
7
|
+
import { resolveProjectConfig } from '../project/config.mjs'
|
|
8
|
+
|
|
9
|
+
export const SUPPORT_BUNDLE_PREVIEW_SCHEMA = 'mnstry.atelier-support-bundle-preview@v1'
|
|
10
|
+
|
|
11
|
+
export const DEFAULT_SUPPORT_FIELDS = [
|
|
12
|
+
'nodeVersion',
|
|
13
|
+
'platform',
|
|
14
|
+
'packageVersion',
|
|
15
|
+
'command',
|
|
16
|
+
'localErrorSummary',
|
|
17
|
+
]
|
|
18
|
+
|
|
19
|
+
export const BANNED_KEY_PATTERNS = [
|
|
20
|
+
/prompt/i,
|
|
21
|
+
/secret/i,
|
|
22
|
+
/token/i,
|
|
23
|
+
/password/i,
|
|
24
|
+
/private/i,
|
|
25
|
+
/email/i,
|
|
26
|
+
/remote/i,
|
|
27
|
+
/url/i,
|
|
28
|
+
]
|
|
29
|
+
|
|
30
|
+
export const BANNED_VALUE_PATTERNS = [
|
|
31
|
+
{ type: 'absolute-path', re: /(?:^|[\s"'])\/(?:Users|home)\/[^/\s"']+\/[^\s"']+/ },
|
|
32
|
+
{ type: 'repo-file-path', re: /(?:^|[\s"'])(?:[a-z0-9._-]+)\/(?:APP|app|src|docs|private|secrets?)\/[^\s"']+/i },
|
|
33
|
+
{ type: 'kg-node-id', re: /(?:^|[\s"'])[a-z][a-z0-9._-]*:[a-z0-9._:-]*[a-z][a-z0-9._:-]*/i },
|
|
34
|
+
{ type: 'git-remote', re: /\b(?:git@|https:\/\/github\.com\/)[^\s"']+/i },
|
|
35
|
+
// Bounded on purpose. The unbounded local-part and domain runs backtracked
|
|
36
|
+
// catastrophically on large non-email input: a few hundred KB of
|
|
37
|
+
// address-shaped characters with no "@" took about a minute to reject, which
|
|
38
|
+
// would turn any capped file attachment into a stall. RFC 5321 caps the
|
|
39
|
+
// local part at 64 octets and the domain at 255, so every real address
|
|
40
|
+
// still matches.
|
|
41
|
+
{ type: 'email', re: /\b[A-Z0-9._%+-]{1,64}@[A-Z0-9.-]{1,255}\.[A-Z]{2,}\b/i },
|
|
42
|
+
{ type: 'hostname-or-url', re: /\b(?:https?:\/\/|wss?:\/\/)(?!localhost\b|127\.0\.0\.1\b|\[?::1\]?)[^\s"']+/i },
|
|
43
|
+
{ type: 'secret', re: /\b(?:sk|pk|ghp|gho|pat|xoxb|AKIA)[-_A-Za-z0-9]{12,}\b/ },
|
|
44
|
+
// Every PEM private-key header, not only the bare PKCS#8 one. The previous
|
|
45
|
+
// form allowed exactly one word between BEGIN and KEY, so an ssh-keygen
|
|
46
|
+
// default key (OPENSSH PRIVATE KEY) and the RSA/EC/DSA/ENCRYPTED and PGP
|
|
47
|
+
// "PRIVATE KEY BLOCK" headers all passed the scan untouched. Kept identical
|
|
48
|
+
// to the structural pattern in scripts/structural-patterns.mjs.
|
|
49
|
+
// The literal must not match itself: this file ships in the tarball and
|
|
50
|
+
// release:audit scans it with that structural pattern.
|
|
51
|
+
// Keys in this kit are JWK, not PEM: the PEM patterns above never saw the
|
|
52
|
+
// one key format atelier itself writes. Both patterns match key material
|
|
53
|
+
// rather than prose about it — a document may discuss privateKeyJwk, and
|
|
54
|
+
// 40 is the floor for a real scalar (Ed25519 and P-256 are both 43
|
|
55
|
+
// base64url characters), so shorter identifiers under a member named d
|
|
56
|
+
// do not fire, and a redacted example in documentation stays attachable. Escaped JSON evades text patterns, which is why a context
|
|
57
|
+
// file that parses as JSON is walked structurally as well.
|
|
58
|
+
{ type: 'jwk-private-key', re: /"d"\s*:\s*"[A-Za-z0-9_-]{40,}"/ },
|
|
59
|
+
{ type: 'jwk-private-key-document', re: /"privateKeyJwk"\s*:\s*\{[^{}]*"d"\s*:\s*"[A-Za-z0-9_-]{40,}"/ },
|
|
60
|
+
{ type: 'private-key', re: /BEGIN (?:[A-Z0-9]+ ){0,4}PRIVATE KEY(?: BLOCK)?|BEGIN (?:RSA|OPENSSH) KEY/ },
|
|
61
|
+
]
|
|
62
|
+
|
|
63
|
+
function stable(value) {
|
|
64
|
+
if (Array.isArray(value)) return value.map(stable)
|
|
65
|
+
if (value && typeof value === 'object') {
|
|
66
|
+
return Object.fromEntries(Object.keys(value).sort().map((key) => [key, stable(value[key])]))
|
|
67
|
+
}
|
|
68
|
+
return value
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
function hashPayload(payload) {
|
|
72
|
+
const clone = { ...payload }
|
|
73
|
+
delete clone.hash
|
|
74
|
+
return crypto.createHash('sha256').update(JSON.stringify(stable(clone))).digest('hex')
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
function count(value) {
|
|
78
|
+
return Array.isArray(value) ? value.length : 0
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
function visit(value, fn, pathParts = []) {
|
|
82
|
+
fn(value, pathParts)
|
|
83
|
+
if (Array.isArray(value)) {
|
|
84
|
+
value.forEach((item, index) => visit(item, fn, [...pathParts, String(index)]))
|
|
85
|
+
} else if (value && typeof value === 'object') {
|
|
86
|
+
for (const [key, item] of Object.entries(value)) {
|
|
87
|
+
visit(item, fn, [...pathParts, key])
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
export function validateSupportBundlePayload(value) {
|
|
93
|
+
const errors = []
|
|
94
|
+
if (!value || typeof value !== 'object' || Array.isArray(value)) {
|
|
95
|
+
return ['support bundle payload must be an object']
|
|
96
|
+
}
|
|
97
|
+
if (value.schema !== SUPPORT_BUNDLE_PREVIEW_SCHEMA) {
|
|
98
|
+
errors.push(`schema must be ${SUPPORT_BUNDLE_PREVIEW_SCHEMA}`)
|
|
99
|
+
}
|
|
100
|
+
if (value.sendPath !== false) errors.push('sendPath must be false')
|
|
101
|
+
if (value.background !== false && value.background != null) errors.push('background must be false when present')
|
|
102
|
+
if (value.telemetry && value.telemetry !== 'none') errors.push('telemetry must be none')
|
|
103
|
+
|
|
104
|
+
visit(value, (item, parts) => {
|
|
105
|
+
const key = parts.at(-1) || ''
|
|
106
|
+
if (key && BANNED_KEY_PATTERNS.some((pattern) => pattern.test(key))) {
|
|
107
|
+
errors.push(`banned key ${parts.join('.')}`)
|
|
108
|
+
}
|
|
109
|
+
if (typeof item === 'string') {
|
|
110
|
+
for (const { type, re } of BANNED_VALUE_PATTERNS) {
|
|
111
|
+
if (re.test(item)) errors.push(`banned value ${type} at ${parts.join('.') || '<root>'}`)
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
})
|
|
115
|
+
return errors
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
export function buildSupportBundlePreview({
|
|
119
|
+
state = 'support_bundle',
|
|
120
|
+
fields = DEFAULT_SUPPORT_FIELDS,
|
|
121
|
+
artifacts = null,
|
|
122
|
+
generatedAt = new Date(0).toISOString(),
|
|
123
|
+
} = {}) {
|
|
124
|
+
const payload = {
|
|
125
|
+
schema: SUPPORT_BUNDLE_PREVIEW_SCHEMA,
|
|
126
|
+
state,
|
|
127
|
+
telemetry: 'none',
|
|
128
|
+
sendPath: false,
|
|
129
|
+
background: false,
|
|
130
|
+
generatedAt,
|
|
131
|
+
fields: [...fields],
|
|
132
|
+
artifacts: state === 'none' ? [] : (artifacts || [
|
|
133
|
+
{
|
|
134
|
+
kind: 'local-diagnostic',
|
|
135
|
+
label: 'operator-reviewed-summary',
|
|
136
|
+
count: count(fields),
|
|
137
|
+
},
|
|
138
|
+
]),
|
|
139
|
+
}
|
|
140
|
+
return {
|
|
141
|
+
...payload,
|
|
142
|
+
hash: hashPayload(payload),
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
export function supportBundlePreview(project = null) {
|
|
147
|
+
const projectShape = project ? ['schema', 'source', 'repos.length'] : DEFAULT_SUPPORT_FIELDS
|
|
148
|
+
return buildSupportBundlePreview({
|
|
149
|
+
state: 'support_bundle',
|
|
150
|
+
fields: projectShape,
|
|
151
|
+
artifacts: [
|
|
152
|
+
{
|
|
153
|
+
kind: 'local-diagnostic',
|
|
154
|
+
label: project ? 'project-config-shape' : 'operator-reviewed-summary',
|
|
155
|
+
count: count(projectShape),
|
|
156
|
+
},
|
|
157
|
+
],
|
|
158
|
+
})
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
export function readSupportBundleJson(file) {
|
|
162
|
+
return JSON.parse(fs.readFileSync(file, 'utf8'))
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
export function writeOrPrintSupportBundle(payload, out = null) {
|
|
166
|
+
const text = `${JSON.stringify(payload, null, 2)}\n`
|
|
167
|
+
if (!out) {
|
|
168
|
+
process.stdout.write(text)
|
|
169
|
+
return
|
|
170
|
+
}
|
|
171
|
+
fs.mkdirSync(path.dirname(out), { recursive: true })
|
|
172
|
+
fs.writeFileSync(out, text, { mode: 0o600 })
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
export function parseSupportBundleArgs(argv = process.argv.slice(2)) {
|
|
176
|
+
const args = { state: 'support_bundle', out: null, projectArgs: [] }
|
|
177
|
+
for (const arg of argv) {
|
|
178
|
+
if (arg === '--none') args.state = 'none'
|
|
179
|
+
else if (arg.startsWith('--state=')) args.state = arg.slice('--state='.length)
|
|
180
|
+
else if (arg.startsWith('--out=')) args.out = arg.slice('--out='.length)
|
|
181
|
+
else if (arg === '--help' || arg === '-h') args.help = true
|
|
182
|
+
else args.projectArgs.push(arg)
|
|
183
|
+
}
|
|
184
|
+
return args
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
function usage() {
|
|
188
|
+
return [
|
|
189
|
+
'Usage: node src/support/support-bundle.mjs [--none|--state=support_bundle] [--out=FILE] [--project-config=FILE]',
|
|
190
|
+
'',
|
|
191
|
+
'Builds a local preview only. It never sends or uploads support data.',
|
|
192
|
+
].join('\n')
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
export function runSupportCommand(argv = process.argv.slice(2)) {
|
|
196
|
+
const args = parseSupportBundleArgs(argv)
|
|
197
|
+
if (args.help) {
|
|
198
|
+
console.log(usage())
|
|
199
|
+
return 0
|
|
200
|
+
}
|
|
201
|
+
const project = resolveProjectConfig({ argv: args.projectArgs })
|
|
202
|
+
const payload = args.state === 'none'
|
|
203
|
+
? buildSupportBundlePreview({ state: 'none' })
|
|
204
|
+
: supportBundlePreview(project)
|
|
205
|
+
const errors = validateSupportBundlePayload(payload)
|
|
206
|
+
if (errors.length) {
|
|
207
|
+
for (const error of errors) console.error(`[atelier-support-bundle] ${error}`)
|
|
208
|
+
return 1
|
|
209
|
+
}
|
|
210
|
+
writeOrPrintSupportBundle(payload, args.out)
|
|
211
|
+
return 0
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
function main() {
|
|
215
|
+
try {
|
|
216
|
+
process.exitCode = runSupportCommand()
|
|
217
|
+
} catch (error) {
|
|
218
|
+
console.error(`[atelier-support-bundle] ${error instanceof Error ? error.message : String(error)}`)
|
|
219
|
+
process.exitCode = 1
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
const currentFile = fileURLToPath(import.meta.url)
|
|
224
|
+
if (process.argv[1] && path.resolve(process.argv[1]) === currentFile) {
|
|
225
|
+
main()
|
|
226
|
+
}
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
export function htmlEscape(value) {
|
|
2
|
+
return String(value ?? '')
|
|
3
|
+
.replaceAll('&', '&')
|
|
4
|
+
.replaceAll('<', '<')
|
|
5
|
+
.replaceAll('>', '>')
|
|
6
|
+
.replaceAll('"', '"')
|
|
7
|
+
.replaceAll("'", ''')
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export function htmlAttr(name, value) {
|
|
11
|
+
if (value == null || value === false) return ''
|
|
12
|
+
if (value === true) return ` ${name}`
|
|
13
|
+
return ` ${name}="${htmlEscape(value)}"`
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export function htmlAttrs(attrs = {}) {
|
|
17
|
+
return Object.entries(attrs).map(([name, value]) => htmlAttr(name, value)).join('')
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export function renderPageShell({ title = 'MNSTRY Atelier', body = '' } = {}) {
|
|
21
|
+
return [
|
|
22
|
+
'<!doctype html>',
|
|
23
|
+
'<html lang="en">',
|
|
24
|
+
'<head>',
|
|
25
|
+
'<meta charset="utf-8">',
|
|
26
|
+
'<meta name="viewport" content="width=device-width, initial-scale=1">',
|
|
27
|
+
`<title>${htmlEscape(title)}</title>`,
|
|
28
|
+
'<style>',
|
|
29
|
+
':root{color-scheme:light dark;font-family:Inter,ui-sans-serif,system-ui,sans-serif}',
|
|
30
|
+
'body{margin:0;padding:32px;background:#f7f7f4;color:#181816}',
|
|
31
|
+
'main{max-width:980px;margin:0 auto}',
|
|
32
|
+
'a{color:#245f73}',
|
|
33
|
+
'.list{display:grid;gap:12px;margin-top:24px}',
|
|
34
|
+
'.item{border:1px solid #d8d8d0;border-radius:8px;padding:16px;background:#fff}',
|
|
35
|
+
'.meta{color:#666;font-size:13px}',
|
|
36
|
+
'pre{white-space:pre-wrap;background:#1f2424;color:#f5f5ee;border-radius:8px;padding:16px;overflow:auto}',
|
|
37
|
+
'.notice{border-left:4px solid #245f73;padding:12px 16px;background:#edf6f7}',
|
|
38
|
+
'</style>',
|
|
39
|
+
'</head>',
|
|
40
|
+
'<body>',
|
|
41
|
+
'<main>',
|
|
42
|
+
body,
|
|
43
|
+
'</main>',
|
|
44
|
+
'</body>',
|
|
45
|
+
'</html>',
|
|
46
|
+
].join('')
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
export function renderProposalListPageHtml(records = []) {
|
|
50
|
+
const items = records.length
|
|
51
|
+
? records.map((record) => {
|
|
52
|
+
const proposal = record.proposal || {}
|
|
53
|
+
return [
|
|
54
|
+
'<article class="item">',
|
|
55
|
+
`<h2><a href="/proposals/${htmlEscape(proposal.id)}">${htmlEscape(proposal.id)}</a></h2>`,
|
|
56
|
+
`<p class="meta">${htmlEscape(proposal.status || 'unknown')} · ${htmlEscape(proposal.path || '')}</p>`,
|
|
57
|
+
`<p>${htmlEscape(proposal.action || '')}</p>`,
|
|
58
|
+
'</article>',
|
|
59
|
+
].join('')
|
|
60
|
+
}).join('')
|
|
61
|
+
: '<p class="meta">No local proposals.</p>'
|
|
62
|
+
|
|
63
|
+
return renderPageShell({
|
|
64
|
+
title: 'Atelier Proposals',
|
|
65
|
+
body: [
|
|
66
|
+
'<h1>Atelier Proposals</h1>',
|
|
67
|
+
'<p class="notice">Review records are local handoffs. No source writes or browser apply endpoint are exposed.</p>',
|
|
68
|
+
`<section class="list">${items}</section>`,
|
|
69
|
+
].join(''),
|
|
70
|
+
})
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
export function renderProposalDetailPageHtml(record) {
|
|
74
|
+
if (!record) {
|
|
75
|
+
return renderPageShell({
|
|
76
|
+
title: 'Proposal Not Found',
|
|
77
|
+
body: '<h1>Proposal Not Found</h1>',
|
|
78
|
+
})
|
|
79
|
+
}
|
|
80
|
+
const proposal = record.proposal || {}
|
|
81
|
+
const copyable = record.copyable || null
|
|
82
|
+
const copyBlock = copyable
|
|
83
|
+
? [
|
|
84
|
+
'<h2>Copy Handoff</h2>',
|
|
85
|
+
'<p class="notice">Accepted Atelier proposal output is copy-only. There is no browser apply endpoint.</p>',
|
|
86
|
+
`<pre>${htmlEscape(copyable.agentInstructions || '')}</pre>`,
|
|
87
|
+
copyable.diff ? `<pre>${htmlEscape(copyable.diff)}</pre>` : '',
|
|
88
|
+
].join('')
|
|
89
|
+
: ''
|
|
90
|
+
|
|
91
|
+
return renderPageShell({
|
|
92
|
+
title: `Review ${proposal.id || 'Proposal'}`,
|
|
93
|
+
body: [
|
|
94
|
+
`<h1>Review ${htmlEscape(proposal.id || 'Proposal')}</h1>`,
|
|
95
|
+
`<p class="meta">${htmlEscape(proposal.status || 'unknown')} · ${htmlEscape(proposal.path || '')}</p>`,
|
|
96
|
+
'<p class="notice">No source writes or apply endpoint are available from this review page.</p>',
|
|
97
|
+
'<h2>Proposal</h2>',
|
|
98
|
+
`<pre>${htmlEscape(JSON.stringify(proposal, null, 2))}</pre>`,
|
|
99
|
+
record.diff ? '<h2>Diff</h2>' : '',
|
|
100
|
+
record.diff ? `<pre>${htmlEscape(record.diff)}</pre>` : '',
|
|
101
|
+
copyBlock,
|
|
102
|
+
].join(''),
|
|
103
|
+
})
|
|
104
|
+
}
|