skedyul 0.1.12 → 0.1.14

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/README.md CHANGED
@@ -60,27 +60,76 @@ For integration-specific RPCs that belong to your platform rather than a tool, p
60
60
 
61
61
  The MCP server exposes `POST /core` (with `{ method, params }`) and `POST /core/webhook` for these operations. They never appear under `tools/list` unlesstaken explicit MCP tooling—they are separate transport-level handlers and do not count against tool request limits. Make sure your service returns the structured channel/message data defined in `src/core/types.ts` so the responses stay consistent, and guard `/core`/`/core/webhook` with your platform’s preferred authentication if you surface them externally.
62
62
 
63
- ## Higher-level helpers
63
+ ## Core API Client
64
64
 
65
- While `server.create` hosts the MCP surface, `skedyul.workplace` and `skedyul.communicationChannel` expose dedicated helpers that talk to `/core` for the same workplace and channel metadata. Each helper returns the typed objects defined in `src/core/types.ts` so integrations can look up a channel/workplace pair before acting on an incoming webhook without manually composing the RPC payload or dealing with authentication.
65
+ The SDK includes a client for the Skedyul Core API that enables lookups across workplaces. This is especially useful in webhook handlers where you need to identify which workspace a request belongs to.
66
66
 
67
- The helpers currently provide:
67
+ ### Configuration
68
68
 
69
- - `workplace.list(filter?: Record<string, unknown>)`
70
- - `workplace.get(id: string)`
71
- - `communicationChannel.list(filter?: Record<string, unknown>)`
72
- - `communicationChannel.get(id: string)`
69
+ Configure the client using environment variables or programmatically:
73
70
 
74
- Example:
71
+ **Environment Variables:**
72
+
73
+ ```bash
74
+ # Base URL for the Skedyul Core API
75
+ SKEDYUL_API_URL=https://app.skedyul.com/api
76
+
77
+ # Your API token (App API or Workplace API)
78
+ SKEDYUL_API_TOKEN=sk_app_xxxxx
79
+ ```
80
+
81
+ **Programmatic Configuration:**
82
+
83
+ ```ts
84
+ import { configure } from 'skedyul'
85
+
86
+ configure({
87
+ baseUrl: 'https://app.skedyul.com/api',
88
+ apiToken: 'sk_app_xxxxx',
89
+ })
90
+ ```
91
+
92
+ ### Token Types
93
+
94
+ - **App API Token (`sk_app_*`)**: Grants access to all workplaces where your app is installed. Use this for webhooks where you need to look up resources across workplaces.
95
+ - **Workplace API Token (`sk_wkp_*`)**: Scoped to a single workplace. Use this when you know the target workspace (e.g., MCP tools).
96
+
97
+ ### Available Methods
98
+
99
+ - `workplace.list({ filter?, limit? })` - List workplaces
100
+ - `workplace.get(id)` - Get a single workplace
101
+ - `communicationChannel.list({ filter?, limit? })` - List communication channels
102
+ - `communicationChannel.get(id)` - Get a single channel
103
+
104
+ ### Example: Webhook Handler
75
105
 
76
106
  ```ts
77
- import { communicationChannel, workplace } from 'skedyul'
107
+ import { communicationChannel, configure } from 'skedyul'
78
108
 
79
- const [channel] = await communicationChannel.list({
80
- filter: { identifierValue: '+15551234567' },
109
+ // Configure once at startup (or use env vars)
110
+ configure({
111
+ baseUrl: process.env.SKEDYUL_API_URL,
112
+ apiToken: process.env.SKEDYUL_API_TOKEN,
81
113
  })
82
114
 
83
- const owner = await workplace.get(channel.workplaceId)
115
+ // In your webhook handler
116
+ async function handleIncomingMessage(phoneNumber: string) {
117
+ // Find the channel across all workplaces where your app is installed
118
+ const channels = await communicationChannel.list({
119
+ filter: { identifierValue: phoneNumber },
120
+ limit: 1,
121
+ })
122
+
123
+ if (channels.length === 0) {
124
+ throw new Error('No channel found for this phone number')
125
+ }
126
+
127
+ const channel = channels[0]
128
+ console.log(`Found channel in workplace: ${channel.workplaceId}`)
129
+
130
+ // Now you can process the message in the correct workspace context
131
+ return channel
132
+ }
84
133
  ```
85
134
 
86
135
  Use these helpers for internal wiring—like pulling the correct workplace for a webhook—without touching the MCP tooling surface directly.
@@ -163,7 +163,7 @@ async function validateCommand(args) {
163
163
  }
164
164
  }
165
165
  // Check if workflows directory exists
166
- const workflowsPath = config.workflows || './workflows';
166
+ const workflowsPath = config.workflowsPath || './workflows';
167
167
  const absoluteWorkflowsPath = path.resolve(path.dirname(configPath), workflowsPath);
168
168
  if (!fs.existsSync(absoluteWorkflowsPath)) {
169
169
  warnings.push(`Workflows directory not found: ${workflowsPath}`);
@@ -178,7 +178,7 @@ async function validateCommand(args) {
178
178
  version: config.version,
179
179
  computeLayer: config.computeLayer,
180
180
  tools: config.tools,
181
- workflows: config.workflows,
181
+ workflowsPath: config.workflowsPath,
182
182
  globalEnvKeys: envKeys.global,
183
183
  installEnvKeys: envKeys.install,
184
184
  requiredInstallEnvKeys: requiredInstallKeys,
package/dist/config.d.ts CHANGED
@@ -35,6 +35,98 @@ export interface InstallConfig {
35
35
  */
36
36
  appModels?: AppModelDefinition[];
37
37
  }
38
+ export interface AppFieldVisibility {
39
+ /** Show in data/detail view */
40
+ data?: boolean;
41
+ /** Show in list view */
42
+ list?: boolean;
43
+ /** Show in filters */
44
+ filters?: boolean;
45
+ }
46
+ export interface AppFieldDefinition {
47
+ /** Human-readable label */
48
+ label: string;
49
+ /** Field handle/key */
50
+ fieldHandle: string;
51
+ /** Entity this field belongs to (e.g., 'contact') */
52
+ entityHandle: string;
53
+ /** Metafield definition handle */
54
+ definitionHandle: string;
55
+ /** Whether this field is required */
56
+ required?: boolean;
57
+ /** Whether this is a system field */
58
+ system?: boolean;
59
+ /** Whether values must be unique */
60
+ unique?: boolean;
61
+ /** Default value */
62
+ defaultValue?: {
63
+ value: unknown;
64
+ };
65
+ /** Visibility settings */
66
+ visibility?: AppFieldVisibility;
67
+ }
68
+ export interface ChannelToolBindings {
69
+ /** Tool name for sending messages on this channel */
70
+ send_message: string;
71
+ }
72
+ export type ChannelIdentifierType = 'DEDICATED_PHONE' | 'TEXT' | 'EMAIL';
73
+ export interface ChannelIdentifierValue {
74
+ /** Type of identifier */
75
+ type: ChannelIdentifierType;
76
+ /** Metafield definition handle for the identifier */
77
+ definitionHandle: string;
78
+ }
79
+ export interface CommunicationChannelDefinition {
80
+ /** Unique handle for this channel type (e.g., 'sms', 'email') */
81
+ handle: string;
82
+ /** Human-readable name */
83
+ name: string;
84
+ /** Icon for UI (lucide icon name) */
85
+ icon?: string;
86
+ /** Tool bindings for this channel */
87
+ tools: ChannelToolBindings;
88
+ /** How the channel identifier is configured */
89
+ identifierValue: ChannelIdentifierValue;
90
+ /** Fields to add to contacts when using this channel */
91
+ appFields?: AppFieldDefinition[];
92
+ /** Additional settings UI */
93
+ settings?: unknown[];
94
+ }
95
+ export interface WorkflowActionInput {
96
+ /** Input key */
97
+ key: string;
98
+ /** Human-readable label */
99
+ label: string;
100
+ /** Reference to a field */
101
+ fieldRef?: {
102
+ fieldHandle: string;
103
+ entityHandle: string;
104
+ };
105
+ /** Template string for the input */
106
+ template?: string;
107
+ }
108
+ export interface WorkflowAction {
109
+ /** Human-readable label */
110
+ label: string;
111
+ /** Action handle/key */
112
+ handle: string;
113
+ /** Whether this action supports batch execution */
114
+ batch?: boolean;
115
+ /** Entity this action operates on */
116
+ entityHandle?: string;
117
+ /** Input definitions for this action */
118
+ inputs?: WorkflowActionInput[];
119
+ }
120
+ export interface WorkflowDefinition {
121
+ /** Human-readable label */
122
+ label: string;
123
+ /** Workflow handle/key */
124
+ handle: string;
125
+ /** Which channel handle this workflow is associated with (optional) */
126
+ channelHandle?: string;
127
+ /** Actions in this workflow */
128
+ actions: WorkflowAction[];
129
+ }
38
130
  export type ComputeLayerType = 'serverless' | 'dedicated';
39
131
  export interface SkedyulConfig {
40
132
  /** App name */
@@ -48,7 +140,7 @@ export interface SkedyulConfig {
48
140
  /** Path to the tool registry file (default: './src/registry.ts') */
49
141
  tools?: string;
50
142
  /** Path to the workflows directory (default: './workflows') */
51
- workflows?: string;
143
+ workflowsPath?: string;
52
144
  /**
53
145
  * Global/version-level environment variables.
54
146
  * These are baked into the container and are the same for all installations.
@@ -60,6 +152,16 @@ export interface SkedyulConfig {
60
152
  * Defines what users need to configure when installing the app.
61
153
  */
62
154
  install?: InstallConfig;
155
+ /**
156
+ * Communication channels this app provides.
157
+ * Defines how the app can send/receive messages.
158
+ */
159
+ communicationChannels?: CommunicationChannelDefinition[];
160
+ /**
161
+ * Workflows this app provides.
162
+ * Can reference channels via channelHandle.
163
+ */
164
+ workflows?: WorkflowDefinition[];
63
165
  }
64
166
  /**
65
167
  * Define a Skedyul app configuration with full type safety.
package/dist/config.js CHANGED
@@ -206,6 +206,42 @@ function validateConfig(config) {
206
206
  }
207
207
  }
208
208
  }
209
+ // Validate communicationChannels
210
+ if (config.communicationChannels) {
211
+ for (let i = 0; i < config.communicationChannels.length; i++) {
212
+ const channel = config.communicationChannels[i];
213
+ if (!channel.handle) {
214
+ errors.push(`communicationChannels[${i}]: Missing required field 'handle'`);
215
+ }
216
+ if (!channel.name) {
217
+ errors.push(`communicationChannels[${i}]: Missing required field 'name'`);
218
+ }
219
+ if (!channel.tools?.send_message) {
220
+ errors.push(`communicationChannels[${i}]: Missing required field 'tools.send_message'`);
221
+ }
222
+ if (!channel.identifierValue?.type) {
223
+ errors.push(`communicationChannels[${i}]: Missing required field 'identifierValue.type'`);
224
+ }
225
+ if (!channel.identifierValue?.definitionHandle) {
226
+ errors.push(`communicationChannels[${i}]: Missing required field 'identifierValue.definitionHandle'`);
227
+ }
228
+ }
229
+ }
230
+ // Validate workflows
231
+ if (config.workflows) {
232
+ for (let i = 0; i < config.workflows.length; i++) {
233
+ const workflow = config.workflows[i];
234
+ if (!workflow.handle) {
235
+ errors.push(`workflows[${i}]: Missing required field 'handle'`);
236
+ }
237
+ if (!workflow.label) {
238
+ errors.push(`workflows[${i}]: Missing required field 'label'`);
239
+ }
240
+ if (!workflow.actions || workflow.actions.length === 0) {
241
+ errors.push(`workflows[${i}]: Must have at least one action`);
242
+ }
243
+ }
244
+ }
209
245
  return { valid: errors.length === 0, errors };
210
246
  }
211
247
  /**
package/dist/index.d.ts CHANGED
@@ -2,4 +2,4 @@ export * from './types';
2
2
  export { server } from './server';
3
3
  export { workplace, communicationChannel } from './core/client';
4
4
  export { defineConfig, loadConfig, validateConfig, getRequiredInstallEnvKeys, getAllEnvKeys, CONFIG_FILE_NAMES, } from './config';
5
- export type { SkedyulConfig, EnvVariableDefinition, EnvSchema, EnvVisibility, InstallConfig, AppModelDefinition, ComputeLayerType, } from './config';
5
+ export type { SkedyulConfig, EnvVariableDefinition, EnvSchema, EnvVisibility, InstallConfig, AppModelDefinition, ComputeLayerType, AppFieldVisibility, AppFieldDefinition, ChannelToolBindings, ChannelIdentifierType, ChannelIdentifierValue, CommunicationChannelDefinition, WorkflowActionInput, WorkflowAction, WorkflowDefinition, } from './config';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "skedyul",
3
- "version": "0.1.12",
3
+ "version": "0.1.14",
4
4
  "description": "The Skedyul SDK for Node.js",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",