@detergent-software/atk 7.10.1-dev.1 → 7.11.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.
- package/README.md +44 -12
- package/build/commands/_app.d.ts.map +1 -1
- package/build/commands/_app.js +6 -1
- package/build/commands/_app.js.map +1 -1
- package/build/commands/login.d.ts +15 -0
- package/build/commands/login.d.ts.map +1 -0
- package/build/commands/login.js +148 -0
- package/build/commands/login.js.map +1 -0
- package/build/commands/logout.d.ts +11 -0
- package/build/commands/logout.d.ts.map +1 -0
- package/build/commands/logout.js +49 -0
- package/build/commands/logout.js.map +1 -0
- package/build/commands/schema.d.ts +1 -1
- package/build/commands/whoami.d.ts +11 -0
- package/build/commands/whoami.d.ts.map +1 -0
- package/build/commands/whoami.js +70 -0
- package/build/commands/whoami.js.map +1 -0
- package/build/components/NoticeBanner.d.ts +12 -0
- package/build/components/NoticeBanner.d.ts.map +1 -0
- package/build/components/NoticeBanner.js +21 -0
- package/build/components/NoticeBanner.js.map +1 -0
- package/build/components/index.d.ts +1 -0
- package/build/components/index.d.ts.map +1 -1
- package/build/components/index.js +1 -0
- package/build/components/index.js.map +1 -1
- package/build/hooks/usePublishState.js +2 -2
- package/build/hooks/usePublishState.js.map +1 -1
- package/build/lib/api/sdk.gen.d.ts +3 -3
- package/build/lib/api/sdk.gen.js +3 -3
- package/build/lib/api/types.gen.d.ts +101 -29
- package/build/lib/api/types.gen.d.ts.map +1 -1
- package/build/lib/api-client.d.ts +3 -3
- package/build/lib/api-client.d.ts.map +1 -1
- package/build/lib/api-client.js +38 -11
- package/build/lib/api-client.js.map +1 -1
- package/build/lib/auth.d.ts +43 -11
- package/build/lib/auth.d.ts.map +1 -1
- package/build/lib/auth.js +106 -39
- package/build/lib/auth.js.map +1 -1
- package/build/lib/diagnostics.d.ts.map +1 -1
- package/build/lib/diagnostics.js +75 -31
- package/build/lib/diagnostics.js.map +1 -1
- package/build/lib/entra.d.ts +86 -0
- package/build/lib/entra.d.ts.map +1 -0
- package/build/lib/entra.js +209 -0
- package/build/lib/entra.js.map +1 -0
- package/build/lib/notices.d.ts +33 -0
- package/build/lib/notices.d.ts.map +1 -0
- package/build/lib/notices.js +43 -0
- package/build/lib/notices.js.map +1 -0
- package/package.json +2 -1
package/build/lib/diagnostics.js
CHANGED
|
@@ -1,7 +1,9 @@
|
|
|
1
|
+
import { existsSync } from 'node:fs';
|
|
1
2
|
import { resolveApiUrl } from './api-client.js';
|
|
2
|
-
import {
|
|
3
|
+
import { getCredential, getLastEntraSession, isGhCliAvailable, validateToken } from './auth.js';
|
|
3
4
|
import { loadConfig, loadProjectConfig } from './config.js';
|
|
4
5
|
import { detectTools, getAdapterDiagnostics } from './detector.js';
|
|
6
|
+
import { getEntraCachePath } from './entra.js';
|
|
5
7
|
import { lockfileExists, readLockfile } from './lockfile.js';
|
|
6
8
|
import { findProjectRoot } from './paths.js';
|
|
7
9
|
import { fetchRegistry } from './registry.js';
|
|
@@ -60,8 +62,8 @@ export async function runDiagnostics(progress, projectRootOverride) {
|
|
|
60
62
|
}
|
|
61
63
|
else {
|
|
62
64
|
checks.push({
|
|
63
|
-
detail: 'Install from https://cli.github.com, or set githubToken in config / GITHUB_TOKEN.',
|
|
64
|
-
message: 'Not found — used as a token source and for `atk unpublish`/`atk promote`',
|
|
65
|
+
detail: 'Install from https://cli.github.com, or set githubToken in config / GITHUB_TOKEN. Not needed when signed in with `atk login`, except for those two commands.',
|
|
66
|
+
message: 'Not found — used as a GitHub token source and for `atk unpublish`/`atk promote`',
|
|
65
67
|
name: 'GitHub CLI (gh)',
|
|
66
68
|
status: 'warn',
|
|
67
69
|
});
|
|
@@ -93,34 +95,9 @@ export async function runDiagnostics(progress, projectRootOverride) {
|
|
|
93
95
|
name: 'ATK API',
|
|
94
96
|
status: 'pass',
|
|
95
97
|
});
|
|
96
|
-
// Check
|
|
97
|
-
progress('Checking
|
|
98
|
-
|
|
99
|
-
const token = await getGitHubToken();
|
|
100
|
-
const validation = await validateToken(token);
|
|
101
|
-
if (validation.valid) {
|
|
102
|
-
checks.push({
|
|
103
|
-
message: `Authenticated as ${validation.username ?? 'unknown'}`,
|
|
104
|
-
name: 'GitHub auth',
|
|
105
|
-
status: 'pass',
|
|
106
|
-
});
|
|
107
|
-
}
|
|
108
|
-
else {
|
|
109
|
-
checks.push({
|
|
110
|
-
detail: 'Run `gh auth login` or update githubToken in config. The token must belong to an active EmergentSoftware member.',
|
|
111
|
-
message: 'Token was rejected by the ATK API (invalid, expired, or not an org member)',
|
|
112
|
-
name: 'GitHub auth',
|
|
113
|
-
status: 'fail',
|
|
114
|
-
});
|
|
115
|
-
}
|
|
116
|
-
}
|
|
117
|
-
catch (err) {
|
|
118
|
-
checks.push({
|
|
119
|
-
message: err instanceof Error ? err.message : 'Failed to get token',
|
|
120
|
-
name: 'GitHub auth',
|
|
121
|
-
status: 'fail',
|
|
122
|
-
});
|
|
123
|
-
}
|
|
98
|
+
// Check authentication against the ATK API
|
|
99
|
+
progress('Checking authentication...');
|
|
100
|
+
checks.push(await checkAuthentication());
|
|
124
101
|
// Check registry access
|
|
125
102
|
progress('Checking registry access...');
|
|
126
103
|
try {
|
|
@@ -244,4 +221,71 @@ export async function runDiagnostics(progress, projectRootOverride) {
|
|
|
244
221
|
}
|
|
245
222
|
return checks;
|
|
246
223
|
}
|
|
224
|
+
const SOURCE_LABELS = {
|
|
225
|
+
ATK_TOKEN: 'ATK_TOKEN',
|
|
226
|
+
config: 'githubToken in config',
|
|
227
|
+
entra: 'atk login session',
|
|
228
|
+
gh: 'gh auth token',
|
|
229
|
+
GITHUB_TOKEN: 'GITHUB_TOKEN',
|
|
230
|
+
};
|
|
231
|
+
/**
|
|
232
|
+
* Resolve the credential chain and validate the result against `GET /me`.
|
|
233
|
+
* When an Entra session file exists but did not supply the credential, the
|
|
234
|
+
* detail says so, since that usually means `atk login` is needed again.
|
|
235
|
+
*/
|
|
236
|
+
async function checkAuthentication() {
|
|
237
|
+
const name = 'Authentication';
|
|
238
|
+
let credential;
|
|
239
|
+
try {
|
|
240
|
+
credential = await getCredential();
|
|
241
|
+
}
|
|
242
|
+
catch (err) {
|
|
243
|
+
return {
|
|
244
|
+
detail: staleEntraSessionNote(),
|
|
245
|
+
message: err instanceof Error ? err.message : 'Failed to resolve a credential',
|
|
246
|
+
name,
|
|
247
|
+
status: 'fail',
|
|
248
|
+
};
|
|
249
|
+
}
|
|
250
|
+
const validation = await validateToken(credential.token);
|
|
251
|
+
const source = SOURCE_LABELS[credential.source];
|
|
252
|
+
if (!validation.valid) {
|
|
253
|
+
return {
|
|
254
|
+
detail: credential.source === 'entra'
|
|
255
|
+
? 'Run `atk login --force` to sign in again.'
|
|
256
|
+
: `Run \`atk login\`, or refresh the GitHub token (${source}). GitHub tokens must belong to an active EmergentSoftware member.`,
|
|
257
|
+
message: `Credential from ${source} was rejected by the ATK API`,
|
|
258
|
+
name,
|
|
259
|
+
status: 'fail',
|
|
260
|
+
};
|
|
261
|
+
}
|
|
262
|
+
const principal = validation.principal;
|
|
263
|
+
const who = principal?.login ?? validation.username ?? 'unknown';
|
|
264
|
+
const scheme = principal?.scheme ?? 'unknown';
|
|
265
|
+
const sunset = principal?.githubAuthSunset
|
|
266
|
+
? `GitHub sign-in to ATK ends on ${principal.githubAuthSunset.slice(0, 10)}; run \`atk login\` to switch to your Emergent account.`
|
|
267
|
+
: undefined;
|
|
268
|
+
return {
|
|
269
|
+
detail: sunset ?? (credential.source === 'entra' ? undefined : staleEntraSessionNote()),
|
|
270
|
+
message: `Signed in as ${who} via ${scheme} (from ${source})`,
|
|
271
|
+
name,
|
|
272
|
+
status: 'pass',
|
|
273
|
+
};
|
|
274
|
+
}
|
|
275
|
+
/** A note when `~/.atk/auth/msal-cache.json` exists but yielded no session. */
|
|
276
|
+
function staleEntraSessionNote() {
|
|
277
|
+
if (!existsSync(getEntraCachePath())) {
|
|
278
|
+
return undefined;
|
|
279
|
+
}
|
|
280
|
+
const session = getLastEntraSession();
|
|
281
|
+
if (!session || session.ok) {
|
|
282
|
+
return undefined;
|
|
283
|
+
}
|
|
284
|
+
const why = session.reason === 'expired'
|
|
285
|
+
? 'the session has expired'
|
|
286
|
+
: session.reason === 'none'
|
|
287
|
+
? 'it holds no account'
|
|
288
|
+
: `it could not be read: ${session.detail ?? 'unknown error'}`;
|
|
289
|
+
return `An Emergent session file exists at ${getEntraCachePath()} but ${why}. Run \`atk login\` to renew it or \`atk logout\` to remove it.`;
|
|
290
|
+
}
|
|
247
291
|
//# sourceMappingURL=diagnostics.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"diagnostics.js","sourceRoot":"","sources":["../../src/lib/diagnostics.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"diagnostics.js","sourceRoot":"","sources":["../../src/lib/diagnostics.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Microsoft Entra ID sign-in for the ATK CLI (`atk login` / `atk logout`).
|
|
3
|
+
*
|
|
4
|
+
* Wraps `@azure/msal-node`'s public-client device-code flow against the
|
|
5
|
+
* `ES ATK CLI` app registration and persists MSAL's token cache to
|
|
6
|
+
* `~/.atk/auth/msal-cache.json` through an `ICachePlugin`. The file holds a
|
|
7
|
+
* refresh token, so it is created with mode 0600 (best effort on Windows) and
|
|
8
|
+
* lives outside `~/.atk/cache/`, which `atk cache clear` deletes wholesale.
|
|
9
|
+
*
|
|
10
|
+
* MSAL is imported lazily, and only once a cache file exists or a sign-in is
|
|
11
|
+
* requested, so GitHub-token users pay no startup cost for it.
|
|
12
|
+
*
|
|
13
|
+
* The tenant, client id and scope are constants: one set of registrations
|
|
14
|
+
* serves dev and prod, so a single sign-in works against both APIs.
|
|
15
|
+
*/
|
|
16
|
+
/** The Emergent Software tenant. */
|
|
17
|
+
export declare const ENTRA_TENANT_ID = "25ee13ae-a8a5-4bc2-bb23-aea90536fb0c";
|
|
18
|
+
export declare const ENTRA_AUTHORITY = "https://login.microsoftonline.com/25ee13ae-a8a5-4bc2-bb23-aea90536fb0c";
|
|
19
|
+
/** `ES ATK CLI`: a public client allowed to use the device-code flow. */
|
|
20
|
+
export declare const ENTRA_CLIENT_ID = "488be9fb-eb4d-464c-8045-a5e4783658c6";
|
|
21
|
+
/** The `ES ATK API` scope every ATK access token must carry. MSAL adds `openid profile offline_access` itself. */
|
|
22
|
+
export declare const ENTRA_SCOPES: string[];
|
|
23
|
+
/** What the CLI shows the user while the device-code flow waits for them. */
|
|
24
|
+
export interface DeviceCodePrompt {
|
|
25
|
+
/** Seconds until the code expires. */
|
|
26
|
+
expiresIn: number;
|
|
27
|
+
/** Microsoft's own instruction text, e.g. "To sign in, use a web browser to open ...". */
|
|
28
|
+
message: string;
|
|
29
|
+
userCode: string;
|
|
30
|
+
verificationUri: string;
|
|
31
|
+
}
|
|
32
|
+
/** The signed-in account, reduced to what the CLI needs. */
|
|
33
|
+
export interface EntraAccount {
|
|
34
|
+
homeAccountId: string;
|
|
35
|
+
name?: string;
|
|
36
|
+
/** The user principal name, e.g. `jasonp@emergentsoftware.net`. */
|
|
37
|
+
username: string;
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* Result of {@link getEntraSession}.
|
|
41
|
+
*
|
|
42
|
+
* - `none`: no cache file, or a cache with no account (never signed in, or signed out).
|
|
43
|
+
* - `expired`: an account exists but the refresh token was rejected; the user must run `atk login`.
|
|
44
|
+
* - `error`: MSAL failed for another reason (network, corrupt cache); `detail` says why.
|
|
45
|
+
*/
|
|
46
|
+
export type EntraSession = {
|
|
47
|
+
account: EntraAccount;
|
|
48
|
+
ok: true;
|
|
49
|
+
token: string;
|
|
50
|
+
} | {
|
|
51
|
+
detail?: string;
|
|
52
|
+
ok: false;
|
|
53
|
+
reason: 'error' | 'expired' | 'none';
|
|
54
|
+
};
|
|
55
|
+
/** Path of the persisted MSAL cache: `~/.atk/auth/msal-cache.json`. */
|
|
56
|
+
export declare function getEntraCachePath(): string;
|
|
57
|
+
/**
|
|
58
|
+
* Silently obtain an access token for the cached Entra account.
|
|
59
|
+
*
|
|
60
|
+
* Short-circuits without importing MSAL when no cache file exists. An
|
|
61
|
+
* interaction-required or invalid-grant failure means the session has expired.
|
|
62
|
+
*/
|
|
63
|
+
export declare function getEntraSession(): Promise<EntraSession>;
|
|
64
|
+
/**
|
|
65
|
+
* Run the device-code flow. `onCode` receives the verification URL and user
|
|
66
|
+
* code as soon as Entra issues them; the promise resolves once the user has
|
|
67
|
+
* completed sign-in in their browser.
|
|
68
|
+
*/
|
|
69
|
+
export declare function loginWithDeviceCode(onCode: (prompt: DeviceCodePrompt) => void): Promise<{
|
|
70
|
+
account: EntraAccount;
|
|
71
|
+
token: string;
|
|
72
|
+
}>;
|
|
73
|
+
/**
|
|
74
|
+
* Remove every account from the MSAL cache and delete the cache file.
|
|
75
|
+
* Returns whether a signed-in account was actually removed.
|
|
76
|
+
*/
|
|
77
|
+
export declare function logoutEntra(): Promise<boolean>;
|
|
78
|
+
/**
|
|
79
|
+
* Remove one account (and its tokens) from the cache. Used by `atk login`
|
|
80
|
+
* when the API refuses a freshly acquired token (e.g. a guest account) so a
|
|
81
|
+
* useless session is not left behind.
|
|
82
|
+
*/
|
|
83
|
+
export declare function removeEntraAccount(account: EntraAccount): Promise<void>;
|
|
84
|
+
/** Forget the memoised MSAL client. Intended for tests. */
|
|
85
|
+
export declare function resetEntraClient(): void;
|
|
86
|
+
//# sourceMappingURL=entra.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"entra.d.ts","sourceRoot":"","sources":["../../src/lib/entra.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AASH,oCAAoC;AACpC,eAAO,MAAM,eAAe,yCAAyC,CAAC;AACtE,eAAO,MAAM,eAAe,2EAAyD,CAAC;AACtF,yEAAyE;AACzE,eAAO,MAAM,eAAe,yCAAyC,CAAC;AACtE,kHAAkH;AAClH,eAAO,MAAM,YAAY,UAAgE,CAAC;AAE1F,6EAA6E;AAC7E,MAAM,WAAW,gBAAgB;IAC/B,sCAAsC;IACtC,SAAS,EAAE,MAAM,CAAC;IAClB,0FAA0F;IAC1F,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,EAAE,MAAM,CAAC;IACjB,eAAe,EAAE,MAAM,CAAC;CACzB;AAED,4DAA4D;AAC5D,MAAM,WAAW,YAAY;IAC3B,aAAa,EAAE,MAAM,CAAC;IACtB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,mEAAmE;IACnE,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED;;;;;;GAMG;AACH,MAAM,MAAM,YAAY,GACpB;IAAE,OAAO,EAAE,YAAY,CAAC;IAAC,EAAE,EAAE,IAAI,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,GAClD;IAAE,MAAM,CAAC,EAAE,MAAM,CAAC;IAAC,EAAE,EAAE,KAAK,CAAC;IAAC,MAAM,EAAE,OAAO,GAAG,SAAS,GAAG,MAAM,CAAA;CAAE,CAAC;AAIzE,uEAAuE;AACvE,wBAAgB,iBAAiB,IAAI,MAAM,CAE1C;AAED;;;;;GAKG;AACH,wBAAsB,eAAe,IAAI,OAAO,CAAC,YAAY,CAAC,CAmB7D;AAED;;;;GAIG;AACH,wBAAsB,mBAAmB,CACvC,MAAM,EAAE,CAAC,MAAM,EAAE,gBAAgB,KAAK,IAAI,GACzC,OAAO,CAAC;IAAE,OAAO,EAAE,YAAY,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,CAAC,CAyBnD;AAED;;;GAGG;AACH,wBAAsB,WAAW,IAAI,OAAO,CAAC,OAAO,CAAC,CAsBpD;AAED;;;;GAIG;AACH,wBAAsB,kBAAkB,CAAC,OAAO,EAAE,YAAY,GAAG,OAAO,CAAC,IAAI,CAAC,CAO7E;AAED,2DAA2D;AAC3D,wBAAgB,gBAAgB,IAAI,IAAI,CAEvC"}
|
|
@@ -0,0 +1,209 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Microsoft Entra ID sign-in for the ATK CLI (`atk login` / `atk logout`).
|
|
3
|
+
*
|
|
4
|
+
* Wraps `@azure/msal-node`'s public-client device-code flow against the
|
|
5
|
+
* `ES ATK CLI` app registration and persists MSAL's token cache to
|
|
6
|
+
* `~/.atk/auth/msal-cache.json` through an `ICachePlugin`. The file holds a
|
|
7
|
+
* refresh token, so it is created with mode 0600 (best effort on Windows) and
|
|
8
|
+
* lives outside `~/.atk/cache/`, which `atk cache clear` deletes wholesale.
|
|
9
|
+
*
|
|
10
|
+
* MSAL is imported lazily, and only once a cache file exists or a sign-in is
|
|
11
|
+
* requested, so GitHub-token users pay no startup cost for it.
|
|
12
|
+
*
|
|
13
|
+
* The tenant, client id and scope are constants: one set of registrations
|
|
14
|
+
* serves dev and prod, so a single sign-in works against both APIs.
|
|
15
|
+
*/
|
|
16
|
+
import { existsSync } from 'node:fs';
|
|
17
|
+
import { mkdir, readFile, rm, writeFile } from 'node:fs/promises';
|
|
18
|
+
import { homedir } from 'node:os';
|
|
19
|
+
import { dirname, join } from 'node:path';
|
|
20
|
+
/** The Emergent Software tenant. */
|
|
21
|
+
export const ENTRA_TENANT_ID = '25ee13ae-a8a5-4bc2-bb23-aea90536fb0c';
|
|
22
|
+
export const ENTRA_AUTHORITY = `https://login.microsoftonline.com/${ENTRA_TENANT_ID}`;
|
|
23
|
+
/** `ES ATK CLI`: a public client allowed to use the device-code flow. */
|
|
24
|
+
export const ENTRA_CLIENT_ID = '488be9fb-eb4d-464c-8045-a5e4783658c6';
|
|
25
|
+
/** The `ES ATK API` scope every ATK access token must carry. MSAL adds `openid profile offline_access` itself. */
|
|
26
|
+
export const ENTRA_SCOPES = ['api://8da5aa72-0565-4ae8-bf5a-db89d8bd3186/access_as_user'];
|
|
27
|
+
let clientPromise;
|
|
28
|
+
/** Path of the persisted MSAL cache: `~/.atk/auth/msal-cache.json`. */
|
|
29
|
+
export function getEntraCachePath() {
|
|
30
|
+
return join(homedir(), '.atk', 'auth', 'msal-cache.json');
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Silently obtain an access token for the cached Entra account.
|
|
34
|
+
*
|
|
35
|
+
* Short-circuits without importing MSAL when no cache file exists. An
|
|
36
|
+
* interaction-required or invalid-grant failure means the session has expired.
|
|
37
|
+
*/
|
|
38
|
+
export async function getEntraSession() {
|
|
39
|
+
if (!existsSync(getEntraCachePath())) {
|
|
40
|
+
return { ok: false, reason: 'none' };
|
|
41
|
+
}
|
|
42
|
+
try {
|
|
43
|
+
const client = await getClient();
|
|
44
|
+
const accounts = await client.getTokenCache().getAllAccounts();
|
|
45
|
+
const account = accounts[0];
|
|
46
|
+
if (!account) {
|
|
47
|
+
return { ok: false, reason: 'none' };
|
|
48
|
+
}
|
|
49
|
+
const result = await client.acquireTokenSilent({ account, scopes: ENTRA_SCOPES });
|
|
50
|
+
return { account: toEntraAccount(result.account ?? account), ok: true, token: result.accessToken };
|
|
51
|
+
}
|
|
52
|
+
catch (err) {
|
|
53
|
+
const detail = err instanceof Error ? err.message : String(err);
|
|
54
|
+
return { detail, ok: false, reason: isSessionExpiredError(err) ? 'expired' : 'error' };
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
/**
|
|
58
|
+
* Run the device-code flow. `onCode` receives the verification URL and user
|
|
59
|
+
* code as soon as Entra issues them; the promise resolves once the user has
|
|
60
|
+
* completed sign-in in their browser.
|
|
61
|
+
*/
|
|
62
|
+
export async function loginWithDeviceCode(onCode) {
|
|
63
|
+
const client = await getClient();
|
|
64
|
+
let result;
|
|
65
|
+
try {
|
|
66
|
+
result = await client.acquireTokenByDeviceCode({
|
|
67
|
+
deviceCodeCallback: (response) => {
|
|
68
|
+
onCode({
|
|
69
|
+
expiresIn: response.expiresIn,
|
|
70
|
+
message: response.message,
|
|
71
|
+
userCode: response.userCode,
|
|
72
|
+
verificationUri: response.verificationUri,
|
|
73
|
+
});
|
|
74
|
+
},
|
|
75
|
+
scopes: ENTRA_SCOPES,
|
|
76
|
+
});
|
|
77
|
+
}
|
|
78
|
+
catch (err) {
|
|
79
|
+
throw new Error(describeLoginFailure(err));
|
|
80
|
+
}
|
|
81
|
+
if (!result?.account) {
|
|
82
|
+
throw new Error('Sign-in did not complete. Run `atk login` again.');
|
|
83
|
+
}
|
|
84
|
+
return { account: toEntraAccount(result.account), token: result.accessToken };
|
|
85
|
+
}
|
|
86
|
+
/**
|
|
87
|
+
* Remove every account from the MSAL cache and delete the cache file.
|
|
88
|
+
* Returns whether a signed-in account was actually removed.
|
|
89
|
+
*/
|
|
90
|
+
export async function logoutEntra() {
|
|
91
|
+
const cachePath = getEntraCachePath();
|
|
92
|
+
if (!existsSync(cachePath)) {
|
|
93
|
+
return false;
|
|
94
|
+
}
|
|
95
|
+
let removed = false;
|
|
96
|
+
try {
|
|
97
|
+
const client = await getClient();
|
|
98
|
+
const cache = client.getTokenCache();
|
|
99
|
+
const accounts = await cache.getAllAccounts();
|
|
100
|
+
for (const account of accounts) {
|
|
101
|
+
await cache.removeAccount(account);
|
|
102
|
+
}
|
|
103
|
+
removed = accounts.length > 0;
|
|
104
|
+
}
|
|
105
|
+
catch {
|
|
106
|
+
// A corrupt cache still counts as a session file worth deleting.
|
|
107
|
+
removed = true;
|
|
108
|
+
}
|
|
109
|
+
await rm(cachePath, { force: true });
|
|
110
|
+
return removed;
|
|
111
|
+
}
|
|
112
|
+
/**
|
|
113
|
+
* Remove one account (and its tokens) from the cache. Used by `atk login`
|
|
114
|
+
* when the API refuses a freshly acquired token (e.g. a guest account) so a
|
|
115
|
+
* useless session is not left behind.
|
|
116
|
+
*/
|
|
117
|
+
export async function removeEntraAccount(account) {
|
|
118
|
+
const client = await getClient();
|
|
119
|
+
const cache = client.getTokenCache();
|
|
120
|
+
const stored = await cache.getAccountByHomeId(account.homeAccountId);
|
|
121
|
+
if (stored) {
|
|
122
|
+
await cache.removeAccount(stored);
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
/** Forget the memoised MSAL client. Intended for tests. */
|
|
126
|
+
export function resetEntraClient() {
|
|
127
|
+
clientPromise = undefined;
|
|
128
|
+
}
|
|
129
|
+
async function buildClient() {
|
|
130
|
+
const msal = await import('@azure/msal-node');
|
|
131
|
+
return new msal.PublicClientApplication({
|
|
132
|
+
auth: { authority: ENTRA_AUTHORITY, clientId: ENTRA_CLIENT_ID },
|
|
133
|
+
cache: { cachePlugin: createFileCachePlugin(getEntraCachePath()) },
|
|
134
|
+
system: {
|
|
135
|
+
loggerOptions: {
|
|
136
|
+
loggerCallback: () => undefined,
|
|
137
|
+
logLevel: msal.LogLevel.Error,
|
|
138
|
+
piiLoggingEnabled: false,
|
|
139
|
+
},
|
|
140
|
+
},
|
|
141
|
+
});
|
|
142
|
+
}
|
|
143
|
+
/**
|
|
144
|
+
* `ICachePlugin` that mirrors MSAL's in-memory cache to {@link getEntraCachePath}.
|
|
145
|
+
* Reads before every cache access; writes only when MSAL reports a change.
|
|
146
|
+
*/
|
|
147
|
+
function createFileCachePlugin(cachePath) {
|
|
148
|
+
return {
|
|
149
|
+
async afterCacheAccess(context) {
|
|
150
|
+
if (!context.cacheHasChanged) {
|
|
151
|
+
return;
|
|
152
|
+
}
|
|
153
|
+
await mkdir(dirname(cachePath), { mode: 0o700, recursive: true });
|
|
154
|
+
await writeFile(cachePath, context.tokenCache.serialize(), { encoding: 'utf-8', mode: 0o600 });
|
|
155
|
+
},
|
|
156
|
+
async beforeCacheAccess(context) {
|
|
157
|
+
let raw;
|
|
158
|
+
try {
|
|
159
|
+
raw = await readFile(cachePath, 'utf-8');
|
|
160
|
+
}
|
|
161
|
+
catch {
|
|
162
|
+
return; // No cache yet: MSAL starts empty.
|
|
163
|
+
}
|
|
164
|
+
context.tokenCache.deserialize(raw);
|
|
165
|
+
},
|
|
166
|
+
};
|
|
167
|
+
}
|
|
168
|
+
function describeLoginFailure(err) {
|
|
169
|
+
const code = errorCodeOf(err);
|
|
170
|
+
if (code === 'device_code_expired') {
|
|
171
|
+
return 'The sign-in code expired before it was used. Run `atk login` again.';
|
|
172
|
+
}
|
|
173
|
+
if (code === 'device_code_polling_cancelled') {
|
|
174
|
+
return 'Sign-in was cancelled.';
|
|
175
|
+
}
|
|
176
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
177
|
+
return `Sign-in failed: ${message}`;
|
|
178
|
+
}
|
|
179
|
+
function errorCodeOf(err) {
|
|
180
|
+
const code = err?.errorCode;
|
|
181
|
+
return typeof code === 'string' ? code : undefined;
|
|
182
|
+
}
|
|
183
|
+
async function getClient() {
|
|
184
|
+
clientPromise ??= buildClient();
|
|
185
|
+
return clientPromise;
|
|
186
|
+
}
|
|
187
|
+
/**
|
|
188
|
+
* Whether an MSAL failure means the user must sign in again (as opposed to a
|
|
189
|
+
* transient or environmental problem). Checked by error name and code rather
|
|
190
|
+
* than `instanceof` so it holds when MSAL is module-mocked in tests.
|
|
191
|
+
*/
|
|
192
|
+
function isSessionExpiredError(err) {
|
|
193
|
+
if (!(err instanceof Error)) {
|
|
194
|
+
return false;
|
|
195
|
+
}
|
|
196
|
+
if (err.name === 'InteractionRequiredAuthError') {
|
|
197
|
+
return true;
|
|
198
|
+
}
|
|
199
|
+
const code = errorCodeOf(err);
|
|
200
|
+
return code === 'invalid_grant' || code === 'refresh_token_expired' || code === 'no_tokens_found';
|
|
201
|
+
}
|
|
202
|
+
function toEntraAccount(account) {
|
|
203
|
+
return {
|
|
204
|
+
homeAccountId: account.homeAccountId,
|
|
205
|
+
...(account.name ? { name: account.name } : {}),
|
|
206
|
+
username: account.username,
|
|
207
|
+
};
|
|
208
|
+
}
|
|
209
|
+
//# sourceMappingURL=entra.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"entra.js","sourceRoot":"","sources":["../../src/lib/entra.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Once-per-run notices that the App shell renders above a command's output.
|
|
3
|
+
*
|
|
4
|
+
* Library code (the API client, the credential chain) records a notice when it
|
|
5
|
+
* learns something the user should see once per invocation, e.g. that GitHub
|
|
6
|
+
* sign-in is being retired, or that the cached Emergent session has expired
|
|
7
|
+
* and a GitHub token was used instead. `_app.tsx` subscribes and renders one
|
|
8
|
+
* line per notice in non-JSON mode; JSON mode never shows them.
|
|
9
|
+
*
|
|
10
|
+
* Notices are deduplicated by `kind`, so recording the same one from every
|
|
11
|
+
* API response still yields a single banner.
|
|
12
|
+
*/
|
|
13
|
+
export type Notice =
|
|
14
|
+
/** The cached Entra session could not be refreshed and a GitHub token was used instead. */
|
|
15
|
+
{
|
|
16
|
+
date: string;
|
|
17
|
+
kind: 'github-sunset';
|
|
18
|
+
}
|
|
19
|
+
/** The API announced (via `Sunset`) the date GitHub sign-in ends. `date` is `YYYY-MM-DD`. */
|
|
20
|
+
| {
|
|
21
|
+
kind: 'entra-expired';
|
|
22
|
+
};
|
|
23
|
+
type Listener = () => void;
|
|
24
|
+
/** Record a notice unless one of the same kind is already queued. */
|
|
25
|
+
export declare function addNotice(notice: Notice): void;
|
|
26
|
+
/** Drop every queued notice. Intended for tests. */
|
|
27
|
+
export declare function clearNotices(): void;
|
|
28
|
+
/** Current notices, as a stable array reference (safe for `useSyncExternalStore`). */
|
|
29
|
+
export declare function getNotices(): readonly Notice[];
|
|
30
|
+
/** Subscribe to changes; returns the unsubscribe function. */
|
|
31
|
+
export declare function subscribeNotices(listener: Listener): () => void;
|
|
32
|
+
export {};
|
|
33
|
+
//# sourceMappingURL=notices.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"notices.d.ts","sourceRoot":"","sources":["../../src/lib/notices.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,MAAM,MAAM,MAAM;AAChB,2FAA2F;AACzF;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,eAAe,CAAA;CAAE;AACzC,6FAA6F;GAC3F;IAAE,IAAI,EAAE,eAAe,CAAA;CAAE,CAAC;AAE9B,KAAK,QAAQ,GAAG,MAAM,IAAI,CAAC;AAK3B,qEAAqE;AACrE,wBAAgB,SAAS,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI,CAQ9C;AAED,oDAAoD;AACpD,wBAAgB,YAAY,IAAI,IAAI,CAKnC;AAED,sFAAsF;AACtF,wBAAgB,UAAU,IAAI,SAAS,MAAM,EAAE,CAE9C;AAED,8DAA8D;AAC9D,wBAAgB,gBAAgB,CAAC,QAAQ,EAAE,QAAQ,GAAG,MAAM,IAAI,CAK/D"}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Once-per-run notices that the App shell renders above a command's output.
|
|
3
|
+
*
|
|
4
|
+
* Library code (the API client, the credential chain) records a notice when it
|
|
5
|
+
* learns something the user should see once per invocation, e.g. that GitHub
|
|
6
|
+
* sign-in is being retired, or that the cached Emergent session has expired
|
|
7
|
+
* and a GitHub token was used instead. `_app.tsx` subscribes and renders one
|
|
8
|
+
* line per notice in non-JSON mode; JSON mode never shows them.
|
|
9
|
+
*
|
|
10
|
+
* Notices are deduplicated by `kind`, so recording the same one from every
|
|
11
|
+
* API response still yields a single banner.
|
|
12
|
+
*/
|
|
13
|
+
let notices = [];
|
|
14
|
+
const listeners = new Set();
|
|
15
|
+
/** Record a notice unless one of the same kind is already queued. */
|
|
16
|
+
export function addNotice(notice) {
|
|
17
|
+
if (notices.some((n) => n.kind === notice.kind)) {
|
|
18
|
+
return;
|
|
19
|
+
}
|
|
20
|
+
notices = [...notices, notice];
|
|
21
|
+
for (const listener of listeners) {
|
|
22
|
+
listener();
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
/** Drop every queued notice. Intended for tests. */
|
|
26
|
+
export function clearNotices() {
|
|
27
|
+
notices = [];
|
|
28
|
+
for (const listener of listeners) {
|
|
29
|
+
listener();
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
/** Current notices, as a stable array reference (safe for `useSyncExternalStore`). */
|
|
33
|
+
export function getNotices() {
|
|
34
|
+
return notices;
|
|
35
|
+
}
|
|
36
|
+
/** Subscribe to changes; returns the unsubscribe function. */
|
|
37
|
+
export function subscribeNotices(listener) {
|
|
38
|
+
listeners.add(listener);
|
|
39
|
+
return () => {
|
|
40
|
+
listeners.delete(listener);
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
//# sourceMappingURL=notices.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"notices.js","sourceRoot":"","sources":["../../src/lib/notices.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAUH,IAAI,OAAO,GAAsB,EAAE,CAAC;AACpC,MAAM,SAAS,GAAG,IAAI,GAAG,EAAY,CAAC;AAEtC,qEAAqE;AACrE,MAAM,UAAU,SAAS,CAAC,MAAc;IACtC,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,MAAM,CAAC,IAAI,CAAC,EAAE,CAAC;QAChD,OAAO;IACT,CAAC;IACD,OAAO,GAAG,CAAC,GAAG,OAAO,EAAE,MAAM,CAAC,CAAC;IAC/B,KAAK,MAAM,QAAQ,IAAI,SAAS,EAAE,CAAC;QACjC,QAAQ,EAAE,CAAC;IACb,CAAC;AACH,CAAC;AAED,oDAAoD;AACpD,MAAM,UAAU,YAAY;IAC1B,OAAO,GAAG,EAAE,CAAC;IACb,KAAK,MAAM,QAAQ,IAAI,SAAS,EAAE,CAAC;QACjC,QAAQ,EAAE,CAAC;IACb,CAAC;AACH,CAAC;AAED,sFAAsF;AACtF,MAAM,UAAU,UAAU;IACxB,OAAO,OAAO,CAAC;AACjB,CAAC;AAED,8DAA8D;AAC9D,MAAM,UAAU,gBAAgB,CAAC,QAAkB;IACjD,SAAS,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC;IACxB,OAAO,GAAG,EAAE;QACV,SAAS,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC;IAC7B,CAAC,CAAC;AACJ,CAAC"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@detergent-software/atk",
|
|
3
|
-
"version": "7.
|
|
3
|
+
"version": "7.11.0",
|
|
4
4
|
"description": "Agentic Tool Kit — CLI for managing AI coding tool assets.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -44,6 +44,7 @@
|
|
|
44
44
|
}
|
|
45
45
|
},
|
|
46
46
|
"dependencies": {
|
|
47
|
+
"@azure/msal-node": "^6.0.0",
|
|
47
48
|
"chalk": "^5.4.0",
|
|
48
49
|
"diff": "8.0.3",
|
|
49
50
|
"ink": "^6.0.0",
|