Skip to content
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,11 @@
# 2.19.7
## 新增
1. `qshell sandbox injection-rule create` / `update` 与 `qshell sandbox create --inline-injection` 新增 `github` 注入类型,通过 `--api-key` / `api-key=` 传入 GitHub Token,平台克隆仓库与匹配 `github.com` / `api.github.com` 出站请求时自动注入,沙箱内不可见明文
2. `qshell sandbox create` 新增 `--resource` 参数,支持在沙箱启动前由平台克隆 GitHub 仓库并挂载到指定路径,格式 `type=github_repository,url=<url>,mount-path=<absPath>[,token=<token>]`(`type` 可省略,`mount-path` 也可写作 `mount`)

## 更新
1. 升级 `github.com/qiniu/go-sdk/v7` 到 `v7.26.11`,附带修复其内部 `Commands.Connect` 重复关闭 channel 引发的 panic

# 2.19.6
## 修复
1. 修复 `qshell sandbox template build` 在 rebuild 场景下清空 `qshell.sandbox.toml` 中 `from_image` / `from_template` 的问题,避免配置了父模板的 Dockerfile 回退到 `FROM scratch` 后构建失败;CLI 显式传入 `--from-image` / `--from-template` 时仍会在 rebuild 场景报错
Expand Down
13 changes: 12 additions & 1 deletion cmd/sandbox.go
Original file line number Diff line number Diff line change
Expand Up @@ -120,7 +120,17 @@ var sandboxCreateCmdBuilder = func(cfg *iqshell.Config) *cobra.Command {
--inline-injection 'type=openai,api-key=sk-xxx' \
--inline-injection 'type=http,base-url=https://api.example.com,headers=Authorization=Bearer token;X-Env=prod'
qshell sbx cr my-template \
--inline-injection 'type=openai,api-key=sk-xxx'`,
--inline-injection 'type=openai,api-key=sk-xxx'

# Create with a GitHub credential inline injection (token passed via api-key)
qshell sandbox create my-template --inline-injection 'type=github,api-key=ghp-xxx'
qshell sbx cr my-template --inline-injection 'type=github,api-key=ghp-xxx'

# Create with a GitHub repository resource mounted into the sandbox
qshell sandbox create my-template \
--resource 'type=github_repository,url=https://github.com/owner/repo.git,mount-path=/workspace/repo,token=ghp-xxx'
qshell sbx cr my-template \
--resource 'url=https://github.com/owner/repo.git,mount-path=/workspace/repo,token=ghp-xxx'`,
Args: cobra.MaximumNArgs(1),
Run: func(cmd *cobra.Command, args []string) {
cfg.CmdCfg.CmdId = docs.SandboxCreateType
Expand All @@ -140,6 +150,7 @@ var sandboxCreateCmdBuilder = func(cfg *iqshell.Config) *cobra.Command {
cmd.Flags().BoolVar(&info.AutoPause, "auto-pause", false, "automatically pause sandbox when timeout expires (instead of killing)")
cmd.Flags().StringArrayVar(&info.InjectionRuleID, "injection-rule", nil, "injection rule IDs to apply when creating the sandbox (can be specified multiple times)")
cmd.Flags().StringArrayVar(&info.InlineInjection, "inline-injection", nil, "inline injection spec to apply when creating the sandbox (can be specified multiple times, format: type=<type>,api-key=<key>,base-url=<url>,headers=<k1=v1;k2=v2>)")
cmd.Flags().StringArrayVar(&info.Resources, "resource", nil, "resource to mount before sandbox starts (can be specified multiple times, format: type=github_repository,url=<url>,mount-path=<absPath>[,token=<token>])")
return cmd
}

Expand Down
20 changes: 14 additions & 6 deletions cmd/sandbox_injection_rule.go
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,11 @@ var injectionRuleCreateCmdBuilder = func(cfg *iqshell.Config) *cobra.Command {

# Create a Qiniu AI API injection rule
qshell sandbox injection-rule create --name qiniu-ai --type qiniu --api-key ak-xxx
qshell sbx ir cr --name qiniu-ai --type qiniu --api-key ak-xxx`,
qshell sbx ir cr --name qiniu-ai --type qiniu --api-key ak-xxx

# Create a GitHub credential injection rule (token passed via --api-key)
qshell sandbox injection-rule create --name github-default --type github --api-key ghp-xxx
qshell sbx ir cr --name github-default --type github --api-key ghp-xxx`,
Run: func(cmd *cobra.Command, args []string) {
cfg.CmdCfg.CmdId = docs.SandboxInjectionRuleCreateType
if !iqshell.CheckAndLoad(cfg, iqshell.CheckAndLoadInfo{}) {
Expand All @@ -113,8 +117,8 @@ var injectionRuleCreateCmdBuilder = func(cfg *iqshell.Config) *cobra.Command {
},
}
cmd.Flags().StringVar(&info.Name, "name", "", "rule name (required, unique per user)")
cmd.Flags().StringVar(&info.Type, "type", "", "injection type: openai, anthropic, gemini, qiniu, http")
cmd.Flags().StringVar(&info.APIKey, "api-key", "", "API key for openai/anthropic/gemini/qiniu injection types (warning: passing secrets via CLI may leak through shell history or process lists)")
cmd.Flags().StringVar(&info.Type, "type", "", "injection type: openai, anthropic, gemini, qiniu, github, http")
cmd.Flags().StringVar(&info.APIKey, "api-key", "", "API key for openai/anthropic/gemini/qiniu, or token for github (warning: passing secrets via CLI may leak through shell history or process lists)")
cmd.Flags().StringVar(&info.BaseURL, "base-url", "", "override base URL or target base URL for http injection")
cmd.Flags().StringVar(&info.Headers, "headers", "", "HTTP headers for custom http injection (comma-separated key=value pairs)")
_ = cmd.MarkFlagRequired("name")
Expand Down Expand Up @@ -143,7 +147,11 @@ var injectionRuleUpdateCmdBuilder = func(cfg *iqshell.Config) *cobra.Command {

# Update to a Qiniu AI API injection
qshell sandbox injection-rule update rule-xxxxxxxxxxxx --type qiniu --api-key ak-new
qshell sbx ir up rule-xxxxxxxxxxxx --type qiniu --api-key ak-new`,
qshell sbx ir up rule-xxxxxxxxxxxx --type qiniu --api-key ak-new

# Update to a GitHub credential injection (token passed via --api-key)
qshell sandbox injection-rule update rule-xxxxxxxxxxxx --type github --api-key ghp-new
qshell sbx ir up rule-xxxxxxxxxxxx --type github --api-key ghp-new`,
Run: func(cmd *cobra.Command, args []string) {
cfg.CmdCfg.CmdId = docs.SandboxInjectionRuleUpdateType
if !iqshell.CheckAndLoad(cfg, iqshell.CheckAndLoadInfo{}) {
Expand All @@ -154,8 +162,8 @@ var injectionRuleUpdateCmdBuilder = func(cfg *iqshell.Config) *cobra.Command {
},
}
cmd.Flags().StringVar(&info.Name, "name", "", "new rule name")
cmd.Flags().StringVar(&info.Type, "type", "", "new injection type: openai, anthropic, gemini, qiniu, http")
cmd.Flags().StringVar(&info.APIKey, "api-key", "", "new API key for openai/anthropic/gemini/qiniu injection types (warning: passing secrets via CLI may leak through shell history or process lists)")
cmd.Flags().StringVar(&info.Type, "type", "", "new injection type: openai, anthropic, gemini, qiniu, github, http")
cmd.Flags().StringVar(&info.APIKey, "api-key", "", "new API key for openai/anthropic/gemini/qiniu, or token for github (warning: passing secrets via CLI may leak through shell history or process lists)")
cmd.Flags().StringVar(&info.BaseURL, "base-url", "", "new base URL or target base URL for http injection")
cmd.Flags().StringVar(&info.Headers, "headers", "", "new HTTP headers for custom http injection (comma-separated key=value pairs)")
return cmd
Expand Down
2 changes: 1 addition & 1 deletion cmd_test/sandbox_injection_rule_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ func TestSandboxInjectionRuleCreateDocumentWithQiniu(t *testing.T) {
testInjectionRuleDocContains(
t,
[]string{"sandbox", "injection-rule", "create"},
"--type <openai|anthropic|gemini|qiniu|http>",
"--type <openai|anthropic|gemini|qiniu|github|http>",
"--name", "qiniu-default",
"--type", "qiniu",
)
Expand Down
23 changes: 20 additions & 3 deletions docs/sandbox_create.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,11 +29,12 @@ $ qshell sandbox create --doc
- `--auto-pause`:超时后自动暂停沙箱,而不是终止沙箱
- `--injection-rule`:创建沙箱时附加的注入规则 ID,可多次指定
- `--inline-injection`:创建沙箱时附加的内联注入配置,可多次指定,格式为 `type=<type>,api-key=<key>,base-url=<url>,headers=<k1=v1;k2=v2>`
- `--resource`:沙箱启动前挂载的资源规约,可多次指定,格式为 `type=github_repository,url=<url>,mount-path=<absPath>[,token=<token>]`(`type` 默认为 `github_repository`,`mount-path` 也可写作 `mount`)

内联注入说明:
- `type` 支持 `openai`、`anthropic`、`gemini`、`qiniu`、`http`
- `api-key` 可用于 `openai`、`anthropic`、`gemini`、`qiniu`
- `base-url` 可用于覆盖默认目标地址;`type=http` 时必填,`type=qiniu` 默认目标地址为 `api.qnaigc.com`
- `type` 支持 `openai`、`anthropic`、`gemini`、`qiniu`、`github`、`http`
- `api-key` 用于 `openai`、`anthropic`、`gemini`、`qiniu` 的 API Key,以及 `github` 的访问 token(token 仅平台可见,沙箱内不可见明文)
- `base-url` 可用于覆盖默认目标地址;`type=http` 时必填,`type=qiniu` 默认目标地址为 `api.qnaigc.com`;`type=github` 固定匹配 `github.com` / `api.github.com`,不支持配置
- `headers` 仅用于 `type=http`,多个请求头使用分号分隔,例如 `headers=Authorization=Bearer token;X-Env=prod`

# 示例
Expand Down Expand Up @@ -86,3 +87,19 @@ $ qshell sandbox create my-template \
--inline-injection 'type=http,base-url=https://api.example.com,headers=Authorization=Bearer token;X-Env=prod'
$ qshell sbx cr my-template --inline-injection 'type=gemini,api-key=sk-gem'
```

9. 创建时附加 GitHub 凭证注入(token 通过 `api-key` 传入)
```
$ qshell sandbox create my-template --inline-injection 'type=github,api-key=ghp-xxx'
$ qshell sbx cr my-template --inline-injection 'type=github,api-key=ghp-xxx'
```

10. 创建时挂载 GitHub 仓库资源(沙箱启动前由平台克隆并挂载到指定路径)
Comment thread
miclle marked this conversation as resolved.
Outdated
```
$ qshell sandbox create my-template \
--resource 'type=github_repository,url=https://github.com/owner/repo.git,mount-path=/workspace/repo,token=ghp-xxx'
$ qshell sbx cr my-template \
--resource 'url=https://github.com/owner/repo.git,mount-path=/workspace/repo,token=ghp-xxx'
```

> 同一沙箱内多个 `--resource github_repository` 当前必须共用同一 `token`;若已通过 `--inline-injection type=github,api-key=...` 配置 GitHub 凭证,资源处可省略 `token`。
16 changes: 11 additions & 5 deletions docs/sandbox_injection_rule_create.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@
# 格式

```bash
qshell sandbox injection-rule create --name <name> --type <openai|anthropic|gemini|qiniu|http> [--api-key <apiKey>] [--base-url <baseURL>] [--headers <headers>]
qshell sbx ir cr --name <name> --type <openai|anthropic|gemini|qiniu|http> [--api-key <apiKey>] [--base-url <baseURL>] [--headers <headers>]
qshell sandbox injection-rule create --name <name> --type <openai|anthropic|gemini|qiniu|github|http> [--api-key <apiKey>] [--base-url <baseURL>] [--headers <headers>]
qshell sbx ir cr --name <name> --type <openai|anthropic|gemini|qiniu|github|http> [--api-key <apiKey>] [--base-url <baseURL>] [--headers <headers>]
```

# 帮助文档
Expand All @@ -18,9 +18,9 @@ $ qshell sandbox injection-rule create --doc
# 参数

- `--name`:规则名称,必填,同一用户下唯一
- `--type`:注入类型,必填,支持 `openai`、`anthropic`、`gemini`、`qiniu`、`http`
- `--api-key`:`openai`、`anthropic`、`gemini`、`qiniu` 类型使用的 API Key。注意:通过 CLI 传递密钥可能泄露到 Shell 历史或进程列表
- `--base-url`:覆盖默认目标地址,或 `http` 类型的目标基础 URL;`qiniu` 默认为 `api.qnaigc.com`
- `--type`:注入类型,必填,支持 `openai`、`anthropic`、`gemini`、`qiniu`、`github`、`http`
- `--api-key`:`openai`、`anthropic`、`gemini`、`qiniu` 类型使用的 API Key,`github` 类型使用的访问 token。注意:通过 CLI 传递密钥可能泄露到 Shell 历史或进程列表
- `--base-url`:覆盖默认目标地址,或 `http` 类型的目标基础 URL;`qiniu` 默认为 `api.qnaigc.com`;`github` 类型固定匹配 `github.com` / `api.github.com`,不支持配置
- `--headers`:`http` 类型的请求头,使用逗号分隔的 `key=value` 形式

# 示例
Expand Down Expand Up @@ -48,3 +48,9 @@ $ qshell sandbox injection-rule create --name api-auth --type http --base-url ht
```bash
$ qshell sandbox injection-rule create --name qiniu-ai --type qiniu --api-key ak-xxx
```

创建 GitHub 凭证注入规则(token 通过 `--api-key` 传入,沙箱内不可见明文):

```bash
$ qshell sandbox injection-rule create --name github-default --type github --api-key ghp-xxx
```
10 changes: 8 additions & 2 deletions docs/sandbox_injection_rule_update.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@
# 格式

```bash
qshell sandbox injection-rule update <ruleID> [--name <name>] [--type <openai|anthropic|gemini|qiniu|http>] [--api-key <apiKey>] [--base-url <baseURL>] [--headers <headers>]
qshell sbx ir up <ruleID> [--name <name>] [--type <openai|anthropic|gemini|qiniu|http>] [--api-key <apiKey>] [--base-url <baseURL>] [--headers <headers>]
qshell sandbox injection-rule update <ruleID> [--name <name>] [--type <openai|anthropic|gemini|qiniu|github|http>] [--api-key <apiKey>] [--base-url <baseURL>] [--headers <headers>]
qshell sbx ir up <ruleID> [--name <name>] [--type <openai|anthropic|gemini|qiniu|github|http>] [--api-key <apiKey>] [--base-url <baseURL>] [--headers <headers>]
```

# 帮助文档
Expand Down Expand Up @@ -51,3 +51,9 @@ $ qshell sandbox injection-rule update rule-xxxxxxxxxxxx --type qiniu --api-key
```bash
$ qshell sandbox injection-rule update rule-xxxxxxxxxxxx --type http --base-url https://api.example.com --headers "Authorization=Bearer newtoken"
```

更新为 GitHub 凭证注入(token 通过 `--api-key` 传入):

```bash
$ qshell sandbox injection-rule update rule-xxxxxxxxxxxx --type github --api-key ghp-new
```
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ require (
github.com/fatih/color v1.18.0
github.com/mitchellh/go-homedir v1.1.0
github.com/muesli/termenv v0.16.0
github.com/qiniu/go-sdk/v7 v7.26.10
github.com/qiniu/go-sdk/v7 v7.26.11
github.com/schollz/progressbar/v3 v3.8.6
github.com/spf13/cast v1.3.1
github.com/spf13/cobra v1.1.3
Expand Down
4 changes: 2 additions & 2 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -294,8 +294,8 @@ github.com/prometheus/procfs v0.0.0-20190507164030-5867b95ac084/go.mod h1:TjEm7z
github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA=
github.com/prometheus/procfs v0.1.3/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU=
github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU=
github.com/qiniu/go-sdk/v7 v7.26.10 h1:1c2c+grH7b8k5inIDKc95CI8+mAzB5YtfQ/dLOB2nNo=
github.com/qiniu/go-sdk/v7 v7.26.10/go.mod h1:ri7fGwbio0pRDFr8EK5TUpx0DbnpIMJ2bMSDxGWfCbk=
github.com/qiniu/go-sdk/v7 v7.26.11 h1:XVGb5cgqYnNaExCirky+OntL1zvxRxBJuYoWMlnHOuE=
github.com/qiniu/go-sdk/v7 v7.26.11/go.mod h1:ri7fGwbio0pRDFr8EK5TUpx0DbnpIMJ2bMSDxGWfCbk=
github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
github.com/rivo/uniseg v0.4.7 h1:WUdvkW8uEhrYfLC4ZzdpI2ztxP1I582+49Oc5Mq64VQ=
github.com/rivo/uniseg v0.4.7/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88=
Expand Down
9 changes: 9 additions & 0 deletions iqshell/sandbox/injection_rule/operations/helpers.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ const (
injectionTypeAnthropic = "anthropic"
injectionTypeGemini = "gemini"
injectionTypeQiniu = "qiniu"
injectionTypeGithub = "github"
injectionTypeHTTP = "http"
)

Expand All @@ -34,6 +35,7 @@ func buildInjectionSpec(input injectionInput) (sandbox.InjectionSpec, error) {
Anthropic: parts.Anthropic,
Gemini: parts.Gemini,
Qiniu: parts.Qiniu,
Github: parts.Github,
HTTP: parts.HTTP,
}, nil
}
Expand All @@ -52,6 +54,8 @@ func formatInjectionType(spec sandbox.InjectionSpec) string {
return injectionTypeGemini
case spec.Qiniu != nil:
return injectionTypeQiniu
case spec.Github != nil:
return injectionTypeGithub
case spec.HTTP != nil:
return injectionTypeHTTP
default:
Expand All @@ -69,6 +73,9 @@ func formatInjectionTarget(spec sandbox.InjectionSpec) string {
return optionalValue(spec.Gemini.BaseURL, "generativelanguage.googleapis.com")
case spec.Qiniu != nil:
return optionalValue(spec.Qiniu.BaseURL, "api.qnaigc.com")
case spec.Github != nil:
// GitHub 注入的目标固定为 github.com / api.github.com,由平台侧匹配,无可配置 base URL
return "github.com, api.github.com"
case spec.HTTP != nil:
return spec.HTTP.BaseURL
default:
Expand Down Expand Up @@ -98,6 +105,8 @@ func hasAPIKey(spec sandbox.InjectionSpec) bool {
return spec.Gemini.APIKey != nil && strings.TrimSpace(*spec.Gemini.APIKey) != ""
case spec.Qiniu != nil:
return spec.Qiniu.APIKey != nil && strings.TrimSpace(*spec.Qiniu.APIKey) != ""
case spec.Github != nil:
return spec.Github.Token != nil && strings.TrimSpace(*spec.Github.Token) != ""
default:
return false
}
Expand Down
36 changes: 36 additions & 0 deletions iqshell/sandbox/injection_rule/operations/helpers_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -131,6 +131,42 @@ func TestFormatInjectionTargetQiniuDefault(t *testing.T) {
}
}

func TestFormatInjectionSummaryGithub(t *testing.T) {
token := "ghp-token"
spec := sandbox.InjectionSpec{
Github: &sandbox.GithubInjection{Token: &token},
}

if got := formatInjectionType(spec); got != "github" {
t.Fatalf("formatInjectionType() = %q, want %q", got, "github")
}
if got := formatInjectionTarget(spec); got != "github.com, api.github.com" {
t.Fatalf("formatInjectionTarget() = %q, want %q", got, "github.com, api.github.com")
}
if got := formatInjectionHeaders(spec); got != "-" {
t.Fatalf("formatInjectionHeaders() = %q, want %q", got, "-")
}
if !hasAPIKey(spec) {
t.Fatal("hasAPIKey() = false, want true for github with token")
}
}

func TestBuildInjectionSpecGithub(t *testing.T) {
spec, err := buildInjectionSpec(injectionInput{
Type: injectionTypeGithub,
APIKey: "ghp-token",
})
if err != nil {
t.Fatalf("buildInjectionSpec(github) error = %v", err)
}
if spec.Github == nil {
t.Fatal("expected Github injection to be set")
}
if spec.Github.Token == nil || *spec.Github.Token != "ghp-token" {
t.Fatalf("Github token = %v, want %q", spec.Github.Token, "ghp-token")
}
}

func TestShouldUpdateInjection(t *testing.T) {
if shouldUpdateInjection(injectionInput{}) {
t.Fatal("shouldUpdateInjection() = true, want false")
Expand Down
Loading
Loading