@solidactions/cli 1.9.0 → 1.10.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.
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.oauthActionsList = oauthActionsList;
|
|
7
|
+
const axios_1 = __importDefault(require("axios"));
|
|
8
|
+
const chalk_1 = __importDefault(require("chalk"));
|
|
9
|
+
const api_1 = require("../utils/api");
|
|
10
|
+
async function oauthActionsList(platform, options) {
|
|
11
|
+
const config = await (0, api_1.requireConfigWithWorkspace)();
|
|
12
|
+
try {
|
|
13
|
+
const params = { platform };
|
|
14
|
+
if (options.limit)
|
|
15
|
+
params.limit = String(options.limit);
|
|
16
|
+
const response = await axios_1.default.get(`${config.host}/api/v1/oauth-actions`, {
|
|
17
|
+
headers: (0, api_1.getApiHeaders)(config),
|
|
18
|
+
params,
|
|
19
|
+
});
|
|
20
|
+
const actions = response.data.oauth_actions || [];
|
|
21
|
+
if (options.json) {
|
|
22
|
+
process.stdout.write(JSON.stringify(actions, null, 2) + '\n');
|
|
23
|
+
return;
|
|
24
|
+
}
|
|
25
|
+
if (actions.length === 0) {
|
|
26
|
+
console.log('No actions found.');
|
|
27
|
+
return;
|
|
28
|
+
}
|
|
29
|
+
for (const a of actions) {
|
|
30
|
+
const method = (a.method || 'GET').toUpperCase().padEnd(6);
|
|
31
|
+
console.log(`${chalk_1.default.cyan(method)} ${chalk_1.default.white(a.path.padEnd(50))} ${chalk_1.default.gray('— ' + (a.title || ''))}`);
|
|
32
|
+
}
|
|
33
|
+
console.log('');
|
|
34
|
+
console.log(chalk_1.default.gray(`${actions.length} action(s) — use "solidactions oauth-actions search ${platform} <query>" to narrow, or "oauth-actions show ${platform} <action_id>" for detail.`));
|
|
35
|
+
}
|
|
36
|
+
catch (error) {
|
|
37
|
+
if (error.response?.status === 401) {
|
|
38
|
+
console.error(chalk_1.default.red('Authentication failed. Run "solidactions login <api-key>".'));
|
|
39
|
+
}
|
|
40
|
+
else if (error.response) {
|
|
41
|
+
console.error(chalk_1.default.red(`Failed: ${error.response.status}`), error.response.data);
|
|
42
|
+
}
|
|
43
|
+
else {
|
|
44
|
+
console.error(chalk_1.default.red('Connection failed:'), error.message);
|
|
45
|
+
}
|
|
46
|
+
process.exit(1);
|
|
47
|
+
}
|
|
48
|
+
}
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.oauthActionsSearch = oauthActionsSearch;
|
|
7
|
+
const axios_1 = __importDefault(require("axios"));
|
|
8
|
+
const chalk_1 = __importDefault(require("chalk"));
|
|
9
|
+
const api_1 = require("../utils/api");
|
|
10
|
+
async function oauthActionsSearch(platform, query, options) {
|
|
11
|
+
const config = await (0, api_1.requireConfigWithWorkspace)();
|
|
12
|
+
try {
|
|
13
|
+
const params = { platform };
|
|
14
|
+
if (query)
|
|
15
|
+
params.q = query;
|
|
16
|
+
if (options.method)
|
|
17
|
+
params.method = options.method;
|
|
18
|
+
if (options.limit)
|
|
19
|
+
params.limit = String(options.limit);
|
|
20
|
+
const response = await axios_1.default.get(`${config.host}/api/v1/oauth-actions`, {
|
|
21
|
+
headers: (0, api_1.getApiHeaders)(config),
|
|
22
|
+
params,
|
|
23
|
+
});
|
|
24
|
+
const actions = response.data.oauth_actions || [];
|
|
25
|
+
if (options.json) {
|
|
26
|
+
process.stdout.write(JSON.stringify(actions, null, 2) + '\n');
|
|
27
|
+
return;
|
|
28
|
+
}
|
|
29
|
+
if (actions.length === 0) {
|
|
30
|
+
console.log('No actions found.');
|
|
31
|
+
return;
|
|
32
|
+
}
|
|
33
|
+
for (const a of actions) {
|
|
34
|
+
const method = (a.method || 'GET').toUpperCase().padEnd(6);
|
|
35
|
+
const path = a.path || '';
|
|
36
|
+
const title = a.title || '';
|
|
37
|
+
console.log(`${chalk_1.default.cyan(method)} ${chalk_1.default.white(path.padEnd(55))} ${chalk_1.default.gray('— ' + title)}`);
|
|
38
|
+
if (a.tags?.length) {
|
|
39
|
+
console.log(chalk_1.default.gray(` tags: ${a.tags.join(', ')}`));
|
|
40
|
+
}
|
|
41
|
+
console.log(chalk_1.default.gray(` action_id: ${a.action_id}`));
|
|
42
|
+
console.log('');
|
|
43
|
+
}
|
|
44
|
+
console.log(chalk_1.default.gray(`${actions.length} action(s) found.`));
|
|
45
|
+
console.log(chalk_1.default.gray(`Run 'solidactions oauth-actions show ${platform} <action_id>' for full schema + paste-ready snippet.`));
|
|
46
|
+
}
|
|
47
|
+
catch (error) {
|
|
48
|
+
if (error.response?.status === 401) {
|
|
49
|
+
console.error(chalk_1.default.red('Authentication failed. Run "solidactions login <api-key>" to re-configure.'));
|
|
50
|
+
}
|
|
51
|
+
else if (error.response) {
|
|
52
|
+
console.error(chalk_1.default.red(`Failed: ${error.response.status}`), error.response.data);
|
|
53
|
+
}
|
|
54
|
+
else {
|
|
55
|
+
console.error(chalk_1.default.red('Connection failed:'), error.message);
|
|
56
|
+
}
|
|
57
|
+
process.exit(1);
|
|
58
|
+
}
|
|
59
|
+
}
|
|
@@ -0,0 +1,157 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.oauthActionsShow = oauthActionsShow;
|
|
7
|
+
const axios_1 = __importDefault(require("axios"));
|
|
8
|
+
const chalk_1 = __importDefault(require("chalk"));
|
|
9
|
+
const api_1 = require("../utils/api");
|
|
10
|
+
async function oauthActionsShow(platform, actionId, options) {
|
|
11
|
+
const config = await (0, api_1.requireConfigWithWorkspace)();
|
|
12
|
+
try {
|
|
13
|
+
const response = await axios_1.default.get(`${config.host}/api/v1/oauth-actions/${platform}/${encodeURIComponent(actionId)}`, {
|
|
14
|
+
headers: (0, api_1.getApiHeaders)(config),
|
|
15
|
+
});
|
|
16
|
+
const action = response.data.oauth_action;
|
|
17
|
+
if (options.json) {
|
|
18
|
+
process.stdout.write(JSON.stringify(action, null, 2) + '\n');
|
|
19
|
+
return;
|
|
20
|
+
}
|
|
21
|
+
renderHumanReadable(action);
|
|
22
|
+
}
|
|
23
|
+
catch (error) {
|
|
24
|
+
if (error.response?.status === 404) {
|
|
25
|
+
console.error(chalk_1.default.red(`Action not found: ${platform}/${actionId}`));
|
|
26
|
+
}
|
|
27
|
+
else if (error.response?.status === 401) {
|
|
28
|
+
console.error(chalk_1.default.red('Authentication failed. Run "solidactions login <api-key>".'));
|
|
29
|
+
}
|
|
30
|
+
else if (error.response) {
|
|
31
|
+
console.error(chalk_1.default.red(`Failed: ${error.response.status}`), error.response.data);
|
|
32
|
+
}
|
|
33
|
+
else {
|
|
34
|
+
console.error(chalk_1.default.red('Connection failed:'), error.message);
|
|
35
|
+
}
|
|
36
|
+
process.exit(1);
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
function renderHumanReadable(action) {
|
|
40
|
+
const method = (action.method || 'GET').toUpperCase();
|
|
41
|
+
const ioSchema = action.io_schema || {};
|
|
42
|
+
const example = ioSchema.ioExample?.input;
|
|
43
|
+
console.log(chalk_1.default.bold(action.title));
|
|
44
|
+
console.log(chalk_1.default.cyan(`${method} ${action.path}`));
|
|
45
|
+
console.log(chalk_1.default.gray(`action_id: ${action.action_id}`));
|
|
46
|
+
if (action.tags?.length)
|
|
47
|
+
console.log(chalk_1.default.gray(`tags: ${action.tags.join(', ')}`));
|
|
48
|
+
console.log('');
|
|
49
|
+
const inputDesc = ioSchema.inputSchema?.description;
|
|
50
|
+
if (inputDesc) {
|
|
51
|
+
console.log(chalk_1.default.bold('Description:'));
|
|
52
|
+
console.log(indent(inputDesc));
|
|
53
|
+
console.log('');
|
|
54
|
+
}
|
|
55
|
+
if (example?.path && Object.keys(example.path).length) {
|
|
56
|
+
console.log(chalk_1.default.bold('Path parameters (example values):'));
|
|
57
|
+
console.log(chalk_1.default.gray(indent(JSON.stringify(example.path, null, 2))));
|
|
58
|
+
console.log('');
|
|
59
|
+
}
|
|
60
|
+
if (example?.query && Object.keys(example.query).length) {
|
|
61
|
+
console.log(chalk_1.default.bold('Query parameters (example values):'));
|
|
62
|
+
console.log(chalk_1.default.gray(indent(JSON.stringify(example.query, null, 2))));
|
|
63
|
+
console.log('');
|
|
64
|
+
}
|
|
65
|
+
if (example?.body !== undefined && example.body !== null) {
|
|
66
|
+
console.log(chalk_1.default.bold('Example request body:'));
|
|
67
|
+
console.log(chalk_1.default.gray(indent(JSON.stringify(example.body, null, 2))));
|
|
68
|
+
console.log('');
|
|
69
|
+
}
|
|
70
|
+
if (ioSchema.ioExample?.output !== undefined) {
|
|
71
|
+
console.log(chalk_1.default.bold('Example response:'));
|
|
72
|
+
console.log(chalk_1.default.gray(indent(JSON.stringify(ioSchema.ioExample.output, null, 2))));
|
|
73
|
+
console.log('');
|
|
74
|
+
}
|
|
75
|
+
console.log(chalk_1.default.bold('Paste-ready snippet:'));
|
|
76
|
+
console.log(chalk_1.default.gray(indent(buildSnippet(action))));
|
|
77
|
+
}
|
|
78
|
+
/**
|
|
79
|
+
* Render a fetch() call against the SA proxy with `{{name}}` path placeholders
|
|
80
|
+
* rewritten to `${name}` template-literal slots, query parameters appended,
|
|
81
|
+
* and the example body inlined verbatim (so the AI substitutes values into the
|
|
82
|
+
* known-good shape rather than inferring it from JSON Schema).
|
|
83
|
+
*/
|
|
84
|
+
function buildSnippet(action) {
|
|
85
|
+
const method = (action.method || 'GET').toUpperCase();
|
|
86
|
+
const platform = action.platform;
|
|
87
|
+
const platformEnv = platform.toUpperCase().replace(/-/g, '_');
|
|
88
|
+
const example = action.io_schema?.ioExample?.input;
|
|
89
|
+
const pathTemplate = (action.path || '').replace(/\{\{\s*([^}]+?)\s*\}\}/g, (_m, name) => '${' + name.trim() + '}');
|
|
90
|
+
const queryEntries = example?.query ? Object.entries(example.query) : [];
|
|
91
|
+
const queryString = queryEntries.length
|
|
92
|
+
? '?' + queryEntries.flatMap(([k, v]) => {
|
|
93
|
+
const encodedKey = encodeURIComponent(k);
|
|
94
|
+
const items = Array.isArray(v) ? v : [v];
|
|
95
|
+
return items.map((item) => `${encodedKey}=\${encodeURIComponent(${JSON.stringify(item)})}`);
|
|
96
|
+
}).join('&')
|
|
97
|
+
: '';
|
|
98
|
+
const proxyHeaders = {
|
|
99
|
+
'Authorization': '__PROXY_AUTH__',
|
|
100
|
+
'X-SA-Connection': '__SA_CONNECTION__',
|
|
101
|
+
};
|
|
102
|
+
const exampleHeaders = example?.headers || {};
|
|
103
|
+
for (const [k, v] of Object.entries(exampleHeaders)) {
|
|
104
|
+
if (isProxyManagedHeader(k))
|
|
105
|
+
continue;
|
|
106
|
+
proxyHeaders[k] = v;
|
|
107
|
+
}
|
|
108
|
+
if (example?.body !== undefined && example.body !== null && !proxyHeaders['Content-Type']) {
|
|
109
|
+
proxyHeaders['Content-Type'] = 'application/json';
|
|
110
|
+
}
|
|
111
|
+
const headersBlock = Object.entries(proxyHeaders)
|
|
112
|
+
.map(([k, v]) => {
|
|
113
|
+
if (v === '__PROXY_AUTH__')
|
|
114
|
+
return ` 'Authorization': \`Bearer \${process.env.SA_PROXY_TOKEN}\`,`;
|
|
115
|
+
if (v === '__SA_CONNECTION__')
|
|
116
|
+
return ` 'X-SA-Connection': process.env.${platformEnv},`;
|
|
117
|
+
return ` ${JSON.stringify(k)}: ${JSON.stringify(v)},`;
|
|
118
|
+
})
|
|
119
|
+
.join('\n');
|
|
120
|
+
const lines = [];
|
|
121
|
+
lines.push('const res = await fetch(');
|
|
122
|
+
lines.push(` \`\${process.env.SA_PROXY_URL}/${platform}${pathTemplate}${queryString}\`,`);
|
|
123
|
+
lines.push(' {');
|
|
124
|
+
lines.push(` method: '${method}',`);
|
|
125
|
+
lines.push(' headers: {');
|
|
126
|
+
lines.push(headersBlock);
|
|
127
|
+
lines.push(' },');
|
|
128
|
+
if (example?.body !== undefined && example.body !== null) {
|
|
129
|
+
const bodyJson = JSON.stringify(example.body, null, 2)
|
|
130
|
+
.split('\n')
|
|
131
|
+
.map((l, i) => (i === 0 ? l : ' ' + l))
|
|
132
|
+
.join('\n');
|
|
133
|
+
if (typeof example.body === 'object' && example.body !== null && 'connectionKey' in example.body) {
|
|
134
|
+
lines.push(` // connectionKey: copy verbatim — the proxy rewrites it server-side from your X-SA-Connection header.`);
|
|
135
|
+
}
|
|
136
|
+
lines.push(` body: JSON.stringify(${bodyJson}),`);
|
|
137
|
+
}
|
|
138
|
+
lines.push(' }');
|
|
139
|
+
lines.push(');');
|
|
140
|
+
lines.push('const data = await res.json();');
|
|
141
|
+
return lines.join('\n');
|
|
142
|
+
}
|
|
143
|
+
function indent(text, prefix = ' ') {
|
|
144
|
+
return text.split('\n').map(line => prefix + line).join('\n');
|
|
145
|
+
}
|
|
146
|
+
/**
|
|
147
|
+
* Defense-in-depth: even though the SAA API sanitizes `Authorization`,
|
|
148
|
+
* `x-pica-*`, and `x-one-*` out of `ioExample.input.headers` before serving,
|
|
149
|
+
* filter them again here so any future drift can't leak proxy internals into
|
|
150
|
+
* a workflow's outbound request.
|
|
151
|
+
*/
|
|
152
|
+
function isProxyManagedHeader(name) {
|
|
153
|
+
const lower = name.toLowerCase();
|
|
154
|
+
return lower === 'authorization'
|
|
155
|
+
|| lower.startsWith('x-pica-')
|
|
156
|
+
|| lower.startsWith('x-one-');
|
|
157
|
+
}
|
package/dist/index.js
CHANGED
|
@@ -28,6 +28,9 @@ const webhook_list_1 = require("./commands/webhook-list");
|
|
|
28
28
|
const dev_1 = require("./commands/dev");
|
|
29
29
|
const ai_init_1 = require("./commands/ai-init");
|
|
30
30
|
const ai_examples_1 = require("./commands/ai-examples");
|
|
31
|
+
const oauth_actions_search_1 = require("./commands/oauth-actions-search");
|
|
32
|
+
const oauth_actions_list_1 = require("./commands/oauth-actions-list");
|
|
33
|
+
const oauth_actions_show_1 = require("./commands/oauth-actions-show");
|
|
31
34
|
const workspaces_1 = require("./commands/workspaces");
|
|
32
35
|
const config_1 = require("./utils/config");
|
|
33
36
|
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
|
@@ -338,6 +341,34 @@ workspace
|
|
|
338
341
|
await (0, workspaces_1.workspaceSet)(input, opts);
|
|
339
342
|
});
|
|
340
343
|
// =============================================================================
|
|
344
|
+
// oauth-actions <subcommand>
|
|
345
|
+
// =============================================================================
|
|
346
|
+
const oauthActionsCmd = program.command('oauth-actions').description('Discover OAuth-backed API operations callable via the SA proxy');
|
|
347
|
+
oauthActionsCmd
|
|
348
|
+
.command('search <platform> [query]')
|
|
349
|
+
.description('Search available actions for a connected platform')
|
|
350
|
+
.option('--method <method>', 'Filter by HTTP method (GET, POST, etc.)')
|
|
351
|
+
.option('--limit <n>', 'Maximum results to return', (v) => parseInt(v, 10))
|
|
352
|
+
.option('--json', 'Emit raw JSON for AI/script consumption')
|
|
353
|
+
.action((platform, query, options) => {
|
|
354
|
+
(0, oauth_actions_search_1.oauthActionsSearch)(platform, query, options);
|
|
355
|
+
});
|
|
356
|
+
oauthActionsCmd
|
|
357
|
+
.command('list <platform>')
|
|
358
|
+
.description('List actions for a connected platform')
|
|
359
|
+
.option('--limit <n>', 'Maximum results to return', (v) => parseInt(v, 10))
|
|
360
|
+
.option('--json', 'Emit raw JSON for AI/script consumption')
|
|
361
|
+
.action((platform, options) => {
|
|
362
|
+
(0, oauth_actions_list_1.oauthActionsList)(platform, options);
|
|
363
|
+
});
|
|
364
|
+
oauthActionsCmd
|
|
365
|
+
.command('show <platform> <action-id>')
|
|
366
|
+
.description('Show full schema, example body, and a paste-ready fetch snippet for one action')
|
|
367
|
+
.option('--json', 'Emit raw JSON for AI/script consumption')
|
|
368
|
+
.action((platform, actionId, options) => {
|
|
369
|
+
(0, oauth_actions_show_1.oauthActionsShow)(platform, actionId, options);
|
|
370
|
+
});
|
|
371
|
+
// =============================================================================
|
|
341
372
|
// ai <subcommand>
|
|
342
373
|
// =============================================================================
|
|
343
374
|
const ai = program.command('ai').description('AI helper tools');
|
package/dist/utils/skills.js
CHANGED