kivault-cli 1.4.1__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.
- kivault_cli/__init__.py +1 -0
- kivault_cli/app.py +363 -0
- kivault_cli/bundled_skills/kivault-cli/SKILL.md +97 -0
- kivault_cli/bundled_skills/kivault-cli/agents/openai.yaml +4 -0
- kivault_cli/bundled_skills/kivault-cli/references/command-guide.md +171 -0
- kivault_cli/client.py +111 -0
- kivault_cli/commands/__init__.py +1 -0
- kivault_cli/commands/identity.py +49 -0
- kivault_cli/commands/items/__init__.py +15 -0
- kivault_cli/commands/items/create.py +202 -0
- kivault_cli/commands/items/listing.py +108 -0
- kivault_cli/commands/items/manage.py +152 -0
- kivault_cli/commands/items/shared.py +39 -0
- kivault_cli/commands/objects.py +255 -0
- kivault_cli/commands/public.py +63 -0
- kivault_cli/commands/tags.py +146 -0
- kivault_cli/commands/vaults.py +141 -0
- kivault_cli/errors.py +12 -0
- kivault_cli/object_upload_tasks.py +97 -0
- kivault_cli/output.py +57 -0
- kivault_cli/settings.py +34 -0
- kivault_cli/utils.py +90 -0
- kivault_cli-1.4.1.dist-info/METADATA +240 -0
- kivault_cli-1.4.1.dist-info/RECORD +26 -0
- kivault_cli-1.4.1.dist-info/WHEEL +4 -0
- kivault_cli-1.4.1.dist-info/entry_points.txt +2 -0
kivault_cli/__init__.py
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"""KiVault CLI package."""
|
kivault_cli/app.py
ADDED
|
@@ -0,0 +1,363 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from enum import Enum
|
|
4
|
+
from importlib.metadata import PackageNotFoundError, version
|
|
5
|
+
from pathlib import Path
|
|
6
|
+
import re
|
|
7
|
+
import subprocess
|
|
8
|
+
import sys
|
|
9
|
+
import tomllib
|
|
10
|
+
from typing import Any, TypedDict
|
|
11
|
+
|
|
12
|
+
from agent_skill_dist import (
|
|
13
|
+
SkillAlreadyExists,
|
|
14
|
+
SkillDistError,
|
|
15
|
+
install_bundled_skill,
|
|
16
|
+
skill_status,
|
|
17
|
+
)
|
|
18
|
+
from agent_skill_dist.cli import already_exists_to_text, install_to_text, status_to_text
|
|
19
|
+
import httpx
|
|
20
|
+
import typer
|
|
21
|
+
|
|
22
|
+
from kivault_cli.commands import items, public, tags, vaults
|
|
23
|
+
from kivault_cli.commands.identity import whoami
|
|
24
|
+
from kivault_cli.errors import CliError
|
|
25
|
+
from kivault_cli.output import emit
|
|
26
|
+
from kivault_cli.settings import load_settings
|
|
27
|
+
from kivault_cli.utils import help_markup_mode
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
PACKAGE_NAME = "kivault-cli"
|
|
31
|
+
RELEASE_APP = "kivault_cli"
|
|
32
|
+
RELEASE_TAG = "default"
|
|
33
|
+
LATEST_RELEASE_URL = "https://releases.kispace.cc/api/public/latest"
|
|
34
|
+
SKILL_PACKAGE = "kivault_cli"
|
|
35
|
+
SKILL_RESOURCE_ROOT = "bundled_skills"
|
|
36
|
+
SKILL_NAME = "kivault-cli"
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
class SkillTarget(str, Enum):
|
|
40
|
+
repo = "repo"
|
|
41
|
+
global_target = "global"
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
class ReleaseInfo(TypedDict):
|
|
45
|
+
version: str
|
|
46
|
+
tags: list[str]
|
|
47
|
+
download_url: str
|
|
48
|
+
release_notes: str | None
|
|
49
|
+
created_at: str
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
def _package_version() -> str:
|
|
53
|
+
pyproject_version = _pyproject_version()
|
|
54
|
+
if pyproject_version:
|
|
55
|
+
return pyproject_version
|
|
56
|
+
try:
|
|
57
|
+
return version(PACKAGE_NAME)
|
|
58
|
+
except PackageNotFoundError:
|
|
59
|
+
return "unknown"
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
def _pyproject_version() -> str | None:
|
|
63
|
+
pyproject_path = Path(__file__).resolve().parents[1] / "pyproject.toml"
|
|
64
|
+
if not pyproject_path.exists():
|
|
65
|
+
return None
|
|
66
|
+
try:
|
|
67
|
+
data = tomllib.loads(pyproject_path.read_text(encoding="utf-8"))
|
|
68
|
+
except (OSError, tomllib.TOMLDecodeError):
|
|
69
|
+
return None
|
|
70
|
+
project = data.get("project")
|
|
71
|
+
if not isinstance(project, dict):
|
|
72
|
+
return None
|
|
73
|
+
project_version = project.get("version")
|
|
74
|
+
if not isinstance(project_version, str):
|
|
75
|
+
return None
|
|
76
|
+
return project_version
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
def _version_callback(value: bool) -> None:
|
|
80
|
+
if not value:
|
|
81
|
+
return
|
|
82
|
+
typer.echo(f"kivault {_package_version()}")
|
|
83
|
+
raise typer.Exit()
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
def _latest_release_url() -> str:
|
|
87
|
+
return f"{LATEST_RELEASE_URL}?app={RELEASE_APP}&tags={RELEASE_TAG}"
|
|
88
|
+
|
|
89
|
+
|
|
90
|
+
def _fetch_latest_release() -> ReleaseInfo:
|
|
91
|
+
try:
|
|
92
|
+
response = httpx.get(
|
|
93
|
+
_latest_release_url(), timeout=30, follow_redirects=True, trust_env=False
|
|
94
|
+
)
|
|
95
|
+
except httpx.HTTPError as exc:
|
|
96
|
+
raise CliError(f"无法获取最新版本信息:{exc}") from exc
|
|
97
|
+
if response.status_code >= 400:
|
|
98
|
+
raise CliError(
|
|
99
|
+
f"无法获取最新版本信息:HTTP {response.status_code} {response.reason_phrase}"
|
|
100
|
+
)
|
|
101
|
+
try:
|
|
102
|
+
payload = response.json()
|
|
103
|
+
except ValueError as exc:
|
|
104
|
+
raise CliError("无法解析最新版本信息:服务端返回的不是 JSON。") from exc
|
|
105
|
+
return _parse_release_info(payload)
|
|
106
|
+
|
|
107
|
+
|
|
108
|
+
def _parse_release_info(payload: Any) -> ReleaseInfo:
|
|
109
|
+
if not isinstance(payload, dict):
|
|
110
|
+
raise CliError("无法解析最新版本信息:响应不是 JSON object。")
|
|
111
|
+
version_value = payload.get("version")
|
|
112
|
+
download_url = payload.get("download_url")
|
|
113
|
+
if not isinstance(version_value, str) or not version_value:
|
|
114
|
+
raise CliError("无法解析最新版本信息:缺少 version。")
|
|
115
|
+
if not isinstance(download_url, str) or not download_url:
|
|
116
|
+
raise CliError("无法解析最新版本信息:缺少 download_url。")
|
|
117
|
+
tags_value = payload.get("tags")
|
|
118
|
+
tags = [str(tag) for tag in tags_value] if isinstance(tags_value, list) else []
|
|
119
|
+
release_notes_value = payload.get("release_notes")
|
|
120
|
+
created_at_value = payload.get("created_at")
|
|
121
|
+
return {
|
|
122
|
+
"version": version_value,
|
|
123
|
+
"tags": tags,
|
|
124
|
+
"download_url": download_url,
|
|
125
|
+
"release_notes": release_notes_value
|
|
126
|
+
if isinstance(release_notes_value, str)
|
|
127
|
+
else None,
|
|
128
|
+
"created_at": created_at_value if isinstance(created_at_value, str) else "",
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
|
|
132
|
+
def _is_newer_version(latest: str, current: str) -> bool:
|
|
133
|
+
return _version_parts(latest) > _version_parts(current)
|
|
134
|
+
|
|
135
|
+
|
|
136
|
+
def _version_parts(value: str) -> tuple[int, ...]:
|
|
137
|
+
parts = [int(part) for part in re.findall(r"\d+", value)]
|
|
138
|
+
return tuple(parts) if parts else (0,)
|
|
139
|
+
|
|
140
|
+
|
|
141
|
+
def _install_release(download_url: str) -> None:
|
|
142
|
+
command = [
|
|
143
|
+
sys.executable,
|
|
144
|
+
"-m",
|
|
145
|
+
"pip",
|
|
146
|
+
"install",
|
|
147
|
+
"--upgrade",
|
|
148
|
+
download_url,
|
|
149
|
+
]
|
|
150
|
+
try:
|
|
151
|
+
subprocess.run(command, check=True, capture_output=True, text=True)
|
|
152
|
+
except FileNotFoundError as exc:
|
|
153
|
+
raise CliError("无法执行 pip:当前 Python 环境不可用。") from exc
|
|
154
|
+
except subprocess.CalledProcessError as exc:
|
|
155
|
+
detail = (exc.stderr or exc.stdout or "").strip()
|
|
156
|
+
message = f"升级失败:pip 退出码 {exc.returncode}"
|
|
157
|
+
if detail:
|
|
158
|
+
message = f"{message}\n{detail}"
|
|
159
|
+
raise CliError(message) from exc
|
|
160
|
+
|
|
161
|
+
|
|
162
|
+
app = typer.Typer(
|
|
163
|
+
no_args_is_help=True,
|
|
164
|
+
rich_markup_mode=help_markup_mode(),
|
|
165
|
+
help=(
|
|
166
|
+
"KiVault 命令行客户端。\n\n"
|
|
167
|
+
"\b\n示例:\n"
|
|
168
|
+
" kivault health\n"
|
|
169
|
+
" kivault --server http://127.0.0.1:8000 whoami\n"
|
|
170
|
+
' kivault item add-text --vault-id vault_123 --title Note --content "hello"'
|
|
171
|
+
),
|
|
172
|
+
)
|
|
173
|
+
skill_app = typer.Typer(
|
|
174
|
+
no_args_is_help=True,
|
|
175
|
+
rich_markup_mode=help_markup_mode(),
|
|
176
|
+
help="安装或查看 KiVault CLI 内置 agent skill。",
|
|
177
|
+
)
|
|
178
|
+
|
|
179
|
+
|
|
180
|
+
@app.callback()
|
|
181
|
+
def cli(
|
|
182
|
+
ctx: typer.Context,
|
|
183
|
+
server: str | None = typer.Option(
|
|
184
|
+
None,
|
|
185
|
+
"--server",
|
|
186
|
+
help="临时覆盖 KiVault 服务地址;未传时使用 KIVAULT_SERVER_URL 或默认 http://127.0.0.1:8000。",
|
|
187
|
+
),
|
|
188
|
+
json_output: bool = typer.Option(
|
|
189
|
+
False, "--json", help="输出服务端返回的原始 JSON,便于脚本处理。"
|
|
190
|
+
),
|
|
191
|
+
version_output: bool = typer.Option(
|
|
192
|
+
False,
|
|
193
|
+
"--version",
|
|
194
|
+
callback=_version_callback,
|
|
195
|
+
is_eager=True,
|
|
196
|
+
help="显示版本号并退出。",
|
|
197
|
+
),
|
|
198
|
+
) -> None:
|
|
199
|
+
ctx.obj = {
|
|
200
|
+
"settings": load_settings(server_override=server),
|
|
201
|
+
"json": json_output,
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
|
|
205
|
+
@app.command()
|
|
206
|
+
def health(ctx: typer.Context) -> None:
|
|
207
|
+
"""检查服务健康状态。
|
|
208
|
+
|
|
209
|
+
\b
|
|
210
|
+
示例:
|
|
211
|
+
kivault health
|
|
212
|
+
kivault --server http://127.0.0.1:8000 health
|
|
213
|
+
"""
|
|
214
|
+
from kivault_cli.client import api
|
|
215
|
+
|
|
216
|
+
emit(ctx, api(ctx, auth=False).request("GET", "/api/health"))
|
|
217
|
+
|
|
218
|
+
|
|
219
|
+
@app.command()
|
|
220
|
+
def upgrade(
|
|
221
|
+
ctx: typer.Context,
|
|
222
|
+
yes: bool = typer.Option(
|
|
223
|
+
False, "--yes", "-y", help="确认升级。未传时只检查最新版本,不安装。"
|
|
224
|
+
),
|
|
225
|
+
) -> None:
|
|
226
|
+
"""检查并升级 KiVault CLI。
|
|
227
|
+
|
|
228
|
+
\b
|
|
229
|
+
示例:
|
|
230
|
+
kivault upgrade
|
|
231
|
+
kivault upgrade --yes
|
|
232
|
+
"""
|
|
233
|
+
current_version = _package_version()
|
|
234
|
+
release = _fetch_latest_release()
|
|
235
|
+
is_newer = _is_newer_version(release["version"], current_version)
|
|
236
|
+
result = {
|
|
237
|
+
"current_version": current_version,
|
|
238
|
+
"latest_version": release["version"],
|
|
239
|
+
"latest_tags": release["tags"],
|
|
240
|
+
"download_url": release["download_url"],
|
|
241
|
+
"release_notes": release["release_notes"],
|
|
242
|
+
"created_at": release["created_at"],
|
|
243
|
+
"upgrade_available": is_newer,
|
|
244
|
+
"installed": False,
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
if not ctx.obj.get("json"):
|
|
248
|
+
typer.echo(f"current: {current_version}")
|
|
249
|
+
typer.echo(f"latest: {release['version']}")
|
|
250
|
+
typer.echo(f"url: {release['download_url']}")
|
|
251
|
+
|
|
252
|
+
if not is_newer:
|
|
253
|
+
if ctx.obj.get("json"):
|
|
254
|
+
emit(ctx, result)
|
|
255
|
+
else:
|
|
256
|
+
typer.echo("当前已经是最新版本,无需升级。")
|
|
257
|
+
return
|
|
258
|
+
|
|
259
|
+
if not yes:
|
|
260
|
+
if ctx.obj.get("json"):
|
|
261
|
+
emit(ctx, result)
|
|
262
|
+
else:
|
|
263
|
+
typer.echo("发现新版本。执行 `kivault upgrade --yes` 确认升级。")
|
|
264
|
+
return
|
|
265
|
+
|
|
266
|
+
if not ctx.obj.get("json"):
|
|
267
|
+
typer.echo("开始升级 KiVault CLI...")
|
|
268
|
+
_install_release(release["download_url"])
|
|
269
|
+
result["installed"] = True
|
|
270
|
+
if ctx.obj.get("json"):
|
|
271
|
+
emit(ctx, result)
|
|
272
|
+
else:
|
|
273
|
+
typer.echo("升级完成。")
|
|
274
|
+
|
|
275
|
+
|
|
276
|
+
@skill_app.command("status")
|
|
277
|
+
def skill_install_status(
|
|
278
|
+
ctx: typer.Context,
|
|
279
|
+
target: SkillTarget = typer.Option(
|
|
280
|
+
SkillTarget.repo,
|
|
281
|
+
"--target",
|
|
282
|
+
help="安装目标。repo 为当前目录 .agents/skills;global 为 CODEX_HOME skills。",
|
|
283
|
+
),
|
|
284
|
+
output: Path | None = typer.Option(
|
|
285
|
+
None, "--output", help="自定义 skills 根目录;最终安装到 <output>/kivault-cli。"
|
|
286
|
+
),
|
|
287
|
+
) -> None:
|
|
288
|
+
"""查看内置 agent skill 的安装状态。"""
|
|
289
|
+
try:
|
|
290
|
+
status = skill_status(
|
|
291
|
+
package=SKILL_PACKAGE,
|
|
292
|
+
distribution=PACKAGE_NAME,
|
|
293
|
+
resource_root=SKILL_RESOURCE_ROOT,
|
|
294
|
+
skill_name=SKILL_NAME,
|
|
295
|
+
package_version=_package_version(),
|
|
296
|
+
target=_skill_dist_target(target),
|
|
297
|
+
output=output,
|
|
298
|
+
)
|
|
299
|
+
except SkillDistError as exc:
|
|
300
|
+
raise CliError(str(exc)) from exc
|
|
301
|
+
|
|
302
|
+
if ctx.obj.get("json"):
|
|
303
|
+
emit(ctx, status.to_dict())
|
|
304
|
+
else:
|
|
305
|
+
typer.echo(status_to_text(status))
|
|
306
|
+
|
|
307
|
+
|
|
308
|
+
@skill_app.command("install")
|
|
309
|
+
def install_skill(
|
|
310
|
+
ctx: typer.Context,
|
|
311
|
+
target: SkillTarget = typer.Option(
|
|
312
|
+
SkillTarget.repo,
|
|
313
|
+
"--target",
|
|
314
|
+
help="安装目标。repo 为当前目录 .agents/skills;global 为 CODEX_HOME skills。",
|
|
315
|
+
),
|
|
316
|
+
output: Path | None = typer.Option(
|
|
317
|
+
None, "--output", help="自定义 skills 根目录;最终安装到 <output>/kivault-cli。"
|
|
318
|
+
),
|
|
319
|
+
yes: bool = typer.Option(
|
|
320
|
+
False, "--yes", "-y", help="目标已存在时允许覆盖。"
|
|
321
|
+
),
|
|
322
|
+
) -> None:
|
|
323
|
+
"""安装 KiVault CLI 内置 agent skill。"""
|
|
324
|
+
try:
|
|
325
|
+
status = install_bundled_skill(
|
|
326
|
+
package=SKILL_PACKAGE,
|
|
327
|
+
distribution=PACKAGE_NAME,
|
|
328
|
+
resource_root=SKILL_RESOURCE_ROOT,
|
|
329
|
+
skill_name=SKILL_NAME,
|
|
330
|
+
package_version=_package_version(),
|
|
331
|
+
target=_skill_dist_target(target),
|
|
332
|
+
output=output,
|
|
333
|
+
yes=yes,
|
|
334
|
+
)
|
|
335
|
+
except SkillAlreadyExists as exc:
|
|
336
|
+
raise CliError(already_exists_to_text(exc)) from exc
|
|
337
|
+
except SkillDistError as exc:
|
|
338
|
+
raise CliError(str(exc)) from exc
|
|
339
|
+
|
|
340
|
+
if ctx.obj.get("json"):
|
|
341
|
+
emit(ctx, status.to_dict())
|
|
342
|
+
else:
|
|
343
|
+
typer.echo(install_to_text(status))
|
|
344
|
+
|
|
345
|
+
|
|
346
|
+
def _skill_dist_target(target: SkillTarget) -> str:
|
|
347
|
+
return target.value
|
|
348
|
+
|
|
349
|
+
|
|
350
|
+
app.command("whoami")(whoami)
|
|
351
|
+
app.add_typer(vaults.app, name="vault")
|
|
352
|
+
app.add_typer(items.app, name="item")
|
|
353
|
+
app.add_typer(tags.app, name="tag")
|
|
354
|
+
app.add_typer(public.app, name="public")
|
|
355
|
+
app.add_typer(skill_app, name="skill")
|
|
356
|
+
|
|
357
|
+
|
|
358
|
+
def run() -> None:
|
|
359
|
+
try:
|
|
360
|
+
app()
|
|
361
|
+
except CliError as exc:
|
|
362
|
+
typer.secho(exc.message, fg=typer.colors.RED, err=True)
|
|
363
|
+
sys.exit(exc.exit_code)
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: kivault-cli
|
|
3
|
+
description: Use the KiVault CLI from this repository to manage KiVault data through the HTTP API. Use when an agent needs to check KiVault health or identity, configure or run the CLI, create/list/update/delete vaults, create/search/export items, upload/download item objects, manage tags, or read public KiVault items and objects. Also use for Chinese requests mentioning KiVault CLI, Vault, Item, Object, Tag, 公开读取, 上传附件, 下载附件, or token/server configuration.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# KiVault CLI
|
|
7
|
+
|
|
8
|
+
Use this skill to operate the local KiVault command-line client safely and consistently.
|
|
9
|
+
|
|
10
|
+
## Quick Start
|
|
11
|
+
|
|
12
|
+
Assume the CLI is installed and available as `kivault`. Check the current CLI version when behavior matters:
|
|
13
|
+
|
|
14
|
+
```bash
|
|
15
|
+
kivault --version
|
|
16
|
+
kivault --help
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
Run commands from the user's current working directory unless a file path or repository checkout path requires otherwise.
|
|
20
|
+
|
|
21
|
+
Set these environment variables for authenticated commands:
|
|
22
|
+
|
|
23
|
+
```bash
|
|
24
|
+
export KIVAULT_SERVER_URL=http://127.0.0.1:8000
|
|
25
|
+
export KIVAULT_TOKEN=<token>
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
`KIVAULT_SERVER_URL` defaults to `http://127.0.0.1:8000`. The CLI has no login command and does not persist tokens.
|
|
29
|
+
|
|
30
|
+
## Operating Rules
|
|
31
|
+
|
|
32
|
+
- Use `--json` for commands whose output will be parsed by an agent or script.
|
|
33
|
+
- `item list` uses the v2 search endpoint. In `--json` mode it returns a page object with `items`, `limit`, `offset`, and `total`, not a bare array.
|
|
34
|
+
- Use `kivault upgrade` to check the release service. Only run `kivault upgrade --yes` when the user explicitly wants to install the latest wheel.
|
|
35
|
+
- Set `KIVAULT_PLAIN_HELP=1` before help commands when plain text is easier to capture.
|
|
36
|
+
- Never print or echo `KIVAULT_TOKEN`; `whoami` is safe because it reports token presence and server identity.
|
|
37
|
+
- Prefer `--no-wait` for file or object uploads in agent shell tools, then poll with `item object upload-task` or `item object upload-task-wait`.
|
|
38
|
+
- Use `--yes` only for deletes the user explicitly requested.
|
|
39
|
+
- Distinguish text content from file uploads: `--content-file` reads UTF-8 text into `Item.content_text`; binary/PDF/image uploads must use `item add-file` or `item object upload`.
|
|
40
|
+
|
|
41
|
+
## Workflow
|
|
42
|
+
|
|
43
|
+
1. Check the server and auth context before mutating data:
|
|
44
|
+
|
|
45
|
+
```bash
|
|
46
|
+
kivault health
|
|
47
|
+
kivault --version
|
|
48
|
+
kivault whoami
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
2. Use `vault`, `item`, `item object`, `tag`, or `public` commands depending on the task.
|
|
52
|
+
3. Read [command-guide.md](references/command-guide.md) before composing non-trivial command sequences, uploads, downloads, public access flows, or destructive operations.
|
|
53
|
+
4. Capture IDs from JSON output, then pass those IDs to follow-up commands.
|
|
54
|
+
|
|
55
|
+
## Common Patterns
|
|
56
|
+
|
|
57
|
+
Create a vault, add Markdown text, and export it:
|
|
58
|
+
|
|
59
|
+
```bash
|
|
60
|
+
kivault --json vault create Inbox
|
|
61
|
+
kivault --json item add-text --vault-id <vault-id> --title "Note.md" --content-file ./note.md
|
|
62
|
+
kivault item export <item-id> --format markdown --output ./note.md
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
Search items with v2 field filters:
|
|
66
|
+
|
|
67
|
+
```bash
|
|
68
|
+
kivault --json item list -q report --search-field title --search-field object_filename --limit 20
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
Check for a CLI upgrade without installing:
|
|
72
|
+
|
|
73
|
+
```bash
|
|
74
|
+
kivault upgrade
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
Upload a file without long blocking waits:
|
|
78
|
+
|
|
79
|
+
```bash
|
|
80
|
+
kivault --json item add-file ./document.pdf --vault-id <vault-id> --no-wait
|
|
81
|
+
kivault --json item object upload-task <item-id> <task-id>
|
|
82
|
+
kivault --json item object upload-task-wait <item-id> <task-id>
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
Download an object:
|
|
86
|
+
|
|
87
|
+
```bash
|
|
88
|
+
kivault item object download <item-id> <object-id> --output ./document.pdf
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
## Validation
|
|
92
|
+
|
|
93
|
+
For CLI code changes, run the project tests:
|
|
94
|
+
|
|
95
|
+
```bash
|
|
96
|
+
.venv/bin/python -m pytest . -q
|
|
97
|
+
```
|
|
@@ -0,0 +1,171 @@
|
|
|
1
|
+
# KiVault CLI Command Guide
|
|
2
|
+
|
|
3
|
+
Use this reference for command composition. Assume the CLI is installed as `kivault`; do not run it through `python main.py` unless the user explicitly asks to work from a source checkout.
|
|
4
|
+
|
|
5
|
+
## Environment
|
|
6
|
+
|
|
7
|
+
Authenticated commands need `KIVAULT_TOKEN`.
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
export KIVAULT_SERVER_URL=http://127.0.0.1:8000
|
|
11
|
+
export KIVAULT_TOKEN=<token>
|
|
12
|
+
```
|
|
13
|
+
|
|
14
|
+
Use `--server <url>` to override the server for one command:
|
|
15
|
+
|
|
16
|
+
```bash
|
|
17
|
+
kivault --server http://127.0.0.1:8000 whoami
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
Check the CLI version:
|
|
21
|
+
|
|
22
|
+
```bash
|
|
23
|
+
kivault --version
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
Check or install the latest wheel from the KiSpace release service:
|
|
27
|
+
|
|
28
|
+
```bash
|
|
29
|
+
kivault upgrade
|
|
30
|
+
kivault upgrade --yes
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
`upgrade` reads `https://releases.kispace.cc/api/public/latest?app=kivault_cli&tags=default`. Without `--yes`, it only reports `current`, `latest`, and the wheel URL. Use `--yes` only when the user explicitly approves installing the latest wheel.
|
|
34
|
+
|
|
35
|
+
Use `--json` when the next step needs IDs or structured data:
|
|
36
|
+
|
|
37
|
+
```bash
|
|
38
|
+
kivault --json item list --vault-id <vault-id>
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
For `item list`, JSON output is a v2 search page object:
|
|
42
|
+
|
|
43
|
+
```json
|
|
44
|
+
{"items": [], "limit": 50, "offset": 0, "total": 0}
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
## Health and Identity
|
|
48
|
+
|
|
49
|
+
```bash
|
|
50
|
+
kivault health
|
|
51
|
+
kivault whoami
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
`health` does not require auth. `whoami` calls `/api/whoami` and reports current server URL plus whether `KIVAULT_TOKEN` is set.
|
|
55
|
+
|
|
56
|
+
## Vaults
|
|
57
|
+
|
|
58
|
+
```bash
|
|
59
|
+
kivault --json vault list
|
|
60
|
+
kivault --json vault create Inbox
|
|
61
|
+
kivault --json vault create Public --visibility public --default-level public --description "公开资料"
|
|
62
|
+
kivault --json vault get <vault-id>
|
|
63
|
+
kivault --json vault update <vault-id> --name Archive
|
|
64
|
+
kivault --json vault update <vault-id> --visibility public --default-level public
|
|
65
|
+
kivault --json vault delete <vault-id> --yes
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
Vault visibility values are `private` and `public`. Default item level values are `public`, `private`, `sensitive`, and `danger`.
|
|
69
|
+
|
|
70
|
+
## Items
|
|
71
|
+
|
|
72
|
+
List and search:
|
|
73
|
+
|
|
74
|
+
```bash
|
|
75
|
+
kivault --json item list --vault-id <vault-id> --limit 50
|
|
76
|
+
kivault --json item list --keyword "deploy note" --type markdown
|
|
77
|
+
kivault --json item list -q report --search-field title --search-field object_filename
|
|
78
|
+
kivault --json item list --tag-id <tag-id> --level private
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
`item list` calls `/api/v2/items/search`. Use `--search-field` to constrain keyword matching; repeat it for multiple fields. Valid search fields are `title`, `content`, `metadata`, `object_filename`, and `object_key`.
|
|
82
|
+
|
|
83
|
+
Create text or Markdown:
|
|
84
|
+
|
|
85
|
+
```bash
|
|
86
|
+
kivault --json item add-text --vault-id <vault-id> --title "Note" --content "hello"
|
|
87
|
+
kivault --json item add-text --vault-id <vault-id> --title "Note.md" --content-file ./note.md
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
Use `item create` when more fields are needed:
|
|
91
|
+
|
|
92
|
+
```bash
|
|
93
|
+
kivault --json item create --vault-id <vault-id> --title "Web clip" --type markdown --source-url https://example.com --source-type web --metadata '{"kind":"clip"}'
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
Update, read, export, delete:
|
|
97
|
+
|
|
98
|
+
```bash
|
|
99
|
+
kivault --json item get <item-id>
|
|
100
|
+
kivault --json item update <item-id> --title "New title"
|
|
101
|
+
kivault --json item update <item-id> --content-file ./note.md --tag-id <tag-a> --tag-id <tag-b>
|
|
102
|
+
kivault item export <item-id> --format markdown --output ./item.md
|
|
103
|
+
kivault --json item export <item-id> --format json
|
|
104
|
+
kivault --json item delete <item-id> --yes
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
Item type values are `text`, `markdown`, `html`, `web_page`, `image`, `audio`, `video`, `pdf`, `document`, `spreadsheet`, `structured_data`, `file`, and `other`. Status values are `active`, `archived`, and `deleted`.
|
|
108
|
+
|
|
109
|
+
## File Items and Objects
|
|
110
|
+
|
|
111
|
+
Create an item and upload an initial attachment:
|
|
112
|
+
|
|
113
|
+
```bash
|
|
114
|
+
kivault --json item add-file ./document.pdf --vault-id <vault-id> --no-wait
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
Add one or more objects to an existing item:
|
|
118
|
+
|
|
119
|
+
```bash
|
|
120
|
+
kivault --json item object upload <item-id> ./a.pdf ./b.png --no-wait
|
|
121
|
+
kivault --json item object upload-multipart <item-id> ./a.pdf ./b.md --no-wait
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
Both upload commands create asynchronous upload tasks. The current implementation sends base64 JSON payloads; `upload-multipart` is a compatibility command name.
|
|
125
|
+
|
|
126
|
+
Check or wait for a task:
|
|
127
|
+
|
|
128
|
+
```bash
|
|
129
|
+
kivault --json item object upload-task <item-id> <task-id>
|
|
130
|
+
kivault --json item object upload-task-wait <item-id> <task-id> --poll-interval 1 --timeout 600
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
List, inspect, download, or delete objects:
|
|
134
|
+
|
|
135
|
+
```bash
|
|
136
|
+
kivault --json item object list <item-id>
|
|
137
|
+
kivault --json item object get <item-id> <object-id>
|
|
138
|
+
kivault item object download <item-id> <object-id> --output ./document.pdf
|
|
139
|
+
kivault --json item object delete <item-id> <object-id> --yes
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
Upload limits from the README: each file must be under 100 MB, and one task can include at most 20 files.
|
|
143
|
+
|
|
144
|
+
## Tags
|
|
145
|
+
|
|
146
|
+
```bash
|
|
147
|
+
kivault --json tag list
|
|
148
|
+
kivault --json tag create research --color '#3b82f6'
|
|
149
|
+
kivault --json tag update <tag-id> --name reading
|
|
150
|
+
kivault --json tag add <tag-id> <item-id>
|
|
151
|
+
kivault --json tag remove <tag-id> <item-id>
|
|
152
|
+
kivault --json tag delete <tag-id> --yes
|
|
153
|
+
```
|
|
154
|
+
|
|
155
|
+
## Public Reads
|
|
156
|
+
|
|
157
|
+
Public commands do not require auth. They work only when the vault is public, the item level is public, and the item status is active.
|
|
158
|
+
|
|
159
|
+
```bash
|
|
160
|
+
kivault --json public item <item-id>
|
|
161
|
+
kivault public download-object <item-id> <object-id> --output ./file.pdf
|
|
162
|
+
```
|
|
163
|
+
|
|
164
|
+
## Help
|
|
165
|
+
|
|
166
|
+
Use plain help for command discovery:
|
|
167
|
+
|
|
168
|
+
```bash
|
|
169
|
+
KIVAULT_PLAIN_HELP=1 kivault item create --help
|
|
170
|
+
KIVAULT_PLAIN_HELP=1 kivault item object upload --help
|
|
171
|
+
```
|