* feat(help): Add CLI help command Fixes #136 * chore: remove binary * feat(mcp): Add TCP transport Fixes #126 * feat(io): Migrate pkg/mcp to use Medium abstraction Fixes #103 * chore(io): Migrate internal/cmd/docs/* to Medium abstraction Fixes #113 * chore(io): Migrate internal/cmd/dev/* to Medium abstraction Fixes #114 * chore(io): Migrate internal/cmd/setup/* to Medium abstraction * chore(io): Complete migration of internal/cmd/dev/* to Medium abstraction * chore(io): Migrate internal/cmd/sdk, pkgcmd, and workspace to Medium abstraction * style: fix formatting in internal/variants Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * refactor(io): simplify local Medium implementation Rewrote to match the simpler TypeScript pattern: - path() sanitizes and returns string directly - Each method calls path() once - No complex symlink validation - Less code, less attack surface Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * test(mcp): update sandboxing tests for simplified Medium The simplified io/local.Medium implementation: - Sanitizes .. to . (no error, path is cleaned) - Allows absolute paths through (caller validates if needed) - Follows symlinks (no traversal blocking) Update tests to match this simplified behavior. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * fix(updater): resolve PkgVersion duplicate declaration Remove var PkgVersion from updater.go since go generate creates const PkgVersion in version.go. Track version.go in git to ensure builds work without running go generate first. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
78 lines
2 KiB
Go
78 lines
2 KiB
Go
package sdk
|
|
|
|
import (
|
|
"fmt"
|
|
"path/filepath"
|
|
"strings"
|
|
|
|
coreio "github.com/host-uk/core/pkg/io"
|
|
)
|
|
|
|
// commonSpecPaths are checked in order when no spec is configured.
|
|
var commonSpecPaths = []string{
|
|
"api/openapi.yaml",
|
|
"api/openapi.json",
|
|
"openapi.yaml",
|
|
"openapi.json",
|
|
"docs/api.yaml",
|
|
"docs/api.json",
|
|
"swagger.yaml",
|
|
"swagger.json",
|
|
}
|
|
|
|
// DetectSpec finds the OpenAPI spec file.
|
|
// Priority: config path -> common paths -> Laravel Scramble.
|
|
func (s *SDK) DetectSpec() (string, error) {
|
|
// 1. Check configured path
|
|
if s.config.Spec != "" {
|
|
specPath := filepath.Join(s.projectDir, s.config.Spec)
|
|
if coreio.Local.IsFile(specPath) {
|
|
return specPath, nil
|
|
}
|
|
return "", fmt.Errorf("sdk.DetectSpec: configured spec not found: %s", s.config.Spec)
|
|
}
|
|
|
|
// 2. Check common paths
|
|
for _, p := range commonSpecPaths {
|
|
specPath := filepath.Join(s.projectDir, p)
|
|
if coreio.Local.IsFile(specPath) {
|
|
return specPath, nil
|
|
}
|
|
}
|
|
|
|
// 3. Try Laravel Scramble detection
|
|
specPath, err := s.detectScramble()
|
|
if err == nil {
|
|
return specPath, nil
|
|
}
|
|
|
|
return "", fmt.Errorf("sdk.DetectSpec: no OpenAPI spec found (checked config, common paths, Scramble)")
|
|
}
|
|
|
|
// detectScramble checks for Laravel Scramble and exports the spec.
|
|
func (s *SDK) detectScramble() (string, error) {
|
|
composerPath := filepath.Join(s.projectDir, "composer.json")
|
|
if !coreio.Local.IsFile(composerPath) {
|
|
return "", fmt.Errorf("no composer.json")
|
|
}
|
|
|
|
// Check for scramble in composer.json
|
|
data, err := coreio.Local.Read(composerPath)
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
|
|
// Simple check for scramble package
|
|
if !containsScramble(data) {
|
|
return "", fmt.Errorf("scramble not found in composer.json")
|
|
}
|
|
|
|
// TODO: Run php artisan scramble:export
|
|
return "", fmt.Errorf("scramble export not implemented")
|
|
}
|
|
|
|
// containsScramble checks if composer.json includes scramble.
|
|
func containsScramble(content string) bool {
|
|
return strings.Contains(content, "dedoc/scramble") ||
|
|
strings.Contains(content, "\"scramble\"")
|
|
}
|