Skip to content

feat(codeup): add a Codeup (codeup.aliyun.com) git provider - #1857

Open
killingwolf wants to merge 6 commits into
Tencent:mainfrom
killingwolf:feat/codeup-provider
Open

killingwolf wants to merge 6 commits into
Tencent:mainfrom
killingwolf:feat/codeup-provider

Conversation

@killingwolf

@killingwolf killingwolf commented Nov 13, 2026 •

Copy link
Copy Markdown

What

Adds a Codeup (Aliyun Yunxiao code management, codeup.aliyun.com) git provider so teamai push opens merge requests automatically instead of falling back to the generic git transport, which only pushes the branch.

Why

Codeup is a mainstream Aliyun code hosting platform. With the generic git provider, a contributor's teamai/push/* branch lands on the host, but the merge request has to be opened by hand on the web. This provider closes that gap the same way the GitLab and GitCode providers do for their hosts.

How

  • REST client for the Yunxiao OpenAPI (https://openapi-rdc.aliyuncs.com, x-yunxiao-token PAT auth). Codeup calls merge requests changeRequests: created with POST …/repositories/{id}/changeRequests and sourceBranch/targetBranch/sourceProjectId/targetProjectId; the numeric repository id is resolved from the full org/group/repo path, the organizationId from CODEUP_ORG_ID or the authenticated user's lastOrganization. Request/response shapes follow the official alibabacloud-devops-mcp-server reference implementation.
  • Token resolution: CODEUP_TOKEN > YUNXIAO_TOKEN > ~/.netrc, with an interactive paste-on-first-init flow that persists to ~/.netrc (0600) plus ~/.teamai/codeup.json (organizationId / username). The netrc read/write handles one-line and multiline machine entries.
  • authenticate() handles a PAT with only codeup scope: the platform user endpoint answers 403 for such tokens (verified live), so the token is validated with an org-scoped repository-list probe and the username comes from CODEUP_USERNAME / the config file. A missing organizationId is asked and persisted interactively; a Region-edition deployment needs none, and an unreachable API is reported as unreachable rather than as a rejected token.
  • Git transport follows the URL the member typed: an https:// URL clones over HTTPS — with the PAT as the Basic password when its username is known too (CODEUP_USERNAME / ~/.teamai/codeup.json / ~/.netrc), otherwise through the member's own git credential setup; an SSH URL or the bare org/group/repo form clones over SSH. CODEUP_TRANSPORT=https|ssh overrides.
  • Multi-level repo paths (org/group/repo) are kept whole — owner holds the first path segment and repo the rest, so ${owner}/${repo} reproduces the full path. Web-route suffixes (/change/1, /tree/main, …) are stripped only in browser URLs and only when a sub-path follows the route segment, so a nested group named like a route (org/team/files/repo) survives. A URL on an explicit foreign host is rejected.
  • findOpen/history lookup for workflow delivery: strictly parsed (an unreadable answer throws, never "no requests"), paginated to a bounded cap, history merged across the three states newest first. Region-edition support via CODEUP_API_BASE_URL / YUNXIAO_EDITION, a doctor check, docs, and 48 unit tests.
  • Reviewers from teamai.yaml are not assigned: the OpenAPI wants numeric reviewerUserIds, which teamai does not resolve today.

Verification

  • 48 unit tests (src/__tests__/codeup-provider.test.ts), plus an opt-in live e2e test (src/__tests__/e2e/codeup-provider-live.test.ts, gated on CODEUP_TOKEN + CODEUP_ORG_ID).
  • Verified end-to-end against a live Codeup organization: repository id resolution, MR creation, 409 duplicate protection, open-MR lookup by branch, and the three error paths (missing token, rejected token, missing organization id) answering with actionable guidance.
  • Field names verified live: state (not status), createdAt/updatedAt, and the MR web path /change/<id>.
  • Real-CLI run in the verify world 20261114T021949-bbcce4 — Build f693074f7f73+dirty, teamai 0.22.0, sha256 df4d06266f99 (the +dirty is the untracked node_modules symlink of the build worktree, not a source change) — against a local stub of the Yunxiao OpenAPI answering the exact shapes above (codeup-scope-only PAT: /oapi/v1/platform/user → 403), with the world's gitconfig rewriting https://codeup.aliyun.com/team/repo.git to a seeded bare repo and a trap rewriting git@codeup.aliyun.com: to a nonexistent path so any SSH attempt fails loudly:
    • teamai init https://codeup.aliyun.com/team/repo.git --agent claude → exit 0: Authenticated as zhangsan (403 whoami → org repo-list probe 200 → CODEUP_USERNAME), Team repo cloned over HTTPS — the SSH trap never fired (the same input cloned git@codeup.aliyun.com … at 2a9a8c3, the P1 regression; fixed), teamai.yaml written with provider: codeup, member registered, claude hooks installed.
    • teamai doctor → exit 0, all checks pass, including the new ✔ Codeup token is configured.
    • teamai push --skill .claude/skills/verify-skill --shared --all → exit 0: pushed branch teamai/push/zhangsan/20261114-103208 (over the same HTTPS URL), resolved the numeric repo id (GET …/repositories/team%2Frepo → 2813489), POST …/repositories/2813489/changeRequests → ✔ Pull Request created: https://codeup.aliyun.com/team/repo/change/1.
    • Ledger last line: Ledger: 1 LEAK — the empty project/.claude/skills directory after teamai uninstall; generic init creates it for every provider (this provider installs no directory or hook of its own) and it is the same family as the known leftover issue teamai uninstall leaves files and directories that init and pull created #1808 listed next to it. No codeup-specific residue: ~/.netrc untouched (token came via env) and ~/.teamai/codeup.json absent (the headless path persists nothing).
    • VERDICT — VERIFIED: claude x codeup x workspace scope via teamai init + teamai push --skill.

🤖 Generated with Claude Code

killingwolf and others added 2 commits November 13, 2026 15:25
Adds a provider for Aliyun Yunxiao (云效) Codeup so `teamai push` opens
merge requests automatically instead of falling back to the generic git
transport, which only pushes the branch.

- src/providers/codeup/: REST client for the Yunxiao OpenAPI
  (openapi-rdc.aliyuncs.com, x-yunxiao-token PAT auth). MRs are created
  as changeRequests with sourceBranch/targetBranch/sourceProjectId/
  targetProjectId; the numeric repo id is resolved from the full
  org/group/repo path, the organizationId from CODEUP_ORG_ID or the
  user's lastOrganization. whoami is /oapi/v1/platform/user.
- Token resolution: CODEUP_TOKEN > YUNXIAO_TOKEN > ~/.netrc, with an
  interactive paste-on-first-init flow that persists to ~/.netrc (0600).
- Git transport stays SSH (git@codeup.aliyun.com:...), like the generic
  provider; HTTPS Basic is used when a netrc username+token pair exists.
- Multi-level repo paths (org/group/repo) are kept whole; browser
  web-route suffixes (/changeRequest/list, /tree/main, ...) are stripped.
- findOpen/history lookup for workflow delivery, Region-edition support
  via CODEUP_API_BASE_URL / YUNXIAO_EDITION, doctor check, and docs.
- Request/response shapes verified against the official
  alibabacloud-devops-mcp-server implementation.

Reviewers from teamai.yaml are not assigned: the OpenAPI wants numeric
reviewerUserIds, which teamai does not resolve.

Co-Authored-By: Claude Code <noreply@anthropic.com>
authenticate() depended on GET /oapi/v1/platform/user returning a
username, but a PAT created with only codeup scope gets 403 there
(verified live) — every `teamai init` with such a token exited at the
auth gate. Now the token is validated with a codeup-scoped probe (the
org repository list) and the username comes from CODEUP_USERNAME or
~/.teamai/codeup.json; interactive init asks for the missing
organizationId and username, validates both, and persists them.

cloneUrl() also stopped switching to HTTPS Basic (PAT as the git
password — an unverified path) as soon as a netrc username+token pair
existed: SSH stays the default, and CODEUP_TRANSPORT=https opts in
explicitly.

Verified end-to-end against a live codeup-scope-only PAT: init auth,
clone, pull, and all three error paths (missing token, rejected token,
missing org id) answer with actionable guidance.

Co-Authored-By: Claude Code <noreply@anthropic.com>
@github-actions

Copy link
Copy Markdown
  • [P1 blocking] Missing real-CLI verification record. The PR changes runtime behavior, but its Verification section records API operations and provider tests, not a real CLI run in a verify world. The Code Review Rules require an end-to-end / real-CLI record for runtime changes. Add one representative world run and its actual result.

  • [P2 non-blocking] Nested repository paths are truncated at route-like names. In src/providers/codeup/repo-url.ts:76, org/group/wiki becomes org/group, and org/group/tree/repo.git is likewise truncated. Route stripping also applies to short inputs and SSH clone URLs, where these segments belong to the repository path. Preserve those paths rather than treating every matching segment as a browser suffix.

  • [P2 non-blocking] Interactive init cannot collect a missing organization ID for an existing token. In src/providers/codeup/index.ts:69, a configured codeup-only PAT without CODEUP_ORG_ID throws even interactively, while the error recommends that same interactive command. Prompt and persist the missing ID. This check also unnecessarily rejects Region-edition authentication, whose API paths need no organization ID.

  • [P2 non-blocking] HTTPS transport ignores the configured username. In src/providers/codeup/codeup-api.ts:454, cloning reads the username exclusively from .netrc. With CODEUP_TOKEN, CODEUP_USERNAME, and CODEUP_TRANSPORT=https configured but no .netrc entry, it silently uses SSH instead. Consult configuredUsername() before falling back to .netrc.

  • [P2 non-blocking] Valid multiline .netrc credentials are not handled correctly. In src/providers/codeup/codeup-api.ts:132, the token regex cannot cross a newline between login and password. Moreover, writeNetrcToken() removes only the machine line of such an entry, leaving its credential lines behind. Read and replace complete machine entries.

  • [P2 non-blocking] Lookup parsing converts unreadable responses into “no requests.” In src/providers/codeup/codeup-api.ts:638 and src/providers/codeup/codeup-api.ts:699, invalid JSON or an unexpected response shape becomes an empty array; invalid individual records become {} and are skipped. Both lookups promise to throw when they cannot determine request state. Reject malformed responses instead of returning an authoritative absence.

  • [P2 non-blocking] History is not returned newest first. In src/providers/codeup/codeup-api.ts:733, independently sorted open, merged, and closed batches are returned concatenated. A newer closed request therefore follows an older merged request, contradicting the function’s stated ordering contract. Sort the combined records before returning.

  • [P2 non-blocking] Transport documentation describes behavior the implementation does not perform. The new Git transport paragraph in docs/guide/providers.md says storing a token and username in .netrc automatically switches cloning to HTTPS. cloneUrl() requires CODEUP_TRANSPORT=https; without it, SSH remains the default. Document that opt-in explicitly.

Reviewed only the specified diff; nothing from the PR was run, built, or installed.

@SaulMoro

Copy link
Copy Markdown
Collaborator

This was generated by AI during triage.

I ran the built CLI in verify worlds at 2a9a8c3 against a local stub of the Yunxiao OpenAPI. Live Codeup wasn't available. init with a codeup URL authenticates and writes provider: codeup. doctor reports the API key check. push --all opens a merge request (/change/1). On main, the same URL falls back to provider: git, and push stops at "not supported for generic Git hosts". So the main path works. Two things block merge, and several more should be fixed.

[P1 blocking]

  • An HTTPS codeup URL is cloned over SSH. cloneUrl (src/providers/codeup/codeup-api.ts) ignores the scheme of the URL the member typed. It returns git@codeup.aliyun.com:<repo>.git unless CODEUP_TRANSPORT=https is set, an API key is present, and ~/.netrc holds a username. Main clones the URL as typed. In a world, teamai init https://codeup.aliyun.com/<org>/<repo>.git succeeds on main and fails on this PR at clone (git@codeup.aliyun.com … git-upload-pack), before any config is written. A member who uses HTTPS git sign-in and has no SSH key can no longer init. The documented opt-in doesn't help with the env setup: with CODEUP_TOKEN + CODEUP_USERNAME + CODEUP_TRANSPORT=https and no netrc entry, the username is null and the clone still goes over SSH. The "SSH, exactly as the generic git provider does" comment and docs/guide/providers.md say otherwise. Take the transport from the URL's scheme, read the username from CODEUP_USERNAME / codeup.json too, and add a cloneUrl case for each scheme.
  • The generated command reference is stale. --provider's help in src/index.ts now lists codeup, but skill-data/core/references/commands.md still has the old list. npm run lint runs check-docs, which fails on that. Fork CI hasn't run yet (four workflows are waiting for approval), so this comes from reading the generator. Run node scripts/check-docs.mjs --write. fix(push): restore --file/--path, removed in 0.30.0 on a mistaken premise #1855 and feat(local-agent): add --replace-token to use the new token without checking the old one #1859 regenerate the same file, so whichever lands second has to regenerate again.

[P2 non-blocking]

  • Dead end for an API key without an org ID. When a codeup-only key can't read the user endpoint and CODEUP_ORG_ID is unset, the provider throws before any prompt, and the message tells the member to run teamai init interactively, which doesn't ask for it. Region edition doesn't need an org ID but is refused the same way.
  • Contract breaches in the PR lookups (src/providers/types.ts):
    • findOpen and history parse with .catch([]), so a 200 with an HTML or error body answers "no open request", where the contract says to throw.
    • history isn't sorted newest first.
    • The 50-row cap is checked on the repo-wide page, so any repo with 49 or more merged requests never returns history. I checked all three with probes.
  • ~/.netrc with a multi-line entry (machine / login / password on separate lines, which is valid netrc) isn't read. Writing a new key removes only the machine line, so the old password line stays and ends up under the entry above it.
  • An unreachable API is reported as a rejected API key. The probe returns no status on a network error, and the message tells the member to generate a new key.
  • Repo path parsing. A nested group named like a web route (org/team/files/repo, org/group/wiki) is cut at that segment and resolves to org/team, which may be a different repo the member can write to.
  • Docs. docs/guide/zh-CN/providers.md has no Codeup section, and README.zh-CN.md, README.ja.md, README.ko.md and README.th.md don't list Codeup while README.md does. Each guide page and every README change together.

[P3 nit]

  • Merge-request creation has no default timeout; GitLab's defaults to 30 s.
  • --provider codeup accepts another host's URL (the same as gitcode).
  • docs/dev/glossary.md and .github/CONTRIBUTING.md list the providers without codeup.

CI review. We agree with seven of its eight findings. Its "short inputs" path case didn't reproduce, because of the i >= 2 guard. It missed the SSH clone, the stale reference, the history cap, the API key error message and the docs.

Merge risk. High until the SSH clone is fixed: it breaks init for HTTPS codeup users, which works on main today. Otherwise it's contained to codeup hosts, and a revert sends them back to provider: git. ~/.netrc and ~/.teamai/codeup.json entries would stay until removed by hand, as for the other providers today.

@jeff-r2026 your call on this one. It's a new provider, with its own API key storage and a change to how codeup URLs are cloned.

@SaulMoro
SaulMoro requested a review from jeff-r2026 November 13, 2026 09:39
killingwolf and others added 3 commits November 14, 2026 09:24
…, netrc and MR lookups

Co-Authored-By: Claude Code <noreply@anthropic.com>
… the generated reference

Co-Authored-By: Claude Code <noreply@anthropic.com>
@github-actions

Copy link
Copy Markdown

Findings

  • [P1 blocking] Missing real-CLI verification record. The PR description still records provider tests and live API operations, not a real CLI run in a verify world. The Code Review Rules explicitly require an end-to-end / real-CLI record for runtime changes. Add one representative world run and its actual result.
  • [P2 non-blocking] Malformed records still become “no requests.” In src/providers/codeup/codeup-api.ts:635, both branch fields are optional, so [{}] or records missing a branch pass validation and are silently skipped. findOpen then returns undefined, and history can return [], despite being unable to determine request state. Require readable branch fields before filtering. The earlier parsing finding is only partially resolved.
  • [P2 non-blocking] Nested browser repository URLs remain truncated. In src/providers/codeup/repo-url.ts:121, https://codeup.aliyun.com/org/group/tree/repo resolves to org/group because tree is treated as a browser suffix. This supported extensionless HTTPS form can therefore select a different repository. Short forms, SSH URLs, and .git URLs are fixed, but route-named nested groups in browser URLs remain affected.
  • [P2 non-blocking] Authentication ignores a known .netrc username. In src/providers/codeup/index.ts:112, the codeup-scope fallback checks only configuredUsername(). With a valid codeup-only PAT and login in .netrc, a configured organization ID, and no separate username setting, noninteractive authentication throws “username is unknown”; interactive authentication unnecessarily prompts again. Fall back to readNetrcUsername(), as cloning already does.

Resolved Earlier Findings
The missing organization-ID prompt and Region-edition check, configured HTTPS username handling, multiline .netrc handling, combined history ordering, and transport documentation mismatch are resolved.

Reviewed only the specified diff; nothing from the PR was run, built, or installed.

@killingwolf

Copy link
Copy Markdown
Author

Pushed f693074 (upstream/main merged in, no force-push) addressing the triage review:

P1 blocking

  • HTTPS URL cloned over SSH — fixed. parseRepoInput records the transport of the URL as typed; cloneUrl follows it: an https:// input clones over HTTPS (with the PAT as the Basic password when the username is known too, read from CODEUP_USERNAME → ~/.teamai/codeup.json → ~/.netrc; otherwise plain, through the member's git credential setup), an SSH URL or the bare form clones over SSH, and CODEUP_TRANSPORT=https|ssh overrides. The world record in the PR body re-runs the failing scenario: init with the HTTPS URL clones through the HTTPS rewrite, the SSH trap never fires, provider: codeup lands in teamai.yaml, doctor passes, and push --skill opens /change/1.
  • Stale command reference — regenerated with node scripts/check-docs.mjs --write after merging upstream (which had landed feat(local-agent): add --replace-token to use the new token without checking the old one #1859's regeneration); npm run lint passes.
  • Missing real-CLI record — added to the PR body: init + doctor + push→MR in world 20261114T021949-bbcce4 against a local stub of the Yunxiao OpenAPI, with the ledger's last line and the leak attributed.

P2 non-blocking

  • Org-id dead end: interactive authenticate() now asks for a missing organizationId and persists it to ~/.teamai/codeup.json (headless keeps the guidance error); the Region edition skips the requirement entirely.
  • Lookup contracts: findOpen/history parse strictly — an unreadable body throws instead of answering "none" (the per-item .catch({}) is gone, and an unreadable state on a branch-matching request throws); both paginate to a bounded cap (10 × 50) instead of giving up on a full repo-wide first page; history merges the three states newest-first.
  • Multiline ~/.netrc: the entry is read by tokenizing (login/password in any order, one line or many), and writing replaces the whole machine entry — no orphaned credential lines.
  • An unreachable API (probe with no HTTP status) is reported as unreachable, distinct from a rejected token.
  • Repo path parsing: web-route stripping now applies only to browser URLs (http(s) scheme, no .git suffix) and only when a sub-path follows the route segment, so org/team/files/repo and org/group/wiki survive in bare, SSH and .git forms; /changeRequest/list and /tree/main suffixes still strip.
  • Docs: docs/guide/zh-CN/providers.md has the full Codeup section; README.zh-CN/ja/ko/th list Codeup; the transport paragraph now matches the implementation (scheme-following, CODEUP_TRANSPORT opt-in, PAT embedded only with a known username). Also updated the provider lists in usage-guide, product-overview and getting-started (EN + zh-CN).

P3 nit

  • MR creation defaults to a 30 s timeout (spawnTimeoutMs ?? 30_000), like GitLab.
  • parseRepoInput rejects a URL whose explicit host is not codeup.aliyun.com, so --provider codeup cannot be aimed at another host.
  • docs/dev/glossary.md and .github/CONTRIBUTING.md provider lists include codeup.

CI on the previous head failed on the oxlint type-aware errors in the new files and on init.test.ts's provider-list assertion; both are fixed — lint is green locally and the touched suites pass (294 tests). The unit test file now has 48 cases, covering the transport choice, netrc multiline round-trips, pagination/sort/strict parsing, region-edition auth, the unreachable-API message and the interactive org-id prompt.

…auth username from ~/.netrc too

Co-Authored-By: Claude Code <noreply@anthropic.com>
@killingwolf

Copy link
Copy Markdown
Author

f3180b3 addresses the second review pass:

  • [P1] Real-CLI record — the review pass started before the body edit landed; the PR body's Verification section now carries the full world record (init + doctor + push→MR against a local Yunxiao stub, build line, ledger last line with the one leak attributed).
  • [P2] Malformed records → "no requests" — fixed: sourceBranch/targetBranch are now required in ListedMrSchema, so a record whose branch fields cannot be read fails the strict parse and both lookups throw (findOpenMr throws on a record whose branch fields cannot be read covers [{"localId":1,"state":"UNDER_REVIEW"}]). Same strictness as the GitCode schemas.
  • [P2] Auth ignores the .netrc username — fixed: the codeup-scope fallback now resolves configuredUsername() ?? readNetrcUsername(), the same pairing the clone URL uses; no more spurious headless error or interactive re-prompt when the netrc entry carries the login.
  • [P2] Extensionless browser URL ambiguity — kept as is, deliberately: https://…/org/group/tree/repo is genuinely ambiguous (a repo path vs. the /tree/<ref> route of org/group), and stripping only applies to browser URLs whose route segment carries a sub-path. The alternatives are worse: never stripping breaks every pasted /tree/main or /changeRequest/list URL (the common case), and stripping everything reintroduces the silent truncation the first review flagged. The residual case now fails loudly in the common shape (the truncated prefix is a group, not a repo → 404 "not found on Codeup"), and both providers.md pages tell members with a route-named nested group to pass the .git clone URL or the bare form, which keep every segment.

codeup suite is at 50 cases; typecheck, npm run lint and the touched suites are green locally.

@github-actions

Copy link
Copy Markdown

Findings

  • [P2 non-blocking] Nested browser repository URLs still resolve to the wrong repository. In src/providers/codeup/repo-url.ts:121, https://codeup.aliyun.com/org/group/tree/repo becomes org/group, so init can clone a different repository. The documented .git workaround does not fix the earlier finding. Preserve ambiguous paths or reject them instead of silently truncating.
  • [P3 nit] An optional .netrc account field stops credential parsing. In src/providers/codeup/codeup-api.ts:157, encountering account reaches else break. A valid entry such as machine codeup.aliyun.com login alice account example password pt-token therefore yields no token. Consume the account pair and continue reading credentials.

Resolved Earlier Findings
The organization-ID prompt and Region handling, HTTPS transport and configured username, basic multiline .netrc handling, strict lookup parsing—including missing branch fields—combined history ordering, transport documentation, and authentication’s .netrc username fallback are resolved.

Testing
The description now supplies a representative real-CLI verify-world record covering init, doctor, and push/MR creation, resolving the missing-verification finding. Its older build revision is a note, not a blocker under the review rules.

Reviewed only the specified diff; nothing from the PR was run, built, or installed.

@SaulMoro

Copy link
Copy Markdown
Collaborator

This was generated by AI during triage.

Re-checked at f693074, then at f3180b3.

Fixed since our last comment

  • Both earlier P1s. In a verify world against a local stub of the OpenAPI:
    • an HTTPS URL clones over HTTPS, and an SSH URL clones over SSH;
    • CODEUP_TRANSPORT=https and CODEUP_TRANSPORT=ssh both override;
    • doctor and status pass, and push opens /change/1;
    • the world reports CLEAN.
      check-docs passes.
  • Re-run, now correct: the org-ID prompt, the 30 s timeout, the foreign-host refusal, multi-line ~/.netrc, the unreachable-API message, the zh-CN guide, and every README.
  • Read in the code, not run: history ordering and paging.
  • Fixed at f3180b3, read in the diff: auth now falls back to the ~/.netrc username (configuredUsername() ?? readNetrcUsername()), and sourceBranch/targetBranch are required in MR lists, so [{}] no longer reads as "no request".

Three things are still worth fixing or settling before merge.

[P2 non-blocking]

  • The API key ends up in the team clone's .git/config. When a key and a username are known, cloneUrl (codeup-api.ts:493-501) clones https://<user>:<key>@codeup.aliyun.com/<repo>.git, and git keeps that URL as remote.origin.url. The file is 0644. In the world it read https://alice:<key>@codeup.aliyun.com/…. Nothing rewrites it later, so after the member rotates the key, fetches keep using the old one. The comment at codeup-api.ts:489-491 also says a key as the git password "is not a verified path on every deployment". If Codeup refuses it, an HTTPS member whose own git sign-in would have worked can't init again, which is what the first P1 was about. docs/designs/gitcode-provider.md notes GitHub moved away from keys in clone URLs. The smaller option: clone the plain HTTPS URL and let the member's git sign-in answer, as main does for codeup today. That also removes the username lookup from the clone path. The API key then stays for OpenAPI calls only. CLI output, error output and config.yaml don't show the key; I checked.
  • A mistyped organization ID is saved before it's checked. askOrganizationId() saves the ID (index.ts:152) before probeCodeupToken runs (index.ts:100). With a typo, the probe fails and the message says the API key was rejected. The bad ID stays in ~/.teamai/codeup.json, and the next run reads it and never asks again. authenticateByPaste already saves only after a successful probe (index.ts:212-221); the same order fixes this. I read this from the code; it needs a terminal and a stub that refuses the ID.
  • Chinese in user-facing output. For example Region 版 (index.ts:30), 企业标识 … 云效 组织管理 → 基本信息 (index.ts:23,140-141, codeup-api.ts:360), 登录名 (index.ts:24), and 个人设置 / 个人访问令牌 (codeup-api.ts:85-86, index.ts:107,167). AGENTS.md asks for English-only CLI output. They help a member find the setting in Codeup's console, so either make them English or keep them with a maintainer's sign-off.

[P3 nit]

  • A browser URL with a nested group named like a route (…/org/team/files/repo) still resolves to org/team. The .git, SSH and short forms are right now, and the browser form can't be told apart without asking the API. You've said you're keeping this as it is, which is fine.
  • The clone relies on parseRepoInput having run first on the same provider object, which stores the transport. Both callers do that today, but nothing enforces it.

CI review. Its P1, the missing real-CLI record, read the body from before your edit 20 seconds later. The record is there now. Two of its P2s are fixed at f3180b3; the third is the browser URL above. The other CI workflows are waiting for approval to run on this fork.

Merge risk. Medium, mainly because of the API key in .git/config. The rest is contained to codeup hosts.

@jeff-r2026 your call on this one.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants