am-memory 0.1.0__py3-none-any.whl
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.
- agent_memory/__init__.py +4 -0
- agent_memory/cli.py +425 -0
- agent_memory/dashboard.py +488 -0
- agent_memory/db.py +264 -0
- agent_memory/dream.py +1057 -0
- agent_memory/embedding.py +278 -0
- agent_memory/extract.py +120 -0
- agent_memory/init_cmd.py +181 -0
- agent_memory/llm.py +43 -0
- agent_memory/llm_extract.py +186 -0
- agent_memory/mcp_server.py +278 -0
- agent_memory/models.py +21 -0
- agent_memory/search.py +380 -0
- agent_memory/session.py +567 -0
- agent_memory/state.py +51 -0
- agent_memory/store.py +717 -0
- agent_memory/vector.py +115 -0
- agent_memory/watch.py +81 -0
- agent_memory/write_queue.py +186 -0
- am_memory-0.1.0.dist-info/METADATA +573 -0
- am_memory-0.1.0.dist-info/RECORD +24 -0
- am_memory-0.1.0.dist-info/WHEEL +5 -0
- am_memory-0.1.0.dist-info/entry_points.txt +2 -0
- am_memory-0.1.0.dist-info/top_level.txt +1 -0
agent_memory/__init__.py
ADDED
agent_memory/cli.py
ADDED
|
@@ -0,0 +1,425 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""CLI entry point for am-memory.
|
|
3
|
+
|
|
4
|
+
Usage:
|
|
5
|
+
am init # one-time setup (MCP + hooks + CLAUDE.md)
|
|
6
|
+
am mcp # start MCP server (stdio)
|
|
7
|
+
am doc save --title T --content C [--priority P1] [--source hook]
|
|
8
|
+
am search --query Q [--max-tokens 1500] [--format inject|json]
|
|
9
|
+
am state set --key K --value V
|
|
10
|
+
am state get --key K
|
|
11
|
+
am session start --project P --topic T # prints session_id
|
|
12
|
+
am session end --session-id S [--summary "..."]
|
|
13
|
+
am session message --session-id S --role R --content C
|
|
14
|
+
am session resume --session-id S [--max-tokens 2000]
|
|
15
|
+
"""
|
|
16
|
+
import sys
|
|
17
|
+
import json
|
|
18
|
+
import argparse
|
|
19
|
+
from agent_memory.store import MemoryStore
|
|
20
|
+
|
|
21
|
+
_store = None
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
def get_store() -> MemoryStore:
|
|
25
|
+
global _store
|
|
26
|
+
if _store is None:
|
|
27
|
+
_store = MemoryStore()
|
|
28
|
+
return _store
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
def cmd_doc(args):
|
|
32
|
+
if args.action == "save":
|
|
33
|
+
content = args.content or sys.stdin.read()
|
|
34
|
+
doc_id = get_store().save(
|
|
35
|
+
title=args.title,
|
|
36
|
+
content=content,
|
|
37
|
+
priority=getattr(args, "priority", "P1"),
|
|
38
|
+
source=getattr(args, "source", "hook"),
|
|
39
|
+
file_path=getattr(args, "file_path", None) or None,
|
|
40
|
+
)
|
|
41
|
+
print(doc_id)
|
|
42
|
+
|
|
43
|
+
elif args.action == "prune":
|
|
44
|
+
deleted = get_store().prune_expired()
|
|
45
|
+
print(f"Pruned {deleted} expired document(s)")
|
|
46
|
+
return
|
|
47
|
+
|
|
48
|
+
elif args.action == "enhance":
|
|
49
|
+
import sqlite3
|
|
50
|
+
import json as _json
|
|
51
|
+
from agent_memory.db import DB_PATH
|
|
52
|
+
from agent_memory.llm_extract import llm_extract
|
|
53
|
+
from agent_memory.vector import embed_doc, vec_to_blob
|
|
54
|
+
|
|
55
|
+
source_filter = getattr(args, "source", None) or None
|
|
56
|
+
force = getattr(args, "force", False)
|
|
57
|
+
|
|
58
|
+
conn = sqlite3.connect(str(DB_PATH))
|
|
59
|
+
conn.row_factory = sqlite3.Row
|
|
60
|
+
|
|
61
|
+
q = "SELECT doc_id, title, raw_content, generator FROM documents WHERE raw_content IS NOT NULL"
|
|
62
|
+
params = []
|
|
63
|
+
if not force:
|
|
64
|
+
q += " AND generator != 'llm'"
|
|
65
|
+
if source_filter:
|
|
66
|
+
q += " AND source = ?"
|
|
67
|
+
params.append(source_filter)
|
|
68
|
+
|
|
69
|
+
docs = conn.execute(q, params).fetchall()
|
|
70
|
+
total = len(docs)
|
|
71
|
+
print(f"Enhancing {total} docs", flush=True)
|
|
72
|
+
|
|
73
|
+
ok = fb = 0
|
|
74
|
+
for i, row in enumerate(docs, 1):
|
|
75
|
+
content = row["raw_content"] or ""
|
|
76
|
+
title = row["title"] or ""
|
|
77
|
+
doc_id = row["doc_id"]
|
|
78
|
+
llm_fields = llm_extract(content, title_hint=title)
|
|
79
|
+
if llm_fields:
|
|
80
|
+
conn.execute(
|
|
81
|
+
"UPDATE documents SET title=?, summary=?, key_facts=?, decisions=?, generator='llm' WHERE doc_id=?",
|
|
82
|
+
(llm_fields["title"] or title, llm_fields["summary"],
|
|
83
|
+
_json.dumps(llm_fields["key_facts"]), _json.dumps(llm_fields["decisions"]), doc_id),
|
|
84
|
+
)
|
|
85
|
+
use = llm_fields
|
|
86
|
+
ok += 1
|
|
87
|
+
else:
|
|
88
|
+
use = {"title": title, "summary": "", "key_facts": []}
|
|
89
|
+
fb += 1
|
|
90
|
+
vec = embed_doc(use.get("title") or title, use.get("summary") or "", use.get("key_facts") or [])
|
|
91
|
+
if vec:
|
|
92
|
+
conn.execute("UPDATE documents SET embedding=? WHERE doc_id=?", (vec_to_blob(vec), doc_id))
|
|
93
|
+
conn.commit()
|
|
94
|
+
print(f"[{i}/{total}] {'llm' if llm_fields else 'rule'} {title[:55]}", flush=True)
|
|
95
|
+
|
|
96
|
+
print(f"Done: {ok} llm {fb} fallback")
|
|
97
|
+
conn.close()
|
|
98
|
+
|
|
99
|
+
|
|
100
|
+
def cmd_search(args):
|
|
101
|
+
results = get_store().search(args.query, max_results=5)
|
|
102
|
+
fmt = getattr(args, "format", "inject")
|
|
103
|
+
if fmt == "json":
|
|
104
|
+
print(json.dumps([{
|
|
105
|
+
"id": r.id, "type": r.type,
|
|
106
|
+
"l1": r.l1, "l2": r.l2,
|
|
107
|
+
"score": r.score, "priority": r.priority,
|
|
108
|
+
} for r in results]))
|
|
109
|
+
else:
|
|
110
|
+
# max_tokens governs inject() token budget, not search()
|
|
111
|
+
print(get_store().inject(results,
|
|
112
|
+
max_tokens=getattr(args, "max_tokens", 3000)))
|
|
113
|
+
|
|
114
|
+
|
|
115
|
+
def cmd_state(args):
|
|
116
|
+
s = get_store()
|
|
117
|
+
if args.action == "set":
|
|
118
|
+
try:
|
|
119
|
+
value = json.loads(args.value)
|
|
120
|
+
except (json.JSONDecodeError, TypeError):
|
|
121
|
+
value = args.value
|
|
122
|
+
s.state.set(args.key, value)
|
|
123
|
+
elif args.action == "get":
|
|
124
|
+
val = s.state.get(args.key)
|
|
125
|
+
if val is None:
|
|
126
|
+
sys.exit(1)
|
|
127
|
+
print(json.dumps(val) if not isinstance(val, str) else val)
|
|
128
|
+
|
|
129
|
+
|
|
130
|
+
def cmd_session(args):
|
|
131
|
+
s = get_store()
|
|
132
|
+
if args.action == "start":
|
|
133
|
+
sid = s.session.start(
|
|
134
|
+
project=getattr(args, "project", ""),
|
|
135
|
+
topic=getattr(args, "topic", ""),
|
|
136
|
+
source=getattr(args, "source", ""),
|
|
137
|
+
)
|
|
138
|
+
print(sid)
|
|
139
|
+
elif args.action == "latest":
|
|
140
|
+
sid = s.session.get_latest_session_id(
|
|
141
|
+
source=getattr(args, "source", None) or None,
|
|
142
|
+
project=getattr(args, "project", None) or None,
|
|
143
|
+
)
|
|
144
|
+
if sid:
|
|
145
|
+
print(sid)
|
|
146
|
+
else:
|
|
147
|
+
sys.exit(1)
|
|
148
|
+
elif args.action == "end":
|
|
149
|
+
s.session.end(
|
|
150
|
+
session_id=args.session_id,
|
|
151
|
+
summary=getattr(args, "summary", None),
|
|
152
|
+
)
|
|
153
|
+
elif args.action == "message":
|
|
154
|
+
s.session.save_message(args.session_id, args.role, args.content)
|
|
155
|
+
elif args.action == "resume":
|
|
156
|
+
ctx = s.session.get_resume_context(
|
|
157
|
+
args.session_id,
|
|
158
|
+
max_tokens=getattr(args, "max_tokens", 2000),
|
|
159
|
+
)
|
|
160
|
+
print(json.dumps(ctx))
|
|
161
|
+
elif args.action == "delete":
|
|
162
|
+
s.session.delete(args.session_id)
|
|
163
|
+
elif args.action == "checkpoint":
|
|
164
|
+
result = s.session.checkpoint(args.session_id)
|
|
165
|
+
print(json.dumps(result))
|
|
166
|
+
elif args.action == "list":
|
|
167
|
+
rows = s.session.list_for_dashboard(
|
|
168
|
+
limit=getattr(args, "limit", 100),
|
|
169
|
+
include_cli=getattr(args, "include_cli", False),
|
|
170
|
+
)
|
|
171
|
+
print(json.dumps(rows))
|
|
172
|
+
|
|
173
|
+
|
|
174
|
+
def cmd_namespace(args):
|
|
175
|
+
s = get_store()
|
|
176
|
+
if args.action == "list":
|
|
177
|
+
namespaces = s.namespace_list()
|
|
178
|
+
if getattr(args, "format", "text") == "json":
|
|
179
|
+
print(json.dumps(namespaces))
|
|
180
|
+
else:
|
|
181
|
+
for ns in namespaces:
|
|
182
|
+
proj = ns["project"]
|
|
183
|
+
count = ns["doc_count"]
|
|
184
|
+
print(f" {proj:30s} {count:4d} docs")
|
|
185
|
+
elif args.action == "stats":
|
|
186
|
+
name = getattr(args, "name", None)
|
|
187
|
+
if not name:
|
|
188
|
+
print("Error: --name required for stats", file=sys.stderr)
|
|
189
|
+
sys.exit(1)
|
|
190
|
+
stats = s.namespace_stats(name)
|
|
191
|
+
print(f"Project: {stats['project']}")
|
|
192
|
+
print(f" Documents: {stats['doc_count']}")
|
|
193
|
+
print(f" P0: {stats['p0_count']} P1: {stats['p1_count']} P2: {stats['p2_count']}")
|
|
194
|
+
|
|
195
|
+
|
|
196
|
+
def cmd_config(args):
|
|
197
|
+
if args.action == "set":
|
|
198
|
+
key = args.key
|
|
199
|
+
value = args.value
|
|
200
|
+
if key == "embedding_provider":
|
|
201
|
+
from agent_memory.embedding import set_provider, _PROVIDERS
|
|
202
|
+
if value not in _PROVIDERS:
|
|
203
|
+
print(f"Error: unknown provider '{value}'. Available: {', '.join(_PROVIDERS)}", file=sys.stderr)
|
|
204
|
+
sys.exit(1)
|
|
205
|
+
set_provider(value)
|
|
206
|
+
print(f"Embedding provider set to: {value}")
|
|
207
|
+
old_dims = get_store() # trigger dimension check warning
|
|
208
|
+
else:
|
|
209
|
+
print(f"Error: unknown config key '{key}'", file=sys.stderr)
|
|
210
|
+
sys.exit(1)
|
|
211
|
+
elif args.action == "get":
|
|
212
|
+
from agent_memory.embedding import _load_config
|
|
213
|
+
config = _load_config()
|
|
214
|
+
key = args.key
|
|
215
|
+
if key:
|
|
216
|
+
val = config.get(key, "(not set)")
|
|
217
|
+
print(f"{key}: {val}")
|
|
218
|
+
else:
|
|
219
|
+
print(json.dumps(config, indent=2))
|
|
220
|
+
|
|
221
|
+
|
|
222
|
+
def cmd_dream(args):
|
|
223
|
+
from agent_memory.dream import Dreamer, DreamLock
|
|
224
|
+
conn = get_store()._conn
|
|
225
|
+
force = getattr(args, "force", False)
|
|
226
|
+
min_hours = getattr(args, "min_hours", 24)
|
|
227
|
+
min_sessions = getattr(args, "min_sessions", 5)
|
|
228
|
+
|
|
229
|
+
dry_run = getattr(args, "dry_run", False)
|
|
230
|
+
dreamer = Dreamer(
|
|
231
|
+
conn=conn,
|
|
232
|
+
min_hours=0 if force else min_hours,
|
|
233
|
+
min_sessions=0 if force else min_sessions,
|
|
234
|
+
)
|
|
235
|
+
|
|
236
|
+
if getattr(args, "status", False):
|
|
237
|
+
lock = DreamLock()
|
|
238
|
+
hours = lock.hours_since_last()
|
|
239
|
+
sessions_since = dreamer._count_sessions_since(lock.last_dream_at())
|
|
240
|
+
print(f"Last dream: {'never' if hours == float('inf') else f'{hours:.1f}h ago'}")
|
|
241
|
+
print(f"Sessions since: {sessions_since}")
|
|
242
|
+
print(f"Gate: {'PASS' if hours >= min_hours and sessions_since >= min_sessions else 'BLOCKED'}")
|
|
243
|
+
return
|
|
244
|
+
|
|
245
|
+
result = dreamer.run(force=force, dry_run=dry_run)
|
|
246
|
+
|
|
247
|
+
if result.success:
|
|
248
|
+
prefix = "[DRY RUN] " if dry_run else ""
|
|
249
|
+
print(f"{prefix}Dream complete ({result.duration_ms}ms)")
|
|
250
|
+
print(f" Sessions reviewed: {result.sessions_reviewed}")
|
|
251
|
+
print(f" Patterns found: {result.patterns_found}")
|
|
252
|
+
print(f" Contradictions resolved: {result.contradictions_resolved}")
|
|
253
|
+
print(f" Documents created: {result.documents_created}")
|
|
254
|
+
print(f" Documents updated: {result.documents_updated}")
|
|
255
|
+
print(f" Documents pruned: {result.documents_pruned}")
|
|
256
|
+
print(f" Stale detected: {result.stale_detected}")
|
|
257
|
+
print(f" Cross-contradictions resolved: {result.cross_contradictions_resolved}")
|
|
258
|
+
print(f" Redundant merged: {result.redundant_merged}")
|
|
259
|
+
if result.planned_actions:
|
|
260
|
+
print(" Planned actions:")
|
|
261
|
+
for action in result.planned_actions:
|
|
262
|
+
atype = action.get("type", "?")
|
|
263
|
+
detail = action.get("project") or action.get("title") or action.get("topic", "")
|
|
264
|
+
print(f" {atype:20s} {str(detail):40s}")
|
|
265
|
+
else:
|
|
266
|
+
print(f"Dream skipped: {result.reason or ', '.join(result.errors)}")
|
|
267
|
+
|
|
268
|
+
|
|
269
|
+
def cmd_mcp(_args):
|
|
270
|
+
from agent_memory.mcp_server import run
|
|
271
|
+
run()
|
|
272
|
+
|
|
273
|
+
|
|
274
|
+
def cmd_serve(args):
|
|
275
|
+
from agent_memory.mcp_server import run
|
|
276
|
+
transport = getattr(args, "transport", "sse")
|
|
277
|
+
port = getattr(args, "port", 3333)
|
|
278
|
+
read_only = getattr(args, "read_only", False)
|
|
279
|
+
run(transport=transport, port=port, read_only=read_only)
|
|
280
|
+
|
|
281
|
+
|
|
282
|
+
def cmd_init(_args):
|
|
283
|
+
from agent_memory.init_cmd import run
|
|
284
|
+
run()
|
|
285
|
+
|
|
286
|
+
|
|
287
|
+
def cmd_dashboard(args):
|
|
288
|
+
from agent_memory.dashboard import start
|
|
289
|
+
port = getattr(args, "port", 8420)
|
|
290
|
+
allow_edits = getattr(args, "allow_edits", False)
|
|
291
|
+
start(port=port, allow_edits=allow_edits)
|
|
292
|
+
|
|
293
|
+
|
|
294
|
+
def cmd_status(args):
|
|
295
|
+
from agent_memory.watch import status_line
|
|
296
|
+
status_line(
|
|
297
|
+
event=getattr(args, "event", "idle"),
|
|
298
|
+
detail=getattr(args, "detail", ""),
|
|
299
|
+
)
|
|
300
|
+
|
|
301
|
+
|
|
302
|
+
def main():
|
|
303
|
+
from agent_memory import __version__
|
|
304
|
+
p = argparse.ArgumentParser(prog="am")
|
|
305
|
+
p.add_argument("--version", action="version", version=f"%(prog)s {__version__}")
|
|
306
|
+
sub = p.add_subparsers(dest="cmd")
|
|
307
|
+
|
|
308
|
+
# doc save
|
|
309
|
+
doc_p = sub.add_parser("doc")
|
|
310
|
+
doc_p.add_argument("action", choices=["save", "enhance", "prune"])
|
|
311
|
+
doc_p.add_argument("--title", default="")
|
|
312
|
+
doc_p.add_argument("--content", default=None)
|
|
313
|
+
doc_p.add_argument("--priority", default="P1")
|
|
314
|
+
doc_p.add_argument(
|
|
315
|
+
"--source",
|
|
316
|
+
default="hook",
|
|
317
|
+
choices=[
|
|
318
|
+
"architectural_decision", "debug_solution", "technical_insight",
|
|
319
|
+
"session_note", "routine",
|
|
320
|
+
"hook", "session_extract", "explicit",
|
|
321
|
+
],
|
|
322
|
+
)
|
|
323
|
+
doc_p.add_argument("--file-path", default=None, dest="file_path")
|
|
324
|
+
doc_p.add_argument("--force", action="store_true", help="Re-enhance already-llm docs")
|
|
325
|
+
|
|
326
|
+
# search
|
|
327
|
+
s_p = sub.add_parser("search")
|
|
328
|
+
s_p.add_argument("--query", required=True)
|
|
329
|
+
s_p.add_argument("--max-tokens", type=int, default=1500, dest="max_tokens")
|
|
330
|
+
s_p.add_argument("--format", default="inject", choices=["inject", "json"])
|
|
331
|
+
|
|
332
|
+
# state
|
|
333
|
+
st_p = sub.add_parser("state")
|
|
334
|
+
st_p.add_argument("action", choices=["set", "get"])
|
|
335
|
+
st_p.add_argument("--key", required=True)
|
|
336
|
+
st_p.add_argument("--value", default=None)
|
|
337
|
+
|
|
338
|
+
# session
|
|
339
|
+
se_p = sub.add_parser("session")
|
|
340
|
+
se_p.add_argument("action", choices=["start", "end", "delete", "message", "resume", "latest", "list", "checkpoint"])
|
|
341
|
+
se_p.add_argument("--project", default="")
|
|
342
|
+
se_p.add_argument("--topic", default="")
|
|
343
|
+
se_p.add_argument("--session-id", default=None, dest="session_id")
|
|
344
|
+
se_p.add_argument("--role", default="user")
|
|
345
|
+
se_p.add_argument("--content", default="")
|
|
346
|
+
se_p.add_argument("--summary", default=None)
|
|
347
|
+
se_p.add_argument("--max-tokens", type=int, default=2000, dest="max_tokens")
|
|
348
|
+
se_p.add_argument("--source", default="", dest="source")
|
|
349
|
+
se_p.add_argument("--limit", type=int, default=100)
|
|
350
|
+
se_p.add_argument("--include-cli", action="store_true", dest="include_cli")
|
|
351
|
+
|
|
352
|
+
# dream
|
|
353
|
+
dr_p = sub.add_parser("dream", help="Background memory consolidation")
|
|
354
|
+
dr_p.add_argument("--force", action="store_true", help="Skip gate checks")
|
|
355
|
+
dr_p.add_argument("--dry-run", action="store_true", dest="dry_run", help="Show planned actions without writing")
|
|
356
|
+
dr_p.add_argument("--status", action="store_true", help="Show dream status")
|
|
357
|
+
dr_p.add_argument("--min-hours", type=float, default=24, dest="min_hours")
|
|
358
|
+
dr_p.add_argument("--min-sessions", type=int, default=5, dest="min_sessions")
|
|
359
|
+
|
|
360
|
+
# status (called by hooks to show inline feedback)
|
|
361
|
+
status_p = sub.add_parser("status", help="Print one-line colored status (used by hooks)")
|
|
362
|
+
status_p.add_argument("--event", default="idle",
|
|
363
|
+
choices=["session", "checkpoint", "message", "save", "search", "prune", "error", "idle"])
|
|
364
|
+
status_p.add_argument("--detail", default="")
|
|
365
|
+
|
|
366
|
+
# config
|
|
367
|
+
cfg_p = sub.add_parser("config", help="Get/set am-memory configuration")
|
|
368
|
+
cfg_p.add_argument("action", choices=["set", "get"])
|
|
369
|
+
cfg_p.add_argument("--key", default=None, help="Config key (e.g. embedding_provider)")
|
|
370
|
+
cfg_p.add_argument("--value", default=None, help="Config value")
|
|
371
|
+
|
|
372
|
+
# namespace
|
|
373
|
+
ns_p = sub.add_parser("namespace", help="Manage project namespaces")
|
|
374
|
+
ns_p.add_argument("action", choices=["list", "stats"])
|
|
375
|
+
ns_p.add_argument("--name", default=None, help="Project name for stats")
|
|
376
|
+
ns_p.add_argument("--format", default="text", choices=["text", "json"])
|
|
377
|
+
|
|
378
|
+
# dashboard
|
|
379
|
+
dash_p = sub.add_parser("dashboard", help="Start web dashboard for knowledge base")
|
|
380
|
+
dash_p.add_argument("--port", type=int, default=8420)
|
|
381
|
+
dash_p.add_argument("--allow-edits", action="store_true", dest="allow_edits",
|
|
382
|
+
help="Enable delete and priority changes from UI")
|
|
383
|
+
|
|
384
|
+
# serve (SSE transport for cross-tool access)
|
|
385
|
+
serve_p = sub.add_parser("serve", help="Start MCP server with SSE transport")
|
|
386
|
+
serve_p.add_argument("--transport", default="sse", choices=["stdio", "sse"])
|
|
387
|
+
serve_p.add_argument("--port", type=int, default=3333, help="HTTP port for SSE (default 3333)")
|
|
388
|
+
serve_p.add_argument("--read-only", action="store_true", dest="read_only",
|
|
389
|
+
help="Disable write tools (am_save, am_state_set)")
|
|
390
|
+
|
|
391
|
+
sub.add_parser("init", help="One-time setup: MCP registration, hooks, CLAUDE.md")
|
|
392
|
+
sub.add_parser("mcp", help="Start MCP server (stdio transport)")
|
|
393
|
+
args = p.parse_args()
|
|
394
|
+
|
|
395
|
+
if args.cmd == "init":
|
|
396
|
+
cmd_init(args)
|
|
397
|
+
elif args.cmd == "mcp":
|
|
398
|
+
cmd_mcp(args)
|
|
399
|
+
elif args.cmd == "doc":
|
|
400
|
+
cmd_doc(args)
|
|
401
|
+
elif args.cmd == "search":
|
|
402
|
+
cmd_search(args)
|
|
403
|
+
elif args.cmd == "state":
|
|
404
|
+
cmd_state(args)
|
|
405
|
+
elif args.cmd == "session":
|
|
406
|
+
cmd_session(args)
|
|
407
|
+
elif args.cmd == "config":
|
|
408
|
+
cmd_config(args)
|
|
409
|
+
elif args.cmd == "namespace":
|
|
410
|
+
cmd_namespace(args)
|
|
411
|
+
elif args.cmd == "dream":
|
|
412
|
+
cmd_dream(args)
|
|
413
|
+
elif args.cmd == "dashboard":
|
|
414
|
+
cmd_dashboard(args)
|
|
415
|
+
elif args.cmd == "serve":
|
|
416
|
+
cmd_serve(args)
|
|
417
|
+
elif args.cmd == "status":
|
|
418
|
+
cmd_status(args)
|
|
419
|
+
else:
|
|
420
|
+
p.print_help()
|
|
421
|
+
sys.exit(1)
|
|
422
|
+
|
|
423
|
+
|
|
424
|
+
if __name__ == "__main__":
|
|
425
|
+
main()
|