@aiwg/cli 2026.10.7 → 2026.10.9

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.
Files changed (33) hide show
  1. package/dist/src/artifacts/cli.js +9 -5
  2. package/dist/src/auth/config.js +3 -8
  3. package/dist/src/auth/resource-credentials.js +35 -2
  4. package/dist/src/cli/handlers/cost-report.js +7 -1
  5. package/dist/src/cli/handlers/decision.js +33 -8
  6. package/dist/src/cli/handlers/diagnose.js +13 -0
  7. package/dist/src/cli/handlers/local-executor.js +33 -6
  8. package/dist/src/cli/handlers/resource-versions.js +8 -5
  9. package/dist/src/cli/handlers/script-runner.js +13 -1
  10. package/dist/src/cli/handlers/use.js +18 -3
  11. package/dist/src/cost/fleet-report.js +20 -22
  12. package/dist/src/decision/driver.js +2 -2
  13. package/dist/src/decision/jev-credentials.js +18 -5
  14. package/dist/src/decision/ledger.js +230 -34
  15. package/dist/src/mcp/cli.mjs +105 -19
  16. package/dist/src/mcp/credentials.d.mts +4 -0
  17. package/dist/src/mcp/credentials.mjs +44 -3
  18. package/dist/src/mcp/grok-build-config.mjs +2 -0
  19. package/dist/src/mcp/omp-config.mjs +2 -0
  20. package/dist/src/mcp/registry.js +19 -15
  21. package/dist/src/mcp/registry.mjs +2 -18
  22. package/dist/src/mcp/tools/agentic-sandbox.mjs +46 -28
  23. package/dist/src/models/grok-build-models.js +20 -0
  24. package/dist/src/providers/provider-definitions.js +2 -2
  25. package/dist/src/resources/web-release.js +76 -65
  26. package/dist/src/serve/shared-host-scheduler.js +4 -1
  27. package/dist/src/storage/backends/fortemi.js +112 -50
  28. package/dist/src/storage/backends/logseq.js +16 -3
  29. package/dist/src/storage/backends/postgres.js +74 -33
  30. package/dist/src/storage/backends/postgrest.js +37 -13
  31. package/dist/src/storage/config.js +6 -1
  32. package/dist/src/uhp/config.js +4 -0
  33. package/package.json +1 -1
@@ -1,3 +1,5 @@
1
+ import { resolveSensitiveInput } from '../../auth/sensitive-input.js';
2
+ import { credentialReferenceInput } from '../../mcp/credentials.mjs';
1
3
  import { STORAGE_BACKEND_CONTRACT } from '../backend-contract.js';
2
4
  import { postgresBatchId, postgresPayloadDigest } from './postgres.js';
3
5
  const DEFAULT_TIMEOUT_MS = 15_000;
@@ -18,6 +20,7 @@ export class PostgrestBackendError extends Error {
18
20
  /** PostgreSQL accessMode=postgrest; not a separate storage engine. */
19
21
  export class PostgrestStorageBackend {
20
22
  options;
23
+ sensitiveInput;
21
24
  descriptor = {
22
25
  contract: STORAGE_BACKEND_CONTRACT,
23
26
  backend: 'postgres-postgrest',
@@ -42,8 +45,9 @@ export class PostgrestStorageBackend {
42
45
  maxResponseBytes;
43
46
  maxBatchSize;
44
47
  maxPageSize;
45
- constructor(options) {
48
+ constructor(options, sensitiveInput = {}) {
46
49
  this.options = options;
50
+ this.sensitiveInput = sensitiveInput;
47
51
  this.baseUrl = normalizeBaseUrl(options.baseUrl);
48
52
  this.fetchImpl = options.fetch ?? globalThis.fetch;
49
53
  if (!this.fetchImpl)
@@ -185,7 +189,7 @@ export class PostgrestStorageBackend {
185
189
  headers['Content-Type'] = options.contentType ?? 'application/json';
186
190
  if (options.prefer)
187
191
  headers.Prefer = options.prefer;
188
- const authorization = this.authorization();
192
+ const authorization = await this.authorization();
189
193
  if (authorization)
190
194
  headers.Authorization = authorization;
191
195
  let response;
@@ -195,8 +199,8 @@ export class PostgrestStorageBackend {
195
199
  signal: AbortSignal.timeout(this.timeoutMs),
196
200
  });
197
201
  }
198
- catch (error) {
199
- throw new PostgrestBackendError('AIWG_POSTGREST_TRANSPORT_FAILED', error instanceof Error ? error.message : String(error), true);
202
+ catch {
203
+ throw new PostgrestBackendError('AIWG_POSTGREST_TRANSPORT_FAILED', 'PostgREST transport failed; check connectivity and authorization.', true);
200
204
  }
201
205
  if (response.status === 204)
202
206
  return undefined;
@@ -204,15 +208,23 @@ export class PostgrestStorageBackend {
204
208
  if (declaredLength > this.maxResponseBytes) {
205
209
  throw new PostgrestBackendError('AIWG_POSTGREST_RESPONSE_TOO_LARGE', `response exceeds ${this.maxResponseBytes} byte ceiling`);
206
210
  }
207
- const responseBody = await response.text();
211
+ let responseBody;
212
+ try {
213
+ responseBody = await response.text();
214
+ }
215
+ catch {
216
+ throw new PostgrestBackendError('AIWG_POSTGREST_TRANSPORT_FAILED', 'PostgREST response could not be read.', true);
217
+ }
208
218
  if (Buffer.byteLength(responseBody) > this.maxResponseBytes) {
209
219
  throw new PostgrestBackendError('AIWG_POSTGREST_RESPONSE_TOO_LARGE', `response exceeds ${this.maxResponseBytes} byte ceiling`);
210
220
  }
211
221
  if (!response.ok) {
212
- const databaseError = parseDatabaseError(responseBody);
222
+ const secrets = authorization
223
+ ? [authorization, authorization.trim(), authorization.trim().replace(/^\S+\s+/, '')].filter(Boolean) : [];
224
+ const databaseError = parseDatabaseError(responseBody, secrets);
213
225
  const retryable = response.status === 408 || response.status === 425 || response.status === 429 ||
214
226
  response.status >= 500 || isRetryableDatabaseCode(databaseError.databaseCode);
215
- const exposedCode = databaseError.message?.startsWith('AIWG_')
227
+ const exposedCode = /^AIWG_[A-Z0-9_]+$/.test(databaseError.message ?? '')
216
228
  ? databaseError.message
217
229
  : retryable ? 'AIWG_POSTGREST_RETRYABLE' : 'AIWG_POSTGREST_REQUEST_FAILED';
218
230
  throw new PostgrestBackendError(exposedCode, `PostgREST request failed with HTTP ${response.status}${databaseError.databaseCode ? ` (database ${databaseError.databaseCode})` : ''}`, retryable, parseRetryAfter(response.headers.get('retry-after')));
@@ -224,12 +236,19 @@ export class PostgrestStorageBackend {
224
236
  throw new PostgrestBackendError('AIWG_POSTGREST_RESPONSE_INVALID', 'PostgREST returned invalid JSON');
225
237
  }
226
238
  }
227
- authorization() {
239
+ async authorization() {
228
240
  if (!this.options.authorizationEnv)
229
241
  return undefined;
230
- const value = process.env[this.options.authorizationEnv];
242
+ const input = credentialReferenceInput(this.options.authorizationEnv, 'PostgREST authorizationEnv');
243
+ let value;
244
+ try {
245
+ value = input.env ? (this.sensitiveInput.environment ?? process.env)[input.env] : await resolveSensitiveInput(input, this.sensitiveInput);
246
+ }
247
+ catch (error) {
248
+ throw new PostgrestBackendError('AIWG_POSTGREST_CREDENTIAL_UNAVAILABLE', error.message);
249
+ }
231
250
  if (!value)
232
- throw new PostgrestBackendError('AIWG_POSTGREST_CREDENTIAL_UNAVAILABLE', `authorization locator ${this.options.authorizationEnv} is not set`);
251
+ throw new PostgrestBackendError('AIWG_POSTGREST_CREDENTIAL_UNAVAILABLE', 'PostgREST authorizationEnv is not set; use ENV_NAME, file:ABSOLUTE_PATH or store:NAME.');
233
252
  return value;
234
253
  }
235
254
  assertMutationIdentity(mutation) {
@@ -294,12 +313,17 @@ function parseCsv(input) {
294
313
  }
295
314
  return { header, rows: output };
296
315
  }
297
- function parseDatabaseError(body) {
316
+ function parseDatabaseError(body, secrets) {
298
317
  try {
299
318
  const parsed = JSON.parse(body);
319
+ // Decode JSON escapes before redaction: encoded echoes must not become codes.
320
+ const redact = (value) => typeof value === 'string'
321
+ ? secrets.reduce((text, secret) => text.split(secret).join('[REDACTED]'), value) : undefined;
322
+ const databaseCode = redact(parsed.code);
323
+ const message = redact(parsed.message);
300
324
  return {
301
- ...(typeof parsed.code === 'string' ? { databaseCode: parsed.code } : {}),
302
- ...(typeof parsed.message === 'string' ? { message: parsed.message } : {}),
325
+ ...(databaseCode && /^[A-Z0-9]{5}$/.test(databaseCode) ? { databaseCode } : {}),
326
+ ...(message ? { message } : {}),
303
327
  };
304
328
  }
305
329
  catch {
@@ -14,6 +14,7 @@
14
14
  * @issue #953
15
15
  */
16
16
  import { existsSync } from 'fs';
17
+ import { credentialReferenceInput } from '../mcp/credentials.mjs';
17
18
  import { readFile } from 'fs/promises';
18
19
  import { resolve, isAbsolute, join } from 'path';
19
20
  import { homedir } from 'os';
@@ -171,8 +172,12 @@ function validateBackendConfig(raw, source) {
171
172
  requireString(obj, 'vault', source);
172
173
  return obj;
173
174
  case 'logseq':
174
- rejectUnknownKeys(obj, ['type', 'graph', 'apiUrl', 'useApi'], source, 'backend');
175
+ rejectUnknownKeys(obj, ['type', 'graph', 'apiUrl', 'apiTokenEnv', 'useApi'], source, 'backend');
175
176
  requireString(obj, 'graph', source);
177
+ if (obj['apiTokenEnv'] !== undefined) {
178
+ requireString(obj, 'apiTokenEnv', source);
179
+ credentialReferenceInput(obj['apiTokenEnv'], 'Logseq apiTokenEnv');
180
+ }
176
181
  return obj;
177
182
  case 'notion': {
178
183
  rejectUnknownKeys(obj, ['type', 'parent', 'externalIdProperty'], source, 'backend');
@@ -100,6 +100,10 @@ export function validateUhpConfig(config) {
100
100
  if (!profile.credential || profile.credential.source !== 'env' || !/^[A-Z_][A-Z0-9_]*$/.test(profile.credential.name ?? '')) {
101
101
  errors.push(`${where}.credential: must be an env secret reference with an uppercase variable name`);
102
102
  }
103
+ if (profile.credential && typeof profile.credential === 'object'
104
+ && Object.keys(profile.credential).some(key => key !== 'source' && key !== 'name')) {
105
+ errors.push(`${where}.credential: must contain only source and name; inline credentials are forbidden`);
106
+ }
103
107
  if (profile.defaultHarness !== undefined && !OBJECT_ID.test(profile.defaultHarness))
104
108
  errors.push(`${where}.defaultHarness: malformed UHP harness id`);
105
109
  if (profile.defaultModel !== undefined && !MODEL_ID.test(profile.defaultModel))
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@aiwg/cli",
3
- "version": "2026.10.7",
3
+ "version": "2026.10.9",
4
4
  "description": "Lightweight AIWG CLI for signed, versioned web-backed resources.",
5
5
  "type": "module",
6
6
  "license": "MIT",