@stravica-ai/digital-operator-pa 0.1.17 → 0.1.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/engineering-pa-cli/bin/estate-sync +0 -0
- package/dist/engineering-pa-cli/bin/launch +0 -0
- package/dist/engineering-pa-cli/bin/pa +0 -0
- package/dist/engineering-pa-cli/bin/pa-coordinator-hook +0 -0
- package/dist/engineering-pa-cli/bin/research +0 -0
- package/dist/engineering-pa-cli/bin/scheduler +0 -0
- package/dist/engineering-pa-cli/bin/secrets +0 -0
- package/dist/engineering-pa-cli/bin/smoke +0 -0
- package/dist/engineering-pa-cli/bin/sync +0 -0
- package/dist/engineering-pa-cli/docs/gsc.md +14 -0
- package/dist/engineering-pa-cli/scripts/migrate-doc-paths.mjs +0 -0
- package/dist/engineering-pa-cli/src/gsc/auth.mjs +37 -15
- package/dist/engineering-pa-cli/src/gsc/cloudflare.mjs +16 -7
- package/dist/engineering-pa-core/bin/desk +0 -0
- package/dist/engineering-pa-core/bin/distill +0 -0
- package/dist/engineering-pa-core/bin/memory-sync +0 -0
- package/dist/engineering-pa-core/bin/slice +0 -0
- package/dist/engineering-pa-migrate-inspect/bin/migrate-inspect +0 -0
- package/dist/engineering-pa-scaffold/bin/pa-init +0 -0
- package/dist/engineering-pa-workspace/src/ws.mjs +0 -0
- package/package.json +6 -5
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
@@ -37,10 +37,20 @@ Two npm packages (declared in `@stravica-ai/digital-operator-pa`):
|
|
|
37
37
|
|
|
38
38
|
5. **Store the SA key in the SOPS vault**:
|
|
39
39
|
```bash
|
|
40
|
+
# Legacy single-file vault:
|
|
40
41
|
pnpm exec secrets set GSC_SERVICE_ACCOUNT_KEY -
|
|
42
|
+
# Scoped-vault estate (workspace or canonical layout):
|
|
43
|
+
pnpm exec secrets set --scope <scope> GSC_SERVICE_ACCOUNT_KEY -
|
|
41
44
|
# paste the entire JSON contents, then Ctrl-D
|
|
42
45
|
```
|
|
43
46
|
|
|
47
|
+
On scoped-vault hosts, `pa gsc` reads the key from whichever pot
|
|
48
|
+
`secrets get` would use — i.e. it honours (in order) `SECRETS_DEFAULT_SCOPE`,
|
|
49
|
+
the `defaultScope` in `~/.config/stravica-pa/defaults.json`, or the
|
|
50
|
+
calling PA's `identity.json` — before falling back to the legacy
|
|
51
|
+
single-file vault. Store the key in the pot the host is already
|
|
52
|
+
configured to read; no `pa gsc`-specific configuration is needed.
|
|
53
|
+
|
|
44
54
|
6. **Store Cloudflare credentials** (also one-time). Two key shapes are
|
|
45
55
|
supported; pick whichever fits your setup:
|
|
46
56
|
|
|
@@ -70,6 +80,10 @@ Two npm packages (declared in `@stravica-ai/digital-operator-pa`):
|
|
|
70
80
|
no `--account` flag is passed and the `CLOUDFLARE_PAGES_*` unsuffixed
|
|
71
81
|
pair is absent. Don't rely on it — set the `CLOUDFLARE_PAGES_*` keys.
|
|
72
82
|
|
|
83
|
+
Same scoped-vault note as the SA key: on scoped hosts these live in
|
|
84
|
+
whichever pot `secrets get` reads (see step 5); use `secrets set --scope
|
|
85
|
+
<scope> ...` to store them there.
|
|
86
|
+
|
|
73
87
|
## Per-domain setup (each time you onboard a new property)
|
|
74
88
|
|
|
75
89
|
7. **Grant the service-account access to the GSC property.** Until the
|
|
File without changes
|
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
// override and never touch the network. The SA-key read also accepts an
|
|
9
9
|
// override so tests can pass in a synthetic key without going through SOPS.
|
|
10
10
|
|
|
11
|
-
import {
|
|
11
|
+
import { resolveCommandPaths, checkVault, secretsFileExists, sopsDecrypt, VaultError } from '../secrets/vault.mjs';
|
|
12
12
|
|
|
13
13
|
// Read-only scope: searchanalytics.query + sites.list + sitemaps.list.
|
|
14
14
|
// Read-write scope: sites.add, sitemaps.submit, site verification.
|
|
@@ -25,9 +25,26 @@ const DEFAULT_VAULT_KEY = 'GSC_SERVICE_ACCOUNT_KEY';
|
|
|
25
25
|
/**
|
|
26
26
|
* Load the service-account JSON object from the SOPS vault.
|
|
27
27
|
*
|
|
28
|
+
* Vault path resolution goes through `resolveCommandPaths` — the same
|
|
29
|
+
* resolver `secrets get`/`set` use — so this honours all the same layouts
|
|
30
|
+
* without duplicating logic: an explicit `overrides.paths` (from a
|
|
31
|
+
* pre-resolved `--scope`/`--file`) is a pass-through; otherwise the
|
|
32
|
+
* default-scope resolver runs (SECRETS_DEFAULT_SCOPE env, defaultScope in
|
|
33
|
+
* ~/.config/stravica-pa/defaults.json, or the calling PA's identity.json)
|
|
34
|
+
* and points at a scoped `.vault/scopes/<scope>.enc.json`; if nothing is
|
|
35
|
+
* configured anywhere it falls back to the legacy single-file vault.
|
|
36
|
+
*
|
|
37
|
+
* A stderr `note: writing to '<scope>' pot ...` line is printed by the
|
|
38
|
+
* resolver on the default-scope path — same behaviour as `secrets get`,
|
|
39
|
+
* kept intentionally consistent so operators see the same routing signal
|
|
40
|
+
* from every consumer of the vault.
|
|
41
|
+
*
|
|
28
42
|
* @param {object} overrides
|
|
29
|
-
* @param {object} [overrides.paths] Vault paths (from
|
|
43
|
+
* @param {object} [overrides.paths] Vault paths already resolved (e.g. from --scope upstream).
|
|
30
44
|
* @param {string} [overrides.vaultKey] Vault key name (default GSC_SERVICE_ACCOUNT_KEY).
|
|
45
|
+
* @param {string} [overrides.cwd] Test seam — cwd used for workspace resolution.
|
|
46
|
+
* @param {object} [overrides.env] Test seam — env used for scope-default resolution.
|
|
47
|
+
* @param {string} [overrides.home] Test seam — home used for keyfile + defaults.json lookup.
|
|
31
48
|
* @param {string} [overrides.serviceAccountKey]
|
|
32
49
|
* Raw SA JSON string. If provided, skips vault entirely (testing).
|
|
33
50
|
* @returns {Promise<object>} Parsed service-account JSON.
|
|
@@ -39,34 +56,39 @@ export async function loadServiceAccountKey(overrides = {}) {
|
|
|
39
56
|
: overrides.serviceAccountKey;
|
|
40
57
|
}
|
|
41
58
|
|
|
42
|
-
const paths = overrides
|
|
59
|
+
const paths = resolveCommandPaths(overrides);
|
|
43
60
|
const vaultKey = overrides.vaultKey || DEFAULT_VAULT_KEY;
|
|
44
61
|
|
|
45
62
|
const state = checkVault(paths);
|
|
46
63
|
if (!state.ok) {
|
|
64
|
+
// Print then throw — main() suppresses messages for errors that carry
|
|
65
|
+
// an exitCode (the throw-site is expected to have reported), so
|
|
66
|
+
// omitting the console.error would exit silently through the pa gsc
|
|
67
|
+
// binary.
|
|
68
|
+
console.error(state.message);
|
|
47
69
|
throw new VaultError(state.message, state.exitCode ?? 1);
|
|
48
70
|
}
|
|
49
71
|
if (!secretsFileExists(paths)) {
|
|
50
|
-
|
|
51
|
-
`
|
|
52
|
-
|
|
53
|
-
);
|
|
72
|
+
const message = `No secrets file at ${paths.secretsFile}.` + (paths.scoped
|
|
73
|
+
? ` Store the service-account JSON with:\n secrets set --scope ${paths.scope} ${vaultKey} -\n(then paste the JSON, then Ctrl-D)`
|
|
74
|
+
: " Run 'secrets init' first.");
|
|
75
|
+
console.error(message);
|
|
76
|
+
throw new VaultError(message, 1);
|
|
54
77
|
}
|
|
55
78
|
const data = await sopsDecrypt(paths, paths.secretsFile);
|
|
56
79
|
if (!(vaultKey in data)) {
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
);
|
|
80
|
+
const scopeHint = paths.scoped ? ` --scope ${paths.scope}` : '';
|
|
81
|
+
const message = `Vault key "${vaultKey}" not found in ${paths.secretsFile}. Store the service-account JSON with:\n secrets set${scopeHint} ${vaultKey} -\n(then paste the JSON, then Ctrl-D)`;
|
|
82
|
+
console.error(message);
|
|
83
|
+
throw new VaultError(message, 1);
|
|
61
84
|
}
|
|
62
85
|
const raw = data[vaultKey];
|
|
63
86
|
try {
|
|
64
87
|
return typeof raw === 'string' ? JSON.parse(raw) : raw;
|
|
65
88
|
} catch (e) {
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
);
|
|
89
|
+
const message = `Vault key "${vaultKey}" is not valid JSON: ${e.message}`;
|
|
90
|
+
console.error(message);
|
|
91
|
+
throw new VaultError(message, 2);
|
|
70
92
|
}
|
|
71
93
|
}
|
|
72
94
|
|
|
@@ -24,7 +24,7 @@
|
|
|
24
24
|
//
|
|
25
25
|
// `fetchImpl` and `now` are injectable for tests.
|
|
26
26
|
|
|
27
|
-
import {
|
|
27
|
+
import { resolveCommandPaths, checkVault, secretsFileExists, sopsDecrypt, VaultError } from '../secrets/vault.mjs';
|
|
28
28
|
|
|
29
29
|
const CF_API = 'https://api.cloudflare.com/client/v4';
|
|
30
30
|
|
|
@@ -122,14 +122,23 @@ export async function loadCloudflareCreds(overrides = {}) {
|
|
|
122
122
|
if (overrides.vaultData) {
|
|
123
123
|
data = overrides.vaultData;
|
|
124
124
|
} else {
|
|
125
|
-
|
|
125
|
+
// resolveCommandPaths honours --scope/--file (via a pre-resolved
|
|
126
|
+
// overrides.paths), the SECRETS_DEFAULT_SCOPE env, defaults.json's
|
|
127
|
+
// defaultScope, and the calling PA's identity.json, before falling
|
|
128
|
+
// back to the legacy single-file vault. Same routing as `secrets get`
|
|
129
|
+
// and `pa gsc`'s SA-key loader — one resolver, one routing signal.
|
|
130
|
+
const paths = resolveCommandPaths(overrides);
|
|
126
131
|
const state = checkVault(paths);
|
|
127
|
-
if (!state.ok)
|
|
132
|
+
if (!state.ok) {
|
|
133
|
+
console.error(state.message);
|
|
134
|
+
throw new VaultError(state.message, state.exitCode ?? 1);
|
|
135
|
+
}
|
|
128
136
|
if (!secretsFileExists(paths)) {
|
|
129
|
-
|
|
130
|
-
`
|
|
131
|
-
|
|
132
|
-
);
|
|
137
|
+
const message = `No secrets file at ${paths.secretsFile}.` + (paths.scoped
|
|
138
|
+
? ` Store Cloudflare credentials with 'secrets set --scope ${paths.scope} <KEY> <value>'.`
|
|
139
|
+
: " Run 'secrets init' first.");
|
|
140
|
+
console.error(message);
|
|
141
|
+
throw new VaultError(message, 1);
|
|
133
142
|
}
|
|
134
143
|
data = await sopsDecrypt(paths, paths.secretsFile);
|
|
135
144
|
}
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@stravica-ai/digital-operator-pa",
|
|
3
|
-
"version": "0.1.
|
|
4
|
-
"description": "Digital Operator PA
|
|
3
|
+
"version": "0.1.18",
|
|
4
|
+
"description": "Digital Operator PA \u2014 single-bundled public package surface (bins + library) for the platform.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./index.mjs",
|
|
7
7
|
"exports": {
|
|
@@ -49,10 +49,11 @@
|
|
|
49
49
|
"engines": {
|
|
50
50
|
"node": ">=24"
|
|
51
51
|
},
|
|
52
|
-
"//": "test pins --test-isolation=none: default process-isolation mode silently undercounts (drops passed tests from totals) when a test awaits async work while stdout is patched by a capture() helper — see packages/cli/test/secrets. Counts must never lie.",
|
|
53
52
|
"scripts": {
|
|
54
53
|
"build": "node build.mjs",
|
|
54
|
+
"prepack": "node build.mjs",
|
|
55
55
|
"test": "node --test --test-isolation=none test/*.test.mjs",
|
|
56
56
|
"dry-publish": "node build.mjs && node scripts/dry-publish.mjs"
|
|
57
|
-
}
|
|
58
|
-
|
|
57
|
+
},
|
|
58
|
+
"//": "test pins --test-isolation=none: default process-isolation mode silently undercounts (drops passed tests from totals) when a test awaits async work while stdout is patched by a capture() helper \u2014 see packages/cli/test/secrets. Counts must never lie."
|
|
59
|
+
}
|