@playola-radio/slipstream 0.1.0 → 0.3.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/README.md +427 -101
- package/SKILL.md +4 -1
- package/contracts/interface/v2/schema.json +3 -3
- package/dist/attach-workflow.js +557 -0
- package/dist/attribution-engine.js +2 -3
- package/dist/attribution-scoring.js +70 -13
- package/dist/boundary-registry.js +25 -0
- package/dist/capture-intent.js +70 -0
- package/dist/capture-scope.js +135 -0
- package/dist/cas.js +61 -14
- package/dist/claude-root-transcript.js +18 -11
- package/dist/cli.js +426 -26
- package/dist/clip-function-parser.js +9 -2
- package/dist/clip-projection-service.js +28 -0
- package/dist/clip-projection-worker.js +4 -0
- package/dist/clip-worker-pool.js +13 -1
- package/dist/codex-root-transcript.js +46 -0
- package/dist/conductor-settings.js +178 -0
- package/dist/conductor.js +313 -0
- package/dist/control-protocol.js +2 -1
- package/dist/daemon.js +1718 -470
- package/dist/engine.js +26 -10
- package/dist/ensure-daemon.js +30 -0
- package/dist/http-reader.js +20 -8
- package/dist/intent-store.js +196 -0
- package/dist/interface-range-resolver.js +28 -1
- package/dist/launch-agent.js +179 -0
- package/dist/lock.js +1 -1
- package/dist/public-events.js +26 -2
- package/dist/question-hook.js +11 -1
- package/dist/questions.js +102 -3
- package/dist/recovery.js +62 -6
- package/dist/session.js +604 -104
- package/dist/store-reader.js +112 -7
- package/dist/task-forwarder.js +8 -8
- package/dist/transcript/discovery.js +242 -111
- package/dist/transcript/fs-io.js +10 -1
- package/dist/transcript/scheduler.js +114 -0
- package/dist/transcript/watcher.js +5 -3
- package/dist/withdraw-workflow.js +106 -0
- package/dist/workspace.js +133 -0
- package/package.json +3 -2
- package/schemas/slipstream.agent.binding.v1.json +90 -0
- package/schemas/slipstream.agent.connection.v1.json +34 -0
- package/schemas/slipstream.agent.unlinked.v1.json +68 -0
- package/schemas/slipstream.capture.gap.v1.json +1 -0
- package/schemas/slipstream.capture.ready.v1.json +55 -0
- package/schemas/slipstream.capture.scope.v1.json +85 -0
- package/schemas/slipstream.capture.stopped.v1.json +65 -0
- package/schemas/slipstream.question.dispatch_attempted.v1.json +11 -1
- package/schemas/slipstream.question.exchange_recorded.v1.json +65 -0
- package/schemas/slipstream.question.relay_updated.v1.json +64 -0
- package/schemas/slipstream.session.resumed.v1.json +4 -1
- package/schemas/slipstream.session.started.v1.json +4 -1
- package/dist/transcript/runner.js +0 -62
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
3
|
+
"$id": "https://slipstream.dev/schemas/slipstream.capture.scope.v1.json",
|
|
4
|
+
"title": "slipstream.capture.scope.v1",
|
|
5
|
+
"$comment": "Which paths capture follows from this point on. policy git: paths git would not merge (ignored and untracked) are not captured; status unavailable means git could not be asked, so affected observations were dropped and their prior state is unknown. slipstream_ignore active (git only): a root .slipstreamignore was frozen at capture start and excludes extra paths, including tracked ones git would keep. policy filesystem: the root is not a git work tree and every path is captured. Self-contained; unknown fields permitted for forward compatibility.",
|
|
6
|
+
"type": "object",
|
|
7
|
+
"required": [
|
|
8
|
+
"specversion",
|
|
9
|
+
"id",
|
|
10
|
+
"source",
|
|
11
|
+
"type",
|
|
12
|
+
"datacontenttype",
|
|
13
|
+
"seq",
|
|
14
|
+
"time",
|
|
15
|
+
"data"
|
|
16
|
+
],
|
|
17
|
+
"properties": {
|
|
18
|
+
"specversion": {
|
|
19
|
+
"const": "1.0"
|
|
20
|
+
},
|
|
21
|
+
"id": {
|
|
22
|
+
"type": "string",
|
|
23
|
+
"pattern": "^[1-9][0-9]*$"
|
|
24
|
+
},
|
|
25
|
+
"source": {
|
|
26
|
+
"type": "string",
|
|
27
|
+
"pattern": "^urn:slipstream:session:"
|
|
28
|
+
},
|
|
29
|
+
"type": {
|
|
30
|
+
"const": "slipstream.capture.scope.v1"
|
|
31
|
+
},
|
|
32
|
+
"datacontenttype": {
|
|
33
|
+
"const": "application/json"
|
|
34
|
+
},
|
|
35
|
+
"seq": {
|
|
36
|
+
"type": "string",
|
|
37
|
+
"pattern": "^[1-9][0-9]*$"
|
|
38
|
+
},
|
|
39
|
+
"time": {
|
|
40
|
+
"type": "string",
|
|
41
|
+
"format": "date-time"
|
|
42
|
+
},
|
|
43
|
+
"data": {
|
|
44
|
+
"type": "object",
|
|
45
|
+
"required": [
|
|
46
|
+
"session_id",
|
|
47
|
+
"policy",
|
|
48
|
+
"status"
|
|
49
|
+
],
|
|
50
|
+
"properties": {
|
|
51
|
+
"session_id": {
|
|
52
|
+
"type": "string"
|
|
53
|
+
}
|
|
54
|
+
},
|
|
55
|
+
"oneOf": [
|
|
56
|
+
{
|
|
57
|
+
"type": "object",
|
|
58
|
+
"required": ["policy", "status"],
|
|
59
|
+
"properties": {
|
|
60
|
+
"policy": { "const": "git" },
|
|
61
|
+
"status": { "const": "active" },
|
|
62
|
+
"slipstream_ignore": { "const": "active" }
|
|
63
|
+
}
|
|
64
|
+
},
|
|
65
|
+
{
|
|
66
|
+
"type": "object",
|
|
67
|
+
"required": ["policy", "status"],
|
|
68
|
+
"properties": {
|
|
69
|
+
"policy": { "const": "git" },
|
|
70
|
+
"status": { "const": "unavailable" },
|
|
71
|
+
"slipstream_ignore": { "const": "active" }
|
|
72
|
+
}
|
|
73
|
+
},
|
|
74
|
+
{
|
|
75
|
+
"type": "object",
|
|
76
|
+
"required": ["policy", "status"],
|
|
77
|
+
"properties": {
|
|
78
|
+
"policy": { "const": "filesystem" },
|
|
79
|
+
"status": { "const": "active" }
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
]
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
}
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
3
|
+
"$id": "https://slipstream.dev/schemas/slipstream.capture.stopped.v1.json",
|
|
4
|
+
"title": "slipstream.capture.stopped.v1",
|
|
5
|
+
"$comment": "The final record of an activation that stopped cleanly: observation stopped, accepted work drained, then this record was made durable before the log closed. It is never written after a lost lock or an unwritable store, so a missing stop together with no live owner heartbeat means not confirmed live, never still recording. detached: an explicit detach. archived: the workspace was archived. maintenance: stopped for garbage collection; capture resumes later on the same session. root-missing: the watched root was deleted or replaced. watcher-failed: the watcher subscription closed or could not be kept. The reason set is closed for v1. Self-contained; unknown fields permitted for forward compatibility.",
|
|
6
|
+
"type": "object",
|
|
7
|
+
"required": [
|
|
8
|
+
"specversion",
|
|
9
|
+
"id",
|
|
10
|
+
"source",
|
|
11
|
+
"type",
|
|
12
|
+
"datacontenttype",
|
|
13
|
+
"seq",
|
|
14
|
+
"time",
|
|
15
|
+
"data"
|
|
16
|
+
],
|
|
17
|
+
"properties": {
|
|
18
|
+
"specversion": {
|
|
19
|
+
"const": "1.0"
|
|
20
|
+
},
|
|
21
|
+
"id": {
|
|
22
|
+
"type": "string",
|
|
23
|
+
"pattern": "^[1-9][0-9]*$"
|
|
24
|
+
},
|
|
25
|
+
"source": {
|
|
26
|
+
"type": "string",
|
|
27
|
+
"pattern": "^urn:slipstream:session:"
|
|
28
|
+
},
|
|
29
|
+
"type": {
|
|
30
|
+
"const": "slipstream.capture.stopped.v1"
|
|
31
|
+
},
|
|
32
|
+
"datacontenttype": {
|
|
33
|
+
"const": "application/json"
|
|
34
|
+
},
|
|
35
|
+
"seq": {
|
|
36
|
+
"type": "string",
|
|
37
|
+
"pattern": "^[1-9][0-9]*$"
|
|
38
|
+
},
|
|
39
|
+
"time": {
|
|
40
|
+
"type": "string",
|
|
41
|
+
"format": "date-time"
|
|
42
|
+
},
|
|
43
|
+
"data": {
|
|
44
|
+
"type": "object",
|
|
45
|
+
"required": [
|
|
46
|
+
"session_id",
|
|
47
|
+
"reason"
|
|
48
|
+
],
|
|
49
|
+
"properties": {
|
|
50
|
+
"session_id": {
|
|
51
|
+
"type": "string"
|
|
52
|
+
},
|
|
53
|
+
"reason": {
|
|
54
|
+
"enum": [
|
|
55
|
+
"detached",
|
|
56
|
+
"archived",
|
|
57
|
+
"maintenance",
|
|
58
|
+
"root-missing",
|
|
59
|
+
"watcher-failed"
|
|
60
|
+
]
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
}
|
|
@@ -21,7 +21,17 @@
|
|
|
21
21
|
"session_id": { "type": "string", "pattern": "^[0-9a-f]{8}-[0-9a-f]{4}-[1-8][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$" },
|
|
22
22
|
"question_id": { "type": "string", "pattern": "^[0-9a-f]{8}-[0-9a-f]{4}-[1-8][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$" },
|
|
23
23
|
"queued_seq": { "type": "string", "pattern": "^[1-9][0-9]*$" },
|
|
24
|
-
"attempted_at_ms": { "type": "integer", "minimum": 0 }
|
|
24
|
+
"attempted_at_ms": { "type": "integer", "minimum": 0 },
|
|
25
|
+
"target": {
|
|
26
|
+
"$comment": "The chat this attempt was handed to. Absent on attempts written before it was recorded; those went to the queued target.",
|
|
27
|
+
"type": "object",
|
|
28
|
+
"required": ["harness", "harness_session_id", "worktree"],
|
|
29
|
+
"properties": {
|
|
30
|
+
"harness": { "enum": ["claude-code", "codex"] },
|
|
31
|
+
"harness_session_id": { "type": "string", "minLength": 1 },
|
|
32
|
+
"worktree": { "type": "string", "minLength": 1 }
|
|
33
|
+
}
|
|
34
|
+
}
|
|
25
35
|
}
|
|
26
36
|
}
|
|
27
37
|
}
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
3
|
+
"$id": "https://slipstream.dev/schemas/slipstream.question.exchange_recorded.v1.json",
|
|
4
|
+
"title": "slipstream.question.exchange_recorded.v1",
|
|
5
|
+
"$comment": "A question asked at a code line together with the answer the client's own forked chat produced, recorded by the daemon without dispatch. The conversation ids are client-reported and unverified (reported_by is always client); they are never a binding and never authorship. Self-contained; unknown fields permitted for forward compatibility.",
|
|
6
|
+
"type": "object",
|
|
7
|
+
"required": ["specversion", "id", "source", "type", "datacontenttype", "seq", "time", "subject", "data"],
|
|
8
|
+
"properties": {
|
|
9
|
+
"specversion": { "const": "1.0" },
|
|
10
|
+
"id": { "type": "string", "pattern": "^[1-9][0-9]*$" },
|
|
11
|
+
"source": { "type": "string", "pattern": "^urn:slipstream:session:[0-9a-f]{8}-[0-9a-f]{4}-[1-8][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$" },
|
|
12
|
+
"type": { "const": "slipstream.question.exchange_recorded.v1" },
|
|
13
|
+
"datacontenttype": { "const": "application/json" },
|
|
14
|
+
"seq": { "type": "string", "pattern": "^[1-9][0-9]*$" },
|
|
15
|
+
"time": { "type": "string", "format": "date-time" },
|
|
16
|
+
"subject": { "type": "string", "pattern": "^question/[0-9a-f]{8}-[0-9a-f]{4}-[1-8][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$" },
|
|
17
|
+
"data": {
|
|
18
|
+
"type": "object",
|
|
19
|
+
"required": ["session_id", "question_id", "request_id", "text", "context", "answer_text", "recorded_at_ms", "conversation"],
|
|
20
|
+
"properties": {
|
|
21
|
+
"session_id": { "$ref": "#/$defs/uuid" },
|
|
22
|
+
"question_id": { "$ref": "#/$defs/uuid" },
|
|
23
|
+
"request_id": { "$ref": "#/$defs/uuid" },
|
|
24
|
+
"text": { "type": "string", "minLength": 1 },
|
|
25
|
+
"context": {
|
|
26
|
+
"type": "object",
|
|
27
|
+
"required": ["change_seq", "path", "snapshot_sha256", "line_start", "line_end", "selected_text"],
|
|
28
|
+
"properties": {
|
|
29
|
+
"change_seq": { "type": "string", "pattern": "^[1-9][0-9]*$" },
|
|
30
|
+
"path": { "type": "string", "minLength": 1 },
|
|
31
|
+
"snapshot_sha256": { "type": "string", "pattern": "^[0-9a-f]{64}$" },
|
|
32
|
+
"line_start": { "type": "integer", "minimum": 1 },
|
|
33
|
+
"line_end": { "type": "integer", "minimum": 1 },
|
|
34
|
+
"selected_text": { "type": "string" }
|
|
35
|
+
}
|
|
36
|
+
},
|
|
37
|
+
"answer_text": { "type": "string", "minLength": 1 },
|
|
38
|
+
"recorded_at_ms": { "type": "integer", "minimum": 0 },
|
|
39
|
+
"reply_to_question_id": { "$ref": "#/$defs/uuid" },
|
|
40
|
+
"conversation": {
|
|
41
|
+
"type": "object",
|
|
42
|
+
"required": ["harness", "parent_session_id", "fork_session_id", "reported_by"],
|
|
43
|
+
"properties": {
|
|
44
|
+
"harness": { "enum": ["claude-code", "codex"] },
|
|
45
|
+
"parent_session_id": { "type": "string", "minLength": 1, "maxLength": 256 },
|
|
46
|
+
"fork_session_id": { "type": "string", "minLength": 1, "maxLength": 256 },
|
|
47
|
+
"reported_by": { "const": "client" }
|
|
48
|
+
}
|
|
49
|
+
},
|
|
50
|
+
"relay": {
|
|
51
|
+
"$comment": "The change request this answer accepted; it counts as that relay's first send. chat_session_id is client-reported and unverified.",
|
|
52
|
+
"type": "object",
|
|
53
|
+
"required": ["relay_id", "chat_session_id"],
|
|
54
|
+
"properties": {
|
|
55
|
+
"relay_id": { "$ref": "#/$defs/uuid" },
|
|
56
|
+
"chat_session_id": { "type": "string", "minLength": 1, "maxLength": 256 }
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
},
|
|
62
|
+
"$defs": {
|
|
63
|
+
"uuid": { "type": "string", "pattern": "^[0-9a-f]{8}-[0-9a-f]{4}-[1-8][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$" }
|
|
64
|
+
}
|
|
65
|
+
}
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
3
|
+
"$id": "https://slipstream.dev/schemas/slipstream.question.relay_updated.v1.json",
|
|
4
|
+
"title": "slipstream.question.relay_updated.v1",
|
|
5
|
+
"$comment": "One later step of a change request sent from a recorded exchange, as the client reported it. Steps are recorded in arrival order with no order checks; a reader folds a relay's steps by the precedence in docs/ask-agent/contract.md. chat_session_id and report text are client-reported and unverified; never a binding and never authorship. Self-contained; unknown fields permitted for forward compatibility.",
|
|
6
|
+
"type": "object",
|
|
7
|
+
"required": ["specversion", "id", "source", "type", "datacontenttype", "seq", "time", "subject", "data"],
|
|
8
|
+
"properties": {
|
|
9
|
+
"specversion": { "const": "1.0" },
|
|
10
|
+
"id": { "type": "string", "pattern": "^[1-9][0-9]*$" },
|
|
11
|
+
"source": { "type": "string", "pattern": "^urn:slipstream:session:[0-9a-f]{8}-[0-9a-f]{4}-[1-8][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$" },
|
|
12
|
+
"type": { "const": "slipstream.question.relay_updated.v1" },
|
|
13
|
+
"datacontenttype": { "const": "application/json" },
|
|
14
|
+
"seq": { "type": "string", "pattern": "^[1-9][0-9]*$" },
|
|
15
|
+
"time": { "type": "string", "format": "date-time" },
|
|
16
|
+
"subject": { "type": "string", "pattern": "^question/[0-9a-f]{8}-[0-9a-f]{4}-[1-8][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$" },
|
|
17
|
+
"data": {
|
|
18
|
+
"type": "object",
|
|
19
|
+
"required": ["session_id", "question_id", "relay_id", "request_id", "update", "recorded_at_ms"],
|
|
20
|
+
"properties": {
|
|
21
|
+
"session_id": { "$ref": "#/$defs/uuid" },
|
|
22
|
+
"question_id": { "$ref": "#/$defs/uuid" },
|
|
23
|
+
"relay_id": { "$ref": "#/$defs/uuid" },
|
|
24
|
+
"request_id": { "$ref": "#/$defs/uuid" },
|
|
25
|
+
"update": {
|
|
26
|
+
"oneOf": [
|
|
27
|
+
{
|
|
28
|
+
"$comment": "Send again: a new relay_id for the same question. A first send rides on the exchange's relay.",
|
|
29
|
+
"type": "object",
|
|
30
|
+
"required": ["kind", "chat_session_id"],
|
|
31
|
+
"properties": {
|
|
32
|
+
"kind": { "const": "sent" },
|
|
33
|
+
"chat_session_id": { "type": "string", "minLength": 1, "maxLength": 256 }
|
|
34
|
+
}
|
|
35
|
+
},
|
|
36
|
+
{
|
|
37
|
+
"type": "object",
|
|
38
|
+
"required": ["kind"],
|
|
39
|
+
"properties": { "kind": { "const": "picked_up" } }
|
|
40
|
+
},
|
|
41
|
+
{
|
|
42
|
+
"type": "object",
|
|
43
|
+
"required": ["kind"],
|
|
44
|
+
"properties": { "kind": { "const": "not_delivered" } }
|
|
45
|
+
},
|
|
46
|
+
{
|
|
47
|
+
"type": "object",
|
|
48
|
+
"required": ["kind", "outcome", "text"],
|
|
49
|
+
"properties": {
|
|
50
|
+
"kind": { "const": "reported" },
|
|
51
|
+
"outcome": { "enum": ["done", "not_done_yet"] },
|
|
52
|
+
"text": { "type": "string", "minLength": 1, "pattern": "\\S" }
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
]
|
|
56
|
+
},
|
|
57
|
+
"recorded_at_ms": { "type": "integer", "minimum": 0 }
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
},
|
|
61
|
+
"$defs": {
|
|
62
|
+
"uuid": { "type": "string", "pattern": "^[0-9a-f]{8}-[0-9a-f]{4}-[1-8][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$" }
|
|
63
|
+
}
|
|
64
|
+
}
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
3
3
|
"$id": "https://slipstream.dev/schemas/slipstream.session.resumed.v1.json",
|
|
4
4
|
"title": "slipstream.session.resumed.v1",
|
|
5
|
-
"$comment": "Recorded
|
|
5
|
+
"$comment": "Recorded at recovered_through_seq + 1 when capture of an existing session resumes (daemon restart or re-attach). It resets live state: there is no current task until a new task.started, questions queued before it are never claimable again, and any earlier agent.connection record is stale. capture_mode watch-only means this activation was started without a chat (attach --watch-only, or recovery of such a capture); absent means a chat attach started it, and watch-only is never inferred from absence. A later chat link never rewrites it. Self-contained; unknown fields permitted for forward compatibility.",
|
|
6
6
|
"type": "object",
|
|
7
7
|
"required": [
|
|
8
8
|
"specversion",
|
|
@@ -63,6 +63,9 @@
|
|
|
63
63
|
"resumed_at_ms": {
|
|
64
64
|
"type": "integer",
|
|
65
65
|
"minimum": 0
|
|
66
|
+
},
|
|
67
|
+
"capture_mode": {
|
|
68
|
+
"const": "watch-only"
|
|
66
69
|
}
|
|
67
70
|
}
|
|
68
71
|
}
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
3
3
|
"$id": "https://slipstream.dev/schemas/slipstream.session.started.v1.json",
|
|
4
4
|
"title": "slipstream.session.started.v1",
|
|
5
|
-
"$comment": "CloudEvents 1.0 envelope. Self-contained by design: GET /v1/schemas/{type} returns everything needed to validate one event type. Unknown fields are permitted deliberately (forward compatibility is a contract).",
|
|
5
|
+
"$comment": "CloudEvents 1.0 envelope. Self-contained by design: GET /v1/schemas/{type} returns everything needed to validate one event type. capture_mode watch-only means this activation was started without a chat (attach --watch-only, or recovery of such a capture); absent means a chat attach started it, and watch-only is never inferred from absence. A later chat link never rewrites it. Unknown fields are permitted deliberately (forward compatibility is a contract).",
|
|
6
6
|
"type": "object",
|
|
7
7
|
"required": [
|
|
8
8
|
"specversion",
|
|
@@ -62,6 +62,9 @@
|
|
|
62
62
|
"started_at_ms": {
|
|
63
63
|
"type": "integer",
|
|
64
64
|
"minimum": 0
|
|
65
|
+
},
|
|
66
|
+
"capture_mode": {
|
|
67
|
+
"const": "watch-only"
|
|
65
68
|
}
|
|
66
69
|
}
|
|
67
70
|
}
|
|
@@ -1,62 +0,0 @@
|
|
|
1
|
-
import { createTranscriptWatcher } from "./watcher.js";
|
|
2
|
-
export function createCoverageRunner(opts) {
|
|
3
|
-
const watchers = opts.harnesses.map((harness) => createTranscriptWatcher({
|
|
4
|
-
harness,
|
|
5
|
-
home: opts.homes[harness],
|
|
6
|
-
root: opts.root,
|
|
7
|
-
codexScanLimit: opts.codexScanLimit,
|
|
8
|
-
discoveryIO: opts.discoveryIO,
|
|
9
|
-
fileIO: opts.fileIO,
|
|
10
|
-
sink: opts.sink,
|
|
11
|
-
publish: opts.publish,
|
|
12
|
-
}));
|
|
13
|
-
const setTimer = opts.setTimer ??
|
|
14
|
-
((ms, fn) => {
|
|
15
|
-
const t = setTimeout(fn, ms);
|
|
16
|
-
t.unref?.();
|
|
17
|
-
return t;
|
|
18
|
-
});
|
|
19
|
-
const clearTimer = opts.clearTimer ?? ((h) => clearTimeout(h));
|
|
20
|
-
let stopped = false;
|
|
21
|
-
let timer;
|
|
22
|
-
let inflight;
|
|
23
|
-
const tick = async () => {
|
|
24
|
-
for (const w of watchers) {
|
|
25
|
-
try {
|
|
26
|
-
await w.tick();
|
|
27
|
-
}
|
|
28
|
-
catch (err) {
|
|
29
|
-
opts.onError?.(err);
|
|
30
|
-
}
|
|
31
|
-
}
|
|
32
|
-
};
|
|
33
|
-
const schedule = () => {
|
|
34
|
-
if (stopped)
|
|
35
|
-
return;
|
|
36
|
-
timer = setTimer(opts.intervalMs, run);
|
|
37
|
-
};
|
|
38
|
-
const run = () => {
|
|
39
|
-
if (stopped)
|
|
40
|
-
return;
|
|
41
|
-
timer = undefined;
|
|
42
|
-
inflight = tick().finally(() => {
|
|
43
|
-
inflight = undefined;
|
|
44
|
-
schedule();
|
|
45
|
-
});
|
|
46
|
-
};
|
|
47
|
-
const start = () => {
|
|
48
|
-
if (stopped || timer !== undefined || inflight !== undefined)
|
|
49
|
-
return;
|
|
50
|
-
run();
|
|
51
|
-
};
|
|
52
|
-
const stop = async () => {
|
|
53
|
-
stopped = true;
|
|
54
|
-
if (timer !== undefined) {
|
|
55
|
-
clearTimer(timer);
|
|
56
|
-
timer = undefined;
|
|
57
|
-
}
|
|
58
|
-
if (inflight)
|
|
59
|
-
await inflight.catch(() => { });
|
|
60
|
-
};
|
|
61
|
-
return { tick, start, stop };
|
|
62
|
-
}
|