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 +1 -1
- package/dist/errors.d.ts +23 -0
- package/dist/errors.js +28 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +2 -1
- package/dist/server.js +75 -104
- package/dist/types.d.ts +12 -14
- package/package.json +1 -1
package/dist/.build-stamp
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
|
|
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:
|
|
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
|
|
882
|
-
if (pathname === '/oauth_callback' && req.method === '
|
|
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
|
-
|
|
905
|
+
sendJSON(res, 404, { error: 'OAuth callback handler not configured' });
|
|
885
906
|
return;
|
|
886
907
|
}
|
|
887
|
-
|
|
888
|
-
|
|
889
|
-
|
|
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
|
-
|
|
905
|
-
|
|
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
|
|
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
|
-
|
|
933
|
-
|
|
934
|
-
|
|
935
|
-
|
|
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
|
-
|
|
946
|
-
|
|
947
|
-
|
|
948
|
-
|
|
949
|
-
|
|
950
|
-
|
|
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 === '
|
|
1575
|
+
if (path === '/oauth_callback' && method === 'POST') {
|
|
1577
1576
|
if (!config.hooks?.oauth_callback) {
|
|
1578
|
-
return createResponse(404, { error: 'OAuth callback handler not configured' },
|
|
1577
|
+
return createResponse(404, { error: 'OAuth callback handler not configured' }, headers);
|
|
1579
1578
|
}
|
|
1580
|
-
|
|
1581
|
-
|
|
1582
|
-
|
|
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
|
-
|
|
1589
|
-
|
|
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 (!
|
|
1599
|
-
return createResponse(400, { error:
|
|
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
|
|
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
|
-
|
|
1626
|
-
|
|
1627
|
-
|
|
1628
|
-
|
|
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
|
-
|
|
1639
|
-
|
|
1640
|
-
|
|
1641
|
-
|
|
1642
|
-
|
|
1643
|
-
|
|
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
|
-
|
|
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?:
|
|
313
|
+
error?: ToolError | null;
|
|
316
314
|
effect?: ToolEffect;
|
|
317
315
|
}
|
|
318
316
|
export interface DedicatedServerInstance {
|