@aiguru/google-web-operations 0.6.6 → 0.6.7
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/.env.example +2 -2
- package/docs/INSTALL.he.md +13 -0
- package/hosts/claude/.claude-plugin/plugin.json +1 -1
- package/hosts/codex/.codex-plugin/plugin.json +1 -1
- package/package.json +1 -4
- package/packages/core/src/admin.js +9 -9
- package/packages/core/src/doctor.js +7 -1
- package/tools/build-package.js +0 -1
- package/tools/install.js +53 -7
- package/bin/postinstall.js +0 -21
package/.env.example
CHANGED
|
@@ -31,8 +31,8 @@ GWO_DOCX_TEMPLATE=
|
|
|
31
31
|
|
|
32
32
|
# --- Delivery (worker --deliver) -------------------------------------------
|
|
33
33
|
RESEND_API_KEY=
|
|
34
|
-
GWO_REPORT_FROM=ops@
|
|
35
|
-
GWO_REPORT_TO=
|
|
34
|
+
GWO_REPORT_FROM= # e.g. ops@your-domain.co.il (a domain verified in Resend)
|
|
35
|
+
GWO_REPORT_TO= # e.g. you@your-domain.co.il
|
|
36
36
|
|
|
37
37
|
# --- GitHub (pull requests for repository-backed sites) --------------------
|
|
38
38
|
# Fine-grained token: Contents write + Pull requests write on the site repos.
|
package/docs/INSTALL.he.md
CHANGED
|
@@ -20,6 +20,16 @@ gwo install
|
|
|
20
20
|
|
|
21
21
|
חלופה בלי npm: להוריד את ה‑tgz מעמוד ה‑Releases בריפו ולהריץ `npm i -g ./aiguru-google-web-operations-<version>.tgz` (ה‑`./` חובה).
|
|
22
22
|
|
|
23
|
+
**איפה להריץ.** בטרמינל של המחשב שבו רצים Claude Code וקודקס (ב‑Windows: PowerShell או CMD). לא מתוך סוכן שרץ בסביבה מבודדת (Cowork, קונטיינר, VM): שם ההתקנה עובדת אבל נמחקת בסוף הסשן ולא נראית להוסטים שלך.
|
|
24
|
+
|
|
25
|
+
**Linux/macOS, שגיאת EACCES** על `/usr/lib/node_modules`: להתקין ל‑prefix של המשתמש במקום `sudo`:
|
|
26
|
+
```
|
|
27
|
+
npm config set prefix ~/.npm-global
|
|
28
|
+
export PATH=~/.npm-global/bin:$PATH # וגם ב-~/.bashrc או ~/.zshrc
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
**המצב הוא לכל משתמש ולכל מחשב.** `~/.gwo` (אתרים, הסכמה לגוגל, `.env`) לא משותף בין מחשבים או בין Windows ל‑VM. במחשב נוסף שאמור לראות את אותם אתרים: להעתיק `sites/*.yaml`, ולעשות את ההסכמה לגוגל מחדש.
|
|
32
|
+
|
|
23
33
|
## 2. הפעלה מחדש ובדיקה
|
|
24
34
|
|
|
25
35
|
הפעלה מחדש של Claude Code ו‑Codex, ואז:
|
|
@@ -34,6 +44,9 @@ gwo doctor
|
|
|
34
44
|
|
|
35
45
|
- Claude Code: `/gwo-setup`
|
|
36
46
|
- Codex: "הגדר את Google Web Operations"
|
|
47
|
+
- Claude for Windows / Mac (הדסקטופ, Cowork): "הגדר את Google Web Operations"
|
|
48
|
+
|
|
49
|
+
**Claude Desktop זה הוסט אחר.** הוא לא קורא את הסקילים מהדיסק. `gwo install desktop` (או `gwo install` כשהאפליקציה מותקנת) רושם את ה‑MCP ב‑`claude_desktop_config.json`, ואורז את הסקילים כקובצי zip ב‑`~/.gwo/skills-for-claude-ai/`. אותם מעלים פעם אחת ב‑claude.ai: Settings > Capabilities > Skills > Upload, ומאז הם מסתנכרנים לכל המחשבים של אותו חשבון. בלי ההעלאה, ה‑MCP עובד אבל אין סקילים, והסוכן יעבוד לפי תיאורי הכלים בלבד.
|
|
37
50
|
|
|
38
51
|
הסוכן מוביל: חיבור לגוגל (לתת לו את הנתיב לקובץ ה‑JSON של לקוח ה‑OAuth, לא את התוכן), הסכמה בדפדפן עם חשבון התפעול, רישום אתר ראשון, ורשימת ההרשאות שהלקוח צריך לתת.
|
|
39
52
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "google-web-operations",
|
|
3
|
-
"version": "0.6.
|
|
3
|
+
"version": "0.6.7",
|
|
4
4
|
"description": "Google Web Operations: agent-run SEO and GEO operations for client websites. One core, one MCP server, two hosts (Claude Code, Codex).",
|
|
5
5
|
"author": {
|
|
6
6
|
"name": "AI Guru"
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "google-web-operations",
|
|
3
|
-
"version": "0.6.
|
|
3
|
+
"version": "0.6.7",
|
|
4
4
|
"description": "Google Web Operations: agent-run SEO and GEO operations for client websites. One core, one MCP server, two hosts (Claude Code, Codex).",
|
|
5
5
|
"author": {
|
|
6
6
|
"name": "AI Guru"
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@aiguru/google-web-operations",
|
|
3
|
-
"version": "0.6.
|
|
3
|
+
"version": "0.6.7",
|
|
4
4
|
"description": "Google Web Operations: agent-run SEO and GEO operations for client websites. One core, one MCP server, two hosts (Claude Code, Codex).",
|
|
5
5
|
"license": "UNLICENSED",
|
|
6
6
|
"private": false,
|
|
@@ -15,9 +15,6 @@
|
|
|
15
15
|
"bin": {
|
|
16
16
|
"gwo": "bin/gwo.js"
|
|
17
17
|
},
|
|
18
|
-
"scripts": {
|
|
19
|
-
"postinstall": "node bin/postinstall.js"
|
|
20
|
-
},
|
|
21
18
|
"dependencies": {
|
|
22
19
|
"@modelcontextprotocol/server": "^2.0.0",
|
|
23
20
|
"jszip": "^3.10.2",
|
|
@@ -24,9 +24,9 @@ function siteSetup(s) {
|
|
|
24
24
|
const missing = [];
|
|
25
25
|
const gsc = s.google.search_console?.property ?? null, ga4 = s.google.ga4?.property_id ?? null, gtm = s.google.tag_manager?.public_id ?? null;
|
|
26
26
|
if (!gsc)
|
|
27
|
-
missing.push('Search Console property (
|
|
27
|
+
missing.push('Search Console property (the client grants access, then discover the properties)');
|
|
28
28
|
if (!ga4)
|
|
29
|
-
missing.push('GA4 property id (
|
|
29
|
+
missing.push('GA4 property id (the client grants access, then discover the properties)');
|
|
30
30
|
if (!gtm)
|
|
31
31
|
missing.push('Tag Manager container (none registered; fine if the site has no container)');
|
|
32
32
|
const adapter = s.write?.adapter ?? 'none';
|
|
@@ -62,22 +62,22 @@ export function setupStatus() {
|
|
|
62
62
|
const sites = listSites({ includeDisabled: true }).map(siteSetup);
|
|
63
63
|
const next = [];
|
|
64
64
|
if (!o.clientFile)
|
|
65
|
-
next.push('
|
|
65
|
+
next.push('Connect Google: create a "Desktop app" OAuth client in Google Cloud (one project for all sites), download its JSON, then in the chat "/gwo-setup" and give it the file path (CLI: save it as secrets/oauth-client.json under the state directory).');
|
|
66
66
|
else if (!o.authorised)
|
|
67
|
-
next.push('
|
|
67
|
+
next.push('Authorise the operations Google account: in the chat "/gwo-setup" (CLI: gwo auth login). A browser opens for consent.');
|
|
68
68
|
else {
|
|
69
69
|
if (!writeScopes.ga4_write)
|
|
70
|
-
next.push('Optional:
|
|
70
|
+
next.push('Optional, before creating key events: add the ga4_write scope (chat: ask for it in /gwo-setup; CLI: gwo auth login --scope ga4_write).');
|
|
71
71
|
if (!writeScopes.gtm_edit || !writeScopes.gtm_publish)
|
|
72
|
-
next.push('Optional:
|
|
72
|
+
next.push('Optional, before Tag Manager changes: add the gtm_edit and gtm_publish scopes (CLI: gwo auth login --scope gtm_edit --scope gtm_publish).');
|
|
73
73
|
}
|
|
74
74
|
if (!sites.length)
|
|
75
|
-
next.push('
|
|
75
|
+
next.push('Register the first site: in the chat "/gwo-add-site https://example.co.il" (CLI: gwo add-site <url> --name "...").');
|
|
76
76
|
if (o.authorised && sites.some((s) => !s.searchConsole || !s.ga4PropertyId))
|
|
77
|
-
next.push('
|
|
77
|
+
next.push('Once the client has added the operations account to Search Console and GA4: discover the properties (chat: "discover the Google properties"; CLI: gwo discover --apply).');
|
|
78
78
|
for (const s of sites)
|
|
79
79
|
for (const m of s.missing)
|
|
80
|
-
if (!/
|
|
80
|
+
if (!/discover the properties|none registered/.test(m))
|
|
81
81
|
next.push(`${s.id}: ${m}`);
|
|
82
82
|
if (!env.RESEND_API_KEY)
|
|
83
83
|
next.push('Optional: RESEND_API_KEY, GWO_REPORT_FROM, GWO_REPORT_TO in .env for emailed worker reports.');
|
|
@@ -4,12 +4,18 @@
|
|
|
4
4
|
* by the chat when the operator asks "is everything set up?".
|
|
5
5
|
*/
|
|
6
6
|
import { setupStatus } from "./admin.js";
|
|
7
|
+
import { hostname, userInfo } from 'node:os';
|
|
7
8
|
import { HOME_DIR, IS_CHECKOUT, ROOT } from "./paths.js";
|
|
8
9
|
const OK = ' [ok] ', MISSING = ' [--] ', OPT = ' [..] ';
|
|
9
10
|
export function renderDoctor(s = setupStatus()) {
|
|
10
11
|
const out = [];
|
|
11
12
|
const [major = 0, minor = 0] = process.versions.node.split('.').map(Number);
|
|
12
|
-
|
|
13
|
+
let who = '';
|
|
14
|
+
try {
|
|
15
|
+
who = `${userInfo().username}@${hostname()}`;
|
|
16
|
+
}
|
|
17
|
+
catch { /* cosmetic */ }
|
|
18
|
+
out.push(`Google Web Operations: doctor${who ? ` (${who})` : ''}`, '');
|
|
13
19
|
out.push(`${major > 22 || (major === 22 && minor >= 18) ? OK : MISSING}Node ${process.versions.node} (needs 22.18+)`);
|
|
14
20
|
out.push(`${OK}package ${ROOT}`);
|
|
15
21
|
out.push(`${OK}state ${IS_CHECKOUT ? 'next to the code (repository checkout)' : HOME_DIR}`);
|
package/tools/build-package.js
CHANGED
|
@@ -37,7 +37,6 @@ const out = {
|
|
|
37
37
|
name: pkg['name'], version: pkg['version'], description: pkg['description'], license: pkg['license'] ?? 'UNLICENSED', private: false,
|
|
38
38
|
type: 'module', engines: pkg['engines'], repository: pkg['repository'],
|
|
39
39
|
bin: { gwo: 'bin/gwo.js' },
|
|
40
|
-
scripts: { postinstall: 'node bin/postinstall.js' },
|
|
41
40
|
dependencies: pkg['dependencies'],
|
|
42
41
|
};
|
|
43
42
|
writeFileSync(path.join(OUT, 'package.json'), JSON.stringify(out, null, 2) + '\n');
|
package/tools/install.js
CHANGED
|
@@ -2,12 +2,12 @@
|
|
|
2
2
|
/**
|
|
3
3
|
* Install or remove the hosts' side of Google Web Operations on this machine.
|
|
4
4
|
*
|
|
5
|
-
* gwo install [claude|codex|all] [--print]
|
|
6
|
-
* gwo uninstall [claude|codex|all]
|
|
5
|
+
* gwo install [claude|codex|desktop|all] [--print]
|
|
6
|
+
* gwo uninstall [claude|codex|desktop|all]
|
|
7
7
|
*
|
|
8
8
|
* Claude Code: the MCP server goes into ~/.claude.json (user scope), skills
|
|
9
9
|
* into ~/.claude/skills/gwo-*, slash commands into
|
|
10
|
-
* ~/.claude/commands/gwo-*.md.
|
|
10
|
+
* ~/.claude/commands/gwo-*.md (both show under /gwo-...).
|
|
11
11
|
* Codex: the MCP server goes into ~/.codex/config.toml, skills into
|
|
12
12
|
* ~/.codex/skills/gwo-*.
|
|
13
13
|
* Both: the operator state directory (GWO_HOME, default ~/.gwo) is
|
|
@@ -24,9 +24,13 @@ import path from 'node:path';
|
|
|
24
24
|
import { HOME_DIR, HOSTS_DIR, IS_CHECKOUT, ROOT } from "../packages/core/src/paths.js";
|
|
25
25
|
const argv = process.argv.slice(2);
|
|
26
26
|
const uninstall = argv.includes('uninstall');
|
|
27
|
-
const targetArg = argv.find((a) => ['claude', 'codex', 'all'].includes(a)) ?? 'all';
|
|
27
|
+
const targetArg = argv.find((a) => ['claude', 'codex', 'desktop', 'all'].includes(a)) ?? 'all';
|
|
28
28
|
const print = argv.includes('--print') || argv.includes('--dry-run');
|
|
29
|
-
|
|
29
|
+
/** Claude Desktop (Claude for Windows/Mac, Cowork) keeps its MCP servers in its own config file. */
|
|
30
|
+
const DESKTOP_DIR = process.platform === 'win32' ? path.join(process.env['APPDATA'] ?? path.join(homedir(), 'AppData', 'Roaming'), 'Claude')
|
|
31
|
+
: process.platform === 'darwin' ? path.join(homedir(), 'Library', 'Application Support', 'Claude') : path.join(homedir(), '.config', 'Claude');
|
|
32
|
+
// "all" covers the desktop app only where it is installed, so a plain `gwo install` never invents its config.
|
|
33
|
+
const targets = targetArg === 'all' ? ['claude', 'codex', ...(existsSync(DESKTOP_DIR) ? ['desktop'] : [])] : [targetArg];
|
|
30
34
|
const SERVER = 'google-web-operations';
|
|
31
35
|
const toPosix = (p) => p.replace(/\\/g, '/');
|
|
32
36
|
const node = process.execPath;
|
|
@@ -87,6 +91,45 @@ function claude() {
|
|
|
87
91
|
copyFileSync(path.join(cmdSrc, n), path.join(cmdDst, n));
|
|
88
92
|
}
|
|
89
93
|
}
|
|
94
|
+
// ---------------------------------------------------------------- Claude Desktop (Claude for Windows / Mac, Cowork)
|
|
95
|
+
/**
|
|
96
|
+
* The desktop app reads MCP servers from claude_desktop_config.json. Skills
|
|
97
|
+
* cannot be installed from a local file: they are uploaded once in claude.ai
|
|
98
|
+
* (Settings > Capabilities > Skills) and sync down. So the skills are packed
|
|
99
|
+
* as one zip per skill under <state>/skills-for-claude-ai/ for that upload.
|
|
100
|
+
*/
|
|
101
|
+
async function desktop() {
|
|
102
|
+
const cfg = path.join(DESKTOP_DIR, 'claude_desktop_config.json');
|
|
103
|
+
const skillsSrc = path.join(HOSTS_DIR, 'claude', 'skills');
|
|
104
|
+
const skills = gwoDirs(skillsSrc);
|
|
105
|
+
const zipDir = path.join(HOME_DIR, 'skills-for-claude-ai');
|
|
106
|
+
log(`\nClaude Desktop (Claude for Windows / Mac, Cowork)`);
|
|
107
|
+
log(` MCP server "${SERVER}" ${uninstall ? 'removed from' : 'in'} ${cfg}`);
|
|
108
|
+
log(` ${skills.length} skills packed as zips -> ${zipDir} (upload once in claude.ai: Settings > Capabilities > Skills)`);
|
|
109
|
+
if (print)
|
|
110
|
+
return;
|
|
111
|
+
mkdirSync(DESKTOP_DIR, { recursive: true });
|
|
112
|
+
let json = {};
|
|
113
|
+
if (existsSync(cfg))
|
|
114
|
+
json = JSON.parse(readFileSync(cfg, 'utf8'));
|
|
115
|
+
const servers = (json['mcpServers'] ?? {});
|
|
116
|
+
if (uninstall)
|
|
117
|
+
delete servers[SERVER];
|
|
118
|
+
else
|
|
119
|
+
servers[SERVER] = { command: node, args: [server] };
|
|
120
|
+
json['mcpServers'] = servers;
|
|
121
|
+
writeFileSync(cfg, JSON.stringify(json, null, 2));
|
|
122
|
+
rmSync(zipDir, { recursive: true, force: true });
|
|
123
|
+
if (!uninstall) {
|
|
124
|
+
const { default: JSZip } = await import('jszip');
|
|
125
|
+
mkdirSync(zipDir, { recursive: true });
|
|
126
|
+
for (const n of skills) {
|
|
127
|
+
const zip = new JSZip();
|
|
128
|
+
zip.file(`${n}/SKILL.md`, readFileSync(path.join(skillsSrc, n, 'SKILL.md')));
|
|
129
|
+
writeFileSync(path.join(zipDir, `${n}.zip`), await zip.generateAsync({ type: 'nodebuffer', compression: 'DEFLATE' }));
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
}
|
|
90
133
|
// ---------------------------------------------------------------- Codex
|
|
91
134
|
function codex() {
|
|
92
135
|
const cfg = path.join(CODEX_HOME, 'config.toml');
|
|
@@ -149,6 +192,8 @@ if (targets.includes('claude'))
|
|
|
149
192
|
claude();
|
|
150
193
|
if (targets.includes('codex'))
|
|
151
194
|
codex();
|
|
195
|
+
if (targets.includes('desktop'))
|
|
196
|
+
await desktop();
|
|
152
197
|
if (print)
|
|
153
198
|
log('\nNothing changed. Run without --print to apply.');
|
|
154
199
|
else if (uninstall)
|
|
@@ -159,8 +204,9 @@ else
|
|
|
159
204
|
'Done.',
|
|
160
205
|
'',
|
|
161
206
|
'Next',
|
|
162
|
-
' 1. Restart Claude Code
|
|
163
|
-
' 2. Claude Code: type /gwo-setup. Codex: say "set up Google Web Operations".',
|
|
207
|
+
' 1. Restart Claude Code, Codex or the Claude desktop app so they pick up the server and skills.',
|
|
208
|
+
' 2. Claude Code: type /gwo-setup. Codex or Claude desktop: say "set up Google Web Operations".',
|
|
209
|
+
...(targets.includes('desktop') ? [' Claude desktop: first upload the skill zips once at claude.ai > Settings > Capabilities > Skills.'] : []),
|
|
164
210
|
' The assistant connects Google, registers the first site and tells you what the client must grant.',
|
|
165
211
|
' 3. Any time: gwo doctor (what is configured, what is missing, the next step)',
|
|
166
212
|
'',
|
package/bin/postinstall.js
DELETED
|
@@ -1,21 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
/**
|
|
3
|
-
* Runs after `npm install` of the package. Says one thing, only when it
|
|
4
|
-
* matters: this is a command-line tool and belongs in a global install.
|
|
5
|
-
* Never fails the install.
|
|
6
|
-
*/
|
|
7
|
-
try {
|
|
8
|
-
if (process.env.npm_config_global !== 'true') {
|
|
9
|
-
console.log([
|
|
10
|
-
'',
|
|
11
|
-
' Google Web Operations was installed into this project, not globally.',
|
|
12
|
-
' It is a command-line tool (gwo) and the hosts need a stable path to it:',
|
|
13
|
-
'',
|
|
14
|
-
' npm i -g @aiguru/google-web-operations',
|
|
15
|
-
' gwo install',
|
|
16
|
-
'',
|
|
17
|
-
' (then remove the local copy: node_modules, package.json, package-lock.json if this folder was empty)',
|
|
18
|
-
'',
|
|
19
|
-
].join('\n'));
|
|
20
|
-
}
|
|
21
|
-
} catch { /* never break an install */ }
|