@soku-ai/cli 0.1.0-alpha.0
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/README.md +91 -0
- package/dist/auth/device.d.ts +36 -0
- package/dist/auth/device.d.ts.map +1 -0
- package/dist/auth/device.js +66 -0
- package/dist/auth/device.js.map +1 -0
- package/dist/auth/store.d.ts +11 -0
- package/dist/auth/store.d.ts.map +1 -0
- package/dist/auth/store.js +89 -0
- package/dist/auth/store.js.map +1 -0
- package/dist/commands/auth.d.ts +4 -0
- package/dist/commands/auth.d.ts.map +1 -0
- package/dist/commands/auth.js +99 -0
- package/dist/commands/auth.js.map +1 -0
- package/dist/commands/brand.d.ts +4 -0
- package/dist/commands/brand.d.ts.map +1 -0
- package/dist/commands/brand.js +53 -0
- package/dist/commands/brand.js.map +1 -0
- package/dist/commands/call.d.ts +4 -0
- package/dist/commands/call.d.ts.map +1 -0
- package/dist/commands/call.js +48 -0
- package/dist/commands/call.js.map +1 -0
- package/dist/commands/egress.d.ts +24 -0
- package/dist/commands/egress.d.ts.map +1 -0
- package/dist/commands/egress.js +197 -0
- package/dist/commands/egress.js.map +1 -0
- package/dist/commands/generated.d.ts +41 -0
- package/dist/commands/generated.d.ts.map +1 -0
- package/dist/commands/generated.js +106 -0
- package/dist/commands/generated.js.map +1 -0
- package/dist/commands/org.d.ts +4 -0
- package/dist/commands/org.d.ts.map +1 -0
- package/dist/commands/org.js +49 -0
- package/dist/commands/org.js.map +1 -0
- package/dist/commands/resources.d.ts +4 -0
- package/dist/commands/resources.d.ts.map +1 -0
- package/dist/commands/resources.js +22 -0
- package/dist/commands/resources.js.map +1 -0
- package/dist/commands/review.d.ts +8 -0
- package/dist/commands/review.d.ts.map +1 -0
- package/dist/commands/review.js +74 -0
- package/dist/commands/review.js.map +1 -0
- package/dist/commands/skill.d.ts +19 -0
- package/dist/commands/skill.d.ts.map +1 -0
- package/dist/commands/skill.js +313 -0
- package/dist/commands/skill.js.map +1 -0
- package/dist/config.d.ts +17 -0
- package/dist/config.d.ts.map +1 -0
- package/dist/config.js +45 -0
- package/dist/config.js.map +1 -0
- package/dist/generated/capabilities.json +770 -0
- package/dist/http/client.d.ts +12 -0
- package/dist/http/client.d.ts.map +1 -0
- package/dist/http/client.js +91 -0
- package/dist/http/client.js.map +1 -0
- package/dist/index.d.ts +4 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +39 -0
- package/dist/index.js.map +1 -0
- package/dist/output/envelope.d.ts +45 -0
- package/dist/output/envelope.d.ts.map +1 -0
- package/dist/output/envelope.js +86 -0
- package/dist/output/envelope.js.map +1 -0
- package/dist/output/unwrap.d.ts +11 -0
- package/dist/output/unwrap.d.ts.map +1 -0
- package/dist/output/unwrap.js +33 -0
- package/dist/output/unwrap.js.map +1 -0
- package/dist/resolve.d.ts +24 -0
- package/dist/resolve.d.ts.map +1 -0
- package/dist/resolve.js +24 -0
- package/dist/resolve.js.map +1 -0
- package/dist/skills/unzip.d.ts +14 -0
- package/dist/skills/unzip.d.ts.map +1 -0
- package/dist/skills/unzip.js +86 -0
- package/dist/skills/unzip.js.map +1 -0
- package/dist/update-check.d.ts +24 -0
- package/dist/update-check.d.ts.map +1 -0
- package/dist/update-check.js +204 -0
- package/dist/update-check.js.map +1 -0
- package/dist/version.d.ts +3 -0
- package/dist/version.d.ts.map +1 -0
- package/dist/version.js +3 -0
- package/dist/version.js.map +1 -0
- package/package.json +61 -0
- package/skills/soku/SKILL.md +225 -0
- package/skills/soku/references/capability-flow.md +71 -0
|
@@ -0,0 +1,204 @@
|
|
|
1
|
+
import { mkdirSync, readFileSync, writeFileSync } from 'node:fs';
|
|
2
|
+
import { dirname, join } from 'node:path';
|
|
3
|
+
import { configDir } from './config.js';
|
|
4
|
+
import { cyan, dim, emitError, emitSuccess, ExitCode } from './output/envelope.js';
|
|
5
|
+
import { CLI_PACKAGE_NAME, CLI_VERSION } from './version.js';
|
|
6
|
+
const DEFAULT_REGISTRY_URL = 'https://registry.npmjs.org';
|
|
7
|
+
const INSTALL_COMMAND = `npm i -g ${CLI_PACKAGE_NAME}`;
|
|
8
|
+
const CACHE_TTL_MS = 24 * 60 * 60 * 1000;
|
|
9
|
+
const REQUEST_TIMEOUT_MS = 2500;
|
|
10
|
+
function cachePath() {
|
|
11
|
+
return join(configDir(), 'update-check.json');
|
|
12
|
+
}
|
|
13
|
+
function readCache(now) {
|
|
14
|
+
try {
|
|
15
|
+
const cache = JSON.parse(readFileSync(cachePath(), 'utf8'));
|
|
16
|
+
const checkedAt = new Date(cache.checked_at).getTime();
|
|
17
|
+
if (!Number.isFinite(checkedAt))
|
|
18
|
+
return null;
|
|
19
|
+
if (now.getTime() - checkedAt > CACHE_TTL_MS)
|
|
20
|
+
return null;
|
|
21
|
+
return cache;
|
|
22
|
+
}
|
|
23
|
+
catch {
|
|
24
|
+
return null;
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
function writeCache(cache) {
|
|
28
|
+
const path = cachePath();
|
|
29
|
+
mkdirSync(dirname(path), { recursive: true });
|
|
30
|
+
writeFileSync(path, `${JSON.stringify(cache, null, 2)}\n`, { mode: 0o600 });
|
|
31
|
+
}
|
|
32
|
+
function parseVersionParts(version) {
|
|
33
|
+
const core = version.split('-', 1)[0] ?? '';
|
|
34
|
+
return core.split('.').map((part) => {
|
|
35
|
+
const n = Number(part);
|
|
36
|
+
return Number.isFinite(n) ? n : 0;
|
|
37
|
+
});
|
|
38
|
+
}
|
|
39
|
+
function prereleaseParts(version) {
|
|
40
|
+
const idx = version.indexOf('-');
|
|
41
|
+
if (idx === -1)
|
|
42
|
+
return [];
|
|
43
|
+
return version.slice(idx + 1).split(/[.+]/).filter(Boolean);
|
|
44
|
+
}
|
|
45
|
+
function comparePrerelease(a, b) {
|
|
46
|
+
const aa = prereleaseParts(a);
|
|
47
|
+
const bb = prereleaseParts(b);
|
|
48
|
+
if (aa.length === 0 && bb.length === 0)
|
|
49
|
+
return 0;
|
|
50
|
+
if (aa.length === 0)
|
|
51
|
+
return 1;
|
|
52
|
+
if (bb.length === 0)
|
|
53
|
+
return -1;
|
|
54
|
+
for (let i = 0; i < Math.max(aa.length, bb.length); i++) {
|
|
55
|
+
const left = aa[i];
|
|
56
|
+
const right = bb[i];
|
|
57
|
+
if (left === undefined)
|
|
58
|
+
return -1;
|
|
59
|
+
if (right === undefined)
|
|
60
|
+
return 1;
|
|
61
|
+
const leftNum = /^\d+$/.test(left) ? Number(left) : null;
|
|
62
|
+
const rightNum = /^\d+$/.test(right) ? Number(right) : null;
|
|
63
|
+
if (leftNum !== null && rightNum !== null) {
|
|
64
|
+
const diff = leftNum - rightNum;
|
|
65
|
+
if (diff !== 0)
|
|
66
|
+
return diff;
|
|
67
|
+
continue;
|
|
68
|
+
}
|
|
69
|
+
if (leftNum !== null)
|
|
70
|
+
return -1;
|
|
71
|
+
if (rightNum !== null)
|
|
72
|
+
return 1;
|
|
73
|
+
const diff = left.localeCompare(right);
|
|
74
|
+
if (diff !== 0)
|
|
75
|
+
return diff;
|
|
76
|
+
}
|
|
77
|
+
return 0;
|
|
78
|
+
}
|
|
79
|
+
export function compareSemverish(a, b) {
|
|
80
|
+
const aa = parseVersionParts(a);
|
|
81
|
+
const bb = parseVersionParts(b);
|
|
82
|
+
for (let i = 0; i < Math.max(aa.length, bb.length); i++) {
|
|
83
|
+
const diff = (aa[i] ?? 0) - (bb[i] ?? 0);
|
|
84
|
+
if (diff !== 0)
|
|
85
|
+
return diff;
|
|
86
|
+
}
|
|
87
|
+
return comparePrerelease(a, b);
|
|
88
|
+
}
|
|
89
|
+
async function fetchLatestVersion(registryUrl, fetchImpl) {
|
|
90
|
+
const controller = new AbortController();
|
|
91
|
+
const timeout = setTimeout(() => controller.abort(), REQUEST_TIMEOUT_MS);
|
|
92
|
+
try {
|
|
93
|
+
const res = await fetchImpl(`${registryUrl.replace(/\/$/, '')}/@soku%2fcli/latest`, {
|
|
94
|
+
signal: controller.signal,
|
|
95
|
+
headers: { Accept: 'application/json' },
|
|
96
|
+
});
|
|
97
|
+
if (res.status === 404)
|
|
98
|
+
return null;
|
|
99
|
+
if (!res.ok) {
|
|
100
|
+
throw new Error(`npm registry returned HTTP ${res.status}`);
|
|
101
|
+
}
|
|
102
|
+
const data = (await res.json());
|
|
103
|
+
if (typeof data.version !== 'string' || data.version.length === 0) {
|
|
104
|
+
throw new Error('npm registry response did not include a version');
|
|
105
|
+
}
|
|
106
|
+
return data.version;
|
|
107
|
+
}
|
|
108
|
+
finally {
|
|
109
|
+
clearTimeout(timeout);
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
export async function checkForUpdate(opts = {}) {
|
|
113
|
+
const currentVersion = opts.currentVersion ?? CLI_VERSION;
|
|
114
|
+
const registryUrl = (opts.registryUrl ?? DEFAULT_REGISTRY_URL).replace(/\/$/, '');
|
|
115
|
+
const now = opts.now ?? new Date();
|
|
116
|
+
if (opts.useCache !== false) {
|
|
117
|
+
const cache = readCache(now);
|
|
118
|
+
if (cache) {
|
|
119
|
+
const latestVersion = cache.latest_version;
|
|
120
|
+
return {
|
|
121
|
+
packageName: CLI_PACKAGE_NAME,
|
|
122
|
+
currentVersion,
|
|
123
|
+
latestVersion,
|
|
124
|
+
published: !cache.package_unpublished,
|
|
125
|
+
updateAvailable: latestVersion !== null && compareSemverish(latestVersion, currentVersion) > 0,
|
|
126
|
+
installCommand: INSTALL_COMMAND,
|
|
127
|
+
checkedAt: cache.checked_at,
|
|
128
|
+
registryUrl,
|
|
129
|
+
};
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
const latestVersion = await fetchLatestVersion(registryUrl, opts.fetchImpl ?? fetch);
|
|
133
|
+
const checkedAt = now.toISOString();
|
|
134
|
+
writeCache({
|
|
135
|
+
checked_at: checkedAt,
|
|
136
|
+
latest_version: latestVersion,
|
|
137
|
+
...(latestVersion === null ? { package_unpublished: true } : {}),
|
|
138
|
+
});
|
|
139
|
+
return {
|
|
140
|
+
packageName: CLI_PACKAGE_NAME,
|
|
141
|
+
currentVersion,
|
|
142
|
+
latestVersion,
|
|
143
|
+
published: latestVersion !== null,
|
|
144
|
+
updateAvailable: latestVersion !== null && compareSemverish(latestVersion, currentVersion) > 0,
|
|
145
|
+
installCommand: INSTALL_COMMAND,
|
|
146
|
+
checkedAt,
|
|
147
|
+
registryUrl,
|
|
148
|
+
};
|
|
149
|
+
}
|
|
150
|
+
function shouldSkipNotice() {
|
|
151
|
+
if (!process.stderr.isTTY)
|
|
152
|
+
return true;
|
|
153
|
+
if (process.env.CI)
|
|
154
|
+
return true;
|
|
155
|
+
if (process.env.SOKU_NO_UPDATE_CHECK === '1')
|
|
156
|
+
return true;
|
|
157
|
+
return false;
|
|
158
|
+
}
|
|
159
|
+
export async function maybeNotifyUpdate() {
|
|
160
|
+
if (shouldSkipNotice())
|
|
161
|
+
return;
|
|
162
|
+
try {
|
|
163
|
+
const result = await checkForUpdate();
|
|
164
|
+
if (!result.updateAvailable || !result.latestVersion)
|
|
165
|
+
return;
|
|
166
|
+
process.stderr.write(`${dim('Update available:')} ${CLI_PACKAGE_NAME} ${result.currentVersion} -> ${result.latestVersion}\n` +
|
|
167
|
+
`${dim('Run')} ${cyan(result.installCommand)}\n`);
|
|
168
|
+
}
|
|
169
|
+
catch {
|
|
170
|
+
// Update checks are advisory. Never fail the user command because npm is
|
|
171
|
+
// unreachable, private, or temporarily returning a malformed response.
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
export function registerUpdateCheckCommand(program) {
|
|
175
|
+
program
|
|
176
|
+
.command('update-check')
|
|
177
|
+
.description('Check whether a newer Soku CLI is available on npm')
|
|
178
|
+
.option('--registry-url <url>', 'Override the npm registry URL')
|
|
179
|
+
.action(async (opts) => {
|
|
180
|
+
try {
|
|
181
|
+
const result = await checkForUpdate({
|
|
182
|
+
registryUrl: opts.registryUrl,
|
|
183
|
+
useCache: false,
|
|
184
|
+
});
|
|
185
|
+
emitSuccess(result, (d) => {
|
|
186
|
+
if (!d.published) {
|
|
187
|
+
return `${CLI_PACKAGE_NAME} is not published on npm yet.\n${dim('Use a local linked build for development.')}`;
|
|
188
|
+
}
|
|
189
|
+
if (!d.latestVersion)
|
|
190
|
+
return `${CLI_PACKAGE_NAME}: no published version found.`;
|
|
191
|
+
if (!d.updateAvailable)
|
|
192
|
+
return `${CLI_PACKAGE_NAME} is up to date (${d.currentVersion}).`;
|
|
193
|
+
return [
|
|
194
|
+
`${CLI_PACKAGE_NAME} ${d.currentVersion} -> ${d.latestVersion}`,
|
|
195
|
+
`${dim('Run')} ${cyan(d.installCommand)}`,
|
|
196
|
+
].join('\n');
|
|
197
|
+
});
|
|
198
|
+
}
|
|
199
|
+
catch (err) {
|
|
200
|
+
emitError('update_check_failed', err instanceof Error ? err.message : String(err), ExitCode.RUNTIME, 'Set SOKU_NO_UPDATE_CHECK=1 to disable background checks.');
|
|
201
|
+
}
|
|
202
|
+
});
|
|
203
|
+
}
|
|
204
|
+
//# sourceMappingURL=update-check.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"update-check.js","sourceRoot":"","sources":["../src/update-check.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"version.d.ts","sourceRoot":"","sources":["../src/version.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,gBAAgB,iBAAiB,CAAA;AAC9C,eAAO,MAAM,WAAW,kBAAkB,CAAA"}
|
package/dist/version.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"version.js","sourceRoot":"","sources":["../src/version.ts"],"names":[],"mappings":"AAAA,MAAM,CAAC,MAAM,gBAAgB,GAAG,cAAc,CAAA;AAC9C,MAAM,CAAC,MAAM,WAAW,GAAG,eAAe,CAAA"}
|
package/package.json
ADDED
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@soku-ai/cli",
|
|
3
|
+
"version": "0.1.0-alpha.0",
|
|
4
|
+
"description": "Soku CLI — call Soku ads/GA4 data capabilities from any AI agent or shell.",
|
|
5
|
+
"license": "Proprietary",
|
|
6
|
+
"author": "Soku",
|
|
7
|
+
"homepage": "https://soku.ai/cli",
|
|
8
|
+
"bugs": {
|
|
9
|
+
"url": "https://github.com/About-Intelligence/nex-studio/issues"
|
|
10
|
+
},
|
|
11
|
+
"repository": {
|
|
12
|
+
"type": "git",
|
|
13
|
+
"url": "git+https://github.com/About-Intelligence/nex-studio.git",
|
|
14
|
+
"directory": "apps/cli"
|
|
15
|
+
},
|
|
16
|
+
"keywords": [
|
|
17
|
+
"soku",
|
|
18
|
+
"cli",
|
|
19
|
+
"ai-agent",
|
|
20
|
+
"marketing-analytics",
|
|
21
|
+
"google-ads",
|
|
22
|
+
"ga4"
|
|
23
|
+
],
|
|
24
|
+
"type": "module",
|
|
25
|
+
"bin": {
|
|
26
|
+
"soku": "dist/index.js"
|
|
27
|
+
},
|
|
28
|
+
"files": [
|
|
29
|
+
"dist",
|
|
30
|
+
"skills"
|
|
31
|
+
],
|
|
32
|
+
"engines": {
|
|
33
|
+
"node": ">=20"
|
|
34
|
+
},
|
|
35
|
+
"publishConfig": {
|
|
36
|
+
"access": "public",
|
|
37
|
+
"registry": "https://registry.npmjs.org/"
|
|
38
|
+
},
|
|
39
|
+
"scripts": {
|
|
40
|
+
"build": "tsc && mkdir -p dist/generated && cp src/generated/capabilities.json dist/generated/capabilities.json",
|
|
41
|
+
"typecheck": "tsc --noEmit",
|
|
42
|
+
"test": "tsc -p tsconfig.test.json && node --test \".test-build/**/*.test.js\"",
|
|
43
|
+
"gen:capabilities": "pnpm run build && node scripts/gen-capabilities.ts",
|
|
44
|
+
"clean": "rm -rf dist .test-build",
|
|
45
|
+
"prepublishOnly": "pnpm run clean && pnpm run build"
|
|
46
|
+
},
|
|
47
|
+
"dependencies": {
|
|
48
|
+
"commander": "^12.1.0",
|
|
49
|
+
"fflate": "^0.8.2",
|
|
50
|
+
"open": "^10.1.0",
|
|
51
|
+
"qrcode-terminal": "^0.12.0"
|
|
52
|
+
},
|
|
53
|
+
"optionalDependencies": {
|
|
54
|
+
"keytar": "^7.9.0"
|
|
55
|
+
},
|
|
56
|
+
"devDependencies": {
|
|
57
|
+
"@types/node": "^22.0.0",
|
|
58
|
+
"@types/qrcode-terminal": "^0.12.2",
|
|
59
|
+
"typescript": "^5.7.0"
|
|
60
|
+
}
|
|
61
|
+
}
|
|
@@ -0,0 +1,225 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: soku
|
|
3
|
+
description: >-
|
|
4
|
+
Use when calling Soku ads/GA4 marketing-data capabilities from the shell —
|
|
5
|
+
running `soku auth login`, switching org/brand with `soku org use` /
|
|
6
|
+
`soku brand use`, discovering capabilities, calling a data action, routing a
|
|
7
|
+
third-party API call (Ahrefs/DataForSEO/Firecrawl/Gemini/…) through
|
|
8
|
+
`soku egress`, handling 401/403 errors, or installing/updating the Soku CLI.
|
|
9
|
+
license: Proprietary
|
|
10
|
+
metadata:
|
|
11
|
+
author: nex-ad
|
|
12
|
+
version: "0.1"
|
|
13
|
+
---
|
|
14
|
+
|
|
15
|
+
# Soku CLI
|
|
16
|
+
|
|
17
|
+
The `soku` CLI calls Soku's ads + GA4 data capabilities over HTTP. It is the
|
|
18
|
+
preferred way for an agent to use Soku data — it works in any shell, with no MCP
|
|
19
|
+
host required. Output is JSON on stdout; errors are a JSON envelope on stderr
|
|
20
|
+
with a semantic exit code.
|
|
21
|
+
|
|
22
|
+
## Output & exit codes
|
|
23
|
+
|
|
24
|
+
Every command prints JSON. When piped (non-TTY) success is
|
|
25
|
+
`{"ok": true, "data": ...}`; errors are `{"ok": false, "error": {"type", "message", "hint"}}`.
|
|
26
|
+
|
|
27
|
+
| Exit | Meaning | What to do |
|
|
28
|
+
|------|---------|------------|
|
|
29
|
+
| 0 | Success | Parse `data`. |
|
|
30
|
+
| 1 | Usage / no workspace selected | Fix arguments, or run `soku org use` / `soku brand use`. |
|
|
31
|
+
| 2 | Auth — not signed in or token expired/revoked | Run `soku auth login` (or set `SOKU_TOKEN`). |
|
|
32
|
+
| 4 | Not found (unknown capability) | Re-check via `soku --help` / `soku <ns> --help`. |
|
|
33
|
+
| 5 | Runtime / network | Retry; if behind a proxy set `ALL_PROXY`. |
|
|
34
|
+
|
|
35
|
+
## Authentication
|
|
36
|
+
|
|
37
|
+
`soku` authenticates once per machine with a long-lived, **org-agnostic** session
|
|
38
|
+
token. The org and brand are chosen at runtime (see Workspace), not at login.
|
|
39
|
+
|
|
40
|
+
### Agent path (recommended): non-blocking split-flow
|
|
41
|
+
|
|
42
|
+
Do NOT block a turn waiting for a human at a browser. Start login with
|
|
43
|
+
`--no-wait`, surface the URL, end your turn, then resume:
|
|
44
|
+
|
|
45
|
+
```bash
|
|
46
|
+
soku auth login --no-wait
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
This returns immediately, e.g.:
|
|
50
|
+
|
|
51
|
+
```json
|
|
52
|
+
{"ok":true,"data":{"user_code":"MWVP-ZTSZ","verification_uri":"https://studio.nex.ad/device","verification_uri_complete":"https://studio.nex.ad/device?user_code=MWVP-ZTSZ","device_code":"...","next":"soku auth login --device-code ..."}}
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
Surface `verification_uri` and `user_code` to the user as your turn's final
|
|
56
|
+
message (treat the URL as an opaque string — do not edit, re-encode, or
|
|
57
|
+
re-assemble it). After the user says they approved, resume polling:
|
|
58
|
+
|
|
59
|
+
```bash
|
|
60
|
+
soku auth login --device-code <device_code>
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
### Human path
|
|
64
|
+
|
|
65
|
+
`soku auth login` opens the browser and polls until the user approves. Add
|
|
66
|
+
`--qr` to render the verification URL as a QR code.
|
|
67
|
+
|
|
68
|
+
### CI / non-interactive
|
|
69
|
+
|
|
70
|
+
Set `SOKU_TOKEN` to a pre-issued token; it overrides stored credentials and
|
|
71
|
+
skips all interactive auth. This is the right path for CI and headless agents —
|
|
72
|
+
it also avoids the OS keychain, which may be unavailable in containers.
|
|
73
|
+
|
|
74
|
+
### Session state
|
|
75
|
+
|
|
76
|
+
- `soku auth status` — show the current identity.
|
|
77
|
+
- `soku auth logout` — delete the stored token.
|
|
78
|
+
- On exit code 2 with `expired` / `key_revoked`, the stored token is dropped
|
|
79
|
+
automatically; re-run `soku auth login`.
|
|
80
|
+
|
|
81
|
+
## Workspace (org + brand)
|
|
82
|
+
|
|
83
|
+
The session token carries no org/brand. Pick a workspace before calling data
|
|
84
|
+
actions; the selection is saved to `~/.soku/config.json` and sent as
|
|
85
|
+
`X-Soku-Org` / `X-Soku-Brand` headers.
|
|
86
|
+
|
|
87
|
+
```bash
|
|
88
|
+
soku org list # organizations you belong to
|
|
89
|
+
soku org use <slug|id> # set active org (id, slug, or name; clears a now-mismatched brand)
|
|
90
|
+
soku brand list # brands in the active org
|
|
91
|
+
soku brand use <slug|id> # id, slug, or name
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
Env overrides for one-off invocations: `SOKU_ORG_ID`, `SOKU_BRAND_ID`.
|
|
95
|
+
|
|
96
|
+
## Calling capabilities (discover with --help, then run)
|
|
97
|
+
|
|
98
|
+
Each data capability is a typed sub-command under its namespace. Discover and
|
|
99
|
+
inspect them with `--help` — never guess action names or flags.
|
|
100
|
+
|
|
101
|
+
```bash
|
|
102
|
+
soku resources list # what's granted (e.g. data-infra)
|
|
103
|
+
soku --help # namespaces: ads, ga4, …
|
|
104
|
+
soku ads --help # actions in the ads namespace
|
|
105
|
+
soku ads query-single-dimension --help # flags, types, and usage for one action
|
|
106
|
+
|
|
107
|
+
soku ads list-ad-accounts --platform google
|
|
108
|
+
soku ads query-single-dimension --account-id 123 --dimension campaign \
|
|
109
|
+
--date-start 2026-05-01 --date-end 2026-05-18
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
`<command> --help` is authoritative for valid flags, required vs optional, types,
|
|
113
|
+
and usage. Read it before invoking an unfamiliar action. Object/list flags take a
|
|
114
|
+
JSON string, e.g. `--filters '{"campaign_id":["123"]}'`.
|
|
115
|
+
|
|
116
|
+
### Raw escape hatch
|
|
117
|
+
|
|
118
|
+
`soku call <namespace> <action>` runs any action by its raw (snake_case) name
|
|
119
|
+
with a JSON payload. Use it only when a typed sub-command isn't available — e.g.
|
|
120
|
+
a newer action than this CLI version ships:
|
|
121
|
+
|
|
122
|
+
```bash
|
|
123
|
+
soku call ads list_ad_accounts -p platform=google
|
|
124
|
+
soku call ads query_single_dimension --payload '{"account_id":"123","dimension":"campaign"}'
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
## Write actions (human approval required)
|
|
128
|
+
|
|
129
|
+
Some actions mutate state (e.g. conversion-group writes) and are **review-gated**.
|
|
130
|
+
They are NOT in the typed command tree — call them with `soku call` and a
|
|
131
|
+
required `--summary` describing the change. The call does NOT execute; it returns
|
|
132
|
+
a pending review that a human approves.
|
|
133
|
+
|
|
134
|
+
```bash
|
|
135
|
+
soku call ads create_conversion_group --summary "create group: qualified leads" -p name="Qualified leads"
|
|
136
|
+
# → {"ok":true,"data":{"status":"pending_review","pending_review_id":"<id>","summary":"..."}}
|
|
137
|
+
|
|
138
|
+
soku review list # see pending reviews
|
|
139
|
+
soku review show <id> # full payload + (after approval) result
|
|
140
|
+
soku review approve <id> # a human approves → the action executes now
|
|
141
|
+
soku review deny <id> --feedback "wrong account"
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
- The write resource must be granted to the session (request it at login:
|
|
145
|
+
`soku auth login --resource data-infra,conversion-groups-write`). Without it,
|
|
146
|
+
`soku call` returns 403.
|
|
147
|
+
- Approval is **single-use**: one review = one execution. Approving an already
|
|
148
|
+
decided review is a no-op that returns the existing result.
|
|
149
|
+
- Do NOT loop `soku review approve` expecting retries; a failed approval is
|
|
150
|
+
terminal — create a fresh `soku call` to try again.
|
|
151
|
+
- As an agent: surface the `pending_review_id` + summary to the user and let
|
|
152
|
+
THEM run `soku review approve`. Do not approve on the user's behalf.
|
|
153
|
+
|
|
154
|
+
## Third-party APIs (egress)
|
|
155
|
+
|
|
156
|
+
Skills that call third-party providers (Ahrefs, DataForSEO, Firecrawl, Gemini, …)
|
|
157
|
+
must route the call through `soku egress`. The credential is injected
|
|
158
|
+
server-side, so **no third-party API key lives on this machine**.
|
|
159
|
+
|
|
160
|
+
Prefix the skill's `curl` with `soku egress --`:
|
|
161
|
+
|
|
162
|
+
```bash
|
|
163
|
+
soku egress -- curl -H "Authorization: Bearer $AHREFS_API_KEY" "https://api.ahrefs.com/v3/..."
|
|
164
|
+
```
|
|
165
|
+
|
|
166
|
+
`$AHREFS_API_KEY` is not set here, and that is expected — `soku egress` strips the
|
|
167
|
+
empty auth header, Soku injects the real key, and the upstream response is
|
|
168
|
+
returned verbatim on stdout.
|
|
169
|
+
|
|
170
|
+
- Do NOT pre-flight check whether an API key env var is set, and do NOT abort a
|
|
171
|
+
skill because a key looks missing (no `[ -z "$X_API_KEY" ]` guards, no
|
|
172
|
+
`if not os.environ.get(...)` early-exits). Assume the credential is available;
|
|
173
|
+
just make the call through `soku egress`.
|
|
174
|
+
- A skill's own `SKILL.md` shows the raw `curl -H "Authorization: Bearer $X"`
|
|
175
|
+
form for reference. Ignore whether the key is set — prefix the whole command
|
|
176
|
+
with `soku egress --`.
|
|
177
|
+
- `soku egress providers` lists the covered hosts. For a host NOT listed, the
|
|
178
|
+
proxy does not inject a credential — follow that skill's own auth instead.
|
|
179
|
+
|
|
180
|
+
## Installing / updating the skill
|
|
181
|
+
|
|
182
|
+
```bash
|
|
183
|
+
soku update-check # check whether the CLI itself needs updating
|
|
184
|
+
soku skill install # this meta-skill into .claude / .codex / .cursor (project)
|
|
185
|
+
soku skill install --global # into ~/.claude, ~/.codex, ~/.cursor
|
|
186
|
+
soku skill install --agent claude --global
|
|
187
|
+
```
|
|
188
|
+
|
|
189
|
+
If `soku update-check` reports an available CLI update, ask the user before
|
|
190
|
+
running:
|
|
191
|
+
|
|
192
|
+
```bash
|
|
193
|
+
npm i -g @soku-ai/cli
|
|
194
|
+
```
|
|
195
|
+
|
|
196
|
+
## Installing business skills (account audits, Google Ads, reports, …)
|
|
197
|
+
|
|
198
|
+
Beyond this meta-skill, Soku publishes a catalog of domain skills (e.g.
|
|
199
|
+
`account-audit`, `google-ads`, `ads-report`) that already know which `soku call`
|
|
200
|
+
actions and `soku egress` providers to use — install one and your agent gains
|
|
201
|
+
that playbook.
|
|
202
|
+
|
|
203
|
+
```bash
|
|
204
|
+
soku skill list # browse the catalog
|
|
205
|
+
soku skill install account-audit # install one (into soku-account-audit/)
|
|
206
|
+
soku skill install ads-report google-ads
|
|
207
|
+
soku skill install --all # install everything
|
|
208
|
+
soku skill installed # what's installed locally
|
|
209
|
+
soku skill remove account-audit
|
|
210
|
+
```
|
|
211
|
+
|
|
212
|
+
Each installed skill carries a "Running this skill with the Soku CLI" section
|
|
213
|
+
(its data actions mapped to `soku call`, its third-party APIs to `soku egress`),
|
|
214
|
+
so it runs through this same CLI — no in-sandbox tools, no local API keys.
|
|
215
|
+
|
|
216
|
+
## Security rules
|
|
217
|
+
|
|
218
|
+
- Never print the access token (it grants account access). Prefer `SOKU_TOKEN`
|
|
219
|
+
for CI rather than echoing it.
|
|
220
|
+
- Reads (`data-infra`) run directly. Writes are review-gated: `soku call`
|
|
221
|
+
creates a pending review and only `soku review approve` (a human action)
|
|
222
|
+
executes it. Don't approve on the user's behalf, and don't assume a
|
|
223
|
+
`--yes`-style bypass exists — there isn't one.
|
|
224
|
+
- Pass user-provided values as separate argv elements (the `-p key=value` form),
|
|
225
|
+
never by string-concatenating them into a shell command.
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
# Capability discovery flow
|
|
2
|
+
|
|
3
|
+
A worked example of the discover → inspect → run loop the agent should follow.
|
|
4
|
+
Each data capability is a typed sub-command under its namespace; `--help` is the
|
|
5
|
+
discovery and inspection surface.
|
|
6
|
+
|
|
7
|
+
## 1. Confirm what's granted
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
soku resources list
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
```json
|
|
14
|
+
{"ok":true,"data":{"resources":[{"id":"data-infra","label":"Data Infra","description":"Read-only Soku synchronized data actions for ads and GA4.","enabled":true}],"count":1}}
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
## 2. Discover namespaces and actions with --help
|
|
18
|
+
|
|
19
|
+
```bash
|
|
20
|
+
soku --help # top level: namespaces (ads, ga4, …) plus auth/org/brand
|
|
21
|
+
soku ads --help # actions in the ads namespace
|
|
22
|
+
soku ga4 --help # actions in the ga4 namespace
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
Common ads actions:
|
|
26
|
+
|
|
27
|
+
- `soku ads list-ad-accounts` — discover account_id values (run first when you have none)
|
|
28
|
+
- `soku ads list-dimensions` — discover queryable dimensions/metrics/filters
|
|
29
|
+
- `soku ads query-single-dimension`, `soku ads query-multi-dimension` — metric queries
|
|
30
|
+
- `soku ads gaql-search` — raw Google Ads GAQL
|
|
31
|
+
- `soku ga4 list-properties`, `soku ga4 get-property-overview`, `soku ga4 list-top-pages`, …
|
|
32
|
+
|
|
33
|
+
## 3. Inspect a command's flags before running
|
|
34
|
+
|
|
35
|
+
```bash
|
|
36
|
+
soku ads query-single-dimension --help
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
The help is authoritative for flag names, required vs optional, types, and the
|
|
40
|
+
appended usage notes. Never guess flags — read `--help` first. Object/list flags
|
|
41
|
+
take a JSON string (e.g. `--filters '{"campaign_id":["123"]}'`).
|
|
42
|
+
|
|
43
|
+
## 4. Run
|
|
44
|
+
|
|
45
|
+
```bash
|
|
46
|
+
soku ads list-ad-accounts --platform google
|
|
47
|
+
soku ads query-single-dimension \
|
|
48
|
+
--account-id 1234567890 \
|
|
49
|
+
--dimension campaign \
|
|
50
|
+
--date-start 2026-05-01 \
|
|
51
|
+
--date-end 2026-05-07
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
## Raw escape hatch
|
|
55
|
+
|
|
56
|
+
When no typed sub-command exists for an action (e.g. a newer action than this
|
|
57
|
+
CLI version ships), call it by its raw snake_case name:
|
|
58
|
+
|
|
59
|
+
```bash
|
|
60
|
+
soku call ads query_single_dimension --payload '{"account_id":"1234567890","dimension":"campaign","date_start":"2026-05-01","date_end":"2026-05-07"}'
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
## Typical chaining
|
|
64
|
+
|
|
65
|
+
1. `soku ads list-ad-accounts` → pick an `account_id`.
|
|
66
|
+
2. `soku ads list-dimensions` → learn valid dimension/metric slugs for that account.
|
|
67
|
+
3. `soku ads query-single-dimension` / `query-multi-dimension` with the chosen
|
|
68
|
+
`--account-id` + slugs.
|
|
69
|
+
|
|
70
|
+
Each command's `--help` lists related actions (`see_also`) so you can jump
|
|
71
|
+
between them without re-scanning everything.
|