@mcpolyglot/cli 0.2.0 → 0.3.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 +27 -0
- package/dist/.tsbuildinfo +1 -1
- package/dist/__tests__/agents.test.d.ts +2 -0
- package/dist/__tests__/agents.test.d.ts.map +1 -0
- package/dist/__tests__/agents.test.js +172 -0
- package/dist/__tests__/agents.test.js.map +1 -0
- package/dist/__tests__/examples.test.d.ts +2 -0
- package/dist/__tests__/examples.test.d.ts.map +1 -0
- package/dist/__tests__/examples.test.js +140 -0
- package/dist/__tests__/examples.test.js.map +1 -0
- package/dist/__tests__/init-doctor.test.d.ts +2 -0
- package/dist/__tests__/init-doctor.test.d.ts.map +1 -0
- package/dist/__tests__/init-doctor.test.js +79 -0
- package/dist/__tests__/init-doctor.test.js.map +1 -0
- package/dist/bin.js +16 -3
- package/dist/bin.js.map +1 -1
- package/dist/commands/doctor-agents.d.ts +5 -0
- package/dist/commands/doctor-agents.d.ts.map +1 -0
- package/dist/commands/doctor-agents.js +33 -0
- package/dist/commands/doctor-agents.js.map +1 -0
- package/dist/commands/doctor.d.ts +10 -0
- package/dist/commands/doctor.d.ts.map +1 -1
- package/dist/commands/doctor.js +79 -1
- package/dist/commands/doctor.js.map +1 -1
- package/dist/commands/init.d.ts +16 -0
- package/dist/commands/init.d.ts.map +1 -1
- package/dist/commands/init.js +85 -5
- package/dist/commands/init.js.map +1 -1
- package/dist/commands/serve.d.ts.map +1 -1
- package/dist/commands/serve.js +34 -12
- package/dist/commands/serve.js.map +1 -1
- package/dist/commands/token.d.ts +5 -0
- package/dist/commands/token.d.ts.map +1 -0
- package/dist/commands/token.js +23 -0
- package/dist/commands/token.js.map +1 -0
- package/dist/factory.d.ts +6 -2
- package/dist/factory.d.ts.map +1 -1
- package/dist/factory.js +84 -12
- package/dist/factory.js.map +1 -1
- package/dist/ui.d.ts.map +1 -1
- package/dist/ui.js +0 -2
- package/dist/ui.js.map +1 -1
- package/package.json +8 -6
|
@@ -0,0 +1,172 @@
|
|
|
1
|
+
import { mkdtempSync, readFileSync, rmSync } from 'node:fs';
|
|
2
|
+
import { createRequire } from 'node:module';
|
|
3
|
+
import { tmpdir } from 'node:os';
|
|
4
|
+
import { join } from 'node:path';
|
|
5
|
+
import { afterAll, afterEach, beforeAll, describe, expect, it } from 'vitest';
|
|
6
|
+
import { ConfigSchema } from '@mcpolyglot/config';
|
|
7
|
+
import { hashToken, StreamableHttpTransport } from '@mcpolyglot/core/transports/streamable-http';
|
|
8
|
+
import { buildServerFromConfig } from '../factory.js';
|
|
9
|
+
// Real HTTP transport + real SQLite through the CLI factory: two agents, one source.
|
|
10
|
+
// better-sqlite3 is connector-sql's dependency; borrow it from there to seed the file.
|
|
11
|
+
const Database = createRequire(new URL('../../../connector-sql/package.json', import.meta.url))('better-sqlite3');
|
|
12
|
+
const silent = { debug() { }, info() { }, warn() { }, error() { } };
|
|
13
|
+
let dir;
|
|
14
|
+
let dbPath;
|
|
15
|
+
beforeAll(() => {
|
|
16
|
+
dir = mkdtempSync(join(tmpdir(), 'mcpolyglot-agents-'));
|
|
17
|
+
dbPath = join(dir, 'app.db');
|
|
18
|
+
const db = new Database(dbPath);
|
|
19
|
+
db.exec(`
|
|
20
|
+
CREATE TABLE users (id INTEGER PRIMARY KEY, email TEXT);
|
|
21
|
+
CREATE TABLE orders (id INTEGER PRIMARY KEY, total INTEGER);
|
|
22
|
+
INSERT INTO users (email) VALUES ('a@x.io');
|
|
23
|
+
INSERT INTO orders (total) VALUES (42);
|
|
24
|
+
`);
|
|
25
|
+
db.close();
|
|
26
|
+
});
|
|
27
|
+
afterAll(() => rmSync(dir, { recursive: true, force: true }));
|
|
28
|
+
let server;
|
|
29
|
+
afterEach(async () => {
|
|
30
|
+
await server?.stop();
|
|
31
|
+
server = undefined;
|
|
32
|
+
});
|
|
33
|
+
async function boot(extra) {
|
|
34
|
+
const auditPath = join(dir, `audit-${Math.random().toString(36).slice(2)}.jsonl`);
|
|
35
|
+
const cfg = ConfigSchema.parse({
|
|
36
|
+
sources: [
|
|
37
|
+
{
|
|
38
|
+
id: 'app',
|
|
39
|
+
kind: 'sqlite',
|
|
40
|
+
url: dbPath,
|
|
41
|
+
scopes: ['schema:read', 'tables:read', 'query:raw'],
|
|
42
|
+
},
|
|
43
|
+
],
|
|
44
|
+
audit: { console: false, path: auditPath },
|
|
45
|
+
...extra,
|
|
46
|
+
});
|
|
47
|
+
const built = await buildServerFromConfig(cfg, silent);
|
|
48
|
+
server = built.server;
|
|
49
|
+
const transport = new StreamableHttpTransport({
|
|
50
|
+
host: '127.0.0.1',
|
|
51
|
+
port: 0,
|
|
52
|
+
auth: cfg.agents ? { kind: 'agents', agents: cfg.agents } : { kind: 'bearer', token: 'shared' },
|
|
53
|
+
logger: silent,
|
|
54
|
+
});
|
|
55
|
+
await server.start(transport);
|
|
56
|
+
const port = transport.httpServer.address().port;
|
|
57
|
+
const rpc = async (token, method, params = {}, headers = {}) => {
|
|
58
|
+
const res = await fetch(`http://127.0.0.1:${port}/mcp`, {
|
|
59
|
+
method: 'POST',
|
|
60
|
+
headers: {
|
|
61
|
+
authorization: `Bearer ${token}`,
|
|
62
|
+
'content-type': 'application/json',
|
|
63
|
+
accept: 'application/json, text/event-stream',
|
|
64
|
+
...headers,
|
|
65
|
+
},
|
|
66
|
+
body: JSON.stringify({ jsonrpc: '2.0', id: 1, method, params }),
|
|
67
|
+
});
|
|
68
|
+
const body = await res.text();
|
|
69
|
+
if (res.status !== 200)
|
|
70
|
+
return { status: res.status, body };
|
|
71
|
+
const data = body.split('\n').find((l) => l.startsWith('data: '));
|
|
72
|
+
return { status: res.status, body: JSON.parse(data ? data.slice(6) : body) };
|
|
73
|
+
};
|
|
74
|
+
const audit = () => readFileSync(auditPath, 'utf8')
|
|
75
|
+
.trim()
|
|
76
|
+
.split('\n')
|
|
77
|
+
.map((l) => JSON.parse(l));
|
|
78
|
+
return { rpc, audit };
|
|
79
|
+
}
|
|
80
|
+
const toolNames = (r) => r.body.result.tools.map((t) => t.name).sort();
|
|
81
|
+
const text = (r) => r.body.result.content.map((c) => c.text).join('');
|
|
82
|
+
describe('multi-agent HTTP', () => {
|
|
83
|
+
const agents = [
|
|
84
|
+
{
|
|
85
|
+
id: 'analyst',
|
|
86
|
+
tokens: [{ hash: hashToken('tok-analyst') }],
|
|
87
|
+
sources: { app: { policy: { tables: { users: 'none' } } } },
|
|
88
|
+
},
|
|
89
|
+
{
|
|
90
|
+
id: 'browser',
|
|
91
|
+
tokens: [{ hash: hashToken('tok-old'), revoked: true }, { hash: hashToken('tok-browser') }],
|
|
92
|
+
scopes: ['schema:read'],
|
|
93
|
+
sources: { app: {} },
|
|
94
|
+
},
|
|
95
|
+
];
|
|
96
|
+
it('filters tools per agent scopes and applies each agent its own policy', async () => {
|
|
97
|
+
const { rpc } = await boot({ agents });
|
|
98
|
+
expect(toolNames(await rpc('tok-analyst', 'tools/list'))).toEqual([
|
|
99
|
+
'app.describe_table',
|
|
100
|
+
'app.list_tables',
|
|
101
|
+
'app.query',
|
|
102
|
+
]);
|
|
103
|
+
// `browser` lacks query:raw/tables:read, so `app.query` doesn't exist for it.
|
|
104
|
+
expect(toolNames(await rpc('tok-browser', 'tools/list'))).toEqual([
|
|
105
|
+
'app.describe_table',
|
|
106
|
+
'app.list_tables',
|
|
107
|
+
]);
|
|
108
|
+
const call = { name: 'app.query', arguments: { sql: 'SELECT email FROM users' } };
|
|
109
|
+
expect((await rpc('tok-browser', 'tools/call', call)).body.error.message).toMatch(/Unknown tool/);
|
|
110
|
+
// Same table, same DB: analyst's policy hides `users`, the source default doesn't.
|
|
111
|
+
const denied = await rpc('tok-analyst', 'tools/call', call);
|
|
112
|
+
expect(denied.body.result.isError).toBe(true);
|
|
113
|
+
expect(text(denied)).toMatch(/forbidden\.policy/);
|
|
114
|
+
const listA = text(await rpc('tok-analyst', 'tools/call', { name: 'app.list_tables', arguments: {} }));
|
|
115
|
+
const listB = text(await rpc('tok-browser', 'tools/call', { name: 'app.list_tables', arguments: {} }));
|
|
116
|
+
expect(listA).not.toMatch(/"users"/);
|
|
117
|
+
expect(listB).toMatch(/"users"/);
|
|
118
|
+
const orders = await rpc('tok-analyst', 'tools/call', {
|
|
119
|
+
name: 'app.query',
|
|
120
|
+
arguments: { sql: 'SELECT total FROM orders' },
|
|
121
|
+
});
|
|
122
|
+
expect(text(orders)).toMatch(/42/);
|
|
123
|
+
});
|
|
124
|
+
it("an agent's policy can narrow the source policy but never widen it", async () => {
|
|
125
|
+
const { rpc } = await boot({
|
|
126
|
+
sources: [
|
|
127
|
+
{
|
|
128
|
+
id: 'app',
|
|
129
|
+
kind: 'sqlite',
|
|
130
|
+
url: dbPath,
|
|
131
|
+
scopes: ['schema:read', 'tables:read', 'query:raw'],
|
|
132
|
+
policy: { tables: { users: 'none' } },
|
|
133
|
+
},
|
|
134
|
+
],
|
|
135
|
+
agents: [
|
|
136
|
+
{
|
|
137
|
+
id: 'sneaky',
|
|
138
|
+
tokens: [{ hash: hashToken('tok-sneaky') }],
|
|
139
|
+
sources: { app: { policy: { tables: { users: 'write' }, defaultAccess: 'read' } } },
|
|
140
|
+
},
|
|
141
|
+
],
|
|
142
|
+
});
|
|
143
|
+
const r = await rpc('tok-sneaky', 'tools/call', {
|
|
144
|
+
name: 'app.query',
|
|
145
|
+
arguments: { sql: 'SELECT email FROM users' },
|
|
146
|
+
});
|
|
147
|
+
expect(text(r)).toMatch(/forbidden\.policy/);
|
|
148
|
+
// Widening to `write` must not register the write tool either.
|
|
149
|
+
expect(toolNames(await rpc('tok-sneaky', 'tools/list'))).not.toContain('app.execute');
|
|
150
|
+
});
|
|
151
|
+
it('rejects revoked and unknown tokens with 401', async () => {
|
|
152
|
+
const { rpc } = await boot({ agents });
|
|
153
|
+
expect((await rpc('tok-old', 'tools/list')).status).toBe(401);
|
|
154
|
+
expect((await rpc('shared', 'tools/list')).status).toBe(401);
|
|
155
|
+
});
|
|
156
|
+
it('audit agentId comes from the token, not the header', async () => {
|
|
157
|
+
const { rpc, audit } = await boot({ agents });
|
|
158
|
+
await rpc('tok-browser', 'tools/call', { name: 'app.list_tables', arguments: {} }, { 'x-mcpolyglot-agent': 'analyst' });
|
|
159
|
+
const [entry] = audit();
|
|
160
|
+
expect(entry.agentId).toBe('browser');
|
|
161
|
+
expect(entry.scopes).toEqual(['schema:read']);
|
|
162
|
+
expect(JSON.stringify(audit())).not.toMatch(/tok-/);
|
|
163
|
+
});
|
|
164
|
+
it('without `agents`: shared token, all tools, header-labelled audit (unchanged)', async () => {
|
|
165
|
+
const { rpc, audit } = await boot({});
|
|
166
|
+
expect(toolNames(await rpc('shared', 'tools/list'))).toHaveLength(3);
|
|
167
|
+
const r = await rpc('shared', 'tools/call', { name: 'app.query', arguments: { sql: 'SELECT email FROM users' } }, { 'x-mcpolyglot-agent': 'whoever' });
|
|
168
|
+
expect(text(r)).toMatch(/"rowCount": 1/);
|
|
169
|
+
expect(audit()[0].agentId).toBe('whoever');
|
|
170
|
+
});
|
|
171
|
+
});
|
|
172
|
+
//# sourceMappingURL=agents.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"agents.test.js","sourceRoot":"","sources":["../../src/__tests__/agents.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"examples.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/examples.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
import { mkdtempSync, readFileSync, rmSync } from 'node:fs';
|
|
2
|
+
import { createRequire } from 'node:module';
|
|
3
|
+
import { tmpdir } from 'node:os';
|
|
4
|
+
import { join } from 'node:path';
|
|
5
|
+
import { afterAll, afterEach, describe, expect, it } from 'vitest';
|
|
6
|
+
import { ConfigSchema } from '@mcpolyglot/config';
|
|
7
|
+
import { StreamableHttpTransport } from '@mcpolyglot/core/transports/streamable-http';
|
|
8
|
+
import { buildServerFromConfig } from '../factory.js';
|
|
9
|
+
// Keeps examples/*/README.md honest: every row of each README's table is exercised here
|
|
10
|
+
// against that example's own config and seed.sql, over the real HTTP transport.
|
|
11
|
+
const Database = createRequire(new URL('../../../connector-sql/package.json', import.meta.url))('better-sqlite3');
|
|
12
|
+
const EXAMPLES = new URL('../../../../examples/', import.meta.url);
|
|
13
|
+
const silent = { debug() { }, info() { }, warn() { }, error() { } };
|
|
14
|
+
const dir = mkdtempSync(join(tmpdir(), 'mcpolyglot-examples-'));
|
|
15
|
+
afterAll(() => rmSync(dir, { recursive: true, force: true }));
|
|
16
|
+
let server;
|
|
17
|
+
afterEach(async () => {
|
|
18
|
+
await server?.stop();
|
|
19
|
+
server = undefined;
|
|
20
|
+
});
|
|
21
|
+
async function boot(example) {
|
|
22
|
+
const dbPath = join(dir, `${example}.db`);
|
|
23
|
+
const seed = new Database(dbPath);
|
|
24
|
+
seed.exec(readFileSync(new URL(`${example}/seed.sql`, EXAMPLES), 'utf8'));
|
|
25
|
+
seed.close();
|
|
26
|
+
const raw = JSON.parse(readFileSync(new URL(`${example}/mcpolyglot.config.json`, EXAMPLES), 'utf8'));
|
|
27
|
+
raw.sources[0].url = dbPath;
|
|
28
|
+
raw.audit = { console: false, path: join(dir, `${example}.audit.jsonl`) };
|
|
29
|
+
const cfg = ConfigSchema.parse(raw);
|
|
30
|
+
const built = await buildServerFromConfig(cfg, silent);
|
|
31
|
+
server = built.server;
|
|
32
|
+
const transport = new StreamableHttpTransport({
|
|
33
|
+
host: '127.0.0.1',
|
|
34
|
+
port: 0,
|
|
35
|
+
auth: cfg.agents ? { kind: 'agents', agents: cfg.agents } : { kind: 'bearer', token: 'shared' },
|
|
36
|
+
logger: silent,
|
|
37
|
+
});
|
|
38
|
+
await server.start(transport);
|
|
39
|
+
const port = transport.httpServer.address().port;
|
|
40
|
+
const rpc = async (token, method, params = {}) => {
|
|
41
|
+
const res = await fetch(`http://127.0.0.1:${port}/mcp`, {
|
|
42
|
+
method: 'POST',
|
|
43
|
+
headers: {
|
|
44
|
+
authorization: `Bearer ${token}`,
|
|
45
|
+
'content-type': 'application/json',
|
|
46
|
+
accept: 'application/json, text/event-stream',
|
|
47
|
+
},
|
|
48
|
+
body: JSON.stringify({ jsonrpc: '2.0', id: 1, method, params }),
|
|
49
|
+
});
|
|
50
|
+
const body = await res.text();
|
|
51
|
+
if (res.status !== 200)
|
|
52
|
+
return { status: res.status, text: body, tools: [] };
|
|
53
|
+
const line = body.split('\n').find((l) => l.startsWith('data: '));
|
|
54
|
+
const msg = JSON.parse(line ? line.slice(6) : body);
|
|
55
|
+
return {
|
|
56
|
+
status: res.status,
|
|
57
|
+
text: msg.error
|
|
58
|
+
? `rpc-error: ${msg.error.message}`
|
|
59
|
+
: (msg.result.content ?? []).map((c) => c.text).join(''),
|
|
60
|
+
isError: Boolean(msg.error || msg.result?.isError),
|
|
61
|
+
tools: (msg.result?.tools ?? []).map((t) => t.name),
|
|
62
|
+
};
|
|
63
|
+
};
|
|
64
|
+
const call = (token, name, args) => rpc(token, 'tools/call', { name, arguments: args });
|
|
65
|
+
const count = (sql) => {
|
|
66
|
+
const db = new Database(dbPath);
|
|
67
|
+
try {
|
|
68
|
+
return db.prepare(sql).get().n;
|
|
69
|
+
}
|
|
70
|
+
finally {
|
|
71
|
+
db.close();
|
|
72
|
+
}
|
|
73
|
+
};
|
|
74
|
+
return { rpc, call, count };
|
|
75
|
+
}
|
|
76
|
+
describe('examples/readonly-analytics', () => {
|
|
77
|
+
it('matches the README table', async () => {
|
|
78
|
+
const { call, rpc } = await boot('readonly-analytics');
|
|
79
|
+
const q = (sql) => call('shared', 'analytics.query', { sql });
|
|
80
|
+
const plans = await q('SELECT plan, count(*) AS n FROM users GROUP BY plan');
|
|
81
|
+
expect(plans.isError).toBe(false);
|
|
82
|
+
expect(plans.text).toContain('"pro"');
|
|
83
|
+
const joined = await q("SELECT u.plan, count(*) AS exports FROM events e JOIN users u ON u.id = e.user_id WHERE e.name = 'export' GROUP BY u.plan");
|
|
84
|
+
expect(joined.isError).toBe(false);
|
|
85
|
+
expect((await q('SELECT password_hash FROM users')).text).toMatch(/forbidden\.policy.*denied column/);
|
|
86
|
+
expect((await q('SELECT * FROM users')).text).toMatch(/forbidden\.policy.*SELECT \*/);
|
|
87
|
+
expect((await q('SELECT body FROM internal_notes')).text).toMatch(/forbidden\.policy.*not accessible/);
|
|
88
|
+
expect((await q('DELETE FROM events WHERE id = 1')).text).toMatch(/forbidden\.policy.*read-only/);
|
|
89
|
+
const list = (await call('shared', 'analytics.list_tables', {})).text;
|
|
90
|
+
expect(list).not.toContain('internal_notes');
|
|
91
|
+
expect(list).not.toContain('password_hash');
|
|
92
|
+
expect((await rpc('shared', 'tools/list')).tools).not.toContain('analytics.execute');
|
|
93
|
+
});
|
|
94
|
+
});
|
|
95
|
+
describe('examples/spend-policy', () => {
|
|
96
|
+
it('matches the README table', async () => {
|
|
97
|
+
const { call, count } = await boot('spend-policy');
|
|
98
|
+
const exec = (sql, extra = {}) => call('shared', 'bank.execute', { sql, ...extra });
|
|
99
|
+
const txCount = () => count('SELECT count(*) AS n FROM transactions');
|
|
100
|
+
const coffee = "INSERT INTO transactions (account_id, amount_cents, memo) VALUES (1, -2500, 'Coffee')";
|
|
101
|
+
expect((await exec(coffee)).text).toContain('"rowsAffected": 1');
|
|
102
|
+
expect(txCount()).toBe(3);
|
|
103
|
+
const tea = "INSERT INTO transactions (account_id, amount_cents, memo) VALUES (2, -300, 'Tea')";
|
|
104
|
+
await exec(tea, { idempotencyKey: 'coffee-1' });
|
|
105
|
+
await exec(tea, { idempotencyKey: 'coffee-1' });
|
|
106
|
+
expect(txCount()).toBe(4);
|
|
107
|
+
const tv = await exec("INSERT INTO transactions (account_id, amount_cents, memo) VALUES (1, -90000, 'TV')");
|
|
108
|
+
expect(tv.isError).toBe(true);
|
|
109
|
+
expect(tv.text).toMatch(/CHECK/i);
|
|
110
|
+
expect(txCount()).toBe(4);
|
|
111
|
+
expect((await exec('UPDATE accounts SET balance_cents = 999999 WHERE id = 1')).text).toMatch(/forbidden\.policy.*read-only/);
|
|
112
|
+
const del = await exec('DELETE FROM transactions WHERE account_id = 1');
|
|
113
|
+
expect(del.text).toMatch(/forbidden\.policy/);
|
|
114
|
+
expect(txCount()).toBe(4);
|
|
115
|
+
expect((await call('shared', 'bank.query', { sql: 'SELECT ssn FROM customers' })).text).toMatch(/forbidden\.policy.*denied column/);
|
|
116
|
+
expect((await exec('DROP TABLE transactions')).text).toMatch(/forbidden\.policy.*always blocked/);
|
|
117
|
+
});
|
|
118
|
+
});
|
|
119
|
+
describe('examples/multi-agent', () => {
|
|
120
|
+
it('matches the README table', async () => {
|
|
121
|
+
const { call, rpc } = await boot('multi-agent');
|
|
122
|
+
const support = 'demo-support-token';
|
|
123
|
+
const finance = 'demo-finance-token';
|
|
124
|
+
// support-bot
|
|
125
|
+
expect((await call(support, 'app.query', { sql: 'SELECT email FROM customers' })).isError).toBe(false);
|
|
126
|
+
expect((await call(support, 'app.execute', {
|
|
127
|
+
sql: "UPDATE tickets SET status = 'closed' WHERE id = 1",
|
|
128
|
+
})).text).toContain('"rowsAffected": 1');
|
|
129
|
+
expect((await call(support, 'app.query', { sql: 'SELECT id FROM invoices' })).text).toMatch(/forbidden\.policy/);
|
|
130
|
+
expect((await call(support, 'app.list_tables', {})).text).not.toContain('invoices');
|
|
131
|
+
// finance-bot
|
|
132
|
+
expect((await call(finance, 'app.query', { sql: 'SELECT id, status FROM invoices' })).isError).toBe(false);
|
|
133
|
+
expect((await call(finance, 'app.query', { sql: 'SELECT email FROM customers' })).text).toMatch(/forbidden\.policy.*denied column/);
|
|
134
|
+
expect((await call(finance, 'app.query', { sql: 'SELECT id FROM tickets' })).text).toMatch(/forbidden\.policy/);
|
|
135
|
+
expect((await rpc(finance, 'tools/list')).tools).not.toContain('app.execute');
|
|
136
|
+
// retired-bot
|
|
137
|
+
expect((await rpc('demo-retired-token', 'tools/list')).status).toBe(401);
|
|
138
|
+
});
|
|
139
|
+
});
|
|
140
|
+
//# sourceMappingURL=examples.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"examples.test.js","sourceRoot":"","sources":["../../src/__tests__/examples.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"init-doctor.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/init-doctor.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
import { mkdtempSync, readFileSync, rmSync } from 'node:fs';
|
|
2
|
+
import { tmpdir } from 'node:os';
|
|
3
|
+
import { join } from 'node:path';
|
|
4
|
+
import { afterAll, beforeAll, describe, expect, it } from 'vitest';
|
|
5
|
+
import { PolicySchema, SqliteDialect } from '@mcpolyglot/connector-sql';
|
|
6
|
+
import { generatePolicy, initCommand } from '../commands/init.js';
|
|
7
|
+
import { checkPolicy } from '../commands/doctor.js';
|
|
8
|
+
let canRun = true;
|
|
9
|
+
try {
|
|
10
|
+
await import('better-sqlite3');
|
|
11
|
+
}
|
|
12
|
+
catch {
|
|
13
|
+
canRun = false;
|
|
14
|
+
}
|
|
15
|
+
const d = canRun ? describe : describe.skip;
|
|
16
|
+
d('init <url> / doctor policy checks against SQLite', () => {
|
|
17
|
+
let dir;
|
|
18
|
+
let dbPath;
|
|
19
|
+
let tables;
|
|
20
|
+
beforeAll(async () => {
|
|
21
|
+
dir = mkdtempSync(join(tmpdir(), 'mcpolyglot-init-'));
|
|
22
|
+
dbPath = join(dir, 'app.db');
|
|
23
|
+
const Database = (await import('better-sqlite3')).default;
|
|
24
|
+
const db = new Database(dbPath);
|
|
25
|
+
db.exec(`
|
|
26
|
+
CREATE TABLE customers (id INTEGER PRIMARY KEY, name TEXT, ssn TEXT, password_hash TEXT);
|
|
27
|
+
CREATE TABLE accounts (id INTEGER PRIMARY KEY, customer_id INTEGER, api_key TEXT, balance INTEGER);
|
|
28
|
+
CREATE TABLE transactions (id INTEGER PRIMARY KEY, account_id INTEGER, amount INTEGER);
|
|
29
|
+
`);
|
|
30
|
+
db.close();
|
|
31
|
+
const dialect = new SqliteDialect(dbPath);
|
|
32
|
+
await dialect.connect();
|
|
33
|
+
tables = await dialect.listTables();
|
|
34
|
+
await dialect.close();
|
|
35
|
+
});
|
|
36
|
+
afterAll(() => rmSync(dir, { recursive: true, force: true }));
|
|
37
|
+
it('generates an all-read policy with defaultAccess none and heuristic denies', () => {
|
|
38
|
+
const policy = PolicySchema.parse(generatePolicy(tables));
|
|
39
|
+
expect(policy.defaultAccess).toBe('none');
|
|
40
|
+
expect(policy.tables).toEqual({ accounts: 'read', customers: 'read', transactions: 'read' });
|
|
41
|
+
expect(policy.denyColumns.sort()).toEqual([
|
|
42
|
+
'accounts.api_key',
|
|
43
|
+
'customers.password_hash',
|
|
44
|
+
'customers.ssn',
|
|
45
|
+
]);
|
|
46
|
+
});
|
|
47
|
+
it('writes the policy into mcpolyglot.config.ts, marked as suggestions', async () => {
|
|
48
|
+
await initCommand({ cwd: dir, url: dbPath, id: 'sqlite.app' });
|
|
49
|
+
const text = readFileSync(join(dir, 'mcpolyglot.config.ts'), 'utf8');
|
|
50
|
+
expect(text).toContain(`defaultAccess: 'none'`);
|
|
51
|
+
expect(text).toContain(`"transactions": 'read'`);
|
|
52
|
+
expect(text).not.toContain(`: 'write'`);
|
|
53
|
+
expect(text).toMatch(/Suggested from column names[\s\S]*"customers.ssn"/);
|
|
54
|
+
// Only a type import: the file must load under npx with no @mcpolyglot/config installed.
|
|
55
|
+
expect(text).toMatch(/^import type \{ McpolyglotConfig \} from '@mcpolyglot\/config';/m);
|
|
56
|
+
expect(text).not.toMatch(/^import \{/m);
|
|
57
|
+
await expect(initCommand({ cwd: dir, url: dbPath })).rejects.toThrow(/already exists/);
|
|
58
|
+
});
|
|
59
|
+
it('doctor flags policy keys and denyColumns that are not in the schema', () => {
|
|
60
|
+
const r = checkPolicy({
|
|
61
|
+
defaultAccess: 'none',
|
|
62
|
+
tables: { customers: 'read', accounts: 'write', ledger: 'read' },
|
|
63
|
+
denyColumns: ['customers.ssn', 'customers.pin', '*.nope'],
|
|
64
|
+
}, tables);
|
|
65
|
+
expect(r.problems).toEqual([
|
|
66
|
+
'policy.tables names "ledger", which is not in the schema',
|
|
67
|
+
'denyColumns has "customers.pin", which matches no column in the schema',
|
|
68
|
+
'denyColumns has "*.nope", which matches no column in the schema',
|
|
69
|
+
]);
|
|
70
|
+
expect(r.readable).toEqual(['customers']);
|
|
71
|
+
expect(r.writable).toEqual(['accounts']);
|
|
72
|
+
expect(r.hidden).toEqual(['transactions']);
|
|
73
|
+
expect(r.hiddenColumns).toEqual(['customers.ssn']);
|
|
74
|
+
});
|
|
75
|
+
it('doctor reports a clean generated policy with no problems', () => {
|
|
76
|
+
expect(checkPolicy(generatePolicy(tables), tables).problems).toEqual([]);
|
|
77
|
+
});
|
|
78
|
+
});
|
|
79
|
+
//# sourceMappingURL=init-doctor.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"init-doctor.test.js","sourceRoot":"","sources":["../../src/__tests__/init-doctor.test.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,YAAY,EAAE,MAAM,EAAE,MAAM,SAAS,CAAC;AAC5D,OAAO,EAAE,MAAM,EAAE,MAAM,SAAS,CAAC;AACjC,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AACjC,OAAO,EAAE,QAAQ,EAAE,SAAS,EAAE,QAAQ,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,QAAQ,CAAC;AAEnE,OAAO,EAAE,YAAY,EAAE,aAAa,EAAE,MAAM,2BAA2B,CAAC;AACxE,OAAO,EAAE,cAAc,EAAE,WAAW,EAAE,MAAM,qBAAqB,CAAC;AAClE,OAAO,EAAE,WAAW,EAAE,MAAM,uBAAuB,CAAC;AAEpD,IAAI,MAAM,GAAG,IAAI,CAAC;AAClB,IAAI,CAAC;IACH,MAAM,MAAM,CAAC,gBAAgB,CAAC,CAAC;AACjC,CAAC;AAAC,MAAM,CAAC;IACP,MAAM,GAAG,KAAK,CAAC;AACjB,CAAC;AACD,MAAM,CAAC,GAAG,MAAM,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,QAAQ,CAAC,IAAI,CAAC;AAE5C,CAAC,CAAC,kDAAkD,EAAE,GAAG,EAAE;IACzD,IAAI,GAAW,CAAC;IAChB,IAAI,MAAc,CAAC;IACnB,IAAI,MAAqB,CAAC;IAE1B,SAAS,CAAC,KAAK,IAAI,EAAE;QACnB,GAAG,GAAG,WAAW,CAAC,IAAI,CAAC,MAAM,EAAE,EAAE,kBAAkB,CAAC,CAAC,CAAC;QACtD,MAAM,GAAG,IAAI,CAAC,GAAG,EAAE,QAAQ,CAAC,CAAC;QAC7B,MAAM,QAAQ,GAAG,CAAC,MAAM,MAAM,CAAC,gBAAgB,CAAC,CAAC,CAAC,OAAO,CAAC;QAC1D,MAAM,EAAE,GAAG,IAAI,QAAQ,CAAC,MAAM,CAAC,CAAC;QAChC,EAAE,CAAC,IAAI,CAAC;;;;KAIP,CAAC,CAAC;QACH,EAAE,CAAC,KAAK,EAAE,CAAC;QACX,MAAM,OAAO,GAAG,IAAI,aAAa,CAAC,MAAM,CAAC,CAAC;QAC1C,MAAM,OAAO,CAAC,OAAO,EAAE,CAAC;QACxB,MAAM,GAAG,MAAM,OAAO,CAAC,UAAU,EAAE,CAAC;QACpC,MAAM,OAAO,CAAC,KAAK,EAAE,CAAC;IACxB,CAAC,CAAC,CAAC;IAEH,QAAQ,CAAC,GAAG,EAAE,CAAC,MAAM,CAAC,GAAG,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC;IAE9D,EAAE,CAAC,2EAA2E,EAAE,GAAG,EAAE;QACnF,MAAM,MAAM,GAAG,YAAY,CAAC,KAAK,CAAC,cAAc,CAAC,MAAM,CAAC,CAAC,CAAC;QAC1D,MAAM,CAAC,MAAM,CAAC,aAAa,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;QAC1C,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,OAAO,CAAC,EAAE,QAAQ,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,EAAE,CAAC,CAAC;QAC7F,MAAM,CAAC,MAAM,CAAC,WAAW,CAAC,IAAI,EAAE,CAAC,CAAC,OAAO,CAAC;YACxC,kBAAkB;YAClB,yBAAyB;YACzB,eAAe;SAChB,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,oEAAoE,EAAE,KAAK,IAAI,EAAE;QAClF,MAAM,WAAW,CAAC,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,MAAM,EAAE,EAAE,EAAE,YAAY,EAAE,CAAC,CAAC;QAC/D,MAAM,IAAI,GAAG,YAAY,CAAC,IAAI,CAAC,GAAG,EAAE,sBAAsB,CAAC,EAAE,MAAM,CAAC,CAAC;QACrE,MAAM,CAAC,IAAI,CAAC,CAAC,SAAS,CAAC,uBAAuB,CAAC,CAAC;QAChD,MAAM,CAAC,IAAI,CAAC,CAAC,SAAS,CAAC,wBAAwB,CAAC,CAAC;QACjD,MAAM,CAAC,IAAI,CAAC,CAAC,GAAG,CAAC,SAAS,CAAC,WAAW,CAAC,CAAC;QACxC,MAAM,CAAC,IAAI,CAAC,CAAC,OAAO,CAAC,mDAAmD,CAAC,CAAC;QAC1E,yFAAyF;QACzF,MAAM,CAAC,IAAI,CAAC,CAAC,OAAO,CAAC,kEAAkE,CAAC,CAAC;QACzF,MAAM,CAAC,IAAI,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,aAAa,CAAC,CAAC;QACxC,MAAM,MAAM,CAAC,WAAW,CAAC,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,MAAM,EAAE,CAAC,CAAC,CAAC,OAAO,CAAC,OAAO,CAAC,gBAAgB,CAAC,CAAC;IACzF,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,qEAAqE,EAAE,GAAG,EAAE;QAC7E,MAAM,CAAC,GAAG,WAAW,CACnB;YACE,aAAa,EAAE,MAAM;YACrB,MAAM,EAAE,EAAE,SAAS,EAAE,MAAM,EAAE,QAAQ,EAAE,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE;YAChE,WAAW,EAAE,CAAC,eAAe,EAAE,eAAe,EAAE,QAAQ,CAAC;SAC1D,EACD,MAAM,CACP,CAAC;QACF,MAAM,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,OAAO,CAAC;YACzB,0DAA0D;YAC1D,wEAAwE;YACxE,iEAAiE;SAClE,CAAC,CAAC;QACH,MAAM,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,OAAO,CAAC,CAAC,WAAW,CAAC,CAAC,CAAC;QAC1C,MAAM,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,OAAO,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC;QACzC,MAAM,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,OAAO,CAAC,CAAC,cAAc,CAAC,CAAC,CAAC;QAC3C,MAAM,CAAC,CAAC,CAAC,aAAa,CAAC,CAAC,OAAO,CAAC,CAAC,eAAe,CAAC,CAAC,CAAC;IACrD,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,0DAA0D,EAAE,GAAG,EAAE;QAClE,MAAM,CAAC,WAAW,CAAC,cAAc,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC,CAAC,QAAQ,CAAC,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC;IAC3E,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC"}
|
package/dist/bin.js
CHANGED
|
@@ -5,6 +5,7 @@ import { serveCommand } from './commands/serve.js';
|
|
|
5
5
|
import { initCommand } from './commands/init.js';
|
|
6
6
|
import { doctorCommand } from './commands/doctor.js';
|
|
7
7
|
import { toolsCommand } from './commands/tools.js';
|
|
8
|
+
import { tokenCreateCommand, tokenHashCommand } from './commands/token.js';
|
|
8
9
|
const VERSION = '0.0.1';
|
|
9
10
|
function bannerHeader() {
|
|
10
11
|
const wordmark = pc.cyan('m') +
|
|
@@ -32,8 +33,10 @@ program
|
|
|
32
33
|
.command('init')
|
|
33
34
|
.description('Interactively scaffold an mcpolyglot.config.ts in the current directory.')
|
|
34
35
|
.option('--cwd <dir>', 'Directory to scaffold into', process.cwd())
|
|
35
|
-
.
|
|
36
|
-
|
|
36
|
+
.argument('[url]', 'DB URL or SQLite path: introspect it and generate a read-only policy')
|
|
37
|
+
.option('--id <id>', 'Source id (with a URL)')
|
|
38
|
+
.action(async (url, opts) => {
|
|
39
|
+
await initCommand({ cwd: opts.cwd, url, id: opts.id });
|
|
37
40
|
});
|
|
38
41
|
program
|
|
39
42
|
.command('serve')
|
|
@@ -65,8 +68,18 @@ program
|
|
|
65
68
|
.action(async (opts) => {
|
|
66
69
|
await toolsCommand({ config: opts.config });
|
|
67
70
|
});
|
|
71
|
+
const token = program.command('token').description('Manage per-agent HTTP bearer tokens.');
|
|
72
|
+
token
|
|
73
|
+
.command('create <agentId>')
|
|
74
|
+
.description('Mint a new token; prints it once with its sha256 hash and a config snippet.')
|
|
75
|
+
.action((agentId) => tokenCreateCommand(agentId));
|
|
76
|
+
token
|
|
77
|
+
.command('hash')
|
|
78
|
+
.description('Print the sha256 hash of a token read from stdin.')
|
|
79
|
+
.action(async () => {
|
|
80
|
+
await tokenHashCommand();
|
|
81
|
+
});
|
|
68
82
|
program.parseAsync(process.argv).catch((err) => {
|
|
69
|
-
// eslint-disable-next-line no-console
|
|
70
83
|
console.error(`mcpolyglot: ${err.message}`);
|
|
71
84
|
process.exit(1);
|
|
72
85
|
});
|
package/dist/bin.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"bin.js","sourceRoot":"","sources":["../src/bin.ts"],"names":[],"mappings":";AACA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACpC,OAAO,EAAE,MAAM,YAAY,CAAC;AAC5B,OAAO,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AACnD,OAAO,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC;AACjD,OAAO,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC;AACrD,OAAO,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;
|
|
1
|
+
{"version":3,"file":"bin.js","sourceRoot":"","sources":["../src/bin.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import type { Connector } from '@mcpolyglot/core';
|
|
2
|
+
import type { McpolyglotConfig } from '@mcpolyglot/config';
|
|
3
|
+
/** Per agent: which sources and tools it can use over HTTP, and which it can't. */
|
|
4
|
+
export declare function doctorAgents(cfg: McpolyglotConfig, connectors: Connector[]): void;
|
|
5
|
+
//# sourceMappingURL=doctor-agents.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"doctor-agents.d.ts","sourceRoot":"","sources":["../../src/commands/doctor-agents.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;AAClD,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,oBAAoB,CAAC;AAI3D,mFAAmF;AACnF,wBAAgB,YAAY,CAAC,GAAG,EAAE,gBAAgB,EAAE,UAAU,EAAE,SAAS,EAAE,GAAG,IAAI,CA0BjF"}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import pc from 'picocolors';
|
|
2
|
+
import { agentScopes } from '../factory.js';
|
|
3
|
+
import { section, ok, warn, stdoutSink, sym } from '../ui.js';
|
|
4
|
+
/** Per agent: which sources and tools it can use over HTTP, and which it can't. */
|
|
5
|
+
export function doctorAgents(cfg, connectors) {
|
|
6
|
+
if (!cfg.agents)
|
|
7
|
+
return;
|
|
8
|
+
section('Agents', stdoutSink);
|
|
9
|
+
for (const a of cfg.agents) {
|
|
10
|
+
const listed = cfg.sources.filter((s) => s.id in a.sources);
|
|
11
|
+
const scopes = agentScopes(a, listed);
|
|
12
|
+
const active = a.tokens.filter((t) => !t.revoked).length;
|
|
13
|
+
const detail = `${active} active token(s) · scopes: ${scopes.join(', ') || 'none'}`;
|
|
14
|
+
if (active === 0)
|
|
15
|
+
warn(a.id, `${detail} — every token is revoked`, stdoutSink);
|
|
16
|
+
else
|
|
17
|
+
ok(a.id, detail, stdoutSink);
|
|
18
|
+
for (const c of connectors) {
|
|
19
|
+
const policy = a.sources[c.id]?.policy ? pc.dim(' (own policy)') : '';
|
|
20
|
+
if (!(c.id in a.sources)) {
|
|
21
|
+
process.stdout.write(` ${sym.err} ${pc.dim(c.id)} ${pc.dim('not listed')}\n`);
|
|
22
|
+
continue;
|
|
23
|
+
}
|
|
24
|
+
for (const t of c.listPrimitiveTools()) {
|
|
25
|
+
const missing = t.scopes.filter((s) => !scopes.includes(s));
|
|
26
|
+
process.stdout.write(missing.length === 0
|
|
27
|
+
? ` ${sym.ok} ${pc.bold(t.name)}${policy}\n`
|
|
28
|
+
: ` ${sym.err} ${t.name} ${pc.dim(`missing ${missing.join(', ')}`)}\n`);
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
//# sourceMappingURL=doctor-agents.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"doctor-agents.js","sourceRoot":"","sources":["../../src/commands/doctor-agents.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,YAAY,CAAC;AAG5B,OAAO,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC;AAC5C,OAAO,EAAE,OAAO,EAAE,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,GAAG,EAAE,MAAM,UAAU,CAAC;AAE9D,mFAAmF;AACnF,MAAM,UAAU,YAAY,CAAC,GAAqB,EAAE,UAAuB;IACzE,IAAI,CAAC,GAAG,CAAC,MAAM;QAAE,OAAO;IACxB,OAAO,CAAC,QAAQ,EAAE,UAAU,CAAC,CAAC;IAC9B,KAAK,MAAM,CAAC,IAAI,GAAG,CAAC,MAAM,EAAE,CAAC;QAC3B,MAAM,MAAM,GAAG,GAAG,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,IAAI,CAAC,CAAC,OAAO,CAAC,CAAC;QAC5D,MAAM,MAAM,GAAG,WAAW,CAAC,CAAC,EAAE,MAAM,CAAC,CAAC;QACtC,MAAM,MAAM,GAAG,CAAC,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,MAAM,CAAC;QACzD,MAAM,MAAM,GAAG,GAAG,MAAM,8BAA8B,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,MAAM,EAAE,CAAC;QACpF,IAAI,MAAM,KAAK,CAAC;YAAE,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,GAAG,MAAM,2BAA2B,EAAE,UAAU,CAAC,CAAC;;YAC1E,EAAE,CAAC,CAAC,CAAC,EAAE,EAAE,MAAM,EAAE,UAAU,CAAC,CAAC;QAClC,KAAK,MAAM,CAAC,IAAI,UAAU,EAAE,CAAC;YAC3B,MAAM,MAAM,GAAG,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,GAAG,CAAC,eAAe,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;YACtE,IAAI,CAAC,CAAC,CAAC,CAAC,EAAE,IAAI,CAAC,CAAC,OAAO,CAAC,EAAE,CAAC;gBACzB,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,SAAS,GAAG,CAAC,GAAG,IAAI,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,KAAK,EAAE,CAAC,GAAG,CAAC,YAAY,CAAC,IAAI,CAAC,CAAC;gBACpF,SAAS;YACX,CAAC;YACD,KAAK,MAAM,CAAC,IAAI,CAAC,CAAC,kBAAkB,EAAE,EAAE,CAAC;gBACvC,MAAM,OAAO,GAAG,CAAC,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC;gBAC5D,OAAO,CAAC,MAAM,CAAC,KAAK,CAClB,OAAO,CAAC,MAAM,KAAK,CAAC;oBAClB,CAAC,CAAC,SAAS,GAAG,CAAC,EAAE,IAAI,EAAE,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,MAAM,IAAI;oBACjD,CAAC,CAAC,SAAS,GAAG,CAAC,GAAG,IAAI,CAAC,CAAC,IAAI,KAAK,EAAE,CAAC,GAAG,CAAC,WAAW,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,IAAI,CAC/E,CAAC;YACJ,CAAC;QACH,CAAC;IACH,CAAC;AACH,CAAC"}
|
|
@@ -1,5 +1,15 @@
|
|
|
1
|
+
import type { TableSchema } from '@mcpolyglot/core';
|
|
1
2
|
export interface DoctorOptions {
|
|
2
3
|
config: string;
|
|
3
4
|
}
|
|
4
5
|
export declare function doctorCommand(opts: DoctorOptions): Promise<boolean>;
|
|
6
|
+
export interface PolicyReport {
|
|
7
|
+
problems: string[];
|
|
8
|
+
readable: string[];
|
|
9
|
+
writable: string[];
|
|
10
|
+
hidden: string[];
|
|
11
|
+
hiddenColumns: string[];
|
|
12
|
+
}
|
|
13
|
+
/** Check a source's policy against its live schema. `policy` must already parse. */
|
|
14
|
+
export declare function checkPolicy(raw: unknown, tables: TableSchema[]): PolicyReport;
|
|
5
15
|
//# sourceMappingURL=doctor.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"doctor.d.ts","sourceRoot":"","sources":["../../src/commands/doctor.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"doctor.d.ts","sourceRoot":"","sources":["../../src/commands/doctor.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AAsBpD,MAAM,WAAW,aAAa;IAC5B,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,wBAAsB,aAAa,CAAC,IAAI,EAAE,aAAa,GAAG,OAAO,CAAC,OAAO,CAAC,CAyGzE;AAED,MAAM,WAAW,YAAY;IAC3B,QAAQ,EAAE,MAAM,EAAE,CAAC;IACnB,QAAQ,EAAE,MAAM,EAAE,CAAC;IACnB,QAAQ,EAAE,MAAM,EAAE,CAAC;IACnB,MAAM,EAAE,MAAM,EAAE,CAAC;IACjB,aAAa,EAAE,MAAM,EAAE,CAAC;CACzB;AAED,oFAAoF;AACpF,wBAAgB,WAAW,CAAC,GAAG,EAAE,OAAO,EAAE,MAAM,EAAE,WAAW,EAAE,GAAG,YAAY,CAyC7E"}
|