@anna-ai/cli 0.1.0
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/README.md +149 -0
- package/dist/bridge-CzEs7jaN.js +145 -0
- package/dist/bridge-t2Qqu3hC.js +3 -0
- package/dist/cli.d.ts +1 -0
- package/dist/cli.js +121 -0
- package/dist/dashboard.html +426 -0
- package/dist/dev-Bgku5ngL.js +163 -0
- package/dist/doctor-D3z4YslL.js +69 -0
- package/dist/fixture-BGjMtqWA.js +278 -0
- package/dist/server-B6-Qdluv.js +255 -0
- package/dist/test/index.d.ts +151 -0
- package/dist/test/index.js +260 -0
- package/package.json +53 -0
- package/templates/minimal/README.md +9 -0
- package/templates/minimal/app.json +7 -0
- package/templates/minimal/bundle/app.js +36 -0
- package/templates/minimal/bundle/index.html +14 -0
- package/templates/minimal/executas/__SLUG__/plugin.py +60 -0
- package/templates/minimal/executas/__SLUG__/pyproject.toml +12 -0
- package/templates/minimal/manifest.json +38 -0
- package/vendor/anna-app-schema/README.md +22 -0
- package/vendor/anna-app-schema/dispatcher_version.txt +1 -0
- package/vendor/anna-app-schema/events/AnnaAppEvent.json +38 -0
- package/vendor/anna-app-schema/host_api/methods.json +170 -0
- package/vendor/anna-app-schema/manifest/AppManifest.json +471 -0
- package/vendor/anna-app-schema/manifest/UiManifestSection.json +273 -0
- package/vendor/anna-app-schema/package.json +25 -0
- package/vendor/anna-app-schema/pyproject.toml +13 -0
|
@@ -0,0 +1,260 @@
|
|
|
1
|
+
//#region src/test/host-api-acl.ts
|
|
2
|
+
const NAMESPACED = {
|
|
3
|
+
tools: true,
|
|
4
|
+
chat: true,
|
|
5
|
+
artifact: true,
|
|
6
|
+
llm: true,
|
|
7
|
+
fs: true,
|
|
8
|
+
storage: true,
|
|
9
|
+
prefs: true,
|
|
10
|
+
window: true
|
|
11
|
+
};
|
|
12
|
+
function deriveAcl(manifest) {
|
|
13
|
+
const ha = manifest.ui?.host_api ?? {};
|
|
14
|
+
const allowed = new Set();
|
|
15
|
+
const allowedTools = new Set();
|
|
16
|
+
let toolsWildcard = false;
|
|
17
|
+
for (const ref of ha.tools ?? []) {
|
|
18
|
+
if (ref === "required:*" || ref === "optional:*" || ref === "*") {
|
|
19
|
+
toolsWildcard = true;
|
|
20
|
+
continue;
|
|
21
|
+
}
|
|
22
|
+
const bare = ref.includes(":") ? ref.split(":", 2)[1] : ref;
|
|
23
|
+
allowedTools.add(bare);
|
|
24
|
+
}
|
|
25
|
+
if (allowedTools.size > 0 || toolsWildcard) allowed.add("tools.invoke");
|
|
26
|
+
for (const ns of Object.keys(NAMESPACED)) {
|
|
27
|
+
if (ns === "tools") continue;
|
|
28
|
+
for (const method of ha[ns] ?? []) allowed.add(`${ns}.${method}`);
|
|
29
|
+
}
|
|
30
|
+
return {
|
|
31
|
+
allowed,
|
|
32
|
+
allowedTools,
|
|
33
|
+
toolsWildcard
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
function isToolAllowed(acl, toolId) {
|
|
37
|
+
if (acl.toolsWildcard) return true;
|
|
38
|
+
return acl.allowedTools.has(toolId);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
//#endregion
|
|
42
|
+
//#region src/test/runtime.ts
|
|
43
|
+
const DEFAULT_TOKEN_TTL_MS = 5 * 60 * 1e3;
|
|
44
|
+
function randHex(n) {
|
|
45
|
+
let s = "";
|
|
46
|
+
for (let i = 0; i < n; i += 1) s += Math.floor(Math.random() * 16).toString(16);
|
|
47
|
+
return s;
|
|
48
|
+
}
|
|
49
|
+
var CallLogImpl = class {
|
|
50
|
+
records = [];
|
|
51
|
+
push(r) {
|
|
52
|
+
this.records.push(r);
|
|
53
|
+
}
|
|
54
|
+
all() {
|
|
55
|
+
return this.records.slice();
|
|
56
|
+
}
|
|
57
|
+
byNs(prefix) {
|
|
58
|
+
return this.records.filter((r) => `${r.ns}.${r.method}` === prefix || r.ns === prefix);
|
|
59
|
+
}
|
|
60
|
+
last() {
|
|
61
|
+
return this.records[this.records.length - 1] ?? null;
|
|
62
|
+
}
|
|
63
|
+
lastOf(prefix) {
|
|
64
|
+
const xs = this.byNs(prefix);
|
|
65
|
+
return xs[xs.length - 1] ?? null;
|
|
66
|
+
}
|
|
67
|
+
clear() {
|
|
68
|
+
this.records.length = 0;
|
|
69
|
+
}
|
|
70
|
+
};
|
|
71
|
+
var EventBusImpl = class {
|
|
72
|
+
listeners = new Map();
|
|
73
|
+
wildcard = new Set();
|
|
74
|
+
emit(name, payload) {
|
|
75
|
+
for (const fn of this.listeners.get(name) ?? []) try {
|
|
76
|
+
fn(payload);
|
|
77
|
+
} catch {}
|
|
78
|
+
for (const fn of this.wildcard) try {
|
|
79
|
+
fn(name, payload);
|
|
80
|
+
} catch {}
|
|
81
|
+
}
|
|
82
|
+
on(name, fn) {
|
|
83
|
+
let set = this.listeners.get(name);
|
|
84
|
+
if (!set) {
|
|
85
|
+
set = new Set();
|
|
86
|
+
this.listeners.set(name, set);
|
|
87
|
+
}
|
|
88
|
+
set.add(fn);
|
|
89
|
+
return () => set.delete(fn);
|
|
90
|
+
}
|
|
91
|
+
/** Internal: tap all events for the auth.refresh handler etc. */
|
|
92
|
+
onAny(fn) {
|
|
93
|
+
this.wildcard.add(fn);
|
|
94
|
+
return () => this.wildcard.delete(fn);
|
|
95
|
+
}
|
|
96
|
+
};
|
|
97
|
+
/**
|
|
98
|
+
* Default mock implementations for namespaces that have safe in-memory
|
|
99
|
+
* behavior. Bundle authors can override any of these via `mocks`.
|
|
100
|
+
*/
|
|
101
|
+
function makeDefaultMocks(state) {
|
|
102
|
+
return {
|
|
103
|
+
"storage.get": (args) => {
|
|
104
|
+
const key = args.key ?? "";
|
|
105
|
+
return state.storage.get(key) ?? null;
|
|
106
|
+
},
|
|
107
|
+
"storage.set": (args) => {
|
|
108
|
+
const { key, value } = args;
|
|
109
|
+
state.storage.set(key, value);
|
|
110
|
+
return null;
|
|
111
|
+
},
|
|
112
|
+
"storage.delete": (args) => {
|
|
113
|
+
const key = args.key ?? "";
|
|
114
|
+
state.storage.delete(key);
|
|
115
|
+
return null;
|
|
116
|
+
},
|
|
117
|
+
"prefs.get": (args) => {
|
|
118
|
+
const key = args.key ?? "";
|
|
119
|
+
return state.prefs.get(key) ?? null;
|
|
120
|
+
},
|
|
121
|
+
"prefs.set": (args) => {
|
|
122
|
+
const { key, value } = args;
|
|
123
|
+
state.prefs.set(key, value);
|
|
124
|
+
return null;
|
|
125
|
+
},
|
|
126
|
+
"chat.write_message": () => null,
|
|
127
|
+
"window.set_title": () => null
|
|
128
|
+
};
|
|
129
|
+
}
|
|
130
|
+
async function mountBundle(opts) {
|
|
131
|
+
const acl = deriveAcl(opts.manifest);
|
|
132
|
+
const calls = new CallLogImpl();
|
|
133
|
+
const events = new EventBusImpl();
|
|
134
|
+
const startedAt = Date.now();
|
|
135
|
+
const ttl = opts.tokenTtlMs ?? DEFAULT_TOKEN_TTL_MS;
|
|
136
|
+
const wid = opts.wid ?? `harness-${randHex(8)}`;
|
|
137
|
+
let token = opts.token ?? randHex(24);
|
|
138
|
+
const state = {
|
|
139
|
+
storage: new Map(),
|
|
140
|
+
prefs: new Map()
|
|
141
|
+
};
|
|
142
|
+
const mocks = {
|
|
143
|
+
...makeDefaultMocks(state),
|
|
144
|
+
...opts.mocks ?? {}
|
|
145
|
+
};
|
|
146
|
+
events.onAny((name, payload) => {
|
|
147
|
+
if (name === "auth.refresh") {
|
|
148
|
+
const next = payload?.token;
|
|
149
|
+
if (typeof next === "string" && next) token = next;
|
|
150
|
+
}
|
|
151
|
+
});
|
|
152
|
+
let seq = 0;
|
|
153
|
+
async function dispatch(ns, method, args) {
|
|
154
|
+
seq += 1;
|
|
155
|
+
const rec = {
|
|
156
|
+
seq,
|
|
157
|
+
t: Date.now() - startedAt,
|
|
158
|
+
ns,
|
|
159
|
+
method,
|
|
160
|
+
args,
|
|
161
|
+
outcome: null
|
|
162
|
+
};
|
|
163
|
+
calls.push(rec);
|
|
164
|
+
const key = `${ns}.${method}`;
|
|
165
|
+
if (!acl.allowed.has(key)) {
|
|
166
|
+
rec.outcome = "denied";
|
|
167
|
+
rec.errorCode = "DENIED";
|
|
168
|
+
rec.errorMessage = `host API '${key}' not declared in manifest.ui.host_api`;
|
|
169
|
+
throw new HostApiError(rec.errorCode, rec.errorMessage);
|
|
170
|
+
}
|
|
171
|
+
if (ns === "tools" && method === "invoke") {
|
|
172
|
+
const toolId = args.tool_id ?? "";
|
|
173
|
+
if (!isToolAllowed(acl, toolId)) {
|
|
174
|
+
rec.outcome = "denied";
|
|
175
|
+
rec.errorCode = "DENIED";
|
|
176
|
+
rec.errorMessage = `tool '${toolId}' not declared in manifest.ui.host_api.tools`;
|
|
177
|
+
throw new HostApiError(rec.errorCode, rec.errorMessage);
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
const handler = mocks[key];
|
|
181
|
+
if (!handler) {
|
|
182
|
+
rec.outcome = "error";
|
|
183
|
+
rec.errorCode = "NO_MOCK";
|
|
184
|
+
rec.errorMessage = `no mock registered for '${key}' (add via opts.mocks or harness.mock)`;
|
|
185
|
+
throw new HostApiError(rec.errorCode, rec.errorMessage);
|
|
186
|
+
}
|
|
187
|
+
try {
|
|
188
|
+
const result = await Promise.resolve(handler(args, {
|
|
189
|
+
ns,
|
|
190
|
+
method
|
|
191
|
+
}));
|
|
192
|
+
rec.outcome = "ok";
|
|
193
|
+
rec.result = result;
|
|
194
|
+
return result;
|
|
195
|
+
} catch (e) {
|
|
196
|
+
rec.outcome = "error";
|
|
197
|
+
const err = e;
|
|
198
|
+
rec.errorCode = err.code ?? "MOCK_ERROR";
|
|
199
|
+
rec.errorMessage = err.message ?? String(e);
|
|
200
|
+
throw e;
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
const facet = (ns) => (method) => (args) => dispatch(ns, method, args ?? {});
|
|
204
|
+
const runtime = {
|
|
205
|
+
get hello() {
|
|
206
|
+
return {
|
|
207
|
+
wid,
|
|
208
|
+
t: token,
|
|
209
|
+
user_id: opts.userId ?? 1
|
|
210
|
+
};
|
|
211
|
+
},
|
|
212
|
+
call: dispatch,
|
|
213
|
+
on: (name, fn) => events.on(name, fn),
|
|
214
|
+
tools: { invoke: (args) => dispatch("tools", "invoke", args) },
|
|
215
|
+
storage: {
|
|
216
|
+
get: (key) => dispatch("storage", "get", { key }),
|
|
217
|
+
set: (key, value) => dispatch("storage", "set", {
|
|
218
|
+
key,
|
|
219
|
+
value
|
|
220
|
+
})
|
|
221
|
+
},
|
|
222
|
+
chat: { write_message: (text, optsArg) => dispatch("chat", "write_message", {
|
|
223
|
+
text,
|
|
224
|
+
...optsArg ?? {}
|
|
225
|
+
}) },
|
|
226
|
+
artifact: { create: (args) => dispatch("artifact", "create", args) },
|
|
227
|
+
llm: { complete: (args) => dispatch("llm", "complete", args) },
|
|
228
|
+
fs: new Proxy({}, { get: (_t, prop) => facet("fs")(prop) }),
|
|
229
|
+
prefs: {
|
|
230
|
+
get: (key) => dispatch("prefs", "get", { key }),
|
|
231
|
+
set: (key, value) => dispatch("prefs", "set", {
|
|
232
|
+
key,
|
|
233
|
+
value
|
|
234
|
+
})
|
|
235
|
+
},
|
|
236
|
+
window: { set_title: (title) => dispatch("window", "set_title", { title }) }
|
|
237
|
+
};
|
|
238
|
+
return {
|
|
239
|
+
runtime,
|
|
240
|
+
calls,
|
|
241
|
+
events,
|
|
242
|
+
acl,
|
|
243
|
+
mock(key, handler) {
|
|
244
|
+
mocks[key] = handler;
|
|
245
|
+
},
|
|
246
|
+
wait: (ms) => new Promise((res) => setTimeout(res, ms))
|
|
247
|
+
};
|
|
248
|
+
}
|
|
249
|
+
/** Error class thrown for ACL denials or missing mocks. */
|
|
250
|
+
var HostApiError = class extends Error {
|
|
251
|
+
code;
|
|
252
|
+
constructor(code, message) {
|
|
253
|
+
super(message);
|
|
254
|
+
this.name = "HostApiError";
|
|
255
|
+
this.code = code;
|
|
256
|
+
}
|
|
257
|
+
};
|
|
258
|
+
|
|
259
|
+
//#endregion
|
|
260
|
+
export { HostApiError, deriveAcl, isToolAllowed, mountBundle };
|
package/package.json
ADDED
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@anna-ai/cli",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Anna App developer CLI: scaffold, validate, harness (Phase 2 MVP: init + validate).",
|
|
5
|
+
"license": "MIT",
|
|
6
|
+
"type": "module",
|
|
7
|
+
"bin": {
|
|
8
|
+
"anna-app": "./dist/cli.js"
|
|
9
|
+
},
|
|
10
|
+
"exports": {
|
|
11
|
+
".": {
|
|
12
|
+
"types": "./dist/cli.d.ts",
|
|
13
|
+
"import": "./dist/cli.js"
|
|
14
|
+
},
|
|
15
|
+
"./test": {
|
|
16
|
+
"types": "./dist/test/index.d.ts",
|
|
17
|
+
"import": "./dist/test/index.js"
|
|
18
|
+
}
|
|
19
|
+
},
|
|
20
|
+
"files": [
|
|
21
|
+
"dist/",
|
|
22
|
+
"templates/",
|
|
23
|
+
"vendor/",
|
|
24
|
+
"README.md"
|
|
25
|
+
],
|
|
26
|
+
"scripts": {
|
|
27
|
+
"build": "tsdown",
|
|
28
|
+
"dev": "tsdown --watch",
|
|
29
|
+
"test": "vitest run",
|
|
30
|
+
"test:watch": "vitest",
|
|
31
|
+
"lint": "tsc --noEmit",
|
|
32
|
+
"sync:schema": "node scripts/sync-schema.mjs",
|
|
33
|
+
"check:runtime-pin": "node scripts/check-runtime-pin.mjs",
|
|
34
|
+
"prepublishOnly": "pnpm lint && pnpm test && pnpm build"
|
|
35
|
+
},
|
|
36
|
+
"dependencies": {
|
|
37
|
+
"ajv": "^8.17.1",
|
|
38
|
+
"ajv-formats": "^3.0.1",
|
|
39
|
+
"commander": "^12.1.0",
|
|
40
|
+
"kleur": "^4.1.5",
|
|
41
|
+
"ws": "^8.18.0"
|
|
42
|
+
},
|
|
43
|
+
"devDependencies": {
|
|
44
|
+
"@types/node": "^22.10.0",
|
|
45
|
+
"@types/ws": "^8.5.13",
|
|
46
|
+
"tsdown": "^0.9.6",
|
|
47
|
+
"typescript": "^5.6.3",
|
|
48
|
+
"vitest": "^2.1.8"
|
|
49
|
+
},
|
|
50
|
+
"engines": {
|
|
51
|
+
"node": ">=22"
|
|
52
|
+
}
|
|
53
|
+
}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
// Minimal Anna App bundle entry. Replace with real logic.
|
|
2
|
+
const TOOL_ID = "__TOOL_ID__";
|
|
3
|
+
|
|
4
|
+
async function main() {
|
|
5
|
+
const status = document.getElementById("status");
|
|
6
|
+
const btn = document.getElementById("primary-btn");
|
|
7
|
+
if (!status || !btn) return;
|
|
8
|
+
|
|
9
|
+
let anna;
|
|
10
|
+
try {
|
|
11
|
+
anna = await AnnaAppRuntime.connect();
|
|
12
|
+
} catch (e) {
|
|
13
|
+
status.textContent = "Standalone preview (no host).";
|
|
14
|
+
return;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
await anna.window.set_title({ title: "__SLUG__" });
|
|
18
|
+
status.textContent = "Ready.";
|
|
19
|
+
|
|
20
|
+
btn.addEventListener("click", async () => {
|
|
21
|
+
status.textContent = "Running…";
|
|
22
|
+
try {
|
|
23
|
+
const out = await anna.tools.invoke({
|
|
24
|
+
tool_id: TOOL_ID,
|
|
25
|
+
method: "ping",
|
|
26
|
+
args: {},
|
|
27
|
+
});
|
|
28
|
+
await anna.storage.set({ key: "__SLUG__:last", value: Date.now() });
|
|
29
|
+
status.textContent = JSON.stringify(out, null, 2);
|
|
30
|
+
} catch (e) {
|
|
31
|
+
status.textContent = "Error: " + e.message;
|
|
32
|
+
}
|
|
33
|
+
});
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
main();
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
<!DOCTYPE html>
|
|
2
|
+
<html lang="en">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="utf-8" />
|
|
5
|
+
<title>__SLUG__</title>
|
|
6
|
+
<script src="/static/anna-apps/_sdk/0.1.0/index.js" defer></script>
|
|
7
|
+
<script src="./app.js" type="module" defer></script>
|
|
8
|
+
</head>
|
|
9
|
+
<body>
|
|
10
|
+
<h1>__SLUG__</h1>
|
|
11
|
+
<button id="primary-btn">Run</button>
|
|
12
|
+
<pre id="status">…</pre>
|
|
13
|
+
</body>
|
|
14
|
+
</html>
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
"""Minimal stdio plugin scaffold for the __SLUG__ Anna App."""
|
|
2
|
+
|
|
3
|
+
import json
|
|
4
|
+
import sys
|
|
5
|
+
|
|
6
|
+
MANIFEST = {
|
|
7
|
+
"name": "__TOOL_ID__",
|
|
8
|
+
"version": "0.1.0",
|
|
9
|
+
"tools": [
|
|
10
|
+
{
|
|
11
|
+
"name": "ping",
|
|
12
|
+
"description": "Smoke-test method.",
|
|
13
|
+
"parameters": {
|
|
14
|
+
"type": "object",
|
|
15
|
+
"properties": {},
|
|
16
|
+
"additionalProperties": False,
|
|
17
|
+
},
|
|
18
|
+
}
|
|
19
|
+
],
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
def invoke(method: str, args: dict) -> dict:
|
|
24
|
+
if method == "ping":
|
|
25
|
+
return {"pong": True}
|
|
26
|
+
raise ValueError(f"unknown method: {method}")
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
def main() -> None:
|
|
30
|
+
for line in sys.stdin:
|
|
31
|
+
line = line.strip()
|
|
32
|
+
if not line:
|
|
33
|
+
continue
|
|
34
|
+
req = json.loads(line)
|
|
35
|
+
try:
|
|
36
|
+
if req.get("method") == "describe":
|
|
37
|
+
result = MANIFEST
|
|
38
|
+
elif req.get("method") == "health":
|
|
39
|
+
result = {"status": "ok"}
|
|
40
|
+
elif req.get("method") == "invoke":
|
|
41
|
+
result = invoke(req["params"]["tool"], req["params"].get("arguments", {}))
|
|
42
|
+
else:
|
|
43
|
+
raise ValueError(f"unknown rpc: {req.get('method')}")
|
|
44
|
+
sys.stdout.write(json.dumps({"jsonrpc": "2.0", "id": req.get("id"), "result": result}) + "\n")
|
|
45
|
+
except Exception as e: # noqa: BLE001
|
|
46
|
+
sys.stdout.write(
|
|
47
|
+
json.dumps(
|
|
48
|
+
{
|
|
49
|
+
"jsonrpc": "2.0",
|
|
50
|
+
"id": req.get("id"),
|
|
51
|
+
"error": {"code": -32601, "message": str(e)},
|
|
52
|
+
}
|
|
53
|
+
)
|
|
54
|
+
+ "\n"
|
|
55
|
+
)
|
|
56
|
+
sys.stdout.flush()
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
if __name__ == "__main__":
|
|
60
|
+
main()
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["hatchling"]
|
|
3
|
+
build-backend = "hatchling.build"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "__TOOL_ID__"
|
|
7
|
+
version = "0.1.0"
|
|
8
|
+
description = "Executa for the __SLUG__ Anna App"
|
|
9
|
+
requires-python = ">=3.10"
|
|
10
|
+
|
|
11
|
+
[project.scripts]
|
|
12
|
+
"__TOOL_ID__" = "__SLUG___executa.plugin:main"
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
{
|
|
2
|
+
"schema": 2,
|
|
3
|
+
"permissions": ["tools.invoke", "storage.read", "storage.write"],
|
|
4
|
+
"required_executas": [
|
|
5
|
+
{
|
|
6
|
+
"tool_id": "__TOOL_ID__"
|
|
7
|
+
}
|
|
8
|
+
],
|
|
9
|
+
"tags": ["__SLUG__"],
|
|
10
|
+
"ui": {
|
|
11
|
+
"bundle": {
|
|
12
|
+
"format": "static-spa",
|
|
13
|
+
"entry": "index.html",
|
|
14
|
+
"external_origins": []
|
|
15
|
+
},
|
|
16
|
+
"views": [
|
|
17
|
+
{
|
|
18
|
+
"name": "main",
|
|
19
|
+
"title": "__SLUG__",
|
|
20
|
+
"default": true,
|
|
21
|
+
"default_size": { "w": 480, "h": 360 },
|
|
22
|
+
"min_size": { "w": 320, "h": 240 }
|
|
23
|
+
}
|
|
24
|
+
],
|
|
25
|
+
"host_api": {
|
|
26
|
+
"tools": ["__TOOL_ID__"],
|
|
27
|
+
"storage": ["get", "set"],
|
|
28
|
+
"window": ["set_title", "ready"]
|
|
29
|
+
},
|
|
30
|
+
"csp_overrides": {},
|
|
31
|
+
"state_merge": "last_writer_wins"
|
|
32
|
+
},
|
|
33
|
+
"dev": {
|
|
34
|
+
"fixtures": ["fixtures/*.jsonl"],
|
|
35
|
+
"seed_storage": {},
|
|
36
|
+
"user_id": 1
|
|
37
|
+
}
|
|
38
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
# @anna/app-schema (v0.1.0)
|
|
2
|
+
|
|
3
|
+
Versioned schema bundle for the Anna App platform. Generated by
|
|
4
|
+
`scripts/export_app_schema.py` in matrix-nexus; do not edit by hand.
|
|
5
|
+
|
|
6
|
+
## Contents
|
|
7
|
+
|
|
8
|
+
* `manifest/AppManifest.json` — JSON Schema for the app manifest.
|
|
9
|
+
* `manifest/UiManifestSection.json` — JSON Schema for the `ui` block.
|
|
10
|
+
* `host_api/methods.json` — Flat `(namespace, method)` table mirroring
|
|
11
|
+
the dispatcher's `_DISPATCH` map. `no_auth=true` methods skip the
|
|
12
|
+
`host_api` ACL gate.
|
|
13
|
+
* `events/AnnaAppEvent.json` — SSE event union (the `kind` enum).
|
|
14
|
+
* `dispatcher_version.txt` — Single source-of-truth bundle version;
|
|
15
|
+
harnesses must refuse to start when their copy disagrees.
|
|
16
|
+
|
|
17
|
+
## Regenerate
|
|
18
|
+
|
|
19
|
+
```bash
|
|
20
|
+
uv run python scripts/export_app_schema.py # write
|
|
21
|
+
uv run python scripts/export_app_schema.py --check # CI gate
|
|
22
|
+
```
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
0.1.0
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$id": "https://schemas.anna.partners/anna-app-event.json",
|
|
3
|
+
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
4
|
+
"additionalProperties": true,
|
|
5
|
+
"description": "SSE envelope union for `event: data_model/AnnaAppEvent` frames. Bundle consumers should treat unknown `kind` values as forward-compatible additions and ignore them.",
|
|
6
|
+
"properties": {
|
|
7
|
+
"by_client_id": {
|
|
8
|
+
"type": [
|
|
9
|
+
"string",
|
|
10
|
+
"null"
|
|
11
|
+
]
|
|
12
|
+
},
|
|
13
|
+
"kind": {
|
|
14
|
+
"enum": [
|
|
15
|
+
"artifact_appended",
|
|
16
|
+
"chat_message_from_app",
|
|
17
|
+
"close_view",
|
|
18
|
+
"geometry_changed",
|
|
19
|
+
"open_view",
|
|
20
|
+
"ping",
|
|
21
|
+
"runtime_state_synced",
|
|
22
|
+
"status_changed",
|
|
23
|
+
"title_changed",
|
|
24
|
+
"window_focus_changed"
|
|
25
|
+
],
|
|
26
|
+
"type": "string"
|
|
27
|
+
},
|
|
28
|
+
"window_uuid": {
|
|
29
|
+
"type": "string"
|
|
30
|
+
}
|
|
31
|
+
},
|
|
32
|
+
"required": [
|
|
33
|
+
"kind"
|
|
34
|
+
],
|
|
35
|
+
"title": "AnnaAppEvent",
|
|
36
|
+
"type": "object",
|
|
37
|
+
"version": "0.1.0"
|
|
38
|
+
}
|