@outerlayer/cli 0.2.0 → 0.4.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.
Files changed (141) hide show
  1. package/CHANGELOG.md +124 -0
  2. package/README.md +226 -59
  3. package/dist/agent-setup-IFH3FB5X.js +197 -0
  4. package/dist/build-TZUUTRGH.js +9 -0
  5. package/dist/build-info.json +1 -1
  6. package/dist/check-WQP2CZDW.js +94 -0
  7. package/dist/chunk-2E54Z3P5.js +232 -0
  8. package/dist/chunk-3TT7RQYB.js +55 -0
  9. package/dist/{chunk-U32VSRLO.js → chunk-3YPKXE5L.js} +90 -632
  10. package/dist/{chunk-Q6CL5THG.js → chunk-5DLBAXY5.js} +208 -61
  11. package/dist/{chunk-R5KBGVII.js → chunk-5QRP3MVS.js} +30 -14
  12. package/dist/chunk-5Y3QQRUH.js +300 -0
  13. package/dist/chunk-6Y63SXFO.js +383 -0
  14. package/dist/chunk-774HEQL6.js +178 -0
  15. package/dist/chunk-7A6RVXPZ.js +2233 -0
  16. package/dist/chunk-7YIOOFVJ.js +71 -0
  17. package/dist/chunk-ABIWDUMS.js +104 -0
  18. package/dist/chunk-AUFG23AA.js +534 -0
  19. package/dist/chunk-B2G7JHHB.js +42 -0
  20. package/dist/chunk-BCJNHMZT.js +34 -0
  21. package/dist/chunk-BCLJSQCV.js +56 -0
  22. package/dist/chunk-BFESLKPP.js +61 -0
  23. package/dist/chunk-BJ3KTMDH.js +63 -0
  24. package/dist/chunk-BKO6JEZI.js +47 -0
  25. package/dist/chunk-BOLTI6LR.js +37 -0
  26. package/dist/chunk-CBPPJSAR.js +89 -0
  27. package/dist/chunk-CBZB6XY6.js +102 -0
  28. package/dist/{chunk-65WXAANR.js → chunk-DVDEBNQJ.js} +17 -2
  29. package/dist/{work-pr-cmd-AZQPWH4H.js → chunk-DVYHCMB2.js} +9 -26
  30. package/dist/{chunk-TFUIDMOB.js → chunk-EABW6AJQ.js} +12 -1
  31. package/dist/{chunk-KFYJV2ZG.js → chunk-EMY4I27X.js} +1 -1
  32. package/dist/{chunk-NTTPJV35.js → chunk-F47JBIAW.js} +3 -1
  33. package/dist/chunk-F6GFZXZ3.js +60 -0
  34. package/dist/{chunk-YOOSBOKS.js → chunk-F7CU5ABH.js} +1 -1
  35. package/dist/chunk-FAJSS2WN.js +1357 -0
  36. package/dist/chunk-FS2VYGZT.js +1764 -0
  37. package/dist/chunk-FV2CCGXK.js +9 -0
  38. package/dist/{chunk-4TNZMO7V.js → chunk-G7FSV7JX.js} +3452 -751
  39. package/dist/{emit-cmd-TWSYYEZI.js → chunk-H5NCNVDA.js} +68 -17
  40. package/dist/chunk-HE2D4EY5.js +83 -0
  41. package/dist/chunk-L2DBRKRA.js +94 -0
  42. package/dist/chunk-LF6MLJCY.js +38 -0
  43. package/dist/chunk-M5POMKKW.js +1051 -0
  44. package/dist/{mcp-install-cmd-FDQH6SEN.js → chunk-MQ3IPHIZ.js} +35 -18
  45. package/dist/{chunk-D77LS3UI.js → chunk-N5FOE5PS.js} +33 -4
  46. package/dist/chunk-N6LURUEF.js +64 -0
  47. package/dist/chunk-NYC54VBD.js +1084 -0
  48. package/dist/chunk-OGFGZQA3.js +23 -0
  49. package/dist/chunk-QOZKPLVJ.js +226 -0
  50. package/dist/chunk-RA3O54FC.js +30 -0
  51. package/dist/chunk-TBT347UY.js +41 -0
  52. package/dist/{chunk-NXLLURA4.js → chunk-UFFXNXLQ.js} +115 -71
  53. package/dist/chunk-USO2DKBD.js +421 -0
  54. package/dist/chunk-W4SQCOZU.js +206 -0
  55. package/dist/{chunk-A3WLZX2F.js → chunk-WIOZAJ2W.js} +15 -2
  56. package/dist/chunk-WO2BXCTQ.js +83 -0
  57. package/dist/{chunk-I3ETLSNE.js → chunk-XCCLFVXM.js} +120 -11
  58. package/dist/{context-materialize-6WKT3RBQ.js → chunk-XDDW4FRS.js} +134 -26
  59. package/dist/chunk-YYYXRJUW.js +101 -0
  60. package/dist/chunk-ZM2IMMYN.js +76 -0
  61. package/dist/chunk-ZMYPLFG3.js +71 -0
  62. package/dist/chunk-ZNA27WEV.js +470 -0
  63. package/dist/{cli-G7DILYJY.js → cli-NHUXABYH.js} +590 -876
  64. package/dist/{paths-D2VGWWFI.js → cli-build-K56DK4DS.js} +1 -1
  65. package/dist/config-XVJYZ4IQ.js +9 -0
  66. package/dist/connect-cmd-OOG4TPU7.js +16 -0
  67. package/dist/context-adopt-UMA4O5NY.js +105 -0
  68. package/dist/context-materialize-G2FUFC72.js +19 -0
  69. package/dist/dist-RW4UPPC2.js +6 -0
  70. package/dist/docker-NEGDLU6D.js +7 -0
  71. package/dist/doctor-53F2PYY7.js +43 -0
  72. package/dist/doctor-UFVL4PZY.js +426 -0
  73. package/dist/{emit-artifact-cmd-UQVT3OKW.js → emit-artifact-cmd-KTYW2TN2.js} +24 -20
  74. package/dist/emit-cmd-DCERAU27.js +9 -0
  75. package/dist/emit-criteria-cmd-VAUUSAYD.js +166 -0
  76. package/dist/{emit-finding-cmd-FDOU2OPD.js → emit-finding-cmd-UR4ID45Z.js} +34 -31
  77. package/dist/{emit-result-cmd-H2T4C2K3.js → emit-result-cmd-FDTUKGKE.js} +34 -62
  78. package/dist/exec-client-4XGXV3XN.js +8 -0
  79. package/dist/guest-init-GENRHNP7.js +8 -0
  80. package/dist/hook-fast-EYENPK6F.js +9 -0
  81. package/dist/{hook-wrap-fast-KXYNX3AD.js → hook-wrap-fast-NPLHJXFK.js} +2 -2
  82. package/dist/host-key-KDYZ5ECC.js +7 -0
  83. package/dist/import-capture-cmd-AVZ4VIVJ.js +68 -0
  84. package/dist/{import-ruler-cmd-7LH2QOMG.js → import-ruler-cmd-GUHL6IY5.js} +1 -1
  85. package/dist/index.js +4 -4
  86. package/dist/init-DXBCOAJK.js +36 -0
  87. package/dist/init-YEXK35CF.js +221 -0
  88. package/dist/init-cmd-NHR7PRSQ.js +135 -0
  89. package/dist/install-cmd-7WXOIFDO.js +55 -0
  90. package/dist/lima-XN65D7GN.js +55 -0
  91. package/dist/login-browser-7XGSV7MA.js +10 -0
  92. package/dist/{config-POF7DEQW.js → logout-cmd-QAUFDJ6J.js} +3 -1
  93. package/dist/{logs-TRNPQM42.js → logs-7RYUH5A6.js} +1 -1
  94. package/dist/loop-VZOLL4WQ.js +43 -0
  95. package/dist/machine-WSG52J75.js +35 -0
  96. package/dist/mcp-install-cmd-RLK4SO3N.js +9 -0
  97. package/dist/{mcp-serve-cmd-57EZZOTL.js → mcp-serve-cmd-JPTP5FMS.js} +20 -9
  98. package/dist/paths-OYKMVYJP.js +6 -0
  99. package/dist/{pidfile-PTW76F56.js → pidfile-UZRH774M.js} +2 -3
  100. package/dist/real-deps-SU24ZA2K.js +21 -0
  101. package/dist/relay-L76HDX72.js +46 -0
  102. package/dist/settings-J2652U5N.js +7 -0
  103. package/dist/starter-pack-LIZYMKYQ.js +10 -0
  104. package/dist/{status-I27IST4L.js → status-2UZKKNB7.js} +30 -13
  105. package/dist/{statusline-fast-3C5OXHDD.js → statusline-fast-SVTMBMD7.js} +3 -2
  106. package/dist/sync-cmd-VPPYCNNM.js +28 -0
  107. package/dist/version-BWM6VLDI.js +6 -0
  108. package/dist/{watch-3DTPJETH.js → watch-5C4ZBBGO.js} +19 -8
  109. package/dist/{work-claim-cmd-5AUNCLS2.js → work-claim-cmd-LSO2B2EX.js} +33 -20
  110. package/dist/work-cmd-UOQO2AD4.js +17 -0
  111. package/dist/work-comment-cmd-WF3EOLAE.js +106 -0
  112. package/dist/{work-launch-LT663PB3.js → work-launch-YI4CJDAP.js} +1 -1
  113. package/dist/work-open-pr-cmd-4SSQZMYG.js +156 -0
  114. package/dist/work-pr-cmd-6AN6RRQJ.js +16 -0
  115. package/package.json +13 -3
  116. package/skill-pack/maintained/amend/SKILL.md +104 -0
  117. package/skill-pack/maintained/emitting-evidence/SKILL.md +99 -0
  118. package/skill-pack/maintained/emitting-evidence/references/agents-snippet.md +20 -0
  119. package/skill-pack/maintained/outerlayer/SKILL.md +55 -0
  120. package/skill-pack/maintained/reporting-findings/SKILL.md +148 -0
  121. package/skill-pack/template/build/SKILL.md +193 -0
  122. package/skill-pack/template/build/references/agent-briefs.md +243 -0
  123. package/skill-pack/template/build/references/criteria-judge.md +91 -0
  124. package/skill-pack/template/build/references/evidence.md +42 -0
  125. package/skill-pack/template/build/references/release.md +74 -0
  126. package/skill-pack/template/build/references/review-briefs.md +275 -0
  127. package/skill-pack/template/build/references/review-loop.md +158 -0
  128. package/skill-pack/template/build/scripts/record-criteria.mjs +235 -0
  129. package/skill-pack/template/spec/SKILL.md +84 -0
  130. package/skill-pack/template/writing-specs/SKILL.md +134 -0
  131. package/dist/chunk-DCNOXRMV.js +0 -589
  132. package/dist/chunk-JJP7YLMN.js +0 -25
  133. package/dist/chunk-OZ7C3XUE.js +0 -34
  134. package/dist/chunk-WQ6VGRGZ.js +0 -150
  135. package/dist/hook-fast-J5LCDHUJ.js +0 -8
  136. package/dist/import-capture-cmd-EUMBGIV3.js +0 -176
  137. package/dist/init-PTBITAUO.js +0 -103
  138. package/dist/login-cmd-IRX6LZT7.js +0 -62
  139. package/dist/loop-ASZRX3CZ.js +0 -648
  140. package/dist/sync-cmd-BBAUZ5JD.js +0 -17
  141. package/dist/work-cmd-2P4BVX47.js +0 -18
@@ -1,16 +1,27 @@
1
1
  #!/usr/bin/env node
2
2
  import { createRequire } from 'node:module';
3
- import { staleBuildRefusal, staleBuildWarning, readEffectiveArtifactSpoolState, localArtifactIds, isArtifactTombstoned, detectActiveSession, launchRefusalForSession, artifactBlobsDir, appendArtifactRecord, artifactsSpoolPath, findPendingArtifactRecord, detectCi, appendArtifactTombstone } from './chunk-U32VSRLO.js';
4
- import { ARTIFACT_MAX_CAPTION_LENGTH, ArtifactCriterionIdSchema, ARTIFACT_MAX_FILENAME_LENGTH, mediaTypeForArtifactPath, inferArtifactKind, resolveRepoIdentity, ARTIFACT_MAX_REPLACES_TARGETS } from './chunk-4TNZMO7V.js';
3
+ import { staleBuildRefusal, staleBuildWarning } from './chunk-3YPKXE5L.js';
4
+ import { detectCi } from './chunk-HE2D4EY5.js';
5
+ import { launchRefusalForSession } from './chunk-BCJNHMZT.js';
6
+ import './chunk-TBT347UY.js';
7
+ import './chunk-F7CU5ABH.js';
8
+ import './chunk-BKO6JEZI.js';
9
+ import { detectActiveSession } from './chunk-BFESLKPP.js';
10
+ import { readEffectiveArtifactSpoolState, localArtifactIds, isArtifactTombstoned, artifactBlobsDir, appendArtifactRecord, artifactsSpoolPath, findPendingArtifactRecord, appendArtifactTombstone } from './chunk-6Y63SXFO.js';
11
+ import { ARTIFACT_MAX_CAPTION_LENGTH, ArtifactCriterionIdSchema, ARTIFACT_MAX_FILENAME_LENGTH, mediaTypeForArtifactPath, inferArtifactKind, resolveRepoIdentity, ARTIFACT_MAX_REPLACES_TARGETS } from './chunk-G7FSV7JX.js';
5
12
  import './chunk-YRYMNBGQ.js';
6
- import './chunk-65WXAANR.js';
13
+ import './chunk-DVDEBNQJ.js';
7
14
  import { parseTrackerNumber, trackerNumberError } from './chunk-WZBHBH3J.js';
15
+ import './chunk-5QRP3MVS.js';
8
16
  import './chunk-ZJSNLVCE.js';
9
- import './chunk-NXLLURA4.js';
10
- import './chunk-I3ETLSNE.js';
11
- import './chunk-YOOSBOKS.js';
12
- import './chunk-R5KBGVII.js';
13
- import { readCloudConfig } from './chunk-TFUIDMOB.js';
17
+ import { defaultFetch } from './chunk-BCLJSQCV.js';
18
+ import { resolveGatewayCredentials, unauthorizedMessage } from './chunk-BOLTI6LR.js';
19
+ import './chunk-EABW6AJQ.js';
20
+ import './chunk-M5POMKKW.js';
21
+ import './chunk-ZM2IMMYN.js';
22
+ import './chunk-UFFXNXLQ.js';
23
+ import './chunk-XCCLFVXM.js';
24
+ import './chunk-BJ3KTMDH.js';
14
25
  import { homeFromEnv } from './chunk-VNVZDWO3.js';
15
26
  import './chunk-Z5GEFMRW.js';
16
27
  import { createHash, randomUUID } from 'crypto';
@@ -251,16 +262,9 @@ async function runEmitArtifact(opts) {
251
262
  "nothing to attach this to \u2014 no recorded session, no PR, and no git repo. Run inside a recorded Claude Code session, from a git checkout, in CI with PR context, or pass --pr <number>."
252
263
  );
253
264
  }
254
- const fileConfig = readCloudConfig(home);
255
- const url = opts.url ?? env.OUTERLAYER_URL ?? fileConfig.url;
256
- const apiKey = opts.apiKey ?? env.OUTERLAYER_API_KEY ?? fileConfig.apiKey;
257
- const appId = opts.appId ?? env.OUTERLAYER_APP_ID ?? fileConfig.appId;
258
- if (!url || !apiKey || !appId) {
259
- const missing = [!url && "--url", !apiKey && "--api-key", !appId && "--app-id"].filter(Boolean).join(", ");
260
- throw new EmitArtifactError(
261
- `missing ${missing}. Run \`outerlayer login --url <your gateway>\` and paste the key at the prompt, or set OUTERLAYER_URL / OUTERLAYER_API_KEY / OUTERLAYER_APP_ID. Mint a key in the dashboard: Settings \u2192 API keys.`
262
- );
263
- }
265
+ const resolved = resolveGatewayCredentials({ ...opts, home, env });
266
+ if (!resolved.ok) throw new EmitArtifactError(resolved.message);
267
+ const { url, credential: apiKey, appId } = resolved;
264
268
  const clientArtifactId = randomUUID();
265
269
  const singleTarget = replacesTargets !== void 0 && replacesTargets.length === 1 ? replacesTargets[0] : void 0;
266
270
  const targetPendingLocally = singleTarget !== void 0 && findPendingArtifactRecord(singleTarget, home) !== void 0;
@@ -287,7 +291,7 @@ async function runEmitArtifact(opts) {
287
291
  },
288
292
  blob: { data: bytes.toString("base64") }
289
293
  };
290
- const fetchImpl = opts.fetchImpl ?? fetch;
294
+ const fetchImpl = opts.fetchImpl ?? defaultFetch;
291
295
  const endpoint = new URL("/v1/artifacts", url).toString();
292
296
  let response;
293
297
  try {
@@ -305,7 +309,7 @@ async function runEmitArtifact(opts) {
305
309
  }
306
310
  if (response.status === 401) {
307
311
  throw new EmitArtifactError(
308
- "not authorized \u2014 the API key is unknown, expired, or bound to a different app. Mint a fresh key: dashboard \u2192 Settings \u2192 API keys."
312
+ unauthorizedMessage(apiKey)
309
313
  );
310
314
  }
311
315
  if (!response.ok) {
@@ -0,0 +1,9 @@
1
+ #!/usr/bin/env node
2
+ import { createRequire } from 'node:module';
3
+ export { EmitError, runEmit } from './chunk-H5NCNVDA.js';
4
+ import './chunk-WO2BXCTQ.js';
5
+ import './chunk-EMY4I27X.js';
6
+ import './chunk-RA3O54FC.js';
7
+ import './chunk-Z5GEFMRW.js';
8
+
9
+ createRequire(import.meta.url);
@@ -0,0 +1,166 @@
1
+ #!/usr/bin/env node
2
+ import { createRequire } from 'node:module';
3
+ import { launchRefusalForSession } from './chunk-BCJNHMZT.js';
4
+ import { detectActiveSession } from './chunk-BFESLKPP.js';
5
+ import { resolveRepoIdentity, CriteriaListSchema } from './chunk-G7FSV7JX.js';
6
+ import { launchedItemForSession } from './chunk-DVDEBNQJ.js';
7
+ import { parseTrackerNumber, trackerNumberError } from './chunk-WZBHBH3J.js';
8
+ import { defaultFetch } from './chunk-BCLJSQCV.js';
9
+ import { resolveGatewayCredentials, unauthorizedMessage } from './chunk-BOLTI6LR.js';
10
+ import './chunk-EABW6AJQ.js';
11
+ import './chunk-M5POMKKW.js';
12
+ import './chunk-ZM2IMMYN.js';
13
+ import './chunk-BJ3KTMDH.js';
14
+ import { homeFromEnv } from './chunk-VNVZDWO3.js';
15
+ import './chunk-Z5GEFMRW.js';
16
+ import { readFileSync } from 'fs';
17
+
18
+ createRequire(import.meta.url);
19
+ var GREEN = "\x1B[32m";
20
+ var DIM = "\x1B[2m";
21
+ var RESET = "\x1B[0m";
22
+ var EmitCriteriaError = class extends Error {
23
+ };
24
+ var REPLACE_REFUSED_MESSAGE = "this item already has a recorded list, and a person must replace it. Run the command yourself, outside the session, with your own login. A repository's .outerlayer/policy.yaml can let sessions replace it with `criteria: { replace: anyone }`.";
25
+ async function readAllStdin() {
26
+ const chunks = [];
27
+ for await (const chunk of process.stdin) chunks.push(Buffer.from(chunk));
28
+ return Buffer.concat(chunks).toString("utf8");
29
+ }
30
+ function valueAt(raw, path) {
31
+ let current = raw;
32
+ for (const key of path) current = current?.[key];
33
+ return current;
34
+ }
35
+ function describeIssue(raw, issue) {
36
+ const where = issue.path.join(".");
37
+ if (issue.code === "custom") return `${where}: ${issue.message}`;
38
+ const value = valueAt(raw, issue.path);
39
+ const leaf = issue.path[issue.path.length - 1];
40
+ if ((leaf === "id" || leaf === "proof") && value !== void 0) {
41
+ return `${where}: ${JSON.stringify(value)} is not accepted (${issue.message})`;
42
+ }
43
+ return `${where}: ${issue.message}`;
44
+ }
45
+ function parseFile(raw) {
46
+ const parsed = CriteriaListSchema.safeParse(raw);
47
+ if (parsed.success) return parsed.data;
48
+ const issue = parsed.error.issues[0];
49
+ throw new EmitCriteriaError(
50
+ `invalid criteria list \u2014 ${issue ? describeIssue(raw, issue) : "malformed"}`
51
+ );
52
+ }
53
+ function resolveTarget(opts) {
54
+ let item;
55
+ if (opts.item !== void 0) {
56
+ const number = parseTrackerNumber(opts.item);
57
+ if (number === void 0) throw new EmitCriteriaError(trackerNumberError("--item", opts.item));
58
+ item = { number };
59
+ }
60
+ const { sessionId } = opts;
61
+ if (sessionId !== void 0) {
62
+ const refusal = launchRefusalForSession(sessionId, opts.home);
63
+ if (refusal !== void 0) throw new EmitCriteriaError(refusal);
64
+ }
65
+ if (item !== void 0) return sessionId !== void 0 ? { item, sessionId } : { item };
66
+ if (sessionId === void 0) {
67
+ throw new EmitCriteriaError(
68
+ "nothing to record this on \u2014 no work item. Pass --item <number>, or run inside a recorded session launched with OUTERLAYER_WORK \u2014 its own launch record names the item automatically."
69
+ );
70
+ }
71
+ const launched = launchedItemForSession(sessionId, opts.home);
72
+ if (launched === void 0) {
73
+ throw new EmitCriteriaError(
74
+ "nothing to record this on \u2014 no work item. Pass --item <number>; this recorded session's own launch record names no item either."
75
+ );
76
+ }
77
+ return { item: { number: launched }, sessionId };
78
+ }
79
+ async function errorCode(response) {
80
+ let text = "";
81
+ try {
82
+ text = await response.text();
83
+ } catch {
84
+ }
85
+ let code = null;
86
+ try {
87
+ const parsed = JSON.parse(text);
88
+ if (typeof parsed.error?.code === "string") code = parsed.error.code;
89
+ } catch {
90
+ }
91
+ return { code, detail: text.replace(/\s+/g, " ").trim().slice(0, 160) };
92
+ }
93
+ async function runEmitCriteria(opts) {
94
+ const cwd = opts.cwd ?? process.cwd();
95
+ const home = opts.home ?? homeFromEnv();
96
+ const env = opts.env ?? process.env;
97
+ const now = opts.now ?? (() => /* @__PURE__ */ new Date());
98
+ let text;
99
+ if (opts.file === "-") {
100
+ text = await (opts.readStdin ?? readAllStdin)();
101
+ } else {
102
+ try {
103
+ text = readFileSync(opts.file, "utf8");
104
+ } catch (err) {
105
+ throw new EmitCriteriaError(`cannot read "${opts.file}": ${String(err)}`);
106
+ }
107
+ }
108
+ let raw;
109
+ try {
110
+ raw = JSON.parse(text);
111
+ } catch (err) {
112
+ throw new EmitCriteriaError(`"${opts.file}" is not valid JSON: ${String(err)}`);
113
+ }
114
+ const list = parseFile(raw);
115
+ const identity = resolveRepoIdentity(cwd);
116
+ const sessionId = detectActiveSession({
117
+ env,
118
+ home,
119
+ cwds: identity.repoRoot !== void 0 ? [identity.repoRoot, cwd] : [cwd],
120
+ nowMs: now().getTime()
121
+ });
122
+ const target = resolveTarget({ item: opts.item, sessionId, home });
123
+ const resolved = resolveGatewayCredentials({ url: opts.url, apiKey: opts.apiKey, appId: opts.appId, cwd, home, env });
124
+ if (!resolved.ok) throw new EmitCriteriaError(resolved.message);
125
+ const { url, credential: apiKey, appId } = resolved;
126
+ const request = {
127
+ ...list,
128
+ item: target.item,
129
+ ...target.sessionId !== void 0 ? { sessionId: target.sessionId } : {}
130
+ };
131
+ const endpoint = new URL("/v1/criteria", url).toString();
132
+ const fetchImpl = opts.fetchImpl ?? defaultFetch;
133
+ let response;
134
+ try {
135
+ response = await fetchImpl(endpoint, {
136
+ method: "POST",
137
+ headers: {
138
+ "content-type": "application/json",
139
+ authorization: `Bearer ${apiKey}`,
140
+ "x-outerlayer-app-id": appId
141
+ },
142
+ body: JSON.stringify(request)
143
+ });
144
+ } catch (err) {
145
+ throw new EmitCriteriaError(`network error reaching ${endpoint}: ${String(err)}`);
146
+ }
147
+ if (response.status === 401) throw new EmitCriteriaError(unauthorizedMessage(apiKey));
148
+ if (!response.ok) {
149
+ const { code, detail } = await errorCode(response);
150
+ if (response.status === 409 && code === "criteria_replace_refused") {
151
+ throw new EmitCriteriaError(`refused (409): ${REPLACE_REFUSED_MESSAGE}`);
152
+ }
153
+ throw new EmitCriteriaError(`emit failed (${response.status})${detail ? `: ${detail}` : ""}`);
154
+ }
155
+ let data = {};
156
+ try {
157
+ data = (await response.json()).data ?? {};
158
+ } catch {
159
+ }
160
+ const count = typeof data.count === "number" ? data.count : list.criteria.length;
161
+ const output = opts.json ? JSON.stringify(data) : `${GREEN}\u2713${RESET} recorded ${count} ${count === 1 ? "criterion" : "criteria"} ${DIM}\xB7${RESET} item #${target.item.number}`;
162
+ if (!opts.quiet) process.stdout.write(output + "\n");
163
+ return { data, output, exitCode: 0 };
164
+ }
165
+
166
+ export { EmitCriteriaError, runEmitCriteria };
@@ -1,16 +1,17 @@
1
1
  #!/usr/bin/env node
2
2
  import { createRequire } from 'node:module';
3
- import { detectActiveSession, launchRefusalForSession } from './chunk-U32VSRLO.js';
4
- import { FindingBatchSchema, resolveRepoIdentity } from './chunk-4TNZMO7V.js';
5
- import './chunk-YRYMNBGQ.js';
6
- import { launchedItemForSession } from './chunk-65WXAANR.js';
3
+ import { launchRefusalForSession } from './chunk-BCJNHMZT.js';
4
+ import { detectActiveSession } from './chunk-BFESLKPP.js';
5
+ import { FindingBatchSchema, resolveRepoIdentity } from './chunk-G7FSV7JX.js';
6
+ import { launchedItemForSession } from './chunk-DVDEBNQJ.js';
7
7
  import { parseTrackerNumber, trackerNumberError } from './chunk-WZBHBH3J.js';
8
8
  import { readLatestProvenanceForSession } from './chunk-ZJSNLVCE.js';
9
- import './chunk-NXLLURA4.js';
10
- import './chunk-I3ETLSNE.js';
11
- import './chunk-YOOSBOKS.js';
12
- import './chunk-R5KBGVII.js';
13
- import { readCloudConfig } from './chunk-TFUIDMOB.js';
9
+ import { defaultFetch } from './chunk-BCLJSQCV.js';
10
+ import { resolveGatewayCredentials, unauthorizedMessage } from './chunk-BOLTI6LR.js';
11
+ import './chunk-EABW6AJQ.js';
12
+ import './chunk-M5POMKKW.js';
13
+ import './chunk-ZM2IMMYN.js';
14
+ import './chunk-BJ3KTMDH.js';
14
15
  import { homeFromEnv } from './chunk-VNVZDWO3.js';
15
16
  import './chunk-Z5GEFMRW.js';
16
17
  import { readFileSync } from 'fs';
@@ -21,6 +22,14 @@ var DIM = "\x1B[2m";
21
22
  var RESET = "\x1B[0m";
22
23
  var EmitFindingError = class extends Error {
23
24
  };
25
+ var REMOVED_FINDING_FLAGS = ["subject", "kind", "severity", "verdict", "fixed", "source"];
26
+ function refuseRemovedFindingFlags(opts) {
27
+ const used = REMOVED_FINDING_FLAGS.find((flag) => opts[flag] !== void 0);
28
+ if (used === void 0) return;
29
+ throw new EmitFindingError(
30
+ `--${used} is no longer accepted: findings record problems in the factory, not review verdicts. Pass --category <context|flaky-test|tooling|environment|defect|other> instead.`
31
+ );
32
+ }
24
33
  function parseItemFlag(raw) {
25
34
  if (raw === void 0) return void 0;
26
35
  const number = parseTrackerNumber(raw);
@@ -50,23 +59,19 @@ function recordFromFlags(opts) {
50
59
  const ruleLine = parseIntFlag(opts.ruleLine, "--rule-line", { min: 1 });
51
60
  const turnIndex = parseIntFlag(opts.turn, "--turn", { min: 0 });
52
61
  const round = parseIntFlag(opts.round, "--round", { min: 0 });
62
+ const hasRule = opts.rulePath !== void 0 || opts.ruleQuote !== void 0 || opts.ruleRelation !== void 0 || ruleLine !== void 0;
53
63
  return {
54
64
  id: opts.id,
55
- subject: opts.subject,
65
+ category: opts.category,
56
66
  title: opts.title,
57
67
  file: opts.file,
58
68
  line: line ?? null,
59
- severity: opts.severity ?? null,
60
- kind: opts.kind,
61
- verdict: opts.verdict,
62
- fixed: opts.fixed ?? false,
63
- rule: opts.rulePath !== void 0 ? {
69
+ rule: hasRule ? {
64
70
  path: opts.rulePath,
65
71
  line: ruleLine ?? null,
66
- quote: opts.ruleQuote ?? "",
72
+ ...opts.ruleQuote !== void 0 ? { quote: opts.ruleQuote } : {},
67
73
  relation: opts.ruleRelation
68
74
  } : null,
69
- source: opts.source,
70
75
  where: {
71
76
  label: opts.where,
72
77
  ...opts.traceId !== void 0 ? { traceId: opts.traceId } : {},
@@ -112,22 +117,15 @@ function resolveUploadTarget(opts) {
112
117
  return { item: { number: launchedNumber }, sessionId };
113
118
  }
114
119
  async function upload(batch, target, opts) {
115
- const fileConfig = readCloudConfig(opts.home);
116
- const url = opts.url ?? opts.env.OUTERLAYER_URL ?? fileConfig.url;
117
- const apiKey = opts.apiKey ?? opts.env.OUTERLAYER_API_KEY ?? fileConfig.apiKey;
118
- const appId = opts.appId ?? opts.env.OUTERLAYER_APP_ID ?? fileConfig.appId;
119
- if (!url || !apiKey || !appId) {
120
- const missing = [!url && "--url", !apiKey && "--api-key", !appId && "--app-id"].filter(Boolean).join(", ");
121
- throw new EmitFindingError(
122
- `missing ${missing}. Run \`outerlayer login --url <your gateway>\` and paste the key at the prompt, or set OUTERLAYER_URL / OUTERLAYER_API_KEY / OUTERLAYER_APP_ID. Mint a key in the dashboard: Settings \u2192 API keys.`
123
- );
124
- }
120
+ const resolved = resolveGatewayCredentials(opts);
121
+ if (!resolved.ok) throw new EmitFindingError(resolved.message);
122
+ const { url, credential: apiKey, appId } = resolved;
125
123
  const request = {
126
124
  ...batch,
127
125
  item: target.item,
128
126
  ...target.sessionId !== void 0 ? { sessionId: target.sessionId } : {}
129
127
  };
130
- const fetchImpl = opts.fetchImpl ?? fetch;
128
+ const fetchImpl = opts.fetchImpl ?? defaultFetch;
131
129
  const endpoint = new URL("/v1/findings", url).toString();
132
130
  let response;
133
131
  try {
@@ -145,7 +143,7 @@ async function upload(batch, target, opts) {
145
143
  }
146
144
  if (response.status === 401) {
147
145
  throw new EmitFindingError(
148
- "not authorized \u2014 the API key is unknown, expired, or bound to a different app. Mint a fresh key: dashboard \u2192 Settings \u2192 API keys."
146
+ unauthorizedMessage(apiKey)
149
147
  );
150
148
  }
151
149
  if (!response.ok) {
@@ -174,7 +172,7 @@ async function runEmitFinding(opts) {
174
172
  const sessionId = detectUploadSessionId({ cwd, home, env, now });
175
173
  const contextSourceSha = opts.contextSourceSha ?? (sessionId !== void 0 ? readLatestProvenanceForSession(sessionId, home)?.sha ?? void 0 : void 0);
176
174
  const rawBatch = {
177
- schemaVersion: 1,
175
+ schemaVersion: 2,
178
176
  ...opts.headSha !== void 0 ? { headSha: opts.headSha } : {},
179
177
  ...contextSourceSha !== void 0 ? { contextSourceSha } : {},
180
178
  findings: [recordFromFlags(opts)]
@@ -216,6 +214,11 @@ async function runEmitFindings(opts) {
216
214
  } catch (err) {
217
215
  throw new EmitFindingError(`"${opts.file}" is not valid JSON: ${String(err)}`);
218
216
  }
217
+ if (typeof raw === "object" && raw !== null && raw.schemaVersion === 1) {
218
+ throw new EmitFindingError(
219
+ `"${opts.file}" uses schemaVersion 1, which is no longer accepted. Use schemaVersion 2: each finding takes a category (context, flaky-test, tooling, environment, defect or other) in place of subject, kind, severity, verdict, fixed and source.`
220
+ );
221
+ }
219
222
  const parsed = FindingBatchSchema.safeParse(raw);
220
223
  if (!parsed.success) {
221
224
  throw new EmitFindingError(`invalid finding batch \u2014 ${firstIssueMessage(parsed)}`);
@@ -234,4 +237,4 @@ async function runEmitFindings(opts) {
234
237
  });
235
238
  }
236
239
 
237
- export { EmitFindingError, runEmitFinding, runEmitFindings };
240
+ export { EmitFindingError, refuseRemovedFindingFlags, runEmitFinding, runEmitFindings };
@@ -1,16 +1,27 @@
1
1
  #!/usr/bin/env node
2
2
  import { createRequire } from 'node:module';
3
- import { detectActiveSession, launchRefusalForSession, detectCi } from './chunk-U32VSRLO.js';
4
- import { EmittedNameSchema, ARTIFACT_REVIEW_CHECK_NAME, EMITTED_RESULT_MAX_LINK_LENGTH, EMITTED_RESULTS, EMITTED_RESULT_MAX_BODY_LENGTH, resolveRepoIdentity } from './chunk-4TNZMO7V.js';
3
+ import './chunk-3YPKXE5L.js';
4
+ import { detectCi } from './chunk-HE2D4EY5.js';
5
+ import { launchRefusalForSession } from './chunk-BCJNHMZT.js';
6
+ import './chunk-TBT347UY.js';
7
+ import './chunk-F7CU5ABH.js';
8
+ import './chunk-BKO6JEZI.js';
9
+ import { detectActiveSession } from './chunk-BFESLKPP.js';
10
+ import './chunk-6Y63SXFO.js';
11
+ import { EmittedNameSchema, EMITTED_RESULT_MAX_LINK_LENGTH, EMITTED_RESULTS, EMITTED_RESULT_MAX_BODY_LENGTH, resolveRepoIdentity } from './chunk-G7FSV7JX.js';
5
12
  import './chunk-YRYMNBGQ.js';
6
- import './chunk-65WXAANR.js';
13
+ import './chunk-DVDEBNQJ.js';
7
14
  import { parseTrackerNumber, trackerNumberError } from './chunk-WZBHBH3J.js';
15
+ import './chunk-5QRP3MVS.js';
8
16
  import './chunk-ZJSNLVCE.js';
9
- import './chunk-NXLLURA4.js';
10
- import './chunk-I3ETLSNE.js';
11
- import './chunk-YOOSBOKS.js';
12
- import './chunk-R5KBGVII.js';
13
- import { readCloudConfig } from './chunk-TFUIDMOB.js';
17
+ import { defaultFetch } from './chunk-BCLJSQCV.js';
18
+ import { resolveGatewayCredentials, unauthorizedMessage } from './chunk-BOLTI6LR.js';
19
+ import './chunk-EABW6AJQ.js';
20
+ import './chunk-M5POMKKW.js';
21
+ import './chunk-ZM2IMMYN.js';
22
+ import './chunk-UFFXNXLQ.js';
23
+ import './chunk-XCCLFVXM.js';
24
+ import './chunk-BJ3KTMDH.js';
14
25
  import { homeFromEnv } from './chunk-VNVZDWO3.js';
15
26
  import './chunk-Z5GEFMRW.js';
16
27
  import { randomUUID } from 'crypto';
@@ -53,23 +64,7 @@ async function runEmitResult(opts) {
53
64
  `invalid name "${opts.name}" \u2014 an emit name is 1-64 characters: a lowercase letter followed by lowercase letters, digits, ".", "_" or "-" (e.g. smoke.pass)`
54
65
  );
55
66
  }
56
- const isArtifactReview = opts.name === ARTIFACT_REVIEW_CHECK_NAME;
57
- const artifactId = opts.artifact === "" ? void 0 : opts.artifact;
58
- if (isArtifactReview) {
59
- if (artifactId === void 0) {
60
- throw new EmitResultError(
61
- `--artifact <id> is required for ${ARTIFACT_REVIEW_CHECK_NAME} \u2014 it names which artifact this verdict is on`
62
- );
63
- }
64
- } else if (artifactId !== void 0) {
65
- throw new EmitResultError(`--artifact is only accepted with ${ARTIFACT_REVIEW_CHECK_NAME}`);
66
- }
67
67
  const link = opts.link === "" ? void 0 : opts.link;
68
- if (isArtifactReview && link !== void 0) {
69
- throw new EmitResultError(
70
- `--link is refused for ${ARTIFACT_REVIEW_CHECK_NAME} \u2014 an artifact verdict is a judgment on evidence already on file, never a run to point at; pass --body / --body-file instead`
71
- );
72
- }
73
68
  if (link !== void 0) {
74
69
  if (!link.startsWith("http://") && !link.startsWith("https://")) {
75
70
  throw new EmitResultError(`invalid --link "${link}" \u2014 expected an http:// or https:// URL`);
@@ -100,8 +95,6 @@ async function runEmitResult(opts) {
100
95
  body = opts.body;
101
96
  } else if (opts.bodyFile !== void 0) {
102
97
  body = await readBodyFile(opts.bodyFile, opts.readStdin);
103
- } else if (isArtifactReview && result === "fail") {
104
- body = await (opts.readStdin ?? readAllStdin)();
105
98
  }
106
99
  if (body !== void 0) {
107
100
  body = body.trim();
@@ -116,13 +109,7 @@ async function runEmitResult(opts) {
116
109
  );
117
110
  }
118
111
  }
119
- if (isArtifactReview) {
120
- if (result === "fail" && body === void 0) {
121
- throw new EmitResultError(
122
- `a failing artifact verdict needs a sentence \u2014 pass --body or --body-file with one sentence saying what is wrong`
123
- );
124
- }
125
- } else if (result === "fail" && link === void 0 && body === void 0) {
112
+ if (result === "fail" && link === void 0 && body === void 0) {
126
113
  throw new EmitResultError(
127
114
  `a failing check needs something to act on \u2014 pass --link with the run URL, or --body / --body-file with one sentence saying what is wrong`
128
115
  );
@@ -135,37 +122,23 @@ async function runEmitResult(opts) {
135
122
  cwds: identity.repoRoot !== void 0 ? [identity.repoRoot, cwd] : [cwd],
136
123
  nowMs: now().getTime()
137
124
  });
138
- if (isArtifactReview && sessionId !== void 0) {
125
+ if (item === void 0 && sessionId === void 0) {
139
126
  throw new EmitResultError(
140
- "an artifact verdict is a person's act \u2014 record it with a person's API key, outside a recorded session."
127
+ "nothing to attach this to \u2014 no work item. Pass --item <number>; a recorded session alone does not name one."
141
128
  );
142
129
  }
143
- if (!isArtifactReview) {
144
- if (item === void 0 && sessionId === void 0) {
145
- throw new EmitResultError(
146
- "nothing to attach this to \u2014 no work item. Pass --item <number>; a recorded session alone does not name one, and a session with no --item is always refused as self-judgment."
147
- );
148
- }
149
- if (sessionId !== void 0) {
150
- const refusal = launchRefusalForSession(sessionId, home);
151
- if (refusal !== void 0) throw new EmitResultError(refusal);
152
- }
153
- if (item === void 0 && sessionId !== void 0) {
154
- throw new EmitResultError(
155
- "a session records a check only on a work item it was not launched for, named with --item \u2014 the item this session was launched for needs a machine key (an API key run outside the recorded session) or CI."
156
- );
157
- }
130
+ if (sessionId !== void 0) {
131
+ const refusal = launchRefusalForSession(sessionId, home);
132
+ if (refusal !== void 0) throw new EmitResultError(refusal);
158
133
  }
159
- const fileConfig = readCloudConfig(home);
160
- const url = opts.url ?? env.OUTERLAYER_URL ?? fileConfig.url;
161
- const apiKey = opts.apiKey ?? env.OUTERLAYER_API_KEY ?? fileConfig.apiKey;
162
- const appId = opts.appId ?? env.OUTERLAYER_APP_ID ?? fileConfig.appId;
163
- if (!url || !apiKey || !appId) {
164
- const missing = [!url && "--url", !apiKey && "--api-key", !appId && "--app-id"].filter(Boolean).join(", ");
134
+ if (item === void 0) {
165
135
  throw new EmitResultError(
166
- `missing ${missing}. Run \`outerlayer login --url <your gateway>\` and paste the key at the prompt, or set OUTERLAYER_URL / OUTERLAYER_API_KEY / OUTERLAYER_APP_ID. Mint a key in the dashboard: Settings \u2192 API keys.`
136
+ "nothing to attach this to \u2014 no work item. Pass --item <number>; a recorded session alone does not name one."
167
137
  );
168
138
  }
139
+ const resolved = resolveGatewayCredentials({ ...opts, home, env });
140
+ if (!resolved.ok) throw new EmitResultError(resolved.message);
141
+ const { url, credential: apiKey, appId } = resolved;
169
142
  const ci = detectCi(env);
170
143
  const request = {
171
144
  schemaVersion: 1,
@@ -178,11 +151,10 @@ async function runEmitResult(opts) {
178
151
  emittedAt: now().toISOString(),
179
152
  ...ci ? { ci: true } : {},
180
153
  ...sessionId !== void 0 ? { sessionId } : {},
181
- ...item !== void 0 ? { item } : {},
182
- ...isArtifactReview ? { artifactId } : {}
154
+ ...item !== void 0 ? { item } : {}
183
155
  }
184
156
  };
185
- const fetchImpl = opts.fetchImpl ?? fetch;
157
+ const fetchImpl = opts.fetchImpl ?? defaultFetch;
186
158
  const endpoint = new URL("/v1/emitted-results", url).toString();
187
159
  let response;
188
160
  try {
@@ -200,7 +172,7 @@ async function runEmitResult(opts) {
200
172
  }
201
173
  if (response.status === 401) {
202
174
  throw new EmitResultError(
203
- "not authorized \u2014 the API key is unknown, expired, or bound to a different app. Mint a fresh key: dashboard \u2192 Settings \u2192 API keys."
175
+ unauthorizedMessage(apiKey)
204
176
  );
205
177
  }
206
178
  if (!response.ok) {
@@ -216,7 +188,7 @@ async function runEmitResult(opts) {
216
188
  data = (await response.json()).data ?? {};
217
189
  } catch {
218
190
  }
219
- const anchorLabel = isArtifactReview ? `artifact ${artifactId}` : `item #${item.number}`;
191
+ const anchorLabel = `item #${item.number}`;
220
192
  const recordedId = typeof data.id === "string" ? data.id : "";
221
193
  const idSuffix = recordedId ? ` ${DIM}\xB7${RESET} ${recordedId}` : "";
222
194
  const output = opts.json ? JSON.stringify(data) : `${GREEN}\u2713${RESET} emitted ${opts.name} (${result}) ${DIM}\xB7${RESET} ${anchorLabel}${idSuffix}`;
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env node
2
+ import { createRequire } from 'node:module';
3
+ export { CAPTURE_LIMIT_BYTES, ExecConnectionError, HANDSHAKE_TIMEOUT_MS, execCapture, runExec, runVmExec, vmExecArgs, waitForAgent } from './chunk-W4SQCOZU.js';
4
+ import './chunk-B2G7JHHB.js';
5
+ import './chunk-AUFG23AA.js';
6
+ import './chunk-Z5GEFMRW.js';
7
+
8
+ createRequire(import.meta.url);
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env node
2
+ import { createRequire } from 'node:module';
3
+ export { DOCKERD_LOG, HELPER_PATH, realGuestHost, relayPortsFrom, runGuestInit } from './chunk-CBPPJSAR.js';
4
+ import './chunk-B2G7JHHB.js';
5
+ import './chunk-AUFG23AA.js';
6
+ import './chunk-Z5GEFMRW.js';
7
+
8
+ createRequire(import.meta.url);
@@ -0,0 +1,9 @@
1
+ #!/usr/bin/env node
2
+ import { createRequire } from 'node:module';
3
+ export { AUTO_SYNC_DEBOUNCE_MS, WORK_LEASE_RENEW_DEBOUNCE_MS, WORK_LINK_RETRIES, contextPossiblyGoverned, handleWorkLaunch, handleWorkSessionEnd, hookFiredMarkerPath, maybeAutoSync, maybeRenewWorkLease, runHookFast, spoolDir } from './chunk-UFFXNXLQ.js';
4
+ import './chunk-XCCLFVXM.js';
5
+ import './chunk-BJ3KTMDH.js';
6
+ import './chunk-VNVZDWO3.js';
7
+ import './chunk-Z5GEFMRW.js';
8
+
9
+ createRequire(import.meta.url);
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
  import { createRequire } from 'node:module';
3
- export { hookExecSpoolPath, hookWrapStdio, installAbortForwarding, makeTailCollector, parseHookWrapArgs, runHookWrapFast, spoolDir } from './chunk-YOOSBOKS.js';
4
- import './chunk-R5KBGVII.js';
3
+ export { hookExecSpoolPath, hookWrapStdio, installAbortForwarding, makeTailCollector, parseHookWrapArgs, runHookWrapFast, spoolDir } from './chunk-F7CU5ABH.js';
4
+ import './chunk-5QRP3MVS.js';
5
5
  import './chunk-VNVZDWO3.js';
6
6
  import './chunk-Z5GEFMRW.js';
7
7
 
@@ -0,0 +1,7 @@
1
+ #!/usr/bin/env node
2
+ import { createRequire } from 'node:module';
3
+ export { ensureHostKey, loadHostKey, signerFor } from './chunk-M5POMKKW.js';
4
+ import './chunk-ZM2IMMYN.js';
5
+ import './chunk-Z5GEFMRW.js';
6
+
7
+ createRequire(import.meta.url);