@the-open-engine/zeroshot 6.10.2 → 6.12.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/cli/commands/providers.js +13 -13
- package/cli/index.js +77 -35
- package/cli/lib/first-run.js +12 -11
- package/cli/lib/update-checker.js +517 -132
- package/lib/agent-cli-provider/adapters/opencode.d.ts.map +1 -1
- package/lib/agent-cli-provider/adapters/opencode.js +3 -0
- package/lib/agent-cli-provider/adapters/opencode.js.map +1 -1
- package/lib/agent-cli-provider/types.d.ts +1 -0
- package/lib/agent-cli-provider/types.d.ts.map +1 -1
- package/lib/agent-cli-provider/types.js.map +1 -1
- package/lib/cluster/client.cjs +146 -0
- package/lib/cluster/client.d.ts +44 -0
- package/lib/cluster/client.mjs +141 -0
- package/lib/cluster/connection.cjs +382 -0
- package/lib/cluster/connection.d.ts +41 -0
- package/lib/cluster/connection.mjs +378 -0
- package/lib/cluster/errors.cjs +44 -0
- package/lib/cluster/errors.d.ts +23 -0
- package/lib/cluster/errors.mjs +32 -0
- package/lib/cluster/frames.cjs +49 -0
- package/lib/cluster/frames.d.ts +12 -0
- package/lib/cluster/frames.mjs +45 -0
- package/lib/cluster/generated/protocol-schema.cjs +5 -0
- package/lib/cluster/generated/protocol-schema.d.ts +1 -0
- package/lib/cluster/generated/protocol-schema.mjs +2 -0
- package/lib/cluster/generated/protocol.cjs +22 -0
- package/lib/cluster/generated/protocol.d.ts +781 -0
- package/lib/cluster/generated/protocol.mjs +19 -0
- package/lib/cluster/index.cjs +40 -0
- package/lib/cluster/index.d.ts +10 -0
- package/lib/cluster/index.mjs +6 -0
- package/lib/cluster/queue.cjs +71 -0
- package/lib/cluster/queue.d.ts +15 -0
- package/lib/cluster/queue.mjs +67 -0
- package/lib/cluster/socket.cjs +15 -0
- package/lib/cluster/socket.d.ts +11 -0
- package/lib/cluster/socket.mjs +12 -0
- package/lib/cluster/subscriptions.cjs +270 -0
- package/lib/cluster/subscriptions.d.ts +69 -0
- package/lib/cluster/subscriptions.mjs +264 -0
- package/lib/cluster/validators.cjs +46 -0
- package/lib/cluster/validators.d.ts +3 -0
- package/lib/cluster/validators.mjs +42 -0
- package/lib/settings.js +195 -23
- package/lib/setup-apply.js +45 -32
- package/lib/setup-undo.js +25 -16
- package/package.json +25 -3
- package/scripts/build-cluster.js +43 -0
- package/scripts/generate-cluster-types.js +203 -0
- package/scripts/release-dry-run.js +6 -6
- package/scripts/rust-distribution.js +933 -0
- package/src/agent/agent-hook-executor.js +14 -6
- package/src/agent/agent-lifecycle.js +25 -8
- package/src/agent/agent-task-executor.js +711 -139
- package/src/agent/output-reformatter.js +54 -41
- package/src/agent/pr-verification.js +11 -3
- package/src/agent/task-execution-handle.js +373 -0
- package/src/agent-cli-provider/adapters/opencode.ts +3 -0
- package/src/agent-cli-provider/types.ts +1 -0
- package/src/agent-wrapper.js +5 -2
- package/src/cluster/client.ts +199 -0
- package/src/cluster/connection.ts +300 -0
- package/src/cluster/errors.ts +32 -0
- package/src/cluster/frames.ts +44 -0
- package/src/cluster/generated/protocol-schema.ts +2 -0
- package/src/cluster/generated/protocol.ts +183 -0
- package/src/cluster/index.ts +38 -0
- package/src/cluster/queue.ts +84 -0
- package/src/cluster/socket.ts +31 -0
- package/src/cluster/subscriptions.ts +256 -0
- package/src/cluster/validators.ts +56 -0
- package/src/cluster/ws.d.ts +7 -0
- package/src/isolation-manager.js +16 -0
- package/src/issue-providers/jira-provider.js +1 -1
- package/src/issue-providers/linear-provider.js +4 -4
- package/task-lib/runner.js +3 -0
|
@@ -0,0 +1,256 @@
|
|
|
1
|
+
import type { AgentAttachEvent, LogRecord, SubscriptionCloseReason, WatchEvent, WatchParams, WatchResult } from './generated/protocol.js';
|
|
2
|
+
import { ClusterProtocolError, ClusterStateError } from './errors.js';
|
|
3
|
+
import { Connection } from './connection.js';
|
|
4
|
+
import type { SubscriptionRegistration } from './connection.js';
|
|
5
|
+
import { isRecord } from './frames.js';
|
|
6
|
+
import type { FrameRecord } from './frames.js';
|
|
7
|
+
import { assertDefinition } from './validators.js';
|
|
8
|
+
|
|
9
|
+
export type SubscriptionClosedItem = {
|
|
10
|
+
readonly type: 'closed';
|
|
11
|
+
readonly reason: SubscriptionCloseReason;
|
|
12
|
+
};
|
|
13
|
+
export type WatchSubscriptionClosedItem = SubscriptionClosedItem & {
|
|
14
|
+
readonly lastDeliveredCursor?: string | null;
|
|
15
|
+
};
|
|
16
|
+
export type SubscriptionItem<T> = { readonly type: 'event'; readonly event: T } | SubscriptionClosedItem;
|
|
17
|
+
export type WatchSubscriptionItem =
|
|
18
|
+
| { readonly type: 'event'; readonly runId: string; readonly cursor: string; readonly event: WatchEvent }
|
|
19
|
+
| WatchSubscriptionClosedItem;
|
|
20
|
+
export interface Subscription<T> extends AsyncIterator<SubscriptionItem<T>>, AsyncIterable<SubscriptionItem<T>> {
|
|
21
|
+
readonly subscriptionId: string;
|
|
22
|
+
readonly retainedCount: number;
|
|
23
|
+
cancel(): Promise<void>;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
type FrameParser<T> = (frame: FrameRecord) => SubscriptionItem<T>;
|
|
27
|
+
class SubscriptionStream<T> implements Subscription<T> {
|
|
28
|
+
#done = false;
|
|
29
|
+
constructor(
|
|
30
|
+
protected readonly connection: Connection,
|
|
31
|
+
protected readonly registration: SubscriptionRegistration,
|
|
32
|
+
private readonly parse: FrameParser<T>,
|
|
33
|
+
) {}
|
|
34
|
+
get subscriptionId(): string { return this.registration.id; }
|
|
35
|
+
get retainedCount(): number { return this.registration.queue.retainedCount; }
|
|
36
|
+
[Symbol.asyncIterator](): this { return this; }
|
|
37
|
+
async next(): Promise<IteratorResult<SubscriptionItem<T>>> {
|
|
38
|
+
if (this.#done) return { done: true, value: undefined };
|
|
39
|
+
const queued = await this.registration.queue.recv();
|
|
40
|
+
if (queued.done) {
|
|
41
|
+
if (this.registration.overflowed) {
|
|
42
|
+
this.registration.overflowed = false; this.#done = true;
|
|
43
|
+
return { done: false, value: { type: 'closed', reason: 'SLOW_CONSUMER' } };
|
|
44
|
+
}
|
|
45
|
+
this.#done = true; return { done: true, value: undefined };
|
|
46
|
+
}
|
|
47
|
+
try {
|
|
48
|
+
const value = this.parse(queued.value);
|
|
49
|
+
if (value.type === 'closed') this.#finishWithoutCancel();
|
|
50
|
+
return { done: false, value };
|
|
51
|
+
} catch (cause) { await this.#terminate(true); throw cause; }
|
|
52
|
+
}
|
|
53
|
+
async return(): Promise<IteratorResult<SubscriptionItem<T>>> {
|
|
54
|
+
await this.#terminate(true); return { done: true, value: undefined };
|
|
55
|
+
}
|
|
56
|
+
async throw(error?: unknown): Promise<IteratorResult<SubscriptionItem<T>>> {
|
|
57
|
+
await this.#terminate(true); throw error;
|
|
58
|
+
}
|
|
59
|
+
cancel(): Promise<void> { return this.#terminate(true); }
|
|
60
|
+
async #terminate(sendCancel: boolean): Promise<void> {
|
|
61
|
+
if (this.#done) return;
|
|
62
|
+
this.#done = true;
|
|
63
|
+
this.connection.unregisterSubscription(this.registration.id, this.registration);
|
|
64
|
+
this.registration.queue.closeAndDiscard();
|
|
65
|
+
if (sendCancel) {
|
|
66
|
+
try { await this.connection.cancelSubscription(this.registration); }
|
|
67
|
+
catch (error) { this.connection.recordDiagnostic(error); }
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
#finishWithoutCancel(): void {
|
|
71
|
+
if (this.#done) return;
|
|
72
|
+
this.#done = true;
|
|
73
|
+
this.connection.unregisterSubscription(this.registration.id, this.registration);
|
|
74
|
+
this.registration.queue.closeAndDiscard();
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
function parseCursorlessClosed(params: FrameRecord, definition: string): SubscriptionClosedItem {
|
|
79
|
+
assertDefinition(definition, params);
|
|
80
|
+
return { type: 'closed', reason: params.reason as SubscriptionCloseReason };
|
|
81
|
+
}
|
|
82
|
+
function parseWatchClosed(params: FrameRecord): WatchSubscriptionClosedItem {
|
|
83
|
+
assertDefinition('SubscriptionClosedNotification', params);
|
|
84
|
+
const cursor = params.lastDeliveredCursor as string | null | undefined;
|
|
85
|
+
return {
|
|
86
|
+
type: 'closed',
|
|
87
|
+
reason: params.reason as SubscriptionCloseReason,
|
|
88
|
+
...(cursor === undefined ? {} : { lastDeliveredCursor: cursor }),
|
|
89
|
+
};
|
|
90
|
+
}
|
|
91
|
+
function subscriptionParser<T>(
|
|
92
|
+
field: string,
|
|
93
|
+
eventDefinition: string,
|
|
94
|
+
closedDefinition: string,
|
|
95
|
+
): FrameParser<T> {
|
|
96
|
+
return (frame) => {
|
|
97
|
+
if (!isRecord(frame.params)) {
|
|
98
|
+
throw new ClusterProtocolError('notification params are missing', 'INVALID_SUBSCRIPTION_EVENT');
|
|
99
|
+
}
|
|
100
|
+
if (frame.method === 'subscription/closed') {
|
|
101
|
+
return parseCursorlessClosed(frame.params, closedDefinition);
|
|
102
|
+
}
|
|
103
|
+
if (frame.method !== 'event') {
|
|
104
|
+
throw new ClusterProtocolError('malformed subscription event', 'INVALID_SUBSCRIPTION_EVENT');
|
|
105
|
+
}
|
|
106
|
+
assertDefinition(eventDefinition, frame.params);
|
|
107
|
+
return { type: 'event', event: frame.params[field] as T };
|
|
108
|
+
};
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
export class LogsSubscriptionStream extends SubscriptionStream<LogRecord> {
|
|
112
|
+
constructor(connection: Connection, registration: SubscriptionRegistration) {
|
|
113
|
+
super(connection, registration, subscriptionParser('record', 'LogEventNotification', 'LogsClosedNotification'));
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
export class AgentAttachSubscriptionStream extends SubscriptionStream<AgentAttachEvent> {
|
|
117
|
+
constructor(connection: Connection, registration: SubscriptionRegistration) {
|
|
118
|
+
super(connection, registration, subscriptionParser('event', 'AgentAttachEventNotification', 'AgentAttachClosedNotification'));
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
type WatchStreamInit = {
|
|
123
|
+
readonly connection: Connection;
|
|
124
|
+
readonly registration: SubscriptionRegistration;
|
|
125
|
+
readonly result: WatchResult;
|
|
126
|
+
readonly params: WatchParams;
|
|
127
|
+
readonly lastSeenRunId?: string;
|
|
128
|
+
readonly lastSeenCursor?: string;
|
|
129
|
+
};
|
|
130
|
+
export class WatchSubscriptionStream implements AsyncIterator<WatchSubscriptionItem>, AsyncIterable<WatchSubscriptionItem> {
|
|
131
|
+
readonly #connection: Connection;
|
|
132
|
+
readonly #registration: SubscriptionRegistration;
|
|
133
|
+
#lastSeenRunId: string | undefined;
|
|
134
|
+
#lastSeenCursor: string | undefined;
|
|
135
|
+
#lastDelivered: string | null | undefined;
|
|
136
|
+
#runId: string | null | undefined;
|
|
137
|
+
#done = false;
|
|
138
|
+
#reconnectConsumed = false;
|
|
139
|
+
#readBarrier: Promise<void> = Promise.resolve();
|
|
140
|
+
constructor(init: WatchStreamInit) {
|
|
141
|
+
this.#connection = init.connection;
|
|
142
|
+
this.#registration = init.registration;
|
|
143
|
+
this.#lastSeenRunId = init.lastSeenRunId;
|
|
144
|
+
this.#lastSeenCursor = init.lastSeenCursor;
|
|
145
|
+
this.#lastDelivered = init.params.fromCursor;
|
|
146
|
+
this.#runId = init.result.runId ?? init.params.runId;
|
|
147
|
+
}
|
|
148
|
+
get subscriptionId(): string { return this.#registration.id; }
|
|
149
|
+
get retainedCount(): number { return this.#registration.queue.retainedCount; }
|
|
150
|
+
get lastDeliveredCursor(): string | null | undefined { return this.#lastDelivered; }
|
|
151
|
+
[Symbol.asyncIterator](): this { return this; }
|
|
152
|
+
next(): Promise<IteratorResult<WatchSubscriptionItem>> {
|
|
153
|
+
const read = this.#readBarrier.then(() => this.#nextLogical());
|
|
154
|
+
this.#readBarrier = read.then(() => undefined, () => undefined);
|
|
155
|
+
return read;
|
|
156
|
+
}
|
|
157
|
+
async #nextLogical(): Promise<IteratorResult<WatchSubscriptionItem>> {
|
|
158
|
+
while (!this.#done) {
|
|
159
|
+
const queued = await this.#registration.queue.recv();
|
|
160
|
+
if (queued.done) return this.#finishQueue();
|
|
161
|
+
const item = await this.#decode(queued.value);
|
|
162
|
+
if (item) return { done: false, value: item };
|
|
163
|
+
}
|
|
164
|
+
return { done: true, value: undefined };
|
|
165
|
+
}
|
|
166
|
+
#finishQueue(): IteratorResult<WatchSubscriptionItem> {
|
|
167
|
+
this.#done = true;
|
|
168
|
+
if (!this.#registration.overflowed) return { done: true, value: undefined };
|
|
169
|
+
this.#registration.overflowed = false;
|
|
170
|
+
const cursor = this.#lastDelivered;
|
|
171
|
+
return {
|
|
172
|
+
done: false,
|
|
173
|
+
value: { type: 'closed', reason: 'SLOW_CONSUMER', ...(cursor === undefined ? {} : { lastDeliveredCursor: cursor }) },
|
|
174
|
+
};
|
|
175
|
+
}
|
|
176
|
+
async #decode(frame: FrameRecord): Promise<WatchSubscriptionItem | undefined> {
|
|
177
|
+
if (!isRecord(frame.params)) return this.#invalid('notification params are missing');
|
|
178
|
+
if (frame.method === 'subscription/closed') {
|
|
179
|
+
const closed = parseWatchClosed(frame.params);
|
|
180
|
+
if (closed.lastDeliveredCursor !== undefined) this.#lastDelivered = closed.lastDeliveredCursor;
|
|
181
|
+
this.#finishWithoutCancel(); return closed;
|
|
182
|
+
}
|
|
183
|
+
if (frame.method !== 'event') return this.#invalid('malformed watch event');
|
|
184
|
+
try { assertDefinition('EventNotification', frame.params); }
|
|
185
|
+
catch { return this.#invalid('malformed watch event'); }
|
|
186
|
+
const { runId, cursor, event } = frame.params as {
|
|
187
|
+
readonly runId: string; readonly cursor: string; readonly event: WatchEvent;
|
|
188
|
+
};
|
|
189
|
+
if (this.#lastSeenRunId === runId && this.#lastSeenCursor === cursor) return undefined;
|
|
190
|
+
this.#lastSeenRunId = runId; this.#lastSeenCursor = cursor;
|
|
191
|
+
this.#runId = runId; this.#lastDelivered = cursor;
|
|
192
|
+
return { type: 'event', runId, cursor, event };
|
|
193
|
+
}
|
|
194
|
+
async return(): Promise<IteratorResult<WatchSubscriptionItem>> {
|
|
195
|
+
await this.#terminate(true); return { done: true, value: undefined };
|
|
196
|
+
}
|
|
197
|
+
async throw(error?: unknown): Promise<IteratorResult<WatchSubscriptionItem>> {
|
|
198
|
+
await this.#terminate(true); throw error;
|
|
199
|
+
}
|
|
200
|
+
cancel(): Promise<void> { return this.#terminate(true); }
|
|
201
|
+
reconnect(freshConnection: Connection): Promise<import('./client.js').WatchSubscription> {
|
|
202
|
+
if (this.#reconnectConsumed) {
|
|
203
|
+
throw new ClusterStateError('watch stream reconnect is one-shot', 'RECONNECT_CONSUMED');
|
|
204
|
+
}
|
|
205
|
+
this.#reconnectConsumed = true; return this.#reconnectOn(freshConnection);
|
|
206
|
+
}
|
|
207
|
+
async #reconnectOn(freshConnection: Connection): Promise<import('./client.js').WatchSubscription> {
|
|
208
|
+
await this.#terminate(true);
|
|
209
|
+
try {
|
|
210
|
+
const runId = this.#runId; const fromCursor = this.#lastDelivered;
|
|
211
|
+
const params: WatchParams = {
|
|
212
|
+
...(runId === undefined ? {} : { runId }),
|
|
213
|
+
...(fromCursor === undefined ? {} : { fromCursor }),
|
|
214
|
+
};
|
|
215
|
+
const established = await freshConnection.openSubscription('watch', params);
|
|
216
|
+
return {
|
|
217
|
+
result: established.result,
|
|
218
|
+
stream: new WatchSubscriptionStream({
|
|
219
|
+
connection: freshConnection,
|
|
220
|
+
registration: established.registration,
|
|
221
|
+
result: established.result,
|
|
222
|
+
params,
|
|
223
|
+
...(this.#lastSeenRunId === undefined ? {} : { lastSeenRunId: this.#lastSeenRunId }),
|
|
224
|
+
...(this.#lastSeenCursor === undefined ? {} : { lastSeenCursor: this.#lastSeenCursor }),
|
|
225
|
+
}),
|
|
226
|
+
};
|
|
227
|
+
} catch (error) { await freshConnection.close(); throw error; }
|
|
228
|
+
}
|
|
229
|
+
async #terminate(sendCancel: boolean): Promise<void> {
|
|
230
|
+
if (this.#done) return;
|
|
231
|
+
this.#done = true;
|
|
232
|
+
this.#connection.unregisterSubscription(this.#registration.id, this.#registration);
|
|
233
|
+
this.#registration.queue.closeAndDiscard();
|
|
234
|
+
if (sendCancel) {
|
|
235
|
+
try { await this.#connection.cancelSubscription(this.#registration); }
|
|
236
|
+
catch (error) { this.#connection.recordDiagnostic(error); }
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
#finishWithoutCancel(): void {
|
|
240
|
+
if (this.#done) return;
|
|
241
|
+
this.#done = true;
|
|
242
|
+
this.#connection.unregisterSubscription(this.#registration.id, this.#registration);
|
|
243
|
+
this.#registration.queue.closeAndDiscard();
|
|
244
|
+
}
|
|
245
|
+
async #invalid(message: string): Promise<never> {
|
|
246
|
+
await this.#terminate(true);
|
|
247
|
+
throw new ClusterProtocolError(message, 'INVALID_SUBSCRIPTION_EVENT');
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
Object.defineProperty(SubscriptionStream.prototype, Symbol.asyncDispose, {
|
|
252
|
+
configurable: true, value: SubscriptionStream.prototype.cancel,
|
|
253
|
+
});
|
|
254
|
+
Object.defineProperty(WatchSubscriptionStream.prototype, Symbol.asyncDispose, {
|
|
255
|
+
configurable: true, value: WatchSubscriptionStream.prototype.cancel,
|
|
256
|
+
});
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import Ajv2020 from 'ajv/dist/2020.js';
|
|
2
|
+
import type { ValidateFunction } from 'ajv';
|
|
3
|
+
import {
|
|
4
|
+
METHOD_RESULT_DEFINITIONS,
|
|
5
|
+
PROTOCOL_VERSION,
|
|
6
|
+
} from './generated/protocol.js';
|
|
7
|
+
import type { ClusterMethod } from './generated/protocol.js';
|
|
8
|
+
import { CLUSTER_PROTOCOL_SCHEMA } from './generated/protocol-schema.js';
|
|
9
|
+
import { ClusterProtocolError } from './errors.js';
|
|
10
|
+
|
|
11
|
+
const ajv = new Ajv2020({
|
|
12
|
+
allErrors: true,
|
|
13
|
+
strict: false,
|
|
14
|
+
validateFormats: false,
|
|
15
|
+
});
|
|
16
|
+
ajv.addSchema(CLUSTER_PROTOCOL_SCHEMA as object, 'openengine-cluster-v1');
|
|
17
|
+
const validators = new Map<string, ValidateFunction>();
|
|
18
|
+
|
|
19
|
+
function validatorFor(definition: string): ValidateFunction {
|
|
20
|
+
const cached = validators.get(definition);
|
|
21
|
+
if (cached) return cached;
|
|
22
|
+
const validator = ajv.compile({
|
|
23
|
+
$ref: `openengine-cluster-v1#/$defs/${definition}`,
|
|
24
|
+
});
|
|
25
|
+
validators.set(definition, validator);
|
|
26
|
+
return validator;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export function assertDefinition(definition: string, value: unknown): void {
|
|
30
|
+
const validate = validatorFor(definition);
|
|
31
|
+
if (validate(value)) return;
|
|
32
|
+
const details = (validate.errors ?? []).map((error) =>
|
|
33
|
+
`${error.instancePath || '/'} ${error.message ?? 'is invalid'}`
|
|
34
|
+
).join('; ');
|
|
35
|
+
throw new ClusterProtocolError(
|
|
36
|
+
`${definition} validation failed: ${details}`,
|
|
37
|
+
'INVALID_RESPONSE',
|
|
38
|
+
);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
export function assertMethodResult(method: ClusterMethod, value: unknown): void {
|
|
42
|
+
try {
|
|
43
|
+
assertDefinition(METHOD_RESULT_DEFINITIONS[method], value);
|
|
44
|
+
} catch (error) {
|
|
45
|
+
const receivedVersion = method === 'initialize' && value !== null && typeof value === 'object'
|
|
46
|
+
? (value as Readonly<Record<string, unknown>>).protocolVersion
|
|
47
|
+
: undefined;
|
|
48
|
+
if (typeof receivedVersion === 'string' && receivedVersion !== PROTOCOL_VERSION) {
|
|
49
|
+
throw new ClusterProtocolError(
|
|
50
|
+
`unsupported protocol version ${receivedVersion}`,
|
|
51
|
+
'UNSUPPORTED_PROTOCOL_VERSION',
|
|
52
|
+
);
|
|
53
|
+
}
|
|
54
|
+
throw error;
|
|
55
|
+
}
|
|
56
|
+
}
|
package/src/isolation-manager.js
CHANGED
|
@@ -699,6 +699,22 @@ class IsolationManager {
|
|
|
699
699
|
});
|
|
700
700
|
}
|
|
701
701
|
|
|
702
|
+
async getContainerEnvironmentValue(clusterId, name) {
|
|
703
|
+
if (!/^[A-Za-z_][A-Za-z0-9_]*$/.test(name)) {
|
|
704
|
+
throw new Error(`Invalid container environment variable name: ${name}`);
|
|
705
|
+
}
|
|
706
|
+
const result = await this.execInContainer(clusterId, ['printenv', name]);
|
|
707
|
+
if (result.code === 1) return null;
|
|
708
|
+
if (result.code !== 0) {
|
|
709
|
+
throw new Error(
|
|
710
|
+
`Failed to read container environment variable ${name}: ${
|
|
711
|
+
result.stderr || `exit ${result.code}`
|
|
712
|
+
}`
|
|
713
|
+
);
|
|
714
|
+
}
|
|
715
|
+
return result.stdout.replace(/\r?\n$/, '');
|
|
716
|
+
}
|
|
717
|
+
|
|
702
718
|
/**
|
|
703
719
|
* Spawn a PTY-like process inside the container
|
|
704
720
|
* Returns a child process that can be used like a PTY
|
|
@@ -174,7 +174,7 @@ class JiraProvider extends IssueProvider {
|
|
|
174
174
|
const issueKey = this._extractIssueKey(identifier, settings);
|
|
175
175
|
|
|
176
176
|
// Fetch issue using jira CLI
|
|
177
|
-
const cmd = `jira
|
|
177
|
+
const cmd = `jira view ${issueKey} --template json`;
|
|
178
178
|
const output = execSync(cmd, { encoding: 'utf8' });
|
|
179
179
|
const issue = JSON.parse(output);
|
|
180
180
|
|
|
@@ -239,10 +239,10 @@ class LinearProvider extends IssueProvider {
|
|
|
239
239
|
}
|
|
240
240
|
|
|
241
241
|
const key = teams[0].key;
|
|
242
|
-
const {
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
242
|
+
const { mutateSettings } = require('../../lib/settings');
|
|
243
|
+
mutateSettings((current) => {
|
|
244
|
+
current.linearTeam = key;
|
|
245
|
+
});
|
|
246
246
|
return key;
|
|
247
247
|
}
|
|
248
248
|
|
package/task-lib/runner.js
CHANGED
|
@@ -125,6 +125,9 @@ function buildProviderOptions(options, runtime, modelSelection) {
|
|
|
125
125
|
...mcpConfigOption(options),
|
|
126
126
|
...claudeSettingsFileOption(),
|
|
127
127
|
...(options.resume ? { resumeSessionId: options.resume } : {}),
|
|
128
|
+
...(process.env.ZEROSHOT_OPENCODE_AGENT?.trim()
|
|
129
|
+
? { agentName: process.env.ZEROSHOT_OPENCODE_AGENT.trim() }
|
|
130
|
+
: {}),
|
|
128
131
|
...(options.continue ? { continueSession: true } : {}),
|
|
129
132
|
};
|
|
130
133
|
}
|