From a78896eeca84ce9fbf3981a1e1d82d553b5d9692 Mon Sep 17 00:00:00 2001 From: yoruuuchan <1587761204@qq.com> Date: Mon, 2 Nov 2026 14:17:06 +0900 Subject: [PATCH] fix(codearts): serve console credential import from the codec control.Accounts.Import resolves the importer through Adapter.Credential.(providers.CredentialImporter), but the CodeArts adapter put Format and PrepareImport on the ImportExport slot instead. Every console import of a codearts-oauth-v1 credential therefore answered unsupported_format: no importer matched the format and ImportAll fell through to its final error. Move the two methods onto credentialCodec, matching kimi and qwenwork, and pin the wiring in the adapter contract test so the slot cannot drift again. Verified against a real endpoint: the same POST /api/accounts/import body answers 400 unsupported_format on the published v0.7.26 image and 201 with a created account on this branch. --- .../unreleased/codearts-credential-import.md | 7 +++++ internal/providers/codearts/codearts_test.go | 2 +- internal/providers/codearts/import.go | 15 +++++---- .../providers/codearts/s04_interfaces_test.go | 31 ++++++++++++++++++- 4 files changed, 47 insertions(+), 8 deletions(-) create mode 100644 changelog/unreleased/codearts-credential-import.md diff --git a/changelog/unreleased/codearts-credential-import.md b/changelog/unreleased/codearts-credential-import.md new file mode 100644 index 00000000..934c3baf --- /dev/null +++ b/changelog/unreleased/codearts-credential-import.md @@ -0,0 +1,7 @@ +### English + +- CodeArts (码道) console credential import works again: the adapter exposes its importer where the console looks for it, so pasting a `codearts-oauth-v1` credential no longer answers `unsupported_format`. + +### 中文 + +- 码道(CodeArts)控制台「导入凭证」恢复正常:适配器按控制台查找的位置暴露导入器,粘贴 `codearts-oauth-v1` 凭证不再返回 `unsupported_format`。 \ No newline at end of file diff --git a/internal/providers/codearts/codearts_test.go b/internal/providers/codearts/codearts_test.go index 7b793d00..7bbd396e 100644 --- a/internal/providers/codearts/codearts_test.go +++ b/internal/providers/codearts/codearts_test.go @@ -672,7 +672,7 @@ func TestAdapterSurface(t *testing.T) { t.Fatalf("adapter missing %s", capability) } } - var _ providers.CredentialImporter = importer{} + var _ providers.CredentialImporter = credentialCodec{} var _ providers.ModelCatalogProvider = client var _ providers.ProviderChat = client var _ providers.AccountProber = client diff --git a/internal/providers/codearts/import.go b/internal/providers/codearts/import.go index b492f371..490d24af 100644 --- a/internal/providers/codearts/import.go +++ b/internal/providers/codearts/import.go @@ -6,16 +6,19 @@ import ( "github.com/caigee-cmd/cli2api/internal/providers" ) -type importer struct{} - -func (importer) Format() string { return CredentialFormat } - -func (importer) ValidateImport(payload []byte) error { return ValidateCredential(payload) } +// The console resolves credential import through Adapter.Credential +// (providers.CredentialImporter), so the codec carries Format and PrepareImport. +// The ImportExport slot keeps ValidateImport and Export only. +func (credentialCodec) Format() string { return CredentialFormat } -func (importer) PrepareImport(payload []byte) (providers.CredentialImport, error) { +func (credentialCodec) PrepareImport(payload []byte) (providers.CredentialImport, error) { return PrepareImport(payload) } +type importer struct{} + +func (importer) ValidateImport(payload []byte) error { return ValidateCredential(payload) } + // Export is intentionally unsupported. A CodeArts credential contains the STS // keys and the refresh token; exporting it would hand out the account. func (importer) Export(context.Context, string) (map[string]any, error) { diff --git a/internal/providers/codearts/s04_interfaces_test.go b/internal/providers/codearts/s04_interfaces_test.go index 3b40f6b1..35caa144 100644 --- a/internal/providers/codearts/s04_interfaces_test.go +++ b/internal/providers/codearts/s04_interfaces_test.go @@ -27,13 +27,13 @@ func (requiredStore) Observe(context.Context, string, string, string, string, st func TestS04InterfaceContract(t *testing.T) { client := NewClient(requiredStore{}) var _ providers.CredentialCodec = credentialCodec{} + var _ providers.CredentialImporter = credentialCodec{} var _ providers.LoginSessionProvider = client var _ providers.LoginCompleter = client var _ providers.ProviderChat = client var _ providers.ModelCatalogProvider = client var _ providers.AccountProber = client var _ providers.ErrorClassifier = classifier{} - var _ providers.CredentialImporter = importer{} var _ providers.ImportExporter = importer{} var _ providers.AccountCheckiner = client @@ -46,6 +46,35 @@ func TestS04InterfaceContract(t *testing.T) { t.Fatalf("adapter missing capability %q", capability) } } + // control.Accounts.Import reads the importer off Adapter.Credential, not + // Adapter.ImportExport: wiring PrepareImport on the wrong slot makes every + // console credential import answer unsupported_format. + if _, ok := adapter.Credential.(providers.CredentialImporter); !ok { + t.Fatal("Adapter.Credential must implement providers.CredentialImporter") + } +} + +func TestCredentialCodecServesConsoleImport(t *testing.T) { + encoded, err := testCredential().Encode() + if err != nil { + t.Fatalf("encode: %v", err) + } + // The console sends the format it reads from the registry and expects a + // ready payload back, so both halves are part of the import contract. + codec, ok := any(credentialCodec{}).(providers.CredentialImporter) + if !ok { + t.Fatal("credentialCodec must implement providers.CredentialImporter") + } + if got := codec.Format(); got != CredentialFormat { + t.Fatalf("format = %q, want %q", got, CredentialFormat) + } + prepared, err := codec.PrepareImport(encoded) + if err != nil { + t.Fatalf("prepare import: %v", err) + } + if !prepared.Ready || len(prepared.Payload) == 0 { + t.Fatalf("prepared = %+v", prepared) + } } func TestMissingSecretReaderSkipsGlobalProxy(t *testing.T) {