hammerkit 1.27.0-next.12 → 1.27.0-next.14

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.
@@ -0,0 +1,210 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.taskToolArgs = exports.taskToolSchema = exports.createTaskTools = exports.describeTasks = exports.describeRun = exports.readLogHint = exports.readTaskListing = exports.LIST_TASKS_MS = void 0;
4
+ // the most tasks a description lists, and of a task's description or command it shows
5
+ const MAX_LISTED = 40;
6
+ const MAX_DESCRIPTION_CHARS = 200;
7
+ const MAX_COMMAND_CHARS = 100;
8
+ // how long a client waits for the tasks of the build file when it starts, it goes on without them
9
+ exports.LIST_TASKS_MS = 15000;
10
+ // The tasks of the published version, or why they could not be read within `ms`.
11
+ async function readTaskListing(list, ms = exports.LIST_TASKS_MS) {
12
+ let timer;
13
+ const timeout = new Promise((_, reject) => {
14
+ timer = setTimeout(() => reject(new Error(`no answer within ${ms / 1000} s`)), ms);
15
+ timer.unref?.();
16
+ });
17
+ try {
18
+ return await Promise.race([list(), timeout]);
19
+ }
20
+ catch (e) {
21
+ return { error: e instanceof Error ? e.message : String(e) };
22
+ }
23
+ finally {
24
+ clearTimeout(timer);
25
+ }
26
+ }
27
+ exports.readTaskListing = readTaskListing;
28
+ // the hint at the end of a cut output or a result, for the model
29
+ function readLogHint(run, task) {
30
+ return `read more of the log with run_logs { "run": "${run}", "task": "${task}" }`;
31
+ }
32
+ exports.readLogHint = readLogHint;
33
+ // A run of a task in a few lines: how it went, why it ran, its last lines.
34
+ function describeRun(task, ran) {
35
+ const result = ran.result;
36
+ const lines = [`run ${ran.run.id}: ${ran.run.state}`];
37
+ if (result) {
38
+ lines.push(`task ${task}: ${result.status}` +
39
+ (result.exitCode !== undefined ? `, exit code ${result.exitCode}` : '') +
40
+ (result.durationMs !== undefined ? `, ${result.durationMs} ms` : '') +
41
+ (result.cache?.hit ? ', from the cache' : ''));
42
+ if (result.error) {
43
+ lines.push(`error: ${result.error}`);
44
+ }
45
+ if (result.cache?.causes?.length) {
46
+ lines.push(`ran because: ${result.cache.causes.join('; ')}`);
47
+ }
48
+ }
49
+ else if (ran.run.error) {
50
+ lines.push(`error: ${ran.run.error}`);
51
+ }
52
+ if (result && result.lastLog.length > 0) {
53
+ lines.push('last lines:', ...result.lastLog);
54
+ }
55
+ lines.push(readLogHint(ran.run.id, task));
56
+ return lines.join('\n');
57
+ }
58
+ exports.describeRun = describeRun;
59
+ /**
60
+ * The tasks for the model, one line each: what it is for (or its command, when the build
61
+ * file says nothing) and whether it takes arguments. `shell` is left out, the shell tool runs it.
62
+ */
63
+ function describeTasks(listing, shell) {
64
+ if (listing === undefined) {
65
+ return '';
66
+ }
67
+ if (!Array.isArray(listing)) {
68
+ return `The tasks of the build file could not be listed (${listing.error}); name one as the build file does.`;
69
+ }
70
+ const tasks = listing.filter((task) => !shell || task.name !== shell);
71
+ if (tasks.length === 0) {
72
+ return 'The published build file has no tasks to run.';
73
+ }
74
+ const lines = tasks.slice(0, MAX_LISTED).map((task) => {
75
+ const parts = [`- ${task.name}`];
76
+ if (task.description) {
77
+ parts.push(`: ${cut(oneLine(task.description), MAX_DESCRIPTION_CHARS)}`);
78
+ }
79
+ else if (task.command) {
80
+ parts.push(`: runs \`${cut(oneLine(task.command), MAX_COMMAND_CHARS)}\``);
81
+ }
82
+ if (task.args === null) {
83
+ parts.push(' (takes args)');
84
+ }
85
+ else if (task.args !== undefined || task.command === null) {
86
+ parts.push(' (takes no args)');
87
+ }
88
+ return parts.join('');
89
+ });
90
+ if (tasks.length > MAX_LISTED) {
91
+ lines.push(`- and ${tasks.length - MAX_LISTED} more`);
92
+ }
93
+ return ['The tasks of the published build file:', ...lines].join('\n');
94
+ }
95
+ exports.describeTasks = describeTasks;
96
+ function oneLine(text) {
97
+ return text.replace(/\s+/g, ' ').trim();
98
+ }
99
+ function cut(text, max) {
100
+ return text.length > max ? `${text.slice(0, max)}…` : text;
101
+ }
102
+ /**
103
+ * `run_task` and `run_logs`, with the tasks the description of `run_task` lists. Without a
104
+ * listing it names none, as the MCP tools before a version could be read.
105
+ */
106
+ function createTaskTools(listing, shell) {
107
+ const tasks = describeTasks(listing, shell);
108
+ return [
109
+ {
110
+ name: 'run_task',
111
+ title: 'Run a task',
112
+ description: 'Run a task of the published build file on the sandbox, such as test or build, and answer how it went: ' +
113
+ 'its status, exit code, why it was not taken from the cache, and its last lines. args are appended to ' +
114
+ 'its last command, for example a test file. It runs in the container, with the network and the ' +
115
+ 'options the build file gives the task.' +
116
+ (tasks ? `\n\n${tasks}` : ''),
117
+ parameters: {
118
+ task: { type: 'string', description: 'The name of the task' },
119
+ args: { type: 'string[]', description: 'Arguments for its last command' },
120
+ },
121
+ required: ['task'],
122
+ async call(client, args, signal) {
123
+ const ran = await client.runTask(args.task, { args: args.args?.length ? args.args : undefined, signal });
124
+ return describeRun(args.task, ran);
125
+ },
126
+ },
127
+ {
128
+ name: 'run_logs',
129
+ title: 'Read the log of a run',
130
+ description: 'Read the log of a run as numbered lines: one task, the last lines (tail), a range (from, limit) or ' +
131
+ 'the lines that contain a text (grep, a fixed string) with lines of context around them.',
132
+ parameters: {
133
+ run: { type: 'string', description: 'The id of the run' },
134
+ task: { type: 'string', description: 'Only the lines of this task' },
135
+ tail: {
136
+ type: 'integer',
137
+ description: 'The last lines, or with grep the last matches',
138
+ minimum: 1,
139
+ maximum: 1000,
140
+ },
141
+ from: { type: 'integer', description: 'The first line to read', minimum: 1 },
142
+ limit: { type: 'integer', description: 'How many lines to read', minimum: 1, maximum: 1000 },
143
+ grep: { type: 'string', description: 'Only lines that contain this text' },
144
+ ignore_case: { type: 'boolean', description: 'Ignore case in grep' },
145
+ context: { type: 'integer', description: 'Lines before and after each match', minimum: 0, maximum: 20 },
146
+ },
147
+ required: ['run'],
148
+ async call(client, args) {
149
+ const page = await client.logs(args.run, {
150
+ task: args.task || undefined,
151
+ tail: args.tail || undefined,
152
+ from: args.from || undefined,
153
+ limit: args.limit || undefined,
154
+ grep: args.grep || undefined,
155
+ i: args.ignore_case || undefined,
156
+ context: args.context || undefined,
157
+ });
158
+ const lines = page.lines.map((line) => `${line.line}${line.match === false ? '-' : ':'} ${args.task ? '' : `[${line.task}] `}${line.text}`);
159
+ lines.push('', `(${page.total} lines${args.grep ? ` searched, ${page.lines.filter((line) => line.match !== false).length} match here` : ''}${page.next ? `, read on with from ${page.next}` : ''}${page.truncated ? ', the server cut the log off at its limit' : ''})`);
160
+ return lines.join('\n');
161
+ },
162
+ },
163
+ ];
164
+ }
165
+ exports.createTaskTools = createTaskTools;
166
+ // The parameters as a JSON schema, for MCP and opencode 2.
167
+ function taskToolSchema(tool) {
168
+ return {
169
+ type: 'object',
170
+ properties: Object.fromEntries(Object.entries(tool.parameters).map(([name, parameter]) => {
171
+ if (parameter.type === 'string[]') {
172
+ return [name, { type: 'array', items: { type: 'string' }, description: parameter.description }];
173
+ }
174
+ if (parameter.type === 'integer') {
175
+ const { maximum } = parameter;
176
+ return [
177
+ name,
178
+ {
179
+ type: 'integer',
180
+ description: parameter.description,
181
+ minimum: parameter.minimum,
182
+ ...(maximum !== undefined ? { maximum } : {}),
183
+ },
184
+ ];
185
+ }
186
+ return [name, { type: parameter.type, description: parameter.description }];
187
+ })),
188
+ required: tool.required,
189
+ };
190
+ }
191
+ exports.taskToolSchema = taskToolSchema;
192
+ // The parameters with the zod of opencode 1.
193
+ function taskToolArgs(tool, z) {
194
+ return Object.fromEntries(Object.entries(tool.parameters).map(([name, parameter]) => {
195
+ let type;
196
+ if (parameter.type === 'string[]') {
197
+ type = z.array(z.string());
198
+ }
199
+ else if (parameter.type === 'integer') {
200
+ type = z.number().int().min(parameter.minimum);
201
+ type = parameter.maximum !== undefined ? type.max(parameter.maximum) : type;
202
+ }
203
+ else {
204
+ type = parameter.type === 'boolean' ? z.boolean() : z.string();
205
+ }
206
+ return [name, (tool.required.includes(name) ? type : type.optional()).describe(parameter.description)];
207
+ }));
208
+ }
209
+ exports.taskToolArgs = taskToolArgs;
210
+ //# sourceMappingURL=task-tools.js.map
@@ -129,7 +129,8 @@ function buildNetworkPolicy(env, item, network, scope, proxy = getRunProxy(item,
129
129
  namespace: env.namespace,
130
130
  name: getNetworkPolicyName(item, scope),
131
131
  annotations: { 'hammerkit.dev/version': (0, version_1.getVersion)() },
132
- labels: { [NETWORK_OF_LABEL]: item.id(), ...(0, job_placement_1.getRunLabels)(env) },
132
+ labels: { [NETWORK_OF_LABEL]: item.id(), ...(scope.owners ? {} : (0, job_placement_1.getRunLabels)(env)) },
133
+ ...(scope.owners ? { ownerReferences: scope.owners } : {}),
133
134
  },
134
135
  spec: {
135
136
  podSelector: { matchLabels: getItemPodLabels(item, scope) },