@unifiedmemory/cli 1.3.9 → 1.3.11

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/commands/init.js CHANGED
@@ -69,17 +69,58 @@ export async function init(options = {}) {
69
69
  console.log(chalk.green(`✓ Organization: ${authData.org_id}`));
70
70
  }
71
71
 
72
- // Step 2: Select/create project
73
- const projectData = await selectOrCreateProject(authData, options);
74
- if (!projectData) {
75
- console.error(chalk.red('❌ Project setup failed'));
76
- process.exit(1);
72
+ // Step 1.5: Check for existing config
73
+ const umDir = path.join(process.cwd(), '.um');
74
+ const configPath = path.join(umDir, 'config.json');
75
+ let projectData = null;
76
+ let skipProjectSetup = false;
77
+
78
+ if (fs.existsSync(configPath)) {
79
+ try {
80
+ const existingConfig = fs.readJSONSync(configPath);
81
+
82
+ console.log(chalk.cyan('\n📋 Existing configuration found:\n'));
83
+ console.log(chalk.gray(` Project: ${existingConfig.project_name}`));
84
+ console.log(chalk.gray(` Project ID: ${existingConfig.project_id}`));
85
+ console.log(chalk.gray(` Organization: ${existingConfig.org_id}`));
86
+ console.log('');
87
+
88
+ const { override } = await inquirer.prompt([{
89
+ type: 'confirm',
90
+ name: 'override',
91
+ message: 'Override existing configuration?',
92
+ default: false,
93
+ }]);
94
+
95
+ if (!override) {
96
+ // Use existing config - skip project selection
97
+ projectData = {
98
+ project_id: existingConfig.project_id,
99
+ project_name: existingConfig.project_name,
100
+ project_slug: existingConfig.project_slug,
101
+ };
102
+ skipProjectSetup = true;
103
+ console.log(chalk.green(`\n✓ Using existing project: ${projectData.project_name}`));
104
+ }
105
+ } catch (error) {
106
+ console.log(chalk.yellow(`\n⚠ Could not read existing config: ${error.message}`));
107
+ console.log(chalk.gray('Continuing with new project setup...\n'));
108
+ }
77
109
  }
78
110
 
79
- console.log(chalk.green(`✓ Project: ${projectData.project_name} (${projectData.project_id})`));
111
+ // Step 2: Select/create project (skip if using existing config)
112
+ if (!skipProjectSetup) {
113
+ projectData = await selectOrCreateProject(authData, options);
114
+ if (!projectData) {
115
+ console.error(chalk.red('❌ Project setup failed'));
116
+ process.exit(1);
117
+ }
118
+
119
+ console.log(chalk.green(`✓ Project: ${projectData.project_name} (${projectData.project_id})`));
80
120
 
81
- // Step 3: Save project config
82
- await saveProjectConfig(authData, projectData);
121
+ // Step 3: Save project config (only if we selected a new project)
122
+ await saveProjectConfig(authData, projectData);
123
+ }
83
124
 
84
125
  // Step 3.5: Fetch available MCP tools for permissions
85
126
  let mcpToolPermissions = null;
package/commands/login.js CHANGED
@@ -222,6 +222,7 @@ export async function login() {
222
222
  );
223
223
 
224
224
  // Update saved token with org-scoped version
225
+ // Preserve originalSid for recovery purposes (org-scoped token won't have sid)
225
226
  saveToken({
226
227
  accessToken: tokenData.access_token,
227
228
  idToken: orgToken.jwt,
@@ -230,7 +231,8 @@ export async function login() {
230
231
  expiresIn: tokenData.expires_in,
231
232
  receivedAt: Date.now(),
232
233
  decoded: parseJWT(orgToken.jwt),
233
- selectedOrg: selectedOrg
234
+ selectedOrg: selectedOrg,
235
+ originalSid: decoded.sid // Preserve session ID from original OAuth token
234
236
  });
235
237
 
236
238
  console.log(chalk.green(`\n✅ Using organization context: ${chalk.bold(selectedOrg.name)}`));
package/index.js CHANGED
@@ -206,7 +206,7 @@ noteCommand
206
206
  // Show welcome splash if no command provided
207
207
  if (process.argv.length === 2) {
208
208
  await showWelcome();
209
- program.help();
209
+ process.exit(0);
210
210
  }
211
211
 
212
212
  program.parse();
@@ -77,10 +77,6 @@ export async function refreshAccessToken(tokenData) {
77
77
  let finalIdToken = newTokenData.id_token;
78
78
  let decoded = parseJWT(refreshedToken);
79
79
 
80
- // Debug: log what we got from refresh
81
- console.error(` Refreshed token has sid: ${decoded?.sid ? 'yes' : 'no'}`);
82
- console.error(` selectedOrg: ${tokenData.selectedOrg?.id || 'none'}`);
83
-
84
80
  // If we have org context, get org-scoped token to ensure JWT has org claims
85
81
  if (tokenData.selectedOrg?.id && decoded?.sid) {
86
82
  try {
@@ -1,5 +1,8 @@
1
- import { getToken } from './token-storage.js';
1
+ import { getToken, saveToken } from './token-storage.js';
2
2
  import { isTokenExpired, refreshAccessToken } from './token-refresh.js';
3
+ import { getOrgScopedToken } from './clerk-api.js';
4
+ import { parseJWT } from './jwt-utils.js';
5
+ import { config } from './config.js';
3
6
 
4
7
  /**
5
8
  * Load token and refresh if expired
@@ -43,5 +46,86 @@ export async function loadAndRefreshToken(requireAuth = true) {
43
46
  }
44
47
  }
45
48
 
49
+ // Check if token has org context but lacks org claims in JWT
50
+ // This can happen if getOrgScopedToken() failed during login
51
+ if (tokenData.selectedOrg?.id && !tokenData.decoded?.o) {
52
+ console.error('⚠️ Token missing org claims, attempting to fix...');
53
+
54
+ // Check for sid in decoded token or preserved originalSid from login
55
+ let sessionId = tokenData.decoded?.sid || tokenData.originalSid;
56
+ let currentToken = tokenData.idToken || tokenData.accessToken;
57
+
58
+ // If no sid, try to refresh first to get a new base token with sid
59
+ if (!sessionId && tokenData.refresh_token) {
60
+ console.error(' Refreshing to obtain session ID...');
61
+ try {
62
+ const tokenParams = {
63
+ client_id: config.clerkClientId,
64
+ refresh_token: tokenData.refresh_token,
65
+ grant_type: 'refresh_token',
66
+ };
67
+
68
+ if (config.clerkClientSecret) {
69
+ tokenParams.client_secret = config.clerkClientSecret;
70
+ }
71
+
72
+ const response = await fetch(`https://${config.clerkDomain}/oauth/token`, {
73
+ method: 'POST',
74
+ headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
75
+ body: new URLSearchParams(tokenParams),
76
+ });
77
+
78
+ if (response.ok) {
79
+ const newTokenData = await response.json();
80
+ const refreshedToken = newTokenData.id_token || newTokenData.access_token;
81
+ const refreshedDecoded = parseJWT(refreshedToken);
82
+
83
+ sessionId = refreshedDecoded?.sid;
84
+ currentToken = refreshedToken;
85
+
86
+ // Update tokenData with refreshed values
87
+ tokenData.accessToken = newTokenData.access_token;
88
+ tokenData.idToken = newTokenData.id_token;
89
+ tokenData.refresh_token = newTokenData.refresh_token || tokenData.refresh_token;
90
+ tokenData.decoded = refreshedDecoded;
91
+
92
+ if (sessionId) {
93
+ console.error(' ✓ Obtained session ID');
94
+ }
95
+ }
96
+ } catch (error) {
97
+ console.error(` Could not refresh token: ${error.message}`);
98
+ }
99
+ }
100
+
101
+ // Now try to get org-scoped token if we have sessionId
102
+ if (sessionId) {
103
+ try {
104
+ const orgToken = await getOrgScopedToken(
105
+ sessionId,
106
+ tokenData.selectedOrg.id,
107
+ currentToken
108
+ );
109
+
110
+ const decoded = parseJWT(orgToken.jwt);
111
+ const updatedToken = {
112
+ ...tokenData,
113
+ idToken: orgToken.jwt,
114
+ decoded: decoded,
115
+ };
116
+
117
+ saveToken(updatedToken);
118
+ console.error('✓ Token updated with org claims');
119
+ return updatedToken;
120
+ } catch (error) {
121
+ console.error(`⚠️ Could not get org-scoped token: ${error.message}`);
122
+ // Continue with existing token - API calls may fail
123
+ }
124
+ } else {
125
+ console.error('⚠️ Could not obtain session ID for org-scoped token');
126
+ console.error(' Please run: um login');
127
+ }
128
+ }
129
+
46
130
  return tokenData;
47
131
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@unifiedmemory/cli",
3
- "version": "1.3.9",
3
+ "version": "1.3.11",
4
4
  "description": "UnifiedMemory CLI - AI code assistant integration",
5
5
  "main": "index.js",
6
6
  "type": "module",