omnimem 0.1.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 +89 -0
- package/bin/omnimem +5 -0
- package/db/schema.sql +78 -0
- package/docs/quickstart-10min.md +35 -0
- package/omnimem/__init__.py +2 -0
- package/omnimem/adapters.py +123 -0
- package/omnimem/cli.py +579 -0
- package/omnimem/core.py +826 -0
- package/omnimem/webui.py +602 -0
- package/package.json +37 -0
- package/scripts/attach_project.sh +25 -0
- package/scripts/bootstrap.sh +77 -0
- package/scripts/detach_project.sh +21 -0
- package/scripts/install.sh +94 -0
- package/scripts/uninstall.sh +13 -0
- package/scripts/verify_phase_a.sh +52 -0
- package/scripts/verify_phase_b.sh +21 -0
- package/scripts/verify_phase_c.sh +19 -0
- package/scripts/verify_phase_d.sh +28 -0
- package/spec/changelog.md +14 -0
- package/spec/memory-envelope.schema.json +111 -0
- package/spec/memory-event.schema.json +31 -0
- package/spec/protocol.md +77 -0
- package/templates/project-minimal/.omnimem-ignore +8 -0
- package/templates/project-minimal/.omnimem-session.md +13 -0
- package/templates/project-minimal/.omnimem.json +10 -0
package/omnimem/cli.py
ADDED
|
@@ -0,0 +1,579 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import argparse
|
|
4
|
+
import json
|
|
5
|
+
import os
|
|
6
|
+
import subprocess
|
|
7
|
+
import shutil
|
|
8
|
+
import sys
|
|
9
|
+
from pathlib import Path
|
|
10
|
+
|
|
11
|
+
from .adapters import (
|
|
12
|
+
notion_query_database,
|
|
13
|
+
notion_write_page,
|
|
14
|
+
r2_get_presigned,
|
|
15
|
+
r2_put_presigned,
|
|
16
|
+
resolve_cred_ref,
|
|
17
|
+
)
|
|
18
|
+
from .core import (
|
|
19
|
+
KIND_SET,
|
|
20
|
+
LAYER_SET,
|
|
21
|
+
build_brief,
|
|
22
|
+
find_memories,
|
|
23
|
+
load_config,
|
|
24
|
+
load_config_with_path,
|
|
25
|
+
parse_list_csv,
|
|
26
|
+
parse_ref,
|
|
27
|
+
resolve_paths,
|
|
28
|
+
run_sync_daemon,
|
|
29
|
+
sync_placeholder,
|
|
30
|
+
verify_storage,
|
|
31
|
+
write_memory,
|
|
32
|
+
)
|
|
33
|
+
from .webui import run_webui
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
def schema_sql_path() -> Path:
|
|
37
|
+
here = Path(__file__).resolve()
|
|
38
|
+
candidates = [
|
|
39
|
+
here.parent.parent / "db" / "schema.sql",
|
|
40
|
+
here.parent.parent.parent / "db" / "schema.sql",
|
|
41
|
+
Path.cwd() / "db" / "schema.sql",
|
|
42
|
+
]
|
|
43
|
+
for c in candidates:
|
|
44
|
+
if c.exists():
|
|
45
|
+
return c
|
|
46
|
+
raise FileNotFoundError("schema.sql not found in expected locations")
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
def add_common_write_args(p: argparse.ArgumentParser) -> None:
|
|
50
|
+
p.add_argument("--config", help="path to omnimem config json")
|
|
51
|
+
p.add_argument("--layer", choices=sorted(LAYER_SET), default="instant")
|
|
52
|
+
p.add_argument("--kind", choices=sorted(KIND_SET), default="note")
|
|
53
|
+
p.add_argument("--summary", required=True)
|
|
54
|
+
p.add_argument("--body", default="")
|
|
55
|
+
p.add_argument("--body-file")
|
|
56
|
+
p.add_argument("--tags", help="comma-separated")
|
|
57
|
+
p.add_argument("--ref", action="append", default=[], help="type:target[:note]")
|
|
58
|
+
p.add_argument("--cred-ref", action="append", default=[])
|
|
59
|
+
p.add_argument("--tool", default="cli")
|
|
60
|
+
p.add_argument("--account", default="default")
|
|
61
|
+
p.add_argument("--device", default="local")
|
|
62
|
+
p.add_argument("--session-id", default="session-local")
|
|
63
|
+
p.add_argument("--project-id", default="global")
|
|
64
|
+
p.add_argument("--workspace", default="")
|
|
65
|
+
p.add_argument("--importance", type=float, default=0.5)
|
|
66
|
+
p.add_argument("--confidence", type=float, default=0.5)
|
|
67
|
+
p.add_argument("--stability", type=float, default=0.5)
|
|
68
|
+
p.add_argument("--reuse-count", type=int, default=0)
|
|
69
|
+
p.add_argument("--volatility", type=float, default=0.5)
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
def body_text(args: argparse.Namespace) -> str:
|
|
73
|
+
if args.body_file:
|
|
74
|
+
return Path(args.body_file).read_text(encoding="utf-8")
|
|
75
|
+
return args.body
|
|
76
|
+
|
|
77
|
+
|
|
78
|
+
def print_json(obj: object) -> None:
|
|
79
|
+
print(json.dumps(obj, ensure_ascii=False, indent=2))
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
def cfg_path_arg(args: argparse.Namespace) -> Path | None:
|
|
83
|
+
raw = getattr(args, "config", None) or getattr(args, "global_config", None)
|
|
84
|
+
return Path(raw) if raw else None
|
|
85
|
+
|
|
86
|
+
|
|
87
|
+
def cmd_write(args: argparse.Namespace) -> int:
|
|
88
|
+
cfg = load_config(cfg_path_arg(args))
|
|
89
|
+
paths = resolve_paths(cfg)
|
|
90
|
+
refs = [parse_ref(x) for x in args.ref]
|
|
91
|
+
result = write_memory(
|
|
92
|
+
paths=paths,
|
|
93
|
+
schema_sql_path=schema_sql_path(),
|
|
94
|
+
layer=args.layer,
|
|
95
|
+
kind=args.kind,
|
|
96
|
+
summary=args.summary,
|
|
97
|
+
body=body_text(args),
|
|
98
|
+
tags=parse_list_csv(args.tags),
|
|
99
|
+
refs=refs,
|
|
100
|
+
cred_refs=args.cred_ref,
|
|
101
|
+
tool=args.tool,
|
|
102
|
+
account=args.account,
|
|
103
|
+
device=args.device,
|
|
104
|
+
session_id=args.session_id,
|
|
105
|
+
project_id=args.project_id,
|
|
106
|
+
workspace=args.workspace,
|
|
107
|
+
importance=args.importance,
|
|
108
|
+
confidence=args.confidence,
|
|
109
|
+
stability=args.stability,
|
|
110
|
+
reuse_count=args.reuse_count,
|
|
111
|
+
volatility=args.volatility,
|
|
112
|
+
event_type="memory.write",
|
|
113
|
+
)
|
|
114
|
+
print_json({
|
|
115
|
+
"ok": True,
|
|
116
|
+
"memory_id": result["memory"]["id"],
|
|
117
|
+
"layer": result["memory"]["layer"],
|
|
118
|
+
"kind": result["memory"]["kind"],
|
|
119
|
+
"body_md_path": result["memory"]["body_md_path"],
|
|
120
|
+
})
|
|
121
|
+
return 0
|
|
122
|
+
|
|
123
|
+
|
|
124
|
+
def cmd_checkpoint(args: argparse.Namespace) -> int:
|
|
125
|
+
cfg = load_config(cfg_path_arg(args))
|
|
126
|
+
paths = resolve_paths(cfg)
|
|
127
|
+
body = (
|
|
128
|
+
"## Checkpoint\n\n"
|
|
129
|
+
f"- Session: {args.session_id}\n"
|
|
130
|
+
f"- Goal: {args.goal}\n"
|
|
131
|
+
f"- Result: {args.result}\n"
|
|
132
|
+
f"- Next: {args.next_step}\n"
|
|
133
|
+
f"- Risks: {args.risks}\n"
|
|
134
|
+
)
|
|
135
|
+
result = write_memory(
|
|
136
|
+
paths=paths,
|
|
137
|
+
schema_sql_path=schema_sql_path(),
|
|
138
|
+
layer=args.layer,
|
|
139
|
+
kind="checkpoint",
|
|
140
|
+
summary=args.summary,
|
|
141
|
+
body=body,
|
|
142
|
+
tags=parse_list_csv(args.tags),
|
|
143
|
+
refs=[parse_ref(x) for x in args.ref],
|
|
144
|
+
cred_refs=args.cred_ref,
|
|
145
|
+
tool=args.tool,
|
|
146
|
+
account=args.account,
|
|
147
|
+
device=args.device,
|
|
148
|
+
session_id=args.session_id,
|
|
149
|
+
project_id=args.project_id,
|
|
150
|
+
workspace=args.workspace,
|
|
151
|
+
importance=args.importance,
|
|
152
|
+
confidence=args.confidence,
|
|
153
|
+
stability=args.stability,
|
|
154
|
+
reuse_count=args.reuse_count,
|
|
155
|
+
volatility=args.volatility,
|
|
156
|
+
event_type="memory.checkpoint",
|
|
157
|
+
)
|
|
158
|
+
print_json({
|
|
159
|
+
"ok": True,
|
|
160
|
+
"checkpoint_id": result["memory"]["id"],
|
|
161
|
+
"body_md_path": result["memory"]["body_md_path"],
|
|
162
|
+
})
|
|
163
|
+
return 0
|
|
164
|
+
|
|
165
|
+
|
|
166
|
+
def cmd_find(args: argparse.Namespace) -> int:
|
|
167
|
+
cfg = load_config(cfg_path_arg(args))
|
|
168
|
+
paths = resolve_paths(cfg)
|
|
169
|
+
rows = find_memories(paths, schema_sql_path(), args.query, args.layer, args.limit)
|
|
170
|
+
print_json({"ok": True, "count": len(rows), "items": rows})
|
|
171
|
+
return 0
|
|
172
|
+
|
|
173
|
+
|
|
174
|
+
def cmd_brief(args: argparse.Namespace) -> int:
|
|
175
|
+
cfg = load_config(cfg_path_arg(args))
|
|
176
|
+
paths = resolve_paths(cfg)
|
|
177
|
+
result = build_brief(paths, schema_sql_path(), args.project_id, args.limit)
|
|
178
|
+
print_json({"ok": True, **result})
|
|
179
|
+
return 0
|
|
180
|
+
|
|
181
|
+
|
|
182
|
+
def cmd_verify(args: argparse.Namespace) -> int:
|
|
183
|
+
cfg = load_config(cfg_path_arg(args))
|
|
184
|
+
paths = resolve_paths(cfg)
|
|
185
|
+
result = verify_storage(paths, schema_sql_path())
|
|
186
|
+
print_json(result)
|
|
187
|
+
return 0 if result.get("ok") else 1
|
|
188
|
+
|
|
189
|
+
|
|
190
|
+
def cmd_sync(args: argparse.Namespace) -> int:
|
|
191
|
+
cfg = load_config(cfg_path_arg(args))
|
|
192
|
+
paths = resolve_paths(cfg)
|
|
193
|
+
sync_cfg = cfg.get("sync", {}).get("github", {})
|
|
194
|
+
remote_name = args.remote_name or sync_cfg.get("remote_name", "origin")
|
|
195
|
+
branch = args.branch or sync_cfg.get("branch", "main")
|
|
196
|
+
remote_url = args.remote_url or sync_cfg.get("remote_url")
|
|
197
|
+
out = sync_placeholder(
|
|
198
|
+
paths,
|
|
199
|
+
schema_sql_path(),
|
|
200
|
+
args.mode,
|
|
201
|
+
remote_name=remote_name,
|
|
202
|
+
branch=branch,
|
|
203
|
+
remote_url=remote_url,
|
|
204
|
+
commit_message=args.commit_message,
|
|
205
|
+
)
|
|
206
|
+
print_json(out)
|
|
207
|
+
return 0 if out.get("ok") else 1
|
|
208
|
+
|
|
209
|
+
|
|
210
|
+
def cmd_webui(args: argparse.Namespace) -> int:
|
|
211
|
+
cfg, cfg_path = load_config_with_path(cfg_path_arg(args))
|
|
212
|
+
run_webui(
|
|
213
|
+
host=args.host,
|
|
214
|
+
port=args.port,
|
|
215
|
+
cfg=cfg,
|
|
216
|
+
cfg_path=cfg_path,
|
|
217
|
+
schema_sql_path=schema_sql_path(),
|
|
218
|
+
sync_runner=sync_placeholder,
|
|
219
|
+
daemon_runner=run_sync_daemon,
|
|
220
|
+
enable_daemon=not args.no_daemon,
|
|
221
|
+
daemon_scan_interval=args.daemon_scan_interval,
|
|
222
|
+
daemon_pull_interval=args.daemon_pull_interval,
|
|
223
|
+
)
|
|
224
|
+
return 0
|
|
225
|
+
|
|
226
|
+
|
|
227
|
+
def cmd_start(args: argparse.Namespace) -> int:
|
|
228
|
+
return cmd_webui(args)
|
|
229
|
+
|
|
230
|
+
|
|
231
|
+
def cmd_config_path(args: argparse.Namespace) -> int:
|
|
232
|
+
_, cfg_path = load_config_with_path(cfg_path_arg(args))
|
|
233
|
+
print_json({"ok": True, "config_path": str(cfg_path)})
|
|
234
|
+
return 0
|
|
235
|
+
|
|
236
|
+
|
|
237
|
+
def cmd_uninstall(args: argparse.Namespace) -> int:
|
|
238
|
+
cfg, cfg_path = load_config_with_path(cfg_path_arg(args))
|
|
239
|
+
paths = resolve_paths(cfg)
|
|
240
|
+
target = paths.root
|
|
241
|
+
|
|
242
|
+
if not args.yes:
|
|
243
|
+
print_json(
|
|
244
|
+
{
|
|
245
|
+
"ok": False,
|
|
246
|
+
"error": "destructive action requires --yes",
|
|
247
|
+
"target": str(target),
|
|
248
|
+
}
|
|
249
|
+
)
|
|
250
|
+
return 1
|
|
251
|
+
|
|
252
|
+
# Safety rail: avoid catastrophic deletion.
|
|
253
|
+
if str(target) in {"/", str(Path.home().resolve())}:
|
|
254
|
+
print_json({"ok": False, "error": f"refuse to uninstall unsafe target: {target}"})
|
|
255
|
+
return 1
|
|
256
|
+
|
|
257
|
+
detached = False
|
|
258
|
+
if args.detach_project:
|
|
259
|
+
project = Path(args.detach_project).expanduser().resolve()
|
|
260
|
+
for name in [".omnimem.json", ".omnimem-session.md", ".omnimem-ignore", ".omnimem-hooks.sh"]:
|
|
261
|
+
fp = project / name
|
|
262
|
+
if fp.exists():
|
|
263
|
+
fp.unlink()
|
|
264
|
+
detached = True
|
|
265
|
+
|
|
266
|
+
if target.exists():
|
|
267
|
+
try:
|
|
268
|
+
shutil.rmtree(target)
|
|
269
|
+
except Exception:
|
|
270
|
+
# On some systems deleting the currently running install tree can fail.
|
|
271
|
+
# Fallback to detached delayed cleanup.
|
|
272
|
+
subprocess.Popen(
|
|
273
|
+
["/bin/sh", "-c", f"sleep 1; rm -rf '{str(target)}'"],
|
|
274
|
+
stdout=subprocess.DEVNULL,
|
|
275
|
+
stderr=subprocess.DEVNULL,
|
|
276
|
+
start_new_session=True,
|
|
277
|
+
)
|
|
278
|
+
if cfg_path.exists() and not str(cfg_path).startswith(str(target)):
|
|
279
|
+
cfg_path.unlink(missing_ok=True)
|
|
280
|
+
|
|
281
|
+
print_json(
|
|
282
|
+
{
|
|
283
|
+
"ok": True,
|
|
284
|
+
"uninstalled": str(target),
|
|
285
|
+
"project_detached": detached,
|
|
286
|
+
}
|
|
287
|
+
)
|
|
288
|
+
return 0
|
|
289
|
+
|
|
290
|
+
|
|
291
|
+
def cmd_bootstrap(args: argparse.Namespace) -> int:
|
|
292
|
+
repo_root = Path(__file__).resolve().parent.parent
|
|
293
|
+
install_script = repo_root / "scripts" / "install.sh"
|
|
294
|
+
attach_script = repo_root / "scripts" / "attach_project.sh"
|
|
295
|
+
|
|
296
|
+
if not install_script.exists():
|
|
297
|
+
print_json({"ok": False, "error": f"install script not found: {install_script}"})
|
|
298
|
+
return 1
|
|
299
|
+
|
|
300
|
+
env = dict(os.environ)
|
|
301
|
+
if args.home:
|
|
302
|
+
env["OMNIMEM_HOME"] = str(Path(args.home).expanduser().resolve())
|
|
303
|
+
|
|
304
|
+
install_cmd = ["bash", str(install_script)]
|
|
305
|
+
if args.wizard:
|
|
306
|
+
install_cmd.append("--wizard")
|
|
307
|
+
if args.remote_name:
|
|
308
|
+
install_cmd.extend(["--remote-name", args.remote_name])
|
|
309
|
+
if args.branch:
|
|
310
|
+
install_cmd.extend(["--branch", args.branch])
|
|
311
|
+
if args.remote_url:
|
|
312
|
+
install_cmd.extend(["--remote-url", args.remote_url])
|
|
313
|
+
subprocess.run(install_cmd, check=True, env=env)
|
|
314
|
+
|
|
315
|
+
attached = False
|
|
316
|
+
if args.attach_project:
|
|
317
|
+
if not attach_script.exists():
|
|
318
|
+
print_json({"ok": False, "error": f"attach script not found: {attach_script}"})
|
|
319
|
+
return 1
|
|
320
|
+
project_id = args.project_id or Path(args.attach_project).name
|
|
321
|
+
subprocess.run(["bash", str(attach_script), args.attach_project, project_id], check=True)
|
|
322
|
+
attached = True
|
|
323
|
+
|
|
324
|
+
print_json(
|
|
325
|
+
{
|
|
326
|
+
"ok": True,
|
|
327
|
+
"installed_home": str(Path(args.home).expanduser().resolve()) if args.home else None,
|
|
328
|
+
"attached_project": attached,
|
|
329
|
+
"next": "~/.omnimem/bin/omnimem start --host 127.0.0.1 --port 8765",
|
|
330
|
+
}
|
|
331
|
+
)
|
|
332
|
+
return 0
|
|
333
|
+
|
|
334
|
+
|
|
335
|
+
def cmd_adapter_cred_resolve(args: argparse.Namespace) -> int:
|
|
336
|
+
value = resolve_cred_ref(args.ref)
|
|
337
|
+
if args.mask:
|
|
338
|
+
shown = value[:2] + "***" + value[-2:] if len(value) >= 6 else "***"
|
|
339
|
+
print_json({"ok": True, "ref": args.ref, "value_preview": shown})
|
|
340
|
+
else:
|
|
341
|
+
print_json({"ok": True, "ref": args.ref, "value": value})
|
|
342
|
+
return 0
|
|
343
|
+
|
|
344
|
+
|
|
345
|
+
def _resolve_token(args: argparse.Namespace) -> str:
|
|
346
|
+
if args.token:
|
|
347
|
+
return args.token
|
|
348
|
+
if args.token_ref:
|
|
349
|
+
return resolve_cred_ref(args.token_ref)
|
|
350
|
+
raise ValueError("token not provided, use --token or --token-ref")
|
|
351
|
+
|
|
352
|
+
|
|
353
|
+
def _resolve_url(url: str | None, url_ref: str | None) -> str:
|
|
354
|
+
if url:
|
|
355
|
+
return url
|
|
356
|
+
if url_ref:
|
|
357
|
+
return resolve_cred_ref(url_ref)
|
|
358
|
+
raise ValueError("url not provided, use --url or --url-ref")
|
|
359
|
+
|
|
360
|
+
|
|
361
|
+
def cmd_adapter_notion_write(args: argparse.Namespace) -> int:
|
|
362
|
+
token = ""
|
|
363
|
+
if not args.dry_run:
|
|
364
|
+
token = _resolve_token(args)
|
|
365
|
+
if args.content_file:
|
|
366
|
+
content = Path(args.content_file).read_text(encoding="utf-8")
|
|
367
|
+
else:
|
|
368
|
+
content = args.content
|
|
369
|
+
out = notion_write_page(
|
|
370
|
+
token=token,
|
|
371
|
+
database_id=args.database_id,
|
|
372
|
+
title=args.title,
|
|
373
|
+
content=content,
|
|
374
|
+
title_property=args.title_property,
|
|
375
|
+
dry_run=args.dry_run,
|
|
376
|
+
)
|
|
377
|
+
print_json(out)
|
|
378
|
+
return 0
|
|
379
|
+
|
|
380
|
+
|
|
381
|
+
def cmd_adapter_notion_query(args: argparse.Namespace) -> int:
|
|
382
|
+
token = ""
|
|
383
|
+
if not args.dry_run:
|
|
384
|
+
token = _resolve_token(args)
|
|
385
|
+
out = notion_query_database(
|
|
386
|
+
token=token,
|
|
387
|
+
database_id=args.database_id,
|
|
388
|
+
page_size=args.page_size,
|
|
389
|
+
dry_run=args.dry_run,
|
|
390
|
+
)
|
|
391
|
+
print_json(out)
|
|
392
|
+
return 0
|
|
393
|
+
|
|
394
|
+
|
|
395
|
+
def cmd_adapter_r2_put(args: argparse.Namespace) -> int:
|
|
396
|
+
out = r2_put_presigned(
|
|
397
|
+
file_path=Path(args.file),
|
|
398
|
+
presigned_url=_resolve_url(args.url, args.url_ref),
|
|
399
|
+
dry_run=args.dry_run,
|
|
400
|
+
)
|
|
401
|
+
print_json(out)
|
|
402
|
+
return 0
|
|
403
|
+
|
|
404
|
+
|
|
405
|
+
def cmd_adapter_r2_get(args: argparse.Namespace) -> int:
|
|
406
|
+
out = r2_get_presigned(
|
|
407
|
+
presigned_url=_resolve_url(args.url, args.url_ref),
|
|
408
|
+
out_path=Path(args.out),
|
|
409
|
+
dry_run=args.dry_run,
|
|
410
|
+
)
|
|
411
|
+
print_json(out)
|
|
412
|
+
return 0
|
|
413
|
+
|
|
414
|
+
|
|
415
|
+
def build_parser() -> argparse.ArgumentParser:
|
|
416
|
+
p = argparse.ArgumentParser(prog="omnimem")
|
|
417
|
+
p.add_argument("--config", dest="global_config", help="path to omnimem config json")
|
|
418
|
+
sub = p.add_subparsers(dest="cmd", required=True)
|
|
419
|
+
|
|
420
|
+
p_write = sub.add_parser("write", help="write a memory")
|
|
421
|
+
add_common_write_args(p_write)
|
|
422
|
+
p_write.set_defaults(func=cmd_write)
|
|
423
|
+
|
|
424
|
+
p_find = sub.add_parser("find", help="find memories")
|
|
425
|
+
p_find.add_argument("--config", help="path to omnimem config json")
|
|
426
|
+
p_find.add_argument("query", nargs="?", default="")
|
|
427
|
+
p_find.add_argument("--layer", choices=sorted(LAYER_SET))
|
|
428
|
+
p_find.add_argument("--limit", type=int, default=10)
|
|
429
|
+
p_find.set_defaults(func=cmd_find)
|
|
430
|
+
|
|
431
|
+
p_checkpoint = sub.add_parser("checkpoint", help="create checkpoint memory")
|
|
432
|
+
p_checkpoint.add_argument("--config", help="path to omnimem config json")
|
|
433
|
+
p_checkpoint.add_argument("--summary", required=True)
|
|
434
|
+
p_checkpoint.add_argument("--goal", default="")
|
|
435
|
+
p_checkpoint.add_argument("--result", default="")
|
|
436
|
+
p_checkpoint.add_argument("--next-step", default="")
|
|
437
|
+
p_checkpoint.add_argument("--risks", default="")
|
|
438
|
+
p_checkpoint.add_argument("--layer", choices=sorted(LAYER_SET), default="short")
|
|
439
|
+
p_checkpoint.add_argument("--tags", help="comma-separated")
|
|
440
|
+
p_checkpoint.add_argument("--ref", action="append", default=[])
|
|
441
|
+
p_checkpoint.add_argument("--cred-ref", action="append", default=[])
|
|
442
|
+
p_checkpoint.add_argument("--tool", default="cli")
|
|
443
|
+
p_checkpoint.add_argument("--account", default="default")
|
|
444
|
+
p_checkpoint.add_argument("--device", default="local")
|
|
445
|
+
p_checkpoint.add_argument("--session-id", default="session-local")
|
|
446
|
+
p_checkpoint.add_argument("--project-id", default="global")
|
|
447
|
+
p_checkpoint.add_argument("--workspace", default="")
|
|
448
|
+
p_checkpoint.add_argument("--importance", type=float, default=0.7)
|
|
449
|
+
p_checkpoint.add_argument("--confidence", type=float, default=0.7)
|
|
450
|
+
p_checkpoint.add_argument("--stability", type=float, default=0.6)
|
|
451
|
+
p_checkpoint.add_argument("--reuse-count", type=int, default=0)
|
|
452
|
+
p_checkpoint.add_argument("--volatility", type=float, default=0.4)
|
|
453
|
+
p_checkpoint.set_defaults(func=cmd_checkpoint)
|
|
454
|
+
|
|
455
|
+
p_brief = sub.add_parser("brief", help="startup summary")
|
|
456
|
+
p_brief.add_argument("--config", help="path to omnimem config json")
|
|
457
|
+
p_brief.add_argument("--project-id", default="")
|
|
458
|
+
p_brief.add_argument("--limit", type=int, default=8)
|
|
459
|
+
p_brief.set_defaults(func=cmd_brief)
|
|
460
|
+
|
|
461
|
+
p_verify = sub.add_parser("verify", help="consistency verification")
|
|
462
|
+
p_verify.add_argument("--config", help="path to omnimem config json")
|
|
463
|
+
p_verify.set_defaults(func=cmd_verify)
|
|
464
|
+
|
|
465
|
+
p_sync = sub.add_parser("sync", help="sync actions")
|
|
466
|
+
p_sync.add_argument("--config", help="path to omnimem config json")
|
|
467
|
+
p_sync.add_argument(
|
|
468
|
+
"--mode",
|
|
469
|
+
choices=["noop", "git", "github-status", "github-push", "github-pull", "github-bootstrap"],
|
|
470
|
+
default="noop",
|
|
471
|
+
)
|
|
472
|
+
p_sync.add_argument("--remote-name", help="git remote name, default origin")
|
|
473
|
+
p_sync.add_argument("--remote-url", help="remote url (https://... or git@...); optional")
|
|
474
|
+
p_sync.add_argument("--branch", help="target branch, default main")
|
|
475
|
+
p_sync.add_argument("--commit-message", default="chore(memory): sync snapshot")
|
|
476
|
+
p_sync.set_defaults(func=cmd_sync)
|
|
477
|
+
|
|
478
|
+
p_webui = sub.add_parser("webui", help="start local web ui")
|
|
479
|
+
p_webui.add_argument("--config", help="path to omnimem config json")
|
|
480
|
+
p_webui.add_argument("--host", default="127.0.0.1")
|
|
481
|
+
p_webui.add_argument("--port", type=int, default=8765)
|
|
482
|
+
p_webui.add_argument("--no-daemon", action="store_true", help="disable background quasi-realtime sync")
|
|
483
|
+
p_webui.add_argument("--daemon-scan-interval", type=int, default=8)
|
|
484
|
+
p_webui.add_argument("--daemon-pull-interval", type=int, default=30)
|
|
485
|
+
p_webui.set_defaults(func=cmd_webui)
|
|
486
|
+
|
|
487
|
+
p_start = sub.add_parser("start", help="start app (webui + sync daemon)")
|
|
488
|
+
p_start.add_argument("--config", help="path to omnimem config json")
|
|
489
|
+
p_start.add_argument("--host", default="127.0.0.1")
|
|
490
|
+
p_start.add_argument("--port", type=int, default=8765)
|
|
491
|
+
p_start.add_argument("--no-daemon", action="store_true", help="disable background quasi-realtime sync")
|
|
492
|
+
p_start.add_argument("--daemon-scan-interval", type=int, default=8)
|
|
493
|
+
p_start.add_argument("--daemon-pull-interval", type=int, default=30)
|
|
494
|
+
p_start.set_defaults(func=cmd_start)
|
|
495
|
+
|
|
496
|
+
p_cfg_path = sub.add_parser("config-path", help="print active config path")
|
|
497
|
+
p_cfg_path.add_argument("--config", help="path to omnimem config json")
|
|
498
|
+
p_cfg_path.set_defaults(func=cmd_config_path)
|
|
499
|
+
|
|
500
|
+
p_uninstall = sub.add_parser("uninstall", help="uninstall local omnimem home")
|
|
501
|
+
p_uninstall.add_argument("--config", help="path to omnimem config json")
|
|
502
|
+
p_uninstall.add_argument("--yes", action="store_true", help="confirm deletion")
|
|
503
|
+
p_uninstall.add_argument("--detach-project", help="optional project path to detach omni files")
|
|
504
|
+
p_uninstall.set_defaults(func=cmd_uninstall)
|
|
505
|
+
|
|
506
|
+
p_bootstrap = sub.add_parser("bootstrap", help="install local runtime from packaged files")
|
|
507
|
+
p_bootstrap.add_argument("--config", help="path to omnimem config json")
|
|
508
|
+
p_bootstrap.add_argument("--wizard", action="store_true")
|
|
509
|
+
p_bootstrap.add_argument("--home", help="optional override via OMNIMEM_HOME")
|
|
510
|
+
p_bootstrap.add_argument("--remote-name", default="origin")
|
|
511
|
+
p_bootstrap.add_argument("--branch", default="main")
|
|
512
|
+
p_bootstrap.add_argument("--remote-url")
|
|
513
|
+
p_bootstrap.add_argument("--attach-project", help="optional project path to attach")
|
|
514
|
+
p_bootstrap.add_argument("--project-id", help="optional project id for attach")
|
|
515
|
+
p_bootstrap.set_defaults(func=cmd_bootstrap)
|
|
516
|
+
|
|
517
|
+
p_adapter = sub.add_parser("adapter", help="external adapters")
|
|
518
|
+
p_adapter.add_argument("--config", help="path to omnimem config json")
|
|
519
|
+
adapter_sub = p_adapter.add_subparsers(dest="adapter_cmd", required=True)
|
|
520
|
+
|
|
521
|
+
p_cred = adapter_sub.add_parser("cred-resolve", help="resolve credential reference")
|
|
522
|
+
p_cred.add_argument("--ref", required=True, help="env://KEY or op://vault/item/field")
|
|
523
|
+
p_cred.add_argument("--mask", action="store_true", help="hide sensitive value in output")
|
|
524
|
+
p_cred.set_defaults(func=cmd_adapter_cred_resolve)
|
|
525
|
+
|
|
526
|
+
p_notion_write = adapter_sub.add_parser("notion-write", help="write a page to notion database")
|
|
527
|
+
p_notion_write.add_argument("--database-id", required=True)
|
|
528
|
+
p_notion_write.add_argument("--title", required=True)
|
|
529
|
+
p_notion_write.add_argument("--content", default="")
|
|
530
|
+
p_notion_write.add_argument("--content-file")
|
|
531
|
+
p_notion_write.add_argument("--title-property", default="Name")
|
|
532
|
+
p_notion_write.add_argument("--token")
|
|
533
|
+
p_notion_write.add_argument("--token-ref")
|
|
534
|
+
p_notion_write.add_argument("--dry-run", action="store_true")
|
|
535
|
+
p_notion_write.set_defaults(func=cmd_adapter_notion_write)
|
|
536
|
+
|
|
537
|
+
p_notion_query = adapter_sub.add_parser("notion-query", help="query notion database")
|
|
538
|
+
p_notion_query.add_argument("--database-id", required=True)
|
|
539
|
+
p_notion_query.add_argument("--page-size", type=int, default=5)
|
|
540
|
+
p_notion_query.add_argument("--token")
|
|
541
|
+
p_notion_query.add_argument("--token-ref")
|
|
542
|
+
p_notion_query.add_argument("--dry-run", action="store_true")
|
|
543
|
+
p_notion_query.set_defaults(func=cmd_adapter_notion_query)
|
|
544
|
+
|
|
545
|
+
p_r2_put = adapter_sub.add_parser("r2-put", help="upload file via presigned PUT URL")
|
|
546
|
+
p_r2_put.add_argument("--file", required=True)
|
|
547
|
+
p_r2_put.add_argument("--url")
|
|
548
|
+
p_r2_put.add_argument("--url-ref")
|
|
549
|
+
p_r2_put.add_argument("--dry-run", action="store_true")
|
|
550
|
+
p_r2_put.set_defaults(func=cmd_adapter_r2_put)
|
|
551
|
+
|
|
552
|
+
p_r2_get = adapter_sub.add_parser("r2-get", help="download file via presigned GET URL")
|
|
553
|
+
p_r2_get.add_argument("--out", required=True)
|
|
554
|
+
p_r2_get.add_argument("--url")
|
|
555
|
+
p_r2_get.add_argument("--url-ref")
|
|
556
|
+
p_r2_get.add_argument("--dry-run", action="store_true")
|
|
557
|
+
p_r2_get.set_defaults(func=cmd_adapter_r2_get)
|
|
558
|
+
|
|
559
|
+
return p
|
|
560
|
+
|
|
561
|
+
|
|
562
|
+
def main(argv: list[str] | None = None) -> int:
|
|
563
|
+
raw_argv = list(argv) if argv is not None else sys.argv[1:]
|
|
564
|
+
if not raw_argv:
|
|
565
|
+
raw_argv = ["start"]
|
|
566
|
+
elif raw_argv[0] in {"--host", "--port"} or raw_argv[0].startswith("--host=") or raw_argv[0].startswith("--port="):
|
|
567
|
+
raw_argv = ["start", *raw_argv]
|
|
568
|
+
|
|
569
|
+
parser = build_parser()
|
|
570
|
+
args = parser.parse_args(raw_argv)
|
|
571
|
+
try:
|
|
572
|
+
return args.func(args)
|
|
573
|
+
except Exception as exc:
|
|
574
|
+
print_json({"ok": False, "error": str(exc)})
|
|
575
|
+
return 1
|
|
576
|
+
|
|
577
|
+
|
|
578
|
+
if __name__ == "__main__":
|
|
579
|
+
sys.exit(main())
|