@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.
Files changed (111) hide show
  1. package/README.md +28 -0
  2. package/dist/agents/core/AgentCLI.d.ts.map +1 -1
  3. package/dist/agents/core/AgentCLI.js +27 -2
  4. package/dist/agents/core/AgentCLI.js.map +1 -1
  5. package/dist/agents/core/BaseAgentAdapter.d.ts.map +1 -1
  6. package/dist/agents/core/BaseAgentAdapter.js +20 -6
  7. package/dist/agents/core/BaseAgentAdapter.js.map +1 -1
  8. package/dist/agents/core/session/ensure-session.d.ts.map +1 -1
  9. package/dist/agents/core/session/ensure-session.js +7 -2
  10. package/dist/agents/core/session/ensure-session.js.map +1 -1
  11. package/dist/agents/core/session/types.d.ts +1 -0
  12. package/dist/agents/core/session/types.d.ts.map +1 -1
  13. package/dist/agents/plugins/claude/claude.plugin.js +4 -4
  14. package/dist/agents/plugins/claude/claude.plugin.js.map +1 -1
  15. package/dist/agents/plugins/claude/plugin/.claude-plugin/plugin.json +1 -1
  16. package/dist/agents/plugins/claude/plugin/skills/codemie-html-report/SKILL.md +273 -0
  17. package/dist/agents/plugins/claude/plugin/skills/codemie-html-report/style-guide/css/base.css +166 -0
  18. package/dist/agents/plugins/claude/plugin/skills/codemie-html-report/style-guide/css/buttons.css +253 -0
  19. package/dist/agents/plugins/claude/plugin/skills/codemie-html-report/style-guide/css/components.css +605 -0
  20. package/dist/agents/plugins/claude/plugin/skills/codemie-html-report/style-guide/css/forms.css +550 -0
  21. package/dist/agents/plugins/claude/plugin/skills/codemie-html-report/style-guide/css/layout.css +410 -0
  22. package/dist/agents/plugins/claude/plugin/skills/codemie-html-report/style-guide/css/tokens.css +323 -0
  23. package/dist/agents/plugins/claude/plugin/skills/codemie-html-report/style-guide/css/typography.css +155 -0
  24. package/dist/agents/plugins/claude/plugin/skills/codemie-html-report/style-guide/css/utilities.css +238 -0
  25. package/dist/agents/plugins/claude/plugin/skills/msgraph/SKILL.md +10 -8
  26. package/dist/agents/plugins/claude/plugin/skills/msgraph/scripts/msgraph.js +298 -149
  27. package/dist/cli/commands/hook.d.ts +2 -0
  28. package/dist/cli/commands/hook.d.ts.map +1 -1
  29. package/dist/cli/commands/hook.js +36 -31
  30. package/dist/cli/commands/hook.js.map +1 -1
  31. package/dist/cli/commands/setup.js +11 -1
  32. package/dist/cli/commands/setup.js.map +1 -1
  33. package/dist/providers/core/codemie-auth-helpers.d.ts +22 -0
  34. package/dist/providers/core/codemie-auth-helpers.d.ts.map +1 -0
  35. package/dist/providers/core/codemie-auth-helpers.js +118 -0
  36. package/dist/providers/core/codemie-auth-helpers.js.map +1 -0
  37. package/dist/providers/core/index.d.ts +1 -0
  38. package/dist/providers/core/index.d.ts.map +1 -1
  39. package/dist/providers/core/index.js +1 -0
  40. package/dist/providers/core/index.js.map +1 -1
  41. package/dist/providers/core/types.d.ts +4 -0
  42. package/dist/providers/core/types.d.ts.map +1 -1
  43. package/dist/providers/core/types.js.map +1 -1
  44. package/dist/providers/index.d.ts +2 -0
  45. package/dist/providers/index.d.ts.map +1 -1
  46. package/dist/providers/index.js +2 -0
  47. package/dist/providers/index.js.map +1 -1
  48. package/dist/providers/plugins/anthropic-subscription/anthropic-subscription.auth.d.ts +11 -0
  49. package/dist/providers/plugins/anthropic-subscription/anthropic-subscription.auth.d.ts.map +1 -0
  50. package/dist/providers/plugins/anthropic-subscription/anthropic-subscription.auth.js +30 -0
  51. package/dist/providers/plugins/anthropic-subscription/anthropic-subscription.auth.js.map +1 -0
  52. package/dist/providers/plugins/anthropic-subscription/anthropic-subscription.setup-steps.d.ts +8 -0
  53. package/dist/providers/plugins/anthropic-subscription/anthropic-subscription.setup-steps.d.ts.map +1 -0
  54. package/dist/providers/plugins/anthropic-subscription/anthropic-subscription.setup-steps.js +88 -0
  55. package/dist/providers/plugins/anthropic-subscription/anthropic-subscription.setup-steps.js.map +1 -0
  56. package/dist/providers/plugins/anthropic-subscription/anthropic-subscription.template.d.ts +11 -0
  57. package/dist/providers/plugins/anthropic-subscription/anthropic-subscription.template.d.ts.map +1 -0
  58. package/dist/providers/plugins/anthropic-subscription/anthropic-subscription.template.js +119 -0
  59. package/dist/providers/plugins/anthropic-subscription/anthropic-subscription.template.js.map +1 -0
  60. package/dist/providers/plugins/anthropic-subscription/index.d.ts +9 -0
  61. package/dist/providers/plugins/anthropic-subscription/index.d.ts.map +1 -0
  62. package/dist/providers/plugins/anthropic-subscription/index.js +9 -0
  63. package/dist/providers/plugins/anthropic-subscription/index.js.map +1 -0
  64. package/dist/providers/plugins/jwt/jwt.setup-steps.js +1 -1
  65. package/dist/providers/plugins/jwt/jwt.setup-steps.js.map +1 -1
  66. package/dist/providers/plugins/sso/proxy/plugins/claude-thinking-transformer.plugin.d.ts +30 -0
  67. package/dist/providers/plugins/sso/proxy/plugins/claude-thinking-transformer.plugin.d.ts.map +1 -0
  68. package/dist/providers/plugins/sso/proxy/plugins/claude-thinking-transformer.plugin.js +111 -0
  69. package/dist/providers/plugins/sso/proxy/plugins/claude-thinking-transformer.plugin.js.map +1 -0
  70. package/dist/providers/plugins/sso/proxy/plugins/index.d.ts +2 -1
  71. package/dist/providers/plugins/sso/proxy/plugins/index.d.ts.map +1 -1
  72. package/dist/providers/plugins/sso/proxy/plugins/index.js +3 -1
  73. package/dist/providers/plugins/sso/proxy/plugins/index.js.map +1 -1
  74. package/dist/providers/plugins/sso/proxy/plugins/sso.session-sync.plugin.d.ts.map +1 -1
  75. package/dist/providers/plugins/sso/proxy/plugins/sso.session-sync.plugin.js +9 -7
  76. package/dist/providers/plugins/sso/proxy/plugins/sso.session-sync.plugin.js.map +1 -1
  77. package/dist/providers/plugins/sso/proxy/plugins/types.d.ts +1 -0
  78. package/dist/providers/plugins/sso/proxy/plugins/types.d.ts.map +1 -1
  79. package/dist/providers/plugins/sso/proxy/proxy-types.d.ts +2 -0
  80. package/dist/providers/plugins/sso/proxy/proxy-types.d.ts.map +1 -1
  81. package/dist/providers/plugins/sso/proxy/sso.proxy.d.ts.map +1 -1
  82. package/dist/providers/plugins/sso/proxy/sso.proxy.js +10 -0
  83. package/dist/providers/plugins/sso/proxy/sso.proxy.js.map +1 -1
  84. package/dist/providers/plugins/sso/session/processors/metrics/metrics-aggregator.js +1 -1
  85. package/dist/providers/plugins/sso/session/processors/metrics/metrics-aggregator.js.map +1 -1
  86. package/dist/providers/plugins/sso/session/processors/metrics/metrics-api-client.d.ts +2 -2
  87. package/dist/providers/plugins/sso/session/processors/metrics/metrics-api-client.d.ts.map +1 -1
  88. package/dist/providers/plugins/sso/session/processors/metrics/metrics-api-client.js +4 -4
  89. package/dist/providers/plugins/sso/session/processors/metrics/metrics-api-client.js.map +1 -1
  90. package/dist/providers/plugins/sso/sso.auth.d.ts +0 -4
  91. package/dist/providers/plugins/sso/sso.auth.d.ts.map +1 -1
  92. package/dist/providers/plugins/sso/sso.auth.js +3 -13
  93. package/dist/providers/plugins/sso/sso.auth.js.map +1 -1
  94. package/dist/providers/plugins/sso/sso.http-client.d.ts +3 -42
  95. package/dist/providers/plugins/sso/sso.http-client.d.ts.map +1 -1
  96. package/dist/providers/plugins/sso/sso.http-client.js +4 -75
  97. package/dist/providers/plugins/sso/sso.http-client.js.map +1 -1
  98. package/dist/providers/plugins/sso/sso.setup-steps.d.ts.map +1 -1
  99. package/dist/providers/plugins/sso/sso.setup-steps.js +15 -66
  100. package/dist/providers/plugins/sso/sso.setup-steps.js.map +1 -1
  101. package/dist/providers/plugins/sso/sso.template.d.ts.map +1 -1
  102. package/dist/providers/plugins/sso/sso.template.js +2 -1
  103. package/dist/providers/plugins/sso/sso.template.js.map +1 -1
  104. package/dist/utils/config.d.ts.map +1 -1
  105. package/dist/utils/config.js +17 -0
  106. package/dist/utils/config.js.map +1 -1
  107. package/dist/utils/processes.d.ts +9 -0
  108. package/dist/utils/processes.d.ts.map +1 -1
  109. package/dist/utils/processes.js +21 -0
  110. package/dist/utils/processes.js.map +1 -1
  111. 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: OAuth2 device code flow with persistent token cache.
6
- * First time: node msgraph.js login
7
- * Subsequent: token refreshed silently from cache.
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 https = require('node:https');
15
- const fs = require('node:fs');
16
- const path = require('node:path');
17
- const os = require('node:os');
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'; // CodeMie APP
21
- const TOKEN_URL = 'https://login.microsoftonline.com/common/oauth2/v2.0/token';
22
- const DEVICE_URL = 'https://login.microsoftonline.com/common/oauth2/v2.0/devicecode';
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 = path.join(os.homedir(), '.ms_graph_token_cache.json');
32
- const GRAPH_BASE = 'https://graph.microsoft.com/v1.0';
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">&#10003; 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">&#10007; 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
- /** Returns a valid token, silently refreshing if needed. Exits if not logged in. */
163
- async function getValidToken() {
164
- const cache = loadCache();
165
- if (!cache?.access_token) {
166
- console.log('NOT_LOGGED_IN');
167
- process.exit(2);
168
- }
169
- const now = Math.floor(Date.now() / 1000);
170
- if (!cache.expires_at || now < cache.expires_at - 60) return cache.access_token;
171
- if (cache.refresh_token) {
172
- const t = await tryRefresh(cache.refresh_token, cache.username);
173
- if (t) return t;
174
- }
175
- console.log('TOKEN_EXPIRED');
176
- process.exit(2);
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
- /** Like getValidToken but returns null instead of exiting (used by status cmd). */
180
- async function tryGetToken() {
181
- const cache = loadCache();
182
- if (!cache?.access_token) return null;
183
- const now = Math.floor(Date.now() / 1000);
184
- if (!cache.expires_at || now < cache.expires_at - 60) return cache.access_token;
185
- if (cache.refresh_token) return tryRefresh(cache.refresh_token, cache.username);
186
- return null;
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
- // Device Code Flow
203
- const device = await oauthPost(DEVICE_URL, { client_id: CLIENT_ID, scope: SCOPES });
204
- if (!device.device_code) throw new Error(`Device flow failed: ${JSON.stringify(device)}`);
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
- while (Date.now() < deadline) {
214
- await new Promise(r => setTimeout(r, interval));
215
- try {
216
- const res = await oauthPost(TOKEN_URL, {
217
- client_id: CLIENT_ID,
218
- grant_type: 'urn:ietf:params:oauth:grant-type:device_code',
219
- device_code: device.device_code,
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
- console.log('Starting Microsoft authentication...');
277
- const token = await getAccessToken(true);
278
- const me = await graphGet('/me', token);
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 at: ${CACHE_FILE}`);
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
- // 1. Resolve target user's AAD ID
558
- const user = await graphGet(`/users/${args.dm}`, token, {
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
- const data = await graphGet(`/me/chats/${args.messages}/messages`, token, {
589
- $top: limit,
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 Authenticate (device code flow)
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: () => cmdLogin(),
1019
- logout: () => cmdLogout(),
1020
- status: () => cmdStatus(),
1021
- me: () => cmdMe(args),
1022
- emails: () => cmdEmails(args),
1023
- calendar: () => cmdCalendar(args),
1024
- sharepoint: () => cmdSharepoint(args),
1025
- teams: () => cmdTeams(args),
1026
- onedrive: () => cmdOnedrive(args),
1027
- people: () => cmdPeople(args),
1028
- org: () => cmdOrg(args),
1029
- onenote: () => cmdOnenote(args),
1030
- transcripts: () => cmdTranscripts(args),
1031
- help: () => { printHelp(); process.exit(0); },
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;CACjB;AAwlCD;;;;;;;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"}
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"}