@posthog/agent 1.24.0 → 1.24.2
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 +33 -0
- package/dist/index.d.ts +11 -11
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +3 -3
- package/dist/src/adapters/claude/claude-adapter.d.ts +3 -3
- package/dist/src/adapters/claude/claude-adapter.d.ts.map +1 -1
- package/dist/src/adapters/claude/claude-adapter.js +111 -156
- package/dist/src/adapters/claude/claude-adapter.js.map +1 -1
- package/dist/src/adapters/claude/tool-mapper.d.ts +1 -1
- package/dist/src/adapters/claude/tool-mapper.d.ts.map +1 -1
- package/dist/src/adapters/claude/tool-mapper.js.map +1 -1
- package/dist/src/adapters/types.d.ts +1 -1
- package/dist/src/adapters/types.d.ts.map +1 -1
- package/dist/src/agent.d.ts +7 -7
- package/dist/src/agent.d.ts.map +1 -1
- package/dist/src/agent.js +85 -143
- package/dist/src/agent.js.map +1 -1
- package/dist/src/agents/execution.js.map +1 -1
- package/dist/src/agents/planning.js.map +1 -1
- package/dist/src/agents/research.js.map +1 -1
- package/dist/src/file-manager.d.ts +4 -4
- package/dist/src/file-manager.d.ts.map +1 -1
- package/dist/src/file-manager.js +58 -59
- package/dist/src/file-manager.js.map +1 -1
- package/dist/src/git-manager.d.ts +1 -1
- package/dist/src/git-manager.d.ts.map +1 -1
- package/dist/src/git-manager.js +70 -87
- package/dist/src/git-manager.js.map +1 -1
- package/dist/src/posthog-api.d.ts +3 -2
- package/dist/src/posthog-api.d.ts.map +1 -1
- package/dist/src/posthog-api.js +22 -22
- package/dist/src/posthog-api.js.map +1 -1
- package/dist/src/prompt-builder.d.ts +3 -3
- package/dist/src/prompt-builder.d.ts.map +1 -1
- package/dist/src/prompt-builder.js +93 -123
- package/dist/src/prompt-builder.js.map +1 -1
- package/dist/src/task-manager.d.ts +4 -4
- package/dist/src/task-manager.d.ts.map +1 -1
- package/dist/src/task-manager.js +18 -19
- package/dist/src/task-manager.js.map +1 -1
- package/dist/src/task-progress-reporter.d.ts +4 -3
- package/dist/src/task-progress-reporter.d.ts.map +1 -1
- package/dist/src/task-progress-reporter.js +54 -59
- package/dist/src/task-progress-reporter.js.map +1 -1
- package/dist/src/template-manager.d.ts +1 -1
- package/dist/src/template-manager.d.ts.map +1 -1
- package/dist/src/template-manager.js +28 -30
- package/dist/src/template-manager.js.map +1 -1
- package/dist/src/todo-manager.d.ts +3 -3
- package/dist/src/todo-manager.d.ts.map +1 -1
- package/dist/src/todo-manager.js +24 -29
- package/dist/src/todo-manager.js.map +1 -1
- package/dist/src/tools/registry.d.ts +1 -1
- package/dist/src/tools/registry.js +60 -60
- package/dist/src/tools/registry.js.map +1 -1
- package/dist/src/tools/types.d.ts +31 -31
- package/dist/src/types.d.ts +33 -33
- package/dist/src/types.d.ts.map +1 -1
- package/dist/src/types.js.map +1 -1
- package/dist/src/utils/logger.d.ts +4 -4
- package/dist/src/utils/logger.d.ts.map +1 -1
- package/dist/src/utils/logger.js +8 -8
- package/dist/src/utils/logger.js.map +1 -1
- package/dist/src/workflow/config.d.ts +1 -1
- package/dist/src/workflow/config.d.ts.map +1 -1
- package/dist/src/workflow/config.js +18 -18
- package/dist/src/workflow/config.js.map +1 -1
- package/dist/src/workflow/steps/build.d.ts +1 -1
- package/dist/src/workflow/steps/build.d.ts.map +1 -1
- package/dist/src/workflow/steps/build.js +38 -46
- package/dist/src/workflow/steps/build.js.map +1 -1
- package/dist/src/workflow/steps/finalize.d.ts +1 -1
- package/dist/src/workflow/steps/finalize.d.ts.map +1 -1
- package/dist/src/workflow/steps/finalize.js +48 -54
- package/dist/src/workflow/steps/finalize.js.map +1 -1
- package/dist/src/workflow/steps/plan.d.ts +1 -1
- package/dist/src/workflow/steps/plan.d.ts.map +1 -1
- package/dist/src/workflow/steps/plan.js +46 -58
- package/dist/src/workflow/steps/plan.js.map +1 -1
- package/dist/src/workflow/steps/research.d.ts +1 -1
- package/dist/src/workflow/steps/research.d.ts.map +1 -1
- package/dist/src/workflow/steps/research.js +56 -68
- package/dist/src/workflow/steps/research.js.map +1 -1
- package/dist/src/workflow/types.d.ts +12 -12
- package/dist/src/workflow/types.d.ts.map +1 -1
- package/dist/src/workflow/utils.d.ts +1 -1
- package/dist/src/workflow/utils.d.ts.map +1 -1
- package/dist/src/workflow/utils.js +4 -7
- package/dist/src/workflow/utils.js.map +1 -1
- package/package.json +6 -6
- package/src/adapters/claude/claude-adapter.ts +168 -220
- package/src/adapters/claude/tool-mapper.ts +2 -2
- package/src/adapters/types.ts +1 -1
- package/src/agent.ts +444 -579
- package/src/agents/execution.ts +1 -1
- package/src/agents/planning.ts +1 -1
- package/src/agents/research.ts +1 -0
- package/src/file-manager.ts +63 -64
- package/src/git-manager.ts +88 -144
- package/src/posthog-api.ts +82 -122
- package/src/prompt-builder.ts +135 -180
- package/src/task-manager.ts +30 -38
- package/src/task-progress-reporter.ts +59 -70
- package/src/template-manager.ts +45 -98
- package/src/todo-manager.ts +30 -35
- package/src/tools/registry.ts +62 -62
- package/src/tools/types.ts +36 -36
- package/src/types.ts +71 -93
- package/src/utils/logger.ts +56 -62
- package/src/workflow/config.ts +48 -48
- package/src/workflow/steps/build.ts +113 -122
- package/src/workflow/steps/finalize.ts +182 -214
- package/src/workflow/steps/plan.ts +131 -151
- package/src/workflow/steps/research.ts +186 -205
- package/src/workflow/types.ts +36 -38
- package/src/workflow/utils.ts +34 -37
|
@@ -1,248 +1,216 @@
|
|
|
1
|
-
import type { LocalArtifact } from
|
|
2
|
-
import type { Task, TaskRunArtifact } from
|
|
3
|
-
import type { WorkflowStepRunner } from
|
|
4
|
-
import { finalizeStepGitActions } from
|
|
1
|
+
import type { LocalArtifact } from '../../file-manager.js';
|
|
2
|
+
import type { Task, TaskRunArtifact } from '../../types.js';
|
|
3
|
+
import type { WorkflowStepRunner } from '../types.js';
|
|
4
|
+
import { finalizeStepGitActions } from '../utils.js';
|
|
5
5
|
|
|
6
6
|
const MAX_SNIPPET_LENGTH = 1200;
|
|
7
7
|
|
|
8
8
|
export const finalizeStep: WorkflowStepRunner = async ({ step, context }) => {
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
9
|
+
const {
|
|
10
|
+
task,
|
|
11
|
+
logger,
|
|
12
|
+
fileManager,
|
|
13
|
+
gitManager,
|
|
14
|
+
posthogAPI,
|
|
15
|
+
progressReporter,
|
|
16
|
+
} = context;
|
|
17
|
+
|
|
18
|
+
const stepLogger = logger.child('FinalizeStep');
|
|
19
|
+
const artifacts = await fileManager.collectTaskArtifacts(task.id);
|
|
20
|
+
let uploadedArtifacts: TaskRunArtifact[] | undefined;
|
|
21
|
+
|
|
22
|
+
if (artifacts.length && posthogAPI && progressReporter.runId) {
|
|
23
|
+
try {
|
|
24
|
+
const payload = artifacts.map((artifact) => ({
|
|
25
|
+
name: artifact.name,
|
|
26
|
+
type: artifact.type,
|
|
27
|
+
content: artifact.content,
|
|
28
|
+
content_type: artifact.contentType,
|
|
29
|
+
}));
|
|
30
|
+
uploadedArtifacts = await posthogAPI.uploadTaskArtifacts(task.id, progressReporter.runId, payload);
|
|
31
|
+
stepLogger.info('Uploaded task artifacts to PostHog', {
|
|
32
|
+
taskId: task.id,
|
|
33
|
+
uploadedCount: uploadedArtifacts.length,
|
|
34
|
+
});
|
|
35
|
+
} catch (error) {
|
|
36
|
+
stepLogger.warn('Failed to upload task artifacts', {
|
|
37
|
+
taskId: task.id,
|
|
38
|
+
error: error instanceof Error ? error.message : String(error),
|
|
39
|
+
});
|
|
40
|
+
}
|
|
41
|
+
} else {
|
|
42
|
+
stepLogger.debug('Skipping artifact upload', {
|
|
43
|
+
hasArtifacts: artifacts.length > 0,
|
|
44
|
+
hasPostHogApi: Boolean(posthogAPI),
|
|
45
|
+
runId: progressReporter.runId,
|
|
46
|
+
});
|
|
44
47
|
}
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
48
|
+
|
|
49
|
+
const prBody = buildPullRequestBody(task, artifacts, uploadedArtifacts);
|
|
50
|
+
await fileManager.cleanupTaskDirectory(task.id);
|
|
51
|
+
await gitManager.addAllPostHogFiles();
|
|
52
|
+
|
|
53
|
+
// Commit the deletion of artifacts
|
|
54
|
+
await finalizeStepGitActions(context, step, {
|
|
55
|
+
commitMessage: `Cleanup task artifacts for ${task.title}`,
|
|
56
|
+
allowEmptyCommit: true
|
|
50
57
|
});
|
|
51
|
-
}
|
|
52
58
|
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
59
|
+
context.stepResults[step.id] = {
|
|
60
|
+
prBody,
|
|
61
|
+
uploadedArtifacts,
|
|
62
|
+
artifactCount: artifacts.length,
|
|
63
|
+
};
|
|
56
64
|
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
commitMessage: `Cleanup task artifacts for ${task.title}`,
|
|
60
|
-
allowEmptyCommit: true,
|
|
61
|
-
});
|
|
65
|
+
return { status: 'completed' };
|
|
66
|
+
};
|
|
62
67
|
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
artifactCount: artifacts.length,
|
|
67
|
-
};
|
|
68
|
+
function buildPullRequestBody(task: Task, artifacts: LocalArtifact[], uploaded?: TaskRunArtifact[]): string {
|
|
69
|
+
const lines: string[] = [];
|
|
70
|
+
const taskSlug = (task as any).slug || task.id;
|
|
68
71
|
|
|
69
|
-
|
|
70
|
-
};
|
|
72
|
+
lines.push('## Task context');
|
|
73
|
+
lines.push(`- **Task**: ${taskSlug}`);
|
|
74
|
+
lines.push(`- **Title**: ${task.title}`);
|
|
75
|
+
lines.push(`- **Origin**: ${task.origin_product}`);
|
|
76
|
+
|
|
77
|
+
if (task.description) {
|
|
78
|
+
lines.push('');
|
|
79
|
+
lines.push('> ' + task.description.trim().split('\n').join('\n> '));
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
const usedFiles = new Set<string>();
|
|
83
|
+
|
|
84
|
+
const contextArtifact = artifacts.find((artifact) => artifact.name === 'context.md');
|
|
85
|
+
if (contextArtifact) {
|
|
86
|
+
lines.push('');
|
|
87
|
+
lines.push('### Task prompt');
|
|
88
|
+
lines.push(contextArtifact.content);
|
|
89
|
+
usedFiles.add(contextArtifact.name);
|
|
90
|
+
}
|
|
71
91
|
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
)
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
lines.push("## Task context");
|
|
81
|
-
lines.push(`- **Task**: ${taskSlug}`);
|
|
82
|
-
lines.push(`- **Title**: ${task.title}`);
|
|
83
|
-
lines.push(`- **Origin**: ${task.origin_product}`);
|
|
84
|
-
|
|
85
|
-
if (task.description) {
|
|
86
|
-
lines.push("");
|
|
87
|
-
lines.push(`> ${task.description.trim().split("\n").join("\n> ")}`);
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
const usedFiles = new Set<string>();
|
|
91
|
-
|
|
92
|
-
const contextArtifact = artifacts.find(
|
|
93
|
-
(artifact) => artifact.name === "context.md",
|
|
94
|
-
);
|
|
95
|
-
if (contextArtifact) {
|
|
96
|
-
lines.push("");
|
|
97
|
-
lines.push("### Task prompt");
|
|
98
|
-
lines.push(contextArtifact.content);
|
|
99
|
-
usedFiles.add(contextArtifact.name);
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
const researchArtifact = artifacts.find(
|
|
103
|
-
(artifact) => artifact.name === "research.json",
|
|
104
|
-
);
|
|
105
|
-
if (researchArtifact) {
|
|
106
|
-
usedFiles.add(researchArtifact.name);
|
|
107
|
-
const researchSection = formatResearchSection(researchArtifact.content);
|
|
108
|
-
if (researchSection) {
|
|
109
|
-
lines.push("");
|
|
110
|
-
lines.push(researchSection);
|
|
92
|
+
const researchArtifact = artifacts.find((artifact) => artifact.name === 'research.json');
|
|
93
|
+
if (researchArtifact) {
|
|
94
|
+
usedFiles.add(researchArtifact.name);
|
|
95
|
+
const researchSection = formatResearchSection(researchArtifact.content);
|
|
96
|
+
if (researchSection) {
|
|
97
|
+
lines.push('');
|
|
98
|
+
lines.push(researchSection);
|
|
99
|
+
}
|
|
111
100
|
}
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
lines.push("");
|
|
131
|
-
lines.push("### Todo list");
|
|
132
|
-
lines.push(summary);
|
|
101
|
+
|
|
102
|
+
const planArtifact = artifacts.find((artifact) => artifact.name === 'plan.md');
|
|
103
|
+
if (planArtifact) {
|
|
104
|
+
lines.push('');
|
|
105
|
+
lines.push('### Implementation plan');
|
|
106
|
+
lines.push(planArtifact.content);
|
|
107
|
+
usedFiles.add(planArtifact.name);
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
const todoArtifact = artifacts.find((artifact) => artifact.name === 'todos.json');
|
|
111
|
+
if (todoArtifact) {
|
|
112
|
+
const summary = summarizeTodos(todoArtifact.content);
|
|
113
|
+
if (summary) {
|
|
114
|
+
lines.push('');
|
|
115
|
+
lines.push('### Todo list');
|
|
116
|
+
lines.push(summary);
|
|
117
|
+
}
|
|
118
|
+
usedFiles.add(todoArtifact.name);
|
|
133
119
|
}
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
for (const artifact of remainingArtifacts) {
|
|
144
|
-
lines.push(`#### ${artifact.name}`);
|
|
145
|
-
lines.push(renderCodeFence(artifact.content));
|
|
120
|
+
|
|
121
|
+
const remainingArtifacts = artifacts.filter((artifact) => !usedFiles.has(artifact.name));
|
|
122
|
+
if (remainingArtifacts.length) {
|
|
123
|
+
lines.push('');
|
|
124
|
+
lines.push('### Additional artifacts');
|
|
125
|
+
for (const artifact of remainingArtifacts) {
|
|
126
|
+
lines.push(`#### ${artifact.name}`);
|
|
127
|
+
lines.push(renderCodeFence(artifact.content));
|
|
128
|
+
}
|
|
146
129
|
}
|
|
147
|
-
}
|
|
148
130
|
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
name: artifact.name,
|
|
153
|
-
type: artifact.type,
|
|
131
|
+
const artifactList = uploaded ?? artifacts.map((artifact) => ({
|
|
132
|
+
name: artifact.name,
|
|
133
|
+
type: artifact.type,
|
|
154
134
|
}));
|
|
155
135
|
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
typeof rawStoragePath === "string" ? rawStoragePath : undefined;
|
|
166
|
-
const storage =
|
|
167
|
-
storagePath && storagePath.trim().length > 0
|
|
168
|
-
? ` – \`${storagePath.trim()}\``
|
|
169
|
-
: "";
|
|
170
|
-
lines.push(`- ${artifact.name} (${artifact.type})${storage}`);
|
|
136
|
+
if (artifactList.length) {
|
|
137
|
+
lines.push('');
|
|
138
|
+
lines.push('### Uploaded artifacts');
|
|
139
|
+
for (const artifact of artifactList) {
|
|
140
|
+
const rawStoragePath = 'storage_path' in artifact ? (artifact as any).storage_path : undefined;
|
|
141
|
+
const storagePath = typeof rawStoragePath === 'string' ? rawStoragePath : undefined;
|
|
142
|
+
const storage = storagePath && storagePath.trim().length > 0 ? ` – \`${storagePath.trim()}\`` : '';
|
|
143
|
+
lines.push(`- ${artifact.name} (${artifact.type})${storage}`);
|
|
144
|
+
}
|
|
171
145
|
}
|
|
172
|
-
}
|
|
173
146
|
|
|
174
|
-
|
|
147
|
+
return lines.join('\n\n');
|
|
175
148
|
}
|
|
176
149
|
|
|
177
150
|
function renderCodeFence(content: string): string {
|
|
178
|
-
|
|
179
|
-
|
|
151
|
+
const snippet = truncate(content, MAX_SNIPPET_LENGTH);
|
|
152
|
+
return ['```', snippet, '```'].join('\n');
|
|
180
153
|
}
|
|
181
154
|
|
|
182
155
|
function truncate(value: string, maxLength: number): string {
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
156
|
+
if (value.length <= maxLength) {
|
|
157
|
+
return value;
|
|
158
|
+
}
|
|
159
|
+
return `${value.slice(0, maxLength)}\n…`;
|
|
187
160
|
}
|
|
188
161
|
|
|
189
162
|
function formatResearchSection(content: string): string | null {
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
);
|
|
217
|
-
|
|
163
|
+
try {
|
|
164
|
+
const parsed = JSON.parse(content);
|
|
165
|
+
const sections: string[] = [];
|
|
166
|
+
|
|
167
|
+
if (parsed.context) {
|
|
168
|
+
sections.push('### Research summary');
|
|
169
|
+
sections.push(parsed.context);
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
if (parsed.questions && parsed.questions.length) {
|
|
173
|
+
sections.push('');
|
|
174
|
+
sections.push('### Questions needing answers');
|
|
175
|
+
for (const question of parsed.questions) {
|
|
176
|
+
sections.push(`- ${question.question ?? question}`);
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
if (parsed.answers && parsed.answers.length) {
|
|
181
|
+
sections.push('');
|
|
182
|
+
sections.push('### Answers provided');
|
|
183
|
+
for (const answer of parsed.answers) {
|
|
184
|
+
const questionId = answer.questionId ? ` (Q: ${answer.questionId})` : '';
|
|
185
|
+
sections.push(`- ${answer.selectedOption || answer.customInput || 'answer'}${questionId}`);
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
return sections.length ? sections.join('\n') : null;
|
|
190
|
+
} catch {
|
|
191
|
+
return null;
|
|
218
192
|
}
|
|
219
|
-
|
|
220
|
-
return sections.length ? sections.join("\n") : null;
|
|
221
|
-
} catch {
|
|
222
|
-
return null;
|
|
223
|
-
}
|
|
224
193
|
}
|
|
225
194
|
|
|
226
195
|
function summarizeTodos(content: string): string | null {
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
196
|
+
try {
|
|
197
|
+
const data = JSON.parse(content);
|
|
198
|
+
const total = data?.metadata?.total ?? data?.items?.length;
|
|
199
|
+
const completed = data?.metadata?.completed ?? data?.items?.filter((item: any) => item.status === 'completed').length;
|
|
200
|
+
|
|
201
|
+
const lines = [
|
|
202
|
+
`Progress: ${completed}/${total} completed`,
|
|
203
|
+
];
|
|
204
|
+
|
|
205
|
+
if (data?.items?.length) {
|
|
206
|
+
for (const item of data.items) {
|
|
207
|
+
lines.push(`- [${item.status}] ${item.content}`);
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
return lines.join('\n');
|
|
212
|
+
} catch {
|
|
213
|
+
return null;
|
|
242
214
|
}
|
|
243
|
-
|
|
244
|
-
return lines.join("\n");
|
|
245
|
-
} catch {
|
|
246
|
-
return null;
|
|
247
|
-
}
|
|
248
215
|
}
|
|
216
|
+
|