skedyul 0.2.147 → 0.2.149

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/.build-stamp CHANGED
@@ -1 +1 @@
1
- 1770681945679
1
+ 1770690573798
package/dist/errors.d.ts CHANGED
@@ -75,3 +75,26 @@ export declare class InvalidConfigurationError extends InstallError {
75
75
  export declare class ConnectionError extends InstallError {
76
76
  constructor(message?: string);
77
77
  }
78
+ /**
79
+ * Error thrown when app authentication/authorization is invalid and needs re-validation.
80
+ *
81
+ * Any app integration can throw this error to signal that the installation's
82
+ * auth status should be set to INVALID, triggering re-authorization flow.
83
+ *
84
+ * The error code 'APP_AUTH_INVALID' is used by the workflow to detect and handle
85
+ * this error generically, regardless of which app throws it.
86
+ *
87
+ * The redirect URL is constructed by the workflow after catching this error,
88
+ * so apps don't need to provide it.
89
+ *
90
+ * @example
91
+ * ```typescript
92
+ * if (tokenExpired) {
93
+ * throw new AppAuthInvalidError('Access token has expired. Please re-authorize the app.')
94
+ * }
95
+ * ```
96
+ */
97
+ export declare class AppAuthInvalidError extends Error {
98
+ readonly code = "APP_AUTH_INVALID";
99
+ constructor(message: string);
100
+ }
package/dist/errors.js CHANGED
@@ -7,7 +7,7 @@
7
7
  * that the frontend can display inline on the install form.
8
8
  */
9
9
  Object.defineProperty(exports, "__esModule", { value: true });
10
- exports.ConnectionError = exports.InvalidConfigurationError = exports.AuthenticationError = exports.MissingRequiredFieldError = exports.InstallError = void 0;
10
+ exports.AppAuthInvalidError = exports.ConnectionError = exports.InvalidConfigurationError = exports.AuthenticationError = exports.MissingRequiredFieldError = exports.InstallError = void 0;
11
11
  /**
12
12
  * Base error class for install handler errors.
13
13
  *
@@ -97,3 +97,30 @@ class ConnectionError extends InstallError {
97
97
  }
98
98
  }
99
99
  exports.ConnectionError = ConnectionError;
100
+ /**
101
+ * Error thrown when app authentication/authorization is invalid and needs re-validation.
102
+ *
103
+ * Any app integration can throw this error to signal that the installation's
104
+ * auth status should be set to INVALID, triggering re-authorization flow.
105
+ *
106
+ * The error code 'APP_AUTH_INVALID' is used by the workflow to detect and handle
107
+ * this error generically, regardless of which app throws it.
108
+ *
109
+ * The redirect URL is constructed by the workflow after catching this error,
110
+ * so apps don't need to provide it.
111
+ *
112
+ * @example
113
+ * ```typescript
114
+ * if (tokenExpired) {
115
+ * throw new AppAuthInvalidError('Access token has expired. Please re-authorize the app.')
116
+ * }
117
+ * ```
118
+ */
119
+ class AppAuthInvalidError extends Error {
120
+ constructor(message) {
121
+ super(message);
122
+ this.code = 'APP_AUTH_INVALID';
123
+ this.name = 'AppAuthInvalidError';
124
+ }
125
+ }
126
+ exports.AppAuthInvalidError = AppAuthInvalidError;
package/dist/index.d.ts CHANGED
@@ -4,7 +4,7 @@ export { ToolResponseMetaSchema } from './types';
4
4
  export * from './schemas';
5
5
  export { server } from './server';
6
6
  export { DEFAULT_DOCKERFILE } from './dockerfile';
7
- export { InstallError, MissingRequiredFieldError, AuthenticationError, InvalidConfigurationError, ConnectionError, } from './errors';
7
+ export { InstallError, MissingRequiredFieldError, AuthenticationError, InvalidConfigurationError, ConnectionError, AppAuthInvalidError, } from './errors';
8
8
  export type { InstallErrorCode } from './errors';
9
9
  export { z };
10
10
  export { workplace, communicationChannel, instance, token, file, webhook, resource, contactAssociationLink, configure, getConfig, runWithConfig, } from './core/client';
package/dist/index.js CHANGED
@@ -14,7 +14,7 @@ var __exportStar = (this && this.__exportStar) || function(m, exports) {
14
14
  for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
15
15
  };
16
16
  Object.defineProperty(exports, "__esModule", { value: true });
17
- exports.getRequiredInstallEnvKeys = exports.getAllEnvKeys = exports.CONFIG_FILE_NAMES = exports.validateConfig = exports.loadConfig = exports.defineConfig = exports.runWithConfig = exports.getConfig = exports.configure = exports.contactAssociationLink = exports.resource = exports.webhook = exports.file = exports.token = exports.instance = exports.communicationChannel = exports.workplace = exports.z = exports.ConnectionError = exports.InvalidConfigurationError = exports.AuthenticationError = exports.MissingRequiredFieldError = exports.InstallError = exports.DEFAULT_DOCKERFILE = exports.server = exports.ToolResponseMetaSchema = void 0;
17
+ exports.getRequiredInstallEnvKeys = exports.getAllEnvKeys = exports.CONFIG_FILE_NAMES = exports.validateConfig = exports.loadConfig = exports.defineConfig = exports.runWithConfig = exports.getConfig = exports.configure = exports.contactAssociationLink = exports.resource = exports.webhook = exports.file = exports.token = exports.instance = exports.communicationChannel = exports.workplace = exports.z = exports.AppAuthInvalidError = exports.ConnectionError = exports.InvalidConfigurationError = exports.AuthenticationError = exports.MissingRequiredFieldError = exports.InstallError = exports.DEFAULT_DOCKERFILE = exports.server = exports.ToolResponseMetaSchema = void 0;
18
18
  const zod_1 = require("zod");
19
19
  Object.defineProperty(exports, "z", { enumerable: true, get: function () { return zod_1.z; } });
20
20
  __exportStar(require("./types"), exports);
@@ -32,6 +32,7 @@ Object.defineProperty(exports, "MissingRequiredFieldError", { enumerable: true,
32
32
  Object.defineProperty(exports, "AuthenticationError", { enumerable: true, get: function () { return errors_1.AuthenticationError; } });
33
33
  Object.defineProperty(exports, "InvalidConfigurationError", { enumerable: true, get: function () { return errors_1.InvalidConfigurationError; } });
34
34
  Object.defineProperty(exports, "ConnectionError", { enumerable: true, get: function () { return errors_1.ConnectionError; } });
35
+ Object.defineProperty(exports, "AppAuthInvalidError", { enumerable: true, get: function () { return errors_1.AppAuthInvalidError; } });
35
36
  var client_1 = require("./core/client");
36
37
  Object.defineProperty(exports, "workplace", { enumerable: true, get: function () { return client_1.workplace; } });
37
38
  Object.defineProperty(exports, "communicationChannel", { enumerable: true, get: function () { return client_1.communicationChannel; } });
package/dist/server.js CHANGED
@@ -403,6 +403,24 @@ function createCallToolHandler(registry, state, onMaxRequests) {
403
403
  };
404
404
  }
405
405
  catch (error) {
406
+ // Check if it's an AppAuthInvalidError
407
+ if (error instanceof errors_1.AppAuthInvalidError) {
408
+ return {
409
+ output: null,
410
+ billing: { credits: 0 },
411
+ meta: {
412
+ success: false,
413
+ message: error.message,
414
+ toolName,
415
+ },
416
+ error: {
417
+ code: error.code,
418
+ message: error.message,
419
+ },
420
+ // Note: redirect URL will be added by workflow after detecting APP_AUTH_INVALID
421
+ };
422
+ }
423
+ // Generic error handling for other errors
406
424
  const errorMessage = error instanceof Error ? error.message : String(error ?? '');
407
425
  return {
408
426
  output: null,
@@ -412,7 +430,10 @@ function createCallToolHandler(registry, state, onMaxRequests) {
412
430
  message: errorMessage,
413
431
  toolName,
414
432
  },
415
- error: errorMessage,
433
+ error: {
434
+ code: 'TOOL_EXECUTION_ERROR',
435
+ message: errorMessage,
436
+ },
416
437
  };
417
438
  }
418
439
  finally {
@@ -878,49 +899,37 @@ function createDedicatedServerInstance(config, tools, callTool, state, mcpServer
878
899
  }
879
900
  return;
880
901
  }
881
- // Handle /oauth_callback endpoint for OAuth callbacks (called by platform route)
882
- if (pathname === '/oauth_callback' && req.method === 'GET') {
902
+ // Handle /oauth_callback endpoint for OAuth callbacks (called by Temporal workflow)
903
+ if (pathname === '/oauth_callback' && req.method === 'POST') {
883
904
  if (!config.hooks?.oauth_callback) {
884
- sendHTML(res, 404, '<html><body><h1>OAuth callback handler not configured</h1></body></html>');
905
+ sendJSON(res, 404, { error: 'OAuth callback handler not configured' });
885
906
  return;
886
907
  }
887
- // Parse query parameters
888
- const url = new URL(req.url || '', `http://${req.headers.host}`);
889
- const query = {};
890
- url.searchParams.forEach((value, key) => {
891
- query[key] = value;
892
- });
893
- // Decode state parameter (contains appInstallationId, workplace, app info)
894
- let stateData = {};
895
- if (query.state) {
896
- try {
897
- stateData = JSON.parse(Buffer.from(query.state, 'base64').toString('utf-8'));
898
- }
899
- catch {
900
- sendHTML(res, 400, '<html><body><h1>Invalid state parameter</h1></body></html>');
901
- return;
902
- }
908
+ let oauthCallbackBody = {};
909
+ try {
910
+ oauthCallbackBody = (await parseJSONBody(req));
903
911
  }
904
- if (!stateData.appInstallationId || !stateData.workplace || !stateData.app) {
905
- sendHTML(res, 400, '<html><body><h1>Missing required state data</h1></body></html>');
912
+ catch {
913
+ sendJSON(res, 400, {
914
+ error: { code: -32700, message: 'Parse error' },
915
+ });
916
+ return;
917
+ }
918
+ if (!oauthCallbackBody.query) {
919
+ sendJSON(res, 400, {
920
+ error: { code: -32602, message: 'Missing query parameter' },
921
+ });
906
922
  return;
907
923
  }
908
- // Get current env vars from request (if any)
909
- // For OAuth callbacks, we might not have env vars yet, so use empty object
910
- const env = {};
911
- const oauthCallbackContext = {
912
- query,
913
- env,
914
- workplace: stateData.workplace,
915
- appInstallationId: stateData.appInstallationId,
916
- app: stateData.app,
917
- };
918
924
  // Build request-scoped config for SDK access
919
- // Use env from process or request if available
925
+ // Use provision-level token from process.env (baked at provisioning time)
920
926
  const oauthCallbackRequestConfig = {
921
927
  baseUrl: process.env.SKEDYUL_API_URL ?? '',
922
928
  apiToken: process.env.SKEDYUL_API_TOKEN ?? '',
923
929
  };
930
+ const oauthCallbackContext = {
931
+ query: oauthCallbackBody.query,
932
+ };
924
933
  try {
925
934
  const oauthCallbackHook = config.hooks.oauth_callback;
926
935
  const oauthCallbackHandler = typeof oauthCallbackHook === 'function'
@@ -929,29 +938,19 @@ function createDedicatedServerInstance(config, tools, callTool, state, mcpServer
929
938
  const result = await (0, client_1.runWithConfig)(oauthCallbackRequestConfig, async () => {
930
939
  return await oauthCallbackHandler(oauthCallbackContext);
931
940
  });
932
- // Return custom HTML if provided, otherwise default success page
933
- const html = result.html ?? `
934
- <html>
935
- <body style="font-family: system-ui; padding: 40px; text-align: center;">
936
- <h1 style="color: #38a169;">✓ Authorization Successful</h1>
937
- <p>You can close this window and return to the app.</p>
938
- </body>
939
- </html>
940
- `;
941
- sendHTML(res, 200, html);
941
+ sendJSON(res, 200, {
942
+ appInstallationId: result.appInstallationId,
943
+ env: result.env ?? {},
944
+ });
942
945
  }
943
946
  catch (err) {
944
947
  const errorMessage = err instanceof Error ? err.message : String(err ?? 'Unknown error');
945
- const errorHtml = `
946
- <html>
947
- <body style="font-family: system-ui; padding: 40px; text-align: center;">
948
- <h1 style="color: #e53e3e;">Authorization Failed</h1>
949
- <p>${errorMessage}</p>
950
- <p>You can close this window.</p>
951
- </body>
952
- </html>
953
- `;
954
- sendHTML(res, 500, errorHtml);
948
+ sendJSON(res, 500, {
949
+ error: {
950
+ code: -32603,
951
+ message: errorMessage,
952
+ },
953
+ });
955
954
  }
956
955
  return;
957
956
  }
@@ -1573,47 +1572,29 @@ function createServerlessInstance(config, tools, callTool, state, mcpServer, reg
1573
1572
  }
1574
1573
  }
1575
1574
  // Handle /oauth_callback endpoint for OAuth callbacks (called by platform route)
1576
- if (path === '/oauth_callback' && method === 'GET') {
1575
+ if (path === '/oauth_callback' && method === 'POST') {
1577
1576
  if (!config.hooks?.oauth_callback) {
1578
- return createResponse(404, { error: 'OAuth callback handler not configured' }, { ...headers, 'Content-Type': 'text/html; charset=utf-8' });
1577
+ return createResponse(404, { error: 'OAuth callback handler not configured' }, headers);
1579
1578
  }
1580
- // Parse query parameters from event
1581
- const query = {};
1582
- if (event.queryStringParameters) {
1583
- Object.entries(event.queryStringParameters).forEach(([key, value]) => {
1584
- if (value)
1585
- query[key] = value;
1586
- });
1579
+ let oauthCallbackBody = {};
1580
+ try {
1581
+ oauthCallbackBody = event.body ? JSON.parse(event.body) : {};
1587
1582
  }
1588
- // Decode state parameter (contains appInstallationId, workplace, app info)
1589
- let stateData = {};
1590
- if (query.state) {
1591
- try {
1592
- stateData = JSON.parse(Buffer.from(query.state, 'base64').toString('utf-8'));
1593
- }
1594
- catch {
1595
- return createResponse(400, { error: 'Invalid state parameter' }, { ...headers, 'Content-Type': 'text/html; charset=utf-8' });
1596
- }
1583
+ catch {
1584
+ return createResponse(400, { error: { code: -32700, message: 'Parse error' } }, headers);
1597
1585
  }
1598
- if (!stateData.appInstallationId || !stateData.workplace || !stateData.app) {
1599
- return createResponse(400, { error: 'Missing required state data' }, { ...headers, 'Content-Type': 'text/html; charset=utf-8' });
1586
+ if (!oauthCallbackBody.query) {
1587
+ return createResponse(400, { error: { code: -32602, message: 'Missing query parameter' } }, headers);
1600
1588
  }
1601
- // Get current env vars from request (if any)
1602
- // For OAuth callbacks, we might not have env vars yet, so use empty object
1603
- const env = {};
1604
- const oauthCallbackContext = {
1605
- query,
1606
- env,
1607
- workplace: stateData.workplace,
1608
- appInstallationId: stateData.appInstallationId,
1609
- app: stateData.app,
1610
- };
1611
1589
  // Build request-scoped config for SDK access
1612
- // Use env from process or request if available
1590
+ // Use provision-level token from process.env (baked at provisioning time)
1613
1591
  const oauthCallbackRequestConfig = {
1614
1592
  baseUrl: process.env.SKEDYUL_API_URL ?? '',
1615
1593
  apiToken: process.env.SKEDYUL_API_TOKEN ?? '',
1616
1594
  };
1595
+ const oauthCallbackContext = {
1596
+ query: oauthCallbackBody.query,
1597
+ };
1617
1598
  try {
1618
1599
  const oauthCallbackHook = config.hooks.oauth_callback;
1619
1600
  const oauthCallbackHandler = typeof oauthCallbackHook === 'function'
@@ -1622,29 +1603,19 @@ function createServerlessInstance(config, tools, callTool, state, mcpServer, reg
1622
1603
  const result = await (0, client_1.runWithConfig)(oauthCallbackRequestConfig, async () => {
1623
1604
  return await oauthCallbackHandler(oauthCallbackContext);
1624
1605
  });
1625
- // Return custom HTML if provided, otherwise default success page
1626
- const html = result.html ?? `
1627
- <html>
1628
- <body style="font-family: system-ui; padding: 40px; text-align: center;">
1629
- <h1 style="color: #38a169;">✓ Authorization Successful</h1>
1630
- <p>You can close this window and return to the app.</p>
1631
- </body>
1632
- </html>
1633
- `;
1634
- return createResponse(200, html, { ...headers, 'Content-Type': 'text/html; charset=utf-8' });
1606
+ return createResponse(200, {
1607
+ appInstallationId: result.appInstallationId,
1608
+ env: result.env ?? {},
1609
+ }, headers);
1635
1610
  }
1636
1611
  catch (err) {
1637
1612
  const errorMessage = err instanceof Error ? err.message : String(err ?? 'Unknown error');
1638
- const errorHtml = `
1639
- <html>
1640
- <body style="font-family: system-ui; padding: 40px; text-align: center;">
1641
- <h1 style="color: #e53e3e;">Authorization Failed</h1>
1642
- <p>${errorMessage}</p>
1643
- <p>You can close this window.</p>
1644
- </body>
1645
- </html>
1646
- `;
1647
- return createResponse(500, errorHtml, { ...headers, 'Content-Type': 'text/html; charset=utf-8' });
1613
+ return createResponse(500, {
1614
+ error: {
1615
+ code: -32603,
1616
+ message: errorMessage,
1617
+ },
1618
+ }, headers);
1648
1619
  }
1649
1620
  }
1650
1621
  if (path === '/health' && method === 'GET') {
package/dist/types.d.ts CHANGED
@@ -104,6 +104,14 @@ export interface ToolEffect {
104
104
  /** URL to navigate to after the tool completes */
105
105
  redirect?: string;
106
106
  }
107
+ /**
108
+ * Structured error information for tool execution results.
109
+ * Uses codes for serialization and workflow detection.
110
+ */
111
+ export interface ToolError {
112
+ code: string;
113
+ message: string;
114
+ }
107
115
  export interface ToolExecutionResult<Output = unknown> {
108
116
  /** Tool-specific output data. Null on error. */
109
117
  output: Output | null;
@@ -113,6 +121,8 @@ export interface ToolExecutionResult<Output = unknown> {
113
121
  meta: ToolResponseMeta;
114
122
  /** Optional client-side effects to execute */
115
123
  effect?: ToolEffect;
124
+ /** Structured error information (null/undefined if no error) */
125
+ error?: ToolError | null;
116
126
  }
117
127
  export interface ToolSchemaWithJson<Schema extends z.ZodTypeAny = z.ZodTypeAny> {
118
128
  zod: Schema;
@@ -265,22 +275,10 @@ export type InstallHandlerResult<Hooks extends ServerHooks = ServerHooks> = HasO
265
275
  export type InstallHandler<Hooks extends ServerHooks = ServerHooks> = (ctx: InstallHandlerContext) => Promise<InstallHandlerResult<Hooks>>;
266
276
  export interface OAuthCallbackContext {
267
277
  query: Record<string, string>;
268
- env: Record<string, string>;
269
- workplace: {
270
- id: string;
271
- subdomain: string;
272
- };
273
- appInstallationId: string;
274
- app: {
275
- id: string;
276
- versionId: string;
277
- handle: string;
278
- versionHandle: string;
279
- };
280
278
  }
281
279
  export interface OAuthCallbackResult {
282
280
  env?: Record<string, string>;
283
- html?: string;
281
+ appInstallationId?: string;
284
282
  }
285
283
  export type OAuthCallbackHandler = (ctx: OAuthCallbackContext) => Promise<OAuthCallbackResult>;
286
284
  export interface ProvisionHandlerContext {
@@ -312,7 +310,7 @@ export interface ToolCallResponse {
312
310
  output: unknown | null;
313
311
  billing: BillingInfo;
314
312
  meta: ToolResponseMeta;
315
- error?: string;
313
+ error?: ToolError | null;
316
314
  effect?: ToolEffect;
317
315
  }
318
316
  export interface DedicatedServerInstance {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "skedyul",
3
- "version": "0.2.147",
3
+ "version": "0.2.149",
4
4
  "description": "The Skedyul SDK for Node.js",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",