@stackone/cli 1.9.1 → 1.9.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/cli.cjs CHANGED
@@ -1,2 +1,2 @@
1
1
  #!/usr/bin/env node
2
- const e=require(`./cliCore-BRPqDxQ9.cjs`),t=new e.CLI;t.run();
2
+ const e=require(`./cliCore-xqwgmaz9.cjs`),t=new e.CLI;t.run();
package/dist/cli.js CHANGED
@@ -1,2 +1,2 @@
1
1
  #!/usr/bin/env node
2
- import{CLI as e}from"./cliCore-57MzGvkG.js";const t=new e;t.run();
2
+ import{CLI as e}from"./cliCore-BFE-6X6w.js";const t=new e;t.run();
@@ -0,0 +1,107 @@
1
+ import{createRequire as e}from"node:module";import t from"chalk";import{Argument as n,Command as r}from"commander";import{exec as i}from"node:child_process";import{access as a,readFile as o,writeFile as s}from"node:fs/promises";import{dirname as c,join as l}from"node:path";import{promisify as u}from"node:util";import d from"ora";import{existsSync as f,lstatSync as p,readFileSync as m,readdirSync as h,rmSync as g,statSync as _,writeFileSync as v}from"fs";import{homedir as ee}from"os";import{basename as y,dirname as te,join as b}from"path";import{stdin as x,stdout as S}from"node:process";import{createInterface as C}from"node:readline";import w from"inquirer";import{fileURLToPath as ne}from"node:url";import{loadConnector as T,validateYamlConnector as E}from"@stackone/connect-sdk";import{fileURLToPath as re}from"url";var D=e(import.meta.url);const O=`config.json`,k=`agent-config.json`;var A=class{static info(e){console.info(t.blue(`ℹ`),e)}static warn(e){console.info(t.yellow(`⚠`),e)}static error(e){console.info(t.red(`✗`),e)}static success(e){console.info(t.green(`✓`),e)}},j=class{static configDir=b(ee(),`.stackone`);static ensureConfigDir(){if(f(this.configDir)){let e=p(this.configDir);if(!e.isDirectory()){let e=null;try{A.info(`.stackone exists as a file, checking for migration to config directory...`),e=m(this.configDir,`utf8`)}catch{}if(g(this.configDir,{force:!0}),D(`fs`).mkdirSync(this.configDir,{recursive:!0}),e)try{JSON.parse(e),v(b(this.configDir,O),e,`utf8`),A.success(`Migrated existing .stackone file content to config.json`)}catch{A.warn(`Note: Existing .stackone file was not valid JSON, skipping migration`)}else A.warn(`Note: Existing .stackone file was not valid JSON, skipping migration`)}}else D(`fs`).mkdirSync(this.configDir,{recursive:!0})}static getConfigDir(){return this.configDir}static writeConfigFile(e,t,n){this.ensureConfigDir();let r=b(this.configDir,e);v(r,t,`utf8`),n?.mode!==void 0&&D(`fs`).chmodSync(r,n.mode)}static readConfigFile(e){this.ensureConfigDir();let t=b(this.configDir,e);if(!f(t))return null;try{return m(t,`utf8`)}catch{return null}}static deleteConfigFile(e){let t=b(this.configDir,e);f(t)&&g(t,{force:!0})}};const M=`stackone-agent-global`,ie=`stackone-agent-local`,ae=process.env.STACKONE_AGENT_SERVER_URL||`https://mcp-internal-falcon.stackonehq.workers.dev/mcp`;async function N(e){j.writeConfigFile(k,JSON.stringify(e,null,2),{mode:384})}async function P(){let e=j.readConfigFile(k);if(!e)return null;try{return JSON.parse(e)}catch{return null}}async function oe(){j.deleteConfigFile(k)}function F(){return ae}function I(){return M}function L(){return ie}function se(e){return new Date(e)<=new Date}function ce(e){return!!e?.apiKey&&!se(e.expiresAt)}const R=u(i);async function z(e){try{let t=await o(e,`utf-8`),n=JSON.parse(t),r=!1;if(n.mcpServers&&typeof n.mcpServers==`object`){for(let[e,t]of Object.entries(n.mcpServers))if(t&&typeof t==`object`&&`transport`in t){let e=t.transport;if(e&&typeof e==`object`&&`headers`in e){let t=e.headers;if(t&&typeof t==`object`&&`Authorization`in t){let e=t.Authorization;typeof e==`string`&&e.startsWith(`Bearer `)&&!e.includes("${")&&(t.Authorization="Bearer ${STACKONE_AGENT_MCP_TOKEN}",r=!0)}}}}return r&&await s(e,JSON.stringify(n,null,2)),r}catch{return!1}}var B=class{async execute(){console.log(t.blue.bold(`🧹 StackOne Agent Cleanup
2
+ `));let e={configsCleaned:0};try{let n=d(`Removing stored configuration...`).start();await oe(),n.succeed(`Removed ~/.stackone/agent-config.json`);let r=d(`Removing MCP servers from Claude...`).start(),i=0;try{let e=I();await R(`claude mcp remove ${e} 2>/dev/null`),i++}catch{}try{let e=L();await R(`claude mcp remove ${e} 2>/dev/null`),i++}catch{}i>0?r.succeed(`Removed ${i} MCP server(s) from Claude`):r.warn(`No MCP servers found in Claude (already removed or not configured)`);let a=d(`Checking for .mcp.json file...`).start(),o=process.cwd(),s=l(o,`.mcp.json`);try{let t=await z(s);t?(e.configsCleaned=1,a.succeed(`Cleaned .mcp.json (replaced tokens with environment variables)`)):a.info(`.mcp.json already uses environment variables or not found`)}catch{a.info(`.mcp.json not found (already cleaned or not created)`)}A.success(`Cleanup complete!`),console.log(t.white(`
3
+ What was removed:`)),console.log(t.cyan(` • ~/.stackone/agent-config.json`)),console.log(t.cyan(` • MCP server from Claude configuration`)),e.configsCleaned>0&&console.log(t.cyan(` • Hardcoded credentials from .mcp.json`)),console.log(t.dim(`
4
+ To authenticate again:`)),console.log(t.cyan(` stackone agent setup --global`),t.dim(`(for global setup)`)),console.log(t.cyan(` stackone agent setup --local`),t.dim(`(for local project setup)`)),console.log(``),process.exit(0)}catch(e){e instanceof Error?A.error(e.message):A.error(`Unknown error occurred`),process.exit(1)}}},V=class{baseUrl;constructor(e){this.baseUrl=e||process.env.AUTH_SERVER_URL||`https://idp-api.stackone.com`}async login(e,t){try{let n=`${this.baseUrl}/auth/sign-in/mcp`,r=await fetch(n,{method:`POST`,headers:{"Content-Type":`application/json`},body:JSON.stringify({email:e,password:t})});if(!r.ok){let e=await r.text(),t={};try{t=JSON.parse(e)}catch{}return{success:!1,error:t.error||`HTTP ${r.status}: ${r.statusText}`}}let i=await r.text(),a=JSON.parse(i);return a.apiKey?{success:!0,apiKey:a.apiKey}:{success:!1,error:`Invalid response format`}}catch(e){return{success:!1,error:e instanceof Error?e.message:`Network error occurred`}}}async validateKey(e){try{let t=`${this.baseUrl}/api-key/verify`,n=await fetch(t,{method:`POST`,headers:{"Content-Type":`application/json`},body:JSON.stringify({key:e})});if(!n.ok){let e=await n.text(),t={};try{t=JSON.parse(e)}catch{}return{valid:!1,error:t.error||`HTTP ${n.status}: ${n.statusText}`}}let r=await n.text();return JSON.parse(r)}catch(e){return{valid:!1,error:e instanceof Error?e.message:`Network error occurred`}}}};const H=new V,U=u(i);function W(e){let t=new Date(e),n=new Date,r=t.getTime()-n.getTime(),i=Math.floor(r/(3600*1e3)),a=t.toLocaleTimeString(`en-US`,{hour:`numeric`,minute:`2-digit`,hour12:!0}),o=t.toDateString()===n.toDateString()?`Today`:t.toDateString()===new Date(n.getTime()+864e5).toDateString()?`Tomorrow`:t.toLocaleDateString(`en-US`,{month:`short`,day:`numeric`});return`${o} at ${a} (${i} hours from now)`}var le=class{async execute(){console.log(t.blue.bold(`🤖 StackOne Agent Setup - Global Configuration
5
+ `));try{let e=process.cwd(),n=await a(`${e}/CLAUDE.md`).then(()=>!0).catch(()=>!1),r=await a(`${e}/.mcp.json`).then(()=>!0).catch(()=>!1);if(n||r){A.warn(`Existing local setup detected:`),n&&console.log(t.dim(` • CLAUDE.md found in current directory`)),r&&console.log(t.dim(` • .mcp.json found in current directory`)),console.log();let{proceed:e}=await w.prompt([{type:`confirm`,name:`proceed`,message:`Re-authenticate globally despite existing local setup?`,default:!0}]);e||(console.log(t.dim(`
6
+ Setup cancelled.`)),process.exit(0)),console.log()}let i=await P(),o;if(i?.email){console.log(t.dim(`Last used email: ${i.email}\n`));let{useExisting:e}=await w.prompt([{type:`confirm`,name:`useExisting`,message:`Sign in with this email?`,default:!0}]);if(e)o=i.email;else{let e=C({input:x,output:S});o=await new Promise(n=>{e.question(t.white(`Email: `),e=>{n(e.trim())})}),e.close(),o||(A.error(`Email is required`),process.exit(1))}}else{let e=C({input:x,output:S});o=await new Promise(n=>{e.question(t.white(`Email: `),e=>{n(e.trim())})}),e.close(),o||(A.error(`Email is required`),process.exit(1))}let s=await w.prompt([{type:`password`,name:`password`,message:`Password:`,mask:`*`}]);s.password||(A.error(`Password is required`),process.exit(1)),console.log();let c=d(`Authenticating...`).start(),l=await H.login(o,s.password);(!l.success||!l.apiKey)&&(c.fail(`Authentication failed`),A.error(`${l.error||`Invalid credentials`}`),console.log(t.dim(`Please check your email and password and try again.
7
+ `)),process.exit(1)),c.succeed(`Login successful!`);let u=new Date(Date.now()+1440*60*1e3).toISOString();await N({apiKey:l.apiKey,userId:o,email:o,expiresAt:u,serverUrl:F()}),A.success(`API key saved to ~/.stackone/agent-config.json`),console.log(t.white(`✓ Key expires at`),t.cyan(W(u)));let f=d(`Setting up global MCP configuration...`).start();try{let e=I(),n=F();try{await U(`claude mcp remove ${e} 2>/dev/null`)}catch{}let r=`claude mcp add ${e} ${n} --scope user --transport http --header "Authorization: Bearer ${l.apiKey}"`;await U(r),f.succeed(`Installed to Claude (global)`),A.success(`Global setup complete!`),console.log(t.white(`Config location:`),t.cyan(`~/.claude.json`)),console.log(t.blue(`
8
+ Verify:`),t.cyan(`claude mcp list`)),console.log(t.dim(`
9
+ 💡 Tip: Run Claude without permission prompts:`),t.cyan(`claude --dangerously-skip-permissions`)),console.log(t.dim(`
10
+ To setup for a specific project, run:`),t.cyan(`stackone agent setup --local`)),console.log(``)}catch(e){f.fail(`Installation to Claude failed`),e instanceof Error&&(A.error(e.message),console.error(t.dim(`You can manually add the server later with:`)),console.error(t.cyan(` claude mcp add ${I()} ${F()} --scope user --transport http --header "Authorization: Bearer ${l.apiKey}"`)))}process.exit(0)}catch(e){e instanceof Error?A.error(e.message):A.error(`Unknown error occurred`),process.exit(1)}}};const G=u(i),ue=ne(import.meta.url),de=c(ue);async function fe(){let e=l(de,`..`,`..`,`..`,`CLAUDE_TEMPLATE.md`);try{return await o(e,`utf-8`)}catch{return`# 🚀 STACKONE API CONFIG BUILDER
11
+
12
+ This document provides the complete methodology for building StackOne API configurations with Claude. Follow this strict workflow to ensure comprehensive, tested, and customer-valuable integrations.
13
+
14
+ ## 🔴 CRITICAL WORKFLOW (STRICT ORDER)
15
+
16
+ When asked to build StackOne API configurations, you MUST follow this exact sequence:
17
+
18
+ 1. **Research Phase (PARALLEL EXECUTION)** → Launch \`discover_actions\` subagent for action discovery + main agent for auth/docs/external repos
19
+ 2. **Synchronization** → Collect and integrate subagent results
20
+ 3. **Version Validation** → \`analyze_versioning()\` → Detect and resolve API version conflicts for discovered endpoints
21
+ 4. **Config Building** → Create comprehensive configuration with all discovered actions
22
+ 5. **YAML Validation** → \`stackone validate src/configs/<provider>.yaml\` → Ensure valid YAML syntax
23
+ 6. **Coverage Validation** → \`check_all_endpoints()\` → Confirm endpoint coverage ≥80%
24
+ 7. **Testing Phase** → \`run_connector_action()\` → Test EVERY action with real API calls
25
+ 8. **Test Completion** → \`check_test_completion()\` → Verify 100% actions tested
26
+ 9. **Security** → \`scramble_credentials()\` → Secure all sensitive data before storage
27
+ 10. **Meta Feedback** → \`meta_feedback()\` → **MANDATORY** - Send feedback to third-party system for tracking
28
+
29
+ **❌ Skip/Disorder = Incomplete Task / Professional Failure**
30
+
31
+ ## 🎯 CORE PRINCIPLES
32
+
33
+ - **MAXIMUM COVERAGE**: Discover and include ALL useful actions that provide customer value
34
+ - **ACTION-FOCUSED**: Think: "what actions would developers commonly perform with this provider?"
35
+ - **CUSTOMER VALUE**: Prioritize actions that solve real business problems
36
+ - **MORE IS BETTER**: Default to comprehensiveness over minimalism
37
+ - **PRACTICAL UTILITY**: Focus on actions developers actually use in production
38
+
39
+ ## 🔍 AVAILABLE TOOLS
40
+
41
+ ### Core Research
42
+ - \`get_stackone_categories()\`, \`get_stackone_actions(category)\`
43
+ - \`get_providers()\`, \`get_provider_coverage(provider)\`
44
+ - \`vector_search(query, provider, k)\`
45
+
46
+ ### Action Discovery
47
+ - \`discover_actions(provider)\` - Autonomous AI agent (5-15 min)
48
+ - \`get_provider_actions(provider)\` - Check S3 for indexed actions
49
+
50
+ ### API Versioning
51
+ - \`analyze_versioning(provider, endpoints)\` - Version validation (2-5 min)
52
+
53
+ ### External Analysis
54
+ - \`get_external_integrations(provider)\`
55
+ - \`analyze_external_integration(integration, provider)\`
56
+ - \`scan_external_repo(repo_url, search_terms)\`
57
+
58
+ ### Testing
59
+ - \`run_connector_action()\` - Test with real API calls
60
+ - \`check_all_endpoints()\` - Validate ≥80% coverage
61
+ - \`check_test_completion()\` - Verify 100% tested
62
+
63
+ ### Security
64
+ - \`scramble_credentials()\` - **REQUIRED** before storage
65
+ - \`meta_feedback()\` - **MANDATORY** after completion
66
+
67
+ ## 📚 DOCUMENTATION
68
+
69
+ See \`src/configs/README.md\` for complete YAML structure, authentication patterns, actions, and field configs.
70
+
71
+ ## 🔐 AUTHENTICATION
72
+
73
+ Your CLI is authenticated via OAuth with StackOne.
74
+
75
+ **Security:** Your access token is stored as \`STACKONE_FALCON_MCP_TOKEN\` environment variable. The \`.mcp.json\` file uses this variable reference - safe to commit!
76
+
77
+ ## ⚡ QUICK START
78
+
79
+ \`\`\`bash
80
+ # Setup authentication (global)
81
+ stackone agent setup --global
82
+
83
+ # Setup local project
84
+ stackone agent setup --local
85
+
86
+ # Cleanup all credentials
87
+ stackone agent cleanup
88
+ \`\`\`
89
+
90
+ ---
91
+
92
+ *Authenticated with StackOne • Agent MCP Server*
93
+ *For full workflow details, see the complete CLAUDE.md in repository*
94
+ `}}async function pe(){let e=`.mcp.json`,t;try{let n=await o(e,`utf-8`);t=JSON.parse(n)}catch{t={mcpServers:{}}}t.mcpServers||={},t.mcpServers[L()]={url:F(),transport:{type:`http`,headers:{Authorization:"Bearer ${STACKONE_AGENT_MCP_TOKEN}"}}},await s(e,JSON.stringify(t,null,2))}var me=class{async execute(){console.log(t.blue.bold(`🤖 StackOne Agent Setup - Local Project
95
+ `));try{let e=process.cwd(),n=await a(`${e}/CLAUDE.md`).then(()=>!0).catch(()=>!1),r=await a(`${e}/.mcp.json`).then(()=>!0).catch(()=>!1);if(n||r){A.warn(`Existing local setup detected:`),n&&console.log(t.dim(` • CLAUDE.md found`)),r&&console.log(t.dim(` • .mcp.json found`)),console.log();let{proceed:e}=await w.prompt([{type:`confirm`,name:`proceed`,message:`Overwrite existing local configuration?`,default:!1}]);e||(console.log(t.dim(`
96
+ Setup cancelled.`)),process.exit(0)),console.log()}let i=await P();if(!i||!ce(i)){A.warn(`Not authenticated globally.`),console.log(t.white(`Let's authenticate now...
97
+ `));let e=C({input:x,output:S}),n=await new Promise(n=>{e.question(t.white(`Email: `),e=>{n(e.trim())})});e.close(),n||(A.error(`Email is required`),process.exit(1));let r=await w.prompt([{type:`password`,name:`password`,message:`Password:`,mask:`*`}]);r.password||(A.error(`Password is required`),process.exit(1)),console.log();let a=d(`Authenticating...`).start(),o=await H.login(n,r.password);(!o.success||!o.apiKey)&&(a.fail(`Authentication failed`),A.error(`${o.error||`Invalid credentials`}`),console.log(t.dim(`Please check your email and password and try again.
98
+ `)),process.exit(1)),a.succeed(`Login successful!`);let s=new Date(Date.now()+1440*60*1e3).toISOString();await N({apiKey:o.apiKey,userId:n,email:n,expiresAt:s,serverUrl:F()}),i={apiKey:o.apiKey,userId:n,email:n,expiresAt:s,serverUrl:F()},console.log()}let o=d(`Setting up local project configuration...`).start();o.text=`Creating CLAUDE.md...`;let c=await fe();await s(`CLAUDE.md`,c),o.succeed(`Created CLAUDE.md`);let l=d(`Creating .mcp.json...`).start();await pe(),l.succeed(`Created .mcp.json (using STACKONE_AGENT_MCP_TOKEN env var)`);let u=d(`Installing to Claude (local)...`).start();try{let e=L();try{await G(`claude mcp remove ${e} 2>/dev/null`)}catch{}let n=`claude mcp add ${e} ${F()} --scope local --transport http --header "Authorization: Bearer ${i.apiKey}"`;await G(n),u.succeed(`Installed to Claude (local)`),A.success(`Local setup complete!`),console.log(t.white(`Files created:`)),console.log(t.cyan(` - CLAUDE.md`)),console.log(t.cyan(` - .mcp.json`)),console.log(t.dim(`
99
+ 💡 The stackone-agent-local MCP server is now available in this project`)),console.log(t.dim(`
100
+ 💡 Tip: Run Claude without permission prompts:`),t.cyan(`claude --dangerously-skip-permissions`)),console.log(t.blue(`
101
+ Verify:`),t.cyan(`claude mcp list`)),console.log(``)}catch(e){u.fail(`Installation to Claude failed`),e instanceof Error&&(A.error(e.message),console.error(t.dim(`Configuration files created, but Claude setup failed.`)),console.error(t.dim(`You can manually add the server later with:`)),console.error(t.cyan(` claude mcp add ${L()} ${F()} --scope local --transport http --header "Authorization: Bearer ${i.apiKey}"`))),console.log(``)}process.exit(0)}catch(e){e instanceof Error?A.error(e.message):A.error(`Unknown error occurred`),process.exit(1)}}};const K={profiles:{}},q=()=>{let e=j.readConfigFile(O);if(!e)return K;try{return JSON.parse(e)}catch{return K}},J=e=>{let t=q();return t.profiles[e]},Y=e=>{let t=q();return!!t.profiles[e]},he=(e,t)=>{let n=q();n.profiles[e]=t,j.writeConfigFile(O,JSON.stringify(n,null,2))},ge=()=>{let e=q();return Object.keys(e.profiles)},_e=`https://api.stackone.com`,ve=`https://api.stackone-dev.com`,ye=`http://localhost:4000`;var be=class{async execute({environment:e}={}){let t=e?.toLowerCase()??`production`;try{let{label:e}=await w.prompt([{type:`input`,name:`label`,message:`Profile label:`,validate:e=>!e||e.trim().length===0?`The profile label is required`:e.includes(` `)?`The profile label cannot contain spaces`:!0}]);if(Y(e)){let{overwrite:t}=await w.prompt([{type:`confirm`,name:`overwrite`,message:`Configuration profile with name "${e}" already exists. Do you want to overwrite it?`,default:!1}]);t||(A.info(`Configuration profile initialization cancelled`),process.exit(0))}let n;if(t===`production`)n=_e;else if(t===`staging`)n=ve;else{let{customUrl:e}=await w.prompt([{type:`input`,name:`customUrl`,message:`API URL:`,default:ye,validate:e=>{if(!e||e.trim().length===0)return`API URL is required`;try{return new URL(e),!0}catch{return`Please enter a valid URL`}}}]);n=e}let{apiKey:r}=await w.prompt([{type:`password`,name:`apiKey`,message:`API Key:`,validate:e=>!e||e.trim().length===0?`API Key is required`:!0}]),i={label:e,environment:t,apiUrl:n,apiKey:r};he(e,i),A.success(`Configuration profile "${e}" saved successfully`),process.exit(0)}catch(e){e.isTtyError?A.error(`Prompt couldn't be rendered in the current environment`):A.error(`Failed to initialize configuration profile: ${e}`),process.exit(1)}}};const X=`https://api.stackone.com`;var xe=class{async execute({profile:e,fileOrDir:t,apiUrl:n,apiKey:r}={}){if(!e&&!r&&(A.error(`Please provide a profile or API key to use for pushing the connector.`),A.info(`You can provide these using the --profile and --api-key options.`),A.info(`Run "stackone init" to create a new configuration profile.`),process.exit(1)),e&&r&&(A.error(`Please provide either a profile or an API key, not both.`),process.exit(1)),e&&n&&A.warn(`Specifying --api-url with a profile won't have any effect. Using API url from profile.`),e&&!Y(e)){A.error(`Configuration profile "${e}" not found.`),A.info(`Run "stackone init" to create a new configuration profile.`);let t=ge();A.info(`Available profiles: ${t.join(`, `)||`none`}`),process.exit(1)}let i=e?J(e):{apiUrl:n??X,apiKey:r};i||(A.error(`Failed to load configuration profile "${e}".`),process.exit(1)),i.apiKey||(A.error(`API key is missing. Please provide a valid API key in the profile or via the --api-key option.`),process.exit(1)),t||(A.error(`File or directory path is required.`),process.exit(1));try{_(t)}catch{A.error(`File or directory not found: ${t}`),process.exit(1)}let a=_(t),o=i.apiUrl??X;if(a.isDirectory()){let e=Z(t),n=0;e.length===0?(A.error(`No .s1.yaml connector files found in the directory: ${t}.`),process.exit(1)):A.info(`Found ${e.length} connector(s) file(s). Processing...`);for(let t of e){console.info(`
102
+ `);let e=await this.uploadFile(t,o,i.apiKey);e&&n++}console.info(`
103
+ `),A.info(`Upload completed: ${n} of ${e.length} file(s) uploaded successfully.`),process.exit(n>0?0:1)}else t.endsWith(`.s1.yaml`)||(A.error(`Only .s1.yaml files are supported for upload.`),process.exit(1));let s=await this.uploadFile(t,o,i.apiKey);process.exit(s?0:1)}async uploadFile(e,n,r){try{console.info(t.blue(`📤 Uploading ${y(e)}...`));let i=Se(e);if(!i)return!1;let a=y(e),o=new FormData,s=new Blob([i],{type:`application/x-yaml`});o.append(`file`,s,a);let c=Buffer.from(r).toString(`base64`),l=await fetch(`${n}/registry/connectors`,{method:`POST`,headers:{Authorization:`Basic ${c}`},body:o});if(!l.ok){let e=await l.text();return A.error(`Upload failed: ${l.status} - ${l.statusText}`),e&&console.error(t.red(e)),!1}let u=await l.json();return console.info(t.green(`✓ Successfully uploaded ${a} with connector "${u.provider}@${u.version}"`)),!0}catch(e){return A.error(`Failed to upload file: ${e}`),!1}}};const Se=e=>{let n=T(e),r=E(n),i=r?.errors??[];if(r.success){let t=e.split(`/`).pop()||e;return A.success(`Connector ${t} is valid!`),n}else i.length>0?(A.error(`Connector ${e} is not valid. Please fix the following errors:\n`),i.forEach(e=>{console.info(t.red(`- L${e.line}: ${e.message}`))})):A.error(`Connector ${e} is not valid. Please check the file for errors.`)},Z=e=>{let t=[],n=h(e,{withFileTypes:!0});for(let r of n){let n=b(e,r.name);r.isDirectory()?t.push(...Z(n)):r.name.endsWith(`.s1.yaml`)&&t.push(n)}return t};var Ce=class{async execute(e){let{watchMode:n,fileOrDir:r}=e,i=d(`Watching for changes...`),a=()=>{console.clear(),console.info(t.yellow(`Watch mode enabled. Press "q" to quit.
104
+ `))};if(r){try{_(r)}catch{A.error(`File or directory not found: ${r}`),process.exit(1)}if(n){let{watch:e}=await import(`./esm-Wey0v-fi.js`),n=await import(`readline`),o=n.createInterface({input:process.stdin,output:process.stdout});process.stdin.setRawMode(!0),process.stdin.resume(),process.stdin.setEncoding(`utf8`);let s=()=>{console.info(t.grey(`
105
+
106
+ Exiting watch mode...`)),c.close(),i.stop(),o.close(),process.exit(0)};a(),console.info(t.blue(`Running connector(s) validations...`)),await Q(r),i.start();let c=e(r||`.`,{ignored:/(^|[/\\])\../,persistent:!0});c.on(`change`,async e=>{a(),i.stop(),console.info(t.blue(`File change detected. Running connector(s) validations...`)),await Q(r),i.start()}),process.stdin.on(`data`,e=>{e.toString()===`q`&&s()}),process.on(`SIGINT`,()=>{s()})}else await Q(r),process.exit(0)}}};const Q=async e=>{let t=_(e);if(t.isDirectory()){let t=h(e),n=t.filter(e=>e.endsWith(`.s1.yaml`));if(n.length===0){A.error(`No StackOne connectors found in directory: ${e}. Connector files need to have the extension .s1.yaml.\n`);return}let r=0,i=0;for(let t of n){let n=`${e}/${t}`,a=_(n);if(a.isFile()){let e=await $(n);e?r++:i++}}A.info(`Validation completed: ${r} valid, ${i} invalid connectors.\n`)}else t.isFile()&&e.endsWith(`s1.yaml`)?await $(e):A.error(`No StackOne connector found: ${e}. Connector files need to have the extension .s1.yaml.\n`)},$=async e=>{let n=T(e),r=E(n),i=r?.errors??[];if(r.success){let t=e.split(`/`).pop()||e;return A.success(`Connector ${t} is valid!\n`),!0}else if(i.length>0)return A.error(`Connector ${e} is not valid. Please fix the following errors:\n`),i.forEach(e=>{console.info(t.red(`- L${e.line}: ${e.message}`))}),console.info(`
107
+ `),!1;else return A.error(`Connector ${e} is not valid. Please check the file for errors.`),!1},we=()=>{try{let e=re(import.meta.url),t=te(e),n=b(t,`..`,`package.json`),r=JSON.parse(m(n,`utf8`));return r.version}catch{return`unknown`}};var Te=class{constructor(e=new r,t=we()){this.program=e,this.version=t,this.setupProgram(),this.registerCommands()}setupProgram(){this.program.name(`stackone`).description(`StackOne CLI`).version(this.version,`-v, --version`)}registerCommands(){let e=new be,r=new xe,i=new Ce,a=new le,o=new me,s=new B;this.program.configureOutput({writeOut:e=>process.stdout.write(e),writeErr:e=>process.stderr.write(e),outputError:(e,n)=>{n(t.red(e))}}),this.program.command(`init`).option(`-e, --env <environment>`,`Specify the environment for the configuration profile`).description(`Initialize & create a StackOne CLI configuration profile`).action(t=>{e.execute({environment:t.env})}),this.program.command(`push`).option(`-p, --profile <label>`,`Configuration profile to use`).option(`--api-url <api url>`,`API URL`).option(`--api-key <api key>`,`API Key`).addArgument(new n(`<path>`,`Connector file or directory to push`)).description(`Push a connector to the StackOne registry`).action((e,t)=>{r.execute({profile:t.profile,fileOrDir:e,apiUrl:t.apiUrl,apiKey:t.apiKey})}),this.program.command(`validate`).option(`-w, --watch`,`Run in watch mode`).addArgument(new n(`<path>`,`Connector file or directory with connectors to validate`)).description(`Validate a StackOne connector`).action((e,t)=>{i.execute({watchMode:t.watch,fileOrDir:e})}),this.program.command(`version`).description(`Show version information`).action(()=>{console.info(`${t.greenBright(`StackOne`)} ${t.grey(`CLI`)} ${t.whiteBright(this.version)}`),process.exit(0)});let c=this.program.command(`agent`).description(`StackOne agent commands`);c.command(`setup`).option(`-g, --global`,`Setup global configuration`).option(`-l, --local`,`Setup local project configuration`).description(`Setup StackOne agent (global or local)`).action(e=>{e.global?a.execute():o.execute()}),c.command(`cleanup`).description(`Remove all API keys and credentials from configurations`).action(()=>{s.execute()})}run(){this.program.parse(process.argv)}};export{Te as CLI};
@@ -0,0 +1,107 @@
1
+ const e=require(`./chunk-CUT6urMc.cjs`),t=e.__toESM(require(`chalk`)),n=e.__toESM(require(`commander`)),r=e.__toESM(require(`node:child_process`)),i=e.__toESM(require(`node:fs/promises`)),a=e.__toESM(require(`node:path`)),o=e.__toESM(require(`node:util`)),s=e.__toESM(require(`ora`)),c=e.__toESM(require(`fs`)),l=e.__toESM(require(`os`)),u=e.__toESM(require(`path`)),d=e.__toESM(require(`node:process`)),f=e.__toESM(require(`node:readline`)),p=e.__toESM(require(`inquirer`)),m=e.__toESM(require(`node:url`)),h=e.__toESM(require(`@stackone/connect-sdk`)),g=e.__toESM(require(`url`)),_=`config.json`,v=`agent-config.json`;var y=class{static info(e){console.info(t.default.blue(`ℹ`),e)}static warn(e){console.info(t.default.yellow(`⚠`),e)}static error(e){console.info(t.default.red(`✗`),e)}static success(e){console.info(t.default.green(`✓`),e)}},b=class{static configDir=(0,u.join)((0,l.homedir)(),`.stackone`);static ensureConfigDir(){if((0,c.existsSync)(this.configDir)){let e=(0,c.lstatSync)(this.configDir);if(!e.isDirectory()){let e=null;try{y.info(`.stackone exists as a file, checking for migration to config directory...`),e=(0,c.readFileSync)(this.configDir,`utf8`)}catch{}if((0,c.rmSync)(this.configDir,{force:!0}),require(`fs`).mkdirSync(this.configDir,{recursive:!0}),e)try{JSON.parse(e),(0,c.writeFileSync)((0,u.join)(this.configDir,_),e,`utf8`),y.success(`Migrated existing .stackone file content to config.json`)}catch{y.warn(`Note: Existing .stackone file was not valid JSON, skipping migration`)}else y.warn(`Note: Existing .stackone file was not valid JSON, skipping migration`)}}else require(`fs`).mkdirSync(this.configDir,{recursive:!0})}static getConfigDir(){return this.configDir}static writeConfigFile(e,t,n){this.ensureConfigDir();let r=(0,u.join)(this.configDir,e);(0,c.writeFileSync)(r,t,`utf8`),n?.mode!==void 0&&require(`fs`).chmodSync(r,n.mode)}static readConfigFile(e){this.ensureConfigDir();let t=(0,u.join)(this.configDir,e);if(!(0,c.existsSync)(t))return null;try{return(0,c.readFileSync)(t,`utf8`)}catch{return null}}static deleteConfigFile(e){let t=(0,u.join)(this.configDir,e);(0,c.existsSync)(t)&&(0,c.rmSync)(t,{force:!0})}};const ee=`stackone-agent-global`,te=`stackone-agent-local`,x=process.env.STACKONE_AGENT_SERVER_URL||`https://mcp-internal-falcon.stackonehq.workers.dev/mcp`;async function S(e){b.writeConfigFile(v,JSON.stringify(e,null,2),{mode:384})}async function C(){let e=b.readConfigFile(v);if(!e)return null;try{return JSON.parse(e)}catch{return null}}async function w(){b.deleteConfigFile(v)}function T(){return x}function E(){return ee}function D(){return te}function O(e){return new Date(e)<=new Date}function k(e){return!!e?.apiKey&&!O(e.expiresAt)}const A=(0,o.promisify)(r.exec);async function j(e){try{let t=await(0,i.readFile)(e,`utf-8`),n=JSON.parse(t),r=!1;if(n.mcpServers&&typeof n.mcpServers==`object`){for(let[e,t]of Object.entries(n.mcpServers))if(t&&typeof t==`object`&&`transport`in t){let e=t.transport;if(e&&typeof e==`object`&&`headers`in e){let t=e.headers;if(t&&typeof t==`object`&&`Authorization`in t){let e=t.Authorization;typeof e==`string`&&e.startsWith(`Bearer `)&&!e.includes("${")&&(t.Authorization="Bearer ${STACKONE_AGENT_MCP_TOKEN}",r=!0)}}}}return r&&await(0,i.writeFile)(e,JSON.stringify(n,null,2)),r}catch{return!1}}var ne=class{async execute(){console.log(t.default.blue.bold(`🧹 StackOne Agent Cleanup
2
+ `));let e={configsCleaned:0};try{let n=(0,s.default)(`Removing stored configuration...`).start();await w(),n.succeed(`Removed ~/.stackone/agent-config.json`);let r=(0,s.default)(`Removing MCP servers from Claude...`).start(),i=0;try{let e=E();await A(`claude mcp remove ${e} 2>/dev/null`),i++}catch{}try{let e=D();await A(`claude mcp remove ${e} 2>/dev/null`),i++}catch{}i>0?r.succeed(`Removed ${i} MCP server(s) from Claude`):r.warn(`No MCP servers found in Claude (already removed or not configured)`);let o=(0,s.default)(`Checking for .mcp.json file...`).start(),c=process.cwd(),l=(0,a.join)(c,`.mcp.json`);try{let t=await j(l);t?(e.configsCleaned=1,o.succeed(`Cleaned .mcp.json (replaced tokens with environment variables)`)):o.info(`.mcp.json already uses environment variables or not found`)}catch{o.info(`.mcp.json not found (already cleaned or not created)`)}y.success(`Cleanup complete!`),console.log(t.default.white(`
3
+ What was removed:`)),console.log(t.default.cyan(` • ~/.stackone/agent-config.json`)),console.log(t.default.cyan(` • MCP server from Claude configuration`)),e.configsCleaned>0&&console.log(t.default.cyan(` • Hardcoded credentials from .mcp.json`)),console.log(t.default.dim(`
4
+ To authenticate again:`)),console.log(t.default.cyan(` stackone agent setup --global`),t.default.dim(`(for global setup)`)),console.log(t.default.cyan(` stackone agent setup --local`),t.default.dim(`(for local project setup)`)),console.log(``),process.exit(0)}catch(e){e instanceof Error?y.error(e.message):y.error(`Unknown error occurred`),process.exit(1)}}},re=class{baseUrl;constructor(e){this.baseUrl=e||process.env.AUTH_SERVER_URL||`https://idp-api.stackone.com`}async login(e,t){try{let n=`${this.baseUrl}/auth/sign-in/mcp`,r=await fetch(n,{method:`POST`,headers:{"Content-Type":`application/json`},body:JSON.stringify({email:e,password:t})});if(!r.ok){let e=await r.text(),t={};try{t=JSON.parse(e)}catch{}return{success:!1,error:t.error||`HTTP ${r.status}: ${r.statusText}`}}let i=await r.text(),a=JSON.parse(i);return a.apiKey?{success:!0,apiKey:a.apiKey}:{success:!1,error:`Invalid response format`}}catch(e){return{success:!1,error:e instanceof Error?e.message:`Network error occurred`}}}async validateKey(e){try{let t=`${this.baseUrl}/api-key/verify`,n=await fetch(t,{method:`POST`,headers:{"Content-Type":`application/json`},body:JSON.stringify({key:e})});if(!n.ok){let e=await n.text(),t={};try{t=JSON.parse(e)}catch{}return{valid:!1,error:t.error||`HTTP ${n.status}: ${n.statusText}`}}let r=await n.text();return JSON.parse(r)}catch(e){return{valid:!1,error:e instanceof Error?e.message:`Network error occurred`}}}};const M=new re,N=(0,o.promisify)(r.exec);function P(e){let t=new Date(e),n=new Date,r=t.getTime()-n.getTime(),i=Math.floor(r/(3600*1e3)),a=t.toLocaleTimeString(`en-US`,{hour:`numeric`,minute:`2-digit`,hour12:!0}),o=t.toDateString()===n.toDateString()?`Today`:t.toDateString()===new Date(n.getTime()+864e5).toDateString()?`Tomorrow`:t.toLocaleDateString(`en-US`,{month:`short`,day:`numeric`});return`${o} at ${a} (${i} hours from now)`}var F=class{async execute(){console.log(t.default.blue.bold(`🤖 StackOne Agent Setup - Global Configuration
5
+ `));try{let e=process.cwd(),n=await(0,i.access)(`${e}/CLAUDE.md`).then(()=>!0).catch(()=>!1),r=await(0,i.access)(`${e}/.mcp.json`).then(()=>!0).catch(()=>!1);if(n||r){y.warn(`Existing local setup detected:`),n&&console.log(t.default.dim(` • CLAUDE.md found in current directory`)),r&&console.log(t.default.dim(` • .mcp.json found in current directory`)),console.log();let{proceed:e}=await p.default.prompt([{type:`confirm`,name:`proceed`,message:`Re-authenticate globally despite existing local setup?`,default:!0}]);e||(console.log(t.default.dim(`
6
+ Setup cancelled.`)),process.exit(0)),console.log()}let a=await C(),o;if(a?.email){console.log(t.default.dim(`Last used email: ${a.email}\n`));let{useExisting:e}=await p.default.prompt([{type:`confirm`,name:`useExisting`,message:`Sign in with this email?`,default:!0}]);if(e)o=a.email;else{let e=(0,f.createInterface)({input:d.stdin,output:d.stdout});o=await new Promise(n=>{e.question(t.default.white(`Email: `),e=>{n(e.trim())})}),e.close(),o||(y.error(`Email is required`),process.exit(1))}}else{let e=(0,f.createInterface)({input:d.stdin,output:d.stdout});o=await new Promise(n=>{e.question(t.default.white(`Email: `),e=>{n(e.trim())})}),e.close(),o||(y.error(`Email is required`),process.exit(1))}let c=await p.default.prompt([{type:`password`,name:`password`,message:`Password:`,mask:`*`}]);c.password||(y.error(`Password is required`),process.exit(1)),console.log();let l=(0,s.default)(`Authenticating...`).start(),u=await M.login(o,c.password);(!u.success||!u.apiKey)&&(l.fail(`Authentication failed`),y.error(`${u.error||`Invalid credentials`}`),console.log(t.default.dim(`Please check your email and password and try again.
7
+ `)),process.exit(1)),l.succeed(`Login successful!`);let m=new Date(Date.now()+1440*60*1e3).toISOString();await S({apiKey:u.apiKey,userId:o,email:o,expiresAt:m,serverUrl:T()}),y.success(`API key saved to ~/.stackone/agent-config.json`),console.log(t.default.white(`✓ Key expires at`),t.default.cyan(P(m)));let h=(0,s.default)(`Setting up global MCP configuration...`).start();try{let e=E(),n=T();try{await N(`claude mcp remove ${e} 2>/dev/null`)}catch{}let r=`claude mcp add ${e} ${n} --scope user --transport http --header "Authorization: Bearer ${u.apiKey}"`;await N(r),h.succeed(`Installed to Claude (global)`),y.success(`Global setup complete!`),console.log(t.default.white(`Config location:`),t.default.cyan(`~/.claude.json`)),console.log(t.default.blue(`
8
+ Verify:`),t.default.cyan(`claude mcp list`)),console.log(t.default.dim(`
9
+ 💡 Tip: Run Claude without permission prompts:`),t.default.cyan(`claude --dangerously-skip-permissions`)),console.log(t.default.dim(`
10
+ To setup for a specific project, run:`),t.default.cyan(`stackone agent setup --local`)),console.log(``)}catch(e){h.fail(`Installation to Claude failed`),e instanceof Error&&(y.error(e.message),console.error(t.default.dim(`You can manually add the server later with:`)),console.error(t.default.cyan(` claude mcp add ${E()} ${T()} --scope user --transport http --header "Authorization: Bearer ${u.apiKey}"`)))}process.exit(0)}catch(e){e instanceof Error?y.error(e.message):y.error(`Unknown error occurred`),process.exit(1)}}};const I=(0,o.promisify)(r.exec),L=(0,m.fileURLToPath)(require(`url`).pathToFileURL(__filename).href),R=(0,a.dirname)(L);async function z(){let e=(0,a.join)(R,`..`,`..`,`..`,`CLAUDE_TEMPLATE.md`);try{return await(0,i.readFile)(e,`utf-8`)}catch{return`# 🚀 STACKONE API CONFIG BUILDER
11
+
12
+ This document provides the complete methodology for building StackOne API configurations with Claude. Follow this strict workflow to ensure comprehensive, tested, and customer-valuable integrations.
13
+
14
+ ## 🔴 CRITICAL WORKFLOW (STRICT ORDER)
15
+
16
+ When asked to build StackOne API configurations, you MUST follow this exact sequence:
17
+
18
+ 1. **Research Phase (PARALLEL EXECUTION)** → Launch \`discover_actions\` subagent for action discovery + main agent for auth/docs/external repos
19
+ 2. **Synchronization** → Collect and integrate subagent results
20
+ 3. **Version Validation** → \`analyze_versioning()\` → Detect and resolve API version conflicts for discovered endpoints
21
+ 4. **Config Building** → Create comprehensive configuration with all discovered actions
22
+ 5. **YAML Validation** → \`stackone validate src/configs/<provider>.yaml\` → Ensure valid YAML syntax
23
+ 6. **Coverage Validation** → \`check_all_endpoints()\` → Confirm endpoint coverage ≥80%
24
+ 7. **Testing Phase** → \`run_connector_action()\` → Test EVERY action with real API calls
25
+ 8. **Test Completion** → \`check_test_completion()\` → Verify 100% actions tested
26
+ 9. **Security** → \`scramble_credentials()\` → Secure all sensitive data before storage
27
+ 10. **Meta Feedback** → \`meta_feedback()\` → **MANDATORY** - Send feedback to third-party system for tracking
28
+
29
+ **❌ Skip/Disorder = Incomplete Task / Professional Failure**
30
+
31
+ ## 🎯 CORE PRINCIPLES
32
+
33
+ - **MAXIMUM COVERAGE**: Discover and include ALL useful actions that provide customer value
34
+ - **ACTION-FOCUSED**: Think: "what actions would developers commonly perform with this provider?"
35
+ - **CUSTOMER VALUE**: Prioritize actions that solve real business problems
36
+ - **MORE IS BETTER**: Default to comprehensiveness over minimalism
37
+ - **PRACTICAL UTILITY**: Focus on actions developers actually use in production
38
+
39
+ ## 🔍 AVAILABLE TOOLS
40
+
41
+ ### Core Research
42
+ - \`get_stackone_categories()\`, \`get_stackone_actions(category)\`
43
+ - \`get_providers()\`, \`get_provider_coverage(provider)\`
44
+ - \`vector_search(query, provider, k)\`
45
+
46
+ ### Action Discovery
47
+ - \`discover_actions(provider)\` - Autonomous AI agent (5-15 min)
48
+ - \`get_provider_actions(provider)\` - Check S3 for indexed actions
49
+
50
+ ### API Versioning
51
+ - \`analyze_versioning(provider, endpoints)\` - Version validation (2-5 min)
52
+
53
+ ### External Analysis
54
+ - \`get_external_integrations(provider)\`
55
+ - \`analyze_external_integration(integration, provider)\`
56
+ - \`scan_external_repo(repo_url, search_terms)\`
57
+
58
+ ### Testing
59
+ - \`run_connector_action()\` - Test with real API calls
60
+ - \`check_all_endpoints()\` - Validate ≥80% coverage
61
+ - \`check_test_completion()\` - Verify 100% tested
62
+
63
+ ### Security
64
+ - \`scramble_credentials()\` - **REQUIRED** before storage
65
+ - \`meta_feedback()\` - **MANDATORY** after completion
66
+
67
+ ## 📚 DOCUMENTATION
68
+
69
+ See \`src/configs/README.md\` for complete YAML structure, authentication patterns, actions, and field configs.
70
+
71
+ ## 🔐 AUTHENTICATION
72
+
73
+ Your CLI is authenticated via OAuth with StackOne.
74
+
75
+ **Security:** Your access token is stored as \`STACKONE_FALCON_MCP_TOKEN\` environment variable. The \`.mcp.json\` file uses this variable reference - safe to commit!
76
+
77
+ ## ⚡ QUICK START
78
+
79
+ \`\`\`bash
80
+ # Setup authentication (global)
81
+ stackone agent setup --global
82
+
83
+ # Setup local project
84
+ stackone agent setup --local
85
+
86
+ # Cleanup all credentials
87
+ stackone agent cleanup
88
+ \`\`\`
89
+
90
+ ---
91
+
92
+ *Authenticated with StackOne • Agent MCP Server*
93
+ *For full workflow details, see the complete CLAUDE.md in repository*
94
+ `}}async function B(){let e=`.mcp.json`,t;try{let n=await(0,i.readFile)(e,`utf-8`);t=JSON.parse(n)}catch{t={mcpServers:{}}}t.mcpServers||={},t.mcpServers[D()]={url:T(),transport:{type:`http`,headers:{Authorization:"Bearer ${STACKONE_AGENT_MCP_TOKEN}"}}},await(0,i.writeFile)(e,JSON.stringify(t,null,2))}var V=class{async execute(){console.log(t.default.blue.bold(`🤖 StackOne Agent Setup - Local Project
95
+ `));try{let e=process.cwd(),n=await(0,i.access)(`${e}/CLAUDE.md`).then(()=>!0).catch(()=>!1),r=await(0,i.access)(`${e}/.mcp.json`).then(()=>!0).catch(()=>!1);if(n||r){y.warn(`Existing local setup detected:`),n&&console.log(t.default.dim(` • CLAUDE.md found`)),r&&console.log(t.default.dim(` • .mcp.json found`)),console.log();let{proceed:e}=await p.default.prompt([{type:`confirm`,name:`proceed`,message:`Overwrite existing local configuration?`,default:!1}]);e||(console.log(t.default.dim(`
96
+ Setup cancelled.`)),process.exit(0)),console.log()}let a=await C();if(!a||!k(a)){y.warn(`Not authenticated globally.`),console.log(t.default.white(`Let's authenticate now...
97
+ `));let e=(0,f.createInterface)({input:d.stdin,output:d.stdout}),n=await new Promise(n=>{e.question(t.default.white(`Email: `),e=>{n(e.trim())})});e.close(),n||(y.error(`Email is required`),process.exit(1));let r=await p.default.prompt([{type:`password`,name:`password`,message:`Password:`,mask:`*`}]);r.password||(y.error(`Password is required`),process.exit(1)),console.log();let i=(0,s.default)(`Authenticating...`).start(),o=await M.login(n,r.password);(!o.success||!o.apiKey)&&(i.fail(`Authentication failed`),y.error(`${o.error||`Invalid credentials`}`),console.log(t.default.dim(`Please check your email and password and try again.
98
+ `)),process.exit(1)),i.succeed(`Login successful!`);let c=new Date(Date.now()+1440*60*1e3).toISOString();await S({apiKey:o.apiKey,userId:n,email:n,expiresAt:c,serverUrl:T()}),a={apiKey:o.apiKey,userId:n,email:n,expiresAt:c,serverUrl:T()},console.log()}let o=(0,s.default)(`Setting up local project configuration...`).start();o.text=`Creating CLAUDE.md...`;let c=await z();await(0,i.writeFile)(`CLAUDE.md`,c),o.succeed(`Created CLAUDE.md`);let l=(0,s.default)(`Creating .mcp.json...`).start();await B(),l.succeed(`Created .mcp.json (using STACKONE_AGENT_MCP_TOKEN env var)`);let u=(0,s.default)(`Installing to Claude (local)...`).start();try{let e=D();try{await I(`claude mcp remove ${e} 2>/dev/null`)}catch{}let n=`claude mcp add ${e} ${T()} --scope local --transport http --header "Authorization: Bearer ${a.apiKey}"`;await I(n),u.succeed(`Installed to Claude (local)`),y.success(`Local setup complete!`),console.log(t.default.white(`Files created:`)),console.log(t.default.cyan(` - CLAUDE.md`)),console.log(t.default.cyan(` - .mcp.json`)),console.log(t.default.dim(`
99
+ 💡 The stackone-agent-local MCP server is now available in this project`)),console.log(t.default.dim(`
100
+ 💡 Tip: Run Claude without permission prompts:`),t.default.cyan(`claude --dangerously-skip-permissions`)),console.log(t.default.blue(`
101
+ Verify:`),t.default.cyan(`claude mcp list`)),console.log(``)}catch(e){u.fail(`Installation to Claude failed`),e instanceof Error&&(y.error(e.message),console.error(t.default.dim(`Configuration files created, but Claude setup failed.`)),console.error(t.default.dim(`You can manually add the server later with:`)),console.error(t.default.cyan(` claude mcp add ${D()} ${T()} --scope local --transport http --header "Authorization: Bearer ${a.apiKey}"`))),console.log(``)}process.exit(0)}catch(e){e instanceof Error?y.error(e.message):y.error(`Unknown error occurred`),process.exit(1)}}};const H={profiles:{}},U=()=>{let e=b.readConfigFile(_);if(!e)return H;try{return JSON.parse(e)}catch{return H}},W=e=>{let t=U();return t.profiles[e]},G=e=>{let t=U();return!!t.profiles[e]},K=(e,t)=>{let n=U();n.profiles[e]=t,b.writeConfigFile(_,JSON.stringify(n,null,2))},q=()=>{let e=U();return Object.keys(e.profiles)},J=`https://api.stackone.com`,Y=`https://api.stackone-dev.com`,ie=`http://localhost:4000`;var ae=class{async execute({environment:e}={}){let t=e?.toLowerCase()??`production`;try{let{label:e}=await p.default.prompt([{type:`input`,name:`label`,message:`Profile label:`,validate:e=>!e||e.trim().length===0?`The profile label is required`:e.includes(` `)?`The profile label cannot contain spaces`:!0}]);if(G(e)){let{overwrite:t}=await p.default.prompt([{type:`confirm`,name:`overwrite`,message:`Configuration profile with name "${e}" already exists. Do you want to overwrite it?`,default:!1}]);t||(y.info(`Configuration profile initialization cancelled`),process.exit(0))}let n;if(t===`production`)n=J;else if(t===`staging`)n=Y;else{let{customUrl:e}=await p.default.prompt([{type:`input`,name:`customUrl`,message:`API URL:`,default:ie,validate:e=>{if(!e||e.trim().length===0)return`API URL is required`;try{return new URL(e),!0}catch{return`Please enter a valid URL`}}}]);n=e}let{apiKey:r}=await p.default.prompt([{type:`password`,name:`apiKey`,message:`API Key:`,validate:e=>!e||e.trim().length===0?`API Key is required`:!0}]),i={label:e,environment:t,apiUrl:n,apiKey:r};K(e,i),y.success(`Configuration profile "${e}" saved successfully`),process.exit(0)}catch(e){e.isTtyError?y.error(`Prompt couldn't be rendered in the current environment`):y.error(`Failed to initialize configuration profile: ${e}`),process.exit(1)}}};const X=`https://api.stackone.com`;var oe=class{async execute({profile:e,fileOrDir:t,apiUrl:n,apiKey:r}={}){if(!e&&!r&&(y.error(`Please provide a profile or API key to use for pushing the connector.`),y.info(`You can provide these using the --profile and --api-key options.`),y.info(`Run "stackone init" to create a new configuration profile.`),process.exit(1)),e&&r&&(y.error(`Please provide either a profile or an API key, not both.`),process.exit(1)),e&&n&&y.warn(`Specifying --api-url with a profile won't have any effect. Using API url from profile.`),e&&!G(e)){y.error(`Configuration profile "${e}" not found.`),y.info(`Run "stackone init" to create a new configuration profile.`);let t=q();y.info(`Available profiles: ${t.join(`, `)||`none`}`),process.exit(1)}let i=e?W(e):{apiUrl:n??X,apiKey:r};i||(y.error(`Failed to load configuration profile "${e}".`),process.exit(1)),i.apiKey||(y.error(`API key is missing. Please provide a valid API key in the profile or via the --api-key option.`),process.exit(1)),t||(y.error(`File or directory path is required.`),process.exit(1));try{(0,c.statSync)(t)}catch{y.error(`File or directory not found: ${t}`),process.exit(1)}let a=(0,c.statSync)(t),o=i.apiUrl??X;if(a.isDirectory()){let e=Z(t),n=0;e.length===0?(y.error(`No .s1.yaml connector files found in the directory: ${t}.`),process.exit(1)):y.info(`Found ${e.length} connector(s) file(s). Processing...`);for(let t of e){console.info(`
102
+ `);let e=await this.uploadFile(t,o,i.apiKey);e&&n++}console.info(`
103
+ `),y.info(`Upload completed: ${n} of ${e.length} file(s) uploaded successfully.`),process.exit(n>0?0:1)}else t.endsWith(`.s1.yaml`)||(y.error(`Only .s1.yaml files are supported for upload.`),process.exit(1));let s=await this.uploadFile(t,o,i.apiKey);process.exit(s?0:1)}async uploadFile(e,n,r){try{console.info(t.default.blue(`📤 Uploading ${(0,u.basename)(e)}...`));let i=se(e);if(!i)return!1;let a=(0,u.basename)(e),o=new FormData,s=new Blob([i],{type:`application/x-yaml`});o.append(`file`,s,a);let c=Buffer.from(r).toString(`base64`),l=await fetch(`${n}/registry/connectors`,{method:`POST`,headers:{Authorization:`Basic ${c}`},body:o});if(!l.ok){let e=await l.text();return y.error(`Upload failed: ${l.status} - ${l.statusText}`),e&&console.error(t.default.red(e)),!1}let d=await l.json();return console.info(t.default.green(`✓ Successfully uploaded ${a} with connector "${d.provider}@${d.version}"`)),!0}catch(e){return y.error(`Failed to upload file: ${e}`),!1}}};const se=e=>{let n=(0,h.loadConnector)(e),r=(0,h.validateYamlConnector)(n),i=r?.errors??[];if(r.success){let t=e.split(`/`).pop()||e;return y.success(`Connector ${t} is valid!`),n}else i.length>0?(y.error(`Connector ${e} is not valid. Please fix the following errors:\n`),i.forEach(e=>{console.info(t.default.red(`- L${e.line}: ${e.message}`))})):y.error(`Connector ${e} is not valid. Please check the file for errors.`)},Z=e=>{let t=[],n=(0,c.readdirSync)(e,{withFileTypes:!0});for(let r of n){let n=(0,u.join)(e,r.name);r.isDirectory()?t.push(...Z(n)):r.name.endsWith(`.s1.yaml`)&&t.push(n)}return t};var ce=class{async execute(e){let{watchMode:n,fileOrDir:r}=e,i=(0,s.default)(`Watching for changes...`),a=()=>{console.clear(),console.info(t.default.yellow(`Watch mode enabled. Press "q" to quit.
104
+ `))};if(r){try{(0,c.statSync)(r)}catch{y.error(`File or directory not found: ${r}`),process.exit(1)}if(n){let{watch:e}=await Promise.resolve().then(()=>require(`./esm-BSZWAx0q.cjs`)),n=await import(`readline`),o=n.createInterface({input:process.stdin,output:process.stdout});process.stdin.setRawMode(!0),process.stdin.resume(),process.stdin.setEncoding(`utf8`);let s=()=>{console.info(t.default.grey(`
105
+
106
+ Exiting watch mode...`)),c.close(),i.stop(),o.close(),process.exit(0)};a(),console.info(t.default.blue(`Running connector(s) validations...`)),await Q(r),i.start();let c=e(r||`.`,{ignored:/(^|[/\\])\../,persistent:!0});c.on(`change`,async e=>{a(),i.stop(),console.info(t.default.blue(`File change detected. Running connector(s) validations...`)),await Q(r),i.start()}),process.stdin.on(`data`,e=>{e.toString()===`q`&&s()}),process.on(`SIGINT`,()=>{s()})}else await Q(r),process.exit(0)}}};const Q=async e=>{let t=(0,c.statSync)(e);if(t.isDirectory()){let t=(0,c.readdirSync)(e),n=t.filter(e=>e.endsWith(`.s1.yaml`));if(n.length===0){y.error(`No StackOne connectors found in directory: ${e}. Connector files need to have the extension .s1.yaml.\n`);return}let r=0,i=0;for(let t of n){let n=`${e}/${t}`,a=(0,c.statSync)(n);if(a.isFile()){let e=await $(n);e?r++:i++}}y.info(`Validation completed: ${r} valid, ${i} invalid connectors.\n`)}else t.isFile()&&e.endsWith(`s1.yaml`)?await $(e):y.error(`No StackOne connector found: ${e}. Connector files need to have the extension .s1.yaml.\n`)},$=async e=>{let n=(0,h.loadConnector)(e),r=(0,h.validateYamlConnector)(n),i=r?.errors??[];if(r.success){let t=e.split(`/`).pop()||e;return y.success(`Connector ${t} is valid!\n`),!0}else if(i.length>0)return y.error(`Connector ${e} is not valid. Please fix the following errors:\n`),i.forEach(e=>{console.info(t.default.red(`- L${e.line}: ${e.message}`))}),console.info(`
107
+ `),!1;else return y.error(`Connector ${e} is not valid. Please check the file for errors.`),!1},le=()=>{try{let e=(0,g.fileURLToPath)(require(`url`).pathToFileURL(__filename).href),t=(0,u.dirname)(e),n=(0,u.join)(t,`..`,`package.json`),r=JSON.parse((0,c.readFileSync)(n,`utf8`));return r.version}catch{return`unknown`}};var ue=class{constructor(e=new n.Command,t=le()){this.program=e,this.version=t,this.setupProgram(),this.registerCommands()}setupProgram(){this.program.name(`stackone`).description(`StackOne CLI`).version(this.version,`-v, --version`)}registerCommands(){let e=new ae,r=new oe,i=new ce,a=new F,o=new V,s=new ne;this.program.configureOutput({writeOut:e=>process.stdout.write(e),writeErr:e=>process.stderr.write(e),outputError:(e,n)=>{n(t.default.red(e))}}),this.program.command(`init`).option(`-e, --env <environment>`,`Specify the environment for the configuration profile`).description(`Initialize & create a StackOne CLI configuration profile`).action(t=>{e.execute({environment:t.env})}),this.program.command(`push`).option(`-p, --profile <label>`,`Configuration profile to use`).option(`--api-url <api url>`,`API URL`).option(`--api-key <api key>`,`API Key`).addArgument(new n.Argument(`<path>`,`Connector file or directory to push`)).description(`Push a connector to the StackOne registry`).action((e,t)=>{r.execute({profile:t.profile,fileOrDir:e,apiUrl:t.apiUrl,apiKey:t.apiKey})}),this.program.command(`validate`).option(`-w, --watch`,`Run in watch mode`).addArgument(new n.Argument(`<path>`,`Connector file or directory with connectors to validate`)).description(`Validate a StackOne connector`).action((e,t)=>{i.execute({watchMode:t.watch,fileOrDir:e})}),this.program.command(`version`).description(`Show version information`).action(()=>{console.info(`${t.default.greenBright(`StackOne`)} ${t.default.grey(`CLI`)} ${t.default.whiteBright(this.version)}`),process.exit(0)});let c=this.program.command(`agent`).description(`StackOne agent commands`);c.command(`setup`).option(`-g, --global`,`Setup global configuration`).option(`-l, --local`,`Setup local project configuration`).description(`Setup StackOne agent (global or local)`).action(e=>{e.global?a.execute():o.execute()}),c.command(`cleanup`).description(`Remove all API keys and credentials from configurations`).action(()=>{s.execute()})}run(){this.program.parse(process.argv)}};Object.defineProperty(exports,`CLI`,{enumerable:!0,get:function(){return ue}});
package/dist/index.cjs CHANGED
@@ -1 +1 @@
1
- const e=require(`./cliCore-BRPqDxQ9.cjs`);exports.CLI=e.CLI;
1
+ const e=require(`./cliCore-xqwgmaz9.cjs`);exports.CLI=e.CLI;
package/dist/index.js CHANGED
@@ -1 +1 @@
1
- import{CLI as e}from"./cliCore-57MzGvkG.js";export{e as CLI};
1
+ import{CLI as e}from"./cliCore-BFE-6X6w.js";export{e as CLI};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@stackone/cli",
3
- "version": "1.9.1",
3
+ "version": "1.9.2",
4
4
  "description": "StackOne Connect CLI tool",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -1,139 +0,0 @@
1
- import e from"chalk";import{Argument as t,Command as n}from"commander";import{exec as r}from"node:child_process";import{access as i,chmod as a,mkdir as o,readFile as s,rm as c,writeFile as l}from"node:fs/promises";import{dirname as u,join as d}from"node:path";import{promisify as f}from"node:util";import p from"ora";import{existsSync as ee}from"node:fs";import{homedir as m}from"node:os";import{stdin as h,stdout as g}from"node:process";import{createInterface as _}from"node:readline";import v from"inquirer";import{fileURLToPath as y}from"node:url";import{existsSync as b,mkdirSync as te,readFileSync as x,readdirSync as S,statSync as C,writeFileSync as ne}from"fs";import{homedir as w}from"os";import{basename as T,dirname as re,join as E}from"path";import{loadConnector as D,validateYamlConnector as O}from"@stackone/connect-sdk";import{fileURLToPath as ie}from"url";const k=d(m(),`.stackone`),A=d(k,`agent-config.json`),ae=`stackone-agent-global`,oe=`stackone-agent-local`,se=process.env.STACKONE_AGENT_SERVER_URL||`https://mcp-internal-falcon.stackonehq.workers.dev/mcp`;async function j(){let e=ee(k);e||await o(k,{recursive:!0,mode:448})}async function M(e){await j(),await l(A,JSON.stringify(e,null,2)),await a(A,384)}async function N(){try{let e=await s(A,`utf-8`);return JSON.parse(e)}catch{return null}}async function ce(){try{await c(A,{force:!0})}catch{}}function P(){return se}function F(){return ae}function I(){return oe}function le(e){return new Date(e)<=new Date}function ue(e){return!!e?.apiKey&&!le(e.expiresAt)}const L=f(r);async function R(e){try{let t=await s(e,`utf-8`),n=JSON.parse(t),r=!1;if(n.mcpServers&&typeof n.mcpServers==`object`){for(let[e,t]of Object.entries(n.mcpServers))if(t&&typeof t==`object`&&`transport`in t){let e=t.transport;if(e&&typeof e==`object`&&`headers`in e){let t=e.headers;if(t&&typeof t==`object`&&`Authorization`in t){let e=t.Authorization;typeof e==`string`&&e.startsWith(`Bearer `)&&!e.includes("${")&&(t.Authorization="Bearer ${STACKONE_AGENT_MCP_TOKEN}",r=!0)}}}}return r&&await l(e,JSON.stringify(n,null,2)),r}catch{return!1}}var z=class{async execute(){console.log(e.blue.bold(`🧹 StackOne Agent Cleanup
2
- `));let t={configsCleaned:0};try{let n=p(`Removing stored configuration...`).start();await ce(),n.succeed(`Removed ~/.stackone/agent-config.json`);let r=p(`Removing MCP servers from Claude...`).start(),i=0;try{let e=F();await L(`claude mcp remove ${e} 2>/dev/null`),i++}catch{}try{let e=I();await L(`claude mcp remove ${e} 2>/dev/null`),i++}catch{}i>0?r.succeed(`Removed ${i} MCP server(s) from Claude`):r.warn(`No MCP servers found in Claude (already removed or not configured)`);let a=p(`Checking for .mcp.json file...`).start(),o=process.cwd(),s=d(o,`.mcp.json`);try{let e=await R(s);e?(t.configsCleaned=1,a.succeed(`Cleaned .mcp.json (replaced tokens with environment variables)`)):a.info(`.mcp.json already uses environment variables or not found`)}catch{a.info(`.mcp.json not found (already cleaned or not created)`)}console.log(e.green(`
3
- ✓ Cleanup complete!`)),console.log(e.white(`
4
- What was removed:`)),console.log(e.cyan(` • ~/.stackone/agent-config.json`)),console.log(e.cyan(` • MCP server from Claude configuration`)),t.configsCleaned>0&&console.log(e.cyan(` • Hardcoded credentials from .mcp.json`)),console.log(e.dim(`
5
- To authenticate again:`)),console.log(e.cyan(` stackone agent setup --global`),e.dim(`(for global setup)`)),console.log(e.cyan(` stackone agent setup --local`),e.dim(`(for local project setup)`)),console.log(``),process.exit(0)}catch(t){t instanceof Error?console.error(e.red(`\n✗ ${t.message}\n`)):console.error(e.red(`
6
- ✗ Unknown error occurred
7
- `)),process.exit(1)}}},B=class{baseUrl;constructor(e){this.baseUrl=e||process.env.AUTH_SERVER_URL||`https://idp-api.stackone.com`}async login(e,t){try{let n=`${this.baseUrl}/auth/sign-in/mcp`,r=await fetch(n,{method:`POST`,headers:{"Content-Type":`application/json`},body:JSON.stringify({email:e,password:t})});if(!r.ok){let e=await r.text(),t={};try{t=JSON.parse(e)}catch{}return{success:!1,error:t.error||`HTTP ${r.status}: ${r.statusText}`}}let i=await r.text(),a=JSON.parse(i);return a.apiKey?{success:!0,apiKey:a.apiKey}:{success:!1,error:`Invalid response format`}}catch(e){return{success:!1,error:e instanceof Error?e.message:`Network error occurred`}}}async validateKey(e){try{let t=`${this.baseUrl}/api-key/verify`,n=await fetch(t,{method:`POST`,headers:{"Content-Type":`application/json`},body:JSON.stringify({key:e})});if(!n.ok){let e=await n.text(),t={};try{t=JSON.parse(e)}catch{}return{valid:!1,error:t.error||`HTTP ${n.status}: ${n.statusText}`}}let r=await n.text();return JSON.parse(r)}catch(e){return{valid:!1,error:e instanceof Error?e.message:`Network error occurred`}}}};const V=new B,H=f(r);function U(e){let t=new Date(e),n=new Date,r=t.getTime()-n.getTime(),i=Math.floor(r/(3600*1e3)),a=t.toLocaleTimeString(`en-US`,{hour:`numeric`,minute:`2-digit`,hour12:!0}),o=t.toDateString()===n.toDateString()?`Today`:t.toDateString()===new Date(n.getTime()+864e5).toDateString()?`Tomorrow`:t.toLocaleDateString(`en-US`,{month:`short`,day:`numeric`});return`${o} at ${a} (${i} hours from now)`}var de=class{async execute(){console.log(e.blue.bold(`🤖 StackOne Agent Setup - Global Configuration
8
- `));try{let t=process.cwd(),n=await i(`${t}/CLAUDE.md`).then(()=>!0).catch(()=>!1),r=await i(`${t}/.mcp.json`).then(()=>!0).catch(()=>!1);if(n||r){console.log(e.yellow(`⚠ Existing local setup detected:`)),n&&console.log(e.dim(` • CLAUDE.md found in current directory`)),r&&console.log(e.dim(` • .mcp.json found in current directory`)),console.log();let{proceed:t}=await v.prompt([{type:`confirm`,name:`proceed`,message:`Re-authenticate globally despite existing local setup?`,default:!0}]);t||(console.log(e.dim(`
9
- Setup cancelled.`)),process.exit(0)),console.log()}let a=await N(),o;if(a?.email){console.log(e.dim(`Last used email: ${a.email}\n`));let{useExisting:t}=await v.prompt([{type:`confirm`,name:`useExisting`,message:`Sign in with this email?`,default:!0}]);if(t)o=a.email;else{let t=_({input:h,output:g});o=await new Promise(n=>{t.question(e.white(`Email: `),e=>{n(e.trim())})}),t.close(),o||(console.error(e.red(`
10
- ✗ Email is required
11
- `)),process.exit(1))}}else{let t=_({input:h,output:g});o=await new Promise(n=>{t.question(e.white(`Email: `),e=>{n(e.trim())})}),t.close(),o||(console.error(e.red(`
12
- ✗ Email is required
13
- `)),process.exit(1))}let s=await v.prompt([{type:`password`,name:`password`,message:`Password:`,mask:`*`}]);s.password||(console.error(e.red(`
14
- ✗ Password is required
15
- `)),process.exit(1)),console.log();let c=p(`Authenticating...`).start(),l=await V.login(o,s.password);(!l.success||!l.apiKey)&&(c.fail(`Authentication failed`),console.error(e.red(`\n✗ ${l.error||`Invalid credentials`}\n`)),console.log(e.dim(`Please check your email and password and try again.
16
- `)),process.exit(1)),c.succeed(`Login successful!`);let u=new Date(Date.now()+1440*60*1e3).toISOString();await M({apiKey:l.apiKey,userId:o,email:o,expiresAt:u,serverUrl:P()}),console.log(e.green.bold(`
17
- ✓ API key saved to ~/.stackone/agent-config.json`)),console.log(e.white(`✓ Key expires at`),e.cyan(U(u)));let d=p(`Setting up global MCP configuration...`).start();try{let t=F(),n=P();try{await H(`claude mcp remove ${t} 2>/dev/null`)}catch{}let r=`claude mcp add ${t} ${n} --scope user --transport http --header "Authorization: Bearer ${l.apiKey}"`;await H(r),d.succeed(`Installed to Claude (global)`),console.log(e.green(`
18
- ✓ Global setup complete!`)),console.log(e.white(`Config location:`),e.cyan(`~/.claude.json`)),console.log(e.blue(`
19
- Verify:`),e.cyan(`claude mcp list`)),console.log(e.dim(`
20
- 💡 Tip: Run Claude without permission prompts:`),e.cyan(`claude --dangerously-skip-permissions`)),console.log(e.dim(`
21
- To setup for a specific project, run:`),e.cyan(`stackone agent setup --local`)),console.log(``)}catch(t){d.fail(`Installation to Claude failed`),t instanceof Error&&(console.error(e.red(`\n✗ ${t.message}\n`)),console.error(e.dim(`You can manually add the server later with:`)),console.error(e.cyan(` claude mcp add ${F()} ${P()} --scope user --transport http --header "Authorization: Bearer ${l.apiKey}"`)))}process.exit(0)}catch(t){t instanceof Error?console.error(e.red(`\n✗ ${t.message}\n`)):console.error(e.red(`
22
- ✗ Unknown error occurred
23
- `)),process.exit(1)}}};const W=f(r),fe=y(import.meta.url),pe=u(fe);async function me(){let e=d(pe,`..`,`..`,`..`,`CLAUDE_TEMPLATE.md`);try{return await s(e,`utf-8`)}catch{return`# 🚀 STACKONE API CONFIG BUILDER
24
-
25
- This document provides the complete methodology for building StackOne API configurations with Claude. Follow this strict workflow to ensure comprehensive, tested, and customer-valuable integrations.
26
-
27
- ## 🔴 CRITICAL WORKFLOW (STRICT ORDER)
28
-
29
- When asked to build StackOne API configurations, you MUST follow this exact sequence:
30
-
31
- 1. **Research Phase (PARALLEL EXECUTION)** → Launch \`discover_actions\` subagent for action discovery + main agent for auth/docs/external repos
32
- 2. **Synchronization** → Collect and integrate subagent results
33
- 3. **Version Validation** → \`analyze_versioning()\` → Detect and resolve API version conflicts for discovered endpoints
34
- 4. **Config Building** → Create comprehensive configuration with all discovered actions
35
- 5. **YAML Validation** → \`stackone validate src/configs/<provider>.yaml\` → Ensure valid YAML syntax
36
- 6. **Coverage Validation** → \`check_all_endpoints()\` → Confirm endpoint coverage ≥80%
37
- 7. **Testing Phase** → \`run_connector_action()\` → Test EVERY action with real API calls
38
- 8. **Test Completion** → \`check_test_completion()\` → Verify 100% actions tested
39
- 9. **Security** → \`scramble_credentials()\` → Secure all sensitive data before storage
40
- 10. **Meta Feedback** → \`meta_feedback()\` → **MANDATORY** - Send feedback to third-party system for tracking
41
-
42
- **❌ Skip/Disorder = Incomplete Task / Professional Failure**
43
-
44
- ## 🎯 CORE PRINCIPLES
45
-
46
- - **MAXIMUM COVERAGE**: Discover and include ALL useful actions that provide customer value
47
- - **ACTION-FOCUSED**: Think: "what actions would developers commonly perform with this provider?"
48
- - **CUSTOMER VALUE**: Prioritize actions that solve real business problems
49
- - **MORE IS BETTER**: Default to comprehensiveness over minimalism
50
- - **PRACTICAL UTILITY**: Focus on actions developers actually use in production
51
-
52
- ## 🔍 AVAILABLE TOOLS
53
-
54
- ### Core Research
55
- - \`get_stackone_categories()\`, \`get_stackone_actions(category)\`
56
- - \`get_providers()\`, \`get_provider_coverage(provider)\`
57
- - \`vector_search(query, provider, k)\`
58
-
59
- ### Action Discovery
60
- - \`discover_actions(provider)\` - Autonomous AI agent (5-15 min)
61
- - \`get_provider_actions(provider)\` - Check S3 for indexed actions
62
-
63
- ### API Versioning
64
- - \`analyze_versioning(provider, endpoints)\` - Version validation (2-5 min)
65
-
66
- ### External Analysis
67
- - \`get_external_integrations(provider)\`
68
- - \`analyze_external_integration(integration, provider)\`
69
- - \`scan_external_repo(repo_url, search_terms)\`
70
-
71
- ### Testing
72
- - \`run_connector_action()\` - Test with real API calls
73
- - \`check_all_endpoints()\` - Validate ≥80% coverage
74
- - \`check_test_completion()\` - Verify 100% tested
75
-
76
- ### Security
77
- - \`scramble_credentials()\` - **REQUIRED** before storage
78
- - \`meta_feedback()\` - **MANDATORY** after completion
79
-
80
- ## 📚 DOCUMENTATION
81
-
82
- See \`src/configs/README.md\` for complete YAML structure, authentication patterns, actions, and field configs.
83
-
84
- ## 🔐 AUTHENTICATION
85
-
86
- Your CLI is authenticated via OAuth with StackOne.
87
-
88
- **Security:** Your access token is stored as \`STACKONE_FALCON_MCP_TOKEN\` environment variable. The \`.mcp.json\` file uses this variable reference - safe to commit!
89
-
90
- ## ⚡ QUICK START
91
-
92
- \`\`\`bash
93
- # Setup authentication (global)
94
- stackone agent setup --global
95
-
96
- # Setup local project
97
- stackone agent setup --local
98
-
99
- # Cleanup all credentials
100
- stackone agent cleanup
101
- \`\`\`
102
-
103
- ---
104
-
105
- *Authenticated with StackOne • Agent MCP Server*
106
- *For full workflow details, see the complete CLAUDE.md in repository*
107
- `}}const he=()=>`{
108
- "mcpServers": {
109
- "${I()}": {
110
- "url": "${P()}",
111
- "transport": {
112
- "type": "http",
113
- "headers": {
114
- "Authorization": "Bearer \${STACKONE_AGENT_MCP_TOKEN}"
115
- }
116
- }
117
- }
118
- }
119
- }`;var ge=class{async execute(){console.log(e.blue.bold(`🤖 StackOne Agent Setup - Local Project
120
- `));try{let t=process.cwd(),n=await i(`${t}/CLAUDE.md`).then(()=>!0).catch(()=>!1),r=await i(`${t}/.mcp.json`).then(()=>!0).catch(()=>!1);if(n||r){console.log(e.yellow(`⚠ Existing local setup detected:`)),n&&console.log(e.dim(` • CLAUDE.md found`)),r&&console.log(e.dim(` • .mcp.json found`)),console.log();let{proceed:t}=await v.prompt([{type:`confirm`,name:`proceed`,message:`Overwrite existing local configuration?`,default:!1}]);t||(console.log(e.dim(`
121
- Setup cancelled.`)),process.exit(0)),console.log()}let a=await N();if(!a||!ue(a)){console.log(e.yellow(`⚠ Not authenticated globally.`)),console.log(e.white(`Let's authenticate now...
122
- `));let t=_({input:h,output:g}),n=await new Promise(n=>{t.question(e.white(`Email: `),e=>{n(e.trim())})});t.close(),n||(console.error(e.red(`
123
- ✗ Email is required
124
- `)),process.exit(1));let r=await v.prompt([{type:`password`,name:`password`,message:`Password:`,mask:`*`}]);r.password||(console.error(e.red(`
125
- ✗ Password is required
126
- `)),process.exit(1)),console.log();let i=p(`Authenticating...`).start(),o=await V.login(n,r.password);(!o.success||!o.apiKey)&&(i.fail(`Authentication failed`),console.error(e.red(`\n✗ ${o.error||`Invalid credentials`}\n`)),console.log(e.dim(`Please check your email and password and try again.
127
- `)),process.exit(1)),i.succeed(`Login successful!`);let s=new Date(Date.now()+1440*60*1e3).toISOString();await M({apiKey:o.apiKey,userId:n,email:n,expiresAt:s,serverUrl:P()}),a={apiKey:o.apiKey,userId:n,email:n,expiresAt:s,serverUrl:P()},console.log()}let o=p(`Setting up local project configuration...`).start();o.text=`Creating CLAUDE.md...`;let s=await me();await l(`CLAUDE.md`,s),o.succeed(`Created CLAUDE.md`);let c=p(`Creating .mcp.json...`).start();await l(`.mcp.json`,he()),c.succeed(`Created .mcp.json (using STACKONE_AGENT_MCP_TOKEN env var)`);let u=p(`Installing to Claude (local)...`).start();try{let t=I();try{await W(`claude mcp remove ${t} 2>/dev/null`)}catch{}let n=`claude mcp add ${t} ${P()} --scope local --transport http --header "Authorization: Bearer ${a.apiKey}"`;await W(n),u.succeed(`Installed to Claude (local)`),console.log(e.green(`
128
- ✓ Local setup complete!`)),console.log(e.white(`Files created:`)),console.log(e.cyan(` - CLAUDE.md`)),console.log(e.cyan(` - .mcp.json`)),console.log(e.dim(`
129
- 💡 The stackone-agent-local MCP server is now available in this project`)),console.log(e.dim(`
130
- 💡 Tip: Run Claude without permission prompts:`),e.cyan(`claude --dangerously-skip-permissions`)),console.log(e.blue(`
131
- Verify:`),e.cyan(`claude mcp list`)),console.log(``)}catch(t){u.fail(`Installation to Claude failed`),t instanceof Error&&(console.error(e.red(`\n✗ ${t.message}\n`)),console.error(e.dim(`Configuration files created, but Claude setup failed.`)),console.error(e.dim(`You can manually add the server later with:`)),console.error(e.cyan(` claude mcp add ${I()} ${P()} --scope local --transport http --header "Authorization: Bearer ${a.apiKey}"`))),console.log(``)}process.exit(0)}catch(t){t instanceof Error?console.error(e.red(`\n✗ ${t.message}\n`)):console.error(e.red(`
132
- ✗ Unknown error occurred
133
- `)),process.exit(1)}}};const G=E(w(),`.stackone`),K={profiles:{}},q=()=>{if(!b(G))return K;try{let e=x(G,`utf-8`);return JSON.parse(e)}catch{return K}},_e=e=>{let t=q();return t.profiles[e]},J=e=>{let t=q();return!!t.profiles[e]},ve=(e,t)=>{let n=q();n.profiles[e]=t;let r=E(w());b(r)||te(r,{recursive:!0}),ne(G,JSON.stringify(n,null,2),`utf-8`)},ye=()=>{let e=q();return Object.keys(e.profiles)};var Y=class{static info(t){console.info(e.blue(`ℹ`),t)}static warn(t){console.info(e.yellow(`⚠`),t)}static error(t){console.info(e.red(`✗`),t)}static success(t){console.info(e.green(`✓`),t)}};const be=`https://api.stackone.com`,xe=`https://api.stackone-dev.com`,Se=`http://localhost:4000`;var Ce=class{async execute({environment:t}={}){let n=t?.toLowerCase()??`production`;try{let{label:t}=await v.prompt([{type:`input`,name:`label`,message:`Profile label:`,validate:e=>!e||e.trim().length===0?`The profile label is required`:e.includes(` `)?`The profile label cannot contain spaces`:!0}]);if(J(t)){let{overwrite:e}=await v.prompt([{type:`confirm`,name:`overwrite`,message:`Configuration profile with name "${t}" already exists. Do you want to overwrite it?`,default:!1}]);e||(Y.info(`Configuration profile initialization cancelled`),process.exit(0))}let r;if(n===`production`)r=be;else if(n===`staging`)r=xe;else{let{customUrl:e}=await v.prompt([{type:`input`,name:`customUrl`,message:`API URL:`,default:Se,validate:e=>{if(!e||e.trim().length===0)return`API URL is required`;try{return new URL(e),!0}catch{return`Please enter a valid URL`}}}]);r=e}let{apiKey:i}=await v.prompt([{type:`password`,name:`apiKey`,message:`API Key:`,validate:e=>!e||e.trim().length===0?`API Key is required`:!0}]),a={label:t,environment:n,apiUrl:r,apiKey:i};ve(t,a),console.info(e.green(`\n✓ Configuration profile "${t}" saved successfully`)),process.exit(0)}catch(e){e.isTtyError?Y.error(`Prompt couldn't be rendered in the current environment`):Y.error(`Failed to initialize configuration profile: ${e}`),process.exit(1)}}};const X=`https://api.stackone.com`;var we=class{async execute({profile:e,fileOrDir:t,apiUrl:n,apiKey:r}={}){if(!e&&!r&&(Y.error(`Please provide a profile or API key to use for pushing the connector.`),Y.info(`You can provide these using the --profile and --api-key options.`),Y.info(`Run "stackone init" to create a new configuration profile.`),process.exit(1)),e&&r&&(Y.error(`Please provide either a profile or an API key, not both.`),process.exit(1)),e&&n&&Y.warn(`Specifying --api-url with a profile won't have any effect. Using API url from profile.`),e&&!J(e)){Y.error(`Configuration profile "${e}" not found.`),Y.info(`Run "stackone init" to create a new configuration profile.`);let t=ye();Y.info(`Available profiles: ${t.join(`, `)||`none`}`),process.exit(1)}let i=e?_e(e):{apiUrl:n??X,apiKey:r};i||(Y.error(`Failed to load configuration profile "${e}".`),process.exit(1)),i.apiKey||(Y.error(`API key is missing. Please provide a valid API key in the profile or via the --api-key option.`),process.exit(1)),t||(Y.error(`File or directory path is required.`),process.exit(1));try{C(t)}catch{Y.error(`File or directory not found: ${t}`),process.exit(1)}let a=C(t),o=i.apiUrl??X;if(a.isDirectory()){let e=Z(t),n=0;e.length===0?(Y.error(`No .s1.yaml connector files found in the directory: ${t}.`),process.exit(1)):Y.info(`Found ${e.length} connector(s) file(s). Processing...`);for(let t of e){console.info(`
134
- `);let e=await this.uploadFile(t,o,i.apiKey);e&&n++}console.info(`
135
- `),Y.info(`Upload completed: ${n} of ${e.length} file(s) uploaded successfully.`),process.exit(n>0?0:1)}else t.endsWith(`.s1.yaml`)||(Y.error(`Only .s1.yaml files are supported for upload.`),process.exit(1));let s=await this.uploadFile(t,o,i.apiKey);process.exit(s?0:1)}async uploadFile(t,n,r){try{console.info(e.blue(`📤 Uploading ${T(t)}...`));let i=Te(t);if(!i)return!1;let a=T(t),o=new FormData,s=new Blob([i],{type:`application/x-yaml`});o.append(`file`,s,a);let c=Buffer.from(r).toString(`base64`),l=await fetch(`${n}/registry/connectors`,{method:`POST`,headers:{Authorization:`Basic ${c}`},body:o});if(!l.ok){let t=await l.text();return Y.error(`Upload failed: ${l.status} - ${l.statusText}`),t&&console.error(e.red(t)),!1}let u=await l.json();return console.info(e.green(`✓ Successfully uploaded ${a} with connector "${u.provider}@${u.version}"`)),!0}catch(e){return Y.error(`Failed to upload file: ${e}`),!1}}};const Te=t=>{let n=D(t),r=O(n),i=r?.errors??[];if(r.success){let e=t.split(`/`).pop()||t;return Y.success(`Connector ${e} is valid!`),n}else i.length>0?(Y.error(`Connector ${t} is not valid. Please fix the following errors:\n`),i.forEach(t=>{console.info(e.red(`- L${t.line}: ${t.message}`))})):Y.error(`Connector ${t} is not valid. Please check the file for errors.`)},Z=e=>{let t=[],n=S(e,{withFileTypes:!0});for(let r of n){let n=E(e,r.name);r.isDirectory()?t.push(...Z(n)):r.name.endsWith(`.s1.yaml`)&&t.push(n)}return t};var Ee=class{async execute(t){let{watchMode:n,fileOrDir:r}=t,i=p(`Watching for changes...`),a=()=>{console.clear(),console.info(e.yellow(`Watch mode enabled. Press "q" to quit.
136
- `))};if(r){try{C(r)}catch{Y.error(`File or directory not found: ${r}`),process.exit(1)}if(n){let{watch:t}=await import(`./esm-Wey0v-fi.js`),n=await import(`readline`),o=n.createInterface({input:process.stdin,output:process.stdout});process.stdin.setRawMode(!0),process.stdin.resume(),process.stdin.setEncoding(`utf8`);let s=()=>{console.info(e.grey(`
137
-
138
- Exiting watch mode...`)),c.close(),i.stop(),o.close(),process.exit(0)};a(),console.info(e.blue(`Running connector(s) validations...`)),await Q(r),i.start();let c=t(r||`.`,{ignored:/(^|[/\\])\../,persistent:!0});c.on(`change`,async t=>{a(),i.stop(),console.info(e.blue(`File change detected. Running connector(s) validations...`)),await Q(r),i.start()}),process.stdin.on(`data`,e=>{e.toString()===`q`&&s()}),process.on(`SIGINT`,()=>{s()})}else await Q(r),process.exit(0)}}};const Q=async e=>{let t=C(e);if(t.isDirectory()){let t=S(e),n=t.filter(e=>e.endsWith(`.s1.yaml`));if(n.length===0){Y.error(`No StackOne connectors found in directory: ${e}. Connector files need to have the extension .s1.yaml.\n`);return}let r=0,i=0;for(let t of n){let n=`${e}/${t}`,a=C(n);if(a.isFile()){let e=await $(n);e?r++:i++}}Y.info(`Validation completed: ${r} valid, ${i} invalid connectors.\n`)}else t.isFile()&&e.endsWith(`s1.yaml`)?await $(e):Y.error(`No StackOne connector found: ${e}. Connector files need to have the extension .s1.yaml.\n`)},$=async t=>{let n=D(t),r=O(n),i=r?.errors??[];if(r.success){let e=t.split(`/`).pop()||t;return Y.success(`Connector ${e} is valid!\n`),!0}else if(i.length>0)return Y.error(`Connector ${t} is not valid. Please fix the following errors:\n`),i.forEach(t=>{console.info(e.red(`- L${t.line}: ${t.message}`))}),console.info(`
139
- `),!1;else return Y.error(`Connector ${t} is not valid. Please check the file for errors.`),!1},De=()=>{try{let e=ie(import.meta.url),t=re(e),n=E(t,`..`,`package.json`),r=JSON.parse(x(n,`utf8`));return r.version}catch{return`unknown`}};var Oe=class{constructor(e=new n,t=De()){this.program=e,this.version=t,this.setupProgram(),this.registerCommands()}setupProgram(){this.program.name(`stackone`).description(`StackOne CLI`).version(this.version,`-v, --version`)}registerCommands(){let n=new Ce,r=new we,i=new Ee,a=new de,o=new ge,s=new z;this.program.configureOutput({writeOut:e=>process.stdout.write(e),writeErr:e=>process.stderr.write(e),outputError:(t,n)=>{n(e.red(t))}}),this.program.command(`init`).option(`-e, --env <environment>`,`Specify the environment for the configuration profile`).description(`Initialize & create a StackOne CLI configuration profile`).action(e=>{n.execute({environment:e.env})}),this.program.command(`push`).option(`-p, --profile <label>`,`Configuration profile to use`).option(`--api-url <api url>`,`API URL`).option(`--api-key <api key>`,`API Key`).addArgument(new t(`<path>`,`Connector file or directory to push`)).description(`Push a connector to the StackOne registry`).action((e,t)=>{r.execute({profile:t.profile,fileOrDir:e,apiUrl:t.apiUrl,apiKey:t.apiKey})}),this.program.command(`validate`).option(`-w, --watch`,`Run in watch mode`).addArgument(new t(`<path>`,`Connector file or directory with connectors to validate`)).description(`Validate a StackOne connector`).action((e,t)=>{i.execute({watchMode:t.watch,fileOrDir:e})}),this.program.command(`version`).description(`Show version information`).action(()=>{console.info(`${e.greenBright(`StackOne`)} ${e.grey(`CLI`)} ${e.whiteBright(this.version)}`),process.exit(0)});let c=this.program.command(`agent`).description(`StackOne agent commands`);c.command(`setup`).option(`-g, --global`,`Setup global configuration`).option(`-l, --local`,`Setup local project configuration`).description(`Setup StackOne agent (global or local)`).action(e=>{e.global?a.execute():o.execute()}),c.command(`cleanup`).description(`Remove all API keys and credentials from configurations`).action(()=>{s.execute()})}run(){this.program.parse(process.argv)}};export{Oe as CLI};
@@ -1,139 +0,0 @@
1
- const e=require(`./chunk-CUT6urMc.cjs`),t=e.__toESM(require(`chalk`)),n=e.__toESM(require(`commander`)),r=e.__toESM(require(`node:child_process`)),i=e.__toESM(require(`node:fs/promises`)),a=e.__toESM(require(`node:path`)),o=e.__toESM(require(`node:util`)),s=e.__toESM(require(`ora`)),c=e.__toESM(require(`node:fs`)),l=e.__toESM(require(`node:os`)),u=e.__toESM(require(`node:process`)),d=e.__toESM(require(`node:readline`)),f=e.__toESM(require(`inquirer`)),p=e.__toESM(require(`node:url`)),m=e.__toESM(require(`fs`)),h=e.__toESM(require(`os`)),g=e.__toESM(require(`path`)),_=e.__toESM(require(`@stackone/connect-sdk`)),ee=e.__toESM(require(`url`)),v=(0,a.join)((0,l.homedir)(),`.stackone`),y=(0,a.join)(v,`agent-config.json`),te=`stackone-agent-global`,b=`stackone-agent-local`,x=process.env.STACKONE_AGENT_SERVER_URL||`https://mcp-internal-falcon.stackonehq.workers.dev/mcp`;async function S(){let e=(0,c.existsSync)(v);e||await(0,i.mkdir)(v,{recursive:!0,mode:448})}async function C(e){await S(),await(0,i.writeFile)(y,JSON.stringify(e,null,2)),await(0,i.chmod)(y,384)}async function w(){try{let e=await(0,i.readFile)(y,`utf-8`);return JSON.parse(e)}catch{return null}}async function ne(){try{await(0,i.rm)(y,{force:!0})}catch{}}function T(){return x}function E(){return te}function D(){return b}function O(e){return new Date(e)<=new Date}function k(e){return!!e?.apiKey&&!O(e.expiresAt)}const A=(0,o.promisify)(r.exec);async function re(e){try{let t=await(0,i.readFile)(e,`utf-8`),n=JSON.parse(t),r=!1;if(n.mcpServers&&typeof n.mcpServers==`object`){for(let[e,t]of Object.entries(n.mcpServers))if(t&&typeof t==`object`&&`transport`in t){let e=t.transport;if(e&&typeof e==`object`&&`headers`in e){let t=e.headers;if(t&&typeof t==`object`&&`Authorization`in t){let e=t.Authorization;typeof e==`string`&&e.startsWith(`Bearer `)&&!e.includes("${")&&(t.Authorization="Bearer ${STACKONE_AGENT_MCP_TOKEN}",r=!0)}}}}return r&&await(0,i.writeFile)(e,JSON.stringify(n,null,2)),r}catch{return!1}}var ie=class{async execute(){console.log(t.default.blue.bold(`🧹 StackOne Agent Cleanup
2
- `));let e={configsCleaned:0};try{let n=(0,s.default)(`Removing stored configuration...`).start();await ne(),n.succeed(`Removed ~/.stackone/agent-config.json`);let r=(0,s.default)(`Removing MCP servers from Claude...`).start(),i=0;try{let e=E();await A(`claude mcp remove ${e} 2>/dev/null`),i++}catch{}try{let e=D();await A(`claude mcp remove ${e} 2>/dev/null`),i++}catch{}i>0?r.succeed(`Removed ${i} MCP server(s) from Claude`):r.warn(`No MCP servers found in Claude (already removed or not configured)`);let o=(0,s.default)(`Checking for .mcp.json file...`).start(),c=process.cwd(),l=(0,a.join)(c,`.mcp.json`);try{let t=await re(l);t?(e.configsCleaned=1,o.succeed(`Cleaned .mcp.json (replaced tokens with environment variables)`)):o.info(`.mcp.json already uses environment variables or not found`)}catch{o.info(`.mcp.json not found (already cleaned or not created)`)}console.log(t.default.green(`
3
- ✓ Cleanup complete!`)),console.log(t.default.white(`
4
- What was removed:`)),console.log(t.default.cyan(` • ~/.stackone/agent-config.json`)),console.log(t.default.cyan(` • MCP server from Claude configuration`)),e.configsCleaned>0&&console.log(t.default.cyan(` • Hardcoded credentials from .mcp.json`)),console.log(t.default.dim(`
5
- To authenticate again:`)),console.log(t.default.cyan(` stackone agent setup --global`),t.default.dim(`(for global setup)`)),console.log(t.default.cyan(` stackone agent setup --local`),t.default.dim(`(for local project setup)`)),console.log(``),process.exit(0)}catch(e){e instanceof Error?console.error(t.default.red(`\n✗ ${e.message}\n`)):console.error(t.default.red(`
6
- ✗ Unknown error occurred
7
- `)),process.exit(1)}}},ae=class{baseUrl;constructor(e){this.baseUrl=e||process.env.AUTH_SERVER_URL||`https://idp-api.stackone.com`}async login(e,t){try{let n=`${this.baseUrl}/auth/sign-in/mcp`,r=await fetch(n,{method:`POST`,headers:{"Content-Type":`application/json`},body:JSON.stringify({email:e,password:t})});if(!r.ok){let e=await r.text(),t={};try{t=JSON.parse(e)}catch{}return{success:!1,error:t.error||`HTTP ${r.status}: ${r.statusText}`}}let i=await r.text(),a=JSON.parse(i);return a.apiKey?{success:!0,apiKey:a.apiKey}:{success:!1,error:`Invalid response format`}}catch(e){return{success:!1,error:e instanceof Error?e.message:`Network error occurred`}}}async validateKey(e){try{let t=`${this.baseUrl}/api-key/verify`,n=await fetch(t,{method:`POST`,headers:{"Content-Type":`application/json`},body:JSON.stringify({key:e})});if(!n.ok){let e=await n.text(),t={};try{t=JSON.parse(e)}catch{}return{valid:!1,error:t.error||`HTTP ${n.status}: ${n.statusText}`}}let r=await n.text();return JSON.parse(r)}catch(e){return{valid:!1,error:e instanceof Error?e.message:`Network error occurred`}}}};const j=new ae,M=(0,o.promisify)(r.exec);function N(e){let t=new Date(e),n=new Date,r=t.getTime()-n.getTime(),i=Math.floor(r/(3600*1e3)),a=t.toLocaleTimeString(`en-US`,{hour:`numeric`,minute:`2-digit`,hour12:!0}),o=t.toDateString()===n.toDateString()?`Today`:t.toDateString()===new Date(n.getTime()+864e5).toDateString()?`Tomorrow`:t.toLocaleDateString(`en-US`,{month:`short`,day:`numeric`});return`${o} at ${a} (${i} hours from now)`}var P=class{async execute(){console.log(t.default.blue.bold(`🤖 StackOne Agent Setup - Global Configuration
8
- `));try{let e=process.cwd(),n=await(0,i.access)(`${e}/CLAUDE.md`).then(()=>!0).catch(()=>!1),r=await(0,i.access)(`${e}/.mcp.json`).then(()=>!0).catch(()=>!1);if(n||r){console.log(t.default.yellow(`⚠ Existing local setup detected:`)),n&&console.log(t.default.dim(` • CLAUDE.md found in current directory`)),r&&console.log(t.default.dim(` • .mcp.json found in current directory`)),console.log();let{proceed:e}=await f.default.prompt([{type:`confirm`,name:`proceed`,message:`Re-authenticate globally despite existing local setup?`,default:!0}]);e||(console.log(t.default.dim(`
9
- Setup cancelled.`)),process.exit(0)),console.log()}let a=await w(),o;if(a?.email){console.log(t.default.dim(`Last used email: ${a.email}\n`));let{useExisting:e}=await f.default.prompt([{type:`confirm`,name:`useExisting`,message:`Sign in with this email?`,default:!0}]);if(e)o=a.email;else{let e=(0,d.createInterface)({input:u.stdin,output:u.stdout});o=await new Promise(n=>{e.question(t.default.white(`Email: `),e=>{n(e.trim())})}),e.close(),o||(console.error(t.default.red(`
10
- ✗ Email is required
11
- `)),process.exit(1))}}else{let e=(0,d.createInterface)({input:u.stdin,output:u.stdout});o=await new Promise(n=>{e.question(t.default.white(`Email: `),e=>{n(e.trim())})}),e.close(),o||(console.error(t.default.red(`
12
- ✗ Email is required
13
- `)),process.exit(1))}let c=await f.default.prompt([{type:`password`,name:`password`,message:`Password:`,mask:`*`}]);c.password||(console.error(t.default.red(`
14
- ✗ Password is required
15
- `)),process.exit(1)),console.log();let l=(0,s.default)(`Authenticating...`).start(),p=await j.login(o,c.password);(!p.success||!p.apiKey)&&(l.fail(`Authentication failed`),console.error(t.default.red(`\n✗ ${p.error||`Invalid credentials`}\n`)),console.log(t.default.dim(`Please check your email and password and try again.
16
- `)),process.exit(1)),l.succeed(`Login successful!`);let m=new Date(Date.now()+1440*60*1e3).toISOString();await C({apiKey:p.apiKey,userId:o,email:o,expiresAt:m,serverUrl:T()}),console.log(t.default.green.bold(`
17
- ✓ API key saved to ~/.stackone/agent-config.json`)),console.log(t.default.white(`✓ Key expires at`),t.default.cyan(N(m)));let h=(0,s.default)(`Setting up global MCP configuration...`).start();try{let e=E(),n=T();try{await M(`claude mcp remove ${e} 2>/dev/null`)}catch{}let r=`claude mcp add ${e} ${n} --scope user --transport http --header "Authorization: Bearer ${p.apiKey}"`;await M(r),h.succeed(`Installed to Claude (global)`),console.log(t.default.green(`
18
- ✓ Global setup complete!`)),console.log(t.default.white(`Config location:`),t.default.cyan(`~/.claude.json`)),console.log(t.default.blue(`
19
- Verify:`),t.default.cyan(`claude mcp list`)),console.log(t.default.dim(`
20
- 💡 Tip: Run Claude without permission prompts:`),t.default.cyan(`claude --dangerously-skip-permissions`)),console.log(t.default.dim(`
21
- To setup for a specific project, run:`),t.default.cyan(`stackone agent setup --local`)),console.log(``)}catch(e){h.fail(`Installation to Claude failed`),e instanceof Error&&(console.error(t.default.red(`\n✗ ${e.message}\n`)),console.error(t.default.dim(`You can manually add the server later with:`)),console.error(t.default.cyan(` claude mcp add ${E()} ${T()} --scope user --transport http --header "Authorization: Bearer ${p.apiKey}"`)))}process.exit(0)}catch(e){e instanceof Error?console.error(t.default.red(`\n✗ ${e.message}\n`)):console.error(t.default.red(`
22
- ✗ Unknown error occurred
23
- `)),process.exit(1)}}};const F=(0,o.promisify)(r.exec),I=(0,p.fileURLToPath)(require(`url`).pathToFileURL(__filename).href),L=(0,a.dirname)(I);async function R(){let e=(0,a.join)(L,`..`,`..`,`..`,`CLAUDE_TEMPLATE.md`);try{return await(0,i.readFile)(e,`utf-8`)}catch{return`# 🚀 STACKONE API CONFIG BUILDER
24
-
25
- This document provides the complete methodology for building StackOne API configurations with Claude. Follow this strict workflow to ensure comprehensive, tested, and customer-valuable integrations.
26
-
27
- ## 🔴 CRITICAL WORKFLOW (STRICT ORDER)
28
-
29
- When asked to build StackOne API configurations, you MUST follow this exact sequence:
30
-
31
- 1. **Research Phase (PARALLEL EXECUTION)** → Launch \`discover_actions\` subagent for action discovery + main agent for auth/docs/external repos
32
- 2. **Synchronization** → Collect and integrate subagent results
33
- 3. **Version Validation** → \`analyze_versioning()\` → Detect and resolve API version conflicts for discovered endpoints
34
- 4. **Config Building** → Create comprehensive configuration with all discovered actions
35
- 5. **YAML Validation** → \`stackone validate src/configs/<provider>.yaml\` → Ensure valid YAML syntax
36
- 6. **Coverage Validation** → \`check_all_endpoints()\` → Confirm endpoint coverage ≥80%
37
- 7. **Testing Phase** → \`run_connector_action()\` → Test EVERY action with real API calls
38
- 8. **Test Completion** → \`check_test_completion()\` → Verify 100% actions tested
39
- 9. **Security** → \`scramble_credentials()\` → Secure all sensitive data before storage
40
- 10. **Meta Feedback** → \`meta_feedback()\` → **MANDATORY** - Send feedback to third-party system for tracking
41
-
42
- **❌ Skip/Disorder = Incomplete Task / Professional Failure**
43
-
44
- ## 🎯 CORE PRINCIPLES
45
-
46
- - **MAXIMUM COVERAGE**: Discover and include ALL useful actions that provide customer value
47
- - **ACTION-FOCUSED**: Think: "what actions would developers commonly perform with this provider?"
48
- - **CUSTOMER VALUE**: Prioritize actions that solve real business problems
49
- - **MORE IS BETTER**: Default to comprehensiveness over minimalism
50
- - **PRACTICAL UTILITY**: Focus on actions developers actually use in production
51
-
52
- ## 🔍 AVAILABLE TOOLS
53
-
54
- ### Core Research
55
- - \`get_stackone_categories()\`, \`get_stackone_actions(category)\`
56
- - \`get_providers()\`, \`get_provider_coverage(provider)\`
57
- - \`vector_search(query, provider, k)\`
58
-
59
- ### Action Discovery
60
- - \`discover_actions(provider)\` - Autonomous AI agent (5-15 min)
61
- - \`get_provider_actions(provider)\` - Check S3 for indexed actions
62
-
63
- ### API Versioning
64
- - \`analyze_versioning(provider, endpoints)\` - Version validation (2-5 min)
65
-
66
- ### External Analysis
67
- - \`get_external_integrations(provider)\`
68
- - \`analyze_external_integration(integration, provider)\`
69
- - \`scan_external_repo(repo_url, search_terms)\`
70
-
71
- ### Testing
72
- - \`run_connector_action()\` - Test with real API calls
73
- - \`check_all_endpoints()\` - Validate ≥80% coverage
74
- - \`check_test_completion()\` - Verify 100% tested
75
-
76
- ### Security
77
- - \`scramble_credentials()\` - **REQUIRED** before storage
78
- - \`meta_feedback()\` - **MANDATORY** after completion
79
-
80
- ## 📚 DOCUMENTATION
81
-
82
- See \`src/configs/README.md\` for complete YAML structure, authentication patterns, actions, and field configs.
83
-
84
- ## 🔐 AUTHENTICATION
85
-
86
- Your CLI is authenticated via OAuth with StackOne.
87
-
88
- **Security:** Your access token is stored as \`STACKONE_FALCON_MCP_TOKEN\` environment variable. The \`.mcp.json\` file uses this variable reference - safe to commit!
89
-
90
- ## ⚡ QUICK START
91
-
92
- \`\`\`bash
93
- # Setup authentication (global)
94
- stackone agent setup --global
95
-
96
- # Setup local project
97
- stackone agent setup --local
98
-
99
- # Cleanup all credentials
100
- stackone agent cleanup
101
- \`\`\`
102
-
103
- ---
104
-
105
- *Authenticated with StackOne • Agent MCP Server*
106
- *For full workflow details, see the complete CLAUDE.md in repository*
107
- `}}const z=()=>`{
108
- "mcpServers": {
109
- "${D()}": {
110
- "url": "${T()}",
111
- "transport": {
112
- "type": "http",
113
- "headers": {
114
- "Authorization": "Bearer \${STACKONE_AGENT_MCP_TOKEN}"
115
- }
116
- }
117
- }
118
- }
119
- }`;var B=class{async execute(){console.log(t.default.blue.bold(`🤖 StackOne Agent Setup - Local Project
120
- `));try{let e=process.cwd(),n=await(0,i.access)(`${e}/CLAUDE.md`).then(()=>!0).catch(()=>!1),r=await(0,i.access)(`${e}/.mcp.json`).then(()=>!0).catch(()=>!1);if(n||r){console.log(t.default.yellow(`⚠ Existing local setup detected:`)),n&&console.log(t.default.dim(` • CLAUDE.md found`)),r&&console.log(t.default.dim(` • .mcp.json found`)),console.log();let{proceed:e}=await f.default.prompt([{type:`confirm`,name:`proceed`,message:`Overwrite existing local configuration?`,default:!1}]);e||(console.log(t.default.dim(`
121
- Setup cancelled.`)),process.exit(0)),console.log()}let a=await w();if(!a||!k(a)){console.log(t.default.yellow(`⚠ Not authenticated globally.`)),console.log(t.default.white(`Let's authenticate now...
122
- `));let e=(0,d.createInterface)({input:u.stdin,output:u.stdout}),n=await new Promise(n=>{e.question(t.default.white(`Email: `),e=>{n(e.trim())})});e.close(),n||(console.error(t.default.red(`
123
- ✗ Email is required
124
- `)),process.exit(1));let r=await f.default.prompt([{type:`password`,name:`password`,message:`Password:`,mask:`*`}]);r.password||(console.error(t.default.red(`
125
- ✗ Password is required
126
- `)),process.exit(1)),console.log();let i=(0,s.default)(`Authenticating...`).start(),o=await j.login(n,r.password);(!o.success||!o.apiKey)&&(i.fail(`Authentication failed`),console.error(t.default.red(`\n✗ ${o.error||`Invalid credentials`}\n`)),console.log(t.default.dim(`Please check your email and password and try again.
127
- `)),process.exit(1)),i.succeed(`Login successful!`);let c=new Date(Date.now()+1440*60*1e3).toISOString();await C({apiKey:o.apiKey,userId:n,email:n,expiresAt:c,serverUrl:T()}),a={apiKey:o.apiKey,userId:n,email:n,expiresAt:c,serverUrl:T()},console.log()}let o=(0,s.default)(`Setting up local project configuration...`).start();o.text=`Creating CLAUDE.md...`;let c=await R();await(0,i.writeFile)(`CLAUDE.md`,c),o.succeed(`Created CLAUDE.md`);let l=(0,s.default)(`Creating .mcp.json...`).start();await(0,i.writeFile)(`.mcp.json`,z()),l.succeed(`Created .mcp.json (using STACKONE_AGENT_MCP_TOKEN env var)`);let p=(0,s.default)(`Installing to Claude (local)...`).start();try{let e=D();try{await F(`claude mcp remove ${e} 2>/dev/null`)}catch{}let n=`claude mcp add ${e} ${T()} --scope local --transport http --header "Authorization: Bearer ${a.apiKey}"`;await F(n),p.succeed(`Installed to Claude (local)`),console.log(t.default.green(`
128
- ✓ Local setup complete!`)),console.log(t.default.white(`Files created:`)),console.log(t.default.cyan(` - CLAUDE.md`)),console.log(t.default.cyan(` - .mcp.json`)),console.log(t.default.dim(`
129
- 💡 The stackone-agent-local MCP server is now available in this project`)),console.log(t.default.dim(`
130
- 💡 Tip: Run Claude without permission prompts:`),t.default.cyan(`claude --dangerously-skip-permissions`)),console.log(t.default.blue(`
131
- Verify:`),t.default.cyan(`claude mcp list`)),console.log(``)}catch(e){p.fail(`Installation to Claude failed`),e instanceof Error&&(console.error(t.default.red(`\n✗ ${e.message}\n`)),console.error(t.default.dim(`Configuration files created, but Claude setup failed.`)),console.error(t.default.dim(`You can manually add the server later with:`)),console.error(t.default.cyan(` claude mcp add ${D()} ${T()} --scope local --transport http --header "Authorization: Bearer ${a.apiKey}"`))),console.log(``)}process.exit(0)}catch(e){e instanceof Error?console.error(t.default.red(`\n✗ ${e.message}\n`)):console.error(t.default.red(`
132
- ✗ Unknown error occurred
133
- `)),process.exit(1)}}};const V=(0,g.join)((0,h.homedir)(),`.stackone`),H={profiles:{}},U=()=>{if(!(0,m.existsSync)(V))return H;try{let e=(0,m.readFileSync)(V,`utf-8`);return JSON.parse(e)}catch{return H}},W=e=>{let t=U();return t.profiles[e]},G=e=>{let t=U();return!!t.profiles[e]},K=(e,t)=>{let n=U();n.profiles[e]=t;let r=(0,g.join)((0,h.homedir)());(0,m.existsSync)(r)||(0,m.mkdirSync)(r,{recursive:!0}),(0,m.writeFileSync)(V,JSON.stringify(n,null,2),`utf-8`)},q=()=>{let e=U();return Object.keys(e.profiles)};var J=class{static info(e){console.info(t.default.blue(`ℹ`),e)}static warn(e){console.info(t.default.yellow(`⚠`),e)}static error(e){console.info(t.default.red(`✗`),e)}static success(e){console.info(t.default.green(`✓`),e)}};const oe=`https://api.stackone.com`,se=`https://api.stackone-dev.com`,ce=`http://localhost:4000`;var le=class{async execute({environment:e}={}){let n=e?.toLowerCase()??`production`;try{let{label:e}=await f.default.prompt([{type:`input`,name:`label`,message:`Profile label:`,validate:e=>!e||e.trim().length===0?`The profile label is required`:e.includes(` `)?`The profile label cannot contain spaces`:!0}]);if(G(e)){let{overwrite:t}=await f.default.prompt([{type:`confirm`,name:`overwrite`,message:`Configuration profile with name "${e}" already exists. Do you want to overwrite it?`,default:!1}]);t||(J.info(`Configuration profile initialization cancelled`),process.exit(0))}let r;if(n===`production`)r=oe;else if(n===`staging`)r=se;else{let{customUrl:e}=await f.default.prompt([{type:`input`,name:`customUrl`,message:`API URL:`,default:ce,validate:e=>{if(!e||e.trim().length===0)return`API URL is required`;try{return new URL(e),!0}catch{return`Please enter a valid URL`}}}]);r=e}let{apiKey:i}=await f.default.prompt([{type:`password`,name:`apiKey`,message:`API Key:`,validate:e=>!e||e.trim().length===0?`API Key is required`:!0}]),a={label:e,environment:n,apiUrl:r,apiKey:i};K(e,a),console.info(t.default.green(`\n✓ Configuration profile "${e}" saved successfully`)),process.exit(0)}catch(e){e.isTtyError?J.error(`Prompt couldn't be rendered in the current environment`):J.error(`Failed to initialize configuration profile: ${e}`),process.exit(1)}}};const Y=`https://api.stackone.com`;var X=class{async execute({profile:e,fileOrDir:t,apiUrl:n,apiKey:r}={}){if(!e&&!r&&(J.error(`Please provide a profile or API key to use for pushing the connector.`),J.info(`You can provide these using the --profile and --api-key options.`),J.info(`Run "stackone init" to create a new configuration profile.`),process.exit(1)),e&&r&&(J.error(`Please provide either a profile or an API key, not both.`),process.exit(1)),e&&n&&J.warn(`Specifying --api-url with a profile won't have any effect. Using API url from profile.`),e&&!G(e)){J.error(`Configuration profile "${e}" not found.`),J.info(`Run "stackone init" to create a new configuration profile.`);let t=q();J.info(`Available profiles: ${t.join(`, `)||`none`}`),process.exit(1)}let i=e?W(e):{apiUrl:n??Y,apiKey:r};i||(J.error(`Failed to load configuration profile "${e}".`),process.exit(1)),i.apiKey||(J.error(`API key is missing. Please provide a valid API key in the profile or via the --api-key option.`),process.exit(1)),t||(J.error(`File or directory path is required.`),process.exit(1));try{(0,m.statSync)(t)}catch{J.error(`File or directory not found: ${t}`),process.exit(1)}let a=(0,m.statSync)(t),o=i.apiUrl??Y;if(a.isDirectory()){let e=Z(t),n=0;e.length===0?(J.error(`No .s1.yaml connector files found in the directory: ${t}.`),process.exit(1)):J.info(`Found ${e.length} connector(s) file(s). Processing...`);for(let t of e){console.info(`
134
- `);let e=await this.uploadFile(t,o,i.apiKey);e&&n++}console.info(`
135
- `),J.info(`Upload completed: ${n} of ${e.length} file(s) uploaded successfully.`),process.exit(n>0?0:1)}else t.endsWith(`.s1.yaml`)||(J.error(`Only .s1.yaml files are supported for upload.`),process.exit(1));let s=await this.uploadFile(t,o,i.apiKey);process.exit(s?0:1)}async uploadFile(e,n,r){try{console.info(t.default.blue(`📤 Uploading ${(0,g.basename)(e)}...`));let i=ue(e);if(!i)return!1;let a=(0,g.basename)(e),o=new FormData,s=new Blob([i],{type:`application/x-yaml`});o.append(`file`,s,a);let c=Buffer.from(r).toString(`base64`),l=await fetch(`${n}/registry/connectors`,{method:`POST`,headers:{Authorization:`Basic ${c}`},body:o});if(!l.ok){let e=await l.text();return J.error(`Upload failed: ${l.status} - ${l.statusText}`),e&&console.error(t.default.red(e)),!1}let u=await l.json();return console.info(t.default.green(`✓ Successfully uploaded ${a} with connector "${u.provider}@${u.version}"`)),!0}catch(e){return J.error(`Failed to upload file: ${e}`),!1}}};const ue=e=>{let n=(0,_.loadConnector)(e),r=(0,_.validateYamlConnector)(n),i=r?.errors??[];if(r.success){let t=e.split(`/`).pop()||e;return J.success(`Connector ${t} is valid!`),n}else i.length>0?(J.error(`Connector ${e} is not valid. Please fix the following errors:\n`),i.forEach(e=>{console.info(t.default.red(`- L${e.line}: ${e.message}`))})):J.error(`Connector ${e} is not valid. Please check the file for errors.`)},Z=e=>{let t=[],n=(0,m.readdirSync)(e,{withFileTypes:!0});for(let r of n){let n=(0,g.join)(e,r.name);r.isDirectory()?t.push(...Z(n)):r.name.endsWith(`.s1.yaml`)&&t.push(n)}return t};var de=class{async execute(e){let{watchMode:n,fileOrDir:r}=e,i=(0,s.default)(`Watching for changes...`),a=()=>{console.clear(),console.info(t.default.yellow(`Watch mode enabled. Press "q" to quit.
136
- `))};if(r){try{(0,m.statSync)(r)}catch{J.error(`File or directory not found: ${r}`),process.exit(1)}if(n){let{watch:e}=await Promise.resolve().then(()=>require(`./esm-BSZWAx0q.cjs`)),n=await import(`readline`),o=n.createInterface({input:process.stdin,output:process.stdout});process.stdin.setRawMode(!0),process.stdin.resume(),process.stdin.setEncoding(`utf8`);let s=()=>{console.info(t.default.grey(`
137
-
138
- Exiting watch mode...`)),c.close(),i.stop(),o.close(),process.exit(0)};a(),console.info(t.default.blue(`Running connector(s) validations...`)),await Q(r),i.start();let c=e(r||`.`,{ignored:/(^|[/\\])\../,persistent:!0});c.on(`change`,async e=>{a(),i.stop(),console.info(t.default.blue(`File change detected. Running connector(s) validations...`)),await Q(r),i.start()}),process.stdin.on(`data`,e=>{e.toString()===`q`&&s()}),process.on(`SIGINT`,()=>{s()})}else await Q(r),process.exit(0)}}};const Q=async e=>{let t=(0,m.statSync)(e);if(t.isDirectory()){let t=(0,m.readdirSync)(e),n=t.filter(e=>e.endsWith(`.s1.yaml`));if(n.length===0){J.error(`No StackOne connectors found in directory: ${e}. Connector files need to have the extension .s1.yaml.\n`);return}let r=0,i=0;for(let t of n){let n=`${e}/${t}`,a=(0,m.statSync)(n);if(a.isFile()){let e=await $(n);e?r++:i++}}J.info(`Validation completed: ${r} valid, ${i} invalid connectors.\n`)}else t.isFile()&&e.endsWith(`s1.yaml`)?await $(e):J.error(`No StackOne connector found: ${e}. Connector files need to have the extension .s1.yaml.\n`)},$=async e=>{let n=(0,_.loadConnector)(e),r=(0,_.validateYamlConnector)(n),i=r?.errors??[];if(r.success){let t=e.split(`/`).pop()||e;return J.success(`Connector ${t} is valid!\n`),!0}else if(i.length>0)return J.error(`Connector ${e} is not valid. Please fix the following errors:\n`),i.forEach(e=>{console.info(t.default.red(`- L${e.line}: ${e.message}`))}),console.info(`
139
- `),!1;else return J.error(`Connector ${e} is not valid. Please check the file for errors.`),!1},fe=()=>{try{let e=(0,ee.fileURLToPath)(require(`url`).pathToFileURL(__filename).href),t=(0,g.dirname)(e),n=(0,g.join)(t,`..`,`package.json`),r=JSON.parse((0,m.readFileSync)(n,`utf8`));return r.version}catch{return`unknown`}};var pe=class{constructor(e=new n.Command,t=fe()){this.program=e,this.version=t,this.setupProgram(),this.registerCommands()}setupProgram(){this.program.name(`stackone`).description(`StackOne CLI`).version(this.version,`-v, --version`)}registerCommands(){let e=new le,r=new X,i=new de,a=new P,o=new B,s=new ie;this.program.configureOutput({writeOut:e=>process.stdout.write(e),writeErr:e=>process.stderr.write(e),outputError:(e,n)=>{n(t.default.red(e))}}),this.program.command(`init`).option(`-e, --env <environment>`,`Specify the environment for the configuration profile`).description(`Initialize & create a StackOne CLI configuration profile`).action(t=>{e.execute({environment:t.env})}),this.program.command(`push`).option(`-p, --profile <label>`,`Configuration profile to use`).option(`--api-url <api url>`,`API URL`).option(`--api-key <api key>`,`API Key`).addArgument(new n.Argument(`<path>`,`Connector file or directory to push`)).description(`Push a connector to the StackOne registry`).action((e,t)=>{r.execute({profile:t.profile,fileOrDir:e,apiUrl:t.apiUrl,apiKey:t.apiKey})}),this.program.command(`validate`).option(`-w, --watch`,`Run in watch mode`).addArgument(new n.Argument(`<path>`,`Connector file or directory with connectors to validate`)).description(`Validate a StackOne connector`).action((e,t)=>{i.execute({watchMode:t.watch,fileOrDir:e})}),this.program.command(`version`).description(`Show version information`).action(()=>{console.info(`${t.default.greenBright(`StackOne`)} ${t.default.grey(`CLI`)} ${t.default.whiteBright(this.version)}`),process.exit(0)});let c=this.program.command(`agent`).description(`StackOne agent commands`);c.command(`setup`).option(`-g, --global`,`Setup global configuration`).option(`-l, --local`,`Setup local project configuration`).description(`Setup StackOne agent (global or local)`).action(e=>{e.global?a.execute():o.execute()}),c.command(`cleanup`).description(`Remove all API keys and credentials from configurations`).action(()=>{s.execute()})}run(){this.program.parse(process.argv)}};Object.defineProperty(exports,`CLI`,{enumerable:!0,get:function(){return pe}});