@detergent-software/atk 7.10.1-dev.1 → 7.11.0-dev.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.
- 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/index.d.ts.map +1 -1
- package/build/commands/index.js +5 -2
- package/build/commands/index.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/uninstall.js +1 -1
- package/build/commands/uninstall.js.map +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
|
@@ -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-dev.2",
|
|
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",
|