copillm 0.3.0-beta.1 → 0.3.0-beta.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,5 +1,5 @@
1
1
  import { clearStoredCredential, loadStoredCredential, loadStoredCredentialForAccount, registerExistingCredentialAsDefault, saveStoredCredential } from "../../auth/credentials.js";
2
- import { readAccountsIndex, assertValidAccountId, InvalidAccountIdError, UnknownAccountError } from "../../auth/accounts.js";
2
+ import { readAccountsIndex, findAccount, assertValidAccountId, InvalidAccountIdError, UnknownAccountError } from "../../auth/accounts.js";
3
3
  import { addAccount, listAccountsDetailed, removeAccountAndCredential, removeAllAccounts, switchDefaultAccount } from "../../auth/accountManager.js";
4
4
  import { loginViaDeviceFlow } from "../../auth/deviceFlow.js";
5
5
  import { inspectGithubIdentity } from "../../auth/githubIdentity.js";
@@ -53,32 +53,71 @@ export async function runAuthLogin(opts, options) {
53
53
  const token = await loginViaDeviceFlow();
54
54
  const saveMode = options.forceSession ? "session" : "auto";
55
55
  const index = readAccountsIndex();
56
- // Pure single-account login: no index and no explicit name. Preserve the
57
- // historical behaviour exactly — legacy storage, no accounts index created.
58
- if (!index && !namedRequested) {
59
- const backend = await saveStoredCredential(token, accountType, { mode: saveMode });
60
- writeCommandOutput(opts, `Login succeeded. Credentials stored via ${describeBackend(backend)}.`, {
61
- status: "ok",
62
- action: "login",
63
- credential_backend: backend
64
- });
56
+ // ---- Explicit name (`--as`) -------------------------------------------
57
+ if (namedRequested) {
58
+ const accountId = opts.as.trim();
59
+ // First time we materialize the index via an explicit name: preserve any
60
+ // pre-existing single account as the default so its token isn't clobbered.
61
+ if (!index) {
62
+ const existing = await loadStoredCredential();
63
+ if (existing) {
64
+ const existingId = (await deriveAccountId(existing.token)) ?? "default";
65
+ if (existingId !== accountId) {
66
+ registerExistingCredentialAsDefault(existingId, existing.accountType);
67
+ }
68
+ }
69
+ }
70
+ const result = await addAccount({ id: accountId, accountType, token, mode: saveMode });
71
+ emitLoginResult(opts, result, false);
65
72
  return;
66
73
  }
67
- const accountId = namedRequested ? opts.as.trim() : index.defaultAccount;
68
- // First time we materialize the index via an explicit name: preserve any
69
- // pre-existing single account as the default so its token isn't clobbered.
70
- if (!index && namedRequested) {
74
+ // ---- No name: auto-manage by the token's GitHub login -----------------
75
+ // Deriving the login lets `copillm auth login` recognise when a *different*
76
+ // GitHub account is signing in and keep both, instead of silently
77
+ // overwriting the previous one. A same-account re-login refreshes in place.
78
+ const newLogin = await deriveAccountId(token);
79
+ if (index) {
80
+ // Add the new login as its own account (or refresh it if already known);
81
+ // when the login can't be determined, refresh the default account.
82
+ const targetId = newLogin ?? index.defaultAccount;
83
+ const wasKnown = findAccount(targetId) !== null;
84
+ const result = await addAccount({ id: targetId, accountType, token, mode: saveMode });
85
+ emitLoginResult(opts, result, !wasKnown && !result.isDefault);
86
+ return;
87
+ }
88
+ // No accounts index yet. If a *different* GitHub account is signing in, move
89
+ // to multi-account instead of overwriting the existing single credential.
90
+ if (newLogin) {
71
91
  const existing = await loadStoredCredential();
72
92
  if (existing) {
73
- const existingId = (await deriveAccountId(existing.token)) ?? "default";
74
- if (existingId !== accountId) {
75
- registerExistingCredentialAsDefault(existingId, existing.accountType);
93
+ const existingLogin = await deriveAccountId(existing.token);
94
+ if (existingLogin !== newLogin) {
95
+ // Different (or undeterminable) account → preserve the prior login as
96
+ // the default and add the new one alongside it.
97
+ registerExistingCredentialAsDefault(existingLogin ?? "default", existing.accountType);
98
+ const result = await addAccount({ id: newLogin, accountType, token, mode: saveMode });
99
+ emitLoginResult(opts, result, !result.isDefault);
100
+ return;
76
101
  }
102
+ // Same account → fall through to an in-place single-account refresh.
77
103
  }
78
104
  }
79
- const result = await addAccount({ id: accountId, accountType, token, mode: saveMode });
105
+ // Single-account login: no prior credential, the same account re-logging in,
106
+ // or an undeterminable login. Preserve the historical behaviour exactly —
107
+ // legacy storage, no accounts index created.
108
+ const backend = await saveStoredCredential(token, accountType, { mode: saveMode });
109
+ writeCommandOutput(opts, `Login succeeded. Credentials stored via ${describeBackend(backend)}.`, {
110
+ status: "ok",
111
+ action: "login",
112
+ credential_backend: backend
113
+ });
114
+ }
115
+ function emitLoginResult(opts, result, hintSwitch) {
80
116
  const defaultSuffix = result.isDefault ? " (default)" : "";
81
- writeCommandOutput(opts, `Login succeeded for account "${result.id}"${defaultSuffix}. Credentials stored via ${describeBackend(result.backend)}.`, {
117
+ const switchHint = hintSwitch
118
+ ? ` It is not the default — run \`copillm auth switch ${result.id}\` to make it so.`
119
+ : "";
120
+ writeCommandOutput(opts, `Login succeeded for account "${result.id}"${defaultSuffix}. Credentials stored via ${describeBackend(result.backend)}.${switchHint}`, {
82
121
  status: "ok",
83
122
  action: "login",
84
123
  account: result.id,
@@ -1,7 +1,7 @@
1
1
  import { createRequire } from "node:module";
2
2
  const FALLBACK_PACKAGE_INFO = {
3
3
  name: "copillm",
4
- version: "0.3.0-beta.1"
4
+ version: "0.3.0-beta.2"
5
5
  };
6
6
  export function getPackageInfo() {
7
7
  const envName = cleanPackageValue(process.env.COPILLM_PACKAGE_NAME);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "copillm",
3
- "version": "0.3.0-beta.1",
3
+ "version": "0.3.0-beta.2",
4
4
  "description": "Local Copilot proxy CLI (OpenAI/Anthropic-compatible)",
5
5
  "license": "MIT",
6
6
  "type": "module",