Skip to content

Commit 188ff5f

Browse files
committed
fix: support cursor auth
1 parent 56b7736 commit 188ff5f

File tree

5 files changed

+62
-53
lines changed

5 files changed

+62
-53
lines changed

packages/schema/package.json

Lines changed: 3 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -27,21 +27,12 @@
2727
"linkDirectory": true
2828
},
2929
"engines": {
30-
"vscode": "^1.102.0"
30+
"vscode": "^1.99.3"
3131
},
3232
"categories": [
3333
"Programming Languages"
3434
],
3535
"contributes": {
36-
"languageModelTools": [
37-
{
38-
"name": "zmodel_mermaid_generator",
39-
"displayName": "ZModel Mermaid Generator",
40-
"modelDescription": "Generate Mermaid charts from ZModel schema files. This tool analyzes the current ZModel file and creates comprehensive entity-relationship diagrams showing all models and their relationships.",
41-
"canBeReferencedInPrompt": true,
42-
"toolReferenceName": "zmodel_mermaid_generator"
43-
}
44-
],
4536
"languages": [
4637
{
4738
"id": "zmodel",
@@ -102,7 +93,7 @@
10293
"commands": [
10394
{
10495
"command": "zenstack.preview-zmodel",
105-
"title": "ZenStack: Preview ZModel",
96+
"title": "ZenStack: Preview ZModel Documentation",
10697
"icon": "$(preview)"
10798
},
10899
{
@@ -183,7 +174,7 @@
183174
"@types/strip-color": "^0.1.0",
184175
"@types/tmp": "^0.2.3",
185176
"@types/uuid": "^8.3.4",
186-
"@types/vscode": "^1.102.0",
177+
"@types/vscode": "^1.99.3",
187178
"@vscode/vsce": "^3.5.0",
188179
"@zenstackhq/runtime": "workspace:*",
189180
"dotenv": "^16.0.3",

packages/schema/src/documentation-cache.ts

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -53,10 +53,10 @@ export class DocumentationCache implements vscode.Disposable {
5353
/**
5454
* Generate a cache key from request body with normalized content
5555
*/
56-
private generateCacheKey(requestBody: { models: string[] }): string {
56+
private generateCacheKey(models: string[]): string {
5757
// Remove ALL whitespace characters from each model string for cache key generation
5858
// This ensures identical content with different formatting uses the same cache
59-
const normalizedModels = requestBody.models.map((model) => model.replace(/\s/g, '')).sort();
59+
const normalizedModels = models.map((model) => model.replace(/\s/g, '')).sort();
6060
const hash = createHash('sha512')
6161
.update(JSON.stringify({ models: normalizedModels }))
6262
.digest('hex');
@@ -73,8 +73,8 @@ export class DocumentationCache implements vscode.Disposable {
7373
/**
7474
* Get cached response if available and valid
7575
*/
76-
async getCachedResponse(requestBody: { models: string[] }): Promise<string | null> {
77-
const cacheKey = this.generateCacheKey(requestBody);
76+
async getCachedResponse(models: string[]): Promise<string | null> {
77+
const cacheKey = this.generateCacheKey(models);
7878
const entry = this.extensionContext.globalState.get<CacheEntry>(cacheKey);
7979

8080
if (entry && this.isCacheValid(entry)) {
@@ -93,8 +93,8 @@ export class DocumentationCache implements vscode.Disposable {
9393
/**
9494
* Cache a response for future use
9595
*/
96-
async setCachedResponse(requestBody: { models: string[] }, data: string): Promise<void> {
97-
const cacheKey = this.generateCacheKey(requestBody);
96+
async setCachedResponse(models: string[], data: string): Promise<void> {
97+
const cacheKey = this.generateCacheKey(models);
9898
const cacheEntry: CacheEntry = {
9999
data,
100100
timestamp: Date.now(),

packages/schema/src/zenstack-auth-provider.ts

Lines changed: 13 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ interface JWTClaims {
1010

1111
export const AUTH_PROVIDER_ID = 'ZenStack';
1212
export const AUTH_URL = 'https://accounts.zenstack.dev';
13+
export const API_URL = 'https://api.zenstack.dev';
1314

1415
export class ZenStackAuthenticationProvider implements vscode.AuthenticationProvider, vscode.Disposable {
1516
private _onDidChangeSessions =
@@ -20,7 +21,6 @@ export class ZenStackAuthenticationProvider implements vscode.AuthenticationProv
2021
private _context: vscode.ExtensionContext;
2122
private _disposable: vscode.Disposable;
2223
private pendingAuth?: {
23-
state: string;
2424
resolve: (session: vscode.AuthenticationSession) => void;
2525
reject: (error: Error) => void;
2626
scopes: readonly string[];
@@ -95,16 +95,22 @@ export class ZenStackAuthenticationProvider implements vscode.AuthenticationProv
9595
reject(new Error('User Cancelled'));
9696
});
9797

98-
// Generate a unique state parameter for security
99-
const state = this.generateState();
100-
// Construct the ZenStack sign-in URL for implicit flow (returns access_token directly)
101-
const signInUrl = new URL('/sign-in', AUTH_URL);
98+
const isCursor = vscode.env.appName == 'Cursor';
10299

100+
let signInUrl = vscode.Uri.parse(new URL('/sign-in', AUTH_URL).toString());
101+
102+
if (isCursor) {
103+
signInUrl = signInUrl.with({
104+
query: `redirect_url=${API_URL}/oauth/oauth_callback?vscodeapp=cursor`,
105+
});
106+
}
107+
108+
console.log('ZenStack sign-in URL:', signInUrl.toString());
103109
// Store the state and resolve function for later use
104-
this.pendingAuth = { state, resolve, reject, scopes };
110+
this.pendingAuth = { resolve, reject, scopes };
105111

106112
// Open the ZenStack sign-in page in the user's default browser
107-
vscode.env.openExternal(vscode.Uri.parse(signInUrl.toString())).then(
113+
vscode.env.openExternal(signInUrl).then(
108114
() => {
109115
console.log('Opened ZenStack sign-in page in browser');
110116
progress.report({ message: 'Waiting for return from browser...' });

packages/schema/src/zmodel-preview.ts

Lines changed: 39 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,12 @@
11
import * as vscode from 'vscode';
22
import * as path from 'path';
33
import * as os from 'os';
4+
import { z } from 'zod';
45
import { LanguageClient } from 'vscode-languageclient/node';
56
import { URI } from 'vscode-uri';
67
import { DocumentationCache } from './documentation-cache';
78
import { requireAuth } from './extension';
9+
import { API_URL } from './zenstack-auth-provider';
810

911
/**
1012
* ZModelPreview class handles ZModel file preview functionality
@@ -13,6 +15,25 @@ export class ZModelPreview implements vscode.Disposable {
1315
private documentationCache: DocumentationCache;
1416
private languageClient: LanguageClient;
1517

18+
// Schema for validating the request body
19+
private static DocRequestSchema = z.object({
20+
models: z.array(
21+
z.object({
22+
path: z.string().optional(),
23+
content: z.string(),
24+
})
25+
),
26+
environments: z
27+
.object({
28+
vscodeAppName: z.string(),
29+
vscodeVersion: z.string(),
30+
vscodeAppHost: z.string(),
31+
osRelease: z.string(),
32+
osType: z.string(),
33+
})
34+
.optional(),
35+
});
36+
1637
constructor(context: vscode.ExtensionContext, client: LanguageClient, cache: DocumentationCache) {
1738
this.documentationCache = cache;
1839
this.languageClient = client;
@@ -141,50 +162,53 @@ export class ZModelPreview implements vscode.Disposable {
141162
const importedURIs = astInfo?.importedURIs;
142163

143164
// get vscode document from importedURIs
144-
const importedTexts = await Promise.all(
165+
const importedModels = await Promise.all(
145166
importedURIs.map(async (uri) => {
146167
try {
147168
const fileUri = vscode.Uri.file(uri.path);
148169
const fileContent = await vscode.workspace.fs.readFile(fileUri);
149-
return Buffer.from(fileContent).toString('utf8');
170+
const filePath = fileUri.path;
171+
return { content: Buffer.from(fileContent).toString('utf8').trim(), path: filePath };
150172
} catch (error) {
151-
console.warn(`Could not read file for URI ${uri}:`, error);
152-
return null;
173+
throw new Error(
174+
`Failed to read imported ZModel file at ${uri.path}: ${
175+
error instanceof Error ? error.message : String(error)
176+
}`
177+
);
153178
}
154179
})
155180
);
156181

157-
const zmodelContent = [document.getText(), ...importedTexts.filter((text) => text !== null)];
158-
159-
// Trim whitespace from each model string
160-
const trimmedZmodelContent = zmodelContent.map((content) => content.trim());
182+
const allModels = [{ content: document.getText().trim(), path: document.uri.path }, ...importedModels];
161183

162184
const session = await requireAuth();
163185
if (!session) {
164186
throw new Error('Authentication required to generate documentation');
165187
}
166188

167189
// Prepare request body
168-
const requestBody = {
169-
models: trimmedZmodelContent,
190+
const requestBody: z.infer<typeof ZModelPreview.DocRequestSchema> = {
191+
models: allModels,
170192
environments: {
171-
editorName: vscode.env.appName,
193+
vscodeAppName: vscode.env.appName,
172194
vscodeVersion: vscode.version,
173-
appHost: vscode.env.appHost,
195+
vscodeAppHost: vscode.env.appHost,
174196
osRelease: os.release(),
175197
osType: os.type(),
176198
},
177199
};
178200

201+
const allModelsContent = allModels.map((m) => m.content);
202+
179203
// Check cache first
180-
const cachedResponse = await this.documentationCache.getCachedResponse(requestBody);
204+
const cachedResponse = await this.documentationCache.getCachedResponse(allModelsContent);
181205
if (cachedResponse) {
182206
return cachedResponse;
183207
}
184208

185209
// record the time spent
186210
const startTime = Date.now();
187-
const apiResponse = await fetch('https://api.zenstack.dev/api/doc', {
211+
const apiResponse = await fetch(`${API_URL}/api/doc`, {
188212
method: 'POST',
189213
headers: {
190214
'Content-Type': 'application/json',
@@ -202,7 +226,7 @@ export class ZModelPreview implements vscode.Disposable {
202226
const responseText = await apiResponse.text();
203227

204228
// Cache the response
205-
await this.documentationCache.setCachedResponse(requestBody, responseText);
229+
await this.documentationCache.setCachedResponse(allModelsContent, responseText);
206230

207231
return responseText;
208232
} catch (error) {

pnpm-lock.yaml

Lines changed: 1 addition & 13 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)