@alfe.ai/openclaw-teams 0.0.38 → 0.0.40
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 +26 -0
- package/dist/index.cjs +2 -2
- package/dist/index.d.cts +2 -2
- package/dist/index.d.ts +2 -2
- package/dist/index.js +2 -2
- package/dist/plugin.cjs +2 -101
- package/dist/plugin.d.cts +13 -34
- package/dist/plugin.d.cts.map +1 -1
- package/dist/plugin.d.ts +13 -34
- package/dist/plugin.d.ts.map +1 -1
- package/dist/plugin.js +2 -104
- package/dist/plugin2.cjs +262 -0
- package/dist/plugin2.d.cts +2 -2
- package/dist/plugin2.d.ts +2 -2
- package/dist/plugin2.js +259 -0
- package/dist/plugin2.js.map +1 -0
- package/package.json +8 -6
- package/dist/plugin.js.map +0 -1
package/README.md
ADDED
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
# @alfe.ai/openclaw-teams
|
|
2
|
+
|
|
3
|
+
OpenClaw tools for sending text and Adaptive Cards through an Alfe agent's
|
|
4
|
+
per-agent Microsoft Teams bot, and for listing the conversations available to
|
|
5
|
+
that bot.
|
|
6
|
+
|
|
7
|
+
The plugin registers `teams_send_message`, `teams_send_card`, and
|
|
8
|
+
`teams_list_channels` synchronously on every OpenClaw registry pass. It resolves
|
|
9
|
+
the local Alfe config only when a tool executes, so a transient config problem
|
|
10
|
+
does not remove the tools from the catalog.
|
|
11
|
+
|
|
12
|
+
## Boundaries
|
|
13
|
+
|
|
14
|
+
- Conversation IDs and text are runtime-validated independently of JSON Schema.
|
|
15
|
+
- Adaptive Cards are canonicalized as bounded JSON: at most 100 KiB, 12 nested
|
|
16
|
+
levels, 200 total container entries, 128 characters per key, and 40,000
|
|
17
|
+
characters per string.
|
|
18
|
+
- Provider diagnostics are private. Only explicit local configuration guidance
|
|
19
|
+
may be returned to the model.
|
|
20
|
+
- Channel results are projected to bounded `id`, `name`, and `description`
|
|
21
|
+
fields and labeled as untrusted external content.
|
|
22
|
+
|
|
23
|
+
Provider credentials, Bot Framework URL validation, outbound deadlines, and
|
|
24
|
+
agent ownership remain enforced by `services/microsoft`.
|
|
25
|
+
|
|
26
|
+
See [Alfe](https://alfe.ai) and the [documentation](https://docs.alfe.ai).
|
package/dist/index.cjs
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
const require_plugin = require("./
|
|
2
|
-
module.exports = require_plugin;
|
|
1
|
+
const require_plugin = require("./plugin2.cjs");
|
|
2
|
+
module.exports = require_plugin.plugin_default;
|
package/dist/index.d.cts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { t as
|
|
2
|
-
export {
|
|
1
|
+
import { t as _default } from "./plugin.cjs";
|
|
2
|
+
export { _default as default };
|
package/dist/index.d.ts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { t as
|
|
2
|
-
export {
|
|
1
|
+
import { t as _default } from "./plugin.js";
|
|
2
|
+
export { _default as default };
|
package/dist/index.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import
|
|
2
|
-
export {
|
|
1
|
+
import { t as plugin_default } from "./plugin2.js";
|
|
2
|
+
export { plugin_default as default };
|
package/dist/plugin.cjs
CHANGED
|
@@ -1,101 +1,2 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
let _alfe_ai_agent_api_client = require("@alfe.ai/agent-api-client");
|
|
4
|
-
let _alfe_ai_openclaw_plugin_kit = require("@alfe.ai/openclaw-plugin-kit");
|
|
5
|
-
//#region src/plugin.ts
|
|
6
|
-
/**
|
|
7
|
-
* @alfe/openclaw-teams — OpenClaw native plugin
|
|
8
|
-
*
|
|
9
|
-
* Registers Microsoft Teams tools with OpenClaw. Tools call the Alfe
|
|
10
|
-
* agent API via @alfe.ai/agent-api-client.
|
|
11
|
-
*
|
|
12
|
-
* This plugin provides:
|
|
13
|
-
* - teams_send_message — send a text message to a Teams conversation
|
|
14
|
-
* - teams_send_card — send an adaptive card to a Teams conversation
|
|
15
|
-
* - teams_list_channels — list Teams channels the bot has access to
|
|
16
|
-
*/
|
|
17
|
-
const pkg = (0, require("node:module").createRequire)(require("url").pathToFileURL(__filename).href)("../package.json");
|
|
18
|
-
const TEAMS_ACTIVATION_KEY = (0, _alfe_ai_openclaw_plugin_kit.getActivationKey)("teams");
|
|
19
|
-
let client = null;
|
|
20
|
-
function getClient() {
|
|
21
|
-
if (!client) throw new Error("Teams plugin not initialized — no API client");
|
|
22
|
-
return client;
|
|
23
|
-
}
|
|
24
|
-
const teamsTools = [
|
|
25
|
-
(0, _alfe_ai_openclaw_plugin_kit.defineTool)({
|
|
26
|
-
name: "teams_send_message",
|
|
27
|
-
description: "Send a text message to a Microsoft Teams conversation. You need the conversationId from an inbound Teams message or from teams_list_channels.",
|
|
28
|
-
parameters: _sinclair_typebox.Type.Object({
|
|
29
|
-
conversationId: _sinclair_typebox.Type.String({ description: "The Teams conversation ID to send the message to" }),
|
|
30
|
-
text: _sinclair_typebox.Type.String({ description: "The message text to send" })
|
|
31
|
-
}),
|
|
32
|
-
handler: async (params) => {
|
|
33
|
-
const { conversationId, text } = params;
|
|
34
|
-
return getClient().sendTeamsMessage({
|
|
35
|
-
conversationId,
|
|
36
|
-
text
|
|
37
|
-
});
|
|
38
|
-
}
|
|
39
|
-
}),
|
|
40
|
-
(0, _alfe_ai_openclaw_plugin_kit.defineTool)({
|
|
41
|
-
name: "teams_send_card",
|
|
42
|
-
description: "Send an Adaptive Card to a Microsoft Teams conversation. Adaptive Cards support rich formatting, inputs, and actions. Pass a valid Adaptive Card JSON object.",
|
|
43
|
-
parameters: _sinclair_typebox.Type.Object({
|
|
44
|
-
conversationId: _sinclair_typebox.Type.String({ description: "The Teams conversation ID to send the card to" }),
|
|
45
|
-
adaptiveCard: _sinclair_typebox.Type.Object({}, {
|
|
46
|
-
description: "Adaptive Card JSON payload (see https://adaptivecards.io)",
|
|
47
|
-
additionalProperties: true
|
|
48
|
-
})
|
|
49
|
-
}),
|
|
50
|
-
handler: async (params) => {
|
|
51
|
-
const { conversationId, adaptiveCard } = params;
|
|
52
|
-
return getClient().sendTeamsMessage({
|
|
53
|
-
conversationId,
|
|
54
|
-
adaptiveCard
|
|
55
|
-
});
|
|
56
|
-
}
|
|
57
|
-
}),
|
|
58
|
-
(0, _alfe_ai_openclaw_plugin_kit.defineTool)({
|
|
59
|
-
name: "teams_list_channels",
|
|
60
|
-
description: "List Microsoft Teams channels and conversations that the bot has access to. Returns channel IDs that can be used with teams_send_message and teams_send_card.",
|
|
61
|
-
parameters: _sinclair_typebox.Type.Object({}),
|
|
62
|
-
handler: async () => {
|
|
63
|
-
return getClient().listTeamsChannels();
|
|
64
|
-
}
|
|
65
|
-
})
|
|
66
|
-
];
|
|
67
|
-
const plugin = {
|
|
68
|
-
id: "@alfe.ai/openclaw-teams",
|
|
69
|
-
name: "Alfe Teams Plugin",
|
|
70
|
-
description: "Microsoft Teams integration — send messages and adaptive cards",
|
|
71
|
-
version: pkg.version,
|
|
72
|
-
activate(api) {
|
|
73
|
-
(0, _alfe_ai_agent_api_client.installToolErrorCapture)(api, { plugin: "openclaw-teams" });
|
|
74
|
-
const log = api.logger;
|
|
75
|
-
for (const tool of teamsTools) api.registerTool(tool);
|
|
76
|
-
log.info(`Registered ${String(teamsTools.length)} Teams tools: ${teamsTools.map((t) => t.name).join(", ")}`);
|
|
77
|
-
(0, _alfe_ai_openclaw_plugin_kit.guardedStart)(TEAMS_ACTIVATION_KEY, log, () => {
|
|
78
|
-
log.info("Alfe Teams plugin activating...");
|
|
79
|
-
try {
|
|
80
|
-
const config = (0, _alfe_ai_config.resolveConfig)();
|
|
81
|
-
client = new _alfe_ai_agent_api_client.AgentApiClient({
|
|
82
|
-
apiKey: config.apiKey,
|
|
83
|
-
apiUrl: config.apiUrl
|
|
84
|
-
});
|
|
85
|
-
} catch (err) {
|
|
86
|
-
log.error(`Failed to resolve config: ${err instanceof Error ? err.message : String(err)}`);
|
|
87
|
-
log.warn("Teams tools will fail — no API config available");
|
|
88
|
-
(0, _alfe_ai_openclaw_plugin_kit.resetActivation)(TEAMS_ACTIVATION_KEY);
|
|
89
|
-
return;
|
|
90
|
-
}
|
|
91
|
-
log.info("Alfe Teams plugin activated");
|
|
92
|
-
});
|
|
93
|
-
},
|
|
94
|
-
deactivate(api) {
|
|
95
|
-
client = null;
|
|
96
|
-
(0, _alfe_ai_openclaw_plugin_kit.resetActivation)(TEAMS_ACTIVATION_KEY);
|
|
97
|
-
api.logger.info("Alfe Teams plugin deactivated");
|
|
98
|
-
}
|
|
99
|
-
};
|
|
100
|
-
//#endregion
|
|
101
|
-
module.exports = plugin;
|
|
1
|
+
const require_plugin = require("./plugin2.cjs");
|
|
2
|
+
module.exports = require_plugin.plugin_default;
|
package/dist/plugin.d.cts
CHANGED
|
@@ -1,20 +1,8 @@
|
|
|
1
|
-
import { TSchema } from "@sinclair/typebox";
|
|
2
|
-
|
|
3
1
|
//#region ../openclaw-plugin-kit/dist/index.d.ts
|
|
4
2
|
|
|
5
3
|
//# sourceMappingURL=types.d.ts.map
|
|
6
4
|
//#endregion
|
|
7
5
|
//#region src/tools.d.ts
|
|
8
|
-
/**
|
|
9
|
-
* Tool-definition helpers — the `defineTool` / `ok` / `errResult` trio that
|
|
10
|
-
* was copy-pasted across 8 plugins (identity, google, teams, mobile,
|
|
11
|
-
* whatsapp, voice, chat a2a-tools, base openclaw).
|
|
12
|
-
*
|
|
13
|
-
* Error handling is standardized on the openclaw-google variant — the only
|
|
14
|
-
* copy that survived non-`Error` throws (`e instanceof Error ? e.message :
|
|
15
|
-
* "Unknown error"`). The other copies did `(e as Error).message`, which
|
|
16
|
-
* crashes the tool executor when a handler throws a string/object.
|
|
17
|
-
*/
|
|
18
6
|
/** Shape returned to OpenClaw from a tool `execute`. */
|
|
19
7
|
interface ToolResult {
|
|
20
8
|
content: {
|
|
@@ -22,15 +10,8 @@ interface ToolResult {
|
|
|
22
10
|
text: string;
|
|
23
11
|
}[];
|
|
24
12
|
details: unknown;
|
|
13
|
+
isError?: boolean;
|
|
25
14
|
}
|
|
26
|
-
/**
|
|
27
|
-
* An OpenClaw tool definition.
|
|
28
|
-
*
|
|
29
|
-
* `parameters` is generic because the fleet is split between TypeBox
|
|
30
|
-
* `TSchema` schemas (identity/google/teams/mobile/whatsapp) and plain
|
|
31
|
-
* JSON-Schema objects (chat a2a-tools). Instantiate with whichever schema
|
|
32
|
-
* type the plugin uses — the kit itself has no schema dependency.
|
|
33
|
-
*/
|
|
34
15
|
interface ToolDef<TParameters = unknown> {
|
|
35
16
|
name: string;
|
|
36
17
|
description: string;
|
|
@@ -38,25 +19,23 @@ interface ToolDef<TParameters = unknown> {
|
|
|
38
19
|
parameters: TParameters;
|
|
39
20
|
execute: (toolCallId: string, params: Record<string, unknown>) => Promise<ToolResult>;
|
|
40
21
|
}
|
|
41
|
-
/**
|
|
22
|
+
/** Deliberately model-safe validation/usage failure. Other exceptions stay private. */
|
|
42
23
|
//#endregion
|
|
43
|
-
//#region src/
|
|
24
|
+
//#region src/types.d.ts
|
|
44
25
|
interface Logger {
|
|
45
|
-
info(
|
|
46
|
-
warn(
|
|
47
|
-
error(
|
|
48
|
-
debug(
|
|
26
|
+
info(message: string, ...args: unknown[]): void;
|
|
27
|
+
warn(message: string, ...args: unknown[]): void;
|
|
28
|
+
error(message: string, ...args: unknown[]): void;
|
|
29
|
+
debug(message: string, ...args: unknown[]): void;
|
|
49
30
|
}
|
|
50
31
|
interface OpenClawPluginApi {
|
|
51
32
|
logger: Logger;
|
|
52
|
-
|
|
53
|
-
registerTool(tool: ToolDef<TSchema>): void;
|
|
54
|
-
registerGatewayMethod(name: string, handler: (...args: unknown[]) => Promise<unknown>): void;
|
|
55
|
-
on(event: string, handler: (...args: unknown[]) => void | Promise<void>, options?: {
|
|
56
|
-
priority?: number;
|
|
57
|
-
}): void;
|
|
33
|
+
registerTool(tool: ToolDef): void;
|
|
58
34
|
}
|
|
59
|
-
|
|
35
|
+
//# sourceMappingURL=types.d.ts.map
|
|
36
|
+
//#endregion
|
|
37
|
+
//#region src/plugin.d.ts
|
|
38
|
+
declare const _default: {
|
|
60
39
|
id: string;
|
|
61
40
|
name: string;
|
|
62
41
|
description: string;
|
|
@@ -65,5 +44,5 @@ declare const plugin: {
|
|
|
65
44
|
deactivate(api: OpenClawPluginApi): void;
|
|
66
45
|
};
|
|
67
46
|
//#endregion
|
|
68
|
-
export {
|
|
47
|
+
export { _default as t };
|
|
69
48
|
//# sourceMappingURL=plugin.d.cts.map
|
package/dist/plugin.d.cts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"plugin.d.cts","names":["KitLogger","ToolResult","ToolDef","TParameters","Record","Promise","ok","errResult","defineTool","IpcResponse","IpcClient","ConnectToDaemonOptions","connectToDaemon","ResolveOpenClawSdkOptions","resolveOpenClawSdk","T","getActivationKey","isActivated","resetActivation","guardedStart"
|
|
1
|
+
{"version":3,"file":"plugin.d.cts","names":["KitLogger","ToolResult","ToolDef","TParameters","Record","Promise","PublicToolError","Error","publicToolError","ok","errResult","defineTool","IpcResponse","IpcClient","IpcModule","ConnectToDaemonOptions","ConnectToDaemonDependencies","connectToDaemon","ResolveOpenClawSdkOptions","ResolveOpenClawSdkDependencies","resolveOpenClawSdk","T","getActivationKey","isActivated","resetActivation","guardedStart"],"sources":["../../openclaw-plugin-kit/dist/index.d.ts","../src/types.ts","../src/plugin.ts"],"sourcesContent":["//#region src/types.d.ts\n/**\n * Minimal logger contract shared by every helper in the kit.\n *\n * Deliberately the *narrowest* shape in the fleet: some plugins declare\n * variadic loggers (`info(msg: string, ...args: unknown[])`), others\n * single-arg (`info(msg: string)`). Both are assignable to this. The kit\n * only ever calls with a single string.\n */\ninterface KitLogger {\n info(msg: string): void;\n warn(msg: string): void;\n error(msg: string): void;\n debug(msg: string): void;\n}\n//# sourceMappingURL=types.d.ts.map\n//#endregion\n//#region src/tools.d.ts\n/** Shape returned to OpenClaw from a tool `execute`. */\ninterface ToolResult {\n content: {\n type: \"text\";\n text: string;\n }[];\n details: unknown;\n isError?: boolean;\n}\ninterface ToolDef<TParameters = unknown> {\n name: string;\n description: string;\n label: string;\n parameters: TParameters;\n execute: (toolCallId: string, params: Record<string, unknown>) => Promise<ToolResult>;\n}\n/** Deliberately model-safe validation/usage failure. Other exceptions stay private. */\ndeclare class PublicToolError extends Error {\n readonly name = \"PublicToolError\";\n}\ndeclare function publicToolError(message: string): PublicToolError;\n/** Wrap bounded JSON in the OpenClaw tool-result envelope. */\ndeclare function ok(data: unknown): ToolResult;\n/** Wrap an explicitly public error in a captured OpenClaw error envelope. */\ndeclare function errResult(message: string): ToolResult;\n/**\n * Define a tool whose unexpected exceptions never expose provider, filesystem,\n * credential, or transport diagnostics to the model. Throw `PublicToolError`\n * only for a message deliberately safe for model display.\n */\ndeclare function defineTool<TParameters>(def: {\n name: string;\n description: string;\n parameters: TParameters;\n handler: (params: Record<string, unknown>) => Promise<unknown>;\n}): ToolDef<TParameters>;\n//# sourceMappingURL=tools.d.ts.map\n//#endregion\n//#region src/daemon.d.ts\ninterface IpcResponse {\n ok: boolean;\n error?: {\n message?: string;\n };\n [key: string]: unknown;\n}\ninterface IpcClient {\n on(event: string, handler: (...args: unknown[]) => void): void;\n request(method: string, params: Record<string, unknown>): Promise<IpcResponse>;\n start(): void;\n stop(): void;\n}\ninterface IpcModule {\n IPCClient: new (socketPath: string, log: KitLogger) => IpcClient;\n}\ninterface ConnectToDaemonOptions {\n pluginId: string;\n capabilities?: readonly string[];\n onMessage?: (msg: Record<string, unknown>) => void;\n standaloneNote?: string;\n}\ninterface ConnectToDaemonDependencies {\n loadIpcModule?: () => Promise<IpcModule>;\n}\ndeclare function connectToDaemon(socketPath: string, log: KitLogger, options: ConnectToDaemonOptions, dependencies?: ConnectToDaemonDependencies): Promise<IpcClient | null>;\n//#endregion\n//#region src/sdk.d.ts\ninterface ResolveOpenClawSdkOptions {\n specifier?: string;\n exportName?: string;\n unresolvableNote?: string;\n}\ninterface ResolveOpenClawSdkDependencies {\n anchors?: readonly string[];\n globalPackageJsonPath?: string;\n requireFrom?: (anchor: string) => (specifier: string) => unknown;\n}\ndeclare function resolveOpenClawSdk<T = unknown>(log: KitLogger, options?: ResolveOpenClawSdkOptions, dependencies?: ResolveOpenClawSdkDependencies): T | null;\n//# sourceMappingURL=sdk.d.ts.map\n\n//#endregion\n//#region src/activation.d.ts\n/**\n * Canonical activation-flag key for a plugin: `__alfe<Name>PluginActivated`.\n *\n * Accepts a short name (`\"google\"`, `\"google-chat\"`) or a full package name\n * (`\"@alfe.ai/openclaw-teams\"`); scope + `openclaw-` prefix are stripped\n * and the remainder PascalCased:\n *\n * getActivationKey(\"google\") → \"__alfeGooglePluginActivated\"\n * getActivationKey(\"google-chat\") → \"__alfeGoogleChatPluginActivated\"\n * getActivationKey(\"@alfe.ai/openclaw-teams\") → \"__alfeTeamsPluginActivated\"\n */\ndeclare function getActivationKey(name: string): string;\n/** True when the activation flag for `key` is currently set. */\ndeclare function isActivated(key: string): boolean;\n/**\n * Clear the activation flag so a later start can run again.\n *\n * Call this LAST in stop/deactivate paths — after side effects are stopped\n * (see the ordering rule in the module doc). Also exported for soft-failure\n * paths inside a `guardedStart` fn that want to log at a custom level and\n * return normally instead of throwing.\n */\ndeclare function resetActivation(key: string): void;\n/**\n * Run a service start exactly once per activation cycle.\n *\n * - If the flag is already set, logs at debug and returns `false` (skipped).\n * - Otherwise sets the flag and runs `fn`.\n * - If `fn` throws synchronously OR returns a promise that rejects, the\n * flag is RESET (so a later activate can retry) and the error is logged.\n * Errors are not rethrown — matching fleet behavior where a failed start\n * must never crash the host's plugin loader.\n *\n * Returns `true` when the start was initiated (even if an async portion\n * later fails), `false` when skipped or when `fn` threw synchronously.\n *\n * Note: cleaning up partial side effects on failure is `fn`'s job (throw\n * only after tearing down what was started); the kit only guarantees the\n * flag reset happens after `fn` has failed — i.e. after `fn`'s own cleanup.\n */\ndeclare function guardedStart(key: string, log: KitLogger, fn: () => void | Promise<void>): boolean;\n//# sourceMappingURL=activation.d.ts.map\n\n//#endregion\nexport { type ConnectToDaemonDependencies, type ConnectToDaemonOptions, type IpcClient, type IpcResponse, type KitLogger, PublicToolError, type ResolveOpenClawSdkDependencies, type ResolveOpenClawSdkOptions, type ToolDef, type ToolResult, connectToDaemon, defineTool, errResult, getActivationKey, guardedStart, isActivated, ok, publicToolError, resetActivation, resolveOpenClawSdk };\n//# sourceMappingURL=index.d.ts.map"],"mappings":";;;;;;UAmBUC,UAAAA;;;;;;;;UAQAC;;;;cAIIC;wCAC0BC,4BAA4BC,QAAQJ;;;;;AAblEA,UCjBO,MAAA,CDiBG;EAQVC,IAAAA,CAAAA,OAAO,EAAA,MAAA,EAAA,GAAA,IAAA,EAAA,OAAA,EAAA,CAAA,EAAA,IAAA;EAAA,IAAA,CAAA,OAAA,EAAA,MAAA,EAAA,GAAA,IAAA,EAAA,OAAA,EAAA,CAAA,EAAA,IAAA;OAIHC,CAAAA,OAAAA,EAAAA,MAAAA,EAAAA,GAAAA,IAAAA,EAAAA,OAAAA,EAAAA,CAAAA,EAAAA,IAAAA;OAC0BC,CAAAA,OAAAA,EAAAA,MAAAA,EAAAA,GAAAA,IAAAA,EAAAA,OAAAA,EAAAA,CAAAA,EAAAA,IAAAA;;UCVvB,iBAAA;UACP;qBACW;;;;;;EDLXH,EAAAA,EAAAA,MAAAA;EAQAC,IAAAA,EAAAA,MAAO;EAAA,WAAA,EAAA,MAAA;SAIHC,EAAAA,MAAAA;UAC0BC,CAAAA,GAAAA,mBAAAA,CAAAA,EAAAA,IAAAA;YAAoCH,CAAAA,GAAAA,mBAAAA,CAAAA,EAAAA,IAAAA"}
|
package/dist/plugin.d.ts
CHANGED
|
@@ -1,20 +1,8 @@
|
|
|
1
|
-
import { TSchema } from "@sinclair/typebox";
|
|
2
|
-
|
|
3
1
|
//#region ../openclaw-plugin-kit/dist/index.d.ts
|
|
4
2
|
|
|
5
3
|
//# sourceMappingURL=types.d.ts.map
|
|
6
4
|
//#endregion
|
|
7
5
|
//#region src/tools.d.ts
|
|
8
|
-
/**
|
|
9
|
-
* Tool-definition helpers — the `defineTool` / `ok` / `errResult` trio that
|
|
10
|
-
* was copy-pasted across 8 plugins (identity, google, teams, mobile,
|
|
11
|
-
* whatsapp, voice, chat a2a-tools, base openclaw).
|
|
12
|
-
*
|
|
13
|
-
* Error handling is standardized on the openclaw-google variant — the only
|
|
14
|
-
* copy that survived non-`Error` throws (`e instanceof Error ? e.message :
|
|
15
|
-
* "Unknown error"`). The other copies did `(e as Error).message`, which
|
|
16
|
-
* crashes the tool executor when a handler throws a string/object.
|
|
17
|
-
*/
|
|
18
6
|
/** Shape returned to OpenClaw from a tool `execute`. */
|
|
19
7
|
interface ToolResult {
|
|
20
8
|
content: {
|
|
@@ -22,15 +10,8 @@ interface ToolResult {
|
|
|
22
10
|
text: string;
|
|
23
11
|
}[];
|
|
24
12
|
details: unknown;
|
|
13
|
+
isError?: boolean;
|
|
25
14
|
}
|
|
26
|
-
/**
|
|
27
|
-
* An OpenClaw tool definition.
|
|
28
|
-
*
|
|
29
|
-
* `parameters` is generic because the fleet is split between TypeBox
|
|
30
|
-
* `TSchema` schemas (identity/google/teams/mobile/whatsapp) and plain
|
|
31
|
-
* JSON-Schema objects (chat a2a-tools). Instantiate with whichever schema
|
|
32
|
-
* type the plugin uses — the kit itself has no schema dependency.
|
|
33
|
-
*/
|
|
34
15
|
interface ToolDef<TParameters = unknown> {
|
|
35
16
|
name: string;
|
|
36
17
|
description: string;
|
|
@@ -38,25 +19,23 @@ interface ToolDef<TParameters = unknown> {
|
|
|
38
19
|
parameters: TParameters;
|
|
39
20
|
execute: (toolCallId: string, params: Record<string, unknown>) => Promise<ToolResult>;
|
|
40
21
|
}
|
|
41
|
-
/**
|
|
22
|
+
/** Deliberately model-safe validation/usage failure. Other exceptions stay private. */
|
|
42
23
|
//#endregion
|
|
43
|
-
//#region src/
|
|
24
|
+
//#region src/types.d.ts
|
|
44
25
|
interface Logger {
|
|
45
|
-
info(
|
|
46
|
-
warn(
|
|
47
|
-
error(
|
|
48
|
-
debug(
|
|
26
|
+
info(message: string, ...args: unknown[]): void;
|
|
27
|
+
warn(message: string, ...args: unknown[]): void;
|
|
28
|
+
error(message: string, ...args: unknown[]): void;
|
|
29
|
+
debug(message: string, ...args: unknown[]): void;
|
|
49
30
|
}
|
|
50
31
|
interface OpenClawPluginApi {
|
|
51
32
|
logger: Logger;
|
|
52
|
-
|
|
53
|
-
registerTool(tool: ToolDef<TSchema>): void;
|
|
54
|
-
registerGatewayMethod(name: string, handler: (...args: unknown[]) => Promise<unknown>): void;
|
|
55
|
-
on(event: string, handler: (...args: unknown[]) => void | Promise<void>, options?: {
|
|
56
|
-
priority?: number;
|
|
57
|
-
}): void;
|
|
33
|
+
registerTool(tool: ToolDef): void;
|
|
58
34
|
}
|
|
59
|
-
|
|
35
|
+
//# sourceMappingURL=types.d.ts.map
|
|
36
|
+
//#endregion
|
|
37
|
+
//#region src/plugin.d.ts
|
|
38
|
+
declare const _default: {
|
|
60
39
|
id: string;
|
|
61
40
|
name: string;
|
|
62
41
|
description: string;
|
|
@@ -65,5 +44,5 @@ declare const plugin: {
|
|
|
65
44
|
deactivate(api: OpenClawPluginApi): void;
|
|
66
45
|
};
|
|
67
46
|
//#endregion
|
|
68
|
-
export {
|
|
47
|
+
export { _default as t };
|
|
69
48
|
//# sourceMappingURL=plugin.d.ts.map
|
package/dist/plugin.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"plugin.d.ts","names":["KitLogger","ToolResult","ToolDef","TParameters","Record","Promise","ok","errResult","defineTool","IpcResponse","IpcClient","ConnectToDaemonOptions","connectToDaemon","ResolveOpenClawSdkOptions","resolveOpenClawSdk","T","getActivationKey","isActivated","resetActivation","guardedStart"
|
|
1
|
+
{"version":3,"file":"plugin.d.ts","names":["KitLogger","ToolResult","ToolDef","TParameters","Record","Promise","PublicToolError","Error","publicToolError","ok","errResult","defineTool","IpcResponse","IpcClient","IpcModule","ConnectToDaemonOptions","ConnectToDaemonDependencies","connectToDaemon","ResolveOpenClawSdkOptions","ResolveOpenClawSdkDependencies","resolveOpenClawSdk","T","getActivationKey","isActivated","resetActivation","guardedStart"],"sources":["../../openclaw-plugin-kit/dist/index.d.ts","../src/types.ts","../src/plugin.ts"],"sourcesContent":["//#region src/types.d.ts\n/**\n * Minimal logger contract shared by every helper in the kit.\n *\n * Deliberately the *narrowest* shape in the fleet: some plugins declare\n * variadic loggers (`info(msg: string, ...args: unknown[])`), others\n * single-arg (`info(msg: string)`). Both are assignable to this. The kit\n * only ever calls with a single string.\n */\ninterface KitLogger {\n info(msg: string): void;\n warn(msg: string): void;\n error(msg: string): void;\n debug(msg: string): void;\n}\n//# sourceMappingURL=types.d.ts.map\n//#endregion\n//#region src/tools.d.ts\n/** Shape returned to OpenClaw from a tool `execute`. */\ninterface ToolResult {\n content: {\n type: \"text\";\n text: string;\n }[];\n details: unknown;\n isError?: boolean;\n}\ninterface ToolDef<TParameters = unknown> {\n name: string;\n description: string;\n label: string;\n parameters: TParameters;\n execute: (toolCallId: string, params: Record<string, unknown>) => Promise<ToolResult>;\n}\n/** Deliberately model-safe validation/usage failure. Other exceptions stay private. */\ndeclare class PublicToolError extends Error {\n readonly name = \"PublicToolError\";\n}\ndeclare function publicToolError(message: string): PublicToolError;\n/** Wrap bounded JSON in the OpenClaw tool-result envelope. */\ndeclare function ok(data: unknown): ToolResult;\n/** Wrap an explicitly public error in a captured OpenClaw error envelope. */\ndeclare function errResult(message: string): ToolResult;\n/**\n * Define a tool whose unexpected exceptions never expose provider, filesystem,\n * credential, or transport diagnostics to the model. Throw `PublicToolError`\n * only for a message deliberately safe for model display.\n */\ndeclare function defineTool<TParameters>(def: {\n name: string;\n description: string;\n parameters: TParameters;\n handler: (params: Record<string, unknown>) => Promise<unknown>;\n}): ToolDef<TParameters>;\n//# sourceMappingURL=tools.d.ts.map\n//#endregion\n//#region src/daemon.d.ts\ninterface IpcResponse {\n ok: boolean;\n error?: {\n message?: string;\n };\n [key: string]: unknown;\n}\ninterface IpcClient {\n on(event: string, handler: (...args: unknown[]) => void): void;\n request(method: string, params: Record<string, unknown>): Promise<IpcResponse>;\n start(): void;\n stop(): void;\n}\ninterface IpcModule {\n IPCClient: new (socketPath: string, log: KitLogger) => IpcClient;\n}\ninterface ConnectToDaemonOptions {\n pluginId: string;\n capabilities?: readonly string[];\n onMessage?: (msg: Record<string, unknown>) => void;\n standaloneNote?: string;\n}\ninterface ConnectToDaemonDependencies {\n loadIpcModule?: () => Promise<IpcModule>;\n}\ndeclare function connectToDaemon(socketPath: string, log: KitLogger, options: ConnectToDaemonOptions, dependencies?: ConnectToDaemonDependencies): Promise<IpcClient | null>;\n//#endregion\n//#region src/sdk.d.ts\ninterface ResolveOpenClawSdkOptions {\n specifier?: string;\n exportName?: string;\n unresolvableNote?: string;\n}\ninterface ResolveOpenClawSdkDependencies {\n anchors?: readonly string[];\n globalPackageJsonPath?: string;\n requireFrom?: (anchor: string) => (specifier: string) => unknown;\n}\ndeclare function resolveOpenClawSdk<T = unknown>(log: KitLogger, options?: ResolveOpenClawSdkOptions, dependencies?: ResolveOpenClawSdkDependencies): T | null;\n//# sourceMappingURL=sdk.d.ts.map\n\n//#endregion\n//#region src/activation.d.ts\n/**\n * Canonical activation-flag key for a plugin: `__alfe<Name>PluginActivated`.\n *\n * Accepts a short name (`\"google\"`, `\"google-chat\"`) or a full package name\n * (`\"@alfe.ai/openclaw-teams\"`); scope + `openclaw-` prefix are stripped\n * and the remainder PascalCased:\n *\n * getActivationKey(\"google\") → \"__alfeGooglePluginActivated\"\n * getActivationKey(\"google-chat\") → \"__alfeGoogleChatPluginActivated\"\n * getActivationKey(\"@alfe.ai/openclaw-teams\") → \"__alfeTeamsPluginActivated\"\n */\ndeclare function getActivationKey(name: string): string;\n/** True when the activation flag for `key` is currently set. */\ndeclare function isActivated(key: string): boolean;\n/**\n * Clear the activation flag so a later start can run again.\n *\n * Call this LAST in stop/deactivate paths — after side effects are stopped\n * (see the ordering rule in the module doc). Also exported for soft-failure\n * paths inside a `guardedStart` fn that want to log at a custom level and\n * return normally instead of throwing.\n */\ndeclare function resetActivation(key: string): void;\n/**\n * Run a service start exactly once per activation cycle.\n *\n * - If the flag is already set, logs at debug and returns `false` (skipped).\n * - Otherwise sets the flag and runs `fn`.\n * - If `fn` throws synchronously OR returns a promise that rejects, the\n * flag is RESET (so a later activate can retry) and the error is logged.\n * Errors are not rethrown — matching fleet behavior where a failed start\n * must never crash the host's plugin loader.\n *\n * Returns `true` when the start was initiated (even if an async portion\n * later fails), `false` when skipped or when `fn` threw synchronously.\n *\n * Note: cleaning up partial side effects on failure is `fn`'s job (throw\n * only after tearing down what was started); the kit only guarantees the\n * flag reset happens after `fn` has failed — i.e. after `fn`'s own cleanup.\n */\ndeclare function guardedStart(key: string, log: KitLogger, fn: () => void | Promise<void>): boolean;\n//# sourceMappingURL=activation.d.ts.map\n\n//#endregion\nexport { type ConnectToDaemonDependencies, type ConnectToDaemonOptions, type IpcClient, type IpcResponse, type KitLogger, PublicToolError, type ResolveOpenClawSdkDependencies, type ResolveOpenClawSdkOptions, type ToolDef, type ToolResult, connectToDaemon, defineTool, errResult, getActivationKey, guardedStart, isActivated, ok, publicToolError, resetActivation, resolveOpenClawSdk };\n//# sourceMappingURL=index.d.ts.map"],"mappings":";;;;;;UAmBUC,UAAAA;;;;;;;;UAQAC;;;;cAIIC;wCAC0BC,4BAA4BC,QAAQJ;;;;;AAblEA,UCjBO,MAAA,CDiBG;EAQVC,IAAAA,CAAAA,OAAO,EAAA,MAAA,EAAA,GAAA,IAAA,EAAA,OAAA,EAAA,CAAA,EAAA,IAAA;EAAA,IAAA,CAAA,OAAA,EAAA,MAAA,EAAA,GAAA,IAAA,EAAA,OAAA,EAAA,CAAA,EAAA,IAAA;OAIHC,CAAAA,OAAAA,EAAAA,MAAAA,EAAAA,GAAAA,IAAAA,EAAAA,OAAAA,EAAAA,CAAAA,EAAAA,IAAAA;OAC0BC,CAAAA,OAAAA,EAAAA,MAAAA,EAAAA,GAAAA,IAAAA,EAAAA,OAAAA,EAAAA,CAAAA,EAAAA,IAAAA;;UCVvB,iBAAA;UACP;qBACW;;;;;;EDLXH,EAAAA,EAAAA,MAAAA;EAQAC,IAAAA,EAAAA,MAAO;EAAA,WAAA,EAAA,MAAA;SAIHC,EAAAA,MAAAA;UAC0BC,CAAAA,GAAAA,mBAAAA,CAAAA,EAAAA,IAAAA;YAAoCH,CAAAA,GAAAA,mBAAAA,CAAAA,EAAAA,IAAAA"}
|
package/dist/plugin.js
CHANGED
|
@@ -1,104 +1,2 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
3
|
-
import { resolveConfig } from "@alfe.ai/config";
|
|
4
|
-
import { AgentApiClient, installToolErrorCapture } from "@alfe.ai/agent-api-client";
|
|
5
|
-
import { defineTool, getActivationKey, guardedStart, resetActivation } from "@alfe.ai/openclaw-plugin-kit";
|
|
6
|
-
//#region src/plugin.ts
|
|
7
|
-
/**
|
|
8
|
-
* @alfe/openclaw-teams — OpenClaw native plugin
|
|
9
|
-
*
|
|
10
|
-
* Registers Microsoft Teams tools with OpenClaw. Tools call the Alfe
|
|
11
|
-
* agent API via @alfe.ai/agent-api-client.
|
|
12
|
-
*
|
|
13
|
-
* This plugin provides:
|
|
14
|
-
* - teams_send_message — send a text message to a Teams conversation
|
|
15
|
-
* - teams_send_card — send an adaptive card to a Teams conversation
|
|
16
|
-
* - teams_list_channels — list Teams channels the bot has access to
|
|
17
|
-
*/
|
|
18
|
-
const pkg = createRequire(import.meta.url)("../package.json");
|
|
19
|
-
const TEAMS_ACTIVATION_KEY = getActivationKey("teams");
|
|
20
|
-
let client = null;
|
|
21
|
-
function getClient() {
|
|
22
|
-
if (!client) throw new Error("Teams plugin not initialized — no API client");
|
|
23
|
-
return client;
|
|
24
|
-
}
|
|
25
|
-
const teamsTools = [
|
|
26
|
-
defineTool({
|
|
27
|
-
name: "teams_send_message",
|
|
28
|
-
description: "Send a text message to a Microsoft Teams conversation. You need the conversationId from an inbound Teams message or from teams_list_channels.",
|
|
29
|
-
parameters: Type.Object({
|
|
30
|
-
conversationId: Type.String({ description: "The Teams conversation ID to send the message to" }),
|
|
31
|
-
text: Type.String({ description: "The message text to send" })
|
|
32
|
-
}),
|
|
33
|
-
handler: async (params) => {
|
|
34
|
-
const { conversationId, text } = params;
|
|
35
|
-
return getClient().sendTeamsMessage({
|
|
36
|
-
conversationId,
|
|
37
|
-
text
|
|
38
|
-
});
|
|
39
|
-
}
|
|
40
|
-
}),
|
|
41
|
-
defineTool({
|
|
42
|
-
name: "teams_send_card",
|
|
43
|
-
description: "Send an Adaptive Card to a Microsoft Teams conversation. Adaptive Cards support rich formatting, inputs, and actions. Pass a valid Adaptive Card JSON object.",
|
|
44
|
-
parameters: Type.Object({
|
|
45
|
-
conversationId: Type.String({ description: "The Teams conversation ID to send the card to" }),
|
|
46
|
-
adaptiveCard: Type.Object({}, {
|
|
47
|
-
description: "Adaptive Card JSON payload (see https://adaptivecards.io)",
|
|
48
|
-
additionalProperties: true
|
|
49
|
-
})
|
|
50
|
-
}),
|
|
51
|
-
handler: async (params) => {
|
|
52
|
-
const { conversationId, adaptiveCard } = params;
|
|
53
|
-
return getClient().sendTeamsMessage({
|
|
54
|
-
conversationId,
|
|
55
|
-
adaptiveCard
|
|
56
|
-
});
|
|
57
|
-
}
|
|
58
|
-
}),
|
|
59
|
-
defineTool({
|
|
60
|
-
name: "teams_list_channels",
|
|
61
|
-
description: "List Microsoft Teams channels and conversations that the bot has access to. Returns channel IDs that can be used with teams_send_message and teams_send_card.",
|
|
62
|
-
parameters: Type.Object({}),
|
|
63
|
-
handler: async () => {
|
|
64
|
-
return getClient().listTeamsChannels();
|
|
65
|
-
}
|
|
66
|
-
})
|
|
67
|
-
];
|
|
68
|
-
const plugin = {
|
|
69
|
-
id: "@alfe.ai/openclaw-teams",
|
|
70
|
-
name: "Alfe Teams Plugin",
|
|
71
|
-
description: "Microsoft Teams integration — send messages and adaptive cards",
|
|
72
|
-
version: pkg.version,
|
|
73
|
-
activate(api) {
|
|
74
|
-
installToolErrorCapture(api, { plugin: "openclaw-teams" });
|
|
75
|
-
const log = api.logger;
|
|
76
|
-
for (const tool of teamsTools) api.registerTool(tool);
|
|
77
|
-
log.info(`Registered ${String(teamsTools.length)} Teams tools: ${teamsTools.map((t) => t.name).join(", ")}`);
|
|
78
|
-
guardedStart(TEAMS_ACTIVATION_KEY, log, () => {
|
|
79
|
-
log.info("Alfe Teams plugin activating...");
|
|
80
|
-
try {
|
|
81
|
-
const config = resolveConfig();
|
|
82
|
-
client = new AgentApiClient({
|
|
83
|
-
apiKey: config.apiKey,
|
|
84
|
-
apiUrl: config.apiUrl
|
|
85
|
-
});
|
|
86
|
-
} catch (err) {
|
|
87
|
-
log.error(`Failed to resolve config: ${err instanceof Error ? err.message : String(err)}`);
|
|
88
|
-
log.warn("Teams tools will fail — no API config available");
|
|
89
|
-
resetActivation(TEAMS_ACTIVATION_KEY);
|
|
90
|
-
return;
|
|
91
|
-
}
|
|
92
|
-
log.info("Alfe Teams plugin activated");
|
|
93
|
-
});
|
|
94
|
-
},
|
|
95
|
-
deactivate(api) {
|
|
96
|
-
client = null;
|
|
97
|
-
resetActivation(TEAMS_ACTIVATION_KEY);
|
|
98
|
-
api.logger.info("Alfe Teams plugin deactivated");
|
|
99
|
-
}
|
|
100
|
-
};
|
|
101
|
-
//#endregion
|
|
102
|
-
export { plugin as default };
|
|
103
|
-
|
|
104
|
-
//# sourceMappingURL=plugin.js.map
|
|
1
|
+
import { t as plugin_default } from "./plugin2.js";
|
|
2
|
+
export { plugin_default as default };
|
package/dist/plugin2.cjs
ADDED
|
@@ -0,0 +1,262 @@
|
|
|
1
|
+
let node_module = require("node:module");
|
|
2
|
+
let _alfe_ai_agent_api_client = require("@alfe.ai/agent-api-client");
|
|
3
|
+
let _alfe_ai_config = require("@alfe.ai/config");
|
|
4
|
+
let _alfe_ai_openclaw_plugin_kit = require("@alfe.ai/openclaw-plugin-kit");
|
|
5
|
+
let _sinclair_typebox = require("@sinclair/typebox");
|
|
6
|
+
const MAX_MESSAGE_TEXT_CHARS = 4e4;
|
|
7
|
+
const MAX_ADAPTIVE_CARD_BYTES = 100 * 1024;
|
|
8
|
+
const MAX_ADAPTIVE_CARD_STRING_CHARS = 4e4;
|
|
9
|
+
const MAX_CHANNELS = 200;
|
|
10
|
+
const MAX_INSPECTED_CHANNELS = 1e3;
|
|
11
|
+
const MAX_CHANNEL_ID_CHARS = 1024;
|
|
12
|
+
const MAX_CHANNEL_NAME_CHARS = 512;
|
|
13
|
+
const MAX_CHANNEL_DESCRIPTION_CHARS = 1e3;
|
|
14
|
+
const MAX_ACTIVITY_ID_CHARS = 1024;
|
|
15
|
+
const FORBIDDEN_CARD_KEYS = new Set([
|
|
16
|
+
"__proto__",
|
|
17
|
+
"constructor",
|
|
18
|
+
"prototype"
|
|
19
|
+
]);
|
|
20
|
+
function parseMessageInput(params) {
|
|
21
|
+
requireExactFields(params, new Set(["conversationId", "text"]), "teams_send_message");
|
|
22
|
+
return {
|
|
23
|
+
conversationId: requireBoundedString(params.conversationId, "conversationId", 256, false),
|
|
24
|
+
text: requireBoundedString(params.text, "text", MAX_MESSAGE_TEXT_CHARS, true)
|
|
25
|
+
};
|
|
26
|
+
}
|
|
27
|
+
function parseCardInput(params) {
|
|
28
|
+
requireExactFields(params, new Set(["conversationId", "adaptiveCard"]), "teams_send_card");
|
|
29
|
+
const conversationId = requireBoundedString(params.conversationId, "conversationId", 256, false);
|
|
30
|
+
const adaptiveCard = cloneAdaptiveCard(params.adaptiveCard);
|
|
31
|
+
const serialized = JSON.stringify(adaptiveCard);
|
|
32
|
+
if (Buffer.byteLength(serialized, "utf8") > 102400) throw (0, _alfe_ai_openclaw_plugin_kit.publicToolError)(`adaptiveCard must not exceed ${String(MAX_ADAPTIVE_CARD_BYTES)} bytes`);
|
|
33
|
+
return {
|
|
34
|
+
conversationId,
|
|
35
|
+
adaptiveCard
|
|
36
|
+
};
|
|
37
|
+
}
|
|
38
|
+
function parseListInput(params) {
|
|
39
|
+
requireExactFields(params, /* @__PURE__ */ new Set(), "teams_list_channels");
|
|
40
|
+
}
|
|
41
|
+
function projectSendResponse(raw) {
|
|
42
|
+
const response = asRecord(raw);
|
|
43
|
+
const activityId = boundedExternalText(response?.activityId, MAX_ACTIVITY_ID_CHARS);
|
|
44
|
+
if (response?.ok !== true || !activityId) throw new Error("Microsoft send response did not match the expected contract");
|
|
45
|
+
return {
|
|
46
|
+
ok: true,
|
|
47
|
+
activityId
|
|
48
|
+
};
|
|
49
|
+
}
|
|
50
|
+
function projectChannelsResponse(raw) {
|
|
51
|
+
const response = asRecord(raw);
|
|
52
|
+
if (!Array.isArray(response?.channels)) throw new Error("Microsoft channels response did not contain a channel list");
|
|
53
|
+
const channels = [];
|
|
54
|
+
const inspectionLimit = Math.min(response.channels.length, MAX_INSPECTED_CHANNELS);
|
|
55
|
+
for (let index = 0; index < inspectionLimit && channels.length < MAX_CHANNELS; index += 1) {
|
|
56
|
+
const candidate = asRecord(response.channels[index]);
|
|
57
|
+
const id = boundedExternalText(candidate?.id, MAX_CHANNEL_ID_CHARS);
|
|
58
|
+
const name = boundedExternalText(candidate?.name, MAX_CHANNEL_NAME_CHARS);
|
|
59
|
+
if (!id || !name) continue;
|
|
60
|
+
const description = boundedExternalText(candidate?.description, MAX_CHANNEL_DESCRIPTION_CHARS);
|
|
61
|
+
channels.push({
|
|
62
|
+
id,
|
|
63
|
+
name,
|
|
64
|
+
...description ? { description } : {}
|
|
65
|
+
});
|
|
66
|
+
}
|
|
67
|
+
return {
|
|
68
|
+
channels,
|
|
69
|
+
truncated: response.channels.length > channels.length,
|
|
70
|
+
notice: "Teams channel names and descriptions are untrusted external content."
|
|
71
|
+
};
|
|
72
|
+
}
|
|
73
|
+
function cloneAdaptiveCard(value) {
|
|
74
|
+
if (!isPlainRecord(value)) throw (0, _alfe_ai_openclaw_plugin_kit.publicToolError)("adaptiveCard must be a JSON object");
|
|
75
|
+
return cloneJsonValue(value, 0, {
|
|
76
|
+
entries: 0,
|
|
77
|
+
seen: /* @__PURE__ */ new WeakSet()
|
|
78
|
+
});
|
|
79
|
+
}
|
|
80
|
+
function cloneJsonValue(value, depth, state) {
|
|
81
|
+
if (depth > 12) throw (0, _alfe_ai_openclaw_plugin_kit.publicToolError)(`adaptiveCard must not exceed ${String(12)} nested levels`);
|
|
82
|
+
if (value === null || typeof value === "boolean") return value;
|
|
83
|
+
if (typeof value === "string") {
|
|
84
|
+
if (value.length > 4e4 || hasForbiddenTextControl(value)) throw (0, _alfe_ai_openclaw_plugin_kit.publicToolError)(`adaptiveCard strings must contain at most ${String(MAX_ADAPTIVE_CARD_STRING_CHARS)} safe characters`);
|
|
85
|
+
return value;
|
|
86
|
+
}
|
|
87
|
+
if (typeof value === "number") {
|
|
88
|
+
if (!Number.isFinite(value)) throw (0, _alfe_ai_openclaw_plugin_kit.publicToolError)("adaptiveCard numbers must be finite");
|
|
89
|
+
return value;
|
|
90
|
+
}
|
|
91
|
+
if (typeof value !== "object") throw (0, _alfe_ai_openclaw_plugin_kit.publicToolError)("adaptiveCard must contain only JSON values");
|
|
92
|
+
if (state.seen.has(value)) throw (0, _alfe_ai_openclaw_plugin_kit.publicToolError)("adaptiveCard must not contain cycles");
|
|
93
|
+
state.seen.add(value);
|
|
94
|
+
if (Array.isArray(value)) {
|
|
95
|
+
const keys = Object.keys(value);
|
|
96
|
+
if (keys.length !== value.length || keys.some((key, index) => key !== String(index))) throw (0, _alfe_ai_openclaw_plugin_kit.publicToolError)("adaptiveCard arrays must be dense JSON arrays");
|
|
97
|
+
addEntries(state, value.length);
|
|
98
|
+
return value.map((item) => cloneJsonValue(item, depth + 1, state));
|
|
99
|
+
}
|
|
100
|
+
if (!isPlainRecord(value)) throw (0, _alfe_ai_openclaw_plugin_kit.publicToolError)("adaptiveCard must contain only JSON objects");
|
|
101
|
+
const keys = Object.keys(value);
|
|
102
|
+
if (Object.getOwnPropertyNames(value).length !== keys.length || Object.getOwnPropertySymbols(value).length > 0) throw (0, _alfe_ai_openclaw_plugin_kit.publicToolError)("adaptiveCard objects must contain only enumerable string fields");
|
|
103
|
+
addEntries(state, keys.length);
|
|
104
|
+
const output = Object.create(null);
|
|
105
|
+
for (const key of keys) {
|
|
106
|
+
const descriptor = Object.getOwnPropertyDescriptor(value, key);
|
|
107
|
+
if (!descriptor || !("value" in descriptor) || !descriptor.enumerable || key.length < 1 || key.length > 128 || hasForbiddenTextControl(key) || FORBIDDEN_CARD_KEYS.has(key)) throw (0, _alfe_ai_openclaw_plugin_kit.publicToolError)(`adaptiveCard keys must contain 1 to ${String(128)} safe characters`);
|
|
108
|
+
output[key] = cloneJsonValue(descriptor.value, depth + 1, state);
|
|
109
|
+
}
|
|
110
|
+
return output;
|
|
111
|
+
}
|
|
112
|
+
function addEntries(state, count) {
|
|
113
|
+
state.entries += count;
|
|
114
|
+
if (state.entries > 200) throw (0, _alfe_ai_openclaw_plugin_kit.publicToolError)(`adaptiveCard must not exceed ${String(200)} total entries`);
|
|
115
|
+
}
|
|
116
|
+
function requireExactFields(params, allowed, toolName) {
|
|
117
|
+
if (Object.keys(params).some((key) => !allowed.has(key))) throw (0, _alfe_ai_openclaw_plugin_kit.publicToolError)(`${toolName} contains unsupported fields`);
|
|
118
|
+
}
|
|
119
|
+
function requireBoundedString(value, label, maximum, allowFormattingControls) {
|
|
120
|
+
if (typeof value !== "string" || value.length < 1 || value.length > maximum || (allowFormattingControls ? hasForbiddenTextControl(value) : hasAnyControl(value))) throw (0, _alfe_ai_openclaw_plugin_kit.publicToolError)(`${label} must contain 1 to ${String(maximum)} safe characters`);
|
|
121
|
+
return value;
|
|
122
|
+
}
|
|
123
|
+
function boundedExternalText(value, maximum) {
|
|
124
|
+
if (typeof value !== "string" || value.length < 1) return void 0;
|
|
125
|
+
return flattenExternalControls(value).replace(/\s+/gu, " ").trim().slice(0, maximum) || void 0;
|
|
126
|
+
}
|
|
127
|
+
function isPlainRecord(value) {
|
|
128
|
+
if (typeof value !== "object" || value === null || Array.isArray(value)) return false;
|
|
129
|
+
const prototype = Object.getPrototypeOf(value);
|
|
130
|
+
return prototype === Object.prototype || prototype === null;
|
|
131
|
+
}
|
|
132
|
+
function hasAnyControl(value) {
|
|
133
|
+
for (let index = 0; index < value.length; index += 1) {
|
|
134
|
+
const code = value.charCodeAt(index);
|
|
135
|
+
if (code < 32 || code >= 127 && code <= 159) return true;
|
|
136
|
+
}
|
|
137
|
+
return false;
|
|
138
|
+
}
|
|
139
|
+
function flattenExternalControls(value) {
|
|
140
|
+
let output = "";
|
|
141
|
+
for (let index = 0; index < value.length; index += 1) {
|
|
142
|
+
const code = value.charCodeAt(index);
|
|
143
|
+
output += code < 32 || code >= 127 && code <= 159 ? " " : value[index];
|
|
144
|
+
}
|
|
145
|
+
return output;
|
|
146
|
+
}
|
|
147
|
+
function hasForbiddenTextControl(value) {
|
|
148
|
+
for (let index = 0; index < value.length; index += 1) {
|
|
149
|
+
const code = value.charCodeAt(index);
|
|
150
|
+
if (code < 32 && code !== 9 && code !== 10 && code !== 13 || code >= 127 && code <= 159) return true;
|
|
151
|
+
}
|
|
152
|
+
return false;
|
|
153
|
+
}
|
|
154
|
+
function asRecord(value) {
|
|
155
|
+
return typeof value === "object" && value !== null && !Array.isArray(value) ? value : void 0;
|
|
156
|
+
}
|
|
157
|
+
//#endregion
|
|
158
|
+
//#region src/tools.ts
|
|
159
|
+
function createTeamsTools(resolver) {
|
|
160
|
+
return [
|
|
161
|
+
(0, _alfe_ai_openclaw_plugin_kit.defineTool)({
|
|
162
|
+
name: "teams_send_message",
|
|
163
|
+
description: "Send text to a Microsoft Teams conversation. Use a conversationId from an inbound Teams message or teams_list_channels.",
|
|
164
|
+
parameters: _sinclair_typebox.Type.Object({
|
|
165
|
+
conversationId: _sinclair_typebox.Type.String({
|
|
166
|
+
minLength: 1,
|
|
167
|
+
maxLength: 256
|
|
168
|
+
}),
|
|
169
|
+
text: _sinclair_typebox.Type.String({
|
|
170
|
+
minLength: 1,
|
|
171
|
+
maxLength: MAX_MESSAGE_TEXT_CHARS
|
|
172
|
+
})
|
|
173
|
+
}, { additionalProperties: false }),
|
|
174
|
+
handler: async (params) => {
|
|
175
|
+
const input = parseMessageInput(params);
|
|
176
|
+
return projectSendResponse(await resolver.getClient().sendTeamsMessage(input));
|
|
177
|
+
}
|
|
178
|
+
}),
|
|
179
|
+
(0, _alfe_ai_openclaw_plugin_kit.defineTool)({
|
|
180
|
+
name: "teams_send_card",
|
|
181
|
+
description: "Send a bounded Adaptive Card JSON object to a Microsoft Teams conversation.",
|
|
182
|
+
parameters: _sinclair_typebox.Type.Object({
|
|
183
|
+
conversationId: _sinclair_typebox.Type.String({
|
|
184
|
+
minLength: 1,
|
|
185
|
+
maxLength: 256
|
|
186
|
+
}),
|
|
187
|
+
adaptiveCard: _sinclair_typebox.Type.Object({}, { additionalProperties: true })
|
|
188
|
+
}, { additionalProperties: false }),
|
|
189
|
+
handler: async (params) => {
|
|
190
|
+
const input = parseCardInput(params);
|
|
191
|
+
return projectSendResponse(await resolver.getClient().sendTeamsMessage(input));
|
|
192
|
+
}
|
|
193
|
+
}),
|
|
194
|
+
(0, _alfe_ai_openclaw_plugin_kit.defineTool)({
|
|
195
|
+
name: "teams_list_channels",
|
|
196
|
+
description: "List bounded Microsoft Teams channel and conversation metadata available to this agent's bot.",
|
|
197
|
+
parameters: _sinclair_typebox.Type.Object({}, { additionalProperties: false }),
|
|
198
|
+
handler: async (params) => {
|
|
199
|
+
parseListInput(params);
|
|
200
|
+
return projectChannelsResponse(await resolver.getClient().listTeamsChannels());
|
|
201
|
+
}
|
|
202
|
+
})
|
|
203
|
+
];
|
|
204
|
+
}
|
|
205
|
+
const PLUGIN_VERSION = validatePackageVersion((0, node_module.createRequire)(require("url").pathToFileURL(__filename).href)("../package.json").version);
|
|
206
|
+
function createTeamsPlugin(dependencies = {}) {
|
|
207
|
+
const resolveRuntimeConfig = dependencies.resolveConfig ?? _alfe_ai_config.resolveConfig;
|
|
208
|
+
const createClient = dependencies.createClient ?? ((config) => new _alfe_ai_agent_api_client.AgentApiClient(config));
|
|
209
|
+
const installErrorCapture = dependencies.installErrorCapture ?? _alfe_ai_agent_api_client.installToolErrorCapture;
|
|
210
|
+
const resolver = createTeamsClientResolver(resolveRuntimeConfig, createClient);
|
|
211
|
+
return {
|
|
212
|
+
id: "@alfe.ai/openclaw-teams",
|
|
213
|
+
name: "Alfe Teams Plugin",
|
|
214
|
+
description: "Microsoft Teams integration — send messages and adaptive cards",
|
|
215
|
+
version: PLUGIN_VERSION,
|
|
216
|
+
activate(api) {
|
|
217
|
+
installErrorCapture(api, { plugin: "openclaw-teams" });
|
|
218
|
+
const tools = createTeamsTools(resolver);
|
|
219
|
+
for (const tool of tools) api.registerTool(tool);
|
|
220
|
+
api.logger.info(`Registered ${String(tools.length)} Microsoft Teams tools`);
|
|
221
|
+
},
|
|
222
|
+
deactivate(api) {
|
|
223
|
+
api.logger.info("Microsoft Teams plugin deactivated");
|
|
224
|
+
}
|
|
225
|
+
};
|
|
226
|
+
}
|
|
227
|
+
function createTeamsClientResolver(resolveRuntimeConfig, createClient) {
|
|
228
|
+
let cached;
|
|
229
|
+
return { getClient() {
|
|
230
|
+
let config;
|
|
231
|
+
try {
|
|
232
|
+
config = resolveRuntimeConfig();
|
|
233
|
+
} catch {
|
|
234
|
+
throw (0, _alfe_ai_openclaw_plugin_kit.publicToolError)("Microsoft Teams is not configured; run alfe setup or repair ~/.alfe/config.toml, then retry.");
|
|
235
|
+
}
|
|
236
|
+
if (cached?.apiKey === config.apiKey && cached.apiUrl === config.apiUrl) return cached.client;
|
|
237
|
+
const client = createClient({
|
|
238
|
+
apiKey: config.apiKey,
|
|
239
|
+
apiUrl: config.apiUrl
|
|
240
|
+
});
|
|
241
|
+
cached = {
|
|
242
|
+
apiKey: config.apiKey,
|
|
243
|
+
apiUrl: config.apiUrl,
|
|
244
|
+
client
|
|
245
|
+
};
|
|
246
|
+
return client;
|
|
247
|
+
} };
|
|
248
|
+
}
|
|
249
|
+
function validatePackageVersion(value) {
|
|
250
|
+
if (typeof value !== "string" || !/^\d+\.\d+\.\d+(?:-[0-9A-Za-z.-]+)?$/u.test(value)) throw new Error("openclaw-teams package version is invalid");
|
|
251
|
+
return value;
|
|
252
|
+
}
|
|
253
|
+
//#endregion
|
|
254
|
+
//#region src/plugin.ts
|
|
255
|
+
var plugin_default = createTeamsPlugin();
|
|
256
|
+
//#endregion
|
|
257
|
+
Object.defineProperty(exports, "plugin_default", {
|
|
258
|
+
enumerable: true,
|
|
259
|
+
get: function() {
|
|
260
|
+
return plugin_default;
|
|
261
|
+
}
|
|
262
|
+
});
|
package/dist/plugin2.d.cts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { t as
|
|
2
|
-
export {
|
|
1
|
+
import { t as _default } from "./plugin.cjs";
|
|
2
|
+
export { _default as default };
|
package/dist/plugin2.d.ts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { t as
|
|
2
|
-
export {
|
|
1
|
+
import { t as _default } from "./plugin.js";
|
|
2
|
+
export { _default as default };
|
package/dist/plugin2.js
ADDED
|
@@ -0,0 +1,259 @@
|
|
|
1
|
+
import { createRequire } from "node:module";
|
|
2
|
+
import { AgentApiClient, installToolErrorCapture } from "@alfe.ai/agent-api-client";
|
|
3
|
+
import { resolveConfig } from "@alfe.ai/config";
|
|
4
|
+
import { defineTool, publicToolError } from "@alfe.ai/openclaw-plugin-kit";
|
|
5
|
+
import { Type } from "@sinclair/typebox";
|
|
6
|
+
const MAX_MESSAGE_TEXT_CHARS = 4e4;
|
|
7
|
+
const MAX_ADAPTIVE_CARD_BYTES = 100 * 1024;
|
|
8
|
+
const MAX_ADAPTIVE_CARD_STRING_CHARS = 4e4;
|
|
9
|
+
const MAX_CHANNELS = 200;
|
|
10
|
+
const MAX_INSPECTED_CHANNELS = 1e3;
|
|
11
|
+
const MAX_CHANNEL_ID_CHARS = 1024;
|
|
12
|
+
const MAX_CHANNEL_NAME_CHARS = 512;
|
|
13
|
+
const MAX_CHANNEL_DESCRIPTION_CHARS = 1e3;
|
|
14
|
+
const MAX_ACTIVITY_ID_CHARS = 1024;
|
|
15
|
+
const FORBIDDEN_CARD_KEYS = new Set([
|
|
16
|
+
"__proto__",
|
|
17
|
+
"constructor",
|
|
18
|
+
"prototype"
|
|
19
|
+
]);
|
|
20
|
+
function parseMessageInput(params) {
|
|
21
|
+
requireExactFields(params, new Set(["conversationId", "text"]), "teams_send_message");
|
|
22
|
+
return {
|
|
23
|
+
conversationId: requireBoundedString(params.conversationId, "conversationId", 256, false),
|
|
24
|
+
text: requireBoundedString(params.text, "text", MAX_MESSAGE_TEXT_CHARS, true)
|
|
25
|
+
};
|
|
26
|
+
}
|
|
27
|
+
function parseCardInput(params) {
|
|
28
|
+
requireExactFields(params, new Set(["conversationId", "adaptiveCard"]), "teams_send_card");
|
|
29
|
+
const conversationId = requireBoundedString(params.conversationId, "conversationId", 256, false);
|
|
30
|
+
const adaptiveCard = cloneAdaptiveCard(params.adaptiveCard);
|
|
31
|
+
const serialized = JSON.stringify(adaptiveCard);
|
|
32
|
+
if (Buffer.byteLength(serialized, "utf8") > 102400) throw publicToolError(`adaptiveCard must not exceed ${String(MAX_ADAPTIVE_CARD_BYTES)} bytes`);
|
|
33
|
+
return {
|
|
34
|
+
conversationId,
|
|
35
|
+
adaptiveCard
|
|
36
|
+
};
|
|
37
|
+
}
|
|
38
|
+
function parseListInput(params) {
|
|
39
|
+
requireExactFields(params, /* @__PURE__ */ new Set(), "teams_list_channels");
|
|
40
|
+
}
|
|
41
|
+
function projectSendResponse(raw) {
|
|
42
|
+
const response = asRecord(raw);
|
|
43
|
+
const activityId = boundedExternalText(response?.activityId, MAX_ACTIVITY_ID_CHARS);
|
|
44
|
+
if (response?.ok !== true || !activityId) throw new Error("Microsoft send response did not match the expected contract");
|
|
45
|
+
return {
|
|
46
|
+
ok: true,
|
|
47
|
+
activityId
|
|
48
|
+
};
|
|
49
|
+
}
|
|
50
|
+
function projectChannelsResponse(raw) {
|
|
51
|
+
const response = asRecord(raw);
|
|
52
|
+
if (!Array.isArray(response?.channels)) throw new Error("Microsoft channels response did not contain a channel list");
|
|
53
|
+
const channels = [];
|
|
54
|
+
const inspectionLimit = Math.min(response.channels.length, MAX_INSPECTED_CHANNELS);
|
|
55
|
+
for (let index = 0; index < inspectionLimit && channels.length < MAX_CHANNELS; index += 1) {
|
|
56
|
+
const candidate = asRecord(response.channels[index]);
|
|
57
|
+
const id = boundedExternalText(candidate?.id, MAX_CHANNEL_ID_CHARS);
|
|
58
|
+
const name = boundedExternalText(candidate?.name, MAX_CHANNEL_NAME_CHARS);
|
|
59
|
+
if (!id || !name) continue;
|
|
60
|
+
const description = boundedExternalText(candidate?.description, MAX_CHANNEL_DESCRIPTION_CHARS);
|
|
61
|
+
channels.push({
|
|
62
|
+
id,
|
|
63
|
+
name,
|
|
64
|
+
...description ? { description } : {}
|
|
65
|
+
});
|
|
66
|
+
}
|
|
67
|
+
return {
|
|
68
|
+
channels,
|
|
69
|
+
truncated: response.channels.length > channels.length,
|
|
70
|
+
notice: "Teams channel names and descriptions are untrusted external content."
|
|
71
|
+
};
|
|
72
|
+
}
|
|
73
|
+
function cloneAdaptiveCard(value) {
|
|
74
|
+
if (!isPlainRecord(value)) throw publicToolError("adaptiveCard must be a JSON object");
|
|
75
|
+
return cloneJsonValue(value, 0, {
|
|
76
|
+
entries: 0,
|
|
77
|
+
seen: /* @__PURE__ */ new WeakSet()
|
|
78
|
+
});
|
|
79
|
+
}
|
|
80
|
+
function cloneJsonValue(value, depth, state) {
|
|
81
|
+
if (depth > 12) throw publicToolError(`adaptiveCard must not exceed ${String(12)} nested levels`);
|
|
82
|
+
if (value === null || typeof value === "boolean") return value;
|
|
83
|
+
if (typeof value === "string") {
|
|
84
|
+
if (value.length > 4e4 || hasForbiddenTextControl(value)) throw publicToolError(`adaptiveCard strings must contain at most ${String(MAX_ADAPTIVE_CARD_STRING_CHARS)} safe characters`);
|
|
85
|
+
return value;
|
|
86
|
+
}
|
|
87
|
+
if (typeof value === "number") {
|
|
88
|
+
if (!Number.isFinite(value)) throw publicToolError("adaptiveCard numbers must be finite");
|
|
89
|
+
return value;
|
|
90
|
+
}
|
|
91
|
+
if (typeof value !== "object") throw publicToolError("adaptiveCard must contain only JSON values");
|
|
92
|
+
if (state.seen.has(value)) throw publicToolError("adaptiveCard must not contain cycles");
|
|
93
|
+
state.seen.add(value);
|
|
94
|
+
if (Array.isArray(value)) {
|
|
95
|
+
const keys = Object.keys(value);
|
|
96
|
+
if (keys.length !== value.length || keys.some((key, index) => key !== String(index))) throw publicToolError("adaptiveCard arrays must be dense JSON arrays");
|
|
97
|
+
addEntries(state, value.length);
|
|
98
|
+
return value.map((item) => cloneJsonValue(item, depth + 1, state));
|
|
99
|
+
}
|
|
100
|
+
if (!isPlainRecord(value)) throw publicToolError("adaptiveCard must contain only JSON objects");
|
|
101
|
+
const keys = Object.keys(value);
|
|
102
|
+
if (Object.getOwnPropertyNames(value).length !== keys.length || Object.getOwnPropertySymbols(value).length > 0) throw publicToolError("adaptiveCard objects must contain only enumerable string fields");
|
|
103
|
+
addEntries(state, keys.length);
|
|
104
|
+
const output = Object.create(null);
|
|
105
|
+
for (const key of keys) {
|
|
106
|
+
const descriptor = Object.getOwnPropertyDescriptor(value, key);
|
|
107
|
+
if (!descriptor || !("value" in descriptor) || !descriptor.enumerable || key.length < 1 || key.length > 128 || hasForbiddenTextControl(key) || FORBIDDEN_CARD_KEYS.has(key)) throw publicToolError(`adaptiveCard keys must contain 1 to ${String(128)} safe characters`);
|
|
108
|
+
output[key] = cloneJsonValue(descriptor.value, depth + 1, state);
|
|
109
|
+
}
|
|
110
|
+
return output;
|
|
111
|
+
}
|
|
112
|
+
function addEntries(state, count) {
|
|
113
|
+
state.entries += count;
|
|
114
|
+
if (state.entries > 200) throw publicToolError(`adaptiveCard must not exceed ${String(200)} total entries`);
|
|
115
|
+
}
|
|
116
|
+
function requireExactFields(params, allowed, toolName) {
|
|
117
|
+
if (Object.keys(params).some((key) => !allowed.has(key))) throw publicToolError(`${toolName} contains unsupported fields`);
|
|
118
|
+
}
|
|
119
|
+
function requireBoundedString(value, label, maximum, allowFormattingControls) {
|
|
120
|
+
if (typeof value !== "string" || value.length < 1 || value.length > maximum || (allowFormattingControls ? hasForbiddenTextControl(value) : hasAnyControl(value))) throw publicToolError(`${label} must contain 1 to ${String(maximum)} safe characters`);
|
|
121
|
+
return value;
|
|
122
|
+
}
|
|
123
|
+
function boundedExternalText(value, maximum) {
|
|
124
|
+
if (typeof value !== "string" || value.length < 1) return void 0;
|
|
125
|
+
return flattenExternalControls(value).replace(/\s+/gu, " ").trim().slice(0, maximum) || void 0;
|
|
126
|
+
}
|
|
127
|
+
function isPlainRecord(value) {
|
|
128
|
+
if (typeof value !== "object" || value === null || Array.isArray(value)) return false;
|
|
129
|
+
const prototype = Object.getPrototypeOf(value);
|
|
130
|
+
return prototype === Object.prototype || prototype === null;
|
|
131
|
+
}
|
|
132
|
+
function hasAnyControl(value) {
|
|
133
|
+
for (let index = 0; index < value.length; index += 1) {
|
|
134
|
+
const code = value.charCodeAt(index);
|
|
135
|
+
if (code < 32 || code >= 127 && code <= 159) return true;
|
|
136
|
+
}
|
|
137
|
+
return false;
|
|
138
|
+
}
|
|
139
|
+
function flattenExternalControls(value) {
|
|
140
|
+
let output = "";
|
|
141
|
+
for (let index = 0; index < value.length; index += 1) {
|
|
142
|
+
const code = value.charCodeAt(index);
|
|
143
|
+
output += code < 32 || code >= 127 && code <= 159 ? " " : value[index];
|
|
144
|
+
}
|
|
145
|
+
return output;
|
|
146
|
+
}
|
|
147
|
+
function hasForbiddenTextControl(value) {
|
|
148
|
+
for (let index = 0; index < value.length; index += 1) {
|
|
149
|
+
const code = value.charCodeAt(index);
|
|
150
|
+
if (code < 32 && code !== 9 && code !== 10 && code !== 13 || code >= 127 && code <= 159) return true;
|
|
151
|
+
}
|
|
152
|
+
return false;
|
|
153
|
+
}
|
|
154
|
+
function asRecord(value) {
|
|
155
|
+
return typeof value === "object" && value !== null && !Array.isArray(value) ? value : void 0;
|
|
156
|
+
}
|
|
157
|
+
//#endregion
|
|
158
|
+
//#region src/tools.ts
|
|
159
|
+
function createTeamsTools(resolver) {
|
|
160
|
+
return [
|
|
161
|
+
defineTool({
|
|
162
|
+
name: "teams_send_message",
|
|
163
|
+
description: "Send text to a Microsoft Teams conversation. Use a conversationId from an inbound Teams message or teams_list_channels.",
|
|
164
|
+
parameters: Type.Object({
|
|
165
|
+
conversationId: Type.String({
|
|
166
|
+
minLength: 1,
|
|
167
|
+
maxLength: 256
|
|
168
|
+
}),
|
|
169
|
+
text: Type.String({
|
|
170
|
+
minLength: 1,
|
|
171
|
+
maxLength: MAX_MESSAGE_TEXT_CHARS
|
|
172
|
+
})
|
|
173
|
+
}, { additionalProperties: false }),
|
|
174
|
+
handler: async (params) => {
|
|
175
|
+
const input = parseMessageInput(params);
|
|
176
|
+
return projectSendResponse(await resolver.getClient().sendTeamsMessage(input));
|
|
177
|
+
}
|
|
178
|
+
}),
|
|
179
|
+
defineTool({
|
|
180
|
+
name: "teams_send_card",
|
|
181
|
+
description: "Send a bounded Adaptive Card JSON object to a Microsoft Teams conversation.",
|
|
182
|
+
parameters: Type.Object({
|
|
183
|
+
conversationId: Type.String({
|
|
184
|
+
minLength: 1,
|
|
185
|
+
maxLength: 256
|
|
186
|
+
}),
|
|
187
|
+
adaptiveCard: Type.Object({}, { additionalProperties: true })
|
|
188
|
+
}, { additionalProperties: false }),
|
|
189
|
+
handler: async (params) => {
|
|
190
|
+
const input = parseCardInput(params);
|
|
191
|
+
return projectSendResponse(await resolver.getClient().sendTeamsMessage(input));
|
|
192
|
+
}
|
|
193
|
+
}),
|
|
194
|
+
defineTool({
|
|
195
|
+
name: "teams_list_channels",
|
|
196
|
+
description: "List bounded Microsoft Teams channel and conversation metadata available to this agent's bot.",
|
|
197
|
+
parameters: Type.Object({}, { additionalProperties: false }),
|
|
198
|
+
handler: async (params) => {
|
|
199
|
+
parseListInput(params);
|
|
200
|
+
return projectChannelsResponse(await resolver.getClient().listTeamsChannels());
|
|
201
|
+
}
|
|
202
|
+
})
|
|
203
|
+
];
|
|
204
|
+
}
|
|
205
|
+
const PLUGIN_VERSION = validatePackageVersion(createRequire(import.meta.url)("../package.json").version);
|
|
206
|
+
function createTeamsPlugin(dependencies = {}) {
|
|
207
|
+
const resolveRuntimeConfig = dependencies.resolveConfig ?? resolveConfig;
|
|
208
|
+
const createClient = dependencies.createClient ?? ((config) => new AgentApiClient(config));
|
|
209
|
+
const installErrorCapture = dependencies.installErrorCapture ?? installToolErrorCapture;
|
|
210
|
+
const resolver = createTeamsClientResolver(resolveRuntimeConfig, createClient);
|
|
211
|
+
return {
|
|
212
|
+
id: "@alfe.ai/openclaw-teams",
|
|
213
|
+
name: "Alfe Teams Plugin",
|
|
214
|
+
description: "Microsoft Teams integration — send messages and adaptive cards",
|
|
215
|
+
version: PLUGIN_VERSION,
|
|
216
|
+
activate(api) {
|
|
217
|
+
installErrorCapture(api, { plugin: "openclaw-teams" });
|
|
218
|
+
const tools = createTeamsTools(resolver);
|
|
219
|
+
for (const tool of tools) api.registerTool(tool);
|
|
220
|
+
api.logger.info(`Registered ${String(tools.length)} Microsoft Teams tools`);
|
|
221
|
+
},
|
|
222
|
+
deactivate(api) {
|
|
223
|
+
api.logger.info("Microsoft Teams plugin deactivated");
|
|
224
|
+
}
|
|
225
|
+
};
|
|
226
|
+
}
|
|
227
|
+
function createTeamsClientResolver(resolveRuntimeConfig, createClient) {
|
|
228
|
+
let cached;
|
|
229
|
+
return { getClient() {
|
|
230
|
+
let config;
|
|
231
|
+
try {
|
|
232
|
+
config = resolveRuntimeConfig();
|
|
233
|
+
} catch {
|
|
234
|
+
throw publicToolError("Microsoft Teams is not configured; run alfe setup or repair ~/.alfe/config.toml, then retry.");
|
|
235
|
+
}
|
|
236
|
+
if (cached?.apiKey === config.apiKey && cached.apiUrl === config.apiUrl) return cached.client;
|
|
237
|
+
const client = createClient({
|
|
238
|
+
apiKey: config.apiKey,
|
|
239
|
+
apiUrl: config.apiUrl
|
|
240
|
+
});
|
|
241
|
+
cached = {
|
|
242
|
+
apiKey: config.apiKey,
|
|
243
|
+
apiUrl: config.apiUrl,
|
|
244
|
+
client
|
|
245
|
+
};
|
|
246
|
+
return client;
|
|
247
|
+
} };
|
|
248
|
+
}
|
|
249
|
+
function validatePackageVersion(value) {
|
|
250
|
+
if (typeof value !== "string" || !/^\d+\.\d+\.\d+(?:-[0-9A-Za-z.-]+)?$/u.test(value)) throw new Error("openclaw-teams package version is invalid");
|
|
251
|
+
return value;
|
|
252
|
+
}
|
|
253
|
+
//#endregion
|
|
254
|
+
//#region src/plugin.ts
|
|
255
|
+
var plugin_default = createTeamsPlugin();
|
|
256
|
+
//#endregion
|
|
257
|
+
export { plugin_default as t };
|
|
258
|
+
|
|
259
|
+
//# sourceMappingURL=plugin2.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"plugin2.js","names":[],"sources":["../src/boundary.ts","../src/tools.ts","../src/runtime.ts","../src/plugin.ts"],"sourcesContent":["import { publicToolError } from \"@alfe.ai/openclaw-plugin-kit\";\n\nexport const MAX_CONVERSATION_ID_CHARS = 256;\nexport const MAX_MESSAGE_TEXT_CHARS = 40_000;\nexport const MAX_ADAPTIVE_CARD_BYTES = 100 * 1024;\nexport const MAX_ADAPTIVE_CARD_DEPTH = 12;\nexport const MAX_ADAPTIVE_CARD_ENTRIES = 200;\nexport const MAX_ADAPTIVE_CARD_KEY_CHARS = 128;\nexport const MAX_ADAPTIVE_CARD_STRING_CHARS = 40_000;\nconst MAX_CHANNELS = 200;\nconst MAX_INSPECTED_CHANNELS = 1_000;\nconst MAX_CHANNEL_ID_CHARS = 1_024;\nconst MAX_CHANNEL_NAME_CHARS = 512;\nconst MAX_CHANNEL_DESCRIPTION_CHARS = 1_000;\nconst MAX_ACTIVITY_ID_CHARS = 1_024;\nconst FORBIDDEN_CARD_KEYS = new Set([\"__proto__\", \"constructor\", \"prototype\"]);\n\ntype JsonValue = null | boolean | number | string | JsonValue[] | { [key: string]: JsonValue };\n\nexport function parseMessageInput(params: Record<string, unknown>): {\n conversationId: string;\n text: string;\n} {\n requireExactFields(params, new Set([\"conversationId\", \"text\"]), \"teams_send_message\");\n return {\n conversationId: requireBoundedString(\n params.conversationId,\n \"conversationId\",\n MAX_CONVERSATION_ID_CHARS,\n false,\n ),\n text: requireBoundedString(params.text, \"text\", MAX_MESSAGE_TEXT_CHARS, true),\n };\n}\n\nexport function parseCardInput(params: Record<string, unknown>): {\n conversationId: string;\n adaptiveCard: Record<string, unknown>;\n} {\n requireExactFields(params, new Set([\"conversationId\", \"adaptiveCard\"]), \"teams_send_card\");\n const conversationId = requireBoundedString(\n params.conversationId,\n \"conversationId\",\n MAX_CONVERSATION_ID_CHARS,\n false,\n );\n const adaptiveCard = cloneAdaptiveCard(params.adaptiveCard);\n const serialized = JSON.stringify(adaptiveCard);\n if (Buffer.byteLength(serialized, \"utf8\") > MAX_ADAPTIVE_CARD_BYTES) {\n throw publicToolError(`adaptiveCard must not exceed ${String(MAX_ADAPTIVE_CARD_BYTES)} bytes`);\n }\n return { conversationId, adaptiveCard };\n}\n\nexport function parseListInput(params: Record<string, unknown>): void {\n requireExactFields(params, new Set(), \"teams_list_channels\");\n}\n\nexport function projectSendResponse(raw: unknown): { ok: true; activityId: string } {\n const response = asRecord(raw);\n const activityId = boundedExternalText(response?.activityId, MAX_ACTIVITY_ID_CHARS);\n if (response?.ok !== true || !activityId) {\n throw new Error(\"Microsoft send response did not match the expected contract\");\n }\n return { ok: true, activityId };\n}\n\nexport function projectChannelsResponse(raw: unknown): Record<string, unknown> {\n const response = asRecord(raw);\n if (!Array.isArray(response?.channels)) {\n throw new Error(\"Microsoft channels response did not contain a channel list\");\n }\n const channels: Record<string, string>[] = [];\n const inspectionLimit = Math.min(response.channels.length, MAX_INSPECTED_CHANNELS);\n for (let index = 0; index < inspectionLimit && channels.length < MAX_CHANNELS; index += 1) {\n const candidate = asRecord(response.channels[index]);\n const id = boundedExternalText(candidate?.id, MAX_CHANNEL_ID_CHARS);\n const name = boundedExternalText(candidate?.name, MAX_CHANNEL_NAME_CHARS);\n if (!id || !name) continue;\n const description = boundedExternalText(\n candidate?.description,\n MAX_CHANNEL_DESCRIPTION_CHARS,\n );\n channels.push({ id, name, ...(description ? { description } : {}) });\n }\n return {\n channels,\n truncated: response.channels.length > channels.length,\n notice: \"Teams channel names and descriptions are untrusted external content.\",\n };\n}\n\nfunction cloneAdaptiveCard(value: unknown): Record<string, unknown> {\n if (!isPlainRecord(value)) {\n throw publicToolError(\"adaptiveCard must be a JSON object\");\n }\n const state = { entries: 0, seen: new WeakSet() };\n return cloneJsonValue(value, 0, state) as Record<string, unknown>;\n}\n\nfunction cloneJsonValue(\n value: unknown,\n depth: number,\n state: { entries: number; seen: WeakSet<object> },\n): JsonValue {\n if (depth > MAX_ADAPTIVE_CARD_DEPTH) {\n throw publicToolError(\n `adaptiveCard must not exceed ${String(MAX_ADAPTIVE_CARD_DEPTH)} nested levels`,\n );\n }\n if (value === null || typeof value === \"boolean\") return value;\n if (typeof value === \"string\") {\n if (value.length > MAX_ADAPTIVE_CARD_STRING_CHARS || hasForbiddenTextControl(value)) {\n throw publicToolError(\n `adaptiveCard strings must contain at most ${String(MAX_ADAPTIVE_CARD_STRING_CHARS)} safe characters`,\n );\n }\n return value;\n }\n if (typeof value === \"number\") {\n if (!Number.isFinite(value)) throw publicToolError(\"adaptiveCard numbers must be finite\");\n return value;\n }\n if (typeof value !== \"object\") {\n throw publicToolError(\"adaptiveCard must contain only JSON values\");\n }\n if (state.seen.has(value)) throw publicToolError(\"adaptiveCard must not contain cycles\");\n state.seen.add(value);\n\n if (Array.isArray(value)) {\n const keys = Object.keys(value);\n if (keys.length !== value.length || keys.some((key, index) => key !== String(index))) {\n throw publicToolError(\"adaptiveCard arrays must be dense JSON arrays\");\n }\n addEntries(state, value.length);\n return value.map((item) => cloneJsonValue(item, depth + 1, state));\n }\n if (!isPlainRecord(value)) throw publicToolError(\"adaptiveCard must contain only JSON objects\");\n const keys = Object.keys(value);\n if (\n Object.getOwnPropertyNames(value).length !== keys.length ||\n Object.getOwnPropertySymbols(value).length > 0\n ) {\n throw publicToolError(\"adaptiveCard objects must contain only enumerable string fields\");\n }\n addEntries(state, keys.length);\n const output = Object.create(null) as Record<string, JsonValue>;\n for (const key of keys) {\n const descriptor = Object.getOwnPropertyDescriptor(value, key);\n if (\n !descriptor ||\n !(\"value\" in descriptor) ||\n !descriptor.enumerable ||\n key.length < 1 ||\n key.length > MAX_ADAPTIVE_CARD_KEY_CHARS ||\n hasForbiddenTextControl(key) ||\n FORBIDDEN_CARD_KEYS.has(key)\n ) {\n throw publicToolError(\n `adaptiveCard keys must contain 1 to ${String(MAX_ADAPTIVE_CARD_KEY_CHARS)} safe characters`,\n );\n }\n output[key] = cloneJsonValue(descriptor.value, depth + 1, state);\n }\n return output;\n}\n\nfunction addEntries(state: { entries: number }, count: number): void {\n state.entries += count;\n if (state.entries > MAX_ADAPTIVE_CARD_ENTRIES) {\n throw publicToolError(\n `adaptiveCard must not exceed ${String(MAX_ADAPTIVE_CARD_ENTRIES)} total entries`,\n );\n }\n}\n\nfunction requireExactFields(\n params: Record<string, unknown>,\n allowed: ReadonlySet<string>,\n toolName: string,\n): void {\n if (Object.keys(params).some((key) => !allowed.has(key))) {\n throw publicToolError(`${toolName} contains unsupported fields`);\n }\n}\n\nfunction requireBoundedString(\n value: unknown,\n label: string,\n maximum: number,\n allowFormattingControls: boolean,\n): string {\n if (\n typeof value !== \"string\" ||\n value.length < 1 ||\n value.length > maximum ||\n (allowFormattingControls ? hasForbiddenTextControl(value) : hasAnyControl(value))\n ) {\n throw publicToolError(`${label} must contain 1 to ${String(maximum)} safe characters`);\n }\n return value;\n}\n\nfunction boundedExternalText(value: unknown, maximum: number): string | undefined {\n if (typeof value !== \"string\" || value.length < 1) return undefined;\n const flattened = flattenExternalControls(value).replace(/\\s+/gu, \" \").trim();\n return flattened.slice(0, maximum) || undefined;\n}\n\nfunction isPlainRecord(value: unknown): value is Record<string, unknown> {\n if (typeof value !== \"object\" || value === null || Array.isArray(value)) return false;\n const prototype = Object.getPrototypeOf(value) as unknown;\n return prototype === Object.prototype || prototype === null;\n}\n\nfunction hasAnyControl(value: string): boolean {\n for (let index = 0; index < value.length; index += 1) {\n const code = value.charCodeAt(index);\n if (code < 32 || (code >= 127 && code <= 159)) return true;\n }\n return false;\n}\n\nfunction flattenExternalControls(value: string): string {\n let output = \"\";\n for (let index = 0; index < value.length; index += 1) {\n const code = value.charCodeAt(index);\n output += code < 32 || (code >= 127 && code <= 159) ? \" \" : value[index];\n }\n return output;\n}\n\nfunction hasForbiddenTextControl(value: string): boolean {\n for (let index = 0; index < value.length; index += 1) {\n const code = value.charCodeAt(index);\n if (\n (code < 32 && code !== 9 && code !== 10 && code !== 13) ||\n (code >= 127 && code <= 159)\n ) return true;\n }\n return false;\n}\n\nfunction asRecord(value: unknown): Record<string, unknown> | undefined {\n return typeof value === \"object\" && value !== null && !Array.isArray(value)\n ? value as Record<string, unknown>\n : undefined;\n}\n","import { Type } from \"@sinclair/typebox\";\nimport { defineTool, type ToolDef } from \"@alfe.ai/openclaw-plugin-kit\";\nimport {\n MAX_CONVERSATION_ID_CHARS,\n MAX_MESSAGE_TEXT_CHARS,\n parseCardInput,\n parseListInput,\n parseMessageInput,\n projectChannelsResponse,\n projectSendResponse,\n} from \"./boundary.js\";\nimport type { TeamsClientResolver } from \"./types.js\";\n\nexport function createTeamsTools(resolver: TeamsClientResolver): ToolDef[] {\n return [\n defineTool({\n name: \"teams_send_message\",\n description:\n \"Send text to a Microsoft Teams conversation. Use a conversationId from an inbound Teams message or teams_list_channels.\",\n parameters: Type.Object({\n conversationId: Type.String({ minLength: 1, maxLength: MAX_CONVERSATION_ID_CHARS }),\n text: Type.String({ minLength: 1, maxLength: MAX_MESSAGE_TEXT_CHARS }),\n }, { additionalProperties: false }),\n handler: async (params) => {\n const input = parseMessageInput(params);\n return projectSendResponse(await resolver.getClient().sendTeamsMessage(input));\n },\n }),\n defineTool({\n name: \"teams_send_card\",\n description:\n \"Send a bounded Adaptive Card JSON object to a Microsoft Teams conversation.\",\n parameters: Type.Object({\n conversationId: Type.String({ minLength: 1, maxLength: MAX_CONVERSATION_ID_CHARS }),\n adaptiveCard: Type.Object({}, { additionalProperties: true }),\n }, { additionalProperties: false }),\n handler: async (params) => {\n const input = parseCardInput(params);\n return projectSendResponse(await resolver.getClient().sendTeamsMessage(input));\n },\n }),\n defineTool({\n name: \"teams_list_channels\",\n description:\n \"List bounded Microsoft Teams channel and conversation metadata available to this agent's bot.\",\n parameters: Type.Object({}, { additionalProperties: false }),\n handler: async (params) => {\n parseListInput(params);\n return projectChannelsResponse(await resolver.getClient().listTeamsChannels());\n },\n }),\n ];\n}\n","import { createRequire } from \"node:module\";\nimport { AgentApiClient, installToolErrorCapture } from \"@alfe.ai/agent-api-client\";\nimport { resolveConfig, type ResolvedConfig } from \"@alfe.ai/config\";\nimport { publicToolError } from \"@alfe.ai/openclaw-plugin-kit\";\nimport { createTeamsTools } from \"./tools.js\";\nimport type { OpenClawPluginApi, TeamsClient, TeamsClientResolver } from \"./types.js\";\n\nconst require = createRequire(import.meta.url);\nconst pkg = require(\"../package.json\") as { version?: unknown };\n\nexport const PLUGIN_VERSION = validatePackageVersion(pkg.version);\n\nexport interface TeamsPluginDependencies {\n resolveConfig?: () => ResolvedConfig;\n createClient?: (config: { apiKey: string; apiUrl: string }) => TeamsClient;\n installErrorCapture?: typeof installToolErrorCapture;\n}\n\nexport function createTeamsPlugin(dependencies: TeamsPluginDependencies = {}) {\n const resolveRuntimeConfig = dependencies.resolveConfig ?? resolveConfig;\n const createClient = dependencies.createClient ?? ((config) => new AgentApiClient(config));\n const installErrorCapture = dependencies.installErrorCapture ?? installToolErrorCapture;\n const resolver = createTeamsClientResolver(resolveRuntimeConfig, createClient);\n\n return {\n id: \"@alfe.ai/openclaw-teams\",\n name: \"Alfe Teams Plugin\",\n description: \"Microsoft Teams integration — send messages and adaptive cards\",\n version: PLUGIN_VERSION,\n\n activate(api: OpenClawPluginApi): void {\n installErrorCapture(api, { plugin: \"openclaw-teams\" });\n const tools = createTeamsTools(resolver);\n for (const tool of tools) api.registerTool(tool);\n api.logger.info(`Registered ${String(tools.length)} Microsoft Teams tools`);\n },\n\n deactivate(api: OpenClawPluginApi): void {\n api.logger.info(\"Microsoft Teams plugin deactivated\");\n },\n };\n}\n\nexport function createTeamsClientResolver(\n resolveRuntimeConfig: () => ResolvedConfig,\n createClient: (config: { apiKey: string; apiUrl: string }) => TeamsClient,\n): TeamsClientResolver {\n let cached: { apiKey: string; apiUrl: string; client: TeamsClient } | undefined;\n return {\n getClient(): TeamsClient {\n let config: ResolvedConfig;\n try {\n config = resolveRuntimeConfig();\n } catch {\n throw publicToolError(\n \"Microsoft Teams is not configured; run alfe setup or repair ~/.alfe/config.toml, then retry.\",\n );\n }\n if (cached?.apiKey === config.apiKey && cached.apiUrl === config.apiUrl) {\n return cached.client;\n }\n const client = createClient({ apiKey: config.apiKey, apiUrl: config.apiUrl });\n cached = { apiKey: config.apiKey, apiUrl: config.apiUrl, client };\n return client;\n },\n };\n}\n\nfunction validatePackageVersion(value: unknown): string {\n if (typeof value !== \"string\" || !/^\\d+\\.\\d+\\.\\d+(?:-[0-9A-Za-z.-]+)?$/u.test(value)) {\n throw new Error(\"openclaw-teams package version is invalid\");\n }\n return value;\n}\n","import { createTeamsPlugin } from \"./runtime.js\";\n\nexport default createTeamsPlugin();\n"],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@alfe.ai/openclaw-teams",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.40",
|
|
4
4
|
"description": "OpenClaw Microsoft Teams plugin for Alfe — send messages and adaptive cards to Teams",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -24,13 +24,14 @@
|
|
|
24
24
|
},
|
|
25
25
|
"files": [
|
|
26
26
|
"dist",
|
|
27
|
-
"openclaw.plugin.json"
|
|
27
|
+
"openclaw.plugin.json",
|
|
28
|
+
"README.md"
|
|
28
29
|
],
|
|
29
30
|
"dependencies": {
|
|
30
31
|
"@sinclair/typebox": "^0.34.48",
|
|
31
|
-
"@alfe.ai/agent-api-client": "0.
|
|
32
|
-
"@alfe.ai/config": "0.4.
|
|
33
|
-
"@alfe.ai/openclaw-plugin-kit": "0.
|
|
32
|
+
"@alfe.ai/agent-api-client": "0.16.0",
|
|
33
|
+
"@alfe.ai/config": "0.4.1",
|
|
34
|
+
"@alfe.ai/openclaw-plugin-kit": "0.2.0"
|
|
34
35
|
},
|
|
35
36
|
"homepage": "https://alfe.ai",
|
|
36
37
|
"author": "Alfe (https://alfe.ai)",
|
|
@@ -45,7 +46,8 @@
|
|
|
45
46
|
"scripts": {
|
|
46
47
|
"build": "tsdown",
|
|
47
48
|
"dev": "tsdown --watch",
|
|
48
|
-
"test": "vitest run
|
|
49
|
+
"test": "vitest run",
|
|
50
|
+
"typecheck": "tsc --noEmit",
|
|
49
51
|
"lint": "eslint ."
|
|
50
52
|
}
|
|
51
53
|
}
|
package/dist/plugin.js.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"plugin.js","names":[],"sources":["../src/plugin.ts"],"sourcesContent":["/**\n * @alfe/openclaw-teams — OpenClaw native plugin\n *\n * Registers Microsoft Teams tools with OpenClaw. Tools call the Alfe\n * agent API via @alfe.ai/agent-api-client.\n *\n * This plugin provides:\n * - teams_send_message — send a text message to a Teams conversation\n * - teams_send_card — send an adaptive card to a Teams conversation\n * - teams_list_channels — list Teams channels the bot has access to\n */\n\nimport { Type, type TSchema } from \"@sinclair/typebox\";\nimport { resolveConfig } from \"@alfe.ai/config\";\nimport { AgentApiClient, installToolErrorCapture } from \"@alfe.ai/agent-api-client\";\nimport {\n defineTool,\n getActivationKey,\n guardedStart,\n resetActivation,\n type ToolDef,\n} from \"@alfe.ai/openclaw-plugin-kit\";\nimport { createRequire } from 'node:module';\nconst require = createRequire(import.meta.url);\nconst pkg = require('../package.json') as { version: string };\n\ninterface Logger {\n info(msg: string, ...args: unknown[]): void;\n warn(msg: string, ...args: unknown[]): void;\n error(msg: string, ...args: unknown[]): void;\n debug(msg: string, ...args: unknown[]): void;\n}\n\ninterface OpenClawPluginApi {\n logger: Logger;\n registrationMode?: \"full\" | \"setup-only\" | \"setup-runtime\" | \"cli-metadata\";\n registerTool(tool: ToolDef<TSchema>): void;\n registerGatewayMethod(name: string, handler: (...args: unknown[]) => Promise<unknown>): void;\n on(event: string, handler: (...args: unknown[]) => void | Promise<void>, options?: { priority?: number }): void;\n}\n\n// ── Activation guard ─────────────────────────────────────────\n\nconst TEAMS_ACTIVATION_KEY = getActivationKey(\"teams\");\n\n// ── API client ───────────────────────────────────────────────\n\nlet client: AgentApiClient | null = null;\n\nfunction getClient(): AgentApiClient {\n if (!client) throw new Error(\"Teams plugin not initialized — no API client\");\n return client;\n}\n\n// ── Tool definitions ─────────────────────────────────────────\n\nconst teamsTools: ToolDef<TSchema>[] = [\n defineTool({\n name: \"teams_send_message\",\n description:\n \"Send a text message to a Microsoft Teams conversation. \" +\n \"You need the conversationId from an inbound Teams message or from teams_list_channels.\",\n parameters: Type.Object({\n conversationId: Type.String({ description: \"The Teams conversation ID to send the message to\" }),\n text: Type.String({ description: \"The message text to send\" }),\n }),\n handler: async (params) => {\n const { conversationId, text } = params as { conversationId: string; text: string };\n return getClient().sendTeamsMessage({ conversationId, text });\n },\n }),\n\n defineTool({\n name: \"teams_send_card\",\n description:\n \"Send an Adaptive Card to a Microsoft Teams conversation. \" +\n \"Adaptive Cards support rich formatting, inputs, and actions. \" +\n \"Pass a valid Adaptive Card JSON object.\",\n parameters: Type.Object({\n conversationId: Type.String({ description: \"The Teams conversation ID to send the card to\" }),\n adaptiveCard: Type.Object({}, {\n description: \"Adaptive Card JSON payload (see https://adaptivecards.io)\",\n additionalProperties: true,\n }),\n }),\n handler: async (params) => {\n const { conversationId, adaptiveCard } = params as {\n conversationId: string;\n adaptiveCard: Record<string, unknown>;\n };\n return getClient().sendTeamsMessage({ conversationId, adaptiveCard });\n },\n }),\n\n defineTool({\n name: \"teams_list_channels\",\n description:\n \"List Microsoft Teams channels and conversations that the bot has access to. \" +\n \"Returns channel IDs that can be used with teams_send_message and teams_send_card.\",\n parameters: Type.Object({}),\n handler: async () => {\n return getClient().listTeamsChannels();\n },\n }),\n];\n\n// ── Plugin definition ────────────────────────────────────────\n\nconst plugin = {\n id: \"@alfe.ai/openclaw-teams\",\n name: \"Alfe Teams Plugin\",\n description: \"Microsoft Teams integration — send messages and adaptive cards\",\n version: pkg.version,\n\n activate(api: OpenClawPluginApi) {\n // First thing, before any registerTool call: tool failures emit a\n // deterministic [ERROR] line the gateway's runtime-output monitor captures\n // to Sentry. See @alfe.ai/agent-api-client tool-error-capture.\n installToolErrorCapture(api, { plugin: \"openclaw-teams\" });\n const log = api.logger;\n\n\n for (const tool of teamsTools) {\n api.registerTool(tool);\n }\n log.info(`Registered ${String(teamsTools.length)} Teams tools: ${teamsTools.map((t) => t.name).join(\", \")}`);\n\n guardedStart(TEAMS_ACTIVATION_KEY, log, () => {\n log.info(\"Alfe Teams plugin activating...\");\n\n try {\n const config = resolveConfig();\n client = new AgentApiClient({ apiKey: config.apiKey, apiUrl: config.apiUrl });\n } catch (err) {\n log.error(`Failed to resolve config: ${err instanceof Error ? err.message : String(err)}`);\n log.warn(\"Teams tools will fail — no API config available\");\n // Reset so a later activate() can retry once config exists.\n resetActivation(TEAMS_ACTIVATION_KEY);\n return;\n }\n\n log.info(\"Alfe Teams plugin activated\");\n });\n },\n\n deactivate(api: OpenClawPluginApi) {\n // Stop side effects BEFORE resetting the activation flag.\n client = null;\n resetActivation(TEAMS_ACTIVATION_KEY);\n api.logger.info(\"Alfe Teams plugin deactivated\");\n },\n};\n\nexport default plugin;\n"],"mappings":";;;;;;;;;;;;;;;;;AAwBA,MAAM,MADU,cAAc,OAAO,KAAK,IAAI,CAC1B,kBAAkB;AAmBtC,MAAM,uBAAuB,iBAAiB,QAAQ;AAItD,IAAI,SAAgC;AAEpC,SAAS,YAA4B;AACnC,KAAI,CAAC,OAAQ,OAAM,IAAI,MAAM,+CAA+C;AAC5E,QAAO;;AAKT,MAAM,aAAiC;CACrC,WAAW;EACT,MAAM;EACN,aACE;EAEF,YAAY,KAAK,OAAO;GACtB,gBAAgB,KAAK,OAAO,EAAE,aAAa,oDAAoD,CAAC;GAChG,MAAM,KAAK,OAAO,EAAE,aAAa,4BAA4B,CAAC;GAC/D,CAAC;EACF,SAAS,OAAO,WAAW;GACzB,MAAM,EAAE,gBAAgB,SAAS;AACjC,UAAO,WAAW,CAAC,iBAAiB;IAAE;IAAgB;IAAM,CAAC;;EAEhE,CAAC;CAEF,WAAW;EACT,MAAM;EACN,aACE;EAGF,YAAY,KAAK,OAAO;GACtB,gBAAgB,KAAK,OAAO,EAAE,aAAa,iDAAiD,CAAC;GAC7F,cAAc,KAAK,OAAO,EAAE,EAAE;IAC5B,aAAa;IACb,sBAAsB;IACvB,CAAC;GACH,CAAC;EACF,SAAS,OAAO,WAAW;GACzB,MAAM,EAAE,gBAAgB,iBAAiB;AAIzC,UAAO,WAAW,CAAC,iBAAiB;IAAE;IAAgB;IAAc,CAAC;;EAExE,CAAC;CAEF,WAAW;EACT,MAAM;EACN,aACE;EAEF,YAAY,KAAK,OAAO,EAAE,CAAC;EAC3B,SAAS,YAAY;AACnB,UAAO,WAAW,CAAC,mBAAmB;;EAEzC,CAAC;CACH;AAID,MAAM,SAAS;CACb,IAAI;CACJ,MAAM;CACN,aAAa;CACb,SAAS,IAAI;CAEb,SAAS,KAAwB;AAI/B,0BAAwB,KAAK,EAAE,QAAQ,kBAAkB,CAAC;EAC1D,MAAM,MAAM,IAAI;AAGhB,OAAK,MAAM,QAAQ,WACjB,KAAI,aAAa,KAAK;AAExB,MAAI,KAAK,cAAc,OAAO,WAAW,OAAO,CAAC,gBAAgB,WAAW,KAAK,MAAM,EAAE,KAAK,CAAC,KAAK,KAAK,GAAG;AAE5G,eAAa,sBAAsB,WAAW;AAC5C,OAAI,KAAK,kCAAkC;AAE3C,OAAI;IACF,MAAM,SAAS,eAAe;AAC9B,aAAS,IAAI,eAAe;KAAE,QAAQ,OAAO;KAAQ,QAAQ,OAAO;KAAQ,CAAC;YACtE,KAAK;AACZ,QAAI,MAAM,6BAA6B,eAAe,QAAQ,IAAI,UAAU,OAAO,IAAI,GAAG;AAC1F,QAAI,KAAK,kDAAkD;AAE3D,oBAAgB,qBAAqB;AACrC;;AAGF,OAAI,KAAK,8BAA8B;IACvC;;CAGJ,WAAW,KAAwB;AAEjC,WAAS;AACT,kBAAgB,qBAAqB;AACrC,MAAI,OAAO,KAAK,gCAAgC;;CAEnD"}
|