nexo-brain 7.13.5 → 7.13.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude-plugin/plugin.json +1 -1
- package/README.md +3 -1
- package/package.json +1 -1
- package/src/client_sync.py +31 -11
- package/src/plugins/cards.py +221 -0
- package/templates/CLAUDE.md.template +5 -4
- package/templates/CODEX.AGENTS.md.template +5 -4
- package/templates/core-prompts/server-mcp-instructions.md +1 -0
- package/tool-enforcement-map.json +49 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "nexo-brain",
|
|
3
|
-
"version": "7.13.
|
|
3
|
+
"version": "7.13.7",
|
|
4
4
|
"description": "Local cognitive runtime for Claude Code \u2014 persistent memory, overnight learning, doctor diagnostics, personal scripts, recovery-aware jobs, startup preflight, and optional dashboard/power helper.",
|
|
5
5
|
"author": {
|
|
6
6
|
"name": "NEXO Brain",
|
package/README.md
CHANGED
|
@@ -18,7 +18,9 @@
|
|
|
18
18
|
|
|
19
19
|
[Watch the overview video](https://nexo-brain.com/watch/) · [Watch on YouTube](https://www.youtube.com/watch?v=i2lkGhKyVqI) · [Open the infographic](https://nexo-brain.com/assets/nexo-brain-infographic-v5.png)
|
|
20
20
|
|
|
21
|
-
Version `7.13.
|
|
21
|
+
Version `7.13.7` is the current packaged-runtime line. Patch release over v7.13.6 — Brain adds an authenticated official protocol-card client (`nexo_card_catalog`, `nexo_card_get`, `nexo_card_match`) so agents can ask the NEXO Desktop backend for the right task protocol at runtime. The protocol corpus stays private on the server; this open-source package ships only the client, tool map, and agent guidance.
|
|
22
|
+
|
|
23
|
+
Previously in `7.13.6`: patch release — Codex hook sync now renders the managed `PreToolUse` shell/exec_command guard with native Windows `cmd.exe` syntax while preserving the existing POSIX command on macOS/Linux. Result: coordinated Desktop bundles can ship the fixed Brain without changing the Mac/Windows installation contract.
|
|
22
24
|
|
|
23
25
|
Previously in `7.13.3`: unified release — doctor now repairs orphan personal script metadata and ignores historical `versions/**` snapshots, `nexo update` prunes runtime snapshots older than two back, protocol compliance self-heals missing task-open/change-log/stale-session gaps, headless automation uses bounded timeouts, Guardian false positives are tightened, and Codex CLI config/default checks are release-gated. Result: coordinated Desktop bundles can ship the new Brain without changing the Mac/Windows installation contract.
|
|
24
26
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "nexo-brain",
|
|
3
|
-
"version": "7.13.
|
|
3
|
+
"version": "7.13.7",
|
|
4
4
|
"mcpName": "io.github.wazionapps/nexo",
|
|
5
5
|
"description": "NEXO Brain — Shared brain for AI agents. Persistent memory, semantic RAG, natural forgetting, metacognitive guard, trust scoring, 150+ MCP tools. Works with Claude Code, Codex, Claude Desktop & any MCP client. 100% local, free.",
|
|
6
6
|
"homepage": "https://nexo-brain.com",
|
package/src/client_sync.py
CHANGED
|
@@ -917,23 +917,43 @@ def _resolve_hook_source_dir(runtime_root: Path) -> Path:
|
|
|
917
917
|
return direct
|
|
918
918
|
|
|
919
919
|
|
|
920
|
-
def
|
|
920
|
+
def _cmd_env_assignment(name: str, value: str | os.PathLike[str]) -> str:
|
|
921
|
+
# `set "VAR=value"` is the safe cmd.exe form; it preserves spaces and
|
|
922
|
+
# avoids trailing quote pollution.
|
|
923
|
+
safe_value = str(value).replace('"', r'\"')
|
|
924
|
+
return f'set "{name}={safe_value}"'
|
|
925
|
+
|
|
926
|
+
|
|
927
|
+
def _cmd_args(*args: str | os.PathLike[str]) -> str:
|
|
928
|
+
return subprocess.list2cmdline([str(arg) for arg in args])
|
|
929
|
+
|
|
930
|
+
|
|
931
|
+
def _render_hook_command(
|
|
932
|
+
spec: dict,
|
|
933
|
+
*,
|
|
934
|
+
nexo_home: Path,
|
|
935
|
+
runtime_root: Path,
|
|
936
|
+
hooks_dir: Path,
|
|
937
|
+
windows_shell: bool | None = None,
|
|
938
|
+
) -> str:
|
|
921
939
|
command_template = spec.get("command_template")
|
|
922
940
|
if callable(command_template):
|
|
923
941
|
return command_template(nexo_home, runtime_root, hooks_dir)
|
|
924
942
|
handler_name = (spec.get("handler") or spec.get("script") or "").strip()
|
|
925
943
|
script_path = hooks_dir / handler_name
|
|
926
|
-
|
|
927
|
-
|
|
928
|
-
|
|
929
|
-
|
|
930
|
-
|
|
931
|
-
)
|
|
932
|
-
return (
|
|
933
|
-
f"NEXO_HOME={shlex.quote(str(nexo_home))} "
|
|
934
|
-
f"NEXO_CODE={shlex.quote(str(runtime_root))} "
|
|
935
|
-
f"bash {shlex.quote(str(script_path))}"
|
|
944
|
+
use_windows_shell = (os.name == "nt") if windows_shell is None else bool(windows_shell)
|
|
945
|
+
env_prefix = (
|
|
946
|
+
f"{_cmd_env_assignment('NEXO_HOME', nexo_home)} && {_cmd_env_assignment('NEXO_CODE', runtime_root)} && "
|
|
947
|
+
if use_windows_shell
|
|
948
|
+
else f"NEXO_HOME={shlex.quote(str(nexo_home))} NEXO_CODE={shlex.quote(str(runtime_root))} "
|
|
936
949
|
)
|
|
950
|
+
if spec.get("interpreter") == "python" or handler_name.endswith(".py"):
|
|
951
|
+
if use_windows_shell:
|
|
952
|
+
return f"{env_prefix}{_cmd_args(_resolve_python(nexo_home), script_path)}"
|
|
953
|
+
return env_prefix + f"{shlex.quote(_resolve_python(nexo_home))} {shlex.quote(str(script_path))}"
|
|
954
|
+
if use_windows_shell:
|
|
955
|
+
return f"{env_prefix}{_cmd_args('bash', script_path)}"
|
|
956
|
+
return env_prefix + f"bash {shlex.quote(str(script_path))}"
|
|
937
957
|
|
|
938
958
|
|
|
939
959
|
def _hook_identity(command: str) -> str:
|
|
@@ -0,0 +1,221 @@
|
|
|
1
|
+
"""Authenticated client for NEXO official protocol cards.
|
|
2
|
+
|
|
3
|
+
The protocol corpus lives on the private NEXO Desktop backend. This open-source
|
|
4
|
+
plugin only knows how to authenticate and fetch cards at runtime.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from __future__ import annotations
|
|
8
|
+
|
|
9
|
+
import json
|
|
10
|
+
import os
|
|
11
|
+
import platform
|
|
12
|
+
import urllib.error
|
|
13
|
+
import urllib.parse
|
|
14
|
+
import urllib.request
|
|
15
|
+
from pathlib import Path
|
|
16
|
+
from typing import Any
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
DEFAULT_API_BASE = "https://nexo-desktop.com"
|
|
20
|
+
SHARED_AUTH_DIRNAME = "nexo-shared-auth"
|
|
21
|
+
SHARED_AUTH_FILENAME = "session.json"
|
|
22
|
+
|
|
23
|
+
_urlopen = urllib.request.urlopen
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
def _json(data: dict[str, Any]) -> str:
|
|
27
|
+
return json.dumps(data, ensure_ascii=False, indent=2)
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
def _as_bool(value: Any, default: bool = False) -> bool:
|
|
31
|
+
if isinstance(value, bool):
|
|
32
|
+
return value
|
|
33
|
+
if value is None:
|
|
34
|
+
return default
|
|
35
|
+
return str(value).strip().lower() in {"1", "true", "yes", "y", "si", "sí", "on"}
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
def _as_int(value: Any, default: int = 5) -> int:
|
|
39
|
+
try:
|
|
40
|
+
return int(value)
|
|
41
|
+
except Exception:
|
|
42
|
+
return default
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
def _api_base() -> str:
|
|
46
|
+
raw = os.environ.get("NEXO_DESKTOP_API_BASE") or os.environ.get("NEXO_CARDS_API_BASE") or DEFAULT_API_BASE
|
|
47
|
+
return raw.strip().rstrip("/") or DEFAULT_API_BASE
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
def _normalize_locale(locale: str = "es") -> str:
|
|
51
|
+
return "en" if str(locale or "").lower().startswith("en") else "es"
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
def _shared_auth_candidates() -> list[Path]:
|
|
55
|
+
candidates: list[Path] = []
|
|
56
|
+
for env_name in ("NEXO_SHARED_AUTH_FILE", "NEXO_DESKTOP_SHARED_AUTH_FILE"):
|
|
57
|
+
raw = os.environ.get(env_name)
|
|
58
|
+
if raw:
|
|
59
|
+
candidates.append(Path(raw).expanduser())
|
|
60
|
+
|
|
61
|
+
appdata = os.environ.get("APPDATA")
|
|
62
|
+
if appdata:
|
|
63
|
+
candidates.append(Path(appdata) / SHARED_AUTH_DIRNAME / SHARED_AUTH_FILENAME)
|
|
64
|
+
|
|
65
|
+
home = Path.home()
|
|
66
|
+
system = platform.system().lower()
|
|
67
|
+
if system == "darwin":
|
|
68
|
+
candidates.append(home / "Library" / "Application Support" / SHARED_AUTH_DIRNAME / SHARED_AUTH_FILENAME)
|
|
69
|
+
elif system == "windows":
|
|
70
|
+
candidates.append(home / "AppData" / "Roaming" / SHARED_AUTH_DIRNAME / SHARED_AUTH_FILENAME)
|
|
71
|
+
else:
|
|
72
|
+
xdg = os.environ.get("XDG_CONFIG_HOME")
|
|
73
|
+
if xdg:
|
|
74
|
+
candidates.append(Path(xdg) / SHARED_AUTH_DIRNAME / SHARED_AUTH_FILENAME)
|
|
75
|
+
candidates.append(home / ".config" / SHARED_AUTH_DIRNAME / SHARED_AUTH_FILENAME)
|
|
76
|
+
|
|
77
|
+
nexo_home = Path(os.environ.get("NEXO_HOME", str(home / ".nexo"))).expanduser()
|
|
78
|
+
candidates.append(nexo_home / "runtime" / "shared-auth" / SHARED_AUTH_FILENAME)
|
|
79
|
+
return candidates
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
def _read_token_from_shared_auth() -> str:
|
|
83
|
+
for path in _shared_auth_candidates():
|
|
84
|
+
try:
|
|
85
|
+
if not path.is_file():
|
|
86
|
+
continue
|
|
87
|
+
payload = json.loads(path.read_text(encoding="utf-8"))
|
|
88
|
+
token = str(payload.get("token") or "").strip()
|
|
89
|
+
if token:
|
|
90
|
+
return token
|
|
91
|
+
except Exception:
|
|
92
|
+
continue
|
|
93
|
+
return ""
|
|
94
|
+
|
|
95
|
+
|
|
96
|
+
def _read_token() -> str:
|
|
97
|
+
for env_name in ("NEXO_DESKTOP_AUTH_TOKEN", "NEXO_CARDS_TOKEN", "NEXO_AUTH_TOKEN"):
|
|
98
|
+
token = str(os.environ.get(env_name) or "").strip()
|
|
99
|
+
if token:
|
|
100
|
+
return token
|
|
101
|
+
return _read_token_from_shared_auth()
|
|
102
|
+
|
|
103
|
+
|
|
104
|
+
def _error(error_type: str, message: str, *, status: int = 0, extra: dict[str, Any] | None = None) -> dict[str, Any]:
|
|
105
|
+
payload: dict[str, Any] = {
|
|
106
|
+
"ok": False,
|
|
107
|
+
"error": {
|
|
108
|
+
"type": error_type,
|
|
109
|
+
"message": message,
|
|
110
|
+
},
|
|
111
|
+
}
|
|
112
|
+
if status:
|
|
113
|
+
payload["status"] = status
|
|
114
|
+
if extra:
|
|
115
|
+
payload["error"].update(extra)
|
|
116
|
+
return payload
|
|
117
|
+
|
|
118
|
+
|
|
119
|
+
def _decode_body(raw: bytes) -> dict[str, Any]:
|
|
120
|
+
if not raw:
|
|
121
|
+
return {}
|
|
122
|
+
try:
|
|
123
|
+
decoded = json.loads(raw.decode("utf-8"))
|
|
124
|
+
return decoded if isinstance(decoded, dict) else {"data": decoded}
|
|
125
|
+
except Exception:
|
|
126
|
+
return {"raw": raw.decode("utf-8", errors="replace")[:1000]}
|
|
127
|
+
|
|
128
|
+
|
|
129
|
+
def _request_json(method: str, path: str, *, body: dict[str, Any] | None = None, locale: str = "es") -> dict[str, Any]:
|
|
130
|
+
token = _read_token()
|
|
131
|
+
if not token:
|
|
132
|
+
return _error(
|
|
133
|
+
"not_authenticated",
|
|
134
|
+
"No hay sesión NEXO Desktop disponible para consultar fichas.",
|
|
135
|
+
)
|
|
136
|
+
|
|
137
|
+
data = json.dumps(body or {}, ensure_ascii=False).encode("utf-8") if body is not None else None
|
|
138
|
+
request = urllib.request.Request(
|
|
139
|
+
f"{_api_base()}{path}",
|
|
140
|
+
data=data,
|
|
141
|
+
method=method.upper(),
|
|
142
|
+
headers={
|
|
143
|
+
"Accept": "application/json",
|
|
144
|
+
"Accept-Language": _normalize_locale(locale),
|
|
145
|
+
"Authorization": f"Bearer {token}",
|
|
146
|
+
**({"Content-Type": "application/json"} if data is not None else {}),
|
|
147
|
+
},
|
|
148
|
+
)
|
|
149
|
+
try:
|
|
150
|
+
with _urlopen(request, timeout=20) as response:
|
|
151
|
+
payload = _decode_body(response.read())
|
|
152
|
+
if isinstance(payload, dict):
|
|
153
|
+
payload.setdefault("ok", True)
|
|
154
|
+
payload.setdefault("status", getattr(response, "status", 200))
|
|
155
|
+
return payload
|
|
156
|
+
return {"ok": True, "data": payload, "status": getattr(response, "status", 200)}
|
|
157
|
+
except urllib.error.HTTPError as exc:
|
|
158
|
+
payload = _decode_body(exc.read())
|
|
159
|
+
error = payload.get("error") if isinstance(payload, dict) else None
|
|
160
|
+
if isinstance(error, dict):
|
|
161
|
+
return _error(
|
|
162
|
+
str(error.get("type") or "request_failed"),
|
|
163
|
+
str(error.get("message") or f"HTTP {exc.code}"),
|
|
164
|
+
status=int(exc.code or 0),
|
|
165
|
+
extra={k: v for k, v in error.items() if k not in {"type", "message"}},
|
|
166
|
+
)
|
|
167
|
+
return _error("request_failed", f"Protocol cards API returned HTTP {exc.code}.", status=int(exc.code or 0))
|
|
168
|
+
except Exception as exc:
|
|
169
|
+
return _error("network_error", str(exc))
|
|
170
|
+
|
|
171
|
+
|
|
172
|
+
def handle_card_catalog(locale: str = "es") -> str:
|
|
173
|
+
"""Return the visible official protocol card catalog. Never includes protocols."""
|
|
174
|
+
params = urllib.parse.urlencode({"locale": _normalize_locale(locale)})
|
|
175
|
+
return _json(_request_json("GET", f"/api/cards/catalog?{params}", locale=locale))
|
|
176
|
+
|
|
177
|
+
|
|
178
|
+
def handle_card_get(slug: str, locale: str = "es") -> str:
|
|
179
|
+
"""Fetch one official protocol card, including protocol text, by slug."""
|
|
180
|
+
clean_slug = str(slug or "").strip()
|
|
181
|
+
if not clean_slug:
|
|
182
|
+
return _json(_error("invalid_input", "slug is required"))
|
|
183
|
+
params = urllib.parse.urlencode({"locale": _normalize_locale(locale)})
|
|
184
|
+
safe_slug = urllib.parse.quote(clean_slug, safe="")
|
|
185
|
+
return _json(_request_json("GET", f"/api/cards/{safe_slug}?{params}", locale=locale))
|
|
186
|
+
|
|
187
|
+
|
|
188
|
+
def handle_card_match(
|
|
189
|
+
query: str,
|
|
190
|
+
limit: int = 5,
|
|
191
|
+
include_protocol: bool = True,
|
|
192
|
+
locale: str = "es",
|
|
193
|
+
category: str = "",
|
|
194
|
+
business_type: str = "",
|
|
195
|
+
) -> str:
|
|
196
|
+
"""Find official protocol cards for a user request.
|
|
197
|
+
|
|
198
|
+
Use this before non-trivial work when available. Protocols are fetched from
|
|
199
|
+
the authenticated backend at runtime; this package does not embed them.
|
|
200
|
+
"""
|
|
201
|
+
clean_query = str(query or "").strip()
|
|
202
|
+
if not clean_query:
|
|
203
|
+
return _json(_error("invalid_input", "query is required"))
|
|
204
|
+
body = {
|
|
205
|
+
"query": clean_query,
|
|
206
|
+
"limit": max(1, min(20, _as_int(limit, 5))),
|
|
207
|
+
"include_protocol": _as_bool(include_protocol, True),
|
|
208
|
+
"locale": _normalize_locale(locale),
|
|
209
|
+
}
|
|
210
|
+
if category:
|
|
211
|
+
body["category"] = str(category).strip()
|
|
212
|
+
if business_type:
|
|
213
|
+
body["business_type"] = str(business_type).strip()
|
|
214
|
+
return _json(_request_json("POST", "/api/cards/match", body=body, locale=locale))
|
|
215
|
+
|
|
216
|
+
|
|
217
|
+
TOOLS = [
|
|
218
|
+
(handle_card_catalog, "nexo_card_catalog", "List visible official NEXO protocol cards from the authenticated backend. Does not return protocol text."),
|
|
219
|
+
(handle_card_get, "nexo_card_get", "Fetch one official NEXO protocol card by slug from the authenticated backend, including protocol text."),
|
|
220
|
+
(handle_card_match, "nexo_card_match", "Find the official NEXO protocol card for a user request. Call before non-trivial work when available."),
|
|
221
|
+
]
|
|
@@ -30,13 +30,14 @@ Claude Code may list `mcp__nexo__*` tools as **deferred** at session start (name
|
|
|
30
30
|
- Diagnostic plane: `nexo_doctor plane='installation_live'` inspects client/install surfaces — consult it when tools appear missing on a fresh install.
|
|
31
31
|
<!-- nexo:end:tools_at_startup -->
|
|
32
32
|
|
|
33
|
-
## Protocol (
|
|
33
|
+
## Protocol (7 rules)
|
|
34
34
|
1. `nexo_startup` once per session and keep the returned `SID`.
|
|
35
35
|
2. `nexo_heartbeat` on every user message.
|
|
36
36
|
3. `nexo_task_open` for any non-trivial work. For `edit` / `execute` / `delegate`, this is the default path. If the work is long multi-step or likely to cross messages/sessions, also open `nexo_workflow_open` and keep it updated. If task_open surfaces blocking conditioned-file learnings, review them and acknowledge guard before any write / delete step.
|
|
37
|
-
4.
|
|
38
|
-
5.
|
|
39
|
-
6.
|
|
37
|
+
4. If `nexo_card_match` is available, call it before non-trivial user-facing creation/analysis work. If it returns an official protocol, follow it silently; if unavailable or no match, continue normally.
|
|
38
|
+
5. Do not say `done`, `fixed`, or `sent` without evidence captured in `nexo_task_close`.
|
|
39
|
+
6. If a correction revealed a reusable pattern, capture or supersede the learning immediately so contradictory rules do not remain active.
|
|
40
|
+
7. On clear end-of-session intent, write the diary before replying.
|
|
40
41
|
|
|
41
42
|
## Response Pacing
|
|
42
43
|
- After the first relevant tool or artifact result, reply with the answer immediately instead of silently chaining more investigation.
|
|
@@ -25,13 +25,14 @@ Codex (and Claude Code) may list `mcp__nexo__*` tools as **deferred** at session
|
|
|
25
25
|
- If discovery still cannot resolve a `nexo_*` tool, then (and only then) treat it as a real runtime gap and surface it as a blocker.
|
|
26
26
|
- Diagnostic plane: `nexo_doctor plane='installation_live'` inspects client/install surfaces — consult it when tools appear missing on a fresh install.
|
|
27
27
|
|
|
28
|
-
## Protocol (
|
|
28
|
+
## Protocol (7 rules)
|
|
29
29
|
1. `nexo_startup` once per session, then keep the returned `SID`.
|
|
30
30
|
2. `nexo_heartbeat` on every user message.
|
|
31
31
|
3. `nexo_task_open` for any non-trivial work. For `edit` / `execute` / `delegate`, this is the default path. If the work is long multi-step or likely to cross messages/sessions, also open `nexo_workflow_open` and keep it updated. If task_open surfaces blocking conditioned-file learnings, review them and acknowledge guard before any write / delete step.
|
|
32
|
-
4.
|
|
33
|
-
5.
|
|
34
|
-
6.
|
|
32
|
+
4. If `nexo_card_match` is available, call it before non-trivial user-facing creation/analysis work. If it returns an official protocol, follow it silently; if unavailable or no match, continue normally.
|
|
33
|
+
5. Do not say `done`, `fixed`, or `sent` without evidence captured in `nexo_task_close`.
|
|
34
|
+
6. If a correction revealed a reusable pattern, capture or supersede the learning immediately so contradictory rules do not remain active.
|
|
35
|
+
7. On clear end-of-session intent, write the diary before replying.
|
|
35
36
|
|
|
36
37
|
## Response Pacing
|
|
37
38
|
- After the first relevant tool or artifact result, answer immediately instead of silently chaining more investigation.
|
|
@@ -7,6 +7,7 @@
|
|
|
7
7
|
- **Diagnostic plane (MANDATORY before diagnosing NEXO):** fix the plane explicitly first — `product_public`, `runtime_personal`, `installation_live`, `database_real`, or `cooperator`. Do not mix product, runtime, install, DB, and agent-behavior explanations in the same diagnosis.
|
|
8
8
|
- **Guard (MANDATORY before ANY code edit):** `nexo_guard_check(files='...', area='...')` BEFORE editing code. No exceptions. Blocking rules→resolve first. `nexo_track(sid=SID, paths=[...])` before shared files
|
|
9
9
|
- **Skills (MANDATORY before multi-step tasks):** `nexo_skill_match(task)` to find reusable procedures. If match found, read it and follow the steps. After completion, `nexo_skill_result(id, success, context)` to record outcome.
|
|
10
|
+
- **Official protocol cards (MANDATORY when available):** before non-trivial user-facing creation/analysis work, call `nexo_card_match(query=...)`. If it returns a protocol, follow it silently. The protocol text is served by the authenticated backend and is not embedded in Brain.
|
|
10
11
|
- **Learnings (MANDATORY on corrections):** When you discover a bug, pattern, or get corrected→`nexo_learning_add` IMMEDIATELY. Do NOT batch. Do NOT wait until end of session.
|
|
11
12
|
|
|
12
13
|
## Rules
|
|
@@ -315,6 +315,55 @@
|
|
|
315
315
|
},
|
|
316
316
|
"triggers_after": []
|
|
317
317
|
},
|
|
318
|
+
"nexo_card_catalog": {
|
|
319
|
+
"description": "List visible official protocol cards from backend",
|
|
320
|
+
"category": "protocol_cards",
|
|
321
|
+
"source": "plugin:cards",
|
|
322
|
+
"requires": [],
|
|
323
|
+
"provides": [
|
|
324
|
+
"official_protocol_card_catalog"
|
|
325
|
+
],
|
|
326
|
+
"internal_calls": [],
|
|
327
|
+
"enforcement": {
|
|
328
|
+
"level": "none",
|
|
329
|
+
"rules": []
|
|
330
|
+
},
|
|
331
|
+
"triggers_after": []
|
|
332
|
+
},
|
|
333
|
+
"nexo_card_get": {
|
|
334
|
+
"description": "Fetch one official protocol card from backend",
|
|
335
|
+
"category": "protocol_cards",
|
|
336
|
+
"source": "plugin:cards",
|
|
337
|
+
"requires": [
|
|
338
|
+
"desktop_auth_token"
|
|
339
|
+
],
|
|
340
|
+
"provides": [
|
|
341
|
+
"official_protocol_card"
|
|
342
|
+
],
|
|
343
|
+
"internal_calls": [],
|
|
344
|
+
"enforcement": {
|
|
345
|
+
"level": "none",
|
|
346
|
+
"rules": []
|
|
347
|
+
},
|
|
348
|
+
"triggers_after": []
|
|
349
|
+
},
|
|
350
|
+
"nexo_card_match": {
|
|
351
|
+
"description": "Match user request to an official protocol card from backend",
|
|
352
|
+
"category": "protocol_cards",
|
|
353
|
+
"source": "plugin:cards",
|
|
354
|
+
"requires": [
|
|
355
|
+
"desktop_auth_token"
|
|
356
|
+
],
|
|
357
|
+
"provides": [
|
|
358
|
+
"official_protocol_card_match"
|
|
359
|
+
],
|
|
360
|
+
"internal_calls": [],
|
|
361
|
+
"enforcement": {
|
|
362
|
+
"level": "none",
|
|
363
|
+
"rules": []
|
|
364
|
+
},
|
|
365
|
+
"triggers_after": []
|
|
366
|
+
},
|
|
318
367
|
"nexo_change_commit": {
|
|
319
368
|
"description": "Link change log entry to git commit",
|
|
320
369
|
"category": "change_log",
|