@kernel.chat/kbot 3.99.30 → 3.99.33
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 +1 -1
- package/dist/agents/security-agent.d.ts +31 -0
- package/dist/agents/security-agent.js +180 -0
- package/dist/agents/security-rules.d.ts +35 -0
- package/dist/agents/security-rules.js +206 -0
- package/dist/agents/specialists.d.ts +6 -0
- package/dist/agents/specialists.js +45 -0
- package/dist/architect.js +5 -0
- package/dist/auth.js +1 -1
- package/dist/channels/matrix.d.ts +4 -0
- package/dist/channels/matrix.js +28 -0
- package/dist/channels/office.d.ts +78 -0
- package/dist/channels/office.js +169 -0
- package/dist/channels/registry.d.ts +8 -0
- package/dist/channels/registry.js +38 -0
- package/dist/channels/signal.d.ts +4 -0
- package/dist/channels/signal.js +29 -0
- package/dist/channels/slack.d.ts +4 -0
- package/dist/channels/slack.js +97 -0
- package/dist/channels/teams.d.ts +4 -0
- package/dist/channels/teams.js +29 -0
- package/dist/channels/telegram.d.ts +4 -0
- package/dist/channels/telegram.js +28 -0
- package/dist/channels/types.d.ts +50 -0
- package/dist/channels/types.js +13 -0
- package/dist/channels/whatsapp.d.ts +4 -0
- package/dist/channels/whatsapp.js +28 -0
- package/dist/computer-use-coordinator.d.ts +44 -0
- package/dist/computer-use-coordinator.js +0 -0
- package/dist/doctor.js +6 -3
- package/dist/file-library.d.ts +76 -0
- package/dist/file-library.js +269 -0
- package/dist/managed-agents-anthropic.d.ts +90 -0
- package/dist/managed-agents-anthropic.js +123 -0
- package/dist/plugins-integrity.d.ts +72 -0
- package/dist/plugins-integrity.js +153 -0
- package/dist/plugins.d.ts +13 -2
- package/dist/plugins.js +87 -10
- package/dist/tools/anthropic-managed-agents-tools.d.ts +22 -0
- package/dist/tools/anthropic-managed-agents-tools.js +191 -0
- package/dist/tools/channel-tools.d.ts +4 -0
- package/dist/tools/channel-tools.js +80 -0
- package/dist/tools/computer-coordinator-tools.d.ts +13 -0
- package/dist/tools/computer-coordinator-tools.js +104 -0
- package/dist/tools/computer.js +463 -299
- package/dist/tools/file-library-tools.d.ts +12 -0
- package/dist/tools/file-library-tools.js +191 -0
- package/dist/tools/image-thoughtful.d.ts +31 -0
- package/dist/tools/image-thoughtful.js +233 -0
- package/dist/tools/index.js +1 -0
- package/dist/tools/security-agent-tools.d.ts +34 -0
- package/dist/tools/security-agent-tools.js +30 -0
- package/dist/tools/swarm-2026-04.d.ts +2 -0
- package/dist/tools/swarm-2026-04.js +91 -0
- package/dist/tools/workspace-agent-tools.d.ts +19 -0
- package/dist/tools/workspace-agent-tools.js +191 -0
- package/dist/workspace-agents.d.ts +132 -0
- package/dist/workspace-agents.js +379 -0
- package/package.json +1 -1
|
@@ -0,0 +1,169 @@
|
|
|
1
|
+
// Microsoft Office channel adapter — drives Word, Excel, PowerPoint via the
|
|
2
|
+
// Microsoft Graph API. Mirrors Microsoft's Copilot Agent Mode (GA April 22,
|
|
3
|
+
// 2026) but lets kbot orchestrate the same operations from the terminal.
|
|
4
|
+
//
|
|
5
|
+
// Auth: bearer token in MICROSOFT_GRAPH_TOKEN. Acquire via the Microsoft
|
|
6
|
+
// identity platform (delegated permissions: Files.ReadWrite, Sites.ReadWrite.All).
|
|
7
|
+
//
|
|
8
|
+
// The standard ChannelAdapter interface is preserved:
|
|
9
|
+
// - send → POST a comment on a drive item (file)
|
|
10
|
+
// - receive → list comments on a drive item
|
|
11
|
+
// - listChannels → enumerate drive root children (folders/files)
|
|
12
|
+
//
|
|
13
|
+
// Office-specific operations are exposed as additional methods on the class:
|
|
14
|
+
// - listFiles, readDocument, writeDocumentSection,
|
|
15
|
+
// appendToWorkbook, addSlide
|
|
16
|
+
const GRAPH_BASE = 'https://graph.microsoft.com/v1.0';
|
|
17
|
+
function getToken() {
|
|
18
|
+
const token = process.env.MICROSOFT_GRAPH_TOKEN;
|
|
19
|
+
if (!token) {
|
|
20
|
+
throw new Error('MICROSOFT_GRAPH_TOKEN is not set — Microsoft Office adapter cannot authenticate');
|
|
21
|
+
}
|
|
22
|
+
return token;
|
|
23
|
+
}
|
|
24
|
+
async function graphFetch(path, init = {}) {
|
|
25
|
+
const token = getToken();
|
|
26
|
+
const headers = {
|
|
27
|
+
Authorization: `Bearer ${token}`,
|
|
28
|
+
Accept: 'application/json',
|
|
29
|
+
...(init.headers ?? {}),
|
|
30
|
+
};
|
|
31
|
+
if (init.body !== undefined && !headers['Content-Type'] && !init.rawBody) {
|
|
32
|
+
headers['Content-Type'] = 'application/json';
|
|
33
|
+
}
|
|
34
|
+
const url = path.startsWith('http') ? path : `${GRAPH_BASE}${path}`;
|
|
35
|
+
const res = await fetch(url, { ...init, headers });
|
|
36
|
+
if (!res.ok) {
|
|
37
|
+
let bodyText = '';
|
|
38
|
+
try {
|
|
39
|
+
bodyText = await res.text();
|
|
40
|
+
}
|
|
41
|
+
catch {
|
|
42
|
+
// ignore body decode errors
|
|
43
|
+
}
|
|
44
|
+
throw new Error(`Microsoft Graph ${init.method ?? 'GET'} ${path} failed: ${res.status} ${res.statusText}${bodyText ? ` — ${bodyText}` : ''}`);
|
|
45
|
+
}
|
|
46
|
+
if (res.status === 204)
|
|
47
|
+
return undefined;
|
|
48
|
+
const contentType = res.headers.get('content-type') ?? '';
|
|
49
|
+
if (contentType.includes('application/json')) {
|
|
50
|
+
return (await res.json());
|
|
51
|
+
}
|
|
52
|
+
// Raw body — return as text. Callers casting to T accept the contract.
|
|
53
|
+
return (await res.text());
|
|
54
|
+
}
|
|
55
|
+
function commentToChannelMessage(c) {
|
|
56
|
+
const text = c.content?.content ?? '';
|
|
57
|
+
const ts = c.createdDateTime ? Date.parse(c.createdDateTime) : Date.now();
|
|
58
|
+
return {
|
|
59
|
+
id: c.id,
|
|
60
|
+
from: c.from?.user?.displayName ?? c.from?.user?.id ?? 'unknown',
|
|
61
|
+
text,
|
|
62
|
+
ts: Number.isNaN(ts) ? Date.now() : ts,
|
|
63
|
+
raw: c,
|
|
64
|
+
};
|
|
65
|
+
}
|
|
66
|
+
export class OfficeChannel {
|
|
67
|
+
name = 'office';
|
|
68
|
+
isConfigured() {
|
|
69
|
+
return Boolean(process.env.MICROSOFT_GRAPH_TOKEN);
|
|
70
|
+
}
|
|
71
|
+
/** Post a comment to a drive item (file). `envelope.channel` is the fileId. */
|
|
72
|
+
async send(envelope) {
|
|
73
|
+
const fileId = envelope.channel;
|
|
74
|
+
const json = await graphFetch(`/me/drive/items/${encodeURIComponent(fileId)}/comments`, {
|
|
75
|
+
method: 'POST',
|
|
76
|
+
body: JSON.stringify({ message: envelope.text }),
|
|
77
|
+
});
|
|
78
|
+
const ts = json.createdDateTime ? Date.parse(json.createdDateTime) : Date.now();
|
|
79
|
+
return { id: json.id ?? '', ts: Number.isNaN(ts) ? Date.now() : ts };
|
|
80
|
+
}
|
|
81
|
+
/** List comments on a drive item. `opts.channel` is the fileId. */
|
|
82
|
+
async receive(opts) {
|
|
83
|
+
const limit = opts.limit ?? 100;
|
|
84
|
+
const json = await graphFetch(`/me/drive/items/${encodeURIComponent(opts.channel)}/comments?$top=${limit}`);
|
|
85
|
+
const items = (json.value ?? []).map(commentToChannelMessage);
|
|
86
|
+
if (opts.oldest !== undefined) {
|
|
87
|
+
return items.filter((m) => m.ts > opts.oldest);
|
|
88
|
+
}
|
|
89
|
+
return items;
|
|
90
|
+
}
|
|
91
|
+
/** List drive root children. */
|
|
92
|
+
async listChannels() {
|
|
93
|
+
const items = await this.listFiles({});
|
|
94
|
+
return items.map((it) => ({
|
|
95
|
+
id: it.id,
|
|
96
|
+
name: it.name ?? it.id,
|
|
97
|
+
topic: it.file?.mimeType ?? (it.folder ? 'folder' : undefined),
|
|
98
|
+
}));
|
|
99
|
+
}
|
|
100
|
+
// ---------- Office-specific operations ----------
|
|
101
|
+
async listFiles(options = {}) {
|
|
102
|
+
let path;
|
|
103
|
+
if (options.driveId && options.folderId) {
|
|
104
|
+
path = `/drives/${encodeURIComponent(options.driveId)}/items/${encodeURIComponent(options.folderId)}/children`;
|
|
105
|
+
}
|
|
106
|
+
else if (options.driveId) {
|
|
107
|
+
path = `/drives/${encodeURIComponent(options.driveId)}/root/children`;
|
|
108
|
+
}
|
|
109
|
+
else if (options.folderId) {
|
|
110
|
+
path = `/me/drive/items/${encodeURIComponent(options.folderId)}/children`;
|
|
111
|
+
}
|
|
112
|
+
else {
|
|
113
|
+
path = '/me/drive/root/children';
|
|
114
|
+
}
|
|
115
|
+
const json = await graphFetch(path);
|
|
116
|
+
return json.value ?? [];
|
|
117
|
+
}
|
|
118
|
+
async readDocument(options) {
|
|
119
|
+
const { fileId, format = 'raw' } = options;
|
|
120
|
+
if (format === 'worksheets') {
|
|
121
|
+
const json = await graphFetch(`/me/drive/items/${encodeURIComponent(fileId)}/workbook/worksheets`);
|
|
122
|
+
return json.value ?? [];
|
|
123
|
+
}
|
|
124
|
+
return graphFetch(`/me/drive/items/${encodeURIComponent(fileId)}/content`, {
|
|
125
|
+
method: 'GET',
|
|
126
|
+
rawBody: true,
|
|
127
|
+
});
|
|
128
|
+
}
|
|
129
|
+
/**
|
|
130
|
+
* Replace an entire Word document with `content`.
|
|
131
|
+
*
|
|
132
|
+
* TODO(office-add-in): For surgical, paragraph- or range-level edits, route
|
|
133
|
+
* through an Office Add-in's `Word.Range` API (Word JavaScript API). The
|
|
134
|
+
* Graph REST surface does not expose Word ranges directly; the standard
|
|
135
|
+
* workaround is the Office Add-in JS bridge.
|
|
136
|
+
*/
|
|
137
|
+
async writeDocumentSection(options) {
|
|
138
|
+
const { fileId, content } = options;
|
|
139
|
+
const body = typeof content === 'string' ? new TextEncoder().encode(content) : content;
|
|
140
|
+
const json = await graphFetch(`/me/drive/items/${encodeURIComponent(fileId)}/content`, {
|
|
141
|
+
method: 'PUT',
|
|
142
|
+
headers: { 'Content-Type': 'application/octet-stream' },
|
|
143
|
+
body: body,
|
|
144
|
+
rawBody: true,
|
|
145
|
+
});
|
|
146
|
+
return { id: json.id, size: json.size };
|
|
147
|
+
}
|
|
148
|
+
async appendToWorkbook(options) {
|
|
149
|
+
const { fileId, sheet, rows, table = 'Table1' } = options;
|
|
150
|
+
const path = `/me/drive/items/${encodeURIComponent(fileId)}/workbook/worksheets/` +
|
|
151
|
+
`${encodeURIComponent(sheet)}/tables/${encodeURIComponent(table)}/rows/add`;
|
|
152
|
+
return graphFetch(path, {
|
|
153
|
+
method: 'POST',
|
|
154
|
+
body: JSON.stringify({ values: rows }),
|
|
155
|
+
});
|
|
156
|
+
}
|
|
157
|
+
/**
|
|
158
|
+
* TODO(pptx): Microsoft Graph does not expose slide-level mutation. The
|
|
159
|
+
* workaround is to regenerate the .pptx (e.g. via `pptxgenjs`) and PUT the
|
|
160
|
+
* bytes back via `/me/drive/items/{fileId}/content`. Until that dependency
|
|
161
|
+
* is wired, this method throws.
|
|
162
|
+
*/
|
|
163
|
+
async addSlide(_options) {
|
|
164
|
+
throw new Error('addSlide requires pptx-genjs — see TODO in office.ts');
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
export const officeAdapter = new OfficeChannel();
|
|
168
|
+
export default officeAdapter;
|
|
169
|
+
//# sourceMappingURL=office.js.map
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type { ChannelAdapter } from './types.js';
|
|
2
|
+
export declare function getChannel(name: string): ChannelAdapter;
|
|
3
|
+
export declare function listChannels(): Array<{
|
|
4
|
+
name: string;
|
|
5
|
+
configured: boolean;
|
|
6
|
+
}>;
|
|
7
|
+
export declare function registerChannel(adapter: ChannelAdapter): void;
|
|
8
|
+
//# sourceMappingURL=registry.d.ts.map
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
// Registry for messaging channel adapters.
|
|
2
|
+
//
|
|
3
|
+
// Consumers should call `getChannel(name)` rather than importing adapters
|
|
4
|
+
// directly; this lets the registry be replaced or extended (e.g. a plugin
|
|
5
|
+
// registering an Instagram adapter at runtime).
|
|
6
|
+
import { slackAdapter } from './slack.js';
|
|
7
|
+
import { whatsappAdapter } from './whatsapp.js';
|
|
8
|
+
import { telegramAdapter } from './telegram.js';
|
|
9
|
+
import { signalAdapter } from './signal.js';
|
|
10
|
+
import { matrixAdapter } from './matrix.js';
|
|
11
|
+
import { teamsAdapter } from './teams.js';
|
|
12
|
+
import { officeAdapter } from './office.js';
|
|
13
|
+
const adapters = {
|
|
14
|
+
slack: slackAdapter,
|
|
15
|
+
whatsapp: whatsappAdapter,
|
|
16
|
+
telegram: telegramAdapter,
|
|
17
|
+
signal: signalAdapter,
|
|
18
|
+
matrix: matrixAdapter,
|
|
19
|
+
teams: teamsAdapter,
|
|
20
|
+
office: officeAdapter,
|
|
21
|
+
};
|
|
22
|
+
export function getChannel(name) {
|
|
23
|
+
const adapter = adapters[name.toLowerCase()];
|
|
24
|
+
if (!adapter) {
|
|
25
|
+
throw new Error(`Unknown channel "${name}". Available: ${Object.keys(adapters).join(', ')}`);
|
|
26
|
+
}
|
|
27
|
+
return adapter;
|
|
28
|
+
}
|
|
29
|
+
export function listChannels() {
|
|
30
|
+
return Object.values(adapters).map((a) => ({
|
|
31
|
+
name: a.name,
|
|
32
|
+
configured: a.isConfigured(),
|
|
33
|
+
}));
|
|
34
|
+
}
|
|
35
|
+
export function registerChannel(adapter) {
|
|
36
|
+
adapters[adapter.name.toLowerCase()] = adapter;
|
|
37
|
+
}
|
|
38
|
+
//# sourceMappingURL=registry.js.map
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
// Signal channel adapter — STUB.
|
|
2
|
+
//
|
|
3
|
+
// TODO: Implement against signal-cli.
|
|
4
|
+
// - Auth: SIGNAL_CLI_PATH env var pointing at the signal-cli binary.
|
|
5
|
+
// - SIGNAL_ACCOUNT env var = the registered E.164 number.
|
|
6
|
+
// - send → spawn `${SIGNAL_CLI_PATH} -a ${SIGNAL_ACCOUNT} send -m <text> <recipient>`
|
|
7
|
+
// (or use signal-cli's JSON-RPC mode for long-running processes).
|
|
8
|
+
// - receive → `signal-cli ... receive --json`, parse JSONL into
|
|
9
|
+
// ChannelMessage records.
|
|
10
|
+
// - listChannels → `signal-cli listGroups -d` for groups; contacts via
|
|
11
|
+
// `listContacts` if needed.
|
|
12
|
+
import { ChannelNotImplementedError } from './types.js';
|
|
13
|
+
export const signalAdapter = {
|
|
14
|
+
name: 'signal',
|
|
15
|
+
isConfigured() {
|
|
16
|
+
return Boolean(process.env.SIGNAL_CLI_PATH);
|
|
17
|
+
},
|
|
18
|
+
async send() {
|
|
19
|
+
throw new ChannelNotImplementedError('signal');
|
|
20
|
+
},
|
|
21
|
+
async receive() {
|
|
22
|
+
throw new ChannelNotImplementedError('signal');
|
|
23
|
+
},
|
|
24
|
+
async listChannels() {
|
|
25
|
+
throw new ChannelNotImplementedError('signal');
|
|
26
|
+
},
|
|
27
|
+
};
|
|
28
|
+
export default signalAdapter;
|
|
29
|
+
//# sourceMappingURL=signal.js.map
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
// Slack channel adapter.
|
|
2
|
+
//
|
|
3
|
+
// Uses the Slack Web API directly via `fetch` — no SDK dependency.
|
|
4
|
+
// Token: SLACK_BOT_TOKEN (xoxb-…) with at minimum chat:write,
|
|
5
|
+
// channels:history, channels:read, groups:read scopes.
|
|
6
|
+
const SLACK_API = 'https://slack.com/api';
|
|
7
|
+
function getToken() {
|
|
8
|
+
const token = process.env.SLACK_BOT_TOKEN;
|
|
9
|
+
if (!token) {
|
|
10
|
+
throw new Error('SLACK_BOT_TOKEN is not set — Slack adapter cannot authenticate');
|
|
11
|
+
}
|
|
12
|
+
return token;
|
|
13
|
+
}
|
|
14
|
+
async function slackFetch(path, body, token) {
|
|
15
|
+
const res = await fetch(`${SLACK_API}/${path}`, {
|
|
16
|
+
method: 'POST',
|
|
17
|
+
headers: {
|
|
18
|
+
'Authorization': `Bearer ${token}`,
|
|
19
|
+
'Content-Type': 'application/json; charset=utf-8',
|
|
20
|
+
},
|
|
21
|
+
body: JSON.stringify(body),
|
|
22
|
+
});
|
|
23
|
+
// Slack always returns 200 with `ok` flag in the body, but guard anyway.
|
|
24
|
+
if (!res.ok) {
|
|
25
|
+
throw new Error(`Slack API ${path} HTTP ${res.status}: ${res.statusText}`);
|
|
26
|
+
}
|
|
27
|
+
const json = (await res.json());
|
|
28
|
+
if (!json.ok) {
|
|
29
|
+
throw new Error(`Slack API ${path} error: ${json.error ?? 'unknown_error'}`);
|
|
30
|
+
}
|
|
31
|
+
return json;
|
|
32
|
+
}
|
|
33
|
+
function tsToMs(ts) {
|
|
34
|
+
if (!ts)
|
|
35
|
+
return Date.now();
|
|
36
|
+
// Slack `ts` is a string like "1700000000.000123" (seconds.microseconds).
|
|
37
|
+
const n = Number.parseFloat(ts);
|
|
38
|
+
if (Number.isNaN(n))
|
|
39
|
+
return Date.now();
|
|
40
|
+
return Math.round(n * 1000);
|
|
41
|
+
}
|
|
42
|
+
function msToTs(ms) {
|
|
43
|
+
if (ms === undefined)
|
|
44
|
+
return undefined;
|
|
45
|
+
return (ms / 1000).toFixed(6);
|
|
46
|
+
}
|
|
47
|
+
export const slackAdapter = {
|
|
48
|
+
name: 'slack',
|
|
49
|
+
isConfigured() {
|
|
50
|
+
return Boolean(process.env.SLACK_BOT_TOKEN);
|
|
51
|
+
},
|
|
52
|
+
async send(envelope) {
|
|
53
|
+
const token = getToken();
|
|
54
|
+
const body = {
|
|
55
|
+
channel: envelope.channel,
|
|
56
|
+
text: envelope.text,
|
|
57
|
+
};
|
|
58
|
+
if (envelope.blocks !== undefined)
|
|
59
|
+
body.blocks = envelope.blocks;
|
|
60
|
+
if (envelope.options)
|
|
61
|
+
Object.assign(body, envelope.options);
|
|
62
|
+
const json = await slackFetch('chat.postMessage', body, token);
|
|
63
|
+
const ts = json.ts ?? json.message?.ts;
|
|
64
|
+
return { id: ts ?? '', ts: tsToMs(ts) };
|
|
65
|
+
},
|
|
66
|
+
async receive(opts) {
|
|
67
|
+
const token = getToken();
|
|
68
|
+
const body = {
|
|
69
|
+
channel: opts.channel,
|
|
70
|
+
limit: opts.limit ?? 100,
|
|
71
|
+
};
|
|
72
|
+
const oldest = msToTs(opts.oldest);
|
|
73
|
+
if (oldest)
|
|
74
|
+
body.oldest = oldest;
|
|
75
|
+
const json = await slackFetch('conversations.history', body, token);
|
|
76
|
+
const messages = json.messages ?? [];
|
|
77
|
+
return messages.map((m) => ({
|
|
78
|
+
id: m.ts,
|
|
79
|
+
from: m.user ?? m.username ?? m.bot_id ?? 'unknown',
|
|
80
|
+
text: m.text ?? '',
|
|
81
|
+
ts: tsToMs(m.ts),
|
|
82
|
+
raw: m,
|
|
83
|
+
}));
|
|
84
|
+
},
|
|
85
|
+
async listChannels() {
|
|
86
|
+
const token = getToken();
|
|
87
|
+
const json = await slackFetch('conversations.list', { exclude_archived: true, limit: 200 }, token);
|
|
88
|
+
const channels = json.channels ?? [];
|
|
89
|
+
return channels.map((c) => ({
|
|
90
|
+
id: c.id,
|
|
91
|
+
name: c.name ?? c.id,
|
|
92
|
+
topic: c.topic?.value,
|
|
93
|
+
}));
|
|
94
|
+
},
|
|
95
|
+
};
|
|
96
|
+
export default slackAdapter;
|
|
97
|
+
//# sourceMappingURL=slack.js.map
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
// Microsoft Teams channel adapter — STUB.
|
|
2
|
+
//
|
|
3
|
+
// TODO: Implement against either:
|
|
4
|
+
// (a) Incoming Webhook (simple, send-only):
|
|
5
|
+
// MICROSOFT_TEAMS_WEBHOOK = https://outlook.office.com/webhook/...
|
|
6
|
+
// send → POST JSON Adaptive Card or MessageCard to the webhook URL.
|
|
7
|
+
// receive/listChannels → unsupported via webhook; surface a clear
|
|
8
|
+
// error pointing the caller at Graph API.
|
|
9
|
+
// (b) Microsoft Graph API (full duplex):
|
|
10
|
+
// requires app registration in Azure AD, delegated/application
|
|
11
|
+
// permissions, and an OAuth2 token. Out of scope for the stub.
|
|
12
|
+
import { ChannelNotImplementedError } from './types.js';
|
|
13
|
+
export const teamsAdapter = {
|
|
14
|
+
name: 'teams',
|
|
15
|
+
isConfigured() {
|
|
16
|
+
return Boolean(process.env.MICROSOFT_TEAMS_WEBHOOK);
|
|
17
|
+
},
|
|
18
|
+
async send() {
|
|
19
|
+
throw new ChannelNotImplementedError('teams');
|
|
20
|
+
},
|
|
21
|
+
async receive() {
|
|
22
|
+
throw new ChannelNotImplementedError('teams');
|
|
23
|
+
},
|
|
24
|
+
async listChannels() {
|
|
25
|
+
throw new ChannelNotImplementedError('teams');
|
|
26
|
+
},
|
|
27
|
+
};
|
|
28
|
+
export default teamsAdapter;
|
|
29
|
+
//# sourceMappingURL=teams.js.map
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
// Telegram channel adapter — STUB.
|
|
2
|
+
//
|
|
3
|
+
// TODO: Implement against the Telegram Bot API.
|
|
4
|
+
// - Auth: TELEGRAM_BOT_TOKEN (from @BotFather).
|
|
5
|
+
// - send → POST https://api.telegram.org/bot{token}/sendMessage
|
|
6
|
+
// body: { chat_id, text, parse_mode?, reply_markup? }
|
|
7
|
+
// - receive → either long-poll via getUpdates (simple) or webhook
|
|
8
|
+
// (production). Translate `update.message` into ChannelMessage.
|
|
9
|
+
// - listChannels → Telegram has no enumeration API; surface chats from
|
|
10
|
+
// a local persisted cache built from received updates.
|
|
11
|
+
import { ChannelNotImplementedError } from './types.js';
|
|
12
|
+
export const telegramAdapter = {
|
|
13
|
+
name: 'telegram',
|
|
14
|
+
isConfigured() {
|
|
15
|
+
return Boolean(process.env.TELEGRAM_BOT_TOKEN);
|
|
16
|
+
},
|
|
17
|
+
async send() {
|
|
18
|
+
throw new ChannelNotImplementedError('telegram');
|
|
19
|
+
},
|
|
20
|
+
async receive() {
|
|
21
|
+
throw new ChannelNotImplementedError('telegram');
|
|
22
|
+
},
|
|
23
|
+
async listChannels() {
|
|
24
|
+
throw new ChannelNotImplementedError('telegram');
|
|
25
|
+
},
|
|
26
|
+
};
|
|
27
|
+
export default telegramAdapter;
|
|
28
|
+
//# sourceMappingURL=telegram.js.map
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
export interface ChannelMessage {
|
|
2
|
+
/** Channel-native message id (string-form). */
|
|
3
|
+
id: string;
|
|
4
|
+
/** Author handle / user id reported by the channel. */
|
|
5
|
+
from: string;
|
|
6
|
+
/** Plain-text body of the message. */
|
|
7
|
+
text: string;
|
|
8
|
+
/** Unix epoch ms. Channels that report seconds should multiply by 1000. */
|
|
9
|
+
ts: number;
|
|
10
|
+
/** Raw, channel-specific payload — adapter consumers may inspect. */
|
|
11
|
+
raw?: unknown;
|
|
12
|
+
}
|
|
13
|
+
export interface ChannelEnvelope {
|
|
14
|
+
/** Target channel id (Slack channel id, chat id, room id, ...). */
|
|
15
|
+
channel: string;
|
|
16
|
+
/** Plain-text body. Required even when blocks are supplied. */
|
|
17
|
+
text: string;
|
|
18
|
+
/** Optional rich content. Slack blocks, Telegram entities, etc. */
|
|
19
|
+
blocks?: unknown;
|
|
20
|
+
/** Adapter-specific options forwarded verbatim. */
|
|
21
|
+
options?: Record<string, unknown>;
|
|
22
|
+
}
|
|
23
|
+
export interface ChannelReceiveOptions {
|
|
24
|
+
channel: string;
|
|
25
|
+
/** Unix epoch ms; only return messages newer than this. */
|
|
26
|
+
oldest?: number;
|
|
27
|
+
/** Maximum number of messages to return (adapter may cap). */
|
|
28
|
+
limit?: number;
|
|
29
|
+
}
|
|
30
|
+
export interface ChannelInfo {
|
|
31
|
+
id: string;
|
|
32
|
+
name: string;
|
|
33
|
+
topic?: string;
|
|
34
|
+
}
|
|
35
|
+
export interface ChannelAdapter {
|
|
36
|
+
/** Stable adapter name — must match registry key. */
|
|
37
|
+
readonly name: string;
|
|
38
|
+
/** True when required env vars / credentials are present. */
|
|
39
|
+
isConfigured(): boolean;
|
|
40
|
+
send(envelope: ChannelEnvelope): Promise<{
|
|
41
|
+
id: string;
|
|
42
|
+
ts: number;
|
|
43
|
+
}>;
|
|
44
|
+
receive(opts: ChannelReceiveOptions): Promise<ChannelMessage[]>;
|
|
45
|
+
listChannels(): Promise<ChannelInfo[]>;
|
|
46
|
+
}
|
|
47
|
+
export declare class ChannelNotImplementedError extends Error {
|
|
48
|
+
constructor(adapter: string);
|
|
49
|
+
}
|
|
50
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
// Shared types for the unified messaging-channel adapter family.
|
|
2
|
+
//
|
|
3
|
+
// Inspired by OpenClaw (steipete/openclaw on npm). Every channel adapter
|
|
4
|
+
// (Slack, WhatsApp, Telegram, Signal, Matrix, Teams, ...) implements
|
|
5
|
+
// the same `ChannelAdapter` interface so kbot tools and agents can
|
|
6
|
+
// send/receive messages without caring which platform they target.
|
|
7
|
+
export class ChannelNotImplementedError extends Error {
|
|
8
|
+
constructor(adapter) {
|
|
9
|
+
super(`${adapter} adapter not implemented yet — see TODO in channels/${adapter}.ts`);
|
|
10
|
+
this.name = 'ChannelNotImplementedError';
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
//# sourceMappingURL=types.js.map
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
// WhatsApp channel adapter — STUB.
|
|
2
|
+
//
|
|
3
|
+
// TODO: Implement against WhatsApp Business Cloud API.
|
|
4
|
+
// - Auth: Bearer token from Meta for Developers (`WHATSAPP_BUSINESS_TOKEN`).
|
|
5
|
+
// - Phone number id: WHATSAPP_PHONE_NUMBER_ID
|
|
6
|
+
// - send → POST https://graph.facebook.com/v20.0/{phone-id}/messages
|
|
7
|
+
// - receive → webhook subscription; this adapter would surface messages
|
|
8
|
+
// queued by the webhook receiver, not poll.
|
|
9
|
+
// - listChannels → WhatsApp does not have channels; return contacts/groups
|
|
10
|
+
// resolved via the Cloud API or a local cache.
|
|
11
|
+
import { ChannelNotImplementedError } from './types.js';
|
|
12
|
+
export const whatsappAdapter = {
|
|
13
|
+
name: 'whatsapp',
|
|
14
|
+
isConfigured() {
|
|
15
|
+
return Boolean(process.env.WHATSAPP_BUSINESS_TOKEN && process.env.WHATSAPP_PHONE_NUMBER_ID);
|
|
16
|
+
},
|
|
17
|
+
async send() {
|
|
18
|
+
throw new ChannelNotImplementedError('whatsapp');
|
|
19
|
+
},
|
|
20
|
+
async receive() {
|
|
21
|
+
throw new ChannelNotImplementedError('whatsapp');
|
|
22
|
+
},
|
|
23
|
+
async listChannels() {
|
|
24
|
+
throw new ChannelNotImplementedError('whatsapp');
|
|
25
|
+
},
|
|
26
|
+
};
|
|
27
|
+
export default whatsappAdapter;
|
|
28
|
+
//# sourceMappingURL=whatsapp.js.map
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
export interface AgentRegistration {
|
|
2
|
+
apps: string[];
|
|
3
|
+
windowIds?: string[];
|
|
4
|
+
}
|
|
5
|
+
export interface LockRecord {
|
|
6
|
+
agentId: string;
|
|
7
|
+
pid: number;
|
|
8
|
+
ts: number;
|
|
9
|
+
windowIds?: string[];
|
|
10
|
+
}
|
|
11
|
+
export interface ClaimResult {
|
|
12
|
+
granted: boolean;
|
|
13
|
+
app: string;
|
|
14
|
+
heldBy?: string;
|
|
15
|
+
since?: number;
|
|
16
|
+
}
|
|
17
|
+
export interface CoordinatorStatus {
|
|
18
|
+
apps: Record<string, {
|
|
19
|
+
heldBy: string;
|
|
20
|
+
since: number;
|
|
21
|
+
} | null>;
|
|
22
|
+
agents: Array<{
|
|
23
|
+
id: string;
|
|
24
|
+
apps: string[];
|
|
25
|
+
claimed: string[];
|
|
26
|
+
}>;
|
|
27
|
+
}
|
|
28
|
+
export declare class Coordinator {
|
|
29
|
+
private root;
|
|
30
|
+
private agents;
|
|
31
|
+
/** in-memory mirror of which apps this coordinator instance has claimed */
|
|
32
|
+
private claimed;
|
|
33
|
+
constructor(root?: string);
|
|
34
|
+
private lockPath;
|
|
35
|
+
private readLock;
|
|
36
|
+
private isStale;
|
|
37
|
+
register(agentId: string, reg: AgentRegistration): void;
|
|
38
|
+
claim(agentId: string, app: string): ClaimResult;
|
|
39
|
+
release(agentId: string, app: string): boolean;
|
|
40
|
+
unregister(agentId: string): string[];
|
|
41
|
+
status(): CoordinatorStatus;
|
|
42
|
+
}
|
|
43
|
+
export declare function createCoordinator(root?: string): Coordinator;
|
|
44
|
+
//# sourceMappingURL=computer-use-coordinator.d.ts.map
|
|
Binary file
|
package/dist/doctor.js
CHANGED
|
@@ -164,7 +164,7 @@ async function checkLocalProviderReachable(providerId, providerConfig) {
|
|
|
164
164
|
else {
|
|
165
165
|
checkUrl = providerConfig.apiUrl.replace('/v1/chat/completions', '/v1/models');
|
|
166
166
|
}
|
|
167
|
-
const res = await fetch(checkUrl, { signal: AbortSignal.timeout(
|
|
167
|
+
const res = await fetch(checkUrl, { signal: AbortSignal.timeout(5000) });
|
|
168
168
|
if (!res.ok) {
|
|
169
169
|
return { name: providerConfig.name, status: 'fail', message: `returned ${res.status}` };
|
|
170
170
|
}
|
|
@@ -194,8 +194,11 @@ async function checkLocalProviderReachable(providerId, providerConfig) {
|
|
|
194
194
|
}
|
|
195
195
|
return { name: providerConfig.name, status: 'pass', message: 'running' };
|
|
196
196
|
}
|
|
197
|
-
catch {
|
|
198
|
-
|
|
197
|
+
catch (err) {
|
|
198
|
+
const reason = err instanceof Error
|
|
199
|
+
? (err.name === 'TimeoutError' ? 'timed out (>5s)' : err.message)
|
|
200
|
+
: 'not responding';
|
|
201
|
+
return { name: providerConfig.name, status: 'fail', message: reason };
|
|
199
202
|
}
|
|
200
203
|
}
|
|
201
204
|
/**
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* File Library — local-first per-user file store.
|
|
3
|
+
*
|
|
4
|
+
* Mirrors ChatGPT's File Library: auto-saves uploaded and created files,
|
|
5
|
+
* lists, searches by name/content, fetches by id. Content-addressed (sha256).
|
|
6
|
+
*
|
|
7
|
+
* Storage layout:
|
|
8
|
+
* <root>/index.json — atomic JSON index of entries
|
|
9
|
+
* <root>/blobs/<id> — raw file bytes (id = sha256 of contents)
|
|
10
|
+
*
|
|
11
|
+
* Default root: ~/.kbot/files (overridable via KBOT_FILE_LIBRARY_ROOT or ctor opt).
|
|
12
|
+
*/
|
|
13
|
+
export declare const MAX_FILE_BYTES: number;
|
|
14
|
+
export type FileSource = "user-upload" | "agent-output" | "tool-output";
|
|
15
|
+
export interface FileEntry {
|
|
16
|
+
id: string;
|
|
17
|
+
name: string;
|
|
18
|
+
mimeType: string;
|
|
19
|
+
size: number;
|
|
20
|
+
createdAt: string;
|
|
21
|
+
source: FileSource;
|
|
22
|
+
tags: string[];
|
|
23
|
+
path: string;
|
|
24
|
+
}
|
|
25
|
+
export interface AddFileInput {
|
|
26
|
+
name: string;
|
|
27
|
+
mimeType: string;
|
|
28
|
+
source: FileSource;
|
|
29
|
+
content?: string;
|
|
30
|
+
buffer?: Buffer;
|
|
31
|
+
tags?: string[];
|
|
32
|
+
}
|
|
33
|
+
export interface ListOptions {
|
|
34
|
+
limit?: number;
|
|
35
|
+
since?: string | Date;
|
|
36
|
+
tag?: string;
|
|
37
|
+
}
|
|
38
|
+
export type SearchMode = "name" | "content" | "both";
|
|
39
|
+
export interface SearchOptions {
|
|
40
|
+
query: string;
|
|
41
|
+
mode: SearchMode;
|
|
42
|
+
}
|
|
43
|
+
export interface FileLibraryOptions {
|
|
44
|
+
root?: string;
|
|
45
|
+
}
|
|
46
|
+
export declare class FileLibrary {
|
|
47
|
+
private readonly root;
|
|
48
|
+
private readonly blobsDir;
|
|
49
|
+
private readonly indexPath;
|
|
50
|
+
private writeLock;
|
|
51
|
+
constructor(opts?: FileLibraryOptions);
|
|
52
|
+
getRoot(): string;
|
|
53
|
+
private ensureDirs;
|
|
54
|
+
private readIndex;
|
|
55
|
+
/**
|
|
56
|
+
* Atomic write: serialize → write to <indexPath>.tmp.<rand> → rename.
|
|
57
|
+
*/
|
|
58
|
+
private writeIndexAtomic;
|
|
59
|
+
/**
|
|
60
|
+
* Run a read-modify-write of the index under the writeLock so concurrent
|
|
61
|
+
* mutations don't lose updates. The mutator may return a fresh IndexFile
|
|
62
|
+
* (or undefined to skip the write).
|
|
63
|
+
*/
|
|
64
|
+
private mutateIndex;
|
|
65
|
+
addFile(input: AddFileInput): Promise<FileEntry>;
|
|
66
|
+
listFiles(opts?: ListOptions): Promise<FileEntry[]>;
|
|
67
|
+
getFile(id: string): Promise<{
|
|
68
|
+
entry: FileEntry;
|
|
69
|
+
buffer: Buffer;
|
|
70
|
+
} | null>;
|
|
71
|
+
removeFile(id: string): Promise<boolean>;
|
|
72
|
+
searchFiles(opts: SearchOptions): Promise<FileEntry[]>;
|
|
73
|
+
}
|
|
74
|
+
export declare function getDefaultFileLibrary(): FileLibrary;
|
|
75
|
+
export declare function _resetDefaultFileLibrary(): void;
|
|
76
|
+
//# sourceMappingURL=file-library.d.ts.map
|