@shin1ohno/sage 0.9.1 → 0.10.0
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/dist/cli/http-server-with-config.d.ts.map +1 -1
- package/dist/cli/http-server-with-config.js +9 -8
- package/dist/cli/http-server-with-config.js.map +1 -1
- package/dist/index.js +62 -10
- package/dist/index.js.map +1 -1
- package/dist/integrations/apple-reminders.d.ts.map +1 -1
- package/dist/integrations/apple-reminders.js +6 -5
- package/dist/integrations/apple-reminders.js.map +1 -1
- package/dist/integrations/calendar-event-creator.d.ts.map +1 -1
- package/dist/integrations/calendar-event-creator.js +2 -1
- package/dist/integrations/calendar-event-creator.js.map +1 -1
- package/dist/integrations/calendar-event-deleter.d.ts.map +1 -1
- package/dist/integrations/calendar-event-deleter.js +2 -1
- package/dist/integrations/calendar-event-deleter.js.map +1 -1
- package/dist/integrations/calendar-event-response.d.ts.map +1 -1
- package/dist/integrations/calendar-event-response.js +4 -3
- package/dist/integrations/calendar-event-response.js.map +1 -1
- package/dist/integrations/calendar-service.d.ts.map +1 -1
- package/dist/integrations/calendar-service.js +6 -5
- package/dist/integrations/calendar-service.js.map +1 -1
- package/dist/integrations/calendar-source-manager.d.ts.map +1 -1
- package/dist/integrations/calendar-source-manager.js +6 -5
- package/dist/integrations/calendar-source-manager.js.map +1 -1
- package/dist/integrations/google-calendar-service.d.ts.map +1 -1
- package/dist/integrations/google-calendar-service.js +2 -1
- package/dist/integrations/google-calendar-service.js.map +1 -1
- package/dist/integrations/notion-mcp.d.ts.map +1 -1
- package/dist/integrations/notion-mcp.js +5 -4
- package/dist/integrations/notion-mcp.js.map +1 -1
- package/dist/integrations/todo-list-manager.d.ts.map +1 -1
- package/dist/integrations/todo-list-manager.js +3 -2
- package/dist/integrations/todo-list-manager.js.map +1 -1
- package/dist/oauth/encryption-service.d.ts.map +1 -1
- package/dist/oauth/encryption-service.js +14 -14
- package/dist/oauth/encryption-service.js.map +1 -1
- package/dist/oauth/file-mutex.d.ts.map +1 -1
- package/dist/oauth/file-mutex.js +3 -2
- package/dist/oauth/file-mutex.js.map +1 -1
- package/dist/oauth/index.d.ts +2 -0
- package/dist/oauth/index.d.ts.map +1 -1
- package/dist/oauth/index.js +4 -0
- package/dist/oauth/index.js.map +1 -1
- package/dist/oauth/oauth-callback-server.d.ts +100 -0
- package/dist/oauth/oauth-callback-server.d.ts.map +1 -0
- package/dist/oauth/oauth-callback-server.js +276 -0
- package/dist/oauth/oauth-callback-server.js.map +1 -0
- package/dist/oauth/oauth-server.d.ts.map +1 -1
- package/dist/oauth/oauth-server.js +13 -17
- package/dist/oauth/oauth-server.js.map +1 -1
- package/dist/oauth/persistent-client-store.d.ts.map +1 -1
- package/dist/oauth/persistent-client-store.js +4 -3
- package/dist/oauth/persistent-client-store.js.map +1 -1
- package/dist/oauth/persistent-refresh-token-store.d.ts.map +1 -1
- package/dist/oauth/persistent-refresh-token-store.js +5 -4
- package/dist/oauth/persistent-refresh-token-store.js.map +1 -1
- package/dist/oauth/persistent-session-store.d.ts.map +1 -1
- package/dist/oauth/persistent-session-store.js +7 -6
- package/dist/oauth/persistent-session-store.js.map +1 -1
- package/dist/remote/cloud-config.d.ts.map +1 -1
- package/dist/remote/cloud-config.js +2 -1
- package/dist/remote/cloud-config.js.map +1 -1
- package/dist/services/container.d.ts.map +1 -1
- package/dist/services/container.js +3 -2
- package/dist/services/container.js.map +1 -1
- package/dist/services/working-cadence.d.ts.map +1 -1
- package/dist/services/working-cadence.js +2 -1
- package/dist/services/working-cadence.js.map +1 -1
- package/dist/tools/oauth/authenticate-google.d.ts +54 -0
- package/dist/tools/oauth/authenticate-google.d.ts.map +1 -0
- package/dist/tools/oauth/authenticate-google.js +180 -0
- package/dist/tools/oauth/authenticate-google.js.map +1 -0
- package/dist/tools/oauth/index.d.ts +26 -0
- package/dist/tools/oauth/index.d.ts.map +1 -0
- package/dist/tools/oauth/index.js +9 -0
- package/dist/tools/oauth/index.js.map +1 -0
- package/dist/utils/browser-opener.d.ts +35 -0
- package/dist/utils/browser-opener.d.ts.map +1 -0
- package/dist/utils/browser-opener.js +71 -0
- package/dist/utils/browser-opener.js.map +1 -0
- package/dist/utils/logger.d.ts +28 -0
- package/dist/utils/logger.d.ts.map +1 -0
- package/dist/utils/logger.js +61 -0
- package/dist/utils/logger.js.map +1 -0
- package/dist/version.js +1 -1
- package/package.json +4 -1
|
@@ -0,0 +1,180 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* authenticate_google Tool Handler
|
|
3
|
+
* Requirements: FR-1 (authenticate_google MCP Tool)
|
|
4
|
+
*
|
|
5
|
+
* Orchestrates the complete Google OAuth flow:
|
|
6
|
+
* 1. Check for existing tokens
|
|
7
|
+
* 2. Start callback server
|
|
8
|
+
* 3. Generate authorization URL
|
|
9
|
+
* 4. Open browser
|
|
10
|
+
* 5. Wait for callback
|
|
11
|
+
* 6. Exchange code for tokens
|
|
12
|
+
* 7. Store tokens
|
|
13
|
+
*/
|
|
14
|
+
import { z } from 'zod';
|
|
15
|
+
import { OAuthCallbackServer } from '../../oauth/oauth-callback-server.js';
|
|
16
|
+
import { GoogleOAuthHandler } from '../../oauth/google-oauth-handler.js';
|
|
17
|
+
import { openBrowser } from '../../utils/browser-opener.js';
|
|
18
|
+
import { oauthLogger } from '../../utils/logger.js';
|
|
19
|
+
/**
|
|
20
|
+
* Arguments for authenticate_google tool
|
|
21
|
+
*/
|
|
22
|
+
export const AuthenticateGoogleArgsSchema = z.object({
|
|
23
|
+
/** Force re-authentication even if tokens exist */
|
|
24
|
+
force: z.boolean().optional().default(false),
|
|
25
|
+
/** Timeout in seconds (default: 300 = 5 minutes) */
|
|
26
|
+
timeout: z.number().optional().default(300),
|
|
27
|
+
});
|
|
28
|
+
/**
|
|
29
|
+
* Handle authenticate_google tool call
|
|
30
|
+
*
|
|
31
|
+
* Orchestrates the complete OAuth flow for Google Calendar authentication.
|
|
32
|
+
*
|
|
33
|
+
* @param args - Tool arguments
|
|
34
|
+
* @param context - OAuth tools context (unused, handler creates its own handler)
|
|
35
|
+
* @returns Authentication result
|
|
36
|
+
*/
|
|
37
|
+
export async function handleAuthenticateGoogle(args, _context) {
|
|
38
|
+
// Validate arguments
|
|
39
|
+
const validatedArgs = AuthenticateGoogleArgsSchema.parse(args);
|
|
40
|
+
const { force, timeout } = validatedArgs;
|
|
41
|
+
// Check environment variables
|
|
42
|
+
const clientId = process.env.GOOGLE_CLIENT_ID;
|
|
43
|
+
const clientSecret = process.env.GOOGLE_CLIENT_SECRET;
|
|
44
|
+
if (!clientId || !clientSecret) {
|
|
45
|
+
return {
|
|
46
|
+
success: false,
|
|
47
|
+
message: 'Google OAuth設定が見つかりません。',
|
|
48
|
+
error: '環境変数 GOOGLE_CLIENT_ID と GOOGLE_CLIENT_SECRET を設定してください。\n' +
|
|
49
|
+
'Google Cloud Console で OAuth クライアントを作成し、認証情報を取得してください。\n' +
|
|
50
|
+
'https://console.cloud.google.com/apis/credentials',
|
|
51
|
+
};
|
|
52
|
+
}
|
|
53
|
+
// Create OAuth handler
|
|
54
|
+
const redirectUri = process.env.GOOGLE_REDIRECT_URI || 'http://127.0.0.1:3000/oauth/callback';
|
|
55
|
+
const oauthHandler = new GoogleOAuthHandler({
|
|
56
|
+
clientId,
|
|
57
|
+
clientSecret,
|
|
58
|
+
redirectUri,
|
|
59
|
+
});
|
|
60
|
+
// Check for existing tokens (unless force is true)
|
|
61
|
+
if (!force) {
|
|
62
|
+
try {
|
|
63
|
+
const existingTokens = await oauthHandler.getTokens();
|
|
64
|
+
if (existingTokens) {
|
|
65
|
+
const isValid = await oauthHandler.validateToken(existingTokens);
|
|
66
|
+
if (isValid) {
|
|
67
|
+
return {
|
|
68
|
+
success: true,
|
|
69
|
+
message: '既に Google Calendar で認証済みです。',
|
|
70
|
+
alreadyAuthenticated: true,
|
|
71
|
+
expiresAt: new Date(existingTokens.expiresAt).toISOString(),
|
|
72
|
+
scopes: existingTokens.scope,
|
|
73
|
+
};
|
|
74
|
+
}
|
|
75
|
+
// Token expired, try to refresh
|
|
76
|
+
try {
|
|
77
|
+
const refreshedTokens = await oauthHandler.refreshAccessToken(existingTokens.refreshToken);
|
|
78
|
+
await oauthHandler.storeTokens(refreshedTokens);
|
|
79
|
+
return {
|
|
80
|
+
success: true,
|
|
81
|
+
message: 'トークンを更新しました。',
|
|
82
|
+
alreadyAuthenticated: true,
|
|
83
|
+
expiresAt: new Date(refreshedTokens.expiresAt).toISOString(),
|
|
84
|
+
scopes: refreshedTokens.scope,
|
|
85
|
+
};
|
|
86
|
+
}
|
|
87
|
+
catch (refreshError) {
|
|
88
|
+
// Refresh failed, need to re-authenticate
|
|
89
|
+
oauthLogger.info('Token refresh failed, initiating re-authentication');
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
catch (error) {
|
|
94
|
+
// No tokens or error reading tokens, proceed with authentication
|
|
95
|
+
oauthLogger.debug({ error }, 'No existing tokens found');
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
// Start callback server
|
|
99
|
+
const callbackServer = new OAuthCallbackServer({
|
|
100
|
+
timeout: timeout * 1000, // Convert to milliseconds
|
|
101
|
+
});
|
|
102
|
+
let serverInfo;
|
|
103
|
+
try {
|
|
104
|
+
serverInfo = await callbackServer.start();
|
|
105
|
+
}
|
|
106
|
+
catch (error) {
|
|
107
|
+
const errorMessage = error instanceof Error ? error.message : 'Unknown error';
|
|
108
|
+
return {
|
|
109
|
+
success: false,
|
|
110
|
+
message: 'コールバックサーバーの起動に失敗しました。',
|
|
111
|
+
error: `${errorMessage}\nポートが使用中の可能性があります。他のアプリケーションを終了してから再試行してください。`,
|
|
112
|
+
};
|
|
113
|
+
}
|
|
114
|
+
try {
|
|
115
|
+
// Generate authorization URL with the callback server's URL
|
|
116
|
+
// Update the redirect URI to use the actual callback URL
|
|
117
|
+
const oauthHandlerWithCallback = new GoogleOAuthHandler({
|
|
118
|
+
clientId,
|
|
119
|
+
clientSecret,
|
|
120
|
+
redirectUri: serverInfo.callbackUrl,
|
|
121
|
+
});
|
|
122
|
+
const authorizationUrl = await oauthHandlerWithCallback.getAuthorizationUrl(serverInfo.callbackUrl);
|
|
123
|
+
oauthLogger.info({ authorizationUrl: authorizationUrl.substring(0, 100) + '...' }, 'Generated authorization URL');
|
|
124
|
+
// Try to open browser
|
|
125
|
+
const browserResult = await openBrowser(authorizationUrl);
|
|
126
|
+
if (!browserResult.success) {
|
|
127
|
+
oauthLogger.warn({ error: browserResult.error }, 'Failed to open browser automatically');
|
|
128
|
+
// Return URL for manual opening
|
|
129
|
+
return {
|
|
130
|
+
success: false,
|
|
131
|
+
message: 'ブラウザを自動で開けませんでした。以下のURLを手動でブラウザに貼り付けてください。',
|
|
132
|
+
authorizationUrl,
|
|
133
|
+
error: browserResult.error,
|
|
134
|
+
};
|
|
135
|
+
}
|
|
136
|
+
oauthLogger.info('Browser opened, waiting for callback...');
|
|
137
|
+
// Wait for callback
|
|
138
|
+
const callbackResult = await callbackServer.waitForCallback();
|
|
139
|
+
if (!callbackResult.success) {
|
|
140
|
+
return {
|
|
141
|
+
success: false,
|
|
142
|
+
message: '認証がキャンセルされたか、エラーが発生しました。',
|
|
143
|
+
error: `${callbackResult.error}: ${callbackResult.errorDescription}`,
|
|
144
|
+
};
|
|
145
|
+
}
|
|
146
|
+
if (!callbackResult.code) {
|
|
147
|
+
return {
|
|
148
|
+
success: false,
|
|
149
|
+
message: '認証コードを取得できませんでした。',
|
|
150
|
+
error: 'Authorization code was not received',
|
|
151
|
+
};
|
|
152
|
+
}
|
|
153
|
+
// Exchange code for tokens
|
|
154
|
+
oauthLogger.info('Exchanging authorization code for tokens...');
|
|
155
|
+
const tokens = await oauthHandlerWithCallback.exchangeCodeForTokens(callbackResult.code, serverInfo.callbackUrl);
|
|
156
|
+
// Store tokens
|
|
157
|
+
await oauthHandlerWithCallback.storeTokens(tokens);
|
|
158
|
+
oauthLogger.info('OAuth tokens stored successfully');
|
|
159
|
+
return {
|
|
160
|
+
success: true,
|
|
161
|
+
message: 'Google Calendar の認証が完了しました!',
|
|
162
|
+
expiresAt: new Date(tokens.expiresAt).toISOString(),
|
|
163
|
+
scopes: tokens.scope,
|
|
164
|
+
};
|
|
165
|
+
}
|
|
166
|
+
catch (error) {
|
|
167
|
+
const errorMessage = error instanceof Error ? error.message : 'Unknown error';
|
|
168
|
+
oauthLogger.error({ error }, 'OAuth flow failed');
|
|
169
|
+
return {
|
|
170
|
+
success: false,
|
|
171
|
+
message: '認証フローでエラーが発生しました。',
|
|
172
|
+
error: errorMessage,
|
|
173
|
+
};
|
|
174
|
+
}
|
|
175
|
+
finally {
|
|
176
|
+
// Always shutdown the server
|
|
177
|
+
await callbackServer.shutdown();
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
//# sourceMappingURL=authenticate-google.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"authenticate-google.js","sourceRoot":"","sources":["../../../src/tools/oauth/authenticate-google.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* OAuth Tools Module
|
|
3
|
+
* Requirements: FR-1 (authenticate_google MCP Tool)
|
|
4
|
+
*
|
|
5
|
+
* Exports OAuth-related MCP tool handlers and context.
|
|
6
|
+
*/
|
|
7
|
+
import { GoogleOAuthHandler } from '../../oauth/google-oauth-handler.js';
|
|
8
|
+
/**
|
|
9
|
+
* OAuth Tools Context
|
|
10
|
+
*
|
|
11
|
+
* Provides access to OAuth-related services for tool handlers.
|
|
12
|
+
*/
|
|
13
|
+
export interface OAuthToolsContext {
|
|
14
|
+
/**
|
|
15
|
+
* Get GoogleOAuthHandler instance
|
|
16
|
+
* Returns null if Google OAuth is not configured
|
|
17
|
+
*/
|
|
18
|
+
getGoogleOAuthHandler: () => GoogleOAuthHandler | null;
|
|
19
|
+
/**
|
|
20
|
+
* Create a new GoogleOAuthHandler with the current config
|
|
21
|
+
*/
|
|
22
|
+
createGoogleOAuthHandler: () => GoogleOAuthHandler | null;
|
|
23
|
+
}
|
|
24
|
+
export { handleAuthenticateGoogle } from './authenticate-google.js';
|
|
25
|
+
export type { AuthenticateGoogleArgs, AuthenticateGoogleResult } from './authenticate-google.js';
|
|
26
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/tools/oauth/index.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EAAE,kBAAkB,EAAE,MAAM,qCAAqC,CAAC;AAEzE;;;;GAIG;AACH,MAAM,WAAW,iBAAiB;IAChC;;;OAGG;IACH,qBAAqB,EAAE,MAAM,kBAAkB,GAAG,IAAI,CAAC;IAEvD;;OAEG;IACH,wBAAwB,EAAE,MAAM,kBAAkB,GAAG,IAAI,CAAC;CAC3D;AAGD,OAAO,EAAE,wBAAwB,EAAE,MAAM,0BAA0B,CAAC;AAGpE,YAAY,EAAE,sBAAsB,EAAE,wBAAwB,EAAE,MAAM,0BAA0B,CAAC"}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* OAuth Tools Module
|
|
3
|
+
* Requirements: FR-1 (authenticate_google MCP Tool)
|
|
4
|
+
*
|
|
5
|
+
* Exports OAuth-related MCP tool handlers and context.
|
|
6
|
+
*/
|
|
7
|
+
// Export handler
|
|
8
|
+
export { handleAuthenticateGoogle } from './authenticate-google.js';
|
|
9
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/tools/oauth/index.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAsBH,iBAAiB;AACjB,OAAO,EAAE,wBAAwB,EAAE,MAAM,0BAA0B,CAAC"}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Browser Opener Utility
|
|
3
|
+
* Requirements: FR-3 (Browser Opening)
|
|
4
|
+
*
|
|
5
|
+
* Cross-platform utility to open URLs in the default browser.
|
|
6
|
+
*/
|
|
7
|
+
/**
|
|
8
|
+
* Result of browser open operation
|
|
9
|
+
*/
|
|
10
|
+
export interface BrowserOpenResult {
|
|
11
|
+
success: boolean;
|
|
12
|
+
error?: string;
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* Open URL in default browser
|
|
16
|
+
*
|
|
17
|
+
* Uses platform-appropriate command:
|
|
18
|
+
* - macOS: open
|
|
19
|
+
* - Linux: xdg-open
|
|
20
|
+
* - Windows: start
|
|
21
|
+
*
|
|
22
|
+
* @param url - URL to open
|
|
23
|
+
* @returns Result indicating success or failure
|
|
24
|
+
*/
|
|
25
|
+
export declare function openBrowser(url: string): Promise<BrowserOpenResult>;
|
|
26
|
+
/**
|
|
27
|
+
* Get the command that would be used to open a URL
|
|
28
|
+
*
|
|
29
|
+
* Useful for testing and debugging.
|
|
30
|
+
*
|
|
31
|
+
* @param url - URL to open
|
|
32
|
+
* @returns Command string
|
|
33
|
+
*/
|
|
34
|
+
export declare function getBrowserCommand(url: string): string;
|
|
35
|
+
//# sourceMappingURL=browser-opener.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"browser-opener.d.ts","sourceRoot":"","sources":["../../src/utils/browser-opener.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAOH;;GAEG;AACH,MAAM,WAAW,iBAAiB;IAChC,OAAO,EAAE,OAAO,CAAC;IACjB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED;;;;;;;;;;GAUG;AACH,wBAAsB,WAAW,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,iBAAiB,CAAC,CA8BzE;AAED;;;;;;;GAOG;AACH,wBAAgB,iBAAiB,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAYrD"}
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Browser Opener Utility
|
|
3
|
+
* Requirements: FR-3 (Browser Opening)
|
|
4
|
+
*
|
|
5
|
+
* Cross-platform utility to open URLs in the default browser.
|
|
6
|
+
*/
|
|
7
|
+
import { exec } from 'child_process';
|
|
8
|
+
import { promisify } from 'util';
|
|
9
|
+
const execAsync = promisify(exec);
|
|
10
|
+
/**
|
|
11
|
+
* Open URL in default browser
|
|
12
|
+
*
|
|
13
|
+
* Uses platform-appropriate command:
|
|
14
|
+
* - macOS: open
|
|
15
|
+
* - Linux: xdg-open
|
|
16
|
+
* - Windows: start
|
|
17
|
+
*
|
|
18
|
+
* @param url - URL to open
|
|
19
|
+
* @returns Result indicating success or failure
|
|
20
|
+
*/
|
|
21
|
+
export async function openBrowser(url) {
|
|
22
|
+
const platform = process.platform;
|
|
23
|
+
let command;
|
|
24
|
+
switch (platform) {
|
|
25
|
+
case 'darwin':
|
|
26
|
+
// macOS
|
|
27
|
+
command = `open "${url}"`;
|
|
28
|
+
break;
|
|
29
|
+
case 'win32':
|
|
30
|
+
// Windows - use start with empty title
|
|
31
|
+
command = `start "" "${url}"`;
|
|
32
|
+
break;
|
|
33
|
+
case 'linux':
|
|
34
|
+
default:
|
|
35
|
+
// Linux and other Unix-like systems
|
|
36
|
+
command = `xdg-open "${url}"`;
|
|
37
|
+
break;
|
|
38
|
+
}
|
|
39
|
+
try {
|
|
40
|
+
await execAsync(command);
|
|
41
|
+
return { success: true };
|
|
42
|
+
}
|
|
43
|
+
catch (error) {
|
|
44
|
+
const errorMessage = error instanceof Error ? error.message : 'Unknown error';
|
|
45
|
+
return {
|
|
46
|
+
success: false,
|
|
47
|
+
error: `Failed to open browser: ${errorMessage}`,
|
|
48
|
+
};
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* Get the command that would be used to open a URL
|
|
53
|
+
*
|
|
54
|
+
* Useful for testing and debugging.
|
|
55
|
+
*
|
|
56
|
+
* @param url - URL to open
|
|
57
|
+
* @returns Command string
|
|
58
|
+
*/
|
|
59
|
+
export function getBrowserCommand(url) {
|
|
60
|
+
const platform = process.platform;
|
|
61
|
+
switch (platform) {
|
|
62
|
+
case 'darwin':
|
|
63
|
+
return `open "${url}"`;
|
|
64
|
+
case 'win32':
|
|
65
|
+
return `start "" "${url}"`;
|
|
66
|
+
case 'linux':
|
|
67
|
+
default:
|
|
68
|
+
return `xdg-open "${url}"`;
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
//# sourceMappingURL=browser-opener.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"browser-opener.js","sourceRoot":"","sources":["../../src/utils/browser-opener.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EAAE,IAAI,EAAE,MAAM,eAAe,CAAC;AACrC,OAAO,EAAE,SAAS,EAAE,MAAM,MAAM,CAAC;AAEjC,MAAM,SAAS,GAAG,SAAS,CAAC,IAAI,CAAC,CAAC;AAUlC;;;;;;;;;;GAUG;AACH,MAAM,CAAC,KAAK,UAAU,WAAW,CAAC,GAAW;IAC3C,MAAM,QAAQ,GAAG,OAAO,CAAC,QAAQ,CAAC;IAClC,IAAI,OAAe,CAAC;IAEpB,QAAQ,QAAQ,EAAE,CAAC;QACjB,KAAK,QAAQ;YACX,QAAQ;YACR,OAAO,GAAG,SAAS,GAAG,GAAG,CAAC;YAC1B,MAAM;QACR,KAAK,OAAO;YACV,uCAAuC;YACvC,OAAO,GAAG,aAAa,GAAG,GAAG,CAAC;YAC9B,MAAM;QACR,KAAK,OAAO,CAAC;QACb;YACE,oCAAoC;YACpC,OAAO,GAAG,aAAa,GAAG,GAAG,CAAC;YAC9B,MAAM;IACV,CAAC;IAED,IAAI,CAAC;QACH,MAAM,SAAS,CAAC,OAAO,CAAC,CAAC;QACzB,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,CAAC;IAC3B,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,MAAM,YAAY,GAAG,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,eAAe,CAAC;QAC9E,OAAO;YACL,OAAO,EAAE,KAAK;YACd,KAAK,EAAE,2BAA2B,YAAY,EAAE;SACjD,CAAC;IACJ,CAAC;AACH,CAAC;AAED;;;;;;;GAOG;AACH,MAAM,UAAU,iBAAiB,CAAC,GAAW;IAC3C,MAAM,QAAQ,GAAG,OAAO,CAAC,QAAQ,CAAC;IAElC,QAAQ,QAAQ,EAAE,CAAC;QACjB,KAAK,QAAQ;YACX,OAAO,SAAS,GAAG,GAAG,CAAC;QACzB,KAAK,OAAO;YACV,OAAO,aAAa,GAAG,GAAG,CAAC;QAC7B,KAAK,OAAO,CAAC;QACb;YACE,OAAO,aAAa,GAAG,GAAG,CAAC;IAC/B,CAAC;AACH,CAAC"}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Logger Utility
|
|
3
|
+
* Provides structured logging with pino
|
|
4
|
+
*/
|
|
5
|
+
import pino from 'pino';
|
|
6
|
+
/**
|
|
7
|
+
* Log level type
|
|
8
|
+
*/
|
|
9
|
+
export type LogLevel = 'trace' | 'debug' | 'info' | 'warn' | 'error' | 'fatal' | 'silent';
|
|
10
|
+
/**
|
|
11
|
+
* Default logger instance
|
|
12
|
+
* Uses pino-pretty in development, JSON in production
|
|
13
|
+
*/
|
|
14
|
+
export declare const logger: pino.Logger<never, boolean>;
|
|
15
|
+
/**
|
|
16
|
+
* Create a child logger with a specific component name
|
|
17
|
+
* @param component - Component name for log context
|
|
18
|
+
*/
|
|
19
|
+
export declare function createLogger(component: string): pino.Logger;
|
|
20
|
+
/**
|
|
21
|
+
* Pre-configured loggers for common components
|
|
22
|
+
*/
|
|
23
|
+
export declare const oauthLogger: pino.Logger<never, boolean>;
|
|
24
|
+
export declare const calendarLogger: pino.Logger<never, boolean>;
|
|
25
|
+
export declare const mcpLogger: pino.Logger<never, boolean>;
|
|
26
|
+
export declare const cliLogger: pino.Logger<never, boolean>;
|
|
27
|
+
export declare const servicesLogger: pino.Logger<never, boolean>;
|
|
28
|
+
//# sourceMappingURL=logger.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"logger.d.ts","sourceRoot":"","sources":["../../src/utils/logger.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,IAAI,MAAM,MAAM,CAAC;AAExB;;GAEG;AACH,MAAM,MAAM,QAAQ,GAAG,OAAO,GAAG,OAAO,GAAG,MAAM,GAAG,MAAM,GAAG,OAAO,GAAG,OAAO,GAAG,QAAQ,CAAC;AAqC1F;;;GAGG;AACH,eAAO,MAAM,MAAM,6BAGjB,CAAC;AAEH;;;GAGG;AACH,wBAAgB,YAAY,CAAC,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC,MAAM,CAE3D;AAED;;GAEG;AACH,eAAO,MAAM,WAAW,6BAAwB,CAAC;AACjD,eAAO,MAAM,cAAc,6BAA2B,CAAC;AACvD,eAAO,MAAM,SAAS,6BAAsB,CAAC;AAC7C,eAAO,MAAM,SAAS,6BAAsB,CAAC;AAC7C,eAAO,MAAM,cAAc,6BAA2B,CAAC"}
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Logger Utility
|
|
3
|
+
* Provides structured logging with pino
|
|
4
|
+
*/
|
|
5
|
+
import pino from 'pino';
|
|
6
|
+
/**
|
|
7
|
+
* Get log level from environment variable
|
|
8
|
+
*/
|
|
9
|
+
function getLogLevel() {
|
|
10
|
+
const level = process.env.LOG_LEVEL?.toLowerCase();
|
|
11
|
+
if (level && ['trace', 'debug', 'info', 'warn', 'error', 'fatal', 'silent'].includes(level)) {
|
|
12
|
+
return level;
|
|
13
|
+
}
|
|
14
|
+
return 'info';
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* Check if running in development mode
|
|
18
|
+
*/
|
|
19
|
+
function isDevelopment() {
|
|
20
|
+
return process.env.NODE_ENV === 'development';
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* Create pino transport options for pretty printing in development
|
|
24
|
+
*/
|
|
25
|
+
function getTransport() {
|
|
26
|
+
if (isDevelopment()) {
|
|
27
|
+
return {
|
|
28
|
+
target: 'pino-pretty',
|
|
29
|
+
options: {
|
|
30
|
+
colorize: true,
|
|
31
|
+
translateTime: 'SYS:standard',
|
|
32
|
+
ignore: 'pid,hostname',
|
|
33
|
+
},
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
return undefined;
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* Default logger instance
|
|
40
|
+
* Uses pino-pretty in development, JSON in production
|
|
41
|
+
*/
|
|
42
|
+
export const logger = pino({
|
|
43
|
+
level: getLogLevel(),
|
|
44
|
+
transport: getTransport(),
|
|
45
|
+
});
|
|
46
|
+
/**
|
|
47
|
+
* Create a child logger with a specific component name
|
|
48
|
+
* @param component - Component name for log context
|
|
49
|
+
*/
|
|
50
|
+
export function createLogger(component) {
|
|
51
|
+
return logger.child({ component });
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* Pre-configured loggers for common components
|
|
55
|
+
*/
|
|
56
|
+
export const oauthLogger = createLogger('oauth');
|
|
57
|
+
export const calendarLogger = createLogger('calendar');
|
|
58
|
+
export const mcpLogger = createLogger('mcp');
|
|
59
|
+
export const cliLogger = createLogger('cli');
|
|
60
|
+
export const servicesLogger = createLogger('services');
|
|
61
|
+
//# sourceMappingURL=logger.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"logger.js","sourceRoot":"","sources":["../../src/utils/logger.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,IAAI,MAAM,MAAM,CAAC;AAOxB;;GAEG;AACH,SAAS,WAAW;IAClB,MAAM,KAAK,GAAG,OAAO,CAAC,GAAG,CAAC,SAAS,EAAE,WAAW,EAAE,CAAC;IACnD,IAAI,KAAK,IAAI,CAAC,OAAO,EAAE,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,OAAO,EAAE,QAAQ,CAAC,CAAC,QAAQ,CAAC,KAAK,CAAC,EAAE,CAAC;QAC5F,OAAO,KAAiB,CAAC;IAC3B,CAAC;IACD,OAAO,MAAM,CAAC;AAChB,CAAC;AAED;;GAEG;AACH,SAAS,aAAa;IACpB,OAAO,OAAO,CAAC,GAAG,CAAC,QAAQ,KAAK,aAAa,CAAC;AAChD,CAAC;AAED;;GAEG;AACH,SAAS,YAAY;IACnB,IAAI,aAAa,EAAE,EAAE,CAAC;QACpB,OAAO;YACL,MAAM,EAAE,aAAa;YACrB,OAAO,EAAE;gBACP,QAAQ,EAAE,IAAI;gBACd,aAAa,EAAE,cAAc;gBAC7B,MAAM,EAAE,cAAc;aACvB;SACF,CAAC;IACJ,CAAC;IACD,OAAO,SAAS,CAAC;AACnB,CAAC;AAED;;;GAGG;AACH,MAAM,CAAC,MAAM,MAAM,GAAG,IAAI,CAAC;IACzB,KAAK,EAAE,WAAW,EAAE;IACpB,SAAS,EAAE,YAAY,EAAE;CAC1B,CAAC,CAAC;AAEH;;;GAGG;AACH,MAAM,UAAU,YAAY,CAAC,SAAiB;IAC5C,OAAO,MAAM,CAAC,KAAK,CAAC,EAAE,SAAS,EAAE,CAAC,CAAC;AACrC,CAAC;AAED;;GAEG;AACH,MAAM,CAAC,MAAM,WAAW,GAAG,YAAY,CAAC,OAAO,CAAC,CAAC;AACjD,MAAM,CAAC,MAAM,cAAc,GAAG,YAAY,CAAC,UAAU,CAAC,CAAC;AACvD,MAAM,CAAC,MAAM,SAAS,GAAG,YAAY,CAAC,KAAK,CAAC,CAAC;AAC7C,MAAM,CAAC,MAAM,SAAS,GAAG,YAAY,CAAC,KAAK,CAAC,CAAC;AAC7C,MAAM,CAAC,MAAM,cAAc,GAAG,YAAY,CAAC,UAAU,CAAC,CAAC"}
|
package/dist/version.js
CHANGED
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
import { readFileSync, existsSync } from 'fs';
|
|
8
8
|
import { join } from 'path';
|
|
9
9
|
// Fallback version - keep in sync with package.json
|
|
10
|
-
const FALLBACK_VERSION = '0.
|
|
10
|
+
const FALLBACK_VERSION = '0.9.2';
|
|
11
11
|
function getVersion() {
|
|
12
12
|
// Try to find package.json from current working directory
|
|
13
13
|
const pkgPath = join(process.cwd(), 'package.json');
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@shin1ohno/sage",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.10.0",
|
|
4
4
|
"description": "AI Task Management Assistant MCP Server for Claude Desktop and Claude Code",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"bin": {
|
|
@@ -48,12 +48,15 @@
|
|
|
48
48
|
"dependencies": {
|
|
49
49
|
"@modelcontextprotocol/sdk": "^1.0.4",
|
|
50
50
|
"googleapis": "^134.0.0",
|
|
51
|
+
"pino": "^10.1.0",
|
|
52
|
+
"pino-pretty": "^13.1.3",
|
|
51
53
|
"run-applescript": "^7.1.0",
|
|
52
54
|
"zod": "^3.23.8"
|
|
53
55
|
},
|
|
54
56
|
"devDependencies": {
|
|
55
57
|
"@types/jest": "^29.0.0",
|
|
56
58
|
"@types/node": "^20.0.0",
|
|
59
|
+
"@types/pino": "^7.0.4",
|
|
57
60
|
"@typescript-eslint/eslint-plugin": "^7.0.0",
|
|
58
61
|
"@typescript-eslint/parser": "^7.0.0",
|
|
59
62
|
"eslint": "^8.0.0",
|