Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
48 commits
Select commit Hold shift + click to select a range
09039d5
[cli] add folder project links
saioai Oct 9, 2026
43f5104
[cli] reject relative project link configuration roots
saioai Oct 9, 2026
d0ccc2e
[cli] preserve explicit project header overrides
saioai Oct 9, 2026
b07f9a5
[cli] isolate project link tests from ambient endpoints
saioai Oct 9, 2026
7d6cbe7
[cli] verify local linking skips shell setup
saioai Oct 9, 2026
2b571f0
[cli] declare supported folder link completion formats
saioai Oct 9, 2026
8476466
[cli] cover quiet folder link output
saioai Oct 9, 2026
8794406
fix: align folder project inspection and recovery
saioai Oct 10, 2026
c171bef
test: select readable errors for copied recovery
saioai Oct 10, 2026
dab5c29
Merge commit '5739ce0f3a6cc5887092554a403bf53357eca9a4' into codex/ne…
saioai Oct 10, 2026
b078b9d
test: match project replay to observed baseline error
saioai Oct 10, 2026
a1f98de
test: name project link coverage by responsibility
saioai Oct 10, 2026
32e5ae6
Add offline authentication configuration status
saioai Oct 16, 2026
6a3d20c
Escape line breaks in auth status fields
saioai Oct 16, 2026
7f3668c
Preserve verbose stderr policy in auth status checks
saioai Oct 16, 2026
cd5f147
test: include auth in legacy prefix completion
saioai Oct 16, 2026
c3e2523
fix: preserve folder links across equivalent directory paths
saioai Oct 16, 2026
67baac0
Merge reviewed offline auth status parent for profile integration
saioai Oct 16, 2026
f7603c7
feat: add local profiles and project switching
saioai Oct 16, 2026
b317f9f
fix: reset profile flags between command invocations
saioai Oct 16, 2026
55dd940
fix: preserve profile source and recovery diagnostics
saioai Oct 16, 2026
86f9c8e
fix: preserve profile context and committed cancellation outcomes
saioai Oct 16, 2026
8772eb0
Preserve profile context across admin and copied commands
saioai Oct 16, 2026
05ca236
Reject folder-link writes after configuration replacement
saioai Oct 16, 2026
6b427eb
Add explicit credential identity status and lookup
saioai Oct 16, 2026
7ce5bd1
Close identity transport and credential state gaps
saioai Oct 16, 2026
6554cc0
Isolate the mTLS reuse fixture from profile selectors
saioai Oct 16, 2026
b8cdc77
Correct identity transport and flag reuse test fixtures
saioai Oct 16, 2026
490ab4d
Preserve executable identity in profile recovery
saioai Oct 16, 2026
cc1e927
Use complete profile schema in recovery fixture
saioai Oct 16, 2026
5d864f0
Integrate current CLI commands and preserve profile outcomes
saioai Oct 18, 2026
652202d
Preserve the invoked CLI in auth status recovery
saioai Oct 18, 2026
1bbc7af
Use shared output contracts after CLI integration
saioai Oct 18, 2026
6b23bb5
Test copied profile recovery in native Windows shells
saioai Oct 18, 2026
6dd15b9
Merge main SDK v3.81 update for row 11 validation
saioai Oct 18, 2026
add40e1
Integrate profiles and identity status with shared request helpers
saioai Oct 19, 2026
f8264b5
Group shared request imports with project dependencies
saioai Oct 19, 2026
bddc8ac
Integrate main SDK v3.82.0 for final row 11 validation
saioai Oct 19, 2026
03af00b
docs: qualify profile registry replacement guarantees
saioai Oct 19, 2026
6ad53e5
Merge current main and complete profile request validation
saioai Oct 19, 2026
bd301f3
Merge remote-tracking branch 'origin/main' into codex/new-project-lin…
saioai Oct 19, 2026
29ae79d
Integrate Admin extraction with profile endpoint selection
saioai Oct 20, 2026
0dafb6b
Preserve profile tests across Audio extraction
saioai Oct 20, 2026
b959275
Repair auth helpers and actionable error guidance
saioai Oct 20, 2026
ae9db24
Correct profile validation inventory counts
saioai Oct 20, 2026
b15e3d0
Preserve profiles across Images extraction
saioai Oct 20, 2026
93b11c2
Keep link setup tests with application admission
saioai Oct 20, 2026
721943a
Preserve profiles across application runtime migration
saioai Oct 20, 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
609 changes: 609 additions & 0 deletions .github/workflows/help-compatibility.yml

Large diffs are not rendered by default.

1,249 changes: 1,249 additions & 0 deletions cmd/openai/auth_status_test.go

Large diffs are not rendered by default.

3 changes: 2 additions & 1 deletion cmd/openai/command_subgroups_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,7 @@ func TestMainCommandSubgroupsHelpAndCompletion(t *testing.T) {
func TestMainCommandSubgroupsLegacyPrefixCompletion(t *testing.T) {
summaries := map[string]string{
"audio": "Transcribe audio, generate speech, and create voices.",
"auth": "Inspect authentication configuration and identity",
"audio:transcriptions": "Convert audio to text.",
"audio:translations": "Translate supported audio to English text.",
"admin:organization:audit-logs": "List organization actions and configuration changes.",
Expand All @@ -126,7 +127,7 @@ func TestMainCommandSubgroupsLegacyPrefixCompletion(t *testing.T) {
{[]string{"admin", ":", "organization", ":", "audit"}, "admin:organization:audit-logs\n", "audit-logs\n", 0},
{[]string{"beta:threads:r"}, "beta:threads:runs\nbeta:threads:runs:steps\n", "runs\nruns:steps\n", 0},
{[]string{"audio:unknown"}, "", "", 0},
{[]string{"au"}, "audio\n", "audio\n", 0},
{[]string{"au"}, "audio\nauth\n", "audio\nauth\n", 0},
{[]string{"__"}, "", "", 0},
{[]string{"@comp"}, "", "", 0},
{[]string{"--format", "audio:t"}, "", "", 11},
Expand Down
1,202 changes: 1,202 additions & 0 deletions cmd/openai/profile_commands_test.go

Large diffs are not rendered by default.

341 changes: 341 additions & 0 deletions cmd/openai/project_link_aliases_test.go

Large diffs are not rendered by default.

261 changes: 261 additions & 0 deletions cmd/openai/project_link_inspection_recovery_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,261 @@
package main

import (
"encoding/json"
"errors"
"net/http"
"os"
"os/exec"
"path/filepath"
"regexp"
"runtime"
"slices"
"strings"
"testing"

"github.com/stretchr/testify/require"
)

// These cases compare local inspection with the existing request-option order.
// They use synthetic headers and do not establish live project access.
func TestMainProjectLinkPlanExpInspectionMatchesRequestHeaders(t *testing.T) {
const response = `{"object":"list","data":[],"has_more":false}`
for _, test := range []struct {
name, saved, source, effective string
env, flags, headers []string
}{
{name: "default", source: "default"},
{name: "custom header fallback", source: "custom_headers", effective: "proj_custom",
env: []string{"OPENAI_CUSTOM_HEADERS=OpenAI-Project: proj_custom"}, headers: []string{"proj_custom"}},
{name: "custom header duplicate casing", source: "custom_headers", effective: "proj_last",
env: []string{"OPENAI_CUSTOM_HEADERS=OpenAI-Project: proj_first\nopenai-project: proj_last"}, headers: []string{"proj_last"}},
{name: "custom header whitespace", source: "custom_headers", effective: "proj_trimmed",
env: []string{"OPENAI_CUSTOM_HEADERS= \tOpEnAi-PrOjEcT\t : \tproj_trimmed\t "}, headers: []string{"proj_trimmed"}},
{name: "custom header empty", source: "custom_headers",
env: []string{"OPENAI_CUSTOM_HEADERS=OpenAI-Project: proj_first\nopenai-project:"}, headers: []string{""}},
{name: "unrelated malformed environment lines", source: "custom_headers", effective: "proj_custom",
env: []string{"OPENAI_CUSTOM_HEADERS=synthetic-ignored-line\nOpenAI-Project: proj_custom\n\nsecond-ignored-line"}, headers: []string{"proj_custom"}},
{name: "folder overrides custom header", saved: "proj_folder", source: "folder", effective: "proj_folder",
env: []string{"OPENAI_CUSTOM_HEADERS=OpenAI-Project: proj_custom"}, headers: []string{"proj_folder"}},
{name: "environment overrides folder and custom header", saved: "proj_folder", source: "environment", effective: "proj_environment",
env: []string{"OPENAI_CUSTOM_HEADERS=OpenAI-Project: proj_custom", "OPENAI_PROJECT_ID=proj_environment"}, headers: []string{"proj_environment"}},
{name: "empty environment overrides custom header", saved: "proj_folder", source: "environment",
env: []string{"OPENAI_CUSTOM_HEADERS=OpenAI-Project: proj_custom", "OPENAI_PROJECT_ID="}, headers: []string{""}},
{name: "explicit header overrides folder", saved: "proj_folder", source: "header", effective: "proj_explicit",
flags: []string{"--header=OpenAI-Project: proj_explicit"}, headers: []string{"proj_explicit"}},
{name: "explicit header overrides environment", saved: "proj_folder", source: "header", effective: "proj_explicit",
env: []string{"OPENAI_PROJECT_ID=proj_environment", "OPENAI_CUSTOM_HEADERS=OpenAI-Project: proj_custom"},
flags: []string{"--header=OpenAI-Project: proj_explicit"}, headers: []string{"proj_explicit"}},
{name: "explicit duplicate casing", saved: "proj_folder", source: "header", effective: "proj_last",
flags: []string{"--header=OpenAI-Project: proj_first", "-H=openai-project: proj_last"}, headers: []string{"proj_last"}},
{name: "explicit empty header", saved: "proj_folder", source: "header",
env: []string{"OPENAI_PROJECT_ID=proj_environment"}, flags: []string{"--header=OpenAI-Project:"}, headers: []string{""}},
{name: "unrelated explicit header preserves custom fallback", source: "custom_headers", effective: "proj_custom",
env: []string{"OPENAI_CUSTOM_HEADERS=OpenAI-Project: proj_custom"}, flags: []string{"--header=X-Synthetic: unrelated"}, headers: []string{"proj_custom"}},
} {
t.Run(test.name, func(t *testing.T) {
home, directory := t.TempDir(), t.TempDir()
if test.saved != "" {
projectLinkCreate(t, home, directory, test.saved)
}
server, requests := globalFlagsServer(t, response)
// Local inspection must tolerate invalid remote configuration without credentials.
localEnv := append(slices.Clone(test.env), "OPENAI_BASE_URL=://synthetic-unusable-endpoint",
"OPENAI_MTLS_CLIENT_CERT_FILE=/synthetic-unusable/certificate")
args := append([]string{"link", "--format=json"}, test.flags...)
got := runProjectLinkMain(t, home, directory, localEnv, "", args...)
require.Zero(t, got.code, "%s", got.stderr)
require.Empty(t, got.stderr)
var status struct {
Project string `json:"project"`
EffectiveProject string `json:"effective_project"`
Source string `json:"source"`
Redacted bool `json:"effective_project_redacted"`
}
require.NoError(t, json.Unmarshal([]byte(got.stdout), &status))
require.Equal(t, test.saved, status.Project)
require.Equal(t, test.source, status.Source)
require.Equal(t, test.effective, status.EffectiveProject)
require.False(t, status.Redacted)
require.Empty(t, requests)
args = append([]string{"files", "list", "--format=raw"}, test.flags...)
got = runProjectLinkMain(t, home, directory, globalFlagsEnv(server, test.env...), "", args...)
require.Equal(t, mainDispatchResult{stdout: response + "\n"}, got)
request := globalFlagsOneRequest(t, requests)
require.Equal(t, http.MethodGet, request.method)
require.Equal(t, "/files", request.path)
require.Equal(t, test.headers, request.header.Values("OpenAI-Project"))
require.Equal(t, status.EffectiveProject, request.header.Get("OpenAI-Project"))
require.Equal(t, "Bearer synthetic-env-key", request.header.Get("Authorization"))
if test.saved != "" {
require.Equal(t, map[string]string{projectLinkCanonicalDirectory(t, directory): test.saved}, projectLinkRegistry(t, home))
}
})
}
}

func TestMainProjectLinkPlanExpCustomProjectRedaction(t *testing.T) {
server, requests := localUtilitiesRequestTrap(t)
for _, source := range []string{"custom_headers", "header"} {
for _, value := range []string{"sk-synthetic-project-secret", "https://synthetic.invalid/private?token=synthetic-project-secret"} {
for _, format := range []string{"text", "json"} {
t.Run(source+"/"+strings.SplitN(value, ":", 2)[0]+"/"+format, func(t *testing.T) {
home, directory := t.TempDir(), t.TempDir()
env := []string{"OPENAI_BASE_URL=" + server.URL}
args := []string{"link", "--format", format}
if source == "header" {
projectLinkCreate(t, home, directory, "proj_saved")
args = append(args, "--header=OpenAI-Project: "+value)
} else {
env = append(env, "OPENAI_CUSTOM_HEADERS=OpenAI-Project: "+value)
}
got := runProjectLinkMain(t, home, directory, env, "", args...)
require.Zero(t, got.code, "%s", got.stderr)
require.Empty(t, got.stderr)
require.NotContains(t, got.stdout, value)
require.NotContains(t, got.stdout, "synthetic-project-secret")
if format == "json" {
var status map[string]any
require.NoError(t, json.Unmarshal([]byte(got.stdout), &status))
require.Equal(t, source, status["source"])
require.Equal(t, true, status["effective_project_redacted"])
require.Equal(t, "", status["effective_project"])
if source == "header" {
require.Equal(t, "proj_saved", status["project"])
}
} else {
require.Contains(t, strings.ToLower(got.stdout), "hidden")
}
})
}
}
}
require.Zero(t, requests.Load(), "local inspection contacted the API")
}

func TestMainProjectLinkPlanExpRemoteFilenameRecipe(t *testing.T) {
home, directory := t.TempDir(), t.TempDir()
projectLinkCreate(t, home, directory, "proj_work")
require.NoError(t, os.WriteFile(filepath.Join(directory, "local-only.txt"), []byte("synthetic local file"), 0o600))
const response = `{"object":"list","data":[{"id":"file_remote_notes","object":"file","filename":"remote-notes.txt"},{"id":"file_remote_data","object":"file","filename":"remote-data.jsonl"}],"has_more":false}`
server, requests := globalFlagsServer(t, response)
got := runProjectLinkMain(t, home, directory, globalFlagsEnv(server), "", "files", "list", "--transform", "filename", "--raw-output")
require.Equal(t, mainDispatchResult{stdout: "remote-notes.txt\nremote-data.jsonl\n"}, got)
request := globalFlagsOneRequest(t, requests)
require.Equal(t, "/files", request.path)
require.Equal(t, "proj_work", request.header.Get("OpenAI-Project"))
}

func TestMainProjectLinkPlanExpStoreFailuresExplainKnownCause(t *testing.T) {
for _, kind := range []string{"size", "directory privacy", "file privacy", "permission denial"} {
t.Run(kind, func(t *testing.T) {
if kind != "size" && runtime.GOOS == "windows" {
t.Skip("Unix permission semantics require native Unix execution")
}
home, directory := t.TempDir(), t.TempDir()
path := projectLinkRegistryPath(home)
config := filepath.Dir(path)
require.NoError(t, os.MkdirAll(config, 0o700))
project := "proj_saved"
if kind == "size" {
project = "proj_" + strings.Repeat("x", 1<<20)
}
before, err := json.Marshal(map[string]string{projectLinkCanonicalDirectory(t, directory): project})
require.NoError(t, err)
require.NoError(t, os.WriteFile(path, before, 0o600))
var required []string
switch kind {
case "size":
required = []string{"1 MiB"}
case "directory privacy":
require.NoError(t, os.Chmod(config, 0o755))
required = []string{"private", "directory", "0700"}
case "file privacy":
require.NoError(t, os.Chmod(path, 0o644))
required = []string{"private", "regular", "project-links.json", "0600"}
case "permission denial":
require.NoError(t, os.Chmod(path, 0o000))
t.Cleanup(func() { require.NoError(t, os.Chmod(path, 0o600)) })
probe, err := os.Open(path)
if err == nil {
require.NoError(t, probe.Close())
t.Skip("current privileges bypass this file's read permissions")
}
require.True(t, errors.Is(err, os.ErrPermission), "permission probe: %v", err)
required = []string{"permission"}
}
server, requests := globalFlagsServer(t, globalFlagsPage)
for _, format := range []string{"text", "json"} {
for _, command := range [][]string{{"link"}, {"link", "--project=proj_replacement"}, {"files", "list"}} {
args := append(slices.Clone(command), "--format-error", format)
got := runProjectLinkMain(t, home, directory, globalFlagsEnv(server), "", args...)
require.NotZero(t, got.code, "%v", args)
require.Empty(t, got.stdout)
message := got.stderr
if format == "json" {
message, _ = decodeMainStructuredError(t, format, got.stderr)["message"].(string)
}
for _, expected := range required {
require.Contains(t, strings.ToLower(message), strings.ToLower(expected))
}
require.Contains(t, message, "This command did not change saved links.")
require.NotContains(t, strings.ToLower(message), "invalid settings")
require.NotContains(t, strings.ToLower(message), "invalid registry")
require.NotContains(t, message, home)
require.Empty(t, requests, "registry failure reached the API")
}
}
require.NoError(t, os.Chmod(path, 0o600))
after, err := os.ReadFile(path)
require.NoError(t, err)
require.Equal(t, before, after)
})
}
}

func TestMainProjectLinkPlanExpCopiedRecoveryPreservesInvocation(t *testing.T) {
if runtime.GOOS == "windows" {
t.Skip("this copied-command case executes a native Unix Bash shell")
}
bash, err := exec.LookPath("bash")
if err != nil {
t.Skip("Bash is unavailable")
}
quote := func(value string) string { return "'" + strings.ReplaceAll(value, "'", "'\\''") + "'" }
work := filepath.Join(t.TempDir(), "CLI with spaces & apostrophe's")
require.NoError(t, os.Mkdir(work, 0o700))
binary, err := os.Executable()
require.NoError(t, err)
executable := filepath.Join(work, "openai")
// This launcher exercises main(), preserving the invoked openai path in argv.
launcher := "#!/bin/sh\nOPENAI_CLI_MAIN_DISPATCH_PROCESS=1 exec " + quote(binary) +
" -test.run='^TestMainDispatchProcess$' -- \"$0\" \"$@\"\n"
require.NoError(t, os.WriteFile(executable, []byte(launcher), 0o700))
decoy := t.TempDir()
require.NoError(t, os.WriteFile(filepath.Join(decoy, "openai"), []byte("#!/bin/sh\nprintf 'wrong executable\\n'\nexit 99\n"), 0o700))
t.Setenv("PATH", decoy+string(os.PathListSeparator)+os.Getenv("PATH"))
home, directory := t.TempDir(), t.TempDir()
registry := projectLinkRegistryPath(home)
require.NoError(t, os.MkdirAll(filepath.Dir(registry), 0o700))
require.NoError(t, os.WriteFile(registry, []byte("{"), 0o600))
server, requests := globalFlagsServer(t, globalFlagsPage)
shell := nativeShell{name: "bash", executable: bash, args: []string{"--noprofile", "--norc", "-c"}}
// Raw data output also selects raw errors unless explicitly overridden.
// Copy the displayed text command, not its JSON string representation.
got := runNativeShell(t, shell, directory, home, server.URL,
"export OPENAI_API_KEY=synthetic-recovery-key; "+quote(executable)+" files list --format=raw --format-error=text")
require.NotZero(t, got.code)
require.Empty(t, got.stdout)
recovery := regexp.MustCompile(`Inspect with (.+? link)\.`).FindStringSubmatch(got.stderr)
require.Len(t, recovery, 2, "%s", got.stderr)
require.Equal(t, quote(executable)+" link", recovery[1])
require.Empty(t, requests)
// Repair the controlled fixture before following the printed inspection command.
require.NoError(t, os.WriteFile(registry, []byte("{}\n"), 0o600))
copied := runNativeShell(t, shell, directory, home, server.URL, recovery[1])
require.Zero(t, copied.code, "%s", copied.stderr)
require.Empty(t, copied.stderr)
require.Contains(t, copied.stdout, "This folder has no saved project link.")
require.NotContains(t, copied.stdout, "wrong executable")
require.Empty(t, requests, "recovery inspection reached the API")
}
Loading
Loading