codeer-cli 0.1.1__py3-none-any.whl → 0.1.2__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.
- codeer_cli/agents.py +5 -0
- codeer_cli/client.py +1 -2
- codeer_cli/commands/agent.py +71 -0
- codeer_cli/commands/kb.py +239 -0
- codeer_cli/commands/profile.py +1 -1
- codeer_cli/constants.py +1 -0
- codeer_cli/kb.py +55 -1
- {codeer_cli-0.1.1.dist-info → codeer_cli-0.1.2.dist-info}/METADATA +56 -9
- {codeer_cli-0.1.1.dist-info → codeer_cli-0.1.2.dist-info}/RECORD +11 -11
- {codeer_cli-0.1.1.dist-info → codeer_cli-0.1.2.dist-info}/WHEEL +1 -1
- {codeer_cli-0.1.1.dist-info → codeer_cli-0.1.2.dist-info}/entry_points.txt +0 -0
codeer_cli/agents.py
CHANGED
|
@@ -153,3 +153,8 @@ def get_version(client: CodeerClient, agent_id: str, history_id: str) -> dict:
|
|
|
153
153
|
def check_impact(client: CodeerClient, agent_id: str) -> dict:
|
|
154
154
|
"""List downstream agents that call this one. Call before publishing breaking changes."""
|
|
155
155
|
return client.get(f"/external/agents/{agent_id}/impact")
|
|
156
|
+
|
|
157
|
+
|
|
158
|
+
def publish_version(client: CodeerClient, agent_id: str, history_id: str) -> dict:
|
|
159
|
+
"""Promote one AgentHistory version to the published runtime version."""
|
|
160
|
+
return client.post(f"/external/agents/{agent_id}/versions/{history_id}:publish", json={})
|
codeer_cli/client.py
CHANGED
|
@@ -85,8 +85,7 @@ class CodeerClient:
|
|
|
85
85
|
if not api_key:
|
|
86
86
|
raise AuthError(
|
|
87
87
|
0,
|
|
88
|
-
"Missing API key. Export CODEER_API_KEY or run `codeer profile add <name
|
|
89
|
-
"and `codeer profile use <name>`.",
|
|
88
|
+
"Missing API key. Export CODEER_API_KEY or run `codeer profile add <name>`.",
|
|
90
89
|
)
|
|
91
90
|
|
|
92
91
|
overrides.pop("workspace_id", None)
|
codeer_cli/commands/agent.py
CHANGED
|
@@ -69,6 +69,21 @@ def register(subparsers):
|
|
|
69
69
|
help="Write stripped full version snapshots to this file; stdout stays compact.")
|
|
70
70
|
p.set_defaults(func=run_versions)
|
|
71
71
|
|
|
72
|
+
p = sub.add_parser("impact", help="Check downstream agents affected by this agent")
|
|
73
|
+
p.add_argument("--agent", required=True)
|
|
74
|
+
p.add_argument("--out", default=None, help="Write full impact detail to this file too")
|
|
75
|
+
p.set_defaults(func=run_impact)
|
|
76
|
+
|
|
77
|
+
p = sub.add_parser("publish", help="Publish an agent version; run --dry-run first")
|
|
78
|
+
p.add_argument("--agent", required=True)
|
|
79
|
+
g = p.add_mutually_exclusive_group(required=True)
|
|
80
|
+
g.add_argument("--history", default=None, help="AgentHistory UUID to publish")
|
|
81
|
+
g.add_argument("--version", type=int, default=None, help="AgentHistory version_number to publish")
|
|
82
|
+
p.add_argument("--dry-run", action="store_true",
|
|
83
|
+
help="Resolve target version and print intended mutation without writing server state.")
|
|
84
|
+
p.add_argument("--out", default=None, help="Write result JSON to this file too")
|
|
85
|
+
p.set_defaults(func=run_publish)
|
|
86
|
+
|
|
72
87
|
|
|
73
88
|
def _tool_summary(tools: list[dict] | None) -> list[dict]:
|
|
74
89
|
out = []
|
|
@@ -229,6 +244,62 @@ def run_versions(args, client) -> int:
|
|
|
229
244
|
return 0
|
|
230
245
|
|
|
231
246
|
|
|
247
|
+
def run_impact(args, client) -> int:
|
|
248
|
+
result = strip_noisy_fields(agents_mod.check_impact(client, args.agent))
|
|
249
|
+
write_json(args.out, result)
|
|
250
|
+
print_json(result)
|
|
251
|
+
return 0
|
|
252
|
+
|
|
253
|
+
|
|
254
|
+
def _resolve_history_for_publish(
|
|
255
|
+
client: CodeerClient,
|
|
256
|
+
agent_id: str,
|
|
257
|
+
history_id: Optional[str],
|
|
258
|
+
version: Optional[int],
|
|
259
|
+
) -> dict:
|
|
260
|
+
if history_id:
|
|
261
|
+
return agents_mod.get_version(client, agent_id, history_id)
|
|
262
|
+
if version is not None:
|
|
263
|
+
for candidate in agents_mod.list_versions(client, agent_id):
|
|
264
|
+
if candidate.get("version_number") == version:
|
|
265
|
+
return agents_mod.get_version(client, agent_id, candidate["id"])
|
|
266
|
+
raise SystemExit(f"no version {version} on agent {agent_id}")
|
|
267
|
+
raise SystemExit("must pass --history or --version")
|
|
268
|
+
|
|
269
|
+
|
|
270
|
+
def run_publish(args, client) -> int:
|
|
271
|
+
history = _resolve_history_for_publish(client, args.agent, args.history, args.version)
|
|
272
|
+
history_id = history["id"]
|
|
273
|
+
summary = {
|
|
274
|
+
"agent_id": args.agent,
|
|
275
|
+
"history_id": history_id,
|
|
276
|
+
"version_number": history.get("version_number"),
|
|
277
|
+
"status": history.get("status"),
|
|
278
|
+
"was_published": history.get("was_published"),
|
|
279
|
+
"version_note": history.get("version_note") or "",
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
if args.dry_run:
|
|
283
|
+
result = {
|
|
284
|
+
"dry_run": True,
|
|
285
|
+
"operation": "agent_publish",
|
|
286
|
+
"method": "POST",
|
|
287
|
+
"path": f"/external/agents/{args.agent}/versions/{history_id}:publish",
|
|
288
|
+
"target": summary,
|
|
289
|
+
"would_write_server_state": True,
|
|
290
|
+
"next_step": "Review this summary, then rerun without --dry-run after approval.",
|
|
291
|
+
}
|
|
292
|
+
print_json(result)
|
|
293
|
+
write_json(args.out, result)
|
|
294
|
+
return 0
|
|
295
|
+
|
|
296
|
+
result = strip_noisy_fields(agents_mod.publish_version(client, args.agent, history_id))
|
|
297
|
+
output = {"target": summary, "response": result}
|
|
298
|
+
print_json(output)
|
|
299
|
+
write_json(args.out, output)
|
|
300
|
+
return 0
|
|
301
|
+
|
|
302
|
+
|
|
232
303
|
|
|
233
304
|
# --- diff helpers ---
|
|
234
305
|
|
codeer_cli/commands/kb.py
CHANGED
|
@@ -11,6 +11,42 @@ POLL_INTERVAL = 3
|
|
|
11
11
|
POLL_TIMEOUT = 600
|
|
12
12
|
|
|
13
13
|
|
|
14
|
+
def _add_crawl_config_args(parser):
|
|
15
|
+
parser.add_argument("--config-json", default=None, help="JSON object for crawl_config")
|
|
16
|
+
parser.add_argument("--limit", type=int, default=None, help="Maximum pages to crawl (backend max: 5000)")
|
|
17
|
+
parser.add_argument("--max-depth", type=int, default=None, help="Maximum crawl depth (backend max: 10)")
|
|
18
|
+
parser.add_argument(
|
|
19
|
+
"--include-path",
|
|
20
|
+
action="append",
|
|
21
|
+
dest="include_paths",
|
|
22
|
+
default=None,
|
|
23
|
+
help="Clean path pattern to include; repeatable. Supports * wildcards.",
|
|
24
|
+
)
|
|
25
|
+
parser.add_argument(
|
|
26
|
+
"--exclude-path",
|
|
27
|
+
action="append",
|
|
28
|
+
dest="exclude_paths",
|
|
29
|
+
default=None,
|
|
30
|
+
help="Clean path pattern to exclude; repeatable. Supports * wildcards.",
|
|
31
|
+
)
|
|
32
|
+
parser.add_argument("--allow-subdomains", action="store_true", default=None,
|
|
33
|
+
help="Allow crawling subdomains of the start URL host")
|
|
34
|
+
parser.add_argument("--allow-external-links", action="store_true", default=None,
|
|
35
|
+
help="Allow crawling links outside the start URL host")
|
|
36
|
+
parser.add_argument("--ignore-query-parameters", action="store_true", dest="ignore_query_parameters", default=None,
|
|
37
|
+
help="Treat URLs that differ only by query string as the same page")
|
|
38
|
+
parser.add_argument("--use-query-parameters", action="store_false", dest="ignore_query_parameters",
|
|
39
|
+
help="Treat URLs with different query strings as distinct pages")
|
|
40
|
+
parser.add_argument("--ignore-sitemap", action="store_true", dest="ignore_sitemap", default=None,
|
|
41
|
+
help="Skip sitemap discovery")
|
|
42
|
+
parser.add_argument("--use-sitemap", action="store_false", dest="ignore_sitemap",
|
|
43
|
+
help="Allow sitemap discovery")
|
|
44
|
+
parser.add_argument("--only-main-content", action="store_true", dest="only_main_content", default=None,
|
|
45
|
+
help="Extract only the main content area")
|
|
46
|
+
parser.add_argument("--include-page-chrome", action="store_false", dest="only_main_content",
|
|
47
|
+
help="Keep page navigation, footer, and other chrome")
|
|
48
|
+
|
|
49
|
+
|
|
14
50
|
def register(subparsers):
|
|
15
51
|
k = subparsers.add_parser("kb", help="Knowledge base operations")
|
|
16
52
|
sub = k.add_subparsers(dest="action", required=True)
|
|
@@ -92,6 +128,51 @@ def register(subparsers):
|
|
|
92
128
|
p.add_argument("--out", default=None, help="Write result JSON to this file too")
|
|
93
129
|
p.set_defaults(func=run_faq_delete)
|
|
94
130
|
|
|
131
|
+
p = sub.add_parser("crawl-create", help="Create a website-crawler KB folder; run --dry-run first")
|
|
132
|
+
p.add_argument("--url", required=True, help="Starting URL to crawl")
|
|
133
|
+
p.add_argument("--folder-name", default=None, help="KB folder name")
|
|
134
|
+
_add_crawl_config_args(p)
|
|
135
|
+
p.add_argument("--dry-run", action="store_true",
|
|
136
|
+
help="Print intended request without writing server state.")
|
|
137
|
+
p.add_argument("--out", default=None, help="Write result JSON to this file too")
|
|
138
|
+
p.set_defaults(func=run_crawl_create)
|
|
139
|
+
|
|
140
|
+
p = sub.add_parser("crawl-update", help="Update a website crawl target; run --dry-run first")
|
|
141
|
+
p.add_argument("--target-id", type=int, required=True)
|
|
142
|
+
p.add_argument("--url", required=True, help="Updated starting URL")
|
|
143
|
+
_add_crawl_config_args(p)
|
|
144
|
+
p.add_argument("--dry-run", action="store_true",
|
|
145
|
+
help="Print intended request without writing server state.")
|
|
146
|
+
p.add_argument("--out", default=None, help="Write result JSON to this file too")
|
|
147
|
+
p.set_defaults(func=run_crawl_update)
|
|
148
|
+
|
|
149
|
+
p = sub.add_parser("crawl-state", help="Read website crawl state for a crawler folder")
|
|
150
|
+
p.add_argument("--folder-id", required=True, help="KnowledgeNode folder UUID")
|
|
151
|
+
p.add_argument("--out", default=None, help="Write result JSON to this file too")
|
|
152
|
+
p.set_defaults(func=run_crawl_state)
|
|
153
|
+
|
|
154
|
+
p = sub.add_parser("crawl-sync", help="Start a website crawl sync job; run --dry-run first")
|
|
155
|
+
p.add_argument("--target-id", type=int, required=True)
|
|
156
|
+
p.add_argument("--dry-run", action="store_true",
|
|
157
|
+
help="Print intended request without writing server state.")
|
|
158
|
+
p.add_argument("--out", default=None, help="Write result JSON to this file too")
|
|
159
|
+
p.set_defaults(func=run_crawl_sync)
|
|
160
|
+
|
|
161
|
+
p = sub.add_parser("crawl-cancel", help="Cancel the active website crawl job; run --dry-run first")
|
|
162
|
+
p.add_argument("--target-id", type=int, required=True)
|
|
163
|
+
p.add_argument("--dry-run", action="store_true",
|
|
164
|
+
help="Print intended request without writing server state.")
|
|
165
|
+
p.add_argument("--out", default=None, help="Write result JSON to this file too")
|
|
166
|
+
p.set_defaults(func=run_crawl_cancel)
|
|
167
|
+
|
|
168
|
+
p = sub.add_parser("crawl-failures", help="List failed pages for a website crawl job")
|
|
169
|
+
p.add_argument("--job-id", type=int, required=True)
|
|
170
|
+
p.add_argument("--status", default="DOWNLOAD_FAILED,FAILED")
|
|
171
|
+
p.add_argument("--limit", type=int, default=50)
|
|
172
|
+
p.add_argument("--offset", type=int, default=0)
|
|
173
|
+
p.add_argument("--out", default=None, help="Write result JSON to this file too")
|
|
174
|
+
p.set_defaults(func=run_crawl_failures)
|
|
175
|
+
|
|
95
176
|
|
|
96
177
|
def _node_summary(node: dict) -> dict:
|
|
97
178
|
return {
|
|
@@ -130,6 +211,40 @@ def _dry_run(path: str | None, result: dict) -> int:
|
|
|
130
211
|
return 0
|
|
131
212
|
|
|
132
213
|
|
|
214
|
+
def _parse_config_json(config_json: str | None) -> dict | None:
|
|
215
|
+
if not config_json:
|
|
216
|
+
return None
|
|
217
|
+
value = json.loads(config_json)
|
|
218
|
+
if not isinstance(value, dict):
|
|
219
|
+
raise SystemExit("--config-json must decode to a JSON object")
|
|
220
|
+
return value
|
|
221
|
+
|
|
222
|
+
|
|
223
|
+
def _crawl_config_from_args(args) -> dict | None:
|
|
224
|
+
config = _parse_config_json(args.config_json)
|
|
225
|
+
has_config = config is not None
|
|
226
|
+
if config is None:
|
|
227
|
+
config = {}
|
|
228
|
+
|
|
229
|
+
for key, attr in (
|
|
230
|
+
("limit", "limit"),
|
|
231
|
+
("maxDepth", "max_depth"),
|
|
232
|
+
("includePaths", "include_paths"),
|
|
233
|
+
("excludePaths", "exclude_paths"),
|
|
234
|
+
("allowSubdomains", "allow_subdomains"),
|
|
235
|
+
("allowExternalLinks", "allow_external_links"),
|
|
236
|
+
("ignoreQueryParameters", "ignore_query_parameters"),
|
|
237
|
+
("ignoreSitemap", "ignore_sitemap"),
|
|
238
|
+
("onlyMainContent", "only_main_content"),
|
|
239
|
+
):
|
|
240
|
+
value = getattr(args, attr)
|
|
241
|
+
if value is not None:
|
|
242
|
+
config[key] = value
|
|
243
|
+
has_config = True
|
|
244
|
+
|
|
245
|
+
return config if has_config else None
|
|
246
|
+
|
|
247
|
+
|
|
133
248
|
def run_list(args, client) -> int:
|
|
134
249
|
workspace_id, organization_id = client.resolve_scope()
|
|
135
250
|
nodes = kb_mod.list_nodes(
|
|
@@ -348,3 +463,127 @@ def run_faq_delete(args, client) -> int:
|
|
|
348
463
|
response = strip_noisy_fields(kb_mod.delete_context_obj_faq(client, faq_id=args.faq_id))
|
|
349
464
|
_print_and_write(args.out, response)
|
|
350
465
|
return 0
|
|
466
|
+
|
|
467
|
+
|
|
468
|
+
def run_crawl_create(args, client) -> int:
|
|
469
|
+
crawl_config = _crawl_config_from_args(args)
|
|
470
|
+
body: dict[str, object] = {"start_url": args.url}
|
|
471
|
+
if args.folder_name is not None:
|
|
472
|
+
body["folder_name"] = args.folder_name
|
|
473
|
+
if crawl_config is not None:
|
|
474
|
+
body["crawl_config"] = crawl_config
|
|
475
|
+
|
|
476
|
+
if args.dry_run:
|
|
477
|
+
return _dry_run(
|
|
478
|
+
args.out,
|
|
479
|
+
{
|
|
480
|
+
"dry_run": True,
|
|
481
|
+
"operation": "kb_crawl_create",
|
|
482
|
+
"method": "POST",
|
|
483
|
+
"path": "/external/knowledge-bases/website-crawls",
|
|
484
|
+
"body": body,
|
|
485
|
+
"would_write_server_state": True,
|
|
486
|
+
"next_step": "Review this summary, then rerun without --dry-run after approval.",
|
|
487
|
+
},
|
|
488
|
+
)
|
|
489
|
+
|
|
490
|
+
response = strip_noisy_fields(
|
|
491
|
+
kb_mod.create_website_crawl(
|
|
492
|
+
client,
|
|
493
|
+
start_url=args.url,
|
|
494
|
+
folder_name=args.folder_name,
|
|
495
|
+
crawl_config=crawl_config,
|
|
496
|
+
)
|
|
497
|
+
)
|
|
498
|
+
_print_and_write(args.out, response)
|
|
499
|
+
return 0
|
|
500
|
+
|
|
501
|
+
|
|
502
|
+
def run_crawl_update(args, client) -> int:
|
|
503
|
+
crawl_config = _crawl_config_from_args(args)
|
|
504
|
+
body: dict[str, object] = {"start_url": args.url}
|
|
505
|
+
if crawl_config is not None:
|
|
506
|
+
body["crawl_config"] = crawl_config
|
|
507
|
+
|
|
508
|
+
if args.dry_run:
|
|
509
|
+
return _dry_run(
|
|
510
|
+
args.out,
|
|
511
|
+
{
|
|
512
|
+
"dry_run": True,
|
|
513
|
+
"operation": "kb_crawl_update",
|
|
514
|
+
"method": "PATCH",
|
|
515
|
+
"path": f"/external/knowledge-bases/website-crawls/{args.target_id}",
|
|
516
|
+
"body": body,
|
|
517
|
+
"would_write_server_state": True,
|
|
518
|
+
"next_step": "Review this summary, then rerun without --dry-run after approval.",
|
|
519
|
+
},
|
|
520
|
+
)
|
|
521
|
+
|
|
522
|
+
response = strip_noisy_fields(
|
|
523
|
+
kb_mod.update_website_crawl(
|
|
524
|
+
client,
|
|
525
|
+
target_id=args.target_id,
|
|
526
|
+
start_url=args.url,
|
|
527
|
+
crawl_config=crawl_config,
|
|
528
|
+
)
|
|
529
|
+
)
|
|
530
|
+
_print_and_write(args.out, response)
|
|
531
|
+
return 0
|
|
532
|
+
|
|
533
|
+
|
|
534
|
+
def run_crawl_state(args, client) -> int:
|
|
535
|
+
response = strip_noisy_fields(kb_mod.get_website_crawl_state(client, folder_id=args.folder_id))
|
|
536
|
+
_print_and_write(args.out, response)
|
|
537
|
+
return 0
|
|
538
|
+
|
|
539
|
+
|
|
540
|
+
def run_crawl_sync(args, client) -> int:
|
|
541
|
+
if args.dry_run:
|
|
542
|
+
return _dry_run(
|
|
543
|
+
args.out,
|
|
544
|
+
{
|
|
545
|
+
"dry_run": True,
|
|
546
|
+
"operation": "kb_crawl_sync",
|
|
547
|
+
"method": "POST",
|
|
548
|
+
"path": f"/external/knowledge-bases/website-crawls/{args.target_id}:sync",
|
|
549
|
+
"would_write_server_state": True,
|
|
550
|
+
"next_step": "Review this summary, then rerun without --dry-run after approval.",
|
|
551
|
+
},
|
|
552
|
+
)
|
|
553
|
+
|
|
554
|
+
response = strip_noisy_fields(kb_mod.sync_website_crawl(client, target_id=args.target_id))
|
|
555
|
+
_print_and_write(args.out, response)
|
|
556
|
+
return 0
|
|
557
|
+
|
|
558
|
+
|
|
559
|
+
def run_crawl_cancel(args, client) -> int:
|
|
560
|
+
if args.dry_run:
|
|
561
|
+
return _dry_run(
|
|
562
|
+
args.out,
|
|
563
|
+
{
|
|
564
|
+
"dry_run": True,
|
|
565
|
+
"operation": "kb_crawl_cancel",
|
|
566
|
+
"method": "POST",
|
|
567
|
+
"path": f"/external/knowledge-bases/website-crawls/{args.target_id}:cancel",
|
|
568
|
+
"would_write_server_state": True,
|
|
569
|
+
"next_step": "Review this summary, then rerun without --dry-run after approval.",
|
|
570
|
+
},
|
|
571
|
+
)
|
|
572
|
+
|
|
573
|
+
response = strip_noisy_fields(kb_mod.cancel_website_crawl(client, target_id=args.target_id))
|
|
574
|
+
_print_and_write(args.out, response)
|
|
575
|
+
return 0
|
|
576
|
+
|
|
577
|
+
|
|
578
|
+
def run_crawl_failures(args, client) -> int:
|
|
579
|
+
response = strip_noisy_fields(
|
|
580
|
+
kb_mod.get_website_crawl_failures(
|
|
581
|
+
client,
|
|
582
|
+
job_id=args.job_id,
|
|
583
|
+
status=args.status,
|
|
584
|
+
limit=args.limit,
|
|
585
|
+
offset=args.offset,
|
|
586
|
+
)
|
|
587
|
+
)
|
|
588
|
+
_print_and_write(args.out, response)
|
|
589
|
+
return 0
|
codeer_cli/commands/profile.py
CHANGED
codeer_cli/constants.py
CHANGED
codeer_cli/kb.py
CHANGED
|
@@ -36,7 +36,7 @@ def _guess_mime(filename: str) -> str:
|
|
|
36
36
|
return guess or "application/octet-stream"
|
|
37
37
|
|
|
38
38
|
|
|
39
|
-
def _base(organization_id: str, workspace_id: str) -> str:
|
|
39
|
+
def _base(organization_id: str = "", workspace_id: str = "") -> str:
|
|
40
40
|
return "/external/knowledge-bases"
|
|
41
41
|
|
|
42
42
|
|
|
@@ -276,3 +276,57 @@ def update_context_obj_faq(
|
|
|
276
276
|
|
|
277
277
|
def delete_context_obj_faq(client: CodeerClient, *, faq_id: int) -> dict:
|
|
278
278
|
return client.delete(f"{_faq_base()}/{faq_id}")
|
|
279
|
+
|
|
280
|
+
|
|
281
|
+
def create_website_crawl(
|
|
282
|
+
client: CodeerClient,
|
|
283
|
+
*,
|
|
284
|
+
start_url: str,
|
|
285
|
+
folder_name: Optional[str] = None,
|
|
286
|
+
crawl_config: Optional[dict[str, Any]] = None,
|
|
287
|
+
) -> dict:
|
|
288
|
+
body: dict[str, Any] = {"start_url": start_url}
|
|
289
|
+
if folder_name is not None:
|
|
290
|
+
body["folder_name"] = folder_name
|
|
291
|
+
if crawl_config is not None:
|
|
292
|
+
body["crawl_config"] = crawl_config
|
|
293
|
+
return client.post(f"{_base()}/website-crawls", json=body)
|
|
294
|
+
|
|
295
|
+
|
|
296
|
+
def update_website_crawl(
|
|
297
|
+
client: CodeerClient,
|
|
298
|
+
*,
|
|
299
|
+
target_id: int,
|
|
300
|
+
start_url: str,
|
|
301
|
+
crawl_config: Optional[dict[str, Any]] = None,
|
|
302
|
+
) -> dict:
|
|
303
|
+
body: dict[str, Any] = {"start_url": start_url}
|
|
304
|
+
if crawl_config is not None:
|
|
305
|
+
body["crawl_config"] = crawl_config
|
|
306
|
+
return client.patch(f"{_base()}/website-crawls/{target_id}", json=body)
|
|
307
|
+
|
|
308
|
+
|
|
309
|
+
def sync_website_crawl(client: CodeerClient, *, target_id: int) -> dict:
|
|
310
|
+
return client.post(f"{_base()}/website-crawls/{target_id}:sync", json={})
|
|
311
|
+
|
|
312
|
+
|
|
313
|
+
def cancel_website_crawl(client: CodeerClient, *, target_id: int) -> dict:
|
|
314
|
+
return client.post(f"{_base()}/website-crawls/{target_id}:cancel", json={})
|
|
315
|
+
|
|
316
|
+
|
|
317
|
+
def get_website_crawl_state(client: CodeerClient, *, folder_id: str) -> dict:
|
|
318
|
+
return client.get(f"{_base()}/nodes/{folder_id}/website-crawl-state")
|
|
319
|
+
|
|
320
|
+
|
|
321
|
+
def get_website_crawl_failures(
|
|
322
|
+
client: CodeerClient,
|
|
323
|
+
*,
|
|
324
|
+
job_id: int,
|
|
325
|
+
status: str = "DOWNLOAD_FAILED,FAILED",
|
|
326
|
+
limit: int = 50,
|
|
327
|
+
offset: int = 0,
|
|
328
|
+
) -> dict:
|
|
329
|
+
return client.get(
|
|
330
|
+
f"{_base()}/website-crawl-jobs/{job_id}/failures",
|
|
331
|
+
params={"status": status, "limit": limit, "offset": offset},
|
|
332
|
+
)
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: codeer-cli
|
|
3
|
-
Version: 0.1.
|
|
3
|
+
Version: 0.1.2
|
|
4
4
|
Summary: Command line tools for managing Codeer agents over the Codeer API.
|
|
5
5
|
Project-URL: Homepage, https://www.codeer.ai
|
|
6
6
|
Author: Codeer.AI
|
|
@@ -22,25 +22,35 @@ Standalone CLI for managing Codeer agents over the Codeer API.
|
|
|
22
22
|
|
|
23
23
|
## User install
|
|
24
24
|
|
|
25
|
-
|
|
26
|
-
line tool:
|
|
25
|
+
Install the CLI from PyPI with `pipx`:
|
|
27
26
|
|
|
28
27
|
```bash
|
|
29
|
-
|
|
28
|
+
pipx install codeer-cli
|
|
30
29
|
```
|
|
31
30
|
|
|
32
|
-
|
|
31
|
+
Verify that the command is available:
|
|
33
32
|
|
|
34
33
|
```bash
|
|
35
|
-
|
|
34
|
+
codeer --help
|
|
36
35
|
```
|
|
37
36
|
|
|
38
|
-
|
|
37
|
+
If `pipx` is not installed:
|
|
39
38
|
|
|
40
|
-
|
|
39
|
+
```bash
|
|
40
|
+
python -m pip install --user pipx
|
|
41
|
+
python -m pipx ensurepath
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
Then restart the terminal and run:
|
|
41
45
|
|
|
42
46
|
```bash
|
|
43
|
-
codeer
|
|
47
|
+
pipx install codeer-cli
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
As a fallback, you can install into your user Python environment:
|
|
51
|
+
|
|
52
|
+
```bash
|
|
53
|
+
python -m pip install --user codeer-cli
|
|
44
54
|
```
|
|
45
55
|
|
|
46
56
|
## Credentials
|
|
@@ -107,6 +117,21 @@ Validate setup before API work:
|
|
|
107
117
|
codeer check
|
|
108
118
|
```
|
|
109
119
|
|
|
120
|
+
## Upgrade and uninstall
|
|
121
|
+
|
|
122
|
+
Upgrade the CLI:
|
|
123
|
+
|
|
124
|
+
```bash
|
|
125
|
+
pipx upgrade codeer-cli
|
|
126
|
+
codeer check
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
Remove the CLI:
|
|
130
|
+
|
|
131
|
+
```bash
|
|
132
|
+
pipx uninstall codeer-cli
|
|
133
|
+
```
|
|
134
|
+
|
|
110
135
|
## Output policy for coding agents
|
|
111
136
|
|
|
112
137
|
The CLI is optimized for Codex, Claude Code, Claude Cowork, and similar coding
|
|
@@ -133,6 +158,28 @@ Avoid piping large raw JSON directly into agent chat. Prefer `--out`, then ask
|
|
|
133
158
|
the coding agent to inspect targeted summaries, IDs, failing cases, or selected
|
|
134
159
|
snippets from the saved file.
|
|
135
160
|
|
|
161
|
+
## Website crawler KBs
|
|
162
|
+
|
|
163
|
+
Website-backed KB folders can be created and updated with `codeer kb crawl-*`.
|
|
164
|
+
Always preview crawler mutations with `--dry-run` first:
|
|
165
|
+
|
|
166
|
+
```bash
|
|
167
|
+
codeer kb crawl-create \
|
|
168
|
+
--url https://example.com/docs \
|
|
169
|
+
--folder-name "Product Docs" \
|
|
170
|
+
--include-path "/docs*" \
|
|
171
|
+
--exclude-path "/docs/private*" \
|
|
172
|
+
--limit 250 \
|
|
173
|
+
--max-depth 3 \
|
|
174
|
+
--only-main-content \
|
|
175
|
+
--dry-run
|
|
176
|
+
```
|
|
177
|
+
|
|
178
|
+
`--include-path` and `--exclude-path` are repeatable clean path patterns. Quote
|
|
179
|
+
paths containing `*` so the shell passes the wildcard to the CLI. Advanced
|
|
180
|
+
settings can still be passed through `--config-json`; explicit crawler flags
|
|
181
|
+
override matching JSON keys.
|
|
182
|
+
|
|
136
183
|
## Context Object FAQ
|
|
137
184
|
|
|
138
185
|
Use Context Object FAQ entries to route high-value questions to a canonical KB
|
|
@@ -1,23 +1,23 @@
|
|
|
1
1
|
codeer_cli/__init__.py,sha256=-0gL8upoSsLAnXAfcRrwqZYJbwG0knzQoFf94O7Nc7c,1817
|
|
2
2
|
codeer_cli/_validate.py,sha256=pKUJa2TyTpERx5xmiYNZRn7tFqDxLZ2fF1rHAf1oz14,5415
|
|
3
|
-
codeer_cli/agents.py,sha256=
|
|
3
|
+
codeer_cli/agents.py,sha256=diodgiGhXlowEi8sbCzcSK1qSeCLF2fBe6QBs3Sq_x8,5617
|
|
4
4
|
codeer_cli/chats.py,sha256=YVrZJhoa-d67o6tzX6riGXsbA-ehyhOxrZ8zRCcJNro,2675
|
|
5
5
|
codeer_cli/cli.py,sha256=kaXTCBfzq64fLJDqKH39cN0ds7qv-F-eRxzUsGmCvv0,3901
|
|
6
|
-
codeer_cli/client.py,sha256
|
|
7
|
-
codeer_cli/constants.py,sha256=
|
|
6
|
+
codeer_cli/client.py,sha256=LpHVqf1IYNg1wFfIHnO9q4xg2h3IiGOitzCnvwB-Bcw,9809
|
|
7
|
+
codeer_cli/constants.py,sha256=D1pV3wCoqYybrKGKeoupYjjFWLfaFviKp1yL7oh6Qso,2323
|
|
8
8
|
codeer_cli/eval_.py,sha256=EsH8f8nT8x9MFlUhpHWSAU4aNPkNceD-Tw5GM15Ae1I,14157
|
|
9
9
|
codeer_cli/histories.py,sha256=tk28git_peX4x703CIDU8u72JtlGaytyrtlHfxlK-7A,5979
|
|
10
|
-
codeer_cli/kb.py,sha256=
|
|
10
|
+
codeer_cli/kb.py,sha256=Y1tLWGLJ9ck92Rwyi2zy3-tkJNautbSRvpXkSVqB9ac,10188
|
|
11
11
|
codeer_cli/parse.py,sha256=qrjZn0MUTjGfucp4cwxy8Pt7WS-0x15kK5F7kWTY8Ps,21818
|
|
12
12
|
codeer_cli/commands/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
13
13
|
codeer_cli/commands/_util.py,sha256=VOB_HMWYzHFNY1ElLOED1HB6fpFpsniqH6Yx3VUlMrY,1644
|
|
14
|
-
codeer_cli/commands/agent.py,sha256=
|
|
14
|
+
codeer_cli/commands/agent.py,sha256=amvfVVrbPOkbYKCGvA6EJB30C-aY6WSdfR7u7FklXbs,14793
|
|
15
15
|
codeer_cli/commands/check.py,sha256=lTxolx1mIJ8jldPhJ5FXqie9nbCLVOO-sDPOHTSy1-w,3817
|
|
16
16
|
codeer_cli/commands/eval_cmd.py,sha256=yvqjPXMOE7V0Hv53iEW5HvIo610dgiIG4BpKGWzZY4I,46965
|
|
17
17
|
codeer_cli/commands/history.py,sha256=Jv7t0GhSZcbZ8OuIXZT34CixXt7ECEVP3nZ-WW_Ya9E,12026
|
|
18
|
-
codeer_cli/commands/kb.py,sha256=
|
|
19
|
-
codeer_cli/commands/profile.py,sha256=
|
|
20
|
-
codeer_cli-0.1.
|
|
21
|
-
codeer_cli-0.1.
|
|
22
|
-
codeer_cli-0.1.
|
|
23
|
-
codeer_cli-0.1.
|
|
18
|
+
codeer_cli/commands/kb.py,sha256=lIoYfP3_VY_S6R9P9r5pcItEjxJY5Tx_eO7eY1n6Gao,23285
|
|
19
|
+
codeer_cli/commands/profile.py,sha256=IdlXC_6cqobsfN3JRrAnt-1OgBUsIFneS9QtR4Un6Kc,6521
|
|
20
|
+
codeer_cli-0.1.2.dist-info/METADATA,sha256=k610R7Tw9ZrPKME69coKFJ1aF-sZWx-5JEX_8glgWx8,5156
|
|
21
|
+
codeer_cli-0.1.2.dist-info/WHEEL,sha256=mffPy8wBnZQn2VnJUU5jE99KsxaSfiyMHV9Yt0aLVxs,87
|
|
22
|
+
codeer_cli-0.1.2.dist-info/entry_points.txt,sha256=-nXIrlm5SR5r7gg3y8AS0tN66MwmvNHsrlwLNQNGD50,47
|
|
23
|
+
codeer_cli-0.1.2.dist-info/RECORD,,
|
|
File without changes
|