@harness-engineering/orchestrator 0.9.0 → 0.9.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/dist/index.d.mts +24 -0
- package/dist/index.d.ts +24 -0
- package/dist/index.js +398 -287
- package/dist/index.mjs +398 -287
- package/package.json +5 -5
package/dist/index.js
CHANGED
|
@@ -2569,6 +2569,9 @@ var RoadmapTrackerAdapter = class {
|
|
|
2569
2569
|
// src/tracker/extensions/linear.ts
|
|
2570
2570
|
var import_types5 = require("@harness-engineering/types");
|
|
2571
2571
|
var LINEAR_GRAPHQL_ENDPOINT = "https://api.linear.app/graphql";
|
|
2572
|
+
function errorMessage(err) {
|
|
2573
|
+
return err instanceof Error ? err.message : String(err);
|
|
2574
|
+
}
|
|
2572
2575
|
var LinearGraphQLClient = class {
|
|
2573
2576
|
apiKey;
|
|
2574
2577
|
endpoint;
|
|
@@ -2579,9 +2582,23 @@ var LinearGraphQLClient = class {
|
|
|
2579
2582
|
this.fetchFn = opts.fetchFn ?? globalThis.fetch;
|
|
2580
2583
|
}
|
|
2581
2584
|
async query(query, variables) {
|
|
2582
|
-
|
|
2585
|
+
const sent = await this.sendRequest(query, variables);
|
|
2586
|
+
if (!sent.ok) return sent;
|
|
2587
|
+
const res = sent.value;
|
|
2588
|
+
if (!res.ok) {
|
|
2589
|
+
return (0, import_types5.Err)(await this.httpError(res));
|
|
2590
|
+
}
|
|
2591
|
+
const parsed = await this.parseEnvelope(res);
|
|
2592
|
+
if (!parsed.ok) return parsed;
|
|
2593
|
+
const envelope = parsed.value;
|
|
2594
|
+
const graphqlError = envelopeError(envelope);
|
|
2595
|
+
if (graphqlError) return (0, import_types5.Err)(graphqlError);
|
|
2596
|
+
return (0, import_types5.Ok)(envelope.data ?? {});
|
|
2597
|
+
}
|
|
2598
|
+
/** POST the operation to Linear, normalizing transport throws into an `Err`. */
|
|
2599
|
+
async sendRequest(query, variables) {
|
|
2583
2600
|
try {
|
|
2584
|
-
res = await this.fetchFn(this.endpoint, {
|
|
2601
|
+
const res = await this.fetchFn(this.endpoint, {
|
|
2585
2602
|
method: "POST",
|
|
2586
2603
|
headers: {
|
|
2587
2604
|
Authorization: this.apiKey,
|
|
@@ -2589,35 +2606,31 @@ var LinearGraphQLClient = class {
|
|
|
2589
2606
|
},
|
|
2590
2607
|
body: JSON.stringify({ query, variables: variables ?? {} })
|
|
2591
2608
|
});
|
|
2609
|
+
return (0, import_types5.Ok)(res);
|
|
2592
2610
|
} catch (err) {
|
|
2593
|
-
return (0, import_types5.Err)(
|
|
2594
|
-
new Error(
|
|
2595
|
-
`Linear GraphQL request failed: ${err instanceof Error ? err.message : String(err)}`
|
|
2596
|
-
)
|
|
2597
|
-
);
|
|
2598
|
-
}
|
|
2599
|
-
if (!res.ok) {
|
|
2600
|
-
const body = await res.text().catch(() => "");
|
|
2601
|
-
const detail = body ? `: ${body.slice(0, 500)}` : "";
|
|
2602
|
-
return (0, import_types5.Err)(new Error(`Linear GraphQL HTTP ${res.status}${detail}`));
|
|
2611
|
+
return (0, import_types5.Err)(new Error(`Linear GraphQL request failed: ${errorMessage(err)}`));
|
|
2603
2612
|
}
|
|
2604
|
-
|
|
2613
|
+
}
|
|
2614
|
+
/** Build the error for a non-2xx HTTP response, including a truncated body. */
|
|
2615
|
+
async httpError(res) {
|
|
2616
|
+
const body = await res.text().catch(() => "");
|
|
2617
|
+
const detail = body ? `: ${body.slice(0, 500)}` : "";
|
|
2618
|
+
return new Error(`Linear GraphQL HTTP ${res.status}${detail}`);
|
|
2619
|
+
}
|
|
2620
|
+
/** Parse the JSON envelope, normalizing parse failures into an `Err`. */
|
|
2621
|
+
async parseEnvelope(res) {
|
|
2605
2622
|
try {
|
|
2606
|
-
|
|
2623
|
+
return (0, import_types5.Ok)(await res.json());
|
|
2607
2624
|
} catch (err) {
|
|
2608
|
-
return (0, import_types5.Err)(
|
|
2609
|
-
new Error(
|
|
2610
|
-
`Linear GraphQL response was not valid JSON: ${err instanceof Error ? err.message : String(err)}`
|
|
2611
|
-
)
|
|
2612
|
-
);
|
|
2613
|
-
}
|
|
2614
|
-
if (envelope.errors && envelope.errors.length > 0) {
|
|
2615
|
-
const message = envelope.errors.map((e) => e.message ?? "unknown error").join("; ");
|
|
2616
|
-
return (0, import_types5.Err)(new Error(`Linear GraphQL error: ${message}`));
|
|
2625
|
+
return (0, import_types5.Err)(new Error(`Linear GraphQL response was not valid JSON: ${errorMessage(err)}`));
|
|
2617
2626
|
}
|
|
2618
|
-
return (0, import_types5.Ok)(envelope.data ?? {});
|
|
2619
2627
|
}
|
|
2620
2628
|
};
|
|
2629
|
+
function envelopeError(envelope) {
|
|
2630
|
+
if (!envelope.errors || envelope.errors.length === 0) return void 0;
|
|
2631
|
+
const message = envelope.errors.map((e) => e.message ?? "unknown error").join("; ");
|
|
2632
|
+
return new Error(`Linear GraphQL error: ${message}`);
|
|
2633
|
+
}
|
|
2621
2634
|
var LinearGraphQLStub = class {
|
|
2622
2635
|
async query(query, _variables) {
|
|
2623
2636
|
console.log("Linear GraphQL query (stub):", query);
|
|
@@ -4805,18 +4818,18 @@ var AnthropicBackend = class {
|
|
|
4805
4818
|
usage
|
|
4806
4819
|
};
|
|
4807
4820
|
} catch (err) {
|
|
4808
|
-
const
|
|
4821
|
+
const errorMessage2 = err instanceof Error ? err.message : "Anthropic request failed";
|
|
4809
4822
|
yield {
|
|
4810
4823
|
type: "error",
|
|
4811
4824
|
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
4812
|
-
content:
|
|
4825
|
+
content: errorMessage2,
|
|
4813
4826
|
sessionId: session.sessionId
|
|
4814
4827
|
};
|
|
4815
4828
|
return {
|
|
4816
4829
|
success: false,
|
|
4817
4830
|
sessionId: session.sessionId,
|
|
4818
4831
|
usage: { inputTokens: 0, outputTokens: 0, totalTokens: 0 },
|
|
4819
|
-
error:
|
|
4832
|
+
error: errorMessage2
|
|
4820
4833
|
};
|
|
4821
4834
|
}
|
|
4822
4835
|
}
|
|
@@ -4907,18 +4920,18 @@ var OpenAIBackend = class {
|
|
|
4907
4920
|
}
|
|
4908
4921
|
}
|
|
4909
4922
|
} catch (err) {
|
|
4910
|
-
const
|
|
4923
|
+
const errorMessage2 = err instanceof Error ? err.message : "OpenAI request failed";
|
|
4911
4924
|
yield {
|
|
4912
4925
|
type: "error",
|
|
4913
4926
|
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
4914
|
-
content:
|
|
4927
|
+
content: errorMessage2,
|
|
4915
4928
|
sessionId: session.sessionId
|
|
4916
4929
|
};
|
|
4917
4930
|
return {
|
|
4918
4931
|
success: false,
|
|
4919
4932
|
sessionId: session.sessionId,
|
|
4920
4933
|
usage: { inputTokens: 0, outputTokens: 0, totalTokens: 0 },
|
|
4921
|
-
error:
|
|
4934
|
+
error: errorMessage2
|
|
4922
4935
|
};
|
|
4923
4936
|
}
|
|
4924
4937
|
const usage = {
|
|
@@ -5029,11 +5042,11 @@ var GeminiBackend = class {
|
|
|
5029
5042
|
}
|
|
5030
5043
|
}
|
|
5031
5044
|
} catch (err) {
|
|
5032
|
-
const
|
|
5045
|
+
const errorMessage2 = err instanceof Error ? err.message : "Gemini request failed";
|
|
5033
5046
|
yield {
|
|
5034
5047
|
type: "error",
|
|
5035
5048
|
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
5036
|
-
content:
|
|
5049
|
+
content: errorMessage2,
|
|
5037
5050
|
sessionId: session.sessionId
|
|
5038
5051
|
};
|
|
5039
5052
|
return {
|
|
@@ -5046,7 +5059,7 @@ var GeminiBackend = class {
|
|
|
5046
5059
|
cacheCreationTokens,
|
|
5047
5060
|
cacheReadTokens
|
|
5048
5061
|
},
|
|
5049
|
-
error:
|
|
5062
|
+
error: errorMessage2
|
|
5050
5063
|
};
|
|
5051
5064
|
}
|
|
5052
5065
|
const usage = {
|
|
@@ -5172,18 +5185,18 @@ var LocalBackend = class {
|
|
|
5172
5185
|
}
|
|
5173
5186
|
}
|
|
5174
5187
|
} catch (err) {
|
|
5175
|
-
const
|
|
5188
|
+
const errorMessage2 = err instanceof Error ? err.message : "Local backend request failed";
|
|
5176
5189
|
yield {
|
|
5177
5190
|
type: "error",
|
|
5178
5191
|
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
5179
|
-
content:
|
|
5192
|
+
content: errorMessage2,
|
|
5180
5193
|
sessionId: session.sessionId
|
|
5181
5194
|
};
|
|
5182
5195
|
return {
|
|
5183
5196
|
success: false,
|
|
5184
5197
|
sessionId: session.sessionId,
|
|
5185
5198
|
usage: { inputTokens: 0, outputTokens: 0, totalTokens: 0 },
|
|
5186
|
-
error:
|
|
5199
|
+
error: errorMessage2
|
|
5187
5200
|
};
|
|
5188
5201
|
}
|
|
5189
5202
|
const usage = { inputTokens, outputTokens, totalTokens };
|
|
@@ -5519,30 +5532,8 @@ var SshBackend = class {
|
|
|
5519
5532
|
config;
|
|
5520
5533
|
spawnImpl;
|
|
5521
5534
|
constructor(config) {
|
|
5522
|
-
|
|
5523
|
-
|
|
5524
|
-
}
|
|
5525
|
-
if (FORBIDDEN_HOST_CHARS.test(config.host) || config.host.startsWith("-")) {
|
|
5526
|
-
throw new Error(
|
|
5527
|
-
`SshBackend: invalid host '${config.host}' (contains shell metacharacters or starts with '-')`
|
|
5528
|
-
);
|
|
5529
|
-
}
|
|
5530
|
-
if (!config.remoteCommand || typeof config.remoteCommand !== "string") {
|
|
5531
|
-
throw new Error("SshBackend: `remoteCommand` is required");
|
|
5532
|
-
}
|
|
5533
|
-
if (config.user !== void 0 && /[\s;&|`$]/.test(config.user)) {
|
|
5534
|
-
throw new Error(`SshBackend: invalid user '${config.user}'`);
|
|
5535
|
-
}
|
|
5536
|
-
this.config = {
|
|
5537
|
-
host: config.host,
|
|
5538
|
-
remoteCommand: config.remoteCommand,
|
|
5539
|
-
sshBinary: config.sshBinary ?? "ssh",
|
|
5540
|
-
sshOptions: config.sshOptions ?? [],
|
|
5541
|
-
timeoutMs: config.timeoutMs ?? DEFAULT_TIMEOUT_MS2,
|
|
5542
|
-
...config.user !== void 0 ? { user: config.user } : {},
|
|
5543
|
-
...config.port !== void 0 ? { port: config.port } : {},
|
|
5544
|
-
...config.identityFile !== void 0 ? { identityFile: config.identityFile } : {}
|
|
5545
|
-
};
|
|
5535
|
+
validateSshConfig(config);
|
|
5536
|
+
this.config = normalizeSshConfig(config);
|
|
5546
5537
|
this.spawnImpl = config.spawnImpl ?? import_node_child_process5.spawn;
|
|
5547
5538
|
}
|
|
5548
5539
|
/**
|
|
@@ -5694,6 +5685,34 @@ var SshBackend = class {
|
|
|
5694
5685
|
});
|
|
5695
5686
|
}
|
|
5696
5687
|
};
|
|
5688
|
+
function validateSshConfig(config) {
|
|
5689
|
+
if (!config.host || typeof config.host !== "string") {
|
|
5690
|
+
throw new Error("SshBackend: `host` is required");
|
|
5691
|
+
}
|
|
5692
|
+
if (FORBIDDEN_HOST_CHARS.test(config.host) || config.host.startsWith("-")) {
|
|
5693
|
+
throw new Error(
|
|
5694
|
+
`SshBackend: invalid host '${config.host}' (contains shell metacharacters or starts with '-')`
|
|
5695
|
+
);
|
|
5696
|
+
}
|
|
5697
|
+
if (!config.remoteCommand || typeof config.remoteCommand !== "string") {
|
|
5698
|
+
throw new Error("SshBackend: `remoteCommand` is required");
|
|
5699
|
+
}
|
|
5700
|
+
if (config.user !== void 0 && /[\s;&|`$]/.test(config.user)) {
|
|
5701
|
+
throw new Error(`SshBackend: invalid user '${config.user}'`);
|
|
5702
|
+
}
|
|
5703
|
+
}
|
|
5704
|
+
function normalizeSshConfig(config) {
|
|
5705
|
+
return {
|
|
5706
|
+
host: config.host,
|
|
5707
|
+
remoteCommand: config.remoteCommand,
|
|
5708
|
+
sshBinary: config.sshBinary ?? "ssh",
|
|
5709
|
+
sshOptions: config.sshOptions ?? [],
|
|
5710
|
+
timeoutMs: config.timeoutMs ?? DEFAULT_TIMEOUT_MS2,
|
|
5711
|
+
...config.user !== void 0 ? { user: config.user } : {},
|
|
5712
|
+
...config.port !== void 0 ? { port: config.port } : {},
|
|
5713
|
+
...config.identityFile !== void 0 ? { identityFile: config.identityFile } : {}
|
|
5714
|
+
};
|
|
5715
|
+
}
|
|
5697
5716
|
function errResult(sessionId, message) {
|
|
5698
5717
|
return {
|
|
5699
5718
|
success: false,
|
|
@@ -5800,23 +5819,8 @@ var OciServerlessBackend = class extends ServerlessBackend {
|
|
|
5800
5819
|
envSource;
|
|
5801
5820
|
constructor(config) {
|
|
5802
5821
|
super();
|
|
5803
|
-
|
|
5804
|
-
|
|
5805
|
-
}
|
|
5806
|
-
if (FORBIDDEN_IMAGE_CHARS.test(config.image) || config.image.startsWith("-")) {
|
|
5807
|
-
throw new Error(
|
|
5808
|
-
`OciServerlessBackend: invalid image '${config.image}' (contains shell metacharacters or starts with '-')`
|
|
5809
|
-
);
|
|
5810
|
-
}
|
|
5811
|
-
this.config = {
|
|
5812
|
-
image: config.image,
|
|
5813
|
-
pullPolicy: config.pullPolicy ?? "if-not-present",
|
|
5814
|
-
runtime: config.runtime ?? "docker",
|
|
5815
|
-
envPassthrough: config.envPassthrough ?? [],
|
|
5816
|
-
timeoutMs: config.timeoutMs ?? DEFAULT_OCI_TIMEOUT_MS,
|
|
5817
|
-
extraArgs: sanitizeExtraArgs(config.extraArgs),
|
|
5818
|
-
...config.registry !== void 0 ? { registry: config.registry } : {}
|
|
5819
|
-
};
|
|
5822
|
+
validateOciImage(config.image);
|
|
5823
|
+
this.config = resolveOciConfig(config);
|
|
5820
5824
|
this.spawnImpl = config.spawnImpl ?? import_node_child_process6.spawn;
|
|
5821
5825
|
this.envSource = config.envSource ?? process.env;
|
|
5822
5826
|
}
|
|
@@ -5979,6 +5983,27 @@ var OciServerlessBackend = class extends ServerlessBackend {
|
|
|
5979
5983
|
});
|
|
5980
5984
|
}
|
|
5981
5985
|
};
|
|
5986
|
+
function validateOciImage(image) {
|
|
5987
|
+
if (!image || typeof image !== "string") {
|
|
5988
|
+
throw new Error("OciServerlessBackend: `image` is required");
|
|
5989
|
+
}
|
|
5990
|
+
if (FORBIDDEN_IMAGE_CHARS.test(image) || image.startsWith("-")) {
|
|
5991
|
+
throw new Error(
|
|
5992
|
+
`OciServerlessBackend: invalid image '${image}' (contains shell metacharacters or starts with '-')`
|
|
5993
|
+
);
|
|
5994
|
+
}
|
|
5995
|
+
}
|
|
5996
|
+
function resolveOciConfig(config) {
|
|
5997
|
+
return {
|
|
5998
|
+
image: config.image,
|
|
5999
|
+
pullPolicy: config.pullPolicy ?? "if-not-present",
|
|
6000
|
+
runtime: config.runtime ?? "docker",
|
|
6001
|
+
envPassthrough: config.envPassthrough ?? [],
|
|
6002
|
+
timeoutMs: config.timeoutMs ?? DEFAULT_OCI_TIMEOUT_MS,
|
|
6003
|
+
extraArgs: sanitizeExtraArgs(config.extraArgs),
|
|
6004
|
+
...config.registry !== void 0 ? { registry: config.registry } : {}
|
|
6005
|
+
};
|
|
6006
|
+
}
|
|
5982
6007
|
function sanitizeExtraArgs(extraArgs) {
|
|
5983
6008
|
if (!extraArgs) return [];
|
|
5984
6009
|
return extraArgs.filter((arg) => !BLOCKED_DOCKER_FLAGS.some((flag) => arg.startsWith(flag)));
|
|
@@ -6050,78 +6075,96 @@ function makeGetModel(model) {
|
|
|
6050
6075
|
if (Array.isArray(model) && model.length > 0) return () => model[0] ?? null;
|
|
6051
6076
|
return () => null;
|
|
6052
6077
|
}
|
|
6078
|
+
function createClaudeBackend(def, options) {
|
|
6079
|
+
return new ClaudeBackend(def.command ?? "claude", {
|
|
6080
|
+
...options.cacheMetrics ? { cacheMetrics: options.cacheMetrics } : {}
|
|
6081
|
+
});
|
|
6082
|
+
}
|
|
6083
|
+
function createAnthropicBackend(def) {
|
|
6084
|
+
return new AnthropicBackend({
|
|
6085
|
+
model: def.model,
|
|
6086
|
+
...def.apiKey !== void 0 ? { apiKey: def.apiKey } : {}
|
|
6087
|
+
});
|
|
6088
|
+
}
|
|
6089
|
+
function createOpenAIBackend(def) {
|
|
6090
|
+
return new OpenAIBackend({
|
|
6091
|
+
model: def.model,
|
|
6092
|
+
...def.apiKey !== void 0 ? { apiKey: def.apiKey } : {}
|
|
6093
|
+
});
|
|
6094
|
+
}
|
|
6095
|
+
function createGeminiBackend(def) {
|
|
6096
|
+
return new GeminiBackend({
|
|
6097
|
+
model: def.model,
|
|
6098
|
+
...def.apiKey !== void 0 ? { apiKey: def.apiKey } : {}
|
|
6099
|
+
});
|
|
6100
|
+
}
|
|
6101
|
+
function createLocalBackend(def) {
|
|
6102
|
+
const isArray = Array.isArray(def.model);
|
|
6103
|
+
return new LocalBackend({
|
|
6104
|
+
endpoint: def.endpoint,
|
|
6105
|
+
...typeof def.model === "string" ? { model: def.model } : {},
|
|
6106
|
+
...isArray ? { getModel: makeGetModel(def.model) } : {},
|
|
6107
|
+
...def.apiKey !== void 0 ? { apiKey: def.apiKey } : {},
|
|
6108
|
+
...def.timeoutMs !== void 0 ? { timeoutMs: def.timeoutMs } : {}
|
|
6109
|
+
});
|
|
6110
|
+
}
|
|
6111
|
+
function createPiBackend(def) {
|
|
6112
|
+
const isArray = Array.isArray(def.model);
|
|
6113
|
+
return new PiBackend({
|
|
6114
|
+
endpoint: def.endpoint,
|
|
6115
|
+
...typeof def.model === "string" ? { model: def.model } : {},
|
|
6116
|
+
...isArray ? { getModel: makeGetModel(def.model) } : {},
|
|
6117
|
+
...def.apiKey !== void 0 ? { apiKey: def.apiKey } : {},
|
|
6118
|
+
...def.timeoutMs !== void 0 ? { timeoutMs: def.timeoutMs } : {}
|
|
6119
|
+
});
|
|
6120
|
+
}
|
|
6121
|
+
function createSshBackend(def) {
|
|
6122
|
+
return new SshBackend({
|
|
6123
|
+
host: def.host,
|
|
6124
|
+
remoteCommand: def.remoteCommand,
|
|
6125
|
+
...def.user !== void 0 ? { user: def.user } : {},
|
|
6126
|
+
...def.port !== void 0 ? { port: def.port } : {},
|
|
6127
|
+
...def.identityFile !== void 0 ? { identityFile: def.identityFile } : {},
|
|
6128
|
+
...def.sshOptions !== void 0 ? { sshOptions: def.sshOptions } : {},
|
|
6129
|
+
...def.sshBinary !== void 0 ? { sshBinary: def.sshBinary } : {}
|
|
6130
|
+
});
|
|
6131
|
+
}
|
|
6132
|
+
function createServerlessBackend(def) {
|
|
6133
|
+
switch (def.adapter) {
|
|
6134
|
+
case "oci":
|
|
6135
|
+
return new OciServerlessBackend({
|
|
6136
|
+
image: def.image,
|
|
6137
|
+
...def.registry !== void 0 ? { registry: def.registry } : {},
|
|
6138
|
+
...def.pullPolicy !== void 0 ? { pullPolicy: def.pullPolicy } : {},
|
|
6139
|
+
...def.envPassthrough !== void 0 ? { envPassthrough: def.envPassthrough } : {},
|
|
6140
|
+
...def.runtime !== void 0 ? { runtime: def.runtime } : {}
|
|
6141
|
+
});
|
|
6142
|
+
default: {
|
|
6143
|
+
const exhaustive = def.adapter;
|
|
6144
|
+
throw new Error(`createBackend: unknown serverless adapter ${JSON.stringify(exhaustive)}`);
|
|
6145
|
+
}
|
|
6146
|
+
}
|
|
6147
|
+
}
|
|
6053
6148
|
function createBackend(def, options = {}) {
|
|
6054
6149
|
switch (def.type) {
|
|
6055
6150
|
case "mock":
|
|
6056
6151
|
return new MockBackend();
|
|
6057
6152
|
case "claude":
|
|
6058
|
-
return
|
|
6059
|
-
...options.cacheMetrics ? { cacheMetrics: options.cacheMetrics } : {}
|
|
6060
|
-
});
|
|
6153
|
+
return createClaudeBackend(def, options);
|
|
6061
6154
|
case "anthropic":
|
|
6062
|
-
return
|
|
6063
|
-
model: def.model,
|
|
6064
|
-
...def.apiKey !== void 0 ? { apiKey: def.apiKey } : {}
|
|
6065
|
-
});
|
|
6155
|
+
return createAnthropicBackend(def);
|
|
6066
6156
|
case "openai":
|
|
6067
|
-
return
|
|
6068
|
-
model: def.model,
|
|
6069
|
-
...def.apiKey !== void 0 ? { apiKey: def.apiKey } : {}
|
|
6070
|
-
});
|
|
6157
|
+
return createOpenAIBackend(def);
|
|
6071
6158
|
case "gemini":
|
|
6072
|
-
return
|
|
6073
|
-
|
|
6074
|
-
|
|
6075
|
-
|
|
6076
|
-
|
|
6077
|
-
|
|
6078
|
-
return
|
|
6079
|
-
|
|
6080
|
-
|
|
6081
|
-
...isArray ? { getModel: makeGetModel(def.model) } : {},
|
|
6082
|
-
...def.apiKey !== void 0 ? { apiKey: def.apiKey } : {},
|
|
6083
|
-
...def.timeoutMs !== void 0 ? { timeoutMs: def.timeoutMs } : {}
|
|
6084
|
-
});
|
|
6085
|
-
}
|
|
6086
|
-
case "pi": {
|
|
6087
|
-
const isArray = Array.isArray(def.model);
|
|
6088
|
-
return new PiBackend({
|
|
6089
|
-
endpoint: def.endpoint,
|
|
6090
|
-
...typeof def.model === "string" ? { model: def.model } : {},
|
|
6091
|
-
...isArray ? { getModel: makeGetModel(def.model) } : {},
|
|
6092
|
-
...def.apiKey !== void 0 ? { apiKey: def.apiKey } : {},
|
|
6093
|
-
...def.timeoutMs !== void 0 ? { timeoutMs: def.timeoutMs } : {}
|
|
6094
|
-
});
|
|
6095
|
-
}
|
|
6096
|
-
case "ssh": {
|
|
6097
|
-
return new SshBackend({
|
|
6098
|
-
host: def.host,
|
|
6099
|
-
remoteCommand: def.remoteCommand,
|
|
6100
|
-
...def.user !== void 0 ? { user: def.user } : {},
|
|
6101
|
-
...def.port !== void 0 ? { port: def.port } : {},
|
|
6102
|
-
...def.identityFile !== void 0 ? { identityFile: def.identityFile } : {},
|
|
6103
|
-
...def.sshOptions !== void 0 ? { sshOptions: def.sshOptions } : {},
|
|
6104
|
-
...def.sshBinary !== void 0 ? { sshBinary: def.sshBinary } : {}
|
|
6105
|
-
});
|
|
6106
|
-
}
|
|
6107
|
-
case "serverless": {
|
|
6108
|
-
switch (def.adapter) {
|
|
6109
|
-
case "oci":
|
|
6110
|
-
return new OciServerlessBackend({
|
|
6111
|
-
image: def.image,
|
|
6112
|
-
...def.registry !== void 0 ? { registry: def.registry } : {},
|
|
6113
|
-
...def.pullPolicy !== void 0 ? { pullPolicy: def.pullPolicy } : {},
|
|
6114
|
-
...def.envPassthrough !== void 0 ? { envPassthrough: def.envPassthrough } : {},
|
|
6115
|
-
...def.runtime !== void 0 ? { runtime: def.runtime } : {}
|
|
6116
|
-
});
|
|
6117
|
-
default: {
|
|
6118
|
-
const exhaustive = def.adapter;
|
|
6119
|
-
throw new Error(
|
|
6120
|
-
`createBackend: unknown serverless adapter ${JSON.stringify(exhaustive)}`
|
|
6121
|
-
);
|
|
6122
|
-
}
|
|
6123
|
-
}
|
|
6124
|
-
}
|
|
6159
|
+
return createGeminiBackend(def);
|
|
6160
|
+
case "local":
|
|
6161
|
+
return createLocalBackend(def);
|
|
6162
|
+
case "pi":
|
|
6163
|
+
return createPiBackend(def);
|
|
6164
|
+
case "ssh":
|
|
6165
|
+
return createSshBackend(def);
|
|
6166
|
+
case "serverless":
|
|
6167
|
+
return createServerlessBackend(def);
|
|
6125
6168
|
default: {
|
|
6126
6169
|
const exhaustive = def;
|
|
6127
6170
|
throw new Error(`createBackend: unknown backend type ${JSON.stringify(exhaustive)}`);
|
|
@@ -9668,26 +9711,41 @@ function hasScope(held, required) {
|
|
|
9668
9711
|
if (held.includes("admin")) return true;
|
|
9669
9712
|
return held.includes(required);
|
|
9670
9713
|
}
|
|
9671
|
-
function
|
|
9672
|
-
const bridgeScope = requiredBridgeScope(method, path24);
|
|
9673
|
-
if (bridgeScope) return bridgeScope;
|
|
9714
|
+
function exactScopeForRoute(method, path24) {
|
|
9674
9715
|
if (path24 === "/api/v1/auth/token" && method === "POST") return "admin";
|
|
9675
9716
|
if (path24 === "/api/v1/auth/tokens" && method === "GET") return "admin";
|
|
9676
9717
|
if (/^\/api\/v1\/auth\/tokens\/[^/]+$/.test(path24) && method === "DELETE") return "admin";
|
|
9677
9718
|
if ((path24 === "/api/state" || path24 === "/api/v1/state") && method === "GET") return "read-status";
|
|
9678
|
-
if (path24.startsWith("/api/interactions")) return "resolve-interaction";
|
|
9679
|
-
if (path24.startsWith("/api/plans")) return "read-status";
|
|
9680
|
-
if (path24.startsWith("/api/analyze") || path24.startsWith("/api/analyses")) return "read-status";
|
|
9681
|
-
if (path24.startsWith("/api/roadmap-actions")) return "modify-roadmap";
|
|
9682
|
-
if (path24.startsWith("/api/dispatch-actions")) return "trigger-job";
|
|
9683
|
-
if (path24.startsWith("/api/local-model") || path24.startsWith("/api/local-models"))
|
|
9684
|
-
return "read-status";
|
|
9685
|
-
if (path24.startsWith("/api/maintenance")) return "trigger-job";
|
|
9686
|
-
if (path24.startsWith("/api/streams")) return "read-status";
|
|
9687
|
-
if (path24.startsWith("/api/sessions")) return "read-status";
|
|
9688
|
-
if (path24 === "/api/chat" || path24.startsWith("/api/chat-proxy")) return "trigger-job";
|
|
9689
9719
|
return null;
|
|
9690
9720
|
}
|
|
9721
|
+
var PREFIX_SCOPES = [
|
|
9722
|
+
["/api/interactions", "resolve-interaction"],
|
|
9723
|
+
["/api/plans", "read-status"],
|
|
9724
|
+
["/api/analyze", "read-status"],
|
|
9725
|
+
["/api/analyses", "read-status"],
|
|
9726
|
+
["/api/roadmap-actions", "modify-roadmap"],
|
|
9727
|
+
["/api/dispatch-actions", "trigger-job"],
|
|
9728
|
+
["/api/local-model", "read-status"],
|
|
9729
|
+
["/api/local-models", "read-status"],
|
|
9730
|
+
["/api/maintenance", "trigger-job"],
|
|
9731
|
+
["/api/streams", "read-status"],
|
|
9732
|
+
["/api/sessions", "read-status"],
|
|
9733
|
+
["/api/chat-proxy", "trigger-job"]
|
|
9734
|
+
];
|
|
9735
|
+
function prefixScopeForPath(path24) {
|
|
9736
|
+
if (path24 === "/api/chat") return "trigger-job";
|
|
9737
|
+
for (const [prefix, scope] of PREFIX_SCOPES) {
|
|
9738
|
+
if (path24.startsWith(prefix)) return scope;
|
|
9739
|
+
}
|
|
9740
|
+
return null;
|
|
9741
|
+
}
|
|
9742
|
+
function requiredScopeForRoute(method, path24) {
|
|
9743
|
+
const bridgeScope = requiredBridgeScope(method, path24);
|
|
9744
|
+
if (bridgeScope) return bridgeScope;
|
|
9745
|
+
const exactScope = exactScopeForRoute(method, path24);
|
|
9746
|
+
if (exactScope) return exactScope;
|
|
9747
|
+
return prefixScopeForPath(path24);
|
|
9748
|
+
}
|
|
9691
9749
|
|
|
9692
9750
|
// src/server/http.ts
|
|
9693
9751
|
var RATE_LIMIT = Number(process.env["HARNESS_RATE_LIMIT"]) || 100;
|
|
@@ -10646,79 +10704,102 @@ function buildAttributes(payload, extras = {}) {
|
|
|
10646
10704
|
}
|
|
10647
10705
|
return attrs;
|
|
10648
10706
|
}
|
|
10707
|
+
function extractIds(payload) {
|
|
10708
|
+
const correlationId = typeof payload["correlationId"] === "string" ? payload["correlationId"] : void 0;
|
|
10709
|
+
const taskId = typeof payload["taskId"] === "string" ? payload["taskId"] : void 0;
|
|
10710
|
+
return { correlationId, taskId };
|
|
10711
|
+
}
|
|
10712
|
+
function resolveActiveRun(registry, correlationId, taskId) {
|
|
10713
|
+
return registry.resolve({
|
|
10714
|
+
...correlationId !== void 0 ? { correlationId } : {},
|
|
10715
|
+
...taskId !== void 0 ? { taskId } : {}
|
|
10716
|
+
});
|
|
10717
|
+
}
|
|
10718
|
+
function openMaintenanceSpan(registry, correlationId, taskId) {
|
|
10719
|
+
const traceId = newTraceId();
|
|
10720
|
+
const spanId = newSpanId();
|
|
10721
|
+
const key = correlationId ?? taskId ?? `run_${spanId}`;
|
|
10722
|
+
registry.open(key, { traceId, spanId });
|
|
10723
|
+
return { traceId, spanId };
|
|
10724
|
+
}
|
|
10725
|
+
function closeMaintenanceSpan(registry, topic, correlationId, taskId) {
|
|
10726
|
+
const existing = resolveActiveRun(registry, correlationId, taskId);
|
|
10727
|
+
const traceId = existing?.traceId ?? newTraceId();
|
|
10728
|
+
const spanId = existing?.spanId ?? newSpanId();
|
|
10729
|
+
const statusCode = topic === TOPICS.MAINTENANCE_ERROR ? 2 : 1;
|
|
10730
|
+
const key = correlationId ?? taskId ?? "";
|
|
10731
|
+
if (key) registry.close(key);
|
|
10732
|
+
return { traceId, spanId, statusCode };
|
|
10733
|
+
}
|
|
10734
|
+
function childSpan(registry, correlationId, taskId) {
|
|
10735
|
+
const parent = resolveActiveRun(registry, correlationId, taskId);
|
|
10736
|
+
const plan = {
|
|
10737
|
+
traceId: parent?.traceId ?? newTraceId(),
|
|
10738
|
+
spanId: newSpanId()
|
|
10739
|
+
};
|
|
10740
|
+
if (parent?.spanId !== void 0) plan.parentSpanId = parent.spanId;
|
|
10741
|
+
return plan;
|
|
10742
|
+
}
|
|
10743
|
+
function planSpan(registry, topic, correlationId, taskId) {
|
|
10744
|
+
if (topic === TOPICS.MAINTENANCE_STARTED) {
|
|
10745
|
+
return openMaintenanceSpan(registry, correlationId, taskId);
|
|
10746
|
+
}
|
|
10747
|
+
if (topic === TOPICS.MAINTENANCE_COMPLETED || topic === TOPICS.MAINTENANCE_ERROR) {
|
|
10748
|
+
return closeMaintenanceSpan(registry, topic, correlationId, taskId);
|
|
10749
|
+
}
|
|
10750
|
+
return childSpan(registry, correlationId, taskId);
|
|
10751
|
+
}
|
|
10752
|
+
function buildSpan(topic, plan, payload) {
|
|
10753
|
+
const startNs = nowNs();
|
|
10754
|
+
return {
|
|
10755
|
+
traceId: plan.traceId,
|
|
10756
|
+
spanId: plan.spanId,
|
|
10757
|
+
...plan.parentSpanId !== void 0 ? { parentSpanId: plan.parentSpanId } : {},
|
|
10758
|
+
name: SPAN_NAME[topic],
|
|
10759
|
+
kind: import_core12.SpanKind.INTERNAL,
|
|
10760
|
+
startTimeNs: startNs,
|
|
10761
|
+
endTimeNs: startNs,
|
|
10762
|
+
attributes: buildAttributes(payload, { "harness.topic": topic }),
|
|
10763
|
+
...plan.statusCode !== void 0 ? { statusCode: plan.statusCode } : {}
|
|
10764
|
+
};
|
|
10765
|
+
}
|
|
10766
|
+
function buildGatewayEvent(topic, payload, correlationId) {
|
|
10767
|
+
return {
|
|
10768
|
+
id: newEventId2(),
|
|
10769
|
+
type: TELEMETRY_TYPE[topic],
|
|
10770
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
10771
|
+
data: payload,
|
|
10772
|
+
...correlationId !== void 0 ? { correlationId } : {}
|
|
10773
|
+
};
|
|
10774
|
+
}
|
|
10775
|
+
async function enqueueToMatchingSubs(store, webhookDelivery, event) {
|
|
10776
|
+
const subs = await store.listForEvent(event.type);
|
|
10777
|
+
if (subs.length === 0) return;
|
|
10778
|
+
const filtered = subs.filter((sub) => sub.events.some((p) => eventMatches(p, event.type)));
|
|
10779
|
+
for (const sub of filtered) {
|
|
10780
|
+
webhookDelivery.enqueue(sub, event);
|
|
10781
|
+
}
|
|
10782
|
+
}
|
|
10783
|
+
function makeTelemetryHandler(ctx, topic) {
|
|
10784
|
+
return (data) => {
|
|
10785
|
+
const payload = data ?? {};
|
|
10786
|
+
const { correlationId, taskId } = extractIds(payload);
|
|
10787
|
+
const plan = planSpan(ctx.registry, topic, correlationId, taskId);
|
|
10788
|
+
ctx.exporter.push(buildSpan(topic, plan, payload));
|
|
10789
|
+
void enqueueToMatchingSubs(
|
|
10790
|
+
ctx.store,
|
|
10791
|
+
ctx.webhookDelivery,
|
|
10792
|
+
buildGatewayEvent(topic, payload, correlationId)
|
|
10793
|
+
);
|
|
10794
|
+
};
|
|
10795
|
+
}
|
|
10649
10796
|
function wireTelemetryFanout(params) {
|
|
10650
10797
|
const { bus, exporter, webhookDelivery, store } = params;
|
|
10651
10798
|
const registry = new ActiveRunRegistry();
|
|
10652
10799
|
const handlers = [];
|
|
10653
|
-
const
|
|
10654
|
-
const subs = await store.listForEvent(event.type);
|
|
10655
|
-
if (subs.length === 0) return;
|
|
10656
|
-
const filtered = subs.filter((sub) => sub.events.some((p) => eventMatches(p, event.type)));
|
|
10657
|
-
for (const sub of filtered) {
|
|
10658
|
-
webhookDelivery.enqueue(sub, event);
|
|
10659
|
-
}
|
|
10660
|
-
};
|
|
10661
|
-
const makeHandler = (topic) => (data) => {
|
|
10662
|
-
const payload = data ?? {};
|
|
10663
|
-
const correlationId = typeof payload["correlationId"] === "string" ? payload["correlationId"] : void 0;
|
|
10664
|
-
const taskId = typeof payload["taskId"] === "string" ? payload["taskId"] : void 0;
|
|
10665
|
-
let traceId;
|
|
10666
|
-
let spanId;
|
|
10667
|
-
let parentSpanId;
|
|
10668
|
-
let statusCode;
|
|
10669
|
-
if (topic === TOPICS.MAINTENANCE_STARTED) {
|
|
10670
|
-
traceId = newTraceId();
|
|
10671
|
-
spanId = newSpanId();
|
|
10672
|
-
const key = correlationId ?? taskId ?? `run_${spanId}`;
|
|
10673
|
-
registry.open(key, { traceId, spanId });
|
|
10674
|
-
} else if (topic === TOPICS.MAINTENANCE_COMPLETED || topic === TOPICS.MAINTENANCE_ERROR) {
|
|
10675
|
-
const existing = registry.resolve({
|
|
10676
|
-
...correlationId !== void 0 ? { correlationId } : {},
|
|
10677
|
-
...taskId !== void 0 ? { taskId } : {}
|
|
10678
|
-
});
|
|
10679
|
-
if (existing !== void 0) {
|
|
10680
|
-
traceId = existing.traceId;
|
|
10681
|
-
spanId = existing.spanId;
|
|
10682
|
-
} else {
|
|
10683
|
-
traceId = newTraceId();
|
|
10684
|
-
spanId = newSpanId();
|
|
10685
|
-
}
|
|
10686
|
-
statusCode = topic === TOPICS.MAINTENANCE_ERROR ? 2 : 1;
|
|
10687
|
-
const key = correlationId ?? taskId ?? "";
|
|
10688
|
-
if (key) registry.close(key);
|
|
10689
|
-
} else {
|
|
10690
|
-
const parent = registry.resolve({
|
|
10691
|
-
...correlationId !== void 0 ? { correlationId } : {},
|
|
10692
|
-
...taskId !== void 0 ? { taskId } : {}
|
|
10693
|
-
});
|
|
10694
|
-
traceId = parent?.traceId ?? newTraceId();
|
|
10695
|
-
spanId = newSpanId();
|
|
10696
|
-
parentSpanId = parent?.spanId;
|
|
10697
|
-
}
|
|
10698
|
-
const startNs = nowNs();
|
|
10699
|
-
const span = {
|
|
10700
|
-
traceId,
|
|
10701
|
-
spanId,
|
|
10702
|
-
...parentSpanId !== void 0 ? { parentSpanId } : {},
|
|
10703
|
-
name: SPAN_NAME[topic],
|
|
10704
|
-
kind: import_core12.SpanKind.INTERNAL,
|
|
10705
|
-
startTimeNs: startNs,
|
|
10706
|
-
endTimeNs: startNs,
|
|
10707
|
-
attributes: buildAttributes(payload, { "harness.topic": topic }),
|
|
10708
|
-
...statusCode !== void 0 ? { statusCode } : {}
|
|
10709
|
-
};
|
|
10710
|
-
exporter.push(span);
|
|
10711
|
-
const gatewayEvent = {
|
|
10712
|
-
id: newEventId2(),
|
|
10713
|
-
type: TELEMETRY_TYPE[topic],
|
|
10714
|
-
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
10715
|
-
data: payload,
|
|
10716
|
-
...correlationId !== void 0 ? { correlationId } : {}
|
|
10717
|
-
};
|
|
10718
|
-
void enqueueToMatchingSubs(gatewayEvent);
|
|
10719
|
-
};
|
|
10800
|
+
const ctx = { registry, exporter, webhookDelivery, store };
|
|
10720
10801
|
for (const topic of Object.values(TOPICS)) {
|
|
10721
|
-
const fn =
|
|
10802
|
+
const fn = makeTelemetryHandler(ctx, topic);
|
|
10722
10803
|
bus.on(topic, fn);
|
|
10723
10804
|
handlers.push({ topic, fn });
|
|
10724
10805
|
}
|
|
@@ -12413,39 +12494,50 @@ function classifyCheckExecutionFailure(output) {
|
|
|
12413
12494
|
function parseStatusLine(output) {
|
|
12414
12495
|
const lines = output.split("\n").map((l) => l.trim()).filter(Boolean);
|
|
12415
12496
|
for (let i = lines.length - 1; i >= 0; i--) {
|
|
12416
|
-
const
|
|
12417
|
-
if (
|
|
12418
|
-
try {
|
|
12419
|
-
const obj = JSON.parse(line);
|
|
12420
|
-
const s = obj.status;
|
|
12421
|
-
if (s === "success" || s === "skipped" || s === "failure" || s === "no-issues") {
|
|
12422
|
-
const parsed = { status: s, rawStatus: s };
|
|
12423
|
-
if (typeof obj.candidatesFound === "number") {
|
|
12424
|
-
parsed.candidatesFound = obj.candidatesFound;
|
|
12425
|
-
}
|
|
12426
|
-
if (typeof obj.error === "string") {
|
|
12427
|
-
parsed.error = obj.error;
|
|
12428
|
-
}
|
|
12429
|
-
if (typeof obj.reason === "string") {
|
|
12430
|
-
parsed.reason = obj.reason;
|
|
12431
|
-
}
|
|
12432
|
-
if (typeof obj.detail === "string" && !parsed.error) {
|
|
12433
|
-
parsed.error = `${parsed.reason ?? "skipped"}: ${obj.detail}`;
|
|
12434
|
-
}
|
|
12435
|
-
return parsed;
|
|
12436
|
-
}
|
|
12437
|
-
if (s === "updated" || s === "no-op") {
|
|
12438
|
-
return { status: "success", rawStatus: s };
|
|
12439
|
-
}
|
|
12440
|
-
if (s === "error") {
|
|
12441
|
-
const message = typeof obj.message === "string" ? obj.message : "unknown error";
|
|
12442
|
-
return { status: "failure", error: message, rawStatus: "error" };
|
|
12443
|
-
}
|
|
12444
|
-
} catch {
|
|
12445
|
-
}
|
|
12497
|
+
const parsed = parseStatusObjectLine(lines[i]);
|
|
12498
|
+
if (parsed) return parsed;
|
|
12446
12499
|
}
|
|
12447
12500
|
return null;
|
|
12448
12501
|
}
|
|
12502
|
+
function parseStatusObjectLine(line) {
|
|
12503
|
+
if (!line || !line.startsWith("{") || !line.endsWith("}")) return null;
|
|
12504
|
+
let obj;
|
|
12505
|
+
try {
|
|
12506
|
+
obj = JSON.parse(line);
|
|
12507
|
+
} catch {
|
|
12508
|
+
return null;
|
|
12509
|
+
}
|
|
12510
|
+
return classifyStatusObject(obj);
|
|
12511
|
+
}
|
|
12512
|
+
var PHASE_STATUSES = /* @__PURE__ */ new Set(["success", "skipped", "failure", "no-issues"]);
|
|
12513
|
+
var SYNC_SUCCESS_STATUSES = /* @__PURE__ */ new Set(["updated", "no-op"]);
|
|
12514
|
+
function classifyStatusObject(obj) {
|
|
12515
|
+
const s = obj.status;
|
|
12516
|
+
if (typeof s !== "string") return null;
|
|
12517
|
+
if (PHASE_STATUSES.has(s)) return buildPhaseStatus(s, obj);
|
|
12518
|
+
if (SYNC_SUCCESS_STATUSES.has(s)) return { status: "success", rawStatus: s };
|
|
12519
|
+
if (s === "error") {
|
|
12520
|
+
const message = typeof obj.message === "string" ? obj.message : "unknown error";
|
|
12521
|
+
return { status: "failure", error: message, rawStatus: "error" };
|
|
12522
|
+
}
|
|
12523
|
+
return null;
|
|
12524
|
+
}
|
|
12525
|
+
function buildPhaseStatus(status, obj) {
|
|
12526
|
+
const parsed = { status, rawStatus: status };
|
|
12527
|
+
if (typeof obj.candidatesFound === "number") {
|
|
12528
|
+
parsed.candidatesFound = obj.candidatesFound;
|
|
12529
|
+
}
|
|
12530
|
+
if (typeof obj.error === "string") {
|
|
12531
|
+
parsed.error = obj.error;
|
|
12532
|
+
}
|
|
12533
|
+
if (typeof obj.reason === "string") {
|
|
12534
|
+
parsed.reason = obj.reason;
|
|
12535
|
+
}
|
|
12536
|
+
if (typeof obj.detail === "string" && !parsed.error) {
|
|
12537
|
+
parsed.error = `${parsed.reason ?? "skipped"}: ${obj.detail}`;
|
|
12538
|
+
}
|
|
12539
|
+
return parsed;
|
|
12540
|
+
}
|
|
12449
12541
|
|
|
12450
12542
|
// src/maintenance/check-script-runner.ts
|
|
12451
12543
|
var import_node_child_process11 = require("child_process");
|
|
@@ -13340,22 +13432,7 @@ var Orchestrator = class extends import_node_events.EventEmitter {
|
|
|
13340
13432
|
});
|
|
13341
13433
|
webhookDelivery.start();
|
|
13342
13434
|
this.setupNotifications(config.notifications);
|
|
13343
|
-
|
|
13344
|
-
if (otlpCfg) {
|
|
13345
|
-
this.otlpExporter = new import_core17.OTLPExporter({
|
|
13346
|
-
endpoint: otlpCfg.endpoint,
|
|
13347
|
-
...otlpCfg.enabled !== void 0 ? { enabled: otlpCfg.enabled } : {},
|
|
13348
|
-
...otlpCfg.headers !== void 0 ? { headers: otlpCfg.headers } : {},
|
|
13349
|
-
...otlpCfg.flushIntervalMs !== void 0 ? { flushIntervalMs: otlpCfg.flushIntervalMs } : {},
|
|
13350
|
-
...otlpCfg.batchSize !== void 0 ? { batchSize: otlpCfg.batchSize } : {}
|
|
13351
|
-
});
|
|
13352
|
-
this.telemetryFanoutOff = wireTelemetryFanout({
|
|
13353
|
-
bus: this,
|
|
13354
|
-
exporter: this.otlpExporter,
|
|
13355
|
-
webhookDelivery,
|
|
13356
|
-
store: webhookStore
|
|
13357
|
-
});
|
|
13358
|
-
}
|
|
13435
|
+
this.setupTelemetryExport(config, webhookStore, webhookDelivery);
|
|
13359
13436
|
this.server = new OrchestratorServer(this, config.server.port, {
|
|
13360
13437
|
interactionQueue: this.interactionQueue,
|
|
13361
13438
|
webhooks: {
|
|
@@ -13378,24 +13455,8 @@ var Orchestrator = class extends import_node_events.EventEmitter {
|
|
|
13378
13455
|
analysisArchive: this.analysisArchive,
|
|
13379
13456
|
roadmapPath: config.tracker.filePath ?? null,
|
|
13380
13457
|
dispatchAdHoc: this.dispatchAdHoc.bind(this),
|
|
13381
|
-
getLocalModelStatus: () =>
|
|
13382
|
-
|
|
13383
|
-
return first.done ? null : first.value.getStatus();
|
|
13384
|
-
},
|
|
13385
|
-
getLocalModelStatuses: () => {
|
|
13386
|
-
const backends = this.config.agent.backends ?? {};
|
|
13387
|
-
const out = [];
|
|
13388
|
-
for (const [name, resolver] of this.localResolvers) {
|
|
13389
|
-
const def = backends[name];
|
|
13390
|
-
if (!def || def.type !== "local" && def.type !== "pi") continue;
|
|
13391
|
-
out.push({
|
|
13392
|
-
...resolver.getStatus(),
|
|
13393
|
-
backendName: name,
|
|
13394
|
-
endpoint: def.endpoint
|
|
13395
|
-
});
|
|
13396
|
-
}
|
|
13397
|
-
return out;
|
|
13398
|
-
}
|
|
13458
|
+
getLocalModelStatus: () => this.getFirstLocalModelStatus(),
|
|
13459
|
+
getLocalModelStatuses: () => this.buildLocalModelStatuses()
|
|
13399
13460
|
});
|
|
13400
13461
|
this.server.setRecorder(this.recorder);
|
|
13401
13462
|
this.interactionQueue.onPush((interaction) => {
|
|
@@ -13403,6 +13464,56 @@ var Orchestrator = class extends import_node_events.EventEmitter {
|
|
|
13403
13464
|
});
|
|
13404
13465
|
}
|
|
13405
13466
|
}
|
|
13467
|
+
/**
|
|
13468
|
+
* Phase 5: construct the OTLP/HTTP trace exporter and wire telemetry fanout.
|
|
13469
|
+
* Only fires when the operator configures `telemetry.export.otlp` in
|
|
13470
|
+
* harness.config.json. Extracted from the server-init block in the
|
|
13471
|
+
* constructor to keep that block's cyclomatic complexity under threshold.
|
|
13472
|
+
*/
|
|
13473
|
+
setupTelemetryExport(config, webhookStore, webhookDelivery) {
|
|
13474
|
+
const otlpCfg = config.telemetry?.export?.otlp;
|
|
13475
|
+
if (!otlpCfg) return;
|
|
13476
|
+
this.otlpExporter = new import_core17.OTLPExporter({
|
|
13477
|
+
endpoint: otlpCfg.endpoint,
|
|
13478
|
+
...otlpCfg.enabled !== void 0 ? { enabled: otlpCfg.enabled } : {},
|
|
13479
|
+
...otlpCfg.headers !== void 0 ? { headers: otlpCfg.headers } : {},
|
|
13480
|
+
...otlpCfg.flushIntervalMs !== void 0 ? { flushIntervalMs: otlpCfg.flushIntervalMs } : {},
|
|
13481
|
+
...otlpCfg.batchSize !== void 0 ? { batchSize: otlpCfg.batchSize } : {}
|
|
13482
|
+
});
|
|
13483
|
+
this.telemetryFanoutOff = wireTelemetryFanout({
|
|
13484
|
+
bus: this,
|
|
13485
|
+
exporter: this.otlpExporter,
|
|
13486
|
+
webhookDelivery,
|
|
13487
|
+
store: webhookStore
|
|
13488
|
+
});
|
|
13489
|
+
}
|
|
13490
|
+
/**
|
|
13491
|
+
* Deprecated alias for /api/v1/local-model/status (Spec 1 endpoint retained
|
|
13492
|
+
* as a compat shim per spec line 35; superseded by getLocalModelStatuses for
|
|
13493
|
+
* the multi-local UI). Returns the first-registered resolver's status.
|
|
13494
|
+
*/
|
|
13495
|
+
getFirstLocalModelStatus() {
|
|
13496
|
+
const first = this.localResolvers.values().next();
|
|
13497
|
+
return first.done ? null : first.value.getStatus();
|
|
13498
|
+
}
|
|
13499
|
+
/**
|
|
13500
|
+
* SC38: build NamedLocalModelStatus[] from each registered resolver, tagged
|
|
13501
|
+
* with its backendName + endpoint from the config.
|
|
13502
|
+
*/
|
|
13503
|
+
buildLocalModelStatuses() {
|
|
13504
|
+
const backends = this.config.agent.backends ?? {};
|
|
13505
|
+
const out = [];
|
|
13506
|
+
for (const [name, resolver] of this.localResolvers) {
|
|
13507
|
+
const def = backends[name];
|
|
13508
|
+
if (!def || def.type !== "local" && def.type !== "pi") continue;
|
|
13509
|
+
out.push({
|
|
13510
|
+
...resolver.getStatus(),
|
|
13511
|
+
backendName: name,
|
|
13512
|
+
endpoint: def.endpoint
|
|
13513
|
+
});
|
|
13514
|
+
}
|
|
13515
|
+
return out;
|
|
13516
|
+
}
|
|
13406
13517
|
createTracker() {
|
|
13407
13518
|
if (this.config.tracker.kind === "github-issues") {
|
|
13408
13519
|
const trackerCfg = {
|