@unifiedmemory/cli 1.3.15 → 1.3.17

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/.env.example CHANGED
@@ -17,17 +17,17 @@
17
17
  # ============================================
18
18
  # Clerk OAuth Configuration
19
19
  # ============================================
20
- # Production defaults are included in the CLI
21
- # Only override these if using a custom Clerk instance
22
- # CLERK_CLIENT_ID=custom_clerk_client_id
23
- # CLERK_DOMAIN=custom-app.clerk.accounts.dev
20
+ # Production defaults are included in the CLI.
21
+ # For local development, uncomment and use dev Clerk values:
22
+ # CLERK_CLIENT_ID=nULlnomaKB9rRGP2
23
+ # CLERK_DOMAIN=clear-caiman-45.clerk.accounts.dev
24
24
 
25
25
  # ============================================
26
26
  # API Configuration
27
27
  # ============================================
28
- # Production default is included in the CLI
29
- # Only override this for local testing or custom deployments
30
- # API_ENDPOINT=https://custom-api-gateway.zuplo.dev
28
+ # Production default is included in the CLI.
29
+ # For local development, uncomment and use dev gateway:
30
+ # API_ENDPOINT=https://rose-asp-main-1c0b114.d2.zuplo.dev
31
31
 
32
32
  # ============================================
33
33
  # OAuth Flow Configuration
package/commands/init.js CHANGED
@@ -141,24 +141,6 @@ export async function init(options = {}) {
141
141
  console.log(' 3. Run `um status` to verify configuration\n');
142
142
  }
143
143
 
144
- /**
145
- * Validate that we have a real organization context (not fallback to user_id)
146
- * @param {Object} authData - Auth data with user_id and org_id
147
- * @returns {Object} { isValid, isPersonalContext, message }
148
- */
149
- function validateOrganizationContext(authData) {
150
- const isPersonalContext = authData.org_id === authData.user_id;
151
-
152
- if (isPersonalContext) {
153
- return {
154
- isValid: false,
155
- isPersonalContext: true,
156
- message: 'You are in personal account context. Organization-scoped operations require an organization.',
157
- };
158
- }
159
-
160
- return { isValid: true, isPersonalContext: false, message: null };
161
- }
162
144
 
163
145
  async function ensureAuthenticated(options) {
164
146
  // Try to load and refresh token if expired
@@ -213,19 +195,6 @@ async function ensureAuthenticated(options) {
213
195
  async function selectOrCreateProject(authData, options) {
214
196
  const apiUrl = authData.api_url || 'https://rose-asp-main-1c0b114.d2.zuplo.dev';
215
197
 
216
- // Validate organization context BEFORE making API calls
217
- const contextValidation = validateOrganizationContext(authData);
218
-
219
- if (!contextValidation.isValid && contextValidation.isPersonalContext) {
220
- console.log(chalk.yellow('\n⚠️ ' + contextValidation.message));
221
- console.log(chalk.gray('\nOrganization-scoped projects require an organization context.'));
222
- console.log(chalk.cyan('\nRecommended actions:'));
223
- console.log(chalk.cyan(' 1. Switch to an organization: um org switch'));
224
- console.log(chalk.cyan(' 2. Create an organization at: https://unifiedmemory.ai'));
225
- console.log(chalk.cyan(' 3. Re-run: um init'));
226
- return null;
227
- }
228
-
229
198
  // Fetch existing projects with enhanced error handling
230
199
  console.log(chalk.gray('Fetching projects...'));
231
200
  const result = await fetchProjects(authData, apiUrl);
package/lib/config.js CHANGED
@@ -10,12 +10,14 @@ dotenvConfig({ path: join(__dirname, '..', '.env') });
10
10
 
11
11
  export const config = {
12
12
  // Clerk OAuth configuration (production defaults, can be overridden via env vars)
13
- clerkClientId: process.env.CLERK_CLIENT_ID || 'nULlnomaKB9rRGP2',
13
+ // TODO: Replace clerkClientId with production Clerk OAuth client ID once created
14
+ clerkClientId: process.env.CLERK_CLIENT_ID || 'bNpTWw0hP3V7ueqN',
14
15
  clerkClientSecret: process.env.CLERK_CLIENT_SECRET, // Optional for PKCE flow
15
- clerkDomain: process.env.CLERK_DOMAIN || 'clear-caiman-45.clerk.accounts.dev',
16
+ clerkDomain: process.env.CLERK_DOMAIN || 'clerk.unifiedmemory.ai',
16
17
 
17
18
  // API configuration (production default, can be overridden via env var)
18
- apiEndpoint: process.env.API_ENDPOINT || 'https://rose-asp-main-1c0b114.d2.zuplo.dev',
19
+ // TODO: Replace with production Zuplo gateway URL once deployed
20
+ apiEndpoint: process.env.API_ENDPOINT || 'https://api.unifiedmemory.ai',
19
21
 
20
22
  // OAuth flow configuration (localhost defaults for callback server)
21
23
  redirectUri: process.env.REDIRECT_URI || 'http://localhost:3333/callback',
package/lib/mcp-proxy.js CHANGED
@@ -2,7 +2,9 @@
2
2
  * MCP Proxy Client - Forwards MCP requests to the gateway HTTP endpoint
3
3
  */
4
4
 
5
- const GATEWAY_MCP_URL = 'https://rose-asp-main-1c0b114.d2.zuplo.dev/mcp';
5
+ import { config } from './config.js';
6
+
7
+ const GATEWAY_MCP_URL = `${config.apiEndpoint}/mcp`;
6
8
 
7
9
  /**
8
10
  * Transform tool schema to hide context parameters (org, proj, user)
package/lib/mcp-server.js CHANGED
@@ -218,15 +218,14 @@ function buildAuthHeaders(authData, projectContext) {
218
218
  headers['X-User-Id'] = authData.decoded.sub;
219
219
  }
220
220
 
221
- // Add X-Org-Id - check JWT claims first, fall back to selectedOrgId
222
- const orgId = authData.decoded?.o?.o_id || authData.selectedOrgId;
221
+ // Add X-Org-Id - check JWT claims first, fall back to selectedOrgId, then userId (personal account)
222
+ const orgId = authData.decoded?.o?.o_id || authData.selectedOrgId || authData.decoded?.sub;
223
223
  const orgName = authData.decoded?.o?.o_name || authData.selectedOrgName;
224
224
 
225
225
  if (orgId) {
226
226
  headers['X-Org-Id'] = orgId;
227
- console.error(`✓ Organization: ${orgName || orgId}`);
228
- } else {
229
- console.error(`✓ Using personal account context`);
227
+ const isPersonal = orgId === authData.decoded?.sub;
228
+ console.error(`✓ ${isPersonal ? 'Personal account' : 'Organization'}: ${orgName || orgId}`);
230
229
  }
231
230
 
232
231
  // Add project context if available
@@ -249,9 +248,9 @@ async function loadFreshAuth(projectContext) {
249
248
  const authData = await loadAndRefreshToken();
250
249
  const authHeaders = buildAuthHeaders(authData, projectContext);
251
250
 
252
- // Build auth context - check JWT claims first, fall back to selectedOrgId
251
+ // Build auth context - check JWT claims first, fall back to selectedOrgId, then userId (personal account)
253
252
  const jwtOrgId = authData.decoded?.o?.o_id;
254
- const orgId = jwtOrgId || authData.selectedOrgId;
253
+ const orgId = jwtOrgId || authData.selectedOrgId || authData.decoded?.sub;
255
254
  const orgName = authData.decoded?.o?.o_name || authData.selectedOrgName;
256
255
  const orgRole = authData.decoded?.o?.o_role;
257
256
 
@@ -259,7 +258,7 @@ async function loadFreshAuth(projectContext) {
259
258
  decoded: authData.decoded,
260
259
  orgContext: orgId ? {
261
260
  id: orgId,
262
- name: orgName,
261
+ name: orgName || 'Personal',
263
262
  role: orgRole,
264
263
  } : null
265
264
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@unifiedmemory/cli",
3
- "version": "1.3.15",
3
+ "version": "1.3.17",
4
4
  "description": "UnifiedMemory CLI - AI code assistant integration",
5
5
  "main": "index.js",
6
6
  "type": "module",
@@ -8,6 +8,11 @@
8
8
 
9
9
  import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest';
10
10
 
11
+ // Mock dotenv so the local .env file doesn't interfere with unit tests
12
+ vi.mock('dotenv', () => ({
13
+ config: vi.fn()
14
+ }));
15
+
11
16
  describe('config', () => {
12
17
  // Store original env vars
13
18
  const originalEnv = { ...process.env };
@@ -35,9 +40,9 @@ describe('config', () => {
35
40
  // Fresh import to get defaults
36
41
  const { config } = await import('../../lib/config.js');
37
42
 
38
- expect(config.clerkClientId).toBe('nULlnomaKB9rRGP2');
39
- expect(config.clerkDomain).toBe('clear-caiman-45.clerk.accounts.dev');
40
- expect(config.apiEndpoint).toBe('https://rose-asp-main-1c0b114.d2.zuplo.dev');
43
+ expect(config.clerkClientId).toBe('bNpTWw0hP3V7ueqN');
44
+ expect(config.clerkDomain).toBe('clerk.unifiedmemory.ai');
45
+ expect(config.apiEndpoint).toBe('https://api.unifiedmemory.ai');
41
46
  expect(config.redirectUri).toBe('http://localhost:3333/callback');
42
47
  expect(config.port).toBe(3333);
43
48
  });
@@ -123,7 +128,7 @@ describe('config', () => {
123
128
  const { config, validateConfig } = await import('../../lib/config.js');
124
129
 
125
130
  // Empty string is falsy, so it falls back to default
126
- expect(config.clerkDomain).toBe('clear-caiman-45.clerk.accounts.dev');
131
+ expect(config.clerkDomain).toBe('clerk.unifiedmemory.ai');
127
132
  expect(validateConfig()).toBe(true);
128
133
  });
129
134
 
@@ -142,7 +147,7 @@ describe('config', () => {
142
147
  const { config, validateConfig } = await import('../../lib/config.js');
143
148
 
144
149
  // Empty string is falsy, so it falls back to default
145
- expect(config.clerkClientId).toBe('nULlnomaKB9rRGP2');
150
+ expect(config.clerkClientId).toBe('bNpTWw0hP3V7ueqN');
146
151
  expect(validateConfig()).toBe(true);
147
152
  });
148
153
 
@@ -8,11 +8,17 @@
8
8
  * are tested indirectly through the exported functions.
9
9
  */
10
10
 
11
- import { describe, it, expect, vi, beforeAll, afterAll, afterEach } from 'vitest';
11
+ import { describe, it, expect, vi, beforeAll, beforeEach, afterAll, afterEach } from 'vitest';
12
12
  import { setupServer } from 'msw/node';
13
13
  import { http, HttpResponse } from 'msw';
14
14
 
15
- const GATEWAY_MCP_URL = 'https://rose-asp-main-1c0b114.d2.zuplo.dev/mcp';
15
+ // Mock dotenv so the local .env file doesn't interfere with unit tests
16
+ vi.mock('dotenv', () => ({
17
+ config: vi.fn()
18
+ }));
19
+
20
+ // Must match the fallback default in lib/config.js (used when no .env is present, e.g. CI)
21
+ const GATEWAY_MCP_URL = 'https://api.unifiedmemory.ai/mcp';
16
22
 
17
23
  // Sample tool with pathParams and headers that should be transformed
18
24
  const sampleToolWithContext = {
@@ -59,6 +65,11 @@ const sampleToolNoContext = {
59
65
  const server = setupServer();
60
66
 
61
67
  beforeAll(() => server.listen({ onUnhandledRequest: 'bypass' }));
68
+ beforeEach(() => {
69
+ vi.resetModules();
70
+ // Clear API_ENDPOINT so config.js uses the hardcoded default matching GATEWAY_MCP_URL above
71
+ delete process.env.API_ENDPOINT;
72
+ });
62
73
  afterEach(() => server.resetHandlers());
63
74
  afterAll(() => server.close());
64
75