@divizend/scratch-core 1.0.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/basic/demo.ts +11 -0
- package/basic/index.ts +490 -0
- package/core/Auth.ts +63 -0
- package/core/Currency.ts +16 -0
- package/core/Env.ts +186 -0
- package/core/Fragment.ts +43 -0
- package/core/FragmentServingMode.ts +37 -0
- package/core/JsonSchemaValidator.ts +173 -0
- package/core/ProjectRoot.ts +76 -0
- package/core/Scratch.ts +44 -0
- package/core/URI.ts +203 -0
- package/core/Universe.ts +406 -0
- package/core/index.ts +27 -0
- package/gsuite/core/GSuite.ts +237 -0
- package/gsuite/core/GSuiteAdmin.ts +81 -0
- package/gsuite/core/GSuiteOrgConfig.ts +47 -0
- package/gsuite/core/GSuiteUser.ts +115 -0
- package/gsuite/core/index.ts +21 -0
- package/gsuite/documents/Document.ts +173 -0
- package/gsuite/documents/Documents.ts +52 -0
- package/gsuite/documents/index.ts +19 -0
- package/gsuite/drive/Drive.ts +118 -0
- package/gsuite/drive/DriveFile.ts +147 -0
- package/gsuite/drive/index.ts +19 -0
- package/gsuite/gmail/Gmail.ts +430 -0
- package/gsuite/gmail/GmailLabel.ts +55 -0
- package/gsuite/gmail/GmailMessage.ts +428 -0
- package/gsuite/gmail/GmailMessagePart.ts +298 -0
- package/gsuite/gmail/GmailThread.ts +97 -0
- package/gsuite/gmail/index.ts +5 -0
- package/gsuite/gmail/utils.ts +184 -0
- package/gsuite/index.ts +28 -0
- package/gsuite/spreadsheets/CellValue.ts +71 -0
- package/gsuite/spreadsheets/Sheet.ts +128 -0
- package/gsuite/spreadsheets/SheetValues.ts +12 -0
- package/gsuite/spreadsheets/Spreadsheet.ts +76 -0
- package/gsuite/spreadsheets/Spreadsheets.ts +52 -0
- package/gsuite/spreadsheets/index.ts +25 -0
- package/gsuite/spreadsheets/utils.ts +52 -0
- package/gsuite/utils.ts +104 -0
- package/http-server/HttpServer.ts +110 -0
- package/http-server/NativeHttpServer.ts +1084 -0
- package/http-server/index.ts +3 -0
- package/http-server/middlewares/01-cors.ts +33 -0
- package/http-server/middlewares/02-static.ts +67 -0
- package/http-server/middlewares/03-request-logger.ts +159 -0
- package/http-server/middlewares/04-body-parser.ts +54 -0
- package/http-server/middlewares/05-no-cache.ts +23 -0
- package/http-server/middlewares/06-response-handler.ts +39 -0
- package/http-server/middlewares/handler-wrapper.ts +250 -0
- package/http-server/middlewares/index.ts +37 -0
- package/http-server/middlewares/types.ts +27 -0
- package/index.ts +24 -0
- package/package.json +37 -0
- package/queue/EmailQueue.ts +228 -0
- package/queue/RateLimiter.ts +54 -0
- package/queue/index.ts +2 -0
- package/resend/Resend.ts +190 -0
- package/resend/index.ts +11 -0
- package/s2/S2.ts +335 -0
- package/s2/index.ts +11 -0
package/core/URI.ts
ADDED
|
@@ -0,0 +1,203 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* URI System - Unified Resource Identification
|
|
3
|
+
*
|
|
4
|
+
* The URI system provides a standardized way to identify and reference
|
|
5
|
+
* resources within the AI Executive system. It supports multiple protocols
|
|
6
|
+
* and resource types, with Gmail integration as the primary use case.
|
|
7
|
+
*
|
|
8
|
+
* URI Format: protocol://domain/resource-type/id[/sub-resource]
|
|
9
|
+
* Examples:
|
|
10
|
+
* - gmail://user@domain.com/message/messageId
|
|
11
|
+
* - gmail://user@domain.com/message/messageId/part/partId
|
|
12
|
+
*
|
|
13
|
+
* @module URI
|
|
14
|
+
* @version 1.0.0
|
|
15
|
+
* @author Divizend GmbH
|
|
16
|
+
*/
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Supported URI protocols for resource identification
|
|
20
|
+
*/
|
|
21
|
+
export enum URIProtocol {
|
|
22
|
+
/** Gmail and Google Workspace resources */
|
|
23
|
+
Gmail = "gmail",
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Types of resources that can be identified by URIs
|
|
28
|
+
*/
|
|
29
|
+
export enum URIType {
|
|
30
|
+
/** Individual Gmail message */
|
|
31
|
+
GmailMessage = "GmailMessage",
|
|
32
|
+
/** Specific part of a Gmail message (attachment, body part) */
|
|
33
|
+
GmailMessagePart = "GmailMessagePart",
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Base URI class for resource identification and parsing
|
|
38
|
+
*
|
|
39
|
+
* Provides a unified interface for accessing URI components and
|
|
40
|
+
* validating URI structure across different protocols and resource types.
|
|
41
|
+
*/
|
|
42
|
+
export class URI {
|
|
43
|
+
/**
|
|
44
|
+
* Creates a new URI instance
|
|
45
|
+
*
|
|
46
|
+
* @param uri - The complete URI string
|
|
47
|
+
* @param props - Parsed URI properties and components
|
|
48
|
+
*/
|
|
49
|
+
constructor(
|
|
50
|
+
public readonly uri: string,
|
|
51
|
+
public readonly props: { [key: string]: string }
|
|
52
|
+
) {}
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* Parses a URI string and creates a URI instance
|
|
56
|
+
*
|
|
57
|
+
* This factory method validates the URI format and extracts
|
|
58
|
+
* relevant components based on the protocol and resource type.
|
|
59
|
+
*
|
|
60
|
+
* @param uri - The URI string to parse
|
|
61
|
+
* @returns URI instance with parsed properties
|
|
62
|
+
* @throws Error if the URI format is invalid or unsupported
|
|
63
|
+
*/
|
|
64
|
+
static fromString(uri: string): URI {
|
|
65
|
+
const props: { [key: string]: string } = {};
|
|
66
|
+
|
|
67
|
+
if (uri.startsWith("gmail://")) {
|
|
68
|
+
const parts = uri.split("/");
|
|
69
|
+
if (parts.length === 5 && parts[3] === "message") {
|
|
70
|
+
// gmail://email/message/messageId
|
|
71
|
+
props["protocol"] = URIProtocol.Gmail;
|
|
72
|
+
props["type"] = URIType.GmailMessage;
|
|
73
|
+
props["email"] = parts[2]!;
|
|
74
|
+
props["messageId"] = parts[4]!;
|
|
75
|
+
} else if (
|
|
76
|
+
parts.length === 7 &&
|
|
77
|
+
parts[3] === "message" &&
|
|
78
|
+
parts[5] === "part"
|
|
79
|
+
) {
|
|
80
|
+
// gmail://email/message/messageId/part/partId
|
|
81
|
+
props["protocol"] = URIProtocol.Gmail;
|
|
82
|
+
props["type"] = URIType.GmailMessagePart;
|
|
83
|
+
props["email"] = parts[2]!;
|
|
84
|
+
props["messageId"] = parts[4]!;
|
|
85
|
+
props["partId"] = parts[6]!;
|
|
86
|
+
} else {
|
|
87
|
+
throw new Error(`Invalid Gmail URI format: ${uri}`);
|
|
88
|
+
}
|
|
89
|
+
} else {
|
|
90
|
+
throw new Error("Invalid URI");
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
return new URI(uri, props);
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
/**
|
|
97
|
+
* Retrieves a URI property by key
|
|
98
|
+
*
|
|
99
|
+
* @param key - The property key to retrieve
|
|
100
|
+
* @returns The property value
|
|
101
|
+
* @throws Error if the key is not found
|
|
102
|
+
*/
|
|
103
|
+
get(key: string) {
|
|
104
|
+
if (!this.props[key]) {
|
|
105
|
+
throw new Error(`Key ${key} not found in URI ${this.uri}`);
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
return this.props[key];
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
/** The protocol used by this URI (e.g., "gmail") */
|
|
112
|
+
get protocol() {
|
|
113
|
+
return this.get("protocol");
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
/** The type of resource this URI identifies */
|
|
117
|
+
get type() {
|
|
118
|
+
return this.get("type");
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
/**
|
|
123
|
+
* Specialized URI class for Gmail message resources
|
|
124
|
+
*
|
|
125
|
+
* Provides type-safe access to Gmail message-specific URI components
|
|
126
|
+
* and validates that the URI represents a Gmail message.
|
|
127
|
+
*/
|
|
128
|
+
export class GmailMessageURI extends URI {
|
|
129
|
+
/**
|
|
130
|
+
* Creates a GmailMessageURI from a URI string
|
|
131
|
+
*
|
|
132
|
+
* @param uri - The URI string to parse
|
|
133
|
+
* @returns GmailMessageURI instance
|
|
134
|
+
* @throws Error if the URI is not a valid Gmail message URI
|
|
135
|
+
*/
|
|
136
|
+
static override fromString(uri: string): GmailMessageURI {
|
|
137
|
+
return GmailMessageURI.fromURI(super.fromString(uri));
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
/**
|
|
141
|
+
* Creates a GmailMessageURI from an existing URI instance
|
|
142
|
+
*
|
|
143
|
+
* @param uri - The URI instance to convert
|
|
144
|
+
* @returns GmailMessageURI instance
|
|
145
|
+
* @throws Error if the URI is not a Gmail message URI
|
|
146
|
+
*/
|
|
147
|
+
static fromURI(uri: URI) {
|
|
148
|
+
if (uri.type !== URIType.GmailMessage) {
|
|
149
|
+
throw new Error(`Not a Gmail URI: ${uri.uri}`);
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
return new GmailMessageURI(uri.uri, uri.props);
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
/** The email address associated with this Gmail message */
|
|
156
|
+
get email() {
|
|
157
|
+
return this.get("email");
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
/** The unique identifier for this Gmail message */
|
|
161
|
+
get messageId() {
|
|
162
|
+
return this.get("messageId");
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
/**
|
|
167
|
+
* Specialized URI class for Gmail message part resources
|
|
168
|
+
*
|
|
169
|
+
* Extends GmailMessageURI to provide access to message part-specific
|
|
170
|
+
* components like partId for attachments and body parts.
|
|
171
|
+
*/
|
|
172
|
+
export class GmailMessagePartURI extends GmailMessageURI {
|
|
173
|
+
/**
|
|
174
|
+
* Creates a GmailMessagePartURI from a URI string
|
|
175
|
+
*
|
|
176
|
+
* @param uri - The URI string to parse
|
|
177
|
+
* @returns GmailMessagePartURI instance
|
|
178
|
+
* @throws Error if the URI is not a valid Gmail message part URI
|
|
179
|
+
*/
|
|
180
|
+
static override fromString(uri: string): GmailMessagePartURI {
|
|
181
|
+
return GmailMessagePartURI.fromURI(super.fromString(uri));
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
/**
|
|
185
|
+
* Creates a GmailMessagePartURI from an existing URI instance
|
|
186
|
+
*
|
|
187
|
+
* @param uri - The URI instance to convert
|
|
188
|
+
* @returns GmailMessagePartURI instance
|
|
189
|
+
* @throws Error if the URI is not a Gmail message part URI
|
|
190
|
+
*/
|
|
191
|
+
static override fromURI(uri: URI) {
|
|
192
|
+
if (uri.type !== URIType.GmailMessagePart) {
|
|
193
|
+
throw new Error(`Not a Gmail message part URI: ${uri.uri}`);
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
return new GmailMessagePartURI(uri.uri, uri.props);
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
/** The unique identifier for this message part */
|
|
200
|
+
get partId() {
|
|
201
|
+
return this.get("partId");
|
|
202
|
+
}
|
|
203
|
+
}
|
package/core/Universe.ts
ADDED
|
@@ -0,0 +1,406 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Universe - Central System Orchestrator
|
|
3
|
+
*
|
|
4
|
+
* Please put ALL other APIs in here.
|
|
5
|
+
*
|
|
6
|
+
* @class Universe
|
|
7
|
+
* @version 1.0.0
|
|
8
|
+
* @author Divizend GmbH
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
import {
|
|
12
|
+
Fragment,
|
|
13
|
+
GSuite,
|
|
14
|
+
GmailMessage,
|
|
15
|
+
GmailMessagePart,
|
|
16
|
+
URI,
|
|
17
|
+
URIType,
|
|
18
|
+
EmailQueue,
|
|
19
|
+
EmailProfile,
|
|
20
|
+
QueuedEmail,
|
|
21
|
+
Resend,
|
|
22
|
+
JsonSchemaValidator,
|
|
23
|
+
S2,
|
|
24
|
+
} from "..";
|
|
25
|
+
import { Auth } from "./Auth";
|
|
26
|
+
import { HttpServer } from "../http-server";
|
|
27
|
+
import { NativeHttpServer } from "../http-server/NativeHttpServer";
|
|
28
|
+
import { ScratchContext, ScratchBlock } from "./Scratch";
|
|
29
|
+
import { resolve, join, dirname } from "node:path";
|
|
30
|
+
import { fileURLToPath } from "node:url";
|
|
31
|
+
import { envOrDefault } from "./Env";
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Enumeration of available Universe modules
|
|
35
|
+
*/
|
|
36
|
+
export enum UniverseModule {
|
|
37
|
+
/** GSuite integration for Gmail and Google Workspace services */
|
|
38
|
+
GSuite = "gsuite",
|
|
39
|
+
/** Resend email service integration */
|
|
40
|
+
Resend = "resend",
|
|
41
|
+
/** Email queue for managing and sending emails */
|
|
42
|
+
EmailQueue = "emailQueue",
|
|
43
|
+
/** S2 streamstore for durable stream storage */
|
|
44
|
+
S2 = "s2",
|
|
45
|
+
/** Endpoints for Scratch block definitions */
|
|
46
|
+
Endpoints = "endpoints",
|
|
47
|
+
/** Authentication module */
|
|
48
|
+
Auth = "auth",
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
export class Universe {
|
|
52
|
+
/** GSuite integration for Gmail and Google Workspace services */
|
|
53
|
+
public gsuite!: GSuite;
|
|
54
|
+
/** Email queue for managing and sending emails */
|
|
55
|
+
public emailQueue!: EmailQueue;
|
|
56
|
+
/** Resend email service integration */
|
|
57
|
+
public resend?: Resend;
|
|
58
|
+
/** S2 streamstore for durable stream storage */
|
|
59
|
+
public s2!: S2;
|
|
60
|
+
/** JSON Schema validator for request validation */
|
|
61
|
+
public jsonSchemaValidator: JsonSchemaValidator = new JsonSchemaValidator();
|
|
62
|
+
/** Authentication module (enabled by default) */
|
|
63
|
+
public auth: Auth = new Auth();
|
|
64
|
+
/** HTTP server for handling requests */
|
|
65
|
+
public httpServer!: HttpServer;
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* Constructs and initializes a new Universe instance
|
|
69
|
+
*
|
|
70
|
+
* This factory method creates a Universe and initializes all its subsystems
|
|
71
|
+
* in parallel for optimal performance. The method ensures proper dependency
|
|
72
|
+
* injection and initialization order.
|
|
73
|
+
*
|
|
74
|
+
* @returns Promise<Universe> - Fully initialized Universe instance
|
|
75
|
+
* @throws Error if any subsystem fails to initialize
|
|
76
|
+
*/
|
|
77
|
+
static async construct({
|
|
78
|
+
gsuite: initGSuite = true,
|
|
79
|
+
endpointsDirectory,
|
|
80
|
+
}: {
|
|
81
|
+
gsuite?: boolean;
|
|
82
|
+
endpointsDirectory?: string;
|
|
83
|
+
} = {}): Promise<Universe> {
|
|
84
|
+
const universe = new Universe();
|
|
85
|
+
|
|
86
|
+
// Initialize all subsystems in parallel for optimal performance
|
|
87
|
+
const [gsuite] = await Promise.all([
|
|
88
|
+
initGSuite ? GSuite.construct(universe) : Promise.resolve(undefined),
|
|
89
|
+
]);
|
|
90
|
+
|
|
91
|
+
universe.gsuite = gsuite!;
|
|
92
|
+
|
|
93
|
+
// Initialize Resend (fetches domains once during construction)
|
|
94
|
+
try {
|
|
95
|
+
universe.resend = await Resend.construct();
|
|
96
|
+
} catch (error) {
|
|
97
|
+
// Resend not configured - this is optional, so we continue without it
|
|
98
|
+
console.warn(
|
|
99
|
+
"Resend not initialized:",
|
|
100
|
+
error instanceof Error ? error.message : String(error)
|
|
101
|
+
);
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
// Initialize S2 streamstore (required)
|
|
105
|
+
try {
|
|
106
|
+
universe.s2 = S2.construct();
|
|
107
|
+
} catch (error) {
|
|
108
|
+
throw new Error(
|
|
109
|
+
`S2 streamstore is required but failed to initialize: ${
|
|
110
|
+
error instanceof Error ? error.message : String(error)
|
|
111
|
+
}`
|
|
112
|
+
);
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
// Initialize email queue with profiles
|
|
116
|
+
const profiles: EmailProfile[] = [];
|
|
117
|
+
|
|
118
|
+
// Resend profile
|
|
119
|
+
if (universe.resend) {
|
|
120
|
+
// Fetch domains once during profile creation
|
|
121
|
+
const resendDomains = universe.resend.getDomains();
|
|
122
|
+
profiles.push({
|
|
123
|
+
domains: resendDomains, // Use cached domains
|
|
124
|
+
getDomains: () => {
|
|
125
|
+
// Return cached domains synchronously
|
|
126
|
+
return universe.resend!.getDomains();
|
|
127
|
+
},
|
|
128
|
+
sendHandler: async (email: QueuedEmail) => {
|
|
129
|
+
const response = await universe.resend!.sendEmail({
|
|
130
|
+
from: email.from,
|
|
131
|
+
to: email.to,
|
|
132
|
+
subject: email.subject,
|
|
133
|
+
html: `<p>${email.content}</p>`,
|
|
134
|
+
});
|
|
135
|
+
|
|
136
|
+
if (!response.ok) {
|
|
137
|
+
throw new Error(
|
|
138
|
+
`Failed to send email via Resend: ${response.text}`
|
|
139
|
+
);
|
|
140
|
+
}
|
|
141
|
+
},
|
|
142
|
+
});
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
// GSuite profile
|
|
146
|
+
if (universe.gsuite) {
|
|
147
|
+
profiles.push({
|
|
148
|
+
domains: [], // Will be populated dynamically
|
|
149
|
+
getDomains: () => {
|
|
150
|
+
try {
|
|
151
|
+
const orgConfigs = (universe.gsuite as any).orgConfigs;
|
|
152
|
+
if (!orgConfigs || Object.keys(orgConfigs).length === 0) {
|
|
153
|
+
return [];
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
const domains: string[] = [];
|
|
157
|
+
for (const orgConfig of Object.values(orgConfigs) as any[]) {
|
|
158
|
+
for (const domain of orgConfig.domains) {
|
|
159
|
+
if (domain.domainName) {
|
|
160
|
+
domains.push(domain.domainName);
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
return domains;
|
|
165
|
+
} catch (error) {
|
|
166
|
+
console.warn("Failed to fetch GSuite domains:", error);
|
|
167
|
+
return [];
|
|
168
|
+
}
|
|
169
|
+
},
|
|
170
|
+
sendHandler: async (email: QueuedEmail) => {
|
|
171
|
+
if (!universe.gsuite) {
|
|
172
|
+
throw new Error("Google Workspace not connected");
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
// Use the "from" email as the GSuite user
|
|
176
|
+
const gsuiteUser = universe.gsuite.user(email.from);
|
|
177
|
+
const gmail = gsuiteUser.gmail();
|
|
178
|
+
await gmail.send({
|
|
179
|
+
to: email.to,
|
|
180
|
+
subject: email.subject,
|
|
181
|
+
body: email.content,
|
|
182
|
+
});
|
|
183
|
+
},
|
|
184
|
+
});
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
universe.emailQueue = new EmailQueue(profiles);
|
|
188
|
+
|
|
189
|
+
// Initialize HTTP server
|
|
190
|
+
universe.httpServer = new NativeHttpServer(universe);
|
|
191
|
+
|
|
192
|
+
// Get project root for static files
|
|
193
|
+
const { getProjectRoot } = await import("./ProjectRoot");
|
|
194
|
+
const projectRoot = await getProjectRoot();
|
|
195
|
+
|
|
196
|
+
// Register static files
|
|
197
|
+
universe.httpServer.registerStaticFiles(projectRoot);
|
|
198
|
+
|
|
199
|
+
// Load and register endpoints (if directory is provided)
|
|
200
|
+
if (endpointsDirectory) {
|
|
201
|
+
const endpointsDir = resolve(endpointsDirectory);
|
|
202
|
+
await universe.httpServer.loadEndpointsFromDirectory(endpointsDir);
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
// Log all registered endpoints (if endpoints were loaded)
|
|
206
|
+
if (endpointsDirectory) {
|
|
207
|
+
const endpointInfos = await universe.httpServer.getRegisteredEndpoints();
|
|
208
|
+
console.log("\n📋 Registered Scratch Endpoints:");
|
|
209
|
+
console.log("=".repeat(50));
|
|
210
|
+
endpointInfos.forEach((info) => {
|
|
211
|
+
console.log(
|
|
212
|
+
` ${info.method.padEnd(4)} ${info.endpoint.padEnd(30)} ${
|
|
213
|
+
info.blockType
|
|
214
|
+
}${info.auth}`
|
|
215
|
+
);
|
|
216
|
+
});
|
|
217
|
+
console.log("=".repeat(50));
|
|
218
|
+
console.log(`Total: ${endpointInfos.length} endpoints\n`);
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
// Start the server (skip if running in Bun, as Bun handles server lifecycle)
|
|
222
|
+
if (typeof Bun === "undefined") {
|
|
223
|
+
const port = parseInt(envOrDefault(undefined, "PORT", "3000"), 10);
|
|
224
|
+
await universe.httpServer.start(port);
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
return universe;
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
/**
|
|
231
|
+
* Retrieves a fragment by its URI identifier
|
|
232
|
+
*
|
|
233
|
+
* This method serves as the central entry point for accessing any content
|
|
234
|
+
* in the system. It parses the URI, determines the content type, and
|
|
235
|
+
* delegates to the appropriate fragment factory method.
|
|
236
|
+
*
|
|
237
|
+
* Supported URI types:
|
|
238
|
+
* - GmailMessage: Individual email messages
|
|
239
|
+
* - GmailMessagePart: Email attachments and body parts
|
|
240
|
+
*
|
|
241
|
+
* @param uri - String URI identifying the fragment to retrieve
|
|
242
|
+
* @returns Promise<Fragment> - The requested fragment instance
|
|
243
|
+
* @throws Error if the URI type is unknown or fragment creation fails
|
|
244
|
+
*/
|
|
245
|
+
/**
|
|
246
|
+
* Check if a specific module is available in this Universe instance
|
|
247
|
+
* @param module - The module to check
|
|
248
|
+
* @returns true if the module is available, false otherwise
|
|
249
|
+
*/
|
|
250
|
+
hasModule(module: UniverseModule): boolean {
|
|
251
|
+
switch (module) {
|
|
252
|
+
case UniverseModule.GSuite:
|
|
253
|
+
return !!this.gsuite;
|
|
254
|
+
case UniverseModule.Resend:
|
|
255
|
+
return !!this.resend;
|
|
256
|
+
case UniverseModule.EmailQueue:
|
|
257
|
+
return !!this.emailQueue;
|
|
258
|
+
case UniverseModule.S2:
|
|
259
|
+
return !!this.s2;
|
|
260
|
+
case UniverseModule.Auth:
|
|
261
|
+
return !!this.auth;
|
|
262
|
+
default:
|
|
263
|
+
return false;
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
/**
|
|
268
|
+
* Call an endpoint by name with the given arguments
|
|
269
|
+
* Maps arguments to parameter names based on the endpoint's schema
|
|
270
|
+
* @param context - The Scratch context to use for the call
|
|
271
|
+
* @param endpointName - The name (opcode) of the endpoint to call
|
|
272
|
+
* @param ...args - Arguments to pass to the endpoint (will be mapped to parameter names)
|
|
273
|
+
* @returns Promise<any> - The result from the endpoint handler
|
|
274
|
+
*/
|
|
275
|
+
async call(
|
|
276
|
+
context: ScratchContext,
|
|
277
|
+
endpointName: string,
|
|
278
|
+
...args: any[]
|
|
279
|
+
): Promise<any> {
|
|
280
|
+
const handler = await this.httpServer.getHandler(endpointName);
|
|
281
|
+
if (!handler) {
|
|
282
|
+
throw new Error(`Endpoint handler not found: ${endpointName}`);
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
// Find endpoint definition to get schema for argument mapping
|
|
286
|
+
let endpointBlock: ScratchBlock | null = null;
|
|
287
|
+
for (const e of this.httpServer.getAllEndpoints()) {
|
|
288
|
+
const block = await e.block({});
|
|
289
|
+
if (block.opcode === endpointName) {
|
|
290
|
+
endpointBlock = block;
|
|
291
|
+
break;
|
|
292
|
+
}
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
// Map arguments to parameter names
|
|
296
|
+
const paramNames = endpointBlock?.schema
|
|
297
|
+
? Object.keys(endpointBlock.schema)
|
|
298
|
+
: [];
|
|
299
|
+
const callInputs =
|
|
300
|
+
args.length > 0
|
|
301
|
+
? Object.fromEntries(
|
|
302
|
+
args.map((arg, i) => [paramNames[i] || `param${i}`, arg])
|
|
303
|
+
)
|
|
304
|
+
: {};
|
|
305
|
+
|
|
306
|
+
// Call the handler
|
|
307
|
+
const callContext = { ...context, universe: this };
|
|
308
|
+
return await handler(callContext, {}, callInputs, context.authHeader);
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
/**
|
|
312
|
+
* Check if all specified modules are available
|
|
313
|
+
* @param modules - Array of modules to check
|
|
314
|
+
* @returns true if all modules are available, false otherwise
|
|
315
|
+
*/
|
|
316
|
+
hasModules(modules: UniverseModule[]): boolean {
|
|
317
|
+
return modules.every((module) => this.hasModule(module));
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
async getFragment(uri: string): Promise<Fragment> {
|
|
321
|
+
const parsedUri = URI.fromString(uri);
|
|
322
|
+
|
|
323
|
+
switch (parsedUri.type) {
|
|
324
|
+
case URIType.GmailMessage: {
|
|
325
|
+
return GmailMessage.fromURI(this, parsedUri);
|
|
326
|
+
}
|
|
327
|
+
case URIType.GmailMessagePart: {
|
|
328
|
+
return GmailMessagePart.fromURI(this, parsedUri);
|
|
329
|
+
}
|
|
330
|
+
default: {
|
|
331
|
+
throw new Error(`Unknown URI type: ${parsedUri.type}`);
|
|
332
|
+
}
|
|
333
|
+
}
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
/**
|
|
337
|
+
* Checks the health of all services in the Universe
|
|
338
|
+
* Aggregates health status from all configured services
|
|
339
|
+
*
|
|
340
|
+
* @returns Promise<{ status: string; services: { [key: string]: any } }>
|
|
341
|
+
*/
|
|
342
|
+
async getHealth(): Promise<{
|
|
343
|
+
status: string;
|
|
344
|
+
services: { [key: string]: any };
|
|
345
|
+
}> {
|
|
346
|
+
const health: any = {
|
|
347
|
+
status: "ok",
|
|
348
|
+
services: {},
|
|
349
|
+
};
|
|
350
|
+
|
|
351
|
+
// Check GSuite
|
|
352
|
+
if (this.gsuite) {
|
|
353
|
+
health.services.gsuite = await this.gsuite.getHealth();
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
// Check Resend
|
|
357
|
+
if (this.resend) {
|
|
358
|
+
health.services.resend = this.resend.getHealth();
|
|
359
|
+
} else {
|
|
360
|
+
health.services.resend = {
|
|
361
|
+
status: "warning",
|
|
362
|
+
message: "RESEND_API_KEY not configured",
|
|
363
|
+
connected: false,
|
|
364
|
+
};
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
// Check S2
|
|
368
|
+
if (this.s2) {
|
|
369
|
+
health.services.s2 = await this.s2.getHealth();
|
|
370
|
+
}
|
|
371
|
+
|
|
372
|
+
// Determine overall status
|
|
373
|
+
const hasErrors = Object.values(health.services).some(
|
|
374
|
+
(service: any) => service.status === "error"
|
|
375
|
+
);
|
|
376
|
+
if (hasErrors) {
|
|
377
|
+
health.status = "error";
|
|
378
|
+
} else {
|
|
379
|
+
const hasWarnings = Object.values(health.services).some(
|
|
380
|
+
(service: any) => service.status === "warning"
|
|
381
|
+
);
|
|
382
|
+
if (hasWarnings) {
|
|
383
|
+
health.status = "warning";
|
|
384
|
+
}
|
|
385
|
+
}
|
|
386
|
+
|
|
387
|
+
return health;
|
|
388
|
+
}
|
|
389
|
+
}
|
|
390
|
+
|
|
391
|
+
// Global singleton instance
|
|
392
|
+
let universeInstance: Universe | null = null;
|
|
393
|
+
|
|
394
|
+
/**
|
|
395
|
+
* Get the global Universe singleton instance
|
|
396
|
+
*/
|
|
397
|
+
export function getUniverse(): Universe | null {
|
|
398
|
+
return universeInstance;
|
|
399
|
+
}
|
|
400
|
+
|
|
401
|
+
/**
|
|
402
|
+
* Set the global Universe singleton instance
|
|
403
|
+
*/
|
|
404
|
+
export function setUniverse(universe: Universe | null): void {
|
|
405
|
+
universeInstance = universe;
|
|
406
|
+
}
|
package/core/index.ts
ADDED
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Core Module - Foundation Components
|
|
3
|
+
*
|
|
4
|
+
* This module contains the fundamental building blocks of the AI Executive system:
|
|
5
|
+
* - Universe: Central orchestration and configuration management
|
|
6
|
+
* - Fragment: Abstract interface for all content types (emails, documents, etc.)
|
|
7
|
+
* - URI: Unified resource identification and parsing system
|
|
8
|
+
* - Currency: Financial data handling and conversion utilities
|
|
9
|
+
*
|
|
10
|
+
* These core components provide the foundation for all higher-level functionality
|
|
11
|
+
* including Gmail integration, workflow automation, and AI processing.
|
|
12
|
+
*
|
|
13
|
+
* @module Core
|
|
14
|
+
* @version 1.0.0
|
|
15
|
+
* @author Divizend GmbH
|
|
16
|
+
*/
|
|
17
|
+
|
|
18
|
+
export * from "./Universe";
|
|
19
|
+
export * from "./Scratch";
|
|
20
|
+
export * from "./Auth";
|
|
21
|
+
export * from "./Fragment";
|
|
22
|
+
export * from "./FragmentServingMode";
|
|
23
|
+
export * from "./URI";
|
|
24
|
+
export * from "./Currency";
|
|
25
|
+
export * from "./JsonSchemaValidator";
|
|
26
|
+
export * from "./Env";
|
|
27
|
+
export * from "./ProjectRoot";
|