@alfe.ai/openclaw-google 0.0.17 → 0.0.19

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/plugin.cjs CHANGED
@@ -9,11 +9,17 @@ let node_os = require("node:os");
9
9
  * @alfe/openclaw-google — OpenClaw native plugin
10
10
  *
11
11
  * Registers Google Workspace account management tools with OpenClaw.
12
- * Provides multi-account awareness so the agent can:
13
- * - List connected Google accounts
14
- * - Run gws CLI commands targeting a specific account
15
- * - Set the default account
16
- * - Disconnect an account
12
+ * Multi-account by design every gws command requires an explicit
13
+ * `email` (schema-enforced) so the LLM picks the target account per call.
14
+ *
15
+ * Tools:
16
+ * - google_list_accounts — list connected Google accounts
17
+ * - google_run_command — run a gws command (requires email)
18
+ * - google_disconnect_account — disconnect an account
19
+ *
20
+ * 2026-05-14 (connections-redesign PR 1): the "default account" concept is
21
+ * gone. Use `google_list_accounts` to discover available accounts, then
22
+ * pass `email` to `google_run_command` to target one.
17
23
  */
18
24
  const pkg = (0, require("node:module").createRequire)(require("url").pathToFileURL(__filename).href)("../package.json");
19
25
  function ok(data) {
@@ -52,9 +58,8 @@ function defineTool(def) {
52
58
  function sanitizeEmail(email) {
53
59
  return email.replace(/@/g, "-").replace(/\./g, "-");
54
60
  }
55
- function resolveConfigDir(email, isDefault) {
56
- const dirName = isDefault ? "gws" : `gws-${sanitizeEmail(email)}`;
57
- return (0, node_path.join)((0, node_os.homedir)(), ".config", dirName);
61
+ function resolveConfigDir(email) {
62
+ return (0, node_path.join)((0, node_os.homedir)(), ".config", `gws-${sanitizeEmail(email)}`);
58
63
  }
59
64
  let client = null;
60
65
  let cachedAccounts = [];
@@ -63,28 +68,15 @@ function getClient() {
63
68
  return client;
64
69
  }
65
70
  async function refreshAccountCache() {
66
- const creds = await getClient().getGoogleCredentials();
67
- cachedAccounts = (creds.accounts ?? [{
68
- email: creds.email,
69
- isDefault: true,
70
- displayName: void 0
71
- }]).map((a, i) => ({
71
+ cachedAccounts = (await getClient().getGoogleCredentials()).accounts.map((a) => ({
72
72
  email: a.email,
73
- isDefault: i === 0 ? true : a.isDefault,
74
73
  displayName: a.displayName,
75
- configDir: resolveConfigDir(a.email, i === 0 ? true : a.isDefault)
74
+ connectedAt: a.connectedAt,
75
+ configDir: resolveConfigDir(a.email)
76
76
  }));
77
77
  return cachedAccounts;
78
78
  }
79
79
  function findAccount(email) {
80
- if (!email) {
81
- const def = cachedAccounts.find((a) => a.isDefault);
82
- if (!def) {
83
- if (cachedAccounts.length === 0) throw new Error("No Google accounts connected");
84
- return cachedAccounts[0];
85
- }
86
- return def;
87
- }
88
80
  const account = cachedAccounts.find((a) => a.email === email);
89
81
  if (!account) throw new Error(`Google account "${email}" not found. Available: ${cachedAccounts.map((a) => a.email).join(", ")}`);
90
82
  return account;
@@ -110,7 +102,7 @@ function runGwsCommand(args, configDir) {
110
102
  const googleTools = [
111
103
  defineTool({
112
104
  name: "google_list_accounts",
113
- description: "List all connected Google Workspace accounts. Shows email, display name, whether it's the default account, and the gws CLI config directory path. Use this to resolve which account to target (e.g., 'Kevin\\'s emails' → kevin@alfe.ai).",
105
+ 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).",
114
106
  parameters: _sinclair_typebox.Type.Object({}),
115
107
  handler: async () => {
116
108
  const accounts = await refreshAccountCache();
@@ -118,7 +110,7 @@ const googleTools = [
118
110
  accounts: accounts.map((a) => ({
119
111
  email: a.email,
120
112
  displayName: a.displayName,
121
- isDefault: a.isDefault,
113
+ connectedAt: a.connectedAt,
122
114
  configDir: a.configDir
123
115
  })),
124
116
  count: accounts.length
@@ -127,13 +119,14 @@ const googleTools = [
127
119
  }),
128
120
  defineTool({
129
121
  name: "google_run_command",
130
- description: "Run a gws (Google Workspace CLI) command targeting a specific account. Automatically sets GOOGLE_WORKSPACE_CLI_CONFIG_DIR for the target account. If no email is specified, uses the default account. Example: google_run_command({ command: 'gmail list', email: 'kevin@alfe.ai' })",
122
+ 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' })",
131
123
  parameters: _sinclair_typebox.Type.Object({
132
124
  command: _sinclair_typebox.Type.String({ description: "The gws CLI command and arguments (e.g., 'gmail list', 'calendar agenda', 'drive list')" }),
133
- email: _sinclair_typebox.Type.Optional(_sinclair_typebox.Type.String({ description: "Email of the Google account to use. Omit to use the default account." }))
125
+ email: _sinclair_typebox.Type.String({ description: "Email of the Google account to use. Required there is no implicit default." })
134
126
  }),
135
127
  handler: async (params) => {
136
128
  const { command, email } = params;
129
+ if (cachedAccounts.length === 0) await refreshAccountCache();
137
130
  const account = findAccount(email);
138
131
  const args = command.split(/\s+/).filter(Boolean);
139
132
  if (args.length === 0) throw new Error("Command cannot be empty");
@@ -145,23 +138,9 @@ const googleTools = [
145
138
  };
146
139
  }
147
140
  }),
148
- defineTool({
149
- name: "google_set_default_account",
150
- description: "Set a specific Google account as the default. The default account is used when no email is specified in google_run_command.",
151
- parameters: _sinclair_typebox.Type.Object({ email: _sinclair_typebox.Type.String({ description: "Email of the Google account to set as default" }) }),
152
- handler: async (params) => {
153
- const { email } = params;
154
- const result = await getClient().setDefaultGoogleAccount(email);
155
- await refreshAccountCache();
156
- return {
157
- message: `${email} is now the default Google account`,
158
- accounts: result.accounts
159
- };
160
- }
161
- }),
162
141
  defineTool({
163
142
  name: "google_disconnect_account",
164
- description: "Disconnect a specific Google account from this agent. Revokes the OAuth token and removes the account. If this was the default account, the next account is promoted.",
143
+ description: "Disconnect a specific Google account from this agent. Revokes the OAuth token and removes the account.",
165
144
  parameters: _sinclair_typebox.Type.Object({ email: _sinclair_typebox.Type.String({ description: "Email of the Google account to disconnect" }) }),
166
145
  handler: async (params) => {
167
146
  const { email } = params;
@@ -177,7 +156,7 @@ const googleTools = [
177
156
  const plugin = {
178
157
  id: "@alfe.ai/openclaw-google",
179
158
  name: "Alfe Google Workspace Plugin",
180
- description: "Multi-account Google Workspace management — list accounts, run gws commands, manage defaults",
159
+ description: "Multi-account Google Workspace management — list accounts and run gws commands with an explicit account selector",
181
160
  version: pkg.version,
182
161
  activate(api) {
183
162
  const log = api.logger;
@@ -1 +1 @@
1
- {"version":3,"file":"plugin.d.cts","names":[],"sources":["../src/plugin.ts"],"mappings":";;;;UAqBU,MAAA,CAeA;MAEW,CAAA,GAAA,EAAA,MAAA,EAAA,GAAA,IAAA,EAAA,OAAA,EAAA,CAAA,EAAA,IAAA;MACkD,CAAA,GAAA,EAAA,MAAA,EAAA,GAAA,IAAA,EAAA,OAAA,EAAA,CAAA,EAAA,IAAA;OAGtD,CAAA,GAAA,EAAA,MAAA,EAAA,GAAA,IAAA,EAAA,OAAA,EAAA,CAAA,EAAA,IAAA;OAAgC,CAAA,GAAA,EAAA,MAAA,EAAA,GAAA,IAAA,EAAA,OAAA,EAAA,CAAA,EAAA,IAAA;;UAdvC,oBAAA,CAeuC;QAEW,CAAA,EAhBjD,MAgBiD,CAAA,MAAA,EAAA,OAAA,CAAA;EAAO,YAAA,CAAA,EAAA,MAAA;EAKzD,QAAA,CAAA,EAAO,MAAA;EAAA,MAAA,CAAA,EAlBN,MAkBM;;UAfP,iBAAA,CAoB8B;QAA4B,EAnB1D,MAmB0D;EAAO,gBAAA,CAAA,EAAA,MAAA,GAAA,YAAA,GAAA,eAAA,GAAA,cAAA;EAwNrE,YA+DL,CAAA,IAAA,EAxSoB,OAwSpB,CAAA,EAAA,IAAA;EAAA,qBAAA,CAAA,IAAA,EAAA,MAAA,EAAA,OAAA,EAAA,CAAA,GAAA,IAAA,EAAA,OAAA,EAAA,EAAA,GAvSsE,OAuStE,CAAA,OAAA,CAAA,CAAA,EAAA,IAAA;iBAzDe,EAAA,OAAA,EAAA;IAmDE,EAAA,EAAA,MAAA;IAAiB,KAAA,EAAA,CAAA,GAAA,EA9RlB,oBA8RkB,EAAA,GAAA,IAAA,GA9Rc,OA8Rd,CAAA,IAAA,CAAA;iBA7RlB,gCAAgC;;4DAEW;;;;UAKlD,OAAA;;;;cAII;wCAC0B,4BAA4B;;;;;;;;cAwN9D;;;;;gBAMU;kBAmDE"}
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;EAyLrE,QAAA,CA+DL,EAAA,MAAA;EAAA,MAAA,CAAA,EA/QU,MA+QV;;UA5QS,iBAAA,CAsQQ;EAAiB,MAAA,EArQzB,MAqQyB;;qBAnQd;uEACkD;;;iBAGtD,gCAAgC;iBAChC,gCAAgC;;4DAEW;;;;UAKlD,OAAA;;;;cAII;wCAC0B,4BAA4B;;;;;;;;cAyL9D;;;;;gBAMU;kBAmDE"}
@@ -1 +1 @@
1
- {"version":3,"file":"plugin.d.ts","names":[],"sources":["../src/plugin.ts"],"mappings":";;;;UAqBU,MAAA,CAeA;MAEW,CAAA,GAAA,EAAA,MAAA,EAAA,GAAA,IAAA,EAAA,OAAA,EAAA,CAAA,EAAA,IAAA;MACkD,CAAA,GAAA,EAAA,MAAA,EAAA,GAAA,IAAA,EAAA,OAAA,EAAA,CAAA,EAAA,IAAA;OAGtD,CAAA,GAAA,EAAA,MAAA,EAAA,GAAA,IAAA,EAAA,OAAA,EAAA,CAAA,EAAA,IAAA;OAAgC,CAAA,GAAA,EAAA,MAAA,EAAA,GAAA,IAAA,EAAA,OAAA,EAAA,CAAA,EAAA,IAAA;;UAdvC,oBAAA,CAeuC;QAEW,CAAA,EAhBjD,MAgBiD,CAAA,MAAA,EAAA,OAAA,CAAA;EAAO,YAAA,CAAA,EAAA,MAAA;EAKzD,QAAA,CAAA,EAAO,MAAA;EAAA,MAAA,CAAA,EAlBN,MAkBM;;UAfP,iBAAA,CAoB8B;QAA4B,EAnB1D,MAmB0D;EAAO,gBAAA,CAAA,EAAA,MAAA,GAAA,YAAA,GAAA,eAAA,GAAA,cAAA;EAwNrE,YA+DL,CAAA,IAAA,EAxSoB,OAwSpB,CAAA,EAAA,IAAA;EAAA,qBAAA,CAAA,IAAA,EAAA,MAAA,EAAA,OAAA,EAAA,CAAA,GAAA,IAAA,EAAA,OAAA,EAAA,EAAA,GAvSsE,OAuStE,CAAA,OAAA,CAAA,CAAA,EAAA,IAAA;iBAzDe,EAAA,OAAA,EAAA;IAmDE,EAAA,EAAA,MAAA;IAAiB,KAAA,EAAA,CAAA,GAAA,EA9RlB,oBA8RkB,EAAA,GAAA,IAAA,GA9Rc,OA8Rd,CAAA,IAAA,CAAA;iBA7RlB,gCAAgC;;4DAEW;;;;UAKlD,OAAA;;;;cAII;wCAC0B,4BAA4B;;;;;;;;cAwN9D;;;;;gBAMU;kBAmDE"}
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;EAyLrE,QAAA,CA+DL,EAAA,MAAA;EAAA,MAAA,CAAA,EA/QU,MA+QV;;UA5QS,iBAAA,CAsQQ;EAAiB,MAAA,EArQzB,MAqQyB;;qBAnQd;uEACkD;;;iBAGtD,gCAAgC;iBAChC,gCAAgC;;4DAEW;;;;UAKlD,OAAA;;;;cAII;wCAC0B,4BAA4B;;;;;;;;cAyL9D;;;;;gBAMU;kBAmDE"}
package/dist/plugin.js CHANGED
@@ -10,11 +10,17 @@ import { homedir } from "node:os";
10
10
  * @alfe/openclaw-google — OpenClaw native plugin
11
11
  *
12
12
  * Registers Google Workspace account management tools with OpenClaw.
13
- * Provides multi-account awareness so the agent can:
14
- * - List connected Google accounts
15
- * - Run gws CLI commands targeting a specific account
16
- * - Set the default account
17
- * - Disconnect an account
13
+ * Multi-account by design every gws command requires an explicit
14
+ * `email` (schema-enforced) so the LLM picks the target account per call.
15
+ *
16
+ * Tools:
17
+ * - google_list_accounts — list connected Google accounts
18
+ * - google_run_command — run a gws command (requires email)
19
+ * - google_disconnect_account — disconnect an account
20
+ *
21
+ * 2026-05-14 (connections-redesign PR 1): the "default account" concept is
22
+ * gone. Use `google_list_accounts` to discover available accounts, then
23
+ * pass `email` to `google_run_command` to target one.
18
24
  */
19
25
  const pkg = createRequire(import.meta.url)("../package.json");
20
26
  function ok(data) {
@@ -53,9 +59,8 @@ function defineTool(def) {
53
59
  function sanitizeEmail(email) {
54
60
  return email.replace(/@/g, "-").replace(/\./g, "-");
55
61
  }
56
- function resolveConfigDir(email, isDefault) {
57
- const dirName = isDefault ? "gws" : `gws-${sanitizeEmail(email)}`;
58
- return join(homedir(), ".config", dirName);
62
+ function resolveConfigDir(email) {
63
+ return join(homedir(), ".config", `gws-${sanitizeEmail(email)}`);
59
64
  }
60
65
  let client = null;
61
66
  let cachedAccounts = [];
@@ -64,28 +69,15 @@ function getClient() {
64
69
  return client;
65
70
  }
66
71
  async function refreshAccountCache() {
67
- const creds = await getClient().getGoogleCredentials();
68
- cachedAccounts = (creds.accounts ?? [{
69
- email: creds.email,
70
- isDefault: true,
71
- displayName: void 0
72
- }]).map((a, i) => ({
72
+ cachedAccounts = (await getClient().getGoogleCredentials()).accounts.map((a) => ({
73
73
  email: a.email,
74
- isDefault: i === 0 ? true : a.isDefault,
75
74
  displayName: a.displayName,
76
- configDir: resolveConfigDir(a.email, i === 0 ? true : a.isDefault)
75
+ connectedAt: a.connectedAt,
76
+ configDir: resolveConfigDir(a.email)
77
77
  }));
78
78
  return cachedAccounts;
79
79
  }
80
80
  function findAccount(email) {
81
- if (!email) {
82
- const def = cachedAccounts.find((a) => a.isDefault);
83
- if (!def) {
84
- if (cachedAccounts.length === 0) throw new Error("No Google accounts connected");
85
- return cachedAccounts[0];
86
- }
87
- return def;
88
- }
89
81
  const account = cachedAccounts.find((a) => a.email === email);
90
82
  if (!account) throw new Error(`Google account "${email}" not found. Available: ${cachedAccounts.map((a) => a.email).join(", ")}`);
91
83
  return account;
@@ -111,7 +103,7 @@ function runGwsCommand(args, configDir) {
111
103
  const googleTools = [
112
104
  defineTool({
113
105
  name: "google_list_accounts",
114
- description: "List all connected Google Workspace accounts. Shows email, display name, whether it's the default account, and the gws CLI config directory path. Use this to resolve which account to target (e.g., 'Kevin\\'s emails' → kevin@alfe.ai).",
106
+ 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).",
115
107
  parameters: Type.Object({}),
116
108
  handler: async () => {
117
109
  const accounts = await refreshAccountCache();
@@ -119,7 +111,7 @@ const googleTools = [
119
111
  accounts: accounts.map((a) => ({
120
112
  email: a.email,
121
113
  displayName: a.displayName,
122
- isDefault: a.isDefault,
114
+ connectedAt: a.connectedAt,
123
115
  configDir: a.configDir
124
116
  })),
125
117
  count: accounts.length
@@ -128,13 +120,14 @@ const googleTools = [
128
120
  }),
129
121
  defineTool({
130
122
  name: "google_run_command",
131
- description: "Run a gws (Google Workspace CLI) command targeting a specific account. Automatically sets GOOGLE_WORKSPACE_CLI_CONFIG_DIR for the target account. If no email is specified, uses the default account. Example: google_run_command({ command: 'gmail list', email: 'kevin@alfe.ai' })",
123
+ 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' })",
132
124
  parameters: Type.Object({
133
125
  command: Type.String({ description: "The gws CLI command and arguments (e.g., 'gmail list', 'calendar agenda', 'drive list')" }),
134
- email: Type.Optional(Type.String({ description: "Email of the Google account to use. Omit to use the default account." }))
126
+ email: Type.String({ description: "Email of the Google account to use. Required there is no implicit default." })
135
127
  }),
136
128
  handler: async (params) => {
137
129
  const { command, email } = params;
130
+ if (cachedAccounts.length === 0) await refreshAccountCache();
138
131
  const account = findAccount(email);
139
132
  const args = command.split(/\s+/).filter(Boolean);
140
133
  if (args.length === 0) throw new Error("Command cannot be empty");
@@ -146,23 +139,9 @@ const googleTools = [
146
139
  };
147
140
  }
148
141
  }),
149
- defineTool({
150
- name: "google_set_default_account",
151
- description: "Set a specific Google account as the default. The default account is used when no email is specified in google_run_command.",
152
- parameters: Type.Object({ email: Type.String({ description: "Email of the Google account to set as default" }) }),
153
- handler: async (params) => {
154
- const { email } = params;
155
- const result = await getClient().setDefaultGoogleAccount(email);
156
- await refreshAccountCache();
157
- return {
158
- message: `${email} is now the default Google account`,
159
- accounts: result.accounts
160
- };
161
- }
162
- }),
163
142
  defineTool({
164
143
  name: "google_disconnect_account",
165
- description: "Disconnect a specific Google account from this agent. Revokes the OAuth token and removes the account. If this was the default account, the next account is promoted.",
144
+ description: "Disconnect a specific Google account from this agent. Revokes the OAuth token and removes the account.",
166
145
  parameters: Type.Object({ email: Type.String({ description: "Email of the Google account to disconnect" }) }),
167
146
  handler: async (params) => {
168
147
  const { email } = params;
@@ -178,7 +157,7 @@ const googleTools = [
178
157
  const plugin = {
179
158
  id: "@alfe.ai/openclaw-google",
180
159
  name: "Alfe Google Workspace Plugin",
181
- description: "Multi-account Google Workspace management — list accounts, run gws commands, manage defaults",
160
+ description: "Multi-account Google Workspace management — list accounts and run gws commands with an explicit account selector",
182
161
  version: pkg.version,
183
162
  activate(api) {
184
163
  const log = api.logger;
@@ -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 * Provides multi-account awareness so the agent can:\n * - List connected Google accounts\n * - Run gws CLI commands targeting a specific account\n * - Set the default account\n * - Disconnect an account\n */\n\nimport { Type, type TSchema } from \"@sinclair/typebox\";\nimport { resolveConfig } from \"@alfe.ai/config\";\nimport { AgentApiClient } 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\ninterface GoogleAccountInfo {\n email: string;\n isDefault: boolean;\n displayName?: string;\n configDir: string;\n}\n\nfunction sanitizeEmail(email: string): string {\n return email.replace(/@/g, \"-\").replace(/\\./g, \"-\");\n}\n\nfunction resolveConfigDir(email: string, isDefault: boolean): string {\n const dirName = isDefault ? \"gws\" : `gws-${sanitizeEmail(email)}`;\n return join(homedir(), \".config\", dirName);\n}\n\n// ── State ───────────────────────────────────────────────────\n\nlet client: AgentApiClient | null = null;\nlet cachedAccounts: GoogleAccountInfo[] = [];\n\nfunction getClient(): AgentApiClient {\n if (!client) throw new Error(\"Google plugin not initialized — no API client\");\n return client;\n}\n\nasync function refreshAccountCache(): Promise<GoogleAccountInfo[]> {\n const creds = await getClient().getGoogleCredentials();\n const accounts = creds.accounts ?? [{\n email: creds.email,\n isDefault: true,\n displayName: undefined,\n }];\n\n cachedAccounts = accounts.map((a, i) => ({\n email: a.email,\n isDefault: i === 0 ? true : a.isDefault,\n displayName: a.displayName,\n configDir: resolveConfigDir(a.email, i === 0 ? true : a.isDefault),\n }));\n\n return cachedAccounts;\n}\n\nfunction findAccount(email?: string): GoogleAccountInfo {\n if (!email) {\n const def = cachedAccounts.find((a) => a.isDefault);\n if (!def) {\n if (cachedAccounts.length === 0) throw new Error(\"No Google accounts connected\");\n return cachedAccounts[0];\n }\n return def;\n }\n\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 \"whether it's the default account, 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 isDefault: a.isDefault,\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 \"If no email is specified, uses the default account. \" +\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.Optional(\n Type.String({\n description: \"Email of the Google account to use. Omit to use the default account.\",\n }),\n ),\n }),\n handler: async (params) => {\n const { command, email } = params as { command: string; email?: string };\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_set_default_account\",\n description:\n \"Set a specific Google account as the default. The default account is \" +\n \"used when no email is specified in google_run_command.\",\n parameters: Type.Object({\n email: Type.String({ description: \"Email of the Google account to set as default\" }),\n }),\n handler: async (params) => {\n const { email } = params as { email: string };\n const result = await getClient().setDefaultGoogleAccount(email);\n await refreshAccountCache();\n return {\n message: `${email} is now the default Google account`,\n accounts: result.accounts,\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 \"If this was the default account, the next account is promoted.\",\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, run gws commands, manage defaults\",\n version: pkg.version,\n\n activate(api: OpenClawPluginApi) {\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 } 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 if (!client) throw new Error(\"Google plugin not initialized — no API client\");\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 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,14 +1,13 @@
1
1
  {
2
2
  "id": "@alfe.ai/openclaw-google",
3
3
  "name": "Google Workspace",
4
- "description": "Multi-account Google Workspace management — list accounts, run gws commands, manage defaults",
4
+ "description": "Multi-account Google Workspace management — list accounts and run gws commands with an explicit account selector",
5
5
  "entry": "./dist/plugin.js",
6
6
  "activation": { "onStartup": false },
7
7
  "contracts": {
8
8
  "tools": [
9
9
  "google_run_command",
10
10
  "google_list_accounts",
11
- "google_set_default_account",
12
11
  "google_disconnect_account"
13
12
  ]
14
13
  },
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@alfe.ai/openclaw-google",
3
- "version": "0.0.17",
3
+ "version": "0.0.19",
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,8 @@
28
28
  ],
29
29
  "dependencies": {
30
30
  "@sinclair/typebox": "^0.34.48",
31
- "@alfe.ai/agent-api-client": "0.1.3",
32
- "@alfe.ai/config": "0.0.8"
31
+ "@alfe.ai/agent-api-client": "0.1.4",
32
+ "@alfe.ai/config": "0.0.9"
33
33
  },
34
34
  "scripts": {
35
35
  "build": "tsdown",