terse-cli 0.3.7 → 0.4.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/dist/commands/auth.js +3 -3
- package/dist/commands/auth.js.map +1 -1
- package/dist/commands/authOrg.js +3 -3
- package/dist/commands/authOrg.js.map +1 -1
- package/dist/commands/authStatus.d.ts.map +1 -1
- package/dist/commands/authStatus.js +2 -3
- package/dist/commands/authStatus.js.map +1 -1
- package/dist/commands/deploy.js +1 -2
- package/dist/commands/deploy.js.map +1 -1
- package/dist/commands/install.d.ts.map +1 -1
- package/dist/commands/install.js +2 -2
- package/dist/commands/install.js.map +1 -1
- package/dist/providers/typescript/TypeScriptProvider.js +1 -1
- package/dist/providers/typescript/TypeScriptProvider.js.map +1 -1
- package/package.json +3 -3
- package/dist/attach.d.ts +0 -3
- package/dist/attach.d.ts.map +0 -1
- package/dist/attach.js +0 -61
- package/dist/attach.js.map +0 -1
- package/dist/auth.d.ts +0 -15
- package/dist/auth.d.ts.map +0 -1
- package/dist/auth.js +0 -169
- package/dist/auth.js.map +0 -1
- package/dist/codegen.d.ts +0 -120
- package/dist/codegen.d.ts.map +0 -1
- package/dist/codegen.js +0 -811
- package/dist/codegen.js.map +0 -1
- package/dist/deploy.d.ts +0 -2
- package/dist/deploy.d.ts.map +0 -1
- package/dist/deploy.js +0 -101
- package/dist/deploy.js.map +0 -1
- package/dist/generate.d.ts +0 -2
- package/dist/generate.d.ts.map +0 -1
- package/dist/generate.js +0 -284
- package/dist/generate.js.map +0 -1
- package/dist/init.d.ts +0 -2
- package/dist/init.d.ts.map +0 -1
- package/dist/init.js +0 -149
- package/dist/init.js.map +0 -1
- package/dist/integrate.d.ts +0 -2
- package/dist/integrate.d.ts.map +0 -1
- package/dist/integrate.js +0 -34
- package/dist/integrate.js.map +0 -1
- package/dist/jsonOutput.d.ts +0 -19
- package/dist/jsonOutput.d.ts.map +0 -1
- package/dist/jsonOutput.js +0 -25
- package/dist/jsonOutput.js.map +0 -1
- package/dist/nonInteractive.d.ts +0 -18
- package/dist/nonInteractive.d.ts.map +0 -1
- package/dist/nonInteractive.js +0 -57
- package/dist/nonInteractive.js.map +0 -1
- package/dist/providers/python/PythonProvider.d.ts +0 -51
- package/dist/providers/python/PythonProvider.d.ts.map +0 -1
- package/dist/providers/python/PythonProvider.js +0 -413
- package/dist/providers/python/PythonProvider.js.map +0 -1
- package/dist/providers/python/preparePythonCodegenData.d.ts +0 -139
- package/dist/providers/python/preparePythonCodegenData.d.ts.map +0 -1
- package/dist/providers/python/preparePythonCodegenData.js +0 -452
- package/dist/providers/python/preparePythonCodegenData.js.map +0 -1
- package/dist/providers/python/pythonTemplateEngine.d.ts +0 -3
- package/dist/providers/python/pythonTemplateEngine.d.ts.map +0 -1
- package/dist/providers/python/pythonTemplateEngine.js +0 -30
- package/dist/providers/python/pythonTemplateEngine.js.map +0 -1
- package/dist/providers/shared/shellUtils.d.ts +0 -2
- package/dist/providers/shared/shellUtils.d.ts.map +0 -1
- package/dist/providers/shared/shellUtils.js +0 -25
- package/dist/providers/shared/shellUtils.js.map +0 -1
- package/dist/providers/typescript/parkSignals.d.ts +0 -7
- package/dist/providers/typescript/parkSignals.d.ts.map +0 -1
- package/dist/providers/typescript/parkSignals.js +0 -45
- package/dist/providers/typescript/parkSignals.js.map +0 -1
- package/dist/providers/typescript/prepareCodegenData.d.ts +0 -166
- package/dist/providers/typescript/prepareCodegenData.d.ts.map +0 -1
- package/dist/providers/typescript/prepareCodegenData.js +0 -1431
- package/dist/providers/typescript/prepareCodegenData.js.map +0 -1
- package/dist/replay.d.ts +0 -3
- package/dist/replay.d.ts.map +0 -1
- package/dist/replay.js +0 -20
- package/dist/replay.js.map +0 -1
- package/dist/run.d.ts +0 -12
- package/dist/run.d.ts.map +0 -1
- package/dist/run.js +0 -183
- package/dist/run.js.map +0 -1
- package/dist/sampleEventCache.d.ts +0 -9
- package/dist/sampleEventCache.d.ts.map +0 -1
- package/dist/sampleEventCache.js +0 -47
- package/dist/sampleEventCache.js.map +0 -1
- package/dist/sampleEventId.d.ts +0 -13
- package/dist/sampleEventId.d.ts.map +0 -1
- package/dist/sampleEventId.js +0 -20
- package/dist/sampleEventId.js.map +0 -1
- package/dist/shared/ApiRoutes.d.ts +0 -329
- package/dist/shared/ApiRoutes.d.ts.map +0 -1
- package/dist/shared/ApiRoutes.js +0 -309
- package/dist/shared/ApiRoutes.js.map +0 -1
- package/dist/shared/ApprovalTypes.d.ts +0 -29
- package/dist/shared/ApprovalTypes.d.ts.map +0 -1
- package/dist/shared/ApprovalTypes.js +0 -8
- package/dist/shared/ApprovalTypes.js.map +0 -1
- package/dist/shared/ClientBoundTools.d.ts +0 -37
- package/dist/shared/ClientBoundTools.d.ts.map +0 -1
- package/dist/shared/ClientBoundTools.js +0 -37
- package/dist/shared/ClientBoundTools.js.map +0 -1
- package/dist/shared/Configs.d.ts +0 -466
- package/dist/shared/Configs.d.ts.map +0 -1
- package/dist/shared/Configs.js +0 -755
- package/dist/shared/Configs.js.map +0 -1
- package/dist/shared/DonatedState.d.ts +0 -5
- package/dist/shared/DonatedState.d.ts.map +0 -1
- package/dist/shared/DonatedState.js +0 -13
- package/dist/shared/DonatedState.js.map +0 -1
- package/dist/shared/Entities.d.ts +0 -8
- package/dist/shared/Entities.d.ts.map +0 -1
- package/dist/shared/Entities.js +0 -9
- package/dist/shared/Entities.js.map +0 -1
- package/dist/shared/FrontendRoutes.d.ts +0 -54
- package/dist/shared/FrontendRoutes.d.ts.map +0 -1
- package/dist/shared/FrontendRoutes.js +0 -54
- package/dist/shared/FrontendRoutes.js.map +0 -1
- package/dist/shared/Integrations.d.ts +0 -218
- package/dist/shared/Integrations.d.ts.map +0 -1
- package/dist/shared/Integrations.js +0 -134
- package/dist/shared/Integrations.js.map +0 -1
- package/dist/shared/InvalidationKeys.d.ts +0 -52
- package/dist/shared/InvalidationKeys.d.ts.map +0 -1
- package/dist/shared/InvalidationKeys.js +0 -163
- package/dist/shared/InvalidationKeys.js.map +0 -1
- package/dist/shared/ModelEvents.d.ts +0 -167
- package/dist/shared/ModelEvents.d.ts.map +0 -1
- package/dist/shared/ModelEvents.js +0 -14
- package/dist/shared/ModelEvents.js.map +0 -1
- package/dist/shared/Notifications.d.ts +0 -43
- package/dist/shared/Notifications.d.ts.map +0 -1
- package/dist/shared/Notifications.js +0 -6
- package/dist/shared/Notifications.js.map +0 -1
- package/dist/shared/RunHistoryTypes.d.ts +0 -95
- package/dist/shared/RunHistoryTypes.d.ts.map +0 -1
- package/dist/shared/RunHistoryTypes.js +0 -11
- package/dist/shared/RunHistoryTypes.js.map +0 -1
- package/dist/shared/SentNotifications.d.ts +0 -30
- package/dist/shared/SentNotifications.d.ts.map +0 -1
- package/dist/shared/SentNotifications.js +0 -13
- package/dist/shared/SentNotifications.js.map +0 -1
- package/dist/shared/SocketEvents.d.ts +0 -49
- package/dist/shared/SocketEvents.d.ts.map +0 -1
- package/dist/shared/SocketEvents.js +0 -53
- package/dist/shared/SocketEvents.js.map +0 -1
- package/dist/shared/Survey.d.ts +0 -10
- package/dist/shared/Survey.d.ts.map +0 -1
- package/dist/shared/Survey.js +0 -2
- package/dist/shared/Survey.js.map +0 -1
- package/dist/shared/TicketSystem.d.ts +0 -154
- package/dist/shared/TicketSystem.d.ts.map +0 -1
- package/dist/shared/TicketSystem.js +0 -21
- package/dist/shared/TicketSystem.js.map +0 -1
- package/dist/shared/ToolDisplayUtils.d.ts +0 -30
- package/dist/shared/ToolDisplayUtils.d.ts.map +0 -1
- package/dist/shared/ToolDisplayUtils.js +0 -1067
- package/dist/shared/ToolDisplayUtils.js.map +0 -1
- package/dist/shared/ToolsTypes.d.ts +0 -18
- package/dist/shared/ToolsTypes.d.ts.map +0 -1
- package/dist/shared/ToolsTypes.js +0 -2
- package/dist/shared/ToolsTypes.js.map +0 -1
- package/dist/shared/types.d.ts +0 -827
- package/dist/shared/types.d.ts.map +0 -1
- package/dist/shared/types.js +0 -17
- package/dist/shared/types.js.map +0 -1
- package/dist/test.d.ts +0 -2
- package/dist/test.d.ts.map +0 -1
- package/dist/test.js +0 -62
- package/dist/test.js.map +0 -1
- package/dist/testRelay.d.ts +0 -6
- package/dist/testRelay.d.ts.map +0 -1
- package/dist/testRelay.js +0 -91
- package/dist/testRelay.js.map +0 -1
- package/dist/urls.d.ts +0 -3
- package/dist/urls.d.ts.map +0 -1
- package/dist/urls.js +0 -27
- package/dist/urls.js.map +0 -1
- package/dist/util.d.ts +0 -4
- package/dist/util.d.ts.map +0 -1
- package/dist/util.js +0 -5
- package/dist/util.js.map +0 -1
package/dist/codegen.js
DELETED
|
@@ -1,811 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Pure codegen function: integration data → TypeScript source text.
|
|
3
|
-
* No I/O — all side effects live in generate.ts.
|
|
4
|
-
*/
|
|
5
|
-
const EMPTY_SECTION = { code: "", imports: new Set() };
|
|
6
|
-
// ── Helpers ───────────────────────────────────────────────────────────
|
|
7
|
-
function toPascalCase(s) {
|
|
8
|
-
return s
|
|
9
|
-
.replace(/[^a-zA-Z0-9]+/g, " ")
|
|
10
|
-
.trim()
|
|
11
|
-
.split(/\s+/)
|
|
12
|
-
.map(w => w.charAt(0).toUpperCase() + w.slice(1))
|
|
13
|
-
.join("");
|
|
14
|
-
}
|
|
15
|
-
function sectionHeader(name) {
|
|
16
|
-
const dashes = "─".repeat(Math.max(1, 58 - name.length));
|
|
17
|
-
return `// ── ${name} ${dashes}`;
|
|
18
|
-
}
|
|
19
|
-
function escapeString(s) {
|
|
20
|
-
return s.replace(/\\/g, "\\\\").replace(/"/g, '\\"');
|
|
21
|
-
}
|
|
22
|
-
/** Map a tool's integration key back to the IntegrationType enum value used by ConfigInstance. */
|
|
23
|
-
function toolIntegrationToIntegrationType(toolIntegration) {
|
|
24
|
-
switch (toolIntegration) {
|
|
25
|
-
case "jira":
|
|
26
|
-
case "confluence":
|
|
27
|
-
return "atlassian";
|
|
28
|
-
default:
|
|
29
|
-
return toolIntegration;
|
|
30
|
-
}
|
|
31
|
-
}
|
|
32
|
-
function generateResourceClass(className, fields, staticNameField, items) {
|
|
33
|
-
const lines = [];
|
|
34
|
-
lines.push(`export class ${className} {`);
|
|
35
|
-
const ctorParams = fields.map(f => `public readonly ${f.classField}: ${f.type}`).join(", ");
|
|
36
|
-
lines.push(` constructor(${ctorParams}) {}`);
|
|
37
|
-
if (items.length > 0) {
|
|
38
|
-
lines.push("");
|
|
39
|
-
const usedNames = new Set();
|
|
40
|
-
for (const item of items) {
|
|
41
|
-
let name = toPascalCase(String(item[staticNameField] || "Unknown"));
|
|
42
|
-
if (!name || /^\d/.test(name))
|
|
43
|
-
name = `_${name}`;
|
|
44
|
-
while (usedNames.has(name))
|
|
45
|
-
name += "_";
|
|
46
|
-
usedNames.add(name);
|
|
47
|
-
const args = fields.map(f => {
|
|
48
|
-
const val = item[f.sourceField];
|
|
49
|
-
if (typeof val === "number")
|
|
50
|
-
return String(val);
|
|
51
|
-
return `"${escapeString(String(val ?? ""))}"`;
|
|
52
|
-
}).join(", ");
|
|
53
|
-
lines.push(` static ${name} = new ${className}(${args})`);
|
|
54
|
-
}
|
|
55
|
-
}
|
|
56
|
-
lines.push("}");
|
|
57
|
-
return lines.join("\n");
|
|
58
|
-
}
|
|
59
|
-
// ── GitHub ────────────────────────────────────────────────────────────
|
|
60
|
-
function generateGitHubSection(instances) {
|
|
61
|
-
if (instances.length === 0)
|
|
62
|
-
return EMPTY_SECTION;
|
|
63
|
-
const inst = instances[0];
|
|
64
|
-
const imports = new Set(["GitHubConfig"]);
|
|
65
|
-
const parts = [sectionHeader("GitHub"), ""];
|
|
66
|
-
const id = inst.integration.id;
|
|
67
|
-
const toStaticName = (raw, fallback) => {
|
|
68
|
-
let name = toPascalCase(raw || fallback);
|
|
69
|
-
if (!name)
|
|
70
|
-
name = fallback;
|
|
71
|
-
if (/^\d/.test(name))
|
|
72
|
-
name = `_${name}`;
|
|
73
|
-
return name;
|
|
74
|
-
};
|
|
75
|
-
const repositoriesWithFullName = inst.repositories.map(repo => {
|
|
76
|
-
const owner = repo.owner || "UnknownOwner";
|
|
77
|
-
const fullName = repo.owner && repo.name ? `${repo.owner}/${repo.name}` : repo.name;
|
|
78
|
-
return { ...repo, owner, fullName };
|
|
79
|
-
});
|
|
80
|
-
const ownerEntries = new Map();
|
|
81
|
-
const usedOwnerNames = new Set();
|
|
82
|
-
for (const repo of repositoriesWithFullName) {
|
|
83
|
-
if (!ownerEntries.has(repo.owner)) {
|
|
84
|
-
let ownerStaticName = toStaticName(repo.owner, "UnknownOwner");
|
|
85
|
-
while (usedOwnerNames.has(ownerStaticName))
|
|
86
|
-
ownerStaticName += "_";
|
|
87
|
-
usedOwnerNames.add(ownerStaticName);
|
|
88
|
-
ownerEntries.set(repo.owner, { staticName: ownerStaticName, repos: [] });
|
|
89
|
-
}
|
|
90
|
-
ownerEntries.get(repo.owner).repos.push(repo);
|
|
91
|
-
}
|
|
92
|
-
parts.push("export class GithubOwner {");
|
|
93
|
-
parts.push(" constructor(public readonly name: string) {}");
|
|
94
|
-
if (ownerEntries.size > 0) {
|
|
95
|
-
parts.push("");
|
|
96
|
-
for (const [owner, data] of ownerEntries) {
|
|
97
|
-
parts.push(` static ${data.staticName} = new GithubOwner("${escapeString(owner)}")`);
|
|
98
|
-
}
|
|
99
|
-
}
|
|
100
|
-
parts.push("}");
|
|
101
|
-
parts.push("");
|
|
102
|
-
parts.push("export class Repos {");
|
|
103
|
-
parts.push(" constructor(");
|
|
104
|
-
parts.push(" public readonly repositoryId: number,");
|
|
105
|
-
parts.push(" public readonly name: string,");
|
|
106
|
-
parts.push(" public readonly owner: GithubOwner,");
|
|
107
|
-
parts.push(" public readonly fullName: string");
|
|
108
|
-
parts.push(" ) {}");
|
|
109
|
-
if (ownerEntries.size > 0) {
|
|
110
|
-
parts.push("");
|
|
111
|
-
for (const [, data] of ownerEntries) {
|
|
112
|
-
const usedRepoNames = new Set();
|
|
113
|
-
parts.push(` static ${data.staticName} = {`);
|
|
114
|
-
for (const repo of data.repos) {
|
|
115
|
-
let repoStaticName = toStaticName(repo.name, "Repos");
|
|
116
|
-
while (usedRepoNames.has(repoStaticName))
|
|
117
|
-
repoStaticName += "_";
|
|
118
|
-
usedRepoNames.add(repoStaticName);
|
|
119
|
-
parts.push(` ${repoStaticName}: new Repos(${repo.id}, "${escapeString(repo.name)}", GithubOwner.${data.staticName}, "${escapeString(repo.fullName)}"),`);
|
|
120
|
-
}
|
|
121
|
-
parts.push(" } as const");
|
|
122
|
-
}
|
|
123
|
-
}
|
|
124
|
-
parts.push("}");
|
|
125
|
-
parts.push("");
|
|
126
|
-
imports.add("GitHubEventType");
|
|
127
|
-
imports.add("TypedTrigger");
|
|
128
|
-
imports.add("GithubPRInputEvent");
|
|
129
|
-
imports.add("GithubPushInputEvent");
|
|
130
|
-
imports.add("GithubInputEvent");
|
|
131
|
-
// Namespace object with typed event triggers
|
|
132
|
-
parts.push("export const GitHub = {");
|
|
133
|
-
parts.push(` /** Trigger on push to a repository */`);
|
|
134
|
-
parts.push(` onPush(opts: { repo: Repos }): TypedTrigger<GithubPushInputEvent> {`);
|
|
135
|
-
parts.push(` return new GitHubConfig("${id}", [opts.repo.repositoryId], [GitHubEventType.PUSH]) as TypedTrigger<GithubPushInputEvent>`);
|
|
136
|
-
parts.push(" },");
|
|
137
|
-
parts.push(` /** Trigger when a pull request is opened */`);
|
|
138
|
-
parts.push(` onPROpened(opts: { repo: Repos }): TypedTrigger<GithubPRInputEvent> {`);
|
|
139
|
-
parts.push(` return new GitHubConfig("${id}", [opts.repo.repositoryId], [GitHubEventType.PR_OPENED]) as TypedTrigger<GithubPRInputEvent>`);
|
|
140
|
-
parts.push(" },");
|
|
141
|
-
parts.push(` /** Trigger when a pull request is merged */`);
|
|
142
|
-
parts.push(` onPRMerged(opts: { repo: Repos }): TypedTrigger<GithubPRInputEvent> {`);
|
|
143
|
-
parts.push(` return new GitHubConfig("${id}", [opts.repo.repositoryId], [GitHubEventType.PR_MERGED]) as TypedTrigger<GithubPRInputEvent>`);
|
|
144
|
-
parts.push(" },");
|
|
145
|
-
parts.push(` /** Trigger when a pull request is closed */`);
|
|
146
|
-
parts.push(` onPRClosed(opts: { repo: Repos }): TypedTrigger<GithubPRInputEvent> {`);
|
|
147
|
-
parts.push(` return new GitHubConfig("${id}", [opts.repo.repositoryId], [GitHubEventType.PR_CLOSED]) as TypedTrigger<GithubPRInputEvent>`);
|
|
148
|
-
parts.push(" },");
|
|
149
|
-
parts.push(` /** Trigger on any pull request event */`);
|
|
150
|
-
parts.push(` onPR(opts: { repo: Repos }): TypedTrigger<GithubPRInputEvent> {`);
|
|
151
|
-
parts.push(` return new GitHubConfig("${id}", [opts.repo.repositoryId], [GitHubEventType.PR_OPENED, GitHubEventType.PR_MERGED, GitHubEventType.PR_CLOSED, GitHubEventType.PR_SYNCHRONIZE]) as TypedTrigger<GithubPRInputEvent>`);
|
|
152
|
-
parts.push(" },");
|
|
153
|
-
parts.push(` /** Trigger on specific GitHub events for the given repositories */`);
|
|
154
|
-
parts.push(` trigger(opts: { repos: Repos[]; eventTypes?: GitHubEventType[] }): TypedTrigger<GithubInputEvent> {`);
|
|
155
|
-
parts.push(` return new GitHubConfig("${id}", opts.repos.map(r => r.repositoryId), opts.eventTypes) as TypedTrigger<GithubInputEvent>`);
|
|
156
|
-
parts.push(" },");
|
|
157
|
-
parts.push(` /** Use in \`skills[]\` */`);
|
|
158
|
-
parts.push(` skill(opts: { repos: Repos[] }): GitHubConfig {`);
|
|
159
|
-
parts.push(` return new GitHubConfig("${id}", opts.repos.map(r => r.repositoryId))`);
|
|
160
|
-
parts.push(" },");
|
|
161
|
-
parts.push("}");
|
|
162
|
-
parts.push("");
|
|
163
|
-
return { code: parts.join("\n"), imports };
|
|
164
|
-
}
|
|
165
|
-
// ── Gmail ─────────────────────────────────────────────────────────────
|
|
166
|
-
function generateGmailSection(instances) {
|
|
167
|
-
if (instances.length === 0)
|
|
168
|
-
return EMPTY_SECTION;
|
|
169
|
-
const imports = new Set(["GmailConfig", "GmailOutputConfig", "GmailDraftOutputConfig"]);
|
|
170
|
-
const parts = [sectionHeader("Gmail"), ""];
|
|
171
|
-
const id = instances[0].id;
|
|
172
|
-
imports.add("GmailEventType");
|
|
173
|
-
parts.push("export const Gmail = {");
|
|
174
|
-
parts.push(` /** Trigger when a new email is received */`);
|
|
175
|
-
parts.push(` onEmail(): GmailConfig {`);
|
|
176
|
-
parts.push(` return new GmailConfig("${id}", [GmailEventType.EMAIL_RECEIVED])`);
|
|
177
|
-
parts.push(" },");
|
|
178
|
-
parts.push(` /** Trigger on all Gmail events */`);
|
|
179
|
-
parts.push(` trigger(opts?: { eventTypes?: GmailEventType[] }): GmailConfig {`);
|
|
180
|
-
parts.push(` return new GmailConfig("${id}", opts?.eventTypes)`);
|
|
181
|
-
parts.push(" },");
|
|
182
|
-
parts.push(` /** Use in \`skills[]\` */`);
|
|
183
|
-
parts.push(` skill(): GmailOutputConfig {`);
|
|
184
|
-
parts.push(` return new GmailOutputConfig("${id}")`);
|
|
185
|
-
parts.push(" },");
|
|
186
|
-
parts.push(` /** Use in \`skills[]\` — creates draft emails */`);
|
|
187
|
-
parts.push(` draftSkill(): GmailDraftOutputConfig {`);
|
|
188
|
-
parts.push(` return new GmailDraftOutputConfig("${id}")`);
|
|
189
|
-
parts.push(" },");
|
|
190
|
-
parts.push("}");
|
|
191
|
-
parts.push("");
|
|
192
|
-
return { code: parts.join("\n"), imports };
|
|
193
|
-
}
|
|
194
|
-
// ── Slack ──────────────────────────────────────────────────────────────
|
|
195
|
-
function generateSlackSection(instances) {
|
|
196
|
-
if (instances.length === 0)
|
|
197
|
-
return EMPTY_SECTION;
|
|
198
|
-
const inst = instances[0];
|
|
199
|
-
const imports = new Set(["SlackConfig", "SlackOutputConfig"]);
|
|
200
|
-
const parts = [sectionHeader("Slack"), ""];
|
|
201
|
-
const id = inst.id;
|
|
202
|
-
parts.push(generateResourceClass("SlackChannel", [
|
|
203
|
-
{ classField: "channelId", type: "string", sourceField: "id" },
|
|
204
|
-
{ classField: "name", type: "string", sourceField: "name" },
|
|
205
|
-
], "name", inst.channels));
|
|
206
|
-
parts.push("");
|
|
207
|
-
imports.add("SlackEventType");
|
|
208
|
-
// Namespace object with typed event triggers
|
|
209
|
-
parts.push("export const Slack = {");
|
|
210
|
-
parts.push(` /** Trigger on any message in a channel */`);
|
|
211
|
-
parts.push(` onMessage(opts: { channel: SlackChannel; userIds?: string[] }): SlackConfig {`);
|
|
212
|
-
parts.push(` return new SlackConfig("${id}", opts.channel.channelId, opts.channel.name, false, opts.userIds, [SlackEventType.MESSAGE])`);
|
|
213
|
-
parts.push(" },");
|
|
214
|
-
parts.push(` /** Trigger on direct messages to the bot */`);
|
|
215
|
-
parts.push(` onDm(opts?: { userIds?: string[] }): SlackConfig {`);
|
|
216
|
-
parts.push(` return new SlackConfig("${id}", undefined, undefined, true, opts?.userIds, [SlackEventType.MESSAGE])`);
|
|
217
|
-
parts.push(" },");
|
|
218
|
-
parts.push(` /** Trigger on all Slack events (messages + DMs) for a channel */`);
|
|
219
|
-
parts.push(` trigger(opts?: { channel?: SlackChannel; listenToUserDms?: boolean; userIds?: string[]; eventTypes?: SlackEventType[] }): SlackConfig {`);
|
|
220
|
-
parts.push(` return new SlackConfig("${id}", opts?.channel?.channelId, opts?.channel?.name, opts?.listenToUserDms, opts?.userIds, opts?.eventTypes)`);
|
|
221
|
-
parts.push(" },");
|
|
222
|
-
parts.push(` /** Use in \`skills[]\` */`);
|
|
223
|
-
parts.push(` skill(opts?: { channel?: SlackChannel; userIds?: string[]; userNames?: string[]; listenToUserDms?: boolean }): SlackOutputConfig {`);
|
|
224
|
-
parts.push(` return new SlackOutputConfig("${id}", opts?.channel?.channelId, opts?.channel?.name, opts?.userIds, opts?.userNames, opts?.listenToUserDms)`);
|
|
225
|
-
parts.push(" },");
|
|
226
|
-
parts.push("}");
|
|
227
|
-
parts.push("");
|
|
228
|
-
return { code: parts.join("\n"), imports };
|
|
229
|
-
}
|
|
230
|
-
// ── Figma ─────────────────────────────────────────────────────────────
|
|
231
|
-
function generateFigmaSection(instances) {
|
|
232
|
-
if (instances.length === 0)
|
|
233
|
-
return EMPTY_SECTION;
|
|
234
|
-
const imports = new Set(["FigmaConfig"]);
|
|
235
|
-
const parts = [sectionHeader("Figma"), ""];
|
|
236
|
-
const id = instances[0].id;
|
|
237
|
-
imports.add("FigmaEventType");
|
|
238
|
-
parts.push("export const Figma = {");
|
|
239
|
-
parts.push(` /** Trigger when a comment is added to a Figma file */`);
|
|
240
|
-
parts.push(` onComment(opts: { fileKey: string; fileName: string; teamId: string }): FigmaConfig {`);
|
|
241
|
-
parts.push(` return new FigmaConfig("${id}", opts.fileKey, opts.fileName, opts.teamId, [FigmaEventType.FILE_COMMENT])`);
|
|
242
|
-
parts.push(" },");
|
|
243
|
-
parts.push(` /** Trigger on all Figma events */`);
|
|
244
|
-
parts.push(` trigger(opts: { fileKey: string; fileName: string; teamId: string; eventTypes?: FigmaEventType[] }): FigmaConfig {`);
|
|
245
|
-
parts.push(` return new FigmaConfig("${id}", opts.fileKey, opts.fileName, opts.teamId, opts.eventTypes)`);
|
|
246
|
-
parts.push(" },");
|
|
247
|
-
parts.push("}");
|
|
248
|
-
parts.push("");
|
|
249
|
-
return { code: parts.join("\n"), imports };
|
|
250
|
-
}
|
|
251
|
-
// ── Linear ────────────────────────────────────────────────────────────
|
|
252
|
-
function generateLinearSection(instances) {
|
|
253
|
-
if (instances.length === 0)
|
|
254
|
-
return EMPTY_SECTION;
|
|
255
|
-
const inst = instances[0];
|
|
256
|
-
const imports = new Set(["LinearInputConfig", "LinearOutputConfig"]);
|
|
257
|
-
const parts = [sectionHeader("Linear"), ""];
|
|
258
|
-
const id = inst.id;
|
|
259
|
-
parts.push(generateResourceClass("LinearTeam", [
|
|
260
|
-
{ classField: "teamId", type: "string", sourceField: "id" },
|
|
261
|
-
{ classField: "name", type: "string", sourceField: "name" },
|
|
262
|
-
{ classField: "key", type: "string", sourceField: "key" },
|
|
263
|
-
], "name", inst.teams));
|
|
264
|
-
parts.push("");
|
|
265
|
-
imports.add("LinearEventType");
|
|
266
|
-
parts.push("export const Linear = {");
|
|
267
|
-
parts.push(` /** Trigger when a new issue is created */`);
|
|
268
|
-
parts.push(` onIssueCreated(opts?: { projectId?: string; projectName?: string }): LinearInputConfig {`);
|
|
269
|
-
parts.push(` return new LinearInputConfig("${id}", opts?.projectId, opts?.projectName, [LinearEventType.ISSUE_CREATED])`);
|
|
270
|
-
parts.push(" },");
|
|
271
|
-
parts.push(` /** Trigger when an issue is updated */`);
|
|
272
|
-
parts.push(` onIssueUpdated(opts?: { projectId?: string; projectName?: string }): LinearInputConfig {`);
|
|
273
|
-
parts.push(` return new LinearInputConfig("${id}", opts?.projectId, opts?.projectName, [LinearEventType.ISSUE_UPDATED])`);
|
|
274
|
-
parts.push(" },");
|
|
275
|
-
parts.push(` /** Trigger when a comment is added to an issue */`);
|
|
276
|
-
parts.push(` onComment(opts?: { projectId?: string; projectName?: string }): LinearInputConfig {`);
|
|
277
|
-
parts.push(` return new LinearInputConfig("${id}", opts?.projectId, opts?.projectName, [LinearEventType.COMMENT_CREATED])`);
|
|
278
|
-
parts.push(" },");
|
|
279
|
-
parts.push(` /** Trigger on all Linear events */`);
|
|
280
|
-
parts.push(` trigger(opts?: { projectId?: string; projectName?: string; eventTypes?: LinearEventType[] }): LinearInputConfig {`);
|
|
281
|
-
parts.push(` return new LinearInputConfig("${id}", opts?.projectId, opts?.projectName, opts?.eventTypes)`);
|
|
282
|
-
parts.push(" },");
|
|
283
|
-
parts.push(` /** Use in \`skills[]\` */`);
|
|
284
|
-
parts.push(` skill(opts?: { team?: LinearTeam; projectId?: string; projectName?: string }): LinearOutputConfig {`);
|
|
285
|
-
parts.push(` return new LinearOutputConfig("${id}", opts?.team?.teamId, opts?.team?.name, opts?.projectId, opts?.projectName)`);
|
|
286
|
-
parts.push(" },");
|
|
287
|
-
parts.push("}");
|
|
288
|
-
parts.push("");
|
|
289
|
-
return { code: parts.join("\n"), imports };
|
|
290
|
-
}
|
|
291
|
-
// ── Jira & Confluence (Atlassian) ─────────────────────────────────────
|
|
292
|
-
function generateAtlassianSection(instances) {
|
|
293
|
-
if (instances.length === 0)
|
|
294
|
-
return EMPTY_SECTION;
|
|
295
|
-
const inst = instances[0];
|
|
296
|
-
const imports = new Set(["JiraConfig", "ConfluenceConfig"]);
|
|
297
|
-
const parts = [sectionHeader("Jira & Confluence"), ""];
|
|
298
|
-
const id = inst.id;
|
|
299
|
-
parts.push(generateResourceClass("JiraProject", [
|
|
300
|
-
{ classField: "projectKey", type: "string", sourceField: "key" },
|
|
301
|
-
{ classField: "projectId", type: "string", sourceField: "id" },
|
|
302
|
-
{ classField: "name", type: "string", sourceField: "name" },
|
|
303
|
-
], "name", inst.jiraProjects));
|
|
304
|
-
parts.push("");
|
|
305
|
-
parts.push(generateResourceClass("ConfluencePage", [
|
|
306
|
-
{ classField: "pageId", type: "string", sourceField: "id" },
|
|
307
|
-
{ classField: "title", type: "string", sourceField: "title" },
|
|
308
|
-
{ classField: "spaceId", type: "string", sourceField: "spaceId" },
|
|
309
|
-
{ classField: "spaceName", type: "string", sourceField: "spaceName" },
|
|
310
|
-
], "title", inst.confluencePages));
|
|
311
|
-
parts.push("");
|
|
312
|
-
imports.add("JiraEventType");
|
|
313
|
-
parts.push("export const Jira = {");
|
|
314
|
-
parts.push(` /** Trigger when a Jira issue is created */`);
|
|
315
|
-
parts.push(` onIssueCreated(opts?: { project?: JiraProject }): JiraConfig {`);
|
|
316
|
-
parts.push(` return new JiraConfig("${id}", opts?.project?.projectKey, opts?.project?.projectId, [JiraEventType.ISSUE_CREATED])`);
|
|
317
|
-
parts.push(" },");
|
|
318
|
-
parts.push(` /** Trigger when a Jira issue is updated */`);
|
|
319
|
-
parts.push(` onIssueUpdated(opts?: { project?: JiraProject }): JiraConfig {`);
|
|
320
|
-
parts.push(` return new JiraConfig("${id}", opts?.project?.projectKey, opts?.project?.projectId, [JiraEventType.ISSUE_UPDATED])`);
|
|
321
|
-
parts.push(" },");
|
|
322
|
-
parts.push(` /** Trigger on all Jira events */`);
|
|
323
|
-
parts.push(` trigger(opts?: { project?: JiraProject; eventTypes?: JiraEventType[] }): JiraConfig {`);
|
|
324
|
-
parts.push(` return new JiraConfig("${id}", opts?.project?.projectKey, opts?.project?.projectId, opts?.eventTypes)`);
|
|
325
|
-
parts.push(" },");
|
|
326
|
-
parts.push(` /** Use in \`skills[]\` */`);
|
|
327
|
-
parts.push(` skill(opts?: { project?: JiraProject }): JiraConfig {`);
|
|
328
|
-
parts.push(` return new JiraConfig("${id}", opts?.project?.projectKey, opts?.project?.projectId)`);
|
|
329
|
-
parts.push(" },");
|
|
330
|
-
parts.push("}");
|
|
331
|
-
parts.push("");
|
|
332
|
-
parts.push("export const Confluence = {");
|
|
333
|
-
parts.push(` /** Use in \`skills[]\` */`);
|
|
334
|
-
parts.push(` skill(opts: { page: ConfluencePage }): ConfluenceConfig {`);
|
|
335
|
-
parts.push(` return new ConfluenceConfig("${id}", opts.page.spaceName, opts.page.spaceId, opts.page.pageId, opts.page.title)`);
|
|
336
|
-
parts.push(" },");
|
|
337
|
-
parts.push("}");
|
|
338
|
-
parts.push("");
|
|
339
|
-
return { code: parts.join("\n"), imports };
|
|
340
|
-
}
|
|
341
|
-
// ── Notion ────────────────────────────────────────────────────────────
|
|
342
|
-
function generateNotionSection(instances) {
|
|
343
|
-
if (instances.length === 0)
|
|
344
|
-
return EMPTY_SECTION;
|
|
345
|
-
const inst = instances[0];
|
|
346
|
-
const imports = new Set(["NotionConfig"]);
|
|
347
|
-
const parts = [sectionHeader("Notion"), ""];
|
|
348
|
-
const id = inst.id;
|
|
349
|
-
parts.push(generateResourceClass("NotionDatabase", [
|
|
350
|
-
{ classField: "databaseId", type: "string", sourceField: "id" },
|
|
351
|
-
{ classField: "title", type: "string", sourceField: "title" },
|
|
352
|
-
], "title", inst.databases));
|
|
353
|
-
parts.push("");
|
|
354
|
-
parts.push(generateResourceClass("NotionPage", [
|
|
355
|
-
{ classField: "pageId", type: "string", sourceField: "id" },
|
|
356
|
-
{ classField: "title", type: "string", sourceField: "title" },
|
|
357
|
-
], "title", inst.pages));
|
|
358
|
-
parts.push("");
|
|
359
|
-
parts.push("export const Notion = {");
|
|
360
|
-
parts.push(` /** Use in \`skills[]\` */`);
|
|
361
|
-
parts.push(` skill(opts?: { databases?: NotionDatabase[]; pages?: NotionPage[] }): NotionConfig {`);
|
|
362
|
-
parts.push(` return new NotionConfig("${id}",`);
|
|
363
|
-
parts.push(` opts?.databases?.map(d => d.databaseId), opts?.databases?.map(d => d.title),`);
|
|
364
|
-
parts.push(` opts?.pages?.map(p => p.pageId), opts?.pages?.map(p => p.title))`);
|
|
365
|
-
parts.push(" },");
|
|
366
|
-
parts.push("}");
|
|
367
|
-
parts.push("");
|
|
368
|
-
return { code: parts.join("\n"), imports };
|
|
369
|
-
}
|
|
370
|
-
// ── PostHog ───────────────────────────────────────────────────────────
|
|
371
|
-
function generatePosthogSection(instances) {
|
|
372
|
-
if (instances.length === 0)
|
|
373
|
-
return EMPTY_SECTION;
|
|
374
|
-
const inst = instances[0];
|
|
375
|
-
const imports = new Set(["PosthogConfig"]);
|
|
376
|
-
const parts = [sectionHeader("PostHog"), ""];
|
|
377
|
-
const id = inst.id;
|
|
378
|
-
parts.push(generateResourceClass("PosthogProject", [
|
|
379
|
-
{ classField: "projectId", type: "string", sourceField: "id" },
|
|
380
|
-
{ classField: "name", type: "string", sourceField: "name" },
|
|
381
|
-
], "name", inst.projects));
|
|
382
|
-
parts.push("");
|
|
383
|
-
parts.push("export const Posthog = {");
|
|
384
|
-
parts.push(` /** Use in \`skills[]\` */`);
|
|
385
|
-
parts.push(` skill(opts: { project: PosthogProject }): PosthogConfig {`);
|
|
386
|
-
parts.push(` return new PosthogConfig("${id}", opts.project.projectId, opts.project.name)`);
|
|
387
|
-
parts.push(" },");
|
|
388
|
-
parts.push("}");
|
|
389
|
-
parts.push("");
|
|
390
|
-
return { code: parts.join("\n"), imports };
|
|
391
|
-
}
|
|
392
|
-
// ── Datadog ───────────────────────────────────────────────────────────
|
|
393
|
-
function generateDatadogSection(instances) {
|
|
394
|
-
if (instances.length === 0)
|
|
395
|
-
return EMPTY_SECTION;
|
|
396
|
-
const inst = instances[0];
|
|
397
|
-
const imports = new Set(["DatadogConfig"]);
|
|
398
|
-
const parts = [sectionHeader("Datadog"), ""];
|
|
399
|
-
const id = inst.id;
|
|
400
|
-
parts.push(generateResourceClass("DatadogIndex", [
|
|
401
|
-
{ classField: "name", type: "string", sourceField: "name" },
|
|
402
|
-
], "name", inst.indexes));
|
|
403
|
-
parts.push("");
|
|
404
|
-
parts.push("export const Datadog = {");
|
|
405
|
-
parts.push(` /** Use in \`skills[]\` */`);
|
|
406
|
-
parts.push(` skill(opts?: { indexes?: DatadogIndex[] }): DatadogConfig {`);
|
|
407
|
-
parts.push(` return new DatadogConfig("${id}", opts?.indexes?.map(i => i.name))`);
|
|
408
|
-
parts.push(" },");
|
|
409
|
-
parts.push("}");
|
|
410
|
-
parts.push("");
|
|
411
|
-
return { code: parts.join("\n"), imports };
|
|
412
|
-
}
|
|
413
|
-
// ── LaunchDarkly ──────────────────────────────────────────────────────
|
|
414
|
-
function generateLaunchDarklySection(instances) {
|
|
415
|
-
if (instances.length === 0)
|
|
416
|
-
return EMPTY_SECTION;
|
|
417
|
-
const inst = instances[0];
|
|
418
|
-
const imports = new Set(["LaunchDarklyConfig"]);
|
|
419
|
-
const parts = [sectionHeader("LaunchDarkly"), ""];
|
|
420
|
-
const id = inst.id;
|
|
421
|
-
parts.push(generateResourceClass("LaunchDarklyProject", [
|
|
422
|
-
{ classField: "projectKey", type: "string", sourceField: "key" },
|
|
423
|
-
{ classField: "name", type: "string", sourceField: "name" },
|
|
424
|
-
], "name", inst.projects));
|
|
425
|
-
parts.push("");
|
|
426
|
-
parts.push("export const LaunchDarkly = {");
|
|
427
|
-
parts.push(` /** Use in \`skills[]\` */`);
|
|
428
|
-
parts.push(` skill(opts: { project: LaunchDarklyProject; environmentKeys: string[] }): LaunchDarklyConfig {`);
|
|
429
|
-
parts.push(` return new LaunchDarklyConfig("${id}", opts.project.projectKey, opts.environmentKeys)`);
|
|
430
|
-
parts.push(" },");
|
|
431
|
-
parts.push("}");
|
|
432
|
-
parts.push("");
|
|
433
|
-
return { code: parts.join("\n"), imports };
|
|
434
|
-
}
|
|
435
|
-
// ── WorkOS ────────────────────────────────────────────────────────────
|
|
436
|
-
function generateWorkOSSection(instances) {
|
|
437
|
-
if (instances.length === 0)
|
|
438
|
-
return EMPTY_SECTION;
|
|
439
|
-
const imports = new Set(["WorkOSInputConfig", "WorkOSOutputConfig", "WorkOSEventType"]);
|
|
440
|
-
const parts = [sectionHeader("WorkOS"), ""];
|
|
441
|
-
const id = instances[0].id;
|
|
442
|
-
parts.push("export const WorkOS = {");
|
|
443
|
-
parts.push(` /** Trigger on user creation */`);
|
|
444
|
-
parts.push(` onUserCreated(): WorkOSInputConfig {`);
|
|
445
|
-
parts.push(` return new WorkOSInputConfig("${id}", [WorkOSEventType.USER_CREATED])`);
|
|
446
|
-
parts.push(" },");
|
|
447
|
-
parts.push(` /** Trigger on user update */`);
|
|
448
|
-
parts.push(` onUserUpdated(): WorkOSInputConfig {`);
|
|
449
|
-
parts.push(` return new WorkOSInputConfig("${id}", [WorkOSEventType.USER_UPDATED])`);
|
|
450
|
-
parts.push(" },");
|
|
451
|
-
parts.push(` /** Trigger on user deletion */`);
|
|
452
|
-
parts.push(` onUserDeleted(): WorkOSInputConfig {`);
|
|
453
|
-
parts.push(` return new WorkOSInputConfig("${id}", [WorkOSEventType.USER_DELETED])`);
|
|
454
|
-
parts.push(" },");
|
|
455
|
-
parts.push(` /** Trigger on organization membership change */`);
|
|
456
|
-
parts.push(` onMembershipChanged(): WorkOSInputConfig {`);
|
|
457
|
-
parts.push(` return new WorkOSInputConfig("${id}", [WorkOSEventType.ORGANIZATION_MEMBERSHIP_CREATED, WorkOSEventType.ORGANIZATION_MEMBERSHIP_UPDATED, WorkOSEventType.ORGANIZATION_MEMBERSHIP_DELETED])`);
|
|
458
|
-
parts.push(" },");
|
|
459
|
-
parts.push(` /** Trigger on invitation accepted */`);
|
|
460
|
-
parts.push(` onInvitationAccepted(): WorkOSInputConfig {`);
|
|
461
|
-
parts.push(` return new WorkOSInputConfig("${id}", [WorkOSEventType.INVITATION_ACCEPTED])`);
|
|
462
|
-
parts.push(" },");
|
|
463
|
-
parts.push(` /** Trigger on specific WorkOS event types */`);
|
|
464
|
-
parts.push(` trigger(opts?: { eventTypes?: WorkOSEventType[] }): WorkOSInputConfig {`);
|
|
465
|
-
parts.push(` return new WorkOSInputConfig("${id}", opts?.eventTypes)`);
|
|
466
|
-
parts.push(" },");
|
|
467
|
-
parts.push(` /** Use in \`skills[]\` */`);
|
|
468
|
-
parts.push(` skill(): WorkOSOutputConfig {`);
|
|
469
|
-
parts.push(` return new WorkOSOutputConfig("${id}")`);
|
|
470
|
-
parts.push(" },");
|
|
471
|
-
parts.push("}");
|
|
472
|
-
parts.push("");
|
|
473
|
-
return { code: parts.join("\n"), imports };
|
|
474
|
-
}
|
|
475
|
-
// ── Attio ─────────────────────────────────────────────────────────────
|
|
476
|
-
function generateAttioSection(instances) {
|
|
477
|
-
if (instances.length === 0)
|
|
478
|
-
return EMPTY_SECTION;
|
|
479
|
-
const inst = instances[0];
|
|
480
|
-
const imports = new Set(["AttioOutputConfig"]);
|
|
481
|
-
const parts = [sectionHeader("Attio"), ""];
|
|
482
|
-
const id = inst.id;
|
|
483
|
-
parts.push(generateResourceClass("AttioObject", [
|
|
484
|
-
{ classField: "apiSlug", type: "string", sourceField: "api_slug" },
|
|
485
|
-
{ classField: "name", type: "string", sourceField: "singular_noun" },
|
|
486
|
-
], "singular_noun", inst.objects));
|
|
487
|
-
parts.push("");
|
|
488
|
-
parts.push("export const Attio = {");
|
|
489
|
-
parts.push(` /** Use in \`skills[]\` */`);
|
|
490
|
-
parts.push(` skill(opts?: { object?: AttioObject }): AttioOutputConfig {`);
|
|
491
|
-
parts.push(` return new AttioOutputConfig("${id}", opts?.object?.apiSlug)`);
|
|
492
|
-
parts.push(" },");
|
|
493
|
-
parts.push("}");
|
|
494
|
-
parts.push("");
|
|
495
|
-
return { code: parts.join("\n"), imports };
|
|
496
|
-
}
|
|
497
|
-
// ── Typed Tools ───────────────────────────────────────────────────────
|
|
498
|
-
/**
|
|
499
|
-
* Converts a displayName like "Create ticket" to camelCase like "createTicket".
|
|
500
|
-
*/
|
|
501
|
-
function toCamelCase(s) {
|
|
502
|
-
const pascal = toPascalCase(s);
|
|
503
|
-
return pascal.charAt(0).toLowerCase() + pascal.slice(1);
|
|
504
|
-
}
|
|
505
|
-
/**
|
|
506
|
-
* Converts a tool name like "linear_create_ticket" to a PascalCase interface name
|
|
507
|
-
* like "LinearCreateTicketParams".
|
|
508
|
-
*/
|
|
509
|
-
function toolNameToInterfaceName(name) {
|
|
510
|
-
return name.split("_").map(w => w.charAt(0).toUpperCase() + w.slice(1)).join("") + "Params";
|
|
511
|
-
}
|
|
512
|
-
/**
|
|
513
|
-
* Converts a JSON Schema type to a TypeScript type string.
|
|
514
|
-
* Handles nested objects, arrays, enums, anyOf (nullable), and primitives.
|
|
515
|
-
*/
|
|
516
|
-
function jsonSchemaToTs(schema, indent) {
|
|
517
|
-
if (!schema)
|
|
518
|
-
return "unknown";
|
|
519
|
-
// Handle enums
|
|
520
|
-
if (schema.enum) {
|
|
521
|
-
return schema.enum.map(v => typeof v === "string" ? `"${escapeString(v)}"` : String(v)).join(" | ");
|
|
522
|
-
}
|
|
523
|
-
// Handle anyOf (commonly used for nullable: [X, { type: "null" }])
|
|
524
|
-
if (schema.anyOf) {
|
|
525
|
-
const nonNull = schema.anyOf.filter(s => s.type !== "null");
|
|
526
|
-
const hasNull = schema.anyOf.some(s => s.type === "null");
|
|
527
|
-
if (nonNull.length === 1 && hasNull) {
|
|
528
|
-
return `${jsonSchemaToTs(nonNull[0], indent)} | null`;
|
|
529
|
-
}
|
|
530
|
-
return schema.anyOf.map(s => jsonSchemaToTs(s, indent)).join(" | ");
|
|
531
|
-
}
|
|
532
|
-
switch (schema.type) {
|
|
533
|
-
case "string":
|
|
534
|
-
return "string";
|
|
535
|
-
case "number":
|
|
536
|
-
case "integer":
|
|
537
|
-
return "number";
|
|
538
|
-
case "boolean":
|
|
539
|
-
return "boolean";
|
|
540
|
-
case "array":
|
|
541
|
-
if (schema.items) {
|
|
542
|
-
const itemType = jsonSchemaToTs(schema.items, indent);
|
|
543
|
-
// Wrap union types in parens for array
|
|
544
|
-
return itemType.includes("|") ? `(${itemType})[]` : `${itemType}[]`;
|
|
545
|
-
}
|
|
546
|
-
return "unknown[]";
|
|
547
|
-
case "object": {
|
|
548
|
-
if (!schema.properties || Object.keys(schema.properties).length === 0) {
|
|
549
|
-
return "Record<string, unknown>";
|
|
550
|
-
}
|
|
551
|
-
const requiredSet = new Set(schema.required || []);
|
|
552
|
-
const pad = " ".repeat(indent);
|
|
553
|
-
const innerPad = " ".repeat(indent + 4);
|
|
554
|
-
const entries = Object.entries(schema.properties).map(([key, prop]) => {
|
|
555
|
-
const optional = requiredSet.has(key) ? "" : "?";
|
|
556
|
-
const tsType = jsonSchemaToTs(prop, indent + 4);
|
|
557
|
-
const desc = prop.description ? ` /** ${prop.description} */\n${innerPad}` : "";
|
|
558
|
-
return `${desc}${key}${optional}: ${tsType}`;
|
|
559
|
-
});
|
|
560
|
-
return `{\n${innerPad}${entries.join(`\n${innerPad}`)}\n${pad}}`;
|
|
561
|
-
}
|
|
562
|
-
default:
|
|
563
|
-
return "unknown";
|
|
564
|
-
}
|
|
565
|
-
}
|
|
566
|
-
function generateToolsSection(tools, input) {
|
|
567
|
-
if (tools.length === 0)
|
|
568
|
-
return EMPTY_SECTION;
|
|
569
|
-
const imports = new Set(["TerseAgent", "ToolOutputByName"]);
|
|
570
|
-
const parts = [sectionHeader("Typed Tools"), ""];
|
|
571
|
-
// Build instance map: integration type → instances with id & displayName
|
|
572
|
-
const instanceMap = new Map();
|
|
573
|
-
instanceMap.set("slack", input.slack.map(s => ({ id: s.id, displayName: s.displayName })));
|
|
574
|
-
instanceMap.set("github", input.github.map(g => ({ id: g.integration.id, displayName: g.integration.account_name || "" })));
|
|
575
|
-
instanceMap.set("gmail", input.gmail.map(g => ({ id: g.id, displayName: g.displayName })));
|
|
576
|
-
instanceMap.set("figma", input.figma.map(f => ({ id: f.id, displayName: f.displayName })));
|
|
577
|
-
instanceMap.set("linear", input.linear.map(l => ({ id: l.id, displayName: l.displayName })));
|
|
578
|
-
instanceMap.set("jira", input.atlassian.map(a => ({ id: a.id, displayName: a.displayName })));
|
|
579
|
-
instanceMap.set("confluence", input.atlassian.map(a => ({ id: a.id, displayName: a.displayName })));
|
|
580
|
-
instanceMap.set("atlassian", input.atlassian.map(a => ({ id: a.id, displayName: a.displayName })));
|
|
581
|
-
instanceMap.set("notion", input.notion.map(n => ({ id: n.id, displayName: n.displayName })));
|
|
582
|
-
instanceMap.set("posthog", input.posthog.map(p => ({ id: p.id, displayName: p.displayName })));
|
|
583
|
-
instanceMap.set("datadog", input.datadog.map(d => ({ id: d.id, displayName: d.displayName })));
|
|
584
|
-
instanceMap.set("launchdarkly", input.launchdarkly.map(l => ({ id: l.id, displayName: l.displayName })));
|
|
585
|
-
instanceMap.set("workos", input.workos.map(w => ({ id: w.id, displayName: w.displayName })));
|
|
586
|
-
instanceMap.set("attio", input.attio.map(a => ({ id: a.id, displayName: a.displayName })));
|
|
587
|
-
// Group tools by integration
|
|
588
|
-
const byIntegration = new Map();
|
|
589
|
-
for (const tool of tools) {
|
|
590
|
-
const key = tool.integration.toLowerCase();
|
|
591
|
-
if (!byIntegration.has(key))
|
|
592
|
-
byIntegration.set(key, []);
|
|
593
|
-
byIntegration.get(key).push(tool);
|
|
594
|
-
}
|
|
595
|
-
// Check if a tool has integrationId that should be auto-filled
|
|
596
|
-
const hasAutoFillId = (tool) => tool.parameters.properties?.integrationId !== undefined;
|
|
597
|
-
// Clone a schema with integrationId removed from properties and required
|
|
598
|
-
const omitIntegrationId = (schema) => {
|
|
599
|
-
const cloned = { ...schema };
|
|
600
|
-
if (cloned.properties) {
|
|
601
|
-
cloned.properties = Object.fromEntries(Object.entries(cloned.properties).filter(([key]) => key !== "integrationId"));
|
|
602
|
-
}
|
|
603
|
-
if (cloned.required) {
|
|
604
|
-
cloned.required = cloned.required.filter(r => r !== "integrationId");
|
|
605
|
-
}
|
|
606
|
-
return cloned;
|
|
607
|
-
};
|
|
608
|
-
// Ensure GitHub repository params are full "owner/repo".
|
|
609
|
-
// Supports:
|
|
610
|
-
// - Repos objects (uses .fullName)
|
|
611
|
-
// - full string "owner/repo" (passthrough)
|
|
612
|
-
// - short repo name string "repo" (resolved via known generated repositories when unique)
|
|
613
|
-
const normalizeGitHubReposParams = (toolName) => {
|
|
614
|
-
switch (toolName) {
|
|
615
|
-
case "readGitHubFile":
|
|
616
|
-
case "listGitHubPullRequests":
|
|
617
|
-
case "listGitHubDirectory":
|
|
618
|
-
case "listGitHubCommits":
|
|
619
|
-
case "summarizeGitHubPullRequestDiff":
|
|
620
|
-
return "{ ...params, repository: __normalizeGitHubRepos((params as any).repository) }";
|
|
621
|
-
case "searchGitHubCode":
|
|
622
|
-
case "grepGitHubCode":
|
|
623
|
-
return "{ ...params, repositoryNames: __normalizeGitHubReposNames((params as any).repositoryNames) }";
|
|
624
|
-
default:
|
|
625
|
-
return "params";
|
|
626
|
-
}
|
|
627
|
-
};
|
|
628
|
-
// Generate per-tool param types (with integrationId omitted where applicable)
|
|
629
|
-
for (const tool of tools) {
|
|
630
|
-
const typeName = toolNameToInterfaceName(tool.name);
|
|
631
|
-
const schema = hasAutoFillId(tool) ? omitIntegrationId(tool.parameters) : tool.parameters;
|
|
632
|
-
const tsType = jsonSchemaToTs(schema, 0);
|
|
633
|
-
if (tool.description) {
|
|
634
|
-
parts.push(`/** ${tool.description} */`);
|
|
635
|
-
}
|
|
636
|
-
// Always use `type` (not `interface`) so params are assignable to Record<string, unknown>
|
|
637
|
-
parts.push(`export type ${typeName} = ${tsType}`);
|
|
638
|
-
parts.push("");
|
|
639
|
-
}
|
|
640
|
-
const groups = [];
|
|
641
|
-
for (const [integration, integrationTools] of byIntegration) {
|
|
642
|
-
const needsAutoFill = integrationTools.some(hasAutoFillId);
|
|
643
|
-
if (needsAutoFill) {
|
|
644
|
-
const instances = instanceMap.get(integration) || [];
|
|
645
|
-
if (instances.length === 0)
|
|
646
|
-
continue;
|
|
647
|
-
groups.push({ key: integration, integration, tools: integrationTools, integrationId: instances[0].id });
|
|
648
|
-
}
|
|
649
|
-
else {
|
|
650
|
-
groups.push({ key: integration, integration, tools: integrationTools });
|
|
651
|
-
}
|
|
652
|
-
}
|
|
653
|
-
groups.sort((a, b) => a.key.localeCompare(b.key));
|
|
654
|
-
const githubToolsExist = groups.some(g => g.integration === "github");
|
|
655
|
-
if (githubToolsExist) {
|
|
656
|
-
const githubRepoFullNames = Array.from(new Set(input.github.flatMap(g => g.repositories.map(r => (r.owner && r.name ? `${r.owner}/${r.name}` : r.name).trim())).filter(Boolean)));
|
|
657
|
-
const nameToFullName = new Map();
|
|
658
|
-
const ambiguousNames = new Set();
|
|
659
|
-
for (const fullName of githubRepoFullNames) {
|
|
660
|
-
const [, repoName = fullName] = fullName.split("/", 2);
|
|
661
|
-
if (nameToFullName.has(repoName) && nameToFullName.get(repoName) !== fullName) {
|
|
662
|
-
ambiguousNames.add(repoName);
|
|
663
|
-
nameToFullName.delete(repoName);
|
|
664
|
-
}
|
|
665
|
-
else if (!ambiguousNames.has(repoName)) {
|
|
666
|
-
nameToFullName.set(repoName, fullName);
|
|
667
|
-
}
|
|
668
|
-
}
|
|
669
|
-
parts.push(`const __githubRepoNameToFullName = new Map<string, string>([`);
|
|
670
|
-
for (const [name, fullName] of Array.from(nameToFullName.entries()).sort((a, b) => a[0].localeCompare(b[0]))) {
|
|
671
|
-
parts.push(` ["${escapeString(name)}", "${escapeString(fullName)}"],`);
|
|
672
|
-
}
|
|
673
|
-
parts.push(`])`);
|
|
674
|
-
parts.push("");
|
|
675
|
-
parts.push(`function __normalizeGitHubRepos(repo: unknown): string {`);
|
|
676
|
-
parts.push(` if (repo && typeof repo === "object" && "fullName" in (repo as Record<string, unknown>)) {`);
|
|
677
|
-
parts.push(` const fullName = (repo as { fullName?: unknown }).fullName`);
|
|
678
|
-
parts.push(` if (typeof fullName === "string" && fullName.length > 0) return fullName`);
|
|
679
|
-
parts.push(` }`);
|
|
680
|
-
parts.push(` if (typeof repo === "string") {`);
|
|
681
|
-
parts.push(` if (repo.includes("/")) return repo`);
|
|
682
|
-
parts.push(` return __githubRepoNameToFullName.get(repo) ?? repo`);
|
|
683
|
-
parts.push(` }`);
|
|
684
|
-
parts.push(` return String(repo ?? "")`);
|
|
685
|
-
parts.push(`}`);
|
|
686
|
-
parts.push("");
|
|
687
|
-
parts.push(`function __normalizeGitHubReposNames(repositories: unknown): string[] {`);
|
|
688
|
-
parts.push(` if (!Array.isArray(repositories)) return []`);
|
|
689
|
-
parts.push(` return repositories.map(repo => __normalizeGitHubRepos(repo))`);
|
|
690
|
-
parts.push(`}`);
|
|
691
|
-
parts.push("");
|
|
692
|
-
}
|
|
693
|
-
// Generate the GeneratedTools type
|
|
694
|
-
parts.push("export type GeneratedTools = {");
|
|
695
|
-
for (const group of groups) {
|
|
696
|
-
parts.push(` ${group.key}: {`);
|
|
697
|
-
for (const tool of group.tools) {
|
|
698
|
-
const methodName = toCamelCase(tool.displayName);
|
|
699
|
-
const paramsType = toolNameToInterfaceName(tool.name);
|
|
700
|
-
if (tool.description) {
|
|
701
|
-
parts.push(` /** ${tool.description} */`);
|
|
702
|
-
}
|
|
703
|
-
parts.push(` ${methodName}(params: ${paramsType}): Promise<ToolOutputByName["${escapeString(tool.name)}"]>`);
|
|
704
|
-
}
|
|
705
|
-
parts.push(" }");
|
|
706
|
-
}
|
|
707
|
-
parts.push("}");
|
|
708
|
-
parts.push("");
|
|
709
|
-
// Module augmentation — adds `tools` to TerseAgent's type
|
|
710
|
-
parts.push('declare module "terse-sdk" {');
|
|
711
|
-
parts.push(" interface TerseAgent {");
|
|
712
|
-
parts.push(" readonly tools: GeneratedTools");
|
|
713
|
-
parts.push(" }");
|
|
714
|
-
parts.push("}");
|
|
715
|
-
parts.push("");
|
|
716
|
-
// Runtime: factory that builds the tools object for a given agent instance,
|
|
717
|
-
// filtered to only include tools whose integration matches the agent's skills.
|
|
718
|
-
parts.push("function createTools(agent: TerseAgent): GeneratedTools {");
|
|
719
|
-
parts.push(" const allowed: Set<string> = new Set(agent.skills.map(s => s.integrationType))");
|
|
720
|
-
parts.push(" return {");
|
|
721
|
-
for (const group of groups) {
|
|
722
|
-
const intType = toolIntegrationToIntegrationType(group.integration);
|
|
723
|
-
parts.push(` ...(allowed.has("${escapeString(intType)}") ? { ${group.key}: {`);
|
|
724
|
-
for (const tool of group.tools) {
|
|
725
|
-
const methodName = toCamelCase(tool.displayName);
|
|
726
|
-
const paramsType = toolNameToInterfaceName(tool.name);
|
|
727
|
-
const normalizedParamsExpr = group.integration === "github" ? normalizeGitHubReposParams(tool.name) : "params";
|
|
728
|
-
if (group.integrationId && hasAutoFillId(tool)) {
|
|
729
|
-
parts.push(` ${methodName}: (params: ${paramsType}) =>`);
|
|
730
|
-
parts.push(` agent.executeTool<ToolOutputByName["${escapeString(tool.name)}"]>("${escapeString(tool.name)}", { ...(${normalizedParamsExpr}), integrationId: "${escapeString(group.integrationId)}" }),`);
|
|
731
|
-
}
|
|
732
|
-
else {
|
|
733
|
-
parts.push(` ${methodName}: (params: ${paramsType}) =>`);
|
|
734
|
-
parts.push(` agent.executeTool<ToolOutputByName["${escapeString(tool.name)}"]>("${escapeString(tool.name)}", ${normalizedParamsExpr}),`);
|
|
735
|
-
}
|
|
736
|
-
}
|
|
737
|
-
parts.push(` } } : {}),`);
|
|
738
|
-
}
|
|
739
|
-
parts.push(" } as GeneratedTools");
|
|
740
|
-
parts.push("}");
|
|
741
|
-
parts.push("");
|
|
742
|
-
// Expose factory on globalThis for CLI (tsx may load a separate module instance)
|
|
743
|
-
parts.push(";(globalThis as any).__terse_createTools = createTools");
|
|
744
|
-
parts.push("");
|
|
745
|
-
return { code: parts.join("\n"), imports };
|
|
746
|
-
}
|
|
747
|
-
// ── System (always included) ──────────────────────────────────────────
|
|
748
|
-
function generateSystemSection() {
|
|
749
|
-
const imports = new Set(["TimeTriggerConfig", "TerseConfig"]);
|
|
750
|
-
const parts = [];
|
|
751
|
-
parts.push(sectionHeader("Schedule"));
|
|
752
|
-
parts.push("");
|
|
753
|
-
parts.push("export const Schedule = {");
|
|
754
|
-
parts.push(` /** Use in \`triggers[]\` — run on a cron schedule */`);
|
|
755
|
-
parts.push(" cron(opts: { expression: string }): TimeTriggerConfig {");
|
|
756
|
-
parts.push(" return new TimeTriggerConfig(opts.expression)");
|
|
757
|
-
parts.push(" },");
|
|
758
|
-
parts.push("}");
|
|
759
|
-
parts.push("");
|
|
760
|
-
parts.push(sectionHeader("Terse"));
|
|
761
|
-
parts.push("");
|
|
762
|
-
parts.push("export const Terse = {");
|
|
763
|
-
parts.push(` /** Use in \`skills[]\` — built-in web search */`);
|
|
764
|
-
parts.push(" skill(): TerseConfig {");
|
|
765
|
-
parts.push(" return new TerseConfig()");
|
|
766
|
-
parts.push(" },");
|
|
767
|
-
parts.push("}");
|
|
768
|
-
parts.push("");
|
|
769
|
-
return { code: parts.join("\n"), imports };
|
|
770
|
-
}
|
|
771
|
-
// ── Main Codegen Entry Point ──────────────────────────────────────────
|
|
772
|
-
export function generateCode(input) {
|
|
773
|
-
const allImports = new Set();
|
|
774
|
-
const sections = [];
|
|
775
|
-
const sectionGenerators = [
|
|
776
|
-
generateGitHubSection(input.github),
|
|
777
|
-
generateGmailSection(input.gmail),
|
|
778
|
-
generateSlackSection(input.slack),
|
|
779
|
-
generateFigmaSection(input.figma),
|
|
780
|
-
generateLinearSection(input.linear),
|
|
781
|
-
generateAtlassianSection(input.atlassian),
|
|
782
|
-
generateNotionSection(input.notion),
|
|
783
|
-
generatePosthogSection(input.posthog),
|
|
784
|
-
generateDatadogSection(input.datadog),
|
|
785
|
-
generateLaunchDarklySection(input.launchdarkly),
|
|
786
|
-
generateWorkOSSection(input.workos),
|
|
787
|
-
generateAttioSection(input.attio),
|
|
788
|
-
generateToolsSection(input.tools, input),
|
|
789
|
-
generateSystemSection(),
|
|
790
|
-
];
|
|
791
|
-
for (const section of sectionGenerators) {
|
|
792
|
-
if (section.code) {
|
|
793
|
-
sections.push(section.code);
|
|
794
|
-
section.imports.forEach(i => allImports.add(i));
|
|
795
|
-
}
|
|
796
|
-
}
|
|
797
|
-
// Build import statement
|
|
798
|
-
const sortedImports = [...allImports].sort();
|
|
799
|
-
const importLine = sortedImports.length <= 3
|
|
800
|
-
? `import { ${sortedImports.join(", ")} } from "terse-sdk"`
|
|
801
|
-
: `import {\n ${sortedImports.join(",\n ")},\n} from "terse-sdk"`;
|
|
802
|
-
const header = [
|
|
803
|
-
"// Auto-generated by `terse generate` — do not edit manually",
|
|
804
|
-
"// Re-run `terse generate` to update.",
|
|
805
|
-
"",
|
|
806
|
-
importLine,
|
|
807
|
-
"",
|
|
808
|
-
];
|
|
809
|
-
return [...header, ...sections].join("\n");
|
|
810
|
-
}
|
|
811
|
-
//# sourceMappingURL=codegen.js.map
|