@alfe.ai/openclaw-google 0.0.40 → 0.0.41

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/dist/index.d.cts CHANGED
@@ -1,2 +1,2 @@
1
- import plugin from "./plugin.cjs";
1
+ import { t as plugin } from "./plugin.cjs";
2
2
  export { plugin as default };
package/dist/index.d.ts CHANGED
@@ -1,2 +1,2 @@
1
- import plugin from "./plugin.js";
1
+ import { t as plugin } from "./plugin.js";
2
2
  export { plugin as default };
package/dist/plugin.cjs CHANGED
@@ -1,6 +1,7 @@
1
1
  let _sinclair_typebox = require("@sinclair/typebox");
2
2
  let _alfe_ai_config = require("@alfe.ai/config");
3
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");
4
5
  let node_child_process = require("node:child_process");
5
6
  let node_path = require("node:path");
6
7
  let node_os = require("node:os");
@@ -22,39 +23,7 @@ let node_os = require("node:os");
22
23
  * pass `email` to `google_run_command` to target one.
23
24
  */
24
25
  const pkg = (0, require("node:module").createRequire)(require("url").pathToFileURL(__filename).href)("../package.json");
25
- function ok(data) {
26
- return {
27
- content: [{
28
- type: "text",
29
- text: JSON.stringify(data)
30
- }],
31
- details: data
32
- };
33
- }
34
- function errResult(message) {
35
- return {
36
- content: [{
37
- type: "text",
38
- text: JSON.stringify({ error: message })
39
- }],
40
- details: { error: message }
41
- };
42
- }
43
- function defineTool(def) {
44
- return {
45
- name: def.name,
46
- description: def.description,
47
- label: def.name,
48
- parameters: def.parameters,
49
- execute: async (_toolCallId, params) => {
50
- try {
51
- return ok(await def.handler(params));
52
- } catch (e) {
53
- return errResult(e instanceof Error ? e.message : "Unknown error");
54
- }
55
- }
56
- };
57
- }
26
+ const GOOGLE_ACTIVATION_KEY = (0, _alfe_ai_openclaw_plugin_kit.getActivationKey)("google");
58
27
  function sanitizeEmail(email) {
59
28
  return email.replace(/@/g, "-").replace(/\./g, "-");
60
29
  }
@@ -106,7 +75,7 @@ function runGwsCommand(args, configDir) {
106
75
  });
107
76
  }
108
77
  const googleTools = [
109
- defineTool({
78
+ (0, _alfe_ai_openclaw_plugin_kit.defineTool)({
110
79
  name: "google_list_accounts",
111
80
  description: "List all connected Google Workspace accounts. Shows email, display name, when each account was connected, and the gws CLI config directory path. Use this to resolve which account to target (e.g., 'Kevin\\'s emails' → kevin@alfe.ai).",
112
81
  parameters: _sinclair_typebox.Type.Object({}),
@@ -123,7 +92,7 @@ const googleTools = [
123
92
  };
124
93
  }
125
94
  }),
126
- defineTool({
95
+ (0, _alfe_ai_openclaw_plugin_kit.defineTool)({
127
96
  name: "google_run_command",
128
97
  description: "Run a gws (Google Workspace CLI) command targeting a specific account. Automatically sets GOOGLE_WORKSPACE_CLI_CONFIG_DIR for the target account. Email is required — call google_list_accounts first if you don't know which account to use. Example: google_run_command({ command: 'gmail list', email: 'kevin@alfe.ai' })",
129
98
  parameters: _sinclair_typebox.Type.Object({
@@ -144,7 +113,7 @@ const googleTools = [
144
113
  };
145
114
  }
146
115
  }),
147
- defineTool({
116
+ (0, _alfe_ai_openclaw_plugin_kit.defineTool)({
148
117
  name: "google_disconnect_account",
149
118
  description: "Disconnect a specific Google account from this agent. Revokes the OAuth token and removes the account.",
150
119
  parameters: _sinclair_typebox.Type.Object({ email: _sinclair_typebox.Type.String({ description: "Email of the Google account to disconnect" }) }),
@@ -170,33 +139,32 @@ const plugin = {
170
139
  for (const tool of googleTools) api.registerTool(tool);
171
140
  log.info(`Registered ${googleTools.length.toString()} Google tools: ${googleTools.map((t) => t.name).join(", ")}`);
172
141
  const startGoogleService = () => {
173
- if (globalThis.__googlePluginActivated === true) {
174
- log.debug("Google plugin already activated — skipping duplicate");
175
- return;
176
- }
177
- globalThis.__googlePluginActivated = true;
178
- log.info("Alfe Google Workspace plugin activating...");
179
- try {
180
- const config = (0, _alfe_ai_config.resolveConfig)();
181
- client = new _alfe_ai_agent_api_client.AgentApiClient({
182
- apiKey: config.apiKey,
183
- apiUrl: config.apiUrl
184
- });
185
- refreshAccountCache().then((accounts) => {
186
- log.info(`Cached ${accounts.length.toString()} Google account(s): ${accounts.map((a) => a.email).join(", ")}`);
187
- }).catch((err) => {
188
- log.warn(`Failed to pre-cache Google accounts: ${err instanceof Error ? err.message : "unknown"}`);
189
- });
190
- } catch (err) {
191
- log.error(`Failed to resolve config: ${err instanceof Error ? err.message : "unknown"}`);
192
- log.warn("Google tools will fail — no API config available");
193
- }
194
- log.info("Alfe Google Workspace plugin activated");
142
+ (0, _alfe_ai_openclaw_plugin_kit.guardedStart)(GOOGLE_ACTIVATION_KEY, log, () => {
143
+ log.info("Alfe Google Workspace plugin activating...");
144
+ try {
145
+ const config = (0, _alfe_ai_config.resolveConfig)();
146
+ client = new _alfe_ai_agent_api_client.AgentApiClient({
147
+ apiKey: config.apiKey,
148
+ apiUrl: config.apiUrl
149
+ });
150
+ refreshAccountCache().then((accounts) => {
151
+ log.info(`Cached ${accounts.length.toString()} Google account(s): ${accounts.map((a) => a.email).join(", ")}`);
152
+ }).catch((err) => {
153
+ log.warn(`Failed to pre-cache Google accounts: ${err instanceof Error ? err.message : "unknown"}`);
154
+ });
155
+ } catch (err) {
156
+ log.error(`Failed to resolve config: ${err instanceof Error ? err.message : "unknown"}`);
157
+ log.warn("Google tools will fail no API config available");
158
+ (0, _alfe_ai_openclaw_plugin_kit.resetActivation)(GOOGLE_ACTIVATION_KEY);
159
+ return;
160
+ }
161
+ log.info("Alfe Google Workspace plugin activated");
162
+ });
195
163
  };
196
164
  const stopGoogleService = () => {
197
- globalThis.__googlePluginActivated = false;
198
165
  client = null;
199
166
  cachedAccounts = [];
167
+ (0, _alfe_ai_openclaw_plugin_kit.resetActivation)(GOOGLE_ACTIVATION_KEY);
200
168
  log.info("Alfe Google Workspace plugin stopped");
201
169
  };
202
170
  if (api.registerService) api.registerService({
@@ -210,9 +178,9 @@ const plugin = {
210
178
  });
211
179
  },
212
180
  deactivate(api) {
213
- globalThis.__googlePluginActivated = false;
214
181
  client = null;
215
182
  cachedAccounts = [];
183
+ (0, _alfe_ai_openclaw_plugin_kit.resetActivation)(GOOGLE_ACTIVATION_KEY);
216
184
  api.logger.info("Alfe Google Workspace plugin deactivated");
217
185
  }
218
186
  };
package/dist/plugin.d.cts CHANGED
@@ -1,5 +1,45 @@
1
1
  import { TSchema } from "@sinclair/typebox";
2
2
 
3
+ //#region ../openclaw-plugin-kit/dist/index.d.ts
4
+
5
+ //# sourceMappingURL=types.d.ts.map
6
+ //#endregion
7
+ //#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
+ /** Shape returned to OpenClaw from a tool `execute`. */
19
+ interface ToolResult {
20
+ content: {
21
+ type: "text";
22
+ text: string;
23
+ }[];
24
+ details: unknown;
25
+ }
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
+ interface ToolDef<TParameters = unknown> {
35
+ name: string;
36
+ description: string;
37
+ label: string;
38
+ parameters: TParameters;
39
+ execute: (toolCallId: string, params: Record<string, unknown>) => Promise<ToolResult>;
40
+ }
41
+ /** Wrap a successful handler result in the OpenClaw tool-result envelope. */
42
+ //#endregion
3
43
  //#region src/plugin.d.ts
4
44
 
5
45
  interface Logger {
@@ -17,7 +57,7 @@ interface PluginServiceContext {
17
57
  interface OpenClawPluginApi {
18
58
  logger: Logger;
19
59
  registrationMode?: "full" | "setup-only" | "setup-runtime" | "cli-metadata";
20
- registerTool(tool: ToolDef): void;
60
+ registerTool(tool: ToolDef<TSchema>): void;
21
61
  registerGatewayMethod(name: string, handler: (...args: unknown[]) => Promise<unknown>): void;
22
62
  registerService?(service: {
23
63
  id: string;
@@ -28,19 +68,6 @@ interface OpenClawPluginApi {
28
68
  priority?: number;
29
69
  }): void;
30
70
  }
31
- interface ToolDef {
32
- name: string;
33
- description: string;
34
- label: string;
35
- parameters: TSchema;
36
- execute: (toolCallId: string, params: Record<string, unknown>) => Promise<{
37
- content: {
38
- type: "text";
39
- text: string;
40
- }[];
41
- details: unknown;
42
- }>;
43
- }
44
71
  declare const plugin: {
45
72
  id: string;
46
73
  name: string;
@@ -50,5 +77,5 @@ declare const plugin: {
50
77
  deactivate(api: OpenClawPluginApi): void;
51
78
  };
52
79
  //#endregion
53
- export { plugin as default };
80
+ export { plugin as t };
54
81
  //# sourceMappingURL=plugin.d.cts.map
@@ -1 +1 @@
1
- {"version":3,"file":"plugin.d.cts","names":[],"sources":["../src/plugin.ts"],"mappings":";;;;UA2BU,MAAA,CAsBuC;MAEW,CAAA,GAAA,EAAA,MAAA,EAAA,GAAA,IAAA,EAAA,OAAA,EAAA,CAAA,EAAA,IAAA;EAAO,IAAA,CAAA,GAAA,EAAA,MAAA,EAAA,GAAA,IAAA,EAAA,OAAA,EAAA,CAAA,EAAA,IAAA;EAKzD,KAAA,CAAA,GAAA,EAAO,MAAA,EAAA,GAAA,IAAA,EAAA,OAAA,EAAA,CAAA,EAAA,IAAA;EAAA,KAAA,CAAA,GAAA,EAAA,MAAA,EAAA,GAAA,IAAA,EAAA,OAAA,EAAA,CAAA,EAAA,IAAA;;UAtBP,oBAAA,CA2B8B;QAA4B,CAAA,EA1BzD,MA0ByD,CAAA,MAAA,EAAA,OAAA,CAAA;EAAO,YAAA,CAAA,EAAA,MAAA;EAiMrE,QAAA,CAmEL,EAAA,MAAA;EAAA,MAAA,CAAA,EA3RU,MA2RV;;UAxRS,iBAAA,CAkRQ;EAAiB,MAAA,EAjRzB,MAiRyB;;qBA/Qd;uEACkD;;;iBAGtD,gCAAgC;iBAChC,gCAAgC;;4DAEW;;;;UAKlD,OAAA;;;;cAII;wCAC0B,4BAA4B;;;;;;;;cAiM9D;;;;;gBAMU;kBAuDE"}
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","ScheduleRefreshOptions","RefreshHandle","scheduleRefresh"],"sources":["../../openclaw-plugin-kit/dist/index.d.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/**\n * Tool-definition helpers — the `defineTool` / `ok` / `errResult` trio that\n * was copy-pasted across 8 plugins (identity, google, teams, mobile,\n * whatsapp, voice, chat a2a-tools, base openclaw).\n *\n * Error handling is standardized on the openclaw-google variant — the only\n * copy that survived non-`Error` throws (`e instanceof Error ? e.message :\n * \"Unknown error\"`). The other copies did `(e as Error).message`, which\n * crashes the tool executor when a handler throws a string/object.\n */\n/** Shape returned to OpenClaw from a tool `execute`. */\ninterface ToolResult {\n content: {\n type: \"text\";\n text: string;\n }[];\n details: unknown;\n}\n/**\n * An OpenClaw tool definition.\n *\n * `parameters` is generic because the fleet is split between TypeBox\n * `TSchema` schemas (identity/google/teams/mobile/whatsapp) and plain\n * JSON-Schema objects (chat a2a-tools). Instantiate with whichever schema\n * type the plugin uses — the kit itself has no schema dependency.\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/** Wrap a successful handler result in the OpenClaw tool-result envelope. */\ndeclare function ok(data: unknown): ToolResult;\n/** Wrap an error message in the OpenClaw tool-result envelope. */\ndeclare function errResult(message: string): ToolResult;\n/**\n * Define an OpenClaw tool from a plain async handler. Handler throws (of\n * any type — `Error` or not) are converted to `errResult` envelopes so the\n * LLM sees a structured error instead of the tool executor crashing.\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\n/** Response envelope for daemon IPC requests. */\ninterface IpcResponse {\n ok: boolean;\n error?: {\n message?: string;\n };\n [key: string]: unknown;\n}\n/** The subset of `@alfe.ai/openclaw`'s IPCClient the kit relies on. */\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 ConnectToDaemonOptions {\n /** Plugin package name sent with `capability.register` (e.g. `@alfe.ai/openclaw-sync`). */\n pluginId: string;\n /** Capabilities to register on every (re)connect. Constants stay per-plugin. */\n capabilities?: readonly string[];\n /**\n * Optional handler for daemon → plugin messages. Only invoked with\n * object payloads — the kit does the message-shape checking.\n */\n onMessage?: (msg: Record<string, unknown>) => void;\n /** Log line when the daemon isn't available (plugin runs standalone). */\n standaloneNote?: string;\n}\n/**\n * Attempt to connect to the Alfe daemon IPC socket. Returns `null` (after\n * an info log) when `@alfe.ai/openclaw` isn't installed — plugins degrade\n * gracefully to standalone mode.\n */\ndeclare function connectToDaemon(socketPath: string, log: KitLogger, options: ConnectToDaemonOptions): Promise<IpcClient | null>;\n//# sourceMappingURL=daemon.d.ts.map\n//#endregion\n//#region src/sdk.d.ts\ninterface ResolveOpenClawSdkOptions {\n /** Module specifier resolved from OpenClaw's context. */\n specifier?: string;\n /** Named export to extract from the resolved module. */\n exportName?: string;\n /** Warn line emitted when the SDK can't be resolved. */\n unresolvableNote?: string;\n}\n/**\n * Resolve a named export from the running OpenClaw process's SDK.\n *\n * Defaults target `dispatchInboundDirectDmWithRuntime` from\n * `openclaw/plugin-sdk/channel-inbound` — the export both existing\n * consumers (chat, google-chat) need. Returns `null` (after a warn log)\n * when unresolvable; callers degrade gracefully.\n */\ndeclare function resolveOpenClawSdk<T = unknown>(log: KitLogger, options?: ResolveOpenClawSdkOptions): 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//#endregion\n//#region src/refresh.d.ts\ninterface ScheduleRefreshOptions {\n /** Cadence between successful refreshes (per-provider token lifetime — stays in the consuming package). */\n intervalMs: number;\n /** Delay before retrying after a failed refresh. */\n retryMs: number;\n /** Run one refresh immediately instead of waiting a full interval first. Default false. */\n immediate?: boolean;\n}\ninterface RefreshHandle {\n /** Cancel the schedule. Safe to call multiple times. */\n stop(): void;\n}\n/**\n * Run `refreshFn` every `intervalMs`; on failure, log a warning and retry\n * after `retryMs`. Timers are unref'd so the schedule never keeps the\n * process alive.\n */\ndeclare function scheduleRefresh(refreshFn: () => Promise<void>, options: ScheduleRefreshOptions, log: KitLogger): RefreshHandle;\n//# sourceMappingURL=refresh.d.ts.map\n\n//#endregion\nexport { type ConnectToDaemonOptions, type IpcClient, type IpcResponse, type KitLogger, type RefreshHandle, type ResolveOpenClawSdkOptions, type ScheduleRefreshOptions, type ToolDef, type ToolResult, connectToDaemon, defineTool, errResult, getActivationKey, guardedStart, isActivated, ok, resetActivation, resolveOpenClawSdk, scheduleRefresh };\n//# sourceMappingURL=index.d.ts.map"],"mappings":";;;;ACkCgB;;;;;AAWC;;;;;;;;;UDhBPC,UAAAA,CC2BuC;SAEW,EAAA;IAAO,IAAA,EAAA,MAAA;IAoK7D,IAAA,EAAA,MAqEL;EAAA,CAAA,EAAA;SA/De,EAAA,OAAA;;;;;;;;;;UDxLNC;;;;cAIIC;wCAC0BC,4BAA4BC,QAAQJ;;;;;;UCflE,MAAA,CAWC;EAAM,IAAA,CAAA,GAAA,EAAA,MAAA,EAAA,GAAA,IAAA,EAAA,OAAA,EAAA,CAAA,EAAA,IAAA;EAGP,IAAA,CAAA,GAAA,EAAA,MAAA,EAAA,GAAiB,IAAA,EAAA,OAAA,EAAA,CAAA,EAAA,IAAA;EAAA,KAAA,CAAA,GAAA,EAAA,MAAA,EAAA,GAAA,IAAA,EAAA,OAAA,EAAA,CAAA,EAAA,IAAA;OACjB,CAAA,GAAA,EAAA,MAAA,EAAA,GAAA,IAAA,EAAA,OAAA,EAAA,CAAA,EAAA,IAAA;;UARA,oBAAA,CAUW;QACkD,CAAA,EAV5D,MAU4D,CAAA,MAAA,EAAA,OAAA,CAAA;cAGtD,CAAA,EAAA,MAAA;UAAgC,CAAA,EAAA,MAAA;QAChC,CAAA,EAXN,MAWM;;UARP,iBAAA,CAUkD;EAAO,MAAA,EATzD,MASyD;EAoK7D,gBAqEL,CAAA,EAAA,MAAA,GAAA,YAAA,GAAA,eAAA,GAAA,cAAA;EAAA,YAAA,CAAA,IAAA,EAhPoB,OAgPpB,CAhP4B,OAgP5B,CAAA,CAAA,EAAA,IAAA;uBA/De,CAAA,IAAA,EAAA,MAAA,EAAA,OAAA,EAAA,CAAA,GAAA,IAAA,EAAA,OAAA,EAAA,EAAA,GAhLuD,OAgLvD,CAAA,OAAA,CAAA,CAAA,EAAA,IAAA;iBAwDE,EAAA,OAAA,EAAA;IAAiB,EAAA,EAAA,MAAA;iBArOlB,gCAAgC;iBAChC,gCAAgC;;4DAEW;;;;cAoKtD;;;;;gBAMU;kBAwDE"}
package/dist/plugin.d.ts CHANGED
@@ -1,5 +1,45 @@
1
1
  import { TSchema } from "@sinclair/typebox";
2
2
 
3
+ //#region ../openclaw-plugin-kit/dist/index.d.ts
4
+
5
+ //# sourceMappingURL=types.d.ts.map
6
+ //#endregion
7
+ //#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
+ /** Shape returned to OpenClaw from a tool `execute`. */
19
+ interface ToolResult {
20
+ content: {
21
+ type: "text";
22
+ text: string;
23
+ }[];
24
+ details: unknown;
25
+ }
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
+ interface ToolDef<TParameters = unknown> {
35
+ name: string;
36
+ description: string;
37
+ label: string;
38
+ parameters: TParameters;
39
+ execute: (toolCallId: string, params: Record<string, unknown>) => Promise<ToolResult>;
40
+ }
41
+ /** Wrap a successful handler result in the OpenClaw tool-result envelope. */
42
+ //#endregion
3
43
  //#region src/plugin.d.ts
4
44
 
5
45
  interface Logger {
@@ -17,7 +57,7 @@ interface PluginServiceContext {
17
57
  interface OpenClawPluginApi {
18
58
  logger: Logger;
19
59
  registrationMode?: "full" | "setup-only" | "setup-runtime" | "cli-metadata";
20
- registerTool(tool: ToolDef): void;
60
+ registerTool(tool: ToolDef<TSchema>): void;
21
61
  registerGatewayMethod(name: string, handler: (...args: unknown[]) => Promise<unknown>): void;
22
62
  registerService?(service: {
23
63
  id: string;
@@ -28,19 +68,6 @@ interface OpenClawPluginApi {
28
68
  priority?: number;
29
69
  }): void;
30
70
  }
31
- interface ToolDef {
32
- name: string;
33
- description: string;
34
- label: string;
35
- parameters: TSchema;
36
- execute: (toolCallId: string, params: Record<string, unknown>) => Promise<{
37
- content: {
38
- type: "text";
39
- text: string;
40
- }[];
41
- details: unknown;
42
- }>;
43
- }
44
71
  declare const plugin: {
45
72
  id: string;
46
73
  name: string;
@@ -50,5 +77,5 @@ declare const plugin: {
50
77
  deactivate(api: OpenClawPluginApi): void;
51
78
  };
52
79
  //#endregion
53
- export { plugin as default };
80
+ export { plugin as t };
54
81
  //# sourceMappingURL=plugin.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"plugin.d.ts","names":[],"sources":["../src/plugin.ts"],"mappings":";;;;UA2BU,MAAA,CAsBuC;MAEW,CAAA,GAAA,EAAA,MAAA,EAAA,GAAA,IAAA,EAAA,OAAA,EAAA,CAAA,EAAA,IAAA;EAAO,IAAA,CAAA,GAAA,EAAA,MAAA,EAAA,GAAA,IAAA,EAAA,OAAA,EAAA,CAAA,EAAA,IAAA;EAKzD,KAAA,CAAA,GAAA,EAAO,MAAA,EAAA,GAAA,IAAA,EAAA,OAAA,EAAA,CAAA,EAAA,IAAA;EAAA,KAAA,CAAA,GAAA,EAAA,MAAA,EAAA,GAAA,IAAA,EAAA,OAAA,EAAA,CAAA,EAAA,IAAA;;UAtBP,oBAAA,CA2B8B;QAA4B,CAAA,EA1BzD,MA0ByD,CAAA,MAAA,EAAA,OAAA,CAAA;EAAO,YAAA,CAAA,EAAA,MAAA;EAiMrE,QAAA,CAmEL,EAAA,MAAA;EAAA,MAAA,CAAA,EA3RU,MA2RV;;UAxRS,iBAAA,CAkRQ;EAAiB,MAAA,EAjRzB,MAiRyB;;qBA/Qd;uEACkD;;;iBAGtD,gCAAgC;iBAChC,gCAAgC;;4DAEW;;;;UAKlD,OAAA;;;;cAII;wCAC0B,4BAA4B;;;;;;;;cAiM9D;;;;;gBAMU;kBAuDE"}
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","ScheduleRefreshOptions","RefreshHandle","scheduleRefresh"],"sources":["../../openclaw-plugin-kit/dist/index.d.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/**\n * Tool-definition helpers — the `defineTool` / `ok` / `errResult` trio that\n * was copy-pasted across 8 plugins (identity, google, teams, mobile,\n * whatsapp, voice, chat a2a-tools, base openclaw).\n *\n * Error handling is standardized on the openclaw-google variant — the only\n * copy that survived non-`Error` throws (`e instanceof Error ? e.message :\n * \"Unknown error\"`). The other copies did `(e as Error).message`, which\n * crashes the tool executor when a handler throws a string/object.\n */\n/** Shape returned to OpenClaw from a tool `execute`. */\ninterface ToolResult {\n content: {\n type: \"text\";\n text: string;\n }[];\n details: unknown;\n}\n/**\n * An OpenClaw tool definition.\n *\n * `parameters` is generic because the fleet is split between TypeBox\n * `TSchema` schemas (identity/google/teams/mobile/whatsapp) and plain\n * JSON-Schema objects (chat a2a-tools). Instantiate with whichever schema\n * type the plugin uses — the kit itself has no schema dependency.\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/** Wrap a successful handler result in the OpenClaw tool-result envelope. */\ndeclare function ok(data: unknown): ToolResult;\n/** Wrap an error message in the OpenClaw tool-result envelope. */\ndeclare function errResult(message: string): ToolResult;\n/**\n * Define an OpenClaw tool from a plain async handler. Handler throws (of\n * any type — `Error` or not) are converted to `errResult` envelopes so the\n * LLM sees a structured error instead of the tool executor crashing.\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\n/** Response envelope for daemon IPC requests. */\ninterface IpcResponse {\n ok: boolean;\n error?: {\n message?: string;\n };\n [key: string]: unknown;\n}\n/** The subset of `@alfe.ai/openclaw`'s IPCClient the kit relies on. */\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 ConnectToDaemonOptions {\n /** Plugin package name sent with `capability.register` (e.g. `@alfe.ai/openclaw-sync`). */\n pluginId: string;\n /** Capabilities to register on every (re)connect. Constants stay per-plugin. */\n capabilities?: readonly string[];\n /**\n * Optional handler for daemon → plugin messages. Only invoked with\n * object payloads — the kit does the message-shape checking.\n */\n onMessage?: (msg: Record<string, unknown>) => void;\n /** Log line when the daemon isn't available (plugin runs standalone). */\n standaloneNote?: string;\n}\n/**\n * Attempt to connect to the Alfe daemon IPC socket. Returns `null` (after\n * an info log) when `@alfe.ai/openclaw` isn't installed — plugins degrade\n * gracefully to standalone mode.\n */\ndeclare function connectToDaemon(socketPath: string, log: KitLogger, options: ConnectToDaemonOptions): Promise<IpcClient | null>;\n//# sourceMappingURL=daemon.d.ts.map\n//#endregion\n//#region src/sdk.d.ts\ninterface ResolveOpenClawSdkOptions {\n /** Module specifier resolved from OpenClaw's context. */\n specifier?: string;\n /** Named export to extract from the resolved module. */\n exportName?: string;\n /** Warn line emitted when the SDK can't be resolved. */\n unresolvableNote?: string;\n}\n/**\n * Resolve a named export from the running OpenClaw process's SDK.\n *\n * Defaults target `dispatchInboundDirectDmWithRuntime` from\n * `openclaw/plugin-sdk/channel-inbound` — the export both existing\n * consumers (chat, google-chat) need. Returns `null` (after a warn log)\n * when unresolvable; callers degrade gracefully.\n */\ndeclare function resolveOpenClawSdk<T = unknown>(log: KitLogger, options?: ResolveOpenClawSdkOptions): 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//#endregion\n//#region src/refresh.d.ts\ninterface ScheduleRefreshOptions {\n /** Cadence between successful refreshes (per-provider token lifetime — stays in the consuming package). */\n intervalMs: number;\n /** Delay before retrying after a failed refresh. */\n retryMs: number;\n /** Run one refresh immediately instead of waiting a full interval first. Default false. */\n immediate?: boolean;\n}\ninterface RefreshHandle {\n /** Cancel the schedule. Safe to call multiple times. */\n stop(): void;\n}\n/**\n * Run `refreshFn` every `intervalMs`; on failure, log a warning and retry\n * after `retryMs`. Timers are unref'd so the schedule never keeps the\n * process alive.\n */\ndeclare function scheduleRefresh(refreshFn: () => Promise<void>, options: ScheduleRefreshOptions, log: KitLogger): RefreshHandle;\n//# sourceMappingURL=refresh.d.ts.map\n\n//#endregion\nexport { type ConnectToDaemonOptions, type IpcClient, type IpcResponse, type KitLogger, type RefreshHandle, type ResolveOpenClawSdkOptions, type ScheduleRefreshOptions, type ToolDef, type ToolResult, connectToDaemon, defineTool, errResult, getActivationKey, guardedStart, isActivated, ok, resetActivation, resolveOpenClawSdk, scheduleRefresh };\n//# sourceMappingURL=index.d.ts.map"],"mappings":";;;;ACkCgB;;;;;AAWC;;;;;;;;;UDhBPC,UAAAA,CC2BuC;SAEW,EAAA;IAAO,IAAA,EAAA,MAAA;IAoK7D,IAAA,EAAA,MAqEL;EAAA,CAAA,EAAA;SA/De,EAAA,OAAA;;;;;;;;;;UDxLNC;;;;cAIIC;wCAC0BC,4BAA4BC,QAAQJ;;;;;;UCflE,MAAA,CAWC;EAAM,IAAA,CAAA,GAAA,EAAA,MAAA,EAAA,GAAA,IAAA,EAAA,OAAA,EAAA,CAAA,EAAA,IAAA;EAGP,IAAA,CAAA,GAAA,EAAA,MAAA,EAAA,GAAiB,IAAA,EAAA,OAAA,EAAA,CAAA,EAAA,IAAA;EAAA,KAAA,CAAA,GAAA,EAAA,MAAA,EAAA,GAAA,IAAA,EAAA,OAAA,EAAA,CAAA,EAAA,IAAA;OACjB,CAAA,GAAA,EAAA,MAAA,EAAA,GAAA,IAAA,EAAA,OAAA,EAAA,CAAA,EAAA,IAAA;;UARA,oBAAA,CAUW;QACkD,CAAA,EAV5D,MAU4D,CAAA,MAAA,EAAA,OAAA,CAAA;cAGtD,CAAA,EAAA,MAAA;UAAgC,CAAA,EAAA,MAAA;QAChC,CAAA,EAXN,MAWM;;UARP,iBAAA,CAUkD;EAAO,MAAA,EATzD,MASyD;EAoK7D,gBAqEL,CAAA,EAAA,MAAA,GAAA,YAAA,GAAA,eAAA,GAAA,cAAA;EAAA,YAAA,CAAA,IAAA,EAhPoB,OAgPpB,CAhP4B,OAgP5B,CAAA,CAAA,EAAA,IAAA;uBA/De,CAAA,IAAA,EAAA,MAAA,EAAA,OAAA,EAAA,CAAA,GAAA,IAAA,EAAA,OAAA,EAAA,EAAA,GAhLuD,OAgLvD,CAAA,OAAA,CAAA,CAAA,EAAA,IAAA;iBAwDE,EAAA,OAAA,EAAA;IAAiB,EAAA,EAAA,MAAA;iBArOlB,gCAAgC;iBAChC,gCAAgC;;4DAEW;;;;cAoKtD;;;;;gBAMU;kBAwDE"}
package/dist/plugin.js CHANGED
@@ -2,6 +2,7 @@ import { createRequire } from "node:module";
2
2
  import { Type } from "@sinclair/typebox";
3
3
  import { resolveConfig } from "@alfe.ai/config";
4
4
  import { AgentApiClient, installToolErrorCapture } from "@alfe.ai/agent-api-client";
5
+ import { defineTool, getActivationKey, guardedStart, resetActivation } from "@alfe.ai/openclaw-plugin-kit";
5
6
  import { execFile } from "node:child_process";
6
7
  import { join } from "node:path";
7
8
  import { homedir } from "node:os";
@@ -23,39 +24,7 @@ import { homedir } from "node:os";
23
24
  * pass `email` to `google_run_command` to target one.
24
25
  */
25
26
  const pkg = createRequire(import.meta.url)("../package.json");
26
- function ok(data) {
27
- return {
28
- content: [{
29
- type: "text",
30
- text: JSON.stringify(data)
31
- }],
32
- details: data
33
- };
34
- }
35
- function errResult(message) {
36
- return {
37
- content: [{
38
- type: "text",
39
- text: JSON.stringify({ error: message })
40
- }],
41
- details: { error: message }
42
- };
43
- }
44
- function defineTool(def) {
45
- return {
46
- name: def.name,
47
- description: def.description,
48
- label: def.name,
49
- parameters: def.parameters,
50
- execute: async (_toolCallId, params) => {
51
- try {
52
- return ok(await def.handler(params));
53
- } catch (e) {
54
- return errResult(e instanceof Error ? e.message : "Unknown error");
55
- }
56
- }
57
- };
58
- }
27
+ const GOOGLE_ACTIVATION_KEY = getActivationKey("google");
59
28
  function sanitizeEmail(email) {
60
29
  return email.replace(/@/g, "-").replace(/\./g, "-");
61
30
  }
@@ -171,33 +140,32 @@ const plugin = {
171
140
  for (const tool of googleTools) api.registerTool(tool);
172
141
  log.info(`Registered ${googleTools.length.toString()} Google tools: ${googleTools.map((t) => t.name).join(", ")}`);
173
142
  const startGoogleService = () => {
174
- if (globalThis.__googlePluginActivated === true) {
175
- log.debug("Google plugin already activated — skipping duplicate");
176
- return;
177
- }
178
- globalThis.__googlePluginActivated = true;
179
- log.info("Alfe Google Workspace plugin activating...");
180
- try {
181
- const config = resolveConfig();
182
- client = new AgentApiClient({
183
- apiKey: config.apiKey,
184
- apiUrl: config.apiUrl
185
- });
186
- refreshAccountCache().then((accounts) => {
187
- log.info(`Cached ${accounts.length.toString()} Google account(s): ${accounts.map((a) => a.email).join(", ")}`);
188
- }).catch((err) => {
189
- log.warn(`Failed to pre-cache Google accounts: ${err instanceof Error ? err.message : "unknown"}`);
190
- });
191
- } catch (err) {
192
- log.error(`Failed to resolve config: ${err instanceof Error ? err.message : "unknown"}`);
193
- log.warn("Google tools will fail — no API config available");
194
- }
195
- log.info("Alfe Google Workspace plugin activated");
143
+ guardedStart(GOOGLE_ACTIVATION_KEY, log, () => {
144
+ log.info("Alfe Google Workspace plugin activating...");
145
+ try {
146
+ const config = resolveConfig();
147
+ client = new AgentApiClient({
148
+ apiKey: config.apiKey,
149
+ apiUrl: config.apiUrl
150
+ });
151
+ refreshAccountCache().then((accounts) => {
152
+ log.info(`Cached ${accounts.length.toString()} Google account(s): ${accounts.map((a) => a.email).join(", ")}`);
153
+ }).catch((err) => {
154
+ log.warn(`Failed to pre-cache Google accounts: ${err instanceof Error ? err.message : "unknown"}`);
155
+ });
156
+ } catch (err) {
157
+ log.error(`Failed to resolve config: ${err instanceof Error ? err.message : "unknown"}`);
158
+ log.warn("Google tools will fail no API config available");
159
+ resetActivation(GOOGLE_ACTIVATION_KEY);
160
+ return;
161
+ }
162
+ log.info("Alfe Google Workspace plugin activated");
163
+ });
196
164
  };
197
165
  const stopGoogleService = () => {
198
- globalThis.__googlePluginActivated = false;
199
166
  client = null;
200
167
  cachedAccounts = [];
168
+ resetActivation(GOOGLE_ACTIVATION_KEY);
201
169
  log.info("Alfe Google Workspace plugin stopped");
202
170
  };
203
171
  if (api.registerService) api.registerService({
@@ -211,9 +179,9 @@ const plugin = {
211
179
  });
212
180
  },
213
181
  deactivate(api) {
214
- globalThis.__googlePluginActivated = false;
215
182
  client = null;
216
183
  cachedAccounts = [];
184
+ resetActivation(GOOGLE_ACTIVATION_KEY);
217
185
  api.logger.info("Alfe Google Workspace plugin deactivated");
218
186
  }
219
187
  };
@@ -1 +1 @@
1
- {"version":3,"file":"plugin.js","names":[],"sources":["../src/plugin.ts"],"sourcesContent":["/**\n * @alfe/openclaw-google — OpenClaw native plugin\n *\n * Registers Google Workspace account management tools with OpenClaw.\n * Multi-account by design — every gws command requires an explicit\n * `email` (schema-enforced) so the LLM picks the target account per call.\n *\n * Tools:\n * - google_list_accounts — list connected Google accounts\n * - google_run_command — run a gws command (requires email)\n * - google_disconnect_account — disconnect an account\n *\n * 2026-05-14 (connections-redesign PR 1): the \"default account\" concept is\n * gone. Use `google_list_accounts` to discover available accounts, then\n * pass `email` to `google_run_command` to target one.\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 { execFile } from \"node:child_process\";\nimport { join } from \"node:path\";\nimport { homedir } from \"node:os\";\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 PluginServiceContext {\n config?: Record<string, unknown>;\n workspaceDir?: string;\n stateDir?: string;\n logger?: Logger;\n}\n\ninterface OpenClawPluginApi {\n logger: Logger;\n registrationMode?: \"full\" | \"setup-only\" | \"setup-runtime\" | \"cli-metadata\";\n registerTool(tool: ToolDef): void;\n registerGatewayMethod(name: string, handler: (...args: unknown[]) => Promise<unknown>): void;\n registerService?(service: {\n id: string;\n start: (ctx: PluginServiceContext) => void | Promise<void>;\n stop?: (ctx: PluginServiceContext) => void | Promise<void>;\n }): void;\n on(event: string, handler: (...args: unknown[]) => void | Promise<void>, options?: { priority?: number }): void;\n}\n\n// ── Tool types ───────────────────────────────────────────────\n\ninterface ToolDef {\n name: string;\n description: string;\n label: string;\n parameters: TSchema;\n execute: (toolCallId: string, params: Record<string, unknown>) => Promise<{\n content: { type: \"text\"; text: string }[];\n details: unknown;\n }>;\n}\n\nfunction ok(data: unknown) {\n return { content: [{ type: \"text\" as const, text: JSON.stringify(data) }], details: data };\n}\n\nfunction errResult(message: string) {\n return { content: [{ type: \"text\" as const, text: JSON.stringify({ error: message }) }], details: { error: message } };\n}\n\nfunction defineTool(def: {\n name: string;\n description: string;\n parameters: TSchema;\n handler: (params: Record<string, unknown>) => Promise<unknown>;\n}): ToolDef {\n return {\n name: def.name,\n description: def.description,\n label: def.name,\n parameters: def.parameters,\n execute: async (_toolCallId: string, params: Record<string, unknown>) => {\n try {\n const result = await def.handler(params);\n return ok(result);\n } catch (e: unknown) {\n return errResult(e instanceof Error ? e.message : \"Unknown error\");\n }\n },\n };\n}\n\n// ── Account config dir resolution ───────────────────────────\n//\n// 2026-05-14 (connections-redesign PR 1): the \"default account\" concept is\n// gone. Every connected Google account gets its own per-email config dir\n// (`gws-<sanitized-email>`); the LLM picks an account by passing `email`\n// explicitly to `google_run_command` (schema-enforced required field).\n\ninterface GoogleAccountInfo {\n email: string;\n displayName?: string;\n connectedAt?: string;\n configDir: string;\n}\n\nfunction sanitizeEmail(email: string): string {\n return email.replace(/@/g, \"-\").replace(/\\./g, \"-\");\n}\n\nfunction resolveConfigDir(email: string): string {\n return join(homedir(), \".config\", `gws-${sanitizeEmail(email)}`);\n}\n\n// ── State ───────────────────────────────────────────────────\n\nlet client: AgentApiClient | null = null;\nlet cachedAccounts: GoogleAccountInfo[] = [];\n\nfunction getClient(): AgentApiClient {\n // Lazily construct the client so tool calls work even when the\n // registerService.start hook never fires (observed on some OpenClaw\n // runtimes — tools register in activate() but the service start hook\n // that eagerly built `client` is not invoked). The eager build in\n // startGoogleService() remains a best-effort pre-cache optimization.\n if (!client) {\n const config = resolveConfig();\n client = new AgentApiClient({ apiKey: config.apiKey, apiUrl: config.apiUrl });\n }\n return client;\n}\n\nasync function refreshAccountCache(): Promise<GoogleAccountInfo[]> {\n const creds = await getClient().getGoogleCredentials();\n cachedAccounts = creds.accounts.map((a) => ({\n email: a.email,\n displayName: a.displayName,\n connectedAt: a.connectedAt,\n configDir: resolveConfigDir(a.email),\n }));\n return cachedAccounts;\n}\n\nfunction findAccount(email: string): GoogleAccountInfo {\n const account = cachedAccounts.find((a) => a.email === email);\n if (!account) {\n throw new Error(\n `Google account \"${email}\" not found. Available: ${cachedAccounts.map((a) => a.email).join(\", \")}`,\n );\n }\n return account;\n}\n\n// ── gws command execution ───────────────────────────────────\n\nfunction runGwsCommand(args: string[], configDir: string): Promise<{ stdout: string; stderr: string; exitCode: number }> {\n return new Promise((resolve) => {\n const env = { ...process.env, GOOGLE_WORKSPACE_CLI_CONFIG_DIR: configDir };\n execFile(\"gws\", args, { env, timeout: 60_000, maxBuffer: 10 * 1024 * 1024 }, (error, stdout, stderr) => {\n resolve({\n stdout,\n stderr,\n exitCode: typeof error?.code === 'number' ? error.code : error ? 1 : 0,\n });\n });\n });\n}\n\n// ── Tool definitions ─────────────────────────────────────────\n\nconst googleTools: ToolDef[] = [\n defineTool({\n name: \"google_list_accounts\",\n description:\n \"List all connected Google Workspace accounts. Shows email, display name, \" +\n \"when each account was connected, and the gws CLI config directory path. \" +\n \"Use this to resolve which account to target (e.g., 'Kevin\\\\'s emails' → kevin@alfe.ai).\",\n parameters: Type.Object({}),\n handler: async () => {\n const accounts = await refreshAccountCache();\n return {\n accounts: accounts.map((a) => ({\n email: a.email,\n displayName: a.displayName,\n connectedAt: a.connectedAt,\n configDir: a.configDir,\n })),\n count: accounts.length,\n };\n },\n }),\n\n defineTool({\n name: \"google_run_command\",\n description:\n \"Run a gws (Google Workspace CLI) command targeting a specific account. \" +\n \"Automatically sets GOOGLE_WORKSPACE_CLI_CONFIG_DIR for the target account. \" +\n \"Email is required — call google_list_accounts first if you don't know which account to use. \" +\n \"Example: google_run_command({ command: 'gmail list', email: 'kevin@alfe.ai' })\",\n parameters: Type.Object({\n command: Type.String({\n description: \"The gws CLI command and arguments (e.g., 'gmail list', 'calendar agenda', 'drive list')\",\n }),\n email: Type.String({\n description: \"Email of the Google account to use. Required — there is no implicit default.\",\n }),\n }),\n handler: async (params) => {\n const { command, email } = params as { command: string; email: string };\n // Refresh cache if this is the first call (covers fresh activations\n // where google_list_accounts wasn't called first).\n if (cachedAccounts.length === 0) await refreshAccountCache();\n const account = findAccount(email);\n\n const args = command.split(/\\s+/).filter(Boolean);\n if (args.length === 0) throw new Error(\"Command cannot be empty\");\n\n const result = await runGwsCommand(args, account.configDir);\n\n return {\n account: account.email,\n command: `gws ${command}`,\n ...result,\n };\n },\n }),\n\n defineTool({\n name: \"google_disconnect_account\",\n description:\n \"Disconnect a specific Google account from this agent. \" +\n \"Revokes the OAuth token and removes the account.\",\n parameters: Type.Object({\n email: Type.String({ description: \"Email of the Google account to disconnect\" }),\n }),\n handler: async (params) => {\n const { email } = params as { email: string };\n const result = await getClient().disconnectGoogleAccount(email);\n await refreshAccountCache();\n return {\n message: `${email} has been disconnected`,\n remainingAccounts: result.accounts,\n };\n },\n }),\n];\n\n// ── Plugin definition ────────────────────────────────────────\n\nconst plugin = {\n id: \"@alfe.ai/openclaw-google\",\n name: \"Alfe Google Workspace Plugin\",\n description: \"Multi-account Google Workspace management — list accounts and run gws commands with an explicit account selector\",\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-google\" });\n const log = api.logger;\n\n for (const tool of googleTools) {\n api.registerTool(tool);\n }\n log.info(`Registered ${googleTools.length.toString()} Google tools: ${googleTools.map((t) => t.name).join(\", \")}`);\n\n const startGoogleService = () => {\n if ((globalThis as Record<string, unknown>).__googlePluginActivated === true) {\n log.debug(\"Google plugin already activated — skipping duplicate\");\n return;\n }\n (globalThis as Record<string, unknown>).__googlePluginActivated = true;\n log.info(\"Alfe Google Workspace plugin activating...\");\n\n try {\n const config = resolveConfig();\n client = new AgentApiClient({ apiKey: config.apiKey, apiUrl: config.apiUrl });\n\n refreshAccountCache()\n .then((accounts) => {\n log.info(`Cached ${accounts.length.toString()} Google account(s): ${accounts.map((a) => a.email).join(\", \")}`);\n })\n .catch((err: unknown) => {\n log.warn(`Failed to pre-cache Google accounts: ${err instanceof Error ? err.message : \"unknown\"}`);\n });\n } catch (err: unknown) {\n log.error(`Failed to resolve config: ${err instanceof Error ? err.message : \"unknown\"}`);\n log.warn(\"Google tools will fail — no API config available\");\n }\n\n log.info(\"Alfe Google Workspace plugin activated\");\n };\n\n const stopGoogleService = () => {\n (globalThis as Record<string, unknown>).__googlePluginActivated = false;\n client = null;\n cachedAccounts = [];\n log.info(\"Alfe Google Workspace plugin stopped\");\n };\n\n if (api.registerService) {\n api.registerService({\n id: \"alfe-google-workspace\",\n start: () => { startGoogleService(); },\n stop: () => { stopGoogleService(); },\n });\n }\n },\n\n deactivate(api: OpenClawPluginApi) {\n (globalThis as Record<string, unknown>).__googlePluginActivated = false;\n client = null;\n cachedAccounts = [];\n api.logger.info(\"Alfe Google Workspace plugin deactivated\");\n },\n};\n\nexport default plugin;\n"],"mappings":"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"}
1
+ {"version":3,"file":"plugin.js","names":[],"sources":["../src/plugin.ts"],"sourcesContent":["/**\n * @alfe/openclaw-google — OpenClaw native plugin\n *\n * Registers Google Workspace account management tools with OpenClaw.\n * Multi-account by design — every gws command requires an explicit\n * `email` (schema-enforced) so the LLM picks the target account per call.\n *\n * Tools:\n * - google_list_accounts — list connected Google accounts\n * - google_run_command — run a gws command (requires email)\n * - google_disconnect_account — disconnect an account\n *\n * 2026-05-14 (connections-redesign PR 1): the \"default account\" concept is\n * gone. Use `google_list_accounts` to discover available accounts, then\n * pass `email` to `google_run_command` to target one.\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 { execFile } from \"node:child_process\";\nimport { join } from \"node:path\";\nimport { homedir } from \"node:os\";\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 PluginServiceContext {\n config?: Record<string, unknown>;\n workspaceDir?: string;\n stateDir?: string;\n logger?: Logger;\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 registerService?(service: {\n id: string;\n start: (ctx: PluginServiceContext) => void | Promise<void>;\n stop?: (ctx: PluginServiceContext) => void | Promise<void>;\n }): void;\n on(event: string, handler: (...args: unknown[]) => void | Promise<void>, options?: { priority?: number }): void;\n}\n\n// ── Activation guard ─────────────────────────────────────────\n\nconst GOOGLE_ACTIVATION_KEY = getActivationKey(\"google\");\n\n// ── Account config dir resolution ───────────────────────────\n//\n// 2026-05-14 (connections-redesign PR 1): the \"default account\" concept is\n// gone. Every connected Google account gets its own per-email config dir\n// (`gws-<sanitized-email>`); the LLM picks an account by passing `email`\n// explicitly to `google_run_command` (schema-enforced required field).\n\ninterface GoogleAccountInfo {\n email: string;\n displayName?: string;\n connectedAt?: string;\n configDir: string;\n}\n\nfunction sanitizeEmail(email: string): string {\n return email.replace(/@/g, \"-\").replace(/\\./g, \"-\");\n}\n\nfunction resolveConfigDir(email: string): string {\n return join(homedir(), \".config\", `gws-${sanitizeEmail(email)}`);\n}\n\n// ── State ───────────────────────────────────────────────────\n\nlet client: AgentApiClient | null = null;\nlet cachedAccounts: GoogleAccountInfo[] = [];\n\nfunction getClient(): AgentApiClient {\n // Lazily construct the client so tool calls work even when the\n // registerService.start hook never fires (observed on some OpenClaw\n // runtimes — tools register in activate() but the service start hook\n // that eagerly built `client` is not invoked). The eager build in\n // startGoogleService() remains a best-effort pre-cache optimization.\n if (!client) {\n const config = resolveConfig();\n client = new AgentApiClient({ apiKey: config.apiKey, apiUrl: config.apiUrl });\n }\n return client;\n}\n\nasync function refreshAccountCache(): Promise<GoogleAccountInfo[]> {\n const creds = await getClient().getGoogleCredentials();\n cachedAccounts = creds.accounts.map((a) => ({\n email: a.email,\n displayName: a.displayName,\n connectedAt: a.connectedAt,\n configDir: resolveConfigDir(a.email),\n }));\n return cachedAccounts;\n}\n\nfunction findAccount(email: string): GoogleAccountInfo {\n const account = cachedAccounts.find((a) => a.email === email);\n if (!account) {\n throw new Error(\n `Google account \"${email}\" not found. Available: ${cachedAccounts.map((a) => a.email).join(\", \")}`,\n );\n }\n return account;\n}\n\n// ── gws command execution ───────────────────────────────────\n\nfunction runGwsCommand(args: string[], configDir: string): Promise<{ stdout: string; stderr: string; exitCode: number }> {\n return new Promise((resolve) => {\n const env = { ...process.env, GOOGLE_WORKSPACE_CLI_CONFIG_DIR: configDir };\n execFile(\"gws\", args, { env, timeout: 60_000, maxBuffer: 10 * 1024 * 1024 }, (error, stdout, stderr) => {\n resolve({\n stdout,\n stderr,\n exitCode: typeof error?.code === 'number' ? error.code : error ? 1 : 0,\n });\n });\n });\n}\n\n// ── Tool definitions ─────────────────────────────────────────\n\nconst googleTools: ToolDef<TSchema>[] = [\n defineTool({\n name: \"google_list_accounts\",\n description:\n \"List all connected Google Workspace accounts. Shows email, display name, \" +\n \"when each account was connected, and the gws CLI config directory path. \" +\n \"Use this to resolve which account to target (e.g., 'Kevin\\\\'s emails' → kevin@alfe.ai).\",\n parameters: Type.Object({}),\n handler: async () => {\n const accounts = await refreshAccountCache();\n return {\n accounts: accounts.map((a) => ({\n email: a.email,\n displayName: a.displayName,\n connectedAt: a.connectedAt,\n configDir: a.configDir,\n })),\n count: accounts.length,\n };\n },\n }),\n\n defineTool({\n name: \"google_run_command\",\n description:\n \"Run a gws (Google Workspace CLI) command targeting a specific account. \" +\n \"Automatically sets GOOGLE_WORKSPACE_CLI_CONFIG_DIR for the target account. \" +\n \"Email is required — call google_list_accounts first if you don't know which account to use. \" +\n \"Example: google_run_command({ command: 'gmail list', email: 'kevin@alfe.ai' })\",\n parameters: Type.Object({\n command: Type.String({\n description: \"The gws CLI command and arguments (e.g., 'gmail list', 'calendar agenda', 'drive list')\",\n }),\n email: Type.String({\n description: \"Email of the Google account to use. Required — there is no implicit default.\",\n }),\n }),\n handler: async (params) => {\n const { command, email } = params as { command: string; email: string };\n // Refresh cache if this is the first call (covers fresh activations\n // where google_list_accounts wasn't called first).\n if (cachedAccounts.length === 0) await refreshAccountCache();\n const account = findAccount(email);\n\n const args = command.split(/\\s+/).filter(Boolean);\n if (args.length === 0) throw new Error(\"Command cannot be empty\");\n\n const result = await runGwsCommand(args, account.configDir);\n\n return {\n account: account.email,\n command: `gws ${command}`,\n ...result,\n };\n },\n }),\n\n defineTool({\n name: \"google_disconnect_account\",\n description:\n \"Disconnect a specific Google account from this agent. \" +\n \"Revokes the OAuth token and removes the account.\",\n parameters: Type.Object({\n email: Type.String({ description: \"Email of the Google account to disconnect\" }),\n }),\n handler: async (params) => {\n const { email } = params as { email: string };\n const result = await getClient().disconnectGoogleAccount(email);\n await refreshAccountCache();\n return {\n message: `${email} has been disconnected`,\n remainingAccounts: result.accounts,\n };\n },\n }),\n];\n\n// ── Plugin definition ────────────────────────────────────────\n\nconst plugin = {\n id: \"@alfe.ai/openclaw-google\",\n name: \"Alfe Google Workspace Plugin\",\n description: \"Multi-account Google Workspace management — list accounts and run gws commands with an explicit account selector\",\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-google\" });\n const log = api.logger;\n\n for (const tool of googleTools) {\n api.registerTool(tool);\n }\n log.info(`Registered ${googleTools.length.toString()} Google tools: ${googleTools.map((t) => t.name).join(\", \")}`);\n\n const startGoogleService = () => {\n guardedStart(GOOGLE_ACTIVATION_KEY, log, () => {\n log.info(\"Alfe Google Workspace plugin activating...\");\n\n try {\n const config = resolveConfig();\n client = new AgentApiClient({ apiKey: config.apiKey, apiUrl: config.apiUrl });\n\n refreshAccountCache()\n .then((accounts) => {\n log.info(`Cached ${accounts.length.toString()} Google account(s): ${accounts.map((a) => a.email).join(\", \")}`);\n })\n .catch((err: unknown) => {\n log.warn(`Failed to pre-cache Google accounts: ${err instanceof Error ? err.message : \"unknown\"}`);\n });\n } catch (err: unknown) {\n log.error(`Failed to resolve config: ${err instanceof Error ? err.message : \"unknown\"}`);\n log.warn(\"Google tools will fail — no API config available\");\n // Reset so a later start can retry once config exists.\n resetActivation(GOOGLE_ACTIVATION_KEY);\n return;\n }\n\n log.info(\"Alfe Google Workspace plugin activated\");\n });\n };\n\n const stopGoogleService = () => {\n // Stop side effects BEFORE resetting the activation flag.\n client = null;\n cachedAccounts = [];\n resetActivation(GOOGLE_ACTIVATION_KEY);\n log.info(\"Alfe Google Workspace plugin stopped\");\n };\n\n if (api.registerService) {\n api.registerService({\n id: \"alfe-google-workspace\",\n start: () => { startGoogleService(); },\n stop: () => { stopGoogleService(); },\n });\n }\n },\n\n deactivate(api: OpenClawPluginApi) {\n // Stop side effects BEFORE resetting the activation flag.\n client = null;\n cachedAccounts = [];\n resetActivation(GOOGLE_ACTIVATION_KEY);\n api.logger.info(\"Alfe Google Workspace plugin deactivated\");\n },\n};\n\nexport default plugin;\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;AAgCA,MAAM,MADU,cAAc,OAAO,KAAK,IAAI,CAC1B,kBAAkB;AA+BtC,MAAM,wBAAwB,iBAAiB,SAAS;AAgBxD,SAAS,cAAc,OAAuB;AAC5C,QAAO,MAAM,QAAQ,MAAM,IAAI,CAAC,QAAQ,OAAO,IAAI;;AAGrD,SAAS,iBAAiB,OAAuB;AAC/C,QAAO,KAAK,SAAS,EAAE,WAAW,OAAO,cAAc,MAAM,GAAG;;AAKlE,IAAI,SAAgC;AACpC,IAAI,iBAAsC,EAAE;AAE5C,SAAS,YAA4B;AAMnC,KAAI,CAAC,QAAQ;EACX,MAAM,SAAS,eAAe;AAC9B,WAAS,IAAI,eAAe;GAAE,QAAQ,OAAO;GAAQ,QAAQ,OAAO;GAAQ,CAAC;;AAE/E,QAAO;;AAGT,eAAe,sBAAoD;AAEjE,mBADc,MAAM,WAAW,CAAC,sBAAsB,EAC/B,SAAS,KAAK,OAAO;EAC1C,OAAO,EAAE;EACT,aAAa,EAAE;EACf,aAAa,EAAE;EACf,WAAW,iBAAiB,EAAE,MAAM;EACrC,EAAE;AACH,QAAO;;AAGT,SAAS,YAAY,OAAkC;CACrD,MAAM,UAAU,eAAe,MAAM,MAAM,EAAE,UAAU,MAAM;AAC7D,KAAI,CAAC,QACH,OAAM,IAAI,MACR,mBAAmB,MAAM,0BAA0B,eAAe,KAAK,MAAM,EAAE,MAAM,CAAC,KAAK,KAAK,GACjG;AAEH,QAAO;;AAKT,SAAS,cAAc,MAAgB,WAAkF;AACvH,QAAO,IAAI,SAAS,YAAY;AAE9B,WAAS,OAAO,MAAM;GAAE,KADZ;IAAE,GAAG,QAAQ;IAAK,iCAAiC;IAAW;GAC7C,SAAS;GAAQ,WAAW,KAAK,OAAO;GAAM,GAAG,OAAO,QAAQ,WAAW;AACtG,WAAQ;IACN;IACA;IACA,UAAU,OAAO,OAAO,SAAS,WAAW,MAAM,OAAO,QAAQ,IAAI;IACtE,CAAC;IACF;GACF;;AAKJ,MAAM,cAAkC;CACtC,WAAW;EACT,MAAM;EACN,aACE;EAGF,YAAY,KAAK,OAAO,EAAE,CAAC;EAC3B,SAAS,YAAY;GACnB,MAAM,WAAW,MAAM,qBAAqB;AAC5C,UAAO;IACL,UAAU,SAAS,KAAK,OAAO;KAC7B,OAAO,EAAE;KACT,aAAa,EAAE;KACf,aAAa,EAAE;KACf,WAAW,EAAE;KACd,EAAE;IACH,OAAO,SAAS;IACjB;;EAEJ,CAAC;CAEF,WAAW;EACT,MAAM;EACN,aACE;EAIF,YAAY,KAAK,OAAO;GACtB,SAAS,KAAK,OAAO,EACnB,aAAa,2FACd,CAAC;GACF,OAAO,KAAK,OAAO,EACjB,aAAa,gFACd,CAAC;GACH,CAAC;EACF,SAAS,OAAO,WAAW;GACzB,MAAM,EAAE,SAAS,UAAU;AAG3B,OAAI,eAAe,WAAW,EAAG,OAAM,qBAAqB;GAC5D,MAAM,UAAU,YAAY,MAAM;GAElC,MAAM,OAAO,QAAQ,MAAM,MAAM,CAAC,OAAO,QAAQ;AACjD,OAAI,KAAK,WAAW,EAAG,OAAM,IAAI,MAAM,0BAA0B;GAEjE,MAAM,SAAS,MAAM,cAAc,MAAM,QAAQ,UAAU;AAE3D,UAAO;IACL,SAAS,QAAQ;IACjB,SAAS,OAAO;IAChB,GAAG;IACJ;;EAEJ,CAAC;CAEF,WAAW;EACT,MAAM;EACN,aACE;EAEF,YAAY,KAAK,OAAO,EACtB,OAAO,KAAK,OAAO,EAAE,aAAa,6CAA6C,CAAC,EACjF,CAAC;EACF,SAAS,OAAO,WAAW;GACzB,MAAM,EAAE,UAAU;GAClB,MAAM,SAAS,MAAM,WAAW,CAAC,wBAAwB,MAAM;AAC/D,SAAM,qBAAqB;AAC3B,UAAO;IACL,SAAS,GAAG,MAAM;IAClB,mBAAmB,OAAO;IAC3B;;EAEJ,CAAC;CACH;AAID,MAAM,SAAS;CACb,IAAI;CACJ,MAAM;CACN,aAAa;CACb,SAAS,IAAI;CAEb,SAAS,KAAwB;AAI/B,0BAAwB,KAAK,EAAE,QAAQ,mBAAmB,CAAC;EAC3D,MAAM,MAAM,IAAI;AAEhB,OAAK,MAAM,QAAQ,YACjB,KAAI,aAAa,KAAK;AAExB,MAAI,KAAK,cAAc,YAAY,OAAO,UAAU,CAAC,iBAAiB,YAAY,KAAK,MAAM,EAAE,KAAK,CAAC,KAAK,KAAK,GAAG;EAElH,MAAM,2BAA2B;AAC/B,gBAAa,uBAAuB,WAAW;AAC7C,QAAI,KAAK,6CAA6C;AAEtD,QAAI;KACF,MAAM,SAAS,eAAe;AAC9B,cAAS,IAAI,eAAe;MAAE,QAAQ,OAAO;MAAQ,QAAQ,OAAO;MAAQ,CAAC;AAE7E,0BAAqB,CAClB,MAAM,aAAa;AAClB,UAAI,KAAK,UAAU,SAAS,OAAO,UAAU,CAAC,sBAAsB,SAAS,KAAK,MAAM,EAAE,MAAM,CAAC,KAAK,KAAK,GAAG;OAC9G,CACD,OAAO,QAAiB;AACvB,UAAI,KAAK,wCAAwC,eAAe,QAAQ,IAAI,UAAU,YAAY;OAClG;aACG,KAAc;AACrB,SAAI,MAAM,6BAA6B,eAAe,QAAQ,IAAI,UAAU,YAAY;AACxF,SAAI,KAAK,mDAAmD;AAE5D,qBAAgB,sBAAsB;AACtC;;AAGF,QAAI,KAAK,yCAAyC;KAClD;;EAGJ,MAAM,0BAA0B;AAE9B,YAAS;AACT,oBAAiB,EAAE;AACnB,mBAAgB,sBAAsB;AACtC,OAAI,KAAK,uCAAuC;;AAGlD,MAAI,IAAI,gBACN,KAAI,gBAAgB;GAClB,IAAI;GACJ,aAAa;AAAE,wBAAoB;;GACnC,YAAY;AAAE,uBAAmB;;GAClC,CAAC;;CAIN,WAAW,KAAwB;AAEjC,WAAS;AACT,mBAAiB,EAAE;AACnB,kBAAgB,sBAAsB;AACtC,MAAI,OAAO,KAAK,2CAA2C;;CAE9D"}
@@ -0,0 +1,2 @@
1
+ import { t as plugin } from "./plugin.cjs";
2
+ export { plugin as default };
@@ -0,0 +1,2 @@
1
+ import { t as plugin } from "./plugin.js";
2
+ export { plugin as default };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@alfe.ai/openclaw-google",
3
- "version": "0.0.40",
3
+ "version": "0.0.41",
4
4
  "description": "OpenClaw Google Workspace plugin — multi-account management and gws CLI integration",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -28,8 +28,9 @@
28
28
  ],
29
29
  "dependencies": {
30
30
  "@sinclair/typebox": "^0.34.48",
31
- "@alfe.ai/agent-api-client": "0.13.0",
32
- "@alfe.ai/config": "0.3.0"
31
+ "@alfe.ai/agent-api-client": "0.14.0",
32
+ "@alfe.ai/config": "0.4.0",
33
+ "@alfe.ai/openclaw-plugin-kit": "0.1.0"
33
34
  },
34
35
  "homepage": "https://alfe.ai",
35
36
  "author": "Alfe (https://alfe.ai)",