@codemieai/code 0.0.55 → 0.0.57
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 +28 -0
- package/dist/agents/core/AgentCLI.d.ts.map +1 -1
- package/dist/agents/core/AgentCLI.js +27 -2
- package/dist/agents/core/AgentCLI.js.map +1 -1
- package/dist/agents/core/BaseAgentAdapter.d.ts.map +1 -1
- package/dist/agents/core/BaseAgentAdapter.js +20 -6
- package/dist/agents/core/BaseAgentAdapter.js.map +1 -1
- package/dist/agents/core/session/ensure-session.d.ts.map +1 -1
- package/dist/agents/core/session/ensure-session.js +7 -2
- package/dist/agents/core/session/ensure-session.js.map +1 -1
- package/dist/agents/core/session/types.d.ts +1 -0
- package/dist/agents/core/session/types.d.ts.map +1 -1
- package/dist/agents/plugins/claude/claude.plugin.js +4 -4
- package/dist/agents/plugins/claude/claude.plugin.js.map +1 -1
- package/dist/agents/plugins/claude/plugin/.claude-plugin/plugin.json +1 -1
- package/dist/agents/plugins/claude/plugin/skills/codemie-html-report/SKILL.md +273 -0
- package/dist/agents/plugins/claude/plugin/skills/codemie-html-report/style-guide/css/base.css +166 -0
- package/dist/agents/plugins/claude/plugin/skills/codemie-html-report/style-guide/css/buttons.css +253 -0
- package/dist/agents/plugins/claude/plugin/skills/codemie-html-report/style-guide/css/components.css +605 -0
- package/dist/agents/plugins/claude/plugin/skills/codemie-html-report/style-guide/css/forms.css +550 -0
- package/dist/agents/plugins/claude/plugin/skills/codemie-html-report/style-guide/css/layout.css +410 -0
- package/dist/agents/plugins/claude/plugin/skills/codemie-html-report/style-guide/css/tokens.css +323 -0
- package/dist/agents/plugins/claude/plugin/skills/codemie-html-report/style-guide/css/typography.css +155 -0
- package/dist/agents/plugins/claude/plugin/skills/codemie-html-report/style-guide/css/utilities.css +238 -0
- package/dist/agents/plugins/claude/plugin/skills/msgraph/SKILL.md +10 -8
- package/dist/agents/plugins/claude/plugin/skills/msgraph/scripts/msgraph.js +298 -149
- package/dist/cli/commands/hook.d.ts +2 -0
- package/dist/cli/commands/hook.d.ts.map +1 -1
- package/dist/cli/commands/hook.js +36 -31
- package/dist/cli/commands/hook.js.map +1 -1
- package/dist/cli/commands/setup.js +11 -1
- package/dist/cli/commands/setup.js.map +1 -1
- package/dist/providers/core/codemie-auth-helpers.d.ts +22 -0
- package/dist/providers/core/codemie-auth-helpers.d.ts.map +1 -0
- package/dist/providers/core/codemie-auth-helpers.js +118 -0
- package/dist/providers/core/codemie-auth-helpers.js.map +1 -0
- package/dist/providers/core/index.d.ts +1 -0
- package/dist/providers/core/index.d.ts.map +1 -1
- package/dist/providers/core/index.js +1 -0
- package/dist/providers/core/index.js.map +1 -1
- package/dist/providers/core/types.d.ts +4 -0
- package/dist/providers/core/types.d.ts.map +1 -1
- package/dist/providers/core/types.js.map +1 -1
- package/dist/providers/index.d.ts +2 -0
- package/dist/providers/index.d.ts.map +1 -1
- package/dist/providers/index.js +2 -0
- package/dist/providers/index.js.map +1 -1
- package/dist/providers/plugins/anthropic-subscription/anthropic-subscription.auth.d.ts +11 -0
- package/dist/providers/plugins/anthropic-subscription/anthropic-subscription.auth.d.ts.map +1 -0
- package/dist/providers/plugins/anthropic-subscription/anthropic-subscription.auth.js +30 -0
- package/dist/providers/plugins/anthropic-subscription/anthropic-subscription.auth.js.map +1 -0
- package/dist/providers/plugins/anthropic-subscription/anthropic-subscription.setup-steps.d.ts +8 -0
- package/dist/providers/plugins/anthropic-subscription/anthropic-subscription.setup-steps.d.ts.map +1 -0
- package/dist/providers/plugins/anthropic-subscription/anthropic-subscription.setup-steps.js +88 -0
- package/dist/providers/plugins/anthropic-subscription/anthropic-subscription.setup-steps.js.map +1 -0
- package/dist/providers/plugins/anthropic-subscription/anthropic-subscription.template.d.ts +11 -0
- package/dist/providers/plugins/anthropic-subscription/anthropic-subscription.template.d.ts.map +1 -0
- package/dist/providers/plugins/anthropic-subscription/anthropic-subscription.template.js +119 -0
- package/dist/providers/plugins/anthropic-subscription/anthropic-subscription.template.js.map +1 -0
- package/dist/providers/plugins/anthropic-subscription/index.d.ts +9 -0
- package/dist/providers/plugins/anthropic-subscription/index.d.ts.map +1 -0
- package/dist/providers/plugins/anthropic-subscription/index.js +9 -0
- package/dist/providers/plugins/anthropic-subscription/index.js.map +1 -0
- package/dist/providers/plugins/jwt/jwt.setup-steps.js +1 -1
- package/dist/providers/plugins/jwt/jwt.setup-steps.js.map +1 -1
- package/dist/providers/plugins/sso/proxy/plugins/claude-thinking-transformer.plugin.d.ts +30 -0
- package/dist/providers/plugins/sso/proxy/plugins/claude-thinking-transformer.plugin.d.ts.map +1 -0
- package/dist/providers/plugins/sso/proxy/plugins/claude-thinking-transformer.plugin.js +111 -0
- package/dist/providers/plugins/sso/proxy/plugins/claude-thinking-transformer.plugin.js.map +1 -0
- package/dist/providers/plugins/sso/proxy/plugins/index.d.ts +2 -1
- package/dist/providers/plugins/sso/proxy/plugins/index.d.ts.map +1 -1
- package/dist/providers/plugins/sso/proxy/plugins/index.js +3 -1
- package/dist/providers/plugins/sso/proxy/plugins/index.js.map +1 -1
- package/dist/providers/plugins/sso/proxy/plugins/sso.session-sync.plugin.d.ts.map +1 -1
- package/dist/providers/plugins/sso/proxy/plugins/sso.session-sync.plugin.js +9 -7
- package/dist/providers/plugins/sso/proxy/plugins/sso.session-sync.plugin.js.map +1 -1
- package/dist/providers/plugins/sso/proxy/plugins/types.d.ts +1 -0
- package/dist/providers/plugins/sso/proxy/plugins/types.d.ts.map +1 -1
- package/dist/providers/plugins/sso/proxy/proxy-types.d.ts +2 -0
- package/dist/providers/plugins/sso/proxy/proxy-types.d.ts.map +1 -1
- package/dist/providers/plugins/sso/proxy/sso.proxy.d.ts.map +1 -1
- package/dist/providers/plugins/sso/proxy/sso.proxy.js +10 -0
- package/dist/providers/plugins/sso/proxy/sso.proxy.js.map +1 -1
- package/dist/providers/plugins/sso/session/processors/metrics/metrics-aggregator.js +1 -1
- package/dist/providers/plugins/sso/session/processors/metrics/metrics-aggregator.js.map +1 -1
- package/dist/providers/plugins/sso/session/processors/metrics/metrics-api-client.d.ts +2 -2
- package/dist/providers/plugins/sso/session/processors/metrics/metrics-api-client.d.ts.map +1 -1
- package/dist/providers/plugins/sso/session/processors/metrics/metrics-api-client.js +4 -4
- package/dist/providers/plugins/sso/session/processors/metrics/metrics-api-client.js.map +1 -1
- package/dist/providers/plugins/sso/sso.auth.d.ts +0 -4
- package/dist/providers/plugins/sso/sso.auth.d.ts.map +1 -1
- package/dist/providers/plugins/sso/sso.auth.js +3 -13
- package/dist/providers/plugins/sso/sso.auth.js.map +1 -1
- package/dist/providers/plugins/sso/sso.http-client.d.ts +3 -42
- package/dist/providers/plugins/sso/sso.http-client.d.ts.map +1 -1
- package/dist/providers/plugins/sso/sso.http-client.js +4 -75
- package/dist/providers/plugins/sso/sso.http-client.js.map +1 -1
- package/dist/providers/plugins/sso/sso.setup-steps.d.ts.map +1 -1
- package/dist/providers/plugins/sso/sso.setup-steps.js +15 -66
- package/dist/providers/plugins/sso/sso.setup-steps.js.map +1 -1
- package/dist/providers/plugins/sso/sso.template.d.ts.map +1 -1
- package/dist/providers/plugins/sso/sso.template.js +2 -1
- package/dist/providers/plugins/sso/sso.template.js.map +1 -1
- package/dist/utils/config.d.ts.map +1 -1
- package/dist/utils/config.js +17 -0
- package/dist/utils/config.js.map +1 -1
- package/dist/utils/processes.d.ts +9 -0
- package/dist/utils/processes.d.ts.map +1 -1
- package/dist/utils/processes.js +21 -0
- package/dist/utils/processes.js.map +1 -1
- package/package.json +3 -3
|
@@ -2,24 +2,40 @@
|
|
|
2
2
|
/**
|
|
3
3
|
* msgraph.js — Microsoft Graph API CLI for Claude Code skill
|
|
4
4
|
*
|
|
5
|
-
* Authentication:
|
|
6
|
-
*
|
|
7
|
-
*
|
|
5
|
+
* Authentication: Authorization Code + PKCE via system browser.
|
|
6
|
+
* The browser-based flow allows the Microsoft Enterprise SSO plug-in (macOS/
|
|
7
|
+
* Intune) and WAM (Windows/Azure AD join) to attach device claims to the
|
|
8
|
+
* Entra ID sign-in, satisfying Conditional Access Policies that require
|
|
9
|
+
* managed/compliant devices.
|
|
10
|
+
*
|
|
11
|
+
* First time: node msgraph.js login (opens browser)
|
|
12
|
+
* Subsequent: token refreshed silently from cache
|
|
13
|
+
*
|
|
14
|
+
* Prerequisites for device claims (CAP compliance):
|
|
15
|
+
* - App registration must have http://localhost as a Mobile and desktop
|
|
16
|
+
* applications redirect URI (loopback matching per RFC 8252).
|
|
17
|
+
* - macOS: device enrolled in Intune + Microsoft Enterprise SSO Extension
|
|
18
|
+
* deployed via MDM profile.
|
|
19
|
+
* - Windows: device Azure AD joined (WAM handles it automatically).
|
|
8
20
|
*
|
|
9
21
|
* Dependencies: node >= 18 (built-in modules only — zero npm installs needed)
|
|
10
22
|
*/
|
|
11
23
|
|
|
12
24
|
'use strict';
|
|
13
25
|
|
|
14
|
-
const
|
|
15
|
-
const
|
|
16
|
-
const
|
|
17
|
-
const
|
|
26
|
+
const crypto = require('node:crypto');
|
|
27
|
+
const http = require('node:http');
|
|
28
|
+
const https = require('node:https');
|
|
29
|
+
const fs = require('node:fs');
|
|
30
|
+
const path = require('node:path');
|
|
31
|
+
const os = require('node:os');
|
|
32
|
+
const { execFile } = require('node:child_process');
|
|
18
33
|
|
|
19
34
|
// ── Config ────────────────────────────────────────────────────────────────────
|
|
20
|
-
const CLIENT_ID = '3d7688c6-f449-4d04-8b0d-57d94818e922';
|
|
21
|
-
const
|
|
22
|
-
const
|
|
35
|
+
const CLIENT_ID = '3d7688c6-f449-4d04-8b0d-57d94818e922';
|
|
36
|
+
const TENANT = 'common';
|
|
37
|
+
const AUTH_URL = `https://login.microsoftonline.com/${TENANT}/oauth2/v2.0/authorize`;
|
|
38
|
+
const TOKEN_URL = `https://login.microsoftonline.com/${TENANT}/oauth2/v2.0/token`;
|
|
23
39
|
const SCOPES = [
|
|
24
40
|
'User.Read', 'Mail.Read', 'Mail.Send',
|
|
25
41
|
'Calendars.Read', 'Calendars.ReadWrite',
|
|
@@ -27,9 +43,11 @@ const SCOPES = [
|
|
|
27
43
|
'Sites.Read.All', 'Chat.Read', 'Chat.ReadWrite',
|
|
28
44
|
'OnlineMeetingTranscript.Read.All', 'OnlineMeetings.Read',
|
|
29
45
|
'People.Read', 'Contacts.Read', 'offline_access',
|
|
46
|
+
'Notes.Read', 'Notes.ReadWrite',
|
|
30
47
|
].join(' ');
|
|
31
|
-
const CACHE_FILE
|
|
32
|
-
const GRAPH_BASE
|
|
48
|
+
const CACHE_FILE = path.join(os.homedir(), '.ms_graph_token_cache.json');
|
|
49
|
+
const GRAPH_BASE = 'https://graph.microsoft.com/v1.0';
|
|
50
|
+
const TIMEOUT_MS = 5 * 60 * 1000;
|
|
33
51
|
|
|
34
52
|
// ── HTTP Helpers ──────────────────────────────────────────────────────────────
|
|
35
53
|
function httpsRequest(urlStr, options = {}, body = null) {
|
|
@@ -93,7 +111,6 @@ async function graphPost(endpoint, token, body) {
|
|
|
93
111
|
return res.body ? JSON.parse(res.body) : {};
|
|
94
112
|
}
|
|
95
113
|
|
|
96
|
-
/** Download file content, following 302 redirects (Graph uses CDN redirects). */
|
|
97
114
|
function graphDownload(endpoint, token) {
|
|
98
115
|
function fetch(url, auth) {
|
|
99
116
|
return new Promise((resolve, reject) => {
|
|
@@ -137,6 +154,75 @@ function saveCache(data) {
|
|
|
137
154
|
fs.writeFileSync(CACHE_FILE, JSON.stringify(data, null, 2), { mode: 0o600 });
|
|
138
155
|
}
|
|
139
156
|
|
|
157
|
+
// ── PKCE helpers ──────────────────────────────────────────────────────────────
|
|
158
|
+
function generatePKCE() {
|
|
159
|
+
const verifier = crypto.randomBytes(32).toString('base64url');
|
|
160
|
+
const challenge = crypto.createHash('sha256').update(verifier).digest('base64url');
|
|
161
|
+
return { verifier, challenge };
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
function openBrowser(url) {
|
|
165
|
+
const cb = err => {
|
|
166
|
+
if (err) console.error(`Warning: could not open browser automatically: ${err.message}`);
|
|
167
|
+
};
|
|
168
|
+
if (process.platform === 'win32') {
|
|
169
|
+
execFile('cmd', ['/c', 'start', '', url], cb);
|
|
170
|
+
} else {
|
|
171
|
+
execFile(process.platform === 'darwin' ? 'open' : 'xdg-open', [url], cb);
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
function startLocalServer() {
|
|
176
|
+
return new Promise((resolve, reject) => {
|
|
177
|
+
const server = http.createServer();
|
|
178
|
+
server.listen(0, 'localhost', () => {
|
|
179
|
+
const { port } = server.address();
|
|
180
|
+
const waitForCode = () => new Promise((res, rej) => {
|
|
181
|
+
const timer = setTimeout(() => {
|
|
182
|
+
server.close();
|
|
183
|
+
rej(new Error('Authentication timed out (5 minutes). Run login again.'));
|
|
184
|
+
}, TIMEOUT_MS);
|
|
185
|
+
|
|
186
|
+
server.on('request', (req, response) => {
|
|
187
|
+
const url = new URL(req.url, `http://localhost:${port}`);
|
|
188
|
+
const code = url.searchParams.get('code');
|
|
189
|
+
const state = url.searchParams.get('state');
|
|
190
|
+
const error = url.searchParams.get('error');
|
|
191
|
+
const desc = url.searchParams.get('error_description');
|
|
192
|
+
|
|
193
|
+
if (!code && !error) {
|
|
194
|
+
response.writeHead(404);
|
|
195
|
+
response.end();
|
|
196
|
+
return;
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
clearTimeout(timer);
|
|
200
|
+
const html = code
|
|
201
|
+
? `<!DOCTYPE html><html><head><title>Login successful</title></head><body style="font-family:sans-serif;padding:40px">
|
|
202
|
+
<h2 style="color:#107c10">✓ Authentication successful</h2>
|
|
203
|
+
<p>You can close this tab and return to the terminal.</p>
|
|
204
|
+
<script>window.close();</script>
|
|
205
|
+
</body></html>`
|
|
206
|
+
: `<!DOCTYPE html><html><head><title>Login failed</title></head><body style="font-family:sans-serif;padding:40px">
|
|
207
|
+
<h2 style="color:#d13438">✗ Authentication failed</h2>
|
|
208
|
+
<p><strong>${error || 'Unknown error'}</strong></p>
|
|
209
|
+
<p>${desc || ''}</p>
|
|
210
|
+
</body></html>`;
|
|
211
|
+
|
|
212
|
+
response.writeHead(200, { 'Content-Type': 'text/html; charset=utf-8' });
|
|
213
|
+
response.end(html, () => {
|
|
214
|
+
server.close();
|
|
215
|
+
if (code) res({ code, state });
|
|
216
|
+
else rej(new Error(`Auth error: ${error}${desc ? ' — ' + desc : ''}`));
|
|
217
|
+
});
|
|
218
|
+
});
|
|
219
|
+
});
|
|
220
|
+
resolve({ port, waitForCode });
|
|
221
|
+
});
|
|
222
|
+
server.on('error', reject);
|
|
223
|
+
});
|
|
224
|
+
}
|
|
225
|
+
|
|
140
226
|
// ── Authentication ────────────────────────────────────────────────────────────
|
|
141
227
|
async function tryRefresh(refreshTkn, username) {
|
|
142
228
|
try {
|
|
@@ -159,31 +245,54 @@ async function tryRefresh(refreshTkn, username) {
|
|
|
159
245
|
return null;
|
|
160
246
|
}
|
|
161
247
|
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
const
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
}
|
|
248
|
+
async function pkceLogin(forcePrompt = false) {
|
|
249
|
+
const { verifier, challenge } = generatePKCE();
|
|
250
|
+
const state = crypto.randomBytes(16).toString('hex');
|
|
251
|
+
const { port, waitForCode } = await startLocalServer();
|
|
252
|
+
const redirectUri = `http://localhost:${port}`;
|
|
253
|
+
|
|
254
|
+
const authParams = new URLSearchParams({
|
|
255
|
+
client_id: CLIENT_ID,
|
|
256
|
+
response_type: 'code',
|
|
257
|
+
redirect_uri: redirectUri,
|
|
258
|
+
scope: SCOPES,
|
|
259
|
+
state,
|
|
260
|
+
code_challenge: challenge,
|
|
261
|
+
code_challenge_method: 'S256',
|
|
262
|
+
prompt: forcePrompt ? 'login' : 'select_account',
|
|
263
|
+
});
|
|
178
264
|
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
265
|
+
const fullAuthUrl = `${AUTH_URL}?${authParams}`;
|
|
266
|
+
console.log('\nOpening browser for Microsoft authentication...');
|
|
267
|
+
console.log('If the browser does not open, navigate to:\n');
|
|
268
|
+
console.log(` ${fullAuthUrl}\n`);
|
|
269
|
+
openBrowser(fullAuthUrl);
|
|
270
|
+
console.log('Waiting for authentication (timeout: 5 minutes)...');
|
|
271
|
+
|
|
272
|
+
const { code, state: returnedState } = await waitForCode();
|
|
273
|
+
if (returnedState !== state) throw new Error('State mismatch — possible CSRF. Login aborted.');
|
|
274
|
+
|
|
275
|
+
process.stdout.write('Exchanging authorization code for tokens...');
|
|
276
|
+
const tokens = await oauthPost(TOKEN_URL, {
|
|
277
|
+
client_id: CLIENT_ID,
|
|
278
|
+
grant_type: 'authorization_code',
|
|
279
|
+
code,
|
|
280
|
+
redirect_uri: redirectUri,
|
|
281
|
+
code_verifier: verifier,
|
|
282
|
+
scope: SCOPES,
|
|
283
|
+
});
|
|
284
|
+
console.log(' done.');
|
|
285
|
+
|
|
286
|
+
if (!tokens.access_token) throw new Error(`Token exchange failed: ${JSON.stringify(tokens)}`);
|
|
287
|
+
|
|
288
|
+
const me = await graphGet('/me', tokens.access_token, { $select: 'userPrincipalName,displayName,id' });
|
|
289
|
+
saveCache({
|
|
290
|
+
access_token: tokens.access_token,
|
|
291
|
+
refresh_token: tokens.refresh_token || '',
|
|
292
|
+
expires_at: Math.floor(Date.now() / 1000) + (tokens.expires_in || 3600),
|
|
293
|
+
username: me.userPrincipalName || '',
|
|
294
|
+
});
|
|
295
|
+
return { token: tokens.access_token, me };
|
|
187
296
|
}
|
|
188
297
|
|
|
189
298
|
async function getAccessToken(forceLogin = false) {
|
|
@@ -198,46 +307,21 @@ async function getAccessToken(forceLogin = false) {
|
|
|
198
307
|
}
|
|
199
308
|
}
|
|
200
309
|
}
|
|
310
|
+
const { token } = await pkceLogin();
|
|
311
|
+
return token;
|
|
312
|
+
}
|
|
201
313
|
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
console.log('\n' + '='.repeat(60));
|
|
207
|
-
console.log(device.message);
|
|
208
|
-
console.log('='.repeat(60) + '\n');
|
|
209
|
-
|
|
210
|
-
const interval = (device.interval || 5) * 1000;
|
|
211
|
-
const deadline = Date.now() + (device.expires_in || 900) * 1000;
|
|
314
|
+
async function getValidToken() {
|
|
315
|
+
return getAccessToken(false);
|
|
316
|
+
}
|
|
212
317
|
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
});
|
|
221
|
-
if (res.access_token) {
|
|
222
|
-
const me = await graphGet('/me', res.access_token, { $select: 'userPrincipalName' });
|
|
223
|
-
saveCache({
|
|
224
|
-
access_token: res.access_token,
|
|
225
|
-
refresh_token: res.refresh_token || '',
|
|
226
|
-
expires_at: Math.floor(Date.now() / 1000) + (res.expires_in || 3600),
|
|
227
|
-
username: me.userPrincipalName || '',
|
|
228
|
-
});
|
|
229
|
-
return res.access_token;
|
|
230
|
-
}
|
|
231
|
-
} catch (err) {
|
|
232
|
-
let body = {};
|
|
233
|
-
try { body = JSON.parse(err.responseBody || '{}'); } catch {}
|
|
234
|
-
if (body.error === 'authorization_pending') continue;
|
|
235
|
-
if (body.error === 'slow_down') { await new Promise(r => setTimeout(r, 5000)); continue; }
|
|
236
|
-
if (body.error === 'expired_token') throw new Error('Device code expired. Run login again.');
|
|
237
|
-
throw err;
|
|
238
|
-
}
|
|
239
|
-
}
|
|
240
|
-
throw new Error('Authentication timed out.');
|
|
318
|
+
async function tryGetToken() {
|
|
319
|
+
const cache = loadCache();
|
|
320
|
+
if (!cache?.access_token) return null;
|
|
321
|
+
const now = Math.floor(Date.now() / 1000);
|
|
322
|
+
if (!cache.expires_at || now < cache.expires_at - 60) return cache.access_token;
|
|
323
|
+
if (cache.refresh_token) return tryRefresh(cache.refresh_token, cache.username);
|
|
324
|
+
return null;
|
|
241
325
|
}
|
|
242
326
|
|
|
243
327
|
// ── Formatters ────────────────────────────────────────────────────────────────
|
|
@@ -272,13 +356,20 @@ function pad(str, len) {
|
|
|
272
356
|
}
|
|
273
357
|
|
|
274
358
|
// ── Commands ──────────────────────────────────────────────────────────────────
|
|
275
|
-
async function cmdLogin() {
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
359
|
+
async function cmdLogin(args) {
|
|
360
|
+
const cache = loadCache();
|
|
361
|
+
if (cache?.access_token) {
|
|
362
|
+
const now = Math.floor(Date.now() / 1000);
|
|
363
|
+
if (cache.expires_at && now < cache.expires_at - 60 && !args.force) {
|
|
364
|
+
console.log(`Already logged in as: ${cache.username}`);
|
|
365
|
+
console.log('Use --force to re-authenticate.');
|
|
366
|
+
return;
|
|
367
|
+
}
|
|
368
|
+
}
|
|
369
|
+
const { token, me } = await pkceLogin(!!args.force);
|
|
279
370
|
console.log(`\nLogged in as: ${me.displayName} <${me.userPrincipalName}>`);
|
|
280
|
-
console.log(`User ID: ${me.id}`);
|
|
281
|
-
console.log(`Token cached
|
|
371
|
+
console.log(`User ID : ${me.id}`);
|
|
372
|
+
console.log(`Token cached: ${CACHE_FILE}`);
|
|
282
373
|
}
|
|
283
374
|
|
|
284
375
|
function cmdLogout() {
|
|
@@ -525,12 +616,6 @@ async function cmdTeams(args) {
|
|
|
525
616
|
return;
|
|
526
617
|
}
|
|
527
618
|
|
|
528
|
-
// ── NEW: resolve a person's AAD user ID from their email/UPN ──────────────
|
|
529
|
-
// Usage: teams --lookup-user someone@company.com
|
|
530
|
-
// Shows AAD ID, display name, title, and the expected oneOnOne chat ID pattern.
|
|
531
|
-
// Note: oneOnOne chat IDs follow the pattern 19:ID1_ID2@unq.gbl.spaces
|
|
532
|
-
// where the IDs appear in the order Teams assigned them (not guaranteed sort order).
|
|
533
|
-
// Always verify by listing --chats and matching the target user's ID fragment.
|
|
534
619
|
if (args.lookupUser) {
|
|
535
620
|
const user = await graphGet(`/users/${args.lookupUser}`, token, {
|
|
536
621
|
$select: 'id,displayName,userPrincipalName,jobTitle,department',
|
|
@@ -549,32 +634,16 @@ async function cmdTeams(args) {
|
|
|
549
634
|
return;
|
|
550
635
|
}
|
|
551
636
|
|
|
552
|
-
// ── NEW: send a DM directly by email address ──────────────────────────────
|
|
553
|
-
// Usage: teams --dm someone@company.com --send "hello"
|
|
554
|
-
// Resolves the user's AAD ID, finds their oneOnOne chat from the chat list,
|
|
555
|
-
// and sends the message. More reliable than guessing the chat ID.
|
|
556
637
|
if (args.dm && args.send) {
|
|
557
|
-
|
|
558
|
-
const
|
|
559
|
-
$select: 'id,displayName',
|
|
560
|
-
});
|
|
561
|
-
|
|
562
|
-
// 2. List chats and find the oneOnOne chat containing the target user's ID
|
|
563
|
-
const chatsData = await graphGet('/me/chats', token, {
|
|
564
|
-
$top: 50,
|
|
565
|
-
$select: 'id,topic,chatType',
|
|
566
|
-
});
|
|
638
|
+
const user = await graphGet(`/users/${args.dm}`, token, { $select: 'id,displayName' });
|
|
639
|
+
const chatsData = await graphGet('/me/chats', token, { $top: 50, $select: 'id,topic,chatType' });
|
|
567
640
|
const chats = chatsData.value || [];
|
|
568
|
-
const directChat = chats.find(c =>
|
|
569
|
-
c.chatType === 'oneOnOne' && c.id.includes(user.id)
|
|
570
|
-
);
|
|
571
|
-
|
|
641
|
+
const directChat = chats.find(c => c.chatType === 'oneOnOne' && c.id.includes(user.id));
|
|
572
642
|
if (!directChat) {
|
|
573
643
|
console.error(`No existing direct chat found with ${user.displayName} (${args.dm}).`);
|
|
574
644
|
console.error(`They may need to message you first, or check --chats list manually.`);
|
|
575
645
|
process.exit(1);
|
|
576
646
|
}
|
|
577
|
-
|
|
578
647
|
const res = await graphPost(`/me/chats/${directChat.id}/messages`, token, {
|
|
579
648
|
body: { content: args.send },
|
|
580
649
|
});
|
|
@@ -582,12 +651,9 @@ async function cmdTeams(args) {
|
|
|
582
651
|
return;
|
|
583
652
|
}
|
|
584
653
|
|
|
585
|
-
// ── FIXED: $select is NOT supported by the Teams chat messages endpoint ───
|
|
586
|
-
// The Graph API returns HTTP 400 if $select is used here. Pass $top only.
|
|
587
654
|
if (args.messages) {
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
});
|
|
655
|
+
// Graph returns HTTP 400 if $select is used on the Teams messages endpoint — pass $top only.
|
|
656
|
+
const data = await graphGet(`/me/chats/${args.messages}/messages`, token, { $top: limit });
|
|
591
657
|
const msgs = data.value || [];
|
|
592
658
|
if (args.json) { console.log(JSON.stringify(msgs, null, 2)); return; }
|
|
593
659
|
console.log(`\nMessages in chat ${args.messages.slice(0, 20)}...:`);
|
|
@@ -621,8 +687,6 @@ async function cmdTeams(args) {
|
|
|
621
687
|
async function cmdTranscripts(args) {
|
|
622
688
|
const token = await getValidToken();
|
|
623
689
|
|
|
624
|
-
// Search calendar events by date + optional subject keyword, then resolve meeting IDs + transcripts
|
|
625
|
-
// Usage: transcripts --start 2026-03-06 [--end 2026-03-06] [--subject "keyword"]
|
|
626
690
|
if (args.list || (!args.meeting && !args.download)) {
|
|
627
691
|
const startDate = args.start || new Date(Date.now() - 7 * 86400 * 1000).toISOString().slice(0, 10);
|
|
628
692
|
const endDate = args.end || startDate;
|
|
@@ -648,7 +712,6 @@ async function cmdTranscripts(args) {
|
|
|
648
712
|
console.log(`\nMeeting: ${e.subject}`);
|
|
649
713
|
console.log(`Start : ${fmtDt(e.start?.dateTime)}`);
|
|
650
714
|
const joinUrl = e.onlineMeeting.joinUrl;
|
|
651
|
-
// Resolve to online meeting object via joinWebUrl
|
|
652
715
|
let meetingId = null;
|
|
653
716
|
try {
|
|
654
717
|
const om = await graphGet('/me/onlineMeetings', token, {
|
|
@@ -688,7 +751,6 @@ async function cmdTranscripts(args) {
|
|
|
688
751
|
return;
|
|
689
752
|
}
|
|
690
753
|
|
|
691
|
-
// List transcripts for a specific meeting ID
|
|
692
754
|
if (args.meeting && !args.transcript) {
|
|
693
755
|
const data = await graphGet(`/me/onlineMeetings/${args.meeting}/transcripts`, token);
|
|
694
756
|
const transcripts = data.value || [];
|
|
@@ -700,7 +762,6 @@ async function cmdTranscripts(args) {
|
|
|
700
762
|
return;
|
|
701
763
|
}
|
|
702
764
|
|
|
703
|
-
// Download transcript content
|
|
704
765
|
if (args.meeting && args.transcript) {
|
|
705
766
|
const contentType = args.vtt ? 'text/vtt' : 'text/plain';
|
|
706
767
|
const url = `${GRAPH_BASE}/me/onlineMeetings/${args.meeting}/transcripts/${args.transcript}/content`;
|
|
@@ -811,6 +872,107 @@ async function cmdPeople(args) {
|
|
|
811
872
|
}
|
|
812
873
|
}
|
|
813
874
|
|
|
875
|
+
function cmdClaims(args) {
|
|
876
|
+
const cache = loadCache();
|
|
877
|
+
if (!cache?.access_token) {
|
|
878
|
+
console.log('NOT_LOGGED_IN — run: node msgraph.js login');
|
|
879
|
+
process.exit(2);
|
|
880
|
+
}
|
|
881
|
+
|
|
882
|
+
let payload;
|
|
883
|
+
try {
|
|
884
|
+
const parts = cache.access_token.split('.');
|
|
885
|
+
if (parts.length !== 3) throw new Error('Not a JWT');
|
|
886
|
+
payload = JSON.parse(Buffer.from(parts[1], 'base64url').toString('utf8'));
|
|
887
|
+
} catch {
|
|
888
|
+
console.error('Error: could not decode cached token.');
|
|
889
|
+
process.exit(1);
|
|
890
|
+
}
|
|
891
|
+
|
|
892
|
+
if (args.json) {
|
|
893
|
+
console.log(JSON.stringify(payload, null, 2));
|
|
894
|
+
return;
|
|
895
|
+
}
|
|
896
|
+
|
|
897
|
+
const tty = process.stdout.isTTY;
|
|
898
|
+
const c = {
|
|
899
|
+
reset: tty ? '\x1b[0m' : '',
|
|
900
|
+
bold: tty ? '\x1b[1m' : '',
|
|
901
|
+
dim: tty ? '\x1b[2m' : '',
|
|
902
|
+
cyan: tty ? '\x1b[36m' : '',
|
|
903
|
+
green: tty ? '\x1b[32m' : '',
|
|
904
|
+
red: tty ? '\x1b[31m' : '',
|
|
905
|
+
yellow: tty ? '\x1b[33m' : '',
|
|
906
|
+
white: tty ? '\x1b[97m' : '',
|
|
907
|
+
gray: tty ? '\x1b[90m' : '',
|
|
908
|
+
};
|
|
909
|
+
|
|
910
|
+
const W = 54;
|
|
911
|
+
const line = () => `${c.gray}${'─'.repeat(W)}${c.reset}`;
|
|
912
|
+
const section = label => `\n${c.bold}${c.cyan} ${label}${c.reset}\n${line()}`;
|
|
913
|
+
const row = (label, value, color = c.white) =>
|
|
914
|
+
` ${c.gray}${label.padEnd(12)}${c.reset} ${color}${value}${c.reset}`;
|
|
915
|
+
const absent = `${c.dim}(not present)${c.reset}`;
|
|
916
|
+
|
|
917
|
+
const now = Math.floor(Date.now() / 1000);
|
|
918
|
+
const expTs = payload.exp || 0;
|
|
919
|
+
const iatTs = payload.iat || 0;
|
|
920
|
+
const secsLeft = expTs - now;
|
|
921
|
+
const minsLeft = Math.floor(secsLeft / 60);
|
|
922
|
+
const expStr = expTs
|
|
923
|
+
? new Date(expTs * 1000).toISOString().slice(0, 19).replace('T', ' ')
|
|
924
|
+
: 'N/A';
|
|
925
|
+
const iatStr = iatTs
|
|
926
|
+
? new Date(iatTs * 1000).toISOString().slice(0, 19).replace('T', ' ')
|
|
927
|
+
: 'N/A';
|
|
928
|
+
const expiryHint = secsLeft > 0
|
|
929
|
+
? `${c.dim} (expires in ${minsLeft}m)${c.reset}`
|
|
930
|
+
: `${c.red} (EXPIRED)${c.reset}`;
|
|
931
|
+
|
|
932
|
+
const deviceId = payload.deviceid || payload.device_id || null;
|
|
933
|
+
const upn = payload.upn || payload.unique_name || 'N/A';
|
|
934
|
+
|
|
935
|
+
console.log(`\n${c.bold}${c.cyan}╭${'─'.repeat(W)}╮${c.reset}`);
|
|
936
|
+
console.log(`${c.bold}${c.cyan}│${c.reset} ${c.bold}Token Claims${c.reset} ${c.gray}${upn.slice(0, W - 16).padEnd(W - 14)}${c.cyan}│${c.reset}`);
|
|
937
|
+
console.log(`${c.bold}${c.cyan}╰${'─'.repeat(W)}╯${c.reset}`);
|
|
938
|
+
|
|
939
|
+
console.log(section('IDENTITY'));
|
|
940
|
+
console.log(row('UPN', upn));
|
|
941
|
+
console.log(row('Object ID', payload.oid || 'N/A', c.gray));
|
|
942
|
+
console.log(row('Tenant ID', payload.tid || 'N/A', c.gray));
|
|
943
|
+
console.log(row('Issued', iatStr, c.dim));
|
|
944
|
+
console.log(row('Expires', expStr) + expiryHint);
|
|
945
|
+
|
|
946
|
+
console.log(section('DEVICE CLAIMS'));
|
|
947
|
+
if (deviceId) {
|
|
948
|
+
console.log(row('Device ID', deviceId, c.green) + ` ${c.green}✓ present${c.reset}`);
|
|
949
|
+
} else {
|
|
950
|
+
console.log(row('Device ID', '(not present)', c.red) + ` ${c.red}✗${c.reset}`);
|
|
951
|
+
console.log(`\n ${c.yellow}⚠ Device claims are missing.${c.reset}`);
|
|
952
|
+
console.log(` ${c.dim} SSO Extension may not be deployed, or this device${c.reset}`);
|
|
953
|
+
console.log(` ${c.dim} is not registered in Entra ID.${c.reset}`);
|
|
954
|
+
}
|
|
955
|
+
console.log(row('Join Type', payload.join_type || absent));
|
|
956
|
+
console.log(row('MDM URL', payload.mdm_compliance_url || absent));
|
|
957
|
+
|
|
958
|
+
console.log(section('AUTH METHODS'));
|
|
959
|
+
const amrList = Array.isArray(payload.amr) ? payload.amr : (payload.amr || '').split(',');
|
|
960
|
+
const amrFmt = amrList.map(m => `${c.bold}${m.trim()}${c.reset}`).join(` ${c.gray}·${c.reset} `);
|
|
961
|
+
console.log(` ${amrFmt}`);
|
|
962
|
+
|
|
963
|
+
console.log(section('SCOPES'));
|
|
964
|
+
const scopes = (payload.scp || '').split(' ').filter(Boolean).sort();
|
|
965
|
+
const colWidth = 26;
|
|
966
|
+
const cols = 2;
|
|
967
|
+
for (let i = 0; i < scopes.length; i += cols) {
|
|
968
|
+
const row2 = scopes.slice(i, i + cols)
|
|
969
|
+
.map(s => `${c.dim}•${c.reset} ${s.padEnd(colWidth)}`)
|
|
970
|
+
.join(' ');
|
|
971
|
+
console.log(` ${row2}`);
|
|
972
|
+
}
|
|
973
|
+
console.log('');
|
|
974
|
+
}
|
|
975
|
+
|
|
814
976
|
async function cmdOrg(args) {
|
|
815
977
|
const token = await getValidToken();
|
|
816
978
|
|
|
@@ -835,7 +997,6 @@ async function cmdOrg(args) {
|
|
|
835
997
|
return;
|
|
836
998
|
}
|
|
837
999
|
|
|
838
|
-
// Default: show org context
|
|
839
1000
|
try {
|
|
840
1001
|
const mgr = await graphGet('/me/manager', token);
|
|
841
1002
|
console.log(`Manager: ${mgr.displayName}`);
|
|
@@ -937,7 +1098,7 @@ async function cmdOnenote(args) {
|
|
|
937
1098
|
// ── CLI Parser ────────────────────────────────────────────────────────────────
|
|
938
1099
|
function parseArgs(argv) {
|
|
939
1100
|
const BOOL = new Set(['json','unread','sites','chats','teamsList','contacts',
|
|
940
|
-
'manager','reports','availability','notebooks','list','vtt','help']);
|
|
1101
|
+
'manager','reports','availability','notebooks','list','vtt','help','force']);
|
|
941
1102
|
const args = { _: [] };
|
|
942
1103
|
let i = 0;
|
|
943
1104
|
while (i < argv.length) {
|
|
@@ -964,9 +1125,10 @@ function printHelp() {
|
|
|
964
1125
|
Usage: node ${name} <command> [options]
|
|
965
1126
|
|
|
966
1127
|
Auth:
|
|
967
|
-
login
|
|
1128
|
+
login [--force] Authenticate via browser (PKCE flow)
|
|
968
1129
|
logout Remove cached credentials
|
|
969
1130
|
status Check login status
|
|
1131
|
+
claims [--json] Decode cached JWT — identity, device claims, auth methods
|
|
970
1132
|
|
|
971
1133
|
Data:
|
|
972
1134
|
me [--json] Your profile
|
|
@@ -985,24 +1147,10 @@ Data:
|
|
|
985
1147
|
onenote [--notebooks] [--sections NOTEBOOK_ID] [--pages SECTION_ID]
|
|
986
1148
|
[--read PAGE_ID] [--search QUERY] [--limit N] [--json]
|
|
987
1149
|
[--create TITLE --section SECTION_ID [--body CONTENT]]
|
|
1150
|
+
transcripts [--start YYYY-MM-DD] [--end YYYY-MM-DD] [--subject KEYWORD]
|
|
1151
|
+
[--meeting ID] [--transcript ID] [--output FILE] [--vtt]
|
|
988
1152
|
|
|
989
1153
|
Add --json to any command for machine-readable output.
|
|
990
|
-
|
|
991
|
-
Examples:
|
|
992
|
-
node ${name} login
|
|
993
|
-
node ${name} emails --limit 20
|
|
994
|
-
node ${name} emails --send user@corp.com --subject "Hi" --body "Hello"
|
|
995
|
-
node ${name} calendar --create "Standup" --start 2024-03-15T09:00 --end 2024-03-15T09:30
|
|
996
|
-
node ${name} teams --chats
|
|
997
|
-
node ${name} teams --lookup-user alice@corp.com
|
|
998
|
-
node ${name} teams --dm alice@corp.com --send "Hello from the avatar!"
|
|
999
|
-
node ${name} onedrive --upload report.pdf --dest "Documents/report.pdf"
|
|
1000
|
-
node ${name} onenote --notebooks
|
|
1001
|
-
node ${name} onenote --sections NOTEBOOK_ID
|
|
1002
|
-
node ${name} onenote --pages SECTION_ID
|
|
1003
|
-
node ${name} onenote --read PAGE_ID
|
|
1004
|
-
node ${name} onenote --search "meeting notes"
|
|
1005
|
-
node ${name} onenote --create "My Note" --section SECTION_ID --body "Content here"
|
|
1006
1154
|
`);
|
|
1007
1155
|
}
|
|
1008
1156
|
|
|
@@ -1015,20 +1163,21 @@ async function main() {
|
|
|
1015
1163
|
const args = parseArgs(argv.slice(1));
|
|
1016
1164
|
|
|
1017
1165
|
const COMMANDS = {
|
|
1018
|
-
login:
|
|
1019
|
-
logout:
|
|
1020
|
-
status:
|
|
1021
|
-
me:
|
|
1022
|
-
emails:
|
|
1023
|
-
calendar:
|
|
1024
|
-
sharepoint:
|
|
1025
|
-
teams:
|
|
1026
|
-
onedrive:
|
|
1027
|
-
people:
|
|
1028
|
-
org:
|
|
1029
|
-
onenote:
|
|
1030
|
-
transcripts:
|
|
1031
|
-
|
|
1166
|
+
login: () => cmdLogin(args),
|
|
1167
|
+
logout: () => cmdLogout(),
|
|
1168
|
+
status: () => cmdStatus(),
|
|
1169
|
+
me: () => cmdMe(args),
|
|
1170
|
+
emails: () => cmdEmails(args),
|
|
1171
|
+
calendar: () => cmdCalendar(args),
|
|
1172
|
+
sharepoint: () => cmdSharepoint(args),
|
|
1173
|
+
teams: () => cmdTeams(args),
|
|
1174
|
+
onedrive: () => cmdOnedrive(args),
|
|
1175
|
+
people: () => cmdPeople(args),
|
|
1176
|
+
org: () => cmdOrg(args),
|
|
1177
|
+
onenote: () => cmdOnenote(args),
|
|
1178
|
+
transcripts: () => cmdTranscripts(args),
|
|
1179
|
+
claims: () => cmdClaims(args),
|
|
1180
|
+
help: () => { printHelp(); process.exit(0); },
|
|
1032
1181
|
};
|
|
1033
1182
|
|
|
1034
1183
|
if (!COMMANDS[command]) {
|
|
@@ -61,6 +61,8 @@ export interface HookProcessingConfig {
|
|
|
61
61
|
model?: string;
|
|
62
62
|
/** SSO URL for credential loading */
|
|
63
63
|
ssoUrl?: string;
|
|
64
|
+
/** Optional dedicated CodeMie API URL for analytics sync */
|
|
65
|
+
syncApiUrl?: string;
|
|
64
66
|
}
|
|
65
67
|
/**
|
|
66
68
|
* Process a hook event programmatically
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"hook.d.ts","sourceRoot":"","sources":["../../../src/cli/commands/hook.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAIpC,OAAO,KAAK,EAAE,aAAa,EAA4D,MAAM,wBAAwB,CAAC;AAGtH;;;;;;GAMG;AAEH;;GAEG;AACH,MAAM,WAAW,iBAAkB,SAAQ,aAAa;IACtD,eAAe,EAAE,cAAc,CAAC;IAChC,MAAM,EAAE,MAAM,CAAC;CAChB;AAED;;GAEG;AACH,MAAM,WAAW,eAAgB,SAAQ,aAAa;IACpD,eAAe,EAAE,YAAY,CAAC;IAC9B,MAAM,EAAE,MAAM,CAAC;IACf,GAAG,EAAE,MAAM,CAAC;CACb;AAED;;GAEG;AACH,MAAM,WAAW,iBAAkB,SAAQ,aAAa;IACtD,eAAe,EAAE,cAAc,CAAC;IAChC,QAAQ,EAAE,MAAM,CAAC;IACjB,qBAAqB,EAAE,MAAM,CAAC;IAC9B,gBAAgB,EAAE,OAAO,CAAC;IAC1B,GAAG,EAAE,MAAM,CAAC;CACb;AAED;;;GAGG;AACH,MAAM,WAAW,oBAAoB;IACnC,4CAA4C;IAC5C,SAAS,EAAE,MAAM,CAAC;IAClB,yBAAyB;IACzB,SAAS,EAAE,MAAM,CAAC;IAClB,yCAAyC;IACzC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,mBAAmB;IACnB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,qCAAqC;IACrC,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,wCAAwC;IACxC,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,qEAAqE;IACrE,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,qBAAqB;IACrB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,+BAA+B;IAC/B,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,mBAAmB;IACnB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,iBAAiB;IACjB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,qCAAqC;IACrC,MAAM,CAAC,EAAE,MAAM,CAAC;
|
|
1
|
+
{"version":3,"file":"hook.d.ts","sourceRoot":"","sources":["../../../src/cli/commands/hook.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAIpC,OAAO,KAAK,EAAE,aAAa,EAA4D,MAAM,wBAAwB,CAAC;AAGtH;;;;;;GAMG;AAEH;;GAEG;AACH,MAAM,WAAW,iBAAkB,SAAQ,aAAa;IACtD,eAAe,EAAE,cAAc,CAAC;IAChC,MAAM,EAAE,MAAM,CAAC;CAChB;AAED;;GAEG;AACH,MAAM,WAAW,eAAgB,SAAQ,aAAa;IACpD,eAAe,EAAE,YAAY,CAAC;IAC9B,MAAM,EAAE,MAAM,CAAC;IACf,GAAG,EAAE,MAAM,CAAC;CACb;AAED;;GAEG;AACH,MAAM,WAAW,iBAAkB,SAAQ,aAAa;IACtD,eAAe,EAAE,cAAc,CAAC;IAChC,QAAQ,EAAE,MAAM,CAAC;IACjB,qBAAqB,EAAE,MAAM,CAAC;IAC9B,gBAAgB,EAAE,OAAO,CAAC;IAC1B,GAAG,EAAE,MAAM,CAAC;CACb;AAED;;;GAGG;AACH,MAAM,WAAW,oBAAoB;IACnC,4CAA4C;IAC5C,SAAS,EAAE,MAAM,CAAC;IAClB,yBAAyB;IACzB,SAAS,EAAE,MAAM,CAAC;IAClB,yCAAyC;IACzC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,mBAAmB;IACnB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,qCAAqC;IACrC,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,wCAAwC;IACxC,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,qEAAqE;IACrE,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,qBAAqB;IACrB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,+BAA+B;IAC/B,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,mBAAmB;IACnB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,iBAAiB;IACjB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,qCAAqC;IACrC,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,4DAA4D;IAC5D,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AA6lCD;;;;;;;GAOG;AACH,wBAAsB,YAAY,CAAC,KAAK,EAAE,aAAa,EAAE,MAAM,CAAC,EAAE,oBAAoB,GAAG,OAAO,CAAC,IAAI,CAAC,CAoBrG;AAED;;;GAGG;AACH,wBAAgB,iBAAiB,IAAI,OAAO,CA8F3C"}
|