merge: 公庫 5 顆 → 23 顆,Arcrun 統一編所有零件(D91/D92/Arcrun#125)
總管複驗才併(第二次審——第一次因基底過舊被攔下):
· cypher sha = 1da6a46e6a17 ← 含 Arcrun#124 修法那顆(第一版帶的是修法前的 8e648747)
· 23 顆逐位元等於 Arcrun 官方成品;五顆既有引擎逐顆對得上 main 的 committed 值
· repo_dirty false、repo_head 45169ab
· excluded 有寫明理由(arcrun-claude-api:wasm 不在版控=不是可出貨零件),不是安靜消失
· auth_static_key 進首裝,靠 {{credential.X}} 隱含依賴抓到
🔴 第一版被攔下的教訓(值得留):它的 commit 訊息寫「位元組完全重現 committed 值」——
那句屬實,但重現的是**舊的** committed 值。
⇒「與版控一致」在基底過舊時,正好是最像正確的錯誤。
環境驗證(我算得穩不穩)≠ 內容驗證(我帶的是不是最新那顆)。
⚠️ 未驗最後一哩:全新安裝實測要 leo 按 CF 授權。
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
Binary file not shown.
File diff suppressed because it is too large
Load Diff
Binary file not shown.
File diff suppressed because it is too large
Load Diff
Binary file not shown.
File diff suppressed because it is too large
Load Diff
Binary file not shown.
File diff suppressed because it is too large
Load Diff
Binary file not shown.
File diff suppressed because it is too large
Load Diff
Binary file not shown.
File diff suppressed because it is too large
Load Diff
Binary file not shown.
File diff suppressed because it is too large
Load Diff
Binary file not shown.
File diff suppressed because it is too large
Load Diff
Binary file not shown.
File diff suppressed because it is too large
Load Diff
Binary file not shown.
File diff suppressed because it is too large
Load Diff
Binary file not shown.
File diff suppressed because it is too large
Load Diff
File diff suppressed because one or more lines are too long
Binary file not shown.
File diff suppressed because it is too large
Load Diff
Binary file not shown.
File diff suppressed because it is too large
Load Diff
Binary file not shown.
File diff suppressed because it is too large
Load Diff
Binary file not shown.
File diff suppressed because it is too large
Load Diff
Binary file not shown.
File diff suppressed because it is too large
Load Diff
Binary file not shown.
File diff suppressed because it is too large
Load Diff
+573
-34
@@ -1,8 +1,8 @@
|
||||
{
|
||||
"schema": 1,
|
||||
"built_for": "arcrun-tier2-worker-artifacts",
|
||||
"generated_at": "2026-08-14T15:26:27.638Z",
|
||||
"repo_head": "340b461f58ed6a9958850f8871cf05381ce95c9d",
|
||||
"generated_at": "2026-08-14T17:00:10.242Z",
|
||||
"repo_head": "45169ab25e974085aaaebaceeb4a67f785239d1b",
|
||||
"repo_dirty": false,
|
||||
"workers": [
|
||||
{
|
||||
@@ -84,38 +84,6 @@
|
||||
},
|
||||
"warnings": []
|
||||
},
|
||||
{
|
||||
"name": "arcrun-http-request",
|
||||
"source_dir": ".component-builds/http_request",
|
||||
"source_commit": "1e85dfb49b0e8d81c0854781d93ee4e6a300c7b3",
|
||||
"main_module": "worker.mjs",
|
||||
"main_file": "arcrun-http-request/worker.mjs",
|
||||
"js_bytes": 80079,
|
||||
"content_sha256": "cdd97364f277587cbade69e09bb40812c68f26a1e8bc9aa632c65b1b962b0b85",
|
||||
"modules": [
|
||||
{
|
||||
"name": "component.wasm",
|
||||
"type": "application/wasm",
|
||||
"file": "arcrun-http-request/component.wasm",
|
||||
"sha256": "cc15cc785703e7bbb8dbff2d38dc84a4ac24e2f44316182730abae0f170ef133"
|
||||
}
|
||||
],
|
||||
"compat_date": "2025-02-19",
|
||||
"compat_flags": [
|
||||
"nodejs_compat",
|
||||
"global_fetch_strictly_public"
|
||||
],
|
||||
"requires": {
|
||||
"kv": [],
|
||||
"d1": [],
|
||||
"vectorize": 0,
|
||||
"ai": false,
|
||||
"vars": {
|
||||
"COMPONENT_ID": "http_request"
|
||||
}
|
||||
},
|
||||
"warnings": []
|
||||
},
|
||||
{
|
||||
"name": "arcrun-code",
|
||||
"source_dir": "registry/components/code",
|
||||
@@ -168,6 +136,577 @@
|
||||
"vars": {}
|
||||
},
|
||||
"warnings": []
|
||||
},
|
||||
{
|
||||
"name": "arcrun-array-ops",
|
||||
"source_dir": ".component-builds/array_ops",
|
||||
"source_commit": "922a57fe3458dfda82bcc25d287cef93f8dab838",
|
||||
"main_module": "worker.mjs",
|
||||
"main_file": "arcrun-array-ops/worker.mjs",
|
||||
"js_bytes": 67802,
|
||||
"content_sha256": "35ad5be6332dde2299efd10be726300c6cace1eb085ab24f08173536ca7fb950",
|
||||
"modules": [
|
||||
{
|
||||
"name": "component.wasm",
|
||||
"type": "application/wasm",
|
||||
"file": "arcrun-array-ops/component.wasm",
|
||||
"sha256": "74dc9d99f8b9ef43628b55c85c05beb1af8f6f90d78d07cd132ffe4b865147ae"
|
||||
}
|
||||
],
|
||||
"compat_date": "2025-02-19",
|
||||
"compat_flags": [],
|
||||
"requires": {
|
||||
"kv": [],
|
||||
"d1": [],
|
||||
"vectorize": 0,
|
||||
"ai": false,
|
||||
"vars": {
|
||||
"COMPONENT_ID": "array_ops"
|
||||
}
|
||||
},
|
||||
"warnings": []
|
||||
},
|
||||
{
|
||||
"name": "arcrun-auth-oauth2",
|
||||
"source_dir": ".component-builds/auth_oauth2",
|
||||
"source_commit": "20c7610371c401f622ef4e6ea1b9ba812963be95",
|
||||
"main_module": "worker.mjs",
|
||||
"main_file": "arcrun-auth-oauth2/worker.mjs",
|
||||
"js_bytes": 80472,
|
||||
"content_sha256": "a9b0d45ee6a63e0eebd45554df16d9fa4e6e717e0c31dac13b630e132f1a96db",
|
||||
"modules": [
|
||||
{
|
||||
"name": "component.wasm",
|
||||
"type": "application/wasm",
|
||||
"file": "arcrun-auth-oauth2/component.wasm",
|
||||
"sha256": "3622c20644e6c8c39487109e7353e2590bad90b1207139e34dfb0cf1c2696664"
|
||||
}
|
||||
],
|
||||
"compat_date": "2025-02-19",
|
||||
"compat_flags": [
|
||||
"nodejs_compat"
|
||||
],
|
||||
"requires": {
|
||||
"kv": [
|
||||
"CREDENTIALS_KV",
|
||||
"RECIPES"
|
||||
],
|
||||
"d1": [],
|
||||
"vectorize": 0,
|
||||
"ai": false,
|
||||
"vars": {
|
||||
"COMPONENT_ID": "auth_oauth2"
|
||||
}
|
||||
},
|
||||
"warnings": []
|
||||
},
|
||||
{
|
||||
"name": "arcrun-auth-service-account",
|
||||
"source_dir": ".component-builds/auth_service_account",
|
||||
"source_commit": "20c7610371c401f622ef4e6ea1b9ba812963be95",
|
||||
"main_module": "worker.mjs",
|
||||
"main_file": "arcrun-auth-service-account/worker.mjs",
|
||||
"js_bytes": 81693,
|
||||
"content_sha256": "68675ec76f3f060309492e55301936a47f4ae0a5e9b00865eb861e0e631e40e5",
|
||||
"modules": [
|
||||
{
|
||||
"name": "component.wasm",
|
||||
"type": "application/wasm",
|
||||
"file": "arcrun-auth-service-account/component.wasm",
|
||||
"sha256": "7d0808b4b9a2df4fbb54977eafe34a5bdb99e0cc941d97462c62e26f3651951e"
|
||||
}
|
||||
],
|
||||
"compat_date": "2025-02-19",
|
||||
"compat_flags": [
|
||||
"nodejs_compat"
|
||||
],
|
||||
"requires": {
|
||||
"kv": [
|
||||
"CREDENTIALS_KV",
|
||||
"RECIPES"
|
||||
],
|
||||
"d1": [],
|
||||
"vectorize": 0,
|
||||
"ai": false,
|
||||
"vars": {
|
||||
"COMPONENT_ID": "auth_service_account"
|
||||
}
|
||||
},
|
||||
"warnings": []
|
||||
},
|
||||
{
|
||||
"name": "arcrun-auth-static-key",
|
||||
"source_dir": ".component-builds/auth_static_key",
|
||||
"source_commit": "20c7610371c401f622ef4e6ea1b9ba812963be95",
|
||||
"main_module": "worker.mjs",
|
||||
"main_file": "arcrun-auth-static-key/worker.mjs",
|
||||
"js_bytes": 81217,
|
||||
"content_sha256": "140c163d2e8c609eebf281de25d16e521d85004f8437cc842613c249e44aa7ff",
|
||||
"modules": [
|
||||
{
|
||||
"name": "component.wasm",
|
||||
"type": "application/wasm",
|
||||
"file": "arcrun-auth-static-key/component.wasm",
|
||||
"sha256": "cd0f69a80ced0979749e0ec30b1981795a49eaa56bdb2ba102cd4b64f4670369"
|
||||
}
|
||||
],
|
||||
"compat_date": "2025-02-19",
|
||||
"compat_flags": [
|
||||
"nodejs_compat"
|
||||
],
|
||||
"requires": {
|
||||
"kv": [
|
||||
"CREDENTIALS_KV",
|
||||
"RECIPES"
|
||||
],
|
||||
"d1": [],
|
||||
"vectorize": 0,
|
||||
"ai": false,
|
||||
"vars": {
|
||||
"COMPONENT_ID": "auth_static_key"
|
||||
}
|
||||
},
|
||||
"warnings": []
|
||||
},
|
||||
{
|
||||
"name": "arcrun-cron",
|
||||
"source_dir": ".component-builds/cron",
|
||||
"source_commit": "6e92ca03727bf22143499d6c0df06b40cb65c17b",
|
||||
"main_module": "worker.mjs",
|
||||
"main_file": "arcrun-cron/worker.mjs",
|
||||
"js_bytes": 78911,
|
||||
"content_sha256": "17068f03f686aee6c0aec9d7d748bbb1998edc8918260033f985697bc718aa76",
|
||||
"modules": [
|
||||
{
|
||||
"name": "component.wasm",
|
||||
"type": "application/wasm",
|
||||
"file": "arcrun-cron/component.wasm",
|
||||
"sha256": "afb3a14f8c8f00f4f5573f44153cb1dcf6fc715b2fb47c2afb0aaf95b74d3cde"
|
||||
}
|
||||
],
|
||||
"compat_date": "2025-02-19",
|
||||
"compat_flags": [
|
||||
"nodejs_compat"
|
||||
],
|
||||
"requires": {
|
||||
"kv": [],
|
||||
"d1": [],
|
||||
"vectorize": 0,
|
||||
"ai": false,
|
||||
"vars": {
|
||||
"COMPONENT_ID": "cron"
|
||||
}
|
||||
},
|
||||
"warnings": []
|
||||
},
|
||||
{
|
||||
"name": "arcrun-date-ops",
|
||||
"source_dir": ".component-builds/date_ops",
|
||||
"source_commit": "922a57fe3458dfda82bcc25d287cef93f8dab838",
|
||||
"main_module": "worker.mjs",
|
||||
"main_file": "arcrun-date-ops/worker.mjs",
|
||||
"js_bytes": 67781,
|
||||
"content_sha256": "60ebc1d17ab0b9d3bb66f1c71ec63eb5d35bc97ed9f8b4cd1c1c95f4eeb171b7",
|
||||
"modules": [
|
||||
{
|
||||
"name": "component.wasm",
|
||||
"type": "application/wasm",
|
||||
"file": "arcrun-date-ops/component.wasm",
|
||||
"sha256": "4e2c1329ce4014aa86a9a6449d8699e67c6c616f5c07088dc21216d4036ca41e"
|
||||
}
|
||||
],
|
||||
"compat_date": "2025-02-19",
|
||||
"compat_flags": [],
|
||||
"requires": {
|
||||
"kv": [],
|
||||
"d1": [],
|
||||
"vectorize": 0,
|
||||
"ai": false,
|
||||
"vars": {
|
||||
"COMPONENT_ID": "date_ops"
|
||||
}
|
||||
},
|
||||
"warnings": []
|
||||
},
|
||||
{
|
||||
"name": "arcrun-filter",
|
||||
"source_dir": ".component-builds/filter",
|
||||
"source_commit": "922a57fe3458dfda82bcc25d287cef93f8dab838",
|
||||
"main_module": "worker.mjs",
|
||||
"main_file": "arcrun-filter/worker.mjs",
|
||||
"js_bytes": 67739,
|
||||
"content_sha256": "c7fd5070ff7c0f0341e9dd364805e393f9d68056a846f48e6bc61ef6f4e67187",
|
||||
"modules": [
|
||||
{
|
||||
"name": "component.wasm",
|
||||
"type": "application/wasm",
|
||||
"file": "arcrun-filter/component.wasm",
|
||||
"sha256": "6a0692b386522918b3b5af79cf7877394d1f02c350018dbaef68ef380dad1b9e"
|
||||
}
|
||||
],
|
||||
"compat_date": "2025-02-19",
|
||||
"compat_flags": [],
|
||||
"requires": {
|
||||
"kv": [],
|
||||
"d1": [],
|
||||
"vectorize": 0,
|
||||
"ai": false,
|
||||
"vars": {
|
||||
"COMPONENT_ID": "filter"
|
||||
}
|
||||
},
|
||||
"warnings": []
|
||||
},
|
||||
{
|
||||
"name": "arcrun-foreach-control",
|
||||
"source_dir": ".component-builds/foreach_control",
|
||||
"source_commit": "922a57fe3458dfda82bcc25d287cef93f8dab838",
|
||||
"main_module": "worker.mjs",
|
||||
"main_file": "arcrun-foreach-control/worker.mjs",
|
||||
"js_bytes": 67928,
|
||||
"content_sha256": "e52410c9611a93ac7fe38c7b11291cc18711c7da3370492788c3c55bc0fb03d5",
|
||||
"modules": [
|
||||
{
|
||||
"name": "component.wasm",
|
||||
"type": "application/wasm",
|
||||
"file": "arcrun-foreach-control/component.wasm",
|
||||
"sha256": "8ae161ab37cc78ef6fae65364a47f7accb572378b395c4637daa2ffa57f14632"
|
||||
}
|
||||
],
|
||||
"compat_date": "2025-02-19",
|
||||
"compat_flags": [],
|
||||
"requires": {
|
||||
"kv": [],
|
||||
"d1": [],
|
||||
"vectorize": 0,
|
||||
"ai": false,
|
||||
"vars": {
|
||||
"COMPONENT_ID": "foreach_control"
|
||||
}
|
||||
},
|
||||
"warnings": []
|
||||
},
|
||||
{
|
||||
"name": "arcrun-http-request",
|
||||
"source_dir": ".component-builds/http_request",
|
||||
"source_commit": "1e85dfb49b0e8d81c0854781d93ee4e6a300c7b3",
|
||||
"main_module": "worker.mjs",
|
||||
"main_file": "arcrun-http-request/worker.mjs",
|
||||
"js_bytes": 80079,
|
||||
"content_sha256": "cdd97364f277587cbade69e09bb40812c68f26a1e8bc9aa632c65b1b962b0b85",
|
||||
"modules": [
|
||||
{
|
||||
"name": "component.wasm",
|
||||
"type": "application/wasm",
|
||||
"file": "arcrun-http-request/component.wasm",
|
||||
"sha256": "cc15cc785703e7bbb8dbff2d38dc84a4ac24e2f44316182730abae0f170ef133"
|
||||
}
|
||||
],
|
||||
"compat_date": "2025-02-19",
|
||||
"compat_flags": [
|
||||
"nodejs_compat",
|
||||
"global_fetch_strictly_public"
|
||||
],
|
||||
"requires": {
|
||||
"kv": [],
|
||||
"d1": [],
|
||||
"vectorize": 0,
|
||||
"ai": false,
|
||||
"vars": {
|
||||
"COMPONENT_ID": "http_request"
|
||||
}
|
||||
},
|
||||
"warnings": []
|
||||
},
|
||||
{
|
||||
"name": "arcrun-if-control",
|
||||
"source_dir": ".component-builds/if_control",
|
||||
"source_commit": "922a57fe3458dfda82bcc25d287cef93f8dab838",
|
||||
"main_module": "worker.mjs",
|
||||
"main_file": "arcrun-if-control/worker.mjs",
|
||||
"js_bytes": 67823,
|
||||
"content_sha256": "557ecf4ee955105af07b2227b08365eb9de8c39ad8ffb323b101397704f438bd",
|
||||
"modules": [
|
||||
{
|
||||
"name": "component.wasm",
|
||||
"type": "application/wasm",
|
||||
"file": "arcrun-if-control/component.wasm",
|
||||
"sha256": "87c81e9cf5e664f60c06a364508dd365d49953e1b7afba6a5753fce5a4c82636"
|
||||
}
|
||||
],
|
||||
"compat_date": "2025-02-19",
|
||||
"compat_flags": [],
|
||||
"requires": {
|
||||
"kv": [],
|
||||
"d1": [],
|
||||
"vectorize": 0,
|
||||
"ai": false,
|
||||
"vars": {
|
||||
"COMPONENT_ID": "if_control"
|
||||
}
|
||||
},
|
||||
"warnings": []
|
||||
},
|
||||
{
|
||||
"name": "arcrun-merge",
|
||||
"source_dir": ".component-builds/merge",
|
||||
"source_commit": "922a57fe3458dfda82bcc25d287cef93f8dab838",
|
||||
"main_module": "worker.mjs",
|
||||
"main_file": "arcrun-merge/worker.mjs",
|
||||
"js_bytes": 67718,
|
||||
"content_sha256": "e4216249436000bbb4db0318cc9028f936ca6537d3d656228cfdd3cb31df25f8",
|
||||
"modules": [
|
||||
{
|
||||
"name": "component.wasm",
|
||||
"type": "application/wasm",
|
||||
"file": "arcrun-merge/component.wasm",
|
||||
"sha256": "55a4d143246df172f60b5e15e7b26e55279deda9db436e3d65bccc25c86d7cd9"
|
||||
}
|
||||
],
|
||||
"compat_date": "2025-02-19",
|
||||
"compat_flags": [],
|
||||
"requires": {
|
||||
"kv": [],
|
||||
"d1": [],
|
||||
"vectorize": 0,
|
||||
"ai": false,
|
||||
"vars": {
|
||||
"COMPONENT_ID": "merge"
|
||||
}
|
||||
},
|
||||
"warnings": []
|
||||
},
|
||||
{
|
||||
"name": "arcrun-number-ops",
|
||||
"source_dir": ".component-builds/number_ops",
|
||||
"source_commit": "922a57fe3458dfda82bcc25d287cef93f8dab838",
|
||||
"main_module": "worker.mjs",
|
||||
"main_file": "arcrun-number-ops/worker.mjs",
|
||||
"js_bytes": 67823,
|
||||
"content_sha256": "91821a291117e033bfa4f013cd88de09479cd4ac43708e37a39df3f31c72aaa6",
|
||||
"modules": [
|
||||
{
|
||||
"name": "component.wasm",
|
||||
"type": "application/wasm",
|
||||
"file": "arcrun-number-ops/component.wasm",
|
||||
"sha256": "452d1940f6e2b2b36dc97c145abcb7b5bacdbfa7631c742827c2f6f6db87b4ea"
|
||||
}
|
||||
],
|
||||
"compat_date": "2025-02-19",
|
||||
"compat_flags": [],
|
||||
"requires": {
|
||||
"kv": [],
|
||||
"d1": [],
|
||||
"vectorize": 0,
|
||||
"ai": false,
|
||||
"vars": {
|
||||
"COMPONENT_ID": "number_ops"
|
||||
}
|
||||
},
|
||||
"warnings": []
|
||||
},
|
||||
{
|
||||
"name": "arcrun-set",
|
||||
"source_dir": ".component-builds/set",
|
||||
"source_commit": "922a57fe3458dfda82bcc25d287cef93f8dab838",
|
||||
"main_module": "worker.mjs",
|
||||
"main_file": "arcrun-set/worker.mjs",
|
||||
"js_bytes": 67676,
|
||||
"content_sha256": "6cac587802cae7dc1651d6b461232abafe40dcf0fbfe9e0adc5b1d8e35b41cc1",
|
||||
"modules": [
|
||||
{
|
||||
"name": "component.wasm",
|
||||
"type": "application/wasm",
|
||||
"file": "arcrun-set/component.wasm",
|
||||
"sha256": "f5afcf6e57401d2d83b21fb971ec80ca56d5e8e4bdb74a004f3d118875ed57c4"
|
||||
}
|
||||
],
|
||||
"compat_date": "2025-02-19",
|
||||
"compat_flags": [],
|
||||
"requires": {
|
||||
"kv": [],
|
||||
"d1": [],
|
||||
"vectorize": 0,
|
||||
"ai": false,
|
||||
"vars": {
|
||||
"COMPONENT_ID": "set"
|
||||
}
|
||||
},
|
||||
"warnings": []
|
||||
},
|
||||
{
|
||||
"name": "arcrun-string-ops",
|
||||
"source_dir": ".component-builds/string_ops",
|
||||
"source_commit": "922a57fe3458dfda82bcc25d287cef93f8dab838",
|
||||
"main_module": "worker.mjs",
|
||||
"main_file": "arcrun-string-ops/worker.mjs",
|
||||
"js_bytes": 67823,
|
||||
"content_sha256": "c85166e357b76828c7020426d912b591006152ac38f024a095a4d9dcd10dee72",
|
||||
"modules": [
|
||||
{
|
||||
"name": "component.wasm",
|
||||
"type": "application/wasm",
|
||||
"file": "arcrun-string-ops/component.wasm",
|
||||
"sha256": "41f3243ee18bb9f4fcbaa417bd773347128dec2971132e240eddec19ba6a9da2"
|
||||
}
|
||||
],
|
||||
"compat_date": "2025-02-19",
|
||||
"compat_flags": [],
|
||||
"requires": {
|
||||
"kv": [],
|
||||
"d1": [],
|
||||
"vectorize": 0,
|
||||
"ai": false,
|
||||
"vars": {
|
||||
"COMPONENT_ID": "string_ops"
|
||||
}
|
||||
},
|
||||
"warnings": []
|
||||
},
|
||||
{
|
||||
"name": "arcrun-switch",
|
||||
"source_dir": ".component-builds/switch",
|
||||
"source_commit": "922a57fe3458dfda82bcc25d287cef93f8dab838",
|
||||
"main_module": "worker.mjs",
|
||||
"main_file": "arcrun-switch/worker.mjs",
|
||||
"js_bytes": 67739,
|
||||
"content_sha256": "e0e7689dbba735a254556272d6d233396c7fa05cc0fba5835a3e20b0ddf00fca",
|
||||
"modules": [
|
||||
{
|
||||
"name": "component.wasm",
|
||||
"type": "application/wasm",
|
||||
"file": "arcrun-switch/component.wasm",
|
||||
"sha256": "6d993dc9b1affe16b600aaa7cae5af977108cc08513bb11d9d9db908285fea8b"
|
||||
}
|
||||
],
|
||||
"compat_date": "2025-02-19",
|
||||
"compat_flags": [],
|
||||
"requires": {
|
||||
"kv": [],
|
||||
"d1": [],
|
||||
"vectorize": 0,
|
||||
"ai": false,
|
||||
"vars": {
|
||||
"COMPONENT_ID": "switch"
|
||||
}
|
||||
},
|
||||
"warnings": []
|
||||
},
|
||||
{
|
||||
"name": "arcrun-try-catch",
|
||||
"source_dir": ".component-builds/try_catch",
|
||||
"source_commit": "922a57fe3458dfda82bcc25d287cef93f8dab838",
|
||||
"main_module": "worker.mjs",
|
||||
"main_file": "arcrun-try-catch/worker.mjs",
|
||||
"js_bytes": 67802,
|
||||
"content_sha256": "a5fbcbf452ab00269dc857997df3059bee7eb1f2c1b322d249146ab4e5e5bac9",
|
||||
"modules": [
|
||||
{
|
||||
"name": "component.wasm",
|
||||
"type": "application/wasm",
|
||||
"file": "arcrun-try-catch/component.wasm",
|
||||
"sha256": "10eaddc5ef0d3dc68e3f009970541ac7f5fce30dcdbecec9a333afd5a29e3486"
|
||||
}
|
||||
],
|
||||
"compat_date": "2025-02-19",
|
||||
"compat_flags": [],
|
||||
"requires": {
|
||||
"kv": [],
|
||||
"d1": [],
|
||||
"vectorize": 0,
|
||||
"ai": false,
|
||||
"vars": {
|
||||
"COMPONENT_ID": "try_catch"
|
||||
}
|
||||
},
|
||||
"warnings": []
|
||||
},
|
||||
{
|
||||
"name": "arcrun-validate-json",
|
||||
"source_dir": ".component-builds/validate_json",
|
||||
"source_commit": "922a57fe3458dfda82bcc25d287cef93f8dab838",
|
||||
"main_module": "worker.mjs",
|
||||
"main_file": "arcrun-validate-json/worker.mjs",
|
||||
"js_bytes": 67886,
|
||||
"content_sha256": "2d0efa26e9fe38db87eb9585daf72e0f2ba8c859d15d1b65f36798656cc4e83b",
|
||||
"modules": [
|
||||
{
|
||||
"name": "component.wasm",
|
||||
"type": "application/wasm",
|
||||
"file": "arcrun-validate-json/component.wasm",
|
||||
"sha256": "8142ffd3cc57efbb9128faaf450b646111b0f2e2575cc29a7b562a7b185912ab"
|
||||
}
|
||||
],
|
||||
"compat_date": "2025-02-19",
|
||||
"compat_flags": [],
|
||||
"requires": {
|
||||
"kv": [],
|
||||
"d1": [],
|
||||
"vectorize": 0,
|
||||
"ai": false,
|
||||
"vars": {
|
||||
"COMPONENT_ID": "validate_json"
|
||||
}
|
||||
},
|
||||
"warnings": []
|
||||
},
|
||||
{
|
||||
"name": "arcrun-wait",
|
||||
"source_dir": ".component-builds/wait",
|
||||
"source_commit": "922a57fe3458dfda82bcc25d287cef93f8dab838",
|
||||
"main_module": "worker.mjs",
|
||||
"main_file": "arcrun-wait/worker.mjs",
|
||||
"js_bytes": 67697,
|
||||
"content_sha256": "3d1b46e7b1ff056abb2c1aeed738d23d58eaa7c8f36b674e4ea2ed1602015f9e",
|
||||
"modules": [
|
||||
{
|
||||
"name": "component.wasm",
|
||||
"type": "application/wasm",
|
||||
"file": "arcrun-wait/component.wasm",
|
||||
"sha256": "52ca2d184bf3dcc646bb94f29f7f3e69b5540276363e83a54cb435ff33ca2f3c"
|
||||
}
|
||||
],
|
||||
"compat_date": "2025-02-19",
|
||||
"compat_flags": [],
|
||||
"requires": {
|
||||
"kv": [],
|
||||
"d1": [],
|
||||
"vectorize": 0,
|
||||
"ai": false,
|
||||
"vars": {
|
||||
"COMPONENT_ID": "wait"
|
||||
}
|
||||
},
|
||||
"warnings": []
|
||||
},
|
||||
{
|
||||
"name": "arcrun-rag-ui",
|
||||
"source_dir": "console-ui/public",
|
||||
"source_commit": "614fe448124dbd25d8228e37723473f1704f3a7a",
|
||||
"main_module": "worker.mjs",
|
||||
"main_file": "arcrun-rag-ui/worker.mjs",
|
||||
"js_bytes": 524513,
|
||||
"content_sha256": "5825f427e4211dd4b0eee969c94c8bfa856f65b8a48fc718e137f3b5d074e1ff",
|
||||
"modules": [],
|
||||
"compat_date": "2026-07-01",
|
||||
"compat_flags": [],
|
||||
"requires": {
|
||||
"kv": [],
|
||||
"d1": [],
|
||||
"vectorize": 0,
|
||||
"ai": false,
|
||||
"vars": {}
|
||||
},
|
||||
"ui_fingerprint": "82bc636990e60172663f7ce90c75d8b434193b8532b21f1d4b57c5fb330cbe11",
|
||||
"ui_file_count": 9,
|
||||
"warnings": []
|
||||
}
|
||||
],
|
||||
"excluded": [
|
||||
{
|
||||
"name": "arcrun-claude-api",
|
||||
"dir": ".component-builds/claude_api",
|
||||
"reason": "component.wasm 不在版控(.gitignore 明文排除=這顆不是可出貨的零件)"
|
||||
}
|
||||
],
|
||||
"notes": []
|
||||
|
||||
@@ -0,0 +1,208 @@
|
||||
#!/usr/bin/env node
|
||||
/**
|
||||
* build-ui-worker.mjs — 把 `console-ui/public` 內嵌成一顆單檔 worker(`arcrun-rag-ui`)。
|
||||
*
|
||||
* 🔴 2026-08-15(D91/Arcrun#125):這支是**從 arcrun-rag 搬過來的**。
|
||||
* leo 原話:「今天開始出貨一律不准在 arcrun rag 或任何別的地方 build,
|
||||
* **這就是 arcrun 的專屬工作**。你告訴我要把 cypher 搬到 arcrun,我說好,
|
||||
* 結果搞到現在還用違反的方式。」
|
||||
*
|
||||
* 實況(2026-08-14 查):五顆 tier2 worker 早就改成「向 Arcrun 取用成品」了
|
||||
* (`build-worker-artifacts.mjs` → `.worker-builds/`),**只有這顆 UI 沒搬**——
|
||||
* arcrun-rag 的 `installer/scripts/build-ui-bundle.mjs` 仍然自己讀本 repo 的
|
||||
* `console-ui/public`、自己拼裝出 worker 原始碼。於是:
|
||||
* · 用戶拿到的 portal 前端,其位元組沒有任何一個 Arcrun commit 說得出來源
|
||||
* · 「成品只有一個產地」這句話對六顆零件裡的五顆成立,對第六顆不成立
|
||||
* ⇒ 規則不是被誰違反,是**它從來沒有被機械驗證過**(同 D54/D64/D90 的形狀)。
|
||||
*
|
||||
* 搬過來之後,UI 與其他零件走同一條路:
|
||||
* 產地(本 repo)→ `.worker-builds/arcrun-rag-ui/worker.mjs` + manifest 指紋
|
||||
* → arcrun-rag 只做「複製 + 算版本」,不再有任何一行產生 worker 原始碼。
|
||||
*
|
||||
* ⚠️ 內嵌邏輯與兩道閘(世代閘、前端 JS 語法閘)**原樣搬過來,一個字沒改**——
|
||||
* 它們驗的是本 repo 的 `console-ui/public`,本來就該長在來源這一側。
|
||||
* 唯一改動:產出物開頭那行註解改成指向本檔(不留「描述不存在機制的註解」)。
|
||||
*
|
||||
* 對外只有一個 API:`buildUiWorker({ uiDir })` → `{ source, fingerprint, fileCount }`。
|
||||
* 寫檔與 manifest 由 `build-worker-artifacts.mjs` 統一處理(產物格式與其他零件一致)。
|
||||
*/
|
||||
import { readdirSync, statSync, readFileSync, writeFileSync } from 'node:fs';
|
||||
import { join, relative, extname } from 'node:path';
|
||||
import { createHash } from 'node:crypto';
|
||||
|
||||
/**
|
||||
* @param {{ uiDir: string }} opts uiDir=`console-ui/public` 的絕對路徑
|
||||
* @returns {Promise<{ source: string, fingerprint: string, fileCount: number }>}
|
||||
*/
|
||||
export async function buildUiWorker({ uiDir }) {
|
||||
const UI = uiDir;
|
||||
|
||||
const TYPES = {
|
||||
'.html': 'text/html; charset=utf-8',
|
||||
'.js': 'application/javascript; charset=utf-8',
|
||||
'.mjs': 'application/javascript; charset=utf-8',
|
||||
'.css': 'text/css; charset=utf-8',
|
||||
'.json': 'application/json; charset=utf-8',
|
||||
'.svg': 'image/svg+xml',
|
||||
'.png': 'image/png',
|
||||
'.ico': 'image/x-icon',
|
||||
};
|
||||
const TEXT_EXT = new Set(['.html', '.js', '.mjs', '.css', '.json', '.svg']);
|
||||
// config.js 由 worker 動態產生(apiBase 注入),不吃靜態版
|
||||
const SKIP = new Set(['/config.js']);
|
||||
|
||||
// ⚠️ 一定要 sort():指紋是 JSON.stringify(files) 算的,鍵的順序=目錄列舉順序。
|
||||
// 不排序的話同樣的內容在不同機器會得到不同指紋(B 版就漏了這一步)。
|
||||
function walk(dir, base, out) {
|
||||
for (const name of readdirSync(dir).sort()) {
|
||||
const p = join(dir, name);
|
||||
if (statSync(p).isDirectory()) { walk(p, base, out); continue; }
|
||||
const key = '/' + relative(base, p).split('\\').join('/');
|
||||
if (SKIP.has(key)) continue;
|
||||
const ext = extname(name);
|
||||
const type = TYPES[ext];
|
||||
if (!type) continue; // 未知型別不進 bundle
|
||||
if (TEXT_EXT.has(ext)) out[key] = { type, b64: false, data: readFileSync(p, 'utf8') };
|
||||
else out[key] = { type, b64: true, data: readFileSync(p).toString('base64') };
|
||||
}
|
||||
}
|
||||
|
||||
// ── 閘① t160 世代閘(B 版)─────────────────────────────────────────────
|
||||
// 打包前驗 portal 頁指紋——舊世代(缺「不需要人工新增」文案/含人工建庫「登記新庫」表單)
|
||||
// 直接拒打包。病史:t159 重打包吃了停在舊世代的分支 public/,把 leo 實例的 UI 打回
|
||||
// 被淘汰的人工登記典範。
|
||||
// 🔴 2026-08-02 修誤判:原本直接對整份原始碼 grep「登記新庫」,但**現行世代的 HTML
|
||||
// 註解裡就寫著「07-27 leo:拿掉『登記新庫』…」**——那是「已經拿掉了」的紀錄,
|
||||
// 是新世代的證據,卻被當成舊世代特徵 ⇒ 閘把對的東西擋下來。
|
||||
// 正解:**先剝掉註解再驗**,只看用戶真的會看到的 UI 內容。
|
||||
{
|
||||
const portalRaw = readFileSync(join(UI, 'portal', 'index.html'), 'utf8');
|
||||
const portal = portalRaw.replace(/<!--[\s\S]*?-->/g, '');
|
||||
if (!portal.includes('不需要人工新增') || portal.includes('登記新庫')) {
|
||||
console.error('✘ 世代閘:--ui 指向的 public 不是現行世代(缺「不需要人工新增」或含「登記新庫」)——拒絕打包舊 UI。');
|
||||
console.error(` 剝註解後:不需要人工新增=${portal.includes('不需要人工新增')}/登記新庫=${portal.includes('登記新庫')}`);
|
||||
process.exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
const files = {};
|
||||
walk(UI, UI, files);
|
||||
if (!files['/portal/index.html']) throw new Error(`/portal/index.html 不在 ${UI} — 路徑錯了?`);
|
||||
|
||||
// ── 閘② t131 前端 JS 語法閘(A 版)────────────────────────────────────
|
||||
// 2026-07-29 事故:誤刪一個 `})();` ⇒ portal 白畫面,驗收沒抓到——
|
||||
// 因為測試只測後端、grep 只看字串,**沒有人驗過前端 JS 語法**。
|
||||
// 打包前用 node --check 驗每個 HTML 內嵌 script,語法錯就拒絕產出 bundle。
|
||||
{
|
||||
const { execFileSync } = await import('node:child_process');
|
||||
const { mkdtempSync } = await import('node:fs');
|
||||
const { tmpdir } = await import('node:os');
|
||||
const tmp = mkdtempSync(join(tmpdir(), 'uicheck-'));
|
||||
for (const [name, f] of Object.entries(files)) {
|
||||
if (!name.endsWith('.html') || f.b64) continue;
|
||||
const blocks = [...String(f.data).matchAll(/<script(?![^>]*\bsrc=)[^>]*>([\s\S]*?)<\/script>/g)];
|
||||
blocks.forEach((m, i) => {
|
||||
const jsPath = join(tmp, `${name.replace(/[^\w]/g, '_')}_${i}.js`);
|
||||
writeFileSync(jsPath, m[1]);
|
||||
try {
|
||||
execFileSync(process.execPath, ['--check', jsPath], { stdio: 'pipe' });
|
||||
} catch (e) {
|
||||
const msg = String(e.stderr || e.message).split('\n').slice(0, 6).join('\n');
|
||||
throw new Error(`❌ ${name} 的第 ${i + 1} 個 <script> 有語法錯,拒絕打包(會導致白畫面):\n${msg}`);
|
||||
}
|
||||
});
|
||||
}
|
||||
console.log('✅ 前端 JS 語法閘通過');
|
||||
}
|
||||
|
||||
// 🔴 2026-08-02(leo 實撞:「一樣從 install 安裝,為什麼會裝到錯誤版本?」)
|
||||
// UI 內容指紋——**UI 過去沒有自己的版本號**,安裝器只能猜特徵
|
||||
// (t168 猜「/favicon.svg 開頭是不是 <svg」),而那只能分辨「有沒有 favicon 路由」
|
||||
// 這一個世代差 ⇒ 舊 UI 早就有 favicon 路由,於是被判定「已是新版」
|
||||
// ⇒ **整批跳過、UI 永遠不重推**,用戶重裝幾次都拿到舊前端。
|
||||
// 正解不是再想一個新特徵去猜(下個世代還會再猜錯),是**讓 UI 自己報版本**。
|
||||
const uiFingerprint = createHash('sha256').update(JSON.stringify(files)).digest('hex');
|
||||
|
||||
const worker = `// arcrun-rag-ui — 用戶實例的 GUI(console-ui/public 內嵌;由 Arcrun scripts/build-ui-worker.mjs 產生,勿手改)
|
||||
const FILES = ${JSON.stringify(files)};
|
||||
const UI_FINGERPRINT = ${JSON.stringify(uiFingerprint)};
|
||||
function b64ToBytes(b64) { const bin = atob(b64); const a = new Uint8Array(bin.length); for (let i = 0; i < bin.length; i++) a[i] = bin.charCodeAt(i); return a; }
|
||||
export default {
|
||||
async fetch(request, env) {
|
||||
const url = new URL(request.url);
|
||||
let p = url.pathname;
|
||||
// 🔴 UI 自報版本(安裝器據此判斷要不要重推,取代「猜 favicon 特徵」)。
|
||||
// 比照 cypher 的 /health。舊世代的 UI 沒有這條路由 ⇒ SPA fallback 回 HTML
|
||||
// ⇒ 安裝器解析 JSON 失敗=判定為舊版=重推。**天然向後相容,不需額外世代判斷。**
|
||||
// bundle_version 由安裝器以 ARCRUN_BUNDLE_VERSION 注入(cypher /health 同一個值);
|
||||
// 沒注入就回空字串,安裝器那端把空值一律當「舊的」處理。
|
||||
if (p === '/__version') {
|
||||
// 🔴 2026-08-08:多報 worker_subdomain / config_ok 兩格。
|
||||
// 這條路由是**安裝器唯一會去問實例的地方**(probeInstanceStale),
|
||||
// 以前它只報版本 ⇒「版本對、但 apiBase 空」的實例在安裝器眼中是「已是最新」
|
||||
// ⇒ 永遠不會被重推=壞掉的實例自己好不了。報了之後,安裝器把
|
||||
// config_ok:false 一律當成 stale ⇒ **下一次安裝/更新就自動治好它**。
|
||||
return new Response(JSON.stringify({
|
||||
ok: true,
|
||||
ui_fingerprint: UI_FINGERPRINT,
|
||||
bundle_version: env.ARCRUN_BUNDLE_VERSION || '',
|
||||
worker_subdomain: env.WORKER_SUBDOMAIN || '',
|
||||
config_ok: Boolean(env.WORKER_SUBDOMAIN),
|
||||
}), {
|
||||
headers: { 'content-type': 'application/json; charset=utf-8', 'cache-control': 'no-store' },
|
||||
});
|
||||
}
|
||||
// config.js 動態產生:apiBase 指向同帳號的 cypher(WORKER_SUBDOMAIN 由安裝器注入)
|
||||
//
|
||||
// 🔴 2026-08-08 事故(leo 的 youlin 實例整頁不能用,頂端紅字「設定檔沒載入」):
|
||||
// 有人用 wrangler deploy 手動補這顆 worker ⇒ 繞過安裝器的變數注入
|
||||
// ⇒ WORKER_SUBDOMAIN 空 ⇒ 這裡**安靜地**退成 apiBase:""(而且回 HTTP 200!)
|
||||
// ⇒ 前端連不到自己的後端,但**部署是「成功」的**:worker 活著、curl 200、
|
||||
// /__version 版本也對——只有真人打開瀏覽器才看得出壞了。
|
||||
// 正解不是「退一個合理的預設值」(沒有合理預設,猜錯更慘),
|
||||
// 是**大聲失敗**:回 500+專用標頭,讓機器檢查一眼看穿。
|
||||
// 瀏覽器不執行非 2xx 的 <script>,所以用戶端行為與今天一致
|
||||
// (portal 照樣顯示「設定檔沒載入」紅字),差別是**機器也看得見了**。
|
||||
// no-store:這頁被邊緣快取過就會「修好了還是壞的」(08-08 實撞,害人誤判修復失敗)。
|
||||
if (p === '/config.js') {
|
||||
const sub = env.WORKER_SUBDOMAIN || '';
|
||||
if (!sub) {
|
||||
return new Response(CONFIG_MISSING_BODY, {
|
||||
status: 500,
|
||||
headers: {
|
||||
'content-type': TYPES_JS,
|
||||
'cache-control': 'no-store',
|
||||
'x-arcrun-config-error': 'WORKER_SUBDOMAIN-missing',
|
||||
},
|
||||
});
|
||||
}
|
||||
const apiBase = \`https://arcrun-cypher-executor.\${sub}.workers.dev\`;
|
||||
return new Response(\`window.ARCRUN_CONFIG = { apiBase: \${JSON.stringify(apiBase)} };\`, {
|
||||
headers: { 'content-type': TYPES_JS, 'cache-control': 'no-store' },
|
||||
});
|
||||
}
|
||||
if (p.endsWith('/')) p += 'index.html';
|
||||
let f = FILES[p] || FILES[p + '/index.html'] || FILES[p + '.html'];
|
||||
if (!f && !p.includes('.')) f = FILES[p + '/index.html'];
|
||||
if (!f) { p = '/portal/index.html'; f = FILES[p]; } // SPA fallback→portal
|
||||
if (!f) return new Response('not found', { status: 404 });
|
||||
const body = f.b64 ? b64ToBytes(f.data) : f.data;
|
||||
return new Response(body, { headers: { 'content-type': f.type, 'cache-control': 'no-cache' } });
|
||||
},
|
||||
};
|
||||
const TYPES_JS = 'application/javascript; charset=utf-8';
|
||||
const CONFIG_MISSING_BODY = [
|
||||
'/* ============================================================',
|
||||
' arcrun-rag-ui:WORKER_SUBDOMAIN 這個變數沒有被注入。',
|
||||
' 算不出 apiBase = 前端不知道自己的後端在哪 = 整個 portal 不能用。',
|
||||
'',
|
||||
' 成因幾乎一定是:這顆 worker 被手動 wrangler deploy 部署,繞過了注入。',
|
||||
' 修法(擇一):',
|
||||
' 1. 重跑一次安裝器(它會注入,且會把這顆判定為 stale 而重推)',
|
||||
' 2. node installer/scripts/deploy-ui.mjs --subdomain <你的 workers.dev 子網域>',
|
||||
' ============================================================ */',
|
||||
'throw new Error("arcrun-rag-ui: WORKER_SUBDOMAIN 未注入,apiBase 無法產生");',
|
||||
].join('\\n');
|
||||
`;
|
||||
return { source: worker, fingerprint: uiFingerprint, fileCount: Object.keys(files).length };
|
||||
}
|
||||
@@ -1,6 +1,8 @@
|
||||
#!/usr/bin/env node
|
||||
/**
|
||||
* build-worker-artifacts.mjs — Arcrun#80:tier2 worker(TS→ 可部署 JS)的**唯一官方編譯點**。
|
||||
* build-worker-artifacts.mjs — **一個 Arcrun 實例會用到的每一顆 worker,都在這裡編**。
|
||||
* 這是 D49/D91 的那個「唯一官方編譯點」:成品放 `.worker-builds/`、commit 進 repo,
|
||||
* 任何人(安裝器、出貨線、self-host 使用者)要用零件都來這裡拿,不准自己編一份。
|
||||
*
|
||||
* 背景(Arcrun#80/arcrun-rag#39):這個 repo 過去只把 tier1 零件(TinyGo→wasm)的成品
|
||||
* commit 進 `.component-builds/{name}/component.wasm`;tier2(cypher-executor / kbdb /
|
||||
@@ -9,6 +11,14 @@
|
||||
* 結果同一份原始碼在不同機器編出不同位元組(見下「已知踩坑」),
|
||||
* 而且「這顆成品是哪個 commit 編的」只有整包一個 `source` 欄位,答不出單顆的來源。
|
||||
*
|
||||
* 🔴 2026-08-15(D91/Arcrun#125)本檔的涵蓋範圍從 5 顆擴到 23 顆,理由不是「順手多編幾顆」:
|
||||
* · 只編 5 顆 ⇒ 下游 bundle 裡就只有 5 顆 ⇒ **「零件用到才下載」沒有貨可以下載**。
|
||||
* 而 `auth_static_key` 是每一支產品工作流解 `{{credential.X}}` 都會打的那一顆
|
||||
* ⇒ 走網頁安裝器裝出來的實例,工作流一跑就 500(Arcrun#124)。
|
||||
* · portal 前端(`arcrun-rag-ui`)當時仍由 arcrun-rag 自己拼裝
|
||||
* ⇒ 使用者拿到的那顆 worker,沒有任何 Arcrun commit 說得出它的來源。
|
||||
* ⇒ 現在三種東西走同一條路、同一份 manifest:引擎 worker/零件 worker/內嵌 UI worker。
|
||||
*
|
||||
* 本腳本要解的:
|
||||
* 1. 編譯只發生在這裡(Arcrun 本體),成品放固定位置 `.worker-builds/`,commit 進 repo——
|
||||
* 與 `.component-builds/*.wasm` 同一個既有慣例(self-host 用戶從 repo 直接拿部署來源)。
|
||||
@@ -37,13 +47,15 @@
|
||||
* 輸出:.worker-builds/<name>/worker.mjs (+ *.wasm)、.worker-builds/manifest.json
|
||||
*/
|
||||
import esbuild from 'esbuild';
|
||||
import { readFileSync, writeFileSync, mkdirSync, copyFileSync, existsSync, rmSync, readdirSync } from 'node:fs';
|
||||
import { readFileSync, writeFileSync, mkdirSync, copyFileSync, existsSync, rmSync, readdirSync, statSync } from 'node:fs';
|
||||
import { join, resolve, basename, relative } from 'node:path';
|
||||
import { fileURLToPath } from 'node:url';
|
||||
import { execSync } from 'node:child_process';
|
||||
import { createHash } from 'node:crypto';
|
||||
// Arcrun#108 出貨閘(見 main() 內註解)。規則本體與掃描器住在 cypher-executor/scripts/。
|
||||
import { scanProject as scanTenantSources } from '../cypher-executor/scripts/check-tenant-source.mjs';
|
||||
// UI worker 的內嵌器(2026-08-15 從 arcrun-rag 搬回來,D91)。
|
||||
import { buildUiWorker } from './build-ui-worker.mjs';
|
||||
|
||||
// REPO 一律用「本檔自己的位置」推導,不吃 cwd/env——這是踩坑①解法的地基:
|
||||
// 不管這個 clone 被放在磁碟哪個絕對路徑,REPO 永遠是「這個 repo 的根目錄」,
|
||||
@@ -52,17 +64,96 @@ const REPO = resolve(fileURLToPath(new URL('.', import.meta.url)), '..');
|
||||
const OUT = join(REPO, '.worker-builds');
|
||||
const CHECK_ONLY = process.argv.includes('--check-only');
|
||||
|
||||
/** 五顆 tier2 worker——與 arcrun-rag `installer/scripts/bundle-components.mjs`
|
||||
* CORE_COMPONENTS 的 build 參數對齊(那邊的 esbuild 呼叫即將被本腳本的成品取代)。
|
||||
/** 引擎與工具 worker(各自的 dir/entry 不成規律,所以逐顆寫)。
|
||||
* name 用 arcrun-rag 那邊的慣例(`arcrun-<kebab>`),方便安裝器直接對號。 */
|
||||
const WORKERS = [
|
||||
const ENGINE_WORKERS = [
|
||||
{ name: 'arcrun-cypher-executor', dir: 'cypher-executor', entry: 'src/index.ts', stripServices: true },
|
||||
{ name: 'arcrun-kbdb', dir: 'kbdb', entry: 'src/index.ts' },
|
||||
{ name: 'arcrun-http-request', dir: '.component-builds/http_request', entry: 'src/index.ts' },
|
||||
{ name: 'arcrun-code', dir: 'registry/components/code', entry: 'index.ts' },
|
||||
{ name: 'arcrun-mcp', dir: 'mcp', entry: 'src/index.ts' },
|
||||
];
|
||||
|
||||
/** 零件 worker 的家。底下每個子目錄=一顆零件(TinyGo 編好的 `component.wasm`
|
||||
* +一層 TS 外殼),格式一致,所以用**掃描**而不是手寫清單。 */
|
||||
const COMPONENT_BUILDS = '.component-builds';
|
||||
|
||||
/**
|
||||
* 掃出所有零件 worker。
|
||||
*
|
||||
* 🔴 2026-08-15(Arcrun#125/D91):這裡以前只手寫了 `http_request` **一顆**,
|
||||
* 其餘 18 顆零件從來沒有官方成品。後果不是「少編了幾顆」,是
|
||||
* **「用到才下載」沒有貨可以下載**——arcrun-rag 的 bundle 只裝得到本檔編出來的東西,
|
||||
* 本檔沒編,公庫裡就永遠沒有它。而 `auth_static_key` 正是每一支產品工作流
|
||||
* 解 `{{credential.X}}` 都會打的那一顆 ⇒ 凡走網頁安裝器裝出來的實例,
|
||||
* 工作流一跑就 500(Arcrun#124 的 `error code: 1042`)。
|
||||
*
|
||||
* 🔴 **不准改回手寫清單**:手寫清單就是 arcrun-rag#27/D48 那個病的形狀
|
||||
* ——「一份人維護的清單」與「磁碟上真的有什麼」必然漂移,而漂移的方向只有一個:
|
||||
* 新增的零件沒人記得加進去,於是它對使用者而言不存在。
|
||||
* worker 名一律讀該零件自己的 `wrangler.toml`(那是它部署時真正會叫的名字),
|
||||
* 不由本檔從目錄名推導——推導=再造一份會漂的真相。
|
||||
*/
|
||||
function discoverComponentWorkers() {
|
||||
const base = join(REPO, COMPONENT_BUILDS);
|
||||
if (!existsSync(base)) return { workers: [], excluded: [] };
|
||||
const workers = [];
|
||||
const excluded = [];
|
||||
for (const id of readdirSync(base).sort()) {
|
||||
const dir = join(COMPONENT_BUILDS, id);
|
||||
const abs = join(REPO, dir);
|
||||
if (!statSync(abs).isDirectory()) continue;
|
||||
const entry = 'src/index.ts';
|
||||
if (!existsSync(join(abs, entry))) continue; // 不是一顆 worker(例如暫存目錄)
|
||||
const name = readWorkerName(join(abs, 'wrangler.toml'));
|
||||
if (!name) throw new Error(`${dir}/wrangler.toml 沒有 name=這顆零件部署後會叫什麼名字沒人說得出來`);
|
||||
|
||||
// 🔴 可出貨的判準=**它的部署位元組在版控裡**,不是「這台機器的磁碟上有」。
|
||||
// `.gitignore` 明文排除三顆(claude_api/km_writer/kbdb_upsert_block)——理由不是
|
||||
// 「還沒編」,是 DECISIONS §1「它們被錯做成零件,該降級成工作流/recipe」。
|
||||
// 本機磁碟上仍留著上次編的 wasm,所以「有沒有檔案」分辨不出這件事,「有沒有進版控」可以。
|
||||
// ⇒ 用這個判準,未來任何一顆被判定不該出貨的零件,**不必回來改本檔**就會自動落在公庫外。
|
||||
if (!isTracked(join(dir, 'component.wasm'))) {
|
||||
excluded.push({ name, dir, reason: 'component.wasm 不在版控(.gitignore 明文排除=這顆不是可出貨的零件)' });
|
||||
continue;
|
||||
}
|
||||
workers.push({ name, dir, entry, component: id });
|
||||
}
|
||||
return { workers, excluded };
|
||||
}
|
||||
|
||||
/** 這個路徑有沒有被 git 追蹤(=任何 clone 都拿得到)。 */
|
||||
function isTracked(relPath) {
|
||||
try {
|
||||
const out = execSync(`git ls-files -- ${JSON.stringify(relPath)}`, { cwd: REPO, encoding: 'utf8' }).trim();
|
||||
return !!out;
|
||||
} catch {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/** 從 wrangler.toml 讀 `name = "..."`(worker 部署後的真名)。 */
|
||||
function readWorkerName(tomlPath) {
|
||||
if (!existsSync(tomlPath)) return null;
|
||||
const m = readFileSync(tomlPath, 'utf8').match(/^\s*name\s*=\s*["']([^"']+)["']/m);
|
||||
return m ? m[1] : null;
|
||||
}
|
||||
|
||||
/** 使用者實例的 GUI:沒有原始碼要打包,是把 `console-ui/public` 內嵌成單檔 worker。
|
||||
* 2026-08-15 從 arcrun-rag 搬回來(D91)——見 scripts/build-ui-worker.mjs 檔頭。 */
|
||||
const UI_WORKER = {
|
||||
name: 'arcrun-rag-ui',
|
||||
dir: 'console-ui/public',
|
||||
builder: 'ui',
|
||||
// 這顆沒有 wrangler.toml(它不由 wrangler 部署,是安裝器用 CF API 上傳的),
|
||||
// 所以 compat 值寫在這裡。**沿用搬家前 arcrun-rag manifest 條目的原值**,
|
||||
// 讓這次搬家只改「誰產生它」,不改「它是什麼」。
|
||||
compat_date: '2026-07-01',
|
||||
compat_flags: [],
|
||||
};
|
||||
|
||||
const { workers: COMPONENT_WORKERS, excluded: EXCLUDED_COMPONENTS } = discoverComponentWorkers();
|
||||
const WORKERS = [...ENGINE_WORKERS, ...COMPONENT_WORKERS, UI_WORKER];
|
||||
|
||||
function sha256(buf) {
|
||||
return createHash('sha256').update(buf).digest('hex');
|
||||
}
|
||||
@@ -143,7 +234,38 @@ function repoHead() {
|
||||
}
|
||||
}
|
||||
|
||||
/** UI worker:沒有原始碼要打包,是把 `console-ui/public` 內嵌成一顆單檔 worker。
|
||||
* 產出格式與 esbuild 那條一致(worker.mjs + manifest 條目),下游因此不必分辨它是哪種。 */
|
||||
async function buildUiOne(w) {
|
||||
const outDir = join(OUT, w.name);
|
||||
mkdirSync(outDir, { recursive: true });
|
||||
const { source, fingerprint, fileCount } = await buildUiWorker({ uiDir: join(REPO, w.dir) });
|
||||
const outFile = join(outDir, 'worker.mjs');
|
||||
writeFileSync(outFile, source);
|
||||
const jsBuf = readFileSync(outFile);
|
||||
return {
|
||||
name: w.name,
|
||||
source_dir: w.dir,
|
||||
source_commit: sourceCommitFor(w.dir),
|
||||
main_module: 'worker.mjs',
|
||||
main_file: `${w.name}/worker.mjs`,
|
||||
js_bytes: jsBuf.length,
|
||||
content_sha256: sha256(jsBuf),
|
||||
modules: [],
|
||||
compat_date: w.compat_date,
|
||||
compat_flags: w.compat_flags,
|
||||
// 內嵌 UI 不需要任何 binding;它只吃兩個由安裝器注入的 var
|
||||
// (WORKER_SUBDOMAIN 算 apiBase、ARCRUN_BUNDLE_VERSION 回報版本)。
|
||||
requires: { kv: [], d1: [], vectorize: 0, ai: false, vars: {} },
|
||||
// UI 自己的內容指紋——安裝器用它判斷「這台實例的前端要不要重推」。
|
||||
ui_fingerprint: fingerprint,
|
||||
ui_file_count: fileCount,
|
||||
warnings: [],
|
||||
};
|
||||
}
|
||||
|
||||
async function buildOne(w) {
|
||||
if (w.builder === 'ui') return buildUiOne(w);
|
||||
const dirAbs = join(REPO, w.dir);
|
||||
const entryAbs = join(dirAbs, w.entry);
|
||||
if (!existsSync(entryAbs)) throw new Error(`entry 不存在: ${entryAbs}`);
|
||||
@@ -205,7 +327,8 @@ async function buildOne(w) {
|
||||
async function main() {
|
||||
console.log(`REPO = ${REPO}`);
|
||||
|
||||
const precheck = WORKERS.map((w) => ({ w, chk: checkNodeModules(w.dir) }));
|
||||
// UI worker 沒有依賴要鎖(它內嵌的是靜態檔,不 import 任何套件)⇒ 不進這道閘。
|
||||
const precheck = WORKERS.filter((w) => w.builder !== 'ui').map((w) => ({ w, chk: checkNodeModules(w.dir) }));
|
||||
const failed = precheck.filter((p) => !p.chk.ok);
|
||||
if (failed.length) {
|
||||
console.error('\n❌ 建置中止:以下 worker 尚未按 lockfile 裝好依賴(踩坑②的閘):\n');
|
||||
@@ -213,6 +336,10 @@ async function main() {
|
||||
console.error('\n這是刻意設計:本腳本不自己跑 install,避免「install 方式不同 → 依賴版本不同 → 位元組不同」。');
|
||||
process.exit(1);
|
||||
}
|
||||
if (EXCLUDED_COMPONENTS.length) {
|
||||
console.log(`\nℹ️ 刻意不進公庫的零件(${EXCLUDED_COMPONENTS.length}):`);
|
||||
for (const e of EXCLUDED_COMPONENTS) console.log(` - ${e.name}:${e.reason}`);
|
||||
}
|
||||
console.log('✔ node_modules 檢查通過:');
|
||||
for (const p of precheck) console.log(` ${p.w.dir} (${p.chk.via})`);
|
||||
|
||||
@@ -245,9 +372,17 @@ async function main() {
|
||||
}
|
||||
|
||||
mkdirSync(OUT, { recursive: true });
|
||||
for (const w of WORKERS) {
|
||||
const d = join(OUT, w.name);
|
||||
if (existsSync(d)) rmSync(d, { recursive: true, force: true });
|
||||
// 🔴 輸出目錄要**只**留這次編出來的東西——包含「上次編了、這次不該再編」的那些。
|
||||
// 不清掉就是棘輪(D48/arcrun-rag#27 的真兇):東西一旦掉進成品目錄就永遠留著,
|
||||
// 沒有任何路徑會把它拿掉,於是「公庫裡有什麼」跟「本檔說該有什麼」慢慢分家。
|
||||
// 實撞(2026-08-15):claude_api 被判定不該出貨後,它上一輪的成品仍留在 .worker-builds/。
|
||||
const want = new Set(WORKERS.map((w) => w.name));
|
||||
for (const name of readdirSync(OUT)) {
|
||||
if (name === 'manifest.json') continue;
|
||||
const d = join(OUT, name);
|
||||
if (!statSync(d).isDirectory()) continue;
|
||||
if (!want.has(name)) console.log(` 🧹 清掉不該在公庫裡的舊成品:${name}`);
|
||||
rmSync(d, { recursive: true, force: true });
|
||||
}
|
||||
|
||||
const head = repoHead();
|
||||
@@ -258,6 +393,10 @@ async function main() {
|
||||
repo_head: head.sha,
|
||||
repo_dirty: head.dirty,
|
||||
workers: [],
|
||||
// 被刻意排除在公庫之外的零件——**寫出來**,不要安靜跳過:
|
||||
// 下游(arcrun-rag 的懶載清單)看得到「這顆不在公庫、以及為什麼」,
|
||||
// 才不會像 2026-08-14 那樣把「沒有貨」誤讀成「機制壞了」。
|
||||
excluded: EXCLUDED_COMPONENTS,
|
||||
notes: [],
|
||||
};
|
||||
|
||||
|
||||
Reference in New Issue
Block a user