@awesomate/hosting-mcp 0.6.2 → 0.7.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.js +18893 -147
- package/package.json +4 -2
- package/skill/awesomate-hosting/SKILL.md +63 -5
- package/skill/awesomate-hosting/scripts/bootstrap.mjs +365 -58
- package/skill/awesomate-hosting/scripts/deploy.sh +8 -9
- package/skill/awesomate-hosting/scripts/pull-live.sh +4 -6
- package/skill/awesomate-hosting/scripts/resolve-account.mjs +100 -0
- package/skill/awesomate-hosting/scripts/ssh-connect.sh +7 -7
- package/skill/awesomate-n8n/SKILL.md +12 -3
|
@@ -52,18 +52,17 @@ done
|
|
|
52
52
|
echo "--from wp-content looks empty (no themes/ or plugins/). Refusing to --delete against live."; exit 1; }
|
|
53
53
|
case "$DOMAIN" in *[!a-z0-9.-]*|"") echo "Invalid --domain."; exit 1;; esac
|
|
54
54
|
|
|
55
|
-
#
|
|
56
|
-
#
|
|
57
|
-
#
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
console.log(`H=${q(c.ssh.host)}; U=${q(c.ssh.user)}; P=${q(c.ssh.port||22)}; K=${q(c.ssh.keyPath)}; API=${q(c.apiBase)}; PAT=${q(c.pat)}`);
|
|
63
|
-
')"; then
|
|
55
|
+
# Resolve the account (folder pin / AWESOMATE_ACCOUNT / sole profile) and its
|
|
56
|
+
# connection details + PAT via the shared resolver (node is present wherever
|
|
57
|
+
# the MCP runs). Capture first, THEN eval — so a resolver failure (no-shell
|
|
58
|
+
# plan exits 2, unpinned multi-account exits 1) aborts here with its own
|
|
59
|
+
# message instead of leaving $U/$P/… unset for `set -u` to trip on later.
|
|
60
|
+
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
61
|
+
if ! CREDS_EVAL="$(node "$SCRIPT_DIR/resolve-account.mjs" --ssh --api)"; then
|
|
64
62
|
exit 1
|
|
65
63
|
fi
|
|
66
64
|
eval "$CREDS_EVAL"
|
|
65
|
+
echo "→ account: $ACCT"
|
|
67
66
|
|
|
68
67
|
# Harden every credentials-derived field BEFORE it reaches ssh/rsync/scp. rsync
|
|
69
68
|
# re-tokenizes its -e remote-shell string on whitespace (ignoring shell quotes),
|
|
@@ -25,15 +25,13 @@ done
|
|
|
25
25
|
[ -n "$DOMAIN" ] && [ -n "$TO" ] || { echo "Usage: pull-live.sh --domain <live-domain> --to <local-dir> [--docroot PATH]"; exit 1; }
|
|
26
26
|
case "$DOMAIN" in *[!a-z0-9.-]*|"") echo "Invalid --domain."; exit 1;; esac
|
|
27
27
|
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
if
|
|
31
|
-
const q = (s) => "'"'"'" + String(s == null ? "" : s).replace(/'"'"'/g, "") + "'"'"'";
|
|
32
|
-
console.log(`H=${q(c.ssh.host)}; U=${q(c.ssh.user)}; P=${q(c.ssh.port||22)}; K=${q(c.ssh.keyPath)}`);
|
|
33
|
-
')"; then
|
|
28
|
+
# Shared resolver: folder pin / AWESOMATE_ACCOUNT / sole profile → ssh block.
|
|
29
|
+
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
30
|
+
if ! CREDS_EVAL="$(node "$SCRIPT_DIR/resolve-account.mjs" --ssh)"; then
|
|
34
31
|
exit 1
|
|
35
32
|
fi
|
|
36
33
|
eval "$CREDS_EVAL"
|
|
34
|
+
echo "→ account: $ACCT"
|
|
37
35
|
|
|
38
36
|
# Harden credentials-derived fields before they reach rsync/scp/ssh — rsync
|
|
39
37
|
# word-splits its -e remote-shell string, so whitespace here could inject ssh
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* Shared account resolver for the awesomate-hosting skill scripts.
|
|
4
|
+
*
|
|
5
|
+
* Mirrors the MCP server's precedence exactly (mcp/src/config.ts):
|
|
6
|
+
* AWESOMATE_ACCOUNT env → .awesomate.json pin (walked up from cwd, stopping
|
|
7
|
+
* at $HOME) → sole profile → defaultProfile → legacy v1 top-level fields.
|
|
8
|
+
* A pin naming a missing profile is a hard error — never a silent fallback.
|
|
9
|
+
*
|
|
10
|
+
* Prints shell-eval assignments (single-quoted, quote-stripped values — same
|
|
11
|
+
* hardening contract the callers' charclass gates assume):
|
|
12
|
+
* node resolve-account.mjs --ssh # ACCT + H/U/P/K
|
|
13
|
+
* node resolve-account.mjs --api # ACCT + API/PAT
|
|
14
|
+
* Flags combine. Exit 1 = no resolvable account, exit 2 = no shell access.
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
import { existsSync, readFileSync } from 'node:fs';
|
|
18
|
+
import { homedir } from 'node:os';
|
|
19
|
+
import { join, dirname, resolve } from 'node:path';
|
|
20
|
+
|
|
21
|
+
function readJson(path) {
|
|
22
|
+
try {
|
|
23
|
+
const parsed = JSON.parse(readFileSync(path, 'utf8'));
|
|
24
|
+
return parsed && typeof parsed === 'object' ? parsed : null;
|
|
25
|
+
} catch { return null; }
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
function fail(msg, code = 1) { console.error(msg); process.exit(code); }
|
|
29
|
+
|
|
30
|
+
const credPath = join(homedir(), '.awesomate', 'credentials.json');
|
|
31
|
+
const file = readJson(credPath) ?? {};
|
|
32
|
+
|
|
33
|
+
const profiles = {};
|
|
34
|
+
if (file.profiles && typeof file.profiles === 'object') {
|
|
35
|
+
for (const [key, profile] of Object.entries(file.profiles)) {
|
|
36
|
+
if (profile && typeof profile.pat === 'string') profiles[key] = profile;
|
|
37
|
+
}
|
|
38
|
+
} else if (typeof file.pat === 'string' && file.pat) {
|
|
39
|
+
profiles[file.slug || 'default'] = file; // legacy v1 single-account file
|
|
40
|
+
}
|
|
41
|
+
const names = Object.keys(profiles);
|
|
42
|
+
|
|
43
|
+
function findPin() {
|
|
44
|
+
const explicit = process.env.AWESOMATE_PIN_FILE;
|
|
45
|
+
if (explicit) return existsSync(explicit) ? explicit : null;
|
|
46
|
+
const home = resolve(homedir());
|
|
47
|
+
let dir = resolve(process.cwd());
|
|
48
|
+
for (;;) {
|
|
49
|
+
const candidate = join(dir, '.awesomate.json');
|
|
50
|
+
if (existsSync(candidate)) return candidate;
|
|
51
|
+
if (dir === home) return null;
|
|
52
|
+
const parent = dirname(dir);
|
|
53
|
+
if (parent === dir) return null;
|
|
54
|
+
dir = parent;
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
let key = null;
|
|
59
|
+
if (process.env.AWESOMATE_ACCOUNT) {
|
|
60
|
+
key = process.env.AWESOMATE_ACCOUNT;
|
|
61
|
+
if (!profiles[key]) {
|
|
62
|
+
fail(`AWESOMATE_ACCOUNT="${key}" has no matching profile in ${credPath}. Available: ${names.join(', ') || '(none)'}.`);
|
|
63
|
+
}
|
|
64
|
+
} else {
|
|
65
|
+
const pinPath = findPin();
|
|
66
|
+
if (pinPath) {
|
|
67
|
+
const pin = readJson(pinPath);
|
|
68
|
+
key = pin && typeof pin.account === 'string' ? pin.account : null;
|
|
69
|
+
if (!key) fail(`${pinPath} exists but has no "account" field. Expected: {"account": "<slug>"}.`);
|
|
70
|
+
if (!profiles[key]) {
|
|
71
|
+
fail(
|
|
72
|
+
`This folder is pinned to account "${key}" (${pinPath}) but no matching profile exists in ${credPath}. ` +
|
|
73
|
+
`Available: ${names.join(', ') || '(none)'}. Connect "${key}" from hub.awesomate.ai/sites (logged in as it) or fix the pin.`,
|
|
74
|
+
);
|
|
75
|
+
}
|
|
76
|
+
} else if (names.length === 1) {
|
|
77
|
+
key = names[0];
|
|
78
|
+
} else if (file.defaultProfile && profiles[file.defaultProfile]) {
|
|
79
|
+
key = file.defaultProfile;
|
|
80
|
+
} else if (names.length === 0) {
|
|
81
|
+
fail('Not connected. Run the Connect Claude Code setup from hub.awesomate.ai/sites.');
|
|
82
|
+
} else {
|
|
83
|
+
fail(
|
|
84
|
+
`Multiple accounts are connected (${names.join(', ')}) and this folder isn't pinned to one. ` +
|
|
85
|
+
'Create .awesomate.json {"account": "<slug>"} in the project root or set AWESOMATE_ACCOUNT.',
|
|
86
|
+
);
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
const profile = profiles[key];
|
|
91
|
+
const q = (s) => "'" + String(s == null ? '' : s).replace(/'/g, '') + "'";
|
|
92
|
+
const out = [`ACCT=${q(profile.slug ?? key)}`];
|
|
93
|
+
if (process.argv.includes('--ssh')) {
|
|
94
|
+
if (!profile.ssh) fail('Your plan does not include shell access (Support Plus+ required).', 2);
|
|
95
|
+
out.push(`H=${q(profile.ssh.host)}`, `U=${q(profile.ssh.user)}`, `P=${q(profile.ssh.port || 22)}`, `K=${q(profile.ssh.keyPath)}`);
|
|
96
|
+
}
|
|
97
|
+
if (process.argv.includes('--api')) {
|
|
98
|
+
out.push(`API=${q(profile.apiBase || 'https://hub.awesomate.ai')}`, `PAT=${q(profile.pat)}`);
|
|
99
|
+
}
|
|
100
|
+
console.log(out.join('; '));
|
|
@@ -8,12 +8,12 @@ set -euo pipefail
|
|
|
8
8
|
CRED="$HOME/.awesomate/credentials.json"
|
|
9
9
|
[ -f "$CRED" ] || { echo "Not connected. Run bootstrap first (Connect Claude Code on hub.awesomate.ai/sites)."; exit 1; }
|
|
10
10
|
|
|
11
|
-
#
|
|
12
|
-
eval
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
11
|
+
# Resolve WHICH account (folder pin / AWESOMATE_ACCOUNT / sole profile) and its
|
|
12
|
+
# ssh block via the shared resolver. Capture then eval so a resolver failure
|
|
13
|
+
# aborts with its own message instead of tripping set -u later.
|
|
14
|
+
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
15
|
+
if ! CREDS_EVAL="$(node "$SCRIPT_DIR/resolve-account.mjs" --ssh)"; then exit 1; fi
|
|
16
|
+
eval "$CREDS_EVAL"
|
|
17
|
+
echo "→ account: $ACCT" >&2
|
|
18
18
|
|
|
19
19
|
exec ssh -i "$K" -p "$P" -o StrictHostKeyChecking=accept-new "$U@$H" "$@"
|
|
@@ -20,7 +20,13 @@ promote back, and delete drafts — via the `awesomate_n8n_deploy` and
|
|
|
20
20
|
|
|
21
21
|
## 0. First run (every session)
|
|
22
22
|
|
|
23
|
-
|
|
23
|
+
Run `awesomate_whoami` first — n8n tools act on whichever ACCOUNT this folder
|
|
24
|
+
resolves to (folder `.awesomate.json` pin / `AWESOMATE_ACCOUNT` / sole
|
|
25
|
+
profile; see the awesomate-hosting skill's multi-account section). If the slug
|
|
26
|
+
isn't the instance the user means, stop and fix the pin/connection before any
|
|
27
|
+
n8n work. Every tool response is stamped `account: <slug>` — watch it.
|
|
28
|
+
|
|
29
|
+
Then call `awesomate_n8n_context` once before any n8n work and cache the result:
|
|
24
30
|
|
|
25
31
|
- `consented: false` → give the user the `settingsUrl` link (Settings →
|
|
26
32
|
Privacy → "Allow Claude Code to Build n8n Workflows"), wait for them to
|
|
@@ -40,8 +46,11 @@ $env, `$json.body`, task-runner limits, activation semantics).
|
|
|
40
46
|
|
|
41
47
|
## 1. Reads
|
|
42
48
|
|
|
43
|
-
PAT as `Authorization: Bearer <pat>`
|
|
44
|
-
|
|
49
|
+
PAT as `Authorization: Bearer <pat>` against `apiBase`. Resolve the ACTIVE
|
|
50
|
+
profile's pat — `node ~/.claude/skills/awesomate-hosting/scripts/resolve-account.mjs --api`
|
|
51
|
+
— never read `credentials.json`'s top-level `pat` directly when a `profiles`
|
|
52
|
+
map exists (it mirrors the default profile, not necessarily this folder's
|
|
53
|
+
account). Never echo the PAT into the conversation.
|
|
45
54
|
|
|
46
55
|
| What | Endpoint |
|
|
47
56
|
|---|---|
|