@aaronsb/google-workspace-mcp 2.0.0-alpha.4
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/LICENSE +21 -0
- package/README.md +193 -0
- package/build/__helpers__/testSetup.d.ts +1 -0
- package/build/__helpers__/testSetup.js +6 -0
- package/build/__helpers__/testSetup.js.map +1 -0
- package/build/__tests__/accounts/credentials.test.d.ts +1 -0
- package/build/__tests__/accounts/credentials.test.js +129 -0
- package/build/__tests__/accounts/credentials.test.js.map +1 -0
- package/build/__tests__/accounts/registry.test.d.ts +1 -0
- package/build/__tests__/accounts/registry.test.js +74 -0
- package/build/__tests__/accounts/registry.test.js.map +1 -0
- package/build/__tests__/executor/errors.test.d.ts +1 -0
- package/build/__tests__/executor/errors.test.js +42 -0
- package/build/__tests__/executor/errors.test.js.map +1 -0
- package/build/__tests__/executor/gws.test.d.ts +1 -0
- package/build/__tests__/executor/gws.test.js +178 -0
- package/build/__tests__/executor/gws.test.js.map +1 -0
- package/build/__tests__/executor/paths.test.d.ts +1 -0
- package/build/__tests__/executor/paths.test.js +60 -0
- package/build/__tests__/executor/paths.test.js.map +1 -0
- package/build/__tests__/executor/workspace.test.d.ts +1 -0
- package/build/__tests__/executor/workspace.test.js +117 -0
- package/build/__tests__/executor/workspace.test.js.map +1 -0
- package/build/__tests__/factory/generator.test.d.ts +1 -0
- package/build/__tests__/factory/generator.test.js +178 -0
- package/build/__tests__/factory/generator.test.js.map +1 -0
- package/build/__tests__/factory/patch-coverage.test.d.ts +10 -0
- package/build/__tests__/factory/patch-coverage.test.js +148 -0
- package/build/__tests__/factory/patch-coverage.test.js.map +1 -0
- package/build/__tests__/factory/safety.test.d.ts +1 -0
- package/build/__tests__/factory/safety.test.js +107 -0
- package/build/__tests__/factory/safety.test.js.map +1 -0
- package/build/__tests__/integration/executor.test.d.ts +5 -0
- package/build/__tests__/integration/executor.test.js +46 -0
- package/build/__tests__/integration/executor.test.js.map +1 -0
- package/build/__tests__/integration/handlers.test.d.ts +6 -0
- package/build/__tests__/integration/handlers.test.js +95 -0
- package/build/__tests__/integration/handlers.test.js.map +1 -0
- package/build/__tests__/integration/setup.d.ts +19 -0
- package/build/__tests__/integration/setup.js +61 -0
- package/build/__tests__/integration/setup.js.map +1 -0
- package/build/__tests__/server/formatting/markdown.test.d.ts +1 -0
- package/build/__tests__/server/formatting/markdown.test.js +149 -0
- package/build/__tests__/server/formatting/markdown.test.js.map +1 -0
- package/build/__tests__/server/formatting/next-steps.test.d.ts +1 -0
- package/build/__tests__/server/formatting/next-steps.test.js +42 -0
- package/build/__tests__/server/formatting/next-steps.test.js.map +1 -0
- package/build/__tests__/server/handler.test.d.ts +1 -0
- package/build/__tests__/server/handler.test.js +97 -0
- package/build/__tests__/server/handler.test.js.map +1 -0
- package/build/__tests__/server/handlers/__mocks__/executor.d.ts +147 -0
- package/build/__tests__/server/handlers/__mocks__/executor.js +114 -0
- package/build/__tests__/server/handlers/__mocks__/executor.js.map +1 -0
- package/build/__tests__/server/handlers/accounts.test.d.ts +1 -0
- package/build/__tests__/server/handlers/accounts.test.js +127 -0
- package/build/__tests__/server/handlers/accounts.test.js.map +1 -0
- package/build/__tests__/server/handlers/calendar.test.d.ts +1 -0
- package/build/__tests__/server/handlers/calendar.test.js +95 -0
- package/build/__tests__/server/handlers/calendar.test.js.map +1 -0
- package/build/__tests__/server/handlers/drive.test.d.ts +1 -0
- package/build/__tests__/server/handlers/drive.test.js +81 -0
- package/build/__tests__/server/handlers/drive.test.js.map +1 -0
- package/build/__tests__/server/handlers/email.test.d.ts +1 -0
- package/build/__tests__/server/handlers/email.test.js +99 -0
- package/build/__tests__/server/handlers/email.test.js.map +1 -0
- package/build/__tests__/server/handlers/validate.test.d.ts +1 -0
- package/build/__tests__/server/handlers/validate.test.js +88 -0
- package/build/__tests__/server/handlers/validate.test.js.map +1 -0
- package/build/__tests__/server/queue.test.d.ts +1 -0
- package/build/__tests__/server/queue.test.js +194 -0
- package/build/__tests__/server/queue.test.js.map +1 -0
- package/build/__tests__/server/server.test.d.ts +7 -0
- package/build/__tests__/server/server.test.js +135 -0
- package/build/__tests__/server/server.test.js.map +1 -0
- package/build/__tests__/server/tools.test.d.ts +1 -0
- package/build/__tests__/server/tools.test.js +91 -0
- package/build/__tests__/server/tools.test.js.map +1 -0
- package/build/accounts/auth.d.ts +24 -0
- package/build/accounts/auth.js +118 -0
- package/build/accounts/auth.js.map +1 -0
- package/build/accounts/credentials.d.ts +11 -0
- package/build/accounts/credentials.js +52 -0
- package/build/accounts/credentials.js.map +1 -0
- package/build/accounts/index.d.ts +6 -0
- package/build/accounts/index.js +4 -0
- package/build/accounts/index.js.map +1 -0
- package/build/accounts/registry.d.ts +11 -0
- package/build/accounts/registry.js +62 -0
- package/build/accounts/registry.js.map +1 -0
- package/build/executor/errors.d.ts +15 -0
- package/build/executor/errors.js +37 -0
- package/build/executor/errors.js.map +1 -0
- package/build/executor/file-output.d.ts +23 -0
- package/build/executor/file-output.js +67 -0
- package/build/executor/file-output.js.map +1 -0
- package/build/executor/gws.d.ts +23 -0
- package/build/executor/gws.js +144 -0
- package/build/executor/gws.js.map +1 -0
- package/build/executor/index.d.ts +4 -0
- package/build/executor/index.js +4 -0
- package/build/executor/index.js.map +1 -0
- package/build/executor/paths.d.ts +6 -0
- package/build/executor/paths.js +25 -0
- package/build/executor/paths.js.map +1 -0
- package/build/executor/workspace.d.ts +38 -0
- package/build/executor/workspace.js +146 -0
- package/build/executor/workspace.js.map +1 -0
- package/build/factory/defaults.d.ts +8 -0
- package/build/factory/defaults.js +101 -0
- package/build/factory/defaults.js.map +1 -0
- package/build/factory/generator.d.ts +23 -0
- package/build/factory/generator.js +253 -0
- package/build/factory/generator.js.map +1 -0
- package/build/factory/manifest.yaml +852 -0
- package/build/factory/patches.d.ts +6 -0
- package/build/factory/patches.js +13 -0
- package/build/factory/patches.js.map +1 -0
- package/build/factory/registry.d.ts +12 -0
- package/build/factory/registry.js +18 -0
- package/build/factory/registry.js.map +1 -0
- package/build/factory/safety.d.ts +68 -0
- package/build/factory/safety.js +157 -0
- package/build/factory/safety.js.map +1 -0
- package/build/factory/types.d.ts +102 -0
- package/build/factory/types.js +6 -0
- package/build/factory/types.js.map +1 -0
- package/build/factory/yaml.d.ts +2 -0
- package/build/factory/yaml.js +3 -0
- package/build/factory/yaml.js.map +1 -0
- package/build/index.d.ts +2 -0
- package/build/index.js +14 -0
- package/build/index.js.map +1 -0
- package/build/server/formatting/markdown.d.ts +21 -0
- package/build/server/formatting/markdown.js +324 -0
- package/build/server/formatting/markdown.js.map +1 -0
- package/build/server/formatting/next-steps.d.ts +9 -0
- package/build/server/formatting/next-steps.js +123 -0
- package/build/server/formatting/next-steps.js.map +1 -0
- package/build/server/handler.d.ts +3 -0
- package/build/server/handler.js +24 -0
- package/build/server/handler.js.map +1 -0
- package/build/server/handlers/accounts.d.ts +2 -0
- package/build/server/handlers/accounts.js +181 -0
- package/build/server/handlers/accounts.js.map +1 -0
- package/build/server/handlers/calendar.d.ts +2 -0
- package/build/server/handlers/calendar.js +93 -0
- package/build/server/handlers/calendar.js.map +1 -0
- package/build/server/handlers/drive.d.ts +2 -0
- package/build/server/handlers/drive.js +74 -0
- package/build/server/handlers/drive.js.map +1 -0
- package/build/server/handlers/email.d.ts +2 -0
- package/build/server/handlers/email.js +115 -0
- package/build/server/handlers/email.js.map +1 -0
- package/build/server/handlers/validate.d.ts +3 -0
- package/build/server/handlers/validate.js +22 -0
- package/build/server/handlers/validate.js.map +1 -0
- package/build/server/handlers/workspace.d.ts +9 -0
- package/build/server/handlers/workspace.js +110 -0
- package/build/server/handlers/workspace.js.map +1 -0
- package/build/server/index.d.ts +4 -0
- package/build/server/index.js +4 -0
- package/build/server/index.js.map +1 -0
- package/build/server/queue.d.ts +11 -0
- package/build/server/queue.js +141 -0
- package/build/server/queue.js.map +1 -0
- package/build/server/server.d.ts +3 -0
- package/build/server/server.js +214 -0
- package/build/server/server.js.map +1 -0
- package/build/server/tools.d.ts +13 -0
- package/build/server/tools.js +99 -0
- package/build/server/tools.js.map +1 -0
- package/build/services/calendar/patch.d.ts +11 -0
- package/build/services/calendar/patch.js +116 -0
- package/build/services/calendar/patch.js.map +1 -0
- package/build/services/drive/patch.d.ts +10 -0
- package/build/services/drive/patch.js +131 -0
- package/build/services/drive/patch.js.map +1 -0
- package/build/services/gmail/attachments.d.ts +19 -0
- package/build/services/gmail/attachments.js +90 -0
- package/build/services/gmail/attachments.js.map +1 -0
- package/build/services/gmail/patch.d.ts +10 -0
- package/build/services/gmail/patch.js +226 -0
- package/build/services/gmail/patch.js.map +1 -0
- package/package.json +34 -0
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Workspace directory — safe sandbox for file I/O operations.
|
|
3
|
+
*
|
|
4
|
+
* All file operations (Drive upload/download, Docs export, Sheets CSV export)
|
|
5
|
+
* are jailed to this directory. Prevents agents from accidentally operating on
|
|
6
|
+
* home directories, document folders, or Google Drive mount points.
|
|
7
|
+
*/
|
|
8
|
+
import * as fs from 'node:fs/promises';
|
|
9
|
+
import * as path from 'node:path';
|
|
10
|
+
import { dataDir } from './paths.js';
|
|
11
|
+
const DEFAULT_WORKSPACE = path.join(dataDir(), 'workspace');
|
|
12
|
+
/** Paths that must never be used as the workspace root. */
|
|
13
|
+
const FORBIDDEN_PATHS = [
|
|
14
|
+
// Home directory itself
|
|
15
|
+
() => process.env.HOME ?? '',
|
|
16
|
+
() => process.env.USERPROFILE ?? '',
|
|
17
|
+
// Common document directories (only when HOME/USERPROFILE is set)
|
|
18
|
+
() => process.env.HOME ? path.join(process.env.HOME, 'Documents') : '',
|
|
19
|
+
() => process.env.HOME ? path.join(process.env.HOME, 'Desktop') : '',
|
|
20
|
+
() => process.env.HOME ? path.join(process.env.HOME, 'Downloads') : '',
|
|
21
|
+
// Windows equivalents
|
|
22
|
+
() => process.env.USERPROFILE ? path.join(process.env.USERPROFILE, 'Documents') : '',
|
|
23
|
+
() => process.env.USERPROFILE ? path.join(process.env.USERPROFILE, 'Desktop') : '',
|
|
24
|
+
() => process.env.USERPROFILE ? path.join(process.env.USERPROFILE, 'Downloads') : '',
|
|
25
|
+
];
|
|
26
|
+
/** Path substrings that indicate a Google Drive mount. */
|
|
27
|
+
const GDRIVE_PATTERNS = [
|
|
28
|
+
'google-drive',
|
|
29
|
+
'Google Drive',
|
|
30
|
+
'GoogleDrive',
|
|
31
|
+
'gdrive',
|
|
32
|
+
'My Drive',
|
|
33
|
+
];
|
|
34
|
+
/** Validate and return the workspace directory path. */
|
|
35
|
+
export function getWorkspaceDir() {
|
|
36
|
+
const configured = process.env.WORKSPACE_DIR;
|
|
37
|
+
// Ignore unresolved mcpb template variables (e.g. "${user_config.workspace_dir}")
|
|
38
|
+
if (configured && !configured.includes('${')) {
|
|
39
|
+
return configured;
|
|
40
|
+
}
|
|
41
|
+
return DEFAULT_WORKSPACE;
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* Validate workspace dir is safe. Throws if it IS a protected directory.
|
|
45
|
+
* Being a subdirectory OF a protected directory is fine (e.g. ~/Documents/mcp-workspace/).
|
|
46
|
+
*/
|
|
47
|
+
export function validateWorkspaceDir(dir) {
|
|
48
|
+
const resolved = path.resolve(dir);
|
|
49
|
+
// Must not BE a protected directory (subdirectories are OK)
|
|
50
|
+
for (const getForbidden of FORBIDDEN_PATHS) {
|
|
51
|
+
const forbidden = getForbidden();
|
|
52
|
+
if (forbidden && path.resolve(forbidden) === resolved) {
|
|
53
|
+
throw new Error(`Workspace directory cannot be ${resolved} itself — ` +
|
|
54
|
+
`use a subdirectory like ${resolved}/mcp-workspace or ${DEFAULT_WORKSPACE}`);
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
// Check for Google Drive mount points
|
|
58
|
+
for (const pattern of GDRIVE_PATTERNS) {
|
|
59
|
+
if (resolved.toLowerCase().includes(pattern.toLowerCase())) {
|
|
60
|
+
throw new Error(`Workspace directory cannot be inside a Google Drive mount (${resolved}) — ` +
|
|
61
|
+
`this could cause sync conflicts and data loss`);
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
// Must not be the filesystem root
|
|
65
|
+
if (resolved === '/' || resolved === 'C:\\') {
|
|
66
|
+
throw new Error('Workspace directory cannot be the filesystem root');
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
/** Check workspace directory status without crashing. */
|
|
70
|
+
export function checkWorkspaceStatus() {
|
|
71
|
+
const dir = getWorkspaceDir();
|
|
72
|
+
try {
|
|
73
|
+
validateWorkspaceDir(dir);
|
|
74
|
+
return { path: dir, valid: true };
|
|
75
|
+
}
|
|
76
|
+
catch (err) {
|
|
77
|
+
return {
|
|
78
|
+
path: dir,
|
|
79
|
+
valid: false,
|
|
80
|
+
warning: err.message,
|
|
81
|
+
};
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
/** Ensure the workspace directory exists and is validated. Returns status instead of throwing. */
|
|
85
|
+
export async function ensureWorkspaceDir() {
|
|
86
|
+
const status = checkWorkspaceStatus();
|
|
87
|
+
if (status.valid) {
|
|
88
|
+
await fs.mkdir(status.path, { recursive: true, mode: 0o755 });
|
|
89
|
+
}
|
|
90
|
+
return status;
|
|
91
|
+
}
|
|
92
|
+
/**
|
|
93
|
+
* Sanitize a filename from external sources (email attachments, Drive metadata).
|
|
94
|
+
* Strips null bytes, control characters, path separators, and other dangerous chars.
|
|
95
|
+
*/
|
|
96
|
+
export function sanitizeFilename(filename) {
|
|
97
|
+
return filename
|
|
98
|
+
// Remove null bytes and control characters
|
|
99
|
+
.replace(/[\x00-\x1f\x7f]/g, '')
|
|
100
|
+
// Remove path separators (prevent directory traversal via filename)
|
|
101
|
+
.replace(/[/\\]/g, '_')
|
|
102
|
+
// Remove other potentially dangerous characters
|
|
103
|
+
.replace(/[<>:"|?*]/g, '_')
|
|
104
|
+
// Collapse multiple underscores
|
|
105
|
+
.replace(/_+/g, '_')
|
|
106
|
+
// Remove leading dots (hidden files) and trailing dots/spaces (Windows)
|
|
107
|
+
.replace(/^\.+/, '')
|
|
108
|
+
.replace(/[. ]+$/, '')
|
|
109
|
+
// Fallback if nothing remains
|
|
110
|
+
|| 'unnamed';
|
|
111
|
+
}
|
|
112
|
+
/**
|
|
113
|
+
* Resolve a file path within the workspace directory.
|
|
114
|
+
* Prevents path traversal (e.g. ../../etc/passwd) and sanitizes the filename.
|
|
115
|
+
*/
|
|
116
|
+
export function resolveWorkspacePath(filename) {
|
|
117
|
+
const dir = getWorkspaceDir();
|
|
118
|
+
const sanitized = sanitizeFilename(filename);
|
|
119
|
+
const resolved = path.resolve(dir, sanitized);
|
|
120
|
+
// Ensure the resolved path is still inside the workspace
|
|
121
|
+
const resolvedDir = path.resolve(dir);
|
|
122
|
+
if (!resolved.startsWith(resolvedDir + path.sep) && resolved !== resolvedDir) {
|
|
123
|
+
throw new Error(`Path traversal detected: "${filename}" resolves outside workspace directory`);
|
|
124
|
+
}
|
|
125
|
+
return resolved;
|
|
126
|
+
}
|
|
127
|
+
/**
|
|
128
|
+
* Verify a file path is safe to read/write after symlink resolution.
|
|
129
|
+
* Must be called before any fs operation on a workspace path.
|
|
130
|
+
*/
|
|
131
|
+
export async function verifyPathSafety(filePath) {
|
|
132
|
+
const dir = path.resolve(getWorkspaceDir());
|
|
133
|
+
try {
|
|
134
|
+
const real = await fs.realpath(filePath);
|
|
135
|
+
if (!real.startsWith(dir + path.sep) && real !== dir) {
|
|
136
|
+
throw new Error(`Symlink escape detected: "${filePath}" resolves to "${real}" outside workspace`);
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
catch (err) {
|
|
140
|
+
// ENOENT is OK — file doesn't exist yet (write case)
|
|
141
|
+
if (err.code === 'ENOENT')
|
|
142
|
+
return;
|
|
143
|
+
throw err;
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
//# sourceMappingURL=workspace.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"workspace.js","sourceRoot":"","sources":["../../src/executor/workspace.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Default formatters — generic markdown renderers for list/detail/action
|
|
3
|
+
* responses. Used when a service has no patch formatter override.
|
|
4
|
+
*/
|
|
5
|
+
import type { HandlerResponse } from '../server/formatting/markdown.js';
|
|
6
|
+
import type { OperationDef } from './types.js';
|
|
7
|
+
/** Route to the appropriate default formatter based on operation type. */
|
|
8
|
+
export declare function formatDefault(data: unknown, opDef: OperationDef): HandlerResponse;
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Default formatters — generic markdown renderers for list/detail/action
|
|
3
|
+
* responses. Used when a service has no patch formatter override.
|
|
4
|
+
*/
|
|
5
|
+
/** Route to the appropriate default formatter based on operation type. */
|
|
6
|
+
export function formatDefault(data, opDef) {
|
|
7
|
+
switch (opDef.type) {
|
|
8
|
+
case 'list':
|
|
9
|
+
return formatDefaultList(data);
|
|
10
|
+
case 'detail':
|
|
11
|
+
return formatDefaultDetail(data);
|
|
12
|
+
case 'action':
|
|
13
|
+
return formatDefaultAction(data);
|
|
14
|
+
default:
|
|
15
|
+
return formatDefaultDetail(data);
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
/** Generic list formatter — renders array items as pipe-delimited rows. */
|
|
19
|
+
function formatDefaultList(data) {
|
|
20
|
+
const raw = data;
|
|
21
|
+
// Try common list wrapper keys
|
|
22
|
+
const items = findArray(raw);
|
|
23
|
+
if (items.length === 0) {
|
|
24
|
+
return { text: 'No results found.', refs: { count: 0 } };
|
|
25
|
+
}
|
|
26
|
+
const lines = items.map(item => {
|
|
27
|
+
const obj = item;
|
|
28
|
+
const id = String(obj.id ?? '');
|
|
29
|
+
const parts = [id];
|
|
30
|
+
// Include a few meaningful string fields
|
|
31
|
+
for (const [key, val] of Object.entries(obj)) {
|
|
32
|
+
if (key === 'id')
|
|
33
|
+
continue;
|
|
34
|
+
if (typeof val === 'string' && val.length < 100) {
|
|
35
|
+
parts.push(val);
|
|
36
|
+
}
|
|
37
|
+
if (parts.length >= 5)
|
|
38
|
+
break;
|
|
39
|
+
}
|
|
40
|
+
return parts.join(' | ');
|
|
41
|
+
});
|
|
42
|
+
return {
|
|
43
|
+
text: `## Results (${items.length})\n\n${lines.join('\n')}`,
|
|
44
|
+
refs: {
|
|
45
|
+
count: items.length,
|
|
46
|
+
id: String(items[0]?.id ?? ''),
|
|
47
|
+
ids: items.map(i => String(i?.id ?? '')),
|
|
48
|
+
},
|
|
49
|
+
};
|
|
50
|
+
}
|
|
51
|
+
/** Generic detail formatter — renders object fields as bold key/value pairs. */
|
|
52
|
+
function formatDefaultDetail(data) {
|
|
53
|
+
const obj = data;
|
|
54
|
+
const title = String(obj.name ?? obj.summary ?? obj.subject ?? obj.title ?? 'Details');
|
|
55
|
+
const id = String(obj.id ?? '');
|
|
56
|
+
const parts = [`## ${title}`, ''];
|
|
57
|
+
for (const [key, val] of Object.entries(obj)) {
|
|
58
|
+
if (val === null || val === undefined)
|
|
59
|
+
continue;
|
|
60
|
+
if (typeof val === 'object')
|
|
61
|
+
continue; // skip nested objects
|
|
62
|
+
parts.push(`**${key}:** ${val}`);
|
|
63
|
+
}
|
|
64
|
+
return {
|
|
65
|
+
text: parts.join('\n'),
|
|
66
|
+
refs: { id, ...extractScalarRefs(obj) },
|
|
67
|
+
};
|
|
68
|
+
}
|
|
69
|
+
/** Generic action formatter — confirmation message with returned fields. */
|
|
70
|
+
function formatDefaultAction(data) {
|
|
71
|
+
const obj = (data ?? {});
|
|
72
|
+
const id = String(obj.id ?? 'unknown');
|
|
73
|
+
const parts = ['Operation completed.'];
|
|
74
|
+
if (obj.id)
|
|
75
|
+
parts.push(`\n**ID:** ${id}`);
|
|
76
|
+
return {
|
|
77
|
+
text: parts.join(''),
|
|
78
|
+
refs: { id, ...extractScalarRefs(obj) },
|
|
79
|
+
};
|
|
80
|
+
}
|
|
81
|
+
/** Find the first array in a response object (items, files, messages, etc). */
|
|
82
|
+
function findArray(obj) {
|
|
83
|
+
if (Array.isArray(obj))
|
|
84
|
+
return obj;
|
|
85
|
+
for (const val of Object.values(obj)) {
|
|
86
|
+
if (Array.isArray(val))
|
|
87
|
+
return val;
|
|
88
|
+
}
|
|
89
|
+
return [];
|
|
90
|
+
}
|
|
91
|
+
/** Pull scalar values from an object for refs. */
|
|
92
|
+
function extractScalarRefs(obj) {
|
|
93
|
+
const refs = {};
|
|
94
|
+
for (const [key, val] of Object.entries(obj)) {
|
|
95
|
+
if (typeof val === 'string' || typeof val === 'number' || typeof val === 'boolean') {
|
|
96
|
+
refs[key] = val;
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
return refs;
|
|
100
|
+
}
|
|
101
|
+
//# sourceMappingURL=defaults.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"defaults.js","sourceRoot":"","sources":["../../src/factory/defaults.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAKH,0EAA0E;AAC1E,MAAM,UAAU,aAAa,CAAC,IAAa,EAAE,KAAmB;IAC9D,QAAQ,KAAK,CAAC,IAAI,EAAE,CAAC;QACnB,KAAK,MAAM;YACT,OAAO,iBAAiB,CAAC,IAAI,CAAC,CAAC;QACjC,KAAK,QAAQ;YACX,OAAO,mBAAmB,CAAC,IAAI,CAAC,CAAC;QACnC,KAAK,QAAQ;YACX,OAAO,mBAAmB,CAAC,IAAI,CAAC,CAAC;QACnC;YACE,OAAO,mBAAmB,CAAC,IAAI,CAAC,CAAC;IACrC,CAAC;AACH,CAAC;AAED,2EAA2E;AAC3E,SAAS,iBAAiB,CAAC,IAAa;IACtC,MAAM,GAAG,GAAG,IAA+B,CAAC;IAC5C,+BAA+B;IAC/B,MAAM,KAAK,GAAG,SAAS,CAAC,GAAG,CAAC,CAAC;IAE7B,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACvB,OAAO,EAAE,IAAI,EAAE,mBAAmB,EAAE,IAAI,EAAE,EAAE,KAAK,EAAE,CAAC,EAAE,EAAE,CAAC;IAC3D,CAAC;IAED,MAAM,KAAK,GAAG,KAAK,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE;QAC7B,MAAM,GAAG,GAAG,IAA+B,CAAC;QAC5C,MAAM,EAAE,GAAG,MAAM,CAAC,GAAG,CAAC,EAAE,IAAI,EAAE,CAAC,CAAC;QAChC,MAAM,KAAK,GAAG,CAAC,EAAE,CAAC,CAAC;QACnB,yCAAyC;QACzC,KAAK,MAAM,CAAC,GAAG,EAAE,GAAG,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC;YAC7C,IAAI,GAAG,KAAK,IAAI;gBAAE,SAAS;YAC3B,IAAI,OAAO,GAAG,KAAK,QAAQ,IAAI,GAAG,CAAC,MAAM,GAAG,GAAG,EAAE,CAAC;gBAChD,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;YAClB,CAAC;YACD,IAAI,KAAK,CAAC,MAAM,IAAI,CAAC;gBAAE,MAAM;QAC/B,CAAC;QACD,OAAO,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;IAC3B,CAAC,CAAC,CAAC;IAEH,OAAO;QACL,IAAI,EAAE,eAAe,KAAK,CAAC,MAAM,QAAQ,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE;QAC3D,IAAI,EAAE;YACJ,KAAK,EAAE,KAAK,CAAC,MAAM;YACnB,EAAE,EAAE,MAAM,CAAE,KAAK,CAAC,CAAC,CAA6B,EAAE,EAAE,IAAI,EAAE,CAAC;YAC3D,GAAG,EAAE,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,MAAM,CAAE,CAA6B,EAAE,EAAE,IAAI,EAAE,CAAC,CAAC;SACtE;KACF,CAAC;AACJ,CAAC;AAED,gFAAgF;AAChF,SAAS,mBAAmB,CAAC,IAAa;IACxC,MAAM,GAAG,GAAG,IAA+B,CAAC;IAC5C,MAAM,KAAK,GAAG,MAAM,CAAC,GAAG,CAAC,IAAI,IAAI,GAAG,CAAC,OAAO,IAAI,GAAG,CAAC,OAAO,IAAI,GAAG,CAAC,KAAK,IAAI,SAAS,CAAC,CAAC;IACvF,MAAM,EAAE,GAAG,MAAM,CAAC,GAAG,CAAC,EAAE,IAAI,EAAE,CAAC,CAAC;IAEhC,MAAM,KAAK,GAAa,CAAC,MAAM,KAAK,EAAE,EAAE,EAAE,CAAC,CAAC;IAC5C,KAAK,MAAM,CAAC,GAAG,EAAE,GAAG,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC;QAC7C,IAAI,GAAG,KAAK,IAAI,IAAI,GAAG,KAAK,SAAS;YAAE,SAAS;QAChD,IAAI,OAAO,GAAG,KAAK,QAAQ;YAAE,SAAS,CAAC,sBAAsB;QAC7D,KAAK,CAAC,IAAI,CAAC,KAAK,GAAG,OAAO,GAAG,EAAE,CAAC,CAAC;IACnC,CAAC;IAED,OAAO;QACL,IAAI,EAAE,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC;QACtB,IAAI,EAAE,EAAE,EAAE,EAAE,GAAG,iBAAiB,CAAC,GAAG,CAAC,EAAE;KACxC,CAAC;AACJ,CAAC;AAED,4EAA4E;AAC5E,SAAS,mBAAmB,CAAC,IAAa;IACxC,MAAM,GAAG,GAAG,CAAC,IAAI,IAAI,EAAE,CAA4B,CAAC;IACpD,MAAM,EAAE,GAAG,MAAM,CAAC,GAAG,CAAC,EAAE,IAAI,SAAS,CAAC,CAAC;IAEvC,MAAM,KAAK,GAAa,CAAC,sBAAsB,CAAC,CAAC;IACjD,IAAI,GAAG,CAAC,EAAE;QAAE,KAAK,CAAC,IAAI,CAAC,aAAa,EAAE,EAAE,CAAC,CAAC;IAE1C,OAAO;QACL,IAAI,EAAE,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC;QACpB,IAAI,EAAE,EAAE,EAAE,EAAE,GAAG,iBAAiB,CAAC,GAAG,CAAC,EAAE;KACxC,CAAC;AACJ,CAAC;AAED,+EAA+E;AAC/E,SAAS,SAAS,CAAC,GAA4B;IAC7C,IAAI,KAAK,CAAC,OAAO,CAAC,GAAG,CAAC;QAAE,OAAO,GAAG,CAAC;IACnC,KAAK,MAAM,GAAG,IAAI,MAAM,CAAC,MAAM,CAAC,GAAG,CAAC,EAAE,CAAC;QACrC,IAAI,KAAK,CAAC,OAAO,CAAC,GAAG,CAAC;YAAE,OAAO,GAAG,CAAC;IACrC,CAAC;IACD,OAAO,EAAE,CAAC;AACZ,CAAC;AAED,kDAAkD;AAClD,SAAS,iBAAiB,CAAC,GAA4B;IACrD,MAAM,IAAI,GAA4B,EAAE,CAAC;IACzC,KAAK,MAAM,CAAC,GAAG,EAAE,GAAG,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC;QAC7C,IAAI,OAAO,GAAG,KAAK,QAAQ,IAAI,OAAO,GAAG,KAAK,QAAQ,IAAI,OAAO,GAAG,KAAK,SAAS,EAAE,CAAC;YACnF,IAAI,CAAC,GAAG,CAAC,GAAG,GAAG,CAAC;QAClB,CAAC;IACH,CAAC;IACD,OAAO,IAAI,CAAC;AACd,CAAC"}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Factory generator — reads the manifest and produces MCP tools.
|
|
3
|
+
*
|
|
4
|
+
* For each service in the manifest, generates:
|
|
5
|
+
* 1. A JSON Schema tool definition (operation enum, typed params)
|
|
6
|
+
* 2. A handler function (maps operations to gws CLI calls, applies formatting)
|
|
7
|
+
*
|
|
8
|
+
* Patches are optional per-service hooks that override default behavior.
|
|
9
|
+
*/
|
|
10
|
+
import type { Manifest, ServiceDef, ServicePatch, GeneratedTool, GeneratedToolSchema, GeneratedHandler } from './types.js';
|
|
11
|
+
/** Called by registry.ts to inject the ESM module directory. */
|
|
12
|
+
export declare function setModuleDir(dir: string): void;
|
|
13
|
+
/**
|
|
14
|
+
* Load and parse the manifest YAML.
|
|
15
|
+
* Searches module-relative paths first, then cwd fallbacks.
|
|
16
|
+
*/
|
|
17
|
+
export declare function loadManifest(path?: string): Manifest;
|
|
18
|
+
/** Generate all tools from the manifest with optional patches. */
|
|
19
|
+
export declare function generateTools(manifest: Manifest, patches?: Record<string, ServicePatch>): GeneratedTool[];
|
|
20
|
+
/** Generate the JSON Schema tool definition from a service declaration. */
|
|
21
|
+
export declare function generateSchema(service: ServiceDef): GeneratedToolSchema;
|
|
22
|
+
/** Generate a handler function for a service. */
|
|
23
|
+
export declare function generateHandler(service: ServiceDef, patch?: ServicePatch): GeneratedHandler;
|
|
@@ -0,0 +1,253 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Factory generator — reads the manifest and produces MCP tools.
|
|
3
|
+
*
|
|
4
|
+
* For each service in the manifest, generates:
|
|
5
|
+
* 1. A JSON Schema tool definition (operation enum, typed params)
|
|
6
|
+
* 2. A handler function (maps operations to gws CLI calls, applies formatting)
|
|
7
|
+
*
|
|
8
|
+
* Patches are optional per-service hooks that override default behavior.
|
|
9
|
+
*/
|
|
10
|
+
import { readFileSync } from 'node:fs';
|
|
11
|
+
import { resolve } from 'node:path';
|
|
12
|
+
import { parse as parseYaml } from '../factory/yaml.js';
|
|
13
|
+
import { execute } from '../executor/gws.js';
|
|
14
|
+
import { requireEmail, clamp } from '../server/handlers/validate.js';
|
|
15
|
+
import { formatDefault } from './defaults.js';
|
|
16
|
+
import { nextSteps } from '../server/formatting/next-steps.js';
|
|
17
|
+
import { evaluatePolicies } from './safety.js';
|
|
18
|
+
/**
|
|
19
|
+
* Module directory for manifest resolution.
|
|
20
|
+
* Set by registry.ts via setModuleDir() using import.meta.url (ESM only).
|
|
21
|
+
* Null in Jest (CJS) — falls back to cwd-based resolution.
|
|
22
|
+
*/
|
|
23
|
+
let _moduleDir;
|
|
24
|
+
/** Called by registry.ts to inject the ESM module directory. */
|
|
25
|
+
export function setModuleDir(dir) {
|
|
26
|
+
_moduleDir = dir;
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Load and parse the manifest YAML.
|
|
30
|
+
* Searches module-relative paths first, then cwd fallbacks.
|
|
31
|
+
*/
|
|
32
|
+
export function loadManifest(path) {
|
|
33
|
+
if (path) {
|
|
34
|
+
return parseYaml(readFileSync(path, 'utf-8'));
|
|
35
|
+
}
|
|
36
|
+
// Search for manifest relative to known anchors.
|
|
37
|
+
// moduleDir is injected by registry.ts using import.meta.url (ESM only).
|
|
38
|
+
// cwd fallbacks handle Jest/dev where cwd is the project root.
|
|
39
|
+
const candidates = [];
|
|
40
|
+
if (_moduleDir) {
|
|
41
|
+
candidates.push(resolve(_moduleDir, 'manifest.yaml')); // build/factory/manifest.yaml
|
|
42
|
+
candidates.push(resolve(_moduleDir, '../../src/factory/manifest.yaml')); // dev: from build/ to src/
|
|
43
|
+
}
|
|
44
|
+
candidates.push(resolve(process.cwd(), 'src/factory/manifest.yaml'));
|
|
45
|
+
candidates.push(resolve(process.cwd(), 'build/factory/manifest.yaml'));
|
|
46
|
+
for (const candidate of candidates) {
|
|
47
|
+
try {
|
|
48
|
+
return parseYaml(readFileSync(candidate, 'utf-8'));
|
|
49
|
+
}
|
|
50
|
+
catch {
|
|
51
|
+
continue;
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
throw new Error(`Could not find manifest.yaml. Searched:\n${candidates.join('\n')}`);
|
|
55
|
+
}
|
|
56
|
+
/** Generate all tools from the manifest with optional patches. */
|
|
57
|
+
export function generateTools(manifest, patches) {
|
|
58
|
+
const tools = [];
|
|
59
|
+
for (const [serviceName, serviceDef] of Object.entries(manifest.services)) {
|
|
60
|
+
const patch = patches?.[serviceName];
|
|
61
|
+
const schema = generateSchema(serviceDef);
|
|
62
|
+
const handler = generateHandler(serviceDef, patch);
|
|
63
|
+
tools.push({ schema, handler });
|
|
64
|
+
}
|
|
65
|
+
return tools;
|
|
66
|
+
}
|
|
67
|
+
/** Generate the JSON Schema tool definition from a service declaration. */
|
|
68
|
+
export function generateSchema(service) {
|
|
69
|
+
const operationNames = Object.keys(service.operations);
|
|
70
|
+
const operationDescriptions = operationNames
|
|
71
|
+
.map(name => `${name}: ${service.operations[name].description}`)
|
|
72
|
+
.join(' | ');
|
|
73
|
+
// Collect all unique params across operations
|
|
74
|
+
const allParams = {};
|
|
75
|
+
for (const op of Object.values(service.operations)) {
|
|
76
|
+
if (!op.params)
|
|
77
|
+
continue;
|
|
78
|
+
for (const [paramName, paramDef] of Object.entries(op.params)) {
|
|
79
|
+
if (!allParams[paramName]) {
|
|
80
|
+
allParams[paramName] = {
|
|
81
|
+
type: paramDef.type,
|
|
82
|
+
description: paramDef.description,
|
|
83
|
+
...(paramDef.enum ? { enum: paramDef.enum } : {}),
|
|
84
|
+
};
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
const properties = {
|
|
89
|
+
operation: {
|
|
90
|
+
type: 'string',
|
|
91
|
+
enum: operationNames,
|
|
92
|
+
description: operationDescriptions,
|
|
93
|
+
},
|
|
94
|
+
};
|
|
95
|
+
if (service.requires_email) {
|
|
96
|
+
properties.email = { type: 'string', description: 'Account email address' };
|
|
97
|
+
}
|
|
98
|
+
for (const [name, def] of Object.entries(allParams)) {
|
|
99
|
+
properties[name] = { type: def.type, description: def.description, ...(def.enum ? { enum: def.enum } : {}) };
|
|
100
|
+
}
|
|
101
|
+
const required = service.requires_email ? ['operation', 'email'] : ['operation'];
|
|
102
|
+
return {
|
|
103
|
+
name: service.tool_name,
|
|
104
|
+
description: service.description,
|
|
105
|
+
inputSchema: {
|
|
106
|
+
type: 'object',
|
|
107
|
+
properties,
|
|
108
|
+
required,
|
|
109
|
+
additionalProperties: false,
|
|
110
|
+
},
|
|
111
|
+
};
|
|
112
|
+
}
|
|
113
|
+
/** Generate a handler function for a service. */
|
|
114
|
+
export function generateHandler(service, patch) {
|
|
115
|
+
// Map tool_name to the next-steps domain key
|
|
116
|
+
const domainMap = {
|
|
117
|
+
manage_email: 'email',
|
|
118
|
+
manage_calendar: 'calendar',
|
|
119
|
+
manage_drive: 'drive',
|
|
120
|
+
};
|
|
121
|
+
const domain = domainMap[service.tool_name] ?? service.gws_service;
|
|
122
|
+
return async (params) => {
|
|
123
|
+
const operation = params.operation;
|
|
124
|
+
const opDef = service.operations[operation];
|
|
125
|
+
if (!opDef) {
|
|
126
|
+
throw new Error(`Unknown ${service.gws_service} operation: ${operation}`);
|
|
127
|
+
}
|
|
128
|
+
const account = service.requires_email ? requireEmail(params) : '';
|
|
129
|
+
const ctx = { operation, params, account };
|
|
130
|
+
// Safety policies — run before anything else, including custom handlers.
|
|
131
|
+
// A blocked operation never reaches the handler or gws.
|
|
132
|
+
const policyResult = evaluatePolicies([], ctx, service.gws_service);
|
|
133
|
+
if (policyResult.action === 'block') {
|
|
134
|
+
return {
|
|
135
|
+
text: `**Blocked by safety policy:** ${policyResult.reason}`,
|
|
136
|
+
refs: { blocked: true, policy: policyResult.reason },
|
|
137
|
+
};
|
|
138
|
+
}
|
|
139
|
+
// Check for a fully custom handler first
|
|
140
|
+
if (patch?.customHandlers?.[operation]) {
|
|
141
|
+
return patch.customHandlers[operation](params, account);
|
|
142
|
+
}
|
|
143
|
+
// Build gws args
|
|
144
|
+
let args = policyResult.action === 'downgrade' && policyResult.replacementArgs
|
|
145
|
+
? policyResult.replacementArgs
|
|
146
|
+
: buildArgs(service.gws_service, opDef, params);
|
|
147
|
+
// beforeExecute hook (service-specific)
|
|
148
|
+
if (patch?.beforeExecute?.[operation]) {
|
|
149
|
+
args = await patch.beforeExecute[operation](args, ctx);
|
|
150
|
+
}
|
|
151
|
+
// Execute gws
|
|
152
|
+
const result = await execute(args, { account, format: 'json' });
|
|
153
|
+
// afterExecute hook
|
|
154
|
+
let data = result.data;
|
|
155
|
+
if (patch?.afterExecute?.[operation]) {
|
|
156
|
+
data = await patch.afterExecute[operation](data, ctx);
|
|
157
|
+
}
|
|
158
|
+
// Format response
|
|
159
|
+
const contextMap = { email: account };
|
|
160
|
+
// Add relevant param values to context for next-steps placeholder resolution
|
|
161
|
+
for (const [key, value] of Object.entries(params)) {
|
|
162
|
+
if (typeof value === 'string')
|
|
163
|
+
contextMap[key] = value;
|
|
164
|
+
}
|
|
165
|
+
let formatted;
|
|
166
|
+
// Check for patch formatters by operation type
|
|
167
|
+
if (opDef.type === 'list' && patch?.formatList) {
|
|
168
|
+
formatted = patch.formatList(data, ctx);
|
|
169
|
+
}
|
|
170
|
+
else if (opDef.type === 'detail' && patch?.formatDetail) {
|
|
171
|
+
formatted = patch.formatDetail(data, ctx);
|
|
172
|
+
}
|
|
173
|
+
else if (opDef.type === 'action' && patch?.formatAction) {
|
|
174
|
+
formatted = patch.formatAction(data, ctx);
|
|
175
|
+
}
|
|
176
|
+
else {
|
|
177
|
+
formatted = formatDefault(data, opDef);
|
|
178
|
+
}
|
|
179
|
+
// Append next-steps
|
|
180
|
+
const stepsText = patch?.nextSteps
|
|
181
|
+
? patch.nextSteps(operation, contextMap)
|
|
182
|
+
: nextSteps(domain, operation, contextMap);
|
|
183
|
+
return {
|
|
184
|
+
text: formatted.text + stepsText,
|
|
185
|
+
refs: formatted.refs,
|
|
186
|
+
};
|
|
187
|
+
};
|
|
188
|
+
}
|
|
189
|
+
/** Build gws CLI args from an operation definition and user params. */
|
|
190
|
+
function buildArgs(gwsService, opDef, params) {
|
|
191
|
+
// Helper-based operations use positional/flag args
|
|
192
|
+
if (opDef.helper) {
|
|
193
|
+
return buildHelperArgs(gwsService, opDef, params);
|
|
194
|
+
}
|
|
195
|
+
// Resource-based operations use --params JSON
|
|
196
|
+
if (opDef.resource) {
|
|
197
|
+
return buildResourceArgs(gwsService, opDef, params);
|
|
198
|
+
}
|
|
199
|
+
throw new Error(`Operation must define either 'resource' or 'helper'`);
|
|
200
|
+
}
|
|
201
|
+
/** Build args for gws resource calls: `gws service resource method --params '{...}'` */
|
|
202
|
+
function buildResourceArgs(gwsService, opDef, params) {
|
|
203
|
+
const resourceParts = opDef.resource.split('.');
|
|
204
|
+
const args = [gwsService, ...resourceParts];
|
|
205
|
+
// Build the --params JSON object
|
|
206
|
+
const gwsParams = { ...opDef.defaults };
|
|
207
|
+
if (opDef.params) {
|
|
208
|
+
for (const [paramName, paramDef] of Object.entries(opDef.params)) {
|
|
209
|
+
const value = params[paramName];
|
|
210
|
+
const targetKey = paramDef.maps_to ?? paramName;
|
|
211
|
+
if (value !== undefined && value !== null) {
|
|
212
|
+
gwsParams[targetKey] = paramDef.max
|
|
213
|
+
? clamp(value, paramDef.default ?? 10, paramDef.max)
|
|
214
|
+
: value;
|
|
215
|
+
}
|
|
216
|
+
else if (paramDef.default !== undefined) {
|
|
217
|
+
gwsParams[targetKey] = paramDef.default;
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
// Clean undefined values
|
|
222
|
+
for (const [key, val] of Object.entries(gwsParams)) {
|
|
223
|
+
if (val === undefined)
|
|
224
|
+
delete gwsParams[key];
|
|
225
|
+
}
|
|
226
|
+
if (Object.keys(gwsParams).length > 0) {
|
|
227
|
+
args.push('--params', JSON.stringify(gwsParams));
|
|
228
|
+
}
|
|
229
|
+
return args;
|
|
230
|
+
}
|
|
231
|
+
/** Build args for gws helper calls: `gws service +helper --flag value ...` */
|
|
232
|
+
function buildHelperArgs(gwsService, opDef, params) {
|
|
233
|
+
const args = [gwsService, opDef.helper];
|
|
234
|
+
// Some helpers take a positional arg (e.g. +reply messageId)
|
|
235
|
+
// Check for required params that aren't in cli_args — those are positional
|
|
236
|
+
if (opDef.params) {
|
|
237
|
+
for (const [paramName, paramDef] of Object.entries(opDef.params)) {
|
|
238
|
+
const value = params[paramName];
|
|
239
|
+
if (value === undefined || value === null)
|
|
240
|
+
continue;
|
|
241
|
+
if (opDef.cli_args?.[paramName]) {
|
|
242
|
+
// Flag-style arg
|
|
243
|
+
args.push(opDef.cli_args[paramName], String(value));
|
|
244
|
+
}
|
|
245
|
+
else if (paramDef.required) {
|
|
246
|
+
// Positional arg
|
|
247
|
+
args.push(String(value));
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
return args;
|
|
252
|
+
}
|
|
253
|
+
//# sourceMappingURL=generator.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"generator.js","sourceRoot":"","sources":["../../src/factory/generator.ts"],"names":[],"mappings":"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"}
|