Skip to content
Open
Show file tree
Hide file tree
Changes from all 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
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
7 changes: 5 additions & 2 deletions docs/automation.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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`
Expand All @@ -535,7 +538,7 @@ Safe branch fields:
- `projectName`
- `appId`
- `region`
- `tokenEnabled`
- `signalingTokenDebugEnabled`

### `project env write`

Expand Down
3 changes: 1 addition & 2 deletions docs/error-codes.md
Original file line number Diff line number Diff line change
Expand Up @@ -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 <id>`) 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 <id>`) to re-fetch project details. |

### Skills (curated workflows)

Expand Down
30 changes: 15 additions & 15 deletions internal/cli/app.go
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down
20 changes: 11 additions & 9 deletions internal/cli/app_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)
}
}
}

Expand Down
6 changes: 0 additions & 6 deletions internal/cli/doctor.go
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down
21 changes: 15 additions & 6 deletions internal/cli/integration_project_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand All @@ -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)
}
Expand All @@ -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)
}
Expand All @@ -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)
}
Expand All @@ -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)
}
Expand Down
2 changes: 1 addition & 1 deletion internal/cli/projects.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
2 changes: 1 addition & 1 deletion internal/cli/render.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"])}})
Expand Down
Loading