@detergent-software/conductor 2.0.0-dev.1 → 2.0.0

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.
Files changed (45) hide show
  1. package/CHANGELOG.md +57 -0
  2. package/README.md +46 -13
  3. package/build/brief-cli.js +24 -11
  4. package/build/brief-cli.js.map +1 -1
  5. package/build/brief.js +2 -0
  6. package/build/brief.js.map +1 -1
  7. package/build/broker-cli.js +41 -2
  8. package/build/broker-cli.js.map +1 -1
  9. package/build/cli.js +40 -1
  10. package/build/cli.js.map +1 -1
  11. package/build/code-host-cli.js +32 -15
  12. package/build/code-host-cli.js.map +1 -1
  13. package/build/config.js +20 -6
  14. package/build/config.js.map +1 -1
  15. package/build/credential.js +13 -2
  16. package/build/credential.js.map +1 -1
  17. package/build/credentials.js +240 -104
  18. package/build/credentials.js.map +1 -1
  19. package/build/daemon.js +2 -0
  20. package/build/daemon.js.map +1 -1
  21. package/build/file-lock.js +106 -0
  22. package/build/file-lock.js.map +1 -0
  23. package/build/help.js +78 -1
  24. package/build/help.js.map +1 -1
  25. package/build/in-process.js +15 -4
  26. package/build/in-process.js.map +1 -1
  27. package/build/lib/providers/ado/http.js +6 -2
  28. package/build/lib/providers/ado/http.js.map +1 -1
  29. package/build/lib/providers/index.js +1 -1
  30. package/build/lib/providers/index.js.map +1 -1
  31. package/build/profile.js +7 -0
  32. package/build/profile.js.map +1 -1
  33. package/build/project-check-cli.js +1 -1
  34. package/build/project-check-cli.js.map +1 -1
  35. package/build/project-check.js +75 -2
  36. package/build/project-check.js.map +1 -1
  37. package/build/search.js +130 -0
  38. package/build/search.js.map +1 -0
  39. package/build/tracker-reads-cli.js +89 -0
  40. package/build/tracker-reads-cli.js.map +1 -0
  41. package/build/wait.js +7 -2
  42. package/build/wait.js.map +1 -1
  43. package/build/watchers/boards.js +8 -0
  44. package/build/watchers/boards.js.map +1 -1
  45. package/package.json +1 -1
package/build/config.js CHANGED
@@ -49,18 +49,32 @@ export const BUILT_IN_PROFILES = {
49
49
  { kinds: ['npm-otp', 'permission', 'failed', 'conflict', 'operator', 'login'], to: ['remote-control', 'pushover'] },
50
50
  ],
51
51
  };
52
- // The accounts one project's provider calls use (credentials.ts). Each is optional: without it,
53
- // the machine's own login (az's default account, gh's active account, the machine's Linear key).
52
+ // The credentials one project's provider calls use (credentials.ts), least privilege first: `token`
53
+ // (Linear's `credential`) names the generic credential (Credential Manager) whose password is a token
54
+ // scoped to this project, and wins over the machine's token and over the CLI login. `tenant` and
55
+ // `user` pick the CLI login (az's tenant, gh's account) when there is no token. Each is optional.
54
56
  // The tenant reaches `az` through a shell on Windows, so it is a tenant id or domain and nothing else.
57
+ const credentialTarget = z.string().trim().min(1);
55
58
  const projectAccountsSchema = z.strictObject({
56
59
  ado: z
57
- .strictObject({ tenant: z.string().regex(/^[\w-]+(\.[\w-]+)*$/, 'a tenant id or domain, such as contoso.com') })
60
+ .strictObject({
61
+ tenant: z
62
+ .string()
63
+ .regex(/^[\w-]+(\.[\w-]+)*$/, 'a tenant id or domain, such as contoso.com')
64
+ .optional(),
65
+ token: credentialTarget.optional(),
66
+ })
58
67
  .optional(),
59
68
  github: z
60
- .strictObject({ user: z.string().regex(/^[A-Za-z0-9](?:[A-Za-z0-9-]*[A-Za-z0-9])?$/, 'a GitHub user name') })
69
+ .strictObject({
70
+ token: credentialTarget.optional(),
71
+ user: z
72
+ .string()
73
+ .regex(/^[A-Za-z0-9](?:[A-Za-z0-9-]*[A-Za-z0-9])?$/, 'a GitHub user name')
74
+ .optional(),
75
+ })
61
76
  .optional(),
62
- // The generic credential (Credential Manager) whose password is the project's Linear key.
63
- linear: z.strictObject({ credential: z.string().trim().min(1) }).optional(),
77
+ linear: z.strictObject({ credential: credentialTarget }).optional(),
64
78
  });
65
79
  // ~/.conductor/config.json: machine settings. Strict, so a misspelt key fails startup instead of
66
80
  // being ignored. Rules live beside it, in rules.json (rules-service.ts).
@@ -1 +1 @@
1
- {"version":3,"file":"config.js","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"config.js","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"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"}
@@ -29,10 +29,20 @@ try {
29
29
  } finally { [AtkConduct.Cred]::CredFree($p) }
30
30
  } catch { [Console]::Error.WriteLine($_.Exception.Message); exit 4 }
31
31
  `;
32
+ export async function credentialSaved(target, env = process.env) {
33
+ if (process.platform !== 'win32')
34
+ return 'missing';
35
+ const result = await run('cmdkey', [`/list:${target}`], { env, timeoutMs: 15_000 });
36
+ if (result.code !== 0)
37
+ return 'unknown';
38
+ if (/\*\s*NONE\s*\*/.test(result.stdout))
39
+ return 'missing';
40
+ return /^\s*Target:/m.test(result.stdout) ? 'saved' : 'unknown';
41
+ }
32
42
  // Reads the generic credential `target`. Windows only.
33
43
  export async function readCredential(target, env = process.env) {
34
44
  if (process.platform !== 'win32')
35
- return { found: false, reason: `the ${target} credential is Windows-only` };
45
+ return { found: false, missing: true, reason: `the ${target} credential is Windows-only` };
36
46
  const result = await run('powershell.exe', ['-NoProfile', '-NonInteractive', '-EncodedCommand', Buffer.from(CRED_READ, 'utf16le').toString('base64')], { env: { ...env, ATK_CONDUCT_CRED_TARGET: target }, timeoutMs: 30_000 });
37
47
  if (result.code === 0) {
38
48
  const newline = result.stdout.indexOf('\n');
@@ -41,7 +51,7 @@ export async function readCredential(target, env = process.env) {
41
51
  return { credential: { secret, user: result.stdout.slice(0, newline).trim() }, found: true };
42
52
  }
43
53
  if (result.code === NOT_FOUND)
44
- return { found: false, reason: `no credential ${target}` };
54
+ return { found: false, missing: true, reason: `no credential ${target}` };
45
55
  // Never echo stdout here: on a partial failure it could hold the secret.
46
56
  const line = result.stderr
47
57
  .split(/\r?\n/)
@@ -49,6 +59,7 @@ export async function readCredential(target, env = process.env) {
49
59
  .find((l) => l && !l.startsWith('#< CLIXML') && !l.startsWith('<Objs'));
50
60
  return {
51
61
  found: false,
62
+ missing: false,
52
63
  reason: `the credential ${target} couldn't be read (${result.error ?? line ?? `exit ${result.code}`})`,
53
64
  };
54
65
  }
@@ -1 +1 @@
1
- {"version":3,"file":"credential.js","sourceRoot":"","sources":["../src/credential.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,GAAG,EAAE,MAAM,UAAU,CAAC;AAY/B,MAAM,SAAS,GAAG,CAAC,CAAC;AACpB,8FAA8F;AAC9F,MAAM,eAAe,GAAG,IAAI,CAAC;AAE7B,sDAAsD;AACtD,MAAM,SAAS,GAAG;;;;;;;;;;;;;;;;kBAgBA,eAAe,YAAY,SAAS;;;;;;;;;CASrD,CAAC;AAEF,uDAAuD;AACvD,MAAM,CAAC,KAAK,UAAU,cAAc,CAAC,MAAc,EAAE,MAAyB,OAAO,CAAC,GAAG;IACvF,IAAI,OAAO,CAAC,QAAQ,KAAK,OAAO;QAAE,OAAO,EAAE,KAAK,EAAE,KAAK,EAAE,MAAM,EAAE,OAAO,MAAM,6BAA6B,EAAE,CAAC;IAC9G,MAAM,MAAM,GAAG,MAAM,GAAG,CACtB,gBAAgB,EAChB,CAAC,YAAY,EAAE,iBAAiB,EAAE,iBAAiB,EAAE,MAAM,CAAC,IAAI,CAAC,SAAS,EAAE,SAAS,CAAC,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAC,EAC1G,EAAE,GAAG,EAAE,EAAE,GAAG,GAAG,EAAE,uBAAuB,EAAE,MAAM,EAAE,EAAE,SAAS,EAAE,MAAM,EAAE,CACxE,CAAC;IACF,IAAI,MAAM,CAAC,IAAI,KAAK,CAAC,EAAE,CAAC;QACtB,MAAM,OAAO,GAAG,MAAM,CAAC,MAAM,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC;QAC5C,MAAM,MAAM,GAAG,MAAM,CAAC,MAAM,CAAC,KAAK,CAAC,OAAO,GAAG,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC;QACvD,IAAI,OAAO,IAAI,CAAC,IAAI,MAAM;YACxB,OAAO,EAAE,UAAU,EAAE,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,EAAE,OAAO,CAAC,CAAC,IAAI,EAAE,EAAE,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC;IACjG,CAAC;IACD,IAAI,MAAM,CAAC,IAAI,KAAK,SAAS;QAAE,OAAO,EAAE,KAAK,EAAE,KAAK,EAAE,MAAM,EAAE,iBAAiB,MAAM,EAAE,EAAE,CAAC;IAC1F,yEAAyE;IACzE,MAAM,IAAI,GAAG,MAAM,CAAC,MAAM;SACvB,KAAK,CAAC,OAAO,CAAC;SACd,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC;SACpB,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,UAAU,CAAC,WAAW,CAAC,IAAI,CAAC,CAAC,CAAC,UAAU,CAAC,OAAO,CAAC,CAAC,CAAC;IAC1E,OAAO;QACL,KAAK,EAAE,KAAK;QACZ,MAAM,EAAE,kBAAkB,MAAM,sBAAsB,MAAM,CAAC,KAAK,IAAI,IAAI,IAAI,QAAQ,MAAM,CAAC,IAAI,EAAE,GAAG;KACvG,CAAC;AACJ,CAAC"}
1
+ {"version":3,"file":"credential.js","sourceRoot":"","sources":["../src/credential.ts"],"names":[],"mappings":"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"}
@@ -1,19 +1,35 @@
1
- import { readCredential as readWindowsCredential } from "./credential.js";
1
+ import { credentialSaved, readCredential as readWindowsCredential, } from "./credential.js";
2
2
  import { ConductorError } from "./errors.js";
3
- import { NotFound } from "./lib/providers/index.js";
3
+ import { adoBasic, adoOrgUrl, NotFound } from "./lib/providers/index.js";
4
4
  import { outputOf, run as runCommand } from "./run.js";
5
- // Each project's accounts (providers design note, section 6): which az tenant, gh account and
6
- // Linear key its provider calls use, from machine config (`projects.<name>`), else the machine's
7
- // own logins. A login that stops working is a spell: one `lost` change when it starts, one
5
+ // Each project's credentials (design note v2, section 8): every provider takes the first credential
6
+ // it finds, least privilege first.
7
+ // 1. project-token: a token scoped to the project, in the generic credential that machine config
8
+ // names (`projects.<name>.ado.token`, `github.token`, `linear.credential`);
9
+ // 2. machine-token: the machine's own token, from a conductor-only env var or generic credential
10
+ // (MACHINE_TOKENS), so another tool's env never changes what the conductor signs in as;
11
+ // 3. cli-login: `az` (the project's tenant, else the default account) or `gh` (the project's user,
12
+ // else the active account), last because it usually carries the widest permissions. Linear has
13
+ // none.
14
+ // Found means there: a token that is there but refused is a lost login, never a quiet fall back to a
15
+ // wider credential. A login that stops working is a spell: one `lost` change when it starts, one
8
16
  // `restored` when a call or a probe works again.
9
17
  export const PROVIDERS = ['ado', 'github', 'linear'];
18
+ export const SOURCES = ['project-token', 'machine-token', 'cli-login'];
10
19
  // Azure DevOps' resource id, for `az account get-access-token`.
11
20
  const ADO_RESOURCE = '499b84ac-1321-427f-aa17-267ca6975798';
12
- // An az token lasts about an hour, a gh token until it is revoked; both are asked again well before.
21
+ // An az token lasts about an hour, a gh token until it is revoked; both are asked again well before,
22
+ // and a token's credential is read again as often, so a replaced or removed one is seen.
13
23
  const TOKEN_MAX_AGE_MS = 20 * 60_000;
14
24
  export const LINEAR_CREDENTIAL = 'atk-conductor-linear';
15
- // The id of the machine's own login. No tenant, gh user or credential target in config can be it:
16
- // the schema allows no parentheses in the first two, and a target that spelt it would be odd.
25
+ // The machine's own token for each provider: the env vars in order, then the generic credential.
26
+ export const MACHINE_TOKENS = {
27
+ ado: { credential: 'conductor-ado', env: ['CONDUCTOR_ADO_TOKEN'] },
28
+ github: { credential: 'conductor-github', env: ['CONDUCTOR_GITHUB_TOKEN'] },
29
+ linear: { credential: LINEAR_CREDENTIAL, env: ['CONDUCTOR_LINEAR_KEY', 'LINEAR_API_KEY'] },
30
+ };
31
+ // The id of the machine's own credentials. No tenant, gh user or credential target in config can be
32
+ // it: the schema allows no parentheses in the first two, and a target that spelt it would be odd.
17
33
  export const MACHINE = '(machine)';
18
34
  const LINEAR_API = 'https://api.linear.app/graphql';
19
35
  const TIMEOUT_MS = 20_000;
@@ -37,28 +53,39 @@ export class LoginNeeded extends ConductorError {
37
53
  export function loginText(account, project) {
38
54
  return `${account.provider} login lost${project === null ? '' : ` for ${project}`} (${account.label})`;
39
55
  }
56
+ // The generic credential the machine's own token is saved under. For Linear,
57
+ // ATK_CONDUCT_LINEAR_CREDENTIAL names another, as in conduct-atk.
58
+ export function machineTarget(provider, env) {
59
+ if (provider === 'linear')
60
+ return env.ATK_CONDUCT_LINEAR_CREDENTIAL?.trim() || LINEAR_CREDENTIAL;
61
+ return MACHINE_TOKENS[provider].credential;
62
+ }
40
63
  export function providerCredentials(deps) {
41
64
  const env = deps.env ?? process.env;
42
65
  const fetcher = deps.fetch ?? globalThis.fetch;
43
66
  const now = deps.now ?? Date.now;
44
67
  const exec = deps.run ?? runCommand;
45
68
  const read = deps.readCredential ?? readWindowsCredential;
69
+ // A seam that reads credentials but checks none is asked for every target.
70
+ const isSaved = deps.credentialSaved ?? (deps.readCredential ? async () => 'unknown' : credentialSaved);
46
71
  const lostNow = new Map();
47
- // Each account's token or key, shared by every project on it. The calls that read them name
48
- // their own project, so a lost login is put on the project whose call found it.
72
+ // Each identity's authorization (ADO's header value, gh's token, Linear's key) and the source it
73
+ // came from, shared by every project with that identity. The calls that read them name their own
74
+ // project, so a lost login is put on the project whose call found it.
49
75
  const adoTokens = new Map();
50
76
  const ghTokens = new Map();
51
77
  const linearKeys = new Map();
52
- const spell = (account) => `${account.provider}:${account.id}`;
78
+ const foundAs = new Map();
79
+ const key = (account) => `${account.provider}:${account.id}`;
53
80
  const failed = (account, project, error) => {
54
- if (!lostNow.has(spell(account))) {
55
- lostNow.set(spell(account), { account, error, project });
81
+ if (!lostNow.has(key(account))) {
82
+ lostNow.set(key(account), { account, error, project });
56
83
  deps.onChange?.({ account, error, kind: 'lost', project });
57
84
  }
58
85
  return new LoginNeeded(account, project, error);
59
86
  };
60
87
  const worked = (account, project) => {
61
- if (!lostNow.delete(spell(account)))
88
+ if (!lostNow.delete(key(account)))
62
89
  return;
63
90
  deps.onChange?.({ account, error: null, kind: 'restored', project });
64
91
  };
@@ -66,82 +93,167 @@ export function providerCredentials(deps) {
66
93
  // a login wait): every other call fails at once, so a pass with many waits on the account runs
67
94
  // no `az`, `gh` or Linear request for it. The re-probe is the spell's back-off.
68
95
  const refuseWhileLost = (account, project, probing) => {
69
- const lost = probing ? undefined : lostNow.get(spell(account));
96
+ const lost = probing ? undefined : lostNow.get(key(account));
70
97
  if (lost)
71
- throw new LoginNeeded(account, project, lost.error);
98
+ throw new LoginNeeded(lost.account, project, lost.error);
72
99
  };
73
- const account = (provider, project) => {
100
+ // The project's credentials as machine config names them, before any is read: its token when it
101
+ // names one, else its CLI login (Linear: the machine's key), with no source yet.
102
+ const configured = (provider, project) => {
74
103
  const entry = project === null ? undefined : deps.projects[project];
104
+ const target = provider === 'linear' ? entry?.linear?.credential : entry?.[provider]?.token;
105
+ if (target !== undefined)
106
+ return tokenAccount(provider, target, target, 'project-token');
75
107
  switch (provider) {
76
108
  case 'ado': {
77
109
  const tenant = entry?.ado?.tenant ?? null;
78
110
  return tenant === null
79
- ? { command: 'az login', id: MACHINE, label: "az's default account", provider }
80
- : { command: `az login --tenant ${tenant}`, id: tenant, label: `az tenant ${tenant}`, provider };
111
+ ? { command: 'az login', id: MACHINE, label: "az's default account", provider, source: null }
112
+ : {
113
+ command: `az login --tenant ${tenant}`,
114
+ id: tenant,
115
+ label: `az tenant ${tenant}`,
116
+ provider,
117
+ source: null,
118
+ };
81
119
  }
82
120
  case 'github': {
83
121
  const user = entry?.github?.user ?? null;
84
122
  return user === null
85
- ? { command: 'gh auth login', id: MACHINE, label: "gh's active account", provider }
86
- : { command: 'gh auth login', id: user, label: `gh account ${user}`, provider };
123
+ ? { command: 'gh auth login', id: MACHINE, label: "gh's active account", provider, source: null }
124
+ : { command: 'gh auth login', id: user, label: `gh account ${user}`, provider, source: null };
87
125
  }
88
126
  case 'linear': {
89
- const target = entry?.linear?.credential ?? null;
90
- const shown = target ?? defaultLinearTarget(env);
127
+ const shown = machineTarget('linear', env);
91
128
  return {
92
129
  command: `cmdkey /generic:${shown} /user:linear /pass`,
93
- id: target ?? MACHINE,
94
- label: target === null ? `LINEAR_API_KEY or credential ${shown}` : `credential ${target}`,
130
+ id: MACHINE,
131
+ label: `${MACHINE_TOKENS.linear.env.join(', ')} or credential ${shown}`,
95
132
  provider,
133
+ source: null,
96
134
  };
97
135
  }
98
136
  }
99
137
  };
100
- // The az token for the project's tenant, kept 20 minutes.
138
+ // The account as last found: the configured identity with its source's words.
139
+ const account = (provider, project) => {
140
+ const base = configured(provider, project);
141
+ return foundAs.get(key(base)) ?? base;
142
+ };
143
+ // The first credential the project has, in the order above. Throws LoginNeeded for a project
144
+ // token or a machine credential that is named but can't be read, and for Linear with no key.
145
+ const find = async (provider, project) => {
146
+ const base = configured(provider, project);
147
+ const as = (found) => ({ ...found, id: base.id });
148
+ if (base.source === 'project-token') {
149
+ const got = await read(base.id, env);
150
+ if (!got.found)
151
+ throw failed(base, project, got.reason);
152
+ return { account: base, token: got.credential.secret };
153
+ }
154
+ // A project that names its own CLI account (an az tenant, a gh user) keeps it: the machine's
155
+ // token is another identity, which may not even reach the project's organization.
156
+ const cli = { account: { ...base, source: 'cli-login' }, token: null };
157
+ if (base.id !== MACHINE && provider !== 'linear')
158
+ return cli;
159
+ const machine = MACHINE_TOKENS[provider];
160
+ for (const name of machine.env) {
161
+ const value = env[name]?.trim();
162
+ if (value) {
163
+ const command = `set ${name} to a working token, or unset it`;
164
+ return { account: as({ command, id: MACHINE, label: name, provider, source: 'machine-token' }), token: value };
165
+ }
166
+ }
167
+ // The machine's credential is optional: one that isn't saved, or can't be read, is passed over
168
+ // (the cheap cmdkey check first, so the usual case starts no PowerShell). Linear has nothing
169
+ // after it.
170
+ const target = machineTarget(provider, env);
171
+ const stored = as(tokenAccount(provider, target, MACHINE, 'machine-token'));
172
+ const saved = await isSaved(target, env);
173
+ const got = saved === 'missing' ? null : await read(target, env);
174
+ if (got?.found)
175
+ return { account: stored, token: got.credential.secret };
176
+ if (provider === 'linear') {
177
+ throw failed(base, project, `no ${machine.env.join(' or ')}, and ${got?.reason ?? `no credential ${target}`}`);
178
+ }
179
+ return cli;
180
+ };
181
+ const remember = (found) => {
182
+ foundAs.set(key(found.account), found.account);
183
+ return found.account;
184
+ };
185
+ // The ADO authorization for the project, kept 20 minutes: a token as Basic, else az's as Bearer.
101
186
  const adoToken = async (project, fresh, probing) => {
102
- const which = account('ado', project);
103
- refuseWhileLost(which, project, probing);
104
- const cached = adoTokens.get(which.id);
187
+ const known = account('ado', project);
188
+ refuseWhileLost(known, project, probing);
189
+ const cached = adoTokens.get(known.id);
105
190
  if (!fresh && cached && now() - cached.at < TOKEN_MAX_AGE_MS)
106
191
  return cached.value;
107
- const tenant = which.id === MACHINE ? [] : ['--tenant', which.id];
108
- // `az` is a .cmd on Windows, so it runs through a shell; its arguments are constants and
109
- // the tenant id from machine config, which the config schema keeps to an id or a domain.
110
- const result = await exec('az', ['account', 'get-access-token', '--resource', ADO_RESOURCE, ...tenant, '--query', 'accessToken', '-o', 'tsv'], { shell: process.platform === 'win32', timeoutMs: 60_000 });
111
- const value = result.stdout.trim();
112
- if (result.code !== 0 || !value) {
113
- throw failed(which, project, `az account get-access-token failed: ${outputOf(result, 300)}`);
192
+ const found = await find('ado', project);
193
+ const which = remember(found);
194
+ let value;
195
+ if (found.token !== null)
196
+ value = adoBasic(found.token);
197
+ else {
198
+ const tenant = which.id === MACHINE ? [] : ['--tenant', which.id];
199
+ // `az` is a .cmd on Windows, so it runs through a shell; its arguments are constants and
200
+ // the tenant id from machine config, which the config schema keeps to an id or a domain.
201
+ const result = await exec('az', ['account', 'get-access-token', '--resource', ADO_RESOURCE, ...tenant, '--query', 'accessToken', '-o', 'tsv'], { shell: process.platform === 'win32', timeoutMs: 60_000 });
202
+ const token = result.stdout.trim();
203
+ if (result.code !== 0 || !token) {
204
+ throw failed(which, project, `az account get-access-token failed: ${outputOf(result, 300)}`);
205
+ }
206
+ // A token from az works: az said so. A PAT's spell ends only when a probe sees ADO take it.
207
+ worked(which, project);
208
+ value = `Bearer ${token}`;
114
209
  }
115
- worked(which, project);
116
210
  adoTokens.set(which.id, { at: now(), value });
117
211
  return value;
118
212
  };
119
- const ado = (project) => (fresh) => adoToken(project, fresh, false);
120
- const ghToken = async (which, project, fresh) => {
121
- const cached = ghTokens.get(which.id);
213
+ const ado = (project) => async (fresh) => {
214
+ const known = account('ado', project);
215
+ const sent = adoTokens.get(known.id)?.value;
216
+ // ADO refused the PAT it was sent (401, or 203: its sign-in page). The credential is read
217
+ // again, so a replaced PAT is tried; the same one is refused again, so that is a lost login
218
+ // on it. az's token is renewed instead.
219
+ if (fresh && sent !== undefined && known.source !== null && known.source !== 'cli-login') {
220
+ const value = await adoToken(project, true, false);
221
+ if (value !== sent)
222
+ return value;
223
+ adoTokens.delete(known.id);
224
+ throw failed(account('ado', project), project, 'Azure DevOps refused the token (HTTP 401 or 203)');
225
+ }
226
+ return adoToken(project, fresh, false);
227
+ };
228
+ const ghToken = async (project, fresh) => {
229
+ const known = account('github', project);
230
+ const cached = ghTokens.get(known.id);
122
231
  if (!fresh && cached && now() - cached.at < TOKEN_MAX_AGE_MS)
123
- return cached.value;
124
- const user = which.id === MACHINE ? [] : ['--user', which.id];
125
- const result = await exec('gh', ['auth', 'token', ...user], { env, timeoutMs: 30_000 });
126
- const value = result.stdout.trim();
127
- if (result.code !== 0 || !value)
128
- throw failed(which, project, `gh auth token failed: ${outputOf(result, 300)}`);
129
- ghTokens.set(which.id, { at: now(), value });
130
- return value;
232
+ return { account: known, token: cached.value };
233
+ const found = await find('github', project);
234
+ const which = remember(found);
235
+ let token = found.token;
236
+ if (token === null) {
237
+ const user = which.id === MACHINE ? [] : ['--user', which.id];
238
+ const result = await exec('gh', ['auth', 'token', ...user], { env, timeoutMs: 30_000 });
239
+ token = result.stdout.trim();
240
+ if (result.code !== 0 || !token)
241
+ throw failed(which, project, `gh auth token failed: ${outputOf(result, 300)}`);
242
+ }
243
+ ghTokens.set(which.id, { at: now(), value: token });
244
+ return { account: which, token };
131
245
  };
132
- // Each call runs `gh api` with the account's token in GH_TOKEN, so a project's gh user needs no
133
- // `gh auth switch`. A 401 asks gh for the token again once; a second is a lost login. Only an
134
- // answer from GitHub (success, or an HTTP status other than 401) says the token works: a
135
- // timeout or a network error says nothing about the login.
136
- // One gh command as the project's account: a 401 asks gh for the token again once (GitHub did
137
- // nothing, so a write is safe to send again), and a second is a lost login. The result, with what
138
- // gh said when it failed.
246
+ // One gh command as the project's credential, its token in GH_TOKEN, so a project's gh user needs
247
+ // no `gh auth switch`. A 401 asks for the token again once (GitHub did nothing, so a write is safe
248
+ // to send again), and a second is a lost login. Only an answer from GitHub (success, or an HTTP
249
+ // status other than 401) says the token works: a timeout or a network error says nothing about
250
+ // the login. The result, with what gh said when it failed.
139
251
  const ghExec = async (project, args, { input, probing = false, timeoutMs = 60_000 } = {}) => {
140
- const which = account('github', project);
141
- refuseWhileLost(which, project, probing);
252
+ refuseWhileLost(account('github', project), project, probing);
142
253
  for (let fresh = probing;; fresh = true) {
254
+ const { account: which, token } = await ghToken(project, fresh);
143
255
  const result = await exec('gh', args, {
144
- env: { ...env, GH_TOKEN: await ghToken(which, project, fresh) },
256
+ env: { ...env, GH_TOKEN: token },
145
257
  ...(input === undefined ? {} : { input }),
146
258
  timeoutMs,
147
259
  });
@@ -172,52 +284,82 @@ export function providerCredentials(deps) {
172
284
  };
173
285
  const gh = (project) => (path) => ghApi(project, path);
174
286
  const ghWrite = (project) => (method, path, body) => ghApi(project, path, { body, method });
175
- // Another gh command as the project's account, such as `run view --log-failed`; its output.
287
+ // Another gh command as the project's credential, such as `run view --log-failed`; its output.
176
288
  const ghRun = (project) => async (args) => {
177
289
  const { result, said } = await ghExec(project, args, { timeoutMs: 120_000 });
178
290
  if (result.code !== 0)
179
291
  throw new Error(`gh ${args.join(' ')} failed: ${said}`);
180
292
  return result.stdout;
181
293
  };
182
- // The project's Linear key, kept per credential target. The key is only returned: never logged,
183
- // stored or put on a command line.
184
- const linear = (project, probing = false) => {
185
- const which = account('linear', project);
186
- return {
187
- account: which,
188
- key: async (fresh = false) => {
189
- refuseWhileLost(which, project, probing);
190
- const cached = linearKeys.get(which.id);
191
- if (!fresh && cached !== undefined)
192
- return cached;
193
- const found = await readLinearKey(which, env, read);
194
- if (found.key === null)
195
- throw failed(which, project, found.reason);
196
- linearKeys.set(which.id, found.key);
197
- return found.key;
198
- },
199
- refused: (detail) => {
200
- linearKeys.delete(which.id);
201
- return failed(which, project, detail);
202
- },
203
- worked: () => worked(which, project),
204
- };
205
- };
294
+ // The project's Linear key, kept per identity. The key is only returned: never logged, stored or
295
+ // put on a command line.
296
+ const linear = (project, probing = false) => ({
297
+ get account() {
298
+ return account('linear', project);
299
+ },
300
+ key: async (fresh = false) => {
301
+ const known = account('linear', project);
302
+ refuseWhileLost(known, project, probing);
303
+ const cached = linearKeys.get(known.id);
304
+ if (!fresh && cached !== undefined)
305
+ return cached;
306
+ const found = await find('linear', project);
307
+ const which = remember(found);
308
+ // find gives Linear a key or throws: it has no CLI login.
309
+ if (found.token === null)
310
+ throw failed(which, project, 'no Linear key');
311
+ linearKeys.set(which.id, found.token);
312
+ return found.token;
313
+ },
314
+ refused: (detail) => {
315
+ const known = account('linear', project);
316
+ linearKeys.delete(known.id);
317
+ return failed(known, project, detail);
318
+ },
319
+ worked: () => worked(account('linear', project), project),
320
+ });
206
321
  const probe = async (provider, project) => {
207
- const which = account(provider, project);
208
322
  try {
209
323
  if (provider === 'ado')
210
- await adoToken(project, true, true);
324
+ await probeAdo(project);
211
325
  else if (provider === 'github')
212
326
  await ghApi(project, 'user', { probing: true });
213
327
  else
214
328
  await probeLinear(linear(project, true));
215
- return { account: which, error: null };
329
+ return { account: account(provider, project), error: null };
216
330
  }
217
331
  catch (error) {
218
- return { account: which, error: error instanceof Error ? error.message : String(error) };
332
+ return {
333
+ account: error instanceof LoginNeeded ? error.account : account(provider, project),
334
+ error: error instanceof Error ? error.message : String(error),
335
+ };
219
336
  }
220
337
  };
338
+ // A fresh ADO credential. az vouches for its own token; a token is asked who it is on the project's
339
+ // organization, which is the only way to learn ADO takes it (it answers 401, or 203 with its
340
+ // sign-in page, when it doesn't).
341
+ const probeAdo = async (project) => {
342
+ adoTokens.delete(account('ado', project).id);
343
+ const value = await adoToken(project, true, true);
344
+ const which = account('ado', project);
345
+ if (which.source === 'cli-login')
346
+ return;
347
+ const organization = deps.adoOrganization?.(project) ?? null;
348
+ if (organization === null)
349
+ throw new Error('no Azure DevOps organization to ask whether it takes the token');
350
+ const response = await fetcher(adoOrgUrl({ organization }, 'connectionData', '7.1-preview'), {
351
+ headers: { authorization: value },
352
+ signal: AbortSignal.timeout(TIMEOUT_MS),
353
+ });
354
+ await response.body?.cancel();
355
+ if (response.status === 401 || response.status === 203) {
356
+ adoTokens.delete(which.id);
357
+ throw failed(which, project, `Azure DevOps refused the token (HTTP ${response.status})`);
358
+ }
359
+ if (!response.ok)
360
+ throw new Error(`Azure DevOps answered ${response.status}`);
361
+ worked(which, project);
362
+ };
221
363
  const probeLinear = async (auth) => {
222
364
  const secret = await auth.key(true);
223
365
  const response = await fetcher(deps.linearUrl ?? LINEAR_API, {
@@ -243,22 +385,16 @@ export function providerCredentials(deps) {
243
385
  probe,
244
386
  };
245
387
  }
246
- // The target the machine's own Linear key is saved under: ATK_CONDUCT_LINEAR_CREDENTIAL names
247
- // another, as in conduct-atk.
248
- function defaultLinearTarget(env) {
249
- return env.ATK_CONDUCT_LINEAR_CREDENTIAL?.trim() || LINEAR_CREDENTIAL;
250
- }
251
- // A project's own credential target, else LINEAR_API_KEY, then the machine's target (ATK-272).
252
- async function readLinearKey(which, env, read) {
253
- if (which.id === MACHINE) {
254
- const fromEnv = env.LINEAR_API_KEY?.trim();
255
- if (fromEnv)
256
- return { key: fromEnv };
257
- }
258
- const target = which.id === MACHINE ? defaultLinearTarget(env) : which.id;
259
- const found = await read(target, env);
260
- if (found.found)
261
- return { key: found.credential.secret };
262
- return { key: null, reason: which.id === MACHINE ? `no LINEAR_API_KEY, and ${found.reason}` : found.reason };
388
+ // The account of a token in the generic credential `target`.
389
+ function tokenAccount(provider, target, id, source) {
390
+ const user = provider === 'linear' ? 'linear' : 'pat';
391
+ const whose = source === 'project-token' ? 'project token' : 'machine token';
392
+ return {
393
+ command: `cmdkey /generic:${target} /user:${user} /pass`,
394
+ id,
395
+ label: `${whose} in credential ${target}`,
396
+ provider,
397
+ source,
398
+ };
263
399
  }
264
400
  //# sourceMappingURL=credentials.js.map