Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
dd42fd8
feat(pat): support batch chmod flows
May 29, 2026
202cb50
feat: use server default agentCode for chmod
May 29, 2026
3f09eb5
feat: surface tool auth metadata
Jun 1, 2026
a7879ed
feat: infer product grant auth metadata
Jun 1, 2026
8b4f05e
Merge release/v1.0.29 into main
Jun 1, 2026
73e2de7
chore: update coverage badge [skip ci]
github-actions[bot] Jun 1, 2026
9eb3099
feat(pat): summarize chmod output by default
Jun 2, 2026
4bbd52f
chore: update coverage badge [skip ci]
github-actions[bot] Jun 2, 2026
3ce64db
fix(pat): preserve batch session metadata
Jun 2, 2026
e653616
fix(pat): align dry-run session and schema docs
Jun 2, 2026
dd1c4e3
chore(config): use public example MCP override URL
Jun 2, 2026
7652bda
fix(pat): align chmod session env handling
Jun 2, 2026
9e7e7db
Merge remote-tracking branch 'upstream/main'
Jun 2, 2026
fc0873b
docs,test(pat): carry chmod batch auth updates
Jun 8, 2026
4c86a9f
fix(pat): carry agentCode in batch auth args
Jun 10, 2026
d0fb3b4
fix(pat): carry agentCode in batch auth args
Jun 10, 2026
d8efa81
fix(pat): let core default missing agent code
Jun 10, 2026
31d081d
fix(pat): require yes for batch grants
Jun 10, 2026
a9eaa1b
test(pat): cover cli authorization matrix
Jun 10, 2026
38a162c
fix(pat): keep canonical agent code env only
Jun 10, 2026
4eea618
fix(pat): guard chmod agent code mismatch
Jun 10, 2026
7f1d36c
fix(pat): require yes for batch chmod
Jun 10, 2026
e637d79
fix(pat): verify chmod fallback agent code
Jun 10, 2026
2e11a23
fix(keychain): add windows storage dir for packaging
Jun 10, 2026
9f744ca
chore(config): default mcp endpoint to prepub
Jun 10, 2026
301abf4
Merge remote-tracking branch 'origin/codex/pat-batch-agentcode-main' …
Jun 10, 2026
b1cd8cb
Merge remote-tracking branch 'upstream/main' into HEAD
Jun 10, 2026
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
2 changes: 1 addition & 1 deletion .github/badges/coverage.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
5 changes: 3 additions & 2 deletions internal/app/runner.go
Original file line number Diff line number Diff line change
Expand Up @@ -111,7 +111,7 @@ func logHostOwnedPATDecisionOnce() {
hostOwnedPATDecisionOnce.Do(func() {
slog.Debug("runtime.host_owned_pat",
"hostOwned", authpkg.HostOwnsPATFlow(),
"agentCodeEnvPresent", os.Getenv(authpkg.AgentCodeEnv) != "",
"agentCodeEnvPresent", authpkg.AgentCodeEnvPresent(),
)
})
}
Expand Down Expand Up @@ -687,9 +687,10 @@ func resolveIdentityHeaders() map[string]string {
if sessionID == "" {
sessionID = os.Getenv(envRewindSessionID)
}
agentCode, _ := authpkg.AgentCodeFromEnv()
envHeaders := map[string]string{
"x-dingtalk-agent": os.Getenv(envDingtalkAgent),
"x-dingtalk-dws-agent-code": strings.TrimSpace(os.Getenv(authpkg.AgentCodeEnv)),
"x-dingtalk-dws-agent-code": agentCode,
"x-dingtalk-trace-id": os.Getenv(envDingtalkTraceID),
"x-dingtalk-session-id": sessionID,
"x-dingtalk-message-id": os.Getenv(envDingtalkMessageID),
Expand Down
11 changes: 11 additions & 0 deletions internal/app/runner_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -328,6 +328,17 @@ func TestResolveIdentityHeadersForwardsAgentCode(t *testing.T) {
}
}

func TestResolveIdentityHeadersIgnoresReversedAgentCodeEnv(t *testing.T) {
setupRuntimeCommandTest(t)
t.Setenv(authpkg.AgentCodeEnv, "")
t.Setenv("DWS_DINGTALK_AGENTCODE", " compat ")

headers := resolveIdentityHeaders()
if got := headers["x-dingtalk-dws-agent-code"]; got != "" {
t.Fatalf("x-dingtalk-dws-agent-code = %q, want empty because reversed env is ignored", got)
}
}

func TestResolveIdentityHeadersSessionEnvPriority(t *testing.T) {
setupRuntimeCommandTest(t)
t.Setenv(envDingtalkSessionID, "ding-session")
Expand Down
4 changes: 2 additions & 2 deletions internal/app/skill_command.go
Original file line number Diff line number Diff line change
Expand Up @@ -40,14 +40,14 @@ func init() {
Name: "DWS_SKILL_API_HOST",
Category: configmeta.CategoryNetwork,
Description: "覆盖 Skill API 地址",
DefaultValue: "https://mcp.dingtalk.com",
DefaultValue: "https://pre-mcp.dingtalk.com",
Example: "https://custom-mcp.example.com",
})
}

const (
// legacySkillAPIHost is the legacy skill market host used by the old cli.
legacySkillAPIHost = "https://mcp.dingtalk.com"
legacySkillAPIHost = "https://pre-mcp.dingtalk.com"
// skillDownloadEndpoint is the API endpoint for downloading skills.
skillDownloadEndpoint = "https://aihub.dingtalk.com/cli/download"
// skillDownloadTimeout is the timeout for skill download operations.
Expand Down
37 changes: 28 additions & 9 deletions internal/auth/channel.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,19 +19,38 @@ import (
)

const (
// AgentCodeEnv is the sole per-spawn environment variable the host injects
// to declare "this process is driven by a third-party Agent host, render
// authorization UI yourselves".
// AgentCodeEnv is the primary per-spawn environment variable the host
// injects to declare "this process is driven by a third-party Agent host,
// render authorization UI yourselves".
AgentCodeEnv = "DINGTALK_DWS_AGENTCODE"
)

// AgentCodeFromEnv returns the effective host agent code and the env name that
// supplied it.
//
// Keep the public env surface intentionally single-spelled. The reversed
// DWS_DINGTALK_AGENTCODE draft name is not consumed, so host-owned PAT mode,
// gateway identity headers, and `pat chmod --agentCode` fallback all agree on
// the same stable signal: DINGTALK_DWS_AGENTCODE.
func AgentCodeFromEnv() (string, string) {
if value := strings.TrimSpace(os.Getenv(AgentCodeEnv)); value != "" {
return value, AgentCodeEnv
}
return "", ""
}

func AgentCodeEnvPresent() bool {
value, _ := AgentCodeFromEnv()
return value != ""
}

// HostOwnsPATFlow reports whether the current process is running under a
// third-party Agent host that will render the PAT authorization card
// itself. The sole trigger is AgentCodeEnv (DINGTALK_DWS_AGENTCODE) being
// non-empty. The CLI deliberately does not consult any other signal
// (DINGTALK_AGENT / DWS_CHANNEL / the wire claw-type header) for this
// decision so that server-side routing tags and the host-owned UI contract
// remain independent concerns.
// itself. The trigger is DINGTALK_DWS_AGENTCODE being non-empty. The CLI
// deliberately does not consult any other signal (DINGTALK_AGENT /
// DWS_CHANNEL / the wire claw-type header) for this decision so that
// server-side routing tags and the host-owned UI contract remain independent
// concerns.
func HostOwnsPATFlow() bool {
return strings.TrimSpace(os.Getenv(AgentCodeEnv)) != ""
return AgentCodeEnvPresent()
}
2 changes: 1 addition & 1 deletion internal/auth/endpoints.go
Original file line number Diff line number Diff line change
Expand Up @@ -129,7 +129,7 @@ func GetDeveloperSettingsURL() string {

// GetMCPBaseURL returns the MCP base URL with priority:
// 1. ~/.dws/mcp_url file content (for pre-release environment)
// 2. Default value (https://mcp.dingtalk.com)
// 2. Default value (https://pre-mcp.dingtalk.com)
func GetMCPBaseURL() string {
return config.GetMCPBaseURL()
}
Expand Down
2 changes: 1 addition & 1 deletion internal/cli/canonical_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1285,7 +1285,7 @@ func TestSchemaCommandOutputsDegradedOnMarketUnreachable(t *testing.T) {

degradedErr := &CatalogDegraded{
Reason: DegradedMarketUnreachable,
Hint: "无法连接 MCP 市场 (mcp.dingtalk.com),请检查网络",
Hint: "无法连接 MCP 市场,请检查网络",
}
cmd := NewSchemaCommand(errorLoader{err: degradedErr})

Expand Down
2 changes: 1 addition & 1 deletion internal/cli/loader.go
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,7 @@ func degradedHint(reason CatalogDegradedReason, serverCount int) string {
if embedded {
return "无法连接 MCP 市场,请检查网络"
}
return "无法连接 MCP 市场 (mcp.dingtalk.com),请检查网络"
return "无法连接 MCP 市场,请检查网络"
case DegradedRuntimeAllFailed:
if embedded {
return fmt.Sprintf("已发现 %d 个服务但连接全部失败,请稍后重试", serverCount)
Expand Down
2 changes: 1 addition & 1 deletion internal/pat/browser_policy.go
Original file line number Diff line number Diff line change
Expand Up @@ -102,7 +102,7 @@ func saveBrowserPolicy(configDir string, policy *BrowserPolicy) error {
}

func ResolveBrowserPolicy(configDir, explicitAgentCode string) (BrowserPolicySelection, error) {
agentCode, err := resolveAgentCode(explicitAgentCode, false)
agentCode, err := resolveAgentCode(explicitAgentCode)
if err != nil {
return BrowserPolicySelection{}, err
}
Expand Down
28 changes: 28 additions & 0 deletions internal/pat/browser_policy_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ package pat
import (
"bytes"
"encoding/json"
"strings"
"testing"
)

Expand Down Expand Up @@ -235,3 +236,30 @@ func TestBrowserPolicyCommand_NoAgentCodeWritesDefaultEvenWhenEnvSet(t *testing.
t.Fatalf("len(policy.Agents) = %d, want 0", got)
}
}

func TestBrowserPolicyCommand_RequiresEnabledFlag(t *testing.T) {
configDir := t.TempDir()
t.Setenv("DWS_CONFIG_DIR", configDir)

cmd := newBrowserPolicyCommand()
var stdout bytes.Buffer
cmd.SetOut(&stdout)
cmd.SetErr(&stdout)
cmd.SetArgs([]string{"--agentCode", "agt-command"})

err := cmd.Execute()
if err == nil {
t.Fatal("browser-policy Execute() error = nil, want missing --enabled error")
}
if !strings.Contains(err.Error(), "--enabled is required") {
t.Fatalf("browser-policy error = %q, want --enabled requirement", err.Error())
}

policy, loadErr := LoadBrowserPolicy(configDir)
if loadErr != nil {
t.Fatalf("LoadBrowserPolicy error = %v", loadErr)
}
if policy.Default != nil || len(policy.Agents) != 0 {
t.Fatalf("policy was modified despite missing --enabled: %#v", policy)
}
}
Loading
Loading