@absolutejs/agent-control 0.1.0 → 0.2.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 +24 -0
- package/dist/console.d.ts +63 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +146 -7
- package/dist/manifest.js +9 -1
- package/dist/manifest.json +1 -1
- package/package.json +4 -4
package/README.md
CHANGED
|
@@ -8,3 +8,27 @@ leased idempotency records so operator retries cannot change the requested input
|
|
|
8
8
|
Scopes are `agents:read`, `agents:revoke`, and `agents:restore`. Mutations require
|
|
9
9
|
an `operationId` and `reason`; PostgreSQL operations can be safely reclaimed
|
|
10
10
|
after a crashed operator process's lease expires.
|
|
11
|
+
|
|
12
|
+
`createAgentControlConsoleHandler()` adds a dependency-free authenticated
|
|
13
|
+
operator console to the same package. Its snapshot shows agent status, pending
|
|
14
|
+
approvals, durable runs and budgets, delegation scope and expiry, memory
|
|
15
|
+
metadata, and scope-specific reputation without exposing memory contents or raw
|
|
16
|
+
action inputs. Approval decisions require `agents:approve`, same-origin
|
|
17
|
+
requests, an explicit mutation-intent header, a reason, and an idempotency key.
|
|
18
|
+
|
|
19
|
+
```ts
|
|
20
|
+
const console = createAgentControlConsoleHandler({
|
|
21
|
+
authorize: verifyOperator,
|
|
22
|
+
operations: createPostgresOperationStore({ client }),
|
|
23
|
+
data: {
|
|
24
|
+
snapshot: (operator) => loadSafeOperatorSnapshot(operator),
|
|
25
|
+
decideApproval: (decision) => applyAgencyDecision(decision),
|
|
26
|
+
},
|
|
27
|
+
});
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
The HTML console uses a per-response Content Security Policy nonce, constructs
|
|
31
|
+
all remote data with DOM `textContent`, cannot be framed, stores no cache, and
|
|
32
|
+
has no third-party assets. The data adapter keeps it provider-neutral and lets
|
|
33
|
+
applications compose `@absolutejs/agency`, `agent-runtime`, `agent-memory`, and
|
|
34
|
+
`agent-reputation` without creating hard dependencies.
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import type { OperationStore, Operator } from "./index";
|
|
2
|
+
export type AgentControlSnapshot = {
|
|
3
|
+
agents: Array<{
|
|
4
|
+
description?: string;
|
|
5
|
+
id: string;
|
|
6
|
+
status: string;
|
|
7
|
+
}>;
|
|
8
|
+
approvals: Array<{
|
|
9
|
+
action: string;
|
|
10
|
+
agentId: string;
|
|
11
|
+
id: string;
|
|
12
|
+
requestedAt: string;
|
|
13
|
+
risk?: string;
|
|
14
|
+
summary: string;
|
|
15
|
+
}>;
|
|
16
|
+
delegations: Array<{
|
|
17
|
+
expiresAt?: string;
|
|
18
|
+
from: string;
|
|
19
|
+
id: string;
|
|
20
|
+
scopes: string[];
|
|
21
|
+
to: string;
|
|
22
|
+
}>;
|
|
23
|
+
memories: Array<{
|
|
24
|
+
agentId: string;
|
|
25
|
+
id: string;
|
|
26
|
+
scope: string;
|
|
27
|
+
updatedAt: string;
|
|
28
|
+
}>;
|
|
29
|
+
reputations: Array<{
|
|
30
|
+
confidence: number;
|
|
31
|
+
scope: string;
|
|
32
|
+
score: number;
|
|
33
|
+
subject: string;
|
|
34
|
+
}>;
|
|
35
|
+
runs: Array<{
|
|
36
|
+
agentId: string;
|
|
37
|
+
budget?: {
|
|
38
|
+
limit: number;
|
|
39
|
+
spent: number;
|
|
40
|
+
unit: string;
|
|
41
|
+
};
|
|
42
|
+
id: string;
|
|
43
|
+
startedAt: string;
|
|
44
|
+
status: string;
|
|
45
|
+
}>;
|
|
46
|
+
};
|
|
47
|
+
export type AgentControlConsoleData = {
|
|
48
|
+
decideApproval: (input: {
|
|
49
|
+
approvalId: string;
|
|
50
|
+
decision: "approve" | "deny";
|
|
51
|
+
operatorId: string;
|
|
52
|
+
reason: string;
|
|
53
|
+
}) => Promise<unknown>;
|
|
54
|
+
snapshot: (operator: Operator) => Promise<AgentControlSnapshot>;
|
|
55
|
+
};
|
|
56
|
+
export declare const sanitizeAgentControlSnapshot: (source: AgentControlSnapshot) => AgentControlSnapshot;
|
|
57
|
+
export declare const createAgentControlConsoleHandler: (options: {
|
|
58
|
+
authorize: (request: Request) => Promise<Operator | undefined> | Operator | undefined;
|
|
59
|
+
basePath?: string;
|
|
60
|
+
data: AgentControlConsoleData;
|
|
61
|
+
now?: () => number;
|
|
62
|
+
operations: OperationStore;
|
|
63
|
+
}) => (request: Request) => Promise<Response | null>;
|
package/dist/index.d.ts
CHANGED
package/dist/index.js
CHANGED
|
@@ -1,29 +1,166 @@
|
|
|
1
1
|
// @bun
|
|
2
|
+
// src/console.ts
|
|
3
|
+
var hash = async (value) => [
|
|
4
|
+
...new Uint8Array(await crypto.subtle.digest("SHA-256", new TextEncoder().encode(JSON.stringify(value))))
|
|
5
|
+
].map((byte) => byte.toString(16).padStart(2, "0")).join("");
|
|
6
|
+
var bounded = (value, field) => {
|
|
7
|
+
if (typeof value !== "string" || value.length > 2000)
|
|
8
|
+
throw new Error(`Invalid agent control snapshot ${field}`);
|
|
9
|
+
return value;
|
|
10
|
+
};
|
|
11
|
+
var boundedNumber = (value, field) => {
|
|
12
|
+
if (typeof value !== "number" || !Number.isFinite(value))
|
|
13
|
+
throw new Error(`Invalid agent control snapshot ${field}`);
|
|
14
|
+
return value;
|
|
15
|
+
};
|
|
16
|
+
var rows = (value, map) => {
|
|
17
|
+
if (!Array.isArray(value) || value.length > 1000)
|
|
18
|
+
throw new Error("Agent control snapshot collection is too large");
|
|
19
|
+
return value.map(map);
|
|
20
|
+
};
|
|
21
|
+
var sanitizeAgentControlSnapshot = (source) => ({
|
|
22
|
+
agents: rows(source.agents, (item) => ({
|
|
23
|
+
...item.description === undefined ? {} : { description: bounded(item.description, "agent.description") },
|
|
24
|
+
id: bounded(item.id, "agent.id"),
|
|
25
|
+
status: bounded(item.status, "agent.status")
|
|
26
|
+
})),
|
|
27
|
+
approvals: rows(source.approvals, (item) => ({
|
|
28
|
+
action: bounded(item.action, "approval.action"),
|
|
29
|
+
agentId: bounded(item.agentId, "approval.agentId"),
|
|
30
|
+
id: bounded(item.id, "approval.id"),
|
|
31
|
+
requestedAt: bounded(item.requestedAt, "approval.requestedAt"),
|
|
32
|
+
...item.risk === undefined ? {} : { risk: bounded(item.risk, "approval.risk") },
|
|
33
|
+
summary: bounded(item.summary, "approval.summary")
|
|
34
|
+
})),
|
|
35
|
+
delegations: rows(source.delegations, (item) => ({
|
|
36
|
+
...item.expiresAt === undefined ? {} : { expiresAt: bounded(item.expiresAt, "delegation.expiresAt") },
|
|
37
|
+
from: bounded(item.from, "delegation.from"),
|
|
38
|
+
id: bounded(item.id, "delegation.id"),
|
|
39
|
+
scopes: rows(item.scopes, (scope) => bounded(scope, "delegation.scope")),
|
|
40
|
+
to: bounded(item.to, "delegation.to")
|
|
41
|
+
})),
|
|
42
|
+
memories: rows(source.memories, (item) => ({
|
|
43
|
+
agentId: bounded(item.agentId, "memory.agentId"),
|
|
44
|
+
id: bounded(item.id, "memory.id"),
|
|
45
|
+
scope: bounded(item.scope, "memory.scope"),
|
|
46
|
+
updatedAt: bounded(item.updatedAt, "memory.updatedAt")
|
|
47
|
+
})),
|
|
48
|
+
reputations: rows(source.reputations, (item) => ({
|
|
49
|
+
confidence: boundedNumber(item.confidence, "reputation.confidence"),
|
|
50
|
+
scope: bounded(item.scope, "reputation.scope"),
|
|
51
|
+
score: boundedNumber(item.score, "reputation.score"),
|
|
52
|
+
subject: bounded(item.subject, "reputation.subject")
|
|
53
|
+
})),
|
|
54
|
+
runs: rows(source.runs, (item) => ({
|
|
55
|
+
agentId: bounded(item.agentId, "run.agentId"),
|
|
56
|
+
...item.budget === undefined ? {} : {
|
|
57
|
+
budget: {
|
|
58
|
+
limit: boundedNumber(item.budget.limit, "run.budget.limit"),
|
|
59
|
+
spent: boundedNumber(item.budget.spent, "run.budget.spent"),
|
|
60
|
+
unit: bounded(item.budget.unit, "run.budget.unit")
|
|
61
|
+
}
|
|
62
|
+
},
|
|
63
|
+
id: bounded(item.id, "run.id"),
|
|
64
|
+
startedAt: bounded(item.startedAt, "run.startedAt"),
|
|
65
|
+
status: bounded(item.status, "run.status")
|
|
66
|
+
}))
|
|
67
|
+
});
|
|
68
|
+
var page = (nonce) => `<!doctype html>
|
|
69
|
+
<html lang="en"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1">
|
|
70
|
+
<title>AbsoluteJS Agent Control</title><style nonce="${nonce}">
|
|
71
|
+
:root{color-scheme:dark;font:15px/1.45 system-ui,sans-serif;background:#09090b;color:#fafafa}body{margin:0}header{padding:1.2rem 1.5rem;border-bottom:1px solid #27272a;display:flex;align-items:center;justify-content:space-between}main{padding:1.5rem;display:grid;gap:1.25rem}.status{color:#a1a1aa}.error{color:#fca5a5}section{background:#18181b;border:1px solid #27272a;border-radius:.75rem;overflow:auto}h2{font-size:1rem;margin:0;padding:1rem}table{border-collapse:collapse;width:100%;min-width:42rem}th,td{text-align:left;padding:.7rem 1rem;border-top:1px solid #27272a;vertical-align:top}th{color:#a1a1aa;font-size:.8rem;text-transform:uppercase}button{background:#27272a;color:#fff;border:1px solid #3f3f46;border-radius:.4rem;padding:.4rem .65rem;margin:.15rem;cursor:pointer}button.approve{background:#14532d}button.deny{background:#7f1d1d}code{font-size:.8rem} @media(max-width:700px){main{padding:.75rem}header{padding:1rem}}
|
|
72
|
+
</style></head><body><header><strong>AbsoluteJS Agent Control</strong><span id="status" class="status">Loading\u2026</span></header><main id="content"></main>
|
|
73
|
+
<script nonce="${nonce}">(()=>{const base=location.pathname.replace(//$/,"");const root=document.querySelector("#content"),status=document.querySelector("#status");const columns={agents:["id","status","description"],approvals:["id","agentId","action","summary","risk","requestedAt"],runs:["id","agentId","status","startedAt","budget"],delegations:["id","from","to","scopes","expiresAt"],memories:["id","agentId","scope","updatedAt"],reputations:["subject","scope","score","confidence"]};const text=v=>v==null?"":typeof v==="object"?JSON.stringify(v):String(v);function render(data){root.replaceChildren();for(const [name,fields] of Object.entries(columns)){const section=document.createElement("section"),heading=document.createElement("h2"),table=document.createElement("table"),head=document.createElement("tr");heading.textContent=name[0].toUpperCase()+name.slice(1);for(const field of fields){const th=document.createElement("th");th.textContent=field;head.append(th)}if(name==="approvals"){const th=document.createElement("th");th.textContent="Decision";head.append(th)}table.append(head);for(const item of data[name]??[]){const row=document.createElement("tr");for(const field of fields){const td=document.createElement("td");td.textContent=text(item[field]);row.append(td)}if(name==="approvals"){const td=document.createElement("td");for(const decision of ["approve","deny"]){const button=document.createElement("button");button.className=decision;button.textContent=decision;button.addEventListener("click",()=>decide(item.id,decision));td.append(button)}row.append(td)}table.append(row)}section.append(heading,table);root.append(section)}}async function load(){try{const response=await fetch(base+"/api/snapshot",{headers:{accept:"application/json"}});if(!response.ok)throw new Error("Snapshot failed: "+response.status);render(await response.json());status.textContent="Live";status.className="status"}catch(error){status.textContent=error.message;status.className="error"}}async function decide(id,decision){const reason=prompt("Reason for "+decision+":");if(!reason)return;status.textContent="Applying\u2026";const response=await fetch(base+"/api/approvals/"+encodeURIComponent(id)+"/"+decision,{method:"POST",headers:{"content-type":"application/json","idempotency-key":crypto.randomUUID(),"x-agent-control-intent":"mutate"},body:JSON.stringify({reason})});if(!response.ok){status.textContent="Decision failed: "+response.status;status.className="error";return}await load()}load()})();</script></body></html>`;
|
|
74
|
+
var createAgentControlConsoleHandler = (options) => {
|
|
75
|
+
const basePath = (options.basePath ?? "/agent-control").replace(/\/$/u, "");
|
|
76
|
+
const now = options.now ?? Date.now;
|
|
77
|
+
return async (request) => {
|
|
78
|
+
const url = new URL(request.url);
|
|
79
|
+
if (url.pathname !== basePath && !url.pathname.startsWith(`${basePath}/`))
|
|
80
|
+
return null;
|
|
81
|
+
const operator = await options.authorize(request);
|
|
82
|
+
if (!operator)
|
|
83
|
+
return new Response("Unauthorized", { status: 401 });
|
|
84
|
+
const required = request.method === "GET" ? "agents:read" : "agents:approve";
|
|
85
|
+
if (!operator.scopes.includes(required))
|
|
86
|
+
return new Response("Forbidden", { status: 403 });
|
|
87
|
+
if (request.method === "GET" && (url.pathname === basePath || url.pathname === `${basePath}/`)) {
|
|
88
|
+
const nonce = crypto.randomUUID().replaceAll("-", "");
|
|
89
|
+
return new Response(page(nonce), {
|
|
90
|
+
headers: {
|
|
91
|
+
"cache-control": "no-store",
|
|
92
|
+
"content-security-policy": `default-src 'none'; connect-src 'self'; img-src 'self'; style-src 'nonce-${nonce}'; script-src 'nonce-${nonce}'; base-uri 'none'; form-action 'self'; frame-ancestors 'none'`,
|
|
93
|
+
"content-type": "text/html; charset=utf-8",
|
|
94
|
+
"referrer-policy": "no-referrer",
|
|
95
|
+
"x-content-type-options": "nosniff"
|
|
96
|
+
}
|
|
97
|
+
});
|
|
98
|
+
}
|
|
99
|
+
if (request.method === "GET" && url.pathname === `${basePath}/api/snapshot`) {
|
|
100
|
+
return Response.json(sanitizeAgentControlSnapshot(await options.data.snapshot(operator)), {
|
|
101
|
+
headers: { "cache-control": "no-store" }
|
|
102
|
+
});
|
|
103
|
+
}
|
|
104
|
+
const match = new RegExp(`^${basePath.replace(/[.*+?^${}()|[\]\\]/gu, "\\$&")}/api/approvals/([^/]+)/(approve|deny)$`, "u").exec(url.pathname);
|
|
105
|
+
if (!match?.[1] || !match[2] || request.method !== "POST")
|
|
106
|
+
return new Response(null, { status: 405 });
|
|
107
|
+
if (request.headers.get("origin") !== url.origin || request.headers.get("x-agent-control-intent") !== "mutate")
|
|
108
|
+
return new Response("Cross-site mutation denied", { status: 403 });
|
|
109
|
+
const operationId = request.headers.get("idempotency-key");
|
|
110
|
+
const body = await request.json().catch(() => {
|
|
111
|
+
return;
|
|
112
|
+
});
|
|
113
|
+
if (!operationId || !body?.reason || body.reason.length > 1000)
|
|
114
|
+
return Response.json({ error: "Idempotency-Key and a bounded reason are required" }, { status: 400 });
|
|
115
|
+
const input = {
|
|
116
|
+
approvalId: decodeURIComponent(match[1]),
|
|
117
|
+
decision: match[2],
|
|
118
|
+
operatorId: operator.id,
|
|
119
|
+
reason: body.reason
|
|
120
|
+
};
|
|
121
|
+
const digest = await hash(input);
|
|
122
|
+
let claim;
|
|
123
|
+
try {
|
|
124
|
+
claim = await options.operations.claim(operationId, digest, now(), 30000);
|
|
125
|
+
} catch (error) {
|
|
126
|
+
return Response.json({ error: error instanceof Error ? error.message : "conflict" }, { status: 409 });
|
|
127
|
+
}
|
|
128
|
+
if (claim.response !== undefined)
|
|
129
|
+
return Response.json(claim.response);
|
|
130
|
+
if (!claim.acquired)
|
|
131
|
+
return Response.json({ error: "operation in progress" }, { status: 409, headers: { "retry-after": "5" } });
|
|
132
|
+
const response = await options.data.decideApproval(input);
|
|
133
|
+
if (!await options.operations.complete(operationId, digest, response))
|
|
134
|
+
throw new Error("Approval operation completion lost");
|
|
135
|
+
return Response.json(response);
|
|
136
|
+
};
|
|
137
|
+
};
|
|
138
|
+
|
|
2
139
|
// src/index.ts
|
|
3
140
|
var createMemoryOperationStore = () => {
|
|
4
|
-
const
|
|
141
|
+
const rows2 = new Map;
|
|
5
142
|
return {
|
|
6
143
|
claim: async (key, digest, now, leaseMs) => {
|
|
7
|
-
const row =
|
|
144
|
+
const row = rows2.get(key);
|
|
8
145
|
if (row?.digest !== undefined && row.digest !== digest)
|
|
9
146
|
throw new Error("Idempotency key reused with different input");
|
|
10
147
|
if (row?.response !== undefined)
|
|
11
148
|
return { acquired: false, response: structuredClone(row.response) };
|
|
12
149
|
if (row && row.leaseUntil > now)
|
|
13
150
|
return { acquired: false };
|
|
14
|
-
|
|
151
|
+
rows2.set(key, { digest, key, leaseUntil: now + leaseMs });
|
|
15
152
|
return { acquired: true };
|
|
16
153
|
},
|
|
17
154
|
complete: async (key, digest, response) => {
|
|
18
|
-
const row =
|
|
155
|
+
const row = rows2.get(key);
|
|
19
156
|
if (!row || row.digest !== digest)
|
|
20
157
|
return false;
|
|
21
|
-
|
|
158
|
+
rows2.set(key, { ...row, response: structuredClone(response) });
|
|
22
159
|
return true;
|
|
23
160
|
}
|
|
24
161
|
};
|
|
25
162
|
};
|
|
26
|
-
var
|
|
163
|
+
var hash2 = async (value) => [
|
|
27
164
|
...new Uint8Array(await crypto.subtle.digest("SHA-256", new TextEncoder().encode(JSON.stringify(value))))
|
|
28
165
|
].map((byte) => byte.toString(16).padStart(2, "0")).join("");
|
|
29
166
|
var createAgentControlHandler = ({
|
|
@@ -54,7 +191,7 @@ var createAgentControlHandler = ({
|
|
|
54
191
|
});
|
|
55
192
|
if (!body?.operationId || !body.reason)
|
|
56
193
|
return Response.json({ error: "operationId and reason are required" }, { status: 400 });
|
|
57
|
-
const digest = await
|
|
194
|
+
const digest = await hash2({ action, agentId, reason: body.reason });
|
|
58
195
|
let claim;
|
|
59
196
|
try {
|
|
60
197
|
claim = await operations.claim(body.operationId, digest, now(), 30000);
|
|
@@ -107,8 +244,10 @@ var createPostgresOperationStore = ({
|
|
|
107
244
|
};
|
|
108
245
|
};
|
|
109
246
|
export {
|
|
247
|
+
sanitizeAgentControlSnapshot,
|
|
110
248
|
createPostgresOperationStore,
|
|
111
249
|
createMemoryOperationStore,
|
|
112
250
|
createAgentControlHandler,
|
|
251
|
+
createAgentControlConsoleHandler,
|
|
113
252
|
agentControlPostgresSchemaSql
|
|
114
253
|
};
|
package/dist/manifest.js
CHANGED
|
@@ -5901,6 +5901,14 @@ var serializedTool = Type.Object({
|
|
|
5901
5901
|
});
|
|
5902
5902
|
var manifestSchema = Type.Object({
|
|
5903
5903
|
contract: Type.Union([Type.Literal(1), Type.Literal(2)]),
|
|
5904
|
+
discovery: Type.Optional(Type.Object({
|
|
5905
|
+
audiences: Type.Optional(Type.Array(Type.String({ minLength: 1 }))),
|
|
5906
|
+
certificationUrl: Type.Optional(Type.String()),
|
|
5907
|
+
intents: Type.Optional(Type.Array(Type.String({ minLength: 1 }))),
|
|
5908
|
+
keywords: Type.Optional(Type.Array(Type.String({ minLength: 1 }))),
|
|
5909
|
+
protocols: Type.Optional(Type.Array(Type.String({ minLength: 1 }))),
|
|
5910
|
+
url: Type.Optional(Type.String())
|
|
5911
|
+
})),
|
|
5904
5912
|
identity: Type.Object({
|
|
5905
5913
|
accent: Type.Optional(Type.String({ pattern: "^#[0-9a-fA-F]{3,8}$" })),
|
|
5906
5914
|
category: Type.String({ minLength: 1 }),
|
|
@@ -5935,7 +5943,7 @@ var manifest = defineManifest()({
|
|
|
5935
5943
|
identity: {
|
|
5936
5944
|
accent: "#dc2626",
|
|
5937
5945
|
category: "security",
|
|
5938
|
-
description: "Authenticated operator API for agent inventory,
|
|
5946
|
+
description: "Authenticated operator API and CSP-hardened console for agent inventory, approvals, runs, budgets, delegations, memory metadata, reputation, durable kill switches, and leased idempotent operations.",
|
|
5939
5947
|
docsUrl: "https://github.com/absolutejs/agent-control",
|
|
5940
5948
|
name: "@absolutejs/agent-control",
|
|
5941
5949
|
tagline: "See and stop every capability an agent holds."
|
package/dist/manifest.json
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
"identity": {
|
|
4
4
|
"accent": "#dc2626",
|
|
5
5
|
"category": "security",
|
|
6
|
-
"description": "Authenticated operator API for agent inventory,
|
|
6
|
+
"description": "Authenticated operator API and CSP-hardened console for agent inventory, approvals, runs, budgets, delegations, memory metadata, reputation, durable kill switches, and leased idempotent operations.",
|
|
7
7
|
"docsUrl": "https://github.com/absolutejs/agent-control",
|
|
8
8
|
"name": "@absolutejs/agent-control",
|
|
9
9
|
"tagline": "See and stop every capability an agent holds."
|
package/package.json
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@absolutejs/agent-control",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"description": "Authenticated operator API
|
|
3
|
+
"version": "0.2.0",
|
|
4
|
+
"description": "Authenticated AI agent operator API and console for approvals, runs, delegations, memory, reputation, and kill switches.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
7
7
|
"repository": {
|
|
8
8
|
"type": "git",
|
|
9
|
-
"url": "https://github.com/absolutejs/agent-control.git"
|
|
9
|
+
"url": "git+https://github.com/absolutejs/agent-control.git"
|
|
10
10
|
},
|
|
11
11
|
"publishConfig": {
|
|
12
12
|
"access": "public"
|
|
@@ -41,7 +41,7 @@
|
|
|
41
41
|
},
|
|
42
42
|
"dependencies": {
|
|
43
43
|
"@absolutejs/agency": "^0.4.0",
|
|
44
|
-
"@absolutejs/manifest": "^0.
|
|
44
|
+
"@absolutejs/manifest": "^0.3.0",
|
|
45
45
|
"@sinclair/typebox": "^0.34.0"
|
|
46
46
|
},
|
|
47
47
|
"devDependencies": {
|