@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.
Files changed (85) hide show
  1. package/README.md +91 -0
  2. package/dist/auth/device.d.ts +36 -0
  3. package/dist/auth/device.d.ts.map +1 -0
  4. package/dist/auth/device.js +66 -0
  5. package/dist/auth/device.js.map +1 -0
  6. package/dist/auth/store.d.ts +11 -0
  7. package/dist/auth/store.d.ts.map +1 -0
  8. package/dist/auth/store.js +89 -0
  9. package/dist/auth/store.js.map +1 -0
  10. package/dist/commands/auth.d.ts +4 -0
  11. package/dist/commands/auth.d.ts.map +1 -0
  12. package/dist/commands/auth.js +99 -0
  13. package/dist/commands/auth.js.map +1 -0
  14. package/dist/commands/brand.d.ts +4 -0
  15. package/dist/commands/brand.d.ts.map +1 -0
  16. package/dist/commands/brand.js +53 -0
  17. package/dist/commands/brand.js.map +1 -0
  18. package/dist/commands/call.d.ts +4 -0
  19. package/dist/commands/call.d.ts.map +1 -0
  20. package/dist/commands/call.js +48 -0
  21. package/dist/commands/call.js.map +1 -0
  22. package/dist/commands/egress.d.ts +24 -0
  23. package/dist/commands/egress.d.ts.map +1 -0
  24. package/dist/commands/egress.js +197 -0
  25. package/dist/commands/egress.js.map +1 -0
  26. package/dist/commands/generated.d.ts +41 -0
  27. package/dist/commands/generated.d.ts.map +1 -0
  28. package/dist/commands/generated.js +106 -0
  29. package/dist/commands/generated.js.map +1 -0
  30. package/dist/commands/org.d.ts +4 -0
  31. package/dist/commands/org.d.ts.map +1 -0
  32. package/dist/commands/org.js +49 -0
  33. package/dist/commands/org.js.map +1 -0
  34. package/dist/commands/resources.d.ts +4 -0
  35. package/dist/commands/resources.d.ts.map +1 -0
  36. package/dist/commands/resources.js +22 -0
  37. package/dist/commands/resources.js.map +1 -0
  38. package/dist/commands/review.d.ts +8 -0
  39. package/dist/commands/review.d.ts.map +1 -0
  40. package/dist/commands/review.js +74 -0
  41. package/dist/commands/review.js.map +1 -0
  42. package/dist/commands/skill.d.ts +19 -0
  43. package/dist/commands/skill.d.ts.map +1 -0
  44. package/dist/commands/skill.js +313 -0
  45. package/dist/commands/skill.js.map +1 -0
  46. package/dist/config.d.ts +17 -0
  47. package/dist/config.d.ts.map +1 -0
  48. package/dist/config.js +45 -0
  49. package/dist/config.js.map +1 -0
  50. package/dist/generated/capabilities.json +770 -0
  51. package/dist/http/client.d.ts +12 -0
  52. package/dist/http/client.d.ts.map +1 -0
  53. package/dist/http/client.js +91 -0
  54. package/dist/http/client.js.map +1 -0
  55. package/dist/index.d.ts +4 -0
  56. package/dist/index.d.ts.map +1 -0
  57. package/dist/index.js +39 -0
  58. package/dist/index.js.map +1 -0
  59. package/dist/output/envelope.d.ts +45 -0
  60. package/dist/output/envelope.d.ts.map +1 -0
  61. package/dist/output/envelope.js +86 -0
  62. package/dist/output/envelope.js.map +1 -0
  63. package/dist/output/unwrap.d.ts +11 -0
  64. package/dist/output/unwrap.d.ts.map +1 -0
  65. package/dist/output/unwrap.js +33 -0
  66. package/dist/output/unwrap.js.map +1 -0
  67. package/dist/resolve.d.ts +24 -0
  68. package/dist/resolve.d.ts.map +1 -0
  69. package/dist/resolve.js +24 -0
  70. package/dist/resolve.js.map +1 -0
  71. package/dist/skills/unzip.d.ts +14 -0
  72. package/dist/skills/unzip.d.ts.map +1 -0
  73. package/dist/skills/unzip.js +86 -0
  74. package/dist/skills/unzip.js.map +1 -0
  75. package/dist/update-check.d.ts +24 -0
  76. package/dist/update-check.d.ts.map +1 -0
  77. package/dist/update-check.js +204 -0
  78. package/dist/update-check.js.map +1 -0
  79. package/dist/version.d.ts +3 -0
  80. package/dist/version.d.ts.map +1 -0
  81. package/dist/version.js +3 -0
  82. package/dist/version.js.map +1 -0
  83. package/package.json +61 -0
  84. package/skills/soku/SKILL.md +225 -0
  85. package/skills/soku/references/capability-flow.md +71 -0
package/README.md ADDED
@@ -0,0 +1,91 @@
1
+ # @soku-ai/cli
2
+
3
+ Call Soku ads/GA4 data capabilities from any shell or AI agent. It's the
4
+ preferred surface for external agents — equivalent to the hosted MCP server, but
5
+ works without an MCP host.
6
+
7
+ ## Install
8
+
9
+ ```bash
10
+ npm i -g @soku-ai/cli
11
+ ```
12
+
13
+ Development builds can be linked from this repo:
14
+
15
+ ```bash
16
+ pnpm --filter @soku-ai/cli run build
17
+ npm link apps/cli
18
+ ```
19
+
20
+ ## Quick start
21
+
22
+ ```bash
23
+ soku auth login # device-login in the browser
24
+ soku org use <slug|id> # pick a workspace (id, slug, or name)
25
+ soku brand use <slug|id>
26
+ soku --help # discover namespaces (ads, ga4, …)
27
+ soku update-check # check npm for a newer CLI release
28
+ soku ads --help # actions in a namespace
29
+ soku ads list-ad-accounts --platform google
30
+ ```
31
+
32
+ Each data capability is a typed sub-command under its namespace; `<command>
33
+ --help` shows its flags. `soku call <ns> <action>` is a raw escape hatch for
34
+ actions not yet exposed as a typed sub-command.
35
+
36
+ ## Authentication
37
+
38
+ `soku` uses an org-agnostic, device-login session token (RFC 8628). You log in
39
+ once; the org and brand are chosen at runtime and sent per request.
40
+
41
+ - **Human:** `soku auth login` opens the browser and waits for approval.
42
+ - **Agent (non-blocking):** `soku auth login --no-wait` returns the verification
43
+ URL immediately; resume with `soku auth login --device-code <code>` after the
44
+ user approves.
45
+ - **CI / headless:** set `SOKU_TOKEN` to a pre-issued token to skip interactive
46
+ auth (and the OS keychain).
47
+
48
+ Token storage: OS keychain when available, else `~/.soku/credentials.json`
49
+ (0600). Set `SOKU_NO_KEYCHAIN=1` to always use the file. Behind a proxy, set
50
+ `ALL_PROXY`.
51
+
52
+ ## Output
53
+
54
+ stdout is JSON (`{"ok":true,"data":...}` when piped; pretty when a TTY).
55
+ Errors go to stderr as `{"ok":false,"error":{type,message,hint}}` with a
56
+ semantic exit code (0 ok / 1 usage / 2 auth / 4 not-found / 5 runtime).
57
+
58
+ ## Use from an AI agent
59
+
60
+ For a fresh agent, point it at the hosted installer guide:
61
+
62
+ ```text
63
+ Read https://soku.ai/cli/skill.md and install the Soku CLI.
64
+ ```
65
+
66
+ Install the bundled skill so Claude Code / Codex / Cursor know how to drive the
67
+ CLI:
68
+
69
+ ```bash
70
+ soku skill install --global # all detected agents
71
+ soku skill install --agent claude # one agent, into the project
72
+ ```
73
+
74
+ ## Updates
75
+
76
+ `soku update-check` queries the npm registry for `@soku-ai/cli@latest` and tells
77
+ the user whether `npm i -g @soku-ai/cli` should be re-run. The CLI also performs a
78
+ TTY-only advisory check at most once every 24 hours, cached in
79
+ `~/.soku/update-check.json`. Set `SOKU_NO_UPDATE_CHECK=1` to disable the
80
+ background notice.
81
+
82
+ ## Environment variables
83
+
84
+ | Variable | Purpose |
85
+ |----------|---------|
86
+ | `SOKU_TOKEN` | Pre-issued session token (overrides stored credentials) |
87
+ | `SOKU_API_BASE` | API base URL override |
88
+ | `SOKU_ORG_ID` / `SOKU_BRAND_ID` | One-off workspace override |
89
+ | `SOKU_NO_KEYCHAIN` | Skip the OS keychain; use the 0600 file |
90
+ | `SOKU_NO_UPDATE_CHECK` | Disable the TTY-only update notice |
91
+ | `ALL_PROXY` | Proxy for outbound HTTPS |
@@ -0,0 +1,36 @@
1
+ /** RFC 8628 device authorization client. */
2
+ export interface DeviceAuthorization {
3
+ device_code: string;
4
+ user_code: string;
5
+ verification_uri: string;
6
+ verification_uri_complete: string;
7
+ expires_in: number;
8
+ interval: number;
9
+ }
10
+ export interface TokenResult {
11
+ access_token: string;
12
+ token_type: string;
13
+ expires_in: number;
14
+ scope: string;
15
+ }
16
+ export declare function requestDeviceCode(opts: {
17
+ apiBase?: string;
18
+ scope?: string;
19
+ }): Promise<DeviceAuthorization>;
20
+ export type PollOutcome = {
21
+ status: 'token';
22
+ token: TokenResult;
23
+ } | {
24
+ status: 'denied';
25
+ } | {
26
+ status: 'expired';
27
+ };
28
+ /** Poll until the grant is approved/denied/expired, honoring slow_down (+5s). */
29
+ export declare function pollForToken(opts: {
30
+ deviceCode: string;
31
+ interval: number;
32
+ expiresIn: number;
33
+ apiBase?: string;
34
+ onTick?: () => void;
35
+ }): Promise<PollOutcome>;
36
+ //# sourceMappingURL=device.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"device.d.ts","sourceRoot":"","sources":["../../src/auth/device.ts"],"names":[],"mappings":"AAAA,4CAA4C;AAI5C,MAAM,WAAW,mBAAmB;IAClC,WAAW,EAAE,MAAM,CAAA;IACnB,SAAS,EAAE,MAAM,CAAA;IACjB,gBAAgB,EAAE,MAAM,CAAA;IACxB,yBAAyB,EAAE,MAAM,CAAA;IACjC,UAAU,EAAE,MAAM,CAAA;IAClB,QAAQ,EAAE,MAAM,CAAA;CACjB;AAED,MAAM,WAAW,WAAW;IAC1B,YAAY,EAAE,MAAM,CAAA;IACpB,UAAU,EAAE,MAAM,CAAA;IAClB,UAAU,EAAE,MAAM,CAAA;IAClB,KAAK,EAAE,MAAM,CAAA;CACd;AAID,wBAAsB,iBAAiB,CAAC,IAAI,EAAE;IAC5C,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB,KAAK,CAAC,EAAE,MAAM,CAAA;CACf,GAAG,OAAO,CAAC,mBAAmB,CAAC,CAW/B;AAED,MAAM,MAAM,WAAW,GACnB;IAAE,MAAM,EAAE,OAAO,CAAC;IAAC,KAAK,EAAE,WAAW,CAAA;CAAE,GACvC;IAAE,MAAM,EAAE,QAAQ,CAAA;CAAE,GACpB;IAAE,MAAM,EAAE,SAAS,CAAA;CAAE,CAAA;AAEzB,iFAAiF;AACjF,wBAAsB,YAAY,CAAC,IAAI,EAAE;IACvC,UAAU,EAAE,MAAM,CAAA;IAClB,QAAQ,EAAE,MAAM,CAAA;IAChB,SAAS,EAAE,MAAM,CAAA;IACjB,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB,MAAM,CAAC,EAAE,MAAM,IAAI,CAAA;CACpB,GAAG,OAAO,CAAC,WAAW,CAAC,CA6CvB"}
@@ -0,0 +1,66 @@
1
+ /** RFC 8628 device authorization client. */
2
+ import { resolveApiBaseUrl } from '../config.js';
3
+ const CLIENT_ID = 'soku-cli';
4
+ export async function requestDeviceCode(opts) {
5
+ const base = resolveApiBaseUrl(opts.apiBase);
6
+ const res = await fetch(`${base}/api/device/code`, {
7
+ method: 'POST',
8
+ headers: { 'Content-Type': 'application/json' },
9
+ body: JSON.stringify({ client_id: CLIENT_ID, scope: opts.scope ?? 'data-infra' }),
10
+ });
11
+ if (!res.ok) {
12
+ throw new Error(`Failed to start device authorization (HTTP ${res.status})`);
13
+ }
14
+ return (await res.json());
15
+ }
16
+ /** Poll until the grant is approved/denied/expired, honoring slow_down (+5s). */
17
+ export async function pollForToken(opts) {
18
+ const base = resolveApiBaseUrl(opts.apiBase);
19
+ let interval = Math.max(opts.interval, 1);
20
+ const deadline = Date.now() + opts.expiresIn * 1000;
21
+ while (Date.now() < deadline) {
22
+ await sleep(interval * 1000);
23
+ opts.onTick?.();
24
+ let res;
25
+ try {
26
+ res = await fetch(`${base}/api/device/token`, {
27
+ method: 'POST',
28
+ headers: { 'Content-Type': 'application/json' },
29
+ body: JSON.stringify({
30
+ grant_type: 'urn:ietf:params:oauth:grant-type:device_code',
31
+ device_code: opts.deviceCode,
32
+ client_id: CLIENT_ID,
33
+ }),
34
+ });
35
+ }
36
+ catch {
37
+ // Transient network blip — keep polling until the deadline rather than
38
+ // giving up. RFC 8628 only terminates on a definitive error code.
39
+ continue;
40
+ }
41
+ if (res.ok) {
42
+ return { status: 'token', token: (await res.json()) };
43
+ }
44
+ const body = (await res.json().catch(() => ({})));
45
+ switch (body.error) {
46
+ case 'authorization_pending':
47
+ break;
48
+ case 'slow_down':
49
+ interval += 5;
50
+ break;
51
+ case 'access_denied':
52
+ return { status: 'denied' };
53
+ case 'expired_token':
54
+ return { status: 'expired' };
55
+ default:
56
+ // Unknown/transient server error (e.g. 5xx, 429): don't treat as a
57
+ // terminal expiry — keep polling until the deadline.
58
+ break;
59
+ }
60
+ }
61
+ return { status: 'expired' };
62
+ }
63
+ function sleep(ms) {
64
+ return new Promise((resolve) => setTimeout(resolve, ms));
65
+ }
66
+ //# sourceMappingURL=device.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"device.js","sourceRoot":"","sources":["../../src/auth/device.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,11 @@
1
+ /** Token storage: env override → OS keychain (keytar) → 0600 file fallback.
2
+ *
3
+ * keytar is a native, optional dependency. In headless/CI/container environments
4
+ * (where agents typically run) it can fail to load entirely, so every keytar
5
+ * call is guarded and we fall back to a 0600 file. Agents/CI should prefer the
6
+ * SOKU_TOKEN env var, which bypasses storage completely.
7
+ */
8
+ export declare function saveToken(token: string): Promise<void>;
9
+ export declare function loadToken(): Promise<string | null>;
10
+ export declare function clearToken(): Promise<void>;
11
+ //# sourceMappingURL=store.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"store.d.ts","sourceRoot":"","sources":["../../src/auth/store.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAwCH,wBAAsB,SAAS,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAa5D;AAED,wBAAsB,SAAS,IAAI,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAmBxD;AAED,wBAAsB,UAAU,IAAI,OAAO,CAAC,IAAI,CAAC,CAchD"}
@@ -0,0 +1,89 @@
1
+ /** Token storage: env override → OS keychain (keytar) → 0600 file fallback.
2
+ *
3
+ * keytar is a native, optional dependency. In headless/CI/container environments
4
+ * (where agents typically run) it can fail to load entirely, so every keytar
5
+ * call is guarded and we fall back to a 0600 file. Agents/CI should prefer the
6
+ * SOKU_TOKEN env var, which bypasses storage completely.
7
+ */
8
+ import { mkdirSync, readFileSync, rmSync, writeFileSync } from 'node:fs';
9
+ import { join } from 'node:path';
10
+ import { configDir } from '../config.js';
11
+ const SERVICE = 'soku-cli';
12
+ const ACCOUNT = 'session';
13
+ let keytarWarned = false;
14
+ async function loadKeytar() {
15
+ // Explicit opt-out (CI/tests/containers): skip the OS keychain entirely.
16
+ if (process.env.SOKU_NO_KEYCHAIN)
17
+ return null;
18
+ try {
19
+ const mod = (await import('keytar'));
20
+ return mod.default ?? mod;
21
+ }
22
+ catch {
23
+ if (!keytarWarned) {
24
+ process.stderr.write('soku: OS keychain unavailable; storing token in ~/.soku/credentials.json (0600). ' +
25
+ 'Set SOKU_TOKEN to avoid on-disk storage.\n');
26
+ keytarWarned = true;
27
+ }
28
+ return null;
29
+ }
30
+ }
31
+ function credentialsPath() {
32
+ return join(configDir(), 'credentials.json');
33
+ }
34
+ export async function saveToken(token) {
35
+ const keytar = await loadKeytar();
36
+ if (keytar) {
37
+ try {
38
+ await keytar.setPassword(SERVICE, ACCOUNT, token);
39
+ return;
40
+ }
41
+ catch {
42
+ // fall through to file
43
+ }
44
+ }
45
+ const path = credentialsPath();
46
+ mkdirSync(configDir(), { recursive: true });
47
+ writeFileSync(path, JSON.stringify({ token }), { mode: 0o600 });
48
+ }
49
+ export async function loadToken() {
50
+ const fromEnv = process.env.SOKU_TOKEN?.trim();
51
+ if (fromEnv)
52
+ return fromEnv;
53
+ const keytar = await loadKeytar();
54
+ if (keytar) {
55
+ try {
56
+ const token = await keytar.getPassword(SERVICE, ACCOUNT);
57
+ if (token)
58
+ return token;
59
+ }
60
+ catch {
61
+ // fall through to file
62
+ }
63
+ }
64
+ try {
65
+ const parsed = JSON.parse(readFileSync(credentialsPath(), 'utf8'));
66
+ return parsed.token ?? null;
67
+ }
68
+ catch {
69
+ return null;
70
+ }
71
+ }
72
+ export async function clearToken() {
73
+ const keytar = await loadKeytar();
74
+ if (keytar) {
75
+ try {
76
+ await keytar.deletePassword(SERVICE, ACCOUNT);
77
+ }
78
+ catch {
79
+ // ignore
80
+ }
81
+ }
82
+ try {
83
+ rmSync(credentialsPath(), { force: true });
84
+ }
85
+ catch {
86
+ // ignore
87
+ }
88
+ }
89
+ //# sourceMappingURL=store.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"store.js","sourceRoot":"","sources":["../../src/auth/store.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,4 @@
1
+ /** `soku auth login | status | logout` */
2
+ import { Command } from 'commander';
3
+ export declare function registerAuthCommands(program: Command): void;
4
+ //# sourceMappingURL=auth.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"auth.d.ts","sourceRoot":"","sources":["../../src/commands/auth.ts"],"names":[],"mappings":"AAAA,0CAA0C;AAE1C,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAA;AAUnC,wBAAgB,oBAAoB,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI,CA4F3D"}
@@ -0,0 +1,99 @@
1
+ /** `soku auth login | status | logout` */
2
+ import open from 'open';
3
+ import qrcode from 'qrcode-terminal';
4
+ import { pollForToken, requestDeviceCode } from '../auth/device.js';
5
+ import { clearToken, loadToken, saveToken } from '../auth/store.js';
6
+ import { updateConfig } from '../config.js';
7
+ import { apiRequest } from '../http/client.js';
8
+ import { cyan, dim, emitError, emitSuccess, ExitCode, green } from '../output/envelope.js';
9
+ export function registerAuthCommands(program) {
10
+ const auth = program.command('auth').description('Authenticate the CLI with Soku');
11
+ auth
12
+ .command('login')
13
+ .description('Sign in via device authorization')
14
+ .option('--api-base <url>', 'Override the API base URL')
15
+ .option('--resource <bundles>', 'Resource bundles to request (comma-separated): data-infra, conversion-groups-write', 'data-infra')
16
+ .option('--no-wait', 'Print the verification URL and exit without polling (for agents)')
17
+ .option('--device-code <code>', 'Resume polling for a previously started login')
18
+ .option('--qr', 'Render the verification URL as a QR code')
19
+ .action(async (opts) => {
20
+ // Persist a non-default API base so subsequent commands reuse it.
21
+ if (opts.apiBase)
22
+ updateConfig({ apiBaseUrl: opts.apiBase });
23
+ // Split-flow resume: an agent started with --no-wait, the human approved,
24
+ // now poll for the token.
25
+ if (opts.deviceCode) {
26
+ await pollAndStore({
27
+ deviceCode: opts.deviceCode,
28
+ interval: 5,
29
+ expiresIn: 900,
30
+ apiBase: opts.apiBase,
31
+ });
32
+ return;
33
+ }
34
+ let auth;
35
+ try {
36
+ auth = await requestDeviceCode({ apiBase: opts.apiBase, scope: opts.resource });
37
+ }
38
+ catch (err) {
39
+ emitError('device_code_failed', err.message, ExitCode.RUNTIME);
40
+ }
41
+ if (!opts.wait) {
42
+ // Non-blocking: hand the URL back to the caller (agent surfaces it to the
43
+ // human), then exit. The agent resumes with `--device-code` next turn.
44
+ emitSuccess({
45
+ device_code: auth.device_code,
46
+ user_code: auth.user_code,
47
+ verification_uri: auth.verification_uri,
48
+ verification_uri_complete: auth.verification_uri_complete,
49
+ expires_in: auth.expires_in,
50
+ interval: auth.interval,
51
+ next: `soku auth login --device-code ${auth.device_code}`,
52
+ });
53
+ }
54
+ // Interactive (human) path.
55
+ process.stderr.write(`\n To connect, visit:\n ${auth.verification_uri}\n and enter code:\n ${auth.user_code}\n\n`);
56
+ if (opts.qr) {
57
+ qrcode.generate(auth.verification_uri_complete, { small: true });
58
+ }
59
+ await open(auth.verification_uri_complete).catch(() => undefined);
60
+ process.stderr.write(' Waiting for approval...\n');
61
+ await pollAndStore({
62
+ deviceCode: auth.device_code,
63
+ interval: auth.interval,
64
+ expiresIn: auth.expires_in,
65
+ apiBase: opts.apiBase,
66
+ });
67
+ });
68
+ auth
69
+ .command('status')
70
+ .description('Show the current session')
71
+ .action(async () => {
72
+ const token = await loadToken();
73
+ if (!token) {
74
+ emitError('not_authenticated', 'Not signed in.', ExitCode.AUTH, 'Run `soku auth login`.');
75
+ }
76
+ const me = await apiRequest('/api/cli/me');
77
+ emitSuccess({ signed_in: true, ...me }, (d) => `${green('✓')} Signed in ${dim(`(${d.scope_type})`)}\n owner: ${cyan(d.owner_id)}`);
78
+ });
79
+ auth
80
+ .command('logout')
81
+ .description('Remove the stored session token')
82
+ .action(async () => {
83
+ await clearToken();
84
+ emitSuccess({ signed_out: true }, () => `${green('✓')} Signed out`);
85
+ });
86
+ }
87
+ async function pollAndStore(opts) {
88
+ const outcome = await pollForToken(opts);
89
+ if (outcome.status === 'denied') {
90
+ emitError('access_denied', 'Authorization was denied.', ExitCode.AUTH);
91
+ }
92
+ if (outcome.status === 'expired') {
93
+ emitError('expired', 'The login request expired before approval.', ExitCode.AUTH, 'Run `soku auth login` again.');
94
+ }
95
+ await saveToken(outcome.token.access_token);
96
+ const days = Math.round(outcome.token.expires_in / 86400);
97
+ emitSuccess({ signed_in: true, expires_in: outcome.token.expires_in, scope: outcome.token.scope }, () => `${green('✓')} Signed in ${dim(`(token valid ~${days} days)`)}\n ${dim('Next: soku org list')}`);
98
+ }
99
+ //# sourceMappingURL=auth.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"auth.js","sourceRoot":"","sources":["../../src/commands/auth.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,4 @@
1
+ /** `soku brand list | use <id>` */
2
+ import { Command } from 'commander';
3
+ export declare function registerBrandCommands(program: Command): void;
4
+ //# sourceMappingURL=brand.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"brand.d.ts","sourceRoot":"","sources":["../../src/commands/brand.ts"],"names":[],"mappings":"AAAA,mCAAmC;AAEnC,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAA;AAcnC,wBAAgB,qBAAqB,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI,CAiF5D"}
@@ -0,0 +1,53 @@
1
+ /** `soku brand list | use <id>` */
2
+ import { loadConfig, updateConfig } from '../config.js';
3
+ import { apiRequest } from '../http/client.js';
4
+ import { cyan, dim, emitError, emitSuccess, ExitCode, green, table } from '../output/envelope.js';
5
+ import { matchRef } from '../resolve.js';
6
+ export function registerBrandCommands(program) {
7
+ const brand = program.command('brand').description('Manage the active brand');
8
+ brand
9
+ .command('list')
10
+ .description('List brands in the active organization')
11
+ .option('--org <orgId>', 'Organization to list brands for (defaults to active org)')
12
+ .action(async (opts) => {
13
+ const orgId = opts.org || loadConfig().activeOrgId;
14
+ if (!orgId) {
15
+ emitError('no_org', 'No active organization.', ExitCode.USAGE, 'Run `soku org use <id>` or pass --org.');
16
+ }
17
+ const data = await apiRequest(`/api/cli/brands?org_id=${encodeURIComponent(orgId)}`);
18
+ const activeBrandId = loadConfig().activeBrandId;
19
+ emitSuccess(data, (d) => table(d.brands.map((b) => ({
20
+ active: b.id === activeBrandId ? green('●') : ' ',
21
+ name: b.name,
22
+ slug: b.slug,
23
+ id: b.id,
24
+ })), [
25
+ { key: 'active', header: ' ' },
26
+ { key: 'name', header: 'NAME' },
27
+ { key: 'slug', header: 'SLUG' },
28
+ { key: 'id', header: 'ID' },
29
+ ]));
30
+ });
31
+ brand
32
+ .command('use <brand>')
33
+ .description('Set the active brand (accepts id, slug, or name)')
34
+ .action(async (ref) => {
35
+ const cfg = loadConfig();
36
+ if (!cfg.activeOrgId) {
37
+ emitError('no_org', 'Select an organization first.', ExitCode.USAGE, 'Run `soku org use <slug|id>`.');
38
+ }
39
+ const { brands } = await apiRequest(`/api/cli/brands?org_id=${encodeURIComponent(cfg.activeOrgId)}`);
40
+ const res = matchRef(brands, ref);
41
+ if (res.kind === 'none') {
42
+ emitError('not_found', `No brand matching "${ref}" in the active org.`, ExitCode.NOT_FOUND, 'Run `soku brand list` to see available brands.');
43
+ }
44
+ if (res.kind === 'ambiguous') {
45
+ const candidates = res.matches.map((b) => `${b.slug} (${b.id})`).join(', ');
46
+ emitError('ambiguous', `"${ref}" matches ${res.matches.length} brands.`, ExitCode.USAGE, `Use a slug or id: ${candidates}`);
47
+ }
48
+ const match = res.item;
49
+ updateConfig({ activeBrandId: match.id });
50
+ emitSuccess({ active_org_id: cfg.activeOrgId, active_brand_id: match.id, name: match.name }, (d) => `${green('✓')} Active brand: ${cyan(d.name)} ${dim(`(${d.active_brand_id})`)}\n ${dim('Next: soku --help (data commands under each namespace)')}`);
51
+ });
52
+ }
53
+ //# sourceMappingURL=brand.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"brand.js","sourceRoot":"","sources":["../../src/commands/brand.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,4 @@
1
+ /** `soku call <namespace> <action> [--payload '<json>' | -p key=value ...]` */
2
+ import { Command } from 'commander';
3
+ export declare function registerCallCommand(program: Command): void;
4
+ //# sourceMappingURL=call.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"call.d.ts","sourceRoot":"","sources":["../../src/commands/call.ts"],"names":[],"mappings":"AAAA,+EAA+E;AAE/E,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAA;AAMnC,wBAAgB,mBAAmB,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI,CAyC1D"}
@@ -0,0 +1,48 @@
1
+ /** `soku call <namespace> <action> [--payload '<json>' | -p key=value ...]` */
2
+ import { apiRequest } from '../http/client.js';
3
+ import { emitError, emitSuccess, ExitCode } from '../output/envelope.js';
4
+ import { unwrapDispatch } from '../output/unwrap.js';
5
+ export function registerCallCommand(program) {
6
+ program
7
+ .command('call <namespace> <action>')
8
+ .description('Invoke a data capability')
9
+ .option('--payload <json>', 'Full JSON payload')
10
+ .option('-p, --param <key=value>', 'Set one payload field (repeatable); values are parsed as JSON when possible', collectParam, {})
11
+ .option('--summary <text>', 'Human-readable summary; required for review-gated write actions (becomes the HITL approval description)')
12
+ .action(async (namespace, action, opts) => {
13
+ let payload = {};
14
+ if (opts.payload) {
15
+ try {
16
+ payload = JSON.parse(opts.payload);
17
+ }
18
+ catch {
19
+ emitError('usage', '--payload must be valid JSON.', ExitCode.USAGE);
20
+ }
21
+ }
22
+ payload = { ...payload, ...opts.param };
23
+ // Review-gated write actions return a pending review; the server reads
24
+ // `_summary` as the human-facing approval description.
25
+ if (opts.summary)
26
+ payload._summary = opts.summary;
27
+ const result = await apiRequest(`/api/cli/call/${encodeURIComponent(namespace)}/${encodeURIComponent(action)}`, { method: 'POST', body: payload, workspace: true });
28
+ emitSuccess(unwrapDispatch(result));
29
+ });
30
+ }
31
+ function collectParam(entry, acc) {
32
+ const eq = entry.indexOf('=');
33
+ if (eq === -1) {
34
+ emitError('usage', `--param must be key=value, got: ${entry}`, ExitCode.USAGE);
35
+ }
36
+ const key = entry.slice(0, eq);
37
+ const raw = entry.slice(eq + 1);
38
+ let value = raw;
39
+ try {
40
+ value = JSON.parse(raw);
41
+ }
42
+ catch {
43
+ value = raw; // keep as string
44
+ }
45
+ acc[key] = value;
46
+ return acc;
47
+ }
48
+ //# sourceMappingURL=call.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"call.js","sourceRoot":"","sources":["../../src/commands/call.ts"],"names":[],"mappings":"AAAA,+EAA+E;AAI/E,OAAO,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAA;AAC9C,OAAO,EAAE,SAAS,EAAE,WAAW,EAAE,QAAQ,EAAE,MAAM,uBAAuB,CAAA;AACxE,OAAO,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAA;AAEpD,MAAM,UAAU,mBAAmB,CAAC,OAAgB;IAClD,OAAO;SACJ,OAAO,CAAC,2BAA2B,CAAC;SACpC,WAAW,CAAC,0BAA0B,CAAC;SACvC,MAAM,CAAC,kBAAkB,EAAE,mBAAmB,CAAC;SAC/C,MAAM,CACL,yBAAyB,EACzB,6EAA6E,EAC7E,YAAY,EACZ,EAA6B,CAC9B;SACA,MAAM,CACL,kBAAkB,EAClB,yGAAyG,CAC1G;SACA,MAAM,CACL,KAAK,EACH,SAAiB,EACjB,MAAc,EACd,IAA4E,EAC5E,EAAE;QACF,IAAI,OAAO,GAA4B,EAAE,CAAA;QACzC,IAAI,IAAI,CAAC,OAAO,EAAE,CAAC;YACjB,IAAI,CAAC;gBACH,OAAO,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,OAAO,CAA4B,CAAA;YAC/D,CAAC;YAAC,MAAM,CAAC;gBACP,SAAS,CAAC,OAAO,EAAE,+BAA+B,EAAE,QAAQ,CAAC,KAAK,CAAC,CAAA;YACrE,CAAC;QACH,CAAC;QACD,OAAO,GAAG,EAAE,GAAG,OAAO,EAAE,GAAG,IAAI,CAAC,KAAK,EAAE,CAAA;QACvC,uEAAuE;QACvE,uDAAuD;QACvD,IAAI,IAAI,CAAC,OAAO;YAAE,OAAO,CAAC,QAAQ,GAAG,IAAI,CAAC,OAAO,CAAA;QAEjD,MAAM,MAAM,GAAG,MAAM,UAAU,CAC7B,iBAAiB,kBAAkB,CAAC,SAAS,CAAC,IAAI,kBAAkB,CAAC,MAAM,CAAC,EAAE,EAC9E,EAAE,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,OAAO,EAAE,SAAS,EAAE,IAAI,EAAE,CACnD,CAAA;QACD,WAAW,CAAC,cAAc,CAAC,MAAM,CAAC,CAAC,CAAA;IACrC,CAAC,CACF,CAAA;AACL,CAAC;AAED,SAAS,YAAY,CAAC,KAAa,EAAE,GAA4B;IAC/D,MAAM,EAAE,GAAG,KAAK,CAAC,OAAO,CAAC,GAAG,CAAC,CAAA;IAC7B,IAAI,EAAE,KAAK,CAAC,CAAC,EAAE,CAAC;QACd,SAAS,CAAC,OAAO,EAAE,mCAAmC,KAAK,EAAE,EAAE,QAAQ,CAAC,KAAK,CAAC,CAAA;IAChF,CAAC;IACD,MAAM,GAAG,GAAG,KAAK,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,CAAA;IAC9B,MAAM,GAAG,GAAG,KAAK,CAAC,KAAK,CAAC,EAAE,GAAG,CAAC,CAAC,CAAA;IAC/B,IAAI,KAAK,GAAY,GAAG,CAAA;IACxB,IAAI,CAAC;QACH,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAA;IACzB,CAAC;IAAC,MAAM,CAAC;QACP,KAAK,GAAG,GAAG,CAAA,CAAC,iBAAiB;IAC/B,CAAC;IACD,GAAG,CAAC,GAAG,CAAC,GAAG,KAAK,CAAA;IAChB,OAAO,GAAG,CAAA;AACZ,CAAC"}
@@ -0,0 +1,24 @@
1
+ /** `soku egress -- <curl…>` — proxy a third-party API call through Soku so the
2
+ * credential is injected server-side (no API key on this machine), and
3
+ * `soku egress providers` — list the covered hosts.
4
+ *
5
+ * The agent prefixes its existing skill `curl` with `soku egress --`; we parse
6
+ * the curl, strip any placeholder auth header, and forward the request to
7
+ * `/api/cli/egress`. The upstream response is streamed back to stdout verbatim,
8
+ * so the skill sees exactly what a direct call would return. Only Soku-level
9
+ * failures (auth, allowlist, billing) become a CLI error envelope.
10
+ */
11
+ import { Command } from 'commander';
12
+ export interface ParsedCurl {
13
+ method?: string;
14
+ url?: string;
15
+ headers: Record<string, string>;
16
+ body?: Buffer;
17
+ }
18
+ /** Extract method / url / headers / body from a curl-style token list. Pure. */
19
+ export declare function parseCurl(tokens: string[]): ParsedCurl;
20
+ /** Drop empty / bare-scheme auth headers so the server injects the real key
21
+ * (an empty `Authorization: Bearer ` would otherwise be treated as BYO). */
22
+ export declare function stripPlaceholderAuth(headers: Record<string, string>): Record<string, string>;
23
+ export declare function registerEgressCommands(program: Command): void;
24
+ //# sourceMappingURL=egress.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"egress.d.ts","sourceRoot":"","sources":["../../src/commands/egress.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAOH,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAA;AAOnC,MAAM,WAAW,UAAU;IACzB,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,GAAG,CAAC,EAAE,MAAM,CAAA;IACZ,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;IAC/B,IAAI,CAAC,EAAE,MAAM,CAAA;CACd;AAWD,gFAAgF;AAChF,wBAAgB,SAAS,CAAC,MAAM,EAAE,MAAM,EAAE,GAAG,UAAU,CA0DtD;AAED;4EAC4E;AAC5E,wBAAgB,oBAAoB,CAAC,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAO5F;AA4FD,wBAAgB,sBAAsB,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI,CAkC7D"}