codeer-cli 0.1.1__tar.gz → 0.1.2__tar.gz

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.
Files changed (25) hide show
  1. {codeer_cli-0.1.1 → codeer_cli-0.1.2}/.gitignore +1 -0
  2. {codeer_cli-0.1.1 → codeer_cli-0.1.2}/PKG-INFO +56 -9
  3. {codeer_cli-0.1.1 → codeer_cli-0.1.2}/README.md +55 -8
  4. {codeer_cli-0.1.1 → codeer_cli-0.1.2}/pyproject.toml +1 -1
  5. {codeer_cli-0.1.1 → codeer_cli-0.1.2}/src/codeer_cli/agents.py +5 -0
  6. {codeer_cli-0.1.1 → codeer_cli-0.1.2}/src/codeer_cli/client.py +1 -2
  7. {codeer_cli-0.1.1 → codeer_cli-0.1.2}/src/codeer_cli/commands/agent.py +71 -0
  8. {codeer_cli-0.1.1 → codeer_cli-0.1.2}/src/codeer_cli/commands/kb.py +239 -0
  9. {codeer_cli-0.1.1 → codeer_cli-0.1.2}/src/codeer_cli/commands/profile.py +1 -1
  10. {codeer_cli-0.1.1 → codeer_cli-0.1.2}/src/codeer_cli/constants.py +1 -0
  11. {codeer_cli-0.1.1 → codeer_cli-0.1.2}/src/codeer_cli/kb.py +55 -1
  12. {codeer_cli-0.1.1 → codeer_cli-0.1.2}/uv.lock +1 -1
  13. {codeer_cli-0.1.1 → codeer_cli-0.1.2}/API_REFERENCE.md +0 -0
  14. {codeer_cli-0.1.1 → codeer_cli-0.1.2}/src/codeer_cli/__init__.py +0 -0
  15. {codeer_cli-0.1.1 → codeer_cli-0.1.2}/src/codeer_cli/_validate.py +0 -0
  16. {codeer_cli-0.1.1 → codeer_cli-0.1.2}/src/codeer_cli/chats.py +0 -0
  17. {codeer_cli-0.1.1 → codeer_cli-0.1.2}/src/codeer_cli/cli.py +0 -0
  18. {codeer_cli-0.1.1 → codeer_cli-0.1.2}/src/codeer_cli/commands/__init__.py +0 -0
  19. {codeer_cli-0.1.1 → codeer_cli-0.1.2}/src/codeer_cli/commands/_util.py +0 -0
  20. {codeer_cli-0.1.1 → codeer_cli-0.1.2}/src/codeer_cli/commands/check.py +0 -0
  21. {codeer_cli-0.1.1 → codeer_cli-0.1.2}/src/codeer_cli/commands/eval_cmd.py +0 -0
  22. {codeer_cli-0.1.1 → codeer_cli-0.1.2}/src/codeer_cli/commands/history.py +0 -0
  23. {codeer_cli-0.1.1 → codeer_cli-0.1.2}/src/codeer_cli/eval_.py +0 -0
  24. {codeer_cli-0.1.1 → codeer_cli-0.1.2}/src/codeer_cli/histories.py +0 -0
  25. {codeer_cli-0.1.1 → codeer_cli-0.1.2}/src/codeer_cli/parse.py +0 -0
@@ -35,3 +35,4 @@ Thumbs.db
35
35
 
36
36
  # Local assistant/tool settings
37
37
  .claude/settings.local.json
38
+ .codeer/
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: codeer-cli
3
- Version: 0.1.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
- After the package is published to PyPI, install the CLI as an isolated command
26
- line tool:
25
+ Install the CLI from PyPI with `pipx`:
27
26
 
28
27
  ```bash
29
- uv tool install codeer-cli
28
+ pipx install codeer-cli
30
29
  ```
31
30
 
32
- Until the package is published, install directly from this repository:
31
+ Verify that the command is available:
33
32
 
34
33
  ```bash
35
- uv tool install 'git+https://github.com/<org>/codeer-skills.git#subdirectory=codeer-cli'
34
+ codeer --help
36
35
  ```
37
36
 
38
- Replace `<org>` with the GitHub organization or user that hosts this repository.
37
+ If `pipx` is not installed:
39
38
 
40
- Verify that the command is available:
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 --help
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
@@ -4,25 +4,35 @@ Standalone CLI for managing Codeer agents over the Codeer API.
4
4
 
5
5
  ## User install
6
6
 
7
- After the package is published to PyPI, install the CLI as an isolated command
8
- line tool:
7
+ Install the CLI from PyPI with `pipx`:
9
8
 
10
9
  ```bash
11
- uv tool install codeer-cli
10
+ pipx install codeer-cli
12
11
  ```
13
12
 
14
- Until the package is published, install directly from this repository:
13
+ Verify that the command is available:
15
14
 
16
15
  ```bash
17
- uv tool install 'git+https://github.com/<org>/codeer-skills.git#subdirectory=codeer-cli'
16
+ codeer --help
18
17
  ```
19
18
 
20
- Replace `<org>` with the GitHub organization or user that hosts this repository.
19
+ If `pipx` is not installed:
21
20
 
22
- Verify that the command is available:
21
+ ```bash
22
+ python -m pip install --user pipx
23
+ python -m pipx ensurepath
24
+ ```
25
+
26
+ Then restart the terminal and run:
23
27
 
24
28
  ```bash
25
- codeer --help
29
+ pipx install codeer-cli
30
+ ```
31
+
32
+ As a fallback, you can install into your user Python environment:
33
+
34
+ ```bash
35
+ python -m pip install --user codeer-cli
26
36
  ```
27
37
 
28
38
  ## Credentials
@@ -89,6 +99,21 @@ Validate setup before API work:
89
99
  codeer check
90
100
  ```
91
101
 
102
+ ## Upgrade and uninstall
103
+
104
+ Upgrade the CLI:
105
+
106
+ ```bash
107
+ pipx upgrade codeer-cli
108
+ codeer check
109
+ ```
110
+
111
+ Remove the CLI:
112
+
113
+ ```bash
114
+ pipx uninstall codeer-cli
115
+ ```
116
+
92
117
  ## Output policy for coding agents
93
118
 
94
119
  The CLI is optimized for Codex, Claude Code, Claude Cowork, and similar coding
@@ -115,6 +140,28 @@ Avoid piping large raw JSON directly into agent chat. Prefer `--out`, then ask
115
140
  the coding agent to inspect targeted summaries, IDs, failing cases, or selected
116
141
  snippets from the saved file.
117
142
 
143
+ ## Website crawler KBs
144
+
145
+ Website-backed KB folders can be created and updated with `codeer kb crawl-*`.
146
+ Always preview crawler mutations with `--dry-run` first:
147
+
148
+ ```bash
149
+ codeer kb crawl-create \
150
+ --url https://example.com/docs \
151
+ --folder-name "Product Docs" \
152
+ --include-path "/docs*" \
153
+ --exclude-path "/docs/private*" \
154
+ --limit 250 \
155
+ --max-depth 3 \
156
+ --only-main-content \
157
+ --dry-run
158
+ ```
159
+
160
+ `--include-path` and `--exclude-path` are repeatable clean path patterns. Quote
161
+ paths containing `*` so the shell passes the wildcard to the CLI. Advanced
162
+ settings can still be passed through `--config-json`; explicit crawler flags
163
+ override matching JSON keys.
164
+
118
165
  ## Context Object FAQ
119
166
 
120
167
  Use Context Object FAQ entries to route high-value questions to a canonical KB
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
4
4
 
5
5
  [project]
6
6
  name = "codeer-cli"
7
- version = "0.1.1"
7
+ version = "0.1.2"
8
8
  description = "Command line tools for managing Codeer agents over the Codeer API."
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.11"
@@ -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={})
@@ -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)
@@ -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
 
@@ -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
@@ -124,7 +124,7 @@ def run_add(args, client=None) -> int:
124
124
  data["default"] = args.name
125
125
  _write_profiles(data)
126
126
  print(f"Saved profile {args.name}")
127
- return 0
127
+ return run_use(args)
128
128
 
129
129
 
130
130
  def run_use(args, client=None) -> int:
@@ -19,6 +19,7 @@ UNIFIED_TOOL_TYPES: frozenset[str] = frozenset({
19
19
  "payment",
20
20
  "memory",
21
21
  "http_request",
22
+ "text2speech",
22
23
  })
23
24
 
24
25
  # Valid type values for a field inside ``custom_form_schema.fields[]``.
@@ -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
+ )
@@ -26,7 +26,7 @@ wheels = [
26
26
 
27
27
  [[package]]
28
28
  name = "codeer-cli"
29
- version = "0.1.0"
29
+ version = "0.1.2"
30
30
  source = { editable = "." }
31
31
  dependencies = [
32
32
  { name = "httpx" },
File without changes