diff --git a/CHANGELOG.md b/CHANGELOG.md index cef0fb4..2c938e0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -15,6 +15,10 @@ Earlier entries pre-date this convention and only carry their version's compare ## [Unreleased] +### Changed + +- Rename the `project show --json` token debug status to `signalingTokenDebugEnabled` and remove the misleading token capability warning from `project doctor`. Update consumers of the old `tokenEnabled` result field. + ## [0.2.10] - 2026-09-20 RTC video-call onboarding and package-manager compatibility. diff --git a/docs/automation.md b/docs/automation.md index f9a99e2..4a86a01 100644 --- a/docs/automation.md +++ b/docs/automation.md @@ -508,6 +508,8 @@ Safe branch fields: ### `project show` +Migration: `data.tokenEnabled` was renamed to `data.signalingTokenDebugEnabled`. The value is unchanged: `true` means the Console signaling Token debugging switch is on (`need_token=0`), not that token issuance is enabled. Consumers of `project show --json` must update the field name. + Example: ```bash @@ -521,7 +523,8 @@ Required `data` fields: - `projectName` - `appId` - `region` -- `tokenEnabled` +- `signalingTokenDebugEnabled` + Whether the Console signaling Token debugging switch is on (`true` means the upstream `need_token` value is `0`). Optional fields: - `appCertificate` @@ -535,7 +538,7 @@ Safe branch fields: - `projectName` - `appId` - `region` -- `tokenEnabled` +- `signalingTokenDebugEnabled` ### `project env write` diff --git a/docs/error-codes.md b/docs/error-codes.md index dc3b70f..95d8018 100644 --- a/docs/error-codes.md +++ b/docs/error-codes.md @@ -94,8 +94,7 @@ These codes appear inside `data.checks[].issues[].code` and (for blocking issues | `WORKSPACE_ENV_PROJECT_MISMATCH` | 1 | The quickstart env file points at a different App ID than the selected project. | Run the command from `suggestedCommand` to overwrite the env file. | | `WORKSPACE_ENV_APP_ID_MISSING` | 1 | A quickstart env file is missing the required app ID key. | Run the command from `suggestedCommand`. | | `WORKSPACE_ENV_APP_ID_MISMATCH` | 1 | A quickstart env file points at a different app ID. | Run the command from `suggestedCommand`. | -| `APP_CREDENTIALS_MISSING` | 1 | The selected project has no app ID / app certificate yet. | Run the command from `suggestedCommand` (`agora project show --project `) to re-fetch credentials; if still missing, enable the app certificate in Console (`agora open --target console`). | -| `TOKEN_CAPABILITY_DISABLED` | (warning) | The project has token issuance disabled. | Enable token issuance in Console. | +| `APP_CREDENTIALS_MISSING` | 1 | The selected project has no App ID in the API response. | Run the command from `suggestedCommand` (`agora project show --project `) to re-fetch project details. | ### Skills (curated workflows) diff --git a/internal/cli/app.go b/internal/cli/app.go index ea5ba64..25251a2 100644 --- a/internal/cli/app.go +++ b/internal/cli/app.go @@ -69,21 +69,21 @@ type projectSummary struct { } type projectDetail struct { - AllowStaticWithDynamic bool `json:"allowStaticWithDynamic"` - AppID string `json:"appId"` - CertificateEnabled bool `json:"certificateEnabled"` - CreatedAt string `json:"createdAt"` - Name string `json:"name"` - ProjectID string `json:"projectId"` - ProjectType string `json:"projectType"` - SignKey *string `json:"signKey"` - Stage int `json:"stage"` - Status string `json:"status"` - TokenEnabled bool `json:"tokenEnabled"` - UpdatedAt string `json:"updatedAt"` - Usage7d int `json:"usage7d"` - UseCaseID *string `json:"useCaseId,omitempty"` - Vid int `json:"vid"` + AllowStaticWithDynamic bool `json:"allowStaticWithDynamic"` + AppID string `json:"appId"` + CertificateEnabled bool `json:"certificateEnabled"` + CreatedAt string `json:"createdAt"` + Name string `json:"name"` + ProjectID string `json:"projectId"` + ProjectType string `json:"projectType"` + SignKey *string `json:"signKey"` + Stage int `json:"stage"` + Status string `json:"status"` + SignalingTokenDebugEnabled bool `json:"tokenEnabled"` + UpdatedAt string `json:"updatedAt"` + Usage7d int `json:"usage7d"` + UseCaseID *string `json:"useCaseId,omitempty"` + Vid int `json:"vid"` } type projectListResponse struct { diff --git a/internal/cli/app_test.go b/internal/cli/app_test.go index 63cd19f..0564075 100644 --- a/internal/cli/app_test.go +++ b/internal/cli/app_test.go @@ -267,15 +267,17 @@ func TestMergeEnvAssignmentsUpdatesExpectedAndCommentsConflicts(t *testing.T) { } } -func TestBuildProjectDoctorResultWarning(t *testing.T) { - project := projectDetail{ProjectID: "prj_1", Name: "Alpha", AppID: "app_1", TokenEnabled: false} - result := buildProjectDoctorResult(project, "global", []featureItem{ - {Feature: "rtc", Message: "rtc included with the project", Status: "included"}, - {Feature: "rtm", Message: "rtm enabled", Status: "enabled"}, - {Feature: "convoai", Message: "convoai enabled", Status: "enabled"}, - }, "convoai", false) - if result.Status != "warning" { - t.Fatalf("expected warning, got %s", result.Status) +func TestBuildProjectDoctorIgnoresSignalingTokenDebug(t *testing.T) { + for _, debugEnabled := range []bool{false, true} { + project := projectDetail{ProjectID: "prj_1", Name: "Alpha", AppID: "app_1", SignalingTokenDebugEnabled: debugEnabled} + result := buildProjectDoctorResult(project, "global", []featureItem{ + {Feature: "rtc", Message: "rtc included with the project", Status: "included"}, + {Feature: "rtm", Message: "rtm enabled", Status: "enabled"}, + {Feature: "convoai", Message: "convoai enabled", Status: "enabled"}, + }, "convoai", false) + if !result.Healthy || result.Status != "healthy" || len(result.Warnings) != 0 { + t.Fatalf("debugEnabled=%v: unexpected doctor result: %+v", debugEnabled, result) + } } } diff --git a/internal/cli/doctor.go b/internal/cli/doctor.go index f54be21..65f9eb4 100644 --- a/internal/cli/doctor.go +++ b/internal/cli/doctor.go @@ -414,12 +414,6 @@ func buildProjectDoctorResult(project projectDetail, region string, features []f SuggestedCommand: recoveryCmd, }) } - if project.TokenEnabled { - configItems = append(configItems, doctorCheckItem{Name: "token_capability", Message: "Token capability enabled for the project", Status: "pass"}) - } else { - configItems = append(configItems, doctorCheckItem{Name: "token_capability", Message: "Token capability is disabled for this project", Status: "warn"}) - warnings = append(warnings, doctorIssue{Code: "TOKEN_CAPABILITY_DISABLED", Message: "Token capability is disabled for this project"}) - } targetName := strings.ToUpper(feature) readinessItems := []doctorCheckItem{{Name: "control_plane_readiness", Message: "Project is ready for " + targetName + " development", Status: "pass"}} if len(blocking) > 0 { diff --git a/internal/cli/integration_project_test.go b/internal/cli/integration_project_test.go index 45e2b43..1388168 100644 --- a/internal/cli/integration_project_test.go +++ b/internal/cli/integration_project_test.go @@ -220,6 +220,7 @@ func TestCLIProjectUseShowFeatureAndDoctorHappyPath(t *testing.T) { beta := buildFakeProject("Project Beta", "prj_9999", "app_9999", "cn") beta.FeatureState.ConvoAIEnabled = true beta.FeatureState.RTMEnabled = true + beta.TokenEnabled = false api.projects[alpha.ProjectID] = &alpha api.projects[beta.ProjectID] = &beta persistSessionForIntegration(t, configHome) @@ -228,7 +229,7 @@ func TestCLIProjectUseShowFeatureAndDoctorHappyPath(t *testing.T) { "XDG_CONFIG_HOME": configHome, "AGORA_API_BASE_URL": api.baseURL, "AGORA_LOG_LEVEL": "error", - }}) + }, workdir: configHome}) if useResult.exitCode != 0 || !strings.Contains(useResult.stdout, `"projectId":"prj_9999"`) { t.Fatalf("unexpected use result: %+v", useResult) } @@ -238,16 +239,24 @@ func TestCLIProjectUseShowFeatureAndDoctorHappyPath(t *testing.T) { "AGORA_API_BASE_URL": api.baseURL, "AGORA_LOG_LEVEL": "error", "AGORA_OUTPUT": "pretty", - }}) - if showPretty.exitCode != 0 || !strings.Contains(showPretty.stdout, "App Certificate") || !strings.Contains(showPretty.stdout, "Region") || !strings.Contains(showPretty.stdout, "[hidden]") || strings.Contains(showPretty.stdout, "4854d28b48a9439c9f2546e2216fc07a") { + }, workdir: configHome}) + if showPretty.exitCode != 0 || !strings.Contains(showPretty.stdout, "App Certificate") || !strings.Contains(showPretty.stdout, "Region") || !strings.Contains(showPretty.stdout, "Signaling Token Debug : no") || !strings.Contains(showPretty.stdout, "[hidden]") || strings.Contains(showPretty.stdout, "4854d28b48a9439c9f2546e2216fc07a") { t.Fatalf("unexpected pretty show output (cert must be [hidden]): %+v", showPretty) } + showJSON := runCLI(t, []string{"project", "show", "--json"}, cliRunOptions{env: map[string]string{ + "XDG_CONFIG_HOME": configHome, + "AGORA_API_BASE_URL": api.baseURL, + "AGORA_LOG_LEVEL": "error", + }, workdir: configHome}) + if showJSON.exitCode != 0 || !strings.Contains(showJSON.stdout, `"signalingTokenDebugEnabled":false`) || strings.Contains(showJSON.stdout, `"tokenEnabled"`) { + t.Fatalf("unexpected JSON show output: %+v", showJSON) + } featureStatus := runCLI(t, []string{"project", "feature", "status", "convoai", "--json"}, cliRunOptions{env: map[string]string{ "XDG_CONFIG_HOME": configHome, "AGORA_API_BASE_URL": api.baseURL, "AGORA_LOG_LEVEL": "error", - }}) + }, workdir: configHome}) if featureStatus.exitCode != 0 || !strings.Contains(featureStatus.stdout, `"status":"enabled"`) { t.Fatalf("unexpected feature status: %+v", featureStatus) } @@ -256,7 +265,7 @@ func TestCLIProjectUseShowFeatureAndDoctorHappyPath(t *testing.T) { "XDG_CONFIG_HOME": configHome, "AGORA_API_BASE_URL": api.baseURL, "AGORA_LOG_LEVEL": "error", - }}) + }, workdir: configHome}) if doctor.exitCode != 0 || !strings.Contains(doctor.stdout, `"status":"healthy"`) { t.Fatalf("unexpected doctor result: %+v", doctor) } @@ -265,7 +274,7 @@ func TestCLIProjectUseShowFeatureAndDoctorHappyPath(t *testing.T) { "XDG_CONFIG_HOME": configHome, "AGORA_API_BASE_URL": api.baseURL, "AGORA_LOG_LEVEL": "error", - }}) + }, workdir: configHome}) if rtmDoctor.exitCode != 0 || !strings.Contains(rtmDoctor.stdout, `"feature":"rtm"`) || !strings.Contains(rtmDoctor.stdout, `"status":"healthy"`) { t.Fatalf("unexpected rtm doctor result: %+v", rtmDoctor) } diff --git a/internal/cli/projects.go b/internal/cli/projects.go index 1fafbd8..b9b0898 100644 --- a/internal/cli/projects.go +++ b/internal/cli/projects.go @@ -467,7 +467,7 @@ func (a *App) projectShow(projectArg string) (map[string]any, error) { if err != nil { return nil, err } - return map[string]any{"action": "show", "appId": target.project.AppID, "appCertificate": target.project.SignKey, "projectId": target.project.ProjectID, "projectName": target.project.Name, "region": target.region, "tokenEnabled": target.project.TokenEnabled}, nil + return map[string]any{"action": "show", "appId": target.project.AppID, "appCertificate": target.project.SignKey, "projectId": target.project.ProjectID, "projectName": target.project.Name, "region": target.region, "signalingTokenDebugEnabled": target.project.SignalingTokenDebugEnabled}, nil } type envFormat string diff --git a/internal/cli/render.go b/internal/cli/render.go index b347d96..4a3a291 100644 --- a/internal/cli/render.go +++ b/internal/cli/render.go @@ -55,7 +55,7 @@ func renderResult(cmd *cobra.Command, command string, data any) error { printBlock(out, "Current Project", [][2]string{{"Name", asString(m["projectName"])}, {"Project ID", asString(m["projectId"])}, {"Region", asString(m["region"])}}) case "project show": m := data.(map[string]any) - printBlock(out, "Project", [][2]string{{"Name", asString(m["projectName"])}, {"Project ID", asString(m["projectId"])}, {"App ID", asString(m["appId"])}, {"App Certificate", redactSensitive(m["appCertificate"])}, {"Region", asString(m["region"])}, {"Token Enabled", asString(m["tokenEnabled"])}}) + printBlock(out, "Project", [][2]string{{"Name", asString(m["projectName"])}, {"Project ID", asString(m["projectId"])}, {"App ID", asString(m["appId"])}, {"App Certificate", redactSensitive(m["appCertificate"])}, {"Region", asString(m["region"])}, {"Signaling Token Debug", asString(m["signalingTokenDebugEnabled"])}}) case "project env write": m := data.(map[string]any) printBlock(out, "Project Env", [][2]string{{"Project", asString(m["projectName"])}, {"Project ID", asString(m["projectId"])}, {"Path", asString(m["path"])}, {"Status", asString(m["status"])}})