@superglue/cli 1.1.3 → 1.1.5
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/.claude-plugin/plugin.json +1 -1
- package/README.md +1 -1
- package/dist/cli.js +126 -11
- package/dist/dist/cli.js +126 -11
- package/dist/dist/skills/superglue/SKILL.md +11 -7
- package/dist/skills/superglue/SKILL.md +11 -7
- package/package.json +1 -1
- package/skills/superglue/SKILL.md +11 -7
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "superglue",
|
|
3
3
|
"description": "Build, test, and manage superglue tools via the sg CLI. Use when working with superglue tools, systems, APIs, SDKs, or the sg command.",
|
|
4
|
-
"version": "1.1.
|
|
4
|
+
"version": "1.1.5",
|
|
5
5
|
"author": {
|
|
6
6
|
"name": "superglue",
|
|
7
7
|
"email": "hello@superglue.cloud"
|
package/README.md
CHANGED
|
@@ -87,7 +87,7 @@ sg tool save --draft <id> # save it
|
|
|
87
87
|
|
|
88
88
|
## Documentation
|
|
89
89
|
|
|
90
|
-
- [
|
|
90
|
+
- [CLI + Skills](https://docs.superglue.cloud/getting-started/cli-skills) — Skill installation and agent setup
|
|
91
91
|
- [Full CLI Reference](./skills/superglue/SKILL.md) — Complete command documentation
|
|
92
92
|
- [superglue Docs](https://docs.superglue.cloud) — Platform documentation
|
|
93
93
|
- [API Reference](https://docs.superglue.cloud/api-reference/) — REST API docs
|
package/dist/cli.js
CHANGED
|
@@ -5329,6 +5329,75 @@ IMPORTANT: Sandbox and production use completely separate credentials and base U
|
|
|
5329
5329
|
docsUrl: "https://ai.google.dev/api",
|
|
5330
5330
|
// there is a openapi spec here: https://generativelanguage.googleapis.com/$discovery/OPENAPI3_0?version=v1beta&key=$GOOGLE_API_KEY - but you need your own google api key to access it
|
|
5331
5331
|
preferredAuthType: "apikey"
|
|
5332
|
+
},
|
|
5333
|
+
tableau: {
|
|
5334
|
+
name: "tableau",
|
|
5335
|
+
apiUrl: "https://{your-server}.online.tableau.com",
|
|
5336
|
+
regex: "^.*(tableau\\.com|online\\.tableau).*$",
|
|
5337
|
+
icon: "lucide:plus",
|
|
5338
|
+
docsUrl: "https://help.tableau.com/current/api/rest_api/en-us/REST/rest_api.htm",
|
|
5339
|
+
preferredAuthType: "apikey",
|
|
5340
|
+
keywords: [
|
|
5341
|
+
"workbooks",
|
|
5342
|
+
"views",
|
|
5343
|
+
"datasources",
|
|
5344
|
+
"projects",
|
|
5345
|
+
"users",
|
|
5346
|
+
"groups",
|
|
5347
|
+
"permissions",
|
|
5348
|
+
"sheets",
|
|
5349
|
+
"published data sources",
|
|
5350
|
+
"sites",
|
|
5351
|
+
"jobs",
|
|
5352
|
+
"favorites",
|
|
5353
|
+
"flows",
|
|
5354
|
+
"metrics",
|
|
5355
|
+
"webhooks",
|
|
5356
|
+
"connected apps",
|
|
5357
|
+
"jwt",
|
|
5358
|
+
"vizql",
|
|
5359
|
+
"row-level security"
|
|
5360
|
+
],
|
|
5361
|
+
systemSpecificInstructions: `Tableau supports two Connected App authentication methods:
|
|
5362
|
+
1. **Direct Trust**: Requires client_id, secret_id, and secret_value \u2014 you generate and sign JWTs yourself
|
|
5363
|
+
2. **OAuth 2.0**: Standard OAuth flow with client_id and client_secret
|
|
5364
|
+
|
|
5365
|
+
DIRECT TRUST CREDENTIALS (from Settings > Connected Apps):
|
|
5366
|
+
- tableau_url: Server URL (e.g., https://prod-uk-a.online.tableau.com)
|
|
5367
|
+
- tableau_site_id: The content URL from browser (e.g., "mysite" from /site/mysite/)
|
|
5368
|
+
- tableau_client_id: Connected App ID
|
|
5369
|
+
- tableau_secret_id: The key ID you generated
|
|
5370
|
+
- tableau_client_secret: The secret value (only shown once when created)
|
|
5371
|
+
|
|
5372
|
+
DIRECT TRUST AUTH FLOW:
|
|
5373
|
+
1. Generate a JWT signed with HMAC-SHA256 using the secret value. Include:
|
|
5374
|
+
- Header: { alg: "HS256", typ: "JWT", kid: secret_id, iss: client_id }
|
|
5375
|
+
- Payload: { iss: client_id, sub: "<username>", aud: "tableau", exp: <now+600>, jti: <uuid>, scp: ["tableau:content:read", "tableau:query:run"] }
|
|
5376
|
+
2. Exchange JWT for access token: POST /api/3.24/auth/signin with body: { credentials: { jwt: "<token>", site: { contentUrl: "<site_id>" } } }
|
|
5377
|
+
3. Use the returned token in X-Tableau-Auth header for all subsequent requests
|
|
5378
|
+
|
|
5379
|
+
AVAILABLE APIs:
|
|
5380
|
+
- REST API (/api/3.24/...): Management & metadata \u2014 list datasources, workbooks, users, download view/dashboard data
|
|
5381
|
+
- Metadata API: GraphQL for relationships and lineage
|
|
5382
|
+
- VizQL Data Service (/api/v1/vizql-data-service/query-datasource): Query datasource rows directly
|
|
5383
|
+
|
|
5384
|
+
ROW-LEVEL SECURITY: The JWT's "sub" claim determines which user's permissions apply. Data is automatically filtered based on that user's access.
|
|
5385
|
+
|
|
5386
|
+
SUPERGLUE EXAMPLE - JWT generation in a transform step (Direct Trust):
|
|
5387
|
+
\`\`\`javascript
|
|
5388
|
+
(data) => {
|
|
5389
|
+
const creds = data._credentials || {};
|
|
5390
|
+
const now = Math.floor(Date.now() / 1000);
|
|
5391
|
+
const header = base64url(JSON.stringify({ alg: 'HS256', typ: 'JWT', kid: creds.tableau_secret_id, iss: creds.tableau_client_id }));
|
|
5392
|
+
const payload = base64url(JSON.stringify({ iss: creds.tableau_client_id, sub: data.username, aud: 'tableau', exp: now + 600, jti: crypto.randomUUID(), scp: ['tableau:content:read', 'tableau:query:run'] }));
|
|
5393
|
+
const signature = crypto.createHmac('sha256', creds.tableau_client_secret).update(header + '.' + payload).digest('base64url');
|
|
5394
|
+
return { jwt: header + '.' + payload + '.' + signature };
|
|
5395
|
+
};
|
|
5396
|
+
\`\`\`
|
|
5397
|
+
|
|
5398
|
+
EXAMPLE WORKFLOWS:
|
|
5399
|
+
- Generate JWT \u2192 Sign in \u2192 List datasources \u2192 Query via VizQL Data Service
|
|
5400
|
+
- Generate JWT \u2192 Sign in \u2192 Get workbook \u2192 Download view/dashboard data as CSV/PDF`
|
|
5332
5401
|
}
|
|
5333
5402
|
};
|
|
5334
5403
|
exports2.systemOptions = [
|
|
@@ -19878,6 +19947,7 @@ var require_utils = __commonJS({
|
|
|
19878
19947
|
exports2.validateExternalUrl = validateExternalUrl;
|
|
19879
19948
|
exports2.flattenAndNamespaceCredentials = flattenAndNamespaceCredentials;
|
|
19880
19949
|
exports2.flattenAndNamespaceSystemUrls = flattenAndNamespaceSystemUrls;
|
|
19950
|
+
exports2.slugify = slugify2;
|
|
19881
19951
|
exports2.generateUniqueId = generateUniqueId;
|
|
19882
19952
|
exports2.waitForSystemProcessing = waitForSystemProcessing;
|
|
19883
19953
|
exports2.inferJsonSchema = inferJsonSchema;
|
|
@@ -20086,6 +20156,10 @@ var require_utils = __commonJS({
|
|
|
20086
20156
|
return acc;
|
|
20087
20157
|
}, {});
|
|
20088
20158
|
}
|
|
20159
|
+
function slugify2(value) {
|
|
20160
|
+
const slug = value.normalize("NFKD").replace(/[\u0300-\u036f]/g, "").toLowerCase().replace(/[^a-z0-9]+/g, "_").replace(/^_+|_+$/g, "");
|
|
20161
|
+
return slug || "system";
|
|
20162
|
+
}
|
|
20089
20163
|
async function generateUniqueId({ baseId, exists }) {
|
|
20090
20164
|
if (!await exists(baseId)) {
|
|
20091
20165
|
return baseId;
|
|
@@ -21210,7 +21284,8 @@ var require_superglue_client = __commonJS({
|
|
|
21210
21284
|
* Used for SDK/playground testing
|
|
21211
21285
|
*/
|
|
21212
21286
|
async runToolConfig(params) {
|
|
21213
|
-
const
|
|
21287
|
+
const path6 = params.createRun ? "/v1/tools/run?createRun=true" : "/v1/tools/run";
|
|
21288
|
+
const response = await this.restRequest("POST", path6, {
|
|
21214
21289
|
tool: params.tool,
|
|
21215
21290
|
payload: params.payload,
|
|
21216
21291
|
credentials: params.credentials,
|
|
@@ -21710,7 +21785,7 @@ var import_node_util = require("util");
|
|
|
21710
21785
|
// package.json
|
|
21711
21786
|
var package_default = {
|
|
21712
21787
|
name: "@superglue/cli",
|
|
21713
|
-
version: "1.1.
|
|
21788
|
+
version: "1.1.5",
|
|
21714
21789
|
bin: {
|
|
21715
21790
|
sg: "./dist/cli.js"
|
|
21716
21791
|
},
|
|
@@ -21799,6 +21874,28 @@ async function getLatestNpmVersion() {
|
|
|
21799
21874
|
return null;
|
|
21800
21875
|
}
|
|
21801
21876
|
}
|
|
21877
|
+
var updateCheckPromise = null;
|
|
21878
|
+
function startBackgroundUpdateCheck() {
|
|
21879
|
+
updateCheckPromise = getLatestNpmVersion();
|
|
21880
|
+
}
|
|
21881
|
+
async function printUpdateNotification() {
|
|
21882
|
+
if (!updateCheckPromise) return;
|
|
21883
|
+
try {
|
|
21884
|
+
const timeout = new Promise((resolve4) => {
|
|
21885
|
+
const timer = setTimeout(() => resolve4(null), 2e3);
|
|
21886
|
+
timer.unref();
|
|
21887
|
+
});
|
|
21888
|
+
const latest = await Promise.race([updateCheckPromise, timeout]);
|
|
21889
|
+
if (latest && compareVersions(CLI_VERSION, latest) < 0) {
|
|
21890
|
+
console.error("");
|
|
21891
|
+
console.error(`${c.cyan}${c.bold} Update available: ${CLI_VERSION} \u2192 ${latest}${c.reset}`);
|
|
21892
|
+
console.error(`${c.cyan} Run ${c.bold}sg update${c.reset}${c.cyan} to upgrade${c.reset}`);
|
|
21893
|
+
console.error("");
|
|
21894
|
+
}
|
|
21895
|
+
} catch {
|
|
21896
|
+
}
|
|
21897
|
+
updateCheckPromise = null;
|
|
21898
|
+
}
|
|
21802
21899
|
async function updateCli() {
|
|
21803
21900
|
const latestVersion = await getLatestNpmVersion();
|
|
21804
21901
|
if (!latestVersion) {
|
|
@@ -22514,7 +22611,7 @@ function registerRunCommand(parent, getContext2) {
|
|
|
22514
22611
|
return arr;
|
|
22515
22612
|
},
|
|
22516
22613
|
[]
|
|
22517
|
-
).option("--include-step-results", "Include raw step results").option("--include-config", "Include full tool config").action(async (opts) => {
|
|
22614
|
+
).option("--include-step-results", "Include raw step results").option("--include-config", "Include full tool config").option("--no-create-run", "Skip creating a run record in the backend").action(async (opts) => {
|
|
22518
22615
|
const { config, client } = getContext2();
|
|
22519
22616
|
const selectors = [opts.tool, opts.draft, opts.config, opts.configFile].filter(Boolean);
|
|
22520
22617
|
if (selectors.length === 0) {
|
|
@@ -22525,6 +22622,10 @@ function registerRunCommand(parent, getContext2) {
|
|
|
22525
22622
|
error("Only one of --tool, --draft, --config, or --config-file can be used");
|
|
22526
22623
|
process.exit(1);
|
|
22527
22624
|
}
|
|
22625
|
+
if (opts.tool && opts.createRun === false) {
|
|
22626
|
+
error("--no-create-run is not supported with --tool (saved tools always create runs)");
|
|
22627
|
+
process.exit(1);
|
|
22628
|
+
}
|
|
22528
22629
|
const filePayloads = await parseFileFlags(opts.file, client);
|
|
22529
22630
|
let payload = opts.payloadFile ? JSON.parse(fs6.readFileSync(opts.payloadFile, "utf-8")) : opts.payload ? JSON.parse(opts.payload) : void 0;
|
|
22530
22631
|
if (payload) {
|
|
@@ -22550,7 +22651,8 @@ function registerRunCommand(parent, getContext2) {
|
|
|
22550
22651
|
tool: toolConfig,
|
|
22551
22652
|
payload,
|
|
22552
22653
|
options: { requestSource: import_shared4.RequestSource.CLI },
|
|
22553
|
-
traceId
|
|
22654
|
+
traceId,
|
|
22655
|
+
createRun: opts.createRun !== false
|
|
22554
22656
|
});
|
|
22555
22657
|
logSub.unsubscribe();
|
|
22556
22658
|
spin.stop();
|
|
@@ -22837,7 +22939,7 @@ async function collectSensitiveCredentials(fields) {
|
|
|
22837
22939
|
return creds;
|
|
22838
22940
|
}
|
|
22839
22941
|
function registerCreateCommand(parent, getContext2) {
|
|
22840
|
-
parent.command("create").description("Create a new system").option("--config <file>", "JSON config file").option("--id <id>", "System ID").option("--name <name>", "Human-readable name").option("--url <url>", "API URL").option("--template <id>", "Template ID").option("--instructions <text>", "Specific instructions").option("--credentials <json>", "Non-sensitive credentials JSON").option("--sensitive-credentials <fields>", "Comma-separated sensitive credential field names").option("--docs-url <url>", "Documentation URL to scrape").option("--openapi-url <url>", "OpenAPI spec URL").addOption(
|
|
22942
|
+
parent.command("create").description("Create a new system").option("--config <file>", "JSON config file").option("--id <id>", "System ID (derived from name if omitted)").option("--name <name>", "Human-readable name (required)").option("--url <url>", "API URL").option("--template <id>", "Template ID").option("--instructions <text>", "Specific instructions").option("--credentials <json>", "Non-sensitive credentials JSON").option("--sensitive-credentials <fields>", "Comma-separated sensitive credential field names").option("--docs-url <url>", "Documentation URL to scrape").option("--openapi-url <url>", "OpenAPI spec URL").addOption(
|
|
22841
22943
|
new import_commander.Option("--env <environment>", "Environment: dev or prod (default: prod)").choices([
|
|
22842
22944
|
"dev",
|
|
22843
22945
|
"prod"
|
|
@@ -22903,10 +23005,17 @@ function registerCreateCommand(parent, getContext2) {
|
|
|
22903
23005
|
const sensitive = await collectSensitiveCredentials(fields);
|
|
22904
23006
|
systemInput.credentials = { ...systemInput.credentials, ...sensitive };
|
|
22905
23007
|
}
|
|
22906
|
-
if (
|
|
22907
|
-
error("System
|
|
23008
|
+
if (typeof systemInput.name !== "string" || systemInput.name.trim() === "") {
|
|
23009
|
+
error("System name is required (--name)");
|
|
22908
23010
|
process.exit(1);
|
|
22909
23011
|
}
|
|
23012
|
+
if (!systemInput.id) {
|
|
23013
|
+
systemInput.id = (0, import_shared6.slugify)(systemInput.name.trim());
|
|
23014
|
+
if (!systemInput.id) {
|
|
23015
|
+
error("System ID could not be derived from name; use --id with letters or numbers");
|
|
23016
|
+
process.exit(1);
|
|
23017
|
+
}
|
|
23018
|
+
}
|
|
22910
23019
|
try {
|
|
22911
23020
|
const spin = spinner(`Creating system ${c.bold}${systemInput.id}${c.reset}...`);
|
|
22912
23021
|
const result = await client.createSystem(systemInput);
|
|
@@ -23530,6 +23639,7 @@ All Commands:
|
|
|
23530
23639
|
sg tool build --id <id> --instruction <text> Build a tool from flags (requires --steps)
|
|
23531
23640
|
sg tool run --tool <id> [--payload <json>] Run a saved tool
|
|
23532
23641
|
sg tool run --draft <id> [--payload <json>] Run a draft tool
|
|
23642
|
+
sg tool run --config <json> [--no-create-run] Run inline config (logs run by default)
|
|
23533
23643
|
sg tool edit --tool <id> --patches <json> Edit a tool via JSON Patch
|
|
23534
23644
|
sg tool edit --draft <id> --patches <json> Edit a draft via JSON Patch
|
|
23535
23645
|
sg tool save --draft <id> Save a draft to the server
|
|
@@ -23538,7 +23648,7 @@ All Commands:
|
|
|
23538
23648
|
sg tool find --id <id> Get full config of a tool
|
|
23539
23649
|
|
|
23540
23650
|
sg system create --config <file> Create a system from JSON config
|
|
23541
|
-
sg system create --
|
|
23651
|
+
sg system create --name <name> --url <url> Create a system from flags
|
|
23542
23652
|
sg system edit --id <id> Edit a system's configuration
|
|
23543
23653
|
sg system list List all systems
|
|
23544
23654
|
sg system find [query] Search systems by keyword
|
|
@@ -23602,15 +23712,20 @@ function extractFlagValue(argv, flag) {
|
|
|
23602
23712
|
return void 0;
|
|
23603
23713
|
}
|
|
23604
23714
|
var subcommand = findSubcommand(process.argv);
|
|
23715
|
+
startBackgroundUpdateCheck();
|
|
23716
|
+
var runAndNotify = async (parsePromise) => {
|
|
23717
|
+
await parsePromise;
|
|
23718
|
+
await printUpdateNotification();
|
|
23719
|
+
};
|
|
23605
23720
|
if (subcommand && commandsRequiringServer.includes(subcommand)) {
|
|
23606
23721
|
const cliApiKey = extractFlagValue(process.argv, "--api-key");
|
|
23607
23722
|
const cliEndpoint = extractFlagValue(process.argv, "--endpoint");
|
|
23608
23723
|
const config = resolveConfig({ apiKey: cliApiKey, endpoint: cliEndpoint });
|
|
23609
23724
|
if (config.apiKey) {
|
|
23610
|
-
checkVersionCompatibility(config.endpoint).then(() => program.
|
|
23725
|
+
runAndNotify(checkVersionCompatibility(config.endpoint).then(() => program.parseAsync()));
|
|
23611
23726
|
} else {
|
|
23612
|
-
program.
|
|
23727
|
+
runAndNotify(program.parseAsync());
|
|
23613
23728
|
}
|
|
23614
23729
|
} else {
|
|
23615
|
-
program.
|
|
23730
|
+
runAndNotify(program.parseAsync());
|
|
23616
23731
|
}
|
package/dist/dist/cli.js
CHANGED
|
@@ -5329,6 +5329,75 @@ IMPORTANT: Sandbox and production use completely separate credentials and base U
|
|
|
5329
5329
|
docsUrl: "https://ai.google.dev/api",
|
|
5330
5330
|
// there is a openapi spec here: https://generativelanguage.googleapis.com/$discovery/OPENAPI3_0?version=v1beta&key=$GOOGLE_API_KEY - but you need your own google api key to access it
|
|
5331
5331
|
preferredAuthType: "apikey"
|
|
5332
|
+
},
|
|
5333
|
+
tableau: {
|
|
5334
|
+
name: "tableau",
|
|
5335
|
+
apiUrl: "https://{your-server}.online.tableau.com",
|
|
5336
|
+
regex: "^.*(tableau\\.com|online\\.tableau).*$",
|
|
5337
|
+
icon: "lucide:plus",
|
|
5338
|
+
docsUrl: "https://help.tableau.com/current/api/rest_api/en-us/REST/rest_api.htm",
|
|
5339
|
+
preferredAuthType: "apikey",
|
|
5340
|
+
keywords: [
|
|
5341
|
+
"workbooks",
|
|
5342
|
+
"views",
|
|
5343
|
+
"datasources",
|
|
5344
|
+
"projects",
|
|
5345
|
+
"users",
|
|
5346
|
+
"groups",
|
|
5347
|
+
"permissions",
|
|
5348
|
+
"sheets",
|
|
5349
|
+
"published data sources",
|
|
5350
|
+
"sites",
|
|
5351
|
+
"jobs",
|
|
5352
|
+
"favorites",
|
|
5353
|
+
"flows",
|
|
5354
|
+
"metrics",
|
|
5355
|
+
"webhooks",
|
|
5356
|
+
"connected apps",
|
|
5357
|
+
"jwt",
|
|
5358
|
+
"vizql",
|
|
5359
|
+
"row-level security"
|
|
5360
|
+
],
|
|
5361
|
+
systemSpecificInstructions: `Tableau supports two Connected App authentication methods:
|
|
5362
|
+
1. **Direct Trust**: Requires client_id, secret_id, and secret_value \u2014 you generate and sign JWTs yourself
|
|
5363
|
+
2. **OAuth 2.0**: Standard OAuth flow with client_id and client_secret
|
|
5364
|
+
|
|
5365
|
+
DIRECT TRUST CREDENTIALS (from Settings > Connected Apps):
|
|
5366
|
+
- tableau_url: Server URL (e.g., https://prod-uk-a.online.tableau.com)
|
|
5367
|
+
- tableau_site_id: The content URL from browser (e.g., "mysite" from /site/mysite/)
|
|
5368
|
+
- tableau_client_id: Connected App ID
|
|
5369
|
+
- tableau_secret_id: The key ID you generated
|
|
5370
|
+
- tableau_client_secret: The secret value (only shown once when created)
|
|
5371
|
+
|
|
5372
|
+
DIRECT TRUST AUTH FLOW:
|
|
5373
|
+
1. Generate a JWT signed with HMAC-SHA256 using the secret value. Include:
|
|
5374
|
+
- Header: { alg: "HS256", typ: "JWT", kid: secret_id, iss: client_id }
|
|
5375
|
+
- Payload: { iss: client_id, sub: "<username>", aud: "tableau", exp: <now+600>, jti: <uuid>, scp: ["tableau:content:read", "tableau:query:run"] }
|
|
5376
|
+
2. Exchange JWT for access token: POST /api/3.24/auth/signin with body: { credentials: { jwt: "<token>", site: { contentUrl: "<site_id>" } } }
|
|
5377
|
+
3. Use the returned token in X-Tableau-Auth header for all subsequent requests
|
|
5378
|
+
|
|
5379
|
+
AVAILABLE APIs:
|
|
5380
|
+
- REST API (/api/3.24/...): Management & metadata \u2014 list datasources, workbooks, users, download view/dashboard data
|
|
5381
|
+
- Metadata API: GraphQL for relationships and lineage
|
|
5382
|
+
- VizQL Data Service (/api/v1/vizql-data-service/query-datasource): Query datasource rows directly
|
|
5383
|
+
|
|
5384
|
+
ROW-LEVEL SECURITY: The JWT's "sub" claim determines which user's permissions apply. Data is automatically filtered based on that user's access.
|
|
5385
|
+
|
|
5386
|
+
SUPERGLUE EXAMPLE - JWT generation in a transform step (Direct Trust):
|
|
5387
|
+
\`\`\`javascript
|
|
5388
|
+
(data) => {
|
|
5389
|
+
const creds = data._credentials || {};
|
|
5390
|
+
const now = Math.floor(Date.now() / 1000);
|
|
5391
|
+
const header = base64url(JSON.stringify({ alg: 'HS256', typ: 'JWT', kid: creds.tableau_secret_id, iss: creds.tableau_client_id }));
|
|
5392
|
+
const payload = base64url(JSON.stringify({ iss: creds.tableau_client_id, sub: data.username, aud: 'tableau', exp: now + 600, jti: crypto.randomUUID(), scp: ['tableau:content:read', 'tableau:query:run'] }));
|
|
5393
|
+
const signature = crypto.createHmac('sha256', creds.tableau_client_secret).update(header + '.' + payload).digest('base64url');
|
|
5394
|
+
return { jwt: header + '.' + payload + '.' + signature };
|
|
5395
|
+
};
|
|
5396
|
+
\`\`\`
|
|
5397
|
+
|
|
5398
|
+
EXAMPLE WORKFLOWS:
|
|
5399
|
+
- Generate JWT \u2192 Sign in \u2192 List datasources \u2192 Query via VizQL Data Service
|
|
5400
|
+
- Generate JWT \u2192 Sign in \u2192 Get workbook \u2192 Download view/dashboard data as CSV/PDF`
|
|
5332
5401
|
}
|
|
5333
5402
|
};
|
|
5334
5403
|
exports2.systemOptions = [
|
|
@@ -19878,6 +19947,7 @@ var require_utils = __commonJS({
|
|
|
19878
19947
|
exports2.validateExternalUrl = validateExternalUrl;
|
|
19879
19948
|
exports2.flattenAndNamespaceCredentials = flattenAndNamespaceCredentials;
|
|
19880
19949
|
exports2.flattenAndNamespaceSystemUrls = flattenAndNamespaceSystemUrls;
|
|
19950
|
+
exports2.slugify = slugify2;
|
|
19881
19951
|
exports2.generateUniqueId = generateUniqueId;
|
|
19882
19952
|
exports2.waitForSystemProcessing = waitForSystemProcessing;
|
|
19883
19953
|
exports2.inferJsonSchema = inferJsonSchema;
|
|
@@ -20086,6 +20156,10 @@ var require_utils = __commonJS({
|
|
|
20086
20156
|
return acc;
|
|
20087
20157
|
}, {});
|
|
20088
20158
|
}
|
|
20159
|
+
function slugify2(value) {
|
|
20160
|
+
const slug = value.normalize("NFKD").replace(/[\u0300-\u036f]/g, "").toLowerCase().replace(/[^a-z0-9]+/g, "_").replace(/^_+|_+$/g, "");
|
|
20161
|
+
return slug || "system";
|
|
20162
|
+
}
|
|
20089
20163
|
async function generateUniqueId({ baseId, exists }) {
|
|
20090
20164
|
if (!await exists(baseId)) {
|
|
20091
20165
|
return baseId;
|
|
@@ -21210,7 +21284,8 @@ var require_superglue_client = __commonJS({
|
|
|
21210
21284
|
* Used for SDK/playground testing
|
|
21211
21285
|
*/
|
|
21212
21286
|
async runToolConfig(params) {
|
|
21213
|
-
const
|
|
21287
|
+
const path6 = params.createRun ? "/v1/tools/run?createRun=true" : "/v1/tools/run";
|
|
21288
|
+
const response = await this.restRequest("POST", path6, {
|
|
21214
21289
|
tool: params.tool,
|
|
21215
21290
|
payload: params.payload,
|
|
21216
21291
|
credentials: params.credentials,
|
|
@@ -21710,7 +21785,7 @@ var import_node_util = require("util");
|
|
|
21710
21785
|
// package.json
|
|
21711
21786
|
var package_default = {
|
|
21712
21787
|
name: "@superglue/cli",
|
|
21713
|
-
version: "1.1.
|
|
21788
|
+
version: "1.1.5",
|
|
21714
21789
|
bin: {
|
|
21715
21790
|
sg: "./dist/cli.js"
|
|
21716
21791
|
},
|
|
@@ -21799,6 +21874,28 @@ async function getLatestNpmVersion() {
|
|
|
21799
21874
|
return null;
|
|
21800
21875
|
}
|
|
21801
21876
|
}
|
|
21877
|
+
var updateCheckPromise = null;
|
|
21878
|
+
function startBackgroundUpdateCheck() {
|
|
21879
|
+
updateCheckPromise = getLatestNpmVersion();
|
|
21880
|
+
}
|
|
21881
|
+
async function printUpdateNotification() {
|
|
21882
|
+
if (!updateCheckPromise) return;
|
|
21883
|
+
try {
|
|
21884
|
+
const timeout = new Promise((resolve4) => {
|
|
21885
|
+
const timer = setTimeout(() => resolve4(null), 2e3);
|
|
21886
|
+
timer.unref();
|
|
21887
|
+
});
|
|
21888
|
+
const latest = await Promise.race([updateCheckPromise, timeout]);
|
|
21889
|
+
if (latest && compareVersions(CLI_VERSION, latest) < 0) {
|
|
21890
|
+
console.error("");
|
|
21891
|
+
console.error(`${c.cyan}${c.bold} Update available: ${CLI_VERSION} \u2192 ${latest}${c.reset}`);
|
|
21892
|
+
console.error(`${c.cyan} Run ${c.bold}sg update${c.reset}${c.cyan} to upgrade${c.reset}`);
|
|
21893
|
+
console.error("");
|
|
21894
|
+
}
|
|
21895
|
+
} catch {
|
|
21896
|
+
}
|
|
21897
|
+
updateCheckPromise = null;
|
|
21898
|
+
}
|
|
21802
21899
|
async function updateCli() {
|
|
21803
21900
|
const latestVersion = await getLatestNpmVersion();
|
|
21804
21901
|
if (!latestVersion) {
|
|
@@ -22514,7 +22611,7 @@ function registerRunCommand(parent, getContext2) {
|
|
|
22514
22611
|
return arr;
|
|
22515
22612
|
},
|
|
22516
22613
|
[]
|
|
22517
|
-
).option("--include-step-results", "Include raw step results").option("--include-config", "Include full tool config").action(async (opts) => {
|
|
22614
|
+
).option("--include-step-results", "Include raw step results").option("--include-config", "Include full tool config").option("--no-create-run", "Skip creating a run record in the backend").action(async (opts) => {
|
|
22518
22615
|
const { config, client } = getContext2();
|
|
22519
22616
|
const selectors = [opts.tool, opts.draft, opts.config, opts.configFile].filter(Boolean);
|
|
22520
22617
|
if (selectors.length === 0) {
|
|
@@ -22525,6 +22622,10 @@ function registerRunCommand(parent, getContext2) {
|
|
|
22525
22622
|
error("Only one of --tool, --draft, --config, or --config-file can be used");
|
|
22526
22623
|
process.exit(1);
|
|
22527
22624
|
}
|
|
22625
|
+
if (opts.tool && opts.createRun === false) {
|
|
22626
|
+
error("--no-create-run is not supported with --tool (saved tools always create runs)");
|
|
22627
|
+
process.exit(1);
|
|
22628
|
+
}
|
|
22528
22629
|
const filePayloads = await parseFileFlags(opts.file, client);
|
|
22529
22630
|
let payload = opts.payloadFile ? JSON.parse(fs6.readFileSync(opts.payloadFile, "utf-8")) : opts.payload ? JSON.parse(opts.payload) : void 0;
|
|
22530
22631
|
if (payload) {
|
|
@@ -22550,7 +22651,8 @@ function registerRunCommand(parent, getContext2) {
|
|
|
22550
22651
|
tool: toolConfig,
|
|
22551
22652
|
payload,
|
|
22552
22653
|
options: { requestSource: import_shared4.RequestSource.CLI },
|
|
22553
|
-
traceId
|
|
22654
|
+
traceId,
|
|
22655
|
+
createRun: opts.createRun !== false
|
|
22554
22656
|
});
|
|
22555
22657
|
logSub.unsubscribe();
|
|
22556
22658
|
spin.stop();
|
|
@@ -22837,7 +22939,7 @@ async function collectSensitiveCredentials(fields) {
|
|
|
22837
22939
|
return creds;
|
|
22838
22940
|
}
|
|
22839
22941
|
function registerCreateCommand(parent, getContext2) {
|
|
22840
|
-
parent.command("create").description("Create a new system").option("--config <file>", "JSON config file").option("--id <id>", "System ID").option("--name <name>", "Human-readable name").option("--url <url>", "API URL").option("--template <id>", "Template ID").option("--instructions <text>", "Specific instructions").option("--credentials <json>", "Non-sensitive credentials JSON").option("--sensitive-credentials <fields>", "Comma-separated sensitive credential field names").option("--docs-url <url>", "Documentation URL to scrape").option("--openapi-url <url>", "OpenAPI spec URL").addOption(
|
|
22942
|
+
parent.command("create").description("Create a new system").option("--config <file>", "JSON config file").option("--id <id>", "System ID (derived from name if omitted)").option("--name <name>", "Human-readable name (required)").option("--url <url>", "API URL").option("--template <id>", "Template ID").option("--instructions <text>", "Specific instructions").option("--credentials <json>", "Non-sensitive credentials JSON").option("--sensitive-credentials <fields>", "Comma-separated sensitive credential field names").option("--docs-url <url>", "Documentation URL to scrape").option("--openapi-url <url>", "OpenAPI spec URL").addOption(
|
|
22841
22943
|
new import_commander.Option("--env <environment>", "Environment: dev or prod (default: prod)").choices([
|
|
22842
22944
|
"dev",
|
|
22843
22945
|
"prod"
|
|
@@ -22903,10 +23005,17 @@ function registerCreateCommand(parent, getContext2) {
|
|
|
22903
23005
|
const sensitive = await collectSensitiveCredentials(fields);
|
|
22904
23006
|
systemInput.credentials = { ...systemInput.credentials, ...sensitive };
|
|
22905
23007
|
}
|
|
22906
|
-
if (
|
|
22907
|
-
error("System
|
|
23008
|
+
if (typeof systemInput.name !== "string" || systemInput.name.trim() === "") {
|
|
23009
|
+
error("System name is required (--name)");
|
|
22908
23010
|
process.exit(1);
|
|
22909
23011
|
}
|
|
23012
|
+
if (!systemInput.id) {
|
|
23013
|
+
systemInput.id = (0, import_shared6.slugify)(systemInput.name.trim());
|
|
23014
|
+
if (!systemInput.id) {
|
|
23015
|
+
error("System ID could not be derived from name; use --id with letters or numbers");
|
|
23016
|
+
process.exit(1);
|
|
23017
|
+
}
|
|
23018
|
+
}
|
|
22910
23019
|
try {
|
|
22911
23020
|
const spin = spinner(`Creating system ${c.bold}${systemInput.id}${c.reset}...`);
|
|
22912
23021
|
const result = await client.createSystem(systemInput);
|
|
@@ -23530,6 +23639,7 @@ All Commands:
|
|
|
23530
23639
|
sg tool build --id <id> --instruction <text> Build a tool from flags (requires --steps)
|
|
23531
23640
|
sg tool run --tool <id> [--payload <json>] Run a saved tool
|
|
23532
23641
|
sg tool run --draft <id> [--payload <json>] Run a draft tool
|
|
23642
|
+
sg tool run --config <json> [--no-create-run] Run inline config (logs run by default)
|
|
23533
23643
|
sg tool edit --tool <id> --patches <json> Edit a tool via JSON Patch
|
|
23534
23644
|
sg tool edit --draft <id> --patches <json> Edit a draft via JSON Patch
|
|
23535
23645
|
sg tool save --draft <id> Save a draft to the server
|
|
@@ -23538,7 +23648,7 @@ All Commands:
|
|
|
23538
23648
|
sg tool find --id <id> Get full config of a tool
|
|
23539
23649
|
|
|
23540
23650
|
sg system create --config <file> Create a system from JSON config
|
|
23541
|
-
sg system create --
|
|
23651
|
+
sg system create --name <name> --url <url> Create a system from flags
|
|
23542
23652
|
sg system edit --id <id> Edit a system's configuration
|
|
23543
23653
|
sg system list List all systems
|
|
23544
23654
|
sg system find [query] Search systems by keyword
|
|
@@ -23602,15 +23712,20 @@ function extractFlagValue(argv, flag) {
|
|
|
23602
23712
|
return void 0;
|
|
23603
23713
|
}
|
|
23604
23714
|
var subcommand = findSubcommand(process.argv);
|
|
23715
|
+
startBackgroundUpdateCheck();
|
|
23716
|
+
var runAndNotify = async (parsePromise) => {
|
|
23717
|
+
await parsePromise;
|
|
23718
|
+
await printUpdateNotification();
|
|
23719
|
+
};
|
|
23605
23720
|
if (subcommand && commandsRequiringServer.includes(subcommand)) {
|
|
23606
23721
|
const cliApiKey = extractFlagValue(process.argv, "--api-key");
|
|
23607
23722
|
const cliEndpoint = extractFlagValue(process.argv, "--endpoint");
|
|
23608
23723
|
const config = resolveConfig({ apiKey: cliApiKey, endpoint: cliEndpoint });
|
|
23609
23724
|
if (config.apiKey) {
|
|
23610
|
-
checkVersionCompatibility(config.endpoint).then(() => program.
|
|
23725
|
+
runAndNotify(checkVersionCompatibility(config.endpoint).then(() => program.parseAsync()));
|
|
23611
23726
|
} else {
|
|
23612
|
-
program.
|
|
23727
|
+
runAndNotify(program.parseAsync());
|
|
23613
23728
|
}
|
|
23614
23729
|
} else {
|
|
23615
|
-
program.
|
|
23730
|
+
runAndNotify(program.parseAsync());
|
|
23616
23731
|
}
|
|
@@ -12,6 +12,11 @@ description: "Build, test, deploy and integrate superglue tools via the sg CLI a
|
|
|
12
12
|
1. **Check CLI exists**: Run `sg --version`. If it fails with `command not found`, install it: `npm install -g @superglue/cli`
|
|
13
13
|
2. **Check superglue CLI config exists**: Run `sg system list`. If it fails with `fetch failed` or auth errors, the CLI needs configuration via `sg init` (see below).
|
|
14
14
|
|
|
15
|
+
**Calling systems and running tools**
|
|
16
|
+
|
|
17
|
+
1. ALWAYS make sure you pull the relevant system context using `sg system find` before calling system endpoints and running inline JSON tools or building new tools.
|
|
18
|
+
2. Use `sg system search-docs` to do a targeted keyword search of system docs if you're unsure about how a system works.
|
|
19
|
+
|
|
15
20
|
**Authentication & Credentials:**
|
|
16
21
|
|
|
17
22
|
- **Auth headers MUST be set explicitly** — credentials are NEVER auto-injected into requests or tool steps
|
|
@@ -173,34 +178,33 @@ sg tool find --id my-tool # exact ID lookup (full config)
|
|
|
173
178
|
|
|
174
179
|
**Required flags:**
|
|
175
180
|
|
|
176
|
-
- `--id <id>` — System ID (lowercase, underscores only, no hyphens)
|
|
177
181
|
- `--name <name>` — Human-readable display name
|
|
178
182
|
- `--url <url>` — API base URL (auto-filled when using `--template`)
|
|
179
183
|
|
|
180
|
-
**
|
|
184
|
+
**Optional:**
|
|
181
185
|
|
|
186
|
+
- `--id <id>` — System ID (lowercase, underscores only, no hyphens). Derived from `--name` if omitted.
|
|
182
187
|
- `--template <id>` — Pre-configured template (auto-fills name and url if not provided)
|
|
183
|
-
- `--url <url>` + `--name <name>` manually
|
|
184
188
|
|
|
185
189
|
**Example with credentials (non-interactive — use this in AI agent / CI contexts):**
|
|
186
190
|
|
|
187
191
|
```bash
|
|
188
|
-
sg system create --
|
|
192
|
+
sg system create --name "My API" --url https://api.example.com \
|
|
189
193
|
--credentials '{"api_key":"sk-xxx"}'
|
|
190
194
|
```
|
|
191
195
|
|
|
192
196
|
**With template:**
|
|
193
197
|
|
|
194
198
|
```bash
|
|
195
|
-
sg system create --
|
|
199
|
+
sg system create --name "Slack" --template slack
|
|
196
200
|
```
|
|
197
201
|
|
|
198
202
|
**Full flag reference:**
|
|
199
203
|
|
|
200
204
|
| Flag | Description |
|
|
201
205
|
| ---------------------------------- | ---------------------------------------------------------------------------- |
|
|
202
|
-
| `--id <id>` | System ID (lowercase, underscores only) — **REQUIRED** |
|
|
203
206
|
| `--name <name>` | Human-readable name — **REQUIRED** |
|
|
207
|
+
| `--id <id>` | System ID (lowercase, underscores only) — derived from name if omitted |
|
|
204
208
|
| `--url <url>` | API base URL — **REQUIRED** (auto-filled by `--template`) |
|
|
205
209
|
| `--template <id>` | Template ID (stripe, shopify, slack, etc.) |
|
|
206
210
|
| `--credentials <json>` | Credentials JSON (use for ALL creds in non-interactive mode) |
|
|
@@ -530,7 +534,7 @@ Templates auto-populate endpoints and OAuth config. Use `sg system find` to disc
|
|
|
530
534
|
|
|
531
535
|
### OAuth Flow
|
|
532
536
|
|
|
533
|
-
1. `sg system create --
|
|
537
|
+
1. `sg system create --name "Gmail" --url https://gmail.googleapis.com --credentials '{"auth_url":"...","token_url":"..."}' --sensitive-credentials client_id,client_secret`
|
|
534
538
|
2. `sg system oauth --system-id gmail --scopes "..."`
|
|
535
539
|
3. User authenticates in browser → tokens saved automatically
|
|
536
540
|
|
|
@@ -12,6 +12,11 @@ description: "Build, test, deploy and integrate superglue tools via the sg CLI a
|
|
|
12
12
|
1. **Check CLI exists**: Run `sg --version`. If it fails with `command not found`, install it: `npm install -g @superglue/cli`
|
|
13
13
|
2. **Check superglue CLI config exists**: Run `sg system list`. If it fails with `fetch failed` or auth errors, the CLI needs configuration via `sg init` (see below).
|
|
14
14
|
|
|
15
|
+
**Calling systems and running tools**
|
|
16
|
+
|
|
17
|
+
1. ALWAYS make sure you pull the relevant system context using `sg system find` before calling system endpoints and running inline JSON tools or building new tools.
|
|
18
|
+
2. Use `sg system search-docs` to do a targeted keyword search of system docs if you're unsure about how a system works.
|
|
19
|
+
|
|
15
20
|
**Authentication & Credentials:**
|
|
16
21
|
|
|
17
22
|
- **Auth headers MUST be set explicitly** — credentials are NEVER auto-injected into requests or tool steps
|
|
@@ -173,34 +178,33 @@ sg tool find --id my-tool # exact ID lookup (full config)
|
|
|
173
178
|
|
|
174
179
|
**Required flags:**
|
|
175
180
|
|
|
176
|
-
- `--id <id>` — System ID (lowercase, underscores only, no hyphens)
|
|
177
181
|
- `--name <name>` — Human-readable display name
|
|
178
182
|
- `--url <url>` — API base URL (auto-filled when using `--template`)
|
|
179
183
|
|
|
180
|
-
**
|
|
184
|
+
**Optional:**
|
|
181
185
|
|
|
186
|
+
- `--id <id>` — System ID (lowercase, underscores only, no hyphens). Derived from `--name` if omitted.
|
|
182
187
|
- `--template <id>` — Pre-configured template (auto-fills name and url if not provided)
|
|
183
|
-
- `--url <url>` + `--name <name>` manually
|
|
184
188
|
|
|
185
189
|
**Example with credentials (non-interactive — use this in AI agent / CI contexts):**
|
|
186
190
|
|
|
187
191
|
```bash
|
|
188
|
-
sg system create --
|
|
192
|
+
sg system create --name "My API" --url https://api.example.com \
|
|
189
193
|
--credentials '{"api_key":"sk-xxx"}'
|
|
190
194
|
```
|
|
191
195
|
|
|
192
196
|
**With template:**
|
|
193
197
|
|
|
194
198
|
```bash
|
|
195
|
-
sg system create --
|
|
199
|
+
sg system create --name "Slack" --template slack
|
|
196
200
|
```
|
|
197
201
|
|
|
198
202
|
**Full flag reference:**
|
|
199
203
|
|
|
200
204
|
| Flag | Description |
|
|
201
205
|
| ---------------------------------- | ---------------------------------------------------------------------------- |
|
|
202
|
-
| `--id <id>` | System ID (lowercase, underscores only) — **REQUIRED** |
|
|
203
206
|
| `--name <name>` | Human-readable name — **REQUIRED** |
|
|
207
|
+
| `--id <id>` | System ID (lowercase, underscores only) — derived from name if omitted |
|
|
204
208
|
| `--url <url>` | API base URL — **REQUIRED** (auto-filled by `--template`) |
|
|
205
209
|
| `--template <id>` | Template ID (stripe, shopify, slack, etc.) |
|
|
206
210
|
| `--credentials <json>` | Credentials JSON (use for ALL creds in non-interactive mode) |
|
|
@@ -530,7 +534,7 @@ Templates auto-populate endpoints and OAuth config. Use `sg system find` to disc
|
|
|
530
534
|
|
|
531
535
|
### OAuth Flow
|
|
532
536
|
|
|
533
|
-
1. `sg system create --
|
|
537
|
+
1. `sg system create --name "Gmail" --url https://gmail.googleapis.com --credentials '{"auth_url":"...","token_url":"..."}' --sensitive-credentials client_id,client_secret`
|
|
534
538
|
2. `sg system oauth --system-id gmail --scopes "..."`
|
|
535
539
|
3. User authenticates in browser → tokens saved automatically
|
|
536
540
|
|
package/package.json
CHANGED
|
@@ -12,6 +12,11 @@ description: "Build, test, deploy and integrate superglue tools via the sg CLI a
|
|
|
12
12
|
1. **Check CLI exists**: Run `sg --version`. If it fails with `command not found`, install it: `npm install -g @superglue/cli`
|
|
13
13
|
2. **Check superglue CLI config exists**: Run `sg system list`. If it fails with `fetch failed` or auth errors, the CLI needs configuration via `sg init` (see below).
|
|
14
14
|
|
|
15
|
+
**Calling systems and running tools**
|
|
16
|
+
|
|
17
|
+
1. ALWAYS make sure you pull the relevant system context using `sg system find` before calling system endpoints and running inline JSON tools or building new tools.
|
|
18
|
+
2. Use `sg system search-docs` to do a targeted keyword search of system docs if you're unsure about how a system works.
|
|
19
|
+
|
|
15
20
|
**Authentication & Credentials:**
|
|
16
21
|
|
|
17
22
|
- **Auth headers MUST be set explicitly** — credentials are NEVER auto-injected into requests or tool steps
|
|
@@ -173,34 +178,33 @@ sg tool find --id my-tool # exact ID lookup (full config)
|
|
|
173
178
|
|
|
174
179
|
**Required flags:**
|
|
175
180
|
|
|
176
|
-
- `--id <id>` — System ID (lowercase, underscores only, no hyphens)
|
|
177
181
|
- `--name <name>` — Human-readable display name
|
|
178
182
|
- `--url <url>` — API base URL (auto-filled when using `--template`)
|
|
179
183
|
|
|
180
|
-
**
|
|
184
|
+
**Optional:**
|
|
181
185
|
|
|
186
|
+
- `--id <id>` — System ID (lowercase, underscores only, no hyphens). Derived from `--name` if omitted.
|
|
182
187
|
- `--template <id>` — Pre-configured template (auto-fills name and url if not provided)
|
|
183
|
-
- `--url <url>` + `--name <name>` manually
|
|
184
188
|
|
|
185
189
|
**Example with credentials (non-interactive — use this in AI agent / CI contexts):**
|
|
186
190
|
|
|
187
191
|
```bash
|
|
188
|
-
sg system create --
|
|
192
|
+
sg system create --name "My API" --url https://api.example.com \
|
|
189
193
|
--credentials '{"api_key":"sk-xxx"}'
|
|
190
194
|
```
|
|
191
195
|
|
|
192
196
|
**With template:**
|
|
193
197
|
|
|
194
198
|
```bash
|
|
195
|
-
sg system create --
|
|
199
|
+
sg system create --name "Slack" --template slack
|
|
196
200
|
```
|
|
197
201
|
|
|
198
202
|
**Full flag reference:**
|
|
199
203
|
|
|
200
204
|
| Flag | Description |
|
|
201
205
|
| ---------------------------------- | ---------------------------------------------------------------------------- |
|
|
202
|
-
| `--id <id>` | System ID (lowercase, underscores only) — **REQUIRED** |
|
|
203
206
|
| `--name <name>` | Human-readable name — **REQUIRED** |
|
|
207
|
+
| `--id <id>` | System ID (lowercase, underscores only) — derived from name if omitted |
|
|
204
208
|
| `--url <url>` | API base URL — **REQUIRED** (auto-filled by `--template`) |
|
|
205
209
|
| `--template <id>` | Template ID (stripe, shopify, slack, etc.) |
|
|
206
210
|
| `--credentials <json>` | Credentials JSON (use for ALL creds in non-interactive mode) |
|
|
@@ -530,7 +534,7 @@ Templates auto-populate endpoints and OAuth config. Use `sg system find` to disc
|
|
|
530
534
|
|
|
531
535
|
### OAuth Flow
|
|
532
536
|
|
|
533
|
-
1. `sg system create --
|
|
537
|
+
1. `sg system create --name "Gmail" --url https://gmail.googleapis.com --credentials '{"auth_url":"...","token_url":"..."}' --sensitive-credentials client_id,client_secret`
|
|
534
538
|
2. `sg system oauth --system-id gmail --scopes "..."`
|
|
535
539
|
3. User authenticates in browser → tokens saved automatically
|
|
536
540
|
|