@event4u/agent-config 17.3.0 → 17.4.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/.augment-plugin/marketplace.json +3 -3
- package/.augment-plugin/plugin.json +1 -1
- package/.claude-plugin/marketplace.json +1 -1
- package/CHANGELOG.md +164 -342
- package/CREDITS.md +2 -2
- package/README.md +1 -1
- package/dist/agent-src/skills/code-intelligence/SKILL.md +3 -0
- package/dist/agent-src/templates/agents/agent-project-settings.example.yml +1 -1
- package/dist/cli/registry.js +2 -2
- package/dist/cli-delegate/{chunk-DHSSP7V3.js → chunk-5AYJGUA3.js} +0 -2
- package/dist/cli-delegate/chunk-DIFZBP4G.js +1336 -0
- package/dist/cli-delegate/{chunk-MT2WOOCY.js → chunk-WY5ZZPGL.js} +2 -2
- package/dist/cli-delegate/{chunk-ORJPBER2.js → chunk-X6LZ7W5A.js} +73 -10
- package/dist/cli-delegate/cmd_conformance.js +175 -183
- package/dist/cli-delegate/cmd_doctor.js +5 -5
- package/dist/cli-delegate/cmd_doctor_neighbours.js +3 -3
- package/dist/cli-delegate/cmd_explain.js +1 -1
- package/dist/cli-delegate/cmd_handoff.js +2 -2
- package/dist/cli-delegate/cmd_memory_get.js +1 -1
- package/dist/cli-delegate/cmd_route_audit.js +1 -1
- package/dist/cli-delegate/cmd_session_recycle.js +2 -2
- package/dist/cli-delegate/cmd_update.js +24 -13
- package/dist/cli-delegate/cmd_upgrade.js +2 -2
- package/dist/cli-delegate/dist-25AZAHOP.js +3240 -0
- package/dist/cli-delegate/dist-P7MFPMF3.js +411 -0
- package/dist/cli-delegate/dist-XAS5ZLQB.js +6 -0
- package/dist/cli-delegate/{upgrade_unattended-VVL6JLSX.js → upgrade_unattended-QCWOKTUN.js} +178 -163
- package/dist/discovery/deprecation-report.md +1 -1
- package/dist/discovery/discovery-manifest.json +3 -3
- package/dist/discovery/discovery-manifest.json.sha256 +1 -1
- package/dist/discovery/discovery-manifest.summary.md +1 -1
- package/dist/discovery/dormancy-report.md +1 -1
- package/dist/discovery/orphan-report.md +1 -1
- package/dist/discovery/packs.json +2 -2
- package/dist/discovery/trust-report.md +1 -1
- package/dist/discovery/workspaces.json +2 -2
- package/dist/hooks/code_graph_refresh.chunk.cjs +56 -27
- package/dist/hooks/code_graph_refresh.chunk.cjs.sha256 +1 -1
- package/dist/hooks/dispatch.js +982 -929
- package/dist/hooks/dispatch.sha256 +1 -1
- package/dist/hooks/egress_engine.chunk.cjs +56 -6
- package/dist/hooks/egress_engine.chunk.cjs.sha256 +1 -1
- package/dist/hooks/update_notice.chunk.cjs +914 -0
- package/dist/hooks/update_notice.chunk.cjs.sha256 +1 -0
- package/dist/mcp/registry-manifest.json +1 -1
- package/dist/mcp/server.json +2 -2
- package/dist/mcp/server.mjs +71259 -66437
- package/dist/scripts/condense.js +9 -6
- package/package.json +4 -1
- package/src/config/evaluator-budgets.json +9 -8
- package/src/config/gate-violation-baselines.json +2 -2
- package/src/config/hook-bundle-budget.json +31 -6
- package/src/config/packed-binary-manifest.json +9 -0
- package/src/config/sigstore/npm_keys.json +50 -0
- package/src/config/sigstore/trust-material.json +9 -0
- package/src/config/sigstore/trusted_root.json +126 -0
- package/src/config/skill-body-budget.json +7 -1
- package/src/scripts/_cli/cmd_update.ts +48 -20
- package/src/scripts/_cli/cmd_upgrade.ts +7 -1
- package/src/scripts/_cli/upgrade_unattended.ts +90 -76
- package/src/scripts/_dispatch.bash +3 -2
- package/src/scripts/_lib/confusables.ts +0 -19
- package/src/scripts/_lib/egress_normalize.ts +53 -23
- package/src/scripts/_lib/egress_text.ts +5 -3
- package/src/scripts/_lib/egress_token.ts +7 -5
- package/src/scripts/_lib/gate_collector_experimental.ts +633 -0
- package/src/scripts/_lib/git_common_dir.ts +10 -0
- package/src/scripts/_lib/graph_feeder_record.ts +3 -2
- package/src/scripts/_lib/install_reach_checks.ts +32 -2
- package/src/scripts/_lib/lineage_backfill.ts +136 -0
- package/src/scripts/_lib/lineage_ledger.ts +211 -0
- package/src/scripts/_lib/lineage_map.ts +55 -0
- package/src/scripts/_lib/lineage_query.ts +140 -0
- package/src/scripts/_lib/lineage_seed.ts +116 -0
- package/src/scripts/_lib/lineage_watch.ts +249 -0
- package/src/scripts/_lib/link_crypto.ts +8 -4
- package/src/scripts/_lib/production_reach.ts +3 -3
- package/src/scripts/_lib/project_registry.ts +227 -0
- package/src/scripts/_lib/provenance_verify.ts +14 -242
- package/src/scripts/_lib/sigstore_provenance.ts +177 -0
- package/src/scripts/_lib/source_id.ts +48 -0
- package/src/scripts/_lib/source_shape.ts +4 -0
- package/src/scripts/_lib/unattended_gates.ts +6 -1
- package/src/scripts/advisories.ts +26 -10
- package/src/scripts/ai_council/council_egress.ts +5 -6
- package/src/scripts/bench_graph_feeder_latency.ts +10 -3
- package/src/scripts/check_dependency_floors.ts +7 -0
- package/src/scripts/check_egress_mutations.ts +1 -0
- package/src/scripts/code_graph/build.ts +67 -3
- package/src/scripts/code_graph/cli.ts +131 -28
- package/src/scripts/code_graph/conventions_default.ts +20 -1
- package/src/scripts/code_graph/detect.ts +5 -5
- package/src/scripts/code_graph/estate_cli.ts +135 -0
- package/src/scripts/code_graph/estate_index.ts +394 -0
- package/src/scripts/code_graph/estate_manifests.ts +223 -0
- package/src/scripts/code_graph/estate_map.ts +141 -0
- package/src/scripts/code_graph/extract.ts +4 -1
- package/src/scripts/code_graph/extract_more.ts +137 -2
- package/src/scripts/code_graph/graph_budget.ts +158 -0
- package/src/scripts/code_graph/graph_home.ts +80 -0
- package/src/scripts/code_graph/lineage_cli.ts +315 -0
- package/src/scripts/code_graph/map.ts +22 -13
- package/src/scripts/code_graph/map_model.ts +67 -12
- package/src/scripts/code_graph/map_verify.ts +68 -5
- package/src/scripts/code_graph/map_viewer/viewer.css +12 -0
- package/src/scripts/code_graph/map_viewer/viewer.js +45 -15
- package/src/scripts/code_graph/project_ref.ts +25 -0
- package/src/scripts/code_graph/projects_cli.ts +254 -0
- package/src/scripts/code_graph/providers/catalog.ts +14 -5
- package/src/scripts/code_graph/providers/django.ts +52 -9
- package/src/scripts/code_graph/providers/index.ts +2 -0
- package/src/scripts/code_graph/providers/rails.ts +204 -0
- package/src/scripts/code_graph/providers/spring.ts +38 -1
- package/src/scripts/code_graph/query.ts +9 -9
- package/src/scripts/code_graph/refresh_lock.ts +12 -8
- package/src/scripts/code_graph/repo_identity.ts +169 -0
- package/src/scripts/code_graph/sqlite_store.ts +22 -25
- package/src/scripts/code_graph/types.ts +6 -2
- package/src/scripts/condense.ts +6 -6
- package/src/scripts/hook_chunks.mjs +7 -0
- package/src/scripts/hook_manifest.json +1 -1
- package/src/scripts/hook_manifest.yaml +20 -17
- package/src/scripts/hooks/code_graph_refresh_hook.ts +10 -7
- package/src/scripts/hooks/dispatch_hook.ts +6 -7
- package/src/scripts/hooks/host_semantics.ts +72 -0
- package/src/scripts/hooks/lazy_concern.ts +4 -0
- package/src/scripts/hooks/user_notice.ts +105 -0
- package/src/scripts/lint_harvest_provenance.ts +137 -11
- package/src/scripts/mcp_server/consumer_tool_catalog.json +69 -15
- package/src/scripts/mcp_server/graph_tools.ts +161 -41
- package/src/scripts/network_sources.ts +24 -6
- package/src/scripts/schemas/provenance-mechanism.schema.json +46 -0
- package/src/scripts/update_notice_hook.ts +75 -7
- package/src/vendor/grammars/README.md +7 -6
- package/src/vendor/grammars/tree-sitter-ruby.wasm +0 -0
- /package/dist/cli-delegate/{chunk-FKYZOWVD.js → chunk-QBAFP67O.js} +0 -0
- /package/dist/cli-delegate/{chunk-BMGUGSY6.js → chunk-TQG6UMPC.js} +0 -0
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
{
|
|
2
|
+
"keys": [
|
|
3
|
+
{
|
|
4
|
+
"keyId": "SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA",
|
|
5
|
+
"keyUsage": "npm:signatures",
|
|
6
|
+
"publicKey": {
|
|
7
|
+
"rawBytes": "MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE1Olb3zMAFFxXKHiIkQO5cJ3Yhl5i6UPp+IhuteBJbuHcA5UogKo0EWtlWwW6KSaKoTNEYL7JlCQiVnkhBktUgg==",
|
|
8
|
+
"keyDetails": "PKIX_ECDSA_P256_SHA_256",
|
|
9
|
+
"validFor": {
|
|
10
|
+
"start": "1999-01-01T00:00:00.000Z",
|
|
11
|
+
"end": "2025-01-29T00:00:00.000Z"
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
},
|
|
15
|
+
{
|
|
16
|
+
"keyId": "SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA",
|
|
17
|
+
"keyUsage": "npm:attestations",
|
|
18
|
+
"publicKey": {
|
|
19
|
+
"rawBytes": "MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE1Olb3zMAFFxXKHiIkQO5cJ3Yhl5i6UPp+IhuteBJbuHcA5UogKo0EWtlWwW6KSaKoTNEYL7JlCQiVnkhBktUgg==",
|
|
20
|
+
"keyDetails": "PKIX_ECDSA_P256_SHA_256",
|
|
21
|
+
"validFor": {
|
|
22
|
+
"start": "2022-12-01T00:00:00.000Z",
|
|
23
|
+
"end": "2025-01-29T00:00:00.000Z"
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
},
|
|
27
|
+
{
|
|
28
|
+
"keyId": "SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U",
|
|
29
|
+
"keyUsage": "npm:signatures",
|
|
30
|
+
"publicKey": {
|
|
31
|
+
"rawBytes": "MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEY6Ya7W++7aUPzvMTrezH6Ycx3c+HOKYCcNGybJZSCJq/fd7Qa8uuAKtdIkUQtQiEKERhAmE5lMMJhP8OkDOa2g==",
|
|
32
|
+
"keyDetails": "PKIX_ECDSA_P256_SHA_256",
|
|
33
|
+
"validFor": {
|
|
34
|
+
"start": "2025-01-13T00:00:00.000Z"
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
},
|
|
38
|
+
{
|
|
39
|
+
"keyId": "SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U",
|
|
40
|
+
"keyUsage": "npm:attestations",
|
|
41
|
+
"publicKey": {
|
|
42
|
+
"rawBytes": "MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEY6Ya7W++7aUPzvMTrezH6Ycx3c+HOKYCcNGybJZSCJq/fd7Qa8uuAKtdIkUQtQiEKERhAmE5lMMJhP8OkDOa2g==",
|
|
43
|
+
"keyDetails": "PKIX_ECDSA_P256_SHA_256",
|
|
44
|
+
"validFor": {
|
|
45
|
+
"start": "2025-01-13T00:00:00.000Z"
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
]
|
|
50
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
{
|
|
2
|
+
"_comment": "Trust material the unattended upgrade verifies release provenance against (road-to-an-update-that-installs-itself-only-when-it-is-safe D15). Pinned, never refreshed at run time: a candidate release can never choose the roots it is checked against. Read by src/scripts/_lib/sigstore_provenance.ts, which refuses when a file's sha256 differs from the one recorded here. Refresh by re-extracting both targets from a newer @sigstore/tuf seeds.json and updating source and digests together; until a release carries new material, an unattended run refuses anything signed under a rotated root and `agent-config upgrade` by hand still works.",
|
|
3
|
+
"source": "@sigstore/tuf@5.0.0 seeds.json, mirror https://tuf-repo-cdn.sigstore.dev, extracted 2026-10-24",
|
|
4
|
+
"files": {
|
|
5
|
+
"trusted_root.json": "6494e21ea73fa7ee769f85f57d5a3e6a08725eae1e38c755fc3517c9e6bc0b66",
|
|
6
|
+
"npm_keys.json": "160677eb6e1c7083c89b166b20f8fe4e837fb71181506aff1991b80b89184f7d"
|
|
7
|
+
},
|
|
8
|
+
"npm_keys_live_check": "Equal on 2026-10-24 to https://registry.npmjs.org/-/npm/v1/keys: SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA (expired 2025-01-29) and SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U (no expiry)."
|
|
9
|
+
}
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
{
|
|
2
|
+
"mediaType": "application/vnd.dev.sigstore.trustedroot+json;version=0.1",
|
|
3
|
+
"tlogs": [
|
|
4
|
+
{
|
|
5
|
+
"baseUrl": "https://rekor.sigstore.dev",
|
|
6
|
+
"hashAlgorithm": "SHA2_256",
|
|
7
|
+
"publicKey": {
|
|
8
|
+
"rawBytes": "MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE2G2Y+2tabdTV5BcGiBIx0a9fAFwrkBbmLSGtks4L3qX6yYY0zufBnhC8Ur/iy55GhWP/9A/bY2LhC30M9+RYtw==",
|
|
9
|
+
"keyDetails": "PKIX_ECDSA_P256_SHA_256",
|
|
10
|
+
"validFor": {
|
|
11
|
+
"start": "2021-01-12T11:53:27Z"
|
|
12
|
+
}
|
|
13
|
+
},
|
|
14
|
+
"logId": {
|
|
15
|
+
"keyId": "wNI9atQGlz+VWfO6LRygH4QUfY/8W4RFwiT5i5WRgB0="
|
|
16
|
+
}
|
|
17
|
+
},
|
|
18
|
+
{
|
|
19
|
+
"baseUrl": "https://log2025-1.rekor.sigstore.dev",
|
|
20
|
+
"hashAlgorithm": "SHA2_256",
|
|
21
|
+
"publicKey": {
|
|
22
|
+
"rawBytes": "MCowBQYDK2VwAyEAt8rlp1knGwjfbcXAYPYAkn0XiLz1x8O4t0YkEhie244=",
|
|
23
|
+
"keyDetails": "PKIX_ED25519",
|
|
24
|
+
"validFor": {
|
|
25
|
+
"start": "2025-09-23T00:00:00Z"
|
|
26
|
+
}
|
|
27
|
+
},
|
|
28
|
+
"logId": {
|
|
29
|
+
"keyId": "zxGZFVvd0FEmjR8WrFwMdcAJ9vtaY/QXf44Y1wUeP6A="
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
],
|
|
33
|
+
"certificateAuthorities": [
|
|
34
|
+
{
|
|
35
|
+
"subject": {
|
|
36
|
+
"organization": "sigstore.dev",
|
|
37
|
+
"commonName": "sigstore"
|
|
38
|
+
},
|
|
39
|
+
"uri": "https://fulcio.sigstore.dev",
|
|
40
|
+
"certChain": {
|
|
41
|
+
"certificates": [
|
|
42
|
+
{
|
|
43
|
+
"rawBytes": "MIIB+DCCAX6gAwIBAgITNVkDZoCiofPDsy7dfm6geLbuhzAKBggqhkjOPQQDAzAqMRUwEwYDVQQKEwxzaWdzdG9yZS5kZXYxETAPBgNVBAMTCHNpZ3N0b3JlMB4XDTIxMDMwNzAzMjAyOVoXDTMxMDIyMzAzMjAyOVowKjEVMBMGA1UEChMMc2lnc3RvcmUuZGV2MREwDwYDVQQDEwhzaWdzdG9yZTB2MBAGByqGSM49AgEGBSuBBAAiA2IABLSyA7Ii5k+pNO8ZEWY0ylemWDowOkNa3kL+GZE5Z5GWehL9/A9bRNA3RbrsZ5i0JcastaRL7Sp5fp/jD5dxqc/UdTVnlvS16an+2Yfswe/QuLolRUCrcOE2+2iA5+tzd6NmMGQwDgYDVR0PAQH/BAQDAgEGMBIGA1UdEwEB/wQIMAYBAf8CAQEwHQYDVR0OBBYEFMjFHQBBmiQpMlEk6w2uSu1KBtPsMB8GA1UdIwQYMBaAFMjFHQBBmiQpMlEk6w2uSu1KBtPsMAoGCCqGSM49BAMDA2gAMGUCMH8liWJfMui6vXXBhjDgY4MwslmN/TJxVe/83WrFomwmNf056y1X48F9c4m3a3ozXAIxAKjRay5/aj/jsKKGIkmQatjI8uupHr/+CxFvaJWmpYqNkLDGRU+9orzh5hI2RrcuaQ=="
|
|
44
|
+
}
|
|
45
|
+
]
|
|
46
|
+
},
|
|
47
|
+
"validFor": {
|
|
48
|
+
"start": "2021-03-07T03:20:29Z",
|
|
49
|
+
"end": "2022-12-31T23:59:59.999Z"
|
|
50
|
+
}
|
|
51
|
+
},
|
|
52
|
+
{
|
|
53
|
+
"subject": {
|
|
54
|
+
"organization": "sigstore.dev",
|
|
55
|
+
"commonName": "sigstore"
|
|
56
|
+
},
|
|
57
|
+
"uri": "https://fulcio.sigstore.dev",
|
|
58
|
+
"certChain": {
|
|
59
|
+
"certificates": [
|
|
60
|
+
{
|
|
61
|
+
"rawBytes": "MIICGjCCAaGgAwIBAgIUALnViVfnU0brJasmRkHrn/UnfaQwCgYIKoZIzj0EAwMwKjEVMBMGA1UEChMMc2lnc3RvcmUuZGV2MREwDwYDVQQDEwhzaWdzdG9yZTAeFw0yMjA0MTMyMDA2MTVaFw0zMTEwMDUxMzU2NThaMDcxFTATBgNVBAoTDHNpZ3N0b3JlLmRldjEeMBwGA1UEAxMVc2lnc3RvcmUtaW50ZXJtZWRpYXRlMHYwEAYHKoZIzj0CAQYFK4EEACIDYgAE8RVS/ysH+NOvuDZyPIZtilgUF9NlarYpAd9HP1vBBH1U5CV77LSS7s0ZiH4nE7Hv7ptS6LvvR/STk798LVgMzLlJ4HeIfF3tHSaexLcYpSASr1kS0N/RgBJz/9jWCiXno3sweTAOBgNVHQ8BAf8EBAMCAQYwEwYDVR0lBAwwCgYIKwYBBQUHAwMwEgYDVR0TAQH/BAgwBgEB/wIBADAdBgNVHQ4EFgQU39Ppz1YkEZb5qNjpKFWixi4YZD8wHwYDVR0jBBgwFoAUWMAeX5FFpWapesyQoZMi0CrFxfowCgYIKoZIzj0EAwMDZwAwZAIwPCsQK4DYiZYDPIaDi5HFKnfxXx6ASSVmERfsynYBiX2X6SJRnZU84/9DZdnFvvxmAjBOt6QpBlc4J/0DxvkTCqpclvziL6BCCPnjdlIB3Pu3BxsPmygUY7Ii2zbdCdliiow="
|
|
62
|
+
},
|
|
63
|
+
{
|
|
64
|
+
"rawBytes": "MIIB9zCCAXygAwIBAgIUALZNAPFdxHPwjeDloDwyYChAO/4wCgYIKoZIzj0EAwMwKjEVMBMGA1UEChMMc2lnc3RvcmUuZGV2MREwDwYDVQQDEwhzaWdzdG9yZTAeFw0yMTEwMDcxMzU2NTlaFw0zMTEwMDUxMzU2NThaMCoxFTATBgNVBAoTDHNpZ3N0b3JlLmRldjERMA8GA1UEAxMIc2lnc3RvcmUwdjAQBgcqhkjOPQIBBgUrgQQAIgNiAAT7XeFT4rb3PQGwS4IajtLk3/OlnpgangaBclYpsYBr5i+4ynB07ceb3LP0OIOZdxexX69c5iVuyJRQ+Hz05yi+UF3uBWAlHpiS5sh0+H2GHE7SXrk1EC5m1Tr19L9gg92jYzBhMA4GA1UdDwEB/wQEAwIBBjAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBRYwB5fkUWlZql6zJChkyLQKsXF+jAfBgNVHSMEGDAWgBRYwB5fkUWlZql6zJChkyLQKsXF+jAKBggqhkjOPQQDAwNpADBmAjEAj1nHeXZp+13NWBNa+EDsDP8G1WWg1tCMWP/WHPqpaVo0jhsweNFZgSs0eE7wYI4qAjEA2WB9ot98sIkoF3vZYdd3/VtWB5b9TNMea7Ix/stJ5TfcLLeABLE4BNJOsQ4vnBHJ"
|
|
65
|
+
}
|
|
66
|
+
]
|
|
67
|
+
},
|
|
68
|
+
"validFor": {
|
|
69
|
+
"start": "2022-04-13T20:06:15Z"
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
],
|
|
73
|
+
"ctlogs": [
|
|
74
|
+
{
|
|
75
|
+
"baseUrl": "https://ctfe.sigstore.dev/test",
|
|
76
|
+
"hashAlgorithm": "SHA2_256",
|
|
77
|
+
"publicKey": {
|
|
78
|
+
"rawBytes": "MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEbfwR+RJudXscgRBRpKX1XFDy3PyudDxz/SfnRi1fT8ekpfBd2O1uoz7jr3Z8nKzxA69EUQ+eFCFI3zeubPWU7w==",
|
|
79
|
+
"keyDetails": "PKIX_ECDSA_P256_SHA_256",
|
|
80
|
+
"validFor": {
|
|
81
|
+
"start": "2021-03-14T00:00:00Z",
|
|
82
|
+
"end": "2022-10-31T23:59:59.999Z"
|
|
83
|
+
}
|
|
84
|
+
},
|
|
85
|
+
"logId": {
|
|
86
|
+
"keyId": "CGCS8ChS/2hF0dFrJ4ScRWcYrBY9wzjSbea8IgY2b3I="
|
|
87
|
+
}
|
|
88
|
+
},
|
|
89
|
+
{
|
|
90
|
+
"baseUrl": "https://ctfe.sigstore.dev/2022",
|
|
91
|
+
"hashAlgorithm": "SHA2_256",
|
|
92
|
+
"publicKey": {
|
|
93
|
+
"rawBytes": "MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEiPSlFi0CmFTfEjCUqF9HuCEcYXNKAaYalIJmBZ8yyezPjTqhxrKBpMnaocVtLJBI1eM3uXnQzQGAJdJ4gs9Fyw==",
|
|
94
|
+
"keyDetails": "PKIX_ECDSA_P256_SHA_256",
|
|
95
|
+
"validFor": {
|
|
96
|
+
"start": "2022-10-20T00:00:00Z"
|
|
97
|
+
}
|
|
98
|
+
},
|
|
99
|
+
"logId": {
|
|
100
|
+
"keyId": "3T0wasbHETJjGR4cmWc3AqJKXrjePK3/h4pygC8p7o4="
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
],
|
|
104
|
+
"timestampAuthorities": [
|
|
105
|
+
{
|
|
106
|
+
"subject": {
|
|
107
|
+
"organization": "sigstore.dev",
|
|
108
|
+
"commonName": "sigstore-tsa-selfsigned"
|
|
109
|
+
},
|
|
110
|
+
"uri": "https://timestamp.sigstore.dev/api/v1/timestamp",
|
|
111
|
+
"certChain": {
|
|
112
|
+
"certificates": [
|
|
113
|
+
{
|
|
114
|
+
"rawBytes": "MIICEDCCAZagAwIBAgIUOhNULwyQYe68wUMvy4qOiyojiwwwCgYIKoZIzj0EAwMwOTEVMBMGA1UEChMMc2lnc3RvcmUuZGV2MSAwHgYDVQQDExdzaWdzdG9yZS10c2Etc2VsZnNpZ25lZDAeFw0yNTA0MDgwNjU5NDNaFw0zNTA0MDYwNjU5NDNaMC4xFTATBgNVBAoTDHNpZ3N0b3JlLmRldjEVMBMGA1UEAxMMc2lnc3RvcmUtdHNhMHYwEAYHKoZIzj0CAQYFK4EEACIDYgAE4ra2Z8hKNig2T9kFjCAToGG30jky+WQv3BzL+mKvh1SKNR/UwuwsfNCg4sryoYAd8E6isovVA3M4aoNdm9QDi50Z8nTEyvqgfDPtTIwXItfiW/AFf1V7uwkbkAoj0xxco2owaDAOBgNVHQ8BAf8EBAMCB4AwHQYDVR0OBBYEFIn9eUOHz9BlRsMCRscsc1t9tOsDMB8GA1UdIwQYMBaAFJjsAe9/u1H/1JUeb4qImFMHic6/MBYGA1UdJQEB/wQMMAoGCCsGAQUFBwMIMAoGCCqGSM49BAMDA2gAMGUCMDtpsV/6KaO0qyF/UMsX2aSUXKQFdoGTptQGc0ftq1csulHPGG6dsmyMNd3JB+G3EQIxAOajvBcjpJmKb4Nv+2Taoj8Uc5+b6ih6FXCCKraSqupe07zqswMcXJTe1cExvHvvlw=="
|
|
115
|
+
},
|
|
116
|
+
{
|
|
117
|
+
"rawBytes": "MIIB9zCCAXygAwIBAgIUV7f0GLDOoEzIh8LXSW80OJiUp14wCgYIKoZIzj0EAwMwOTEVMBMGA1UEChMMc2lnc3RvcmUuZGV2MSAwHgYDVQQDExdzaWdzdG9yZS10c2Etc2VsZnNpZ25lZDAeFw0yNTA0MDgwNjU5NDNaFw0zNTA0MDYwNjU5NDNaMDkxFTATBgNVBAoTDHNpZ3N0b3JlLmRldjEgMB4GA1UEAxMXc2lnc3RvcmUtdHNhLXNlbGZzaWduZWQwdjAQBgcqhkjOPQIBBgUrgQQAIgNiAAQUQNtfRT/ou3YATa6wB/kKTe70cfJwyRIBovMnt8RcJph/COE82uyS6FmppLLL1VBPGcPfpQPYJNXzWwi8icwhKQ6W/Qe2h3oebBb2FHpwNJDqo+TMaC/tdfkv/ElJB72jRTBDMA4GA1UdDwEB/wQEAwIBBjASBgNVHRMBAf8ECDAGAQH/AgEAMB0GA1UdDgQWBBSY7AHvf7tR/9SVHm+KiJhTB4nOvzAKBggqhkjOPQQDAwNpADBmAjEAwGEGrfGZR1cen1R8/DTVMI943LssZmJRtDp/i7SfGHmGRP6gRbuj9vOK3b67Z0QQAjEAuT2H673LQEaHTcyQSZrkp4mX7WwkmF+sVbkYY5mXN+RMH13KUEHHOqASaemYWK/E"
|
|
118
|
+
}
|
|
119
|
+
]
|
|
120
|
+
},
|
|
121
|
+
"validFor": {
|
|
122
|
+
"start": "2025-07-04T00:00:00Z"
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
]
|
|
126
|
+
}
|
|
@@ -38,7 +38,7 @@
|
|
|
38
38
|
"character-consistency": 8555,
|
|
39
39
|
"check-refs": 4188,
|
|
40
40
|
"churn-prevention": 8014,
|
|
41
|
-
"code-intelligence":
|
|
41
|
+
"code-intelligence": 17182,
|
|
42
42
|
"code-refactoring": 13717,
|
|
43
43
|
"code-review": 16261,
|
|
44
44
|
"command-routing": 6040,
|
|
@@ -687,6 +687,12 @@
|
|
|
687
687
|
"old": 16492,
|
|
688
688
|
"new": 16998,
|
|
689
689
|
"reason": "road-to-a-map-that-draws-itself (ADR-297): step 0 sends an orientation question to agents/map/MAP.md before any graph query, and the MCP table gains rows for graph_map_status and graph_area. Both are the skill's routing surface for the new map; trimming them would hide the tools the roadmap added."
|
|
690
|
+
},
|
|
691
|
+
{
|
|
692
|
+
"skill": "code-intelligence",
|
|
693
|
+
"old": 16998,
|
|
694
|
+
"new": 17182,
|
|
695
|
+
"reason": "road-to-a-graph-that-follows-you (ADR-299): the MCP table gains a row for graph_estate and one line naming the new project parameter on every other graph tool. The ceiling sat 3 bytes above the body, so documenting the tool at all needs a raise; the wording was compressed first, from 262 to 184 bytes."
|
|
690
696
|
}
|
|
691
697
|
]
|
|
692
698
|
}
|
|
@@ -21,6 +21,12 @@
|
|
|
21
21
|
* Downgrades are allowed; the pin is a project decision.
|
|
22
22
|
* - (no flag) — pin to the registry's `latest` tag.
|
|
23
23
|
*
|
|
24
|
+
* The global install moves first. Only the CLI follows the pin (`pin_resolver`
|
|
25
|
+
* re-execs through npx); hooks, rules and skills run from the global install. So
|
|
26
|
+
* when `installed.lock` records an older version than the target, this runs
|
|
27
|
+
* `agent-config upgrade` to exactly that version and writes the pin only once it
|
|
28
|
+
* succeeded — offline, where it cannot, the pin is refused instead.
|
|
29
|
+
*
|
|
24
30
|
* Write target: the **deepest** `.agent-settings.yml` in the project cascade
|
|
25
31
|
* that already carries the `agent_config_version` key. When no file carries
|
|
26
32
|
* it, the repo-root file is created/edited. Comments and key ordering are
|
|
@@ -405,6 +411,10 @@ interface MainOptions {
|
|
|
405
411
|
state_path?: string | null;
|
|
406
412
|
out?: OutSink;
|
|
407
413
|
err?: OutSink;
|
|
414
|
+
/** The version the global install recorded in `installed.lock`, or `null` when there is none. */
|
|
415
|
+
global_version?: () => string | null;
|
|
416
|
+
/** Install exactly this version globally; returns the exit code. */
|
|
417
|
+
upgrader?: (version: string, out: OutSink, err: OutSink) => Promise<number>;
|
|
408
418
|
}
|
|
409
419
|
|
|
410
420
|
interface Args {
|
|
@@ -526,6 +536,29 @@ export async function main(argv: string[] | null = null, options: MainOptions =
|
|
|
526
536
|
return 0;
|
|
527
537
|
}
|
|
528
538
|
|
|
539
|
+
// The pin only steers the CLI (`pin_resolver` re-execs through npx); hooks,
|
|
540
|
+
// rules and skills run from the global install. A pin ahead of it would
|
|
541
|
+
// leave the project naming one version while every session runs another,
|
|
542
|
+
// so the global install moves first, and the pin only once it has.
|
|
543
|
+
const global = (options.global_version ?? _global_install_version)();
|
|
544
|
+
if (global !== null && update_check._is_newer(latest, global)) {
|
|
545
|
+
if (offline) {
|
|
546
|
+
_print(
|
|
547
|
+
err,
|
|
548
|
+
`❌ agent-config: the global install is ${global} and cannot be upgraded to ${latest} offline; ` +
|
|
549
|
+
'the pin is unchanged. Run `agent-config update` again with registry access.',
|
|
550
|
+
);
|
|
551
|
+
return 1;
|
|
552
|
+
}
|
|
553
|
+
_print(out, `→ The global install is ${global}; installing ${latest} first so the pin matches what runs.`);
|
|
554
|
+
const upgrader = options.upgrader ?? _upgrade_global;
|
|
555
|
+
const rc = await upgrader(latest, out, err);
|
|
556
|
+
if (rc !== 0) {
|
|
557
|
+
_print(err, `❌ agent-config: the global upgrade to ${latest} failed (exit ${rc}); the pin is unchanged.`);
|
|
558
|
+
return 1;
|
|
559
|
+
}
|
|
560
|
+
}
|
|
561
|
+
|
|
529
562
|
const pin_file = _find_pin_file(cwd);
|
|
530
563
|
const changed = _write_pin(pin_file, latest);
|
|
531
564
|
const rel = _relativeToOr(pin_file, cwd);
|
|
@@ -542,7 +575,6 @@ export async function main(argv: string[] | null = null, options: MainOptions =
|
|
|
542
575
|
cache_warmer(latest);
|
|
543
576
|
}
|
|
544
577
|
_refresh_state(latest, latest, state_path);
|
|
545
|
-
_report_global_install(latest, out);
|
|
546
578
|
return 0;
|
|
547
579
|
}
|
|
548
580
|
|
|
@@ -556,28 +588,24 @@ function _relativeToOr(target: string, base: string): string {
|
|
|
556
588
|
}
|
|
557
589
|
|
|
558
590
|
/**
|
|
559
|
-
*
|
|
591
|
+
* The version the global install recorded, or `null` when there is no global
|
|
592
|
+
* install to keep in step with.
|
|
560
593
|
*
|
|
561
|
-
* Read-only by design: `installed.lock`
|
|
562
|
-
*
|
|
563
|
-
*
|
|
564
|
-
*
|
|
565
|
-
* `upgrade --check` reported "up to date" over the old binary.
|
|
594
|
+
* Read-only by design: `installed.lock` is written by the global install itself.
|
|
595
|
+
* This command once rewrote it to the pin, so the lock claimed a version no
|
|
596
|
+
* `npm install -g` had installed and `upgrade --check` reported "up to date"
|
|
597
|
+
* over the old binary.
|
|
566
598
|
*/
|
|
567
|
-
function
|
|
599
|
+
function _global_install_version(): string | null {
|
|
568
600
|
const existing = installed_lock.read_lockfile(installed_lock.lockfile_path());
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
out,
|
|
578
|
-
`ℹ️ The global install is ${_normalize(recorded)}; this pin does not change it. ` +
|
|
579
|
-
`Run \`agent-config upgrade\` to install ${version} globally.`,
|
|
580
|
-
);
|
|
601
|
+
const recorded = (existing as unknown as Record<string, unknown> | null)?.['agent_config_version'];
|
|
602
|
+
return typeof recorded === 'string' && recorded.trim() !== '' ? _normalize(recorded) : null;
|
|
603
|
+
}
|
|
604
|
+
|
|
605
|
+
/** `agent-config upgrade`, in process, to exactly `version`. */
|
|
606
|
+
async function _upgrade_global(version: string, out: OutSink, err: OutSink): Promise<number> {
|
|
607
|
+
const upgrade = await import('./cmd_upgrade.js');
|
|
608
|
+
return upgrade.main([], { version, out, err });
|
|
581
609
|
}
|
|
582
610
|
|
|
583
611
|
/** Read `version` from the package's own `package.json`. */
|
|
@@ -505,6 +505,12 @@ interface MainOptions {
|
|
|
505
505
|
project_root?: string | null;
|
|
506
506
|
out?: OutSink;
|
|
507
507
|
err?: OutSink;
|
|
508
|
+
/**
|
|
509
|
+
* Install this exact version instead of `latest`. Programmatic only — set by
|
|
510
|
+
* `agent-config update`, which must install the version it is about to pin;
|
|
511
|
+
* the command line keeps `update --to` as the one way to name a version.
|
|
512
|
+
*/
|
|
513
|
+
version?: string;
|
|
508
514
|
}
|
|
509
515
|
|
|
510
516
|
export async function main(argv: string[] | null = null, options: MainOptions = {}): Promise<number> {
|
|
@@ -549,7 +555,7 @@ export async function main(argv: string[] | null = null, options: MainOptions =
|
|
|
549
555
|
return 0;
|
|
550
556
|
}
|
|
551
557
|
|
|
552
|
-
const target = `${PACKAGE_NAME}
|
|
558
|
+
const target = `${PACKAGE_NAME}@${options.version ?? 'latest'}`;
|
|
553
559
|
const npm_cmd: string[] = ['npm', 'install', '-g', target];
|
|
554
560
|
// --no-ui: the setup wizard is an onboarding surface, not an upgrade
|
|
555
561
|
// step — its foreground server used to block the upgrade until Ctrl-C.
|
|
@@ -22,17 +22,14 @@
|
|
|
22
22
|
* registry, a live session) · `1` a step failed and the previous version was
|
|
23
23
|
* restored · `4` a step failed and the restore did not reach the previous version.
|
|
24
24
|
*/
|
|
25
|
+
import * as crypto from 'node:crypto';
|
|
25
26
|
import * as fs from 'node:fs';
|
|
27
|
+
import * as os from 'node:os';
|
|
26
28
|
import * as path from 'node:path';
|
|
27
29
|
import { spawnSync } from 'node:child_process';
|
|
28
30
|
import process from 'node:process';
|
|
29
31
|
|
|
30
|
-
import {
|
|
31
|
-
EXPECTED_WORKFLOW_REF,
|
|
32
|
-
attestation_workflow_ref,
|
|
33
|
-
provenance_matches_publish,
|
|
34
|
-
verify_attestation_signature,
|
|
35
|
-
} from '../_lib/provenance_verify.js';
|
|
32
|
+
import { type ReleaseToVerify, verify_release_provenance } from '../_lib/sigstore_provenance.js';
|
|
36
33
|
import { read_candidate, unattended_decision, type RegistryCandidate } from '../_lib/unattended_gates.js';
|
|
37
34
|
import type { InstallOrigin } from '../_lib/classify_update.js';
|
|
38
35
|
import * as installed_lock from '../_lib/installed_lock.js';
|
|
@@ -97,10 +94,13 @@ export interface StepResult {
|
|
|
97
94
|
|
|
98
95
|
export type Packument = { readonly ok: true; readonly doc: unknown } | { readonly ok: false; readonly reason: string };
|
|
99
96
|
|
|
97
|
+
/** A verified tarball on disk, the only thing the install step may install — or why there is none. */
|
|
98
|
+
export type Provenance = { readonly ok: true; readonly tarball: string } | { readonly ok: false; readonly reason: string };
|
|
99
|
+
|
|
100
100
|
export interface UnattendedDeps {
|
|
101
101
|
readonly fetchPackument: () => Promise<Packument>;
|
|
102
|
-
/**
|
|
103
|
-
readonly verifyProvenance: (candidate: RegistryCandidate) => Promise<
|
|
102
|
+
/** Download the candidate, verify its attestations against it, and hand back the verified file. */
|
|
103
|
+
readonly verifyProvenance: (candidate: RegistryCandidate) => Promise<Provenance>;
|
|
104
104
|
readonly run: (cmd: string[]) => StepResult;
|
|
105
105
|
/** The version recorded in `installed.lock`; `''` when unknown. */
|
|
106
106
|
readonly readInstalled: () => string;
|
|
@@ -243,9 +243,10 @@ function release_lock(deps: Pick<UnattendedDeps, 'lockPath' | 'pid'>): void {
|
|
|
243
243
|
|
|
244
244
|
// --- the run -----------------------------------------------------------------
|
|
245
245
|
|
|
246
|
-
|
|
246
|
+
/** `spec` is the verified tarball for an upgrade, and `<package>@<version>` only for a restore. */
|
|
247
|
+
function install_steps(spec: string, deps: UnattendedDeps): string[][] {
|
|
247
248
|
return [
|
|
248
|
-
['npm', 'install', '-g',
|
|
249
|
+
['npm', 'install', '-g', spec],
|
|
249
250
|
[deps.agentConfigBin, 'global', '--no-ui'],
|
|
250
251
|
...deps.globalSettings.map((target) => [deps.agentConfigBin, 'settings:sync', '--path', target]),
|
|
251
252
|
];
|
|
@@ -263,7 +264,7 @@ function run_steps(steps: string[][], deps: UnattendedDeps, honourSignal = true)
|
|
|
263
264
|
|
|
264
265
|
/** Restored means both the binary and `installed.lock` read the previous version again. */
|
|
265
266
|
function restore(previous: string, deps: UnattendedDeps): number {
|
|
266
|
-
const back = run_steps(install_steps(previous
|
|
267
|
+
const back = run_steps(install_steps(`${PACKAGE_NAME}@${previous}`, deps).slice(0, 2), deps, false);
|
|
267
268
|
const lock = deps.readInstalled();
|
|
268
269
|
const binary = deps.readBinary();
|
|
269
270
|
if (back.ok && lock === previous && binary === previous) {
|
|
@@ -279,7 +280,10 @@ function restore(previous: string, deps: UnattendedDeps): number {
|
|
|
279
280
|
return EXIT_RESTORE_FAILED;
|
|
280
281
|
}
|
|
281
282
|
|
|
282
|
-
async function decide(
|
|
283
|
+
async function decide(
|
|
284
|
+
installed: string,
|
|
285
|
+
deps: UnattendedDeps,
|
|
286
|
+
): Promise<{ candidate: RegistryCandidate; tarball: string } | { refused: string[] }> {
|
|
283
287
|
const fetched = await deps.fetchPackument();
|
|
284
288
|
if (!fetched.ok) return { refused: [`registry unavailable: ${fetched.reason}`] };
|
|
285
289
|
const candidate = read_candidate(fetched.doc);
|
|
@@ -292,85 +296,89 @@ async function decide(installed: string, deps: UnattendedDeps): Promise<{ candid
|
|
|
292
296
|
env: { ...deps.env, now: deps.now().toISOString() },
|
|
293
297
|
});
|
|
294
298
|
if (!decision.install) return { refused: [...decision.reasons] };
|
|
295
|
-
//
|
|
296
|
-
//
|
|
297
|
-
//
|
|
298
|
-
// the one workflow this package publishes from before anything installs.
|
|
299
|
+
// `decision` accepted an attestation that EXISTS and carries an SLSA
|
|
300
|
+
// predicate, which any workflow with `id-token: write` can produce; the
|
|
301
|
+
// tarball is verified here against pinned roots before anything installs.
|
|
299
302
|
const provenance = await deps.verifyProvenance(candidate);
|
|
300
|
-
if (provenance
|
|
301
|
-
return { candidate };
|
|
303
|
+
if (!provenance.ok) return { refused: [provenance.reason] };
|
|
304
|
+
return { candidate, tarball: provenance.tarball };
|
|
302
305
|
}
|
|
303
306
|
|
|
307
|
+
/** Largest tarball the unattended path downloads; a release is about 16 MB. */
|
|
308
|
+
export const MAX_TARBALL_BYTES = 64 * 1024 * 1024;
|
|
309
|
+
|
|
304
310
|
/**
|
|
305
|
-
*
|
|
306
|
-
*
|
|
307
|
-
*
|
|
311
|
+
* Download the candidate's tarball, check it against the packument's integrity
|
|
312
|
+
* and against both attestations, and write exactly those bytes to a private
|
|
313
|
+
* temp file — the file the install step installs. Returns that file, or why
|
|
314
|
+
* there is none.
|
|
308
315
|
*
|
|
309
|
-
*
|
|
310
|
-
*
|
|
311
|
-
*
|
|
312
|
-
* The sentence described a composition nobody wrote, which is the worst shape
|
|
313
|
-
* a security note can have, so it is replaced by what the code does rather
|
|
314
|
-
* than corrected in place.
|
|
316
|
+
* The bytes verified and the bytes installed are one buffer: installing by
|
|
317
|
+
* `<package>@<version>` would let the registry serve different bytes to the
|
|
318
|
+
* second request than to the first.
|
|
315
319
|
*
|
|
316
|
-
*
|
|
317
|
-
*
|
|
318
|
-
*
|
|
319
|
-
*
|
|
320
|
-
*
|
|
321
|
-
* `verify_attestation_signature`'s header states it in full.
|
|
322
|
-
*
|
|
323
|
-
* A bundle that cannot be fetched REFUSES rather than passing. Unlike the live
|
|
324
|
-
* session register, there is no second independent gate behind this one: an
|
|
325
|
-
* unreachable attestation is exactly what an attacker serving a tarball
|
|
326
|
-
* without one produces, so failing open here would hand back the property the
|
|
327
|
-
* check exists to establish.
|
|
320
|
+
* Every URL comes from the packument, so each must be on the registry's own
|
|
321
|
+
* origin; a hostile answer would otherwise choose both the host fetched and the
|
|
322
|
+
* document verified. A fetch that fails REFUSES rather than passing: an
|
|
323
|
+
* unreachable attestation is exactly what an attacker serving a tarball without
|
|
324
|
+
* one produces.
|
|
328
325
|
*/
|
|
329
326
|
export async function verify_provenance_default(
|
|
330
327
|
candidate: RegistryCandidate,
|
|
331
328
|
fetch_impl: typeof fetch = fetch,
|
|
332
|
-
timeoutMs =
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
329
|
+
timeoutMs = 60_000,
|
|
330
|
+
verify: (release: ReleaseToVerify) => Promise<string | null> = (r) => verify_release_provenance(r),
|
|
331
|
+
): Promise<Provenance> {
|
|
332
|
+
const v = candidate.version;
|
|
333
|
+
const registry = new URL(PACKUMENT_URL).origin;
|
|
334
|
+
const refuse = (reason: string): Provenance => ({ ok: false, reason: `${v}: ${reason}` });
|
|
335
|
+
for (const [what, url] of [
|
|
336
|
+
['attestation bundle', candidate.attestationsUrl],
|
|
337
|
+
['tarball', candidate.tarballUrl],
|
|
338
|
+
] as const) {
|
|
339
|
+
if (url === null) return refuse(`the registry names no ${what} to verify`);
|
|
340
|
+
let origin = '';
|
|
341
|
+
try {
|
|
342
|
+
origin = new URL(url).origin;
|
|
343
|
+
} catch {
|
|
344
|
+
// an unparsable URL is refused below like a foreign one
|
|
345
|
+
}
|
|
346
|
+
if (origin !== registry) return refuse(`the ${what} url is not on ${registry}`);
|
|
336
347
|
}
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
348
|
+
const integrity = /^sha512-([A-Za-z0-9+/=]+)$/.exec(candidate.integrity ?? '');
|
|
349
|
+
if (integrity === null) return refuse('the registry records no sha512 integrity for the tarball');
|
|
350
|
+
|
|
351
|
+
const get = async (url: string): Promise<Response> => {
|
|
352
|
+
const resp = await fetch_impl(url, { signal: AbortSignal.timeout(timeoutMs), redirect: 'error' });
|
|
353
|
+
if (!resp.ok) throw new Error(`HTTP ${resp.status}`);
|
|
354
|
+
return resp;
|
|
355
|
+
};
|
|
356
|
+
let attestations: unknown;
|
|
357
|
+
let bytes: Buffer;
|
|
340
358
|
try {
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
359
|
+
attestations = await (await get(candidate.attestationsUrl as string)).json();
|
|
360
|
+
const resp = await get(candidate.tarballUrl as string);
|
|
361
|
+
if (Number(resp.headers.get('content-length') ?? 0) > MAX_TARBALL_BYTES) return refuse('the tarball is larger than allowed');
|
|
362
|
+
bytes = Buffer.from(await resp.arrayBuffer());
|
|
363
|
+
} catch (err) {
|
|
364
|
+
return refuse(`download failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
344
365
|
}
|
|
345
|
-
if (
|
|
346
|
-
|
|
366
|
+
if (bytes.length > MAX_TARBALL_BYTES) return refuse('the tarball is larger than allowed');
|
|
367
|
+
const digest = crypto.createHash('sha512').update(bytes).digest();
|
|
368
|
+
if (!digest.equals(Buffer.from(integrity[1] as string, 'base64'))) {
|
|
369
|
+
return refuse('the downloaded tarball does not match the integrity the registry records');
|
|
347
370
|
}
|
|
348
|
-
|
|
371
|
+
const reason = await verify({ version: v, tarballSha512Hex: digest.toString('hex'), attestations });
|
|
372
|
+
if (reason !== null) return refuse(reason);
|
|
373
|
+
|
|
349
374
|
try {
|
|
350
|
-
const
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
});
|
|
355
|
-
if (!resp.ok) return `attestation fetch failed: HTTP ${resp.status}`;
|
|
356
|
-
doc = await resp.json();
|
|
375
|
+
const dir = fs.mkdtempSync(path.join(os.tmpdir(), 'agent-config-upgrade-'));
|
|
376
|
+
const tarball = path.join(dir, `agent-config-${v}.tgz`);
|
|
377
|
+
fs.writeFileSync(tarball, bytes, { mode: 0o600, flag: 'wx' });
|
|
378
|
+
return { ok: true, tarball };
|
|
357
379
|
} catch (err) {
|
|
358
|
-
return `
|
|
359
|
-
}
|
|
360
|
-
if (!provenance_matches_publish(doc)) {
|
|
361
|
-
const named = attestation_workflow_ref(doc);
|
|
362
|
-
return named === null
|
|
363
|
-
? `${candidate.version} has no readable provenance workflow in its attestation`
|
|
364
|
-
: `${candidate.version} was built by ${named}, not ${EXPECTED_WORKFLOW_REF}`;
|
|
380
|
+
return refuse(`cannot write the verified tarball: ${err instanceof Error ? err.message : String(err)}`);
|
|
365
381
|
}
|
|
366
|
-
// The payload says the right thing; this asks whether the payload is the
|
|
367
|
-
// one that was signed, and whether the certificate that signed it carries
|
|
368
|
-
// the same identity. It does NOT establish who issued that certificate —
|
|
369
|
-
// see `verify_attestation_signature`'s own note and the `dsse-trust-root`
|
|
370
|
-
// blocker, which is where the trust root is decided.
|
|
371
|
-
const signature = await verify_attestation_signature(doc);
|
|
372
|
-
if (signature !== null) return `${candidate.version}: ${signature}`;
|
|
373
|
-
return null;
|
|
374
382
|
}
|
|
375
383
|
|
|
376
384
|
/** Contexts where even the registry fetch and the lock file are side effects nobody asked for. */
|
|
@@ -397,14 +405,16 @@ export async function run_unattended(deps: UnattendedDeps): Promise<number> {
|
|
|
397
405
|
say(deps.err, `${PREFIX}: not installing — ${locked}.`);
|
|
398
406
|
return EXIT_REFUSED;
|
|
399
407
|
}
|
|
408
|
+
let verified: string | null = null;
|
|
400
409
|
try {
|
|
401
410
|
const verdict = await decide(previous, deps);
|
|
402
411
|
if ('refused' in verdict) {
|
|
403
412
|
say(deps.out, `ℹ️ ${PREFIX}: not installing (installed: ${previous}) — ${verdict.refused.join('; ')}.`);
|
|
404
413
|
return EXIT_REFUSED;
|
|
405
414
|
}
|
|
415
|
+
verified = verdict.tarball;
|
|
406
416
|
const target = verdict.candidate.version;
|
|
407
|
-
const steps = install_steps(
|
|
417
|
+
const steps = install_steps(verified, deps);
|
|
408
418
|
if (deps.activationBlocked !== null) {
|
|
409
419
|
const how = deps.dryRun ? 'dry run: a real run would not install' : 'not installing';
|
|
410
420
|
say(deps.out, `ℹ️ ${PREFIX}: every gate passed for ${target}, but ${how} — ${deps.activationBlocked}.`);
|
|
@@ -422,10 +432,13 @@ export async function run_unattended(deps: UnattendedDeps): Promise<number> {
|
|
|
422
432
|
}
|
|
423
433
|
const what = `${ran.cmd.join(' ')} (exit ${ran.result.rc})`;
|
|
424
434
|
if (/\bEACCES\b/.test(ran.result.stderr)) {
|
|
435
|
+
// The verified file is removed when this run ends, so the command a
|
|
436
|
+
// person can still run names the version instead.
|
|
437
|
+
const manual = ran.cmd === steps[0] ? ['npm', 'install', '-g', `${PACKAGE_NAME}@${target}`] : ran.cmd;
|
|
425
438
|
say(
|
|
426
439
|
deps.err,
|
|
427
440
|
`❌ ${PREFIX}: permission denied on ${what}. agent-config never runs sudo; ` +
|
|
428
|
-
`run this yourself with the right permissions: ${
|
|
441
|
+
`run this yourself with the right permissions: ${manual.join(' ')}`,
|
|
429
442
|
);
|
|
430
443
|
// Denied on the very first write, npm changed nothing; any later step leaves a mixed install.
|
|
431
444
|
const untouched = ran.cmd === steps[0] && deps.readInstalled() === previous && deps.readBinary() === previous;
|
|
@@ -435,6 +448,7 @@ export async function run_unattended(deps: UnattendedDeps): Promise<number> {
|
|
|
435
448
|
say(deps.err, `❌ ${PREFIX}: step failed: ${what} — restoring ${previous}.`);
|
|
436
449
|
return restore(previous, deps);
|
|
437
450
|
} finally {
|
|
451
|
+
if (verified !== null) fs.rmSync(path.dirname(verified), { recursive: true, force: true });
|
|
438
452
|
release_lock(deps);
|
|
439
453
|
}
|
|
440
454
|
}
|
|
@@ -104,7 +104,8 @@ EOF
|
|
|
104
104
|
cat <<'EOF'
|
|
105
105
|
|
|
106
106
|
Tier 1 — power-user (release shape, audit, migration):
|
|
107
|
-
update
|
|
107
|
+
update Pin agent_config_version in .agent-settings.yml; installs that
|
|
108
|
+
version globally first when the global install is older
|
|
108
109
|
Flags: --check (read-only) | --to <version> (explicit pin)
|
|
109
110
|
upgrade Install the latest @event4u/agent-config globally
|
|
110
111
|
(npm i -g) and refresh the global install + plugin.
|
|
@@ -1400,7 +1401,7 @@ cmd_use() {
|
|
|
1400
1401
|
}
|
|
1401
1402
|
|
|
1402
1403
|
# `agent-config update` — flip the agent_config_version pin in
|
|
1403
|
-
# .agent-settings.yml. See scripts/_cli/cmd_update.ts (P3.1 of
|
|
1404
|
+
# .agent-settings.yml, upgrading the global install first when it is older. See scripts/_cli/cmd_update.ts (P3.1 of
|
|
1404
1405
|
# road-to-portable-runtime-and-update-check.md).
|
|
1405
1406
|
cmd_update() {
|
|
1406
1407
|
exec_ts "$PACKAGE_ROOT/src/scripts/_cli/cmd_update.ts" "$@"
|