multimodel-dev-os 3.1.0 → 3.5.0
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/.ai/policies/registry-policy.yaml +29 -1
- package/.ai/registries/trusted-keys.yaml +12 -0
- package/.ai/schema/registry-manifest.schema.json +31 -2
- package/.ai/schema/registry-policy.schema.json +37 -1
- package/.ai/schema/trusted-keys.schema.json +69 -0
- package/AGENTS.md +22 -26
- package/MEMORY.md +34 -11
- package/README.md +2 -1
- package/RUNBOOK.md +28 -36
- package/TASKS.md +15 -5
- package/bin/multimodel-dev-os.js +1366 -548
- package/docs/.vitepress/config.js +3 -1
- package/docs/architecture.md +3 -1
- package/docs/index.md +5 -5
- package/docs/npm-publishing.md +5 -5
- package/docs/package-safety.md +17 -0
- package/docs/public/llms-full.txt +5 -1
- package/docs/public/llms.txt +6 -1
- package/docs/public/sitemap.xml +15 -0
- package/docs/registry-policy.md +29 -1
- package/docs/registry-security.md +73 -6
- package/docs/registry-signing.md +70 -0
- package/docs/registry-sync.md +5 -2
- package/docs/registry-trust-store.md +66 -0
- package/docs/release-policy.md +6 -5
- package/docs/security-threat-model.md +96 -0
- package/docs/testing.md +25 -2
- package/docs/trusted-registries.md +1 -1
- package/docs/v3-roadmap.md +17 -6
- package/docs/v3.5.0-readiness.md +46 -0
- package/package.json +5 -2
- package/scripts/build-cli.js +45 -3
- package/scripts/check-build-fresh.js +52 -0
- package/scripts/install.ps1 +1 -1
- package/scripts/install.sh +1 -1
- package/scripts/verify.js +327 -14
- package/scripts/verify.sh +10 -0
- package/src/catalog/loader.js +117 -0
- package/src/cli/args.js +118 -0
- package/src/cli/help.js +60 -0
- package/src/cli/main.js +6263 -0
- package/src/core/globals.js +52 -0
- package/src/core/hashes.js +15 -0
- package/src/core/policy.js +44 -0
- package/src/core/security.js +61 -0
- package/src/core/yaml.js +136 -0
- package/src/plugin/manifest.js +95 -0
- package/src/registry/provenance.js +114 -0
- package/src/registry/signing.js +392 -0
- package/src/registry/sources.js +40 -0
- package/src/registry/trust-store.js +41 -0
- package/src/registry/validation.js +45 -0
- package/src/registry/verdict.js +51 -0
- package/tests/README.md +37 -0
- package/tests/fixtures/README.md +22 -0
- package/tests/fixtures/custom-template-example/README.md +10 -0
- package/tests/fixtures/proposals/approved-append-line.md +28 -0
- package/tests/fixtures/proposals/approved-create-file.md +29 -0
- package/tests/fixtures/proposals/approved-replace-text.md +30 -0
- package/tests/fixtures/proposals/existing-create-file-no-overwrite.md +29 -0
- package/tests/fixtures/proposals/no-operations.md +18 -0
- package/tests/fixtures/proposals/path-traversal.md +29 -0
- package/tests/fixtures/proposals/pending-proposal.md +29 -0
- package/tests/fixtures/proposals/protected-path.md +29 -0
- package/tests/fixtures/proposals/replace-multiple-without-allow.md +30 -0
- package/tests/fixtures/registry-overrides/README.md +20 -0
- package/tests/fixtures/signed-registries/README.md +4 -0
- package/tests/fixtures/signed-registries/revoked-key/catalog.yaml +8 -0
- package/tests/fixtures/signed-registries/revoked-key/expected-verdict.json +7 -0
- package/tests/fixtures/signed-registries/revoked-key/registry-manifest.yaml +14 -0
- package/tests/fixtures/signed-registries/tampered-manifest/catalog.yaml +8 -0
- package/tests/fixtures/signed-registries/tampered-manifest/expected-verdict.json +7 -0
- package/tests/fixtures/signed-registries/tampered-manifest/registry-manifest.yaml +14 -0
- package/tests/fixtures/signed-registries/trusted-keys.yaml +23 -0
- package/tests/fixtures/signed-registries/unsigned-remote-required/catalog.yaml +8 -0
- package/tests/fixtures/signed-registries/unsigned-remote-required/expected-verdict.json +7 -0
- package/tests/fixtures/signed-registries/unsigned-remote-required/registry-manifest.yaml +9 -0
- package/tests/fixtures/signed-registries/unsupported-algorithm/catalog.yaml +8 -0
- package/tests/fixtures/signed-registries/unsupported-algorithm/expected-verdict.json +7 -0
- package/tests/fixtures/signed-registries/unsupported-algorithm/registry-manifest.yaml +14 -0
- package/tests/fixtures/signed-registries/valid-signed-registry/catalog.yaml +8 -0
- package/tests/fixtures/signed-registries/valid-signed-registry/expected-verdict.json +7 -0
- package/tests/fixtures/signed-registries/valid-signed-registry/registry-manifest.yaml +14 -0
- package/tests/fixtures/signed-registries/wrong-key/catalog.yaml +8 -0
- package/tests/fixtures/signed-registries/wrong-key/expected-verdict.json +7 -0
- package/tests/fixtures/signed-registries/wrong-key/registry-manifest.yaml +14 -0
- package/tests/smoke/README.md +37 -0
- package/tests/smoke/cli-smoke.md +49 -0
- package/tests/unit/build-output.test.js +40 -0
- package/tests/unit/catalog-loader.test.js +44 -0
- package/tests/unit/path-safety.test.js +62 -0
- package/tests/unit/plugin-manifest.test.js +94 -0
- package/tests/unit/prepublish-guard.test.js +35 -0
- package/tests/unit/registry-e2e-signature-fixtures.test.js +288 -0
- package/tests/unit/registry-policy.test.js +52 -0
- package/tests/unit/registry-provenance.test.js +185 -0
- package/tests/unit/registry-public-signing.test.js +109 -0
- package/tests/unit/registry-signature-policy.test.js +100 -0
- package/tests/unit/registry-signing.test.js +193 -0
- package/tests/unit/registry-trust-store.test.js +133 -0
- package/tests/unit/registry-url-validation.test.js +64 -0
- package/tests/unit/yaml.test.js +92 -0
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
---
|
|
2
|
+
id: proposal-20260611-000000
|
|
3
|
+
created_at: 2026-06-11T00:00:00Z
|
|
4
|
+
title: Approved Create File Proposal
|
|
5
|
+
problem: Missing custom templates in tests.
|
|
6
|
+
evidence: Directory is empty.
|
|
7
|
+
risk_level: low
|
|
8
|
+
affected_files:
|
|
9
|
+
- tests/fixtures/custom-template-example/new-file.md
|
|
10
|
+
suggested_change: Create a new markdown file.
|
|
11
|
+
verify_command: npm run verify
|
|
12
|
+
rollback_plan: rm tests/fixtures/custom-template-example/new-file.md
|
|
13
|
+
approval_status: approved
|
|
14
|
+
---
|
|
15
|
+
|
|
16
|
+
# Approved Create File Proposal
|
|
17
|
+
|
|
18
|
+
```json
|
|
19
|
+
{
|
|
20
|
+
"operations": [
|
|
21
|
+
{
|
|
22
|
+
"type": "create_file",
|
|
23
|
+
"path": "tests/fixtures/custom-template-example/new-file.md",
|
|
24
|
+
"content": "new file content\n",
|
|
25
|
+
"overwrite": true
|
|
26
|
+
}
|
|
27
|
+
]
|
|
28
|
+
}
|
|
29
|
+
```
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
---
|
|
2
|
+
id: proposal-20260611-000002
|
|
3
|
+
created_at: 2026-06-11T00:00:02Z
|
|
4
|
+
title: Approved Replace Text Proposal
|
|
5
|
+
problem: Test replace text behavior.
|
|
6
|
+
evidence: N/A
|
|
7
|
+
risk_level: low
|
|
8
|
+
affected_files:
|
|
9
|
+
- tests/fixtures/custom-template-example/replace-target.md
|
|
10
|
+
suggested_change: Replace placeholder in the target.
|
|
11
|
+
verify_command: npm run verify
|
|
12
|
+
rollback_plan: git checkout -- tests/fixtures/custom-template-example/replace-target.md
|
|
13
|
+
approval_status: approved
|
|
14
|
+
---
|
|
15
|
+
|
|
16
|
+
# Approved Replace Text Proposal
|
|
17
|
+
|
|
18
|
+
```json
|
|
19
|
+
{
|
|
20
|
+
"operations": [
|
|
21
|
+
{
|
|
22
|
+
"type": "replace_text",
|
|
23
|
+
"path": "tests/fixtures/custom-template-example/replace-target.md",
|
|
24
|
+
"find": "placeholder",
|
|
25
|
+
"replace": "replaced content",
|
|
26
|
+
"allow_multiple": false
|
|
27
|
+
}
|
|
28
|
+
]
|
|
29
|
+
}
|
|
30
|
+
```
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
---
|
|
2
|
+
id: proposal-20260611-000004
|
|
3
|
+
created_at: 2026-06-11T00:00:04Z
|
|
4
|
+
title: Existing Create File No Overwrite Proposal
|
|
5
|
+
problem: Test existing file without overwrite flag.
|
|
6
|
+
evidence: N/A
|
|
7
|
+
risk_level: low
|
|
8
|
+
affected_files:
|
|
9
|
+
- tests/fixtures/custom-template-example/existing.md
|
|
10
|
+
suggested_change: Create file without overwrite flag.
|
|
11
|
+
verify_command: npm run verify
|
|
12
|
+
rollback_plan: N/A
|
|
13
|
+
approval_status: approved
|
|
14
|
+
---
|
|
15
|
+
|
|
16
|
+
# Existing Create File No Overwrite Proposal
|
|
17
|
+
|
|
18
|
+
```json
|
|
19
|
+
{
|
|
20
|
+
"operations": [
|
|
21
|
+
{
|
|
22
|
+
"type": "create_file",
|
|
23
|
+
"path": "tests/fixtures/custom-template-example/existing.md",
|
|
24
|
+
"content": "new content\n",
|
|
25
|
+
"overwrite": false
|
|
26
|
+
}
|
|
27
|
+
]
|
|
28
|
+
}
|
|
29
|
+
```
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
---
|
|
2
|
+
id: proposal-20260611-333333
|
|
3
|
+
created_at: 2026-06-11T33:33:33Z
|
|
4
|
+
title: No Operations Proposal
|
|
5
|
+
problem: Vague proposal without machine readable block.
|
|
6
|
+
evidence: N/A
|
|
7
|
+
risk_level: low
|
|
8
|
+
affected_files:
|
|
9
|
+
- README.md
|
|
10
|
+
suggested_change: Edit README.md manually.
|
|
11
|
+
verify_command: npm run verify
|
|
12
|
+
rollback_plan: git checkout -- README.md
|
|
13
|
+
approval_status: approved
|
|
14
|
+
---
|
|
15
|
+
|
|
16
|
+
# No Operations Proposal
|
|
17
|
+
|
|
18
|
+
Please edit the README.md to describe the codebase in more detail.
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
---
|
|
2
|
+
id: proposal-20260611-000003
|
|
3
|
+
created_at: 2026-06-11T00:00:03Z
|
|
4
|
+
title: Path Traversal Proposal
|
|
5
|
+
problem: Test directory traversal boundaries check.
|
|
6
|
+
evidence: N/A
|
|
7
|
+
risk_level: low
|
|
8
|
+
affected_files:
|
|
9
|
+
- ../outside.md
|
|
10
|
+
suggested_change: Modify file outside root.
|
|
11
|
+
verify_command: npm run verify
|
|
12
|
+
rollback_plan: N/A
|
|
13
|
+
approval_status: approved
|
|
14
|
+
---
|
|
15
|
+
|
|
16
|
+
# Path Traversal Proposal
|
|
17
|
+
|
|
18
|
+
```json
|
|
19
|
+
{
|
|
20
|
+
"operations": [
|
|
21
|
+
{
|
|
22
|
+
"type": "create_file",
|
|
23
|
+
"path": "../outside-file.md",
|
|
24
|
+
"content": "outside content\n",
|
|
25
|
+
"overwrite": true
|
|
26
|
+
}
|
|
27
|
+
]
|
|
28
|
+
}
|
|
29
|
+
```
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
---
|
|
2
|
+
id: proposal-20260611-111111
|
|
3
|
+
created_at: 2026-06-11T11:11:11Z
|
|
4
|
+
title: Pending Proposal
|
|
5
|
+
problem: Test pending proposal.
|
|
6
|
+
evidence: N/A
|
|
7
|
+
risk_level: low
|
|
8
|
+
affected_files:
|
|
9
|
+
- tests/fixtures/custom-template-example/pending.md
|
|
10
|
+
suggested_change: Create pending file.
|
|
11
|
+
verify_command: npm run verify
|
|
12
|
+
rollback_plan: rm tests/fixtures/custom-template-example/pending.md
|
|
13
|
+
approval_status: pending
|
|
14
|
+
---
|
|
15
|
+
|
|
16
|
+
# Pending Proposal
|
|
17
|
+
|
|
18
|
+
```json
|
|
19
|
+
{
|
|
20
|
+
"operations": [
|
|
21
|
+
{
|
|
22
|
+
"type": "create_file",
|
|
23
|
+
"path": "tests/fixtures/custom-template-example/pending.md",
|
|
24
|
+
"content": "pending\n",
|
|
25
|
+
"overwrite": true
|
|
26
|
+
}
|
|
27
|
+
]
|
|
28
|
+
}
|
|
29
|
+
```
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
---
|
|
2
|
+
id: proposal-20260611-222222
|
|
3
|
+
created_at: 2026-06-11T22:22:22Z
|
|
4
|
+
title: Protected Path Proposal
|
|
5
|
+
problem: Try to modify protected path.
|
|
6
|
+
evidence: N/A
|
|
7
|
+
risk_level: low
|
|
8
|
+
affected_files:
|
|
9
|
+
- .env
|
|
10
|
+
suggested_change: Modify .env file.
|
|
11
|
+
verify_command: npm run verify
|
|
12
|
+
rollback_plan: git checkout -- .env
|
|
13
|
+
approval_status: approved
|
|
14
|
+
---
|
|
15
|
+
|
|
16
|
+
# Protected Path Proposal
|
|
17
|
+
|
|
18
|
+
```json
|
|
19
|
+
{
|
|
20
|
+
"operations": [
|
|
21
|
+
{
|
|
22
|
+
"type": "create_file",
|
|
23
|
+
"path": ".env",
|
|
24
|
+
"content": "SECRET_KEY=stolen\n",
|
|
25
|
+
"overwrite": true
|
|
26
|
+
}
|
|
27
|
+
]
|
|
28
|
+
}
|
|
29
|
+
```
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
---
|
|
2
|
+
id: proposal-20260611-000005
|
|
3
|
+
created_at: 2026-06-11T00:00:05Z
|
|
4
|
+
title: Replace Multiple Without Allow Proposal
|
|
5
|
+
problem: Test replace text matching multiple times without allow_multiple.
|
|
6
|
+
evidence: N/A
|
|
7
|
+
risk_level: low
|
|
8
|
+
affected_files:
|
|
9
|
+
- tests/fixtures/custom-template-example/multiple.md
|
|
10
|
+
suggested_change: Replace multiple occurrences without allow_multiple flag.
|
|
11
|
+
verify_command: npm run verify
|
|
12
|
+
rollback_plan: N/A
|
|
13
|
+
approval_status: approved
|
|
14
|
+
---
|
|
15
|
+
|
|
16
|
+
# Replace Multiple Without Allow Proposal
|
|
17
|
+
|
|
18
|
+
```json
|
|
19
|
+
{
|
|
20
|
+
"operations": [
|
|
21
|
+
{
|
|
22
|
+
"type": "replace_text",
|
|
23
|
+
"path": "tests/fixtures/custom-template-example/multiple.md",
|
|
24
|
+
"find": "target",
|
|
25
|
+
"replace": "replaced",
|
|
26
|
+
"allow_multiple": false
|
|
27
|
+
}
|
|
28
|
+
]
|
|
29
|
+
}
|
|
30
|
+
```
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
# Registry Overrides Test Fixture Guide
|
|
2
|
+
|
|
3
|
+
This fixture folder demonstrates and validates overriding the default model/adapter/template registries using custom YAML files and the `--registry` flag.
|
|
4
|
+
|
|
5
|
+
## Files Structure
|
|
6
|
+
|
|
7
|
+
For testing registry overrides, you can specify custom templates/adapters files:
|
|
8
|
+
|
|
9
|
+
* `custom-templates.yaml`: Defines mock template profiles.
|
|
10
|
+
* `custom-adapters.yaml`: Defines mock adapters setup.
|
|
11
|
+
|
|
12
|
+
## Usage in Testing
|
|
13
|
+
|
|
14
|
+
```bash
|
|
15
|
+
# List templates from the custom templates fixture file
|
|
16
|
+
node bin/multimodel-dev-os.js templates --registry tests/fixtures/registry-overrides/custom-templates.yaml
|
|
17
|
+
|
|
18
|
+
# Run validations on a custom template entry
|
|
19
|
+
node bin/multimodel-dev-os.js validate-template my-mock-template --registry tests/fixtures/registry-overrides/custom-templates.yaml
|
|
20
|
+
```
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
registry_name: "test-fixture-registry"
|
|
2
|
+
publisher: "Mock Valid Publisher"
|
|
3
|
+
version: "1.0.0"
|
|
4
|
+
generated_at: "2026-06-20T12:00:00Z"
|
|
5
|
+
minimum_mmdo_version: "3.2.0"
|
|
6
|
+
safety_policy_version: "1.0.0"
|
|
7
|
+
catalog_hash: "sha256:289e7ff82e8037b27471990d4106f76e7f8e44e26aeb049787c185a30249420e"
|
|
8
|
+
files_hashes:
|
|
9
|
+
catalog.yaml: "sha256:289e7ff82e8037b27471990d4106f76e7f8e44e26aeb049787c185a30249420e"
|
|
10
|
+
signature:
|
|
11
|
+
algorithm: "ed25519"
|
|
12
|
+
key_id: "test-key-revoked"
|
|
13
|
+
signature: "LyuSIQ72kyOuNVqie1P/vZVi2PqFsCS//Q6/mAstTG1zDQvGPd5HwH8t1FPEOtP7Sbb31udJ7NJY3cdCWpACCg=="
|
|
14
|
+
signed_fields: ["registry_name", "publisher", "version", "generated_at", "catalog_hash", "minimum_mmdo_version", "safety_policy_version"]
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
registry_name: "test-fixture-registry"
|
|
2
|
+
publisher: "Mock Valid Publisher"
|
|
3
|
+
version: "1.0.0"
|
|
4
|
+
generated_at: "2026-06-20T12:00:00Z"
|
|
5
|
+
minimum_mmdo_version: "3.2.0"
|
|
6
|
+
safety_policy_version: "1.0.0"
|
|
7
|
+
catalog_hash: "sha256:289e7ff82e8037b27471990d4106f76e7f8e44e26aeb049787c185a30249420e"
|
|
8
|
+
files_hashes:
|
|
9
|
+
catalog.yaml: "sha256:289e7ff82e8037b27471990d4106f76e7f8e44e26aeb049787c185a30249420e"
|
|
10
|
+
signature:
|
|
11
|
+
algorithm: "ed25519"
|
|
12
|
+
key_id: "test-key-valid"
|
|
13
|
+
signature: "MCowBQYDK2VwAyEA9vWwyE5+fY0dvEzl9S1UcvtoMkOAIDhDCzZAkP+CVNo="
|
|
14
|
+
signed_fields: ["registry_name", "publisher", "version", "generated_at", "catalog_hash", "minimum_mmdo_version", "safety_policy_version"]
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
|
|
2
|
+
# MultiModel Dev OS Test-Only Trusted Keys Fixture
|
|
3
|
+
# WARNING: These keys are for unit testing and E2E verification validation only.
|
|
4
|
+
# DO NOT USE IN PRODUCTION.
|
|
5
|
+
trusted_publishers:
|
|
6
|
+
- key_id: test-key-valid
|
|
7
|
+
name: "Mock Valid Publisher"
|
|
8
|
+
algorithm: ed25519
|
|
9
|
+
public_key: "MCowBQYDK2VwAyEAU1uu8t1/5aXZRvNDNkKpBRJ2SgkKJtlX6pMmuF90tx8="
|
|
10
|
+
scopes: ["registry"]
|
|
11
|
+
status: "active"
|
|
12
|
+
- key_id: test-key-revoked
|
|
13
|
+
name: "Mock Revoked Publisher"
|
|
14
|
+
algorithm: ed25519
|
|
15
|
+
public_key: "MCowBQYDK2VwAyEAvcxhJeWu5VSDxdAva1GwtzgWspYFJ9U1TRJ1a7bUyRA="
|
|
16
|
+
scopes: ["registry"]
|
|
17
|
+
status: "revoked"
|
|
18
|
+
- key_id: test-key-disabled
|
|
19
|
+
name: "Mock Disabled Publisher"
|
|
20
|
+
algorithm: ed25519
|
|
21
|
+
public_key: "MCowBQYDK2VwAyEA9uWCD3w/zkUoQ9RTpQUZ52sib3ctl3fre1PZZowVmvg="
|
|
22
|
+
scopes: ["registry"]
|
|
23
|
+
status: "disabled"
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
registry_name: "test-fixture-registry"
|
|
2
|
+
publisher: "Mock Valid Publisher"
|
|
3
|
+
version: "1.0.0"
|
|
4
|
+
generated_at: "2026-06-20T12:00:00Z"
|
|
5
|
+
minimum_mmdo_version: "3.2.0"
|
|
6
|
+
safety_policy_version: "1.0.0"
|
|
7
|
+
catalog_hash: "sha256:289e7ff82e8037b27471990d4106f76e7f8e44e26aeb049787c185a30249420e"
|
|
8
|
+
files_hashes:
|
|
9
|
+
catalog.yaml: "sha256:289e7ff82e8037b27471990d4106f76e7f8e44e26aeb049787c185a30249420e"
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
registry_name: "test-fixture-registry"
|
|
2
|
+
publisher: "Mock Valid Publisher"
|
|
3
|
+
version: "1.0.0"
|
|
4
|
+
generated_at: "2026-06-20T12:00:00Z"
|
|
5
|
+
minimum_mmdo_version: "3.2.0"
|
|
6
|
+
safety_policy_version: "1.0.0"
|
|
7
|
+
catalog_hash: "sha256:289e7ff82e8037b27471990d4106f76e7f8e44e26aeb049787c185a30249420e"
|
|
8
|
+
files_hashes:
|
|
9
|
+
catalog.yaml: "sha256:289e7ff82e8037b27471990d4106f76e7f8e44e26aeb049787c185a30249420e"
|
|
10
|
+
signature:
|
|
11
|
+
algorithm: "rsa-sha256"
|
|
12
|
+
key_id: "test-key-valid"
|
|
13
|
+
signature: "mock-sig"
|
|
14
|
+
signed_fields: ["registry_name", "publisher", "version", "generated_at", "catalog_hash", "minimum_mmdo_version", "safety_policy_version"]
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
registry_name: "test-fixture-registry"
|
|
2
|
+
publisher: "Mock Valid Publisher"
|
|
3
|
+
version: "1.0.0"
|
|
4
|
+
generated_at: "2026-06-20T12:00:00Z"
|
|
5
|
+
minimum_mmdo_version: "3.2.0"
|
|
6
|
+
safety_policy_version: "1.0.0"
|
|
7
|
+
catalog_hash: "sha256:289e7ff82e8037b27471990d4106f76e7f8e44e26aeb049787c185a30249420e"
|
|
8
|
+
files_hashes:
|
|
9
|
+
catalog.yaml: "sha256:289e7ff82e8037b27471990d4106f76e7f8e44e26aeb049787c185a30249420e"
|
|
10
|
+
signature:
|
|
11
|
+
algorithm: "ed25519"
|
|
12
|
+
key_id: "test-key-valid"
|
|
13
|
+
signature: "CjhqbNnmTh9wzBTBRlnkaCjZDTvxj8rImdtwOQIBOu0I+kCd8ec3x/jcNvJesy/Fqd2yl70vToqKAiVxD0GVAQ=="
|
|
14
|
+
signed_fields: ["registry_name", "publisher", "version", "generated_at", "catalog_hash", "minimum_mmdo_version", "safety_policy_version"]
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
registry_name: "test-fixture-registry"
|
|
2
|
+
publisher: "Mock Valid Publisher"
|
|
3
|
+
version: "1.0.0"
|
|
4
|
+
generated_at: "2026-06-20T12:00:00Z"
|
|
5
|
+
minimum_mmdo_version: "3.2.0"
|
|
6
|
+
safety_policy_version: "1.0.0"
|
|
7
|
+
catalog_hash: "sha256:289e7ff82e8037b27471990d4106f76e7f8e44e26aeb049787c185a30249420e"
|
|
8
|
+
files_hashes:
|
|
9
|
+
catalog.yaml: "sha256:289e7ff82e8037b27471990d4106f76e7f8e44e26aeb049787c185a30249420e"
|
|
10
|
+
signature:
|
|
11
|
+
algorithm: "ed25519"
|
|
12
|
+
key_id: "test-key-wrong"
|
|
13
|
+
signature: "XChca7Nje+hM8QPfHHL8iwVjW8mL9wOeNhZBOo0hloV5CHhaFH9qm0k5e2xvFLMLOc3j+8CMCqJozPdWgiqxBw=="
|
|
14
|
+
signed_fields: ["registry_name", "publisher", "version", "generated_at", "catalog_hash", "minimum_mmdo_version", "safety_policy_version"]
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
# Smoke Tests Playbook
|
|
2
|
+
|
|
3
|
+
Follow these quick manual verification commands to assert the functional integrity of `multimodel-dev-os` before pushing tag releases.
|
|
4
|
+
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## 1. Quick Verification Pipeline
|
|
8
|
+
|
|
9
|
+
Run the following actions sequentially inside a target sandbox:
|
|
10
|
+
|
|
11
|
+
```bash
|
|
12
|
+
# 1. Inspect version parameters and command menu
|
|
13
|
+
node bin/multimodel-dev-os.js --help
|
|
14
|
+
|
|
15
|
+
# 2. View all built-in configurations
|
|
16
|
+
node bin/multimodel-dev-os.js templates
|
|
17
|
+
|
|
18
|
+
# 3. Inspect a specific template's specifications
|
|
19
|
+
node bin/multimodel-dev-os.js show-template nextjs-saas
|
|
20
|
+
|
|
21
|
+
# 4. Dry-run scaffold inside the current repository
|
|
22
|
+
node bin/multimodel-dev-os.js init --template general-app --dry-run
|
|
23
|
+
|
|
24
|
+
# 5. Execute strict validation lints
|
|
25
|
+
node bin/multimodel-dev-os.js validate --target .
|
|
26
|
+
|
|
27
|
+
# 6. Execute diagnostic doctor checkups
|
|
28
|
+
node bin/multimodel-dev-os.js doctor --target .
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
---
|
|
32
|
+
|
|
33
|
+
## 2. Dry-Run Verification
|
|
34
|
+
|
|
35
|
+
Assert that no physical files are created when running the `--dry-run` parameter:
|
|
36
|
+
- Verify stdout logs indicate `[DRY-RUN] Would create...`
|
|
37
|
+
- Confirm `git status` reports zero untracked files or modifications.
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
# CLI Smoke Test Specification
|
|
2
|
+
|
|
3
|
+
This document lists the essential CLI commands to execute when verifying the MultiModel Dev OS installation stability.
|
|
4
|
+
|
|
5
|
+
## Sanity Commands
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
# Verify help output
|
|
9
|
+
node bin/multimodel-dev-os.js --help
|
|
10
|
+
|
|
11
|
+
# List templates
|
|
12
|
+
node bin/multimodel-dev-os.js templates
|
|
13
|
+
node bin/multimodel-dev-os.js templates --json
|
|
14
|
+
|
|
15
|
+
# List models and adapters
|
|
16
|
+
node bin/multimodel-dev-os.js models --json
|
|
17
|
+
node bin/multimodel-dev-os.js adapters --json
|
|
18
|
+
|
|
19
|
+
# List active skills
|
|
20
|
+
node bin/multimodel-dev-os.js skills
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
## Validation & Doctor Commands
|
|
24
|
+
|
|
25
|
+
```bash
|
|
26
|
+
# Validate built-in resources
|
|
27
|
+
node bin/multimodel-dev-os.js validate-template nextjs-saas
|
|
28
|
+
node bin/multimodel-dev-os.js validate-adapter cursor
|
|
29
|
+
node bin/multimodel-dev-os.js validate-skill custom-skill.example
|
|
30
|
+
|
|
31
|
+
# Validate all registry entries
|
|
32
|
+
node bin/multimodel-dev-os.js validate --all-registries
|
|
33
|
+
|
|
34
|
+
# Release advisory checkup
|
|
35
|
+
node bin/multimodel-dev-os.js doctor --release
|
|
36
|
+
|
|
37
|
+
# Token budget checks
|
|
38
|
+
node bin/multimodel-dev-os.js doctor --tokens --threshold 100KB
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
## Initialization Dry-Runs
|
|
42
|
+
|
|
43
|
+
```bash
|
|
44
|
+
# Dry-run general app init
|
|
45
|
+
node bin/multimodel-dev-os.js init --dry-run --template general-app
|
|
46
|
+
|
|
47
|
+
# Dry-run with adapter injections
|
|
48
|
+
node bin/multimodel-dev-os.js init --dry-run --template nextjs-saas -a cursor -a claude
|
|
49
|
+
```
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { describe, it, expect } from 'vitest';
|
|
2
|
+
import { existsSync, readFileSync } from 'fs';
|
|
3
|
+
import { join } from 'path';
|
|
4
|
+
import { execSync } from 'child_process';
|
|
5
|
+
|
|
6
|
+
describe('Build Output Verification', () => {
|
|
7
|
+
const buildPath = join(process.cwd(), 'bin', 'multimodel-dev-os.js');
|
|
8
|
+
|
|
9
|
+
it('should compile the output file successfully', () => {
|
|
10
|
+
expect(existsSync(buildPath)).toBe(true);
|
|
11
|
+
});
|
|
12
|
+
|
|
13
|
+
it('should contain exactly one shebang at the top', () => {
|
|
14
|
+
const content = readFileSync(buildPath, 'utf8');
|
|
15
|
+
const totalShebangs = (content.match(/#!/g) || []).length;
|
|
16
|
+
|
|
17
|
+
expect(content.startsWith('#!/usr/bin/env node')).toBe(true);
|
|
18
|
+
expect(totalShebangs).toBe(1);
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
it('should contain the generation warning header', () => {
|
|
22
|
+
const content = readFileSync(buildPath, 'utf8');
|
|
23
|
+
expect(content).toContain('// Generated from src/. Do not edit directly.');
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
it('should not contain dangerous registry URL interpolation', () => {
|
|
27
|
+
const content = readFileSync(buildPath, 'utf8');
|
|
28
|
+
|
|
29
|
+
const hasUnsafeSync = content.includes("mod.get('${targetUrl}'") || (content.includes('execSync(`node -e "') && content.includes('${targetUrl}'));
|
|
30
|
+
expect(hasUnsafeSync).toBe(false);
|
|
31
|
+
|
|
32
|
+
expect(content).toContain('execFileSync(process.execPath');
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
it('should be completely fresh and match the source modules', () => {
|
|
36
|
+
expect(() => {
|
|
37
|
+
execSync('node scripts/check-build-fresh.js', { stdio: 'ignore' });
|
|
38
|
+
}).not.toThrow();
|
|
39
|
+
});
|
|
40
|
+
});
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import { describe, it, expect, beforeAll, afterAll } from 'vitest';
|
|
2
|
+
import { loadCatalogFromSource, loadCatalog } from '../../src/catalog/loader.js';
|
|
3
|
+
import { existsSync, mkdirSync, writeFileSync, rmSync } from 'fs';
|
|
4
|
+
import { join } from 'path';
|
|
5
|
+
|
|
6
|
+
describe('Catalog Loader', () => {
|
|
7
|
+
const tempDir = join(process.cwd(), 'temp-catalog-test');
|
|
8
|
+
const localCatalogDir = join(tempDir, '.ai', 'plugins');
|
|
9
|
+
const localCatalogFile = join(localCatalogDir, 'catalog.yaml');
|
|
10
|
+
|
|
11
|
+
beforeAll(() => {
|
|
12
|
+
mkdirSync(localCatalogDir, { recursive: true });
|
|
13
|
+
});
|
|
14
|
+
|
|
15
|
+
afterAll(() => {
|
|
16
|
+
if (existsSync(tempDir)) {
|
|
17
|
+
rmSync(tempDir, { recursive: true, force: true });
|
|
18
|
+
}
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
it('should load bundled catalog from fallback or global if no target is specified', () => {
|
|
22
|
+
const catalog = loadCatalog();
|
|
23
|
+
expect(catalog).toBeDefined();
|
|
24
|
+
expect(Array.isArray(catalog.plugins)).toBe(true);
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
it('should load local catalog when source is "local"', () => {
|
|
28
|
+
const localYaml = `
|
|
29
|
+
catalog:
|
|
30
|
+
plugins:
|
|
31
|
+
- name: local-plugin
|
|
32
|
+
slug: local-slug
|
|
33
|
+
version: 1.0.0
|
|
34
|
+
description: local test
|
|
35
|
+
author: test
|
|
36
|
+
`;
|
|
37
|
+
writeFileSync(localCatalogFile, localYaml, 'utf8');
|
|
38
|
+
|
|
39
|
+
const result = loadCatalogFromSource('local', { target: tempDir });
|
|
40
|
+
expect(result.plugins).toHaveLength(1);
|
|
41
|
+
expect(result.plugins[0].name).toBe('local-plugin');
|
|
42
|
+
expect(result.plugins[0]._source).toBe('local');
|
|
43
|
+
});
|
|
44
|
+
});
|