@gotcos/glasses-server 6.54.0 → 6.56.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/CHANGELOG.md +33 -0
- package/README.md +16 -0
- package/bin/control2-foundation.cjs +12 -0
- package/bin/task-runtime-check.cjs +65 -0
- package/docs/control2-foundation.md +79 -0
- package/docs/control2-sandbox.md +47 -0
- package/docs/control2-task-runtime.md +68 -0
- package/docs/work-reviews.md +25 -0
- package/docs/work-task-runtime.md +82 -0
- package/package.json +18 -4
- package/server/index.ts +8 -0
- package/server/lib/control2-draft.ts +118 -0
- package/server/lib/control2-foundation.ts +164 -0
- package/server/lib/control2-sandbox.ts +192 -0
- package/server/lib/lens-gist.ts +110 -16
- package/server/lib/task-bridge.ts +85 -0
- package/server/lib/task-store.ts +75 -10
- package/server/lib/work-activity.ts +106 -0
- package/server/lib/work-review-backend.ts +28 -0
- package/server/lib/work-review-runtime.ts +203 -0
- package/server/lib/work-review-store.ts +102 -0
- package/server/routes/control2-foundation.ts +56 -0
- package/server/routes/health.ts +4 -3
- package/server/routes/meetings.ts +24 -59
- package/server/routes/work-board.ts +79 -0
- package/server/routes/work-reviews.ts +24 -0
- package/server/scripts/control2-draft-canary.ts +18 -0
- package/server/scripts/control2-foundation-lab.ts +45 -0
- package/server/scripts/control2-sandbox-canary.ts +4 -0
- package/task-runtime/LICENSE +21 -0
- package/task-runtime/manifest.json +37 -0
- package/task-runtime/task_runtime.py +380 -0
- package/work-task-runtime/cos_atomic.py +90 -0
- package/work-task-runtime/manifest.json +33 -0
- package/work-task-runtime/runtime_io.py +27 -0
- package/work-task-runtime/task_bridge.py +87 -0
- package/work-task-runtime/task_checkout.py +380 -0
- package/work-task-runtime/task_commands.py +275 -0
- package/work-task-runtime/task_dedup.py +11 -0
- package/work-task-runtime/task_rows.py +420 -0
- package/work-task-runtime/task_work_metadata.py +107 -0
- package/work-task-runtime/task_write.py +645 -0
|
@@ -0,0 +1,275 @@
|
|
|
1
|
+
"""Extracted canonical task handlers only; see manifest provenance."""
|
|
2
|
+
import json, re, sys
|
|
3
|
+
|
|
4
|
+
def cmd_task_rows(args):
|
|
5
|
+
from task_rows import full_domains, cmd_task_rows as emit_rows
|
|
6
|
+
|
|
7
|
+
domain = args[0] if args and not args[0].startswith("-") else None
|
|
8
|
+
day = None
|
|
9
|
+
if "--day" in args:
|
|
10
|
+
idx = args.index("--day")
|
|
11
|
+
if idx + 1 < len(args):
|
|
12
|
+
day = args[idx + 1]
|
|
13
|
+
if domain not in full_domains():
|
|
14
|
+
print(json.dumps({
|
|
15
|
+
"error": {
|
|
16
|
+
"code": "invalid_domain",
|
|
17
|
+
"message": f"full domain required, got {domain!r}",
|
|
18
|
+
}
|
|
19
|
+
}))
|
|
20
|
+
return
|
|
21
|
+
if not day or not re.fullmatch(r"\d{4}-\d{2}-\d{2}", day):
|
|
22
|
+
print(json.dumps({
|
|
23
|
+
"error": {
|
|
24
|
+
"code": "day_required",
|
|
25
|
+
"message": "--day YYYY-MM-DD is required",
|
|
26
|
+
}
|
|
27
|
+
}))
|
|
28
|
+
return
|
|
29
|
+
print(json.dumps(emit_rows(domain, day)))
|
|
30
|
+
|
|
31
|
+
def _bridge_error(code: str, message: str) -> None:
|
|
32
|
+
print(json.dumps({"error": {"code": code, "message": message}}))
|
|
33
|
+
|
|
34
|
+
def _bridge_flag(args, name: str):
|
|
35
|
+
if name in args:
|
|
36
|
+
idx = args.index(name)
|
|
37
|
+
if idx + 1 < len(args):
|
|
38
|
+
return args[idx + 1]
|
|
39
|
+
return None
|
|
40
|
+
|
|
41
|
+
def _bridge_domain(args):
|
|
42
|
+
from task_rows import full_domains
|
|
43
|
+
|
|
44
|
+
domain = args[0] if args and not args[0].startswith("-") else None
|
|
45
|
+
if domain not in full_domains():
|
|
46
|
+
_bridge_error("invalid_domain", f"full domain required, got {domain!r}")
|
|
47
|
+
return None
|
|
48
|
+
return domain
|
|
49
|
+
|
|
50
|
+
def _bridge_write(fn):
|
|
51
|
+
from task_write import LockLost, TaskFileLocked, TaskRunning
|
|
52
|
+
from task_work_metadata import WorkMetadataError
|
|
53
|
+
|
|
54
|
+
try:
|
|
55
|
+
return fn()
|
|
56
|
+
except TaskFileLocked as exc:
|
|
57
|
+
_bridge_error("task_file_locked", str(exc))
|
|
58
|
+
except TaskRunning as exc:
|
|
59
|
+
_bridge_error("task_running", str(exc))
|
|
60
|
+
except LockLost as exc:
|
|
61
|
+
_bridge_error("lock_lost", str(exc))
|
|
62
|
+
except WorkMetadataError as exc:
|
|
63
|
+
_bridge_error(exc.code, str(exc))
|
|
64
|
+
except ValueError as exc:
|
|
65
|
+
_bridge_error("invalid_section", str(exc))
|
|
66
|
+
return None
|
|
67
|
+
|
|
68
|
+
def cmd_task_capture(args):
|
|
69
|
+
from task_write import capture
|
|
70
|
+
|
|
71
|
+
domain = _bridge_domain(args)
|
|
72
|
+
if domain is None:
|
|
73
|
+
return
|
|
74
|
+
section = _bridge_flag(args, "--section")
|
|
75
|
+
if not section:
|
|
76
|
+
_bridge_error("section_required", "--section is required")
|
|
77
|
+
return
|
|
78
|
+
text = sys.stdin.read().strip()
|
|
79
|
+
if not text:
|
|
80
|
+
_bridge_error("text_required", "task text required on STDIN")
|
|
81
|
+
return
|
|
82
|
+
result = _bridge_write(lambda: capture(
|
|
83
|
+
domain,
|
|
84
|
+
text,
|
|
85
|
+
section=section,
|
|
86
|
+
run_at=_bridge_flag(args, "--run-at"),
|
|
87
|
+
purge_before=_bridge_flag(args, "--purge-before"),
|
|
88
|
+
))
|
|
89
|
+
if result is not None:
|
|
90
|
+
print(json.dumps(result))
|
|
91
|
+
|
|
92
|
+
def cmd_task_set_run_at(args):
|
|
93
|
+
from task_write import set_run_at
|
|
94
|
+
|
|
95
|
+
domain = _bridge_domain(args)
|
|
96
|
+
if domain is None:
|
|
97
|
+
return
|
|
98
|
+
if len(args) < 3:
|
|
99
|
+
_bridge_error("argv_required", "usage: task-set-run-at <domain> <id> <YYYY-MM-DD HH:MM>|--clear")
|
|
100
|
+
return
|
|
101
|
+
task_id = args[1]
|
|
102
|
+
raw = args[2]
|
|
103
|
+
run_at = None if raw == "--clear" else raw
|
|
104
|
+
ok = _bridge_write(lambda: set_run_at(domain, task_id, run_at))
|
|
105
|
+
if ok is None:
|
|
106
|
+
return
|
|
107
|
+
if ok is False:
|
|
108
|
+
_bridge_error("task_not_found", f"no task {task_id} in {domain}")
|
|
109
|
+
return
|
|
110
|
+
print(json.dumps({"ok": True}))
|
|
111
|
+
|
|
112
|
+
def cmd_task_set_text(args):
|
|
113
|
+
"""Rewrite a task's words. Text arrives on STDIN so it needs no quoting."""
|
|
114
|
+
from task_write import set_text
|
|
115
|
+
|
|
116
|
+
domain = _bridge_domain(args)
|
|
117
|
+
if domain is None:
|
|
118
|
+
return
|
|
119
|
+
if len(args) < 2:
|
|
120
|
+
_bridge_error("argv_required", "usage: task-set-text <domain> <id> (text on STDIN)")
|
|
121
|
+
return
|
|
122
|
+
task_id = args[1]
|
|
123
|
+
text = sys.stdin.read().strip()
|
|
124
|
+
if not text:
|
|
125
|
+
_bridge_error("text_required", "task text required on STDIN")
|
|
126
|
+
return
|
|
127
|
+
ok = _bridge_write(lambda: set_text(domain, task_id, text))
|
|
128
|
+
if ok is None:
|
|
129
|
+
return
|
|
130
|
+
if ok is False:
|
|
131
|
+
_bridge_error("task_not_found", f"no task {task_id} in {domain}")
|
|
132
|
+
return
|
|
133
|
+
print(json.dumps({"ok": True}))
|
|
134
|
+
|
|
135
|
+
def cmd_task_work_write(args, *, linking=False):
|
|
136
|
+
from task_write import set_work_stage, link_meeting
|
|
137
|
+
domain = _bridge_domain(args)
|
|
138
|
+
if domain is None:
|
|
139
|
+
return
|
|
140
|
+
if len(args) != (2 if linking else 3):
|
|
141
|
+
_bridge_error("argv_required", "Expected domain, task ID, and phase for stage writes.")
|
|
142
|
+
return
|
|
143
|
+
try:
|
|
144
|
+
raw = sys.stdin.read(32_769)
|
|
145
|
+
if len(raw.encode()) > 32_768:
|
|
146
|
+
raise ValueError()
|
|
147
|
+
body = json.loads(raw)
|
|
148
|
+
if not isinstance(body, dict) or not isinstance(body.get("expectedText"), str) or not isinstance(body.get("expectedRevision"), str):
|
|
149
|
+
raise ValueError()
|
|
150
|
+
allowed = {"expectedText", "expectedRevision", "meeting", "removeRecordId"} if linking else {"expectedText", "expectedRevision"}
|
|
151
|
+
if set(body) - allowed:
|
|
152
|
+
raise ValueError()
|
|
153
|
+
except (ValueError, TypeError):
|
|
154
|
+
_bridge_error("invalid_work_request", "Bounded JSON with expectedText and expectedRevision is required.")
|
|
155
|
+
return
|
|
156
|
+
if linking:
|
|
157
|
+
ok = _bridge_write(lambda: link_meeting(domain, args[1], expected_text=body["expectedText"], expected_revision=body["expectedRevision"], meeting=body.get("meeting"), remove_record_id=body.get("removeRecordId")))
|
|
158
|
+
else:
|
|
159
|
+
ok = _bridge_write(lambda: set_work_stage(domain, args[1], args[2], expected_text=body["expectedText"], expected_revision=body["expectedRevision"]))
|
|
160
|
+
if ok is False:
|
|
161
|
+
_bridge_error("task_not_found", "Task not found in this domain.")
|
|
162
|
+
elif ok is not None:
|
|
163
|
+
print(json.dumps({"ok": True}))
|
|
164
|
+
|
|
165
|
+
def cmd_task_set_stage(args):
|
|
166
|
+
"""Move a task between board stages. Clearing returns it to planning, which
|
|
167
|
+
is the unmarked default, so the marker is removed rather than written."""
|
|
168
|
+
from task_write import set_stage
|
|
169
|
+
|
|
170
|
+
domain = _bridge_domain(args)
|
|
171
|
+
if domain is None:
|
|
172
|
+
return
|
|
173
|
+
if len(args) < 3:
|
|
174
|
+
_bridge_error("argv_required", "usage: task-set-stage <domain> <id> planning|active|review|--clear")
|
|
175
|
+
return
|
|
176
|
+
# Validated HERE, not via except: _bridge_write catches ValueError itself and
|
|
177
|
+
# reports it as invalid_section, so a try/except around it never fires.
|
|
178
|
+
stage = args[2]
|
|
179
|
+
if stage not in ("--clear", "planning", "active", "review"):
|
|
180
|
+
_bridge_error("invalid_stage", f"invalid stage: {stage}")
|
|
181
|
+
return
|
|
182
|
+
ok = _bridge_write(lambda: set_stage(domain, task_id=args[1], stage=stage))
|
|
183
|
+
if ok is None:
|
|
184
|
+
return
|
|
185
|
+
if ok is False:
|
|
186
|
+
_bridge_error("task_not_found", f"no task {args[1]} in {domain}")
|
|
187
|
+
return
|
|
188
|
+
print(json.dumps({"ok": True}))
|
|
189
|
+
|
|
190
|
+
def cmd_task_set_done_when(args):
|
|
191
|
+
"""Set or clear the finish line. Text arrives on STDIN so it needs no
|
|
192
|
+
quoting; an empty body clears it. A dispatch is refused while it is empty."""
|
|
193
|
+
from task_write import set_done_when
|
|
194
|
+
|
|
195
|
+
domain = _bridge_domain(args)
|
|
196
|
+
if domain is None:
|
|
197
|
+
return
|
|
198
|
+
if len(args) < 2:
|
|
199
|
+
_bridge_error("argv_required", "usage: task-set-done-when <domain> <id> (text on STDIN, empty clears)")
|
|
200
|
+
return
|
|
201
|
+
text = sys.stdin.read().strip()
|
|
202
|
+
if "\n" in text or "**" in text:
|
|
203
|
+
_bridge_error("invalid_done_when", "done_when must be a single line without ** markup")
|
|
204
|
+
return
|
|
205
|
+
ok = _bridge_write(lambda: set_done_when(domain, task_id=args[1], text=text))
|
|
206
|
+
if ok is None:
|
|
207
|
+
return
|
|
208
|
+
if ok is False:
|
|
209
|
+
_bridge_error("task_not_found", f"no task {args[1]} in {domain}")
|
|
210
|
+
return
|
|
211
|
+
print(json.dumps({"ok": True}))
|
|
212
|
+
|
|
213
|
+
def cmd_task_set_marker(args):
|
|
214
|
+
from task_write import set_marker
|
|
215
|
+
|
|
216
|
+
domain = _bridge_domain(args)
|
|
217
|
+
if domain is None:
|
|
218
|
+
return
|
|
219
|
+
if len(args) < 3:
|
|
220
|
+
_bridge_error("argv_required", "usage: task-set-marker <domain> <id> running|done:<N>|failed:<N>|failed|--clear")
|
|
221
|
+
return
|
|
222
|
+
marker = None if args[2] == "--clear" else args[2]
|
|
223
|
+
ok = _bridge_write(lambda: set_marker(domain, task_id=args[1], marker=marker))
|
|
224
|
+
if ok is None:
|
|
225
|
+
return
|
|
226
|
+
if ok is False:
|
|
227
|
+
_bridge_error("task_not_found", f"no task {args[1]} in {domain}")
|
|
228
|
+
return
|
|
229
|
+
print(json.dumps({"ok": True}))
|
|
230
|
+
|
|
231
|
+
def cmd_task_move(args):
|
|
232
|
+
from task_write import move
|
|
233
|
+
|
|
234
|
+
domain = _bridge_domain(args)
|
|
235
|
+
if domain is None:
|
|
236
|
+
return
|
|
237
|
+
if len(args) < 2:
|
|
238
|
+
_bridge_error("argv_required", "usage: task-move <domain> <id> --section <section>")
|
|
239
|
+
return
|
|
240
|
+
section = _bridge_flag(args, "--section")
|
|
241
|
+
if not section:
|
|
242
|
+
_bridge_error("section_required", "--section is required")
|
|
243
|
+
return
|
|
244
|
+
ok = _bridge_write(lambda: move(
|
|
245
|
+
domain, args[1], section, purge_before=_bridge_flag(args, "--purge-before")
|
|
246
|
+
))
|
|
247
|
+
if ok is None:
|
|
248
|
+
return
|
|
249
|
+
if ok is False:
|
|
250
|
+
_bridge_error("task_not_found", f"no task {args[1]} in {domain}")
|
|
251
|
+
return
|
|
252
|
+
print(json.dumps({"ok": True}))
|
|
253
|
+
|
|
254
|
+
def cmd_task_check(args):
|
|
255
|
+
from task_write import check
|
|
256
|
+
|
|
257
|
+
domain = _bridge_domain(args)
|
|
258
|
+
if domain is None:
|
|
259
|
+
return
|
|
260
|
+
text = _bridge_flag(args, "--text")
|
|
261
|
+
task_id = None
|
|
262
|
+
if len(args) >= 2 and not args[1].startswith("-"):
|
|
263
|
+
task_id = args[1]
|
|
264
|
+
if not task_id and not text:
|
|
265
|
+
_bridge_error("argv_required", "usage: task-check <domain> <id>|--text <…> [--uncheck]")
|
|
266
|
+
return
|
|
267
|
+
status = _bridge_write(lambda: check(
|
|
268
|
+
domain, task_id, text=text, uncheck="--uncheck" in args
|
|
269
|
+
))
|
|
270
|
+
if status is None:
|
|
271
|
+
return
|
|
272
|
+
if status != "ok":
|
|
273
|
+
_bridge_error(status, status.replace("_", " "))
|
|
274
|
+
return
|
|
275
|
+
print(json.dumps({"ok": True}))
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import string
|
|
2
|
+
class TaskDeduplicator:
|
|
3
|
+
@staticmethod
|
|
4
|
+
def normalize(text: str) -> str:
|
|
5
|
+
"""Normalize text for comparison."""
|
|
6
|
+
from task_rows import strip_markers
|
|
7
|
+
text, _ = strip_markers(text)
|
|
8
|
+
text = text.lower()
|
|
9
|
+
text = text.translate(str.maketrans('', '', string.punctuation))
|
|
10
|
+
text = ' '.join(text.split())
|
|
11
|
+
return text
|