sync: collector/ 同步自 inkstone/arcrun-rag@10eb2cb(桌面小幫手 0.18.53)
This commit is contained in:
@@ -1,10 +1,10 @@
|
||||
{
|
||||
"_algo": 4,
|
||||
"version": "0.18.52",
|
||||
"fingerprint": "5ab62f966033ea4b",
|
||||
"version": "0.18.53",
|
||||
"fingerprint": "32f62120aa472b65",
|
||||
"files": {
|
||||
".gitignore": "c7a98c44ecc882a174720e43dabd4c04d7cf2503586426dcccaa213d5a27df38",
|
||||
"CHANGELOG.md": "718ac1789302c16de2af1595b4081a0b0b2eff88ffd007f33f78720b79521c18",
|
||||
"CHANGELOG.md": "8032ea736ef5672f1f7971edad3ee3a707fe31fe0c5d0d05d34a35302e2a7853",
|
||||
"DAEMON_LINE": "d5019abbdc8a5f2919e9e3510391891cd7fbdf0765bf16ec83caa779f370116d",
|
||||
"README.md": "9d92cac236b20a0b183eea3e7f5e39ad492f05192c4ea602eb11c3d09967327f",
|
||||
"cardloop_test.go": "fdf09b62650c328f5d8075d964a0ca2dada5f8d9a271202768472998b45e31a8",
|
||||
@@ -25,7 +25,7 @@
|
||||
"cloudquota_test.go": "c2ab6ada0b09f2092a07b84e00090bb74dc81f8204906dde8dd4c53cfd24b9c1",
|
||||
"cmd/arcrun-app/.gitignore": "91248524c8b00d155960f42ad6707ce36bd91ff44f66289eec45972d3915ad37",
|
||||
"cmd/arcrun-app/README.md": "23e53fdf53fcd96b6d8d7a121eb91d3242d11b69d655770473b745c49bb387b9",
|
||||
"cmd/arcrun-app/app.go": "098e6a01aa0a298248ae9cc96275b209e5b8c9669325ba691c46383470216698",
|
||||
"cmd/arcrun-app/app.go": "55fddf43ad52aed29214d7a5a2ff4985acbdb2d0c7c0b724c2008e30e3f392e6",
|
||||
"cmd/arcrun-app/apps.go": "0df6da9d55adbdec500fbb01005a305287d1dbf796b7e463f2b2f222626b4c96",
|
||||
"cmd/arcrun-app/apps_test.go": "0e2f137f8b49f421e869ab3192ca12c8bb756029d8730487ccc44733aaf7f96c",
|
||||
"cmd/arcrun-app/assets/store/README.md": "a9028a727947c5038ca938c664b18e4851ae3bf0e6d8838c82aedea43d02ded2",
|
||||
@@ -95,7 +95,7 @@
|
||||
"cmd/arcrun-app/frontend/src/assets/fonts/OFL.txt": "8656c2a27357b3140047a39b27e3cd928256544ad8a004ef77087f761358208c",
|
||||
"cmd/arcrun-app/frontend/src/assets/fonts/nunito-v16-latin-regular.woff2": "06f3af3fe52542d40ad9bc14ec03e04deaabd09ec369221cc8f536db1c72bf55",
|
||||
"cmd/arcrun-app/frontend/src/assets/images/logo-universal.png": "157a874acd3363634747287c0b01034e9f1eec838a4cf39827dfea789e730494",
|
||||
"cmd/arcrun-app/frontend/src/main.js": "2c4ac3017bead3a99812ce8919a513fb0e23dd752285a426c649c6e774d0f08d",
|
||||
"cmd/arcrun-app/frontend/src/main.js": "f521930ec60300b5fb80400d40d3cd9da2a9bd8579d042e81f0db6635636d2f8",
|
||||
"cmd/arcrun-app/frontend/src/style.css": "2cc59ab0be7668f2b8e2e42f655beb506a91f2d31414e0a40dbab33fffc8349a",
|
||||
"cmd/arcrun-app/gen-icons.py": "df5b3b33e500fc564badfc8bd2a9ca6a43f7d7e3b31111c41e3b322137c4c96c",
|
||||
"cmd/arcrun-app/go.mod": "8b9e57bcfd44886d1ec06480e033af706ff3fed470786c57074900dd48ecf86f",
|
||||
@@ -110,6 +110,8 @@
|
||||
"cmd/arcrun-app/selfupdate_test.go": "1c0752f3b3f4f262620e096558ded38ed21db94195b760a3f3b046ce9dbdd4ab",
|
||||
"cmd/arcrun-app/skipped_test.go": "f4c9de16c349c0b7f48ce62a6506364c87e3d4fbc99f415e3e6aec7d3da7f1aa",
|
||||
"cmd/arcrun-app/supervise.go": "f7eb6c74524b5d8d0abe520b831a98b85d19d18c00d1a585493ababcbd01c81f",
|
||||
"cmd/arcrun-app/syncing_sub.go": "8d681209809da3324fddb30a06319ffaa19907bfa3763109ca2e33ae6ac50b5f",
|
||||
"cmd/arcrun-app/syncing_sub_test.go": "db32b573d087bbef3f1c69e30b714a02be64ea169a8f777962561e8f9f1c6259",
|
||||
"cmd/arcrun-app/tray_darwin.go": "ff3cb22fc2663b08b9f3cd14887ab4411ed338ec8920b58ad0fac029f09edae0",
|
||||
"cmd/arcrun-app/tray_darwin.m": "b4ddf0bb420adf00d580d65c8cfbbe130a78e803dd1e92a1617ddaf6ef172fac",
|
||||
"cmd/arcrun-app/tray_other.go": "0c3df95b5f7999f6319d0bf5e24fdbf821bf97a79db04077d9e7eaf358299880",
|
||||
@@ -128,7 +130,7 @@
|
||||
"convert_table_test.go": "d0371b7566ef3152f9dd42f9f990e0dffa1c50a0c8e874a28415fa2e4c188394",
|
||||
"convert_test.go": "04f3fa30d1be5f910c0e0be3308ced2963191ab030a9eaccd986ef581fcd4e18",
|
||||
"convert_wiring_test.go": "3389817e596341369a3fc102846bf5b081cee668e86ebf4830eb633978de652b",
|
||||
"direct.go": "3a79d03fce931115739a0113494a61331155172c7f9e5cbee126203b3e616458",
|
||||
"direct.go": "d7122f5efe522fd6be285402ee20c63af4b5545cf48591089ed9c5ee2d34a852",
|
||||
"direct_extract_test.go": "317d0057d2d368fabc406351a7477f72e074d2b8c8459b1dc021febdf16902f4",
|
||||
"direct_multi_test.go": "a76593fef26231e18b7402f6f7376624d2471f8a719b3ce08fc4a0f5a3d3cf1e",
|
||||
"direct_pacing.go": "b54838ce85f72ecd2d3388fa7cf89050c87fb10bd67dca07cd18d49568a8837c",
|
||||
@@ -148,7 +150,7 @@
|
||||
"folderindex_helpers_test.go": "2cf17b70c396225f728e8e160cfc75d7bdc76ac1ce8e574d1450b020050c0734",
|
||||
"folderindex_test.go": "a5319eb1e92473cb5c32d3beddb7889997556227824de300dd39953b43beab70",
|
||||
"foldershape.go": "b1b237f5674674efdc9bdea849b63c571a014e64bec33af42fb7c4769fa7582b",
|
||||
"foldertree.go": "fb86c8d869a476d77d548e9c9a7c14be51c76fa1400a6497016b705ff9c4ce13",
|
||||
"foldertree.go": "7a7d478f7c6fb75536d149d1a61a0643358d77b3de5e5b043397052923c74784",
|
||||
"foldertree_live_test.go": "ace96ffdf7670a69761973e810fb6548c7036d2768141e09db515313df1e9a50",
|
||||
"foldertree_local_test.go": "ce919dc2a21ec96716e3d43a7f493367346e212243b4ab22c2d960fc26408f3a",
|
||||
"foldertree_machine_test.go": "f5dc73afc261b0fb0298f27962628e8538abc704540b52d66e13bf85600a59dd",
|
||||
@@ -168,6 +170,8 @@
|
||||
"inventory_test.go": "16e0f83a09d61dc4cded032fd493d30bfc747c38116b871fb02b2f0bf2b0216a",
|
||||
"lint.go": "63af1289978a02c9722ac8bb934a916b9e4e65cffcfec0749eb07590496960f3",
|
||||
"lint_test.go": "2f57a04b9d697fba2d078eb08f49d2fec1640c7e3c61a60bc3821d2bb0798153",
|
||||
"live_status.go": "3395ea5cfcf1ddb137e6e3e2d243db88626dd3d2dd313c594f09cde9986eba54",
|
||||
"live_status_test.go": "883ea2b9b70f41e20bd861b124f9101cbf98cab14fb7a3a98bc4b309eb9ffbb2",
|
||||
"logseq_bak_residue_test.go": "52aa2f47ac12ca2e77e6112d6de88c873f1f418bae32b2a4de7a35028f835257",
|
||||
"machineid.go": "d4224e684ecfbafe2efc7e0b95377f9e669ee1256a442aa72ce2cab3b219fc56",
|
||||
"machineid_test.go": "5b54ac62ee1fb28b8f0a873888aeddb844af0aa74472bfb849573cfc6691a290",
|
||||
@@ -199,13 +203,13 @@
|
||||
"sourcerepair.go": "59fa1956181904f6661ab397e42b4f1c9b8ac81f382fdf720cc5b0831390f9d9",
|
||||
"sourcerepair_live_test.go": "2a7f5d6fada420a7f55fa52b87361842755c68c1cb58f8c3fd0153341f709a0a",
|
||||
"sourcerepair_test.go": "2a1ddddaad8ac7adc36350859e2d742c0fea12b99370a4d4831d390db1cc6720",
|
||||
"stallguard.go": "13be8ae905492054d516f466856bc6949b16e1af6ea32068f94b21635799bdc6",
|
||||
"stallguard.go": "3f5d36d72f84cc3431536dbb20219bd4793b11e573b5ac7f817cbe9e45d234d7",
|
||||
"stallguard_test.go": "678b82a8448d1394a0a85d4af1f133c980f5ff0dcb54953b0fcf1671d186147f",
|
||||
"supervisor/hidewindow_other.go": "291b4ecb0783ff3e813c68b5464161d9af2f84d31c773c7f1f710f218bb5d610",
|
||||
"supervisor/hidewindow_windows.go": "29796dbe90ecb35e6f1451dacf956d42928c6a393271befc5c7f6863d5315fb3",
|
||||
"supervisor/supervisor.go": "f79b32e92e67058a056dc749a6182573eefcbbc9bc7b2cd5a997f4e8066a004e",
|
||||
"supervisor/supervisor_test.go": "f5582d54c55065fbc834fe96754df9c8cd6efe9f88f30208bbce84ddea08fea7",
|
||||
"sync_status.go": "799d3481c361647c9cf1ae40ac4c550d6690a87f843c514f8b830d811e7bba1f",
|
||||
"sync_status.go": "353256d46d5cc9521c7798637dbaa06d2d0deabf163605409ca94e59fa2e2098",
|
||||
"sync_status_test.go": "73bbc83c9e8fc18575895a7b7b5feab7a76d41f05a74d74bbbdbf98b045ccfa2",
|
||||
"t52_library_test.go": "14188d0afdad0f9aa16d19a74ea28c45c3d18fcfeabc5deb6fb0466d05778a51",
|
||||
"template_install.go": "d0aa0098e431696a63ef8d3714c124591b8ba193ccd5a8359a4ea03e397ee358",
|
||||
|
||||
@@ -22,5 +22,6 @@
|
||||
"0.18.49": "7cb86e646aeaa057",
|
||||
"0.18.50": "b30716c3ccec9e65",
|
||||
"0.18.51": "3cfcc815cf185a68",
|
||||
"0.18.52": "5ab62f966033ea4b"
|
||||
"0.18.52": "5ab62f966033ea4b",
|
||||
"0.18.53": "32f62120aa472b65"
|
||||
}
|
||||
|
||||
@@ -126,6 +126,8 @@ type syncStatus struct {
|
||||
// 同上:形狀定義在 collector/progress.go/sync_status.go,這裡原樣接住。
|
||||
// 這是資料夾那一列打不打勾的**唯一**依據(見 folder_badge.go)。
|
||||
FolderProgress map[string]collector.SyncProgress `json:"folder_progress,omitempty"`
|
||||
// arcrun-rag#200:一輪還沒跑完時做到哪(形狀定義在 collector/sync_status.go)。
|
||||
InRound *collector.RoundProgress `json:"in_round,omitempty"`
|
||||
}
|
||||
|
||||
type skippedDoc struct {
|
||||
@@ -649,7 +651,7 @@ func describeStatus(s syncStatus) (syncing bool, big, sub string) {
|
||||
return false, "同步引擎沒有在跑", sub
|
||||
}
|
||||
if collectorSyncing() {
|
||||
return true, "同步中… 正在讀檔並整理成知識卡", "請稍候,完成後會顯示整理了幾份"
|
||||
return true, "同步中… 正在讀檔並整理成知識卡", syncingSub(s.InRound, time.Now(), accountLabelFor)
|
||||
}
|
||||
if s.ExtractorError != "" && !s.ExtractorOK {
|
||||
return false, "需要你處理一下", "⚠ " + s.ExtractorError
|
||||
|
||||
@@ -1140,7 +1140,39 @@ function render(s) {
|
||||
if (first && page === 'apps' && (s.accounts || []).length) loadApps(appsAccIdx);
|
||||
}
|
||||
|
||||
async function tick() { try { render(await go.GetState()); } catch (e) {} }
|
||||
async function tick() { try { render(await go.GetState()); } catch (e) {} refreshOpenTrees(); }
|
||||
|
||||
// 🔴 `inkstone/arcrun-rag#200`:展開著的樹要跟著同步進度走。
|
||||
//
|
||||
// 以前樹第一次展開讀一次就一直留在記憶體裡——而 collector 一輪途中每送上一份就會
|
||||
// 重寫 folder-trees.json。leo 2026-09-13 看著 ISEP 每層 0 / N 等了一整晚,
|
||||
// 雲端那時已經收到 11 份:**就算檔案更新了,畫面也不會去讀**。
|
||||
//
|
||||
// 只重讀「現在展開著」的那幾棵,每 3 秒一次(不是每秒——樹上限 300 個節點,
|
||||
// GetFolderTree 註解 ③ 講過為什麼不掛在每秒的 GetState 上)。內容沒變就不重畫,
|
||||
// 使用者正在點開的節點與「為什麼」不會因為重讀而跳掉。讀失敗不吵,下一次再試。
|
||||
let treeRefreshAt = 0;
|
||||
let treeRefreshing = false;
|
||||
async function refreshOpenTrees() {
|
||||
const now = Date.now();
|
||||
if (treeRefreshing || now - treeRefreshAt < 3000) return;
|
||||
treeRefreshAt = now;
|
||||
treeRefreshing = true;
|
||||
try {
|
||||
for (const path of Object.keys(treeState.open)) {
|
||||
if (!treeState.open[path] || treeState.data[path] === undefined) continue;
|
||||
try {
|
||||
const next = (await go.GetFolderTree(path)) || null;
|
||||
if (JSON.stringify(next) !== JSON.stringify(treeState.data[path])) {
|
||||
treeState.data[path] = next;
|
||||
renderFolderTree(path);
|
||||
}
|
||||
} catch (e) { /* 下一次再試 */ }
|
||||
}
|
||||
} finally {
|
||||
treeRefreshing = false;
|
||||
}
|
||||
}
|
||||
|
||||
// ── 動作 ──
|
||||
$('btnSync').onclick = async () => { await go.SyncNow(); tick(); };
|
||||
|
||||
@@ -0,0 +1,90 @@
|
||||
// syncing_sub.go — 「同步中」底下那一行要講得出做到哪、卡在哪(`inkstone/arcrun-rag#200`)。
|
||||
//
|
||||
// 🔴 病(2026-09-13 leo 截圖):畫面整晚寫「同步中… 正在讀檔並整理成知識卡/
|
||||
// 請稍候,完成後會顯示整理了幾份」,而 collector.log 裡明明印著
|
||||
// 「還在等知識庫 leo21c 回覆『送出一份筆記』,已經等了 30 秒」。
|
||||
// **會說話的那句只寫進了記錄檔,畫面上是一句永遠不變的「請稍候」**——
|
||||
// 使用者分不出「正在做」和「卡住了」,只能猜。
|
||||
//
|
||||
// 資料來源只有一個:collector 一輪途中寫進 status.json 的 in_round(collector/live_status.go)。
|
||||
// 這裡不判斷任何事,只把它念成一句話。
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"arcrun-rag/collector"
|
||||
)
|
||||
|
||||
// syncingIdleWarnAfter=多久沒有任何新寫入,就明講「已經 N 分鐘沒有新進展」。
|
||||
// 「還在等」每 30 秒會寫一次,所以正常等待不會碰到這條;碰到=真的沒在動。
|
||||
const syncingIdleWarnAfter = 5 * time.Minute
|
||||
|
||||
const syncingSubFallback = "請稍候,完成後會顯示整理了幾份"
|
||||
|
||||
// syncingSub 把「這一輪做到哪」念成一句話。r 為 nil(舊版 collector、剛開工還沒寫)=沿用原本那句。
|
||||
// label 把主機名翻成使用者認得的知識庫名字(測試可以換掉)。
|
||||
func syncingSub(r *collector.RoundProgress, now time.Time, label func(host string) string) string {
|
||||
if r == nil {
|
||||
return syncingSubFallback
|
||||
}
|
||||
where := ""
|
||||
if r.Account != "" {
|
||||
where = label(r.Account)
|
||||
}
|
||||
if r.Folder != "" {
|
||||
name := filepath.Base(r.Folder)
|
||||
if where != "" {
|
||||
where = fmt.Sprintf("%s(%s)", name, where)
|
||||
} else {
|
||||
where = name
|
||||
}
|
||||
}
|
||||
|
||||
// 卡住的時候,只講卡在哪——這是使用者此刻唯一需要知道的事。
|
||||
if w := r.Waiting; w != nil && strings.TrimSpace(w.Note) != "" {
|
||||
if where != "" {
|
||||
return fmt.Sprintf("卡在「%s」:%s", where, w.Note)
|
||||
}
|
||||
return "卡住了:" + w.Note
|
||||
}
|
||||
|
||||
var parts []string
|
||||
if where != "" {
|
||||
head := "正在處理 " + where
|
||||
if r.Step != "" {
|
||||
head += ":" + r.Step
|
||||
}
|
||||
parts = append(parts, head)
|
||||
}
|
||||
if r.Ingested > 0 {
|
||||
parts = append(parts, fmt.Sprintf("這一輪已送上 %d 份", r.Ingested))
|
||||
}
|
||||
if r.Failed > 0 {
|
||||
parts = append(parts, fmt.Sprintf("%d 份沒送成功", r.Failed))
|
||||
}
|
||||
if t, err := time.Parse(time.RFC3339, r.UpdatedAt); err == nil {
|
||||
if idle := now.Sub(t); idle > syncingIdleWarnAfter {
|
||||
parts = append(parts, fmt.Sprintf("已經 %d 分鐘沒有新進展", int(idle.Minutes())))
|
||||
}
|
||||
}
|
||||
if len(parts) == 0 {
|
||||
return syncingSubFallback
|
||||
}
|
||||
return strings.Join(parts, " · ")
|
||||
}
|
||||
|
||||
// accountLabelFor 把 status.json 裡的主機名翻成畫面左邊那個知識庫名字(與 UIAccount.Name 同一套)。
|
||||
func accountLabelFor(host string) string {
|
||||
if cfg, err := loadCfg(); err == nil {
|
||||
for _, a := range cfg.Accounts {
|
||||
if shortHost(a.CypherURL) == host {
|
||||
return accountName(a)
|
||||
}
|
||||
}
|
||||
}
|
||||
return host
|
||||
}
|
||||
@@ -0,0 +1,85 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"arcrun-rag/collector"
|
||||
)
|
||||
|
||||
// TestSyncingSubSaysWhereAndWhy 守住 `inkstone/arcrun-rag#200`:
|
||||
// 「同步中」底下那一行要講得出做到哪、卡在哪,不再是一句永遠不變的「請稍候」。
|
||||
func TestSyncingSubSaysWhereAndWhy(t *testing.T) {
|
||||
now := time.Date(2026, 9, 14, 0, 30, 0, 0, time.Local)
|
||||
label := func(host string) string {
|
||||
if host == "arcrun-cypher-executor.leo21c.workers.dev" {
|
||||
return "leo21c"
|
||||
}
|
||||
return host
|
||||
}
|
||||
fresh := now.Add(-10 * time.Second).Format(time.RFC3339)
|
||||
|
||||
cases := []struct {
|
||||
name string
|
||||
in *collector.RoundProgress
|
||||
want []string // 全部都要出現
|
||||
not []string // 一個都不准出現
|
||||
}{
|
||||
{
|
||||
name: "舊版 collector 沒寫 in_round=沿用原本那句",
|
||||
in: nil,
|
||||
want: []string{syncingSubFallback},
|
||||
},
|
||||
{
|
||||
name: "正在送=講資料夾、知識庫、哪件事、送了幾份",
|
||||
in: &collector.RoundProgress{
|
||||
Account: "arcrun-cypher-executor.leo21c.workers.dev", Folder: "/Users/x/Documents/tech_projects/ISEP",
|
||||
Step: "送出一份筆記", Ingested: 11, UpdatedAt: fresh,
|
||||
},
|
||||
want: []string{"正在處理 ISEP(leo21c)", "送出一份筆記", "這一輪已送上 11 份"},
|
||||
not: []string{"請稍候", "分鐘沒有新進展"},
|
||||
},
|
||||
{
|
||||
name: "卡住=只講卡在哪",
|
||||
in: &collector.RoundProgress{
|
||||
Account: "arcrun-cypher-executor.leo21c.workers.dev", Folder: "/Users/x/Documents/KB",
|
||||
Step: "送出目錄索引", UpdatedAt: fresh,
|
||||
Waiting: &collector.StalledCall{Step: "送出目錄索引",
|
||||
Note: "還在等知識庫「arcrun-cypher-executor.leo21c.workers.dev」回覆「送出目錄索引」,已經等了 30 秒。"},
|
||||
},
|
||||
want: []string{"卡在「KB(leo21c)」", "已經等了 30 秒"},
|
||||
not: []string{"請稍候", "正在處理"},
|
||||
},
|
||||
{
|
||||
name: "很久沒有任何寫入=明講沒在動",
|
||||
in: &collector.RoundProgress{
|
||||
Account: "arcrun-cypher-executor.leo21c.workers.dev", Step: "確認雲端 AI 可不可以用",
|
||||
UpdatedAt: now.Add(-12 * time.Minute).Format(time.RFC3339),
|
||||
},
|
||||
want: []string{"正在處理 leo21c", "已經 12 分鐘沒有新進展"},
|
||||
},
|
||||
{
|
||||
name: "有失敗也要說",
|
||||
in: &collector.RoundProgress{
|
||||
Account: "h", Folder: "/a/B", Ingested: 2, Failed: 1, UpdatedAt: fresh,
|
||||
},
|
||||
want: []string{"這一輪已送上 2 份", "1 份沒送成功"},
|
||||
},
|
||||
}
|
||||
for _, c := range cases {
|
||||
t.Run(c.name, func(t *testing.T) {
|
||||
got := syncingSub(c.in, now, label)
|
||||
for _, w := range c.want {
|
||||
if !strings.Contains(got, w) {
|
||||
t.Errorf("缺「%s」:%q", w, got)
|
||||
}
|
||||
}
|
||||
for _, n := range c.not {
|
||||
if strings.Contains(got, n) {
|
||||
t.Errorf("不該出現「%s」:%q", n, got)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user