@alfe.ai/openclaw-google 0.0.17 → 0.0.18
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 +23 -44
- package/dist/plugin.d.cts.map +1 -1
- package/dist/plugin.d.ts.map +1 -1
- package/dist/plugin.js +23 -44
- package/dist/plugin.js.map +1 -1
- package/openclaw.plugin.json +1 -2
- package/package.json +2 -2
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
|
-
*
|
|
13
|
-
*
|
|
14
|
-
*
|
|
15
|
-
*
|
|
16
|
-
* -
|
|
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
|
|
56
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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,
|
|
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
|
-
|
|
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.
|
|
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.
|
|
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.
|
|
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
|
|
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;
|
package/dist/plugin.d.cts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"plugin.d.cts","names":[],"sources":["../src/plugin.ts"],"mappings":";;;;
|
|
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"}
|
package/dist/plugin.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"plugin.d.ts","names":[],"sources":["../src/plugin.ts"],"mappings":";;;;
|
|
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
|
-
*
|
|
14
|
-
*
|
|
15
|
-
*
|
|
16
|
-
*
|
|
17
|
-
* -
|
|
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
|
|
57
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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,
|
|
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
|
-
|
|
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.
|
|
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.
|
|
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.
|
|
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
|
|
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;
|
package/dist/plugin.js.map
CHANGED
|
@@ -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":";;;;;;;;;;;;;;;;;;;;;;;;AAyBA,MAAM,MADU,cAAc,OAAO,KAAK,IAAI,CAC1B,kBAAkB;AA0CtC,SAAS,GAAG,MAAe;AACzB,QAAO;EAAE,SAAS,CAAC;GAAE,MAAM;GAAiB,MAAM,KAAK,UAAU,KAAK;GAAE,CAAC;EAAE,SAAS;EAAM;;AAG5F,SAAS,UAAU,SAAiB;AAClC,QAAO;EAAE,SAAS,CAAC;GAAE,MAAM;GAAiB,MAAM,KAAK,UAAU,EAAE,OAAO,SAAS,CAAC;GAAE,CAAC;EAAE,SAAS,EAAE,OAAO,SAAS;EAAE;;AAGxH,SAAS,WAAW,KAKR;AACV,QAAO;EACL,MAAM,IAAI;EACV,aAAa,IAAI;EACjB,OAAO,IAAI;EACX,YAAY,IAAI;EAChB,SAAS,OAAO,aAAqB,WAAoC;AACvE,OAAI;AAEF,WAAO,GADQ,MAAM,IAAI,QAAQ,OAAO,CACvB;YACV,GAAY;AACnB,WAAO,UAAU,aAAa,QAAQ,EAAE,UAAU,gBAAgB;;;EAGvE;;AAiBH,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;AACnC,KAAI,CAAC,OAAQ,OAAM,IAAI,MAAM,gDAAgD;AAC7E,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,cAAyB;CAC7B,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;EAC/B,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,OAAK,WAAuC,4BAA4B,MAAM;AAC5E,QAAI,MAAM,uDAAuD;AACjE;;AAED,cAAuC,0BAA0B;AAClE,OAAI,KAAK,6CAA6C;AAEtD,OAAI;IACF,MAAM,SAAS,eAAe;AAC9B,aAAS,IAAI,eAAe;KAAE,QAAQ,OAAO;KAAQ,QAAQ,OAAO;KAAQ,CAAC;AAE7E,yBAAqB,CAClB,MAAM,aAAa;AAClB,SAAI,KAAK,UAAU,SAAS,OAAO,UAAU,CAAC,sBAAsB,SAAS,KAAK,MAAM,EAAE,MAAM,CAAC,KAAK,KAAK,GAAG;MAC9G,CACD,OAAO,QAAiB;AACvB,SAAI,KAAK,wCAAwC,eAAe,QAAQ,IAAI,UAAU,YAAY;MAClG;YACG,KAAc;AACrB,QAAI,MAAM,6BAA6B,eAAe,QAAQ,IAAI,UAAU,YAAY;AACxF,QAAI,KAAK,mDAAmD;;AAG9D,OAAI,KAAK,yCAAyC;;EAGpD,MAAM,0BAA0B;AAC7B,cAAuC,0BAA0B;AAClE,YAAS;AACT,oBAAiB,EAAE;AACnB,OAAI,KAAK,uCAAuC;;AAGlD,MAAI,IAAI,gBACN,KAAI,gBAAgB;GAClB,IAAI;GACJ,aAAa;AAAE,wBAAoB;;GACnC,YAAY;AAAE,uBAAmB;;GAClC,CAAC;;CAIN,WAAW,KAAwB;AAChC,aAAuC,0BAA0B;AAClE,WAAS;AACT,mBAAiB,EAAE;AACnB,MAAI,OAAO,KAAK,2CAA2C;;CAE9D"}
|
package/openclaw.plugin.json
CHANGED
|
@@ -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
|
|
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.
|
|
3
|
+
"version": "0.0.18",
|
|
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,7 +28,7 @@
|
|
|
28
28
|
],
|
|
29
29
|
"dependencies": {
|
|
30
30
|
"@sinclair/typebox": "^0.34.48",
|
|
31
|
-
"@alfe.ai/agent-api-client": "0.1.
|
|
31
|
+
"@alfe.ai/agent-api-client": "0.1.4",
|
|
32
32
|
"@alfe.ai/config": "0.0.8"
|
|
33
33
|
},
|
|
34
34
|
"scripts": {
|