@qloo/qloo-harness 0.1.18
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 +21 -0
- package/README.md +394 -0
- package/THIRD_PARTY_NOTICES.md +40 -0
- package/dist/app.js +48 -0
- package/dist/bin.js +14 -0
- package/dist/build.js +117 -0
- package/dist/cli.js +95 -0
- package/dist/doctor.js +1019 -0
- package/dist/exec.js +782 -0
- package/dist/guided-journey.js +223 -0
- package/dist/index.d.ts +1321 -0
- package/dist/index.js +156 -0
- package/dist/integration-plan.js +1097 -0
- package/dist/mcp.js +909 -0
- package/dist/observability.js +115 -0
- package/dist/paths.js +77 -0
- package/dist/plan.js +163 -0
- package/dist/profiles.js +63 -0
- package/dist/project-context.js +364 -0
- package/dist/qloo-presentation.js +358 -0
- package/dist/qloo-tools.js +2195 -0
- package/dist/resolution-provider.js +1380 -0
- package/dist/router.js +6061 -0
- package/dist/runtime/explore-policy.js +666 -0
- package/dist/runtime/pi-adapter.js +259 -0
- package/dist/runtime/pi-command-policy.js +74 -0
- package/dist/runtime/qloo-header.js +173 -0
- package/dist/runtime/resources.js +37 -0
- package/dist/setup.js +1138 -0
- package/dist/update-manager.js +906 -0
- package/dist/workflow-executor.js +976 -0
- package/package.json +72 -0
- package/resources/BUILD.md +24 -0
- package/resources/EXPLORE.md +34 -0
- package/resources/INTEGRATE.md +28 -0
- package/resources/PLAN.md +23 -0
- package/resources/SYSTEM.md +49 -0
package/dist/exec.js
ADDED
|
@@ -0,0 +1,782 @@
|
|
|
1
|
+
import { createRequire as __qlooCreateRequire } from "node:module";
|
|
2
|
+
const require = __qlooCreateRequire(import.meta.url);
|
|
3
|
+
|
|
4
|
+
// apps/qloo-harness/dist/exec.js
|
|
5
|
+
import { randomUUID } from "node:crypto";
|
|
6
|
+
import { readFile, stat } from "node:fs/promises";
|
|
7
|
+
|
|
8
|
+
// packages/qloo-tool-schema/dist/upstream.lock.json
|
|
9
|
+
var upstream_lock_default = {
|
|
10
|
+
lockVersion: 1,
|
|
11
|
+
source: {
|
|
12
|
+
documentationUrl: "https://docs.qloo.com",
|
|
13
|
+
registryUrl: "https://dash.readme.com/api/v1/api-registry/4svkj1svmrti5tfx",
|
|
14
|
+
registryId: "4svkj1svmrti5tfx",
|
|
15
|
+
retrievedAt: "2026-08-20",
|
|
16
|
+
apiTitle: "Qloo API",
|
|
17
|
+
apiVersion: "2.0",
|
|
18
|
+
openapiVersion: "3.1.0",
|
|
19
|
+
sha256: "da8d4b830f2a646815d25afcaa653f81f1ad939707e2e2560d2d0b9f103076f1"
|
|
20
|
+
},
|
|
21
|
+
expectations: {
|
|
22
|
+
productionServer: "https://api.qloo.com",
|
|
23
|
+
authentication: {
|
|
24
|
+
scheme: "ApiKey",
|
|
25
|
+
type: "apiKey",
|
|
26
|
+
location: "header",
|
|
27
|
+
header: "X-Api-Key"
|
|
28
|
+
},
|
|
29
|
+
getPaths: [
|
|
30
|
+
"/",
|
|
31
|
+
"/entities",
|
|
32
|
+
"/search",
|
|
33
|
+
"/v2/analysis/compare",
|
|
34
|
+
"/v2/audiences",
|
|
35
|
+
"/v2/audiences/types",
|
|
36
|
+
"/v2/insights",
|
|
37
|
+
"/v2/tags",
|
|
38
|
+
"/v2/tags/types",
|
|
39
|
+
"/v2/trending"
|
|
40
|
+
]
|
|
41
|
+
},
|
|
42
|
+
documentedExceptions: [
|
|
43
|
+
{
|
|
44
|
+
value: "urn:demographics",
|
|
45
|
+
scope: "/v2/insights filter.type",
|
|
46
|
+
status: "documented-but-not-enumerated",
|
|
47
|
+
documentationUrl: "https://docs.qloo.com/reference/demographics-use-case",
|
|
48
|
+
treatment: "Preserve as an explicit documentation exception; do not synthesize an endpoint payload schema from it."
|
|
49
|
+
},
|
|
50
|
+
{
|
|
51
|
+
value: "urn:tag",
|
|
52
|
+
scope: "/v2/insights filter.type",
|
|
53
|
+
status: "documented-but-not-enumerated",
|
|
54
|
+
documentationUrl: "https://docs.qloo.com/reference/taste-analysis",
|
|
55
|
+
treatment: "The pinned specification mentions this value in diversify.by documentation but omits it from the filter.type enum; preserve the discrepancy for review."
|
|
56
|
+
}
|
|
57
|
+
]
|
|
58
|
+
};
|
|
59
|
+
|
|
60
|
+
// packages/qloo-tool-schema/dist/src/catalog.js
|
|
61
|
+
var QLOO_TOOL_CATALOG_VERSION = "1.0.0";
|
|
62
|
+
var QLOO_TOOL_RESULT_SCHEMA_VERSION = "1.0-preview.1";
|
|
63
|
+
var QLOO_WORKFLOW_OPERATION_IDS = [
|
|
64
|
+
"recommend",
|
|
65
|
+
"rank",
|
|
66
|
+
"describe",
|
|
67
|
+
"where_popular",
|
|
68
|
+
"compare_audiences",
|
|
69
|
+
"entity_tags",
|
|
70
|
+
"audience_demographics",
|
|
71
|
+
"trends",
|
|
72
|
+
"find_tags"
|
|
73
|
+
];
|
|
74
|
+
var QLOO_CAPABILITIES_TOOL_NAME = "qloo_capabilities";
|
|
75
|
+
var QLOO_INSIGHTS_ENTITY_TYPES = [
|
|
76
|
+
{ id: "artist", urn: "urn:entity:artist" },
|
|
77
|
+
{ id: "book", urn: "urn:entity:book" },
|
|
78
|
+
{ id: "brand", urn: "urn:entity:brand" },
|
|
79
|
+
{ id: "movie", urn: "urn:entity:movie" },
|
|
80
|
+
{ id: "person", urn: "urn:entity:person" },
|
|
81
|
+
{ id: "place", urn: "urn:entity:place" },
|
|
82
|
+
{ id: "podcast", urn: "urn:entity:podcast" },
|
|
83
|
+
{ id: "tv_show", urn: "urn:entity:tv_show" },
|
|
84
|
+
{ id: "videogame", urn: "urn:entity:videogame" }
|
|
85
|
+
];
|
|
86
|
+
var QLOO_SEARCH_ENTITY_TYPES = [
|
|
87
|
+
...QLOO_INSIGHTS_ENTITY_TYPES,
|
|
88
|
+
{ id: "locality", urn: "urn:entity:locality" },
|
|
89
|
+
{ id: "actor", urn: "urn:entity:actor" },
|
|
90
|
+
{ id: "album", urn: "urn:entity:album" },
|
|
91
|
+
{ id: "author", urn: "urn:entity:author" },
|
|
92
|
+
{ id: "director", urn: "urn:entity:director" }
|
|
93
|
+
];
|
|
94
|
+
function buildEntityTypeAliases(entityTypes) {
|
|
95
|
+
const aliases = /* @__PURE__ */ new Map();
|
|
96
|
+
const urnById = new Map(entityTypes.map(({ id, urn }) => [id, urn]));
|
|
97
|
+
for (const entityType of entityTypes) {
|
|
98
|
+
aliases.set(entityType.id, entityType.urn);
|
|
99
|
+
aliases.set(entityType.urn, entityType.urn);
|
|
100
|
+
aliases.set(`${entityType.id}s`, entityType.urn);
|
|
101
|
+
}
|
|
102
|
+
const addAlias = (alias, id) => {
|
|
103
|
+
const urn = urnById.get(id);
|
|
104
|
+
if (urn)
|
|
105
|
+
aliases.set(alias, urn);
|
|
106
|
+
};
|
|
107
|
+
addAlias("people", "person");
|
|
108
|
+
addAlias("localities", "locality");
|
|
109
|
+
addAlias("tv", "tv_show");
|
|
110
|
+
addAlias("show", "tv_show");
|
|
111
|
+
addAlias("shows", "tv_show");
|
|
112
|
+
addAlias("tv show", "tv_show");
|
|
113
|
+
addAlias("tv shows", "tv_show");
|
|
114
|
+
addAlias("game", "videogame");
|
|
115
|
+
addAlias("games", "videogame");
|
|
116
|
+
addAlias("video game", "videogame");
|
|
117
|
+
addAlias("video games", "videogame");
|
|
118
|
+
return aliases;
|
|
119
|
+
}
|
|
120
|
+
var insightsEntityTypeByAlias = buildEntityTypeAliases(QLOO_INSIGHTS_ENTITY_TYPES);
|
|
121
|
+
var searchEntityTypeByAlias = buildEntityTypeAliases(QLOO_SEARCH_ENTITY_TYPES);
|
|
122
|
+
var operationSummaries = {
|
|
123
|
+
recommend: "Find entities for a taste profile, audience, or collection of signals.",
|
|
124
|
+
rank: "Rank a supplied set of options for signals, location, or audience.",
|
|
125
|
+
describe: "Resolve and describe one Qloo entity.",
|
|
126
|
+
where_popular: "Find geographic areas where an entity has comparatively strong affinity.",
|
|
127
|
+
compare_audiences: "Contrast two signal groups and identify shared or differentiating affinities.",
|
|
128
|
+
entity_tags: "Retrieve concepts and tags that characterize entities.",
|
|
129
|
+
audience_demographics: "Retrieve aggregate audience segments associated with an entity.",
|
|
130
|
+
trends: "Retrieve interest or trend information over a time range.",
|
|
131
|
+
find_tags: "Search the Qloo tag ontology by natural-language concept."
|
|
132
|
+
};
|
|
133
|
+
var QLOO_WORKFLOW_OPERATIONS = Object.freeze(QLOO_WORKFLOW_OPERATION_IDS.map((id) => Object.freeze({
|
|
134
|
+
id,
|
|
135
|
+
toolName: `qloo_${id}`,
|
|
136
|
+
summary: operationSummaries[id],
|
|
137
|
+
since: QLOO_TOOL_CATALOG_VERSION
|
|
138
|
+
})));
|
|
139
|
+
var upstreamLock = upstream_lock_default;
|
|
140
|
+
var upstreamGetPaths = Object.freeze([
|
|
141
|
+
...upstreamLock.expectations.getPaths
|
|
142
|
+
]);
|
|
143
|
+
var documentedExceptions = Object.freeze(upstreamLock.documentedExceptions.map((exception) => Object.freeze({ ...exception })));
|
|
144
|
+
var QLOO_CAPABILITIES_TOOL = Object.freeze({
|
|
145
|
+
name: QLOO_CAPABILITIES_TOOL_NAME,
|
|
146
|
+
description: "Return the versioned Qloo workflow catalog and pinned upstream API contract metadata without making a network request.",
|
|
147
|
+
inputSchema: Object.freeze({
|
|
148
|
+
$schema: "https://json-schema.org/draft/2020-12/schema",
|
|
149
|
+
type: "object",
|
|
150
|
+
additionalProperties: false,
|
|
151
|
+
properties: Object.freeze({
|
|
152
|
+
operation_ids: Object.freeze({
|
|
153
|
+
type: "array",
|
|
154
|
+
description: "Optional canonical operation IDs to include. Omit for the complete catalog.",
|
|
155
|
+
uniqueItems: true,
|
|
156
|
+
items: Object.freeze({
|
|
157
|
+
type: "string",
|
|
158
|
+
enum: QLOO_WORKFLOW_OPERATION_IDS
|
|
159
|
+
})
|
|
160
|
+
})
|
|
161
|
+
})
|
|
162
|
+
})
|
|
163
|
+
});
|
|
164
|
+
var operationIds = new Set(QLOO_WORKFLOW_OPERATION_IDS);
|
|
165
|
+
function isQlooWorkflowOperationId(value) {
|
|
166
|
+
return operationIds.has(value);
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
// packages/qloo-tool-schema/dist/src/workflow-contracts.js
|
|
170
|
+
import { Type } from "typebox";
|
|
171
|
+
import { Value } from "typebox/value";
|
|
172
|
+
var QLOO_TOOL_DEFAULT_LIMIT = 10;
|
|
173
|
+
var QLOO_TOOL_MAX_RESULTS = 20;
|
|
174
|
+
var QLOO_TAG_BOOLEAN_OPERATORS = ["union", "intersection"];
|
|
175
|
+
var insightsEntityTypeIds = QLOO_INSIGHTS_ENTITY_TYPES.map(({ id }) => id);
|
|
176
|
+
var searchEntityTypeIds = QLOO_SEARCH_ENTITY_TYPES.map(({ id }) => id);
|
|
177
|
+
var QLOO_INSIGHTS_ENTITY_TYPE_SCHEMA = Type.String({
|
|
178
|
+
enum: insightsEntityTypeIds,
|
|
179
|
+
description: `Qloo Insights entity type: ${insightsEntityTypeIds.join(", ")}.`
|
|
180
|
+
});
|
|
181
|
+
var QLOO_SEARCH_ENTITY_TYPE_SCHEMA = Type.String({
|
|
182
|
+
enum: searchEntityTypeIds,
|
|
183
|
+
description: `Qloo search entity type: ${searchEntityTypeIds.join(", ")}.`
|
|
184
|
+
});
|
|
185
|
+
var QLOO_TAG_BOOLEAN_OPERATOR_SCHEMA = Type.String({
|
|
186
|
+
enum: QLOO_TAG_BOOLEAN_OPERATORS,
|
|
187
|
+
description: "Combine tag values with union (any) or intersection (all)."
|
|
188
|
+
});
|
|
189
|
+
var QLOO_LIMIT_SCHEMA = Type.Optional(Type.Integer({
|
|
190
|
+
minimum: 1,
|
|
191
|
+
maximum: QLOO_TOOL_MAX_RESULTS,
|
|
192
|
+
default: QLOO_TOOL_DEFAULT_LIMIT,
|
|
193
|
+
description: `Maximum results to return (1-${QLOO_TOOL_MAX_RESULTS}).`
|
|
194
|
+
}));
|
|
195
|
+
var QLOO_ENTITY_INPUTS_SCHEMA = Type.Array(Type.String({ minLength: 1 }), {
|
|
196
|
+
minItems: 1,
|
|
197
|
+
maxItems: 10,
|
|
198
|
+
description: "Entity names or Qloo entity UUIDs. Keep one combined taste profile in one call."
|
|
199
|
+
});
|
|
200
|
+
var QLOO_TAG_INPUTS_SCHEMA = Type.Array(Type.String({ minLength: 1 }), {
|
|
201
|
+
minItems: 1,
|
|
202
|
+
maxItems: 10,
|
|
203
|
+
description: "Natural-language tag concepts or stable Qloo tag URNs."
|
|
204
|
+
});
|
|
205
|
+
var QLOO_DEMOGRAPHIC_SCHEMA = Type.String({
|
|
206
|
+
minLength: 1,
|
|
207
|
+
description: "Audience demographic in natural language, such as 'young women', 'men 25-34', or 'millennials'. It is converted to documented Qloo demographic signals.",
|
|
208
|
+
examples: ["young women", "men 25-34", "millennials"]
|
|
209
|
+
});
|
|
210
|
+
var QLOO_REQUEST_PREVIEW_SCHEMA = Type.Object({
|
|
211
|
+
method: Type.Literal("GET"),
|
|
212
|
+
path: Type.String({ minLength: 1 }),
|
|
213
|
+
query: Type.Unknown()
|
|
214
|
+
}, { additionalProperties: false });
|
|
215
|
+
var QLOO_RESULT_ENVELOPE_SCHEMA = Type.Object({
|
|
216
|
+
schema_version: Type.Literal(QLOO_TOOL_RESULT_SCHEMA_VERSION),
|
|
217
|
+
operation: Type.String(),
|
|
218
|
+
status: Type.Union([
|
|
219
|
+
Type.Literal("ok"),
|
|
220
|
+
Type.Literal("empty"),
|
|
221
|
+
Type.Literal("needs_input"),
|
|
222
|
+
Type.Literal("partial"),
|
|
223
|
+
Type.Literal("degraded"),
|
|
224
|
+
Type.Literal("error")
|
|
225
|
+
]),
|
|
226
|
+
summary: Type.Optional(Type.String()),
|
|
227
|
+
interpretation: Type.Optional(Type.Unknown()),
|
|
228
|
+
results: Type.Optional(Type.Unknown()),
|
|
229
|
+
result_count: Type.Optional(Type.Integer({ minimum: 0 })),
|
|
230
|
+
warnings: Type.Optional(Type.Array(Type.String())),
|
|
231
|
+
query_intent: Type.Optional(Type.Unknown()),
|
|
232
|
+
error: Type.Optional(Type.Object({
|
|
233
|
+
code: Type.String(),
|
|
234
|
+
layer: Type.String(),
|
|
235
|
+
retryable: Type.Boolean(),
|
|
236
|
+
recovery: Type.String()
|
|
237
|
+
}, { additionalProperties: false })),
|
|
238
|
+
execution: Type.Object({
|
|
239
|
+
transport: Type.String({ enum: ["direct", "cli", "mcp"] }),
|
|
240
|
+
transport_name: Type.String(),
|
|
241
|
+
correlation_id: Type.String(),
|
|
242
|
+
duration_ms: Type.Number({ minimum: 0 })
|
|
243
|
+
}, { additionalProperties: false }),
|
|
244
|
+
provenance: Type.Optional(Type.Object({
|
|
245
|
+
source: Type.String(),
|
|
246
|
+
endpoint: Type.String(),
|
|
247
|
+
documentation: Type.String(),
|
|
248
|
+
transport: Type.Optional(Type.String()),
|
|
249
|
+
requests: Type.Optional(Type.Array(QLOO_REQUEST_PREVIEW_SCHEMA, {
|
|
250
|
+
maxItems: 20
|
|
251
|
+
}))
|
|
252
|
+
}, { additionalProperties: false }))
|
|
253
|
+
}, { additionalProperties: true });
|
|
254
|
+
var QLOO_WORKFLOW_INPUT_SCHEMAS = {
|
|
255
|
+
recommend: Type.Object({
|
|
256
|
+
target_type: QLOO_INSIGHTS_ENTITY_TYPE_SCHEMA,
|
|
257
|
+
signals: Type.Optional(QLOO_ENTITY_INPUTS_SCHEMA),
|
|
258
|
+
signal_tags: Type.Optional(QLOO_TAG_INPUTS_SCHEMA),
|
|
259
|
+
signal_tags_operator: Type.Optional(QLOO_TAG_BOOLEAN_OPERATOR_SCHEMA),
|
|
260
|
+
signal_location: Type.Optional(Type.String({ minLength: 1 })),
|
|
261
|
+
demographic: Type.Optional(QLOO_DEMOGRAPHIC_SCHEMA),
|
|
262
|
+
filter_location: Type.Optional(Type.String({ minLength: 1 })),
|
|
263
|
+
include_tags: Type.Optional(QLOO_TAG_INPUTS_SCHEMA),
|
|
264
|
+
include_tags_operator: Type.Optional(QLOO_TAG_BOOLEAN_OPERATOR_SCHEMA),
|
|
265
|
+
exclude_tags: Type.Optional(QLOO_TAG_INPUTS_SCHEMA),
|
|
266
|
+
exclude_tags_operator: Type.Optional(QLOO_TAG_BOOLEAN_OPERATOR_SCHEMA),
|
|
267
|
+
explain: Type.Optional(Type.Boolean({ default: true })),
|
|
268
|
+
limit: QLOO_LIMIT_SCHEMA
|
|
269
|
+
}, { additionalProperties: false }),
|
|
270
|
+
rank: Type.Object({
|
|
271
|
+
options: QLOO_ENTITY_INPUTS_SCHEMA,
|
|
272
|
+
option_type: QLOO_INSIGHTS_ENTITY_TYPE_SCHEMA,
|
|
273
|
+
signals: Type.Optional(QLOO_ENTITY_INPUTS_SCHEMA),
|
|
274
|
+
signal_location: Type.Optional(Type.String({ minLength: 1 })),
|
|
275
|
+
demographic: Type.Optional(QLOO_DEMOGRAPHIC_SCHEMA),
|
|
276
|
+
include_tags: Type.Optional(QLOO_TAG_INPUTS_SCHEMA),
|
|
277
|
+
exclude_tags: Type.Optional(QLOO_TAG_INPUTS_SCHEMA)
|
|
278
|
+
}, { additionalProperties: false }),
|
|
279
|
+
describe: Type.Object({
|
|
280
|
+
entity: Type.String({ minLength: 1 }),
|
|
281
|
+
type: Type.Optional(QLOO_SEARCH_ENTITY_TYPE_SCHEMA)
|
|
282
|
+
}, { additionalProperties: false }),
|
|
283
|
+
where_popular: Type.Object({
|
|
284
|
+
entity: Type.String({ minLength: 1 }),
|
|
285
|
+
entity_type: Type.Optional(QLOO_SEARCH_ENTITY_TYPE_SCHEMA),
|
|
286
|
+
within: Type.String({
|
|
287
|
+
minLength: 1,
|
|
288
|
+
description: "Required geographic area that constrains the heatmap."
|
|
289
|
+
}),
|
|
290
|
+
limit: QLOO_LIMIT_SCHEMA
|
|
291
|
+
}, { additionalProperties: false }),
|
|
292
|
+
compare_audiences: Type.Object({
|
|
293
|
+
group_a: QLOO_ENTITY_INPUTS_SCHEMA,
|
|
294
|
+
group_b: QLOO_ENTITY_INPUTS_SCHEMA,
|
|
295
|
+
target_type: Type.Optional(QLOO_INSIGHTS_ENTITY_TYPE_SCHEMA),
|
|
296
|
+
limit: QLOO_LIMIT_SCHEMA
|
|
297
|
+
}, { additionalProperties: false }),
|
|
298
|
+
entity_tags: Type.Object({
|
|
299
|
+
entities: QLOO_ENTITY_INPUTS_SCHEMA,
|
|
300
|
+
entity_type: Type.Optional(QLOO_SEARCH_ENTITY_TYPE_SCHEMA),
|
|
301
|
+
limit: QLOO_LIMIT_SCHEMA
|
|
302
|
+
}, { additionalProperties: false }),
|
|
303
|
+
audience_demographics: Type.Object({
|
|
304
|
+
entity: Type.String({ minLength: 1 }),
|
|
305
|
+
entity_type: Type.Optional(QLOO_SEARCH_ENTITY_TYPE_SCHEMA),
|
|
306
|
+
limit: QLOO_LIMIT_SCHEMA
|
|
307
|
+
}, { additionalProperties: false }),
|
|
308
|
+
trends: Type.Object({
|
|
309
|
+
entities: Type.Array(Type.String({ minLength: 1 }), { minItems: 1, maxItems: 5 }),
|
|
310
|
+
entity_type: QLOO_INSIGHTS_ENTITY_TYPE_SCHEMA,
|
|
311
|
+
start_date: Type.String({
|
|
312
|
+
pattern: "^\\d{4}-\\d{2}-\\d{2}$",
|
|
313
|
+
description: "Inclusive start date in YYYY-MM-DD format."
|
|
314
|
+
}),
|
|
315
|
+
end_date: Type.String({
|
|
316
|
+
pattern: "^\\d{4}-\\d{2}-\\d{2}$",
|
|
317
|
+
description: "Inclusive end date in YYYY-MM-DD format."
|
|
318
|
+
}),
|
|
319
|
+
limit: QLOO_LIMIT_SCHEMA
|
|
320
|
+
}, { additionalProperties: false }),
|
|
321
|
+
find_tags: Type.Object({
|
|
322
|
+
query: Type.String({ minLength: 1 }),
|
|
323
|
+
semantic: Type.Optional(Type.Boolean({ default: true })),
|
|
324
|
+
limit: QLOO_LIMIT_SCHEMA
|
|
325
|
+
}, { additionalProperties: false })
|
|
326
|
+
};
|
|
327
|
+
var sharedWorkflowBehavior = Object.freeze({
|
|
328
|
+
readOnly: true,
|
|
329
|
+
idempotent: true,
|
|
330
|
+
mutating: false,
|
|
331
|
+
destructive: false,
|
|
332
|
+
openWorld: true,
|
|
333
|
+
concurrency: "safe_for_independent_inputs",
|
|
334
|
+
requiredCredential: "qloo_api",
|
|
335
|
+
defaultResultBudget: QLOO_TOOL_DEFAULT_LIMIT,
|
|
336
|
+
maximumResultBudget: QLOO_TOOL_MAX_RESULTS,
|
|
337
|
+
safeRetryClasses: Object.freeze(["dns", "connect", "timeout", "rate_limit", "upstream_5xx"]),
|
|
338
|
+
supportedTransports: Object.freeze(["direct", "mcp"])
|
|
339
|
+
});
|
|
340
|
+
var mediumLatencyOperations = /* @__PURE__ */ new Set([
|
|
341
|
+
"recommend",
|
|
342
|
+
"rank",
|
|
343
|
+
"compare_audiences",
|
|
344
|
+
"entity_tags",
|
|
345
|
+
"audience_demographics",
|
|
346
|
+
"trends"
|
|
347
|
+
]);
|
|
348
|
+
var QLOO_WORKFLOW_BEHAVIORS = Object.freeze(Object.fromEntries(Object.keys(QLOO_WORKFLOW_INPUT_SCHEMAS).map((operation) => [
|
|
349
|
+
operation,
|
|
350
|
+
Object.freeze({
|
|
351
|
+
...sharedWorkflowBehavior,
|
|
352
|
+
expectedLatency: mediumLatencyOperations.has(operation) ? "medium" : "low"
|
|
353
|
+
})
|
|
354
|
+
])));
|
|
355
|
+
var QLOO_WORKFLOW_METADATA = {
|
|
356
|
+
recommend: {
|
|
357
|
+
toolName: "qloo_recommend",
|
|
358
|
+
label: "Qloo recommendations",
|
|
359
|
+
description: "Find Qloo entities related to one combined entity, tag, location, and demographic taste profile. Signals influence affinity; filters constrain output.",
|
|
360
|
+
promptSnippet: "Recommend entities from a combined Qloo taste profile",
|
|
361
|
+
promptGuidelines: [
|
|
362
|
+
"Pass every related taste and audience signal together in one qloo_recommend call.",
|
|
363
|
+
"Use signal_location for audience location and filter_location to constrain result geography.",
|
|
364
|
+
"Include demographic and location in the same call when both describe the audience."
|
|
365
|
+
],
|
|
366
|
+
documentation: ["https://docs.qloo.com/reference/insights-api-deep-dive"]
|
|
367
|
+
},
|
|
368
|
+
rank: {
|
|
369
|
+
toolName: "qloo_rank",
|
|
370
|
+
label: "Qloo rank",
|
|
371
|
+
description: "Rank one supplied option set against one shared entity, location, demographic, and tag profile. Scores from separate calls are not comparable.",
|
|
372
|
+
promptSnippet: "Rank a caller-supplied shortlist with Qloo",
|
|
373
|
+
promptGuidelines: ["Pass every option in one qloo_rank call so scores remain comparable."],
|
|
374
|
+
documentation: ["https://docs.qloo.com/reference/insights-api-deep-dive"]
|
|
375
|
+
},
|
|
376
|
+
describe: {
|
|
377
|
+
toolName: "qloo_describe",
|
|
378
|
+
label: "Qloo entity",
|
|
379
|
+
description: "Resolve one named entity or Qloo UUID and return only metadata present in Qloo.",
|
|
380
|
+
promptSnippet: "Resolve and describe one Qloo entity",
|
|
381
|
+
documentation: ["https://docs.qloo.com/reference/get-search", "https://docs.qloo.com/reference/get-entities"]
|
|
382
|
+
},
|
|
383
|
+
where_popular: {
|
|
384
|
+
toolName: "qloo_where_popular",
|
|
385
|
+
label: "Qloo geographic affinity",
|
|
386
|
+
description: "Find geographic areas where one entity has strong query-relative affinity. This is distinct from filtering recommendations to a place.",
|
|
387
|
+
promptSnippet: "Find where an entity is comparatively popular",
|
|
388
|
+
documentation: ["https://docs.qloo.com/reference/heatmaps-use-case"]
|
|
389
|
+
},
|
|
390
|
+
compare_audiences: {
|
|
391
|
+
toolName: "qloo_compare_audiences",
|
|
392
|
+
label: "Qloo audience comparison",
|
|
393
|
+
description: "Compare two complete groups of entity signals in one Qloo analysis request.",
|
|
394
|
+
promptSnippet: "Compare two Qloo taste-signal groups",
|
|
395
|
+
documentation: ["https://docs.qloo.com/reference/analysis-compare"]
|
|
396
|
+
},
|
|
397
|
+
entity_tags: {
|
|
398
|
+
toolName: "qloo_entity_tags",
|
|
399
|
+
label: "Qloo entity tags",
|
|
400
|
+
description: "Retrieve Qloo concepts that jointly characterize one or more entities using the documented urn:tag Insights workflow.",
|
|
401
|
+
promptSnippet: "Find concepts that characterize Qloo entities",
|
|
402
|
+
documentation: ["https://docs.qloo.com/reference/taste-analysis"]
|
|
403
|
+
},
|
|
404
|
+
audience_demographics: {
|
|
405
|
+
toolName: "qloo_audience_demographics",
|
|
406
|
+
label: "Qloo audience demographics",
|
|
407
|
+
description: "Retrieve aggregate demographic distributions associated with one entity through the documented urn:demographics Insights workflow.",
|
|
408
|
+
promptSnippet: "Inspect aggregate demographics associated with a Qloo entity",
|
|
409
|
+
documentation: ["https://docs.qloo.com/reference/demographics-use-case"]
|
|
410
|
+
},
|
|
411
|
+
trends: {
|
|
412
|
+
toolName: "qloo_trends",
|
|
413
|
+
label: "Qloo trends",
|
|
414
|
+
description: "Retrieve Qloo time-series popularity metrics for one to five resolved entities over an ISO date range.",
|
|
415
|
+
promptSnippet: "Track Qloo entity popularity metrics over time",
|
|
416
|
+
documentation: ["https://docs.qloo.com/reference/get-trending"]
|
|
417
|
+
},
|
|
418
|
+
find_tags: {
|
|
419
|
+
toolName: "qloo_find_tags",
|
|
420
|
+
label: "Qloo tag search",
|
|
421
|
+
description: "Search Qloo's tag ontology by natural-language concept and return stable tag URNs.",
|
|
422
|
+
promptSnippet: "Search the Qloo tag ontology",
|
|
423
|
+
documentation: ["https://docs.qloo.com/reference/get-tags-1"]
|
|
424
|
+
}
|
|
425
|
+
};
|
|
426
|
+
var QLOO_WORKFLOW_CONTRACTS = Object.freeze(Object.fromEntries(Object.entries(QLOO_WORKFLOW_METADATA).map(([id, metadata]) => [
|
|
427
|
+
id,
|
|
428
|
+
Object.freeze({
|
|
429
|
+
id,
|
|
430
|
+
...metadata,
|
|
431
|
+
behavior: QLOO_WORKFLOW_BEHAVIORS[id],
|
|
432
|
+
inputSchema: QLOO_WORKFLOW_INPUT_SCHEMAS[id],
|
|
433
|
+
resultSchema: QLOO_RESULT_ENVELOPE_SCHEMA
|
|
434
|
+
})
|
|
435
|
+
])));
|
|
436
|
+
|
|
437
|
+
// packages/qloo-tool-schema/dist/src/resolution-contracts.js
|
|
438
|
+
import { Type as Type2 } from "typebox";
|
|
439
|
+
var QLOO_RESOLUTION_CONTRACT_VERSION = "1.0-preview.1";
|
|
440
|
+
var QLOO_RESOLUTION_PROVIDER_DESCRIPTOR_SCHEMA = Type2.Object({
|
|
441
|
+
contract_version: Type2.Literal(QLOO_RESOLUTION_CONTRACT_VERSION),
|
|
442
|
+
provider_id: Type2.String({ minLength: 1 }),
|
|
443
|
+
name: Type2.String({ minLength: 1 }),
|
|
444
|
+
entity_strategy: Type2.String({ minLength: 1 }),
|
|
445
|
+
tag_strategy: Type2.String({ minLength: 1 }),
|
|
446
|
+
remote: Type2.Boolean(),
|
|
447
|
+
uses_model: Type2.Boolean(),
|
|
448
|
+
fallback: Type2.Literal("none")
|
|
449
|
+
}, { additionalProperties: false });
|
|
450
|
+
var QLOO_RESOLUTION_CANDIDATE_SCHEMA = Type2.Object({
|
|
451
|
+
id: Type2.String({ minLength: 1 }),
|
|
452
|
+
name: Type2.String({ minLength: 1 }),
|
|
453
|
+
type: Type2.Optional(Type2.String({ minLength: 1 })),
|
|
454
|
+
rank: Type2.Optional(Type2.Integer({ minimum: 1 })),
|
|
455
|
+
score: Type2.Optional(Type2.Number()),
|
|
456
|
+
popularity: Type2.Optional(Type2.Number()),
|
|
457
|
+
parent_types: Type2.Optional(Type2.Array(Type2.String({ minLength: 1 }), {
|
|
458
|
+
maxItems: 20,
|
|
459
|
+
uniqueItems: true
|
|
460
|
+
})),
|
|
461
|
+
description: Type2.Optional(Type2.String({ minLength: 1, maxLength: 500 })),
|
|
462
|
+
release_year: Type2.Optional(Type2.Integer()),
|
|
463
|
+
address: Type2.Optional(Type2.Unknown())
|
|
464
|
+
}, { additionalProperties: false });
|
|
465
|
+
var QLOO_RESOLUTION_PROVENANCE_SCHEMA = Type2.Object({
|
|
466
|
+
contract_version: Type2.Literal(QLOO_RESOLUTION_CONTRACT_VERSION),
|
|
467
|
+
provider_id: Type2.String({ minLength: 1 }),
|
|
468
|
+
provider_name: Type2.String({ minLength: 1 }),
|
|
469
|
+
endpoint: Type2.String({ minLength: 1 }),
|
|
470
|
+
strategy: Type2.String({ minLength: 1 }),
|
|
471
|
+
remote: Type2.Boolean(),
|
|
472
|
+
uses_model: Type2.Boolean(),
|
|
473
|
+
provider_version: Type2.Optional(Type2.String({ minLength: 1 })),
|
|
474
|
+
cache_status: Type2.Optional(Type2.String({ minLength: 1 }))
|
|
475
|
+
}, { additionalProperties: false });
|
|
476
|
+
var QLOO_RESOLUTION_OUTCOME_COMMON_SCHEMA = {
|
|
477
|
+
input: Type2.String({ minLength: 1 }),
|
|
478
|
+
provenance: QLOO_RESOLUTION_PROVENANCE_SCHEMA,
|
|
479
|
+
warnings: Type2.Optional(Type2.Array(Type2.String()))
|
|
480
|
+
};
|
|
481
|
+
var QLOO_RESOLUTION_OUTCOME_SCHEMA = Type2.Union([
|
|
482
|
+
Type2.Object({
|
|
483
|
+
...QLOO_RESOLUTION_OUTCOME_COMMON_SCHEMA,
|
|
484
|
+
status: Type2.Literal("resolved"),
|
|
485
|
+
match: Type2.Union([
|
|
486
|
+
Type2.Literal("identifier"),
|
|
487
|
+
Type2.Literal("exact"),
|
|
488
|
+
Type2.Literal("semantic")
|
|
489
|
+
]),
|
|
490
|
+
selected: QLOO_RESOLUTION_CANDIDATE_SCHEMA,
|
|
491
|
+
alternatives: Type2.Optional(Type2.Array(QLOO_RESOLUTION_CANDIDATE_SCHEMA))
|
|
492
|
+
}, { additionalProperties: false }),
|
|
493
|
+
Type2.Object({
|
|
494
|
+
...QLOO_RESOLUTION_OUTCOME_COMMON_SCHEMA,
|
|
495
|
+
status: Type2.Literal("ambiguous"),
|
|
496
|
+
candidates: Type2.Array(QLOO_RESOLUTION_CANDIDATE_SCHEMA)
|
|
497
|
+
}, { additionalProperties: false }),
|
|
498
|
+
Type2.Object({
|
|
499
|
+
...QLOO_RESOLUTION_OUTCOME_COMMON_SCHEMA,
|
|
500
|
+
status: Type2.Literal("not_found"),
|
|
501
|
+
candidates: Type2.Optional(Type2.Array(QLOO_RESOLUTION_CANDIDATE_SCHEMA))
|
|
502
|
+
}, { additionalProperties: false })
|
|
503
|
+
]);
|
|
504
|
+
|
|
505
|
+
// apps/qloo-harness/dist/exec.js
|
|
506
|
+
import { createDirectQlooWorkflowExecutorFromEnvironment } from "./qloo-tools.js";
|
|
507
|
+
import { QlooWorkflowExecutionError } from "./workflow-executor.js";
|
|
508
|
+
var QLOO_EXEC_EVENT_SCHEMA_VERSION = "1.0";
|
|
509
|
+
var QLOO_EXEC_MAX_INPUT_BYTES = 1048576;
|
|
510
|
+
var QLOO_EXEC_HELP = `Execute one validated Qloo workflow without a model or TUI
|
|
511
|
+
|
|
512
|
+
Usage:
|
|
513
|
+
qloo exec <operation> --input '<json>'
|
|
514
|
+
qloo exec <operation> --input-file <path>
|
|
515
|
+
printf '<json>' | qloo exec <operation>
|
|
516
|
+
qloo exec <operation> --input '<json>' --jsonl
|
|
517
|
+
|
|
518
|
+
Operations:
|
|
519
|
+
${QLOO_WORKFLOW_OPERATION_IDS.join(", ")}
|
|
520
|
+
|
|
521
|
+
Output:
|
|
522
|
+
The default is one normalized JSON result on stdout.
|
|
523
|
+
--jsonl emits versioned started and completed/failed events.
|
|
524
|
+
|
|
525
|
+
The execution transport is the direct Qloo API. No CLI or MCP fallback occurs.`;
|
|
526
|
+
var QlooExecUsageError = class extends Error {
|
|
527
|
+
constructor(message) {
|
|
528
|
+
super(message);
|
|
529
|
+
this.name = "QlooExecUsageError";
|
|
530
|
+
}
|
|
531
|
+
};
|
|
532
|
+
function optionValue(argv, index, name) {
|
|
533
|
+
const argument = argv[index];
|
|
534
|
+
if (argument === name) {
|
|
535
|
+
const value = argv[index + 1];
|
|
536
|
+
if (value === void 0)
|
|
537
|
+
throw new QlooExecUsageError(`${name} requires a value`);
|
|
538
|
+
return { value, consumed: 2 };
|
|
539
|
+
}
|
|
540
|
+
const prefix = `${name}=`;
|
|
541
|
+
return argument?.startsWith(prefix) ? { value: argument.slice(prefix.length), consumed: 1 } : void 0;
|
|
542
|
+
}
|
|
543
|
+
function parseExecArguments(argv) {
|
|
544
|
+
const operation = argv[0];
|
|
545
|
+
if (!operation || !isQlooWorkflowOperationId(operation)) {
|
|
546
|
+
throw new QlooExecUsageError(operation ? `unknown workflow operation "${operation}"` : "a workflow operation is required");
|
|
547
|
+
}
|
|
548
|
+
let inlineInput;
|
|
549
|
+
let inputFile;
|
|
550
|
+
let jsonl = false;
|
|
551
|
+
for (let index = 1; index < argv.length; ) {
|
|
552
|
+
const argument = argv[index];
|
|
553
|
+
const inputOption = optionValue(argv, index, "--input");
|
|
554
|
+
if (inputOption) {
|
|
555
|
+
if (inlineInput !== void 0)
|
|
556
|
+
throw new QlooExecUsageError("--input may only be specified once");
|
|
557
|
+
inlineInput = inputOption.value;
|
|
558
|
+
index += inputOption.consumed;
|
|
559
|
+
continue;
|
|
560
|
+
}
|
|
561
|
+
const fileOption = optionValue(argv, index, "--input-file");
|
|
562
|
+
if (fileOption) {
|
|
563
|
+
if (inputFile !== void 0)
|
|
564
|
+
throw new QlooExecUsageError("--input-file may only be specified once");
|
|
565
|
+
inputFile = fileOption.value;
|
|
566
|
+
index += fileOption.consumed;
|
|
567
|
+
continue;
|
|
568
|
+
}
|
|
569
|
+
if (argument === "--jsonl") {
|
|
570
|
+
if (jsonl)
|
|
571
|
+
throw new QlooExecUsageError("--jsonl may only be specified once");
|
|
572
|
+
jsonl = true;
|
|
573
|
+
index += 1;
|
|
574
|
+
continue;
|
|
575
|
+
}
|
|
576
|
+
throw new QlooExecUsageError(`unknown option "${argument ?? ""}"`);
|
|
577
|
+
}
|
|
578
|
+
if (inlineInput !== void 0 && inputFile !== void 0) {
|
|
579
|
+
throw new QlooExecUsageError("choose exactly one of --input or --input-file");
|
|
580
|
+
}
|
|
581
|
+
return {
|
|
582
|
+
operation,
|
|
583
|
+
...inlineInput === void 0 ? {} : { inlineInput },
|
|
584
|
+
...inputFile === void 0 ? {} : { inputFile },
|
|
585
|
+
jsonl
|
|
586
|
+
};
|
|
587
|
+
}
|
|
588
|
+
async function readBoundedInputFile(path, maximumBytes) {
|
|
589
|
+
const metadata = await stat(path);
|
|
590
|
+
if (!metadata.isFile())
|
|
591
|
+
throw new QlooExecUsageError(`input path is not a regular file: ${path}`);
|
|
592
|
+
if (metadata.size > maximumBytes) {
|
|
593
|
+
throw new QlooExecUsageError(`input exceeds the ${maximumBytes}-byte limit`);
|
|
594
|
+
}
|
|
595
|
+
const value = await readFile(path, "utf8");
|
|
596
|
+
if (Buffer.byteLength(value) > maximumBytes) {
|
|
597
|
+
throw new QlooExecUsageError(`input exceeds the ${maximumBytes}-byte limit`);
|
|
598
|
+
}
|
|
599
|
+
return value;
|
|
600
|
+
}
|
|
601
|
+
async function readBoundedStdin(stream, maximumBytes) {
|
|
602
|
+
return new Promise((resolve, reject) => {
|
|
603
|
+
const chunks = [];
|
|
604
|
+
let size = 0;
|
|
605
|
+
const cleanup = () => {
|
|
606
|
+
stream.removeListener("data", onData);
|
|
607
|
+
stream.removeListener("end", onEnd);
|
|
608
|
+
stream.removeListener("error", onError);
|
|
609
|
+
};
|
|
610
|
+
const onData = (chunk) => {
|
|
611
|
+
const value = Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk);
|
|
612
|
+
size += value.length;
|
|
613
|
+
if (size > maximumBytes) {
|
|
614
|
+
cleanup();
|
|
615
|
+
stream.pause();
|
|
616
|
+
reject(new QlooExecUsageError(`input exceeds the ${maximumBytes}-byte limit`));
|
|
617
|
+
return;
|
|
618
|
+
}
|
|
619
|
+
chunks.push(value);
|
|
620
|
+
};
|
|
621
|
+
const onEnd = () => {
|
|
622
|
+
cleanup();
|
|
623
|
+
resolve(Buffer.concat(chunks).toString("utf8"));
|
|
624
|
+
};
|
|
625
|
+
const onError = (error) => {
|
|
626
|
+
cleanup();
|
|
627
|
+
reject(error);
|
|
628
|
+
};
|
|
629
|
+
stream.on("data", onData);
|
|
630
|
+
stream.once("end", onEnd);
|
|
631
|
+
stream.once("error", onError);
|
|
632
|
+
});
|
|
633
|
+
}
|
|
634
|
+
function parseInputObject(value) {
|
|
635
|
+
let parsed;
|
|
636
|
+
try {
|
|
637
|
+
parsed = JSON.parse(value);
|
|
638
|
+
} catch {
|
|
639
|
+
throw new QlooExecUsageError("workflow input must be valid JSON");
|
|
640
|
+
}
|
|
641
|
+
if (parsed === null || typeof parsed !== "object" || Array.isArray(parsed)) {
|
|
642
|
+
throw new QlooExecUsageError("workflow input must be one JSON object");
|
|
643
|
+
}
|
|
644
|
+
return parsed;
|
|
645
|
+
}
|
|
646
|
+
function failureExitCode(error) {
|
|
647
|
+
if (error.code === "TOOL_INPUT")
|
|
648
|
+
return 2;
|
|
649
|
+
if (error.code === "QLOO_AUTH")
|
|
650
|
+
return 4;
|
|
651
|
+
if (error.code === "CANCELLED")
|
|
652
|
+
return 130;
|
|
653
|
+
return 1;
|
|
654
|
+
}
|
|
655
|
+
function writeJsonLine(writeOut, value) {
|
|
656
|
+
writeOut(`${JSON.stringify(value)}
|
|
657
|
+
`);
|
|
658
|
+
}
|
|
659
|
+
function failedEvent(operation, correlationId, sequence, error) {
|
|
660
|
+
return {
|
|
661
|
+
schema_version: QLOO_EXEC_EVENT_SCHEMA_VERSION,
|
|
662
|
+
event: "failed",
|
|
663
|
+
sequence,
|
|
664
|
+
operation,
|
|
665
|
+
correlation_id: correlationId,
|
|
666
|
+
error: {
|
|
667
|
+
code: error.code,
|
|
668
|
+
layer: error.layer,
|
|
669
|
+
retryable: error.retryable,
|
|
670
|
+
recovery: error.recovery
|
|
671
|
+
}
|
|
672
|
+
};
|
|
673
|
+
}
|
|
674
|
+
async function runQlooExec(argv, options = {}) {
|
|
675
|
+
const writeOut = options.writeOut ?? ((text) => process.stdout.write(text));
|
|
676
|
+
const writeError = options.writeError ?? ((text) => process.stderr.write(text));
|
|
677
|
+
if (argv.length === 1 && (argv[0] === "--help" || argv[0] === "-h")) {
|
|
678
|
+
writeOut(`${QLOO_EXEC_HELP}
|
|
679
|
+
`);
|
|
680
|
+
return 0;
|
|
681
|
+
}
|
|
682
|
+
let parsed;
|
|
683
|
+
try {
|
|
684
|
+
parsed = parseExecArguments(argv);
|
|
685
|
+
} catch (error) {
|
|
686
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
687
|
+
writeError(`qloo exec: ${message}
|
|
688
|
+
|
|
689
|
+
${QLOO_EXEC_HELP}
|
|
690
|
+
`);
|
|
691
|
+
return 2;
|
|
692
|
+
}
|
|
693
|
+
const maximumInputBytes = options.maximumInputBytes ?? QLOO_EXEC_MAX_INPUT_BYTES;
|
|
694
|
+
let serializedInput;
|
|
695
|
+
try {
|
|
696
|
+
if (parsed.inlineInput !== void 0)
|
|
697
|
+
serializedInput = parsed.inlineInput;
|
|
698
|
+
else if (parsed.inputFile !== void 0) {
|
|
699
|
+
serializedInput = await (options.readInputFile ?? readBoundedInputFile)(parsed.inputFile, maximumInputBytes);
|
|
700
|
+
} else {
|
|
701
|
+
if (options.stdinIsTTY ?? process.stdin.isTTY === true) {
|
|
702
|
+
throw new QlooExecUsageError("provide --input, --input-file, or pipe one JSON object on stdin");
|
|
703
|
+
}
|
|
704
|
+
serializedInput = await readBoundedStdin(options.stdin ?? process.stdin, maximumInputBytes);
|
|
705
|
+
}
|
|
706
|
+
if (Buffer.byteLength(serializedInput) > maximumInputBytes) {
|
|
707
|
+
throw new QlooExecUsageError(`input exceeds the ${maximumInputBytes}-byte limit`);
|
|
708
|
+
}
|
|
709
|
+
} catch (error) {
|
|
710
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
711
|
+
writeError(`qloo exec: unable to read workflow input: ${message}
|
|
712
|
+
`);
|
|
713
|
+
return 2;
|
|
714
|
+
}
|
|
715
|
+
let input;
|
|
716
|
+
try {
|
|
717
|
+
input = parseInputObject(serializedInput);
|
|
718
|
+
} catch (error) {
|
|
719
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
720
|
+
writeError(`qloo exec: ${message}
|
|
721
|
+
`);
|
|
722
|
+
return 2;
|
|
723
|
+
}
|
|
724
|
+
const correlationId = (options.createCorrelationId ?? (() => `exec-${randomUUID()}`))();
|
|
725
|
+
const executor = (options.createExecutor ?? (() => createDirectQlooWorkflowExecutorFromEnvironment({
|
|
726
|
+
...options.env ? { env: options.env } : {}
|
|
727
|
+
})))();
|
|
728
|
+
if (!executor) {
|
|
729
|
+
const failure = failedEvent(parsed.operation, correlationId, 1, {
|
|
730
|
+
code: "QLOO_AUTH",
|
|
731
|
+
layer: "qloo",
|
|
732
|
+
retryable: false,
|
|
733
|
+
recovery: "Set QLOO_API_KEY or run qloo config set api-key, then retry the same operation."
|
|
734
|
+
});
|
|
735
|
+
if (parsed.jsonl)
|
|
736
|
+
writeJsonLine(writeOut, failure);
|
|
737
|
+
else
|
|
738
|
+
writeJsonLine(writeOut, { error: failure.error, operation: failure.operation, correlation_id: correlationId });
|
|
739
|
+
return 4;
|
|
740
|
+
}
|
|
741
|
+
if (parsed.jsonl) {
|
|
742
|
+
writeJsonLine(writeOut, {
|
|
743
|
+
schema_version: QLOO_EXEC_EVENT_SCHEMA_VERSION,
|
|
744
|
+
event: "started",
|
|
745
|
+
sequence: 1,
|
|
746
|
+
operation: parsed.operation,
|
|
747
|
+
correlation_id: correlationId,
|
|
748
|
+
transport: "direct"
|
|
749
|
+
});
|
|
750
|
+
}
|
|
751
|
+
try {
|
|
752
|
+
const execution = await executor.execute(parsed.operation, input, {
|
|
753
|
+
correlationId,
|
|
754
|
+
...options.signal ? { signal: options.signal } : {}
|
|
755
|
+
});
|
|
756
|
+
if (parsed.jsonl) {
|
|
757
|
+
writeJsonLine(writeOut, {
|
|
758
|
+
schema_version: QLOO_EXEC_EVENT_SCHEMA_VERSION,
|
|
759
|
+
event: "completed",
|
|
760
|
+
sequence: 2,
|
|
761
|
+
operation: parsed.operation,
|
|
762
|
+
correlation_id: execution.correlationId,
|
|
763
|
+
duration_ms: execution.durationMs,
|
|
764
|
+
result: execution.result
|
|
765
|
+
});
|
|
766
|
+
} else {
|
|
767
|
+
writeJsonLine(writeOut, execution.result);
|
|
768
|
+
}
|
|
769
|
+
return 0;
|
|
770
|
+
} catch (error) {
|
|
771
|
+
const wrapped = error instanceof QlooWorkflowExecutionError ? error : new QlooWorkflowExecutionError(parsed.operation, executor.transport, "Qloo exec failed inside the harness.", error, { correlationId });
|
|
772
|
+
const failure = failedEvent(parsed.operation, correlationId, parsed.jsonl ? 2 : 1, wrapped);
|
|
773
|
+
writeJsonLine(writeOut, parsed.jsonl ? failure : { error: failure.error, operation: failure.operation, correlation_id: correlationId });
|
|
774
|
+
return failureExitCode(wrapped);
|
|
775
|
+
}
|
|
776
|
+
}
|
|
777
|
+
export {
|
|
778
|
+
QLOO_EXEC_EVENT_SCHEMA_VERSION,
|
|
779
|
+
QLOO_EXEC_HELP,
|
|
780
|
+
QLOO_EXEC_MAX_INPUT_BYTES,
|
|
781
|
+
runQlooExec
|
|
782
|
+
};
|