opencode-extended-sidebar 0.2.90 → 0.2.91
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/.oesignore +17 -17
- package/CHANGELOG.md +96 -95
- package/LICENSE +21 -21
- package/README.md +150 -235
- package/oes.json +13 -11
- package/package.json +100 -100
- package/scripts/install-git-hooks.mjs +29 -29
- package/src/pware.oc.core/constants/index.ts +18 -18
- package/src/pware.oc.core/constants/pware.oc.core.constants.eventKind.ts +29 -29
- package/src/pware.oc.core/constants/pware.oc.core.constants.eventName.ts +15 -13
- package/src/pware.oc.core/constants/pware.oc.core.constants.eventType.ts +241 -241
- package/src/pware.oc.core/constants/pware.oc.core.constants.myWork.ts +55 -55
- package/src/pware.oc.core/constants/pware.oc.core.constants.partType.ts +65 -65
- package/src/pware.oc.core/constants/pware.oc.core.constants.phase.ts +58 -58
- package/src/pware.oc.core/constants/pware.oc.core.constants.pulse.ts +63 -63
- package/src/pware.oc.core/constants/pware.oc.core.constants.rowKind.ts +34 -34
- package/src/pware.oc.core/constants/pware.oc.core.constants.status.ts +74 -74
- package/src/pware.oc.core/constants/pware.oc.core.constants.toolName.ts +69 -69
- package/src/pware.oc.core/git/index.ts +7 -7
- package/src/pware.oc.core/git/pware.oc.core.git.ts +237 -237
- package/src/pware.oc.core/git/pware.oc.core.gitignore.ts +87 -87
- package/src/pware.oc.core/index.ts +19 -18
- package/src/pware.oc.core/pware.oc.core.bus.ts +42 -42
- package/src/pware.oc.core/pware.oc.core.cache.ts +48 -48
- package/src/pware.oc.core/pware.oc.core.clipboard.ts +97 -97
- package/src/pware.oc.core/pware.oc.core.debug.ts +280 -280
- package/src/pware.oc.core/pware.oc.core.events.ts +79 -79
- package/src/pware.oc.core/pware.oc.core.glyph.ts +135 -135
- package/src/pware.oc.core/pware.oc.core.layout.ts +234 -234
- package/src/pware.oc.core/pware.oc.core.oes.ts +99 -89
- package/src/pware.oc.core/pware.oc.core.paths.ts +240 -240
- package/src/pware.oc.core/pware.oc.core.preview.ts +74 -74
- package/src/pware.oc.core/pware.oc.core.pulse.ts +726 -726
- package/src/pware.oc.core/pware.oc.core.runtimeConfig.ts +41 -0
- package/src/pware.oc.core/pware.oc.core.sqlite.ts +164 -175
- package/src/pware.oc.core/pware.oc.core.sqliteGateway.ts +89 -0
- package/src/pware.oc.core/pware.oc.core.status.ts +214 -214
- package/src/pware.oc.core/pware.oc.core.timing.ts +63 -63
- package/src/pware.oc.core/pware.oc.core.width.ts +215 -215
- package/src/pware.oc.omo/constants/index.ts +15 -15
- package/src/pware.oc.omo/constants/pware.oc.omo.constants.backgroundTask.ts +18 -18
- package/src/pware.oc.omo/constants/pware.oc.omo.constants.boulderStatus.ts +49 -49
- package/src/pware.oc.omo/constants/pware.oc.omo.constants.docKind.ts +29 -29
- package/src/pware.oc.omo/constants/pware.oc.omo.constants.eventName.ts +11 -11
- package/src/pware.oc.omo/constants/pware.oc.omo.constants.planStatus.ts +60 -60
- package/src/pware.oc.omo/constants/pware.oc.omo.constants.reviewStatus.ts +49 -49
- package/src/pware.oc.omo/constants/pware.oc.omo.constants.startWork.ts +25 -25
- package/src/pware.oc.omo/constants/pware.oc.omo.constants.verdict.ts +38 -38
- package/src/pware.oc.omo/index.ts +7 -7
- package/src/pware.oc.omo/resolver/index.ts +19 -19
- package/src/pware.oc.omo/resolver/pware.oc.omo.resolver.approval.ts +179 -179
- package/src/pware.oc.omo/resolver/pware.oc.omo.resolver.approvalGroup.ts +116 -116
- package/src/pware.oc.omo/resolver/pware.oc.omo.resolver.approvalState.ts +34 -34
- package/src/pware.oc.omo/resolver/pware.oc.omo.resolver.boulder.ts +503 -503
- package/src/pware.oc.omo/resolver/pware.oc.omo.resolver.config.ts +40 -40
- package/src/pware.oc.omo/resolver/pware.oc.omo.resolver.doc.ts +269 -269
- package/src/pware.oc.omo/resolver/pware.oc.omo.resolver.draftFile.ts +18 -18
- package/src/pware.oc.omo/resolver/pware.oc.omo.resolver.notepadsFile.ts +18 -18
- package/src/pware.oc.omo/resolver/pware.oc.omo.resolver.plan.ts +155 -155
- package/src/pware.oc.omo/resolver/pware.oc.omo.resolver.planFile.ts +363 -361
- package/src/pware.oc.omo/resolver/pware.oc.omo.resolver.proofFile.ts +18 -18
- package/src/pware.oc.omo/resolver/pware.oc.omo.resolver.rulesFile.ts +11 -11
- package/src/pware.oc.omo/resolver/pware.oc.omo.resolver.runContinuationFile.ts +14 -14
- package/src/pware.oc.opencode/constants/index.ts +10 -10
- package/src/pware.oc.opencode/constants/pware.oc.opencode.constants.eventName.ts +15 -15
- package/src/pware.oc.opencode/constants/pware.oc.opencode.constants.fileTouch.ts +18 -18
- package/src/pware.oc.opencode/constants/pware.oc.opencode.constants.questionKind.ts +45 -45
- package/src/pware.oc.opencode/constants/pware.oc.opencode.constants.sessionStatus.ts +58 -58
- package/src/pware.oc.opencode/index.ts +8 -8
- package/src/pware.oc.opencode/pware.oc.opencode.events.ts +182 -182
- package/src/pware.oc.opencode/pware.oc.opencode.files.ts +399 -399
- package/src/pware.oc.opencode/resolver/index.ts +223 -170
- package/src/pware.oc.opencode/resolver/pware.oc.opencode.resolver.file.ts +150 -162
- package/src/pware.oc.opencode/resolver/pware.oc.opencode.resolver.question.ts +149 -185
- package/src/pware.oc.opencode/resolver/pware.oc.opencode.resolver.session.ts +248 -211
- package/src/pware.oc.opencode/resolver/pware.oc.opencode.resolver.todo.ts +22 -26
- package/src/pware.oc.opencode/resolver/pware.oc.opencode.resolver.tool.ts +224 -236
- package/src/pware.oc.perf/index.ts +14 -14
- package/src/pware.oc.perf/pware.oc.perf.asciichart.d.ts +15 -15
- package/src/pware.oc.perf/pware.oc.perf.charts.ts +284 -284
- package/src/pware.oc.perf/pware.oc.perf.reader.ts +962 -927
- package/src/pware.oc.perf/pware.oc.perf.realtime.ts +88 -88
- package/src/pware.oc.perf/pware.oc.perf.realtimeBlock.ts +118 -118
- package/src/pware.oc.perf/pware.oc.perf.realtimeCpuRam.ts +79 -79
- package/src/pware.oc.perf/pware.oc.perf.realtimeSampler.ts +71 -71
- package/src/pware.oc.perf/pware.oc.perf.realtimeTimeline.ts +214 -214
- package/src/pware.oc.perf/pware.oc.perf.self.ts +179 -179
- package/src/pware.oc.perf/pware.oc.perf.view.tsx +463 -462
- package/src/pware.oc.runtime/index.ts +13 -13
- package/src/pware.oc.runtime/pware.oc.runtime.monitor.ts +142 -142
- package/src/pware.oc.runtime/pware.oc.runtime.mywork-enrich.ts +87 -79
- package/src/pware.oc.runtime/pware.oc.runtime.mywork.ts +282 -253
- package/src/pware.oc.runtime/pware.oc.runtime.omoRead.ts +37 -0
- package/src/pware.oc.runtime/pware.oc.runtime.questions.ts +83 -95
- package/src/pware.oc.runtime/pware.oc.runtime.snapshotClient.ts +130 -129
- package/src/pware.oc.runtime/pware.oc.runtime.source.ts +105 -105
- package/src/pware.oc.runtime/pware.oc.runtime.worker.ts +44 -43
- package/src/pware.oc.runtime/resolver/index.ts +268 -224
- package/src/pware.oc.runtime/resolver/pware.oc.runtime.resolver.delegate.ts +146 -146
- package/src/pware.oc.ui/index.ts +12 -12
- package/src/pware.oc.ui/pware.oc.ui.chrome.tsx +381 -339
- package/src/pware.oc.ui/pware.oc.ui.glyphs.tsx +144 -144
- package/src/pware.oc.ui/pware.oc.ui.host.tsx +111 -111
- package/src/pware.oc.ui/pware.oc.ui.live.tsx +82 -82
- package/src/pware.oc.ui/pware.oc.ui.menudialogs.tsx +843 -843
- package/src/pware.oc.ui/pware.oc.ui.sections.tsx +552 -526
- package/src/pware.oc.ui/pware.oc.ui.sidebar.tsx +1718 -1629
- package/src/pware.oc.ui.tsx +98 -98
|
@@ -1,95 +1,83 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* pware.oc.runtime.questions
|
|
3
|
-
*
|
|
4
|
-
* In-memory, per-session cache of open questions for the "My work" queue. A
|
|
5
|
-
* later wiring task feeds it from cheap host-event hints (targeted
|
|
6
|
-
* `listSessionQuestions` on the session a `message.part.updated` just touched)
|
|
7
|
-
* and a periodic `listOpenQuestions` backstop, replacing the expensive
|
|
8
|
-
* project-wide scan that used to run on every snapshot.
|
|
9
|
-
*
|
|
10
|
-
* `createQuestionCache()` returns a fresh instance — no module-level mutable
|
|
11
|
-
* singleton — so tests build their own and the wiring task owns the one real
|
|
12
|
-
* instance. State is `bySession`, a Map keyed by `sessionId`; `get` flattens
|
|
13
|
-
* and dedupes it. The two read functions below already soft-fail to `[]`, so
|
|
14
|
-
* this cache only holds/merges their results and needs no try/catch of its own.
|
|
15
|
-
*/
|
|
16
|
-
import {
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
/**
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
}
|
|
52
|
-
out
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
const result = listSessionQuestions({ dbPath, sessionId, projectId })
|
|
85
|
-
if (result.length > 0) bySession.set(sessionId, result)
|
|
86
|
-
else bySession.delete(sessionId)
|
|
87
|
-
},
|
|
88
|
-
reconcile(dbPath, projectId) {
|
|
89
|
-
refill(dbPath, projectId)
|
|
90
|
-
},
|
|
91
|
-
reset() {
|
|
92
|
-
bySession = new Map()
|
|
93
|
-
},
|
|
94
|
-
}
|
|
95
|
-
}
|
|
1
|
+
/**
|
|
2
|
+
* pware.oc.runtime.questions
|
|
3
|
+
*
|
|
4
|
+
* In-memory, per-session cache of open questions for the "My work" queue. A
|
|
5
|
+
* later wiring task feeds it from cheap host-event hints (targeted
|
|
6
|
+
* `listSessionQuestions` on the session a `message.part.updated` just touched)
|
|
7
|
+
* and a periodic `listOpenQuestions` backstop, replacing the expensive
|
|
8
|
+
* project-wide scan that used to run on every snapshot.
|
|
9
|
+
*
|
|
10
|
+
* `createQuestionCache()` returns a fresh instance — no module-level mutable
|
|
11
|
+
* singleton — so tests build their own and the wiring task owns the one real
|
|
12
|
+
* instance. State is `bySession`, a Map keyed by `sessionId`; `get` flattens
|
|
13
|
+
* and dedupes it. The two read functions below already soft-fail to `[]`, so
|
|
14
|
+
* this cache only holds/merges their results and needs no try/catch of its own.
|
|
15
|
+
*/
|
|
16
|
+
import type { OpenQuestion } from "../pware.oc.opencode/resolver/pware.oc.opencode.resolver.question.js"
|
|
17
|
+
|
|
18
|
+
export type QuestionCache = {
|
|
19
|
+
/** Project-wide open questions, deduped by partId, sorted `startedAt` DESC (nulls last). */
|
|
20
|
+
get: () => OpenQuestion[]
|
|
21
|
+
seed: (questions: readonly OpenQuestion[]) => void
|
|
22
|
+
touch: (sessionId: string, questions: readonly OpenQuestion[]) => void
|
|
23
|
+
/** Drop every cached session. */
|
|
24
|
+
reset: () => void
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Flatten all per-session slices, dedupe by `partId` (a question may briefly
|
|
29
|
+
* appear in two sessions' slices — keep the first), and sort by `startedAt`
|
|
30
|
+
* descending with nulls last. Pure — never mutates its input.
|
|
31
|
+
*/
|
|
32
|
+
export function mergeQuestions(
|
|
33
|
+
bySession: ReadonlyMap<string, readonly OpenQuestion[]>,
|
|
34
|
+
): OpenQuestion[] {
|
|
35
|
+
const seen = new Set<string>()
|
|
36
|
+
const out: OpenQuestion[] = []
|
|
37
|
+
for (const list of bySession.values()) {
|
|
38
|
+
for (const q of list) {
|
|
39
|
+
if (seen.has(q.partId)) continue
|
|
40
|
+
seen.add(q.partId)
|
|
41
|
+
out.push(q)
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
out.sort((a, b) => {
|
|
45
|
+
const x = a.startedAt
|
|
46
|
+
const y = b.startedAt
|
|
47
|
+
if (x === y) return 0
|
|
48
|
+
if (x == null) return 1
|
|
49
|
+
if (y == null) return -1
|
|
50
|
+
return y - x
|
|
51
|
+
})
|
|
52
|
+
return out
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
export function createQuestionCache(): QuestionCache {
|
|
56
|
+
let bySession = new Map<string, OpenQuestion[]>()
|
|
57
|
+
|
|
58
|
+
function refill(questions: readonly OpenQuestion[]): void {
|
|
59
|
+
const next = new Map<string, OpenQuestion[]>()
|
|
60
|
+
for (const q of questions) {
|
|
61
|
+
const bucket = next.get(q.sessionId)
|
|
62
|
+
if (bucket) bucket.push(q)
|
|
63
|
+
else next.set(q.sessionId, [q])
|
|
64
|
+
}
|
|
65
|
+
bySession = next
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
return {
|
|
69
|
+
get() {
|
|
70
|
+
return mergeQuestions(bySession)
|
|
71
|
+
},
|
|
72
|
+
seed(questions) {
|
|
73
|
+
refill(questions)
|
|
74
|
+
},
|
|
75
|
+
touch(sessionId, questions) {
|
|
76
|
+
if (questions.length > 0) bySession.set(sessionId, [...questions])
|
|
77
|
+
else bySession.delete(sessionId)
|
|
78
|
+
},
|
|
79
|
+
reset() {
|
|
80
|
+
bySession = new Map()
|
|
81
|
+
},
|
|
82
|
+
}
|
|
83
|
+
}
|
|
@@ -1,129 +1,130 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* pware.oc.runtime.snapshotClient
|
|
3
|
-
*
|
|
4
|
-
* Async facade over the snapshot worker. `readRuntimeSnapshotAsync` posts a
|
|
5
|
-
* snapshot request and resolves with the composed snapshot; when the worker
|
|
6
|
-
* cannot be spawned, errors, or fails to answer within the timeout it
|
|
7
|
-
*
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
import {
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
type
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
let
|
|
38
|
-
let
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
if (
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
if (
|
|
73
|
-
|
|
74
|
-
}
|
|
75
|
-
w.onerror = (event) => {
|
|
76
|
-
const message =
|
|
77
|
-
typeof event === "object" && event !== null && "message" in event
|
|
78
|
-
? String((event as { message: unknown }).message)
|
|
79
|
-
: String(event)
|
|
80
|
-
failAll(message)
|
|
81
|
-
try {
|
|
82
|
-
w.terminate()
|
|
83
|
-
} catch {
|
|
84
|
-
// ignore
|
|
85
|
-
}
|
|
86
|
-
worker = null
|
|
87
|
-
}
|
|
88
|
-
worker = w
|
|
89
|
-
dbg("snapshot.worker", "spawned", {})
|
|
90
|
-
return w
|
|
91
|
-
} catch (err) {
|
|
92
|
-
dbg("snapshot.worker", "spawn failed", { error: err instanceof Error ? err.message : String(err) })
|
|
93
|
-
workerFailed = true
|
|
94
|
-
return null
|
|
95
|
-
}
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
}
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
1
|
+
/**
|
|
2
|
+
* pware.oc.runtime.snapshotClient
|
|
3
|
+
*
|
|
4
|
+
* Async facade over the snapshot worker. `readRuntimeSnapshotAsync` posts a
|
|
5
|
+
* snapshot request and resolves with the composed snapshot; when the worker
|
|
6
|
+
* cannot be spawned, errors, or fails to answer within the timeout it runs the
|
|
7
|
+
* same paced request gateway in the host process.
|
|
8
|
+
*/
|
|
9
|
+
import { dbg } from "../pware.oc.core/pware.oc.core.debug.js"
|
|
10
|
+
import { readRuntimeSnapshot, type RuntimeSnapshot } from "./resolver/index.js"
|
|
11
|
+
|
|
12
|
+
export type SnapshotRequestOpts = {
|
|
13
|
+
sessionId: string
|
|
14
|
+
projectRoot: string | null
|
|
15
|
+
dbPath?: string
|
|
16
|
+
questionHint?: string
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
type SnapshotDoneMessage = {
|
|
20
|
+
type: "snapshot:done"
|
|
21
|
+
id: number
|
|
22
|
+
ok: boolean
|
|
23
|
+
snap?: RuntimeSnapshot
|
|
24
|
+
error?: string
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/** Bound on a single worker round-trip before the sync path takes over. */
|
|
28
|
+
const WORKER_TIMEOUT_MS = 4_000
|
|
29
|
+
|
|
30
|
+
type PendingEntry = {
|
|
31
|
+
opts: SnapshotRequestOpts
|
|
32
|
+
resolve: (snap: RuntimeSnapshot) => void
|
|
33
|
+
timer: ReturnType<typeof setTimeout>
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
let worker: Worker | null = null
|
|
37
|
+
let workerFailed = false
|
|
38
|
+
let nextId = 0
|
|
39
|
+
const pending = new Map<number, PendingEntry>()
|
|
40
|
+
|
|
41
|
+
function fallbackRead(opts: SnapshotRequestOpts): Promise<RuntimeSnapshot> {
|
|
42
|
+
return readRuntimeSnapshot(opts)
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
function settle(id: number, snap: RuntimeSnapshot): void {
|
|
46
|
+
const entry = pending.get(id)
|
|
47
|
+
if (!entry) return
|
|
48
|
+
pending.delete(id)
|
|
49
|
+
clearTimeout(entry.timer)
|
|
50
|
+
entry.resolve(snap)
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
function failAll(reason: string): void {
|
|
54
|
+
dbg("snapshot.worker", "failed", { reason })
|
|
55
|
+
workerFailed = true
|
|
56
|
+
for (const id of [...pending.keys()]) {
|
|
57
|
+
const entry = pending.get(id)
|
|
58
|
+
if (entry) void fallbackRead(entry.opts).then((snap) => settle(id, snap))
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
function ensureWorker(): Worker | null {
|
|
63
|
+
if (workerFailed) return null
|
|
64
|
+
if (worker) return worker
|
|
65
|
+
try {
|
|
66
|
+
const w = new Worker(new URL("./pware.oc.runtime.worker.ts", import.meta.url), { type: "module" })
|
|
67
|
+
w.onmessage = (event: MessageEvent<SnapshotDoneMessage>) => {
|
|
68
|
+
const msg = event.data
|
|
69
|
+
if (!msg || msg.type !== "snapshot:done") return
|
|
70
|
+
const entry = pending.get(msg.id)
|
|
71
|
+
if (!entry) return
|
|
72
|
+
if (msg.ok && msg.snap) settle(msg.id, msg.snap)
|
|
73
|
+
else void fallbackRead(entry.opts).then((snap) => settle(msg.id, snap))
|
|
74
|
+
}
|
|
75
|
+
w.onerror = (event) => {
|
|
76
|
+
const message =
|
|
77
|
+
typeof event === "object" && event !== null && "message" in event
|
|
78
|
+
? String((event as { message: unknown }).message)
|
|
79
|
+
: String(event)
|
|
80
|
+
failAll(message)
|
|
81
|
+
try {
|
|
82
|
+
w.terminate()
|
|
83
|
+
} catch {
|
|
84
|
+
// ignore
|
|
85
|
+
}
|
|
86
|
+
worker = null
|
|
87
|
+
}
|
|
88
|
+
worker = w
|
|
89
|
+
dbg("snapshot.worker", "spawned", {})
|
|
90
|
+
return w
|
|
91
|
+
} catch (err) {
|
|
92
|
+
dbg("snapshot.worker", "spawn failed", { error: err instanceof Error ? err.message : String(err) })
|
|
93
|
+
workerFailed = true
|
|
94
|
+
return null
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
export function readRuntimeSnapshotAsync(opts: SnapshotRequestOpts): Promise<RuntimeSnapshot> {
|
|
99
|
+
const w = ensureWorker()
|
|
100
|
+
if (!w) return fallbackRead(opts)
|
|
101
|
+
const id = ++nextId
|
|
102
|
+
return new Promise<RuntimeSnapshot>((resolve) => {
|
|
103
|
+
const timer = setTimeout(() => {
|
|
104
|
+
void fallbackRead(opts).then((snap) => settle(id, snap))
|
|
105
|
+
}, WORKER_TIMEOUT_MS)
|
|
106
|
+
pending.set(id, { opts, resolve, timer })
|
|
107
|
+
try {
|
|
108
|
+
w.postMessage({ type: "snapshot", id, opts })
|
|
109
|
+
} catch (err) {
|
|
110
|
+
dbg("snapshot.worker", "post failed", { error: err instanceof Error ? err.message : String(err) })
|
|
111
|
+
void fallbackRead(opts).then((snap) => settle(id, snap))
|
|
112
|
+
}
|
|
113
|
+
})
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
/** Terminate the shared worker (idempotent). Safe to call on plugin shutdown. */
|
|
117
|
+
export function shutdownSnapshotWorker(): void {
|
|
118
|
+
if (!worker) return
|
|
119
|
+
try {
|
|
120
|
+
worker.postMessage({ type: "shutdown" })
|
|
121
|
+
} catch {
|
|
122
|
+
// ignore
|
|
123
|
+
}
|
|
124
|
+
try {
|
|
125
|
+
worker.terminate()
|
|
126
|
+
} catch {
|
|
127
|
+
// ignore
|
|
128
|
+
}
|
|
129
|
+
worker = null
|
|
130
|
+
}
|
|
@@ -1,105 +1,105 @@
|
|
|
1
|
-
import type { PwareEventBus } from "../pware.oc.core/pware.oc.core.bus.js"
|
|
2
|
-
import { EVENT_SCAN_DEBOUNCE_MS } from "../pware.oc.core/pware.oc.core.timing.js"
|
|
3
|
-
import {
|
|
4
|
-
EV_OES_QUESTION_HINT,
|
|
5
|
-
EV_OES_REFRESH_HINT,
|
|
6
|
-
} from "../pware.oc.core/constants/pware.oc.core.constants.eventName.js"
|
|
7
|
-
import {
|
|
8
|
-
EV_OMO_BOULDER_CHANGED,
|
|
9
|
-
EV_OMO_CONFIG_CHANGED,
|
|
10
|
-
EV_OMO_DOCS_CHANGED,
|
|
11
|
-
} from "../pware.oc.omo/constants/pware.oc.omo.constants.eventName.js"
|
|
12
|
-
import { startMonitor, type MonitorHandle } from "./pware.oc.runtime.monitor.js"
|
|
13
|
-
import { shutdownSnapshotWorker } from "./pware.oc.runtime.snapshotClient.js"
|
|
14
|
-
|
|
15
|
-
export type RuntimeSourceOptions = {
|
|
16
|
-
bus: PwareEventBus
|
|
17
|
-
sessionId: string
|
|
18
|
-
projectRoot: string | null
|
|
19
|
-
dbPath?: string
|
|
20
|
-
pollMs?: number
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
export type RuntimeSourceHandle = {
|
|
24
|
-
stop: () => void
|
|
25
|
-
refresh: () => void
|
|
26
|
-
setSession: (sessionId: string) => void
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
export function startRuntimeSource(opts: RuntimeSourceOptions): RuntimeSourceHandle {
|
|
30
|
-
let stopped = false
|
|
31
|
-
let watchedSessionId = opts.sessionId
|
|
32
|
-
let debounce: ReturnType<typeof setTimeout> | null = null
|
|
33
|
-
let questionDebounce: ReturnType<typeof setTimeout> | null = null
|
|
34
|
-
let pendingQuestionSession: string | null = null
|
|
35
|
-
|
|
36
|
-
const bindMonitor = (sessionId: string): MonitorHandle =>
|
|
37
|
-
startMonitor({
|
|
38
|
-
sessionId,
|
|
39
|
-
projectRoot: opts.projectRoot,
|
|
40
|
-
dbPath: opts.dbPath,
|
|
41
|
-
pollMs: opts.pollMs,
|
|
42
|
-
emit: opts.bus.emit,
|
|
43
|
-
})
|
|
44
|
-
|
|
45
|
-
let monitor = bindMonitor(watchedSessionId)
|
|
46
|
-
|
|
47
|
-
const scheduleRefresh = (): void => {
|
|
48
|
-
if (stopped) return
|
|
49
|
-
if (debounce) clearTimeout(debounce)
|
|
50
|
-
debounce = setTimeout(() => {
|
|
51
|
-
debounce = null
|
|
52
|
-
monitor.refresh()
|
|
53
|
-
}, EVENT_SCAN_DEBOUNCE_MS)
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
const scheduleQuestionHint = (sessionId: string): void => {
|
|
57
|
-
if (stopped) return
|
|
58
|
-
pendingQuestionSession = sessionId
|
|
59
|
-
if (questionDebounce) clearTimeout(questionDebounce)
|
|
60
|
-
questionDebounce = setTimeout(() => {
|
|
61
|
-
questionDebounce = null
|
|
62
|
-
const sid = pendingQuestionSession
|
|
63
|
-
pendingQuestionSession = null
|
|
64
|
-
if (sid) monitor.question(sid)
|
|
65
|
-
}, EVENT_SCAN_DEBOUNCE_MS)
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
const offRefreshHint = opts.bus.on(EV_OES_REFRESH_HINT, scheduleRefresh)
|
|
69
|
-
const offQuestionHint = opts.bus.on(EV_OES_QUESTION_HINT, (evt) => {
|
|
70
|
-
const data = evt.data as { sessionId?: string } | null | undefined
|
|
71
|
-
if (data?.sessionId) scheduleQuestionHint(data.sessionId)
|
|
72
|
-
})
|
|
73
|
-
const offBoulderChanged = opts.bus.on(EV_OMO_BOULDER_CHANGED, scheduleRefresh)
|
|
74
|
-
const offDocsChanged = opts.bus.on(EV_OMO_DOCS_CHANGED, scheduleRefresh)
|
|
75
|
-
const offConfigChanged = opts.bus.on(EV_OMO_CONFIG_CHANGED, scheduleRefresh)
|
|
76
|
-
|
|
77
|
-
return {
|
|
78
|
-
refresh: () => monitor.refresh(),
|
|
79
|
-
setSession: (sessionId: string) => {
|
|
80
|
-
if (!sessionId || sessionId === watchedSessionId) return
|
|
81
|
-
watchedSessionId = sessionId
|
|
82
|
-
if (debounce) {
|
|
83
|
-
clearTimeout(debounce)
|
|
84
|
-
debounce = null
|
|
85
|
-
}
|
|
86
|
-
monitor.stop()
|
|
87
|
-
monitor = bindMonitor(sessionId)
|
|
88
|
-
},
|
|
89
|
-
stop: () => {
|
|
90
|
-
stopped = true
|
|
91
|
-
if (debounce) clearTimeout(debounce)
|
|
92
|
-
debounce = null
|
|
93
|
-
if (questionDebounce) clearTimeout(questionDebounce)
|
|
94
|
-
questionDebounce = null
|
|
95
|
-
pendingQuestionSession = null
|
|
96
|
-
offRefreshHint()
|
|
97
|
-
offQuestionHint()
|
|
98
|
-
offBoulderChanged()
|
|
99
|
-
offDocsChanged()
|
|
100
|
-
offConfigChanged()
|
|
101
|
-
monitor.stop()
|
|
102
|
-
shutdownSnapshotWorker()
|
|
103
|
-
},
|
|
104
|
-
}
|
|
105
|
-
}
|
|
1
|
+
import type { PwareEventBus } from "../pware.oc.core/pware.oc.core.bus.js"
|
|
2
|
+
import { EVENT_SCAN_DEBOUNCE_MS } from "../pware.oc.core/pware.oc.core.timing.js"
|
|
3
|
+
import {
|
|
4
|
+
EV_OES_QUESTION_HINT,
|
|
5
|
+
EV_OES_REFRESH_HINT,
|
|
6
|
+
} from "../pware.oc.core/constants/pware.oc.core.constants.eventName.js"
|
|
7
|
+
import {
|
|
8
|
+
EV_OMO_BOULDER_CHANGED,
|
|
9
|
+
EV_OMO_CONFIG_CHANGED,
|
|
10
|
+
EV_OMO_DOCS_CHANGED,
|
|
11
|
+
} from "../pware.oc.omo/constants/pware.oc.omo.constants.eventName.js"
|
|
12
|
+
import { startMonitor, type MonitorHandle } from "./pware.oc.runtime.monitor.js"
|
|
13
|
+
import { shutdownSnapshotWorker } from "./pware.oc.runtime.snapshotClient.js"
|
|
14
|
+
|
|
15
|
+
export type RuntimeSourceOptions = {
|
|
16
|
+
bus: PwareEventBus
|
|
17
|
+
sessionId: string
|
|
18
|
+
projectRoot: string | null
|
|
19
|
+
dbPath?: string
|
|
20
|
+
pollMs?: number
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export type RuntimeSourceHandle = {
|
|
24
|
+
stop: () => void
|
|
25
|
+
refresh: () => void
|
|
26
|
+
setSession: (sessionId: string) => void
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export function startRuntimeSource(opts: RuntimeSourceOptions): RuntimeSourceHandle {
|
|
30
|
+
let stopped = false
|
|
31
|
+
let watchedSessionId = opts.sessionId
|
|
32
|
+
let debounce: ReturnType<typeof setTimeout> | null = null
|
|
33
|
+
let questionDebounce: ReturnType<typeof setTimeout> | null = null
|
|
34
|
+
let pendingQuestionSession: string | null = null
|
|
35
|
+
|
|
36
|
+
const bindMonitor = (sessionId: string): MonitorHandle =>
|
|
37
|
+
startMonitor({
|
|
38
|
+
sessionId,
|
|
39
|
+
projectRoot: opts.projectRoot,
|
|
40
|
+
dbPath: opts.dbPath,
|
|
41
|
+
pollMs: opts.pollMs,
|
|
42
|
+
emit: opts.bus.emit,
|
|
43
|
+
})
|
|
44
|
+
|
|
45
|
+
let monitor = bindMonitor(watchedSessionId)
|
|
46
|
+
|
|
47
|
+
const scheduleRefresh = (): void => {
|
|
48
|
+
if (stopped) return
|
|
49
|
+
if (debounce) clearTimeout(debounce)
|
|
50
|
+
debounce = setTimeout(() => {
|
|
51
|
+
debounce = null
|
|
52
|
+
monitor.refresh()
|
|
53
|
+
}, EVENT_SCAN_DEBOUNCE_MS)
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
const scheduleQuestionHint = (sessionId: string): void => {
|
|
57
|
+
if (stopped) return
|
|
58
|
+
pendingQuestionSession = sessionId
|
|
59
|
+
if (questionDebounce) clearTimeout(questionDebounce)
|
|
60
|
+
questionDebounce = setTimeout(() => {
|
|
61
|
+
questionDebounce = null
|
|
62
|
+
const sid = pendingQuestionSession
|
|
63
|
+
pendingQuestionSession = null
|
|
64
|
+
if (sid) monitor.question(sid)
|
|
65
|
+
}, EVENT_SCAN_DEBOUNCE_MS)
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
const offRefreshHint = opts.bus.on(EV_OES_REFRESH_HINT, scheduleRefresh)
|
|
69
|
+
const offQuestionHint = opts.bus.on(EV_OES_QUESTION_HINT, (evt) => {
|
|
70
|
+
const data = evt.data as { sessionId?: string } | null | undefined
|
|
71
|
+
if (data?.sessionId) scheduleQuestionHint(data.sessionId)
|
|
72
|
+
})
|
|
73
|
+
const offBoulderChanged = opts.bus.on(EV_OMO_BOULDER_CHANGED, scheduleRefresh)
|
|
74
|
+
const offDocsChanged = opts.bus.on(EV_OMO_DOCS_CHANGED, scheduleRefresh)
|
|
75
|
+
const offConfigChanged = opts.bus.on(EV_OMO_CONFIG_CHANGED, scheduleRefresh)
|
|
76
|
+
|
|
77
|
+
return {
|
|
78
|
+
refresh: () => monitor.refresh(),
|
|
79
|
+
setSession: (sessionId: string) => {
|
|
80
|
+
if (!sessionId || sessionId === watchedSessionId) return
|
|
81
|
+
watchedSessionId = sessionId
|
|
82
|
+
if (debounce) {
|
|
83
|
+
clearTimeout(debounce)
|
|
84
|
+
debounce = null
|
|
85
|
+
}
|
|
86
|
+
monitor.stop()
|
|
87
|
+
monitor = bindMonitor(sessionId)
|
|
88
|
+
},
|
|
89
|
+
stop: () => {
|
|
90
|
+
stopped = true
|
|
91
|
+
if (debounce) clearTimeout(debounce)
|
|
92
|
+
debounce = null
|
|
93
|
+
if (questionDebounce) clearTimeout(questionDebounce)
|
|
94
|
+
questionDebounce = null
|
|
95
|
+
pendingQuestionSession = null
|
|
96
|
+
offRefreshHint()
|
|
97
|
+
offQuestionHint()
|
|
98
|
+
offBoulderChanged()
|
|
99
|
+
offDocsChanged()
|
|
100
|
+
offConfigChanged()
|
|
101
|
+
monitor.stop()
|
|
102
|
+
shutdownSnapshotWorker()
|
|
103
|
+
},
|
|
104
|
+
}
|
|
105
|
+
}
|