services-as-software 2.0.2 → 2.1.3

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.
@@ -1,5 +1,5 @@
1
1
 
2
2
  
3
- > services-as-software@2.0.1 build /Users/nathanclevenger/projects/primitives.org.ai/packages/services-as-software
3
+ > services-as-software@2.1.3 build /Users/nathanclevenger/projects/primitives.org.ai/packages/services-as-software
4
4
  > tsc
5
5
 
package/CHANGELOG.md CHANGED
@@ -1,5 +1,33 @@
1
1
  # services-as-software
2
2
 
3
+ ## 2.1.3
4
+
5
+ ### Patch Changes
6
+
7
+ - Updated dependencies
8
+ - ai-functions@2.1.3
9
+ - ai-database@2.1.3
10
+ - digital-workers@2.1.3
11
+
12
+ ## 2.1.1
13
+
14
+ ### Patch Changes
15
+
16
+ - Updated dependencies [6beb531]
17
+ - ai-functions@2.1.1
18
+ - ai-database@2.1.1
19
+ - digital-workers@2.1.1
20
+
21
+ ## 2.0.3
22
+
23
+ ### Patch Changes
24
+
25
+ - Updated dependencies
26
+ - rpc.do@0.2.0
27
+ - ai-database@2.0.3
28
+ - ai-functions@2.0.3
29
+ - digital-workers@2.0.3
30
+
3
31
  ## 2.0.2
4
32
 
5
33
  ### Patch Changes
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025 .org.ai
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,68 @@
1
+ /**
2
+ * Example: Using the Service Client
3
+ *
4
+ * This example demonstrates how to connect to a remote service using the Client
5
+ */
6
+ import { Client, Provider, providers } from '../src/index.js';
7
+ // Connect to a service using a direct URL
8
+ const translationClient = Client({
9
+ url: 'https://api.example.com/translation',
10
+ auth: {
11
+ type: 'api-key',
12
+ credentials: {
13
+ apiKey: 'your-api-key-here',
14
+ },
15
+ },
16
+ });
17
+ // Use the client
18
+ async function useDirectClient() {
19
+ const result = await translationClient.do('translate', {
20
+ text: 'Hello, world!',
21
+ to: 'es',
22
+ });
23
+ console.log('Translation:', result);
24
+ // Get a quote
25
+ const quote = await translationClient.quote({ text: 'Large document...', to: 'es' }, 1000);
26
+ console.log('Quote:', quote);
27
+ // Subscribe to a plan
28
+ const subscription = await translationClient.subscribe('pro');
29
+ console.log('Subscription:', subscription);
30
+ }
31
+ // Use a provider to access multiple services
32
+ const awsProvider = providers.aws({
33
+ accessKeyId: 'your-access-key',
34
+ secretAccessKey: 'your-secret-key',
35
+ region: 'us-east-1',
36
+ });
37
+ async function useProvider() {
38
+ // Get the translate service from AWS
39
+ const translate = awsProvider.service('translate');
40
+ const result = await translate.do('translate', {
41
+ text: 'Hello, world!',
42
+ sourceLang: 'en',
43
+ targetLang: 'es',
44
+ });
45
+ console.log('AWS Translation:', result);
46
+ }
47
+ // Create a custom provider
48
+ const customProvider = Provider({
49
+ name: 'My Company',
50
+ baseUrl: 'https://api.mycompany.com',
51
+ auth: {
52
+ type: 'jwt',
53
+ credentials: {
54
+ token: 'your-jwt-token',
55
+ },
56
+ },
57
+ services: ['translation', 'summarization', 'sentiment'],
58
+ });
59
+ async function useCustomProvider() {
60
+ const translation = customProvider.service('translation');
61
+ const result = await translation.do('translate', {
62
+ text: 'Hello',
63
+ to: 'fr',
64
+ });
65
+ console.log('Custom provider result:', result);
66
+ }
67
+ // Export examples
68
+ export { useDirectClient, useProvider, useCustomProvider };
@@ -0,0 +1,214 @@
1
+ /**
2
+ * Example: Translation Service
3
+ *
4
+ * This example demonstrates how to create a translation service with:
5
+ * - Multiple endpoints (translate, detect language)
6
+ * - Per-use pricing
7
+ * - Rate limiting
8
+ * - Usage tracking
9
+ */
10
+ import { Service, Endpoint, POST, GET } from '../src/index.js';
11
+ // Define the translation service
12
+ const translationService = Service({
13
+ name: 'translation-service',
14
+ version: '1.0.0',
15
+ description: 'AI-powered translation service',
16
+ status: 'active',
17
+ // Default pricing for all endpoints
18
+ pricing: {
19
+ model: 'per-use',
20
+ pricePerUnit: 0.01, // $0.01 per character
21
+ currency: 'USD',
22
+ freeTier: {
23
+ units: 10000, // 10,000 free characters per month
24
+ resetInterval: 'monthly',
25
+ },
26
+ },
27
+ // Service endpoints
28
+ endpoints: [
29
+ POST({
30
+ name: 'translate',
31
+ description: 'Translate text between languages',
32
+ path: '/translate',
33
+ input: {
34
+ type: 'object',
35
+ properties: {
36
+ text: { type: 'string', description: 'Text to translate' },
37
+ from: { type: 'string', description: 'Source language code (optional)' },
38
+ to: { type: 'string', description: 'Target language code' },
39
+ },
40
+ required: ['text', 'to'],
41
+ additionalProperties: false,
42
+ },
43
+ output: {
44
+ type: 'object',
45
+ properties: {
46
+ translatedText: { type: 'string', description: 'Translated text' },
47
+ sourceLanguage: { type: 'string', description: 'Detected or specified source language' },
48
+ confidence: { type: 'number', description: 'Translation confidence (0-1)' },
49
+ },
50
+ required: ['translatedText', 'sourceLanguage', 'confidence'],
51
+ additionalProperties: false,
52
+ },
53
+ handler: async (input, context) => {
54
+ // Track usage for billing
55
+ if (context?.usage && context.customerId) {
56
+ await context.usage.track({
57
+ customerId: context.customerId,
58
+ resource: 'translate',
59
+ quantity: input.text.length, // Charge per character
60
+ timestamp: new Date(),
61
+ });
62
+ }
63
+ // In a real implementation, this would call an AI translation API
64
+ return {
65
+ translatedText: `[Translated to ${input.to}] ${input.text}`,
66
+ sourceLanguage: input.from || 'auto-detected',
67
+ confidence: 0.95,
68
+ };
69
+ },
70
+ rateLimit: {
71
+ requests: 100,
72
+ window: 60000, // 100 requests per minute
73
+ },
74
+ }),
75
+ GET({
76
+ name: 'detect',
77
+ description: 'Detect the language of text',
78
+ path: '/detect',
79
+ input: {
80
+ type: 'object',
81
+ properties: {
82
+ text: { type: 'string', description: 'Text to analyze' },
83
+ },
84
+ required: ['text'],
85
+ additionalProperties: false,
86
+ },
87
+ output: {
88
+ type: 'object',
89
+ properties: {
90
+ language: { type: 'string', description: 'Detected language code' },
91
+ confidence: { type: 'number', description: 'Detection confidence (0-1)' },
92
+ },
93
+ required: ['language', 'confidence'],
94
+ additionalProperties: false,
95
+ },
96
+ handler: async (input) => {
97
+ // Language detection logic
98
+ return {
99
+ language: 'en',
100
+ confidence: 0.98,
101
+ };
102
+ },
103
+ pricing: {
104
+ model: 'per-use',
105
+ pricePerUnit: 0.001, // $0.001 per detection (cheaper than translation)
106
+ currency: 'USD',
107
+ },
108
+ }),
109
+ Endpoint({
110
+ name: 'languages',
111
+ description: 'Get supported languages',
112
+ method: 'GET',
113
+ path: '/languages',
114
+ handler: async () => {
115
+ return {
116
+ languages: [
117
+ { code: 'en', name: 'English' },
118
+ { code: 'es', name: 'Spanish' },
119
+ { code: 'fr', name: 'French' },
120
+ { code: 'de', name: 'German' },
121
+ { code: 'ja', name: 'Japanese' },
122
+ { code: 'zh', name: 'Chinese' },
123
+ ],
124
+ };
125
+ },
126
+ requiresAuth: false, // Public endpoint
127
+ }),
128
+ ],
129
+ // Subscription plans
130
+ plans: [
131
+ {
132
+ id: 'starter',
133
+ name: 'Starter Plan',
134
+ description: 'For individual developers',
135
+ pricing: {
136
+ model: 'subscription',
137
+ basePrice: 9.99,
138
+ currency: 'USD',
139
+ interval: 'monthly',
140
+ },
141
+ entitlements: ['api-access', 'basic-support'],
142
+ features: [
143
+ '100,000 characters/month',
144
+ 'Email support',
145
+ 'Basic languages',
146
+ ],
147
+ limits: {
148
+ characters: 100000,
149
+ },
150
+ },
151
+ {
152
+ id: 'pro',
153
+ name: 'Pro Plan',
154
+ description: 'For growing businesses',
155
+ pricing: {
156
+ model: 'subscription',
157
+ basePrice: 49.99,
158
+ currency: 'USD',
159
+ interval: 'monthly',
160
+ },
161
+ entitlements: ['api-access', 'priority-support', 'advanced-features'],
162
+ features: [
163
+ '1,000,000 characters/month',
164
+ 'Priority support',
165
+ 'All languages',
166
+ 'Custom models',
167
+ ],
168
+ limits: {
169
+ characters: 1000000,
170
+ },
171
+ },
172
+ ],
173
+ // KPIs
174
+ kpis: [
175
+ {
176
+ id: 'translations-per-day',
177
+ name: 'Translations per Day',
178
+ description: 'Number of translation requests processed daily',
179
+ calculate: async () => {
180
+ // In a real implementation, query the database
181
+ return 1247;
182
+ },
183
+ target: 1000,
184
+ unit: 'requests',
185
+ },
186
+ {
187
+ id: 'average-confidence',
188
+ name: 'Average Translation Confidence',
189
+ description: 'Average confidence score across all translations',
190
+ calculate: async () => {
191
+ return 0.94;
192
+ },
193
+ target: 0.95,
194
+ unit: 'score',
195
+ },
196
+ ],
197
+ });
198
+ // Example usage
199
+ async function example() {
200
+ // Call the translate endpoint
201
+ const result = await translationService.call('translate', {
202
+ text: 'Hello, world!',
203
+ to: 'es',
204
+ });
205
+ console.log('Translation result:', result);
206
+ // Get KPIs
207
+ const kpis = await translationService.kpis();
208
+ console.log('KPIs:', kpis);
209
+ // Subscribe to a plan
210
+ const subscription = await translationService.subscribe('pro');
211
+ console.log('Subscription:', subscription);
212
+ }
213
+ // Export the service
214
+ export { translationService };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "services-as-software",
3
- "version": "2.0.2",
3
+ "version": "2.1.3",
4
4
  "description": "Primitives for building AI-powered services that operate as software",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -11,19 +11,10 @@
11
11
  "types": "./dist/index.d.ts"
12
12
  }
13
13
  },
14
- "scripts": {
15
- "build": "tsc",
16
- "dev": "tsc --watch",
17
- "test": "vitest",
18
- "typecheck": "tsc --noEmit",
19
- "lint": "eslint .",
20
- "clean": "rm -rf dist"
21
- },
22
14
  "dependencies": {
23
- "ai-database": "2.0.2",
24
- "ai-functions": "2.0.2",
25
- "digital-workers": "2.0.2",
26
- "rpc.do": "^0.1.0"
15
+ "ai-database": "2.1.3",
16
+ "ai-functions": "2.1.3",
17
+ "digital-workers": "2.1.3"
27
18
  },
28
19
  "keywords": [
29
20
  "services",
@@ -31,5 +22,13 @@
31
22
  "ai",
32
23
  "primitives"
33
24
  ],
34
- "license": "MIT"
35
- }
25
+ "license": "MIT",
26
+ "scripts": {
27
+ "build": "tsc",
28
+ "dev": "tsc --watch",
29
+ "test": "vitest",
30
+ "typecheck": "tsc --noEmit",
31
+ "lint": "eslint .",
32
+ "clean": "rm -rf dist"
33
+ }
34
+ }
package/src/client.js ADDED
@@ -0,0 +1,102 @@
1
+ /**
2
+ * Service client for connecting to remote services
3
+ */
4
+ /**
5
+ * Create a client to connect to a remote service
6
+ *
7
+ * @example
8
+ * ```ts
9
+ * const client = Client({
10
+ * url: 'https://api.example.com/translation',
11
+ * auth: {
12
+ * type: 'api-key',
13
+ * credentials: { apiKey: process.env.API_KEY },
14
+ * },
15
+ * })
16
+ *
17
+ * const result = await client.do('translate', {
18
+ * text: 'Hello world',
19
+ * to: 'es',
20
+ * })
21
+ * ```
22
+ */
23
+ export function Client(config) {
24
+ const baseUrl = config.url || '';
25
+ const headers = {
26
+ 'Content-Type': 'application/json',
27
+ ...config.headers,
28
+ };
29
+ // Add auth headers
30
+ if (config.auth) {
31
+ switch (config.auth.type) {
32
+ case 'api-key':
33
+ headers['Authorization'] = `Bearer ${config.auth.credentials.apiKey || config.auth.credentials.token}`;
34
+ break;
35
+ case 'basic':
36
+ const basicAuth = Buffer.from(`${config.auth.credentials.username}:${config.auth.credentials.password}`).toString('base64');
37
+ headers['Authorization'] = `Basic ${basicAuth}`;
38
+ break;
39
+ case 'jwt':
40
+ headers['Authorization'] = `Bearer ${config.auth.credentials.token}`;
41
+ break;
42
+ // OAuth would require more complex flow
43
+ }
44
+ }
45
+ /**
46
+ * Make an HTTP request to the service
47
+ */
48
+ async function request(endpoint, body) {
49
+ const url = `${baseUrl}/${endpoint.replace(/^\//, '')}`;
50
+ const response = await fetch(url, {
51
+ method: 'POST',
52
+ headers,
53
+ body: body ? JSON.stringify(body) : undefined,
54
+ signal: config.timeout ? AbortSignal.timeout(config.timeout) : undefined,
55
+ });
56
+ if (!response.ok) {
57
+ throw new Error(`Service request failed: ${response.status} ${response.statusText}`);
58
+ }
59
+ return response.json();
60
+ }
61
+ return {
62
+ async ask(question, context) {
63
+ const result = await request('ask', { question, context });
64
+ return result.answer;
65
+ },
66
+ async deliver(orderId, results) {
67
+ await request('deliver', { orderId, results });
68
+ },
69
+ async do(action, input) {
70
+ return request('do', { action, input });
71
+ },
72
+ async generate(prompt, options) {
73
+ return request('generate', { prompt, options });
74
+ },
75
+ async is(value, type) {
76
+ const result = await request('is', { value, type });
77
+ return result.result;
78
+ },
79
+ async notify(notification) {
80
+ await request('notify', notification);
81
+ },
82
+ async order(product, quantity) {
83
+ return request('order', { product, quantity });
84
+ },
85
+ async quote(product, quantity) {
86
+ return request('quote', { product, quantity });
87
+ },
88
+ async subscribe(planId) {
89
+ return request('subscribe', { planId });
90
+ },
91
+ async entitlements() {
92
+ const result = await request('entitlements', {});
93
+ return result.entitlements;
94
+ },
95
+ async kpis() {
96
+ return request('kpis', {});
97
+ },
98
+ async okrs() {
99
+ return request('okrs', {});
100
+ },
101
+ };
102
+ }
@@ -0,0 +1,95 @@
1
+ /**
2
+ * Endpoint helper for defining service endpoints
3
+ */
4
+ /**
5
+ * Create an endpoint definition
6
+ *
7
+ * @example
8
+ * ```ts
9
+ * const translateEndpoint = Endpoint({
10
+ * name: 'translate',
11
+ * description: 'Translate text between languages',
12
+ * method: 'POST',
13
+ * path: '/translate',
14
+ * input: {
15
+ * type: 'object',
16
+ * properties: {
17
+ * text: { type: 'string', description: 'Text to translate' },
18
+ * from: { type: 'string', description: 'Source language code' },
19
+ * to: { type: 'string', description: 'Target language code' },
20
+ * },
21
+ * required: ['text', 'to'],
22
+ * additionalProperties: false,
23
+ * },
24
+ * output: {
25
+ * type: 'object',
26
+ * properties: {
27
+ * translatedText: { type: 'string' },
28
+ * confidence: { type: 'number' },
29
+ * },
30
+ * required: ['translatedText'],
31
+ * additionalProperties: false,
32
+ * },
33
+ * handler: async (input, context) => {
34
+ * // Translation logic here
35
+ * return {
36
+ * translatedText: `Translated: ${input.text}`,
37
+ * confidence: 0.95,
38
+ * }
39
+ * },
40
+ * pricing: {
41
+ * model: 'per-use',
42
+ * pricePerUnit: 0.01,
43
+ * currency: 'USD',
44
+ * },
45
+ * rateLimit: {
46
+ * requests: 100,
47
+ * window: 60000, // 1 minute
48
+ * },
49
+ * })
50
+ * ```
51
+ */
52
+ export function Endpoint(config) {
53
+ return {
54
+ name: config.name,
55
+ description: config.description,
56
+ method: config.method || 'POST',
57
+ path: config.path || `/${config.name}`,
58
+ input: config.input,
59
+ output: config.output,
60
+ handler: config.handler,
61
+ pricing: config.pricing,
62
+ rateLimit: config.rateLimit,
63
+ requiresAuth: config.requiresAuth !== false, // Default to true
64
+ };
65
+ }
66
+ /**
67
+ * Create a GET endpoint
68
+ */
69
+ export function GET(config) {
70
+ return Endpoint({ ...config, method: 'GET' });
71
+ }
72
+ /**
73
+ * Create a POST endpoint
74
+ */
75
+ export function POST(config) {
76
+ return Endpoint({ ...config, method: 'POST' });
77
+ }
78
+ /**
79
+ * Create a PUT endpoint
80
+ */
81
+ export function PUT(config) {
82
+ return Endpoint({ ...config, method: 'PUT' });
83
+ }
84
+ /**
85
+ * Create a DELETE endpoint
86
+ */
87
+ export function DELETE(config) {
88
+ return Endpoint({ ...config, method: 'DELETE' });
89
+ }
90
+ /**
91
+ * Create a PATCH endpoint
92
+ */
93
+ export function PATCH(config) {
94
+ return Endpoint({ ...config, method: 'PATCH' });
95
+ }