@vectros-ai/cli 0.26.0 → 0.27.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/AUTHORING.md +85 -7
- package/CHANGELOG.md +38 -0
- package/README.md +70 -17
- package/dist/cli.js +2355 -469
- package/dist/cli.js.map +1 -1
- package/dist/cli.mjs +2347 -461
- package/dist/cli.mjs.map +1 -1
- package/package.json +4 -4
package/dist/cli.mjs
CHANGED
|
@@ -526,8 +526,8 @@ var require_graceful_fs = __commonJS({
|
|
|
526
526
|
fs5.createReadStream = createReadStream3;
|
|
527
527
|
fs5.createWriteStream = createWriteStream;
|
|
528
528
|
var fs$readFile = fs5.readFile;
|
|
529
|
-
fs5.readFile =
|
|
530
|
-
function
|
|
529
|
+
fs5.readFile = readFile5;
|
|
530
|
+
function readFile5(path4, options, cb) {
|
|
531
531
|
if (typeof options === "function")
|
|
532
532
|
cb = options, options = null;
|
|
533
533
|
return go$readFile(path4, options, cb);
|
|
@@ -1619,7 +1619,7 @@ var require_proper_lockfile = __commonJS({
|
|
|
1619
1619
|
// src/cli-bootstrap.ts
|
|
1620
1620
|
import * as readline2 from "readline/promises";
|
|
1621
1621
|
|
|
1622
|
-
//
|
|
1622
|
+
// node_modules/@vectros-ai/sdk/dist/index.mjs
|
|
1623
1623
|
var __defProp2 = Object.defineProperty;
|
|
1624
1624
|
var __export = (target, all) => {
|
|
1625
1625
|
for (var name in all)
|
|
@@ -1640,6 +1640,7 @@ __export(api_exports, {
|
|
|
1640
1640
|
ContentTooLargeError: () => ContentTooLargeError,
|
|
1641
1641
|
DocumentAskRequest: () => DocumentAskRequest,
|
|
1642
1642
|
DocumentContextEvent: () => DocumentContextEvent,
|
|
1643
|
+
DocumentExtraction: () => DocumentExtraction,
|
|
1643
1644
|
DocumentLookupRequest: () => DocumentLookupRequest,
|
|
1644
1645
|
DocumentPatchRequest: () => DocumentPatchRequest,
|
|
1645
1646
|
DocumentRequest: () => DocumentRequest,
|
|
@@ -1692,6 +1693,7 @@ __export(api_exports, {
|
|
|
1692
1693
|
UserExistsResponse: () => UserExistsResponse,
|
|
1693
1694
|
UserRequest: () => UserRequest,
|
|
1694
1695
|
UserResponse: () => UserResponse,
|
|
1696
|
+
agents: () => agents_exports,
|
|
1695
1697
|
auth: () => auth_exports,
|
|
1696
1698
|
compliance: () => compliance_exports,
|
|
1697
1699
|
documents: () => documents_exports,
|
|
@@ -1957,6 +1959,7 @@ var UnprocessableEntityError = class extends VectrosError {
|
|
|
1957
1959
|
this.name = this.constructor.name;
|
|
1958
1960
|
}
|
|
1959
1961
|
};
|
|
1962
|
+
var agents_exports = {};
|
|
1960
1963
|
var auth_exports = {};
|
|
1961
1964
|
var ErasureRequest;
|
|
1962
1965
|
((ErasureRequest2) => {
|
|
@@ -2161,6 +2164,18 @@ var DocumentContextEvent;
|
|
|
2161
2164
|
DocumentContext: "document_context"
|
|
2162
2165
|
};
|
|
2163
2166
|
})(DocumentContextEvent || (DocumentContextEvent = {}));
|
|
2167
|
+
var DocumentExtraction;
|
|
2168
|
+
((DocumentExtraction2) => {
|
|
2169
|
+
DocumentExtraction2.Outcome = {
|
|
2170
|
+
Ok: "OK",
|
|
2171
|
+
Truncated: "TRUNCATED",
|
|
2172
|
+
NoText: "NO_TEXT",
|
|
2173
|
+
Encrypted: "ENCRYPTED",
|
|
2174
|
+
Unsupported: "UNSUPPORTED",
|
|
2175
|
+
TooLarge: "TOO_LARGE",
|
|
2176
|
+
Unreadable: "UNREADABLE"
|
|
2177
|
+
};
|
|
2178
|
+
})(DocumentExtraction || (DocumentExtraction = {}));
|
|
2164
2179
|
var DocumentRequest;
|
|
2165
2180
|
((DocumentRequest2) => {
|
|
2166
2181
|
DocumentRequest2.IndexMode = {
|
|
@@ -4029,24 +4044,32 @@ function handleNonStatusCodeError(error, rawResponse, method, path4) {
|
|
|
4029
4044
|
});
|
|
4030
4045
|
}
|
|
4031
4046
|
}
|
|
4032
|
-
var
|
|
4047
|
+
var AgentsClient = class {
|
|
4033
4048
|
constructor(options) {
|
|
4034
4049
|
this._options = normalizeClientOptionsWithAuth(options);
|
|
4035
4050
|
}
|
|
4036
4051
|
/**
|
|
4037
|
-
* Returns
|
|
4052
|
+
* Returns a page of the agents in the app context, newest first, each at its latest version. A caller holding `runs:c` but not `agents:r` gets only each agent's `id`, `name`, `description`, `latestVersion` and `status`. Retired and purged agents are left out unless `includeRetired=true`. Requires `agents:r` or `runs:c`.
|
|
4038
4053
|
*
|
|
4039
|
-
* @param {
|
|
4054
|
+
* @param {Vectros.ListAgentsRequest} request
|
|
4055
|
+
* @param {AgentsClient.RequestOptions} requestOptions - Request-specific configuration.
|
|
4040
4056
|
*
|
|
4041
|
-
* @throws {@link Vectros.
|
|
4057
|
+
* @throws {@link Vectros.ForbiddenError}
|
|
4042
4058
|
*
|
|
4043
4059
|
* @example
|
|
4044
|
-
* await client.
|
|
4060
|
+
* await client.agents.listAgents()
|
|
4045
4061
|
*/
|
|
4046
|
-
|
|
4047
|
-
return HttpResponsePromise.fromPromise(this.
|
|
4062
|
+
listAgents(request = {}, requestOptions) {
|
|
4063
|
+
return HttpResponsePromise.fromPromise(this.__listAgents(request, requestOptions));
|
|
4048
4064
|
}
|
|
4049
|
-
async
|
|
4065
|
+
async __listAgents(request = {}, requestOptions) {
|
|
4066
|
+
const { contextId, includeRetired, startFrom, limit } = request;
|
|
4067
|
+
const _queryParams = {
|
|
4068
|
+
contextId,
|
|
4069
|
+
includeRetired,
|
|
4070
|
+
startFrom,
|
|
4071
|
+
limit
|
|
4072
|
+
};
|
|
4050
4073
|
const _authRequest = await this._options.authProvider.getAuthRequest();
|
|
4051
4074
|
const _headers = mergeHeaders(
|
|
4052
4075
|
_authRequest.headers,
|
|
@@ -4056,11 +4079,11 @@ var AuthClient = class {
|
|
|
4056
4079
|
const _response = await fetcher({
|
|
4057
4080
|
url: url_exports.join(
|
|
4058
4081
|
await Supplier.get(this._options.baseUrl) ?? await Supplier.get(this._options.environment),
|
|
4059
|
-
"v1/
|
|
4082
|
+
"v1/agents"
|
|
4060
4083
|
),
|
|
4061
4084
|
method: "GET",
|
|
4062
4085
|
headers: _headers,
|
|
4063
|
-
queryString: url_exports.queryBuilder().mergeAdditional(requestOptions?.queryParams).build(),
|
|
4086
|
+
queryString: url_exports.queryBuilder().addMany(_queryParams).mergeAdditional(requestOptions?.queryParams).build(),
|
|
4064
4087
|
timeoutMs: (requestOptions?.timeoutInSeconds ?? this._options?.timeoutInSeconds ?? 60) * 1e3,
|
|
4065
4088
|
maxRetries: requestOptions?.maxRetries ?? this._options?.maxRetries,
|
|
4066
4089
|
abortSignal: requestOptions?.abortSignal,
|
|
@@ -4072,8 +4095,8 @@ var AuthClient = class {
|
|
|
4072
4095
|
}
|
|
4073
4096
|
if (_response.error.reason === "status-code") {
|
|
4074
4097
|
switch (_response.error.statusCode) {
|
|
4075
|
-
case
|
|
4076
|
-
throw new
|
|
4098
|
+
case 403:
|
|
4099
|
+
throw new ForbiddenError(_response.error.body, _response.rawResponse);
|
|
4077
4100
|
default:
|
|
4078
4101
|
throw new VectrosError({
|
|
4079
4102
|
statusCode: _response.error.statusCode,
|
|
@@ -4082,59 +4105,34 @@ var AuthClient = class {
|
|
|
4082
4105
|
});
|
|
4083
4106
|
}
|
|
4084
4107
|
}
|
|
4085
|
-
return handleNonStatusCodeError(_response.error, _response.rawResponse, "GET", "/v1/
|
|
4108
|
+
return handleNonStatusCodeError(_response.error, _response.rawResponse, "GET", "/v1/agents");
|
|
4086
4109
|
}
|
|
4087
4110
|
/**
|
|
4088
|
-
*
|
|
4111
|
+
* Stores a new agent at version 1, or returns the existing agent of this name unchanged. With `?upsert=true` an existing agent whose definition differs gets a new version instead. Requires the `agents:c` scope (also `agents:u` with `?upsert=true`).
|
|
4089
4112
|
*
|
|
4090
|
-
* @param {Vectros.
|
|
4091
|
-
* @param {
|
|
4113
|
+
* @param {Vectros.CreateAgentRequest} request
|
|
4114
|
+
* @param {AgentsClient.RequestOptions} requestOptions - Request-specific configuration.
|
|
4092
4115
|
*
|
|
4093
4116
|
* @throws {@link Vectros.BadRequestError}
|
|
4094
4117
|
* @throws {@link Vectros.ForbiddenError}
|
|
4118
|
+
* @throws {@link Vectros.ConflictError}
|
|
4119
|
+
* @throws {@link Vectros.TooManyRequestsError}
|
|
4095
4120
|
*
|
|
4096
4121
|
* @example
|
|
4097
|
-
* await client.
|
|
4098
|
-
*
|
|
4099
|
-
*
|
|
4100
|
-
*
|
|
4101
|
-
* action: "read",
|
|
4102
|
-
* callerKeyId: "key_abc123",
|
|
4103
|
-
* resourceType: "intake_form",
|
|
4104
|
-
* resourceId: "rec_456"
|
|
4122
|
+
* await client.agents.createAgent({
|
|
4123
|
+
* body: {
|
|
4124
|
+
* model: "claude-sonnet-5-5"
|
|
4125
|
+
* }
|
|
4105
4126
|
* })
|
|
4106
4127
|
*/
|
|
4107
|
-
|
|
4108
|
-
return HttpResponsePromise.fromPromise(this.
|
|
4128
|
+
createAgent(request, requestOptions) {
|
|
4129
|
+
return HttpResponsePromise.fromPromise(this.__createAgent(request, requestOptions));
|
|
4109
4130
|
}
|
|
4110
|
-
async
|
|
4111
|
-
const {
|
|
4112
|
-
subjectType,
|
|
4113
|
-
subjectId,
|
|
4114
|
-
contextId,
|
|
4115
|
-
from: from_,
|
|
4116
|
-
to,
|
|
4117
|
-
action,
|
|
4118
|
-
callerKeyId,
|
|
4119
|
-
resourceType,
|
|
4120
|
-
resourceId,
|
|
4121
|
-
revealedSensitive,
|
|
4122
|
-
startFrom,
|
|
4123
|
-
limit
|
|
4124
|
-
} = request;
|
|
4131
|
+
async __createAgent(request, requestOptions) {
|
|
4132
|
+
const { upsert, contextId, body: _body } = request;
|
|
4125
4133
|
const _queryParams = {
|
|
4126
|
-
|
|
4127
|
-
|
|
4128
|
-
contextId,
|
|
4129
|
-
from: from_,
|
|
4130
|
-
to,
|
|
4131
|
-
action,
|
|
4132
|
-
callerKeyId,
|
|
4133
|
-
resourceType,
|
|
4134
|
-
resourceId,
|
|
4135
|
-
revealedSensitive,
|
|
4136
|
-
startFrom,
|
|
4137
|
-
limit
|
|
4134
|
+
upsert,
|
|
4135
|
+
contextId
|
|
4138
4136
|
};
|
|
4139
4137
|
const _authRequest = await this._options.authProvider.getAuthRequest();
|
|
4140
4138
|
const _headers = mergeHeaders(
|
|
@@ -4145,11 +4143,14 @@ var AuthClient = class {
|
|
|
4145
4143
|
const _response = await fetcher({
|
|
4146
4144
|
url: url_exports.join(
|
|
4147
4145
|
await Supplier.get(this._options.baseUrl) ?? await Supplier.get(this._options.environment),
|
|
4148
|
-
"v1/
|
|
4146
|
+
"v1/agents"
|
|
4149
4147
|
),
|
|
4150
|
-
method: "
|
|
4148
|
+
method: "POST",
|
|
4151
4149
|
headers: _headers,
|
|
4150
|
+
contentType: "application/json",
|
|
4152
4151
|
queryString: url_exports.queryBuilder().addMany(_queryParams).mergeAdditional(requestOptions?.queryParams).build(),
|
|
4152
|
+
requestType: "json",
|
|
4153
|
+
body: _body,
|
|
4153
4154
|
timeoutMs: (requestOptions?.timeoutInSeconds ?? this._options?.timeoutInSeconds ?? 60) * 1e3,
|
|
4154
4155
|
maxRetries: requestOptions?.maxRetries ?? this._options?.maxRetries,
|
|
4155
4156
|
abortSignal: requestOptions?.abortSignal,
|
|
@@ -4165,6 +4166,10 @@ var AuthClient = class {
|
|
|
4165
4166
|
throw new BadRequestError(_response.error.body, _response.rawResponse);
|
|
4166
4167
|
case 403:
|
|
4167
4168
|
throw new ForbiddenError(_response.error.body, _response.rawResponse);
|
|
4169
|
+
case 409:
|
|
4170
|
+
throw new ConflictError(_response.error.body, _response.rawResponse);
|
|
4171
|
+
case 429:
|
|
4172
|
+
throw new TooManyRequestsError(_response.error.body, _response.rawResponse);
|
|
4168
4173
|
default:
|
|
4169
4174
|
throw new VectrosError({
|
|
4170
4175
|
statusCode: _response.error.statusCode,
|
|
@@ -4173,22 +4178,31 @@ var AuthClient = class {
|
|
|
4173
4178
|
});
|
|
4174
4179
|
}
|
|
4175
4180
|
}
|
|
4176
|
-
return handleNonStatusCodeError(_response.error, _response.rawResponse, "
|
|
4181
|
+
return handleNonStatusCodeError(_response.error, _response.rawResponse, "POST", "/v1/agents");
|
|
4177
4182
|
}
|
|
4178
4183
|
/**
|
|
4179
|
-
*
|
|
4184
|
+
* Returns the agent at its latest version, or at `version`. Requires the `agents:r` scope.
|
|
4180
4185
|
*
|
|
4181
|
-
* @param {
|
|
4186
|
+
* @param {Vectros.GetAgentRequest} request
|
|
4187
|
+
* @param {AgentsClient.RequestOptions} requestOptions - Request-specific configuration.
|
|
4182
4188
|
*
|
|
4183
4189
|
* @throws {@link Vectros.ForbiddenError}
|
|
4190
|
+
* @throws {@link Vectros.NotFoundError}
|
|
4184
4191
|
*
|
|
4185
4192
|
* @example
|
|
4186
|
-
* await client.
|
|
4193
|
+
* await client.agents.getAgent({
|
|
4194
|
+
* id: "id"
|
|
4195
|
+
* })
|
|
4187
4196
|
*/
|
|
4188
|
-
|
|
4189
|
-
return HttpResponsePromise.fromPromise(this.
|
|
4197
|
+
getAgent(request, requestOptions) {
|
|
4198
|
+
return HttpResponsePromise.fromPromise(this.__getAgent(request, requestOptions));
|
|
4190
4199
|
}
|
|
4191
|
-
async
|
|
4200
|
+
async __getAgent(request, requestOptions) {
|
|
4201
|
+
const { id, contextId, version } = request;
|
|
4202
|
+
const _queryParams = {
|
|
4203
|
+
contextId,
|
|
4204
|
+
version
|
|
4205
|
+
};
|
|
4192
4206
|
const _authRequest = await this._options.authProvider.getAuthRequest();
|
|
4193
4207
|
const _headers = mergeHeaders(
|
|
4194
4208
|
_authRequest.headers,
|
|
@@ -4198,11 +4212,11 @@ var AuthClient = class {
|
|
|
4198
4212
|
const _response = await fetcher({
|
|
4199
4213
|
url: url_exports.join(
|
|
4200
4214
|
await Supplier.get(this._options.baseUrl) ?? await Supplier.get(this._options.environment),
|
|
4201
|
-
|
|
4215
|
+
`v1/agents/${url_exports.encodePathParam(id)}`
|
|
4202
4216
|
),
|
|
4203
4217
|
method: "GET",
|
|
4204
4218
|
headers: _headers,
|
|
4205
|
-
queryString: url_exports.queryBuilder().mergeAdditional(requestOptions?.queryParams).build(),
|
|
4219
|
+
queryString: url_exports.queryBuilder().addMany(_queryParams).mergeAdditional(requestOptions?.queryParams).build(),
|
|
4206
4220
|
timeoutMs: (requestOptions?.timeoutInSeconds ?? this._options?.timeoutInSeconds ?? 60) * 1e3,
|
|
4207
4221
|
maxRetries: requestOptions?.maxRetries ?? this._options?.maxRetries,
|
|
4208
4222
|
abortSignal: requestOptions?.abortSignal,
|
|
@@ -4216,6 +4230,8 @@ var AuthClient = class {
|
|
|
4216
4230
|
switch (_response.error.statusCode) {
|
|
4217
4231
|
case 403:
|
|
4218
4232
|
throw new ForbiddenError(_response.error.body, _response.rawResponse);
|
|
4233
|
+
case 404:
|
|
4234
|
+
throw new NotFoundError(_response.error.body, _response.rawResponse);
|
|
4219
4235
|
default:
|
|
4220
4236
|
throw new VectrosError({
|
|
4221
4237
|
statusCode: _response.error.statusCode,
|
|
@@ -4224,13 +4240,13 @@ var AuthClient = class {
|
|
|
4224
4240
|
});
|
|
4225
4241
|
}
|
|
4226
4242
|
}
|
|
4227
|
-
return handleNonStatusCodeError(_response.error, _response.rawResponse, "GET", "/v1/
|
|
4243
|
+
return handleNonStatusCodeError(_response.error, _response.rawResponse, "GET", "/v1/agents/{id}");
|
|
4228
4244
|
}
|
|
4229
4245
|
/**
|
|
4230
|
-
*
|
|
4246
|
+
* Stores the body as the agent's next version. Earlier versions never change. Every optional field you leave out takes its default; `model` is required. With `?restore=true` a retired agent becomes active again with this version. Requires the `agents:u` scope.
|
|
4231
4247
|
*
|
|
4232
|
-
* @param {Vectros.
|
|
4233
|
-
* @param {
|
|
4248
|
+
* @param {Vectros.UpdateAgentRequest} request
|
|
4249
|
+
* @param {AgentsClient.RequestOptions} requestOptions - Request-specific configuration.
|
|
4234
4250
|
*
|
|
4235
4251
|
* @throws {@link Vectros.BadRequestError}
|
|
4236
4252
|
* @throws {@link Vectros.ForbiddenError}
|
|
@@ -4239,17 +4255,22 @@ var AuthClient = class {
|
|
|
4239
4255
|
* @throws {@link Vectros.TooManyRequestsError}
|
|
4240
4256
|
*
|
|
4241
4257
|
* @example
|
|
4242
|
-
* await client.
|
|
4243
|
-
*
|
|
4244
|
-
*
|
|
4245
|
-
*
|
|
4246
|
-
*
|
|
4258
|
+
* await client.agents.updateAgent({
|
|
4259
|
+
* id: "id",
|
|
4260
|
+
* body: {
|
|
4261
|
+
* model: "claude-sonnet-5-5"
|
|
4262
|
+
* }
|
|
4247
4263
|
* })
|
|
4248
4264
|
*/
|
|
4249
|
-
|
|
4250
|
-
return HttpResponsePromise.fromPromise(this.
|
|
4265
|
+
updateAgent(request, requestOptions) {
|
|
4266
|
+
return HttpResponsePromise.fromPromise(this.__updateAgent(request, requestOptions));
|
|
4251
4267
|
}
|
|
4252
|
-
async
|
|
4268
|
+
async __updateAgent(request, requestOptions) {
|
|
4269
|
+
const { id, restore, contextId, body: _body } = request;
|
|
4270
|
+
const _queryParams = {
|
|
4271
|
+
restore,
|
|
4272
|
+
contextId
|
|
4273
|
+
};
|
|
4253
4274
|
const _authRequest = await this._options.authProvider.getAuthRequest();
|
|
4254
4275
|
const _headers = mergeHeaders(
|
|
4255
4276
|
_authRequest.headers,
|
|
@@ -4259,14 +4280,14 @@ var AuthClient = class {
|
|
|
4259
4280
|
const _response = await fetcher({
|
|
4260
4281
|
url: url_exports.join(
|
|
4261
4282
|
await Supplier.get(this._options.baseUrl) ?? await Supplier.get(this._options.environment),
|
|
4262
|
-
|
|
4283
|
+
`v1/agents/${url_exports.encodePathParam(id)}`
|
|
4263
4284
|
),
|
|
4264
|
-
method: "
|
|
4285
|
+
method: "PUT",
|
|
4265
4286
|
headers: _headers,
|
|
4266
4287
|
contentType: "application/json",
|
|
4267
|
-
queryString: url_exports.queryBuilder().mergeAdditional(requestOptions?.queryParams).build(),
|
|
4288
|
+
queryString: url_exports.queryBuilder().addMany(_queryParams).mergeAdditional(requestOptions?.queryParams).build(),
|
|
4268
4289
|
requestType: "json",
|
|
4269
|
-
body:
|
|
4290
|
+
body: _body,
|
|
4270
4291
|
timeoutMs: (requestOptions?.timeoutInSeconds ?? this._options?.timeoutInSeconds ?? 60) * 1e3,
|
|
4271
4292
|
maxRetries: requestOptions?.maxRetries ?? this._options?.maxRetries,
|
|
4272
4293
|
abortSignal: requestOptions?.abortSignal,
|
|
@@ -4296,27 +4317,35 @@ var AuthClient = class {
|
|
|
4296
4317
|
});
|
|
4297
4318
|
}
|
|
4298
4319
|
}
|
|
4299
|
-
return handleNonStatusCodeError(_response.error, _response.rawResponse, "
|
|
4320
|
+
return handleNonStatusCodeError(_response.error, _response.rawResponse, "PUT", "/v1/agents/{id}");
|
|
4300
4321
|
}
|
|
4301
4322
|
/**
|
|
4302
|
-
*
|
|
4323
|
+
* Retires the agent: its versions stay readable, it leaves the default listing, and it can be restored. With `?purge=true` a retired agent's content (every version's prompt, description, tools, model, limits and budget) is erased, leaving its id, name, version numbers and timestamps; its change history keeps only a one-way hash of the prompt, description and tools. If the agent is restored while a purge is running, the purge stops (409) and versions it already erased stay erased; purge it again after retiring it. With `?purge=true&version=N` one version that is not the latest is erased. The name of a purged agent stays reserved. Requires the `agents:d` scope.
|
|
4303
4324
|
*
|
|
4304
|
-
* @param {Vectros.
|
|
4305
|
-
* @param {
|
|
4325
|
+
* @param {Vectros.DeleteAgentRequest} request
|
|
4326
|
+
* @param {AgentsClient.RequestOptions} requestOptions - Request-specific configuration.
|
|
4306
4327
|
*
|
|
4328
|
+
* @throws {@link Vectros.BadRequestError}
|
|
4307
4329
|
* @throws {@link Vectros.ForbiddenError}
|
|
4308
4330
|
* @throws {@link Vectros.NotFoundError}
|
|
4331
|
+
* @throws {@link Vectros.ConflictError}
|
|
4332
|
+
* @throws {@link Vectros.TooManyRequestsError}
|
|
4309
4333
|
*
|
|
4310
4334
|
* @example
|
|
4311
|
-
* await client.
|
|
4312
|
-
*
|
|
4335
|
+
* await client.agents.deleteAgent({
|
|
4336
|
+
* id: "id"
|
|
4313
4337
|
* })
|
|
4314
4338
|
*/
|
|
4315
|
-
|
|
4316
|
-
return HttpResponsePromise.fromPromise(this.
|
|
4339
|
+
deleteAgent(request, requestOptions) {
|
|
4340
|
+
return HttpResponsePromise.fromPromise(this.__deleteAgent(request, requestOptions));
|
|
4317
4341
|
}
|
|
4318
|
-
async
|
|
4319
|
-
const {
|
|
4342
|
+
async __deleteAgent(request, requestOptions) {
|
|
4343
|
+
const { id, purge, version, contextId } = request;
|
|
4344
|
+
const _queryParams = {
|
|
4345
|
+
purge,
|
|
4346
|
+
version,
|
|
4347
|
+
contextId
|
|
4348
|
+
};
|
|
4320
4349
|
const _authRequest = await this._options.authProvider.getAuthRequest();
|
|
4321
4350
|
const _headers = mergeHeaders(
|
|
4322
4351
|
_authRequest.headers,
|
|
@@ -4326,11 +4355,11 @@ var AuthClient = class {
|
|
|
4326
4355
|
const _response = await fetcher({
|
|
4327
4356
|
url: url_exports.join(
|
|
4328
4357
|
await Supplier.get(this._options.baseUrl) ?? await Supplier.get(this._options.environment),
|
|
4329
|
-
`v1/
|
|
4358
|
+
`v1/agents/${url_exports.encodePathParam(id)}`
|
|
4330
4359
|
),
|
|
4331
|
-
method: "
|
|
4360
|
+
method: "DELETE",
|
|
4332
4361
|
headers: _headers,
|
|
4333
|
-
queryString: url_exports.queryBuilder().mergeAdditional(requestOptions?.queryParams).build(),
|
|
4362
|
+
queryString: url_exports.queryBuilder().addMany(_queryParams).mergeAdditional(requestOptions?.queryParams).build(),
|
|
4334
4363
|
timeoutMs: (requestOptions?.timeoutInSeconds ?? this._options?.timeoutInSeconds ?? 60) * 1e3,
|
|
4335
4364
|
maxRetries: requestOptions?.maxRetries ?? this._options?.maxRetries,
|
|
4336
4365
|
abortSignal: requestOptions?.abortSignal,
|
|
@@ -4342,10 +4371,16 @@ var AuthClient = class {
|
|
|
4342
4371
|
}
|
|
4343
4372
|
if (_response.error.reason === "status-code") {
|
|
4344
4373
|
switch (_response.error.statusCode) {
|
|
4374
|
+
case 400:
|
|
4375
|
+
throw new BadRequestError(_response.error.body, _response.rawResponse);
|
|
4345
4376
|
case 403:
|
|
4346
4377
|
throw new ForbiddenError(_response.error.body, _response.rawResponse);
|
|
4347
4378
|
case 404:
|
|
4348
4379
|
throw new NotFoundError(_response.error.body, _response.rawResponse);
|
|
4380
|
+
case 409:
|
|
4381
|
+
throw new ConflictError(_response.error.body, _response.rawResponse);
|
|
4382
|
+
case 429:
|
|
4383
|
+
throw new TooManyRequestsError(_response.error.body, _response.rawResponse);
|
|
4349
4384
|
default:
|
|
4350
4385
|
throw new VectrosError({
|
|
4351
4386
|
statusCode: _response.error.statusCode,
|
|
@@ -4354,28 +4389,33 @@ var AuthClient = class {
|
|
|
4354
4389
|
});
|
|
4355
4390
|
}
|
|
4356
4391
|
}
|
|
4357
|
-
return handleNonStatusCodeError(_response.error, _response.rawResponse, "
|
|
4392
|
+
return handleNonStatusCodeError(_response.error, _response.rawResponse, "DELETE", "/v1/agents/{id}");
|
|
4358
4393
|
}
|
|
4359
4394
|
/**
|
|
4360
|
-
*
|
|
4395
|
+
* Returns a page of the agent's versions, newest first: who wrote each and when, and whether it was purged. With `includeContent=true` each entry carries its full definition. Requires the `agents:r` scope.
|
|
4361
4396
|
*
|
|
4362
|
-
* @param {Vectros.
|
|
4363
|
-
* @param {
|
|
4397
|
+
* @param {Vectros.ListAgentVersionsRequest} request
|
|
4398
|
+
* @param {AgentsClient.RequestOptions} requestOptions - Request-specific configuration.
|
|
4364
4399
|
*
|
|
4365
4400
|
* @throws {@link Vectros.ForbiddenError}
|
|
4366
4401
|
* @throws {@link Vectros.NotFoundError}
|
|
4367
|
-
* @throws {@link Vectros.TooManyRequestsError}
|
|
4368
4402
|
*
|
|
4369
4403
|
* @example
|
|
4370
|
-
* await client.
|
|
4371
|
-
*
|
|
4404
|
+
* await client.agents.listAgentVersions({
|
|
4405
|
+
* id: "id"
|
|
4372
4406
|
* })
|
|
4373
4407
|
*/
|
|
4374
|
-
|
|
4375
|
-
return HttpResponsePromise.fromPromise(this.
|
|
4408
|
+
listAgentVersions(request, requestOptions) {
|
|
4409
|
+
return HttpResponsePromise.fromPromise(this.__listAgentVersions(request, requestOptions));
|
|
4376
4410
|
}
|
|
4377
|
-
async
|
|
4378
|
-
const {
|
|
4411
|
+
async __listAgentVersions(request, requestOptions) {
|
|
4412
|
+
const { id, contextId, includeContent, startFrom, limit } = request;
|
|
4413
|
+
const _queryParams = {
|
|
4414
|
+
contextId,
|
|
4415
|
+
includeContent,
|
|
4416
|
+
startFrom,
|
|
4417
|
+
limit
|
|
4418
|
+
};
|
|
4379
4419
|
const _authRequest = await this._options.authProvider.getAuthRequest();
|
|
4380
4420
|
const _headers = mergeHeaders(
|
|
4381
4421
|
_authRequest.headers,
|
|
@@ -4385,11 +4425,11 @@ var AuthClient = class {
|
|
|
4385
4425
|
const _response = await fetcher({
|
|
4386
4426
|
url: url_exports.join(
|
|
4387
4427
|
await Supplier.get(this._options.baseUrl) ?? await Supplier.get(this._options.environment),
|
|
4388
|
-
`v1/
|
|
4428
|
+
`v1/agents/${url_exports.encodePathParam(id)}/versions`
|
|
4389
4429
|
),
|
|
4390
|
-
method: "
|
|
4430
|
+
method: "GET",
|
|
4391
4431
|
headers: _headers,
|
|
4392
|
-
queryString: url_exports.queryBuilder().mergeAdditional(requestOptions?.queryParams).build(),
|
|
4432
|
+
queryString: url_exports.queryBuilder().addMany(_queryParams).mergeAdditional(requestOptions?.queryParams).build(),
|
|
4393
4433
|
timeoutMs: (requestOptions?.timeoutInSeconds ?? this._options?.timeoutInSeconds ?? 60) * 1e3,
|
|
4394
4434
|
maxRetries: requestOptions?.maxRetries ?? this._options?.maxRetries,
|
|
4395
4435
|
abortSignal: requestOptions?.abortSignal,
|
|
@@ -4397,7 +4437,7 @@ var AuthClient = class {
|
|
|
4397
4437
|
logging: this._options.logging
|
|
4398
4438
|
});
|
|
4399
4439
|
if (_response.ok) {
|
|
4400
|
-
return { data:
|
|
4440
|
+
return { data: _response.body, rawResponse: _response.rawResponse };
|
|
4401
4441
|
}
|
|
4402
4442
|
if (_response.error.reason === "status-code") {
|
|
4403
4443
|
switch (_response.error.statusCode) {
|
|
@@ -4405,8 +4445,6 @@ var AuthClient = class {
|
|
|
4405
4445
|
throw new ForbiddenError(_response.error.body, _response.rawResponse);
|
|
4406
4446
|
case 404:
|
|
4407
4447
|
throw new NotFoundError(_response.error.body, _response.rawResponse);
|
|
4408
|
-
case 429:
|
|
4409
|
-
throw new TooManyRequestsError(_response.error.body, _response.rawResponse);
|
|
4410
4448
|
default:
|
|
4411
4449
|
throw new VectrosError({
|
|
4412
4450
|
statusCode: _response.error.statusCode,
|
|
@@ -4415,45 +4453,32 @@ var AuthClient = class {
|
|
|
4415
4453
|
});
|
|
4416
4454
|
}
|
|
4417
4455
|
}
|
|
4418
|
-
return handleNonStatusCodeError(
|
|
4419
|
-
_response.error,
|
|
4420
|
-
_response.rawResponse,
|
|
4421
|
-
"DELETE",
|
|
4422
|
-
"/v1/admin/keys/scoped/{keyId}"
|
|
4423
|
-
);
|
|
4456
|
+
return handleNonStatusCodeError(_response.error, _response.rawResponse, "GET", "/v1/agents/{id}/versions");
|
|
4424
4457
|
}
|
|
4425
4458
|
/**
|
|
4426
|
-
*
|
|
4459
|
+
* Stops a run. The answer is `202` with the run still `RUNNING`: read it with `GET /v1/runs/{id}` to see it end. A model call in progress is cut within a few seconds; a tool call in progress is first allowed to finish, for up to about 30 seconds, so its change is recorded and billed, and the run stops as soon as it has. The run then ends `CANCELLED` with `errorCode` `CANCELLED_BY_CALLER`, unless it had already completed, in which case it stays `COMPLETED`. Its reservation is settled once, like any run's: a model call in progress when it stopped is charged up to its maximum. A run that has ended (or that had not started, which this cancel ends) answers `200` with its final state, so a cancel can always be retried. A run can be cancelled by the credential that started it (for a token bound to a user, by that user) and by your root key. Requires the `runs:c` scope; with `runs:r` as well the answer is the whole run, otherwise only its `id`, `status`, `terminal`, `reason` and `errorCode`. Takes no body.
|
|
4427
4460
|
*
|
|
4428
|
-
* @param {Vectros.
|
|
4429
|
-
* @param {
|
|
4461
|
+
* @param {Vectros.CancelRunRequest} request
|
|
4462
|
+
* @param {AgentsClient.RequestOptions} requestOptions - Request-specific configuration.
|
|
4430
4463
|
*
|
|
4431
4464
|
* @throws {@link Vectros.BadRequestError}
|
|
4465
|
+
* @throws {@link Vectros.UnauthorizedError}
|
|
4432
4466
|
* @throws {@link Vectros.ForbiddenError}
|
|
4467
|
+
* @throws {@link Vectros.NotFoundError}
|
|
4468
|
+
* @throws {@link Vectros.TooManyRequestsError}
|
|
4433
4469
|
*
|
|
4434
4470
|
* @example
|
|
4435
|
-
* await client.
|
|
4436
|
-
*
|
|
4437
|
-
* resource: "documents",
|
|
4438
|
-
* method: "POST",
|
|
4439
|
-
* keyId: "key_abc123",
|
|
4440
|
-
* contextId: "ctx_intake"
|
|
4471
|
+
* await client.agents.cancelRun({
|
|
4472
|
+
* id: "id"
|
|
4441
4473
|
* })
|
|
4442
4474
|
*/
|
|
4443
|
-
|
|
4444
|
-
return HttpResponsePromise.fromPromise(this.
|
|
4475
|
+
cancelRun(request, requestOptions) {
|
|
4476
|
+
return HttpResponsePromise.fromPromise(this.__cancelRun(request, requestOptions));
|
|
4445
4477
|
}
|
|
4446
|
-
async
|
|
4447
|
-
const {
|
|
4478
|
+
async __cancelRun(request, requestOptions) {
|
|
4479
|
+
const { id, contextId } = request;
|
|
4448
4480
|
const _queryParams = {
|
|
4449
|
-
|
|
4450
|
-
endTime,
|
|
4451
|
-
resource,
|
|
4452
|
-
method,
|
|
4453
|
-
keyId,
|
|
4454
|
-
contextId,
|
|
4455
|
-
errorsOnly,
|
|
4456
|
-
limit
|
|
4481
|
+
contextId
|
|
4457
4482
|
};
|
|
4458
4483
|
const _authRequest = await this._options.authProvider.getAuthRequest();
|
|
4459
4484
|
const _headers = mergeHeaders(
|
|
@@ -4464,9 +4489,9 @@ var AuthClient = class {
|
|
|
4464
4489
|
const _response = await fetcher({
|
|
4465
4490
|
url: url_exports.join(
|
|
4466
4491
|
await Supplier.get(this._options.baseUrl) ?? await Supplier.get(this._options.environment),
|
|
4467
|
-
|
|
4492
|
+
`v1/runs/${url_exports.encodePathParam(id)}/cancel`
|
|
4468
4493
|
),
|
|
4469
|
-
method: "
|
|
4494
|
+
method: "POST",
|
|
4470
4495
|
headers: _headers,
|
|
4471
4496
|
queryString: url_exports.queryBuilder().addMany(_queryParams).mergeAdditional(requestOptions?.queryParams).build(),
|
|
4472
4497
|
timeoutMs: (requestOptions?.timeoutInSeconds ?? this._options?.timeoutInSeconds ?? 60) * 1e3,
|
|
@@ -4482,8 +4507,14 @@ var AuthClient = class {
|
|
|
4482
4507
|
switch (_response.error.statusCode) {
|
|
4483
4508
|
case 400:
|
|
4484
4509
|
throw new BadRequestError(_response.error.body, _response.rawResponse);
|
|
4510
|
+
case 401:
|
|
4511
|
+
throw new UnauthorizedError(_response.error.body, _response.rawResponse);
|
|
4485
4512
|
case 403:
|
|
4486
4513
|
throw new ForbiddenError(_response.error.body, _response.rawResponse);
|
|
4514
|
+
case 404:
|
|
4515
|
+
throw new NotFoundError(_response.error.body, _response.rawResponse);
|
|
4516
|
+
case 429:
|
|
4517
|
+
throw new TooManyRequestsError(_response.error.body, _response.rawResponse);
|
|
4487
4518
|
default:
|
|
4488
4519
|
throw new VectrosError({
|
|
4489
4520
|
statusCode: _response.error.statusCode,
|
|
@@ -4492,30 +4523,31 @@ var AuthClient = class {
|
|
|
4492
4523
|
});
|
|
4493
4524
|
}
|
|
4494
4525
|
}
|
|
4495
|
-
return handleNonStatusCodeError(_response.error, _response.rawResponse, "
|
|
4526
|
+
return handleNonStatusCodeError(_response.error, _response.rawResponse, "POST", "/v1/runs/{id}/cancel");
|
|
4496
4527
|
}
|
|
4497
4528
|
/**
|
|
4498
|
-
* Returns the
|
|
4529
|
+
* Returns the status and metadata of one run: the agent version that ran, how it ended, its tool calls (never their arguments or results) and what it was charged. It never returns text: the answer is delivered only in the run's stream. A run is visible to the credential that started it (for a token bound to a user, to that user) and to your root key, and is kept for 30 days after it ends. Requires the `runs:r` scope.
|
|
4499
4530
|
*
|
|
4500
|
-
* @param {Vectros.
|
|
4501
|
-
* @param {
|
|
4531
|
+
* @param {Vectros.GetRunRequest} request
|
|
4532
|
+
* @param {AgentsClient.RequestOptions} requestOptions - Request-specific configuration.
|
|
4502
4533
|
*
|
|
4534
|
+
* @throws {@link Vectros.BadRequestError}
|
|
4535
|
+
* @throws {@link Vectros.UnauthorizedError}
|
|
4503
4536
|
* @throws {@link Vectros.ForbiddenError}
|
|
4504
4537
|
* @throws {@link Vectros.NotFoundError}
|
|
4505
4538
|
*
|
|
4506
4539
|
* @example
|
|
4507
|
-
* await client.
|
|
4508
|
-
*
|
|
4540
|
+
* await client.agents.getRun({
|
|
4541
|
+
* id: "id"
|
|
4509
4542
|
* })
|
|
4510
4543
|
*/
|
|
4511
|
-
|
|
4512
|
-
return HttpResponsePromise.fromPromise(this.
|
|
4544
|
+
getRun(request, requestOptions) {
|
|
4545
|
+
return HttpResponsePromise.fromPromise(this.__getRun(request, requestOptions));
|
|
4513
4546
|
}
|
|
4514
|
-
async
|
|
4515
|
-
const {
|
|
4547
|
+
async __getRun(request, requestOptions) {
|
|
4548
|
+
const { id, contextId } = request;
|
|
4516
4549
|
const _queryParams = {
|
|
4517
|
-
|
|
4518
|
-
limit
|
|
4550
|
+
contextId
|
|
4519
4551
|
};
|
|
4520
4552
|
const _authRequest = await this._options.authProvider.getAuthRequest();
|
|
4521
4553
|
const _headers = mergeHeaders(
|
|
@@ -4526,7 +4558,7 @@ var AuthClient = class {
|
|
|
4526
4558
|
const _response = await fetcher({
|
|
4527
4559
|
url: url_exports.join(
|
|
4528
4560
|
await Supplier.get(this._options.baseUrl) ?? await Supplier.get(this._options.environment),
|
|
4529
|
-
`v1/
|
|
4561
|
+
`v1/runs/${url_exports.encodePathParam(id)}`
|
|
4530
4562
|
),
|
|
4531
4563
|
method: "GET",
|
|
4532
4564
|
headers: _headers,
|
|
@@ -4542,6 +4574,10 @@ var AuthClient = class {
|
|
|
4542
4574
|
}
|
|
4543
4575
|
if (_response.error.reason === "status-code") {
|
|
4544
4576
|
switch (_response.error.statusCode) {
|
|
4577
|
+
case 400:
|
|
4578
|
+
throw new BadRequestError(_response.error.body, _response.rawResponse);
|
|
4579
|
+
case 401:
|
|
4580
|
+
throw new UnauthorizedError(_response.error.body, _response.rawResponse);
|
|
4545
4581
|
case 403:
|
|
4546
4582
|
throw new ForbiddenError(_response.error.body, _response.rawResponse);
|
|
4547
4583
|
case 404:
|
|
@@ -4554,31 +4590,635 @@ var AuthClient = class {
|
|
|
4554
4590
|
});
|
|
4555
4591
|
}
|
|
4556
4592
|
}
|
|
4557
|
-
return handleNonStatusCodeError(
|
|
4558
|
-
_response.error,
|
|
4559
|
-
_response.rawResponse,
|
|
4560
|
-
"GET",
|
|
4561
|
-
"/v1/app-contexts/{contextId}/profiles"
|
|
4562
|
-
);
|
|
4593
|
+
return handleNonStatusCodeError(_response.error, _response.rawResponse, "GET", "/v1/runs/{id}");
|
|
4563
4594
|
}
|
|
4564
4595
|
/**
|
|
4565
|
-
*
|
|
4566
|
-
|
|
4567
|
-
|
|
4568
|
-
|
|
4569
|
-
|
|
4570
|
-
|
|
4571
|
-
|
|
4572
|
-
|
|
4573
|
-
|
|
4574
|
-
|
|
4575
|
-
|
|
4576
|
-
|
|
4596
|
+
* Starts a run of a stored agent and streams what it does as Server-Sent Events. The response is always `text/event-stream`, whatever `Accept` you send; its `X-Vectros-Run-Id` header names the run. The run's tool calls act as you, with your own permissions, and only through the tools the agent lists. On a prepaid account the run reserves its budget from your inference balance when it starts and returns what its model calls did not use when it ends; a fixed fee per run, tool calls and run time are billed separately, against your plan. Read each event's `type`; ignore types and fields you do not know; stop at the event with `terminal: true`. A stream that ends without one: read the run with `GET /v1/runs/{id}`. Closing the stream stops the run only on a best-effort basis. Requires the `runs:c` scope, and agent runs switched on for your tenant by an account owner.
|
|
4597
|
+
*/
|
|
4598
|
+
createRun(request, requestOptions) {
|
|
4599
|
+
return HttpResponsePromise.fromPromise(this.__createRun(request, requestOptions));
|
|
4600
|
+
}
|
|
4601
|
+
async __createRun(request, requestOptions) {
|
|
4602
|
+
const { "Idempotency-Key": idempotencyKey, ..._body } = request;
|
|
4603
|
+
const _authRequest = await this._options.authProvider.getAuthRequest();
|
|
4604
|
+
const _headers = mergeHeaders(
|
|
4605
|
+
_authRequest.headers,
|
|
4606
|
+
this._options?.headers,
|
|
4607
|
+
mergeOnlyDefinedHeaders({ "Idempotency-Key": idempotencyKey }),
|
|
4608
|
+
requestOptions?.headers
|
|
4609
|
+
);
|
|
4610
|
+
const _response = await fetcher({
|
|
4611
|
+
url: url_exports.join(
|
|
4612
|
+
await Supplier.get(this._options.baseUrl) ?? await Supplier.get(this._options.environment),
|
|
4613
|
+
"v1/runs"
|
|
4614
|
+
),
|
|
4615
|
+
method: "POST",
|
|
4616
|
+
headers: _headers,
|
|
4617
|
+
contentType: "application/json",
|
|
4618
|
+
queryString: url_exports.queryBuilder().mergeAdditional(requestOptions?.queryParams).build(),
|
|
4619
|
+
requestType: "json",
|
|
4620
|
+
body: _body,
|
|
4621
|
+
responseType: "sse",
|
|
4622
|
+
timeoutMs: (requestOptions?.timeoutInSeconds ?? this._options?.timeoutInSeconds ?? 60) * 1e3,
|
|
4623
|
+
maxRetries: requestOptions?.maxRetries ?? this._options?.maxRetries,
|
|
4624
|
+
abortSignal: requestOptions?.abortSignal,
|
|
4625
|
+
fetchFn: this._options?.fetch,
|
|
4626
|
+
logging: this._options.logging
|
|
4627
|
+
});
|
|
4628
|
+
if (_response.ok) {
|
|
4629
|
+
return {
|
|
4630
|
+
data: new Stream({
|
|
4631
|
+
stream: _response.body,
|
|
4632
|
+
parse: (data) => data,
|
|
4633
|
+
signal: requestOptions?.abortSignal,
|
|
4634
|
+
eventShape: {
|
|
4635
|
+
type: "sse"
|
|
4636
|
+
}
|
|
4637
|
+
}),
|
|
4638
|
+
rawResponse: _response.rawResponse
|
|
4639
|
+
};
|
|
4640
|
+
}
|
|
4641
|
+
if (_response.error.reason === "status-code") {
|
|
4642
|
+
switch (_response.error.statusCode) {
|
|
4643
|
+
case 400:
|
|
4644
|
+
throw new BadRequestError(_response.error.body, _response.rawResponse);
|
|
4645
|
+
case 401:
|
|
4646
|
+
throw new UnauthorizedError(_response.error.body, _response.rawResponse);
|
|
4647
|
+
case 402:
|
|
4648
|
+
throw new PaymentRequiredError(_response.error.body, _response.rawResponse);
|
|
4649
|
+
case 403:
|
|
4650
|
+
throw new ForbiddenError(_response.error.body, _response.rawResponse);
|
|
4651
|
+
case 404:
|
|
4652
|
+
throw new NotFoundError(_response.error.body, _response.rawResponse);
|
|
4653
|
+
case 409:
|
|
4654
|
+
throw new ConflictError(_response.error.body, _response.rawResponse);
|
|
4655
|
+
case 413:
|
|
4656
|
+
throw new ContentTooLargeError(_response.error.body, _response.rawResponse);
|
|
4657
|
+
case 422:
|
|
4658
|
+
throw new UnprocessableEntityError(_response.error.body, _response.rawResponse);
|
|
4659
|
+
case 429:
|
|
4660
|
+
throw new TooManyRequestsError(_response.error.body, _response.rawResponse);
|
|
4661
|
+
case 503:
|
|
4662
|
+
throw new ServiceUnavailableError(_response.error.body, _response.rawResponse);
|
|
4663
|
+
default:
|
|
4664
|
+
throw new VectrosError({
|
|
4665
|
+
statusCode: _response.error.statusCode,
|
|
4666
|
+
body: _response.error.body,
|
|
4667
|
+
rawResponse: _response.rawResponse
|
|
4668
|
+
});
|
|
4669
|
+
}
|
|
4670
|
+
}
|
|
4671
|
+
return handleNonStatusCodeError(_response.error, _response.rawResponse, "POST", "/v1/runs");
|
|
4672
|
+
}
|
|
4673
|
+
};
|
|
4674
|
+
var AuthClient = class {
|
|
4675
|
+
constructor(options) {
|
|
4676
|
+
this._options = normalizeClientOptionsWithAuth(options);
|
|
4677
|
+
}
|
|
4678
|
+
/**
|
|
4679
|
+
* Returns the platform's JWT signing public key in RFC 7517 JWKS format. Use it with any JWKS-aware JWT library to verify `inv_*` invite tokens, `st_*` scoped tokens, and other platform-signed tokens locally, without calling back to the API for each verification. The response carries a one-hour `Cache-Control`, so cache it and re-fetch roughly hourly rather than on every verification. The `kid` value changes when the key rotates; re-fetch this document whenever you encounter a token signed with an unknown `kid`.
|
|
4680
|
+
*
|
|
4681
|
+
* @param {AuthClient.RequestOptions} requestOptions - Request-specific configuration.
|
|
4682
|
+
*
|
|
4683
|
+
* @throws {@link Vectros.InternalServerError}
|
|
4684
|
+
*
|
|
4685
|
+
* @example
|
|
4686
|
+
* await client.auth.getJwks()
|
|
4687
|
+
*/
|
|
4688
|
+
getJwks(requestOptions) {
|
|
4689
|
+
return HttpResponsePromise.fromPromise(this.__getJwks(requestOptions));
|
|
4690
|
+
}
|
|
4691
|
+
async __getJwks(requestOptions) {
|
|
4692
|
+
const _authRequest = await this._options.authProvider.getAuthRequest();
|
|
4693
|
+
const _headers = mergeHeaders(
|
|
4694
|
+
_authRequest.headers,
|
|
4695
|
+
this._options?.headers,
|
|
4696
|
+
requestOptions?.headers
|
|
4697
|
+
);
|
|
4698
|
+
const _response = await fetcher({
|
|
4699
|
+
url: url_exports.join(
|
|
4700
|
+
await Supplier.get(this._options.baseUrl) ?? await Supplier.get(this._options.environment),
|
|
4701
|
+
"v1/auth/jwks"
|
|
4702
|
+
),
|
|
4703
|
+
method: "GET",
|
|
4704
|
+
headers: _headers,
|
|
4705
|
+
queryString: url_exports.queryBuilder().mergeAdditional(requestOptions?.queryParams).build(),
|
|
4706
|
+
timeoutMs: (requestOptions?.timeoutInSeconds ?? this._options?.timeoutInSeconds ?? 60) * 1e3,
|
|
4707
|
+
maxRetries: requestOptions?.maxRetries ?? this._options?.maxRetries,
|
|
4708
|
+
abortSignal: requestOptions?.abortSignal,
|
|
4709
|
+
fetchFn: this._options?.fetch,
|
|
4710
|
+
logging: this._options.logging
|
|
4711
|
+
});
|
|
4712
|
+
if (_response.ok) {
|
|
4713
|
+
return { data: _response.body, rawResponse: _response.rawResponse };
|
|
4714
|
+
}
|
|
4715
|
+
if (_response.error.reason === "status-code") {
|
|
4716
|
+
switch (_response.error.statusCode) {
|
|
4717
|
+
case 500:
|
|
4718
|
+
throw new InternalServerError(_response.error.body, _response.rawResponse);
|
|
4719
|
+
default:
|
|
4720
|
+
throw new VectrosError({
|
|
4721
|
+
statusCode: _response.error.statusCode,
|
|
4722
|
+
body: _response.error.body,
|
|
4723
|
+
rawResponse: _response.rawResponse
|
|
4724
|
+
});
|
|
4725
|
+
}
|
|
4726
|
+
}
|
|
4727
|
+
return handleNonStatusCodeError(_response.error, _response.rawResponse, "GET", "/v1/auth/jwks");
|
|
4728
|
+
}
|
|
4729
|
+
/**
|
|
4730
|
+
* Returns a page of per-subject PHI read-access rows: who read which subject's PHI, when, against which record, and whether any sensitive value was actually revealed in plaintext. Metadata only — never the PHI itself. Rows exist only where read-access logging is enabled. Provide at least one query axis: a subject (`subjectType` + `subjectId`) within a `contextId` for the primary accounting query; `resourceId` within a `contextId` for 'who read this record'; `callerKeyId` for 'what did this credential read' (account-wide forensic); or `contextId` alone to enumerate a whole context. `from`/`to` bound the time window. Results are scoped to your account, derived from your token — never from input. Requires the `access-log:r` scope.
|
|
4731
|
+
*
|
|
4732
|
+
* @param {Vectros.GetAccessLogRequest} request
|
|
4733
|
+
* @param {AuthClient.RequestOptions} requestOptions - Request-specific configuration.
|
|
4734
|
+
*
|
|
4735
|
+
* @throws {@link Vectros.BadRequestError}
|
|
4736
|
+
* @throws {@link Vectros.ForbiddenError}
|
|
4737
|
+
*
|
|
4738
|
+
* @example
|
|
4739
|
+
* await client.auth.getAccessLog({
|
|
4740
|
+
* subjectType: "user",
|
|
4741
|
+
* subjectId: "user_abc123",
|
|
4742
|
+
* contextId: "ctx_intake",
|
|
4743
|
+
* action: "read",
|
|
4744
|
+
* callerKeyId: "key_abc123",
|
|
4745
|
+
* resourceType: "intake_form",
|
|
4746
|
+
* resourceId: "rec_456"
|
|
4747
|
+
* })
|
|
4748
|
+
*/
|
|
4749
|
+
getAccessLog(request = {}, requestOptions) {
|
|
4750
|
+
return HttpResponsePromise.fromPromise(this.__getAccessLog(request, requestOptions));
|
|
4751
|
+
}
|
|
4752
|
+
async __getAccessLog(request = {}, requestOptions) {
|
|
4753
|
+
const {
|
|
4754
|
+
subjectType,
|
|
4755
|
+
subjectId,
|
|
4756
|
+
contextId,
|
|
4757
|
+
from: from_,
|
|
4758
|
+
to,
|
|
4759
|
+
action,
|
|
4760
|
+
callerKeyId,
|
|
4761
|
+
resourceType,
|
|
4762
|
+
resourceId,
|
|
4763
|
+
revealedSensitive,
|
|
4764
|
+
startFrom,
|
|
4765
|
+
limit
|
|
4766
|
+
} = request;
|
|
4767
|
+
const _queryParams = {
|
|
4768
|
+
subjectType,
|
|
4769
|
+
subjectId,
|
|
4770
|
+
contextId,
|
|
4771
|
+
from: from_,
|
|
4772
|
+
to,
|
|
4773
|
+
action,
|
|
4774
|
+
callerKeyId,
|
|
4775
|
+
resourceType,
|
|
4776
|
+
resourceId,
|
|
4777
|
+
revealedSensitive,
|
|
4778
|
+
startFrom,
|
|
4779
|
+
limit
|
|
4780
|
+
};
|
|
4781
|
+
const _authRequest = await this._options.authProvider.getAuthRequest();
|
|
4782
|
+
const _headers = mergeHeaders(
|
|
4783
|
+
_authRequest.headers,
|
|
4784
|
+
this._options?.headers,
|
|
4785
|
+
requestOptions?.headers
|
|
4786
|
+
);
|
|
4787
|
+
const _response = await fetcher({
|
|
4788
|
+
url: url_exports.join(
|
|
4789
|
+
await Supplier.get(this._options.baseUrl) ?? await Supplier.get(this._options.environment),
|
|
4790
|
+
"v1/admin/access-log"
|
|
4791
|
+
),
|
|
4792
|
+
method: "GET",
|
|
4793
|
+
headers: _headers,
|
|
4794
|
+
queryString: url_exports.queryBuilder().addMany(_queryParams).mergeAdditional(requestOptions?.queryParams).build(),
|
|
4795
|
+
timeoutMs: (requestOptions?.timeoutInSeconds ?? this._options?.timeoutInSeconds ?? 60) * 1e3,
|
|
4796
|
+
maxRetries: requestOptions?.maxRetries ?? this._options?.maxRetries,
|
|
4797
|
+
abortSignal: requestOptions?.abortSignal,
|
|
4798
|
+
fetchFn: this._options?.fetch,
|
|
4799
|
+
logging: this._options.logging
|
|
4800
|
+
});
|
|
4801
|
+
if (_response.ok) {
|
|
4802
|
+
return { data: _response.body, rawResponse: _response.rawResponse };
|
|
4803
|
+
}
|
|
4804
|
+
if (_response.error.reason === "status-code") {
|
|
4805
|
+
switch (_response.error.statusCode) {
|
|
4806
|
+
case 400:
|
|
4807
|
+
throw new BadRequestError(_response.error.body, _response.rawResponse);
|
|
4808
|
+
case 403:
|
|
4809
|
+
throw new ForbiddenError(_response.error.body, _response.rawResponse);
|
|
4810
|
+
default:
|
|
4811
|
+
throw new VectrosError({
|
|
4812
|
+
statusCode: _response.error.statusCode,
|
|
4813
|
+
body: _response.error.body,
|
|
4814
|
+
rawResponse: _response.rawResponse
|
|
4815
|
+
});
|
|
4816
|
+
}
|
|
4817
|
+
}
|
|
4818
|
+
return handleNonStatusCodeError(_response.error, _response.rawResponse, "GET", "/v1/admin/access-log");
|
|
4819
|
+
}
|
|
4820
|
+
/**
|
|
4821
|
+
* Lists your scoped API keys (`ssk_*`) in your credential's own environment — a live key lists live keys, a test key lists test keys. Revoked keys are excluded. Requires the `keys:r` scope.
|
|
4822
|
+
*
|
|
4823
|
+
* @param {AuthClient.RequestOptions} requestOptions - Request-specific configuration.
|
|
4824
|
+
*
|
|
4825
|
+
* @throws {@link Vectros.ForbiddenError}
|
|
4826
|
+
*
|
|
4827
|
+
* @example
|
|
4828
|
+
* await client.auth.listScopedKeys()
|
|
4829
|
+
*/
|
|
4830
|
+
listScopedKeys(requestOptions) {
|
|
4831
|
+
return HttpResponsePromise.fromPromise(this.__listScopedKeys(requestOptions));
|
|
4832
|
+
}
|
|
4833
|
+
async __listScopedKeys(requestOptions) {
|
|
4834
|
+
const _authRequest = await this._options.authProvider.getAuthRequest();
|
|
4835
|
+
const _headers = mergeHeaders(
|
|
4836
|
+
_authRequest.headers,
|
|
4837
|
+
this._options?.headers,
|
|
4838
|
+
requestOptions?.headers
|
|
4839
|
+
);
|
|
4840
|
+
const _response = await fetcher({
|
|
4841
|
+
url: url_exports.join(
|
|
4842
|
+
await Supplier.get(this._options.baseUrl) ?? await Supplier.get(this._options.environment),
|
|
4843
|
+
"v1/admin/keys/scoped"
|
|
4844
|
+
),
|
|
4845
|
+
method: "GET",
|
|
4846
|
+
headers: _headers,
|
|
4847
|
+
queryString: url_exports.queryBuilder().mergeAdditional(requestOptions?.queryParams).build(),
|
|
4848
|
+
timeoutMs: (requestOptions?.timeoutInSeconds ?? this._options?.timeoutInSeconds ?? 60) * 1e3,
|
|
4849
|
+
maxRetries: requestOptions?.maxRetries ?? this._options?.maxRetries,
|
|
4850
|
+
abortSignal: requestOptions?.abortSignal,
|
|
4851
|
+
fetchFn: this._options?.fetch,
|
|
4852
|
+
logging: this._options.logging
|
|
4853
|
+
});
|
|
4854
|
+
if (_response.ok) {
|
|
4855
|
+
return { data: _response.body, rawResponse: _response.rawResponse };
|
|
4856
|
+
}
|
|
4857
|
+
if (_response.error.reason === "status-code") {
|
|
4858
|
+
switch (_response.error.statusCode) {
|
|
4859
|
+
case 403:
|
|
4860
|
+
throw new ForbiddenError(_response.error.body, _response.rawResponse);
|
|
4861
|
+
default:
|
|
4862
|
+
throw new VectrosError({
|
|
4863
|
+
statusCode: _response.error.statusCode,
|
|
4864
|
+
body: _response.error.body,
|
|
4865
|
+
rawResponse: _response.rawResponse
|
|
4866
|
+
});
|
|
4867
|
+
}
|
|
4868
|
+
}
|
|
4869
|
+
return handleNonStatusCodeError(_response.error, _response.rawResponse, "GET", "/v1/admin/keys/scoped");
|
|
4870
|
+
}
|
|
4871
|
+
/**
|
|
4872
|
+
* Creates a scoped API key (an `ssk_*` secret) that inherits its permissions from an existing access profile in your account. The call is idempotent on the combination of tenant, context, user, and key name: re-issuing the same request returns the existing key WITHOUT re-disclosing its raw secret. The raw key is returned ONLY in this response — store it securely, as it cannot be retrieved again. Requires the `keys:c` scope. If you use a scoped credential, `keys:c` alone is not sufficient: because the minted key is durably bound to the profile you name, the profile's effective scopes may not exceed your own, and you may only mint against a profile whose `identityOverrides` values your own identity holds. Minting a key bound to your OWN principal needs nothing further; minting one bound to a DIFFERENT principal additionally requires the `delegate-mint` capability (`granted_capabilities`) on your credential — without it the request is refused. A root API key (`sk_`) is exempt from all three bounds. The profile you name must be `active` AND the user you name must not be `SUSPENDED`: either is refused with `409`, and no root exemption applies to that. Both refusals stop ISSUANCE only. Suspending the profile or the user additionally stops credentials already bound to it, within about five minutes. A `PENDING` user is fine.
|
|
4873
|
+
*
|
|
4874
|
+
* @param {Vectros.CreateScopedKeyRequest} request
|
|
4875
|
+
* @param {AuthClient.RequestOptions} requestOptions - Request-specific configuration.
|
|
4876
|
+
*
|
|
4877
|
+
* @throws {@link Vectros.BadRequestError}
|
|
4878
|
+
* @throws {@link Vectros.ForbiddenError}
|
|
4879
|
+
* @throws {@link Vectros.NotFoundError}
|
|
4880
|
+
* @throws {@link Vectros.ConflictError}
|
|
4881
|
+
* @throws {@link Vectros.TooManyRequestsError}
|
|
4882
|
+
*
|
|
4883
|
+
* @example
|
|
4884
|
+
* await client.auth.createScopedKey({
|
|
4885
|
+
* keyName: "research-bot production",
|
|
4886
|
+
* tenantId: "ten_live_xxx",
|
|
4887
|
+
* contextId: "myapp",
|
|
4888
|
+
* userId: "alice-001"
|
|
4889
|
+
* })
|
|
4890
|
+
*/
|
|
4891
|
+
createScopedKey(request, requestOptions) {
|
|
4892
|
+
return HttpResponsePromise.fromPromise(this.__createScopedKey(request, requestOptions));
|
|
4893
|
+
}
|
|
4894
|
+
async __createScopedKey(request, requestOptions) {
|
|
4895
|
+
const _authRequest = await this._options.authProvider.getAuthRequest();
|
|
4896
|
+
const _headers = mergeHeaders(
|
|
4897
|
+
_authRequest.headers,
|
|
4898
|
+
this._options?.headers,
|
|
4899
|
+
requestOptions?.headers
|
|
4900
|
+
);
|
|
4901
|
+
const _response = await fetcher({
|
|
4902
|
+
url: url_exports.join(
|
|
4903
|
+
await Supplier.get(this._options.baseUrl) ?? await Supplier.get(this._options.environment),
|
|
4904
|
+
"v1/admin/keys/scoped"
|
|
4905
|
+
),
|
|
4906
|
+
method: "POST",
|
|
4907
|
+
headers: _headers,
|
|
4908
|
+
contentType: "application/json",
|
|
4909
|
+
queryString: url_exports.queryBuilder().mergeAdditional(requestOptions?.queryParams).build(),
|
|
4910
|
+
requestType: "json",
|
|
4911
|
+
body: request,
|
|
4912
|
+
timeoutMs: (requestOptions?.timeoutInSeconds ?? this._options?.timeoutInSeconds ?? 60) * 1e3,
|
|
4913
|
+
maxRetries: requestOptions?.maxRetries ?? this._options?.maxRetries,
|
|
4914
|
+
abortSignal: requestOptions?.abortSignal,
|
|
4915
|
+
fetchFn: this._options?.fetch,
|
|
4916
|
+
logging: this._options.logging
|
|
4917
|
+
});
|
|
4918
|
+
if (_response.ok) {
|
|
4919
|
+
return { data: _response.body, rawResponse: _response.rawResponse };
|
|
4920
|
+
}
|
|
4921
|
+
if (_response.error.reason === "status-code") {
|
|
4922
|
+
switch (_response.error.statusCode) {
|
|
4923
|
+
case 400:
|
|
4924
|
+
throw new BadRequestError(_response.error.body, _response.rawResponse);
|
|
4925
|
+
case 403:
|
|
4926
|
+
throw new ForbiddenError(_response.error.body, _response.rawResponse);
|
|
4927
|
+
case 404:
|
|
4928
|
+
throw new NotFoundError(_response.error.body, _response.rawResponse);
|
|
4929
|
+
case 409:
|
|
4930
|
+
throw new ConflictError(_response.error.body, _response.rawResponse);
|
|
4931
|
+
case 429:
|
|
4932
|
+
throw new TooManyRequestsError(_response.error.body, _response.rawResponse);
|
|
4933
|
+
default:
|
|
4934
|
+
throw new VectrosError({
|
|
4935
|
+
statusCode: _response.error.statusCode,
|
|
4936
|
+
body: _response.error.body,
|
|
4937
|
+
rawResponse: _response.rawResponse
|
|
4938
|
+
});
|
|
4939
|
+
}
|
|
4940
|
+
}
|
|
4941
|
+
return handleNonStatusCodeError(_response.error, _response.rawResponse, "POST", "/v1/admin/keys/scoped");
|
|
4942
|
+
}
|
|
4943
|
+
/**
|
|
4944
|
+
* Returns the metadata for a single scoped API key. The raw secret is NOT included — it is only ever returned once, when the key is first created. Requires the `keys:r` scope.
|
|
4945
|
+
*
|
|
4946
|
+
* @param {Vectros.GetScopedKeyRequest} request
|
|
4947
|
+
* @param {AuthClient.RequestOptions} requestOptions - Request-specific configuration.
|
|
4948
|
+
*
|
|
4949
|
+
* @throws {@link Vectros.ForbiddenError}
|
|
4950
|
+
* @throws {@link Vectros.NotFoundError}
|
|
4951
|
+
*
|
|
4952
|
+
* @example
|
|
4953
|
+
* await client.auth.getScopedKey({
|
|
4954
|
+
* keyId: "keyId"
|
|
4955
|
+
* })
|
|
4956
|
+
*/
|
|
4957
|
+
getScopedKey(request, requestOptions) {
|
|
4958
|
+
return HttpResponsePromise.fromPromise(this.__getScopedKey(request, requestOptions));
|
|
4959
|
+
}
|
|
4960
|
+
async __getScopedKey(request, requestOptions) {
|
|
4961
|
+
const { keyId } = request;
|
|
4962
|
+
const _authRequest = await this._options.authProvider.getAuthRequest();
|
|
4963
|
+
const _headers = mergeHeaders(
|
|
4964
|
+
_authRequest.headers,
|
|
4965
|
+
this._options?.headers,
|
|
4966
|
+
requestOptions?.headers
|
|
4967
|
+
);
|
|
4968
|
+
const _response = await fetcher({
|
|
4969
|
+
url: url_exports.join(
|
|
4970
|
+
await Supplier.get(this._options.baseUrl) ?? await Supplier.get(this._options.environment),
|
|
4971
|
+
`v1/admin/keys/scoped/${url_exports.encodePathParam(keyId)}`
|
|
4972
|
+
),
|
|
4973
|
+
method: "GET",
|
|
4974
|
+
headers: _headers,
|
|
4975
|
+
queryString: url_exports.queryBuilder().mergeAdditional(requestOptions?.queryParams).build(),
|
|
4976
|
+
timeoutMs: (requestOptions?.timeoutInSeconds ?? this._options?.timeoutInSeconds ?? 60) * 1e3,
|
|
4977
|
+
maxRetries: requestOptions?.maxRetries ?? this._options?.maxRetries,
|
|
4978
|
+
abortSignal: requestOptions?.abortSignal,
|
|
4979
|
+
fetchFn: this._options?.fetch,
|
|
4980
|
+
logging: this._options.logging
|
|
4981
|
+
});
|
|
4982
|
+
if (_response.ok) {
|
|
4983
|
+
return { data: _response.body, rawResponse: _response.rawResponse };
|
|
4984
|
+
}
|
|
4985
|
+
if (_response.error.reason === "status-code") {
|
|
4986
|
+
switch (_response.error.statusCode) {
|
|
4987
|
+
case 403:
|
|
4988
|
+
throw new ForbiddenError(_response.error.body, _response.rawResponse);
|
|
4989
|
+
case 404:
|
|
4990
|
+
throw new NotFoundError(_response.error.body, _response.rawResponse);
|
|
4991
|
+
default:
|
|
4992
|
+
throw new VectrosError({
|
|
4993
|
+
statusCode: _response.error.statusCode,
|
|
4994
|
+
body: _response.error.body,
|
|
4995
|
+
rawResponse: _response.rawResponse
|
|
4996
|
+
});
|
|
4997
|
+
}
|
|
4998
|
+
}
|
|
4999
|
+
return handleNonStatusCodeError(_response.error, _response.rawResponse, "GET", "/v1/admin/keys/scoped/{keyId}");
|
|
5000
|
+
}
|
|
5001
|
+
/**
|
|
5002
|
+
* Revokes a scoped API key. Its status changes to `revoked` and it stops working within about 5 minutes, the maximum time authorization is cached. Revocation is permanent. Requires the `keys:d` scope.
|
|
5003
|
+
*
|
|
5004
|
+
* @param {Vectros.RevokeScopedKeyRequest} request
|
|
5005
|
+
* @param {AuthClient.RequestOptions} requestOptions - Request-specific configuration.
|
|
5006
|
+
*
|
|
5007
|
+
* @throws {@link Vectros.ForbiddenError}
|
|
5008
|
+
* @throws {@link Vectros.NotFoundError}
|
|
5009
|
+
* @throws {@link Vectros.TooManyRequestsError}
|
|
5010
|
+
*
|
|
5011
|
+
* @example
|
|
5012
|
+
* await client.auth.revokeScopedKey({
|
|
5013
|
+
* keyId: "keyId"
|
|
5014
|
+
* })
|
|
5015
|
+
*/
|
|
5016
|
+
revokeScopedKey(request, requestOptions) {
|
|
5017
|
+
return HttpResponsePromise.fromPromise(this.__revokeScopedKey(request, requestOptions));
|
|
5018
|
+
}
|
|
5019
|
+
async __revokeScopedKey(request, requestOptions) {
|
|
5020
|
+
const { keyId } = request;
|
|
5021
|
+
const _authRequest = await this._options.authProvider.getAuthRequest();
|
|
5022
|
+
const _headers = mergeHeaders(
|
|
5023
|
+
_authRequest.headers,
|
|
5024
|
+
this._options?.headers,
|
|
5025
|
+
requestOptions?.headers
|
|
5026
|
+
);
|
|
5027
|
+
const _response = await fetcher({
|
|
5028
|
+
url: url_exports.join(
|
|
5029
|
+
await Supplier.get(this._options.baseUrl) ?? await Supplier.get(this._options.environment),
|
|
5030
|
+
`v1/admin/keys/scoped/${url_exports.encodePathParam(keyId)}`
|
|
5031
|
+
),
|
|
5032
|
+
method: "DELETE",
|
|
5033
|
+
headers: _headers,
|
|
5034
|
+
queryString: url_exports.queryBuilder().mergeAdditional(requestOptions?.queryParams).build(),
|
|
5035
|
+
timeoutMs: (requestOptions?.timeoutInSeconds ?? this._options?.timeoutInSeconds ?? 60) * 1e3,
|
|
5036
|
+
maxRetries: requestOptions?.maxRetries ?? this._options?.maxRetries,
|
|
5037
|
+
abortSignal: requestOptions?.abortSignal,
|
|
5038
|
+
fetchFn: this._options?.fetch,
|
|
5039
|
+
logging: this._options.logging
|
|
5040
|
+
});
|
|
5041
|
+
if (_response.ok) {
|
|
5042
|
+
return { data: void 0, rawResponse: _response.rawResponse };
|
|
5043
|
+
}
|
|
5044
|
+
if (_response.error.reason === "status-code") {
|
|
5045
|
+
switch (_response.error.statusCode) {
|
|
5046
|
+
case 403:
|
|
5047
|
+
throw new ForbiddenError(_response.error.body, _response.rawResponse);
|
|
5048
|
+
case 404:
|
|
5049
|
+
throw new NotFoundError(_response.error.body, _response.rawResponse);
|
|
5050
|
+
case 429:
|
|
5051
|
+
throw new TooManyRequestsError(_response.error.body, _response.rawResponse);
|
|
5052
|
+
default:
|
|
5053
|
+
throw new VectrosError({
|
|
5054
|
+
statusCode: _response.error.statusCode,
|
|
5055
|
+
body: _response.error.body,
|
|
5056
|
+
rawResponse: _response.rawResponse
|
|
5057
|
+
});
|
|
5058
|
+
}
|
|
5059
|
+
}
|
|
5060
|
+
return handleNonStatusCodeError(
|
|
5061
|
+
_response.error,
|
|
5062
|
+
_response.rawResponse,
|
|
5063
|
+
"DELETE",
|
|
5064
|
+
"/v1/admin/keys/scoped/{keyId}"
|
|
5065
|
+
);
|
|
5066
|
+
}
|
|
5067
|
+
/**
|
|
5068
|
+
* Returns recent API call logs for your account. Each entry represents one API request; request and response bodies are never logged. `startTime` and `endTime` must be ISO-8601 UTC (e.g. `2025-01-15T09:00:00Z`); `endTime` defaults to now. Filter by resource, method, key id, or context id, or set `errorsOnly` to see only failures. Results are scoped to your account, derived from your token — never from input. Requires the `logs:r` scope.
|
|
5069
|
+
*
|
|
5070
|
+
* @param {Vectros.GetAdminLogsRequest} request
|
|
5071
|
+
* @param {AuthClient.RequestOptions} requestOptions - Request-specific configuration.
|
|
5072
|
+
*
|
|
5073
|
+
* @throws {@link Vectros.BadRequestError}
|
|
5074
|
+
* @throws {@link Vectros.ForbiddenError}
|
|
5075
|
+
*
|
|
5076
|
+
* @example
|
|
5077
|
+
* await client.auth.getAdminLogs({
|
|
5078
|
+
* startTime: "startTime",
|
|
5079
|
+
* resource: "documents",
|
|
5080
|
+
* method: "POST",
|
|
5081
|
+
* keyId: "key_abc123",
|
|
5082
|
+
* contextId: "ctx_intake"
|
|
5083
|
+
* })
|
|
5084
|
+
*/
|
|
5085
|
+
getAdminLogs(request, requestOptions) {
|
|
5086
|
+
return HttpResponsePromise.fromPromise(this.__getAdminLogs(request, requestOptions));
|
|
5087
|
+
}
|
|
5088
|
+
async __getAdminLogs(request, requestOptions) {
|
|
5089
|
+
const { startTime, endTime, resource, method, keyId, contextId, errorsOnly, limit } = request;
|
|
5090
|
+
const _queryParams = {
|
|
5091
|
+
startTime,
|
|
5092
|
+
endTime,
|
|
5093
|
+
resource,
|
|
5094
|
+
method,
|
|
5095
|
+
keyId,
|
|
5096
|
+
contextId,
|
|
5097
|
+
errorsOnly,
|
|
5098
|
+
limit
|
|
5099
|
+
};
|
|
5100
|
+
const _authRequest = await this._options.authProvider.getAuthRequest();
|
|
5101
|
+
const _headers = mergeHeaders(
|
|
5102
|
+
_authRequest.headers,
|
|
5103
|
+
this._options?.headers,
|
|
5104
|
+
requestOptions?.headers
|
|
5105
|
+
);
|
|
5106
|
+
const _response = await fetcher({
|
|
5107
|
+
url: url_exports.join(
|
|
5108
|
+
await Supplier.get(this._options.baseUrl) ?? await Supplier.get(this._options.environment),
|
|
5109
|
+
"v1/admin/logs"
|
|
5110
|
+
),
|
|
5111
|
+
method: "GET",
|
|
5112
|
+
headers: _headers,
|
|
5113
|
+
queryString: url_exports.queryBuilder().addMany(_queryParams).mergeAdditional(requestOptions?.queryParams).build(),
|
|
5114
|
+
timeoutMs: (requestOptions?.timeoutInSeconds ?? this._options?.timeoutInSeconds ?? 60) * 1e3,
|
|
5115
|
+
maxRetries: requestOptions?.maxRetries ?? this._options?.maxRetries,
|
|
5116
|
+
abortSignal: requestOptions?.abortSignal,
|
|
5117
|
+
fetchFn: this._options?.fetch,
|
|
5118
|
+
logging: this._options.logging
|
|
5119
|
+
});
|
|
5120
|
+
if (_response.ok) {
|
|
5121
|
+
return { data: _response.body, rawResponse: _response.rawResponse };
|
|
5122
|
+
}
|
|
5123
|
+
if (_response.error.reason === "status-code") {
|
|
5124
|
+
switch (_response.error.statusCode) {
|
|
5125
|
+
case 400:
|
|
5126
|
+
throw new BadRequestError(_response.error.body, _response.rawResponse);
|
|
5127
|
+
case 403:
|
|
5128
|
+
throw new ForbiddenError(_response.error.body, _response.rawResponse);
|
|
5129
|
+
default:
|
|
5130
|
+
throw new VectrosError({
|
|
5131
|
+
statusCode: _response.error.statusCode,
|
|
5132
|
+
body: _response.error.body,
|
|
5133
|
+
rawResponse: _response.rawResponse
|
|
5134
|
+
});
|
|
5135
|
+
}
|
|
5136
|
+
}
|
|
5137
|
+
return handleNonStatusCodeError(_response.error, _response.rawResponse, "GET", "/v1/admin/logs");
|
|
5138
|
+
}
|
|
5139
|
+
/**
|
|
5140
|
+
* Returns the access profiles assigned within the given app context — in effect, who has access to this context and with what scopes. Each profile binds a principal to either a set of inline scopes or a referenced role. Results are paginated. Requires the `profiles:r` scope.
|
|
5141
|
+
*
|
|
5142
|
+
* @param {Vectros.ListAccessProfilesRequest} request
|
|
5143
|
+
* @param {AuthClient.RequestOptions} requestOptions - Request-specific configuration.
|
|
5144
|
+
*
|
|
5145
|
+
* @throws {@link Vectros.ForbiddenError}
|
|
5146
|
+
* @throws {@link Vectros.NotFoundError}
|
|
5147
|
+
*
|
|
5148
|
+
* @example
|
|
5149
|
+
* await client.auth.listAccessProfiles({
|
|
5150
|
+
* contextId: "myapp"
|
|
5151
|
+
* })
|
|
5152
|
+
*/
|
|
5153
|
+
listAccessProfiles(request, requestOptions) {
|
|
5154
|
+
return HttpResponsePromise.fromPromise(this.__listAccessProfiles(request, requestOptions));
|
|
5155
|
+
}
|
|
5156
|
+
async __listAccessProfiles(request, requestOptions) {
|
|
5157
|
+
const { contextId, startFrom, limit } = request;
|
|
5158
|
+
const _queryParams = {
|
|
5159
|
+
startFrom,
|
|
5160
|
+
limit
|
|
5161
|
+
};
|
|
5162
|
+
const _authRequest = await this._options.authProvider.getAuthRequest();
|
|
5163
|
+
const _headers = mergeHeaders(
|
|
5164
|
+
_authRequest.headers,
|
|
5165
|
+
this._options?.headers,
|
|
5166
|
+
requestOptions?.headers
|
|
5167
|
+
);
|
|
5168
|
+
const _response = await fetcher({
|
|
5169
|
+
url: url_exports.join(
|
|
5170
|
+
await Supplier.get(this._options.baseUrl) ?? await Supplier.get(this._options.environment),
|
|
5171
|
+
`v1/app-contexts/${url_exports.encodePathParam(contextId)}/profiles`
|
|
5172
|
+
),
|
|
5173
|
+
method: "GET",
|
|
5174
|
+
headers: _headers,
|
|
5175
|
+
queryString: url_exports.queryBuilder().addMany(_queryParams).mergeAdditional(requestOptions?.queryParams).build(),
|
|
5176
|
+
timeoutMs: (requestOptions?.timeoutInSeconds ?? this._options?.timeoutInSeconds ?? 60) * 1e3,
|
|
5177
|
+
maxRetries: requestOptions?.maxRetries ?? this._options?.maxRetries,
|
|
5178
|
+
abortSignal: requestOptions?.abortSignal,
|
|
5179
|
+
fetchFn: this._options?.fetch,
|
|
5180
|
+
logging: this._options.logging
|
|
5181
|
+
});
|
|
5182
|
+
if (_response.ok) {
|
|
5183
|
+
return { data: _response.body, rawResponse: _response.rawResponse };
|
|
5184
|
+
}
|
|
5185
|
+
if (_response.error.reason === "status-code") {
|
|
5186
|
+
switch (_response.error.statusCode) {
|
|
5187
|
+
case 403:
|
|
5188
|
+
throw new ForbiddenError(_response.error.body, _response.rawResponse);
|
|
5189
|
+
case 404:
|
|
5190
|
+
throw new NotFoundError(_response.error.body, _response.rawResponse);
|
|
5191
|
+
default:
|
|
5192
|
+
throw new VectrosError({
|
|
5193
|
+
statusCode: _response.error.statusCode,
|
|
5194
|
+
body: _response.error.body,
|
|
5195
|
+
rawResponse: _response.rawResponse
|
|
5196
|
+
});
|
|
5197
|
+
}
|
|
5198
|
+
}
|
|
5199
|
+
return handleNonStatusCodeError(
|
|
5200
|
+
_response.error,
|
|
5201
|
+
_response.rawResponse,
|
|
5202
|
+
"GET",
|
|
5203
|
+
"/v1/app-contexts/{contextId}/profiles"
|
|
5204
|
+
);
|
|
5205
|
+
}
|
|
5206
|
+
/**
|
|
5207
|
+
* Creates a new access profile under the given app context. This call is idempotent by `principalId`: if a profile with the same `principalId` already exists, the existing profile is returned (with status 200) instead of creating a duplicate. The response's `created` field (and the HTTP status — 201 when created, 200 when an existing profile was returned) tells the two apart. To overwrite an existing profile's `scopes`/`roleId`, `identityOverrides`, and `status` instead of returning it unchanged, set `?upsert=true` (this also requires the `profiles:u` scope, and applies the same `identityOverrides` bounds the update endpoint documents — a scoped credential may not repoint or clear an identity value it does not itself hold). The `principalId` must name a principal that already exists: a `usr_` principal must be a live user in your tenant, so create the user before granting it a profile. A `usr_` id that names no such user is rejected, and no profile is created. `key_` principals are not checked this way. You must provide exactly one of `scopes` (an inline list of scopes) or `roleId` (a reference to a role); supplying both, or neither, is rejected. `identityOverrides` is keyed by ownership namespace in `scope:<namespace>` form — `scope:org` and `scope:client` for the reserved namespaces, or any namespace you have registered — and may name at most two; any other key (including the account identifier or `userId`) is rejected. If you use a scoped credential, the profile's effective scopes may not exceed your own; a root API key (`sk_`) is exempt. Requires the `profiles:c` scope.
|
|
5208
|
+
*
|
|
5209
|
+
* @param {Vectros.CreateAccessProfileRequest} request
|
|
5210
|
+
* @param {AuthClient.RequestOptions} requestOptions - Request-specific configuration.
|
|
5211
|
+
*
|
|
5212
|
+
* @throws {@link Vectros.BadRequestError}
|
|
5213
|
+
* @throws {@link Vectros.ForbiddenError}
|
|
5214
|
+
* @throws {@link Vectros.NotFoundError}
|
|
5215
|
+
* @throws {@link Vectros.ConflictError}
|
|
5216
|
+
* @throws {@link Vectros.TooManyRequestsError}
|
|
5217
|
+
*
|
|
5218
|
+
* @example
|
|
4577
5219
|
* await client.auth.createAccessProfile({
|
|
4578
5220
|
* contextId: "contextId",
|
|
4579
|
-
* body: {
|
|
4580
|
-
* principalId: "usr_6ba7b810-9dad-11d1-80b4-00c04fd430c8"
|
|
4581
|
-
* }
|
|
5221
|
+
* body: {}
|
|
4582
5222
|
* })
|
|
4583
5223
|
*/
|
|
4584
5224
|
createAccessProfile(request, requestOptions) {
|
|
@@ -4714,10 +5354,7 @@ var AuthClient = class {
|
|
|
4714
5354
|
*
|
|
4715
5355
|
* @example
|
|
4716
5356
|
* await client.auth.createAppContext({
|
|
4717
|
-
* body: {
|
|
4718
|
-
* contextId: "myapp",
|
|
4719
|
-
* name: "My Internal App"
|
|
4720
|
-
* }
|
|
5357
|
+
* body: {}
|
|
4721
5358
|
* })
|
|
4722
5359
|
*/
|
|
4723
5360
|
createAppContext(request, requestOptions) {
|
|
@@ -4860,13 +5497,7 @@ var AuthClient = class {
|
|
|
4860
5497
|
* @example
|
|
4861
5498
|
* await client.auth.createRole({
|
|
4862
5499
|
* contextId: "contextId",
|
|
4863
|
-
* body: {
|
|
4864
|
-
* roleId: "engineering-member",
|
|
4865
|
-
* name: "Engineering Team Member",
|
|
4866
|
-
* scopes: [{
|
|
4867
|
-
* allowed_actions: ["records:cru", "search:r"]
|
|
4868
|
-
* }]
|
|
4869
|
-
* }
|
|
5500
|
+
* body: {}
|
|
4870
5501
|
* })
|
|
4871
5502
|
*/
|
|
4872
5503
|
createRole(request, requestOptions) {
|
|
@@ -5010,9 +5641,7 @@ var AuthClient = class {
|
|
|
5010
5641
|
* await client.auth.updateAccessProfile({
|
|
5011
5642
|
* contextId: "contextId",
|
|
5012
5643
|
* principalId: "principalId",
|
|
5013
|
-
* body: {
|
|
5014
|
-
* principalId: "usr_6ba7b810-9dad-11d1-80b4-00c04fd430c8"
|
|
5015
|
-
* }
|
|
5644
|
+
* body: {}
|
|
5016
5645
|
* })
|
|
5017
5646
|
*/
|
|
5018
5647
|
updateAccessProfile(request, requestOptions) {
|
|
@@ -5215,10 +5844,7 @@ var AuthClient = class {
|
|
|
5215
5844
|
* @example
|
|
5216
5845
|
* await client.auth.updateAppContext({
|
|
5217
5846
|
* contextId: "contextId",
|
|
5218
|
-
* body: {
|
|
5219
|
-
* contextId: "myapp",
|
|
5220
|
-
* name: "My Internal App"
|
|
5221
|
-
* }
|
|
5847
|
+
* body: {}
|
|
5222
5848
|
* })
|
|
5223
5849
|
*/
|
|
5224
5850
|
updateAppContext(request, requestOptions) {
|
|
@@ -5423,13 +6049,7 @@ var AuthClient = class {
|
|
|
5423
6049
|
* await client.auth.updateRole({
|
|
5424
6050
|
* contextId: "contextId",
|
|
5425
6051
|
* roleId: "roleId",
|
|
5426
|
-
* body: {
|
|
5427
|
-
* roleId: "engineering-member",
|
|
5428
|
-
* name: "Engineering Team Member",
|
|
5429
|
-
* scopes: [{
|
|
5430
|
-
* allowed_actions: ["records:cru", "search:r"]
|
|
5431
|
-
* }]
|
|
5432
|
-
* }
|
|
6052
|
+
* body: {}
|
|
5433
6053
|
* })
|
|
5434
6054
|
*/
|
|
5435
6055
|
updateRole(request, requestOptions) {
|
|
@@ -6881,7 +7501,7 @@ var DocumentsClient = class {
|
|
|
6881
7501
|
this._options = normalizeClientOptionsWithAuth(options);
|
|
6882
7502
|
}
|
|
6883
7503
|
/**
|
|
6884
|
-
* Returns a paginated list of your documents, optionally filtered by folder (`folderId`)
|
|
7504
|
+
* Returns a paginated list of your documents, optionally filtered by folder (`folderId`), owner (`userId` or `scope`) and/or type (`type`). The response is a `{data, nextCursor}` envelope; pass `nextCursor` back as `startFrom` to fetch the next page. A page can hold fewer documents than `limit`, or none, while `nextCursor` is still set: keep paging until `nextCursor` is null. Requires the `documents:r` scope.
|
|
6885
7505
|
*
|
|
6886
7506
|
* @param {Vectros.ListDocumentsRequest} request
|
|
6887
7507
|
* @param {DocumentsClient.RequestOptions} requestOptions - Request-specific configuration.
|
|
@@ -6891,6 +7511,7 @@ var DocumentsClient = class {
|
|
|
6891
7511
|
* userId: "550e8400-e29b-41d4-a716-446655440000",
|
|
6892
7512
|
* scope: "group:eng-team",
|
|
6893
7513
|
* folderId: "f47ac10b-58cc-4372-a567-0e02b2c3d479",
|
|
7514
|
+
* type: "investigation_report",
|
|
6894
7515
|
* startFrom: "b3BhcXVlLWN1cnNvci1mcm9tLXRoZS1wcmV2aW91cy1wYWdl"
|
|
6895
7516
|
* })
|
|
6896
7517
|
*/
|
|
@@ -6898,11 +7519,12 @@ var DocumentsClient = class {
|
|
|
6898
7519
|
return HttpResponsePromise.fromPromise(this.__listDocuments(request, requestOptions));
|
|
6899
7520
|
}
|
|
6900
7521
|
async __listDocuments(request = {}, requestOptions) {
|
|
6901
|
-
const { userId, scope, folderId, startFrom, limit } = request;
|
|
7522
|
+
const { userId, scope, folderId, type: type_, startFrom, limit } = request;
|
|
6902
7523
|
const _queryParams = {
|
|
6903
7524
|
userId,
|
|
6904
7525
|
scope,
|
|
6905
7526
|
folderId,
|
|
7527
|
+
type: type_,
|
|
6906
7528
|
startFrom,
|
|
6907
7529
|
limit
|
|
6908
7530
|
};
|
|
@@ -6952,9 +7574,7 @@ var DocumentsClient = class {
|
|
|
6952
7574
|
*
|
|
6953
7575
|
* @example
|
|
6954
7576
|
* await client.documents.ingestDocument({
|
|
6955
|
-
* body: {
|
|
6956
|
-
* title: "Patient Intake Form \u2014 Jane Doe"
|
|
6957
|
-
* }
|
|
7577
|
+
* body: {}
|
|
6958
7578
|
* })
|
|
6959
7579
|
*/
|
|
6960
7580
|
ingestDocument(request, requestOptions) {
|
|
@@ -7084,9 +7704,7 @@ var DocumentsClient = class {
|
|
|
7084
7704
|
* @example
|
|
7085
7705
|
* await client.documents.updateDocument({
|
|
7086
7706
|
* id: "id",
|
|
7087
|
-
* body: {
|
|
7088
|
-
* title: "Patient Intake Form \u2014 Jane Doe"
|
|
7089
|
-
* }
|
|
7707
|
+
* body: {}
|
|
7090
7708
|
* })
|
|
7091
7709
|
*/
|
|
7092
7710
|
updateDocument(request, requestOptions) {
|
|
@@ -7484,11 +8102,12 @@ var DocumentsClient = class {
|
|
|
7484
8102
|
return handleNonStatusCodeError(_response.error, _response.rawResponse, "GET", "/v1/documents/{id}/download");
|
|
7485
8103
|
}
|
|
7486
8104
|
/**
|
|
7487
|
-
* Returns the document's full text body when it is retained: retained for text-ingested documents, and for file-uploaded documents unless they were uploaded with `storeText=false` (which discards the extracted text once indexing completes — the original file remains available via `GET /v1/documents/{id}/download`). Returns 404 when the document does not exist, its text was not retained,
|
|
8105
|
+
* Returns the document's full text body when it is retained: retained for text-ingested documents, and for file-uploaded documents unless they were uploaded with `storeText=false` (which discards the extracted text once indexing completes — the original file remains available via `GET /v1/documents/{id}/download`). Returns 404 when the document does not exist, its text was not retained, extraction has not yet completed, or no text could be extracted from its file (see the document's `extraction`). To read a long text in parts, pass `limit` (and `offset` from the second part on): offsets and lengths count Unicode characters (code points), `totalLength` is the length of the whole text, and `nextOffset` is where the next part starts, absent once the end is reached. Requires the `documents:r` scope.
|
|
7488
8106
|
*
|
|
7489
8107
|
* @param {Vectros.GetDocumentTextRequest} request
|
|
7490
8108
|
* @param {DocumentsClient.RequestOptions} requestOptions - Request-specific configuration.
|
|
7491
8109
|
*
|
|
8110
|
+
* @throws {@link Vectros.BadRequestError}
|
|
7492
8111
|
* @throws {@link Vectros.NotFoundError}
|
|
7493
8112
|
*
|
|
7494
8113
|
* @example
|
|
@@ -7500,7 +8119,11 @@ var DocumentsClient = class {
|
|
|
7500
8119
|
return HttpResponsePromise.fromPromise(this.__getDocumentText(request, requestOptions));
|
|
7501
8120
|
}
|
|
7502
8121
|
async __getDocumentText(request, requestOptions) {
|
|
7503
|
-
const { id } = request;
|
|
8122
|
+
const { id, offset, limit } = request;
|
|
8123
|
+
const _queryParams = {
|
|
8124
|
+
offset,
|
|
8125
|
+
limit
|
|
8126
|
+
};
|
|
7504
8127
|
const _authRequest = await this._options.authProvider.getAuthRequest();
|
|
7505
8128
|
const _headers = mergeHeaders(
|
|
7506
8129
|
_authRequest.headers,
|
|
@@ -7514,7 +8137,7 @@ var DocumentsClient = class {
|
|
|
7514
8137
|
),
|
|
7515
8138
|
method: "GET",
|
|
7516
8139
|
headers: _headers,
|
|
7517
|
-
queryString: url_exports.queryBuilder().mergeAdditional(requestOptions?.queryParams).build(),
|
|
8140
|
+
queryString: url_exports.queryBuilder().addMany(_queryParams).mergeAdditional(requestOptions?.queryParams).build(),
|
|
7518
8141
|
timeoutMs: (requestOptions?.timeoutInSeconds ?? this._options?.timeoutInSeconds ?? 60) * 1e3,
|
|
7519
8142
|
maxRetries: requestOptions?.maxRetries ?? this._options?.maxRetries,
|
|
7520
8143
|
abortSignal: requestOptions?.abortSignal,
|
|
@@ -7526,6 +8149,8 @@ var DocumentsClient = class {
|
|
|
7526
8149
|
}
|
|
7527
8150
|
if (_response.error.reason === "status-code") {
|
|
7528
8151
|
switch (_response.error.statusCode) {
|
|
8152
|
+
case 400:
|
|
8153
|
+
throw new BadRequestError(_response.error.body, _response.rawResponse);
|
|
7529
8154
|
case 404:
|
|
7530
8155
|
throw new NotFoundError(_response.error.body, _response.rawResponse);
|
|
7531
8156
|
default:
|
|
@@ -7745,9 +8370,7 @@ var FoldersClient = class {
|
|
|
7745
8370
|
*
|
|
7746
8371
|
* @example
|
|
7747
8372
|
* await client.folders.createFolder({
|
|
7748
|
-
* body: {
|
|
7749
|
-
* name: "Patient Records 2024"
|
|
7750
|
-
* }
|
|
8373
|
+
* body: {}
|
|
7751
8374
|
* })
|
|
7752
8375
|
*/
|
|
7753
8376
|
createFolder(request, requestOptions) {
|
|
@@ -7875,9 +8498,7 @@ var FoldersClient = class {
|
|
|
7875
8498
|
* @example
|
|
7876
8499
|
* await client.folders.updateFolder({
|
|
7877
8500
|
* id: "id",
|
|
7878
|
-
* body: {
|
|
7879
|
-
* name: "Patient Records 2024"
|
|
7880
|
-
* }
|
|
8501
|
+
* body: {}
|
|
7881
8502
|
* })
|
|
7882
8503
|
*/
|
|
7883
8504
|
updateFolder(request, requestOptions) {
|
|
@@ -8010,9 +8631,7 @@ var FoldersClient = class {
|
|
|
8010
8631
|
* @example
|
|
8011
8632
|
* await client.folders.patchFolder({
|
|
8012
8633
|
* id: "id",
|
|
8013
|
-
* body: {
|
|
8014
|
-
* name: "Patient Records 2024"
|
|
8015
|
-
* }
|
|
8634
|
+
* body: {}
|
|
8016
8635
|
* })
|
|
8017
8636
|
*/
|
|
8018
8637
|
patchFolder(request, requestOptions) {
|
|
@@ -8227,9 +8846,7 @@ var IdentityClient = class {
|
|
|
8227
8846
|
* await client.identity.createEntity({
|
|
8228
8847
|
* namespace: "team",
|
|
8229
8848
|
* contextId: "myapp",
|
|
8230
|
-
* body: {
|
|
8231
|
-
* externalId: "team_eng_platform"
|
|
8232
|
-
* }
|
|
8849
|
+
* body: {}
|
|
8233
8850
|
* })
|
|
8234
8851
|
*/
|
|
8235
8852
|
createEntity(request, requestOptions) {
|
|
@@ -8365,9 +8982,7 @@ var IdentityClient = class {
|
|
|
8365
8982
|
* namespace: "team",
|
|
8366
8983
|
* id: "id",
|
|
8367
8984
|
* contextId: "myapp",
|
|
8368
|
-
* body: {
|
|
8369
|
-
* externalId: "team_eng_platform"
|
|
8370
|
-
* }
|
|
8985
|
+
* body: {}
|
|
8371
8986
|
* })
|
|
8372
8987
|
*/
|
|
8373
8988
|
updateEntity(request, requestOptions) {
|
|
@@ -8707,10 +9322,7 @@ var IdentityClient = class {
|
|
|
8707
9322
|
* @example
|
|
8708
9323
|
* await client.identity.updateNamespace({
|
|
8709
9324
|
* namespace: "team",
|
|
8710
|
-
* body: {
|
|
8711
|
-
* namespace: "team",
|
|
8712
|
-
* specificityRank: 1500
|
|
8713
|
-
* }
|
|
9325
|
+
* body: {}
|
|
8714
9326
|
* })
|
|
8715
9327
|
*/
|
|
8716
9328
|
updateNamespace(request, requestOptions) {
|
|
@@ -8897,10 +9509,7 @@ var IdentityClient = class {
|
|
|
8897
9509
|
*
|
|
8898
9510
|
* @example
|
|
8899
9511
|
* await client.identity.registerNamespace({
|
|
8900
|
-
* body: {
|
|
8901
|
-
* namespace: "team",
|
|
8902
|
-
* specificityRank: 1500
|
|
8903
|
-
* }
|
|
9512
|
+
* body: {}
|
|
8904
9513
|
* })
|
|
8905
9514
|
*/
|
|
8906
9515
|
registerNamespace(request, requestOptions) {
|
|
@@ -9033,9 +9642,7 @@ var IdentityClient = class {
|
|
|
9033
9642
|
*
|
|
9034
9643
|
* @example
|
|
9035
9644
|
* await client.identity.createUser({
|
|
9036
|
-
* body: {
|
|
9037
|
-
* externalId: "usr_12345"
|
|
9038
|
-
* }
|
|
9645
|
+
* body: {}
|
|
9039
9646
|
* })
|
|
9040
9647
|
*/
|
|
9041
9648
|
createUser(request, requestOptions) {
|
|
@@ -9154,6 +9761,7 @@ var IdentityClient = class {
|
|
|
9154
9761
|
* @param {IdentityClient.RequestOptions} requestOptions - Request-specific configuration.
|
|
9155
9762
|
*
|
|
9156
9763
|
* @throws {@link Vectros.BadRequestError}
|
|
9764
|
+
* @throws {@link Vectros.ForbiddenError}
|
|
9157
9765
|
* @throws {@link Vectros.NotFoundError}
|
|
9158
9766
|
* @throws {@link Vectros.ConflictError}
|
|
9159
9767
|
* @throws {@link Vectros.TooManyRequestsError}
|
|
@@ -9161,9 +9769,7 @@ var IdentityClient = class {
|
|
|
9161
9769
|
* @example
|
|
9162
9770
|
* await client.identity.updateUser({
|
|
9163
9771
|
* id: "550e8400-e29b-41d4-a716-446655440000",
|
|
9164
|
-
* body: {
|
|
9165
|
-
* externalId: "usr_12345"
|
|
9166
|
-
* }
|
|
9772
|
+
* body: {}
|
|
9167
9773
|
* })
|
|
9168
9774
|
*/
|
|
9169
9775
|
updateUser(request, requestOptions) {
|
|
@@ -9201,6 +9807,8 @@ var IdentityClient = class {
|
|
|
9201
9807
|
switch (_response.error.statusCode) {
|
|
9202
9808
|
case 400:
|
|
9203
9809
|
throw new BadRequestError(_response.error.body, _response.rawResponse);
|
|
9810
|
+
case 403:
|
|
9811
|
+
throw new ForbiddenError(_response.error.body, _response.rawResponse);
|
|
9204
9812
|
case 404:
|
|
9205
9813
|
throw new NotFoundError(_response.error.body, _response.rawResponse);
|
|
9206
9814
|
case 409:
|
|
@@ -9223,6 +9831,7 @@ var IdentityClient = class {
|
|
|
9223
9831
|
* @param {Vectros.DeleteUserRequest} request
|
|
9224
9832
|
* @param {IdentityClient.RequestOptions} requestOptions - Request-specific configuration.
|
|
9225
9833
|
*
|
|
9834
|
+
* @throws {@link Vectros.ForbiddenError}
|
|
9226
9835
|
* @throws {@link Vectros.NotFoundError}
|
|
9227
9836
|
* @throws {@link Vectros.ConflictError}
|
|
9228
9837
|
* @throws {@link Vectros.TooManyRequestsError}
|
|
@@ -9262,6 +9871,8 @@ var IdentityClient = class {
|
|
|
9262
9871
|
}
|
|
9263
9872
|
if (_response.error.reason === "status-code") {
|
|
9264
9873
|
switch (_response.error.statusCode) {
|
|
9874
|
+
case 403:
|
|
9875
|
+
throw new ForbiddenError(_response.error.body, _response.rawResponse);
|
|
9265
9876
|
case 404:
|
|
9266
9877
|
throw new NotFoundError(_response.error.body, _response.rawResponse);
|
|
9267
9878
|
case 409:
|
|
@@ -9466,7 +10077,7 @@ var InferenceClient = class {
|
|
|
9466
10077
|
this._options = normalizeClientOptionsWithAuth(options);
|
|
9467
10078
|
}
|
|
9468
10079
|
/**
|
|
9469
|
-
* Returns the whole platform model catalog, whatever your plan: `availableOn` names each model's plan tiers. Each entry also carries the context window and the credit rates per 1K input and output tokens: `regionPricing.base` for global-region requests, `regionPricing.us` for default requests,
|
|
10080
|
+
* Returns the whole platform model catalog, whatever your plan: `availableOn` names each model's plan tiers. Each entry also carries the context window and the credit rates per 1K input and output tokens: `regionPricing.base` for global-region requests, `regionPricing.us` for default requests, `longContext` for prompts over its threshold, and `cachePricing` for prompt-cache tokens on a model that has cache prices. A model reached through a `providerAlias` is not listed. Use this to populate model pickers and to validate a request before calling `/v1/chat`, `/v1/rag`, or `/v1/documents/{id}/ask`.
|
|
9470
10081
|
*
|
|
9471
10082
|
* @param {InferenceClient.RequestOptions} requestOptions - Request-specific configuration.
|
|
9472
10083
|
*
|
|
@@ -9510,7 +10121,7 @@ var InferenceClient = class {
|
|
|
9510
10121
|
return handleNonStatusCodeError(_response.error, _response.rawResponse, "GET", "/v1/models");
|
|
9511
10122
|
}
|
|
9512
10123
|
/**
|
|
9513
|
-
* Streams a model response as Server-Sent Events (SSE). Send the full conversation history in the `messages` array; a message with role `system` is extracted and used as the system prompt. On a pre-paid account, token cost is debited from your inference balance; usage-billed accounts accrue it for post-billing. A request routed through your own `providerAlias` incurs no Vectros token cost. A
|
|
10124
|
+
* Streams a model response as Server-Sent Events (SSE). Send the full conversation history in the `messages` array; a message with role `system` is extracted and used as the system prompt. On a pre-paid account, token cost is debited from your inference balance; usage-billed accounts accrue it for post-billing. A request routed through your own `providerAlias` incurs no Vectros token cost. A per-call platform fee is charged to your monthly credit allowance, except when the call fails on the server side: 0.2 credits for a call of up to 60 seconds, then 1 credit per 5 minutes of the call's duration. Requires the `inference:r` scope.
|
|
9514
10125
|
*/
|
|
9515
10126
|
chatInference(request, requestOptions) {
|
|
9516
10127
|
return HttpResponsePromise.fromPromise(this.__chatInference(request, requestOptions));
|
|
@@ -9582,7 +10193,7 @@ var InferenceClient = class {
|
|
|
9582
10193
|
return handleNonStatusCodeError(_response.error, _response.rawResponse, "POST", "/v1/chat");
|
|
9583
10194
|
}
|
|
9584
10195
|
/**
|
|
9585
|
-
* Loads a single document's extracted text, supplies it as context, and streams a model answer about it. The document must
|
|
10196
|
+
* Loads a single document's extracted text, supplies it as context, and streams a model answer about it. The document must have finished processing and have text. If the document exceeds the 32K-token cap, the call returns 413 with no credits charged — use `POST /v1/rag` instead to answer over larger or multi-document collections. Requires the `inference:r` scope.
|
|
9586
10197
|
*/
|
|
9587
10198
|
documentAsk(request, requestOptions) {
|
|
9588
10199
|
return HttpResponsePromise.fromPromise(this.__documentAsk(request, requestOptions));
|
|
@@ -10717,8 +11328,7 @@ var SchemasClient = class {
|
|
|
10717
11328
|
* }],
|
|
10718
11329
|
* capabilities: {
|
|
10719
11330
|
* "auditHistory": true
|
|
10720
|
-
* }
|
|
10721
|
-
* allowedSurfaces: ["record"]
|
|
11331
|
+
* }
|
|
10722
11332
|
* }
|
|
10723
11333
|
* })
|
|
10724
11334
|
*/
|
|
@@ -10848,11 +11458,7 @@ var SchemasClient = class {
|
|
|
10848
11458
|
* @example
|
|
10849
11459
|
* await client.schemas.updateSchema({
|
|
10850
11460
|
* id: "6ba7b810-9dad-11d1-80b4-00c04fd430c8",
|
|
10851
|
-
* body: {
|
|
10852
|
-
* typeName: "intake_form",
|
|
10853
|
-
* displayName: "Client Intake Form",
|
|
10854
|
-
* allowedSurfaces: ["record"]
|
|
10855
|
-
* }
|
|
11461
|
+
* body: {}
|
|
10856
11462
|
* })
|
|
10857
11463
|
*/
|
|
10858
11464
|
updateSchema(request, requestOptions) {
|
|
@@ -11392,7 +11998,7 @@ var ScriptsClient = class {
|
|
|
11392
11998
|
return handleNonStatusCodeError(_response.error, _response.rawResponse, "PUT", "/v1/scripts/{id}");
|
|
11393
11999
|
}
|
|
11394
12000
|
/**
|
|
11395
|
-
* Permanently deletes one script version by ID. Deleting one version does not affect any other version of the same name. Requires the `scripts:d` scope. Refused with 409 if a live trigger rule still references this version — either directly (a pinned `scriptRef`), or via `"latest"` when this IS the newest version of its name. Delete or re-point those rules first.
|
|
12001
|
+
* Permanently deletes one script version by ID. Deleting one version does not affect any other version of the same name. Requires the `scripts:d` scope. Refused with 409 if a live trigger rule still references this version — either directly (a pinned `scriptRef`), or via `"latest"` when this IS the newest version of its name. Delete or re-point those rules first. Also refused with a retryable 409, deleting nothing, when it cannot be told yet whether this is the newest version (for example just after several of this name's newest versions were deleted, or while its first version is gone and the listing has not caught up) and a `"latest"` rule depends on the script: try again shortly.
|
|
11396
12002
|
*
|
|
11397
12003
|
* @param {Vectros.DeleteScriptRequest} request
|
|
11398
12004
|
* @param {ScriptsClient.RequestOptions} requestOptions - Request-specific configuration.
|
|
@@ -11656,17 +12262,7 @@ var TriggersClient = class {
|
|
|
11656
12262
|
*
|
|
11657
12263
|
* @example
|
|
11658
12264
|
* await client.triggers.createTrigger({
|
|
11659
|
-
* body: {
|
|
11660
|
-
* name: "on-intake-create",
|
|
11661
|
-
* firingSource: {
|
|
11662
|
-
* schemaId: "sch_9f8e7d6c",
|
|
11663
|
-
* event: "CREATE"
|
|
11664
|
-
* },
|
|
11665
|
-
* scriptRef: {
|
|
11666
|
-
* name: "notify-assignee",
|
|
11667
|
-
* version: "3"
|
|
11668
|
-
* }
|
|
11669
|
-
* }
|
|
12265
|
+
* body: {}
|
|
11670
12266
|
* })
|
|
11671
12267
|
*/
|
|
11672
12268
|
createTrigger(request, requestOptions) {
|
|
@@ -11795,17 +12391,7 @@ var TriggersClient = class {
|
|
|
11795
12391
|
* @example
|
|
11796
12392
|
* await client.triggers.updateTrigger({
|
|
11797
12393
|
* id: "id",
|
|
11798
|
-
* body: {
|
|
11799
|
-
* name: "on-intake-create",
|
|
11800
|
-
* firingSource: {
|
|
11801
|
-
* schemaId: "sch_9f8e7d6c",
|
|
11802
|
-
* event: "CREATE"
|
|
11803
|
-
* },
|
|
11804
|
-
* scriptRef: {
|
|
11805
|
-
* name: "notify-assignee",
|
|
11806
|
-
* version: "3"
|
|
11807
|
-
* }
|
|
11808
|
-
* }
|
|
12394
|
+
* body: {}
|
|
11809
12395
|
* })
|
|
11810
12396
|
*/
|
|
11811
12397
|
updateTrigger(request, requestOptions) {
|
|
@@ -11930,6 +12516,9 @@ var VectrosClient = class {
|
|
|
11930
12516
|
get auth() {
|
|
11931
12517
|
return this._auth ?? (this._auth = new AuthClient(this._options));
|
|
11932
12518
|
}
|
|
12519
|
+
get agents() {
|
|
12520
|
+
return this._agents ?? (this._agents = new AgentsClient(this._options));
|
|
12521
|
+
}
|
|
11933
12522
|
get documents() {
|
|
11934
12523
|
return this._documents ?? (this._documents = new DocumentsClient(this._options));
|
|
11935
12524
|
}
|
|
@@ -11997,7 +12586,11 @@ var logging;
|
|
|
11997
12586
|
import { z } from "zod";
|
|
11998
12587
|
import { z as z2 } from "zod";
|
|
11999
12588
|
var CONTEXT_ID_RE = /^[a-z][a-z0-9-]{2,30}$/;
|
|
12000
|
-
var FORGERY_CHARS_RE =
|
|
12589
|
+
var FORGERY_CHARS_RE = (
|
|
12590
|
+
// eslint-disable-next-line no-control-regex -- matching control characters is what this pattern is for
|
|
12591
|
+
/[\x00-\x1f\x7f-\x9f\u00ad\u061c\u115f\u1160\u180e\u200b-\u200f\u2028\u2029\u202a-\u202e\u2060-\u2064\u2066-\u206f\u3164\ufeff\uffa0\ufff9-\ufffb\u{1bca0}-\u{1bca3}\u{1d173}-\u{1d17a}\u{e0000}-\u{e007f}]/u
|
|
12592
|
+
);
|
|
12593
|
+
var FORGERY_CHARS_GLOBAL_RE = new RegExp(FORGERY_CHARS_RE.source, "gu");
|
|
12001
12594
|
function safeDisplayString(base) {
|
|
12002
12595
|
return base.refine((v) => !FORGERY_CHARS_RE.test(v), {
|
|
12003
12596
|
message: "must not contain control characters, invisible/zero-width characters, line/paragraph separators, or bidi marks/overrides \u2014 these can forge, hide, or reorder text in a rendered plan preview"
|
|
@@ -12164,7 +12757,8 @@ var BlueprintLookupFieldSchema = z.union([
|
|
|
12164
12757
|
// or a declared field on this schema. Locked at create. Valid on a
|
|
12165
12758
|
// composite lookup too — it orders WITHIN a group when a query supplies
|
|
12166
12759
|
// fewer values than the lookup declares, not across the whole result.
|
|
12167
|
-
|
|
12760
|
+
// Printed by the CLI's lint findings, so held to the displayed-text rule like the other names here.
|
|
12761
|
+
sortBy: safeDisplayString(z.string().min(1)).optional(),
|
|
12168
12762
|
// Opt a field past the fixed fast-index budget into a higher-cost
|
|
12169
12763
|
// secondary index. No effect on a field that fits within the budget.
|
|
12170
12764
|
allowOverflow: z.boolean().optional()
|
|
@@ -12381,7 +12975,7 @@ var BlueprintAccessProfileSchema = z.object({
|
|
|
12381
12975
|
// this file, and so a future preview enhancement that starts showing these values (a very
|
|
12382
12976
|
// plausible one, given the existing renderPlanDataScope precedent) doesn't silently reopen
|
|
12383
12977
|
// this exact class on a field that looks, byte-for-byte, like every other one already closed.
|
|
12384
|
-
dataScope: z.record(z.array(z.union([safeDisplayString(z.string().min(1)), z.null()]))).optional(),
|
|
12978
|
+
dataScope: z.record(z.string(), z.array(z.union([safeDisplayString(z.string().min(1)), z.null()]))).optional(),
|
|
12385
12979
|
// Optional identity overrides for the service principal's profile — scope
|
|
12386
12980
|
// values its key STAMPS onto everything it writes (a credential can only
|
|
12387
12981
|
// stamp ownership its identity carries). Keys are the namespaced `scope:<ns>`
|
|
@@ -12394,7 +12988,7 @@ var BlueprintAccessProfileSchema = z.object({
|
|
|
12394
12988
|
// below (`lintIdentityOverrides`, placeholder-aware — see that function's
|
|
12395
12989
|
// docstring); this `.min(1)` is only the shape-level "non-blank" guard on
|
|
12396
12990
|
// the value, not the grammar check.
|
|
12397
|
-
identityOverrides: z.record(z.string().min(1)).optional(),
|
|
12991
|
+
identityOverrides: z.record(z.string(), z.string().min(1)).optional(),
|
|
12398
12992
|
// Optional capabilities granted to the blueprint's own service-principal
|
|
12399
12993
|
// profile. DECISION: allowed here, not role-clause-only. On the platform,
|
|
12400
12994
|
// an access profile's scopes are themselves a list of clauses — the exact
|
|
@@ -12458,7 +13052,7 @@ var BlueprintAccessProfileSchema = z.object({
|
|
|
12458
13052
|
});
|
|
12459
13053
|
var BlueprintRoleClauseSchema = z.object({
|
|
12460
13054
|
allowedActions: z.array(safeDisplayString(z.string().min(1))).min(1),
|
|
12461
|
-
dataScope: z.record(z.array(z.union([safeDisplayString(z.string().min(1)), z.null()]))).optional(),
|
|
13055
|
+
dataScope: z.record(z.string(), z.array(z.union([safeDisplayString(z.string().min(1)), z.null()]))).optional(),
|
|
12462
13056
|
dataScopeRef: z.string().min(1).optional(),
|
|
12463
13057
|
capabilities: GrantedCapabilitiesSchema,
|
|
12464
13058
|
assignableRoles: AssignableRolesSchema
|
|
@@ -12472,11 +13066,12 @@ var BlueprintRoleClauseSchema = z.object({
|
|
|
12472
13066
|
}
|
|
12473
13067
|
});
|
|
12474
13068
|
var BlueprintFragmentsSchema = z.record(
|
|
12475
|
-
z.
|
|
13069
|
+
z.string(),
|
|
13070
|
+
z.record(z.string(), z.array(z.union([safeDisplayString(z.string().min(1)), z.null()])))
|
|
12476
13071
|
);
|
|
12477
13072
|
var BlueprintRolesSchema = z.record(safeDisplayString(z.string().min(1)), z.array(BlueprintRoleClauseSchema).min(1));
|
|
12478
|
-
var BlueprintRoleAssumableSchema = z.record(z.array(safeDisplayString(z.string().min(1))).min(1));
|
|
12479
|
-
var BlueprintRoleAssumableMapSchema = z.record(BlueprintRoleAssumableSchema);
|
|
13073
|
+
var BlueprintRoleAssumableSchema = z.record(z.string(), z.array(safeDisplayString(z.string().min(1))).min(1));
|
|
13074
|
+
var BlueprintRoleAssumableMapSchema = z.record(z.string(), BlueprintRoleAssumableSchema);
|
|
12480
13075
|
var BlueprintTriggerFiringSourceSchema = z.object({
|
|
12481
13076
|
schemaName: z.string().min(1),
|
|
12482
13077
|
event: z.enum(["CREATE", "UPDATE", "DELETE"])
|
|
@@ -12510,7 +13105,7 @@ var BlueprintTriggerEntrySchema = z.object({
|
|
|
12510
13105
|
scriptRef: BlueprintTriggerScriptRefSchema,
|
|
12511
13106
|
allowedActions: z.array(safeDisplayString(z.string().min(1))).min(1).optional(),
|
|
12512
13107
|
roleIds: z.array(z.string().min(1)).min(1).optional(),
|
|
12513
|
-
dataScope: z.record(z.array(z.union([safeDisplayString(z.string().min(1)), z.null()]))).optional(),
|
|
13108
|
+
dataScope: z.record(z.string(), z.array(z.union([safeDisplayString(z.string().min(1)), z.null()]))).optional(),
|
|
12514
13109
|
capabilities: GrantedCapabilitiesSchema,
|
|
12515
13110
|
manifest: BlueprintTriggerManifestSchema,
|
|
12516
13111
|
/**
|
|
@@ -12590,7 +13185,113 @@ var BlueprintScriptEntrySchema = z.object({
|
|
|
12590
13185
|
message: `'declaredInputContract' exceeds the platform's ${SCRIPT_INPUT_CONTRACT_MAX_BYTES.toLocaleString("en-US")}-byte cap (measured in UTF-8 bytes, not characters)`
|
|
12591
13186
|
}).optional()
|
|
12592
13187
|
}).strict();
|
|
12593
|
-
var BlueprintScriptsSchema = z.record(BlueprintScriptEntrySchema);
|
|
13188
|
+
var BlueprintScriptsSchema = z.record(z.string(), BlueprintScriptEntrySchema);
|
|
13189
|
+
var BLUEPRINT_FORMAT_VERSION = 2;
|
|
13190
|
+
var AGENT_LIMITS = {
|
|
13191
|
+
descriptionChars: 500,
|
|
13192
|
+
modelChars: 100,
|
|
13193
|
+
systemChars: 32e3,
|
|
13194
|
+
tools: 50,
|
|
13195
|
+
maxSteps: 25,
|
|
13196
|
+
minTokensPerCall: 16,
|
|
13197
|
+
maxTokensPerCall: 8192,
|
|
13198
|
+
maxToolCalls: 50,
|
|
13199
|
+
maxWrites: 1e3,
|
|
13200
|
+
maxInferenceCents: 500
|
|
13201
|
+
};
|
|
13202
|
+
var AGENT_TOOL_NAME_RE = /^[A-Za-z0-9_-]{1,64}$/;
|
|
13203
|
+
var BlueprintAgentToolSchema = z.object({
|
|
13204
|
+
/** The tool's name in the platform's tool catalog. Checked against the catalog when the blueprint is applied. */
|
|
13205
|
+
name: z.string().regex(AGENT_TOOL_NAME_RE, {
|
|
13206
|
+
message: "a tool name is 1-64 characters of letters, digits, underscores ('_') and hyphens ('-')"
|
|
13207
|
+
}),
|
|
13208
|
+
/** Required `true` for a tool that creates, changes or deletes data. */
|
|
13209
|
+
allowWrites: z.boolean().optional()
|
|
13210
|
+
}).strict();
|
|
13211
|
+
var BlueprintAgentEntrySchema = z.object({
|
|
13212
|
+
description: z.string().max(AGENT_LIMITS.descriptionChars).optional(),
|
|
13213
|
+
/**
|
|
13214
|
+
* Whose identity the agent's tool calls run with. `CALLER` (the caller who starts the run) is the only value the platform accepts
|
|
13215
|
+
* today. Checked here for its SHAPE only, never against a list: the set will grow, and the platform refuses a value it does not
|
|
13216
|
+
* support when the blueprint is applied, so an older copy of this package never refuses a value a newer platform accepts.
|
|
13217
|
+
*/
|
|
13218
|
+
authority: z.string().regex(/^[A-Z][A-Z_]{0,31}$/, { message: "authority is an upper-case name such as 'CALLER'" }).optional(),
|
|
13219
|
+
/** The model the agent runs on, by its id in the model catalog. */
|
|
13220
|
+
// Shown in the plan preview, so held to the same rule as every other displayed text.
|
|
13221
|
+
model: safeDisplayString(z.string().min(1).max(AGENT_LIMITS.modelChars)),
|
|
13222
|
+
/** The system prompt. */
|
|
13223
|
+
system: z.string().max(AGENT_LIMITS.systemChars).optional(),
|
|
13224
|
+
/** The tool whitelist. */
|
|
13225
|
+
tools: z.array(BlueprintAgentToolSchema).max(AGENT_LIMITS.tools).optional(),
|
|
13226
|
+
limits: z.object({
|
|
13227
|
+
maxSteps: z.number().int().min(1).max(AGENT_LIMITS.maxSteps).optional(),
|
|
13228
|
+
maxTokensPerCall: z.number().int().min(AGENT_LIMITS.minTokensPerCall).max(AGENT_LIMITS.maxTokensPerCall).optional(),
|
|
13229
|
+
maxToolCalls: z.number().int().min(0).max(AGENT_LIMITS.maxToolCalls).optional(),
|
|
13230
|
+
maxWrites: z.number().int().min(0).max(AGENT_LIMITS.maxWrites).optional()
|
|
13231
|
+
}).strict().optional(),
|
|
13232
|
+
budget: z.object({
|
|
13233
|
+
/** US cents per run, up to 500. The platform also enforces a minimum and refuses a lower value when the blueprint is applied. */
|
|
13234
|
+
maxInferenceCents: z.number().int().min(1).max(AGENT_LIMITS.maxInferenceCents).optional()
|
|
13235
|
+
}).strict().optional()
|
|
13236
|
+
}).strict();
|
|
13237
|
+
var BlueprintAgentsSchema = z.record(safeDisplayString(z.string()), BlueprintAgentEntrySchema);
|
|
13238
|
+
function assertSupportedFormatVersion(input) {
|
|
13239
|
+
if (input === null || typeof input !== "object") return;
|
|
13240
|
+
const declared = input.formatVersion;
|
|
13241
|
+
if (typeof declared === "number" && Number.isInteger(declared) && declared > BLUEPRINT_FORMAT_VERSION) {
|
|
13242
|
+
throw new BlueprintValidationError(
|
|
13243
|
+
`This blueprint uses format version ${declared}; this tool supports format versions up to ${BLUEPRINT_FORMAT_VERSION}. Upgrade @vectros-ai/cli and @vectros-ai/blueprints and try again.`,
|
|
13244
|
+
[{ path: "formatVersion", message: `unsupported format version ${declared}` }]
|
|
13245
|
+
);
|
|
13246
|
+
}
|
|
13247
|
+
}
|
|
13248
|
+
function lintAgents(value, ctx) {
|
|
13249
|
+
const bp = value;
|
|
13250
|
+
const agents = bp.agents;
|
|
13251
|
+
if (!agents) return;
|
|
13252
|
+
if ((bp.formatVersion ?? 1) < 2) {
|
|
13253
|
+
ctx.addIssue({
|
|
13254
|
+
code: z.ZodIssueCode.custom,
|
|
13255
|
+
path: ["formatVersion"],
|
|
13256
|
+
message: "an 'agents' section needs 'formatVersion: 2' (so a tool that reads an older format refuses the file instead of misreading it)"
|
|
13257
|
+
});
|
|
13258
|
+
}
|
|
13259
|
+
for (const [name, agent] of Object.entries(agents)) {
|
|
13260
|
+
if (!SCRIPT_NAME_RE.test(name)) {
|
|
13261
|
+
ctx.addIssue({
|
|
13262
|
+
code: z.ZodIssueCode.custom,
|
|
13263
|
+
path: ["agents", name],
|
|
13264
|
+
message: `agents['${displaySafe(name, 80)}'] is not a valid agent name \u2014 use 1-64 characters of letters, digits, underscores ('_') and hyphens ('-'). The map key IS the name the platform stores.`
|
|
13265
|
+
});
|
|
13266
|
+
}
|
|
13267
|
+
const tools = agent?.tools ?? [];
|
|
13268
|
+
const seen = /* @__PURE__ */ new Set();
|
|
13269
|
+
tools.forEach((tool, i) => {
|
|
13270
|
+
if (seen.has(tool.name)) {
|
|
13271
|
+
ctx.addIssue({
|
|
13272
|
+
code: z.ZodIssueCode.custom,
|
|
13273
|
+
path: ["agents", name, "tools", i, "name"],
|
|
13274
|
+
message: `tool '${displaySafe(tool.name, 80)}' is listed twice`
|
|
13275
|
+
});
|
|
13276
|
+
}
|
|
13277
|
+
seen.add(tool.name);
|
|
13278
|
+
});
|
|
13279
|
+
if (tools.some((t) => t.allowWrites === true) && (agent?.limits?.maxWrites ?? 0) < 1) {
|
|
13280
|
+
ctx.addIssue({
|
|
13281
|
+
code: z.ZodIssueCode.custom,
|
|
13282
|
+
path: ["agents", name, "limits", "maxWrites"],
|
|
13283
|
+
message: "limits.maxWrites must be at least 1 when a tool has allowWrites: true"
|
|
13284
|
+
});
|
|
13285
|
+
}
|
|
13286
|
+
if (tools.length > 0 && agent?.limits?.maxToolCalls === 0) {
|
|
13287
|
+
ctx.addIssue({
|
|
13288
|
+
code: z.ZodIssueCode.custom,
|
|
13289
|
+
path: ["agents", name, "limits", "maxToolCalls"],
|
|
13290
|
+
message: "limits.maxToolCalls must be at least 1 when tools are listed"
|
|
13291
|
+
});
|
|
13292
|
+
}
|
|
13293
|
+
}
|
|
13294
|
+
}
|
|
12594
13295
|
var IDENTITY_NAMESPACE_RE = /^[a-z][a-z0-9_-]{1,31}$/;
|
|
12595
13296
|
var FORBIDDEN_IDENTITY_NAMESPACES = /* @__PURE__ */ new Set(["record", "document", "entity", "user"]);
|
|
12596
13297
|
var IdentityKindSchema = z.string().min(1).superRefine((val, ctx) => {
|
|
@@ -12618,7 +13319,7 @@ var IdentityDeclSchema = z.object({
|
|
|
12618
13319
|
// sibling fields) — it does reach a platform API payload as an entity's `name`.
|
|
12619
13320
|
externalId: safeDisplayString(z.string().min(1)),
|
|
12620
13321
|
displayName: safeDisplayString(z.string().min(1)).optional(),
|
|
12621
|
-
metadata: z.record(z.unknown()).optional()
|
|
13322
|
+
metadata: z.record(z.string(), z.unknown()).optional()
|
|
12622
13323
|
}).strict();
|
|
12623
13324
|
var IDENTITY_NAME_RE = /^[A-Za-z_]\w*$/;
|
|
12624
13325
|
var IdentitiesDeclSchema = z.record(
|
|
@@ -12764,8 +13465,7 @@ var BlueprintIssuerSchema = z.object({
|
|
|
12764
13465
|
*/
|
|
12765
13466
|
capturedClaims: z.array(
|
|
12766
13467
|
// `safeDisplayString` (a `.refine`) then the blank-name `.refine` — both ordered AFTER
|
|
12767
|
-
// `.max`,
|
|
12768
|
-
// chains fine onto a ZodEffects — it's on the ZodType base, not ZodString-specific).
|
|
13468
|
+
// `.max`, by the same last-call convention.
|
|
12769
13469
|
// `.min(1)` would be a LENGTH check and would admit a whitespace-only name while the
|
|
12770
13470
|
// message claims "non-blank"; the platform rejects on isBlank(). Note the value is
|
|
12771
13471
|
// REJECTED, not trimmed — it reaches the wire exactly as authored, matching how the
|
|
@@ -12978,7 +13678,7 @@ var RecordSeedSchema = z.object({
|
|
|
12978
13678
|
surface: z.literal("record"),
|
|
12979
13679
|
...SeedCommonShape,
|
|
12980
13680
|
/** The record payload, validated against the bound schema. */
|
|
12981
|
-
fields: z.record(z.unknown())
|
|
13681
|
+
fields: z.record(z.string(), z.unknown())
|
|
12982
13682
|
}).strict();
|
|
12983
13683
|
var DocumentSeedSchema = z.object({
|
|
12984
13684
|
surface: z.literal("document"),
|
|
@@ -12988,7 +13688,7 @@ var DocumentSeedSchema = z.object({
|
|
|
12988
13688
|
/** Raw text content to ingest + index — REQUIRED and non-empty (the platform rejects a blank ingest). */
|
|
12989
13689
|
text: z.string().min(1),
|
|
12990
13690
|
/** Optional structured payload bound to the schema (the document's `fields`). */
|
|
12991
|
-
fields: z.record(z.unknown()).optional()
|
|
13691
|
+
fields: z.record(z.string(), z.unknown()).optional()
|
|
12992
13692
|
}).strict();
|
|
12993
13693
|
var BlueprintSeedRecordSchema = z.discriminatedUnion("surface", [
|
|
12994
13694
|
RecordSeedSchema,
|
|
@@ -13613,6 +14313,8 @@ var BLUEPRINT_FIELD_PHASES = Object.freeze({
|
|
|
13613
14313
|
// principal/AccessProfile pairs instead of one), so it applies in the identical in-context phase,
|
|
13614
14314
|
// under the same re-minted context-confined token.
|
|
13615
14315
|
triggers: "in-context",
|
|
14316
|
+
// Written with the same in-context, context-confined token as scripts and triggers.
|
|
14317
|
+
agents: "in-context",
|
|
13616
14318
|
// Setting it requires the platform provisioning capability, which only the bootstrap credential
|
|
13617
14319
|
// carries — the same reasoning as issuers/namespaces above. It is applied to the app context
|
|
13618
14320
|
// itself, which the bootstrap phase already creates, so there is nothing for the in-context pass
|
|
@@ -13739,7 +14441,7 @@ var BlueprintSchema = z.object({
|
|
|
13739
14441
|
// safeDisplayString: rendered raw into buildPlan's context step (`identity projection:
|
|
13740
14442
|
// [...]`) — same rendered-plan-preview shape as `capturedClaims` (its structural pair);
|
|
13741
14443
|
// the same ordering rule applies (safeDisplayString's own `.refine` before the blank-name `.refine` below,
|
|
13742
|
-
// both after `.max
|
|
14444
|
+
// both after `.max`, by the last-call convention).
|
|
13743
14445
|
safeDisplayString(
|
|
13744
14446
|
z.string().max(MAX_CAPTURED_CLAIM_NAME_LENGTH, {
|
|
13745
14447
|
message: `an identityProjectionClaims entry must be at most ${MAX_CAPTURED_CLAIM_NAME_LENGTH} characters`
|
|
@@ -13770,7 +14472,16 @@ var BlueprintSchema = z.object({
|
|
|
13770
14472
|
* composes) are live authority, not a declaration awaiting a future release. Author the grant as
|
|
13771
14473
|
* narrowly as you would any other credential's.
|
|
13772
14474
|
*/
|
|
13773
|
-
triggers: BlueprintTriggersSchema.optional()
|
|
14475
|
+
triggers: BlueprintTriggersSchema.optional(),
|
|
14476
|
+
/**
|
|
14477
|
+
* The blueprint format version; absent means 1. A blueprint with an `agents` section declares 2.
|
|
14478
|
+
*/
|
|
14479
|
+
formatVersion: z.number().int().min(1).max(BLUEPRINT_FORMAT_VERSION).optional(),
|
|
14480
|
+
/**
|
|
14481
|
+
* Optional stored agents: agentName -> definition. A run of an agent calls tools with the identity
|
|
14482
|
+
* and permissions of the caller who starts the run, further limited by the agent's tool whitelist.
|
|
14483
|
+
*/
|
|
14484
|
+
agents: BlueprintAgentsSchema.optional()
|
|
13774
14485
|
}).strict().superRefine((bp, ctx) => {
|
|
13775
14486
|
lintSelfTokenPlacement(bp, ctx);
|
|
13776
14487
|
lintIdentityRefs(bp, ctx);
|
|
@@ -13789,17 +14500,46 @@ var BlueprintSchema = z.object({
|
|
|
13789
14500
|
lintTriggers(bp, ctx);
|
|
13790
14501
|
lintTriggerFields(bp, ctx);
|
|
13791
14502
|
lintScripts(bp, ctx);
|
|
14503
|
+
lintAgents(bp, ctx);
|
|
13792
14504
|
});
|
|
14505
|
+
function displaySafe(text, maxLength = Number.POSITIVE_INFINITY) {
|
|
14506
|
+
const shown = Array.from(text.replace(FORGERY_CHARS_GLOBAL_RE, "?"));
|
|
14507
|
+
return shown.length > maxLength ? `${shown.slice(0, maxLength).join("")}...` : shown.join("");
|
|
14508
|
+
}
|
|
14509
|
+
function displaySafeInline(text, maxLength = Number.POSITIVE_INFINITY) {
|
|
14510
|
+
return displaySafe(text.replace(/\r\n|[\r\n\u0085\u2028\u2029]/g, " "), maxLength);
|
|
14511
|
+
}
|
|
14512
|
+
function displaySafeLines(text) {
|
|
14513
|
+
return text.replace(/\r\n/g, "\n").split("\n").map((line) => displaySafe(line)).join("\n");
|
|
14514
|
+
}
|
|
14515
|
+
function neutralizeIssues(issues) {
|
|
14516
|
+
return issues.map((i) => ({ path: displaySafe(i.path), message: displaySafe(i.message) }));
|
|
14517
|
+
}
|
|
14518
|
+
function quotedKeysSafe(issue) {
|
|
14519
|
+
return displaySafe(issue.message, issue.code === "unrecognized_keys" ? 300 : Number.POSITIVE_INFINITY);
|
|
14520
|
+
}
|
|
13793
14521
|
function formatIssuePath(path4) {
|
|
13794
14522
|
let out = "";
|
|
13795
14523
|
for (const seg of path4) {
|
|
13796
14524
|
if (typeof seg === "number") out += `[${seg}]`;
|
|
13797
|
-
else out += out.length ? `.${seg}` : seg;
|
|
14525
|
+
else out += out.length ? `.${displaySafe(String(seg))}` : displaySafe(String(seg));
|
|
13798
14526
|
}
|
|
13799
14527
|
return out.length ? out : "(root)";
|
|
13800
14528
|
}
|
|
14529
|
+
function flattenZodIssues(error) {
|
|
14530
|
+
const out = [];
|
|
14531
|
+
for (const i of error.issues) {
|
|
14532
|
+
const nested = i.code === "invalid_key" ? i.issues : void 0;
|
|
14533
|
+
if (nested && nested.length > 0) {
|
|
14534
|
+
for (const n of nested) out.push({ path: [...i.path, ...n.path], message: quotedKeysSafe(n) });
|
|
14535
|
+
} else {
|
|
14536
|
+
out.push({ path: [...i.path], message: quotedKeysSafe(i) });
|
|
14537
|
+
}
|
|
14538
|
+
}
|
|
14539
|
+
return out;
|
|
14540
|
+
}
|
|
13801
14541
|
function toBlueprintIssues(error) {
|
|
13802
|
-
return error.
|
|
14542
|
+
return flattenZodIssues(error).map((i) => ({ path: formatIssuePath(i.path), message: i.message }));
|
|
13803
14543
|
}
|
|
13804
14544
|
function renderIssues(issues) {
|
|
13805
14545
|
return issues.map((i) => ` \u2022 ${i.path}: ${i.message}`).join("\n");
|
|
@@ -13812,12 +14552,13 @@ var BlueprintValidationError = class extends Error {
|
|
|
13812
14552
|
*/
|
|
13813
14553
|
issues;
|
|
13814
14554
|
constructor(message, issues = []) {
|
|
13815
|
-
super(message);
|
|
14555
|
+
super(displaySafeLines(message));
|
|
13816
14556
|
this.name = "BlueprintValidationError";
|
|
13817
|
-
this.issues = issues;
|
|
14557
|
+
this.issues = neutralizeIssues(issues);
|
|
13818
14558
|
}
|
|
13819
14559
|
};
|
|
13820
14560
|
function parseBlueprint(input) {
|
|
14561
|
+
assertSupportedFormatVersion(input);
|
|
13821
14562
|
const result = BlueprintSchema.safeParse(input);
|
|
13822
14563
|
if (!result.success) {
|
|
13823
14564
|
const issues = toBlueprintIssues(result.error);
|
|
@@ -13833,9 +14574,9 @@ var IDENTITY_TOKEN_RE = /\$\{\{\s*identities\.([A-Za-z_]\w*)\s*\}\}/g;
|
|
|
13833
14574
|
var BlueprintIdentityError = class extends Error {
|
|
13834
14575
|
issues;
|
|
13835
14576
|
constructor(message, issues = []) {
|
|
13836
|
-
super(message);
|
|
14577
|
+
super(displaySafeLines(message));
|
|
13837
14578
|
this.name = "BlueprintIdentityError";
|
|
13838
|
-
this.issues = issues;
|
|
14579
|
+
this.issues = neutralizeIssues(issues);
|
|
13839
14580
|
}
|
|
13840
14581
|
};
|
|
13841
14582
|
function collectIdentityReferences(value) {
|
|
@@ -13873,8 +14614,8 @@ async function resolveBlueprintIdentities(raw, resolve) {
|
|
|
13873
14614
|
if (!parsed.success) {
|
|
13874
14615
|
throw new BlueprintIdentityError(
|
|
13875
14616
|
"Blueprint identities block is invalid",
|
|
13876
|
-
parsed.error.
|
|
13877
|
-
path: i.path.length ? `identities.${i.path.join(".")}` : "identities",
|
|
14617
|
+
flattenZodIssues(parsed.error).map((i) => ({
|
|
14618
|
+
path: i.path.length ? `identities.${i.path.map(String).join(".")}` : "identities",
|
|
13878
14619
|
message: i.message
|
|
13879
14620
|
}))
|
|
13880
14621
|
);
|
|
@@ -13883,7 +14624,7 @@ async function resolveBlueprintIdentities(raw, resolve) {
|
|
|
13883
14624
|
const undeclared = refs.filter((name) => !(name in declared));
|
|
13884
14625
|
if (undeclared.length) {
|
|
13885
14626
|
throw new BlueprintIdentityError(
|
|
13886
|
-
`Blueprint references undeclared identities: ${undeclared.join(", ")}`,
|
|
14627
|
+
`Blueprint references undeclared identities: ${undeclared.map((n) => displaySafe(n, 80)).join(", ")}`,
|
|
13887
14628
|
undeclared.map((name) => ({
|
|
13888
14629
|
path: `identities.${name}`,
|
|
13889
14630
|
message: `'\${{ identities.${name} }}' is referenced but not declared in the 'identities' block`
|
|
@@ -13900,8 +14641,11 @@ async function resolveBlueprintIdentities(raw, resolve) {
|
|
|
13900
14641
|
idMap[name] = id;
|
|
13901
14642
|
} catch (err) {
|
|
13902
14643
|
throw new BlueprintIdentityError(
|
|
13903
|
-
`Failed to resolve identity '${name}' (${decl.kind} externalId=${decl.externalId}): ${
|
|
13904
|
-
|
|
14644
|
+
`Failed to resolve identity '${displaySafe(name, 80)}' (${decl.kind} externalId=${displaySafe(decl.externalId, 80)}): ${displaySafeInline(
|
|
14645
|
+
err instanceof Error ? err.message : String(err),
|
|
14646
|
+
500
|
|
14647
|
+
)}`,
|
|
14648
|
+
[{ path: `identities.${name}`, message: displaySafeInline(err instanceof Error ? err.message : String(err), 500) }]
|
|
13905
14649
|
);
|
|
13906
14650
|
}
|
|
13907
14651
|
}
|
|
@@ -13914,13 +14658,13 @@ var InputDeclSchema = z2.object({
|
|
|
13914
14658
|
default: z2.union([z2.string(), z2.number(), z2.boolean()]).optional(),
|
|
13915
14659
|
description: z2.string().optional()
|
|
13916
14660
|
}).strict();
|
|
13917
|
-
var InputsDeclSchema = z2.record(InputDeclSchema);
|
|
14661
|
+
var InputsDeclSchema = z2.record(z2.string(), InputDeclSchema);
|
|
13918
14662
|
var BlueprintInputError = class extends Error {
|
|
13919
14663
|
issues;
|
|
13920
14664
|
constructor(message, issues = []) {
|
|
13921
|
-
super(message);
|
|
14665
|
+
super(displaySafeLines(message));
|
|
13922
14666
|
this.name = "BlueprintInputError";
|
|
13923
|
-
this.issues = issues;
|
|
14667
|
+
this.issues = neutralizeIssues(issues);
|
|
13924
14668
|
}
|
|
13925
14669
|
};
|
|
13926
14670
|
function renderIssues2(issues) {
|
|
@@ -14112,7 +14856,7 @@ function resolveBlueprintInputs(raw, supplied = {}) {
|
|
|
14112
14856
|
const parsed = InputsDeclSchema.safeParse(rawInputs);
|
|
14113
14857
|
if (!parsed.success) {
|
|
14114
14858
|
for (const i of parsed.error.issues) {
|
|
14115
|
-
const p = i.path.length ? `inputs.${i.path.join(".")}` : "inputs";
|
|
14859
|
+
const p = i.path.length ? `inputs.${i.path.map(String).join(".")}` : "inputs";
|
|
14116
14860
|
issues.push({ path: p, message: i.message });
|
|
14117
14861
|
}
|
|
14118
14862
|
} else {
|
|
@@ -14149,8 +14893,9 @@ function resolveBlueprintInputs(raw, supplied = {}) {
|
|
|
14149
14893
|
const ctx = { declared, values, builtins, issues };
|
|
14150
14894
|
const substituted = substituteValue(body, ctx, "");
|
|
14151
14895
|
if (issues.length) {
|
|
14896
|
+
const clean = neutralizeIssues(issues);
|
|
14152
14897
|
throw new BlueprintInputError(`Blueprint variable resolution failed:
|
|
14153
|
-
${renderIssues2(
|
|
14898
|
+
${renderIssues2(clean)}`, clean);
|
|
14154
14899
|
}
|
|
14155
14900
|
return substituted;
|
|
14156
14901
|
}
|
|
@@ -15755,6 +16500,48 @@ function createLogger2(ctx = {}) {
|
|
|
15755
16500
|
);
|
|
15756
16501
|
}
|
|
15757
16502
|
|
|
16503
|
+
// src/terminal-text.ts
|
|
16504
|
+
var STRIPPED = /[\p{Cc}\p{Cf}\p{Zl}\p{Zp}]/gu;
|
|
16505
|
+
function stripControlCharacters(value, maxLength = 200) {
|
|
16506
|
+
const characters = Array.from(value.replace(STRIPPED, ""));
|
|
16507
|
+
return characters.length > maxLength ? `${characters.slice(0, maxLength).join("")}...` : characters.join("");
|
|
16508
|
+
}
|
|
16509
|
+
function singleLine(value, maxLength = 200) {
|
|
16510
|
+
return stripControlCharacters(value.replace(/\r\n|[\r\n\u0085\u2028\u2029\t\v\f]/g, " "), maxLength);
|
|
16511
|
+
}
|
|
16512
|
+
function cleanBlock(value, maxLineLength = 2e3, limits = {}) {
|
|
16513
|
+
const maxLines = limits.maxLines ?? 200;
|
|
16514
|
+
const maxChars = limits.maxChars ?? 2e4;
|
|
16515
|
+
const lines = value.split(/\r\n|[\r\n\u0085\u2028\u2029]/);
|
|
16516
|
+
let cut = lines.length > maxLines;
|
|
16517
|
+
let text = lines.slice(0, maxLines).map((line) => stripControlCharacters(line.replace(/\t/g, " "), maxLineLength)).join("\n");
|
|
16518
|
+
if (text.length > maxChars) {
|
|
16519
|
+
text = text.slice(0, maxChars);
|
|
16520
|
+
cut = true;
|
|
16521
|
+
}
|
|
16522
|
+
return cut ? `${text}
|
|
16523
|
+
[truncated]` : text;
|
|
16524
|
+
}
|
|
16525
|
+
function shortPath(value, maxLength = 300) {
|
|
16526
|
+
const clean = Array.from(singleLine(value, Number.MAX_SAFE_INTEGER));
|
|
16527
|
+
if (clean.length <= maxLength) return clean.join("");
|
|
16528
|
+
const head = Math.floor((maxLength - 3) / 3);
|
|
16529
|
+
return `${clean.slice(0, head).join("")}...${clean.slice(clean.length - (maxLength - 3 - head)).join("")}`;
|
|
16530
|
+
}
|
|
16531
|
+
function errorText(err) {
|
|
16532
|
+
return cleanBlock(err instanceof Error ? err.message : String(err));
|
|
16533
|
+
}
|
|
16534
|
+
function quotedBlock(value, indent = " ") {
|
|
16535
|
+
return cleanBlock(value).split("\n").map((line, i) => i === 0 || line === "" ? line : `${indent}${line}`).join("\n");
|
|
16536
|
+
}
|
|
16537
|
+
function asText(value, fallback = "(unprintable)") {
|
|
16538
|
+
try {
|
|
16539
|
+
return String(value);
|
|
16540
|
+
} catch {
|
|
16541
|
+
return fallback;
|
|
16542
|
+
}
|
|
16543
|
+
}
|
|
16544
|
+
|
|
15758
16545
|
// src/output.ts
|
|
15759
16546
|
function formatJson(value) {
|
|
15760
16547
|
return JSON.stringify(value, null, 2);
|
|
@@ -15796,19 +16583,19 @@ function extractServerError(err) {
|
|
|
15796
16583
|
if (e.body && typeof e.body === "object") {
|
|
15797
16584
|
const b = e.body;
|
|
15798
16585
|
if (typeof b.message === "string" && b.message.trim()) serverMessage = b.message.trim();
|
|
15799
|
-
if (typeof b.requestId === "string" && b.requestId.trim()) requestId = b.requestId.trim();
|
|
15800
|
-
if (typeof b.errorCode === "string" && b.errorCode.trim()) errorCode = b.errorCode.trim();
|
|
16586
|
+
if (typeof b.requestId === "string" && b.requestId.trim()) requestId = singleLine(b.requestId.trim(), 100);
|
|
16587
|
+
if (typeof b.errorCode === "string" && b.errorCode.trim()) errorCode = singleLine(b.errorCode.trim(), 100);
|
|
15801
16588
|
} else if (typeof e.body === "string" && e.body.trim()) {
|
|
15802
16589
|
serverMessage = e.body.trim();
|
|
15803
16590
|
}
|
|
15804
|
-
if (serverMessage) return { statusCode, message: serverMessage, requestId, errorCode };
|
|
16591
|
+
if (serverMessage) return { statusCode, message: cleanBlock(serverMessage), requestId, errorCode };
|
|
15805
16592
|
const message = typeof e.message === "string" ? e.message : "";
|
|
15806
16593
|
const isSdkEnvelope = /\nStatus code:/.test(message);
|
|
15807
|
-
if (message && !isSdkEnvelope) return { statusCode, message, requestId, errorCode };
|
|
16594
|
+
if (message && !isSdkEnvelope) return { statusCode, message: cleanBlock(message), requestId, errorCode };
|
|
15808
16595
|
if (statusCode) return { statusCode, message: `HTTP ${statusCode}`, requestId, errorCode };
|
|
15809
|
-
if (message) return { statusCode, message, requestId, errorCode };
|
|
16596
|
+
if (message) return { statusCode, message: cleanBlock(message), requestId, errorCode };
|
|
15810
16597
|
}
|
|
15811
|
-
return { message: String(err) };
|
|
16598
|
+
return { message: cleanBlock(String(err)) };
|
|
15812
16599
|
}
|
|
15813
16600
|
function formatError(err) {
|
|
15814
16601
|
return extractServerError(err).message;
|
|
@@ -16026,6 +16813,33 @@ var PROVISION_OPTIONS = [
|
|
|
16026
16813
|
]
|
|
16027
16814
|
}
|
|
16028
16815
|
},
|
|
16816
|
+
{
|
|
16817
|
+
flag: "--allow-agent-adoption",
|
|
16818
|
+
stacked: true,
|
|
16819
|
+
modes: ["bootstrap", "apply"],
|
|
16820
|
+
help: {
|
|
16821
|
+
bootstrap: [
|
|
16822
|
+
"Required to write a blueprint's agent over an agent that already exists",
|
|
16823
|
+
"in this context but carries no blueprint mark (made by hand, or by a tool",
|
|
16824
|
+
"that set none) \u2014 without it, bootstrap refuses for that name, naming it,",
|
|
16825
|
+
"and writes nothing to the agent. Adopting marks the agent as this",
|
|
16826
|
+
"blueprint's and writes the declared definition as its next version if it",
|
|
16827
|
+
"differs, so every run using its latest version changes. Never needed for a",
|
|
16828
|
+
"brand-new agent, or one this blueprint already manages. Does NOT help if",
|
|
16829
|
+
"the agent is managed by a DIFFERENT blueprint \u2014 that is always refused."
|
|
16830
|
+
],
|
|
16831
|
+
apply: [
|
|
16832
|
+
"Required to write a blueprint's agent over an agent that already exists",
|
|
16833
|
+
"in this context but carries no blueprint mark (made by hand, or by a tool",
|
|
16834
|
+
"that set none) \u2014 without it, apply refuses for that name, naming it,",
|
|
16835
|
+
"and writes nothing to the agent. Adopting marks the agent as this",
|
|
16836
|
+
"blueprint's and writes the declared definition as its next version if it",
|
|
16837
|
+
"differs, so every run using its latest version changes. Never needed for a",
|
|
16838
|
+
"brand-new agent, or one this blueprint already manages. Does NOT help if",
|
|
16839
|
+
"the agent is managed by a DIFFERENT blueprint \u2014 that is always refused."
|
|
16840
|
+
]
|
|
16841
|
+
}
|
|
16842
|
+
},
|
|
16029
16843
|
{
|
|
16030
16844
|
flag: "--tenant",
|
|
16031
16845
|
value: "test|live",
|
|
@@ -16167,16 +16981,24 @@ var PROVISION_OPTIONS = [
|
|
|
16167
16981
|
"\u2014 e.g. a deliberate second machine for an integration you already own.",
|
|
16168
16982
|
"Refused by default so an unexpected collision halts a CI run loudly",
|
|
16169
16983
|
"instead of silently minting a credential bound to the wrong identity.",
|
|
16170
|
-
"
|
|
16984
|
+
"Not needed for --rotate or an ordinary same-machine re-apply of the",
|
|
16985
|
+
"blueprint's own service principal (a trigger principal is the exception, below).",
|
|
16986
|
+
"Also required the same way when a trigger's service principal already exists",
|
|
16987
|
+
"and this blueprint did not mark it (made by an earlier release, or by hand).",
|
|
16988
|
+
"The first confirmation marks it as this blueprint's (needs users:u); then it is not asked again."
|
|
16171
16989
|
],
|
|
16172
16990
|
apply: [
|
|
16173
16991
|
"Required (with --yes, or with no TTY) when the blueprint's service principal",
|
|
16174
16992
|
"already exists in your tenant. Apply never reads a local key record, so that is",
|
|
16175
16993
|
"every apply once the principal exists, on any machine, including one that ran",
|
|
16176
|
-
"bootstrap. It lets apply rewrite that principal's access profile
|
|
16177
|
-
"
|
|
16178
|
-
"
|
|
16179
|
-
"
|
|
16994
|
+
"bootstrap. It lets apply rewrite that principal's access profile. It also",
|
|
16995
|
+
"covers a trigger's service principal that already exists and that this",
|
|
16996
|
+
"blueprint did not mark (made by an earlier release, or by hand): the first",
|
|
16997
|
+
"confirmation marks it as this blueprint's (needs users:u; apply says if it",
|
|
16998
|
+
"could not), and it is not asked again. In a",
|
|
16999
|
+
"pipeline this flag is STANDING consent: it also waves through a blueprint whose",
|
|
17000
|
+
"servicePrincipal.externalId or trigger name collides with an unrelated identity,",
|
|
17001
|
+
"so keep the blueprint's source reviewed.",
|
|
16180
17002
|
"Every run that proceeds prints which principal it reused and why."
|
|
16181
17003
|
]
|
|
16182
17004
|
}
|
|
@@ -16492,13 +17314,6 @@ import { mkdir, readFile, writeFile, rm } from "fs/promises";
|
|
|
16492
17314
|
import { homedir } from "os";
|
|
16493
17315
|
import { join as join2 } from "path";
|
|
16494
17316
|
|
|
16495
|
-
// src/terminal-text.ts
|
|
16496
|
-
var STRIPPED = /[\p{Cc}\p{Cf}\p{Zl}\p{Zp}]/gu;
|
|
16497
|
-
function stripControlCharacters(value, maxLength = 200) {
|
|
16498
|
-
const characters = Array.from(value.replace(STRIPPED, ""));
|
|
16499
|
-
return characters.length > maxLength ? `${characters.slice(0, maxLength).join("")}...` : characters.join("");
|
|
16500
|
-
}
|
|
16501
|
-
|
|
16502
17317
|
// src/pkce.ts
|
|
16503
17318
|
import { createHash, randomBytes, timingSafeEqual } from "crypto";
|
|
16504
17319
|
|
|
@@ -16799,10 +17614,10 @@ function parseDocument(raw, filePath) {
|
|
|
16799
17614
|
const ext = extname(filePath).toLowerCase();
|
|
16800
17615
|
if (ext === ".yaml" || ext === ".yml") {
|
|
16801
17616
|
try {
|
|
16802
|
-
return parseYaml(raw);
|
|
17617
|
+
return parseYaml(raw, { logLevel: "error" });
|
|
16803
17618
|
} catch (err) {
|
|
16804
17619
|
if (err instanceof YAMLParseError) {
|
|
16805
|
-
throw new Error(`Blueprint YAML is invalid in '${filePath}': ${err.message}`);
|
|
17620
|
+
throw new Error(`Blueprint YAML is invalid in '${shortPath(filePath, 300)}': ${quotedBlock(err.message)}`);
|
|
16806
17621
|
}
|
|
16807
17622
|
throw err;
|
|
16808
17623
|
}
|
|
@@ -16811,7 +17626,7 @@ function parseDocument(raw, filePath) {
|
|
|
16811
17626
|
return JSON.parse(raw);
|
|
16812
17627
|
} catch (err) {
|
|
16813
17628
|
throw new BlueprintValidationError(
|
|
16814
|
-
`Blueprint is not valid JSON in '${filePath}': ${err instanceof Error ? err.message : String(err)}`
|
|
17629
|
+
`Blueprint is not valid JSON in '${shortPath(filePath, 300)}': ${quotedBlock(err instanceof Error ? err.message : String(err))}`
|
|
16815
17630
|
);
|
|
16816
17631
|
}
|
|
16817
17632
|
}
|
|
@@ -16821,7 +17636,7 @@ async function loadBlueprintFile(filePath, supplied = {}) {
|
|
|
16821
17636
|
raw = await readFile2(filePath, "utf8");
|
|
16822
17637
|
} catch (err) {
|
|
16823
17638
|
const msg = err instanceof Error ? err.message : String(err);
|
|
16824
|
-
throw new Error(`Cannot read blueprint file '${filePath}': ${msg}`);
|
|
17639
|
+
throw new Error(`Cannot read blueprint file '${shortPath(filePath, 300)}': ${quotedBlock(msg)}`);
|
|
16825
17640
|
}
|
|
16826
17641
|
const parsed = parseDocument(raw, filePath);
|
|
16827
17642
|
const resolved = resolveBlueprintInputs(parsed, supplied);
|
|
@@ -16843,11 +17658,11 @@ async function loadValuesFile(filePath) {
|
|
|
16843
17658
|
try {
|
|
16844
17659
|
raw = await readFile2(filePath, "utf8");
|
|
16845
17660
|
} catch (err) {
|
|
16846
|
-
throw new Error(`Cannot read --values file '${filePath}': ${err instanceof Error ? err.message : String(err)}`);
|
|
17661
|
+
throw new Error(`Cannot read --values file '${shortPath(filePath, 300)}': ${quotedBlock(err instanceof Error ? err.message : String(err))}`);
|
|
16847
17662
|
}
|
|
16848
17663
|
const parsed = parseDocument(raw, filePath);
|
|
16849
17664
|
if (parsed === null || typeof parsed !== "object" || Array.isArray(parsed)) {
|
|
16850
|
-
throw new Error(`--values file '${filePath}' must be a map of name: value pairs.`);
|
|
17665
|
+
throw new Error(`--values file '${shortPath(filePath, 300)}' must be a map of name: value pairs.`);
|
|
16851
17666
|
}
|
|
16852
17667
|
return parsed;
|
|
16853
17668
|
}
|
|
@@ -16866,7 +17681,8 @@ var DATA_PLANE_ALLOWLIST = [
|
|
|
16866
17681
|
"documents",
|
|
16867
17682
|
"folders",
|
|
16868
17683
|
"inference",
|
|
16869
|
-
"entities"
|
|
17684
|
+
"entities",
|
|
17685
|
+
"runs"
|
|
16870
17686
|
];
|
|
16871
17687
|
var KNOWN_CONTROL_PLANE_RESOURCES = [
|
|
16872
17688
|
"keys",
|
|
@@ -16908,7 +17724,10 @@ var KNOWN_CONTROL_PLANE_RESOURCES = [
|
|
|
16908
17724
|
// no admitted resource has an `x` verb — adding it would instead make `records:x` pass the op
|
|
16909
17725
|
// check and be admitted as data-plane.
|
|
16910
17726
|
"scripts",
|
|
16911
|
-
"triggers"
|
|
17727
|
+
"triggers",
|
|
17728
|
+
// Stored agent definitions: authoring one steers every run of it, so the platform treats it as
|
|
17729
|
+
// control-plane on every verb. Listed for MESSAGE QUALITY, as above.
|
|
17730
|
+
"agents"
|
|
16912
17731
|
];
|
|
16913
17732
|
var PARTIALLY_ADMITTED_OPS = /* @__PURE__ */ new Map([
|
|
16914
17733
|
["profiles", /* @__PURE__ */ new Set(["r", "u", "d"])]
|
|
@@ -17146,6 +17965,9 @@ function assertBlueprintGates(blueprint) {
|
|
|
17146
17965
|
}
|
|
17147
17966
|
}
|
|
17148
17967
|
for (const [triggerName, entry] of Object.entries(blueprint.triggers ?? {})) {
|
|
17968
|
+
const carried = entry.allowedActions ?? (entry.roleIds ?? []).flatMap((id) => (blueprint.roles?.[id] ?? []).flatMap((clause) => clause.allowedActions));
|
|
17969
|
+
const refused = triggerRefusedGrants(carried);
|
|
17970
|
+
if (refused.length > 0) throw withTriggerContext(new ScopeGateError(refused), triggerName);
|
|
17149
17971
|
if (entry.allowedActions === void 0) continue;
|
|
17150
17972
|
try {
|
|
17151
17973
|
assertScopeGate(entry.allowedActions, entry.capabilities);
|
|
@@ -17184,6 +18006,20 @@ function assertScriptGrantsNameADeclaredScript(blueprint) {
|
|
|
17184
18006
|
}
|
|
17185
18007
|
if (rejected.length > 0) throw new ScopeGateError(rejected);
|
|
17186
18008
|
}
|
|
18009
|
+
var TRIGGER_REFUSED_RESOURCES = /* @__PURE__ */ new Set(["runs"]);
|
|
18010
|
+
function triggerRefusedGrants(actions) {
|
|
18011
|
+
const out = [];
|
|
18012
|
+
for (const action of actions) {
|
|
18013
|
+
const resource = action.trim().split(":")[0];
|
|
18014
|
+
if (TRIGGER_REFUSED_RESOURCES.has(resource)) {
|
|
18015
|
+
out.push({
|
|
18016
|
+
action,
|
|
18017
|
+
reason: `requests '${resource}' for a trigger. A trigger principal is unattended and fires on every matching write, so it may not be granted '${resource}'; grant it to a role or the blueprint's own credential instead`
|
|
18018
|
+
});
|
|
18019
|
+
}
|
|
18020
|
+
}
|
|
18021
|
+
return out;
|
|
18022
|
+
}
|
|
17187
18023
|
function withTriggerContext(err, triggerName) {
|
|
17188
18024
|
if (err instanceof ScopeGateError || err instanceof CapabilityGateError) {
|
|
17189
18025
|
for (const r of err.rejected) r.source ??= `trigger '${triggerName}'`;
|
|
@@ -17481,6 +18317,45 @@ async function drainPages(fetch2) {
|
|
|
17481
18317
|
return out;
|
|
17482
18318
|
}
|
|
17483
18319
|
|
|
18320
|
+
// src/agent-api.ts
|
|
18321
|
+
var AGENT_NAME_RE = /^[A-Za-z0-9_-]{1,64}$/;
|
|
18322
|
+
var APPLY_AGENT_SCOPES = "'agents:c', plus 'agents:u' and 'agents:r' when the agent already exists (changed or not)";
|
|
18323
|
+
function agentErrorHint(err, contextId, scopes) {
|
|
18324
|
+
const info = extractServerError(err);
|
|
18325
|
+
if (info.statusCode === 403) {
|
|
18326
|
+
return `This needs the ${scopes} scope on the credential, in context '${contextId}'. Grant it to the principal doing this, or use a credential that holds it.`;
|
|
18327
|
+
}
|
|
18328
|
+
if (info.statusCode === 400 && /already in use/i.test(info.message)) {
|
|
18329
|
+
return "If the agent exists, this credential may be unable to read it: re-applying or writing over an existing agent needs 'agents:r' and 'agents:u' as well as 'agents:c'.";
|
|
18330
|
+
}
|
|
18331
|
+
if (info.errorCode === "AGENT_RETIRED" || info.statusCode === 409 && /\b(retired|purged)\b/i.test(info.message)) {
|
|
18332
|
+
return "A retired agent keeps its name. Restore it with `vectros agents version <id> --restore --file <definition>` (a purged agent cannot be restored), or remove the agent from the blueprint.";
|
|
18333
|
+
}
|
|
18334
|
+
return "";
|
|
18335
|
+
}
|
|
18336
|
+
function agentRequestError(err, subject, contextId, scopes) {
|
|
18337
|
+
const info = extractServerError(err);
|
|
18338
|
+
const hint = agentErrorHint(err, contextId, scopes);
|
|
18339
|
+
const message = singleLine(info.message, 500);
|
|
18340
|
+
if (info.statusCode === 403) {
|
|
18341
|
+
return new Error(`${subject}: the platform refused the request (403). ${hint} Platform message: ${message}`, { cause: err });
|
|
18342
|
+
}
|
|
18343
|
+
return new Error(`${subject}: ${message}${hint ? ` ${hint}` : ""}`, { cause: err });
|
|
18344
|
+
}
|
|
18345
|
+
function sanitizedAgentError(err) {
|
|
18346
|
+
const info = extractServerError(err);
|
|
18347
|
+
const message = singleLine(info.message, 500);
|
|
18348
|
+
return Object.assign(new Error(message), {
|
|
18349
|
+
statusCode: info.statusCode,
|
|
18350
|
+
body: {
|
|
18351
|
+
message,
|
|
18352
|
+
requestId: info.requestId === void 0 ? void 0 : singleLine(info.requestId, 100),
|
|
18353
|
+
errorCode: info.errorCode === void 0 ? void 0 : singleLine(info.errorCode, 100)
|
|
18354
|
+
},
|
|
18355
|
+
cause: err
|
|
18356
|
+
});
|
|
18357
|
+
}
|
|
18358
|
+
|
|
17484
18359
|
// src/shell-quote.ts
|
|
17485
18360
|
function shellQuote(value) {
|
|
17486
18361
|
if (/^[A-Za-z0-9_@%+=:,./-]+$/.test(value)) return value;
|
|
@@ -17494,6 +18369,19 @@ function actionOf(created) {
|
|
|
17494
18369
|
function triggerPrincipalExternalId(blueprintName, triggerName) {
|
|
17495
18370
|
return `${blueprintName}-trigger-${triggerName}`;
|
|
17496
18371
|
}
|
|
18372
|
+
function triggerPrincipalProvenance(payload, blueprintName) {
|
|
18373
|
+
const marker = payload?.provisionedBy;
|
|
18374
|
+
if (marker === blueprintName) return { kind: "ours" };
|
|
18375
|
+
if (typeof marker === "string" && marker.length > 0) return { kind: "other-blueprint", markedBy: marker };
|
|
18376
|
+
return { kind: "unmarked" };
|
|
18377
|
+
}
|
|
18378
|
+
var SENSITIVE_MASK = "[redacted]";
|
|
18379
|
+
function payloadIsMasked(value) {
|
|
18380
|
+
if (value === SENSITIVE_MASK) return true;
|
|
18381
|
+
if (Array.isArray(value)) return value.some(payloadIsMasked);
|
|
18382
|
+
if (value !== null && typeof value === "object") return Object.values(value).some(payloadIsMasked);
|
|
18383
|
+
return false;
|
|
18384
|
+
}
|
|
17497
18385
|
var SCRIPT_CONFLICT_RETRY_DELAY_MS = 750;
|
|
17498
18386
|
function isVersionConflict(err) {
|
|
17499
18387
|
return typeof err === "object" && err !== null && err.statusCode === 409;
|
|
@@ -17547,7 +18435,7 @@ function scriptMatches(stored, declared) {
|
|
|
17547
18435
|
var ScriptOwnershipConflictError = class extends Error {
|
|
17548
18436
|
constructor(name, owner, blueprintName) {
|
|
17549
18437
|
super(
|
|
17550
|
-
`Script '${name}' is already provisioned by a different blueprint ('${owner}', this blueprint is '${blueprintName}') \u2014 refusing to push over it. No script version is pushed (anything already provisioned earlier in this same apply is unaffected). If you intended to replace that blueprint's script, do it under ITS name; if two blueprints legitimately need distinct code, give this one a different script name.`
|
|
18438
|
+
`Script '${name}' is already provisioned by a different blueprint ('${singleLine(owner, 100)}', this blueprint is '${blueprintName}') \u2014 refusing to push over it. No script version is pushed (anything already provisioned earlier in this same apply is unaffected). If you intended to replace that blueprint's script, do it under ITS name; if two blueprints legitimately need distinct code, give this one a different script name.`
|
|
17551
18439
|
);
|
|
17552
18440
|
this.name = "ScriptOwnershipConflictError";
|
|
17553
18441
|
}
|
|
@@ -17660,6 +18548,86 @@ async function ensureScripts(args) {
|
|
|
17660
18548
|
}
|
|
17661
18549
|
return actions;
|
|
17662
18550
|
}
|
|
18551
|
+
var AgentAdoptionNotAllowedError = class extends Error {
|
|
18552
|
+
constructor(agentName) {
|
|
18553
|
+
super(
|
|
18554
|
+
`Agent '${agentName}' already exists in this context and no blueprint manages it (it was made by hand, or by a tool that set no mark). Applying would mark it as this blueprint's and, if the declared definition differs, write that definition as its next version, so every run that uses the agent's latest version would start behaving differently. Nothing has been written to it, and its earlier versions stay readable. Re-run with --allow-agent-adoption once you have confirmed this blueprint should take over '${agentName}'. (Anything provisioned earlier in this same apply is unaffected.)`
|
|
18555
|
+
);
|
|
18556
|
+
this.agentName = agentName;
|
|
18557
|
+
this.name = "AgentAdoptionNotAllowedError";
|
|
18558
|
+
}
|
|
18559
|
+
agentName;
|
|
18560
|
+
};
|
|
18561
|
+
var AgentOwnershipConflictError = class extends Error {
|
|
18562
|
+
constructor(agentName, markedBy, blueprintName) {
|
|
18563
|
+
super(
|
|
18564
|
+
`Agent '${agentName}' is managed by the blueprint '${singleLine(markedBy, 100)}', not this one ('${blueprintName}'). Nothing has been written to it. An agent's mark cannot be changed, so give this blueprint's agent a different name, or apply the blueprint that manages it.`
|
|
18565
|
+
);
|
|
18566
|
+
this.agentName = agentName;
|
|
18567
|
+
this.markedBy = markedBy;
|
|
18568
|
+
this.name = "AgentOwnershipConflictError";
|
|
18569
|
+
}
|
|
18570
|
+
agentName;
|
|
18571
|
+
markedBy;
|
|
18572
|
+
};
|
|
18573
|
+
function isDefiniteRefusal(err) {
|
|
18574
|
+
const status = err?.statusCode;
|
|
18575
|
+
return typeof status === "number" && status >= 400 && status < 500 && status !== 408 && status !== 429;
|
|
18576
|
+
}
|
|
18577
|
+
async function ensureAgents(args) {
|
|
18578
|
+
const { ctxClient, agents, blueprintName, contextId, allowAgentAdoption, onAgentCreated, onAgentSeen, onAgentAttempt, onAgentRefused, log } = args;
|
|
18579
|
+
const actions = [];
|
|
18580
|
+
const trackedByCaller = onAgentAttempt !== void 0;
|
|
18581
|
+
const write = async (name, upsert) => {
|
|
18582
|
+
try {
|
|
18583
|
+
return await ctxClient.agents.createAgent(
|
|
18584
|
+
{
|
|
18585
|
+
contextId,
|
|
18586
|
+
...upsert ? { upsert: true } : {},
|
|
18587
|
+
body: { ...agents[name], name, provisionedBy: blueprintName }
|
|
18588
|
+
},
|
|
18589
|
+
// The caller is keeping track of the agents it creates so that it can remove them. A create the client retried by
|
|
18590
|
+
// itself after a gateway error would come back as an agent that already existed, hiding that this call made it.
|
|
18591
|
+
trackedByCaller ? { maxRetries: 0 } : void 0
|
|
18592
|
+
);
|
|
18593
|
+
} catch (err) {
|
|
18594
|
+
if (isDefiniteRefusal(err)) onAgentRefused?.(name);
|
|
18595
|
+
throw agentRequestError(err, `Agent '${name}'`, contextId, APPLY_AGENT_SCOPES);
|
|
18596
|
+
}
|
|
18597
|
+
};
|
|
18598
|
+
const versionOf = (name, answer) => {
|
|
18599
|
+
const version = answer.version ?? answer.latestVersion;
|
|
18600
|
+
if (!answer.id || version === void 0) {
|
|
18601
|
+
throw new Error(`Agent '${name}': the platform's answer named no agent id or version.`);
|
|
18602
|
+
}
|
|
18603
|
+
return version;
|
|
18604
|
+
};
|
|
18605
|
+
for (const name of Object.keys(agents)) {
|
|
18606
|
+
if (onAgentAttempt?.(name) === false) break;
|
|
18607
|
+
const first = await write(name, false);
|
|
18608
|
+
if (!first.created) onAgentSeen?.({ name, id: first.id });
|
|
18609
|
+
const before = versionOf(name, first);
|
|
18610
|
+
if (first.created) {
|
|
18611
|
+
onAgentCreated?.({ name, id: first.id });
|
|
18612
|
+
actions.push({ name, action: "created", id: first.id, version: before });
|
|
18613
|
+
log.debug({ component: "loader", agentName: name, agentVersion: before }, "agent created at version 1");
|
|
18614
|
+
continue;
|
|
18615
|
+
}
|
|
18616
|
+
const owner = first.provisionedBy?.trim() || void 0;
|
|
18617
|
+
if (owner !== void 0 && owner !== blueprintName) throw new AgentOwnershipConflictError(name, owner, blueprintName);
|
|
18618
|
+
const adopting = owner === void 0;
|
|
18619
|
+
if (adopting && !allowAgentAdoption) throw new AgentAdoptionNotAllowedError(name);
|
|
18620
|
+
const second = await write(name, true);
|
|
18621
|
+
const after = versionOf(name, second);
|
|
18622
|
+
const action = adopting ? "adopted" : after > before ? "versioned" : "unchanged";
|
|
18623
|
+
actions.push({ name, action, id: first.id, version: after, previousVersion: before });
|
|
18624
|
+
log.debug(
|
|
18625
|
+
{ component: "loader", agentName: name, agentVersionBefore: before, agentVersionAfter: after, action },
|
|
18626
|
+
adopting ? "agent adopted into this blueprint" : action === "versioned" ? "agent definition changed \u2014 next version written" : "agent already current"
|
|
18627
|
+
);
|
|
18628
|
+
}
|
|
18629
|
+
return actions;
|
|
18630
|
+
}
|
|
17663
18631
|
async function pruneUndeclaredTriggers(args) {
|
|
17664
18632
|
const { ctxClient, blueprintName, contextId, declaredNames, log } = args;
|
|
17665
18633
|
const pruned = [];
|
|
@@ -17857,6 +18825,26 @@ var ExistingPrincipalNotConfirmedError = class extends Error {
|
|
|
17857
18825
|
externalId;
|
|
17858
18826
|
mintsKey;
|
|
17859
18827
|
};
|
|
18828
|
+
var ExistingTriggerPrincipalNotConfirmedError = class extends Error {
|
|
18829
|
+
constructor(contextId, triggerName, userId, externalId, provenance, markedBy) {
|
|
18830
|
+
super(
|
|
18831
|
+
`The service principal for trigger '${triggerName}' (externalId '${externalId}', userId '${userId}', context '${contextId}') already existed in this tenant and ` + (provenance === "other-blueprint" ? `is marked as provisioned by the blueprint '${singleLine(markedBy ?? "", 100)}', not this one. ` : `carries no provenance marker, so it was not created by this blueprint's trigger provisioning (it may be from an earlier CLI release, or created by hand). `) + `Applying would write that identity's access profile to this blueprint's trigger grant, and nothing has been written to it yet.`
|
|
18832
|
+
);
|
|
18833
|
+
this.contextId = contextId;
|
|
18834
|
+
this.triggerName = triggerName;
|
|
18835
|
+
this.userId = userId;
|
|
18836
|
+
this.externalId = externalId;
|
|
18837
|
+
this.provenance = provenance;
|
|
18838
|
+
this.markedBy = markedBy;
|
|
18839
|
+
this.name = "ExistingTriggerPrincipalNotConfirmedError";
|
|
18840
|
+
}
|
|
18841
|
+
contextId;
|
|
18842
|
+
triggerName;
|
|
18843
|
+
userId;
|
|
18844
|
+
externalId;
|
|
18845
|
+
provenance;
|
|
18846
|
+
markedBy;
|
|
18847
|
+
};
|
|
17860
18848
|
function mapEnumValues(enumValues) {
|
|
17861
18849
|
if (!enumValues) return void 0;
|
|
17862
18850
|
return enumValues.map((value) => ({ value }));
|
|
@@ -17946,6 +18934,9 @@ var UnresolvedIdentitiesError = class extends Error {
|
|
|
17946
18934
|
this.name = "UnresolvedIdentitiesError";
|
|
17947
18935
|
}
|
|
17948
18936
|
};
|
|
18937
|
+
function declaresAgents(blueprint) {
|
|
18938
|
+
return Object.keys(blueprint.agents ?? {}).length > 0;
|
|
18939
|
+
}
|
|
17949
18940
|
function assertNoUnresolvedIdentities(blueprint) {
|
|
17950
18941
|
const hasBlock = blueprint.identities && Object.keys(blueprint.identities).length > 0;
|
|
17951
18942
|
const refs = collectIdentityReferences(blueprint);
|
|
@@ -18080,6 +19071,11 @@ async function runLoader(client, input) {
|
|
|
18080
19071
|
readExistingKey,
|
|
18081
19072
|
persistMintedKey,
|
|
18082
19073
|
allowScriptAdoption = false,
|
|
19074
|
+
allowAgentAdoption = false,
|
|
19075
|
+
onAgentCreated,
|
|
19076
|
+
onAgentSeen,
|
|
19077
|
+
onAgentAttempt,
|
|
19078
|
+
onAgentRefused,
|
|
18083
19079
|
log
|
|
18084
19080
|
} = input;
|
|
18085
19081
|
if (skipKey && rotate) {
|
|
@@ -18088,9 +19084,66 @@ async function runLoader(client, input) {
|
|
|
18088
19084
|
assertNoUnresolvedIdentities(blueprint);
|
|
18089
19085
|
const { contextAction, servicePrincipalUserId: userId, userAction, namespaceActions } = provisioned;
|
|
18090
19086
|
assertBlueprintGates(blueprint);
|
|
19087
|
+
const triggerPrincipalIds = /* @__PURE__ */ new Map();
|
|
19088
|
+
const triggerPrincipalsToMark = /* @__PURE__ */ new Map();
|
|
19089
|
+
const pendingConfirmation = [];
|
|
19090
|
+
for (const triggerName of Object.keys(blueprint.triggers ?? {})) {
|
|
19091
|
+
const externalId = triggerPrincipalExternalId(blueprint.name, triggerName);
|
|
19092
|
+
const displayName = `Trigger: ${triggerName}`;
|
|
19093
|
+
const triggerUser = await client.identity.createUser({
|
|
19094
|
+
body: { externalId, type: "SERVICE", payload: { displayName, provisionedBy: blueprint.name } }
|
|
19095
|
+
});
|
|
19096
|
+
const triggerUserId = triggerUser.id;
|
|
19097
|
+
if (!triggerUserId) {
|
|
19098
|
+
throw new Error(`Trigger '${triggerName}' service-principal create returned no user id.`);
|
|
19099
|
+
}
|
|
19100
|
+
triggerPrincipalIds.set(triggerName, triggerUserId);
|
|
19101
|
+
if (triggerUser.created) continue;
|
|
19102
|
+
const provenance = triggerPrincipalProvenance(triggerUser.payload, blueprint.name);
|
|
19103
|
+
if (provenance.kind === "ours") continue;
|
|
19104
|
+
pendingConfirmation.push({
|
|
19105
|
+
triggerName,
|
|
19106
|
+
externalId,
|
|
19107
|
+
userId: triggerUserId,
|
|
19108
|
+
userType: triggerUser.type,
|
|
19109
|
+
provenance: provenance.kind,
|
|
19110
|
+
markedBy: provenance.kind === "other-blueprint" ? provenance.markedBy : void 0
|
|
19111
|
+
});
|
|
19112
|
+
}
|
|
19113
|
+
let askedTheOperator = false;
|
|
19114
|
+
for (const p of pendingConfirmation) {
|
|
19115
|
+
let proceed = true;
|
|
19116
|
+
if (input.confirmExistingTriggerPrincipal) {
|
|
19117
|
+
askedTheOperator = true;
|
|
19118
|
+
proceed = await input.confirmExistingTriggerPrincipal({
|
|
19119
|
+
contextId: blueprint.contextId,
|
|
19120
|
+
triggerName: p.triggerName,
|
|
19121
|
+
userId: p.userId,
|
|
19122
|
+
externalId: p.externalId,
|
|
19123
|
+
provenance: p.provenance,
|
|
19124
|
+
markedBy: p.markedBy,
|
|
19125
|
+
userType: p.userType
|
|
19126
|
+
});
|
|
19127
|
+
}
|
|
19128
|
+
if (!proceed) {
|
|
19129
|
+
throw new ExistingTriggerPrincipalNotConfirmedError(
|
|
19130
|
+
blueprint.contextId,
|
|
19131
|
+
p.triggerName,
|
|
19132
|
+
p.userId,
|
|
19133
|
+
p.externalId,
|
|
19134
|
+
p.provenance,
|
|
19135
|
+
p.markedBy
|
|
19136
|
+
);
|
|
19137
|
+
}
|
|
19138
|
+
if (input.confirmExistingTriggerPrincipal) {
|
|
19139
|
+
triggerPrincipalsToMark.set(p.triggerName, { externalId: p.externalId, userId: p.userId, userType: p.userType });
|
|
19140
|
+
}
|
|
19141
|
+
}
|
|
19142
|
+
const triggerPrincipalsReused = [];
|
|
18091
19143
|
const needsContextPinnedBootstrapForIssuers = (blueprint.issuers ?? []).length > 0;
|
|
18092
|
-
const
|
|
18093
|
-
|
|
19144
|
+
const staleAfterPrompt = askedTheOperator && needsContextPinnedBootstrapForIssuers && !!input.remintBootstrapForContext;
|
|
19145
|
+
const ctxBootstrapClient = (staleAfterPrompt ? await input.remintBootstrapForContext(blueprint.contextId) : void 0) ?? provisioned.ctxBootstrapClient ?? (needsContextPinnedBootstrapForIssuers ? input.remintBootstrapForContext ? await input.remintBootstrapForContext(blueprint.contextId) : client : void 0);
|
|
19146
|
+
if (ctxBootstrapClient && (staleAfterPrompt || !provisioned.ctxBootstrapClient) && input.remintBootstrapForContext) {
|
|
18094
19147
|
log.debug({ component: "loader", contextId: blueprint.contextId }, "re-minted context-pinned bootstrap client (issuers)");
|
|
18095
19148
|
}
|
|
18096
19149
|
const issuerActions = [];
|
|
@@ -18272,16 +19325,9 @@ async function runLoader(client, input) {
|
|
|
18272
19325
|
`Trigger '${triggerName}' names schema '${entry.firingSource.schemaName}', which has no resolved schemaId \u2014 the schema failed to provision.`
|
|
18273
19326
|
);
|
|
18274
19327
|
}
|
|
18275
|
-
const
|
|
18276
|
-
body: {
|
|
18277
|
-
externalId: triggerPrincipalExternalId(blueprint.name, triggerName),
|
|
18278
|
-
type: "SERVICE",
|
|
18279
|
-
payload: { displayName: `Trigger: ${triggerName}` }
|
|
18280
|
-
}
|
|
18281
|
-
});
|
|
18282
|
-
const triggerUserId = triggerUser.id;
|
|
19328
|
+
const triggerUserId = triggerPrincipalIds.get(triggerName);
|
|
18283
19329
|
if (!triggerUserId) {
|
|
18284
|
-
throw new Error(`Trigger '${triggerName}'
|
|
19330
|
+
throw new Error(`Trigger '${triggerName}' has no resolved service principal.`);
|
|
18285
19331
|
}
|
|
18286
19332
|
const triggerPlaceholderProfileBody = {
|
|
18287
19333
|
principalId: `usr_${triggerUserId}`,
|
|
@@ -18346,6 +19392,44 @@ async function runLoader(client, input) {
|
|
|
18346
19392
|
{ component: "loader", triggerName, principalId: `usr_${triggerUserId}` },
|
|
18347
19393
|
"trigger access profile widened to its declared grant"
|
|
18348
19394
|
);
|
|
19395
|
+
const toMark = triggerPrincipalsToMark.get(triggerName);
|
|
19396
|
+
if (toMark) {
|
|
19397
|
+
let reason;
|
|
19398
|
+
try {
|
|
19399
|
+
if (toMark.userType !== void 0 && toMark.userType !== "SERVICE") {
|
|
19400
|
+
reason = `it is a ${toMark.userType} user, not a service principal`;
|
|
19401
|
+
} else {
|
|
19402
|
+
const fresh = await ctxClient.identity.getUser({ id: toMark.userId });
|
|
19403
|
+
const payload = fresh.payload;
|
|
19404
|
+
if (fresh.type !== void 0 && fresh.type !== "SERVICE") {
|
|
19405
|
+
reason = `it is a ${fresh.type} user, not a service principal`;
|
|
19406
|
+
} else if (payloadIsMasked(payload)) {
|
|
19407
|
+
reason = "its payload is masked for this credential (a schema marks fields sensitive), so writing it back would replace real values";
|
|
19408
|
+
} else {
|
|
19409
|
+
await ctxClient.identity.createUser({
|
|
19410
|
+
upsert: true,
|
|
19411
|
+
body: {
|
|
19412
|
+
externalId: toMark.externalId,
|
|
19413
|
+
type: "SERVICE",
|
|
19414
|
+
payload: {
|
|
19415
|
+
...payload !== null && typeof payload === "object" && !Array.isArray(payload) ? payload : {},
|
|
19416
|
+
provisionedBy: blueprint.name
|
|
19417
|
+
}
|
|
19418
|
+
}
|
|
19419
|
+
});
|
|
19420
|
+
}
|
|
19421
|
+
}
|
|
19422
|
+
} catch (err) {
|
|
19423
|
+
reason = err instanceof Error ? err.message : String(err);
|
|
19424
|
+
}
|
|
19425
|
+
if (reason !== void 0) {
|
|
19426
|
+
log.warn(
|
|
19427
|
+
{ component: "loader", triggerName, reason },
|
|
19428
|
+
"could not mark the confirmed trigger principal as this blueprint's; the next run will ask again"
|
|
19429
|
+
);
|
|
19430
|
+
}
|
|
19431
|
+
triggerPrincipalsReused.push({ triggerName, externalId: toMark.externalId, marked: reason === void 0, ...reason !== void 0 ? { reason } : {} });
|
|
19432
|
+
}
|
|
18349
19433
|
}
|
|
18350
19434
|
let triggerPrunes = [];
|
|
18351
19435
|
let triggerPruneSkipped = [];
|
|
@@ -18368,6 +19452,18 @@ async function runLoader(client, input) {
|
|
|
18368
19452
|
log
|
|
18369
19453
|
}));
|
|
18370
19454
|
}
|
|
19455
|
+
const agentActions = declaresAgents(blueprint) ? await ensureAgents({
|
|
19456
|
+
ctxClient,
|
|
19457
|
+
agents: blueprint.agents ?? {},
|
|
19458
|
+
blueprintName: blueprint.name,
|
|
19459
|
+
contextId: blueprint.contextId,
|
|
19460
|
+
allowAgentAdoption,
|
|
19461
|
+
onAgentCreated,
|
|
19462
|
+
onAgentSeen,
|
|
19463
|
+
onAgentAttempt,
|
|
19464
|
+
onAgentRefused,
|
|
19465
|
+
log
|
|
19466
|
+
}) : [];
|
|
18371
19467
|
const keyName = skipKey ? "" : keyNameFor(blueprint.name, machineId);
|
|
18372
19468
|
let keyAction = skipKey ? "skipped" : "created";
|
|
18373
19469
|
let revokedKeyId;
|
|
@@ -18528,7 +19624,9 @@ async function runLoader(client, input) {
|
|
|
18528
19624
|
schemaActions,
|
|
18529
19625
|
roleActions,
|
|
18530
19626
|
scriptActions,
|
|
19627
|
+
agentActions,
|
|
18531
19628
|
triggerActions,
|
|
19629
|
+
triggerPrincipalsReused,
|
|
18532
19630
|
triggerPrunes,
|
|
18533
19631
|
triggerPruneSkipped,
|
|
18534
19632
|
issuerActions,
|
|
@@ -18572,6 +19670,12 @@ function buildPlan(blueprint, opts = {}) {
|
|
|
18572
19670
|
summary: `namespace '${ns.namespace}' (${ownerNote}${entityNote}${membershipNote}) [${BLUEPRINT_FIELD_PHASES.namespaces}-token phase]`
|
|
18573
19671
|
});
|
|
18574
19672
|
}
|
|
19673
|
+
for (const triggerName of Object.keys(blueprint.triggers ?? {})) {
|
|
19674
|
+
steps.push({
|
|
19675
|
+
kind: "principal",
|
|
19676
|
+
summary: `trigger '${triggerName}' principal '${triggerPrincipalExternalId(blueprint.name, triggerName)}' (SERVICE, marked as this blueprint's; an existing one without that mark needs confirmation)`
|
|
19677
|
+
});
|
|
19678
|
+
}
|
|
18575
19679
|
for (const iss of blueprint.issuers ?? []) {
|
|
18576
19680
|
const issuerUrl = sanitizeForPlanDisplay(iss.issuer);
|
|
18577
19681
|
const jwksNote = `, jwksUri: ${sanitizeForPlanDisplay(iss.jwksUri)}`;
|
|
@@ -18643,10 +19747,6 @@ function buildPlan(blueprint, opts = {}) {
|
|
|
18643
19747
|
});
|
|
18644
19748
|
}
|
|
18645
19749
|
for (const [triggerName, entry] of Object.entries(blueprint.triggers ?? {})) {
|
|
18646
|
-
steps.push({
|
|
18647
|
-
kind: "principal",
|
|
18648
|
-
summary: `trigger '${triggerName}' principal '${blueprint.name}-trigger-${triggerName}' (SERVICE)`
|
|
18649
|
-
});
|
|
18650
19750
|
steps.push({
|
|
18651
19751
|
kind: "profile",
|
|
18652
19752
|
summary: `trigger '${triggerName}' profile: minimal placeholder grant (minted before the trigger row exists)`
|
|
@@ -18671,6 +19771,19 @@ function buildPlan(blueprint, opts = {}) {
|
|
|
18671
19771
|
});
|
|
18672
19772
|
}
|
|
18673
19773
|
}
|
|
19774
|
+
for (const [agentName, entry] of Object.entries(blueprint.agents ?? {})) {
|
|
19775
|
+
const tools = (entry.tools ?? []).map((t) => t.allowWrites ? `${sanitizeForPlanDisplay(t.name, 64)} (writes)` : sanitizeForPlanDisplay(t.name, 64));
|
|
19776
|
+
const parts = [`model ${sanitizeForPlanDisplay(entry.model)}`, tools.length ? `tools [${tools.join(", ")}]` : "no tools"];
|
|
19777
|
+
if (entry.authority !== void 0) parts.push(`authority ${sanitizeForPlanDisplay(entry.authority, 32)}`);
|
|
19778
|
+
const limits = Object.entries(entry.limits ?? {}).filter(([, v]) => v !== void 0);
|
|
19779
|
+
if (limits.length) parts.push(`limits {${limits.map(([k, v]) => `${k} ${v}`).join(", ")}}`);
|
|
19780
|
+
if (entry.budget?.maxInferenceCents !== void 0) parts.push(`budget ${entry.budget.maxInferenceCents} cents per run`);
|
|
19781
|
+
if (entry.system) parts.push(`system prompt ${Array.from(entry.system).length} characters`);
|
|
19782
|
+
steps.push({
|
|
19783
|
+
kind: "agent",
|
|
19784
|
+
summary: `agent '${sanitizeForPlanDisplay(agentName, 64)}' (ensure the stored definition matches: ${parts.join(", ")}; a changed definition becomes the next version) [${BLUEPRINT_FIELD_PHASES.agents}-token phase]`
|
|
19785
|
+
});
|
|
19786
|
+
}
|
|
18674
19787
|
if (!opts.noKey) {
|
|
18675
19788
|
steps.push({ kind: "key", summary: `key ${blueprint.name}-<machine> (mint 1 scoped ssk_*)` });
|
|
18676
19789
|
}
|
|
@@ -18723,6 +19836,19 @@ function renderPlan(blueprint, steps, opts = {}) {
|
|
|
18723
19836
|
` and would break any trigger still referencing it. Remove one deliberately with`,
|
|
18724
19837
|
` 'vectros scripts delete', after 'vectros scripts list' shows you what is there.`
|
|
18725
19838
|
);
|
|
19839
|
+
if (steps.some((s) => s.kind === "agent")) {
|
|
19840
|
+
legendLines.push(
|
|
19841
|
+
` Counter-exception: an agent is never edited in place \u2014 a changed definition ADDS a new`,
|
|
19842
|
+
` immutable version, and an unchanged one writes nothing. Agents are not reconciled by`,
|
|
19843
|
+
` absence either: one this blueprint declared earlier and no longer does stays stored and`,
|
|
19844
|
+
` runnable. Retire it deliberately through the API (DELETE /v1/agents/{id}).`,
|
|
19845
|
+
` An agent that already exists and that nothing manages (made by hand) is REFUSED unless`,
|
|
19846
|
+
` --allow-agent-adoption is passed: adopting it marks it as this blueprint's and gives it`,
|
|
19847
|
+
` a new version if the definition differs. Another blueprint's agent is refused, and so`,
|
|
19848
|
+
` is a retired one. Writing agents needs 'agents:c' on the applying credential, plus`,
|
|
19849
|
+
` 'agents:u' and 'agents:r' for an agent that already exists.`
|
|
19850
|
+
);
|
|
19851
|
+
}
|
|
18726
19852
|
if (blueprint.issuers?.some((i) => i.restrictedToDomain === void 0)) {
|
|
18727
19853
|
legendLines.push(
|
|
18728
19854
|
` Note: a newly registered issuer without restrictedToDomain starts pending verification on platform`,
|
|
@@ -18797,7 +19923,7 @@ function displayChallengeValue(value) {
|
|
|
18797
19923
|
return value === void 0 ? void 0 : printable(value) ?? WITHHELD;
|
|
18798
19924
|
}
|
|
18799
19925
|
function renderChallengeBlock(view, ctx, now) {
|
|
18800
|
-
const id = view.issuerId ?? "(unknown)";
|
|
19926
|
+
const id = singleLine(String(view.issuerId ?? "(unknown)"), 100);
|
|
18801
19927
|
const rawClaim = view.verificationClaim;
|
|
18802
19928
|
const rawNonce = view.verificationNonce;
|
|
18803
19929
|
const claim = rawClaim === void 0 ? "(not reported)" : printable(rawClaim) ?? WITHHELD;
|
|
@@ -18842,7 +19968,7 @@ function renderChallengeBlock(view, ctx, now) {
|
|
|
18842
19968
|
`;
|
|
18843
19969
|
}
|
|
18844
19970
|
function renderExpiredBlock(view, ctx, rerun) {
|
|
18845
|
-
const id = view.issuerId ?? "(unknown)";
|
|
19971
|
+
const id = singleLine(String(view.issuerId ?? "(unknown)"), 100);
|
|
18846
19972
|
const expired = view.verificationExpiresAt === void 0 ? "(unknown)" : printable(view.verificationExpiresAt) ?? WITHHELD;
|
|
18847
19973
|
const lines = [];
|
|
18848
19974
|
lines.push(` \u2716 issuer '${id}' is STILL pending verification and its challenge EXPIRED at ${expired} (by this machine's clock).`);
|
|
@@ -20977,6 +22103,7 @@ function parseBootstrapArgs(argv) {
|
|
|
20977
22103
|
noSeed: false,
|
|
20978
22104
|
allowTenantWideNamespaces: false,
|
|
20979
22105
|
allowScriptAdoption: false,
|
|
22106
|
+
allowAgentAdoption: false,
|
|
20980
22107
|
requireVerifiedIssuers: false,
|
|
20981
22108
|
client: "desktop",
|
|
20982
22109
|
tenant: "live",
|
|
@@ -21045,6 +22172,9 @@ function parseBootstrapArgs(argv) {
|
|
|
21045
22172
|
case "--allow-script-adoption":
|
|
21046
22173
|
args.allowScriptAdoption = true;
|
|
21047
22174
|
break;
|
|
22175
|
+
case "--allow-agent-adoption":
|
|
22176
|
+
args.allowAgentAdoption = true;
|
|
22177
|
+
break;
|
|
21048
22178
|
case "--require-verified-issuers":
|
|
21049
22179
|
args.requireVerifiedIssuers = true;
|
|
21050
22180
|
break;
|
|
@@ -21102,6 +22232,38 @@ function buildConfirmExistingPrincipal(args, prompt, isTty) {
|
|
|
21102
22232
|
return args.confirmExistingPrincipal;
|
|
21103
22233
|
};
|
|
21104
22234
|
}
|
|
22235
|
+
function buildConfirmExistingTriggerPrincipal(args, prompt, isTty, say = (text) => void process.stdout.write(text)) {
|
|
22236
|
+
const reusing = (d, how) => say(
|
|
22237
|
+
` \u26A0 reusing existing trigger service principal '${d.externalId}' (trigger '${d.triggerName}') \u2014 proceeding because ${how}.
|
|
22238
|
+
`
|
|
22239
|
+
);
|
|
22240
|
+
return async (d) => {
|
|
22241
|
+
if (!args.yes && isTty) {
|
|
22242
|
+
const ans = await prompt(
|
|
22243
|
+
`
|
|
22244
|
+
\u26A0 The service principal '${d.externalId}' for trigger '${d.triggerName}' already exists in your tenant ` + (d.userType !== void 0 && d.userType !== "SERVICE" ? `and is a ${d.userType} user, not a service principal.
|
|
22245
|
+
` : d.provenance === "other-blueprint" ? `and is marked as provisioned by the blueprint '${singleLine(d.markedBy ?? "", 100)}', not this one.
|
|
22246
|
+
` : `and carries no provenance marker (an earlier CLI release, or created by hand).
|
|
22247
|
+
`) + ` Continuing writes that identity's access profile to this blueprint's trigger grant, and marks it as
|
|
22248
|
+
this blueprint's. Expected when you re-apply a blueprint you already manage. If you don't recognize
|
|
22249
|
+
this identity, do NOT proceed: it may be a name collision with something you don't control.
|
|
22250
|
+
Proceed and use it? [y/N]: `
|
|
22251
|
+
);
|
|
22252
|
+
const ok = existingPrincipalConfirmed(ans);
|
|
22253
|
+
if (ok) reusing(d, "you confirmed it at the prompt");
|
|
22254
|
+
return ok;
|
|
22255
|
+
}
|
|
22256
|
+
if (args.confirmExistingPrincipal) reusing(d, "--confirm-existing-principal was passed");
|
|
22257
|
+
return args.confirmExistingPrincipal;
|
|
22258
|
+
};
|
|
22259
|
+
}
|
|
22260
|
+
function triggerPrincipalMarkLines(reused) {
|
|
22261
|
+
return (reused ?? []).map(
|
|
22262
|
+
(r) => r.marked ? ` \u2714 '${r.externalId}' is now marked as this blueprint's, so the next run will not ask again.
|
|
22263
|
+
` : ` \u26A0 '${r.externalId}' was NOT marked as this blueprint's (${singleLine(asText(r.reason ?? "unknown reason"), 300)}), so the next run will ask again.
|
|
22264
|
+
`
|
|
22265
|
+
).join("");
|
|
22266
|
+
}
|
|
21105
22267
|
function seedSummaryLine(opts) {
|
|
21106
22268
|
if (opts.noSeed) {
|
|
21107
22269
|
return opts.declinedInteractively ? " \u2714 Seeded 0 record(s) \u2014 skipped (you declined sample data)\n" : " \u2714 Seeded 0 record(s) \u2014 skipped (--no-seed)\n";
|
|
@@ -21112,7 +22274,7 @@ function seedSummaryLine(opts) {
|
|
|
21112
22274
|
function seedIndexWarningBlock(failures) {
|
|
21113
22275
|
if (failures.length === 0) return "";
|
|
21114
22276
|
const lines = failures.map((f) => {
|
|
21115
|
-
const reason = f.message ?? f.code ?? "indexing failed";
|
|
22277
|
+
const reason = singleLine(asText(f.message ?? f.code ?? "indexing failed"), 300);
|
|
21116
22278
|
return ` \u2022 ${f.typeName}/${f.externalId} (${f.surface}): ${reason}
|
|
21117
22279
|
`;
|
|
21118
22280
|
});
|
|
@@ -21180,6 +22342,22 @@ function scriptTally(items) {
|
|
|
21180
22342
|
if (unchanged) seg.push(`${unchanged} already current \u2014 nothing written`);
|
|
21181
22343
|
return `${items.length} script${items.length === 1 ? "" : "s"} (${seg.join("; ")})`;
|
|
21182
22344
|
}
|
|
22345
|
+
function agentTally(items) {
|
|
22346
|
+
if (items.length === 0) return null;
|
|
22347
|
+
const seg = items.map((i) => {
|
|
22348
|
+
switch (i.action) {
|
|
22349
|
+
case "created":
|
|
22350
|
+
return `'${i.name}' created at version ${i.version}`;
|
|
22351
|
+
case "unchanged":
|
|
22352
|
+
return `'${i.name}' unchanged at version ${i.version}`;
|
|
22353
|
+
case "versioned":
|
|
22354
|
+
return `'${i.name}' version ${i.previousVersion} -> ${i.version}`;
|
|
22355
|
+
case "adopted":
|
|
22356
|
+
return i.previousVersion === i.version ? `'${i.name}' adopted (marked as this blueprint's, unchanged at version ${i.version})` : `'${i.name}' adopted (marked as this blueprint's, version ${i.previousVersion} -> ${i.version})`;
|
|
22357
|
+
}
|
|
22358
|
+
});
|
|
22359
|
+
return `${items.length} agent${items.length === 1 ? "" : "s"} (${seg.join("; ")})`;
|
|
22360
|
+
}
|
|
21183
22361
|
function applyOutcomeSummary(result, opts = {}) {
|
|
21184
22362
|
const parts = [
|
|
21185
22363
|
reconcileTally(result.schemaActions, "schema"),
|
|
@@ -21196,13 +22374,15 @@ function applyOutcomeSummary(result, opts = {}) {
|
|
|
21196
22374
|
// wording applies unchanged; buildPlan's dry-run preview already discloses each trigger's own
|
|
21197
22375
|
// steps individually, this is the apply-time tally counterpart.
|
|
21198
22376
|
reconcileTally(result.triggerActions, "trigger"),
|
|
22377
|
+
// Agents — written after the triggers, so reported after them.
|
|
22378
|
+
agentTally(result.agentActions),
|
|
21199
22379
|
`profile ${result.profileAction}`
|
|
21200
22380
|
].filter((p) => p !== null);
|
|
21201
22381
|
const detail = parts.length ? ` \u2014 ${parts.join("; ")}` : "";
|
|
21202
22382
|
const principalNote = (opts.principalNote ?? true) && result.userAction === "reconciled" ? ` \u26A0 service principal already existed in this tenant before this run (not newly created)
|
|
21203
22383
|
` : "";
|
|
21204
22384
|
const pruneNotes = result.triggerPrunes.map(
|
|
21205
|
-
(p) => ` \u26A0 trigger '${p.name}' removed from the blueprint \u2014 de-provisioned (rule + access profile deleted)
|
|
22385
|
+
(p) => ` \u26A0 trigger '${singleLine(asText(p.name), 100)}' removed from the blueprint \u2014 de-provisioned (rule + access profile deleted)
|
|
21206
22386
|
` + // Not a warning: 404 on the profile means it was already gone, so the end state is exactly
|
|
21207
22387
|
// what a clean prune produces. Said out loud only so the absent second delete isn't read as
|
|
21208
22388
|
// a silent skip. Every OTHER profile-delete failure lands in triggerPruneSkipped instead.
|
|
@@ -21210,9 +22390,9 @@ function applyOutcomeSummary(result, opts = {}) {
|
|
|
21210
22390
|
`)
|
|
21211
22391
|
).join("");
|
|
21212
22392
|
const pruneSkippedNotes = result.triggerPruneSkipped.map(
|
|
21213
|
-
(s) => s.failed === false ? s.name ? ` \u2139 trigger '${s.name}' is not declared here and was left in place \u2014 ${s.reason}.
|
|
22393
|
+
(s) => s.failed === false ? s.name ? ` \u2139 trigger '${singleLine(asText(s.name), 100)}' is not declared here and was left in place \u2014 ${s.reason}.
|
|
21214
22394
|
` : ` \u2139 a trigger rule was left in place \u2014 ${s.reason}.
|
|
21215
|
-
` : s.name ? ` \u26A0 trigger '${s.name}' is no longer declared but could NOT be removed \u2014 ${s.reason}. It is still live.
|
|
22395
|
+
` : s.name ? ` \u26A0 trigger '${singleLine(asText(s.name), 100)}' is no longer declared but could NOT be removed \u2014 ${s.reason}. It is still live.
|
|
21216
22396
|
` : ` \u26A0 triggers could not be reconciled this run \u2014 ${s.reason}. A trigger removed from this blueprint may still be live.
|
|
21217
22397
|
`
|
|
21218
22398
|
).join("");
|
|
@@ -21365,7 +22545,7 @@ async function runBootstrapCli(argv, deps = {}) {
|
|
|
21365
22545
|
try {
|
|
21366
22546
|
args = parseBootstrapArgs(argv);
|
|
21367
22547
|
} catch (err) {
|
|
21368
|
-
process.stderr.write(`${redactCredentials(
|
|
22548
|
+
process.stderr.write(`${redactCredentials(errorText(err))}
|
|
21369
22549
|
|
|
21370
22550
|
${BOOTSTRAP_HELP}
|
|
21371
22551
|
`);
|
|
@@ -21497,7 +22677,7 @@ ${IDENTITIES_TO_ENSURE_HEADING}
|
|
|
21497
22677
|
if (!keyring) {
|
|
21498
22678
|
process.stderr.write(
|
|
21499
22679
|
`
|
|
21500
|
-
\u26A0 Could not open the keyring (${
|
|
22680
|
+
\u26A0 Could not open the keyring (${errorText(keyringOpenError)}).
|
|
21501
22681
|
Falling back to embedding the raw secret directly in the MCP config \u2014 this is NOT
|
|
21502
22682
|
login-gated. Run \`vectros keyring doctor\` once it's fixed.
|
|
21503
22683
|
`
|
|
@@ -21538,7 +22718,7 @@ ${IDENTITIES_TO_ENSURE_HEADING}
|
|
|
21538
22718
|
} catch (err) {
|
|
21539
22719
|
process.stderr.write(
|
|
21540
22720
|
`
|
|
21541
|
-
\u26A0 Could not store this key in the keyring (${
|
|
22721
|
+
\u26A0 Could not store this key in the keyring (${errorText(err)}).
|
|
21542
22722
|
Falling back to embedding the raw secret directly in the MCP config \u2014 this is NOT
|
|
21543
22723
|
login-gated. Run \`vectros keyring doctor\` to see why the store failed.
|
|
21544
22724
|
`
|
|
@@ -21570,10 +22750,12 @@ ${IDENTITIES_TO_ENSURE_HEADING}
|
|
|
21570
22750
|
rotate: args.rotate,
|
|
21571
22751
|
skipSeed: noSeed,
|
|
21572
22752
|
allowScriptAdoption: args.allowScriptAdoption,
|
|
22753
|
+
allowAgentAdoption: args.allowAgentAdoption,
|
|
21573
22754
|
readExistingKey,
|
|
21574
22755
|
persistMintedKey,
|
|
21575
22756
|
provisioned,
|
|
21576
22757
|
remintBootstrapForContext,
|
|
22758
|
+
confirmExistingTriggerPrincipal: buildConfirmExistingTriggerPrincipal(args, promptCtx.prompt, isTty),
|
|
21577
22759
|
remintForContext: buildRemintForContext({
|
|
21578
22760
|
cognitoToken,
|
|
21579
22761
|
apiBaseUrl: baseUrl,
|
|
@@ -21582,6 +22764,7 @@ ${IDENTITIES_TO_ENSURE_HEADING}
|
|
|
21582
22764
|
}),
|
|
21583
22765
|
log
|
|
21584
22766
|
});
|
|
22767
|
+
process.stdout.write(triggerPrincipalMarkLines(result.triggerPrincipalsReused));
|
|
21585
22768
|
const finishApply = () => finishIssuers(
|
|
21586
22769
|
result.issuerActions,
|
|
21587
22770
|
{ context: blueprint.contextId, tenant: args.tenant, env: args.env, baseUrl: args.baseUrl },
|
|
@@ -21650,7 +22833,7 @@ ${codeAddHint}`);
|
|
|
21650
22833
|
codeAddHint
|
|
21651
22834
|
);
|
|
21652
22835
|
} catch (err) {
|
|
21653
|
-
process.stderr.write(` \u26A0 Could not merge config (${
|
|
22836
|
+
process.stderr.write(` \u26A0 Could not merge config (${errorText(err)}).
|
|
21654
22837
|
`);
|
|
21655
22838
|
process.stdout.write(`
|
|
21656
22839
|
Add this manually instead:
|
|
@@ -21662,24 +22845,37 @@ ${codeAddHint}`);
|
|
|
21662
22845
|
} catch (err) {
|
|
21663
22846
|
if (err instanceof SessionExpiredError) {
|
|
21664
22847
|
process.stderr.write(`
|
|
21665
|
-
\u2716 ${err
|
|
22848
|
+
\u2716 ${errorText(err)}
|
|
21666
22849
|
`);
|
|
21667
22850
|
return 2;
|
|
21668
22851
|
}
|
|
21669
22852
|
if (err instanceof LostKeyError) {
|
|
21670
22853
|
process.stderr.write(`
|
|
21671
|
-
\u2716 ${err
|
|
22854
|
+
\u2716 ${errorText(err)}
|
|
21672
22855
|
`);
|
|
21673
22856
|
return 3;
|
|
21674
22857
|
}
|
|
21675
22858
|
if (err instanceof ExistingPrincipalNotConfirmedError) {
|
|
21676
22859
|
process.stderr.write(
|
|
21677
22860
|
`
|
|
21678
|
-
\u2716 ${err
|
|
22861
|
+
\u2716 ${errorText(err)}
|
|
21679
22862
|
|
|
21680
22863
|
If this is expected (a re-apply, or a deliberate second machine for an integration you already own),
|
|
21681
22864
|
re-run with --confirm-existing-principal, or confirm interactively.
|
|
21682
22865
|
If you don't recognize this identity, investigate the blueprint's servicePrincipal.externalId before re-running.
|
|
22866
|
+
`
|
|
22867
|
+
);
|
|
22868
|
+
return 3;
|
|
22869
|
+
}
|
|
22870
|
+
if (err instanceof ExistingTriggerPrincipalNotConfirmedError) {
|
|
22871
|
+
process.stderr.write(
|
|
22872
|
+
`
|
|
22873
|
+
\u2716 ${errorText(err)}
|
|
22874
|
+
|
|
22875
|
+
If this is expected (a re-apply of a blueprint you manage), re-run with --confirm-existing-principal,
|
|
22876
|
+
or confirm interactively; the principal is then marked as this blueprint's and not asked about again.
|
|
22877
|
+
If you don't recognize this identity, investigate the trigger's service principal first.
|
|
22878
|
+
No schema, role, access profile, trigger, key or seed record was written.
|
|
21683
22879
|
`
|
|
21684
22880
|
);
|
|
21685
22881
|
return 3;
|
|
@@ -21687,7 +22883,7 @@ ${codeAddHint}`);
|
|
|
21687
22883
|
if (err instanceof BlueprintIdentityError || err instanceof UnresolvedIdentitiesError) {
|
|
21688
22884
|
process.stderr.write(
|
|
21689
22885
|
`
|
|
21690
|
-
\u2716 ${err
|
|
22886
|
+
\u2716 ${errorText(err)}
|
|
21691
22887
|
The app-context, service principal, and any declared namespaces were already provisioned. No schemas, profile, roles, key, or seed records were.
|
|
21692
22888
|
`
|
|
21693
22889
|
);
|
|
@@ -21696,7 +22892,7 @@ ${codeAddHint}`);
|
|
|
21696
22892
|
if (err instanceof IssuerScopeNotAppliedError) {
|
|
21697
22893
|
process.stderr.write(
|
|
21698
22894
|
`
|
|
21699
|
-
\u2716 ${err
|
|
22895
|
+
\u2716 ${errorText(err)}
|
|
21700
22896
|
The app-context, service principal, and any declared namespaces were already provisioned. No schemas, profile, roles, key, or seed records were.
|
|
21701
22897
|
`
|
|
21702
22898
|
);
|
|
@@ -21704,7 +22900,7 @@ ${codeAddHint}`);
|
|
|
21704
22900
|
}
|
|
21705
22901
|
if (err instanceof TenantWideNamespacesNotAllowedError) {
|
|
21706
22902
|
process.stderr.write(`
|
|
21707
|
-
\u2716 ${err
|
|
22903
|
+
\u2716 ${errorText(err)}
|
|
21708
22904
|
Nothing was provisioned.
|
|
21709
22905
|
`);
|
|
21710
22906
|
return 2;
|
|
@@ -21712,7 +22908,7 @@ ${codeAddHint}`);
|
|
|
21712
22908
|
process.stderr.write(`
|
|
21713
22909
|
${formatCliError(err, { action: "Bootstrap failed" })}`);
|
|
21714
22910
|
if (process.env.VECTROS_MCP_DEBUG && err instanceof Error && err.stack) {
|
|
21715
|
-
process.stderr.write(`${err.stack}
|
|
22911
|
+
process.stderr.write(`${cleanBlock(err.stack)}
|
|
21716
22912
|
`);
|
|
21717
22913
|
}
|
|
21718
22914
|
return 1;
|
|
@@ -21737,6 +22933,7 @@ function parseApplyArgs(argv) {
|
|
|
21737
22933
|
seed: false,
|
|
21738
22934
|
allowTenantWideNamespaces: false,
|
|
21739
22935
|
allowScriptAdoption: false,
|
|
22936
|
+
allowAgentAdoption: false,
|
|
21740
22937
|
requireVerifiedIssuers: false,
|
|
21741
22938
|
confirmExistingPrincipal: false,
|
|
21742
22939
|
prune: false,
|
|
@@ -21797,6 +22994,9 @@ function parseApplyArgs(argv) {
|
|
|
21797
22994
|
case "--allow-script-adoption":
|
|
21798
22995
|
args.allowScriptAdoption = true;
|
|
21799
22996
|
break;
|
|
22997
|
+
case "--allow-agent-adoption":
|
|
22998
|
+
args.allowAgentAdoption = true;
|
|
22999
|
+
break;
|
|
21800
23000
|
case "--require-verified-issuers":
|
|
21801
23001
|
args.requireVerifiedIssuers = true;
|
|
21802
23002
|
break;
|
|
@@ -21869,16 +23069,18 @@ Usage:
|
|
|
21869
23069
|
[--set name=value ...] [--values <file>] [--seed]
|
|
21870
23070
|
[--confirm-existing-principal] [--require-verified-issuers]
|
|
21871
23071
|
[--allow-tenant-wide-namespaces] [--allow-script-adoption]
|
|
23072
|
+
[--allow-agent-adoption]
|
|
21872
23073
|
[--token <t> | env VECTROS_BOOTSTRAP_TOKEN] [--base-url <url>]
|
|
21873
23074
|
|
|
21874
23075
|
<file|name> is a path to your own blueprint (YAML/JSON) or the name of a bundled one
|
|
21875
23076
|
(${BLUEPRINT_NAMES.join(", ")}). Everything it declares is created or reconciled \u2014 the context, its
|
|
21876
|
-
service principal and access profile, schemas, roles, issuers, triggers and
|
|
23077
|
+
service principal and access profile, schemas, roles, issuers, triggers, scripts and agents (and, only with --seed, its
|
|
21877
23078
|
sample seed records) \u2014 but NO scoped key is minted, nothing is written to a keyring and no MCP client config is
|
|
21878
23079
|
touched, so a CI runner can re-apply a changed blueprint. To also get a credential, use 'vectros bootstrap'. It
|
|
21879
23080
|
authenticates the same way bootstrap does and refuses on the same gates (scope, capability, tenant-wide namespaces)
|
|
21880
23081
|
before anything changes. (A script pushed onto an existing hand-authored name is checked when the scripts are
|
|
21881
|
-
pushed, partway through: --allow-script-adoption.
|
|
23082
|
+
pushed, partway through: --allow-script-adoption. An agent that exists with no blueprint mark is checked when the
|
|
23083
|
+
agents are written, also partway through: --allow-agent-adoption.)
|
|
21882
23084
|
|
|
21883
23085
|
Before it changes anything it prints the account, tenant, context and API it is pointed at, then the plan, and
|
|
21884
23086
|
asks to proceed unless --yes is given. With no terminal --yes is required.
|
|
@@ -21913,7 +23115,7 @@ Exit codes:
|
|
|
21913
23115
|
3 needs a confirmation it did not get (an existing service principal: --confirm-existing-principal)
|
|
21914
23116
|
4 applied in full, but --require-verified-issuers was given and an issuer is pending verification`;
|
|
21915
23117
|
function blueprintRefusal(err) {
|
|
21916
|
-
const message =
|
|
23118
|
+
const message = errorText(err);
|
|
21917
23119
|
if (message.startsWith("Unknown blueprint ")) {
|
|
21918
23120
|
return `No bundled blueprint matches the argument you gave. Bundled blueprints: ${BLUEPRINT_NAMES.join(", ")} (or pass a path like ./my.yaml).`;
|
|
21919
23121
|
}
|
|
@@ -21934,7 +23136,7 @@ async function runApplyCli(argv, deps = {}) {
|
|
|
21934
23136
|
} catch (err) {
|
|
21935
23137
|
process.stderr.write(
|
|
21936
23138
|
`
|
|
21937
|
-
\u2716 ${redactCredentials(
|
|
23139
|
+
\u2716 ${redactCredentials(errorText(err))}
|
|
21938
23140
|
See 'vectros blueprint apply --help'. Nothing was changed.
|
|
21939
23141
|
`
|
|
21940
23142
|
);
|
|
@@ -21964,7 +23166,7 @@ async function runApplyCli(argv, deps = {}) {
|
|
|
21964
23166
|
try {
|
|
21965
23167
|
baseUrl = envToBaseUrl(args.env, args.baseUrl ?? process.env.VECTROS_API_BASE_URL);
|
|
21966
23168
|
} catch (err) {
|
|
21967
|
-
return refuse(
|
|
23169
|
+
return refuse(errorText(err));
|
|
21968
23170
|
}
|
|
21969
23171
|
const promptCtx = deps.createPrompt ? deps.createPrompt() : (() => {
|
|
21970
23172
|
const rl = readline3.createInterface({ input: process.stdin, output: process.stdout });
|
|
@@ -22000,7 +23202,7 @@ async function runApplyCli(argv, deps = {}) {
|
|
|
22000
23202
|
try {
|
|
22001
23203
|
cognitoToken = await resolveBridgeCognitoToken(args, promptCtx.prompt, isTty, deps.resolveCognitoToken);
|
|
22002
23204
|
} catch (err) {
|
|
22003
|
-
return refuse(
|
|
23205
|
+
return refuse(errorText(err));
|
|
22004
23206
|
}
|
|
22005
23207
|
const mintBridge = deps.mintBridgeToken ?? mintBridgeToken;
|
|
22006
23208
|
const mintContext = deps.mintContextToken ?? mintContextToken;
|
|
@@ -22118,6 +23320,7 @@ ${formatCliError(err, { action: "Could not mint a fresh bootstrap token after yo
|
|
|
22118
23320
|
client,
|
|
22119
23321
|
log
|
|
22120
23322
|
);
|
|
23323
|
+
const confirmTriggerPrincipal = buildConfirmExistingTriggerPrincipal(args, promptCtx.prompt, isTty);
|
|
22121
23324
|
const result = await (deps.runLoader ?? runLoader)(client, {
|
|
22122
23325
|
blueprint: resolvedBlueprint,
|
|
22123
23326
|
tenantId,
|
|
@@ -22127,14 +23330,17 @@ ${formatCliError(err, { action: "Could not mint a fresh bootstrap token after yo
|
|
|
22127
23330
|
// Off unless asked for: apply never deletes on its own. `blueprint-test` sets the same switch off.
|
|
22128
23331
|
pruneTriggers: args.prune,
|
|
22129
23332
|
allowScriptAdoption: args.allowScriptAdoption,
|
|
23333
|
+
allowAgentAdoption: args.allowAgentAdoption,
|
|
22130
23334
|
readExistingKey: async () => void 0,
|
|
22131
23335
|
persistMintedKey: async () => {
|
|
22132
23336
|
},
|
|
22133
23337
|
provisioned,
|
|
22134
23338
|
remintBootstrapForContext,
|
|
23339
|
+
confirmExistingTriggerPrincipal: confirmTriggerPrincipal,
|
|
22135
23340
|
remintForContext: buildRemintForContext({ cognitoToken, apiBaseUrl: baseUrl, tenant, mintContext }),
|
|
22136
23341
|
log
|
|
22137
23342
|
});
|
|
23343
|
+
process.stdout.write(triggerPrincipalMarkLines(result.triggerPrincipalsReused));
|
|
22138
23344
|
process.stdout.write(
|
|
22139
23345
|
applyOutcomeSummary(result, { principalNote: false }) + ` \u2714 Applied '${blueprint.name}' to the ${tenant} tenant, context '${blueprint.contextId}'. No scoped key was minted and no client configuration was touched.
|
|
22140
23346
|
` + applySeedSummary(blueprint, args.seed, result.seededCount) + seedIndexWarningBlock(result.seedIndexFailures) + (args.prune ? "" : ` \u2139 Triggers were not pruned (--prune was not passed): a trigger this blueprint provisioned earlier and its file no longer declares is left in place. Pass --prune to remove it.
|
|
@@ -22167,12 +23373,26 @@ function reportApplyError(err) {
|
|
|
22167
23373
|
if (err instanceof ExistingPrincipalNotConfirmedError) {
|
|
22168
23374
|
process.stderr.write(
|
|
22169
23375
|
`
|
|
22170
|
-
\u2716 ${err
|
|
23376
|
+
\u2716 ${errorText(err)}
|
|
22171
23377
|
|
|
22172
23378
|
If this is expected (a re-apply of a blueprint you manage), re-run with --confirm-existing-principal
|
|
22173
23379
|
(with --yes in a pipeline, where it is standing consent), or confirm at the prompt.
|
|
22174
23380
|
If you don't recognize this identity, investigate the blueprint's servicePrincipal.externalId first.
|
|
22175
23381
|
Only the principal lookup had run; no context, grant or anything else was created or changed.
|
|
23382
|
+
`
|
|
23383
|
+
);
|
|
23384
|
+
return 3;
|
|
23385
|
+
}
|
|
23386
|
+
if (err instanceof ExistingTriggerPrincipalNotConfirmedError) {
|
|
23387
|
+
process.stderr.write(
|
|
23388
|
+
`
|
|
23389
|
+
\u2716 ${errorText(err)}
|
|
23390
|
+
|
|
23391
|
+
If this is expected (a re-apply of a blueprint you manage), re-run with --confirm-existing-principal
|
|
23392
|
+
(with --yes in a pipeline, where it is standing consent), or confirm at the prompt; the principal is then
|
|
23393
|
+
marked as this blueprint's and not asked about again.
|
|
23394
|
+
If you don't recognize this identity, investigate the trigger's service principal first.
|
|
23395
|
+
No schema, role, access profile, trigger or seed record was written.
|
|
22176
23396
|
`
|
|
22177
23397
|
);
|
|
22178
23398
|
return 3;
|
|
@@ -22180,7 +23400,7 @@ function reportApplyError(err) {
|
|
|
22180
23400
|
if (err instanceof BlueprintIdentityError || err instanceof UnresolvedIdentitiesError) {
|
|
22181
23401
|
process.stderr.write(
|
|
22182
23402
|
`
|
|
22183
|
-
\u2716 ${err
|
|
23403
|
+
\u2716 ${errorText(err)}
|
|
22184
23404
|
The app-context, service principal, and any declared namespaces were already provisioned. No schemas, profile, roles or seed records were.
|
|
22185
23405
|
`
|
|
22186
23406
|
);
|
|
@@ -22189,7 +23409,7 @@ function reportApplyError(err) {
|
|
|
22189
23409
|
if (err instanceof IssuerScopeNotAppliedError) {
|
|
22190
23410
|
process.stderr.write(
|
|
22191
23411
|
`
|
|
22192
|
-
\u2716 ${err
|
|
23412
|
+
\u2716 ${errorText(err)}
|
|
22193
23413
|
The app-context, service principal, and any declared namespaces were already provisioned. No schemas, profile, roles or seed records were.
|
|
22194
23414
|
`
|
|
22195
23415
|
);
|
|
@@ -22197,7 +23417,7 @@ function reportApplyError(err) {
|
|
|
22197
23417
|
}
|
|
22198
23418
|
if (err instanceof TenantWideNamespacesNotAllowedError) {
|
|
22199
23419
|
process.stderr.write(`
|
|
22200
|
-
\u2716 ${err
|
|
23420
|
+
\u2716 ${errorText(err)}
|
|
22201
23421
|
Nothing was changed.
|
|
22202
23422
|
`);
|
|
22203
23423
|
return 2;
|
|
@@ -22208,7 +23428,7 @@ ${formatCliError(err, { action: "Apply failed" })} The apply stopped part-way:
|
|
|
22208
23428
|
`
|
|
22209
23429
|
);
|
|
22210
23430
|
if (process.env.VECTROS_MCP_DEBUG && err instanceof Error && err.stack) {
|
|
22211
|
-
process.stderr.write(`${err.stack}
|
|
23431
|
+
process.stderr.write(`${cleanBlock(err.stack)}
|
|
22212
23432
|
`);
|
|
22213
23433
|
}
|
|
22214
23434
|
return 1;
|
|
@@ -22216,6 +23436,8 @@ ${formatCliError(err, { action: "Apply failed" })} The apply stopped part-way:
|
|
|
22216
23436
|
|
|
22217
23437
|
// src/lint.ts
|
|
22218
23438
|
var ENUM_COMPATIBLE_TYPES = /* @__PURE__ */ new Set(["enum", "string", "string[]"]);
|
|
23439
|
+
var PLATFORM_RESERVED_PAYLOAD_FIELD_NAMES = ["externalId", "partnerUserId", "scopes", "userId"];
|
|
23440
|
+
var PAYLOAD_RESERVING_SURFACES = ["record", "document"];
|
|
22219
23441
|
var BUILT_IN_SORT_BY = /* @__PURE__ */ new Set(["createdAt", "lastUpdated"]);
|
|
22220
23442
|
function lookupFieldNames(lf) {
|
|
22221
23443
|
if (typeof lf === "string") return [lf];
|
|
@@ -22289,6 +23511,17 @@ function lintBlueprint(bp) {
|
|
|
22289
23511
|
}
|
|
22290
23512
|
fieldIds.add(f.fieldId);
|
|
22291
23513
|
fieldsById.set(f.fieldId, f);
|
|
23514
|
+
if (PLATFORM_RESERVED_PAYLOAD_FIELD_NAMES.includes(f.fieldId) && (schema.allowedSurfaces ?? ["record"]).some((surface) => PAYLOAD_RESERVING_SURFACES.includes(surface))) {
|
|
23515
|
+
const seeds = (bp.seed ?? []).filter((rec) => rec.typeName === schema.typeName);
|
|
23516
|
+
const seedSetsIt = seeds.some((rec) => Object.prototype.hasOwnProperty.call(rec.fields ?? {}, f.fieldId));
|
|
23517
|
+
const seedsFail = seedSetsIt || f.required === true && seeds.some((rec) => rec.fields !== void 0);
|
|
23518
|
+
const hint = f.fieldId === "externalId" || f.fieldId === "scopes" ? `'${f.fieldId}' is a top-level field of a record or document, and a seed sets it at the top level of the seed, not as a schema field. ` : `Choose a different fieldId. `;
|
|
23519
|
+
findings.push({
|
|
23520
|
+
severity: "warning",
|
|
23521
|
+
rule: "reserved-payload-field-name",
|
|
23522
|
+
message: `schema '${schema.typeName}', field '${f.fieldId}': the platform reserves '${f.fieldId}' (with ${PLATFORM_RESERVED_PAYLOAD_FIELD_NAMES.filter((n) => n !== f.fieldId).join(", ")}) for the top level of a record or document, so a payload that carries it is refused and this field can never hold a value. ` + hint + (seedsFail ? `This schema's seed(s) will fail when they are provisioned (a seed that sets it, or a required field the seed cannot fill). Remove the field.` : f.required === true ? `The field is required, so every write to this schema will fail. Remove the field.` : `No seed trips it today, but any write that sets it will fail. Remove the field.`)
|
|
23523
|
+
});
|
|
23524
|
+
}
|
|
22292
23525
|
if (f.enumValues && f.enumValues.length > 0 && !ENUM_COMPATIBLE_TYPES.has(f.fieldType)) {
|
|
22293
23526
|
findings.push({
|
|
22294
23527
|
severity: "warning",
|
|
@@ -22305,6 +23538,13 @@ function lintBlueprint(bp) {
|
|
|
22305
23538
|
}
|
|
22306
23539
|
for (const lf of schema.lookupFields ?? []) {
|
|
22307
23540
|
for (const name of lookupFieldNames(lf)) {
|
|
23541
|
+
if (PLATFORM_RESERVED_PAYLOAD_FIELD_NAMES.includes(name)) {
|
|
23542
|
+
findings.push({
|
|
23543
|
+
severity: "error",
|
|
23544
|
+
rule: "reserved-lookup-field-name",
|
|
23545
|
+
message: `schema '${schema.typeName}': lookupField '${name}' is a name the platform reserves (${PLATFORM_RESERVED_PAYLOAD_FIELD_NAMES.join(", ")}), so it refuses the schema when it is created. A record is found by externalId through its own top-level externalId; choose a different name for an unrelated payload identifier.`
|
|
23546
|
+
});
|
|
23547
|
+
}
|
|
22308
23548
|
if (!fieldIds.has(name)) {
|
|
22309
23549
|
findings.push({
|
|
22310
23550
|
severity: "error",
|
|
@@ -22523,7 +23763,7 @@ schemas:
|
|
|
22523
23763
|
lookupFields: [title] # extra O(1) exact-match lookup indexes (max 10)
|
|
22524
23764
|
|
|
22525
23765
|
accessProfile:
|
|
22526
|
-
# SCOPE GATE: data-plane only (records/schemas/search/documents/folders/inference/entities),
|
|
23766
|
+
# SCOPE GATE: data-plane only (records/schemas/search/documents/folders/inference/entities/runs),
|
|
22527
23767
|
# PLUS profiles:r/u/d (bounded by the platform to redistributing access you already have,
|
|
22528
23768
|
# never granting more), profiles:c:self (create a profile for yourself only \u2014 the
|
|
22529
23769
|
# platform resolves "self" from the caller, not from anything you declare here) and
|
|
@@ -22549,7 +23789,7 @@ accessProfile:
|
|
|
22549
23789
|
dataScope: {}
|
|
22550
23790
|
# capabilities is a SEPARATE gated dimension from allowedActions \u2014 named platform
|
|
22551
23791
|
# effects a resource:ops verb can't express. Only 'member-lifecycle' and
|
|
22552
|
-
# 'delegate-principal-stamp' are admitted
|
|
23792
|
+
# 'delegate-principal-stamp' are admitted; every other name is REJECTED, same
|
|
22553
23793
|
# posture as the scope gate above. 'member-lifecycle' is also what lets this clause
|
|
22554
23794
|
# carry a bare profiles:c.
|
|
22555
23795
|
# capabilities: [member-lifecycle]
|
|
@@ -22680,7 +23920,7 @@ async function resolveAndValidate(arg, verb, supplied) {
|
|
|
22680
23920
|
try {
|
|
22681
23921
|
blueprint = await resolveBlueprintArg(arg, supplied);
|
|
22682
23922
|
} catch (err) {
|
|
22683
|
-
process.stderr.write(` \u2716 ${
|
|
23923
|
+
process.stderr.write(` \u2716 ${errorText(err)}
|
|
22684
23924
|
`);
|
|
22685
23925
|
return null;
|
|
22686
23926
|
}
|
|
@@ -22707,7 +23947,7 @@ async function resolveAndValidate(arg, verb, supplied) {
|
|
|
22707
23947
|
for (const f of findings) {
|
|
22708
23948
|
const mark = f.severity === "error" ? "\u2716" : "\u26A0";
|
|
22709
23949
|
const stream = f.severity === "error" ? process.stderr : process.stdout;
|
|
22710
|
-
stream.write(` ${mark} [${f.rule}] ${f.message}
|
|
23950
|
+
stream.write(` ${mark} [${f.rule}] ${singleLine(f.message, 2e3)}
|
|
22711
23951
|
`);
|
|
22712
23952
|
}
|
|
22713
23953
|
if (!lintPasses(findings)) {
|
|
@@ -22722,7 +23962,7 @@ async function runValidate(args) {
|
|
|
22722
23962
|
try {
|
|
22723
23963
|
supplied = await buildInputSupply(args.set, args.values);
|
|
22724
23964
|
} catch (err) {
|
|
22725
|
-
process.stderr.write(` \u2716 ${
|
|
23965
|
+
process.stderr.write(` \u2716 ${errorText(err)}
|
|
22726
23966
|
`);
|
|
22727
23967
|
return 2;
|
|
22728
23968
|
}
|
|
@@ -22734,6 +23974,13 @@ async function runValidate(args) {
|
|
|
22734
23974
|
process.stdout.write(
|
|
22735
23975
|
` \u2714 '${blueprint.name}' is valid \u2014 structural \u2713, scope gate \u2713 [${effectiveAccessProfileActions(blueprint).join(", ")}]${roleNote}` + (capGate.accepted.length ? `, capability gate \u2713 [${capGate.accepted.join(", ")}]` : "") + `, lint \u2713` + (warnCount ? ` (${warnCount} warning${warnCount === 1 ? "" : "s"})` : "") + "\n"
|
|
22736
23976
|
);
|
|
23977
|
+
if (declaresAgents(blueprint)) {
|
|
23978
|
+
const n = Object.keys(blueprint.agents ?? {}).length;
|
|
23979
|
+
process.stdout.write(
|
|
23980
|
+
` \u2714 The 'agents' section is valid (${n} agent${n === 1 ? "" : "s"}). 'apply' creates each through POST /v1/agents, which needs the 'agents:c' scope on the credential that applies it ('agents:u' and 'agents:r' as well for an agent that already exists).
|
|
23981
|
+
`
|
|
23982
|
+
);
|
|
23983
|
+
}
|
|
22737
23984
|
return 0;
|
|
22738
23985
|
}
|
|
22739
23986
|
async function runPlan(args) {
|
|
@@ -22741,13 +23988,20 @@ async function runPlan(args) {
|
|
|
22741
23988
|
try {
|
|
22742
23989
|
supplied = await buildInputSupply(args.set, args.values);
|
|
22743
23990
|
} catch (err) {
|
|
22744
|
-
process.stderr.write(` \u2716 ${
|
|
23991
|
+
process.stderr.write(` \u2716 ${errorText(err)}
|
|
22745
23992
|
`);
|
|
22746
23993
|
return 2;
|
|
22747
23994
|
}
|
|
22748
23995
|
const blueprint = await resolveAndValidate(args.file, "plan", supplied);
|
|
22749
23996
|
if (!blueprint) return 2;
|
|
22750
|
-
|
|
23997
|
+
let steps;
|
|
23998
|
+
try {
|
|
23999
|
+
steps = buildPlan(blueprint);
|
|
24000
|
+
} catch (err) {
|
|
24001
|
+
process.stderr.write(` \u2716 ${errorText(err)}
|
|
24002
|
+
`);
|
|
24003
|
+
return 2;
|
|
24004
|
+
}
|
|
22751
24005
|
process.stdout.write(`
|
|
22752
24006
|
${renderPlan(blueprint, steps)}
|
|
22753
24007
|
`);
|
|
@@ -22817,7 +24071,7 @@ async function runInit(args) {
|
|
|
22817
24071
|
try {
|
|
22818
24072
|
await writeFile2(outPath, scaffold, "utf8");
|
|
22819
24073
|
} catch (err) {
|
|
22820
|
-
process.stderr.write(` \u2716 Could not write ${outPath}: ${
|
|
24074
|
+
process.stderr.write(` \u2716 Could not write ${outPath}: ${errorText(err)}
|
|
22821
24075
|
`);
|
|
22822
24076
|
return 2;
|
|
22823
24077
|
}
|
|
@@ -22850,7 +24104,7 @@ async function runBlueprintCli(argv, deps = {}) {
|
|
|
22850
24104
|
try {
|
|
22851
24105
|
args = parseBlueprintArgs(argv);
|
|
22852
24106
|
} catch (err) {
|
|
22853
|
-
process.stderr.write(`${
|
|
24107
|
+
process.stderr.write(`${errorText(err)}
|
|
22854
24108
|
|
|
22855
24109
|
${BLUEPRINT_HELP}
|
|
22856
24110
|
`);
|
|
@@ -25875,100 +27129,338 @@ async function runScriptsList(opts, deps = {}) {
|
|
|
25875
27129
|
}
|
|
25876
27130
|
process.stdout.write(
|
|
25877
27131
|
"\n" + formatTable(rows, [
|
|
25878
|
-
{ header: "name", get: (s) => s.name },
|
|
25879
|
-
{ header: "version", get: (s) => s.scriptVersion === void 0 ? void 0 : String(s.scriptVersion) },
|
|
25880
|
-
{ header: "id", get: (s) => s.id },
|
|
25881
|
-
{ header: "bytes", get: (s) => s.source === void 0 ? void 0 : String(utf8Bytes2(s.source)) },
|
|
25882
|
-
{ header: "createdAt", get: (s) => s.createdAt }
|
|
25883
|
-
]) + "\n"
|
|
27132
|
+
{ header: "name", get: (s) => s.name },
|
|
27133
|
+
{ header: "version", get: (s) => s.scriptVersion === void 0 ? void 0 : String(s.scriptVersion) },
|
|
27134
|
+
{ header: "id", get: (s) => s.id },
|
|
27135
|
+
{ header: "bytes", get: (s) => s.source === void 0 ? void 0 : String(utf8Bytes2(s.source)) },
|
|
27136
|
+
{ header: "createdAt", get: (s) => s.createdAt }
|
|
27137
|
+
]) + "\n"
|
|
27138
|
+
);
|
|
27139
|
+
return 0;
|
|
27140
|
+
} catch (err) {
|
|
27141
|
+
process.stderr.write(formatCliError(err, { action: "scripts list failed" }));
|
|
27142
|
+
return 1;
|
|
27143
|
+
}
|
|
27144
|
+
});
|
|
27145
|
+
}
|
|
27146
|
+
async function runScriptsGet(opts, deps = {}) {
|
|
27147
|
+
return withAuth(opts, deps, async (auth) => {
|
|
27148
|
+
const client = auth.client;
|
|
27149
|
+
try {
|
|
27150
|
+
const script = await client.scripts.getScript({ id: opts.id });
|
|
27151
|
+
if (opts.json) {
|
|
27152
|
+
process.stdout.write(`${formatJson(script)}
|
|
27153
|
+
`);
|
|
27154
|
+
return 0;
|
|
27155
|
+
}
|
|
27156
|
+
process.stdout.write(`
|
|
27157
|
+
${renderScript(script, { source: true })}
|
|
27158
|
+
`);
|
|
27159
|
+
return 0;
|
|
27160
|
+
} catch (err) {
|
|
27161
|
+
process.stderr.write(formatCliError(err, { action: "scripts get failed" }));
|
|
27162
|
+
return 1;
|
|
27163
|
+
}
|
|
27164
|
+
});
|
|
27165
|
+
}
|
|
27166
|
+
async function runScriptsDelete(opts, deps = {}) {
|
|
27167
|
+
process.stderr.write(
|
|
27168
|
+
`
|
|
27169
|
+
\u26A0 Deleting script version '${opts.id}' is permanent \u2014 the stored source is the only copy.
|
|
27170
|
+
Other versions of the same name are unaffected.
|
|
27171
|
+
The platform refuses this delete if a trigger rule still references this version \u2014
|
|
27172
|
+
directly, or via 'latest' if this is the name's current newest version \u2014 and names the
|
|
27173
|
+
offending rule instead of deleting. If no rule references it, the delete proceeds.
|
|
27174
|
+
And if this is the name's LATEST version, its identity is REUSED: the next push of that
|
|
27175
|
+
name recomputes the same version number and the same id, so anything holding a pinned
|
|
27176
|
+
'${opts.id}' or a name@version reference will silently resolve to different source.
|
|
27177
|
+
|
|
27178
|
+
`
|
|
27179
|
+
);
|
|
27180
|
+
if (!opts.force) {
|
|
27181
|
+
const isTty = deps.isTty ?? Boolean(process.stdin.isTTY);
|
|
27182
|
+
if (!isTty) {
|
|
27183
|
+
process.stderr.write(
|
|
27184
|
+
` \u2716 scripts delete is irreversible and needs an interactive confirmation.
|
|
27185
|
+
Re-run with --force to delete '${opts.id}' without a prompt.
|
|
27186
|
+
`
|
|
27187
|
+
);
|
|
27188
|
+
return 2;
|
|
27189
|
+
}
|
|
27190
|
+
const promptCtx = deps.createPrompt ? deps.createPrompt() : (() => {
|
|
27191
|
+
const rl = readline8.createInterface({ input: process.stdin, output: process.stdout });
|
|
27192
|
+
return { prompt: (q) => rl.question(q), close: () => rl.close() };
|
|
27193
|
+
})();
|
|
27194
|
+
try {
|
|
27195
|
+
const typed = await promptCtx.prompt(` Type the version id ('${opts.id}') to confirm: `);
|
|
27196
|
+
if (typed.trim() !== opts.id) {
|
|
27197
|
+
process.stderr.write(` \u2716 Confirmation did not match \u2014 nothing deleted.
|
|
27198
|
+
`);
|
|
27199
|
+
return 1;
|
|
27200
|
+
}
|
|
27201
|
+
} finally {
|
|
27202
|
+
promptCtx.close();
|
|
27203
|
+
}
|
|
27204
|
+
}
|
|
27205
|
+
return withAuth(opts, deps, async (auth) => {
|
|
27206
|
+
const client = auth.client;
|
|
27207
|
+
try {
|
|
27208
|
+
await client.scripts.deleteScript({ id: opts.id });
|
|
27209
|
+
if (opts.json) {
|
|
27210
|
+
process.stdout.write(`${formatJson({ id: opts.id, deleted: true })}
|
|
27211
|
+
`);
|
|
27212
|
+
return 0;
|
|
27213
|
+
}
|
|
27214
|
+
process.stdout.write(` \u2714 Deleted script version '${opts.id}' from '${opts.context}'.
|
|
27215
|
+
`);
|
|
27216
|
+
return 0;
|
|
27217
|
+
} catch (err) {
|
|
27218
|
+
process.stderr.write(formatCliError(err, { action: "scripts delete failed" }));
|
|
27219
|
+
return 1;
|
|
27220
|
+
}
|
|
27221
|
+
});
|
|
27222
|
+
}
|
|
27223
|
+
function errText(err) {
|
|
27224
|
+
return err instanceof Error ? err.message : String(err);
|
|
27225
|
+
}
|
|
27226
|
+
|
|
27227
|
+
// src/commands/agents.ts
|
|
27228
|
+
import { readFile as readFile4 } from "fs/promises";
|
|
27229
|
+
import { extname as extname2 } from "path";
|
|
27230
|
+
import { parse as parseYaml2 } from "yaml";
|
|
27231
|
+
var DEFINITION_KEYS = /* @__PURE__ */ new Set(["description", "authority", "model", "system", "tools", "limits", "budget"]);
|
|
27232
|
+
async function readDefinition(path4, name) {
|
|
27233
|
+
let raw;
|
|
27234
|
+
try {
|
|
27235
|
+
raw = await readFile4(path4, "utf8");
|
|
27236
|
+
} catch (err) {
|
|
27237
|
+
return { error: `could not read --file '${shortPath(path4, 300)}': ${quotedBlock(err instanceof Error ? err.message : String(err))}` };
|
|
27238
|
+
}
|
|
27239
|
+
let parsed;
|
|
27240
|
+
try {
|
|
27241
|
+
const ext = extname2(path4).toLowerCase();
|
|
27242
|
+
parsed = ext === ".yaml" || ext === ".yml" ? parseYaml2(raw, { logLevel: "error" }) : JSON.parse(raw);
|
|
27243
|
+
} catch (err) {
|
|
27244
|
+
return { error: `--file '${shortPath(path4, 300)}' is not valid ${/\.ya?ml$/i.test(path4) ? "YAML" : "JSON"}: ${quotedBlock(err instanceof Error ? err.message : String(err))}` };
|
|
27245
|
+
}
|
|
27246
|
+
if (parsed === null || typeof parsed !== "object" || Array.isArray(parsed)) {
|
|
27247
|
+
return { error: `--file '${shortPath(path4, 300)}' must hold one agent definition (an object with at least a 'model').` };
|
|
27248
|
+
}
|
|
27249
|
+
const entries = Object.entries(parsed);
|
|
27250
|
+
const unknown = entries.map(([k]) => k).filter((k) => !DEFINITION_KEYS.has(k) && k !== "name");
|
|
27251
|
+
if (unknown.length > 0) {
|
|
27252
|
+
return {
|
|
27253
|
+
error: `--file '${shortPath(path4, 300)}' has ${unknown.length === 1 ? "a key" : "keys"} an agent definition does not use: ${unknown.map((k) => `'${singleLine(k, 80)}'`).join(", ")}. The fields are ${[...DEFINITION_KEYS].join(", ")}.`
|
|
27254
|
+
};
|
|
27255
|
+
}
|
|
27256
|
+
const declaredName = parsed.name;
|
|
27257
|
+
if (declaredName !== void 0 && typeof declaredName !== "string") {
|
|
27258
|
+
return { error: `--file '${shortPath(path4, 300)}' has a 'name' that is not a string.` };
|
|
27259
|
+
}
|
|
27260
|
+
if (declaredName !== void 0 && name !== void 0 && declaredName !== name) {
|
|
27261
|
+
return {
|
|
27262
|
+
error: `--file '${shortPath(path4, 300)}' names the agent '${singleLine(String(declaredName), 80)}', not '${name}'. Remove 'name' from the file or make them match.`
|
|
27263
|
+
};
|
|
27264
|
+
}
|
|
27265
|
+
const { name: _name, ...definition } = parsed;
|
|
27266
|
+
void _name;
|
|
27267
|
+
if (typeof definition.model !== "string" || definition.model.trim().length === 0) {
|
|
27268
|
+
return { error: `--file '${shortPath(path4, 300)}' needs a 'model': the id of the model the agent runs on.` };
|
|
27269
|
+
}
|
|
27270
|
+
if (singleLine(definition.model, definition.model.length + 1) !== definition.model) {
|
|
27271
|
+
return { error: `--file '${shortPath(path4, 300)}' has a 'model' with a line break or a control character in it; a model id is plain text on one line.` };
|
|
27272
|
+
}
|
|
27273
|
+
return { definition, ...declaredName !== void 0 ? { declaredName } : {} };
|
|
27274
|
+
}
|
|
27275
|
+
function withHint(hint) {
|
|
27276
|
+
return hint ? ` ${hint}
|
|
27277
|
+
` : "";
|
|
27278
|
+
}
|
|
27279
|
+
function toolsLine(a) {
|
|
27280
|
+
if (!a.tools) return void 0;
|
|
27281
|
+
if (a.tools.length === 0) return "(none)";
|
|
27282
|
+
return a.tools.map((t) => t.allowWrites ? `${t.name} (writes)` : t.name).join(", ");
|
|
27283
|
+
}
|
|
27284
|
+
function oneLine(value, maxLength = 200) {
|
|
27285
|
+
return value === void 0 ? void 0 : singleLine(value, maxLength);
|
|
27286
|
+
}
|
|
27287
|
+
function indentedBlock(value, maxLength = 1e5) {
|
|
27288
|
+
if (value === void 0) return void 0;
|
|
27289
|
+
return value.split(/\r\n|[\r\n\u0085\u2028\u2029]/).map((line, i) => {
|
|
27290
|
+
const clean = stripControlCharacters(line.replace(/\t/g, " "), maxLength);
|
|
27291
|
+
return i === 0 || clean === "" ? clean : ` ${clean}`;
|
|
27292
|
+
}).join("\n").trimEnd();
|
|
27293
|
+
}
|
|
27294
|
+
function renderAgent(a) {
|
|
27295
|
+
return formatKeyValues([
|
|
27296
|
+
["id", oneLine(a.id)],
|
|
27297
|
+
["name", oneLine(a.name)],
|
|
27298
|
+
["status", oneLine(a.status)],
|
|
27299
|
+
["version", oneLine(a.version === void 0 ? void 0 : String(a.version))],
|
|
27300
|
+
["latestVersion", oneLine(a.latestVersion === void 0 ? void 0 : String(a.latestVersion))],
|
|
27301
|
+
["model", oneLine(a.model)],
|
|
27302
|
+
["authority", oneLine(a.authority)],
|
|
27303
|
+
["description", oneLine(a.description, 1e3)],
|
|
27304
|
+
["tools", oneLine(toolsLine(a), 5e3)],
|
|
27305
|
+
["limits", oneLine(a.limits === void 0 ? void 0 : JSON.stringify(a.limits), 1e3)],
|
|
27306
|
+
["budget", oneLine(a.budget === void 0 ? void 0 : JSON.stringify(a.budget), 1e3)],
|
|
27307
|
+
["provisionedBy", oneLine(a.provisionedBy)],
|
|
27308
|
+
["createdAt", oneLine(a.createdAt)],
|
|
27309
|
+
["system", indentedBlock(a.system)]
|
|
27310
|
+
]);
|
|
27311
|
+
}
|
|
27312
|
+
async function runAgentsList(opts, deps = {}) {
|
|
27313
|
+
return withAuth(opts, deps, async (auth) => {
|
|
27314
|
+
const client = auth.client;
|
|
27315
|
+
try {
|
|
27316
|
+
const rows = await drainPages(
|
|
27317
|
+
(startFrom) => client.agents.listAgents({
|
|
27318
|
+
contextId: opts.context,
|
|
27319
|
+
limit: PAGE_LIMIT,
|
|
27320
|
+
...opts.includeRetired ? { includeRetired: true } : {},
|
|
27321
|
+
...startFrom ? { startFrom } : {}
|
|
27322
|
+
})
|
|
27323
|
+
);
|
|
27324
|
+
if (opts.json) {
|
|
27325
|
+
process.stdout.write(`${formatJson(rows)}
|
|
27326
|
+
`);
|
|
27327
|
+
return 0;
|
|
27328
|
+
}
|
|
27329
|
+
if (rows.length === 0) {
|
|
27330
|
+
process.stdout.write(`
|
|
27331
|
+
No agents in '${opts.context}'.
|
|
27332
|
+
`);
|
|
27333
|
+
return 0;
|
|
27334
|
+
}
|
|
27335
|
+
const summaryOnly = rows.every((a) => a.model === void 0);
|
|
27336
|
+
process.stdout.write(
|
|
27337
|
+
"\n" + formatTable(rows, [
|
|
27338
|
+
{ header: "name", get: (a) => oneLine(a.name) },
|
|
27339
|
+
{ header: "version", get: (a) => oneLine(a.latestVersion === void 0 ? void 0 : String(a.latestVersion)) },
|
|
27340
|
+
{ header: "status", get: (a) => oneLine(a.status) },
|
|
27341
|
+
{ header: "model", get: (a) => oneLine(a.model) },
|
|
27342
|
+
{ header: "id", get: (a) => oneLine(a.id) }
|
|
27343
|
+
]) + "\n" + (summaryOnly ? "\n (A summary: this credential may start runs but cannot read agent definitions, which needs the 'agents:r' scope.)\n" : "")
|
|
25884
27344
|
);
|
|
25885
27345
|
return 0;
|
|
25886
27346
|
} catch (err) {
|
|
25887
|
-
process.stderr.write(formatCliError(err, { action: "
|
|
27347
|
+
process.stderr.write(formatCliError(sanitizedAgentError(err), { action: "agents list failed" }) + withHint(agentErrorHint(err, opts.context, "'agents:r'")));
|
|
25888
27348
|
return 1;
|
|
25889
27349
|
}
|
|
25890
27350
|
});
|
|
25891
27351
|
}
|
|
25892
|
-
async function
|
|
27352
|
+
async function runAgentsGet(opts, deps = {}) {
|
|
25893
27353
|
return withAuth(opts, deps, async (auth) => {
|
|
25894
27354
|
const client = auth.client;
|
|
25895
27355
|
try {
|
|
25896
|
-
const
|
|
27356
|
+
const agent = await client.agents.getAgent({
|
|
27357
|
+
id: opts.id,
|
|
27358
|
+
contextId: opts.context,
|
|
27359
|
+
...opts.version !== void 0 ? { version: opts.version } : {}
|
|
27360
|
+
});
|
|
25897
27361
|
if (opts.json) {
|
|
25898
|
-
process.stdout.write(`${formatJson(
|
|
27362
|
+
process.stdout.write(`${formatJson(agent)}
|
|
25899
27363
|
`);
|
|
25900
27364
|
return 0;
|
|
25901
27365
|
}
|
|
25902
27366
|
process.stdout.write(`
|
|
25903
|
-
${
|
|
27367
|
+
${renderAgent(agent)}
|
|
25904
27368
|
`);
|
|
25905
27369
|
return 0;
|
|
25906
27370
|
} catch (err) {
|
|
25907
|
-
process.stderr.write(formatCliError(err, { action: "
|
|
27371
|
+
process.stderr.write(formatCliError(sanitizedAgentError(err), { action: "agents get failed" }) + withHint(agentErrorHint(err, opts.context, "'agents:r'")));
|
|
25908
27372
|
return 1;
|
|
25909
27373
|
}
|
|
25910
27374
|
});
|
|
25911
27375
|
}
|
|
25912
|
-
async function
|
|
25913
|
-
|
|
25914
|
-
|
|
25915
|
-
\
|
|
25916
|
-
Other versions of the same name are unaffected.
|
|
25917
|
-
The platform refuses this delete if a trigger rule still references this version \u2014
|
|
25918
|
-
directly, or via 'latest' if this is the name's current newest version \u2014 and names the
|
|
25919
|
-
offending rule instead of deleting. If no rule references it, the delete proceeds.
|
|
25920
|
-
And if this is the name's LATEST version, its identity is REUSED: the next push of that
|
|
25921
|
-
name recomputes the same version number and the same id, so anything holding a pinned
|
|
25922
|
-
'${opts.id}' or a name@version reference will silently resolve to different source.
|
|
25923
|
-
|
|
25924
|
-
`
|
|
25925
|
-
);
|
|
25926
|
-
if (!opts.force) {
|
|
25927
|
-
const isTty = deps.isTty ?? Boolean(process.stdin.isTTY);
|
|
25928
|
-
if (!isTty) {
|
|
25929
|
-
process.stderr.write(
|
|
25930
|
-
` \u2716 scripts delete is irreversible and needs an interactive confirmation.
|
|
25931
|
-
Re-run with --force to delete '${opts.id}' without a prompt.
|
|
27376
|
+
async function runAgentsCreate(opts, deps = {}) {
|
|
27377
|
+
if (!AGENT_NAME_RE.test(opts.name)) {
|
|
27378
|
+
process.stderr.write(
|
|
27379
|
+
` \u2716 '${opts.name}' is not a valid agent name \u2014 use 1-64 characters of letters, digits, underscores ('_') and hyphens ('-').
|
|
25932
27380
|
`
|
|
25933
|
-
|
|
25934
|
-
|
|
25935
|
-
|
|
25936
|
-
|
|
25937
|
-
|
|
25938
|
-
|
|
25939
|
-
|
|
27381
|
+
);
|
|
27382
|
+
return 2;
|
|
27383
|
+
}
|
|
27384
|
+
const read = await readDefinition(opts.file, opts.name);
|
|
27385
|
+
if ("error" in read) {
|
|
27386
|
+
process.stderr.write(` \u2716 ${read.error}
|
|
27387
|
+
`);
|
|
27388
|
+
return 2;
|
|
27389
|
+
}
|
|
27390
|
+
return withAuth(opts, deps, async (auth) => {
|
|
27391
|
+
const client = auth.client;
|
|
25940
27392
|
try {
|
|
25941
|
-
const
|
|
25942
|
-
|
|
25943
|
-
|
|
27393
|
+
const agent = await client.agents.createAgent({
|
|
27394
|
+
contextId: opts.context,
|
|
27395
|
+
body: { ...read.definition, name: opts.name }
|
|
27396
|
+
});
|
|
27397
|
+
if (opts.json) {
|
|
27398
|
+
process.stdout.write(`${formatJson(agent)}
|
|
25944
27399
|
`);
|
|
25945
|
-
return
|
|
27400
|
+
return 0;
|
|
25946
27401
|
}
|
|
25947
|
-
|
|
25948
|
-
|
|
27402
|
+
if (agent.created === false) {
|
|
27403
|
+
process.stdout.write(
|
|
27404
|
+
`
|
|
27405
|
+
= agent '${opts.name}' already exists at version ${oneLine(String(agent.version ?? agent.latestVersion))} \u2014 nothing was written.
|
|
27406
|
+
To change it, write a new version: vectros agents version ${oneLine(agent.id)} --context ${opts.context} --file <definition>
|
|
27407
|
+
`
|
|
27408
|
+
);
|
|
27409
|
+
return 0;
|
|
27410
|
+
}
|
|
27411
|
+
process.stdout.write(`
|
|
27412
|
+
\u2714 created agent '${opts.name}' at version ${oneLine(String(agent.version ?? 1))} in '${opts.context}'
|
|
27413
|
+
${renderAgent(agent)}
|
|
27414
|
+
`);
|
|
27415
|
+
return 0;
|
|
27416
|
+
} catch (err) {
|
|
27417
|
+
process.stderr.write(
|
|
27418
|
+
formatCliError(sanitizedAgentError(err), { action: `agents create '${opts.name}' failed` }) + withHint(agentErrorHint(err, opts.context, "'agents:c'"))
|
|
27419
|
+
);
|
|
27420
|
+
return 1;
|
|
25949
27421
|
}
|
|
27422
|
+
});
|
|
27423
|
+
}
|
|
27424
|
+
async function runAgentsVersion(opts, deps = {}) {
|
|
27425
|
+
const read = await readDefinition(opts.file);
|
|
27426
|
+
if ("error" in read) {
|
|
27427
|
+
process.stderr.write(` \u2716 ${read.error}
|
|
27428
|
+
`);
|
|
27429
|
+
return 2;
|
|
25950
27430
|
}
|
|
25951
27431
|
return withAuth(opts, deps, async (auth) => {
|
|
25952
27432
|
const client = auth.client;
|
|
25953
27433
|
try {
|
|
25954
|
-
await client.
|
|
27434
|
+
const agent = await client.agents.updateAgent({
|
|
27435
|
+
id: opts.id,
|
|
27436
|
+
contextId: opts.context,
|
|
27437
|
+
...opts.restore ? { restore: true } : {},
|
|
27438
|
+
body: {
|
|
27439
|
+
...read.definition,
|
|
27440
|
+
...read.declaredName !== void 0 ? { name: read.declaredName } : {},
|
|
27441
|
+
...opts.expectedVersion !== void 0 ? { expectedVersion: opts.expectedVersion } : {}
|
|
27442
|
+
}
|
|
27443
|
+
});
|
|
25955
27444
|
if (opts.json) {
|
|
25956
|
-
process.stdout.write(`${formatJson(
|
|
27445
|
+
process.stdout.write(`${formatJson(agent)}
|
|
25957
27446
|
`);
|
|
25958
27447
|
return 0;
|
|
25959
27448
|
}
|
|
25960
|
-
process.stdout.write(
|
|
25961
|
-
`
|
|
27449
|
+
process.stdout.write(
|
|
27450
|
+
`
|
|
27451
|
+
\u2714 wrote version ${oneLine(String(agent.version ?? agent.latestVersion))} of agent '${oneLine(agent.name ?? opts.id)}' in '${opts.context}'
|
|
27452
|
+
${renderAgent(agent)}
|
|
27453
|
+
`
|
|
27454
|
+
);
|
|
25962
27455
|
return 0;
|
|
25963
27456
|
} catch (err) {
|
|
25964
|
-
process.stderr.write(
|
|
27457
|
+
process.stderr.write(
|
|
27458
|
+
formatCliError(sanitizedAgentError(err), { action: `agents version '${opts.id}' failed` }) + withHint(agentErrorHint(err, opts.context, "'agents:u'"))
|
|
27459
|
+
);
|
|
25965
27460
|
return 1;
|
|
25966
27461
|
}
|
|
25967
27462
|
});
|
|
25968
27463
|
}
|
|
25969
|
-
function errText(err) {
|
|
25970
|
-
return err instanceof Error ? err.message : String(err);
|
|
25971
|
-
}
|
|
25972
27464
|
|
|
25973
27465
|
// src/commands/assume.ts
|
|
25974
27466
|
function parseAssumeScopes(raw) {
|
|
@@ -26230,8 +27722,10 @@ function withTimeout(p, ms) {
|
|
|
26230
27722
|
|
|
26231
27723
|
// src/commands/blueprint-test.ts
|
|
26232
27724
|
import { randomUUID as randomUUID2 } from "crypto";
|
|
27725
|
+
import { writeSync } from "fs";
|
|
26233
27726
|
|
|
26234
27727
|
// src/harness.ts
|
|
27728
|
+
import { createHash as createHash2 } from "crypto";
|
|
26235
27729
|
var errMsg = (e) => e instanceof Error ? e.message : String(e);
|
|
26236
27730
|
var isNotFound2 = (e) => typeof e === "object" && e !== null && e.statusCode === 404;
|
|
26237
27731
|
var SNAPSHOT_PAGE_SIZE = 100;
|
|
@@ -26438,7 +27932,7 @@ async function runAssertions(client, bootstrapClient, deps, blueprint, tenantId,
|
|
|
26438
27932
|
}
|
|
26439
27933
|
return assertions;
|
|
26440
27934
|
}
|
|
26441
|
-
async function runTeardown(client, bootstrapClient, blueprint, machineId, snapshot, result) {
|
|
27935
|
+
async function runTeardown(client, bootstrapClient, blueprint, machineId, snapshot, result, runAgentNames, createdAgentIds, seenAgentNames, attemptedAgentNames, refusedAgentNames) {
|
|
26442
27936
|
const steps = [];
|
|
26443
27937
|
const keyName = result?.keyName ?? keyNameFor(blueprint.name, machineId);
|
|
26444
27938
|
try {
|
|
@@ -26456,6 +27950,7 @@ async function runTeardown(client, bootstrapClient, blueprint, machineId, snapsh
|
|
|
26456
27950
|
} catch (e) {
|
|
26457
27951
|
steps.push({ step: `revoke key ${keyName}`, ok: false, error: errMsg(e) });
|
|
26458
27952
|
}
|
|
27953
|
+
steps.push(...await teardownAgents(client, blueprint, runAgentNames, createdAgentIds, seenAgentNames, attemptedAgentNames, refusedAgentNames, result));
|
|
26459
27954
|
if (!result) return steps;
|
|
26460
27955
|
for (const id of result.seededRecordIds) {
|
|
26461
27956
|
try {
|
|
@@ -26649,19 +28144,247 @@ async function runTeardown(client, bootstrapClient, blueprint, machineId, snapsh
|
|
|
26649
28144
|
}
|
|
26650
28145
|
return steps;
|
|
26651
28146
|
}
|
|
28147
|
+
var AGENT_RUN_SUFFIX_LENGTH = 10;
|
|
28148
|
+
var MAX_TESTABLE_AGENT_NAME_LENGTH = 64 - AGENT_RUN_SUFFIX_LENGTH;
|
|
28149
|
+
function agentRunSuffix(machineId) {
|
|
28150
|
+
return `-t${createHash2("sha256").update(machineId).digest("hex").slice(0, 8)}`;
|
|
28151
|
+
}
|
|
28152
|
+
function agentNamesProblem(blueprint) {
|
|
28153
|
+
const tooLong = Object.keys(blueprint.agents ?? {}).filter((name) => name.length > MAX_TESTABLE_AGENT_NAME_LENGTH);
|
|
28154
|
+
if (tooLong.length === 0) return void 0;
|
|
28155
|
+
return `blueprint-test gives each run its own agent names by adding ${AGENT_RUN_SUFFIX_LENGTH} characters, so an agent name can be at most ${MAX_TESTABLE_AGENT_NAME_LENGTH} characters here. Too long: ${tooLong.map((n) => `'${n}'`).join(", ")}.`;
|
|
28156
|
+
}
|
|
28157
|
+
function scopeAgentsToRun(blueprint, machineId) {
|
|
28158
|
+
const declared = Object.entries(blueprint.agents ?? {});
|
|
28159
|
+
if (declared.length === 0) return { blueprint, names: {} };
|
|
28160
|
+
const problem = agentNamesProblem(blueprint);
|
|
28161
|
+
if (problem) throw new Error(problem);
|
|
28162
|
+
const suffix = agentRunSuffix(machineId);
|
|
28163
|
+
const names = /* @__PURE__ */ Object.create(null);
|
|
28164
|
+
const agents = /* @__PURE__ */ Object.create(null);
|
|
28165
|
+
for (const [name, definition] of declared) {
|
|
28166
|
+
names[name] = `${name}${suffix}`;
|
|
28167
|
+
agents[names[name]] = definition;
|
|
28168
|
+
}
|
|
28169
|
+
return { blueprint: { ...blueprint, agents }, names };
|
|
28170
|
+
}
|
|
28171
|
+
function isAgentRemovalFailure(step) {
|
|
28172
|
+
return !step.ok && /^(retire|purge) agent /.test(step.step);
|
|
28173
|
+
}
|
|
28174
|
+
async function teardownAgents(client, blueprint, runNames, createdIds, seenAgentNames, attemptedAgentNames, refusedAgentNames, result) {
|
|
28175
|
+
const steps = [];
|
|
28176
|
+
if (runNames.size === 0) return steps;
|
|
28177
|
+
const found = new Map(createdIds);
|
|
28178
|
+
const notOurs = new Set(seenAgentNames);
|
|
28179
|
+
for (const a of result?.agentActions ?? []) {
|
|
28180
|
+
if (!runNames.has(a.name)) continue;
|
|
28181
|
+
if (a.action === "created") found.set(a.name, a.id);
|
|
28182
|
+
else {
|
|
28183
|
+
notOurs.add(a.name);
|
|
28184
|
+
steps.push({ step: `retire agent ${a.name}`, ok: true, skipped: true, error: `skipped \u2014 this run did not create it (${a.action})` });
|
|
28185
|
+
}
|
|
28186
|
+
}
|
|
28187
|
+
if (!result) {
|
|
28188
|
+
for (const name of [...seenAgentNames].sort()) {
|
|
28189
|
+
steps.push({ step: `retire agent ${name}`, ok: true, skipped: true, error: "skipped \u2014 it existed before this run created anything" });
|
|
28190
|
+
}
|
|
28191
|
+
}
|
|
28192
|
+
const lost = new Set(
|
|
28193
|
+
[...attemptedAgentNames].filter((n) => runNames.has(n) && !found.has(n) && !notOurs.has(n) && !refusedAgentNames.has(n))
|
|
28194
|
+
);
|
|
28195
|
+
if (lost.size > 0 && !result) {
|
|
28196
|
+
let listed = false;
|
|
28197
|
+
try {
|
|
28198
|
+
let startFrom;
|
|
28199
|
+
for (; ; ) {
|
|
28200
|
+
const page = await client.agents.listAgents({ contextId: blueprint.contextId, limit: SNAPSHOT_PAGE_SIZE, startFrom });
|
|
28201
|
+
for (const row of page.data ?? []) {
|
|
28202
|
+
if (!row.name || !row.id || !lost.has(row.name)) continue;
|
|
28203
|
+
if (row.provisionedBy === blueprint.name || row.provisionedBy === void 0) found.set(row.name, row.id);
|
|
28204
|
+
}
|
|
28205
|
+
if (!page.nextCursor) break;
|
|
28206
|
+
startFrom = page.nextCursor;
|
|
28207
|
+
}
|
|
28208
|
+
listed = true;
|
|
28209
|
+
} catch (e) {
|
|
28210
|
+
if (isNotFound2(e)) {
|
|
28211
|
+
steps.push({ step: "list agents", ok: true, skipped: true, error: "skipped \u2014 the context does not exist, so nothing was created" });
|
|
28212
|
+
} else {
|
|
28213
|
+
steps.push({
|
|
28214
|
+
step: "list agents",
|
|
28215
|
+
ok: false,
|
|
28216
|
+
error: `could not list agents, so an agent created before a failure may remain: ${errMsg(e)}`
|
|
28217
|
+
});
|
|
28218
|
+
}
|
|
28219
|
+
}
|
|
28220
|
+
if (listed) {
|
|
28221
|
+
for (const name of [...lost].filter((n) => !found.has(n)).sort()) {
|
|
28222
|
+
steps.push({
|
|
28223
|
+
step: `check agent ${name}`,
|
|
28224
|
+
ok: true,
|
|
28225
|
+
skipped: true,
|
|
28226
|
+
error: "could not confirm whether it was created (its create got no definite answer and the listing does not show it); check with `vectros agents list --context " + blueprint.contextId + " --include-retired`"
|
|
28227
|
+
});
|
|
28228
|
+
}
|
|
28229
|
+
}
|
|
28230
|
+
}
|
|
28231
|
+
for (const [name, id] of [...found].sort(([a], [b]) => a.localeCompare(b))) {
|
|
28232
|
+
try {
|
|
28233
|
+
await client.agents.deleteAgent({ id, contextId: blueprint.contextId });
|
|
28234
|
+
steps.push({ step: `retire agent ${name}`, ok: true });
|
|
28235
|
+
} catch (e) {
|
|
28236
|
+
steps.push({
|
|
28237
|
+
step: `retire agent ${name}`,
|
|
28238
|
+
ok: false,
|
|
28239
|
+
error: `the agent is still active and can be run; retire it with DELETE /v1/agents/${id} (needs agents:d): ${errMsg(e)}`
|
|
28240
|
+
});
|
|
28241
|
+
continue;
|
|
28242
|
+
}
|
|
28243
|
+
try {
|
|
28244
|
+
await client.agents.deleteAgent({ id, contextId: blueprint.contextId, purge: true });
|
|
28245
|
+
steps.push({ step: `purge agent ${name}`, ok: true });
|
|
28246
|
+
} catch (e) {
|
|
28247
|
+
steps.push({ step: `purge agent ${name}`, ok: false, error: `retired, but its content was not erased; erase it with DELETE /v1/agents/${id}?purge=true (needs agents:d): ${errMsg(e)}` });
|
|
28248
|
+
}
|
|
28249
|
+
}
|
|
28250
|
+
return steps;
|
|
28251
|
+
}
|
|
28252
|
+
function onlyAttempted(names, attempted) {
|
|
28253
|
+
const kept = Object.entries(names).filter(([, runName]) => attempted.has(runName));
|
|
28254
|
+
return kept.length > 0 ? Object.fromEntries(kept) : void 0;
|
|
28255
|
+
}
|
|
28256
|
+
var HarnessInterrupted = class extends Error {
|
|
28257
|
+
constructor(signal) {
|
|
28258
|
+
super(`interrupted by ${signal}`);
|
|
28259
|
+
this.signal = signal;
|
|
28260
|
+
}
|
|
28261
|
+
signal;
|
|
28262
|
+
};
|
|
28263
|
+
function watchForInterrupt(deps, run) {
|
|
28264
|
+
const source = deps.signals ?? process;
|
|
28265
|
+
const say = (text) => {
|
|
28266
|
+
try {
|
|
28267
|
+
(deps.notify ?? (() => {
|
|
28268
|
+
}))(text);
|
|
28269
|
+
} catch {
|
|
28270
|
+
}
|
|
28271
|
+
};
|
|
28272
|
+
const forceExit = deps.forceExit ?? ((code) => process.exit(code));
|
|
28273
|
+
let first;
|
|
28274
|
+
let toreDown = false;
|
|
28275
|
+
let standing = () => ({ created: /* @__PURE__ */ new Map(), inFlight: [] });
|
|
28276
|
+
const heard = (signal) => {
|
|
28277
|
+
if (first !== void 0) {
|
|
28278
|
+
try {
|
|
28279
|
+
const { created, inFlight } = standing();
|
|
28280
|
+
const lines = [...created].map(([name, rawId]) => {
|
|
28281
|
+
try {
|
|
28282
|
+
const id = singleLine(String(rawId), 200);
|
|
28283
|
+
return ` ${name}: retire it with DELETE /v1/agents/${id} (needs agents:d), then erase it with DELETE /v1/agents/${id}?purge=true
|
|
28284
|
+
`;
|
|
28285
|
+
} catch {
|
|
28286
|
+
return ` ${name}: its id could not be shown; find it with \`vectros agents list --context ${run.contextId} --include-retired\`
|
|
28287
|
+
`;
|
|
28288
|
+
}
|
|
28289
|
+
});
|
|
28290
|
+
const pending = inFlight.map((name) => ` ${name}: a create was sent and not answered; find it with \`vectros agents list --context ${run.contextId} --include-retired\`
|
|
28291
|
+
`);
|
|
28292
|
+
say(
|
|
28293
|
+
`
|
|
28294
|
+
\u2716 ${signal} again: leaving before teardown finished.
|
|
28295
|
+
` + (lines.length > 0 ? ` Agents this run created, which may still be active:
|
|
28296
|
+
${lines.join("")}` : "") + (pending.length > 0 ? ` Creates that may have gone through:
|
|
28297
|
+
${pending.join("")}` : "") + (lines.length + pending.length === 0 ? " This run had not created, or begun to create, an agent.\n" : "") + ` Not listed: anything else the run wrote in '${run.contextId}' (schemas, roles, the access profile, a scoped key named '${run.keyName}').
|
|
28298
|
+
`
|
|
28299
|
+
);
|
|
28300
|
+
} catch {
|
|
28301
|
+
}
|
|
28302
|
+
forceExit(first === "SIGTERM" || signal === "SIGTERM" ? 143 : 130);
|
|
28303
|
+
return;
|
|
28304
|
+
}
|
|
28305
|
+
first = signal;
|
|
28306
|
+
say(
|
|
28307
|
+
run.keep ? `
|
|
28308
|
+
\u26A0 ${signal}: no further agents will be written; any other step running or still to come (a key mint, seed records) runs to its end. --keep was given, so nothing will be torn down. Send it again to leave now.
|
|
28309
|
+
` : toreDown ? `
|
|
28310
|
+
\u26A0 ${signal}: teardown is already running and will finish. Send it again to leave now.
|
|
28311
|
+
` : `
|
|
28312
|
+
\u26A0 ${signal}: no further agents will be written; any other step running or still to come (a key mint, seed records) runs to its end, then what this run created is torn down. Send it again to leave now.
|
|
28313
|
+
`
|
|
28314
|
+
);
|
|
28315
|
+
};
|
|
28316
|
+
const onInt = () => heard("SIGINT");
|
|
28317
|
+
const onTerm = () => heard("SIGTERM");
|
|
28318
|
+
source.on("SIGINT", onInt);
|
|
28319
|
+
source.on("SIGTERM", onTerm);
|
|
28320
|
+
return {
|
|
28321
|
+
get signal() {
|
|
28322
|
+
return first;
|
|
28323
|
+
},
|
|
28324
|
+
throwIfInterrupted() {
|
|
28325
|
+
if (first !== void 0) throw new HarnessInterrupted(first);
|
|
28326
|
+
},
|
|
28327
|
+
setAgentStanding(fn) {
|
|
28328
|
+
standing = fn;
|
|
28329
|
+
},
|
|
28330
|
+
tearingDown() {
|
|
28331
|
+
toreDown = true;
|
|
28332
|
+
},
|
|
28333
|
+
dispose() {
|
|
28334
|
+
source.off("SIGINT", onInt);
|
|
28335
|
+
source.off("SIGTERM", onTerm);
|
|
28336
|
+
}
|
|
28337
|
+
};
|
|
28338
|
+
}
|
|
26652
28339
|
async function runBlueprintHarness(client, input, deps) {
|
|
28340
|
+
const interrupt = watchForInterrupt(deps, {
|
|
28341
|
+
keep: input.keep ?? false,
|
|
28342
|
+
contextId: input.blueprint.contextId,
|
|
28343
|
+
keyName: keyNameFor(input.blueprint.name, input.machineId)
|
|
28344
|
+
});
|
|
28345
|
+
try {
|
|
28346
|
+
return await runHarness(client, input, deps, interrupt);
|
|
28347
|
+
} catch (err) {
|
|
28348
|
+
if (interrupt.signal !== void 0) {
|
|
28349
|
+
let carried = err;
|
|
28350
|
+
try {
|
|
28351
|
+
carried = Object.assign(err !== null && typeof err === "object" ? err : new Error(String(err)), { interruptedBy: interrupt.signal });
|
|
28352
|
+
} catch {
|
|
28353
|
+
const shape = err;
|
|
28354
|
+
let message = "unknown error";
|
|
28355
|
+
try {
|
|
28356
|
+
message = typeof shape?.message === "string" ? shape.message : extractServerError(err).message;
|
|
28357
|
+
} catch {
|
|
28358
|
+
}
|
|
28359
|
+
carried = Object.assign(new Error(message, { cause: err }), {
|
|
28360
|
+
interruptedBy: interrupt.signal,
|
|
28361
|
+
...shape && typeof shape === "object" && "statusCode" in shape ? { statusCode: shape.statusCode } : {},
|
|
28362
|
+
...shape && typeof shape === "object" && "body" in shape ? { body: shape.body } : {}
|
|
28363
|
+
});
|
|
28364
|
+
}
|
|
28365
|
+
throw carried;
|
|
28366
|
+
}
|
|
28367
|
+
throw err;
|
|
28368
|
+
} finally {
|
|
28369
|
+
interrupt.dispose();
|
|
28370
|
+
}
|
|
28371
|
+
}
|
|
28372
|
+
async function runHarness(client, input, deps, interrupt) {
|
|
26653
28373
|
const { tenantId, machineId, keep = false, log, remintForContext, remintBootstrapForContext } = input;
|
|
28374
|
+
const scoped = scopeAgentsToRun(input.blueprint, machineId);
|
|
28375
|
+
const runBlueprintInput = scoped.blueprint;
|
|
28376
|
+
const agentNames = Object.keys(scoped.names).length > 0 ? scoped.names : void 0;
|
|
26654
28377
|
const readExistingKey = async () => void 0;
|
|
26655
28378
|
const persistMintedKey = async () => {
|
|
26656
28379
|
};
|
|
26657
28380
|
const provisioned = await provisionContext(client, {
|
|
26658
|
-
blueprint:
|
|
28381
|
+
blueprint: runBlueprintInput,
|
|
26659
28382
|
remintBootstrapForContext,
|
|
26660
28383
|
readExistingKey,
|
|
26661
28384
|
log
|
|
26662
28385
|
});
|
|
26663
28386
|
const blueprint = await resolveApplyTimeIdentities(
|
|
26664
|
-
|
|
28387
|
+
runBlueprintInput,
|
|
26665
28388
|
provisioned,
|
|
26666
28389
|
client,
|
|
26667
28390
|
log
|
|
@@ -26704,10 +28427,19 @@ async function runBlueprintHarness(client, input, deps) {
|
|
|
26704
28427
|
existingIssuerIds: issuerSnapshot.existingIssuerIds,
|
|
26705
28428
|
failed: contextSnapshot.failed || issuerSnapshot.failed
|
|
26706
28429
|
};
|
|
28430
|
+
const createdAgentIds = /* @__PURE__ */ new Map();
|
|
28431
|
+
const seenAgentNames = /* @__PURE__ */ new Set();
|
|
28432
|
+
const attemptedAgentNames = /* @__PURE__ */ new Set();
|
|
28433
|
+
const refusedAgentNames = /* @__PURE__ */ new Set();
|
|
26707
28434
|
let result;
|
|
26708
28435
|
let assertions = [];
|
|
26709
28436
|
let applyError;
|
|
26710
|
-
|
|
28437
|
+
interrupt.setAgentStanding(() => ({
|
|
28438
|
+
created: createdAgentIds,
|
|
28439
|
+
inFlight: [...attemptedAgentNames].filter((n) => !createdAgentIds.has(n) && !seenAgentNames.has(n) && !refusedAgentNames.has(n))
|
|
28440
|
+
}));
|
|
28441
|
+
const applyAndAssert = (async () => {
|
|
28442
|
+
interrupt.throwIfInterrupted();
|
|
26711
28443
|
result = await runLoader(client, {
|
|
26712
28444
|
blueprint,
|
|
26713
28445
|
tenantId,
|
|
@@ -26718,6 +28450,13 @@ async function runBlueprintHarness(client, input, deps) {
|
|
|
26718
28450
|
provisioned,
|
|
26719
28451
|
remintForContext,
|
|
26720
28452
|
remintBootstrapForContext,
|
|
28453
|
+
onAgentCreated: ({ name, id }) => createdAgentIds.set(name, id),
|
|
28454
|
+
onAgentSeen: ({ name }) => seenAgentNames.add(name),
|
|
28455
|
+
onAgentAttempt: (name) => {
|
|
28456
|
+
if (interrupt.signal !== void 0) return false;
|
|
28457
|
+
attemptedAgentNames.add(name);
|
|
28458
|
+
},
|
|
28459
|
+
onAgentRefused: (name) => refusedAgentNames.add(name),
|
|
26721
28460
|
// CREATED-ONLY, and this is the seam that keeps it true for triggers. The loader's step 5d
|
|
26722
28461
|
// reconciles triggers by ABSENCE — it deletes rules this blueprint provisioned and no longer
|
|
26723
28462
|
// declares. That is right for a real bootstrap run, which owns its context; it is wrong here.
|
|
@@ -26735,6 +28474,7 @@ async function runBlueprintHarness(client, input, deps) {
|
|
|
26735
28474
|
// fresh, collision-free name, exactly as the harness already expects for schemas/roles/issuers.
|
|
26736
28475
|
});
|
|
26737
28476
|
log.debug({ component: "harness", contextId: result.contextId, keyName: result.keyName }, "applied");
|
|
28477
|
+
interrupt.throwIfInterrupted();
|
|
26738
28478
|
assertions = await runAssertions(
|
|
26739
28479
|
await remint(blueprint.contextId),
|
|
26740
28480
|
hasIssuers || hasNamespaces ? await remintBootstrap(blueprint.contextId) : client,
|
|
@@ -26743,10 +28483,12 @@ async function runBlueprintHarness(client, input, deps) {
|
|
|
26743
28483
|
tenantId,
|
|
26744
28484
|
result
|
|
26745
28485
|
);
|
|
26746
|
-
}
|
|
28486
|
+
})().catch((e) => {
|
|
26747
28487
|
applyError = errMsg(e);
|
|
26748
28488
|
log.warn({ component: "harness", err: applyError }, "apply phase failed");
|
|
26749
|
-
}
|
|
28489
|
+
});
|
|
28490
|
+
await applyAndAssert;
|
|
28491
|
+
interrupt.tearingDown();
|
|
26750
28492
|
let teardown = [];
|
|
26751
28493
|
if (!keep) {
|
|
26752
28494
|
let tdClient = client;
|
|
@@ -26761,9 +28503,22 @@ async function runBlueprintHarness(client, input, deps) {
|
|
|
26761
28503
|
} catch {
|
|
26762
28504
|
}
|
|
26763
28505
|
}
|
|
26764
|
-
teardown = await runTeardown(
|
|
28506
|
+
teardown = await runTeardown(
|
|
28507
|
+
tdClient,
|
|
28508
|
+
tdBootstrapClient,
|
|
28509
|
+
blueprint,
|
|
28510
|
+
machineId,
|
|
28511
|
+
snapshot,
|
|
28512
|
+
result,
|
|
28513
|
+
new Set(Object.values(scoped.names)),
|
|
28514
|
+
createdAgentIds,
|
|
28515
|
+
seenAgentNames,
|
|
28516
|
+
attemptedAgentNames,
|
|
28517
|
+
refusedAgentNames
|
|
28518
|
+
);
|
|
26765
28519
|
}
|
|
26766
|
-
const
|
|
28520
|
+
const shownAgentNames = agentNames && interrupt.signal ? onlyAttempted(agentNames, attemptedAgentNames) : agentNames;
|
|
28521
|
+
const passed = applyError === void 0 && assertions.length > 0 && assertions.every((a) => a.ok) && !teardown.some(isAgentRemovalFailure) && interrupt.signal === void 0;
|
|
26767
28522
|
return {
|
|
26768
28523
|
passed,
|
|
26769
28524
|
applied: result ? {
|
|
@@ -26777,7 +28532,9 @@ async function runBlueprintHarness(client, input, deps) {
|
|
|
26777
28532
|
assertions,
|
|
26778
28533
|
teardown,
|
|
26779
28534
|
kept: keep,
|
|
26780
|
-
error: applyError
|
|
28535
|
+
error: applyError,
|
|
28536
|
+
...shownAgentNames ? { agentNames: shownAgentNames } : {},
|
|
28537
|
+
...interrupt.signal ? { interrupted: interrupt.signal } : {}
|
|
26781
28538
|
};
|
|
26782
28539
|
}
|
|
26783
28540
|
|
|
@@ -26791,6 +28548,47 @@ async function resolveTestBlueprint(fileOrName) {
|
|
|
26791
28548
|
}
|
|
26792
28549
|
return validateBlueprint(raw);
|
|
26793
28550
|
}
|
|
28551
|
+
function noticeIoForFd(fd, maxWaitMs = 2e3) {
|
|
28552
|
+
return {
|
|
28553
|
+
write: (bytes, offset) => writeSync(fd, bytes, offset),
|
|
28554
|
+
sleep: (ms) => void Atomics.wait(new Int32Array(new SharedArrayBuffer(4)), 0, 0, ms),
|
|
28555
|
+
maxWaitMs
|
|
28556
|
+
};
|
|
28557
|
+
}
|
|
28558
|
+
var stderrIo = noticeIoForFd(2);
|
|
28559
|
+
function writeNotice(text, io = stderrIo) {
|
|
28560
|
+
const bytes = Buffer.from(text);
|
|
28561
|
+
const giveUp = () => {
|
|
28562
|
+
try {
|
|
28563
|
+
io.write(Buffer.from("\n [notice truncated]\n"), 0);
|
|
28564
|
+
} catch {
|
|
28565
|
+
}
|
|
28566
|
+
};
|
|
28567
|
+
let done = 0;
|
|
28568
|
+
let waited = 0;
|
|
28569
|
+
while (done < bytes.length) {
|
|
28570
|
+
try {
|
|
28571
|
+
const wrote = io.write(bytes, done);
|
|
28572
|
+
if (wrote > 0) {
|
|
28573
|
+
done += wrote;
|
|
28574
|
+
continue;
|
|
28575
|
+
}
|
|
28576
|
+
if (waited >= io.maxWaitMs) return giveUp();
|
|
28577
|
+
io.sleep(10);
|
|
28578
|
+
waited += 10;
|
|
28579
|
+
} catch (err) {
|
|
28580
|
+
if (err.code !== "EAGAIN") return;
|
|
28581
|
+
if (waited >= io.maxWaitMs) return giveUp();
|
|
28582
|
+
io.sleep(10);
|
|
28583
|
+
waited += 10;
|
|
28584
|
+
}
|
|
28585
|
+
}
|
|
28586
|
+
}
|
|
28587
|
+
function harnessExitCode(report) {
|
|
28588
|
+
if (report.interrupted === "SIGINT") return 130;
|
|
28589
|
+
if (report.interrupted === "SIGTERM") return 143;
|
|
28590
|
+
return report.passed ? 0 : 1;
|
|
28591
|
+
}
|
|
26794
28592
|
function renderReport(blueprint, env, report) {
|
|
26795
28593
|
const lines = [];
|
|
26796
28594
|
lines.push("");
|
|
@@ -26801,12 +28599,16 @@ function renderReport(blueprint, env, report) {
|
|
|
26801
28599
|
` \u2714 applied \u2014 key ${a.keyName} (${a.keyAction}), ${Object.keys(a.schemaIds).length} schema(s)` + (a.seededCount ? `, ${a.seededCount} seed(s)` : "")
|
|
26802
28600
|
);
|
|
26803
28601
|
} else {
|
|
26804
|
-
lines.push(` \u2716 apply failed \u2014 ${report.error ?? "unknown error"}`);
|
|
28602
|
+
lines.push(` \u2716 apply failed \u2014 ${singleLine(report.error ?? "unknown error", 600)}`);
|
|
28603
|
+
}
|
|
28604
|
+
if (report.agentNames && Object.keys(report.agentNames).length > 0) {
|
|
28605
|
+
lines.push(" Agents ran under names of their own, so a repeat run never meets this run's agents:");
|
|
28606
|
+
for (const [declared, ran] of Object.entries(report.agentNames)) lines.push(` ${declared} \u2192 ${ran}`);
|
|
26805
28607
|
}
|
|
26806
28608
|
if (report.assertions.length) {
|
|
26807
28609
|
lines.push("");
|
|
26808
28610
|
lines.push(" Assertions:");
|
|
26809
|
-
for (const a of report.assertions) lines.push(` ${a.ok ? "\u2714" : "\u2716"} ${a.name}: ${a.detail}`);
|
|
28611
|
+
for (const a of report.assertions) lines.push(` ${a.ok ? "\u2714" : "\u2716"} ${singleLine(a.name, 200)}: ${singleLine(a.detail, 600)}`);
|
|
26810
28612
|
}
|
|
26811
28613
|
if (report.kept) {
|
|
26812
28614
|
lines.push("");
|
|
@@ -26816,11 +28618,19 @@ function renderReport(blueprint, env, report) {
|
|
|
26816
28618
|
lines.push(" Teardown:");
|
|
26817
28619
|
for (const t of report.teardown) {
|
|
26818
28620
|
const mark = !t.ok ? "\u2716" : t.skipped ? "\u21B7" : "\u2714";
|
|
26819
|
-
lines.push(` ${mark} ${t.step}${t.error ? ` \u2014 ${t.error}` : ""}`);
|
|
28621
|
+
lines.push(` ${mark} ${singleLine(t.step, 300)}${t.error ? ` \u2014 ${singleLine(t.error, 600)}` : ""}`);
|
|
26820
28622
|
}
|
|
26821
28623
|
const tdFail = report.teardown.filter((t) => !t.ok);
|
|
26822
28624
|
if (tdFail.length) lines.push(` \u26A0 ${tdFail.length} teardown step(s) failed \u2014 manual cleanup may be needed.`);
|
|
26823
28625
|
}
|
|
28626
|
+
if (report.interrupted) {
|
|
28627
|
+
lines.push(
|
|
28628
|
+
report.kept ? ` \u2716 Stopped by ${report.interrupted}: no further agents were written, and teardown was skipped (--keep), so what this run created is still in place. The run does not pass.` : ` \u2716 Stopped by ${report.interrupted}: no further agents were written${report.assertions.length === 0 ? " and the assertions were skipped" : ""}, and what this run created was torn down as after any failure. The run does not pass.`
|
|
28629
|
+
);
|
|
28630
|
+
}
|
|
28631
|
+
if (report.teardown.some(isAgentRemovalFailure)) {
|
|
28632
|
+
lines.push(" \u2716 An agent could not be retired or purged, so this run fails: it is still active or still holds its content.");
|
|
28633
|
+
}
|
|
26824
28634
|
lines.push("");
|
|
26825
28635
|
lines.push(report.passed ? " RESULT: PASS" : " RESULT: FAIL");
|
|
26826
28636
|
lines.push("");
|
|
@@ -26849,6 +28659,13 @@ async function runBlueprintTest(fileOrName, opts, deps = {}) {
|
|
|
26849
28659
|
`);
|
|
26850
28660
|
return 1;
|
|
26851
28661
|
}
|
|
28662
|
+
const agentNamesIssue = agentNamesProblem(blueprint);
|
|
28663
|
+
if (agentNamesIssue) {
|
|
28664
|
+
process.stderr.write(` \u2716 ${agentNamesIssue}
|
|
28665
|
+
Nothing was changed.
|
|
28666
|
+
`);
|
|
28667
|
+
return 2;
|
|
28668
|
+
}
|
|
26852
28669
|
return withAuth({ ...opts, provisioning: true }, deps, async (auth, log) => {
|
|
26853
28670
|
const machineId = `harness-${Date.now()}-${randomUUID2().slice(0, 8)}`;
|
|
26854
28671
|
try {
|
|
@@ -26890,10 +28707,10 @@ async function runBlueprintTest(fileOrName, opts, deps = {}) {
|
|
|
26890
28707
|
environment: auth.baseUrl
|
|
26891
28708
|
});
|
|
26892
28709
|
};
|
|
26893
|
-
const report = await runBlueprintHarness(
|
|
28710
|
+
const report = await (deps.runHarness ?? runBlueprintHarness)(
|
|
26894
28711
|
client,
|
|
26895
28712
|
{ blueprint, tenantId, machineId, keep: opts.keep, log, remintForContext, remintBootstrapForContext },
|
|
26896
|
-
{ pingWithKey }
|
|
28713
|
+
{ pingWithKey, notify: deps.notify ?? ((text) => writeNotice(text)) }
|
|
26897
28714
|
);
|
|
26898
28715
|
if (opts.json) {
|
|
26899
28716
|
const envelope = {
|
|
@@ -26907,10 +28724,16 @@ async function runBlueprintTest(fileOrName, opts, deps = {}) {
|
|
|
26907
28724
|
process.stdout.write(`${renderReport(blueprint, auth.env, report)}
|
|
26908
28725
|
`);
|
|
26909
28726
|
}
|
|
26910
|
-
return report
|
|
28727
|
+
return harnessExitCode(report);
|
|
26911
28728
|
} catch (err) {
|
|
26912
|
-
process.stderr.write(formatCliError(err, { action: "blueprint-test failed" }));
|
|
26913
|
-
|
|
28729
|
+
process.stderr.write(formatCliError(sanitizedAgentError(err), { action: "blueprint-test failed" }));
|
|
28730
|
+
const by = err?.interruptedBy;
|
|
28731
|
+
if (by && opts.json) {
|
|
28732
|
+
const failed = { passed: false, assertions: [], teardown: [], kept: Boolean(opts.keep), error: singleLine(sanitizedAgentError(err).message, 600), interrupted: by };
|
|
28733
|
+
process.stdout.write(`${formatJson({ blueprint: { name: blueprint.name, version: blueprint.version, contextId: blueprint.contextId }, env: auth.env, ...failed })}
|
|
28734
|
+
`);
|
|
28735
|
+
}
|
|
28736
|
+
return by === "SIGINT" ? 130 : by === "SIGTERM" ? 143 : 1;
|
|
26914
28737
|
}
|
|
26915
28738
|
});
|
|
26916
28739
|
}
|
|
@@ -27094,9 +28917,9 @@ function emitJoined(opts) {
|
|
|
27094
28917
|
|
|
27095
28918
|
// src/build-info.ts
|
|
27096
28919
|
var BUILD_INFO = {
|
|
27097
|
-
cli: true ? "0.
|
|
27098
|
-
sdk: true ? "0.
|
|
27099
|
-
blueprints: true ? "0.
|
|
28920
|
+
cli: true ? "0.27.0" : "dev",
|
|
28921
|
+
sdk: true ? "0.49.0" : "dev",
|
|
28922
|
+
blueprints: true ? "0.21.0" : "dev"
|
|
27100
28923
|
};
|
|
27101
28924
|
function formatBuildInfo() {
|
|
27102
28925
|
return `vectros ${BUILD_INFO.cli} (sdk ${BUILD_INFO.sdk}, blueprints ${BUILD_INFO.blueprints})`;
|
|
@@ -27133,6 +28956,17 @@ function parseTenantFlag(raw) {
|
|
|
27133
28956
|
}
|
|
27134
28957
|
return raw;
|
|
27135
28958
|
}
|
|
28959
|
+
var POSITIVE_INT_INVALID = /* @__PURE__ */ Symbol("parsePositiveInt:invalid");
|
|
28960
|
+
function parsePositiveInt(raw, flag) {
|
|
28961
|
+
if (raw === void 0) return void 0;
|
|
28962
|
+
if (!/^[1-9][0-9]{0,8}$/.test(raw)) {
|
|
28963
|
+
process.stderr.write(` \u2716 ${flag} must be a positive whole number (got '${raw}')
|
|
28964
|
+
`);
|
|
28965
|
+
process.exitCode = 2;
|
|
28966
|
+
return POSITIVE_INT_INVALID;
|
|
28967
|
+
}
|
|
28968
|
+
return Number(raw);
|
|
28969
|
+
}
|
|
27136
28970
|
function toCommonOptions(flags) {
|
|
27137
28971
|
return {
|
|
27138
28972
|
env: flags.env,
|
|
@@ -27187,6 +29021,7 @@ function buildProgram(deps = {}) {
|
|
|
27187
29021
|
registerDomainsCommands(program, deps);
|
|
27188
29022
|
registerNamespaceCommands(program, deps);
|
|
27189
29023
|
registerScriptsCommands(program, deps);
|
|
29024
|
+
registerAgentsCommands(program, deps);
|
|
27190
29025
|
registerAssumeCommand(program, deps);
|
|
27191
29026
|
registerJoinCommand(program, deps);
|
|
27192
29027
|
registerKeyringCommands(program);
|
|
@@ -27642,6 +29477,57 @@ function registerScriptsCommands(program, deps) {
|
|
|
27642
29477
|
}
|
|
27643
29478
|
);
|
|
27644
29479
|
}
|
|
29480
|
+
function registerAgentsCommands(program, deps) {
|
|
29481
|
+
const agents = program.command("agents").description("Manage stored agents (named, versioned model + prompt + tool whitelist definitions).");
|
|
29482
|
+
const list = agents.command("list").description("List the agents in a context, each at its newest version.").requiredOption("--context <c>", "app context to read").option("--include-retired", "also list retired agents").option("--tenant <tenant>", "target tenant: 'test' or 'live' (default live)");
|
|
29483
|
+
addCommonAuthOptions(list).action(
|
|
29484
|
+
async (flags) => {
|
|
29485
|
+
const tenant = parseTenantFlag(flags.tenant);
|
|
29486
|
+
if (tenant === TENANT_FLAG_INVALID) return;
|
|
29487
|
+
process.exitCode = await runAgentsList(
|
|
29488
|
+
{ ...toCommonOptions(flags), context: flags.context, includeRetired: Boolean(flags.includeRetired), tenant },
|
|
29489
|
+
deps
|
|
29490
|
+
);
|
|
29491
|
+
}
|
|
29492
|
+
);
|
|
29493
|
+
const get = agents.command("get").description("Show one agent at its newest version, or at an earlier one with --at-version.").argument("<id>", "the agent's id (see `agents list`)").requiredOption("--context <c>", "app context to read").option("--at-version <n>", "show an earlier version number (the newest when omitted)").option("--tenant <tenant>", "target tenant: 'test' or 'live' (default live)");
|
|
29494
|
+
addCommonAuthOptions(get).action(
|
|
29495
|
+
async (id, flags) => {
|
|
29496
|
+
const tenant = parseTenantFlag(flags.tenant);
|
|
29497
|
+
if (tenant === TENANT_FLAG_INVALID) return;
|
|
29498
|
+
const version2 = parsePositiveInt(flags.atVersion, "--at-version");
|
|
29499
|
+
if (version2 === POSITIVE_INT_INVALID) return;
|
|
29500
|
+
process.exitCode = await runAgentsGet(
|
|
29501
|
+
{ ...toCommonOptions(flags), id, context: flags.context, version: version2, tenant },
|
|
29502
|
+
deps
|
|
29503
|
+
);
|
|
29504
|
+
}
|
|
29505
|
+
);
|
|
29506
|
+
const create = agents.command("create").description("Store an agent at version 1 from a definition file (an existing name is returned unchanged).").argument("<name>", "agent name: 1-64 chars of letters, digits, '_' and '-'").requiredOption("--context <c>", "app context to create it in").requiredOption("--file <path>", "JSON or YAML file holding the definition (model, system, tools, limits, budget)").option("--tenant <tenant>", "target tenant: 'test' or 'live' (default live)");
|
|
29507
|
+
addCommonAuthOptions(create).action(
|
|
29508
|
+
async (name, flags) => {
|
|
29509
|
+
const tenant = parseTenantFlag(flags.tenant);
|
|
29510
|
+
if (tenant === TENANT_FLAG_INVALID) return;
|
|
29511
|
+
process.exitCode = await runAgentsCreate(
|
|
29512
|
+
{ ...toCommonOptions(flags), name, context: flags.context, file: flags.file, tenant },
|
|
29513
|
+
deps
|
|
29514
|
+
);
|
|
29515
|
+
}
|
|
29516
|
+
);
|
|
29517
|
+
const version = agents.command("version").description("Write the next version of an agent from a definition file (a full replacement: omitted fields take their defaults).").argument("<id>", "the agent's id (see `agents list`)").requiredOption("--context <c>", "app context the agent is in").requiredOption("--file <path>", "JSON or YAML file holding the new definition").option("--expected-version <n>", "refuse the write if the agent has a newer version than this one").option("--restore", "bring a retired agent back, with this definition as its next version").option("--tenant <tenant>", "target tenant: 'test' or 'live' (default live)");
|
|
29518
|
+
addCommonAuthOptions(version).action(
|
|
29519
|
+
async (id, flags) => {
|
|
29520
|
+
const tenant = parseTenantFlag(flags.tenant);
|
|
29521
|
+
if (tenant === TENANT_FLAG_INVALID) return;
|
|
29522
|
+
const expectedVersion = parsePositiveInt(flags.expectedVersion, "--expected-version");
|
|
29523
|
+
if (expectedVersion === POSITIVE_INT_INVALID) return;
|
|
29524
|
+
process.exitCode = await runAgentsVersion(
|
|
29525
|
+
{ ...toCommonOptions(flags), id, context: flags.context, file: flags.file, expectedVersion, restore: Boolean(flags.restore), tenant },
|
|
29526
|
+
deps
|
|
29527
|
+
);
|
|
29528
|
+
}
|
|
29529
|
+
);
|
|
29530
|
+
}
|
|
27645
29531
|
function registerRoleCommands(program, deps) {
|
|
27646
29532
|
const role = program.command("role").description("Manage context-scoped roles (reusable scope rules; bind via `access grant --role`).");
|
|
27647
29533
|
const create = role.command("create").description("Create (idempotent by roleId) a single-clause role from --actions.").requiredOption("--context <c>", "app context").requiredOption("--role-id <id>", "role id (3-31 chars; lowercase letter + lowercase/digit/dash)").requiredOption("--name <name>", "human-readable name").requiredOption("--actions <csv>", "allowed actions, comma-separated (e.g. records:cru,search:r)").option(
|
|
@@ -27967,10 +29853,10 @@ async function main() {
|
|
|
27967
29853
|
await buildProgram().parseAsync(process.argv);
|
|
27968
29854
|
}
|
|
27969
29855
|
main().catch((err) => {
|
|
27970
|
-
process.stderr.write(`fatal: ${
|
|
29856
|
+
process.stderr.write(`fatal: ${errorText(err)}
|
|
27971
29857
|
`);
|
|
27972
29858
|
if (process.env.VECTROS_MCP_DEBUG && err instanceof Error && err.stack) {
|
|
27973
|
-
process.stderr.write(`${err.stack}
|
|
29859
|
+
process.stderr.write(`${cleanBlock(err.stack)}
|
|
27974
29860
|
`);
|
|
27975
29861
|
}
|
|
27976
29862
|
process.exitCode = 2;
|